Hi Marc,
On 2014/11/24 22:35, Marc Zyngier wrote:

> Now, the bit of code that allow us to use the ITS as a MSI controller.
> Both MSI and MSI-X are supported.
> 
> Signed-off-by: Marc Zyngier <marc.zyng...@arm.com>
> ---
>  drivers/irqchip/irq-gic-v3-its.c   | 176 
> +++++++++++++++++++++++++++++++++++++
>  include/linux/irqchip/arm-gic-v3.h |   6 ++
>  2 files changed, 182 insertions(+)
> 
> diff --git a/drivers/irqchip/irq-gic-v3-its.c 
> b/drivers/irqchip/irq-gic-v3-its.c
> index d687fd4..532c6df 100644
> --- a/drivers/irqchip/irq-gic-v3-its.c
> +++ b/drivers/irqchip/irq-gic-v3-its.c
> @@ -587,12 +587,47 @@ static int its_set_affinity(struct irq_data *d, const 
> struct cpumask *mask_val,
>       return IRQ_SET_MASK_OK_DONE;
>  }
>  
> +static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
> +{
> +     struct its_device *its_dev = irq_data_get_irq_chip_data(d);
> +     struct its_node *its;
> +     u64 addr;
> +
> +     its = its_dev->its;
> +     addr = its->phys_base + GITS_TRANSLATER;
> +
> +     msg->address_lo         = addr & ((1UL << 32) - 1);
> +     msg->address_hi         = addr >> 32;
> +     msg->data               = its_get_event_id(d);
> +}
> +
>  static struct irq_chip its_irq_chip = {
>       .name                   = "ITS",
>       .irq_mask               = its_mask_irq,
>       .irq_unmask             = its_unmask_irq,
>       .irq_eoi                = its_eoi_irq,
>       .irq_set_affinity       = its_set_affinity,
> +     .irq_compose_msi_msg    = its_irq_compose_msi_msg,
> +};
> +
> +static void its_mask_msi_irq(struct irq_data *d)
> +{
> +     pci_msi_mask_irq(d);
> +     irq_chip_mask_parent(d);
> +}
> +
> +static void its_unmask_msi_irq(struct irq_data *d)
> +{
> +     pci_msi_unmask_irq(d);
> +     irq_chip_unmask_parent(d);
> +}
> +
> +static struct irq_chip its_msi_irq_chip = {
> +     .name                   = "ITS-MSI",
> +     .irq_unmask             = its_unmask_msi_irq,
> +     .irq_mask               = its_mask_msi_irq,
> +     .irq_eoi                = irq_chip_eoi_parent,
> +     .irq_write_msi_msg      = pci_msi_domain_write_msg,
>  };
>  
>  /*
> @@ -1055,3 +1090,144 @@ static void its_free_device(struct its_device 
> *its_dev)
>       kfree(its_dev->itt);
>       kfree(its_dev);
>  }
> +
> +static int its_alloc_device_irq(struct its_device *dev, irq_hw_number_t 
> *hwirq)
> +{
> +     int idx;
> +
> +     idx = find_first_zero_bit(dev->lpi_map, dev->nr_lpis);
> +     if (idx == dev->nr_lpis)
> +             return -ENOSPC;
> +
> +     *hwirq = dev->lpi_base + idx;
> +     set_bit(idx, dev->lpi_map);
> +
> +     /* Map the GIC irq ID to the device */
> +     its_send_mapvi(dev, *hwirq, idx);

It would be better if we do hardware-level initialization in 
domain.{activate,deactivate}.

