Here is an updated patch addressing most of Uwe's and Peter's
[coreboot.git] / src / southbridge / intel / i3100 / i3100_lpc.c
1 /*
2  * This file is part of the coreboot project.
3  *
4  * Copyright (C) 2004 Linux Networx
5  * Copyright (C) 2008 Arastra, Inc.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19  *
20  */
21
22 /* This code is based on src/southbridge/intel/esb6300/esb6300_lpc.c */
23
24 #include <console/console.h>
25 #include <device/device.h>
26 #include <device/pci.h>
27 #include <device/pci_ids.h>
28 #include <device/pci_ops.h>
29 #include <pc80/mc146818rtc.h>
30 #include <pc80/isa-dma.h>
31 #include <arch/io.h>
32 #include "i3100.h"
33
34 #define ACPI_BAR 0x40
35 #define GPIO_BAR 0x48
36 #define RCBA 0xf0
37
38 #define NMI_OFF 0
39 #define MAINBOARD_POWER_OFF 0
40 #define MAINBOARD_POWER_ON  1
41
42 #ifndef MAINBOARD_POWER_ON_AFTER_FAIL
43 #define MAINBOARD_POWER_ON_AFTER_FAIL MAINBOARD_POWER_ON
44 #endif
45
46 #define ALL             (0xff << 24)
47 #define NONE            (0)
48 #define DISABLED        (1 << 16)
49 #define ENABLED         (0 << 16)
50 #define TRIGGER_EDGE    (0 << 15)
51 #define TRIGGER_LEVEL   (1 << 15)
52 #define POLARITY_HIGH   (0 << 13)
53 #define POLARITY_LOW    (1 << 13)
54 #define PHYSICAL_DEST   (0 << 11)
55 #define LOGICAL_DEST    (1 << 11)
56 #define ExtINT          (7 << 8)
57 #define NMI             (4 << 8)
58 #define SMI             (2 << 8)
59 #define INT             (1 << 8)
60
61 static void setup_ioapic(device_t dev)
62 {
63         int i;
64         u32 value_low, value_high;
65         u32 ioapic_base = 0xfec00000;
66         volatile u32 *l;
67         u32 interrupts;
68         struct resource *res;
69
70         /* Enable IO APIC */
71         res = find_resource(dev, RCBA);
72         if (!res) {
73                 return;
74         }
75         *((u8 *)(res->base + 0x31ff)) |= (1 << 0);
76
77         l = (u32 *) ioapic_base;
78
79         l[0] = 0x01;
80         interrupts = (l[04] >> 16) & 0xff;
81         for (i = 0; i < interrupts; i++) {
82                 l[0] = (i * 2) + 0x10;
83                 l[4] = DISABLED;
84                 value_low = l[4];
85                 l[0] = (i * 2) + 0x11;
86                 l[4] = NONE; /* Should this be an address? */
87                 value_high = l[4];
88                 if (value_low == 0xffffffff) {
89                         printk_warning("%d IO APIC not responding.\n",
90                                 dev_path(dev));
91                         return;
92                 }
93         }
94
95         /* Put the APIC in virtual wire mode */
96         l[0] = 0x10;
97         l[4] = ENABLED | TRIGGER_EDGE | POLARITY_HIGH | PHYSICAL_DEST | ExtINT;
98 }
99
100 #define SERIRQ_CNTL 0x64
101 static void i3100_enable_serial_irqs(device_t dev)
102 {
103         /* set packet length and toggle silent mode bit */
104         pci_write_config8(dev, SERIRQ_CNTL, (1 << 7)|(1 << 6)|((21 - 17) << 2)|(0 << 0));
105         pci_write_config8(dev, SERIRQ_CNTL, (1 << 7)|(0 << 6)|((21 - 17) << 2)|(0 << 0));
106 }
107
108 typedef struct southbridge_intel_i3100_config config_t;
109
110 static void set_i3100_gpio_use_sel(
111         device_t dev, struct resource *res, config_t *config)
112 {
113         u32 gpio_use_sel, gpio_use_sel2;
114
115         gpio_use_sel  = 0x1b0ce7c3;
116         gpio_use_sel2 = 0x00000107;
117         outl(gpio_use_sel,  res->base + 0x00);
118         outl(gpio_use_sel2, res->base + 0x30);
119 }
120
121 static void set_i3100_gpio_direction(
122         device_t dev, struct resource *res, config_t *config)
123 {
124         u32 gpio_io_sel, gpio_io_sel2;
125
126         gpio_io_sel  = 0xed00ffff;
127         gpio_io_sel2 = 0x00000307;
128         outl(gpio_io_sel,  res->base + 0x04);
129         outl(gpio_io_sel2, res->base + 0x34);
130 }
131
132 static void set_i3100_gpio_level(
133         device_t dev, struct resource *res, config_t *config)
134 {
135         u32 gpio_lvl, gpio_lvl2;
136         u32 gpio_blink;
137
138         gpio_lvl   = 0x00030000;
139         gpio_blink = 0x00000000;
140         gpio_lvl2  = 0x00000300;
141         outl(gpio_lvl,   res->base + 0x0c);
142         outl(gpio_blink, res->base + 0x18);
143         outl(gpio_lvl2,  res->base + 0x38);
144 }
145
146 static void set_i3100_gpio_inv(
147         device_t dev, struct resource *res, config_t *config)
148 {
149         u32 gpio_inv;
150
151         gpio_inv   = 0x00006000;
152         outl(gpio_inv,   res->base + 0x2c);
153 }
154
155 static void i3100_pirq_init(device_t dev)
156 {
157         config_t *config;
158
159         /* Get the chip configuration */
160         config = dev->chip_info;
161
162         if(config->pirq_a_d) {
163                 pci_write_config32(dev, 0x60, config->pirq_a_d);
164         }
165         if(config->pirq_e_h) {
166                 pci_write_config32(dev, 0x68, config->pirq_e_h);
167         }
168 }
169
170
171 static void i3100_gpio_init(device_t dev)
172 {
173         struct resource *res;
174         config_t *config;
175
176         /* Skip if I don't have any configuration */
177         if (!dev->chip_info) {
178                 return;
179         }
180         /* The programmer is responsible for ensuring
181          * a valid gpio configuration.
182          */
183
184         /* Get the chip configuration */
185         config = dev->chip_info;
186         /* Find the GPIO bar */
187         res = find_resource(dev, GPIO_BAR);
188         if (!res) {
189                 return;
190         }
191
192         /* Set the use selects */
193         set_i3100_gpio_use_sel(dev, res, config);
194
195         /* Set the IO direction */
196         set_i3100_gpio_direction(dev, res, config);
197
198         /* Setup the input inverters */
199         set_i3100_gpio_inv(dev, res, config);
200
201         /* Set the value on the GPIO output pins */
202         set_i3100_gpio_level(dev, res, config);
203
204 }
205
206
207 static void lpc_init(struct device *dev)
208 {
209         u8 byte;
210         int pwr_on = MAINBOARD_POWER_ON_AFTER_FAIL;
211
212         setup_ioapic(dev);
213
214         /* Decode 0xffc00000 - 0xffffffff to fwh idsel 0 */
215         pci_write_config32(dev, 0xd0, 0x00000000);
216
217         i3100_enable_serial_irqs(dev);
218
219         get_option(&pwr_on, "power_on_after_fail");
220         byte = pci_read_config8(dev, 0xa4);
221         byte &= 0xfe;
222         if (!pwr_on) {
223                 byte |= 1;
224         }
225         pci_write_config8(dev, 0xa4, byte);
226         printk_info("set power %s after power fail\n", pwr_on ? "on" : "off");
227
228         /* Set up the PIRQ */
229         i3100_pirq_init(dev);
230
231         /* Set the state of the gpio lines */
232         i3100_gpio_init(dev);
233
234         /* Initialize the real time clock */
235         rtc_init(0);
236
237         /* Initialize isa dma */
238         isa_dma_init();
239 }
240
241 static void i3100_lpc_read_resources(device_t dev)
242 {
243         struct resource *res;
244
245         /* Get the normal pci resources of this device */
246         pci_dev_read_resources(dev);
247
248         /* Add the ACPI BAR */
249         res = pci_get_resource(dev, ACPI_BAR);
250
251         /* Add the GPIO BAR */
252         res = pci_get_resource(dev, GPIO_BAR);
253
254         /* Add an extra subtractive resource for both memory and I/O */
255         res = new_resource(dev, IOINDEX_SUBTRACTIVE(0, 0));
256         res->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE | IORESOURCE_ASSIGNED;
257
258         res = new_resource(dev, IOINDEX_SUBTRACTIVE(1, 0));
259         res->flags = IORESOURCE_MEM | IORESOURCE_SUBTRACTIVE | IORESOURCE_ASSIGNED;
260
261         /* Add resource for RCBA */
262         res = new_resource(dev, RCBA);
263         res->size = 0x4000;
264         res->limit = 0xffffc000;
265         res->align = 14;
266         res->gran = 14;
267         res->flags = IORESOURCE_MEM;
268 }
269
270 static void i3100_lpc_enable_resources(device_t dev)
271 {
272         u8 acpi_cntl, gpio_cntl;
273
274         /* Enable the normal pci resources */
275         pci_dev_enable_resources(dev);
276
277         /* Enable the ACPI bar */
278         acpi_cntl = pci_read_config8(dev, 0x44);
279         acpi_cntl |= (1 << 4);
280         pci_write_config8(dev, 0x44, acpi_cntl);
281
282         /* Enable the GPIO bar */
283         gpio_cntl = pci_read_config8(dev, 0x4c);
284         gpio_cntl |= (1 << 4);
285         pci_write_config8(dev, 0x4c, gpio_cntl);
286
287         /* Enable the RCBA */
288         pci_write_config32(dev, RCBA, pci_read_config32(dev, RCBA) | (1 << 0));
289
290         enable_childrens_resources(dev);
291 }
292
293 static struct pci_operations lops_pci = {
294         .set_subsystem = 0,
295 };
296
297 static struct device_operations lpc_ops  = {
298         .read_resources   = i3100_lpc_read_resources,
299         .set_resources    = pci_dev_set_resources,
300         .enable_resources = i3100_lpc_enable_resources,
301         .init             = lpc_init,
302         .scan_bus         = scan_static_bus,
303         .enable           = i3100_enable,
304         .ops_pci          = &lops_pci,
305 };
306
307 static struct pci_driver lpc_driver __pci_driver = {
308         .ops    = &lpc_ops,
309         .vendor = PCI_VENDOR_ID_INTEL,
310         .device = PCI_DEVICE_ID_INTEL_3100_LPC,
311 };