Re: [PATCH v5 3/3] PCI: mobiveil: Add MSI support for Mobiveil PCIe Host

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Re-sending the mail in Plain text mode.

Marc,

On Fri, Dec 15, 2017 at 9:43 PM, Marc Zyngier <marc.zyngier@xxxxxxx> wrote:
>
> On 13/12/17 16:08, Subrahmanya Lingappa wrote:
> > Adds MSI support for Mobiveil PCIe Host Bridge IP driver
> >
> > Signed-off-by: Subrahmanya Lingappa <l.subrahmanya@xxxxxxxxxxxxxx>
> > Cc: Bjorn Helgaas <bhelgaas@xxxxxxxxxx>
> > Cc: Lorenzo Pieralisi <lorenzo.pieralisi@xxxxxxx>
> > Cc: Marc Zyngier <marc.zyngier@xxxxxxx>
> > Cc: linux-pci@xxxxxxxxxxxxxxx
> > ---
> >  drivers/pci/host/pcie-mobiveil.c | 222 ++++++++++++++++++++++++++++++++++++++-
> >  1 file changed, 220 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/pci/host/pcie-mobiveil.c b/drivers/pci/host/pcie-mobiveil.c
> > index 8611aaa..39818d5 100644
> > --- a/drivers/pci/host/pcie-mobiveil.c
> > +++ b/drivers/pci/host/pcie-mobiveil.c
> > @@ -14,6 +14,7 @@
> >  #include <linux/irqdomain.h>
> >  #include <linux/kernel.h>
> >  #include <linux/module.h>
> > +#include <linux/msi.h>
> >  #include <linux/of_address.h>
> >  #include <linux/of_irq.h>
> >  #include <linux/of_platform.h>
> > @@ -55,6 +56,7 @@
> >  #define PAB_INTP_AMBA_MISC_ENB       0x0b0c
> >  #define PAB_INTP_AMBA_MISC_STAT      0x0b1c
> >  #define  PAB_INTP_INTX_MASK  0x1e0
> > +#define  PAB_INTP_MSI_MASK   0x8
> >
> >  #define PAB_AXI_AMAP_CTRL(win)       PAB_REG_ADDR_16(0x0ba0, win)
> >  #define  WIN_ENABLE_SHIFT    0
> > @@ -85,8 +87,19 @@
> >
> >  /* supported number of interrupts */
> >  #define PCI_NUM_INTX         4
> > +#define PCI_NUM_MSI          16
> >  #define PAB_INTA_POS         5
> >
> > +/* MSI registers */
> > +#define MSI_BASE_LO_OFFSET   0x04
> > +#define MSI_BASE_HI_OFFSET   0x08
> > +#define MSI_SIZE_OFFSET              0x0c
> > +#define MSI_ENABLE_OFFSET    0x14
> > +#define MSI_STATUS_OFFSET    0x18
> > +#define MSI_DATA_OFFSET              0x20
> > +#define MSI_ADDR_L_OFFSET    0x24
> > +#define MSI_ADDR_H_OFFSET    0x28
> > +
> >  /* outbound and inbound window definitions */
> >  #define WIN_NUM_0            0
> >  #define WIN_NUM_1            1
> > @@ -105,11 +118,22 @@
> >  #define UINT64_MAX           (u64)(~((u64)0))
> >  #endif
> >
> > +struct mobiveil_msi {                        /* MSI information */
> > +     struct mutex lock;              /* protect bitmap variable */
> > +     struct irq_domain *msi_domain;
> > +     struct irq_domain *dev_domain;
> > +     phys_addr_t msi_pages_phys;
> > +     int *msi_pages;
> > +     int num_of_vectors;
> > +     DECLARE_BITMAP(msi_irq_in_use, PCI_NUM_MSI);
> > +};
> > +
> >  struct mobiveil_pcie {
> >       struct platform_device *pdev;
> >       struct list_head resources;
> >       void __iomem *config_axi_slave_base;    /* endpoint config base */
> >       void __iomem *csr_axi_slave_base;       /* root port config base */
> > +     void __iomem *apb_csr_base;     /* MSI register base */
> >       struct irq_domain *intx_domain;
> >       int irq;
> >       int apio_wins;
> > @@ -118,6 +142,7 @@ struct mobiveil_pcie {
> >       int ib_wins_configured; /*  configured inbound windows */
> >       struct resource *ob_io_res;
> >       char root_bus_nr;
> > +     struct mobiveil_msi msi;
> >  };
> >
> >  static inline void csr_writel(struct mobiveil_pcie *pcie, const u32 value,
> > @@ -202,11 +227,18 @@ static void mobiveil_pcie_isr(struct irq_desc *desc)
> >       struct irq_chip *chip = irq_desc_get_chip(desc);
> >       struct mobiveil_pcie *pcie = irq_desc_get_handler_data(desc);
> >       struct device *dev = &pcie->pdev->dev;
> > -     u32 intr_status;
> > +     struct mobiveil_msi *msi = &pcie->msi;
> > +     u32 msi_data, msi_addr_lo, msi_addr_hi;
> > +     u32 intr_status, msi_status;
> >       unsigned long shifted_status;
> >       u32 bit, virq;
> >       u32 val, mask;
> >
> > +     /*
> > +      * The core provides a single interrupt for both INTx/MSI messages.
> > +      * So we'll read both INTx and MSI status
> > +      */
> > +
> >       chained_irq_enter(chip, desc);
> >
> >       /* read INTx status */
> > @@ -241,6 +273,41 @@ static void mobiveil_pcie_isr(struct irq_desc *desc)
> >               } while ((shifted_status >>  PAB_INTA_POS) != 0);
> >       }
> >
> > +     /* read extra MSI status register */
> > +     msi_status = readl(pcie->apb_csr_base + MSI_STATUS_OFFSET);
>
> You are using the non-relaxed accessors here, while the rest of the
> driver used the _relaxed version. Why is that so?
>
i
t was a slip, I will correct it to use relaxed order.

