diff options
author | Marcelo Roberto Jimenez <mroberto@cpti.cetuc.puc-rio.br> | 2010-12-16 15:34:51 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2010-12-21 09:53:46 -0500 |
commit | b080ac8ad47aeeb845d8d11924f09255cf49b5e9 (patch) | |
tree | f42dcdbbdf725b798a20137fd8af0c10ab8d07bc /arch/arm/mach-sa1100/pci-nanoengine.c | |
parent | fa87672ab30ce6564393778b8cbc67fc32712a30 (diff) |
ARM: 6459/2: sa1100: Add nanoEngine PCI support.
This patch adds nanoEngine's PCI support.
Signed-off-by: Marcelo Roberto Jimenez <mroberto@cpti.cetuc.puc-rio.br>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-sa1100/pci-nanoengine.c')
-rw-r--r-- | arch/arm/mach-sa1100/pci-nanoengine.c | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/arch/arm/mach-sa1100/pci-nanoengine.c b/arch/arm/mach-sa1100/pci-nanoengine.c new file mode 100644 index 000000000000..fba7a913f12b --- /dev/null +++ b/arch/arm/mach-sa1100/pci-nanoengine.c | |||
@@ -0,0 +1,284 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-sa1100/pci-nanoengine.c | ||
3 | * | ||
4 | * PCI functions for BSE nanoEngine PCI | ||
5 | * | ||
6 | * Copyright (C) 2010 Marcelo Roberto Jimenez <mroberto@cpti.cetuc.puc-rio.br> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/irq.h> | ||
24 | #include <linux/pci.h> | ||
25 | #include <linux/spinlock.h> | ||
26 | |||
27 | #include <asm/mach/pci.h> | ||
28 | #include <asm/mach-types.h> | ||
29 | |||
30 | #include <mach/nanoengine.h> | ||
31 | |||
32 | static DEFINE_SPINLOCK(nano_lock); | ||
33 | |||
34 | static int nanoengine_get_pci_address(struct pci_bus *bus, | ||
35 | unsigned int devfn, int where, unsigned long *address) | ||
36 | { | ||
37 | int ret = PCIBIOS_DEVICE_NOT_FOUND; | ||
38 | unsigned int busnr = bus->number; | ||
39 | |||
40 | *address = NANO_PCI_CONFIG_SPACE_VIRT + | ||
41 | ((bus->number << 16) | (devfn << 8) | (where & ~3)); | ||
42 | |||
43 | ret = (busnr > 255 || devfn > 255 || where > 255) ? | ||
44 | PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; | ||
45 | |||
46 | return ret; | ||
47 | } | ||
48 | |||
49 | static int nanoengine_read_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
50 | int size, u32 *val) | ||
51 | { | ||
52 | int ret; | ||
53 | unsigned long address; | ||
54 | unsigned long flags; | ||
55 | u32 v; | ||
56 | |||
57 | /* nanoEngine PCI bridge does not return -1 for a non-existing | ||
58 | * device. We must fake the answer. We know that the only valid | ||
59 | * device is device zero at bus 0, which is the network chip. */ | ||
60 | if (bus->number != 0 || (devfn >> 3) != 0) { | ||
61 | v = -1; | ||
62 | nanoengine_get_pci_address(bus, devfn, where, &address); | ||
63 | goto exit_function; | ||
64 | } | ||
65 | |||
66 | spin_lock_irqsave(&nano_lock, flags); | ||
67 | |||
68 | ret = nanoengine_get_pci_address(bus, devfn, where, &address); | ||
69 | if (ret != PCIBIOS_SUCCESSFUL) | ||
70 | return ret; | ||
71 | v = __raw_readl(address); | ||
72 | |||
73 | spin_unlock_irqrestore(&nano_lock, flags); | ||
74 | |||
75 | v >>= ((where & 3) * 8); | ||
76 | v &= (unsigned long)(-1) >> ((4 - size) * 8); | ||
77 | |||
78 | exit_function: | ||
79 | *val = v; | ||
80 | return PCIBIOS_SUCCESSFUL; | ||
81 | } | ||
82 | |||
83 | static int nanoengine_write_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
84 | int size, u32 val) | ||
85 | { | ||
86 | int ret; | ||
87 | unsigned long address; | ||
88 | unsigned long flags; | ||
89 | unsigned shift; | ||
90 | u32 v; | ||
91 | |||
92 | shift = (where & 3) * 8; | ||
93 | |||
94 | spin_lock_irqsave(&nano_lock, flags); | ||
95 | |||
96 | ret = nanoengine_get_pci_address(bus, devfn, where, &address); | ||
97 | if (ret != PCIBIOS_SUCCESSFUL) | ||
98 | return ret; | ||
99 | v = __raw_readl(address); | ||
100 | switch (size) { | ||
101 | case 1: | ||
102 | v &= ~(0xFF << shift); | ||
103 | v |= val << shift; | ||
104 | break; | ||
105 | case 2: | ||
106 | v &= ~(0xFFFF << shift); | ||
107 | v |= val << shift; | ||
108 | break; | ||
109 | case 4: | ||
110 | v = val; | ||
111 | break; | ||
112 | } | ||
113 | __raw_writel(v, address); | ||
114 | |||
115 | spin_unlock_irqrestore(&nano_lock, flags); | ||
116 | |||
117 | return PCIBIOS_SUCCESSFUL; | ||
118 | } | ||
119 | |||
120 | static struct pci_ops pci_nano_ops = { | ||
121 | .read = nanoengine_read_config, | ||
122 | .write = nanoengine_write_config, | ||
123 | }; | ||
124 | |||
125 | static int __init pci_nanoengine_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
126 | { | ||
127 | return NANOENGINE_IRQ_GPIO_PCI; | ||
128 | } | ||
129 | |||
130 | struct pci_bus * __init pci_nanoengine_scan_bus(int nr, struct pci_sys_data *sys) | ||
131 | { | ||
132 | return pci_scan_bus(sys->busnr, &pci_nano_ops, sys); | ||
133 | } | ||
134 | |||
135 | static struct resource pci_io_ports = { | ||
136 | .name = "PCI IO", | ||
137 | .start = 0x400, | ||
138 | .end = 0x7FF, | ||
139 | .flags = IORESOURCE_IO, | ||
140 | }; | ||
141 | |||
142 | static struct resource pci_non_prefetchable_memory = { | ||
143 | .name = "PCI non-prefetchable", | ||
144 | .start = NANO_PCI_MEM_RW_PHYS, | ||
145 | /* nanoEngine documentation says there is a 1 Megabyte window here, | ||
146 | * but PCI reports just 128 + 8 kbytes. */ | ||
147 | .end = NANO_PCI_MEM_RW_PHYS + NANO_PCI_MEM_RW_SIZE - 1, | ||
148 | /* .end = NANO_PCI_MEM_RW_PHYS + SZ_128K + SZ_8K - 1,*/ | ||
149 | .flags = IORESOURCE_MEM, | ||
150 | }; | ||
151 | |||
152 | /* | ||
153 | * nanoEngine PCI reports 1 Megabyte of prefetchable memory, but it | ||
154 | * overlaps with previously defined memory. | ||
155 | * | ||
156 | * Here is what happens: | ||
157 | * | ||
158 | # dmesg | ||
159 | ... | ||
160 | pci 0000:00:00.0: [8086:1209] type 0 class 0x000200 | ||
161 | pci 0000:00:00.0: reg 10: [mem 0x00021000-0x00021fff] | ||
162 | pci 0000:00:00.0: reg 14: [io 0x0000-0x003f] | ||
163 | pci 0000:00:00.0: reg 18: [mem 0x00000000-0x0001ffff] | ||
164 | pci 0000:00:00.0: reg 30: [mem 0x00000000-0x000fffff pref] | ||
165 | pci 0000:00:00.0: supports D1 D2 | ||
166 | pci 0000:00:00.0: PME# supported from D0 D1 D2 D3hot | ||
167 | pci 0000:00:00.0: PME# disabled | ||
168 | PCI: bus0: Fast back to back transfers enabled | ||
169 | pci 0000:00:00.0: BAR 6: can't assign mem pref (size 0x100000) | ||
170 | pci 0000:00:00.0: BAR 2: assigned [mem 0x18600000-0x1861ffff] | ||
171 | pci 0000:00:00.0: BAR 2: set to [mem 0x18600000-0x1861ffff] (PCI address [0x0-0x1ffff]) | ||
172 | pci 0000:00:00.0: BAR 0: assigned [mem 0x18620000-0x18620fff] | ||
173 | pci 0000:00:00.0: BAR 0: set to [mem 0x18620000-0x18620fff] (PCI address [0x20000-0x20fff]) | ||
174 | pci 0000:00:00.0: BAR 1: assigned [io 0x0400-0x043f] | ||
175 | pci 0000:00:00.0: BAR 1: set to [io 0x0400-0x043f] (PCI address [0x0-0x3f]) | ||
176 | * | ||
177 | * On the other hand, if we do not request the prefetchable memory resource, | ||
178 | * linux will alloc it first and the two non-prefetchable memory areas that | ||
179 | * are our real interest will not be mapped. So we choose to map it to an | ||
180 | * unused area. It gets recognized as expansion ROM, but becomes disabled. | ||
181 | * | ||
182 | * Here is what happens then: | ||
183 | * | ||
184 | # dmesg | ||
185 | ... | ||
186 | pci 0000:00:00.0: [8086:1209] type 0 class 0x000200 | ||
187 | pci 0000:00:00.0: reg 10: [mem 0x00021000-0x00021fff] | ||
188 | pci 0000:00:00.0: reg 14: [io 0x0000-0x003f] | ||
189 | pci 0000:00:00.0: reg 18: [mem 0x00000000-0x0001ffff] | ||
190 | pci 0000:00:00.0: reg 30: [mem 0x00000000-0x000fffff pref] | ||
191 | pci 0000:00:00.0: supports D1 D2 | ||
192 | pci 0000:00:00.0: PME# supported from D0 D1 D2 D3hot | ||
193 | pci 0000:00:00.0: PME# disabled | ||
194 | PCI: bus0: Fast back to back transfers enabled | ||
195 | pci 0000:00:00.0: BAR 6: assigned [mem 0x78000000-0x780fffff pref] | ||
196 | pci 0000:00:00.0: BAR 2: assigned [mem 0x18600000-0x1861ffff] | ||
197 | pci 0000:00:00.0: BAR 2: set to [mem 0x18600000-0x1861ffff] (PCI address [0x0-0x1ffff]) | ||
198 | pci 0000:00:00.0: BAR 0: assigned [mem 0x18620000-0x18620fff] | ||
199 | pci 0000:00:00.0: BAR 0: set to [mem 0x18620000-0x18620fff] (PCI address [0x20000-0x20fff]) | ||
200 | pci 0000:00:00.0: BAR 1: assigned [io 0x0400-0x043f] | ||
201 | pci 0000:00:00.0: BAR 1: set to [io 0x0400-0x043f] (PCI address [0x0-0x3f]) | ||
202 | |||
203 | # lspci -vv -s 0000:00:00.0 | ||
204 | 00:00.0 Class 0200: Device 8086:1209 (rev 09) | ||
205 | Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr+ Stepping- SERR+ FastB2B- DisINTx- | ||
206 | Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR+ <PERR+ INTx- | ||
207 | Latency: 0 (2000ns min, 14000ns max), Cache Line Size: 32 bytes | ||
208 | Interrupt: pin A routed to IRQ 0 | ||
209 | Region 0: Memory at 18620000 (32-bit, non-prefetchable) [size=4K] | ||
210 | Region 1: I/O ports at 0400 [size=64] | ||
211 | Region 2: [virtual] Memory at 18600000 (32-bit, non-prefetchable) [size=128K] | ||
212 | [virtual] Expansion ROM at 78000000 [disabled] [size=1M] | ||
213 | Capabilities: [dc] Power Management version 2 | ||
214 | Flags: PMEClk- DSI+ D1+ D2+ AuxCurrent=0mA PME(D0+,D1+,D2+,D3hot+,D3cold-) | ||
215 | Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=2 PME- | ||
216 | Kernel driver in use: e100 | ||
217 | Kernel modules: e100 | ||
218 | * | ||
219 | */ | ||
220 | static struct resource pci_prefetchable_memory = { | ||
221 | .name = "PCI prefetchable", | ||
222 | .start = 0x78000000, | ||
223 | .end = 0x78000000 + NANO_PCI_MEM_RW_SIZE - 1, | ||
224 | .flags = IORESOURCE_MEM | IORESOURCE_PREFETCH, | ||
225 | }; | ||
226 | |||
227 | static int __init pci_nanoengine_setup_resources(struct resource **resource) | ||
228 | { | ||
229 | if (request_resource(&ioport_resource, &pci_io_ports)) { | ||
230 | printk(KERN_ERR "PCI: unable to allocate io port region\n"); | ||
231 | return -EBUSY; | ||
232 | } | ||
233 | if (request_resource(&iomem_resource, &pci_non_prefetchable_memory)) { | ||
234 | release_resource(&pci_io_ports); | ||
235 | printk(KERN_ERR "PCI: unable to allocate non prefetchable\n"); | ||
236 | return -EBUSY; | ||
237 | } | ||
238 | if (request_resource(&iomem_resource, &pci_prefetchable_memory)) { | ||
239 | release_resource(&pci_io_ports); | ||
240 | release_resource(&pci_non_prefetchable_memory); | ||
241 | printk(KERN_ERR "PCI: unable to allocate prefetchable\n"); | ||
242 | return -EBUSY; | ||
243 | } | ||
244 | resource[0] = &pci_io_ports; | ||
245 | resource[1] = &pci_non_prefetchable_memory; | ||
246 | resource[2] = &pci_prefetchable_memory; | ||
247 | |||
248 | return 1; | ||
249 | } | ||
250 | |||
251 | int __init pci_nanoengine_setup(int nr, struct pci_sys_data *sys) | ||
252 | { | ||
253 | int ret = 0; | ||
254 | |||
255 | if (nr == 0) { | ||
256 | sys->mem_offset = NANO_PCI_MEM_RW_PHYS; | ||
257 | sys->io_offset = 0x400; | ||
258 | ret = pci_nanoengine_setup_resources(sys->resource); | ||
259 | /* Enable alternate memory bus master mode, see | ||
260 | * "Intel StrongARM SA1110 Developer's Manual", | ||
261 | * section 10.8, "Alternate Memory Bus Master Mode". */ | ||
262 | GPDR = (GPDR & ~GPIO_MBREQ) | GPIO_MBGNT; | ||
263 | GAFR |= GPIO_MBGNT | GPIO_MBREQ; | ||
264 | TUCR |= TUCR_MBGPIO; | ||
265 | } | ||
266 | |||
267 | return ret; | ||
268 | } | ||
269 | |||
270 | static struct hw_pci nanoengine_pci __initdata = { | ||
271 | .map_irq = pci_nanoengine_map_irq, | ||
272 | .nr_controllers = 1, | ||
273 | .scan = pci_nanoengine_scan_bus, | ||
274 | .setup = pci_nanoengine_setup, | ||
275 | }; | ||
276 | |||
277 | static int __init nanoengine_pci_init(void) | ||
278 | { | ||
279 | if (machine_is_nanoengine()) | ||
280 | pci_common_init(&nanoengine_pci); | ||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | subsys_initcall(nanoengine_pci_init); | ||