Merge tag 'pci-v4.17-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaa...
[muen/linux.git] / drivers / pci / dwc / pcie-designware-host.c
index dc9303a..6c40907 100644 (file)
@@ -8,6 +8,7 @@
  * Author: Jingoo Han <jg1.han@samsung.com>
  */
 
+#include <linux/irqchip/chained_irq.h>
 #include <linux/irqdomain.h>
 #include <linux/of_address.h>
 #include <linux/of_pci.h>
@@ -42,22 +43,46 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
        return dw_pcie_write(pci->dbi_base + where, size, val);
 }
 
-static struct irq_chip dw_msi_irq_chip = {
+static void dw_msi_ack_irq(struct irq_data *d)
+{
+       irq_chip_ack_parent(d);
+}
+
+static void dw_msi_mask_irq(struct irq_data *d)
+{
+       pci_msi_mask_irq(d);
+       irq_chip_mask_parent(d);
+}
+
+static void dw_msi_unmask_irq(struct irq_data *d)
+{
+       pci_msi_unmask_irq(d);
+       irq_chip_unmask_parent(d);
+}
+
+static struct irq_chip dw_pcie_msi_irq_chip = {
        .name = "PCI-MSI",
-       .irq_enable = pci_msi_unmask_irq,
-       .irq_disable = pci_msi_mask_irq,
-       .irq_mask = pci_msi_mask_irq,
-       .irq_unmask = pci_msi_unmask_irq,
+       .irq_ack = dw_msi_ack_irq,
+       .irq_mask = dw_msi_mask_irq,
+       .irq_unmask = dw_msi_unmask_irq,
+};
+
+static struct msi_domain_info dw_pcie_msi_domain_info = {
+       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                  MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI),
+       .chip   = &dw_pcie_msi_irq_chip,
 };
 
 /* MSI int handler */
 irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)
 {
-       u32 val;
        int i, pos, irq;
+       u32 val, num_ctrls;
        irqreturn_t ret = IRQ_NONE;
 
-       for (i = 0; i < MAX_MSI_CTRLS; i++) {
+       num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL;
+
+       for (i = 0; i < num_ctrls; i++) {
                dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4,
                                    &val);
                if (!val)
@@ -78,206 +103,216 @@ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)
        return ret;
 }
 
-void dw_pcie_msi_init(struct pcie_port *pp)
+/* Chained MSI interrupt service routine */
+static void dw_chained_msi_isr(struct irq_desc *desc)
 {
-       struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
-       struct device *dev = pci->dev;
-       struct page *page;
-       u64 msi_target;
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct pcie_port *pp;
 
-       page = alloc_page(GFP_KERNEL);
-       pp->msi_data = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE);
-       if (dma_mapping_error(dev, pp->msi_data)) {
-               dev_err(dev, "failed to map MSI data\n");
-               __free_page(page);
-               return;
-       }
-       msi_target = (u64)pp->msi_data;
+       chained_irq_enter(chip, desc);
 
-       /* program the msi_data */
-       dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4,
-                           (u32)(msi_target & 0xffffffff));
-       dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4,
-                           (u32)(msi_target >> 32 & 0xffffffff));
+       pp = irq_desc_get_handler_data(desc);
+       dw_handle_msi_irq(pp);
+
+       chained_irq_exit(chip, desc);
 }
 
-static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
+static void dw_pci_setup_msi_msg(struct irq_data *data, struct msi_msg *msg)
 {
-       unsigned int res, bit, val;
+       struct pcie_port *pp = irq_data_get_irq_chip_data(data);
+       struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+       u64 msi_target;
 
-       res = (irq / 32) * 12;
-       bit = irq % 32;
-       dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val);
-       val &= ~(1 << bit);
-       dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val);
-}
+       if (pp->ops->get_msi_addr)
+               msi_target = pp->ops->get_msi_addr(pp);
+       else
+               msi_target = (u64)pp->msi_data;
 