> +
> +     return 0;
> +}
> +
> +static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
> +                        int nvec, msi_alloc_info_t *info)
> +{
> +     struct pci_dev *pdev;
> +     struct its_node *its;
> +     u32 dev_id;
> +     struct its_device *its_dev;
> +
> +     if (!dev_is_pci(dev))
> +             return -EINVAL;
> +
> +     pdev = to_pci_dev(dev);
> +     dev_id = PCI_DEVID(pdev->bus->number, pdev->devfn);
> +     its = domain->parent->host_data;
> +
> +     its_dev = its_find_device(its, dev_id);
> +     if (WARN_ON(its_dev))
> +             return -EINVAL;
> +
> +     its_dev = its_create_device(its, dev_id, nvec);
> +     if (!its_dev)
> +             return -ENOMEM;
> +
> +     dev_dbg(&pdev->dev, "ITT %d entries, %d bits\n", nvec, ilog2(nvec));
> +
> +     info->scratchpad[0].ptr = its_dev;
> +     info->scratchpad[1].ptr = dev;
> +     return 0;
> +}
> +
> +static struct msi_domain_ops its_pci_msi_ops = {
> +     .msi_prepare    = its_msi_prepare,
> +};
> +
> +static struct msi_domain_info its_pci_msi_domain_info = {
> +     .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX),
> +     .ops    = &its_pci_msi_ops,
> +     .chip   = &its_msi_irq_chip,
> +};
> +
> +static int its_irq_gic_domain_alloc(struct irq_domain *domain,
> +                                 unsigned int virq,
> +                                 irq_hw_number_t hwirq)
> +{
> +     struct of_phandle_args args;
> +
> +     args.np = domain->parent->of_node;
> +     args.args_count = 3;
> +     args.args[0] = GIC_IRQ_TYPE_LPI;
> +     args.args[1] = hwirq;
> +     args.args[2] = IRQ_TYPE_EDGE_RISING;
> +
> +     return irq_domain_alloc_irqs_parent(domain, virq, 1, &args);
> +}
> +
> +static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +                             unsigned int nr_irqs, void *args)
> +{
> +     msi_alloc_info_t *info = args;
> +     struct its_device *its_dev = info->scratchpad[0].ptr;
> +     irq_hw_number_t hwirq;
> +     int err;
> +     int i;
> +
> +     for (i = 0; i < nr_irqs; i++) {
> +             err = its_alloc_device_irq(its_dev, &hwirq);
> +             if (err)
> +                     return err;
> +
> +             err = its_irq_gic_domain_alloc(domain, virq + i, hwirq);
> +             if (err)
> +                     return err;
> +
> +             irq_domain_set_hwirq_and_chip(domain, virq + i,
> +                                           hwirq, &its_irq_chip, its_dev);
> +             dev_dbg(info->scratchpad[1].ptr, "ID:%d pID:%d vID:%d\n",
> +                     (int)(hwirq - its_dev->lpi_base), (int)hwirq, virq + i);
> +     }
> +
> +     return 0;
> +}
> +
> +static void its_irq_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 its_device *its_dev = irq_data_get_irq_chip_data(d);
> +     int i;
> +
> +     for (i = 0; i < nr_irqs; i++) {
> +             struct irq_data *data = irq_domain_get_irq_data(domain,
> +                                                             virq + i);
> +             int event = its_get_event_id(data);
> +
> +             /* Stop the delivery of interrupts */
> +             its_send_discard(its_dev, event);
> +
> +             /* Mark interrupt index as unused */
> +             clear_bit(event, its_dev->lpi_map);
> +
> +             /* Nuke the entry in the domain */
> +             irq_domain_reset_irq_data(d);

I think you mean "data" here, instead of "d"?

Regards,
        Abel

> +     }
> +
> +     /* If all interrupts have been freed, start mopping the floor */
> +     if (bitmap_empty(its_dev->lpi_map, its_dev->nr_lpis)) {
> +             its_lpi_free(its_dev->lpi_map,
> +                          its_dev->lpi_base,
> +                          its_dev->nr_lpis);
> +
> +             /* Unmap device/itt */
> +             its_send_mapd(its_dev, 0);
> +             its_free_device(its_dev);
> +     }
> +
> +     irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops its_domain_ops = {
> +     .alloc                  = its_irq_domain_alloc,
> +     .free                   = its_irq_domain_free,
> +};
> diff --git a/include/linux/irqchip/arm-gic-v3.h 
> b/include/linux/irqchip/arm-gic-v3.h
> index 21c9d70..0ed30d7 100644
> --- a/include/linux/irqchip/arm-gic-v3.h
> +++ b/include/linux/irqchip/arm-gic-v3.h
> @@ -295,6 +295,12 @@
>  
>  #include <linux/stringify.h>
>  
> +/*
> + * We need a value to serve as a irq-type for LPIs. Choose one that will
> + * hopefully pique the interest of the reviewer.
> + */
> +#define GIC_IRQ_TYPE_LPI             0xa110c8ed
> +
>  struct rdists {
>       struct {
>               void __iomem    *rd_base;



--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to