diff options
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/Kconfig | 18 | ||||
-rw-r--r-- | arch/x86/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/x86/kernel/olpc.c | 260 | ||||
-rw-r--r-- | arch/x86/pci/Makefile_32 | 1 | ||||
-rw-r--r-- | arch/x86/pci/init.c | 3 | ||||
-rw-r--r-- | arch/x86/pci/olpc.c | 313 | ||||
-rw-r--r-- | arch/x86/pci/pci.h | 1 |
7 files changed, 597 insertions, 1 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 1d9d874cba59..f70e3e3a9fa7 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -1504,6 +1504,10 @@ config PCI_GODIRECT | |||
1504 | config PCI_GOANY | 1504 | config PCI_GOANY |
1505 | bool "Any" | 1505 | bool "Any" |
1506 | 1506 | ||
1507 | config PCI_GOOLPC | ||
1508 | bool "OLPC" | ||
1509 | depends on OLPC | ||
1510 | |||
1507 | endchoice | 1511 | endchoice |
1508 | 1512 | ||
1509 | config PCI_BIOS | 1513 | config PCI_BIOS |
@@ -1513,12 +1517,17 @@ config PCI_BIOS | |||
1513 | # x86-64 doesn't support PCI BIOS access from long mode so always go direct. | 1517 | # x86-64 doesn't support PCI BIOS access from long mode so always go direct. |
1514 | config PCI_DIRECT | 1518 | config PCI_DIRECT |
1515 | def_bool y | 1519 | def_bool y |
1516 | depends on PCI && (X86_64 || (PCI_GODIRECT || PCI_GOANY) || X86_VISWS) | 1520 | depends on PCI && (X86_64 || (PCI_GODIRECT || PCI_GOANY || PCI_GOOLPC) || X86_VISWS) |
1517 | 1521 | ||
1518 | config PCI_MMCONFIG | 1522 | config PCI_MMCONFIG |
1519 | def_bool y | 1523 | def_bool y |
1520 | depends on X86_32 && PCI && ACPI && (PCI_GOMMCONFIG || PCI_GOANY) | 1524 | depends on X86_32 && PCI && ACPI && (PCI_GOMMCONFIG || PCI_GOANY) |
1521 | 1525 | ||
1526 | config PCI_OLPC | ||
1527 | bool | ||
1528 | depends on PCI && PCI_GOOLPC | ||
1529 | default y | ||
1530 | |||
1522 | config PCI_DOMAINS | 1531 | config PCI_DOMAINS |
1523 | def_bool y | 1532 | def_bool y |
1524 | depends on PCI | 1533 | depends on PCI |
@@ -1638,6 +1647,13 @@ config GEODE_MFGPT_TIMER | |||
1638 | MFGPTs have a better resolution and max interval than the | 1647 | MFGPTs have a better resolution and max interval than the |
1639 | generic PIT, and are suitable for use as high-res timers. | 1648 | generic PIT, and are suitable for use as high-res timers. |
1640 | 1649 | ||
1650 | config OLPC | ||
1651 | bool "One Laptop Per Child support" | ||
1652 | default n | ||
1653 | help | ||
1654 | Add support for detecting the unique features of the OLPC | ||
1655 | XO hardware. | ||
1656 | |||
1641 | endif # X86_32 | 1657 | endif # X86_32 |
1642 | 1658 | ||
1643 | config K8_NB | 1659 | config K8_NB |
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index fa19c3819540..350eb1b2a208 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile | |||
@@ -91,6 +91,8 @@ endif | |||
91 | obj-$(CONFIG_SCx200) += scx200.o | 91 | obj-$(CONFIG_SCx200) += scx200.o |
92 | scx200-y += scx200_32.o | 92 | scx200-y += scx200_32.o |
93 | 93 | ||
94 | obj-$(CONFIG_OLPC) += olpc.o | ||
95 | |||
94 | ### | 96 | ### |
95 | # 64 bit specific files | 97 | # 64 bit specific files |
96 | ifeq ($(CONFIG_X86_64),y) | 98 | ifeq ($(CONFIG_X86_64),y) |
diff --git a/arch/x86/kernel/olpc.c b/arch/x86/kernel/olpc.c new file mode 100644 index 000000000000..3e6672274807 --- /dev/null +++ b/arch/x86/kernel/olpc.c | |||
@@ -0,0 +1,260 @@ | |||
1 | /* | ||
2 | * Support for the OLPC DCON and OLPC EC access | ||
3 | * | ||
4 | * Copyright © 2006 Advanced Micro Devices, Inc. | ||
5 | * Copyright © 2007-2008 Andres Salomon <dilinger@debian.org> | ||
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 as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/spinlock.h> | ||
18 | #include <linux/io.h> | ||
19 | #include <linux/string.h> | ||
20 | #include <asm/geode.h> | ||
21 | #include <asm/olpc.h> | ||
22 | |||
23 | #ifdef CONFIG_OPEN_FIRMWARE | ||
24 | #include <asm/ofw.h> | ||
25 | #endif | ||
26 | |||
27 | struct olpc_platform_t olpc_platform_info; | ||
28 | EXPORT_SYMBOL_GPL(olpc_platform_info); | ||
29 | |||
30 | static DEFINE_SPINLOCK(ec_lock); | ||
31 | |||
32 | /* what the timeout *should* be (in ms) */ | ||
33 | #define EC_BASE_TIMEOUT 20 | ||
34 | |||
35 | /* the timeout that bugs in the EC might force us to actually use */ | ||
36 | static int ec_timeout = EC_BASE_TIMEOUT; | ||
37 | |||
38 | static int __init olpc_ec_timeout_set(char *str) | ||
39 | { | ||
40 | if (get_option(&str, &ec_timeout) != 1) { | ||
41 | ec_timeout = EC_BASE_TIMEOUT; | ||
42 | printk(KERN_ERR "olpc-ec: invalid argument to " | ||
43 | "'olpc_ec_timeout=', ignoring!\n"); | ||
44 | } | ||
45 | printk(KERN_DEBUG "olpc-ec: using %d ms delay for EC commands.\n", | ||
46 | ec_timeout); | ||
47 | return 1; | ||
48 | } | ||
49 | __setup("olpc_ec_timeout=", olpc_ec_timeout_set); | ||
50 | |||
51 | /* | ||
52 | * These {i,o}bf_status functions return whether the buffers are full or not. | ||
53 | */ | ||
54 | |||
55 | static inline unsigned int ibf_status(unsigned int port) | ||
56 | { | ||
57 | return !!(inb(port) & 0x02); | ||
58 | } | ||
59 | |||
60 | static inline unsigned int obf_status(unsigned int port) | ||
61 | { | ||
62 | return inb(port) & 0x01; | ||
63 | } | ||
64 | |||
65 | #define wait_on_ibf(p, d) __wait_on_ibf(__LINE__, (p), (d)) | ||
66 | static int __wait_on_ibf(unsigned int line, unsigned int port, int desired) | ||
67 | { | ||
68 | unsigned int timeo; | ||
69 | int state = ibf_status(port); | ||
70 | |||
71 | for (timeo = ec_timeout; state != desired && timeo; timeo--) { | ||
72 | mdelay(1); | ||
73 | state = ibf_status(port); | ||
74 | } | ||
75 | |||
76 | if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) && | ||
77 | timeo < (ec_timeout - EC_BASE_TIMEOUT)) { | ||
78 | printk(KERN_WARNING "olpc-ec: %d: waited %u ms for IBF!\n", | ||
79 | line, ec_timeout - timeo); | ||
80 | } | ||
81 | |||
82 | return !(state == desired); | ||
83 | } | ||
84 | |||
85 | #define wait_on_obf(p, d) __wait_on_obf(__LINE__, (p), (d)) | ||
86 | static int __wait_on_obf(unsigned int line, unsigned int port, int desired) | ||
87 | { | ||
88 | unsigned int timeo; | ||
89 | int state = obf_status(port); | ||
90 | |||
91 | for (timeo = ec_timeout; state != desired && timeo; timeo--) { | ||
92 | mdelay(1); | ||
93 | state = obf_status(port); | ||
94 | } | ||
95 | |||
96 | if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) && | ||
97 | timeo < (ec_timeout - EC_BASE_TIMEOUT)) { | ||
98 | printk(KERN_WARNING "olpc-ec: %d: waited %u ms for OBF!\n", | ||
99 | line, ec_timeout - timeo); | ||
100 | } | ||
101 | |||
102 | return !(state == desired); | ||
103 | } | ||
104 | |||
105 | /* | ||
106 | * This allows the kernel to run Embedded Controller commands. The EC is | ||
107 | * documented at <http://wiki.laptop.org/go/Embedded_controller>, and the | ||
108 | * available EC commands are here: | ||
109 | * <http://wiki.laptop.org/go/Ec_specification>. Unfortunately, while | ||
110 | * OpenFirmware's source is available, the EC's is not. | ||
111 | */ | ||
112 | int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen, | ||
113 | unsigned char *outbuf, size_t outlen) | ||
114 | { | ||
115 | unsigned long flags; | ||
116 | int ret = -EIO; | ||
117 | int i; | ||
118 | |||
119 | spin_lock_irqsave(&ec_lock, flags); | ||
120 | |||
121 | /* Clear OBF */ | ||
122 | for (i = 0; i < 10 && (obf_status(0x6c) == 1); i++) | ||
123 | inb(0x68); | ||
124 | if (i == 10) { | ||
125 | printk(KERN_ERR "olpc-ec: timeout while attempting to " | ||
126 | "clear OBF flag!\n"); | ||
127 | goto err; | ||
128 | } | ||
129 | |||
130 | if (wait_on_ibf(0x6c, 0)) { | ||
131 | printk(KERN_ERR "olpc-ec: timeout waiting for EC to " | ||
132 | "quiesce!\n"); | ||
133 | goto err; | ||
134 | } | ||
135 | |||
136 | restart: | ||
137 | /* | ||
138 | * Note that if we time out during any IBF checks, that's a failure; | ||
139 | * we have to return. There's no way for the kernel to clear that. | ||
140 | * | ||
141 | * If we time out during an OBF check, we can restart the command; | ||
142 | * reissuing it will clear the OBF flag, and we should be alright. | ||
143 | * The OBF flag will sometimes misbehave due to what we believe | ||
144 | * is a hardware quirk.. | ||
145 | */ | ||
146 | printk(KERN_DEBUG "olpc-ec: running cmd 0x%x\n", cmd); | ||
147 | outb(cmd, 0x6c); | ||
148 | |||
149 | if (wait_on_ibf(0x6c, 0)) { | ||
150 | printk(KERN_ERR "olpc-ec: timeout waiting for EC to read " | ||
151 | "command!\n"); | ||
152 | goto err; | ||
153 | } | ||
154 | |||
155 | if (inbuf && inlen) { | ||
156 | /* write data to EC */ | ||
157 | for (i = 0; i < inlen; i++) { | ||
158 | if (wait_on_ibf(0x6c, 0)) { | ||
159 | printk(KERN_ERR "olpc-ec: timeout waiting for" | ||
160 | " EC accept data!\n"); | ||
161 | goto err; | ||
162 | } | ||
163 | printk(KERN_DEBUG "olpc-ec: sending cmd arg 0x%x\n", | ||
164 | inbuf[i]); | ||
165 | outb(inbuf[i], 0x68); | ||
166 | } | ||
167 | } | ||
168 | if (outbuf && outlen) { | ||
169 | /* read data from EC */ | ||
170 | for (i = 0; i < outlen; i++) { | ||
171 | if (wait_on_obf(0x6c, 1)) { | ||
172 | printk(KERN_ERR "olpc-ec: timeout waiting for" | ||
173 | " EC to provide data!\n"); | ||
174 | goto restart; | ||
175 | } | ||
176 | outbuf[i] = inb(0x68); | ||
177 | printk(KERN_DEBUG "olpc-ec: received 0x%x\n", | ||
178 | outbuf[i]); | ||
179 | } | ||
180 | } | ||
181 | |||
182 | ret = 0; | ||
183 | err: | ||
184 | spin_unlock_irqrestore(&ec_lock, flags); | ||
185 | return ret; | ||
186 | } | ||
187 | EXPORT_SYMBOL_GPL(olpc_ec_cmd); | ||
188 | |||
189 | #ifdef CONFIG_OPEN_FIRMWARE | ||
190 | static void __init platform_detect(void) | ||
191 | { | ||
192 | size_t propsize; | ||
193 | u32 rev; | ||
194 | |||
195 | if (ofw("getprop", 4, 1, NULL, "board-revision-int", &rev, 4, | ||
196 | &propsize) || propsize != 4) { | ||
197 | printk(KERN_ERR "ofw: getprop call failed!\n"); | ||
198 | rev = 0; | ||
199 | } | ||
200 | olpc_platform_info.boardrev = be32_to_cpu(rev); | ||
201 | } | ||
202 | #else | ||
203 | static void __init platform_detect(void) | ||
204 | { | ||
205 | /* stopgap until OFW support is added to the kernel */ | ||
206 | olpc_platform_info.boardrev = be32_to_cpu(0xc2); | ||
207 | } | ||
208 | #endif | ||
209 | |||
210 | static int __init olpc_init(void) | ||
211 | { | ||
212 | unsigned char *romsig; | ||
213 | |||
214 | /* The ioremap check is dangerous; limit what we run it on */ | ||
215 | if (!is_geode() || geode_has_vsa2()) | ||
216 | return 0; | ||
217 | |||
218 | spin_lock_init(&ec_lock); | ||
219 | |||
220 | romsig = ioremap(0xffffffc0, 16); | ||
221 | if (!romsig) | ||
222 | return 0; | ||
223 | |||
224 | if (strncmp(romsig, "CL1 Q", 7)) | ||
225 | goto unmap; | ||
226 | if (strncmp(romsig+6, romsig+13, 3)) { | ||
227 | printk(KERN_INFO "OLPC BIOS signature looks invalid. " | ||
228 | "Assuming not OLPC\n"); | ||
229 | goto unmap; | ||
230 | } | ||
231 | |||
232 | printk(KERN_INFO "OLPC board with OpenFirmware %.16s\n", romsig); | ||
233 | olpc_platform_info.flags |= OLPC_F_PRESENT; | ||
234 | |||
235 | /* get the platform revision */ | ||
236 | platform_detect(); | ||
237 | |||
238 | /* assume B1 and above models always have a DCON */ | ||
239 | if (olpc_board_at_least(olpc_board(0xb1))) | ||
240 | olpc_platform_info.flags |= OLPC_F_DCON; | ||
241 | |||
242 | /* get the EC revision */ | ||
243 | olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0, | ||
244 | (unsigned char *) &olpc_platform_info.ecver, 1); | ||
245 | |||
246 | /* check to see if the VSA exists */ | ||
247 | if (geode_has_vsa2()) | ||
248 | olpc_platform_info.flags |= OLPC_F_VSA; | ||
249 | |||
250 | printk(KERN_INFO "OLPC board revision %s%X (EC=%x)\n", | ||
251 | ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "", | ||
252 | olpc_platform_info.boardrev >> 4, | ||
253 | olpc_platform_info.ecver); | ||
254 | |||
255 | unmap: | ||
256 | iounmap(romsig); | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | postcore_initcall(olpc_init); | ||
diff --git a/arch/x86/pci/Makefile_32 b/arch/x86/pci/Makefile_32 index cdd6828b5abb..b859047a6376 100644 --- a/arch/x86/pci/Makefile_32 +++ b/arch/x86/pci/Makefile_32 | |||
@@ -3,6 +3,7 @@ obj-y := i386.o init.o | |||
3 | obj-$(CONFIG_PCI_BIOS) += pcbios.o | 3 | obj-$(CONFIG_PCI_BIOS) += pcbios.o |
4 | obj-$(CONFIG_PCI_MMCONFIG) += mmconfig_32.o direct.o mmconfig-shared.o | 4 | obj-$(CONFIG_PCI_MMCONFIG) += mmconfig_32.o direct.o mmconfig-shared.o |
5 | obj-$(CONFIG_PCI_DIRECT) += direct.o | 5 | obj-$(CONFIG_PCI_DIRECT) += direct.o |
6 | obj-$(CONFIG_PCI_OLPC) += olpc.o | ||
6 | 7 | ||
7 | pci-y := fixup.o | 8 | pci-y := fixup.o |
8 | pci-$(CONFIG_ACPI) += acpi.o | 9 | pci-$(CONFIG_ACPI) += acpi.o |
diff --git a/arch/x86/pci/init.c b/arch/x86/pci/init.c index 3de9f9ba2da6..0f5f7dd2a620 100644 --- a/arch/x86/pci/init.c +++ b/arch/x86/pci/init.c | |||
@@ -14,6 +14,9 @@ static __init int pci_access_init(void) | |||
14 | #ifdef CONFIG_PCI_MMCONFIG | 14 | #ifdef CONFIG_PCI_MMCONFIG |
15 | pci_mmcfg_init(type); | 15 | pci_mmcfg_init(type); |
16 | #endif | 16 | #endif |
17 | #ifdef CONFIG_PCI_OLPC | ||
18 | pci_olpc_init(); | ||
19 | #endif | ||
17 | if (raw_pci_ops) | 20 | if (raw_pci_ops) |
18 | return 0; | 21 | return 0; |
19 | #ifdef CONFIG_PCI_BIOS | 22 | #ifdef CONFIG_PCI_BIOS |
diff --git a/arch/x86/pci/olpc.c b/arch/x86/pci/olpc.c new file mode 100644 index 000000000000..5e7636558c02 --- /dev/null +++ b/arch/x86/pci/olpc.c | |||
@@ -0,0 +1,313 @@ | |||
1 | /* | ||
2 | * Low-level PCI config space access for OLPC systems who lack the VSA | ||
3 | * PCI virtualization software. | ||
4 | * | ||
5 | * Copyright © 2006 Advanced Micro Devices, 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 as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * The AMD Geode chipset (ie: GX2 processor, cs5536 I/O companion device) | ||
13 | * has some I/O functions (display, southbridge, sound, USB HCIs, etc) | ||
14 | * that more or less behave like PCI devices, but the hardware doesn't | ||
15 | * directly implement the PCI configuration space headers. AMD provides | ||
16 | * "VSA" (Virtual System Architecture) software that emulates PCI config | ||
17 | * space for these devices, by trapping I/O accesses to PCI config register | ||
18 | * (CF8/CFC) and running some code in System Management Mode interrupt state. | ||
19 | * On the OLPC platform, we don't want to use that VSA code because | ||
20 | * (a) it slows down suspend/resume, and (b) recompiling it requires special | ||
21 | * compilers that are hard to get. So instead of letting the complex VSA | ||
22 | * code simulate the PCI config registers for the on-chip devices, we | ||
23 | * just simulate them the easy way, by inserting the code into the | ||
24 | * pci_write_config and pci_read_config path. Most of the config registers | ||
25 | * are read-only anyway, so the bulk of the simulation is just table lookup. | ||
26 | */ | ||
27 | |||
28 | #include <linux/pci.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <asm/olpc.h> | ||
31 | #include <asm/geode.h> | ||
32 | #include "pci.h" | ||
33 | |||
34 | /* | ||
35 | * In the tables below, the first two line (8 longwords) are the | ||
36 | * size masks that are used when the higher level PCI code determines | ||
37 | * the size of the region by writing ~0 to a base address register | ||
38 | * and reading back the result. | ||
39 | * | ||
40 | * The following lines are the values that are read during normal | ||
41 | * PCI config access cycles, i.e. not after just having written | ||
42 | * ~0 to a base address register. | ||
43 | */ | ||
44 | |||
45 | static const uint32_t lxnb_hdr[] = { /* dev 1 function 0 - devfn = 8 */ | ||
46 | 0x0, 0x0, 0x0, 0x0, | ||
47 | 0x0, 0x0, 0x0, 0x0, | ||
48 | |||
49 | 0x281022, 0x2200005, 0x6000021, 0x80f808, /* AMD Vendor ID */ | ||
50 | 0x0, 0x0, 0x0, 0x0, /* No virtual registers, hence no BAR */ | ||
51 | 0x0, 0x0, 0x0, 0x28100b, | ||
52 | 0x0, 0x0, 0x0, 0x0, | ||
53 | 0x0, 0x0, 0x0, 0x0, | ||
54 | 0x0, 0x0, 0x0, 0x0, | ||
55 | 0x0, 0x0, 0x0, 0x0, | ||
56 | }; | ||
57 | |||
58 | static const uint32_t gxnb_hdr[] = { /* dev 1 function 0 - devfn = 8 */ | ||
59 | 0xfffffffd, 0x0, 0x0, 0x0, | ||
60 | 0x0, 0x0, 0x0, 0x0, | ||
61 | |||
62 | 0x28100b, 0x2200005, 0x6000021, 0x80f808, /* NSC Vendor ID */ | ||
63 | 0xac1d, 0x0, 0x0, 0x0, /* I/O BAR - base of virtual registers */ | ||
64 | 0x0, 0x0, 0x0, 0x28100b, | ||
65 | 0x0, 0x0, 0x0, 0x0, | ||
66 | 0x0, 0x0, 0x0, 0x0, | ||
67 | 0x0, 0x0, 0x0, 0x0, | ||
68 | 0x0, 0x0, 0x0, 0x0, | ||
69 | }; | ||
70 | |||
71 | static const uint32_t lxfb_hdr[] = { /* dev 1 function 1 - devfn = 9 */ | ||
72 | 0xff000008, 0xffffc000, 0xffffc000, 0xffffc000, | ||
73 | 0xffffc000, 0x0, 0x0, 0x0, | ||
74 | |||
75 | 0x20811022, 0x2200003, 0x3000000, 0x0, /* AMD Vendor ID */ | ||
76 | 0xfd000000, 0xfe000000, 0xfe004000, 0xfe008000, /* FB, GP, VG, DF */ | ||
77 | 0xfe00c000, 0x0, 0x0, 0x30100b, /* VIP */ | ||
78 | 0x0, 0x0, 0x0, 0x10e, /* INTA, IRQ14 for graphics accel */ | ||
79 | 0x0, 0x0, 0x0, 0x0, | ||
80 | 0x3d0, 0x3c0, 0xa0000, 0x0, /* VG IO, VG IO, EGA FB, MONO FB */ | ||
81 | 0x0, 0x0, 0x0, 0x0, | ||
82 | }; | ||
83 | |||
84 | static const uint32_t gxfb_hdr[] = { /* dev 1 function 1 - devfn = 9 */ | ||
85 | 0xff800008, 0xffffc000, 0xffffc000, 0xffffc000, | ||
86 | 0x0, 0x0, 0x0, 0x0, | ||
87 | |||
88 | 0x30100b, 0x2200003, 0x3000000, 0x0, /* NSC Vendor ID */ | ||
89 | 0xfd000000, 0xfe000000, 0xfe004000, 0xfe008000, /* FB, GP, VG, DF */ | ||
90 | 0x0, 0x0, 0x0, 0x30100b, | ||
91 | 0x0, 0x0, 0x0, 0x0, | ||
92 | 0x0, 0x0, 0x0, 0x0, | ||
93 | 0x3d0, 0x3c0, 0xa0000, 0x0, /* VG IO, VG IO, EGA FB, MONO FB */ | ||
94 | 0x0, 0x0, 0x0, 0x0, | ||
95 | }; | ||
96 | |||
97 | static const uint32_t aes_hdr[] = { /* dev 1 function 2 - devfn = 0xa */ | ||
98 | 0xffffc000, 0x0, 0x0, 0x0, | ||
99 | 0x0, 0x0, 0x0, 0x0, | ||
100 | |||
101 | 0x20821022, 0x2a00006, 0x10100000, 0x8, /* NSC Vendor ID */ | ||
102 | 0xfe010000, 0x0, 0x0, 0x0, /* AES registers */ | ||
103 | 0x0, 0x0, 0x0, 0x20821022, | ||
104 | 0x0, 0x0, 0x0, 0x0, | ||
105 | 0x0, 0x0, 0x0, 0x0, | ||
106 | 0x0, 0x0, 0x0, 0x0, | ||
107 | 0x0, 0x0, 0x0, 0x0, | ||
108 | }; | ||
109 | |||
110 | |||
111 | static const uint32_t isa_hdr[] = { /* dev f function 0 - devfn = 78 */ | ||
112 | 0xfffffff9, 0xffffff01, 0xffffffc1, 0xffffffe1, | ||
113 | 0xffffff81, 0xffffffc1, 0x0, 0x0, | ||
114 | |||
115 | 0x20901022, 0x2a00049, 0x6010003, 0x802000, | ||
116 | 0x18b1, 0x1001, 0x1801, 0x1881, /* SMB-8 GPIO-256 MFGPT-64 IRQ-32 */ | ||
117 | 0x1401, 0x1841, 0x0, 0x20901022, /* PMS-128 ACPI-64 */ | ||
118 | 0x0, 0x0, 0x0, 0x0, | ||
119 | 0x0, 0x0, 0x0, 0x0, | ||
120 | 0x0, 0x0, 0x0, 0xaa5b, /* IRQ steering */ | ||
121 | 0x0, 0x0, 0x0, 0x0, | ||
122 | }; | ||
123 | |||
124 | static const uint32_t ac97_hdr[] = { /* dev f function 3 - devfn = 7b */ | ||
125 | 0xffffff81, 0x0, 0x0, 0x0, | ||
126 | 0x0, 0x0, 0x0, 0x0, | ||
127 | |||
128 | 0x20931022, 0x2a00041, 0x4010001, 0x0, | ||
129 | 0x1481, 0x0, 0x0, 0x0, /* I/O BAR-128 */ | ||
130 | 0x0, 0x0, 0x0, 0x20931022, | ||
131 | 0x0, 0x0, 0x0, 0x205, /* IntB, IRQ5 */ | ||
132 | 0x0, 0x0, 0x0, 0x0, | ||
133 | 0x0, 0x0, 0x0, 0x0, | ||
134 | 0x0, 0x0, 0x0, 0x0, | ||
135 | }; | ||
136 | |||
137 | static const uint32_t ohci_hdr[] = { /* dev f function 4 - devfn = 7c */ | ||
138 | 0xfffff000, 0x0, 0x0, 0x0, | ||
139 | 0x0, 0x0, 0x0, 0x0, | ||
140 | |||
141 | 0x20941022, 0x2300006, 0xc031002, 0x0, | ||
142 | 0xfe01a000, 0x0, 0x0, 0x0, /* MEMBAR-1000 */ | ||
143 | 0x0, 0x0, 0x0, 0x20941022, | ||
144 | 0x0, 0x40, 0x0, 0x40a, /* CapPtr INT-D, IRQA */ | ||
145 | 0xc8020001, 0x0, 0x0, 0x0, /* Capabilities - 40 is R/O, | ||
146 | 44 is mask 8103 (power control) */ | ||
147 | 0x0, 0x0, 0x0, 0x0, | ||
148 | 0x0, 0x0, 0x0, 0x0, | ||
149 | }; | ||
150 | |||
151 | static const uint32_t ehci_hdr[] = { /* dev f function 4 - devfn = 7d */ | ||
152 | 0xfffff000, 0x0, 0x0, 0x0, | ||
153 | 0x0, 0x0, 0x0, 0x0, | ||
154 | |||
155 | 0x20951022, 0x2300006, 0xc032002, 0x0, | ||
156 | 0xfe01b000, 0x0, 0x0, 0x0, /* MEMBAR-1000 */ | ||
157 | 0x0, 0x0, 0x0, 0x20951022, | ||
158 | 0x0, 0x40, 0x0, 0x40a, /* CapPtr INT-D, IRQA */ | ||
159 | 0xc8020001, 0x0, 0x0, 0x0, /* Capabilities - 40 is R/O, 44 is | ||
160 | mask 8103 (power control) */ | ||
161 | #if 0 | ||
162 | 0x1, 0x40080000, 0x0, 0x0, /* EECP - see EHCI spec section 2.1.7 */ | ||
163 | #endif | ||
164 | 0x01000001, 0x0, 0x0, 0x0, /* EECP - see EHCI spec section 2.1.7 */ | ||
165 | 0x2020, 0x0, 0x0, 0x0, /* (EHCI page 8) 60 SBRN (R/O), | ||
166 | 61 FLADJ (R/W), PORTWAKECAP */ | ||
167 | }; | ||
168 | |||
169 | static uint32_t ff_loc = ~0; | ||
170 | static uint32_t zero_loc; | ||
171 | static int bar_probing; /* Set after a write of ~0 to a BAR */ | ||
172 | static int is_lx; | ||
173 | |||
174 | #define NB_SLOT 0x1 /* Northbridge - GX chip - Device 1 */ | ||
175 | #define SB_SLOT 0xf /* Southbridge - CS5536 chip - Device F */ | ||
176 | |||
177 | static int is_simulated(unsigned int bus, unsigned int devfn) | ||
178 | { | ||
179 | return (!bus && ((PCI_SLOT(devfn) == NB_SLOT) || | ||
180 | (PCI_SLOT(devfn) == SB_SLOT))); | ||
181 | } | ||
182 | |||
183 | static uint32_t *hdr_addr(const uint32_t *hdr, int reg) | ||
184 | { | ||
185 | uint32_t addr; | ||
186 | |||
187 | /* | ||
188 | * This is a little bit tricky. The header maps consist of | ||
189 | * 0x20 bytes of size masks, followed by 0x70 bytes of header data. | ||
190 | * In the normal case, when not probing a BAR's size, we want | ||
191 | * to access the header data, so we add 0x20 to the reg offset, | ||
192 | * thus skipping the size mask area. | ||
193 | * In the BAR probing case, we want to access the size mask for | ||
194 | * the BAR, so we subtract 0x10 (the config header offset for | ||
195 | * BAR0), and don't skip the size mask area. | ||
196 | */ | ||
197 | |||
198 | addr = (uint32_t)hdr + reg + (bar_probing ? -0x10 : 0x20); | ||
199 | |||
200 | bar_probing = 0; | ||
201 | return (uint32_t *)addr; | ||
202 | } | ||
203 | |||
204 | static int pci_olpc_read(unsigned int seg, unsigned int bus, | ||
205 | unsigned int devfn, int reg, int len, uint32_t *value) | ||
206 | { | ||
207 | uint32_t *addr; | ||
208 | |||
209 | /* Use the hardware mechanism for non-simulated devices */ | ||
210 | if (!is_simulated(bus, devfn)) | ||
211 | return pci_direct_conf1.read(seg, bus, devfn, reg, len, value); | ||
212 | |||
213 | /* | ||
214 | * No device has config registers past 0x70, so we save table space | ||
215 | * by not storing entries for the nonexistent registers | ||
216 | */ | ||
217 | if (reg >= 0x70) | ||
218 | addr = &zero_loc; | ||
219 | else { | ||
220 | switch (devfn) { | ||
221 | case 0x8: | ||
222 | addr = hdr_addr(is_lx ? lxnb_hdr : gxnb_hdr, reg); | ||
223 | break; | ||
224 | case 0x9: | ||
225 | addr = hdr_addr(is_lx ? lxfb_hdr : gxfb_hdr, reg); | ||
226 | break; | ||
227 | case 0xa: | ||
228 | addr = is_lx ? hdr_addr(aes_hdr, reg) : &ff_loc; | ||
229 | break; | ||
230 | case 0x78: | ||
231 | addr = hdr_addr(isa_hdr, reg); | ||
232 | break; | ||
233 | case 0x7b: | ||
234 | addr = hdr_addr(ac97_hdr, reg); | ||
235 | break; | ||
236 | case 0x7c: | ||
237 | addr = hdr_addr(ohci_hdr, reg); | ||
238 | break; | ||
239 | case 0x7d: | ||
240 | addr = hdr_addr(ehci_hdr, reg); | ||
241 | break; | ||
242 | default: | ||
243 | addr = &ff_loc; | ||
244 | break; | ||
245 | } | ||
246 | } | ||
247 | switch (len) { | ||
248 | case 1: | ||
249 | *value = *(uint8_t *)addr; | ||
250 | break; | ||
251 | case 2: | ||
252 | *value = *(uint16_t *)addr; | ||
253 | break; | ||
254 | case 4: | ||
255 | *value = *addr; | ||
256 | break; | ||
257 | default: | ||
258 | BUG(); | ||
259 | } | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static int pci_olpc_write(unsigned int seg, unsigned int bus, | ||
265 | unsigned int devfn, int reg, int len, uint32_t value) | ||
266 | { | ||
267 | /* Use the hardware mechanism for non-simulated devices */ | ||
268 | if (!is_simulated(bus, devfn)) | ||
269 | return pci_direct_conf1.write(seg, bus, devfn, reg, len, value); | ||
270 | |||
271 | /* XXX we may want to extend this to simulate EHCI power management */ | ||
272 | |||
273 | /* | ||
274 | * Mostly we just discard writes, but if the write is a size probe | ||
275 | * (i.e. writing ~0 to a BAR), we remember it and arrange to return | ||
276 | * the appropriate size mask on the next read. This is cheating | ||
277 | * to some extent, because it depends on the fact that the next | ||
278 | * access after such a write will always be a read to the same BAR. | ||
279 | */ | ||
280 | |||
281 | if ((reg >= 0x10) && (reg < 0x2c)) { | ||
282 | /* write is to a BAR */ | ||
283 | if (value == ~0) | ||
284 | bar_probing = 1; | ||
285 | } else { | ||
286 | /* | ||
287 | * No warning on writes to ROM BAR, CMD, LATENCY_TIMER, | ||
288 | * CACHE_LINE_SIZE, or PM registers. | ||
289 | */ | ||
290 | if ((reg != PCI_ROM_ADDRESS) && (reg != PCI_COMMAND_MASTER) && | ||
291 | (reg != PCI_LATENCY_TIMER) && | ||
292 | (reg != PCI_CACHE_LINE_SIZE) && (reg != 0x44)) | ||
293 | printk(KERN_WARNING "OLPC PCI: Config write to devfn" | ||
294 | " %x reg %x value %x\n", devfn, reg, value); | ||
295 | } | ||
296 | |||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | static struct pci_raw_ops pci_olpc_conf = { | ||
301 | .read = pci_olpc_read, | ||
302 | .write = pci_olpc_write, | ||
303 | }; | ||
304 | |||
305 | void __init pci_olpc_init(void) | ||
306 | { | ||
307 | if (!machine_is_olpc() || olpc_has_vsa()) | ||
308 | return; | ||
309 | |||
310 | printk(KERN_INFO "PCI: Using configuration type OLPC\n"); | ||
311 | raw_pci_ops = &pci_olpc_conf; | ||
312 | is_lx = is_geode_lx(); | ||
313 | } | ||
diff --git a/arch/x86/pci/pci.h b/arch/x86/pci/pci.h index c4bddaeff619..7d84e813e958 100644 --- a/arch/x86/pci/pci.h +++ b/arch/x86/pci/pci.h | |||
@@ -98,6 +98,7 @@ extern int pci_direct_probe(void); | |||
98 | extern void pci_direct_init(int type); | 98 | extern void pci_direct_init(int type); |
99 | extern void pci_pcbios_init(void); | 99 | extern void pci_pcbios_init(void); |
100 | extern void pci_mmcfg_init(int type); | 100 | extern void pci_mmcfg_init(int type); |
101 | extern void pci_olpc_init(void); | ||
101 | 102 | ||
102 | /* pci-mmconfig.c */ | 103 | /* pci-mmconfig.c */ |
103 | 104 | ||