[SPARC64]: More SUN4V PCI controller work.
authorDavid S. Miller <davem@sunset.davemloft.net>
Fri, 10 Feb 2006 06:05:54 +0000 (22:05 -0800)
committerDavid S. Miller <davem@sunset.davemloft.net>
Mon, 20 Mar 2006 09:12:11 +0000 (01:12 -0800)
Add assembler file for PCI hypervisor calls.
Setup basic skeleton of SUN4V PCI controller driver.

Add 32-bit devhandle to PBM struct, as this is needed for
hypervisor calls.

Signed-off-by: David S. Miller <davem@davemloft.net>
arch/sparc64/kernel/Makefile
arch/sparc64/kernel/pci_sun4v.c
arch/sparc64/kernel/pci_sun4v.h [new file with mode: 0644]
arch/sparc64/kernel/pci_sun4v_asm.S [new file with mode: 0644]
include/asm-sparc64/pbm.h

index 44043390e34f66dbdd8807879c45070d826e9cb8..fedfd9c6729ddb356f668b3406d1ff8e3166f76e 100644 (file)
@@ -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
index c1a077196c57d15055ab281eda9c6751e952805f..1d61353e264459608bead0318111bd499894562b 100644 (file)
@@ -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 = ((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 *)&regs, 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 (file)
index 0000000..d3ac7ec
--- /dev/null
@@ -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 (file)
index 0000000..fd2fe0e
--- /dev/null
@@ -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
index dd35a2c7798a1732013781b56710e8003b6a8e16..1396f110939adbfb4bad5b8e7f803658f209c79f 100644 (file)
@@ -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