These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / arch / powerpc / platforms / powernv / pci.c
index bca2aeb..ad8c3f4 100644 (file)
@@ -45,7 +45,7 @@
 //#define cfg_dbg(fmt...)      printk(fmt)
 
 #ifdef CONFIG_PCI_MSI
-static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
+int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
 {
        struct pci_controller *hose = pci_bus_to_host(pdev->bus);
        struct pnv_phb *phb = hose->private_data;
@@ -61,7 +61,7 @@ static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
        if (pdev->no_64bit_msi && !phb->msi32_support)
                return -ENODEV;
 
-       list_for_each_entry(entry, &pdev->msi_list, list) {
+       for_each_pci_msi_entry(entry, pdev) {
                if (!entry->msi_attrib.is_64 && !phb->msi32_support) {
                        pr_warn("%s: Supports only 64-bit MSIs\n",
                                pci_name(pdev));
@@ -94,22 +94,23 @@ static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
        return 0;
 }
 
-static void pnv_teardown_msi_irqs(struct pci_dev *pdev)
+void pnv_teardown_msi_irqs(struct pci_dev *pdev)
 {
        struct pci_controller *hose = pci_bus_to_host(pdev->bus);
        struct pnv_phb *phb = hose->private_data;
        struct msi_desc *entry;
+       irq_hw_number_t hwirq;
 
        if (WARN_ON(!phb))
                return;
 
-       list_for_each_entry(entry, &pdev->msi_list, list) {
+       for_each_pci_msi_entry(entry, pdev) {
                if (entry->irq == NO_IRQ)
                        continue;
+               hwirq = virq_to_hw(entry->irq);
                irq_set_msi_desc(entry->irq, NULL);
-               msi_bitmap_free_hwirqs(&phb->msi_bmp,
-                       virq_to_hw(entry->irq) - phb->msi_base, 1);
                irq_dispose_mapping(entry->irq);
+               msi_bitmap_free_hwirqs(&phb->msi_bmp, hwirq - phb->msi_base, 1);
        }
 }
 #endif /* CONFIG_PCI_MSI */
@@ -572,80 +573,158 @@ struct pci_ops pnv_pci_ops = {
        .write = pnv_pci_write_config,
 };
 
-static int pnv_tce_build(struct iommu_table *tbl, long index, long npages,
-                        unsigned long uaddr, enum dma_data_direction direction,
-                        struct dma_attrs *attrs, bool rm)
+static __be64 *pnv_tce(struct iommu_table *tbl, long idx)
 {
-       u64 proto_tce;
-       __be64 *tcep, *tces;
-       u64 rpn;
+       __be64 *tmp = ((__be64 *)tbl->it_base);
+       int  level = tbl->it_indirect_levels;
+       const long shift = ilog2(tbl->it_level_size);
+       unsigned long mask = (tbl->it_level_size - 1) << (level * shift);
+
+       while (level) {
+               int n = (idx & mask) >> (level * shift);
+               unsigned long tce = be64_to_cpu(tmp[n]);
+
+               tmp = __va(tce & ~(TCE_PCI_READ | TCE_PCI_WRITE));
+               idx &= ~mask;
+               mask >>= shift;
+               --level;
+       }
 
-       proto_tce = TCE_PCI_READ; // Read allowed
+       return tmp + idx;
+}
 
-       if (direction != DMA_TO_DEVICE)
-               proto_tce |= TCE_PCI_WRITE;
+int pnv_tce_build(struct iommu_table *tbl, long index, long npages,
+               unsigned long uaddr, enum dma_data_direction direction,
+               struct dma_attrs *attrs)
+{
+       u64 proto_tce = iommu_direction_to_tce_perm(direction);
+       u64 rpn = __pa(uaddr) >> tbl->it_page_shift;
+       long i;
 
-       tces = tcep = ((__be64 *)tbl->it_base) + index - tbl->it_offset;
-       rpn = __pa(uaddr) >> tbl->it_page_shift;
+       if (proto_tce & TCE_PCI_WRITE)
+               proto_tce |= TCE_PCI_READ;
 
-       while (npages--)
-               *(tcep++) = cpu_to_be64(proto_tce |
-                               (rpn++ << tbl->it_page_shift));
+       for (i = 0; i < npages; i++) {
+               unsigned long newtce = proto_tce |
+                       ((rpn + i) << tbl->it_page_shift);
+               unsigned long idx = index - tbl->it_offset + i;
 
-       /* Some implementations won't cache invalid TCEs and thus may not
-        * need that flush. We'll probably turn it_type into a bit mask
-        * of flags if that becomes the case
-        */
-       if (tbl->it_type & TCE_PCI_SWINV_CREATE)
-               pnv_pci_ioda_tce_invalidate(tbl, tces, tcep - 1, rm);
+               *(pnv_tce(tbl, idx)) = cpu_to_be64(newtce);
+       }
 
        return 0;
 }
 
-static int pnv_tce_build_vm(struct iommu_table *tbl, long index, long npages,
-                           unsigned long uaddr,
-                           enum dma_data_direction direction,
-                           struct dma_attrs *attrs)
+#ifdef CONFIG_IOMMU_API
+int pnv_tce_xchg(struct iommu_table *tbl, long index,
+               unsigned long *hpa, enum dma_data_direction *direction)
 {
-       return pnv_tce_build(tbl, index, npages, uaddr, direction, attrs,
-                       false);
+       u64 proto_tce = iommu_direction_to_tce_perm(*direction);
+       unsigned long newtce = *hpa | proto_tce, oldtce;
+       unsigned long idx = index - tbl->it_offset;
+
+       BUG_ON(*hpa & ~IOMMU_PAGE_MASK(tbl));
+
+       if (newtce & TCE_PCI_WRITE)
+               newtce |= TCE_PCI_READ;
+
+       oldtce = xchg(pnv_tce(tbl, idx), cpu_to_be64(newtce));
+       *hpa = be64_to_cpu(oldtce) & ~(TCE_PCI_READ | TCE_PCI_WRITE);
+       *direction = iommu_tce_direction(oldtce);
+
+       return 0;
 }
+#endif
 
-static void pnv_tce_free(struct iommu_table *tbl, long index, long npages,
-               bool rm)
+void pnv_tce_free(struct iommu_table *tbl, long index, long npages)
 {
-       __be64 *tcep, *tces;
+       long i;
 
-       tces = tcep = ((__be64 *)tbl->it_base) + index - tbl->it_offset;
+       for (i = 0; i < npages; i++) {
+               unsigned long idx = index - tbl->it_offset + i;
 
-       while (npages--)
-               *(tcep++) = cpu_to_be64(0);
+               *(pnv_tce(tbl, idx)) = cpu_to_be64(0);
+       }
+}
 
-       if (tbl->it_type & TCE_PCI_SWINV_FREE)
-               pnv_pci_ioda_tce_invalidate(tbl, tces, tcep - 1, rm);
+unsigned long pnv_tce_get(struct iommu_table *tbl, long index)
+{
+       return *(pnv_tce(tbl, index - tbl->it_offset));
 }
 
-static void pnv_tce_free_vm(struct iommu_table *tbl, long index, long npages)
+struct iommu_table *pnv_pci_table_alloc(int nid)
 {
-       pnv_tce_free(tbl, index, npages, false);
+       struct iommu_table *tbl;
+
+       tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL, nid);
+       INIT_LIST_HEAD_RCU(&tbl->it_group_list);
+
+       return tbl;
 }
 
-static unsigned long pnv_tce_get(struct iommu_table *tbl, long index)
+long pnv_pci_link_table_and_group(int node, int num,
+               struct iommu_table *tbl,
+               struct iommu_table_group *table_group)
 {
-       return ((u64 *)tbl->it_base)[index - tbl->it_offset];
+       struct iommu_table_group_link *tgl = NULL;
+
+       if (WARN_ON(!tbl || !table_group))
+               return -EINVAL;
+
+       tgl = kzalloc_node(sizeof(struct iommu_table_group_link), GFP_KERNEL,
+                       node);
+       if (!tgl)
+               return -ENOMEM;
+
+       tgl->table_group = table_group;
+       list_add_rcu(&tgl->next, &tbl->it_group_list);
+
+       table_group->tables[num] = tbl;
+
+       return 0;
 }
 
-static int pnv_tce_build_rm(struct iommu_table *tbl, long index, long npages,
-                           unsigned long uaddr,
-                           enum dma_data_direction direction,
-                           struct dma_attrs *attrs)
+static void pnv_iommu_table_group_link_free(struct rcu_head *head)
 {
-       return pnv_tce_build(tbl, index, npages, uaddr, direction, attrs, true);
+       struct iommu_table_group_link *tgl = container_of(head,
+                       struct iommu_table_group_link, rcu);
+
+       kfree(tgl);
 }
 
-static void pnv_tce_free_rm(struct iommu_table *tbl, long index, long npages)
+void pnv_pci_unlink_table_and_group(struct iommu_table *tbl,
+               struct iommu_table_group *table_group)
 {
-       pnv_tce_free(tbl, index, npages, true);
+       long i;
+       bool found;
+       struct iommu_table_group_link *tgl;
+
+       if (!tbl || !table_group)
+               return;
+
+       /* Remove link to a group from table's list of attached groups */
+       found = false;
+       list_for_each_entry_rcu(tgl, &tbl->it_group_list, next) {
+               if (tgl->table_group == table_group) {
+                       list_del_rcu(&tgl->next);
+                       call_rcu(&tgl->rcu, pnv_iommu_table_group_link_free);
+                       found = true;
+                       break;
+               }
+       }
+       if (WARN_ON(!found))
+               return;
+
+       /* Clean a pointer to iommu_table in iommu_table_group::tables[] */
+       found = false;
+       for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) {
+               if (table_group->tables[i] == tbl) {
+                       table_group->tables[i] = NULL;
+                       found = true;
+                       break;
+               }
+       }
+       WARN_ON(!found);
 }
 
 void pnv_pci_setup_iommu_table(struct iommu_table *tbl,
@@ -662,7 +741,7 @@ void pnv_pci_setup_iommu_table(struct iommu_table *tbl,
        tbl->it_type = TCE_PCI;
 }
 
-static void pnv_pci_dma_dev_setup(struct pci_dev *pdev)
+void pnv_pci_dma_dev_setup(struct pci_dev *pdev)
 {
        struct pci_controller *hose = pci_bus_to_host(pdev->bus);
        struct pnv_phb *phb = hose->private_data;
@@ -689,37 +768,33 @@ static void pnv_pci_dma_dev_setup(struct pci_dev *pdev)
                phb->dma_dev_setup(phb, pdev);
 }
 
-int pnv_pci_dma_set_mask(struct pci_dev *pdev, u64 dma_mask)
+void pnv_pci_dma_bus_setup(struct pci_bus *bus)
 {
-       struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+       struct pci_controller *hose = bus->sysdata;
        struct pnv_phb *phb = hose->private_data;
+       struct pnv_ioda_pe *pe;
 
-       if (phb && phb->dma_set_mask)
-               return phb->dma_set_mask(phb, pdev, dma_mask);
-       return __dma_set_mask(&pdev->dev, dma_mask);
-}
-
-u64 pnv_pci_dma_get_required_mask(struct pci_dev *pdev)
-{
-       struct pci_controller *hose = pci_bus_to_host(pdev->bus);
-       struct pnv_phb *phb = hose->private_data;
+       list_for_each_entry(pe, &phb->ioda.pe_list, list) {
+               if (!(pe->flags & (PNV_IODA_PE_BUS | PNV_IODA_PE_BUS_ALL)))
+                       continue;
 
-       if (phb && phb->dma_get_required_mask)
-               return phb->dma_get_required_mask(phb, pdev);
+               if (!pe->pbus)
+                       continue;
 
-       return __dma_get_required_mask(&pdev->dev);
+               if (bus->number == ((pe->rid >> 8) & 0xFF)) {
+                       pe->pbus = bus;
+                       break;
+               }
+       }
 }
 
 void pnv_pci_shutdown(void)
 {
        struct pci_controller *hose;
 
-       list_for_each_entry(hose, &hose_list, list_node) {
-               struct pnv_phb *phb = hose->private_data;
-
-               if (phb && phb->shutdown)
-                       phb->shutdown(phb);
-       }
+       list_for_each_entry(hose, &hose_list, list_node)
+               if (hose->controller_ops.shutdown)
+                       hose->controller_ops.shutdown(hose);
 }
 
 /* Fixup wrong class code in p7ioc and p8 root complex */
@@ -762,22 +837,7 @@ void __init pnv_pci_init(void)
        pci_devs_phb_init();
 
        /* Configure IOMMU DMA hooks */
-       ppc_md.tce_build = pnv_tce_build_vm;
-       ppc_md.tce_free = pnv_tce_free_vm;
-       ppc_md.tce_build_rm = pnv_tce_build_rm;
-       ppc_md.tce_free_rm = pnv_tce_free_rm;
-       ppc_md.tce_get = pnv_tce_get;
        set_pci_dma_ops(&dma_iommu_ops);
-
-       /* Configure MSIs */
-#ifdef CONFIG_PCI_MSI
-       ppc_md.setup_msi_irqs = pnv_setup_msi_irqs;
-       ppc_md.teardown_msi_irqs = pnv_teardown_msi_irqs;
-#endif
 }
 
 machine_subsys_initcall_sync(powernv, tce_iommu_bus_notifier_init);
-
-struct pci_controller_ops pnv_pci_controller_ops = {
-       .dma_dev_setup = pnv_pci_dma_dev_setup,
-};