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 44043390e34..fedfd9c6729 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 \ power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \ - pci_psycho.o pci_sabre.o pci_schizo.o pci_sun4v.o + pci_psycho.o pci_sabre.o pci_schizo.o \ + pci_sun4v.o pci_sun4v_asm.o obj-$(CONFIG_SMP) += smp.o trampoline.o obj-$(CONFIG_SPARC32_COMPAT) += sys32.o sys_sparc32.o signal32.o obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c index c1a077196c5..1d61353e264 100644 --- a/arch/sparc64/kernel/pci_sun4v.c +++ b/arch/sparc64/kernel/pci_sun4v.c @@ -21,6 +21,8 @@ #include "pci_impl.h" #include "iommu_common.h" +#include "pci_sun4v.h" + static void *pci_4v_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp) { return NULL; @@ -67,8 +69,292 @@ struct pci_iommu_ops pci_sun4v_iommu_ops = { .dma_sync_sg_for_cpu = pci_4v_dma_sync_sg_for_cpu, }; +/* SUN4V PCI configuration space accessors. */ + +static int pci_sun4v_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, + int where, int size, u32 *value) +{ + /* XXX Implement me! XXX */ + return 0; +} + +static int pci_sun4v_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, + int where, int size, u32 value) +{ + /* XXX Implement me! XXX */ + return 0; +} + +static struct pci_ops pci_sun4v_ops = { + .read = pci_sun4v_read_pci_cfg, + .write = pci_sun4v_write_pci_cfg, +}; + + +static void pci_sun4v_scan_bus(struct pci_controller_info *p) +{ + /* XXX Implement me! XXX */ +} + +static unsigned int pci_sun4v_irq_build(struct pci_pbm_info *pbm, + struct pci_dev *pdev, + unsigned int ino) +{ + /* XXX Implement me! XXX */ + return 0; +} + +/* XXX correct? XXX */ +static void pci_sun4v_base_address_update(struct pci_dev *pdev, int resource) +{ + struct pcidev_cookie *pcp = pdev->sysdata; + struct pci_pbm_info *pbm = pcp->pbm; + struct resource *res, *root; + u32 reg; + int where, size, is_64bit; + + res = &pdev->resource[resource]; + if (resource < 6) { + where = PCI_BASE_ADDRESS_0 + (resource * 4); + } else if (resource == PCI_ROM_RESOURCE) { + where = pdev->rom_base_reg; + } else { + /* Somebody might have asked allocation of a non-standard resource */ + return; + } + + is_64bit = 0; + if (res->flags & IORESOURCE_IO) + root = &pbm->io_space; + else { + root = &pbm->mem_space; + if ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) + == PCI_BASE_ADDRESS_MEM_TYPE_64) + is_64bit = 1; + } + + size = res->end - res->start; + pci_read_config_dword(pdev, where, ®); + reg = ((reg & size) | + (((u32)(res->start - root->start)) & ~size)); + if (resource == PCI_ROM_RESOURCE) { + reg |= PCI_ROM_ADDRESS_ENABLE; + res->flags |= IORESOURCE_ROM_ENABLE; + } + pci_write_config_dword(pdev, where, reg); + + /* This knows that the upper 32-bits of the address + * must be zero. Our PCI common layer enforces this. + */ + if (is_64bit) + pci_write_config_dword(pdev, where + 4, 0); +} + +/* XXX correct? XXX */ +static void pci_sun4v_resource_adjust(struct pci_dev *pdev, + struct resource *res, + struct resource *root) +{ + res->start += root->start; + res->end += root->start; +} + +/* Use ranges property to determine where PCI MEM, I/O, and Config + * space are for this PCI bus module. + */ +static void pci_sun4v_determine_mem_io_space(struct pci_pbm_info *pbm) +{ + int i, saw_cfg, saw_mem, saw_io; + + saw_cfg = saw_mem = saw_io = 0; + for (i = 0; i < pbm->num_pbm_ranges; i++) { + struct linux_prom_pci_ranges *pr = &pbm->pbm_ranges[i]; + unsigned long a; + int type; + + type = (pr->child_phys_hi >> 24) & 0x3; + a = (((unsigned long)pr->parent_phys_hi << 32UL) | + ((unsigned long)pr->parent_phys_lo << 0UL)); + + switch (type) { + case 0: + /* PCI config space, 16MB */ + pbm->config_space = a; + saw_cfg = 1; + break; + + case 1: + /* 16-bit IO space, 16MB */ + pbm->io_space.start = a; + pbm->io_space.end = a + ((16UL*1024UL*1024UL) - 1UL); + pbm->io_space.flags = IORESOURCE_IO; + saw_io = 1; + break; + + case 2: + /* 32-bit MEM space, 2GB */ + pbm->mem_space.start = a; + pbm->mem_space.end = a + (0x80000000UL - 1UL); + pbm->mem_space.flags = IORESOURCE_MEM; + saw_mem = 1; + break; + + default: + break; + }; + } + + if (!saw_cfg || !saw_io || !saw_mem) { + prom_printf("%s: Fatal error, missing %s PBM range.\n", + pbm->name, + ((!saw_cfg ? + "CFG" : + (!saw_io ? + "IO" : "MEM")))); + prom_halt(); + } + + printk("%s: PCI CFG[%lx] IO[%lx] MEM[%lx]\n", + pbm->name, + pbm->config_space, + pbm->io_space.start, + pbm->mem_space.start); +} + +static void pbm_register_toplevel_resources(struct pci_controller_info *p, + struct pci_pbm_info *pbm) +{ + pbm->io_space.name = pbm->mem_space.name = pbm->name; + + request_resource(&ioport_resource, &pbm->io_space); + request_resource(&iomem_resource, &pbm->mem_space); + pci_register_legacy_regions(&pbm->io_space, + &pbm->mem_space); +} + +static void pci_sun4v_iommu_init(struct pci_pbm_info *pbm) +{ + /* XXX Implement me! XXX */ +} + +static void pci_sun4v_pbm_init(struct pci_controller_info *p, int prom_node) +{ + struct pci_pbm_info *pbm; + struct linux_prom64_registers regs; + unsigned int busrange[2]; + int err; + + /* XXX */ + pbm = &p->pbm_A; + + pbm->parent = p; + pbm->prom_node = prom_node; + pbm->pci_first_slot = 1; + + prom_getproperty(prom_node, "reg", (char *)®s, sizeof(regs)); + pbm->devhandle = (regs.phys_addr >> 32UL) & 0x0fffffff; + + sprintf(pbm->name, "SUN4V-PCI%d PBM%c", + p->index, (pbm == &p->pbm_A ? 'A' : 'B')); + + printk("%s: devhandle[%x]\n", pbm->name, pbm->devhandle); + + prom_getstring(prom_node, "name", + pbm->prom_name, sizeof(pbm->prom_name)); + + err = prom_getproperty(prom_node, "ranges", + (char *) pbm->pbm_ranges, + sizeof(pbm->pbm_ranges)); + if (err == 0 || err == -1) { + prom_printf("%s: Fatal error, no ranges property.\n", + pbm->name); + prom_halt(); + } + + pbm->num_pbm_ranges = + (err / sizeof(struct linux_prom_pci_ranges)); + + pci_sun4v_determine_mem_io_space(pbm); + pbm_register_toplevel_resources(p, pbm); + + err = prom_getproperty(prom_node, "interrupt-map", + (char *)pbm->pbm_intmap, + sizeof(pbm->pbm_intmap)); + if (err != -1) { + pbm->num_pbm_intmap = (err / sizeof(struct linux_prom_pci_intmap)); + err = prom_getproperty(prom_node, "interrupt-map-mask", + (char *)&pbm->pbm_intmask, + sizeof(pbm->pbm_intmask)); + if (err == -1) { + prom_printf("%s: Fatal error, no " + "interrupt-map-mask.\n", pbm->name); + prom_halt(); + } + } else { + pbm->num_pbm_intmap = 0; + memset(&pbm->pbm_intmask, 0, sizeof(pbm->pbm_intmask)); + } + + err = prom_getproperty(prom_node, "bus-range", + (char *)&busrange[0], + sizeof(busrange)); + if (err == 0 || err == -1) { + prom_printf("%s: Fatal error, no bus-range.\n", pbm->name); + prom_halt(); + } + pbm->pci_first_busno = busrange[0]; + pbm->pci_last_busno = busrange[1]; + + pci_sun4v_iommu_init(pbm); +} + void sun4v_pci_init(int node, char *model_name) { + struct pci_controller_info *p; + struct pci_iommu *iommu; + + p = kmalloc(sizeof(struct pci_controller_info), GFP_ATOMIC); + if (!p) { + prom_printf("SUN4V_PCI: Fatal memory allocation error.\n"); + prom_halt(); + } + memset(p, 0, sizeof(*p)); + + iommu = kmalloc(sizeof(struct pci_iommu), GFP_ATOMIC); + if (!iommu) { + prom_printf("SCHIZO: Fatal memory allocation error.\n"); + prom_halt(); + } + memset(iommu, 0, sizeof(*iommu)); + p->pbm_A.iommu = iommu; + + iommu = kmalloc(sizeof(struct pci_iommu), GFP_ATOMIC); + if (!iommu) { + prom_printf("SCHIZO: Fatal memory allocation error.\n"); + prom_halt(); + } + memset(iommu, 0, sizeof(*iommu)); + p->pbm_B.iommu = iommu; + + p->next = pci_controller_root; + pci_controller_root = p; + + p->index = pci_num_controllers++; + p->pbms_same_domain = 0; + + p->scan_bus = pci_sun4v_scan_bus; + p->irq_build = pci_sun4v_irq_build; + p->base_address_update = pci_sun4v_base_address_update; + p->resource_adjust = pci_sun4v_resource_adjust; + p->pci_ops = &pci_sun4v_ops; + + /* Like PSYCHO and SCHIZO we have a 2GB aligned area + * for memory space. + */ + pci_memspace_mask = 0x7fffffffUL; + + pci_sun4v_pbm_init(p, node); + prom_printf("sun4v_pci_init: Implement me.\n"); prom_halt(); } diff --git a/arch/sparc64/kernel/pci_sun4v.h b/arch/sparc64/kernel/pci_sun4v.h new file mode 100644 index 00000000000..d3ac7ece4b3 --- /dev/null +++ b/arch/sparc64/kernel/pci_sun4v.h @@ -0,0 +1,20 @@ +/* pci_sun4v.h: SUN4V specific PCI controller support. + * + * Copyright (C) 2006 David S. Miller (davem@davemloft.net) + */ + +#ifndef _PCI_SUN4V_H +#define _PCI_SUN4V_H + +extern unsigned long pci_sun4v_devino_to_sysino(unsigned long devhandle, + unsigned long deino); +extern unsigned long pci_sun4v_iommu_map(unsigned long devhandle, + unsigned long tsbid, + unsigned long num_ttes, + unsigned long io_attributes, + unsigned long io_page_list_pa); +extern unsigned long pci_sun4v_iommu_demap(unsigned long devhandle, + unsigned long tsbid, + unsigned long num_ttes); + +#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 00000000000..fd2fe0edf16 --- /dev/null +++ b/arch/sparc64/kernel/pci_sun4v_asm.S @@ -0,0 +1,56 @@ +/* pci_sun4v_asm: Hypervisor calls for PCI support. + * + * Copyright (C) 2006 David S. Miller <davem@davemloft.net> + */ + +#include <asm/hypervisor.h> + + /* %o0: devhandle + * %o1: devino + * + * returns %o0: sysino + */ + .globl pci_sun4v_devino_to_sysino +pci_sun4v_devino_to_sysino: + mov %o1, %o2 + mov %o0, %o1 + mov HV_FAST_INTR_DEVINO2SYSINO, %o0 + ta HV_FAST_TRAP + retl + mov %o1, %o0 + + /* %o0: devhandle + * %o1: tsbid + * %o2: num ttes + * %o3: io_attributes + * %o4: io_page_list phys address + * + * returns %o0: num ttes mapped + */ + .globl pci_sun4v_iommu_map +pci_sun4v_iommu_map: + mov %o4, %o5 + mov %o3, %o4 + mov %o2, %o3 + mov %o1, %o2 + mov %o0, %o1 + mov HV_FAST_PCI_IOMMU_MAP, %o0 + ta HV_FAST_TRAP + retl + mov %o1, %o0 + + /* %o0: devhandle + * %o1: tsbid + * %o2: num ttes + * + * returns %o0: num ttes demapped + */ + .globl pci_sun4v_iommu_demap +pci_sun4v_iommu_demap: + mov %o2, %o3 + mov %o1, %o2 + mov %o0, %o1 + mov HV_FAST_PCI_IOMMU_DEMAP, %o0 + ta HV_FAST_TRAP + retl + mov %o1, %o0 diff --git a/include/asm-sparc64/pbm.h b/include/asm-sparc64/pbm.h index dd35a2c7798..1396f110939 100644 --- a/include/asm-sparc64/pbm.h +++ b/include/asm-sparc64/pbm.h @@ -139,6 +139,9 @@ struct pci_pbm_info { /* Opaque 32-bit system bus Port ID. */ u32 portid; + /* Opaque 32-bit handle used for hypervisor calls. */ + u32 devhandle; + /* Chipset version information. */ int chip_type; #define PBM_CHIP_TYPE_SABRE 1 |