powerpc/pnv/pci: Fix incorrect PE reservation attempt on some 64-bit BARs
[cascardo/linux.git] / arch / powerpc / platforms / powernv / pci-ioda.c
index d7502f2..fd9444f 100644 (file)
@@ -111,10 +111,17 @@ static int __init iommu_setup(char *str)
 }
 early_param("iommu", iommu_setup);
 
-static inline bool pnv_pci_is_mem_pref_64(unsigned long flags)
+static inline bool pnv_pci_is_m64(struct pnv_phb *phb, struct resource *r)
 {
-       return ((flags & (IORESOURCE_MEM_64 | IORESOURCE_PREFETCH)) ==
-               (IORESOURCE_MEM_64 | IORESOURCE_PREFETCH));
+       /*
+        * WARNING: We cannot rely on the resource flags. The Linux PCI
+        * allocation code sometimes decides to put a 64-bit prefetchable
+        * BAR in the 32-bit window, so we have to compare the addresses.
+        *
+        * For simplicity we only test resource start.
+        */
+       return (r->start >= phb->ioda.m64_base &&
+               r->start < (phb->ioda.m64_base + phb->ioda.m64_size));
 }
 
 static struct pnv_ioda_pe *pnv_ioda_init_pe(struct pnv_phb *phb, int pe_no)
@@ -191,9 +198,6 @@ static int pnv_ioda2_init_m64(struct pnv_phb *phb)
                goto fail;
        }
 
-       /* Mark the M64 BAR assigned */
-       set_bit(phb->ioda.m64_bar_idx, &phb->ioda.m64_bar_alloc);
-
        /*
         * Exclude the segments for reserved and root bus PE, which
         * are first or last two PEs.
@@ -232,7 +236,7 @@ static void pnv_ioda_reserve_dev_m64_pe(struct pci_dev *pdev,
        sgsz = phb->ioda.m64_segsize;
        for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
                r = &pdev->resource[i];
-               if (!r->parent || !pnv_pci_is_mem_pref_64(r->flags))
+               if (!r->parent || !pnv_pci_is_m64(phb, r))
                        continue;
 
                start = _ALIGN_DOWN(r->start - base, sgsz);
@@ -404,6 +408,7 @@ static void __init pnv_ioda_parse_m64_window(struct pnv_phb *phb)
        struct pci_controller *hose = phb->hose;
        struct device_node *dn = hose->dn;
        struct resource *res;
+       u32 m64_range[2], i;
        const u32 *r;
        u64 pci_addr;
 
@@ -424,6 +429,30 @@ static void __init pnv_ioda_parse_m64_window(struct pnv_phb *phb)
                return;
        }
 
+       /*
+        * Find the available M64 BAR range and pickup the last one for
+        * covering the whole 64-bits space. We support only one range.
+        */
+       if (of_property_read_u32_array(dn, "ibm,opal-available-m64-ranges",
+                                      m64_range, 2)) {
+               /* In absence of the property, assume 0..15 */
+               m64_range[0] = 0;
+               m64_range[1] = 16;
+       }
+       /* We only support 64 bits in our allocator */
+       if (m64_range[1] > 63) {
+               pr_warn("%s: Limiting M64 range to 63 (from %d) on PHB#%x\n",
+                       __func__, m64_range[1], phb->hose->global_number);
+               m64_range[1] = 63;
+       }
+       /* Empty range, no m64 */
+       if (m64_range[1] <= m64_range[0]) {
+               pr_warn("%s: M64 empty, disabling M64 usage on PHB#%x\n",
+                       __func__, phb->hose->global_number);
+               return;
+       }
+
+       /* Configure M64 informations */
        res = &hose->mem_resources[1];
        res->name = dn->full_name;
        res->start = of_translate_address(dn, r + 2);
@@ -436,11 +465,28 @@ static void __init pnv_ioda_parse_m64_window(struct pnv_phb *phb)
        phb->ioda.m64_segsize = phb->ioda.m64_size / phb->ioda.total_pe_num;
        phb->ioda.m64_base = pci_addr;
 
-       pr_info(" MEM64 0x%016llx..0x%016llx -> 0x%016llx\n",
-                       res->start, res->end, pci_addr);
+       /* This lines up nicely with the display from processing OF ranges */
+       pr_info(" MEM 0x%016llx..0x%016llx -> 0x%016llx (M64 #%d..%d)\n",
+               res->start, res->end, pci_addr, m64_range[0],
+               m64_range[0] + m64_range[1] - 1);
+
+       /* Mark all M64 used up by default */
+       phb->ioda.m64_bar_alloc = (unsigned long)-1;
 
        /* Use last M64 BAR to cover M64 window */