-static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base,
-                           unsigned int nvec, unsigned int pos)
-{
-       unsigned int i;
-
-       for (i = 0; i < nvec; i++) {
-               irq_set_msi_desc_off(irq_base, i, NULL);
-               /* Disable corresponding interrupt on MSI controller */
-               if (pp->ops->msi_clear_irq)
-                       pp->ops->msi_clear_irq(pp, pos + i);
-               else
-                       dw_pcie_msi_clear_irq(pp, pos + i);
-       }
+       msg->address_lo = lower_32_bits(msi_target);
+       msg->address_hi = upper_32_bits(msi_target);
 
-       bitmap_release_region(pp->msi_irq_in_use, pos, order_base_2(nvec));
+       if (pp->ops->get_msi_data)
+               msg->data = pp->ops->get_msi_data(pp, data->hwirq);
+       else
+               msg->data = data->hwirq;
+
+       dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n",
+               (int)data->hwirq, msg->address_hi, msg->address_lo);
 }
 
-static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
+static int dw_pci_msi_set_affinity(struct irq_data *irq_data,
+                                  const struct cpumask *mask, bool force)
 {
-       unsigned int res, bit, val;
-
-       res = (irq / 32) * 12;
-       bit = irq % 32;
-       dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val);
-       val |= 1 << bit;
-       dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val);
+       return -EINVAL;
 }
 
-static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos)
+static void dw_pci_bottom_mask(struct irq_data *data)
 {
-       int irq, pos0, i;
-       struct pcie_port *pp;
-
-       pp = (struct pcie_port *)msi_desc_to_pci_sysdata(desc);
-       pos0 = bitmap_find_free_region(pp->msi_irq_in_use, MAX_MSI_IRQS,
-                                      order_base_2(no_irqs));
-       if (pos0 < 0)
-               goto no_valid_irq;
+       struct pcie_port *pp = irq_data_get_irq_chip_data(data);
+       unsigned int res, bit, ctrl;
+       unsigned long flags;
 
-       irq = irq_find_mapping(pp->irq_domain, pos0);
-       if (!irq)
-               goto no_valid_irq;
+       raw_spin_lock_irqsave(&pp->lock, flags);
 
-       /*
-        * irq_create_mapping (called from dw_pcie_host_init) pre-allocates
-        * descs so there is no need to allocate descs here. We can therefore
-        * assume that if irq_find_mapping above returns non-zero, then the
-        * descs are also successfully allocated.
-        */
+       if (pp->ops->msi_clear_irq) {
+               pp->ops->msi_clear_irq(pp, data->hwirq);
+       } else {
+               ctrl = data->hwirq / 32;
+               res = ctrl * 12;
+               bit = data->hwirq % 32;
 
-       for (i = 0; i < no_irqs; i++) {
-               if (irq_set_msi_desc_off(irq, i, desc) != 0) {
-                       clear_irq_range(pp, irq, i, pos0);
-                       goto no_valid_irq;
-               }
-               /*Enable corresponding interrupt in MSI interrupt controller */
-               if (pp->ops->msi_set_irq)
-                       pp->ops->msi_set_irq(pp, pos0 + i);
-               else
-                       dw_pcie_msi_set_irq(pp, pos0 + i);
+               pp->irq_status[ctrl] &= ~(1 << bit);
+               dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4,
+                                   pp->irq_status[ctrl]);
        }
 
-       *pos = pos0;
-       desc->nvec_used = no_irqs;
-       desc->msi_attrib.multiple = order_base_2(no_irqs);
-
-       return irq;
-
-no_valid_irq:
-       *pos = pos0;
-       return -ENOSPC;
+       raw_spin_unlock_irqrestore(&pp->lock, flags);
 }
 
-static void dw_msi_setup_msg(struct pcie_port *pp, unsigned int irq, u32 pos)
+static void dw_pci_bottom_unmask(struct irq_data *data)
 {
-       struct msi_msg msg;
-       u64 msi_target;
+       struct pcie_port *pp = irq_data_get_irq_chip_data(data);
+       unsigned int res, bit, ctrl;
+       unsigned long flags;
 
-       if (pp->ops->get_msi_addr)
-               msi_target = pp->ops->get_msi_addr(pp);
-       else
-               msi_target = (u64)pp->msi_data;
+       raw_spin_lock_irqsave(&pp->lock, flags);
 
-       msg.address_lo = (u32)(msi_target & 0xffffffff);
-       msg.address_hi = (u32)(msi_target >> 32 & 0xffffffff);
+       if (pp->ops->msi_set_irq) {
+               pp->ops->msi_set_irq(pp, data->hwirq);
+       } else {
+               ctrl = data->hwirq / 32;
+               res = ctrl * 12;
+               bit = data->hwirq % 32;
 
-       if (pp->ops->get_msi_data)
-               msg.data = pp->ops->get_msi_data(pp, pos);
-       else
-               msg.data = pos;
+               pp->irq_status[ctrl] |= 1 << bit;
+               dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4,
+                                   pp->irq_status[ctrl]);
+       }
 
-       pci_write_msi_msg(irq, &msg);
+       raw_spin_unlock_irqrestore(&pp->lock, flags);
 }
 
-static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev,
-                           struct msi_desc *desc)
+static void dw_pci_bottom_ack(struct irq_data *d)
 {
-       int irq, pos;
-       struct pcie_port *pp = pdev->bus->sysdata;
-
-       if (desc->msi_attrib.is_msix)
-               return -EINVAL;
-
-       irq = assign_irq(1, desc, &pos);
-       if (irq < 0)
-               return irq;
+       struct msi_desc *msi = irq_data_get_msi_desc(d);
+       struct pcie_port *pp;
 
-       dw_msi_setup_msg(pp, irq, pos);
+       pp = msi_desc_to_pci_sysdata(msi);
 
-       return 0;
+       if (pp->ops->msi_irq_ack)
+               pp->ops->msi_irq_ack(d->hwirq, pp);
 }
 
-static int dw_msi_setup_irqs(struct msi_controller *chip, struct pci_dev *pdev,
-                            int nvec, int type)
+static struct irq_chip dw_pci_msi_bottom_irq_chip = {
+       .name = "DWPCI-MSI",
+       .irq_ack = dw_pci_bottom_ack,
+       .irq_compose_msi_msg = dw_pci_setup_msi_msg,
+       .irq_set_affinity = dw_pci_msi_set_affinity,
+       .irq_mask = dw_pci_bottom_mask,
+       .irq_unmask = dw_pci_bottom_unmask,
+};
+
+static int dw_pcie_irq_domain_alloc(struct irq_domain *domain,
+                                   unsigned int virq, unsigned int nr_irqs,
+                                   void *args)
 {
-#ifdef CONFIG_PCI_MSI
-       int irq, pos;
-       struct msi_desc *desc;
-       struct pcie_port *pp = pdev->bus->sysdata;
+       struct pcie_port *pp = domain->host_data;
+       unsigned long flags;
+       u32 i;
+       int bit;
+
+       raw_spin_lock_irqsave(&pp->lock, flags);
 
-       /* MSI-X interrupts are not supported */
-       if (type == PCI_CAP_ID_MSIX)
-               return -EINVAL;
+       bit = bitmap_find_free_region(pp->msi_irq_in_use, pp->num_vectors,
+                                     order_base_2(nr_irqs));
 
-       WARN_ON(!list_is_singular(&pdev->dev.msi_list));
-       desc = list_entry(pdev->dev.msi_list.next, struct msi_desc, list);
+       raw_spin_unlock_irqrestore(&pp->lock, flags);
 
-       irq = assign_irq(nvec, desc, &pos);
-       if (irq < 0)
-               return irq;
+       if (bit < 0)
+               return -ENOSPC;
 
-       dw_msi_setup_msg(pp, irq, pos);
+       for (i = 0; i < nr_irqs; i++)
+               irq_domain_set_info(domain, virq + i, bit + i,
+                                   &dw_pci_msi_bottom_irq_chip,
+                                   pp, handle_edge_irq,
+                                   NULL, NULL);
 
        return 0;
-#else
-       return -EINVAL;
-#endif
 }
 
-static void dw_msi_teardown_irq(struct msi_controller *chip, unsigned int irq)
+static void dw_pcie_irq_domain_free(struct irq_domain *domain,
+                                   unsigned int virq, unsigned int nr_irqs)
 {
-       struct irq_data *data = irq_get_irq_data(irq);
-       struct msi_desc *msi = irq_data_get_msi_desc(data);
-       struct pcie_port *pp = (struct pcie_port *)msi_desc_to_pci_sysdata(msi);
-
-       clear_irq_range(pp, irq, 1, data->hwirq);
+       struct irq_data *data = irq_domain_get_irq_data(domain, virq);
+       struct pcie_port *pp = irq_data_get_irq_chip_data(data);
+       unsigned long flags;
+
+       raw_spin_lock_irqsave(&pp->lock, flags);
+       bitmap_release_region(pp->msi_irq_in_use, data->hwirq,
+                             order_base_2(nr_irqs));
+       raw_spin_unlock_irqrestore(&pp->lock, flags);
 }
 
-static struct msi_controller dw_pcie_msi_chip = {
-       .setup_irq = dw_msi_setup_irq,
-       .setup_irqs = dw_msi_setup_irqs,
-       .teardown_irq = dw_msi_teardown_irq,
+static const struct irq_domain_ops dw_pcie_msi_domain_ops = {
+       .alloc  = dw_pcie_irq_domain_alloc,
+       .free   = dw_pcie_irq_domain_free,
 };
 
-static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
-                          irq_hw_number_t hwirq)
+int dw_pcie_allocate_domains(struct pcie_port *pp)
 {
-       irq_set_chip_and_handler(irq, &dw_msi_irq_chip, handle_simple_irq);
-       irq_set_chip_data(irq, domain->host_data);
+       struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+       struct fwnode_handle *fwnode = of_node_to_fwnode(pci->dev->of_node);
+
+       pp->irq_domain = irq_domain_create_linear(fwnode, pp->num_vectors,
+                                              &dw_pcie_msi_domain_ops, pp);
+       if (!pp->irq_domain) {
+               dev_err(pci->dev, "failed to create IRQ domain\n");
+               return -ENOMEM;
+       }
+
+       pp->msi_domain = pci_msi_create_irq_domain(fwnode,
+                                                  &dw_pcie_msi_domain_info,
+                                                  pp->irq_domain);
+       if (!pp->msi_domain) {
+               dev_err(pci->dev, "failed to create MSI domain\n");
+               irq_domain_remove(pp->irq_domain);
+               return -ENOMEM;
+       }
 
        return 0;
 }
 