>
> > +
> > +     /* handle MSI interrupts */
> > +     if ((intr_status & PAB_INTP_MSI_MASK) || (msi_status & 1)) {
> > +             do {
> > +                     msi_data = readl(pcie->apb_csr_base + MSI_DATA_OFFSET);
> > +
> > +                     /*
> > +                      * MSI_STATUS_OFFSET gets updated to zero once we have
> > +                      * popped not only the data but also address from MSI
> > +                      * hardware FIFO.so keeping these following two dummy
> > +                      * reads.
> > +                      */
> > +                     msi_addr_lo = readl(pcie->apb_csr_base +
> > +                                     MSI_ADDR_L_OFFSET);
> > +                     msi_addr_hi = readl(pcie->apb_csr_base +
> > +                                     MSI_ADDR_H_OFFSET);
>
> Is that really valid? Don't you have to issue a 64bit access?
>
Seems like its valid, because its just a pair of DWORD

accesses to config register space to read MSI data captured inside
bridge hardware FIFO.
is there any reason you think its invalid?

>
> > +                     dev_dbg(dev,
> > +                             "MSI registers, data: %08x, addr: %08x:%08x\n",
> > +                             msi_data, msi_addr_hi, msi_addr_lo);
> > +
> > +                     if (msi_data) {
> > +                             virq = irq_find_mapping(msi->dev_domain,
> > +                                     msi_data);
> > +                             if (virq)
> > +                                     generic_handle_irq(virq);
> > +                     } else
> > +                             dev_err_ratelimited(dev, "MSI data null\n");
>
> Braces on both sides of the else statement.
>
> Also, why is msi_data==0 not valid? You can definitely allocate it from
> your bitmap, and I'm expecting that there is a strong correlation
> between what you read from MSI_DATA_OFFSET and the payload that the
> endpoint writes.
>
Agreed msi_data==0 should also be valid. will be fixed.

>
> > +
> > +                     msi_status = readl(pcie->apb_csr_base +
> > +                                     MSI_STATUS_OFFSET);
> > +             } while (msi_status & 1);
> > +     }
> > +
> >       csr_writel(pcie, intr_status, PAB_INTP_AMBA_MISC_STAT);
> >       chained_irq_exit(chip, desc);
> >  }
> > @@ -274,6 +341,12 @@ static int mobiveil_pcie_parse_dt(struct mobiveil_pcie *pcie)
> >       if (IS_ERR(pcie->csr_axi_slave_base))
> >               return PTR_ERR(pcie->csr_axi_slave_base);
> >
> > +     /* map MSI config resource */
> > +     res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apb_csr");
> > +     pcie->apb_csr_base = devm_pci_remap_cfg_resource(dev, res);
> > +     if (IS_ERR(pcie->apb_csr_base))
> > +             return PTR_ERR(pcie->apb_csr_base);
> > +
> >       /* read the number of windows requested */
> >       if (!pcie->apio_wins &&
> >               of_property_read_u32(node, "apio-wins", &pcie->apio_wins)) {
> > @@ -430,6 +503,27 @@ static int mobiveil_bringup_link(struct mobiveil_pcie *pcie)
> >       return -ETIMEDOUT;
> >  }
> >
> > +static void mobiveil_pcie_enable_msi(struct mobiveil_pcie *pcie)
> > +{
> > +     phys_addr_t msg_addr;
> > +     struct mobiveil_msi *msi = &pcie->msi;
> > +
> > +
> > +     pcie->msi.num_of_vectors = PCI_NUM_MSI;
> > +
> > +     msi->msi_pages = (void *)__get_free_pages(GFP_DMA, 0);
>
> That old trick again... Do you really need to carve out a RAM page for
> this? Can't you use just some dummy physical address instead? The base
> address of your PCIe RC, for example.
>
Due to the current hardware bug

we do need a real RAM location to write this MSI data into, as we cant
use any dummy address for now.

>
> > +     msg_addr = virt_to_phys((void *)msi->msi_pages);
> > +     msi->msi_pages_phys = (phys_addr_t)msg_addr;
> > +
> > +     writel_relaxed(lower_32_bits(msg_addr),
> > +             pcie->apb_csr_base + MSI_BASE_LO_OFFSET);
> > +     writel_relaxed(upper_32_bits(msg_addr),
> > +             pcie->apb_csr_base + MSI_BASE_HI_OFFSET);
> > +     writel_relaxed(4096, pcie->apb_csr_base + MSI_SIZE_OFFSET);
> > +     writel_relaxed(1,
> > +             pcie->apb_csr_base + MSI_ENABLE_OFFSET);
>
> nit: No need to break all these writes, each of them can fit on a single
> line.
>
> > +}
> > +
> >  static int mobiveil_host_init(struct mobiveil_pcie *pcie)
> >  {
> >       u32 value;
> > @@ -465,7 +559,8 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie)
> >               (1 << PEX_PIO_ENABLE_SHIFT),
> >               PAB_CTRL);
> >
> > -     csr_writel(pcie, PAB_INTP_INTX_MASK, PAB_INTP_AMBA_MISC_ENB);
> > +     csr_writel(pcie, (PAB_INTP_INTX_MASK | PAB_INTP_MSI_MASK),
> > +             PAB_INTP_AMBA_MISC_ENB);
> >
> >       /* program PIO Enable Bit to 1 and Config Window Enable Bit to 1 in
> >        * PAB_AXI_PIO_CTRL Register
> > @@ -503,6 +598,10 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie)
> >               }
> >       }
> >
> > +     /* setup MSI hardware registers */
> > +     if (IS_ENABLED(CONFIG_PCI_MSI))
>
> Your driver already depends on PCI_MSI_IRQ_DOMAIN, which depends on
> PCI_MSI. So this IS_ENABLED() is pointless.
>
as Lorenzo pointed out on the other thread, I will take out
PCI_MSI_IRQ_DOMAIN dependency.
So looks like better to follow other tested drivers and continue to keep it no ?

