powerpc/44x: Fix address decoding setup of PCI 2.x cells
[sfrench/cifs-2.6.git] / arch / powerpc / sysdev / ppc4xx_pci.c
index d3e4d61030b59039759ad4d4d2eaa9318e32abe0..5558d932b4d5c87b84f5a3826577607fa5220514 100644 (file)
@@ -194,11 +194,58 @@ static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose,
  * 4xx PCI 2.x part
  */
 
+static int __init ppc4xx_setup_one_pci_PMM(struct pci_controller       *hose,
+                                          void __iomem                 *reg,
+                                          u64                          plb_addr,
+                                          u64                          pci_addr,
+                                          u64                          size,
+                                          unsigned int                 flags,
+                                          int                          index)
+{
+       u32 ma, pcila, pciha;
+
+       /* Hack warning ! The "old" PCI 2.x cell only let us configure the low
+        * 32-bit of incoming PLB addresses. The top 4 bits of the 36-bit
+        * address are actually hard wired to a value that appears to depend
+        * on the specific SoC. For example, it's 0 on 440EP and 1 on 440EPx.
+        *
+        * The trick here is we just crop those top bits and ignore them when
+        * programming the chip. That means the device-tree has to be right
+        * for the specific part used (we don't print a warning if it's wrong
+        * but on the other hand, you'll crash quickly enough), but at least
+        * this code should work whatever the hard coded value is
+        */
+       plb_addr &= 0xffffffffull;
+
+       /* Note: Due to the above hack, the test below doesn't actually test
+        * if you address is above 4G, but it tests that address and
+        * (address + size) are both contained in the same 4G
+        */
+       if ((plb_addr + size) > 0xffffffffull || !is_power_of_2(size) ||
+           size < 0x1000 || (plb_addr & (size - 1)) != 0) {
+               printk(KERN_WARNING "%s: Resource out of range\n",
+                      hose->dn->full_name);
+               return -1;
+       }
+       ma = (0xffffffffu << ilog2(size)) | 1;
+       if (flags & IORESOURCE_PREFETCH)
+               ma |= 2;
+
+       pciha = RES_TO_U32_HIGH(pci_addr);
+       pcila = RES_TO_U32_LOW(pci_addr);
+
+       writel(plb_addr, reg + PCIL0_PMM0LA + (0x10 * index));
+       writel(pcila, reg + PCIL0_PMM0PCILA + (0x10 * index));
+       writel(pciha, reg + PCIL0_PMM0PCIHA + (0x10 * index));
+       writel(ma, reg + PCIL0_PMM0MA + (0x10 * index));
+
+       return 0;
+}
+
 static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose,
                                             void __iomem *reg)
 {
-       u32 la, ma, pcila, pciha;
-       int i, j;
+       int i, j, found_isa_hole = 0;
 
        /* Setup outbound memory windows */
        for (i = j = 0; i < 3; i++) {
@@ -213,28 +260,29 @@ static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose,
                        break;
                }
 
-               /* Calculate register values */
-               la = res->start;
-               pciha = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
-               pcila = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
-
-               ma = res->end + 1 - res->start;
-               if (!is_power_of_2(ma) || ma < 0x1000 || ma > 0xffffffffu) {
-                       printk(KERN_WARNING "%s: Resource out of range\n",
-                              hose->dn->full_name);
-                       continue;
+               /* Configure the resource */
+               if (ppc4xx_setup_one_pci_PMM(hose, reg,
+                                            res->start,
+                                            res->start - hose->pci_mem_offset,
+                                            res->end + 1 - res->start,
+                                            res->flags,
+                                            j) == 0) {
+                       j++;
+
+                       /* If the resource PCI address is 0 then we have our
+                        * ISA memory hole
+                        */
+                       if (res->start == hose->pci_mem_offset)
+                               found_isa_hole = 1;
                }
-               ma = (0xffffffffu << ilog2(ma)) | 0x1;
-               if (res->flags & IORESOURCE_PREFETCH)
-                       ma |= 0x2;
-
-               /* Program register values */
-               writel(la, reg + PCIL0_PMM0LA + (0x10 * j));
-               writel(pcila, reg + PCIL0_PMM0PCILA + (0x10 * j));
-               writel(pciha, reg + PCIL0_PMM0PCIHA + (0x10 * j));
-               writel(ma, reg + PCIL0_PMM0MA + (0x10 * j));
-               j++;
        }
+
+       /* Handle ISA memory hole if not already covered */
+       if (j <= 2 && !found_isa_hole && hose->isa_mem_size)
+               if (ppc4xx_setup_one_pci_PMM(hose, reg, hose->isa_mem_phys, 0,
+                                            hose->isa_mem_size, 0, j) == 0)
+                       printk(KERN_INFO "%s: Legacy ISA memory support enabled\n",
+                              hose->dn->full_name);
 }
 
 static void __init ppc4xx_configure_pci_PTMs(struct pci_controller *hose,
@@ -352,11 +400,52 @@ static void __init ppc4xx_probe_pci_bridge(struct device_node *np)
  * 4xx PCI-X part
  */
 
+static int __init ppc4xx_setup_one_pcix_POM(struct pci_controller      *hose,
+                                           void __iomem                *reg,
+                                           u64                         plb_addr,
+                                           u64                         pci_addr,
+                                           u64                         size,
+                                           unsigned int                flags,
+                                           int                         index)
+{
+       u32 lah, lal, pciah, pcial, sa;
+
+       if (!is_power_of_2(size) || size < 0x1000 ||
+           (plb_addr & (size - 1)) != 0) {
+               printk(KERN_WARNING "%s: Resource out of range\n",
+                      hose->dn->full_name);
+               return -1;
+       }
+
+       /* Calculate register values */
+       lah = RES_TO_U32_HIGH(plb_addr);
+       lal = RES_TO_U32_LOW(plb_addr);
+       pciah = RES_TO_U32_HIGH(pci_addr);
+       pcial = RES_TO_U32_LOW(pci_addr);
+       sa = (0xffffffffu << ilog2(size)) | 0x1;
+
+       /* Program register values */
+       if (index == 0) {
+               writel(lah, reg + PCIX0_POM0LAH);
+               writel(lal, reg + PCIX0_POM0LAL);
+               writel(pciah, reg + PCIX0_POM0PCIAH);
+               writel(pcial, reg + PCIX0_POM0PCIAL);
+               writel(sa, reg + PCIX0_POM0SA);
+       } else {
+               writel(lah, reg + PCIX0_POM1LAH);
+               writel(lal, reg + PCIX0_POM1LAL);
+               writel(pciah, reg + PCIX0_POM1PCIAH);
+               writel(pcial, reg + PCIX0_POM1PCIAL);
+               writel(sa, reg + PCIX0_POM1SA);
+       }
+
+       return 0;
+}
+
 static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
                                              void __iomem *reg)
 {
-       u32 lah, lal, pciah, pcial, sa;
-       int i, j;
+       int i, j, found_isa_hole = 0;
 
        /* Setup outbound memory windows */
        for (i = j = 0; i < 3; i++) {
@@ -371,36 +460,29 @@ static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
                        break;
                }
 
-               /* Calculate register values */
-               lah = RES_TO_U32_HIGH(res->start);
-               lal = RES_TO_U32_LOW(res->start);
-               pciah = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
-               pcial = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
-               sa = res->end + 1 - res->start;
-               if (!is_power_of_2(sa) || sa < 0x100000 ||
-                   sa > 0xffffffffu) {
-                       printk(KERN_WARNING "%s: Resource out of range\n",
-                              hose->dn->full_name);
-                       continue;
+               /* Configure the resource */
+               if (ppc4xx_setup_one_pcix_POM(hose, reg,
+                                             res->start,
+                                             res->start - hose->pci_mem_offset,
+                                             res->end + 1 - res->start,
+                                             res->flags,
+                                             j) == 0) {
+                       j++;
+
+                       /* If the resource PCI address is 0 then we have our
+                        * ISA memory hole
+                        */
+                       if (res->start == hose->pci_mem_offset)
+                               found_isa_hole = 1;
                }
-               sa = (0xffffffffu << ilog2(sa)) | 0x1;
-
-               /* Program register values */
-               if (j == 0) {
-                       writel(lah, reg + PCIX0_POM0LAH);
-                       writel(lal, reg + PCIX0_POM0LAL);
-                       writel(pciah, reg + PCIX0_POM0PCIAH);
-                       writel(pcial, reg + PCIX0_POM0PCIAL);
-                       writel(sa, reg + PCIX0_POM0SA);
-               } else {
-                       writel(lah, reg + PCIX0_POM1LAH);
-                       writel(lal, reg + PCIX0_POM1LAL);
-                       writel(pciah, reg + PCIX0_POM1PCIAH);
-                       writel(pcial, reg + PCIX0_POM1PCIAL);
-                       writel(sa, reg + PCIX0_POM1SA);
-               }
-               j++;
        }
+
+       /* Handle ISA memory hole if not already covered */
+       if (j <= 1 && !found_isa_hole && hose->isa_mem_size)
+               if (ppc4xx_setup_one_pcix_POM(hose, reg, hose->isa_mem_phys, 0,
+                                             hose->isa_mem_size, 0, j) == 0)
+                       printk(KERN_INFO "%s: Legacy ISA memory support enabled\n",
+                              hose->dn->full_name);
 }
 
 static void __init ppc4xx_configure_pcix_PIMs(struct pci_controller *hose,
@@ -1317,12 +1399,72 @@ static struct pci_ops ppc4xx_pciex_pci_ops =
        .write = ppc4xx_pciex_write_config,
 };
 
+static int __init ppc4xx_setup_one_pciex_POM(struct ppc4xx_pciex_port  *port,
+                                            struct pci_controller      *hose,
+                                            void __iomem               *mbase,
+                                            u64                        plb_addr,
+                                            u64                        pci_addr,
+                                            u64                        size,
+                                            unsigned int               flags,
+                                            int                        index)
+{
+       u32 lah, lal, pciah, pcial, sa;
+
+       if (!is_power_of_2(size) ||
+           (index < 2 && size < 0x100000) ||
+           (index == 2 && size < 0x100) ||
+           (plb_addr & (size - 1)) != 0) {
+               printk(KERN_WARNING "%s: Resource out of range\n",
+                      hose->dn->full_name);
+               return -1;
+       }
+
+       /* Calculate register values */
+       lah = RES_TO_U32_HIGH(plb_addr);
+       lal = RES_TO_U32_LOW(plb_addr);
+       pciah = RES_TO_U32_HIGH(pci_addr);
+       pcial = RES_TO_U32_LOW(pci_addr);
+       sa = (0xffffffffu << ilog2(size)) | 0x1;
+
+       /* Program register values */
+       switch (index) {
+       case 0:
+               out_le32(mbase + PECFG_POM0LAH, pciah);
+               out_le32(mbase + PECFG_POM0LAL, pcial);
+               dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAH, lah);
+               dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAL, lal);
+               dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKH, 0x7fffffff);
+               /* Note that 3 here means enabled | single region */
+               dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, sa | 3);
+               break;
+       case 1:
+               out_le32(mbase + PECFG_POM1LAH, pciah);
+               out_le32(mbase + PECFG_POM1LAL, pcial);
+               dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAH, lah);
+               dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAL, lal);
+               dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKH, 0x7fffffff);
+               /* Note that 3 here means enabled | single region */
+               dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, sa | 3);
+               break;
+       case 2:
+               out_le32(mbase + PECFG_POM2LAH, pciah);
+               out_le32(mbase + PECFG_POM2LAL, pcial);
+               dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAH, lah);
+               dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAL, lal);
+               dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKH, 0x7fffffff);
+               /* Note that 3 here means enabled | IO space !!! */
+               dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, sa | 3);
+               break;
+       }
+
+       return 0;
+}
+
 static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port,
                                               struct pci_controller *hose,
                                               void __iomem *mbase)
 {
-       u32 lah, lal, pciah, pcial, sa;
-       int i, j;
+       int i, j, found_isa_hole = 0;
 
        /* Setup outbound memory windows */
        for (i = j = 0; i < 3; i++) {
@@ -1337,53 +1479,38 @@ static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port,
                        break;
                }
 
-               /* Calculate register values */
-               lah = RES_TO_U32_HIGH(res->start);
-               lal = RES_TO_U32_LOW(res->start);
-               pciah = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
-               pcial = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
-               sa = res->end + 1 - res->start;
-               if (!is_power_of_2(sa) || sa < 0x100000 ||
-                   sa > 0xffffffffu) {
-                       printk(KERN_WARNING "%s: Resource out of range\n",
-                              port->node->full_name);
-                       continue;
+               /* Configure the resource */
+               if (ppc4xx_setup_one_pciex_POM(port, hose, mbase,
+                                              res->start,
+                                              res->start - hose->pci_mem_offset,
+                                              res->end + 1 - res->start,
+                                              res->flags,
+                                              j) == 0) {
+                       j++;
+
+                       /* If the resource PCI address is 0 then we have our
+                        * ISA memory hole
+                        */
+                       if (res->start == hose->pci_mem_offset)
+                               found_isa_hole = 1;
                }
-               sa = (0xffffffffu << ilog2(sa)) | 0x1;
-
-               /* Program register values */
-               switch (j) {
-               case 0:
-                       out_le32(mbase + PECFG_POM0LAH, pciah);
-                       out_le32(mbase + PECFG_POM0LAL, pcial);
-                       dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAH, lah);
-                       dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAL, lal);
-                       dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKH, 0x7fffffff);
-                       dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, sa | 3);
-                       break;
-               case 1:
-                       out_le32(mbase + PECFG_POM1LAH, pciah);
-                       out_le32(mbase + PECFG_POM1LAL, pcial);
-                       dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAH, lah);
-                       dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAL, lal);
-                       dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKH, 0x7fffffff);
-                       dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, sa | 3);
-                       break;
-               }
-               j++;
        }
 
-       /* Configure IO, always 64K starting at 0 */
-       if (hose->io_resource.flags & IORESOURCE_IO) {
-               lah = RES_TO_U32_HIGH(hose->io_base_phys);
-               lal = RES_TO_U32_LOW(hose->io_base_phys);
-               out_le32(mbase + PECFG_POM2LAH, 0);
-               out_le32(mbase + PECFG_POM2LAL, 0);
-               dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAH, lah);
-               dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAL, lal);
-               dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKH, 0x7fffffff);
-               dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, 0xffff0000 | 3);
-       }
+       /* Handle ISA memory hole if not already covered */
+       if (j <= 1 && !found_isa_hole && hose->isa_mem_size)
+               if (ppc4xx_setup_one_pciex_POM(port, hose, mbase,
+                                              hose->isa_mem_phys, 0,
+                                              hose->isa_mem_size, 0, j) == 0)
+                       printk(KERN_INFO "%s: Legacy ISA memory support enabled\n",
+                              hose->dn->full_name);
+
+       /* Configure IO, always 64K starting at 0. We hard wire it to 64K !
+        * Note also that it -has- to be region index 2 on this HW
+        */
+       if (hose->io_resource.flags & IORESOURCE_IO)
+               ppc4xx_setup_one_pciex_POM(port, hose, mbase,
+                                          hose->io_base_phys, 0,
+                                          0x10000, IORESOURCE_IO, 2);
 }
 
 static void __init ppc4xx_configure_pciex_PIMs(struct ppc4xx_pciex_port *port,