-static const struct irq_domain_ops msi_domain_ops = {
-       .map = dw_pcie_msi_map,
-};
+void dw_pcie_free_msi(struct pcie_port *pp)
+{
+       irq_set_chained_handler(pp->msi_irq, NULL);
+       irq_set_handler_data(pp->msi_irq, NULL);
+
+       irq_domain_remove(pp->msi_domain);
+       irq_domain_remove(pp->irq_domain);
+}
+
+void dw_pcie_msi_init(struct pcie_port *pp)
+{
+       struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+       struct device *dev = pci->dev;
+       struct page *page;
+       u64 msi_target;
+
+       page = alloc_page(GFP_KERNEL);
+       pp->msi_data = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE);
+       if (dma_mapping_error(dev, pp->msi_data)) {
+               dev_err(dev, "failed to map MSI data\n");
+               __free_page(page);
+               return;
+       }
+       msi_target = (u64)pp->msi_data;
+
+       /* program the msi_data */
+       dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4,
+                           lower_32_bits(msi_target));
+       dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4,
+                           upper_32_bits(msi_target));
+}
 
 int dw_pcie_host_init(struct pcie_port *pp)
 {
@@ -285,11 +320,13 @@ int dw_pcie_host_init(struct pcie_port *pp)
        struct device *dev = pci->dev;
        struct device_node *np = dev->of_node;
        struct platform_device *pdev = to_platform_device(dev);
+       struct resource_entry *win, *tmp;
        struct pci_bus *bus, *child;
        struct pci_host_bridge *bridge;
        struct resource *cfg_res;
-       int i, ret;
-       struct resource_entry *win, *tmp;
+       int ret;
+
+       raw_spin_lock_init(&pci->pp.lock);
 
        cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");
        if (cfg_res) {
@@ -388,20 +425,35 @@ int dw_pcie_host_init(struct pcie_port *pp)
                pci->num_viewport = 2;
 
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
-               if (!pp->ops->msi_host_init) {
-                       pp->irq_domain = irq_domain_add_linear(dev->of_node,
-                                               MAX_MSI_IRQS, &msi_domain_ops,
-                                               &dw_pcie_msi_chip);
-                       if (!pp->irq_domain) {
-                               dev_err(dev, "irq domain init failed\n");
-                               ret = -ENXIO;
+               /*
+                * If a specific SoC driver needs to change the
+                * default number of vectors, it needs to implement
+                * the set_num_vectors callback.
+                */
+               if (!pp->ops->set_num_vectors) {
+                       pp->num_vectors = MSI_DEF_NUM_VECTORS;
+               } else {
+                       pp->ops->set_num_vectors(pp);
+
+                       if (pp->num_vectors > MAX_MSI_IRQS ||
+                           pp->num_vectors == 0) {
+                               dev_err(dev,
+                                       "Invalid number of vectors\n");
                                goto error;
                        }
+               }
 
-                       for (i = 0; i < MAX_MSI_IRQS; i++)
-                               irq_create_mapping(pp->irq_domain, i);
+               if (!pp->ops->msi_host_init) {
+                       ret = dw_pcie_allocate_domains(pp);
+                       if (ret)
+                               goto error;
+
+                       if (pp->msi_irq)
+                               irq_set_chained_handler_and_data(pp->msi_irq,
+                                                           dw_chained_msi_isr,
+                                                           pp);
                } else {
-                       ret = pp->ops->msi_host_init(pp, &dw_pcie_msi_chip);
+                       ret = pp->ops->msi_host_init(pp);
                        if (ret < 0)
                                goto error;
                }
@@ -421,10 +473,6 @@ int dw_pcie_host_init(struct pcie_port *pp)
        bridge->ops = &dw_pcie_ops;
        bridge->map_irq = of_irq_parse_and_map_pci;
        bridge->swizzle_irq = pci_common_swizzle;
-       if (IS_ENABLED(CONFIG_PCI_MSI)) {
-               bridge->msi = &dw_pcie_msi_chip;
-               dw_pcie_msi_chip.dev = dev;
-       }
 
        ret = pci_scan_root_bus_bridge(bridge);
        if (ret)
@@ -593,11 +641,17 @@ static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci)
 
 void dw_pcie_setup_rc(struct pcie_port *pp)
 {
-       u32 val;
+       u32 val, ctrl, num_ctrls;
        struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
 
        dw_pcie_setup(pci);
 
+       num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL;
+
+       /* Initialize IRQ Status array */
+       for (ctrl = 0; ctrl < num_ctrls; ctrl++)
+               dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + (ctrl * 12), 4,
+                                   &pp->irq_status[ctrl]);
        /* setup RC BARs */
        dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004);
        dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000);