-       phb->ioda.m64_bar_idx = 15;
+       m64_range[1]--;
+       phb->ioda.m64_bar_idx = m64_range[0] + m64_range[1];
+
+       pr_info(" Using M64 #%d as default window\n", phb->ioda.m64_bar_idx);
+
+       /* Mark remaining ones free */
+       for (i = m64_range[0]; i < m64_range[1]; i++)
+               clear_bit(i, &phb->ioda.m64_bar_alloc);
+
+       /*
+        * Setup init functions for M64 based on IODA version, IODA3 uses
+        * the IODA2 code.
+        */
        if (phb->type == PNV_PHB_IODA1)
                phb->init_m64 = pnv_ioda1_init_m64;
        else
@@ -1767,7 +1813,7 @@ static void pnv_pci_p7ioc_tce_invalidate(struct iommu_table *tbl,
 static int pnv_ioda1_tce_build(struct iommu_table *tbl, long index,
                long npages, unsigned long uaddr,
                enum dma_data_direction direction,
-               struct dma_attrs *attrs)
+               unsigned long attrs)
 {
        int ret = pnv_tce_build(tbl, index, npages, uaddr, direction,
                        attrs);
@@ -1838,7 +1884,7 @@ static void pnv_pci_phb3_tce_invalidate(struct pnv_ioda_pe *pe, bool rm,
                                        unsigned shift, unsigned long index,
                                        unsigned long npages)
 {
-       __be64 __iomem *invalidate = pnv_ioda_get_inval_reg(pe->phb, false);
+       __be64 __iomem *invalidate = pnv_ioda_get_inval_reg(pe->phb, rm);
        unsigned long start, end, inc;
 
        /* We'll invalidate DMA address in PE scope */
@@ -1911,7 +1957,7 @@ static void pnv_pci_ioda2_tce_invalidate(struct iommu_table *tbl,
 static int pnv_ioda2_tce_build(struct iommu_table *tbl, long index,
                long npages, unsigned long uaddr,
                enum dma_data_direction direction,
-               struct dma_attrs *attrs)
+               unsigned long attrs)
 {
        int ret = pnv_tce_build(tbl, index, npages, uaddr, direction,
                        attrs);
@@ -2824,7 +2870,7 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev)
                res = &pdev->resource[i + PCI_IOV_RESOURCES];
                if (!res->flags || res->parent)
                        continue;
-               if (!pnv_pci_is_mem_pref_64(res->flags)) {
+               if (!pnv_pci_is_m64(phb, res)) {
                        dev_warn(&pdev->dev, "Don't support SR-IOV with"
                                        " non M64 VF BAR%d: %pR. \n",
                                 i, res);
@@ -2919,7 +2965,7 @@ static void pnv_ioda_setup_pe_res(struct pnv_ioda_pe *pe,
                        index++;
                }
        } else if ((res->flags & IORESOURCE_MEM) &&
-                  !pnv_pci_is_mem_pref_64(res->flags)) {
+                  !pnv_pci_is_m64(phb, res)) {
                region.start = res->start -
                               phb->hose->mem_offset[0] -
                               phb->ioda.m32_pci_base;
@@ -3044,9 +3090,12 @@ static resource_size_t pnv_pci_window_alignment(struct pci_bus *bus,
                bridge = bridge->bus->self;
        }
 
-       /* We fail back to M32 if M64 isn't supported */
-       if (phb->ioda.m64_segsize &&
-           pnv_pci_is_mem_pref_64(type))
+       /*
+        * We fall back to M32 if M64 isn't supported. We enforce the M64
+        * alignment for any 64-bit resource, PCIe doesn't care and
+        * bridges only do 64-bit prefetchable anyway.
+        */
+       if (phb->ioda.m64_segsize && (type & IORESOURCE_MEM_64))
                return phb->ioda.m64_segsize;
        if (type & IORESOURCE_MEM)
                return phb->ioda.m32_segsize;
@@ -3086,7 +3135,7 @@ static void pnv_pci_fixup_bridge_resources(struct pci_bus *bus,
                w = NULL;
                if (r->flags & type & IORESOURCE_IO)
                        w = &hose->io_resource;
-               else if (pnv_pci_is_mem_pref_64(r->flags) &&
+               else if (pnv_pci_is_m64(phb, r) &&
                         (type & IORESOURCE_PREFETCH) &&
                         phb->ioda.m64_segsize)
                        w = &hose->mem_resources[1];
@@ -3477,6 +3526,9 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np,
        void *aux;
        long rc;
 
+       if (!of_device_is_available(np))
+               return;
+
        pr_info("Initializing %s PHB (%s)\n",
                pnv_phb_names[ioda_type], of_node_full_name(np));