>
> > +             mobiveil_pcie_enable_msi(pcie);
> > +
> >       return err;
> >  }
> >
> > @@ -520,6 +619,118 @@ static int mobiveil_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
> >       .map = mobiveil_pcie_intx_map,
> >  };
> >
> > +static struct irq_chip mobiveil_msi_irq_chip = {
> > +     .name = "Mobiveil PCIe MSI",
> > +     .irq_mask = pci_msi_mask_irq,
> > +     .irq_unmask = pci_msi_unmask_irq,
> > +};
> > +
> > +static struct msi_domain_info mobiveil_msi_domain_info = {
> > +     .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> > +                  MSI_FLAG_PCI_MSIX),
> > +     .chip   = &mobiveil_msi_irq_chip,
> > +};
> > +
> > +static void mobiveil_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> > +{
> > +     struct mobiveil_pcie *pcie = irq_data_get_irq_chip_data(data);
> > +     phys_addr_t addr = virt_to_phys((void *)pcie->msi.msi_pages +
> > +                             (data->hwirq * sizeof(int)));
> > +
> > +     msg->address_lo = lower_32_bits(addr);
> > +     msg->address_hi = upper_32_bits(addr);
> > +     msg->data = data->hwirq;
> > +
> > +     dev_dbg(&pcie->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n",
> > +             (int)data->hwirq, msg->address_hi, msg->address_lo);
> > +}
> > +
> > +static int mobiveil_msi_set_affinity(struct irq_data *irq_data,
> > +             const struct cpumask *mask, bool force)
> > +{
> > +     return -EINVAL;
> > +}
> > +
> > +static struct irq_chip mobiveil_msi_bottom_irq_chip = {
> > +     .name                   = "Mobiveil MSI",
> > +     .irq_compose_msi_msg    = mobiveil_compose_msi_msg,
> > +     .irq_set_affinity       = mobiveil_msi_set_affinity,
> > +};
> > +
> > +static int mobiveil_irq_msi_domain_alloc(struct irq_domain *domain,
> > +             unsigned int virq, unsigned int nr_irqs, void *args)
> > +{
> > +     struct mobiveil_pcie *pcie = domain->host_data;
> > +     struct mobiveil_msi *msi = &pcie->msi;
> > +     unsigned long bit;
> > +
> > +     WARN_ON(nr_irqs != 1);
> > +     mutex_lock(&msi->lock);
> > +
> > +     bit = find_first_zero_bit(msi->msi_irq_in_use, msi->num_of_vectors);
> > +     if (bit >= msi->num_of_vectors) {
> > +             mutex_unlock(&msi->lock);
> > +             return -ENOSPC;
> > +     }
> > +
> > +     set_bit(bit, msi->msi_irq_in_use);
> > +
> > +     mutex_unlock(&msi->lock);
> > +
> > +     irq_domain_set_info(domain, virq, bit, &mobiveil_msi_bottom_irq_chip,
> > +                         domain->host_data, handle_simple_irq,
> > +                         NULL, NULL);
> > +     return 0;
> > +}
> > +
> > +static void mobiveil_irq_msi_domain_free(struct irq_domain *domain,
> > +             unsigned int virq, unsigned int nr_irqs)
> > +{
> > +     struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> > +     struct mobiveil_pcie *pcie = irq_data_get_irq_chip_data(d);
> > +     struct mobiveil_msi *msi = &pcie->msi;
> > +
> > +     mutex_lock(&msi->lock);
> > +
> > +     if (!test_bit(d->hwirq, msi->msi_irq_in_use)) {
> > +             dev_err(&pcie->pdev->dev, "trying to free unused MSI#%lu\n",
> > +                     d->hwirq);
> > +     } else {
> > +             __clear_bit(d->hwirq, msi->msi_irq_in_use);
> > +     }
> > +
> > +     mutex_unlock(&msi->lock);
> > +}
> > +
> > +static const struct irq_domain_ops msi_domain_ops = {
> > +     .alloc  = mobiveil_irq_msi_domain_alloc,
> > +     .free   = mobiveil_irq_msi_domain_free,
> > +};
> > +
> > +static int mobiveil_allocate_msi_domains(struct mobiveil_pcie *pcie)
> > +{
> > +     struct device *dev = &pcie->pdev->dev;
> > +     struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
> > +     struct mobiveil_msi *msi = &pcie->msi;
> > +
> > +     mutex_init(&pcie->msi.lock);
> > +     msi->dev_domain = irq_domain_add_linear(NULL, msi->num_of_vectors,
> > +                                          &msi_domain_ops, pcie);
> > +     if (!msi->dev_domain) {
> > +             dev_err(dev, "failed to create IRQ domain\n");
> > +             return -ENOMEM;
> > +     }
> > +
> > +     msi->msi_domain = pci_msi_create_irq_domain(fwnode,
> > +                             &mobiveil_msi_domain_info, msi->dev_domain);
> > +     if (!msi->msi_domain) {
> > +             dev_err(dev, "failed to create MSI domain\n");
> > +             irq_domain_remove(msi->dev_domain);
> > +             return -ENOMEM;
> > +     }
> > +     return 0;
> > +}
> > +
> >  static int mobiveil_pcie_init_irq_domain(struct mobiveil_pcie *pcie)
> >  {
> >       struct device *dev = &pcie->pdev->dev;
> > @@ -535,6 +746,13 @@ static int mobiveil_pcie_init_irq_domain(struct mobiveil_pcie *pcie)
> >               return -ENODEV;
> >       }
> >
> > +#ifdef CONFIG_PCI_MSI
>
> Useless #ifdef
>
> > +     /* setup MSI */
> > +     ret = mobiveil_allocate_msi_domains(pcie);
> > +     if (ret)
> > +             return ret;
> > +#endif
> > +
> >       return 0;
> >  }
> >
> >
>
> Now, there is something that I find a bit annoying: This code looks very
> similar to drivers/pci/host/pcie-altera-msi.c, to the extent that I
> suspect this is the same IP.
>
Its a different IP.

>
> I suggest you investigate whether you can reuse the existing code
> instead of adding yet another MSI driver to the kernel.
>
> Thanks,
>
>         M.
> --
> Jazz is not dead. It just smells funny...


Thanks,
Subrahmanya



[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux