]> err.no Git - linux-2.6/blobdiff - arch/sparc64/kernel/pci.c
Merge branch 'smsc47b397-new-id' into release
[linux-2.6] / arch / sparc64 / kernel / pci.c
index 38a32bc95d22bb0423a6bd28b2ff75f18e69fef1..545356b00e2e14c956841ba8dc751041782d5ef3 100644 (file)
@@ -29,8 +29,6 @@
 
 #include "pci_impl.h"
 
-unsigned long pci_memspace_mask = 0xffffffffUL;
-
 #ifndef CONFIG_PCI
 /* A "nop" PCI implementation. */
 asmlinkage int sys_pciconfig_read(unsigned long bus, unsigned long dfn,
@@ -209,8 +207,7 @@ static struct {
        { "SUNW,sun4v-pci", sun4v_pci_init },
        { "pciex108e,80f0", fire_pci_init },
 };
-#define PCI_NUM_CONTROLLER_TYPES (sizeof(pci_controller_table) / \
-                                 sizeof(pci_controller_table[0]))
+#define PCI_NUM_CONTROLLER_TYPES       ARRAY_SIZE(pci_controller_table)
 
 static int __init pci_controller_init(const char *model_name, int namelen, struct device_node *dp)
 {
@@ -228,20 +225,6 @@ static int __init pci_controller_init(const char *model_name, int namelen, struc
        return 0;
 }
 
-static int __init pci_is_controller(const char *model_name, int namelen, struct device_node *dp)
-{
-       int i;
-
-       for (i = 0; i < PCI_NUM_CONTROLLER_TYPES; i++) {
-               if (!strncmp(model_name,
-                            pci_controller_table[i].model_name,
-                            namelen)) {
-                       return 1;
-               }
-       }
-       return 0;
-}
-
 static int __init pci_controller_scan(int (*handler)(const char *, int, struct device_node *))
 {
        struct device_node *dp;
@@ -276,19 +259,6 @@ static int __init pci_controller_scan(int (*handler)(const char *, int, struct d
        return count;
 }
 
-
-/* Is there some PCI controller in the system?  */
-int __init pcic_present(void)
-{
-       return pci_controller_scan(pci_is_controller);
-}
-
-const struct pci_iommu_ops *pci_iommu_ops;
-EXPORT_SYMBOL(pci_iommu_ops);
-
-extern const struct pci_iommu_ops pci_sun4u_iommu_ops,
-       pci_sun4v_iommu_ops;
-
 /* Find each controller in the system, attach and initialize
  * software state structure for each and link into the
  * pci_pbm_root.  Setup the controller enough such
@@ -296,11 +266,6 @@ extern const struct pci_iommu_ops pci_sun4u_iommu_ops,
  */
 static void __init pci_controller_probe(void)
 {
-       if (tlb_type == hypervisor)
-               pci_iommu_ops = &pci_sun4v_iommu_ops;
-       else
-               pci_iommu_ops = &pci_sun4u_iommu_ops;
-
        printk("PCI: Probing for controllers.\n");
 
        pci_controller_scan(pci_controller_init);
@@ -404,7 +369,10 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
        sd->host_controller = pbm;
        sd->prom_node = node;
        sd->op = of_find_device_by_node(node);
-       sd->msi_num = 0xffffffff;
+
+       sd = &sd->op->dev.archdata;
+       sd->iommu = pbm->iommu;
+       sd->stc = &pbm->stc;
 
        type = of_get_property(node, "device_type", NULL);
        if (type == NULL)
@@ -422,10 +390,15 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
        dev->multifunction = 0;         /* maybe a lie? */
 
        if (host_controller) {
-               dev->vendor = 0x108e;
-               dev->device = 0x8000;
-               dev->subsystem_vendor = 0x0000;
-               dev->subsystem_device = 0x0000;
+               if (tlb_type != hypervisor) {
+                       pci_read_config_word(dev, PCI_VENDOR_ID,
+                                            &dev->vendor);
+                       pci_read_config_word(dev, PCI_DEVICE_ID,
+                                            &dev->device);
+               } else {
+                       dev->vendor = PCI_VENDOR_ID_SUN;
+                       dev->device = 0x80f0;
+               }
                dev->cfg_size = 256;
                dev->class = PCI_CLASS_BRIDGE_HOST << 8;
                sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(bus),
@@ -448,6 +421,7 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
                 */
                pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
                dev->class = class >> 8;
+               dev->revision = class & 0xff;
 
                sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(bus),
                        dev->bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn));
@@ -522,6 +496,89 @@ static void pci_resource_adjust(struct resource *res,
        res->end += root->start;
 }
 
+/* For PCI bus devices which lack a 'ranges' property we interrogate
+ * the config space values to set the resources, just like the generic
+ * Linux PCI probing code does.
+ */
+static void __devinit pci_cfg_fake_ranges(struct pci_dev *dev,
+                                         struct pci_bus *bus,
+                                         struct pci_pbm_info *pbm)
+{
+       struct resource *res;
+       u8 io_base_lo, io_limit_lo;
+       u16 mem_base_lo, mem_limit_lo;
+       unsigned long base, limit;
+
+       pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo);
+       pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo);
+       base = (io_base_lo & PCI_IO_RANGE_MASK) << 8;
+       limit = (io_limit_lo & PCI_IO_RANGE_MASK) << 8;
+
+       if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) {
+               u16 io_base_hi, io_limit_hi;
+
+               pci_read_config_word(dev, PCI_IO_BASE_UPPER16, &io_base_hi);
+               pci_read_config_word(dev, PCI_IO_LIMIT_UPPER16, &io_limit_hi);
+               base |= (io_base_hi << 16);
+               limit |= (io_limit_hi << 16);
+       }
+
+       res = bus->resource[0];
+       if (base <= limit) {
+               res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO;
+               if (!res->start)
+                       res->start = base;
+               if (!res->end)
+                       res->end = limit + 0xfff;
+               pci_resource_adjust(res, &pbm->io_space);
+       }
+
+       pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo);
+       pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo);
+       base = (mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16;
+       limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16;
+
+       res = bus->resource[1];
+       if (base <= limit) {
+               res->flags = ((mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) |
+                             IORESOURCE_MEM);
+               res->start = base;
+               res->end = limit + 0xfffff;
+               pci_resource_adjust(res, &pbm->mem_space);
+       }
+
+       pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo);
+       pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo);
+       base = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
+       limit = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;
+
+       if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {
+               u32 mem_base_hi, mem_limit_hi;
+
+               pci_read_config_dword(dev, PCI_PREF_BASE_UPPER32, &mem_base_hi);
+               pci_read_config_dword(dev, PCI_PREF_LIMIT_UPPER32, &mem_limit_hi);
+
+               /*
+                * Some bridges set the base > limit by default, and some
+                * (broken) BIOSes do not initialize them.  If we find
+                * this, just assume they are not being used.
+                */
+               if (mem_base_hi <= mem_limit_hi) {
+                       base |= ((long) mem_base_hi) << 32;
+                       limit |= ((long) mem_limit_hi) << 32;
+               }
+       }
+
+       res = bus->resource[2];
+       if (base <= limit) {
+               res->flags = ((mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) |
+                             IORESOURCE_MEM | IORESOURCE_PREFETCH);
+               res->start = base;
+               res->end = limit + 0xfffff;
+               pci_resource_adjust(res, &pbm->mem_space);
+       }
+}
+
 /* Cook up fake bus resources for SUNW,simba PCI bridges which lack
  * a proper 'ranges' property.
  */
@@ -581,13 +638,8 @@ static void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm,
        simba = 0;
        if (ranges == NULL) {
                const char *model = of_get_property(node, "model", NULL);
-               if (model && !strcmp(model, "SUNW,simba")) {
+               if (model && !strcmp(model, "SUNW,simba"))
                        simba = 1;
-               } else {
-                       printk(KERN_DEBUG "Can't get ranges for PCI-PCI bridge %s\n",
-                              node->full_name);
-                       return;
-               }
        }
 
        bus = pci_add_new_bus(dev->bus, dev, busrange[0]);
@@ -611,7 +663,10 @@ static void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm,
        }
        if (simba) {
                apb_fake_ranges(dev, bus, pbm);
-               goto simba_cont;
+               goto after_ranges;
+       } else if (ranges == NULL) {
+               pci_cfg_fake_ranges(dev, bus, pbm);
+               goto after_ranges;
        }
        i = 1;
        for (; len >= 32; len -= 32, ranges += 8) {
@@ -650,7 +705,7 @@ static void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm,
                 */
                pci_resource_adjust(res, root);
        }
-simba_cont:
+after_ranges:
        sprintf(bus->name, "PCI Bus %04x:%02x", pci_domain_nr(bus),
                bus->number);
        if (ofpci_verbose)
@@ -665,7 +720,7 @@ static void __devinit pci_of_scan_bus(struct pci_pbm_info *pbm,
 {
        struct device_node *child;
        const u32 *reg;
-       int reglen, devfn;
+       int reglen, devfn, prev_devfn;
        struct pci_dev *dev;
 
        if (ofpci_verbose)
@@ -673,14 +728,25 @@ static void __devinit pci_of_scan_bus(struct pci_pbm_info *pbm,
                       node->full_name, bus->number);
 
        child = NULL;
+       prev_devfn = -1;
        while ((child = of_get_next_child(node, child)) != NULL) {
                if (ofpci_verbose)
                        printk("  * %s\n", child->full_name);
                reg = of_get_property(child, "reg", &reglen);
                if (reg == NULL || reglen < 20)
                        continue;
+
                devfn = (reg[0] >> 8) & 0xff;
 
+               /* This is a workaround for some device trees
+                * which list PCI devices twice.  On the V100
+                * for example, device number 3 is listed twice.
+                * Once as "pm" and once again as "lomp".
+                */
+               if (devfn == prev_devfn)
+                       continue;
+               prev_devfn = devfn;
+
                /* create a new pci_dev for this device */
                dev = of_create_pci_dev(pbm, child, bus, devfn, 0);
                if (!dev)
@@ -736,7 +802,7 @@ int pci_host_bridge_read_pci_cfg(struct pci_bus *bus_dev,
 {
        static u8 fake_pci_config[] = {
                0x8e, 0x10, /* Vendor: 0x108e (Sun) */
-               0x00, 0x80, /* Device: 0x8000 (PBM) */
+               0xf0, 0x80, /* Device: 0x80f0 (Fire) */
                0x46, 0x01, /* Command: 0x0146 (SERR, PARITY, MASTER, MEM) */
                0xa0, 0x22, /* Status: 0x02a0 (DEVSEL_MED, FB2B, 66MHZ) */
                0x00, 0x00, 0x00, 0x06, /* Class: 0x06000000 host bridge */
@@ -976,8 +1042,8 @@ static int __pci_mmap_make_offset_bus(struct pci_dev *pdev, struct vm_area_struc
        return 0;
 }
 
-/* Adjust vm_pgoff of VMA such that it is the physical page offset corresponding
- * to the 32-bit pci bus offset for DEV requested by the user.
+/* Adjust vm_pgoff of VMA such that it is the physical page offset
+ * corresponding to the 32-bit pci bus offset for DEV requested by the user.
  *
  * Basically, the user finds the base address for his device which he wishes
  * to mmap.  They read the 32-bit value from the config space base register,
@@ -986,21 +1052,35 @@ static int __pci_mmap_make_offset_bus(struct pci_dev *pdev, struct vm_area_struc
  *
  * Returns negative error code on failure, zero on success.
  */
-static int __pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vma,
+static int __pci_mmap_make_offset(struct pci_dev *pdev,
+                                 struct vm_area_struct *vma,
                                  enum pci_mmap_state mmap_state)
 {
-       unsigned long user_offset = vma->vm_pgoff << PAGE_SHIFT;
-       unsigned long user32 = user_offset & pci_memspace_mask;
-       unsigned long largest_base, this_base, addr32;
-       int i;
+       unsigned long user_paddr, user_size;
+       int i, err;
+
+       /* First compute the physical address in vma->vm_pgoff,
+        * making sure the user offset is within range in the
+        * appropriate PCI space.
+        */
+       err = __pci_mmap_make_offset_bus(pdev, vma, mmap_state);
+       if (err)
+               return err;
+
+       /* If this is a mapping on a host bridge, any address
+        * is OK.
+        */
+       if ((pdev->class >> 8) == PCI_CLASS_BRIDGE_HOST)
+               return err;
 
-       if ((dev->class >> 8) == PCI_CLASS_BRIDGE_HOST)
-               return __pci_mmap_make_offset_bus(dev, vma, mmap_state);
+       /* Otherwise make sure it's in the range for one of the
+        * device's resources.
+        */
+       user_paddr = vma->vm_pgoff << PAGE_SHIFT;
+       user_size = vma->vm_end - vma->vm_start;
 
-       /* Figure out which base address this is for. */
-       largest_base = 0UL;
        for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
-               struct resource *rp = &dev->resource[i];
+               struct resource *rp = &pdev->resource[i];
 
                /* Active? */
                if (!rp->flags)
@@ -1018,26 +1098,14 @@ static int __pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vm
                                continue;
                }
 
-               this_base = rp->start;
-
-               addr32 = (this_base & PAGE_MASK) & pci_memspace_mask;
-
-               if (mmap_state == pci_mmap_io)
-                       addr32 &= 0xffffff;
-
-               if (addr32 <= user32 && this_base > largest_base)
-                       largest_base = this_base;
+               if ((rp->start <= user_paddr) &&
+                   (user_paddr + user_size) <= (rp->end + 1UL))
+                       break;
        }
 
-       if (largest_base == 0UL)
+       if (i > PCI_ROM_RESOURCE)
                return -EINVAL;
 
-       /* Now construct the final physical address. */
-       if (mmap_state == pci_mmap_io)
-               vma->vm_pgoff = (((largest_base & ~0xffffffUL) | user32) >> PAGE_SHIFT);
-       else
-               vma->vm_pgoff = (((largest_base & ~(pci_memspace_mask)) | user32) >> PAGE_SHIFT);
-
        return 0;
 }
 
@@ -1139,4 +1207,67 @@ struct device_node *pci_device_to_OF_node(struct pci_dev *pdev)
 }
 EXPORT_SYMBOL(pci_device_to_OF_node);
 
+static void ali_sound_dma_hack(struct pci_dev *pdev, int set_bit)
+{
+       struct pci_dev *ali_isa_bridge;
+       u8 val;
+
+       /* ALI sound chips generate 31-bits of DMA, a special register
+        * determines what bit 31 is emitted as.
+        */
+       ali_isa_bridge = pci_get_device(PCI_VENDOR_ID_AL,
+                                        PCI_DEVICE_ID_AL_M1533,
+                                        NULL);
+
+       pci_read_config_byte(ali_isa_bridge, 0x7e, &val);
+       if (set_bit)
+               val |= 0x01;
+       else
+               val &= ~0x01;
+       pci_write_config_byte(ali_isa_bridge, 0x7e, val);
+       pci_dev_put(ali_isa_bridge);
+}
+
+int pci_dma_supported(struct pci_dev *pdev, u64 device_mask)
+{
+       u64 dma_addr_mask;
+
+       if (pdev == NULL) {
+               dma_addr_mask = 0xffffffff;
+       } else {
+               struct iommu *iommu = pdev->dev.archdata.iommu;
+
+               dma_addr_mask = iommu->dma_addr_mask;
+
+               if (pdev->vendor == PCI_VENDOR_ID_AL &&
+                   pdev->device == PCI_DEVICE_ID_AL_M5451 &&
+                   device_mask == 0x7fffffff) {
+                       ali_sound_dma_hack(pdev,
+                                          (dma_addr_mask & 0x80000000) != 0);
+                       return 1;
+               }
+       }
+
+       if (device_mask >= (1UL << 32UL))
+               return 0;
+
+       return (device_mask & dma_addr_mask) == dma_addr_mask;
+}
+
+void pci_resource_to_user(const struct pci_dev *pdev, int bar,
+                         const struct resource *rp, resource_size_t *start,
+                         resource_size_t *end)
+{
+       struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
+       unsigned long offset;
+
+       if (rp->flags & IORESOURCE_IO)
+               offset = pbm->io_space.start;
+       else
+               offset = pbm->mem_space.start;
+
+       *start = rp->start - offset;
+       *end = rp->end - offset;
+}
+
 #endif /* !(CONFIG_PCI) */