diff options
-rw-r--r-- | arch/sparc64/kernel/Makefile | 3 | ||||
-rw-r--r-- | arch/sparc64/kernel/pci_sun4v.c | 286 | ||||
-rw-r--r-- | arch/sparc64/kernel/pci_sun4v.h | 20 | ||||
-rw-r--r-- | arch/sparc64/kernel/pci_sun4v_asm.S | 56 | ||||
-rw-r--r-- | include/asm-sparc64/pbm.h | 3 |
5 files changed, 367 insertions, 1 deletions
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index 44043390e34f..fedfd9c6729d 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile | |||
@@ -14,7 +14,8 @@ obj-y := process.o setup.o cpu.o idprom.o \ | |||
14 | power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o | 14 | power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o |
15 | 15 | ||
16 | obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \ | 16 | obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \ |
17 | pci_psycho.o pci_sabre.o pci_schizo.o pci_sun4v.o | 17 | pci_psycho.o pci_sabre.o pci_schizo.o \ |
18 | pci_sun4v.o pci_sun4v_asm.o | ||
18 | obj-$(CONFIG_SMP) += smp.o trampoline.o | 19 | obj-$(CONFIG_SMP) += smp.o trampoline.o |
19 | obj-$(CONFIG_SPARC32_COMPAT) += sys32.o sys_sparc32.o signal32.o | 20 | obj-$(CONFIG_SPARC32_COMPAT) += sys32.o sys_sparc32.o signal32.o |
20 | obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o | 21 | obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o |
diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c index c1a077196c57..1d61353e2644 100644 --- a/arch/sparc64/kernel/pci_sun4v.c +++ b/arch/sparc64/kernel/pci_sun4v.c | |||
@@ -21,6 +21,8 @@ | |||
21 | #include "pci_impl.h" | 21 | #include "pci_impl.h" |
22 | #include "iommu_common.h" | 22 | #include "iommu_common.h" |
23 | 23 | ||
24 | #include "pci_sun4v.h" | ||
25 | |||
24 | static void *pci_4v_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp) | 26 | static void *pci_4v_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp) |
25 | { | 27 | { |
26 | return NULL; | 28 | return NULL; |
@@ -67,8 +69,292 @@ struct pci_iommu_ops pci_sun4v_iommu_ops = { | |||
67 | .dma_sync_sg_for_cpu = pci_4v_dma_sync_sg_for_cpu, | 69 | .dma_sync_sg_for_cpu = pci_4v_dma_sync_sg_for_cpu, |
68 | }; | 70 | }; |
69 | 71 | ||
72 | /* SUN4V PCI configuration space accessors. */ | ||
73 | |||
74 | static int pci_sun4v_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, | ||
75 | int where, int size, u32 *value) | ||
76 | { | ||
77 | /* XXX Implement me! XXX */ | ||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | static int pci_sun4v_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, | ||
82 | int where, int size, u32 value) | ||
83 | { | ||
84 | /* XXX Implement me! XXX */ | ||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static struct pci_ops pci_sun4v_ops = { | ||
89 | .read = pci_sun4v_read_pci_cfg, | ||
90 | .write = pci_sun4v_write_pci_cfg, | ||
91 | }; | ||
92 | |||
93 | |||
94 | static void pci_sun4v_scan_bus(struct pci_controller_info *p) | ||
95 | { | ||
96 | /* XXX Implement me! XXX */ | ||
97 | } | ||
98 | |||
99 | static unsigned int pci_sun4v_irq_build(struct pci_pbm_info *pbm, | ||
100 | struct pci_dev *pdev, | ||
101 | unsigned int ino) | ||
102 | { | ||
103 | /* XXX Implement me! XXX */ | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | /* XXX correct? XXX */ | ||
108 | static void pci_sun4v_base_address_update(struct pci_dev *pdev, int resource) | ||
109 | { | ||
110 | struct pcidev_cookie *pcp = pdev->sysdata; | ||
111 | struct pci_pbm_info *pbm = pcp->pbm; | ||
112 | struct resource *res, *root; | ||
113 | u32 reg; | ||
114 | int where, size, is_64bit; | ||
115 | |||
116 | res = &pdev->resource[resource]; | ||
117 | if (resource < 6) { | ||
118 | where = PCI_BASE_ADDRESS_0 + (resource * 4); | ||
119 | } else if (resource == PCI_ROM_RESOURCE) { | ||
120 | where = pdev->rom_base_reg; | ||
121 | } else { | ||
122 | /* Somebody might have asked allocation of a non-standard resource */ | ||
123 | return; | ||
124 | } | ||
125 | |||
126 | is_64bit = 0; | ||
127 | if (res->flags & IORESOURCE_IO) | ||
128 | root = &pbm->io_space; | ||
129 | else { | ||
130 | root = &pbm->mem_space; | ||
131 | if ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) | ||
132 | == PCI_BASE_ADDRESS_MEM_TYPE_64) | ||
133 | is_64bit = 1; | ||
134 | } | ||
135 | |||
136 | size = res->end - res->start; | ||
137 | pci_read_config_dword(pdev, where, ®); | ||
138 | reg = ((reg & size) | | ||
139 | (((u32)(res->start - root->start)) & ~size)); | ||
140 | if (resource == PCI_ROM_RESOURCE) { | ||
141 | reg |= PCI_ROM_ADDRESS_ENABLE; | ||
142 | res->flags |= IORESOURCE_ROM_ENABLE; | ||
143 | } | ||
144 | pci_write_config_dword(pdev, where, reg); | ||
145 | |||
146 | /* This knows that the upper 32-bits of the address | ||
147 | * must be zero. Our PCI common layer enforces this. | ||
148 | */ | ||
149 | if (is_64bit) | ||
150 | pci_write_config_dword(pdev, where + 4, 0); | ||
151 | } | ||
152 | |||
153 | /* XXX correct? XXX */ | ||
154 | static void pci_sun4v_resource_adjust(struct pci_dev *pdev, | ||
155 | struct resource *res, | ||
156 | struct resource *root) | ||
157 | { | ||
158 | res->start += root->start; | ||
159 | res->end += root->start; | ||
160 | } | ||
161 | |||
162 | /* Use ranges property to determine where PCI MEM, I/O, and Config | ||
163 | * space are for this PCI bus module. | ||
164 | */ | ||
165 | static void pci_sun4v_determine_mem_io_space(struct pci_pbm_info *pbm) | ||
166 | { | ||
167 | int i, saw_cfg, saw_mem, saw_io; | ||
168 | |||
169 | saw_cfg = saw_mem = saw_io = 0; | ||
170 | for (i = 0; i < pbm->num_pbm_ranges; i++) { | ||
171 | struct linux_prom_pci_ranges *pr = &pbm->pbm_ranges[i]; | ||
172 | unsigned long a; | ||
173 | int type; | ||
174 | |||
175 | type = (pr->child_phys_hi >> 24) & 0x3; | ||
176 | a = (((unsigned long)pr->parent_phys_hi << 32UL) | | ||
177 | ((unsigned long)pr->parent_phys_lo << 0UL)); | ||
178 | |||
179 | switch (type) { | ||
180 | case 0: | ||
181 | /* PCI config space, 16MB */ | ||
182 | pbm->config_space = a; | ||
183 | saw_cfg = 1; | ||
184 | break; | ||
185 | |||
186 | case 1: | ||
187 | /* 16-bit IO space, 16MB */ | ||
188 | pbm->io_space.start = a; | ||
189 | pbm->io_space.end = a + ((16UL*1024UL*1024UL) - 1UL); | ||
190 | pbm->io_space.flags = IORESOURCE_IO; | ||
191 | saw_io = 1; | ||
192 | break; | ||
193 | |||
194 | case 2: | ||
195 | /* 32-bit MEM space, 2GB */ | ||
196 | pbm->mem_space.start = a; | ||
197 | pbm->mem_space.end = a + (0x80000000UL - 1UL); | ||
198 | pbm->mem_space.flags = IORESOURCE_MEM; | ||
199 | saw_mem = 1; | ||
200 | break; | ||
201 | |||
202 | default: | ||
203 | break; | ||
204 | }; | ||
205 | } | ||
206 | |||
207 | if (!saw_cfg || !saw_io || !saw_mem) { | ||
208 | prom_printf("%s: Fatal error, missing %s PBM range.\n", | ||
209 | pbm->name, | ||
210 | ((!saw_cfg ? | ||
211 | "CFG" : | ||
212 | (!saw_io ? | ||
213 | "IO" : "MEM")))); | ||
214 | prom_halt(); | ||
215 | } | ||
216 | |||
217 | printk("%s: PCI CFG[%lx] IO[%lx] MEM[%lx]\n", | ||
218 | pbm->name, | ||
219 | pbm->config_space, | ||
220 | pbm->io_space.start, | ||
221 | pbm->mem_space.start); | ||
222 | } | ||
223 | |||
224 | static void pbm_register_toplevel_resources(struct pci_controller_info *p, | ||
225 | struct pci_pbm_info *pbm) | ||
226 | { | ||
227 | pbm->io_space.name = pbm->mem_space.name = pbm->name; | ||
228 | |||
229 | request_resource(&ioport_resource, &pbm->io_space); | ||
230 | request_resource(&iomem_resource, &pbm->mem_space); | ||
231 | pci_register_legacy_regions(&pbm->io_space, | ||
232 | &pbm->mem_space); | ||
233 | } | ||
234 | |||
235 | static void pci_sun4v_iommu_init(struct pci_pbm_info *pbm) | ||
236 | { | ||
237 | /* XXX Implement me! XXX */ | ||
238 | } | ||
239 | |||
240 | static void pci_sun4v_pbm_init(struct pci_controller_info *p, int prom_node) | ||
241 | { | ||
242 | struct pci_pbm_info *pbm; | ||
243 | struct linux_prom64_registers regs; | ||
244 | unsigned int busrange[2]; | ||
245 | int err; | ||
246 | |||
247 | /* XXX */ | ||
248 | pbm = &p->pbm_A; | ||
249 | |||
250 | pbm->parent = p; | ||
251 | pbm->prom_node = prom_node; | ||
252 | pbm->pci_first_slot = 1; | ||
253 | |||
254 | prom_getproperty(prom_node, "reg", (char *)®s, sizeof(regs)); | ||
255 | pbm->devhandle = (regs.phys_addr >> 32UL) & 0x0fffffff; | ||
256 | |||
257 | sprintf(pbm->name, "SUN4V-PCI%d PBM%c", | ||
258 | p->index, (pbm == &p->pbm_A ? 'A' : 'B')); | ||
259 | |||
260 | printk("%s: devhandle[%x]\n", pbm->name, pbm->devhandle); | ||
261 | |||
262 | prom_getstring(prom_node, "name", | ||
263 | pbm->prom_name, sizeof(pbm->prom_name)); | ||
264 | |||
265 | err = prom_getproperty(prom_node, "ranges", | ||
266 | (char *) pbm->pbm_ranges, | ||
267 | sizeof(pbm->pbm_ranges)); | ||
268 | if (err == 0 || err == -1) { | ||
269 | prom_printf("%s: Fatal error, no ranges property.\n", | ||
270 | pbm->name); | ||
271 | prom_halt(); | ||
272 | } | ||
273 | |||
274 | pbm->num_pbm_ranges = | ||
275 | (err / sizeof(struct linux_prom_pci_ranges)); | ||
276 | |||
277 | pci_sun4v_determine_mem_io_space(pbm); | ||
278 | pbm_register_toplevel_resources(p, pbm); | ||
279 | |||
280 | err = prom_getproperty(prom_node, "interrupt-map", | ||
281 | (char *)pbm->pbm_intmap, | ||
282 | sizeof(pbm->pbm_intmap)); | ||
283 | if (err != -1) { | ||
284 | pbm->num_pbm_intmap = (err / sizeof(struct linux_prom_pci_intmap)); | ||
285 | err = prom_getproperty(prom_node, "interrupt-map-mask", | ||
286 | (char *)&pbm->pbm_intmask, | ||
287 | sizeof(pbm->pbm_intmask)); | ||
288 | if (err == -1) { | ||
289 | prom_printf("%s: Fatal error, no " | ||
290 | "interrupt-map-mask.\n", pbm->name); | ||
291 | prom_halt(); | ||
292 | } | ||
293 | } else { | ||
294 | pbm->num_pbm_intmap = 0; | ||
295 | memset(&pbm->pbm_intmask, 0, sizeof(pbm->pbm_intmask)); | ||
296 | } | ||
297 | |||
298 | err = prom_getproperty(prom_node, "bus-range", | ||
299 | (char *)&busrange[0], | ||
300 | sizeof(busrange)); | ||
301 | if (err == 0 || err == -1) { | ||
302 | prom_printf("%s: Fatal error, no bus-range.\n", pbm->name); | ||
303 | prom_halt(); | ||
304 | } | ||
305 | pbm->pci_first_busno = busrange[0]; | ||
306 | pbm->pci_last_busno = busrange[1]; | ||
307 | |||
308 | pci_sun4v_iommu_init(pbm); | ||
309 | } | ||
310 | |||
70 | void sun4v_pci_init(int node, char *model_name) | 311 | void sun4v_pci_init(int node, char *model_name) |
71 | { | 312 | { |
313 | struct pci_controller_info *p; | ||
314 | struct pci_iommu *iommu; | ||
315 | |||
316 | p = kmalloc(sizeof(struct pci_controller_info), GFP_ATOMIC); | ||
317 | if (!p) { | ||
318 | prom_printf("SUN4V_PCI: Fatal memory allocation error.\n"); | ||
319 | prom_halt(); | ||
320 | } | ||
321 | memset(p, 0, sizeof(*p)); | ||
322 | |||
323 | iommu = kmalloc(sizeof(struct pci_iommu), GFP_ATOMIC); | ||
324 | if (!iommu) { | ||
325 | prom_printf("SCHIZO: Fatal memory allocation error.\n"); | ||
326 | prom_halt(); | ||
327 | } | ||
328 | memset(iommu, 0, sizeof(*iommu)); | ||
329 | p->pbm_A.iommu = iommu; | ||
330 | |||
331 | iommu = kmalloc(sizeof(struct pci_iommu), GFP_ATOMIC); | ||
332 | if (!iommu) { | ||
333 | prom_printf("SCHIZO: Fatal memory allocation error.\n"); | ||
334 | prom_halt(); | ||
335 | } | ||
336 | memset(iommu, 0, sizeof(*iommu)); | ||
337 | p->pbm_B.iommu = iommu; | ||
338 | |||
339 | p->next = pci_controller_root; | ||
340 | pci_controller_root = p; | ||
341 | |||
342 | p->index = pci_num_controllers++; | ||
343 | p->pbms_same_domain = 0; | ||
344 | |||
345 | p->scan_bus = pci_sun4v_scan_bus; | ||
346 | p->irq_build = pci_sun4v_irq_build; | ||
347 | p->base_address_update = pci_sun4v_base_address_update; | ||
348 | p->resource_adjust = pci_sun4v_resource_adjust; | ||
349 | p->pci_ops = &pci_sun4v_ops; | ||
350 | |||
351 | /* Like PSYCHO and SCHIZO we have a 2GB aligned area | ||
352 | * for memory space. | ||
353 | */ | ||
354 | pci_memspace_mask = 0x7fffffffUL; | ||
355 | |||
356 | pci_sun4v_pbm_init(p, node); | ||
357 | |||
72 | prom_printf("sun4v_pci_init: Implement me.\n"); | 358 | prom_printf("sun4v_pci_init: Implement me.\n"); |
73 | prom_halt(); | 359 | prom_halt(); |
74 | } | 360 | } |
diff --git a/arch/sparc64/kernel/pci_sun4v.h b/arch/sparc64/kernel/pci_sun4v.h new file mode 100644 index 000000000000..d3ac7ece4b31 --- /dev/null +++ b/arch/sparc64/kernel/pci_sun4v.h | |||
@@ -0,0 +1,20 @@ | |||
1 | /* pci_sun4v.h: SUN4V specific PCI controller support. | ||
2 | * | ||
3 | * Copyright (C) 2006 David S. Miller (davem@davemloft.net) | ||
4 | */ | ||
5 | |||
6 | #ifndef _PCI_SUN4V_H | ||
7 | #define _PCI_SUN4V_H | ||
8 | |||
9 | extern unsigned long pci_sun4v_devino_to_sysino(unsigned long devhandle, | ||
10 | unsigned long deino); | ||
11 | extern unsigned long pci_sun4v_iommu_map(unsigned long devhandle, | ||
12 | unsigned long tsbid, | ||
13 | unsigned long num_ttes, | ||
14 | unsigned long io_attributes, | ||
15 | unsigned long io_page_list_pa); | ||
16 | extern unsigned long pci_sun4v_iommu_demap(unsigned long devhandle, | ||
17 | unsigned long tsbid, | ||
18 | unsigned long num_ttes); | ||
19 | |||
20 | #endif /* !(_PCI_SUN4V_H) */ | ||
diff --git a/arch/sparc64/kernel/pci_sun4v_asm.S b/arch/sparc64/kernel/pci_sun4v_asm.S new file mode 100644 index 000000000000..fd2fe0edf168 --- /dev/null +++ b/arch/sparc64/kernel/pci_sun4v_asm.S | |||
@@ -0,0 +1,56 @@ | |||
1 | /* pci_sun4v_asm: Hypervisor calls for PCI support. | ||
2 | * | ||
3 | * Copyright (C) 2006 David S. Miller <davem@davemloft.net> | ||
4 | */ | ||
5 | |||
6 | #include <asm/hypervisor.h> | ||
7 | |||
8 | /* %o0: devhandle | ||
9 | * %o1: devino | ||
10 | * | ||
11 | * returns %o0: sysino | ||
12 | */ | ||
13 | .globl pci_sun4v_devino_to_sysino | ||
14 | pci_sun4v_devino_to_sysino: | ||
15 | mov %o1, %o2 | ||
16 | mov %o0, %o1 | ||
17 | mov HV_FAST_INTR_DEVINO2SYSINO, %o0 | ||
18 | ta HV_FAST_TRAP | ||
19 | retl | ||
20 | mov %o1, %o0 | ||
21 | |||
22 | /* %o0: devhandle | ||
23 | * %o1: tsbid | ||
24 | * %o2: num ttes | ||
25 | * %o3: io_attributes | ||
26 | * %o4: io_page_list phys address | ||
27 | * | ||
28 | * returns %o0: num ttes mapped | ||
29 | */ | ||
30 | .globl pci_sun4v_iommu_map | ||
31 | pci_sun4v_iommu_map: | ||
32 | mov %o4, %o5 | ||
33 | mov %o3, %o4 | ||
34 | mov %o2, %o3 | ||
35 | mov %o1, %o2 | ||
36 | mov %o0, %o1 | ||
37 | mov HV_FAST_PCI_IOMMU_MAP, %o0 | ||
38 | ta HV_FAST_TRAP | ||
39 | retl | ||
40 | mov %o1, %o0 | ||
41 | |||
42 | /* %o0: devhandle | ||
43 | * %o1: tsbid | ||
44 | * %o2: num ttes | ||
45 | * | ||
46 | * returns %o0: num ttes demapped | ||
47 | */ | ||
48 | .globl pci_sun4v_iommu_demap | ||
49 | pci_sun4v_iommu_demap: | ||
50 | mov %o2, %o3 | ||
51 | mov %o1, %o2 | ||
52 | mov %o0, %o1 | ||
53 | mov HV_FAST_PCI_IOMMU_DEMAP, %o0 | ||
54 | ta HV_FAST_TRAP | ||
55 | retl | ||
56 | mov %o1, %o0 | ||
diff --git a/include/asm-sparc64/pbm.h b/include/asm-sparc64/pbm.h index dd35a2c7798a..1396f110939a 100644 --- a/include/asm-sparc64/pbm.h +++ b/include/asm-sparc64/pbm.h | |||
@@ -139,6 +139,9 @@ struct pci_pbm_info { | |||
139 | /* Opaque 32-bit system bus Port ID. */ | 139 | /* Opaque 32-bit system bus Port ID. */ |
140 | u32 portid; | 140 | u32 portid; |
141 | 141 | ||
142 | /* Opaque 32-bit handle used for hypervisor calls. */ | ||
143 | u32 devhandle; | ||
144 | |||
142 | /* Chipset version information. */ | 145 | /* Chipset version information. */ |
143 | int chip_type; | 146 | int chip_type; |
144 | #define PBM_CHIP_TYPE_SABRE 1 | 147 | #define PBM_CHIP_TYPE_SABRE 1 |