blob: 7e0fc89b69721e4a9bd344af5403acd95c0c9489 [file] [log] [blame]
Yinghai Luc65bd562007-02-01 00:10:05 +00001/*
Stefan Reinauer7e61e452008-01-18 10:35:56 +00002 * This file is part of the coreboot project.
Yinghai Luc65bd562007-02-01 00:10:05 +00003 *
4 * Copyright (C) 2003 Linux Networx
5 * Copyright (C) 2003 SuSE Linux AG
6 * Copyright (C) 2004 Tyan Computer
7 * Written by Yinghai Lu <yhlu@tyan.com> for Tyan Computer.
8 * Copyright (C) 2006,2007 AMD
9 * Written by Yinghai Lu <yinghai.lu@amd.com> for AMD.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
Yinghai Luc65bd562007-02-01 00:10:05 +000020 */
21
22#include <console/console.h>
23#include <device/device.h>
24#include <device/pci.h>
25#include <device/pnp.h>
26#include <device/pci_ids.h>
27#include <device/pci_ops.h>
28#include <pc80/mc146818rtc.h>
29#include <pc80/isa-dma.h>
Yinghai Luc65bd562007-02-01 00:10:05 +000030#include <arch/io.h>
Stefan Reinauer0401bd82010-01-16 18:31:34 +000031#include <arch/ioapic.h>
Yinghai Luc65bd562007-02-01 00:10:05 +000032#include <cpu/x86/lapic.h>
Vladimir Serbinenko6985d4e2014-09-21 14:31:19 +020033#include <arch/acpi.h>
Carl-Daniel Hailfinger2ee67792008-10-01 12:52:52 +000034#include <stdlib.h>
Vladimir Serbinenkof21271e2014-10-16 18:00:27 +020035#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
Vladimir Serbinenko6985d4e2014-09-21 14:31:19 +020036#include <arch/acpigen.h>
37#endif
Vladimir Serbinenko47432542014-10-05 14:54:26 +020038#include <cpu/amd/powernow.h>
Yinghai Luc65bd562007-02-01 00:10:05 +000039#include "mcp55.h"
40
41#define NMI_OFF 0
42
Yinghai Luc65bd562007-02-01 00:10:05 +000043// 0x7a or e3
44#define PREVIOUS_POWER_STATE 0x7A
45
46#define MAINBOARD_POWER_OFF 0
47#define MAINBOARD_POWER_ON 1
Yinghai Luf327d9f2008-02-20 17:41:38 +000048#define SLOW_CPU_OFF 0
49#define SLOW_CPU__ON 1
Yinghai Luc65bd562007-02-01 00:10:05 +000050
Elyes HAOUAS1df39c32018-05-19 10:55:18 +020051static void lpc_common_init(struct device *dev, int master)
Yinghai Luc65bd562007-02-01 00:10:05 +000052{
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +000053 u8 byte;
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080054 void *ioapic_base;
Yinghai Luc65bd562007-02-01 00:10:05 +000055
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +000056 /* IOAPIC initialization. */
Yinghai Luc65bd562007-02-01 00:10:05 +000057 byte = pci_read_config8(dev, 0x74);
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +000058 byte |= (1 << 0); /* Enable IOAPIC. */
Yinghai Luc65bd562007-02-01 00:10:05 +000059 pci_write_config8(dev, 0x74, byte);
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080060 ioapic_base = (void *)pci_read_config32(dev, PCI_BASE_ADDRESS_1); /* 0x14 */
Yinghai Luc65bd562007-02-01 00:10:05 +000061
Stefan Reinauer0401bd82010-01-16 18:31:34 +000062 if (master)
63 setup_ioapic(ioapic_base, 0);
Stefan Reinauer14e22772010-04-27 06:56:47 +000064 else
Stefan Reinauer0401bd82010-01-16 18:31:34 +000065 clear_ioapic(ioapic_base);
Yinghai Luc65bd562007-02-01 00:10:05 +000066}
67
Elyes HAOUAS1df39c32018-05-19 10:55:18 +020068static void lpc_slave_init(struct device *dev)
Yinghai Luc65bd562007-02-01 00:10:05 +000069{
Yinghai Luf327d9f2008-02-20 17:41:38 +000070 lpc_common_init(dev, 0);
Yinghai Luc65bd562007-02-01 00:10:05 +000071}
72
Yinghai Luc65bd562007-02-01 00:10:05 +000073static void enable_hpet(struct device *dev)
74{
75 unsigned long hpet_address;
76
Jonathan A. Kollasch6f0e8bd2015-07-15 13:47:33 -050077 pci_write_config32(dev, 0x44, CONFIG_HPET_ADDRESS|1);
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +000078 hpet_address=pci_read_config32(dev, 0x44) & 0xfffffffe;
Myles Watson08e0fb82010-03-22 16:33:25 +000079 printk(BIOS_DEBUG, "enabling HPET @0x%lx\n", hpet_address);
Yinghai Luc65bd562007-02-01 00:10:05 +000080}
Yinghai Luc65bd562007-02-01 00:10:05 +000081
Elyes HAOUAS1df39c32018-05-19 10:55:18 +020082static void lpc_init(struct device *dev)
Yinghai Luc65bd562007-02-01 00:10:05 +000083{
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +000084 u8 byte, byte_old;
85 int on, nmi_option;
Yinghai Luc65bd562007-02-01 00:10:05 +000086
Yinghai Luf327d9f2008-02-20 17:41:38 +000087 lpc_common_init(dev, 1);
Yinghai Luc65bd562007-02-01 00:10:05 +000088
Yinghai Luc65bd562007-02-01 00:10:05 +000089 /* power after power fail */
90
91#if 1
Nico Huber9faae2b2018-11-14 00:00:35 +010092 on = CONFIG_MAINBOARD_POWER_FAILURE_STATE;
Luc Verhaegena9c5ea02009-06-03 14:19:33 +000093 get_option(&on, "power_on_after_fail");
Yinghai Luc65bd562007-02-01 00:10:05 +000094 byte = pci_read_config8(dev, PREVIOUS_POWER_STATE);
95 byte &= ~0x40;
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +000096 if (!on)
Yinghai Luc65bd562007-02-01 00:10:05 +000097 byte |= 0x40;
Yinghai Luc65bd562007-02-01 00:10:05 +000098 pci_write_config8(dev, PREVIOUS_POWER_STATE, byte);
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +000099 printk(BIOS_INFO, "set power %s after power fail\n", on ? "on" : "off");
Yinghai Luc65bd562007-02-01 00:10:05 +0000100#endif
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000101 /* Throttle the CPU speed down for testing. */
Yinghai Luc65bd562007-02-01 00:10:05 +0000102 on = SLOW_CPU_OFF;
Luc Verhaegena9c5ea02009-06-03 14:19:33 +0000103 get_option(&on, "slow_cpu");
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000104 if (on) {
105 u16 pm10_bar;
106 u32 dword;
107 pm10_bar = (pci_read_config16(dev, 0x60) & 0xff00);
108 outl(((on << 1) + 0x10), (pm10_bar + 0x10));
Yinghai Luc65bd562007-02-01 00:10:05 +0000109 dword = inl(pm10_bar + 0x10);
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000110 on = 8 - on;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000111 printk(BIOS_DEBUG, "Throttling CPU %2d.%1.1d percent.\n",
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000112 (on * 12) + (on >> 1), (on & 1) * 5);
Yinghai Luc65bd562007-02-01 00:10:05 +0000113 }
114
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000115 /* Enable error reporting. */
116 /* Set up sync flood detected. */
Yinghai Luc65bd562007-02-01 00:10:05 +0000117 byte = pci_read_config8(dev, 0x47);
118 byte |= (1 << 1);
119 pci_write_config8(dev, 0x47, byte);
120
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000121 /* Set up NMI on errors. */
122 byte = inb(0x70); /* RTC70 */
Yinghai Luc65bd562007-02-01 00:10:05 +0000123 byte_old = byte;
124 nmi_option = NMI_OFF;
Luc Verhaegena9c5ea02009-06-03 14:19:33 +0000125 get_option(&nmi_option, "nmi");
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000126 if (nmi_option)
127 byte &= ~(1 << 7); /* Set NMI. */
128 else
Elyes HAOUAS64e091f2018-04-26 22:16:42 +0200129 byte |= (1 << 7); /* Can't mask NMI from PCI-E and NMI_NOW. */
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000130 if (byte != byte_old)
Ed Swierke42e1422009-07-10 15:05:35 +0000131 outb(byte, 0x70);
Yinghai Luc65bd562007-02-01 00:10:05 +0000132
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000133 /* Initialize the real time clock. */
Gabe Blackb3f08c62014-04-30 17:12:25 -0700134 cmos_init(0);
Yinghai Luc65bd562007-02-01 00:10:05 +0000135
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000136 /* Initialize ISA DMA. */
Yinghai Luc65bd562007-02-01 00:10:05 +0000137 isa_dma_init();
138
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000139 /* Initialize the High Precision Event Timers (HPET). */
Harald Gutmann62cfe162009-06-19 12:03:40 +0000140 enable_hpet(dev);
Yinghai Luc65bd562007-02-01 00:10:05 +0000141}
142
Elyes HAOUAS1df39c32018-05-19 10:55:18 +0200143static void mcp55_lpc_read_resources(struct device *dev)
Yinghai Luc65bd562007-02-01 00:10:05 +0000144{
145 struct resource *res;
Yinghai Luc65bd562007-02-01 00:10:05 +0000146
Myles Watson29cc9ed2009-07-02 18:56:24 +0000147 /* Get the normal PCI resources of this device. */
148 /* We got one for APIC, or one more for TRAP. */
149 pci_dev_read_resources(dev);
Yinghai Luc65bd562007-02-01 00:10:05 +0000150
Myles Watson29cc9ed2009-07-02 18:56:24 +0000151 /* Add an extra subtractive resource for both memory and I/O. */
Yinghai Luc65bd562007-02-01 00:10:05 +0000152 res = new_resource(dev, IOINDEX_SUBTRACTIVE(0, 0));
Myles Watson29cc9ed2009-07-02 18:56:24 +0000153 res->base = 0;
154 res->size = 0x1000;
155 res->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE |
156 IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
Yinghai Luc65bd562007-02-01 00:10:05 +0000157
158 res = new_resource(dev, IOINDEX_SUBTRACTIVE(1, 0));
Myles Watson29cc9ed2009-07-02 18:56:24 +0000159 res->base = 0xff800000;
160 res->size = 0x00800000; /* 8 MB for flash */
161 res->flags = IORESOURCE_MEM | IORESOURCE_SUBTRACTIVE |
162 IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
Yinghai Luc65bd562007-02-01 00:10:05 +0000163
Myles Watson29cc9ed2009-07-02 18:56:24 +0000164 res = new_resource(dev, 3); /* IOAPIC */
Uwe Hermann74d1a6e2010-10-12 17:34:08 +0000165 res->base = IO_APIC_ADDR;
Myles Watson29cc9ed2009-07-02 18:56:24 +0000166 res->size = 0x00001000;
167 res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
Yinghai Luc65bd562007-02-01 00:10:05 +0000168}
169
170/**
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000171 * Enable resources for children devices.
Yinghai Luc65bd562007-02-01 00:10:05 +0000172 *
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000173 * @param dev The device whose children's resources are to be enabled.
Yinghai Luc65bd562007-02-01 00:10:05 +0000174 */
Elyes HAOUAS1df39c32018-05-19 10:55:18 +0200175static void mcp55_lpc_enable_childrens_resources(struct device *dev)
Yinghai Luc65bd562007-02-01 00:10:05 +0000176{
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000177 u32 reg, reg_var[4];
178 int i, var_num = 0;
Myles Watson894a3472010-06-09 22:41:35 +0000179 struct bus *link;
Yinghai Luc65bd562007-02-01 00:10:05 +0000180
181 reg = pci_read_config32(dev, 0xa0);
182
Myles Watson894a3472010-06-09 22:41:35 +0000183 for (link = dev->link_list; link; link = link->next) {
Elyes HAOUAS1df39c32018-05-19 10:55:18 +0200184 struct device *child;
Myles Watson894a3472010-06-09 22:41:35 +0000185 for (child = link->children; child; child = child->sibling) {
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000186 if (child->enabled && (child->path.type == DEVICE_PATH_PNP)) {
Myles Watsonc25cc112010-05-21 14:33:48 +0000187 struct resource *res;
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000188 for (res = child->resource_list; res; res = res->next) {
189 unsigned long base, end; /* Don't need long long. */
190 if (!(res->flags & IORESOURCE_IO))
191 continue;
Yinghai Luc65bd562007-02-01 00:10:05 +0000192 base = res->base;
193 end = resource_end(res);
Myles Watson08e0fb82010-03-22 16:33:25 +0000194 printk(BIOS_DEBUG, "mcp55 lpc decode:%s, base=0x%08lx, end=0x%08lx\n",dev_path(child),base, end);
Elyes HAOUASf9de5a42018-05-03 17:21:02 +0200195 switch (base) {
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000196 case 0x3f8: /* COM1 */
197 reg |= (1 << 0);
198 break;
199 case 0x2f8: /* COM2 */
200 reg |= (1 << 1);
201 break;
202 case 0x378: /* Parallel 1 */
203 reg |= (1 << 24);
204 break;
205 case 0x3f0: /* FD0 */
206 reg |= (1 << 20);
207 break;
208 case 0x220: /* Audio 0 */
209 reg |= (1 << 8);
210 break;
211 case 0x300: /* Midi 0 */
212 reg |= (1 << 12);
213 break;
Yinghai Luc65bd562007-02-01 00:10:05 +0000214 }
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000215 if ((base == 0x290)
216 || (base >= 0x400)) {
217 /* Only 4 var; compact them? */
218 if (var_num >= 4)
219 continue;
220 reg |= (1 << (28 + var_num));
221 reg_var[var_num++] = (base & 0xffff) | ((end & 0xffff) << 16);
Yinghai Luc65bd562007-02-01 00:10:05 +0000222 }
223 }
224 }
225 }
226 }
227 pci_write_config32(dev, 0xa0, reg);
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000228 for (i = 0; i < var_num; i++)
229 pci_write_config32(dev, 0xa8 + i * 4, reg_var[i]);
Yinghai Luc65bd562007-02-01 00:10:05 +0000230}
231
Elyes HAOUAS1df39c32018-05-19 10:55:18 +0200232static void mcp55_lpc_enable_resources(struct device *dev)
Yinghai Luc65bd562007-02-01 00:10:05 +0000233{
234 pci_dev_enable_resources(dev);
235 mcp55_lpc_enable_childrens_resources(dev);
236}
237
Arthur Heymans1eef32d2016-12-28 12:35:06 +0100238#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
Elyes HAOUAS1df39c32018-05-19 10:55:18 +0200239static void southbridge_acpi_fill_ssdt_generator(struct device *device)
Arthur Heymans1eef32d2016-12-28 12:35:06 +0100240{
241 amd_generate_powernow(0, 0, 0);
242}
243#endif
244
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000245static struct device_operations lpc_ops = {
246 .read_resources = mcp55_lpc_read_resources,
247 .set_resources = pci_dev_set_resources,
248 .enable_resources = mcp55_lpc_enable_resources,
Arthur Heymans1eef32d2016-12-28 12:35:06 +0100249#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
250 .acpi_fill_ssdt_generator = southbridge_acpi_fill_ssdt_generator,
251 .write_acpi_tables = acpi_write_hpet,
252#endif
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000253 .init = lpc_init,
Kyösti Mälkkid0e212c2015-02-26 20:47:47 +0200254 .scan_bus = scan_lpc_bus,
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000255 .ops_pci = &mcp55_pci_ops,
Yinghai Luc65bd562007-02-01 00:10:05 +0000256};
Patrick Georgiefff7332012-07-26 19:48:23 +0200257static const unsigned short lpc_ids[] = {
258 PCI_DEVICE_ID_NVIDIA_MCP55_LPC,
259 PCI_DEVICE_ID_NVIDIA_MCP55_PRO,
260 PCI_DEVICE_ID_NVIDIA_MCP55_LPC_2,
261 PCI_DEVICE_ID_NVIDIA_MCP55_LPC_3,
262 PCI_DEVICE_ID_NVIDIA_MCP55_LPC_4,
263 PCI_DEVICE_ID_NVIDIA_MCP55_LPC_5,
264 PCI_DEVICE_ID_NVIDIA_MCP55_LPC_6,
265 0
266};
Stefan Reinauerf1cf1f72007-10-24 09:08:58 +0000267static const struct pci_driver lpc_driver __pci_driver = {
Yinghai Luc65bd562007-02-01 00:10:05 +0000268 .ops = &lpc_ops,
269 .vendor = PCI_VENDOR_ID_NVIDIA,
Patrick Georgiefff7332012-07-26 19:48:23 +0200270 .devices = lpc_ids,
Yinghai Luc65bd562007-02-01 00:10:05 +0000271};
272
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000273static struct device_operations lpc_slave_ops = {
274 .read_resources = mcp55_lpc_read_resources,
275 .set_resources = pci_dev_set_resources,
276 .enable_resources = pci_dev_enable_resources,
Vladimir Serbinenko83f81ca2014-11-09 13:30:50 +0100277#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
Vladimir Serbinenko6985d4e2014-09-21 14:31:19 +0200278 .write_acpi_tables = acpi_write_hpet,
279#endif
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000280 .init = lpc_slave_init,
Uwe Hermannc7f0c8f2011-01-04 19:51:33 +0000281 .ops_pci = &mcp55_pci_ops,
Yinghai Luc65bd562007-02-01 00:10:05 +0000282};
283
Stefan Reinauerf1cf1f72007-10-24 09:08:58 +0000284static const struct pci_driver lpc_driver_slave __pci_driver = {
Yinghai Luc65bd562007-02-01 00:10:05 +0000285 .ops = &lpc_slave_ops,
286 .vendor = PCI_VENDOR_ID_NVIDIA,
287 .device = PCI_DEVICE_ID_NVIDIA_MCP55_SLAVE,
288};