[PATCHv8 06/10] irqchip: armada-370-xp: implement MSI support

Gregory CLEMENT gregory.clement at free-electrons.com
Fri Aug 9 02:44:46 EDT 2013


On 09/08/2013 00:17, Thomas Petazzoni wrote:
> This commit introduces the support for the MSI interrupts in the
> armada-370-xp interrupt controller driver. It registers an MSI chip to
> the MSI chip registry, which will be used by the Marvell PCIe host
> controller driver.
> 
> The MSI interrupts use the 16 high doorbells, and are therefore
> notified using IRQ1 of the main interrupt controller.
> 
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
Acked-by: Gregory CLEMENT <gregory.clement at free-electrons.com>
> ---
>  .../devicetree/bindings/arm/armada-370-xp-mpic.txt |   3 +
>  drivers/irqchip/irq-armada-370-xp.c                | 182 ++++++++++++++++++++-
>  2 files changed, 184 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt b/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt
> index 61df564..d74091a 100644
> --- a/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt
> +++ b/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt
> @@ -4,6 +4,8 @@ Marvell Armada 370 and Armada XP Interrupt Controller
>  Required properties:
>  - compatible: Should be "marvell,mpic"
>  - interrupt-controller: Identifies the node as an interrupt controller.
> +- msi-controller: Identifies the node as an PCI Message Signaled
> +  Interrupt controller.
>  - #interrupt-cells: The number of cells to define the interrupts. Should be 1.
>    The cell is the IRQ number
>  
> @@ -24,6 +26,7 @@ Example:
>                #address-cells = <1>;
>                #size-cells = <1>;
>                interrupt-controller;
> +              msi-controller;
>                reg = <0xd0020a00 0x1d0>,
>                      <0xd0021070 0x58>;
>          };
> diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
> index 26adc74..433cc85 100644
> --- a/drivers/irqchip/irq-armada-370-xp.c
> +++ b/drivers/irqchip/irq-armada-370-xp.c
> @@ -21,7 +21,10 @@
>  #include <linux/io.h>
>  #include <linux/of_address.h>
>  #include <linux/of_irq.h>
> +#include <linux/of_pci.h>
>  #include <linux/irqdomain.h>
> +#include <linux/slab.h>
> +#include <linux/msi.h>
>  #include <asm/mach/arch.h>
>  #include <asm/exception.h>
>  #include <asm/smp_plat.h>
> @@ -51,12 +54,22 @@
>  #define IPI_DOORBELL_START                      (0)
>  #define IPI_DOORBELL_END                        (8)
>  #define IPI_DOORBELL_MASK                       0xFF
> +#define PCI_MSI_DOORBELL_START                  (16)
> +#define PCI_MSI_DOORBELL_NR                     (16)
> +#define PCI_MSI_DOORBELL_END                    (32)
> +#define PCI_MSI_DOORBELL_MASK                   0xFFFF0000
>  
>  static DEFINE_RAW_SPINLOCK(irq_controller_lock);
>  
>  static void __iomem *per_cpu_int_base;
>  static void __iomem *main_int_base;
>  static struct irq_domain *armada_370_xp_mpic_domain;
> +#ifdef CONFIG_PCI_MSI
> +static struct irq_domain *armada_370_xp_msi_domain;
> +static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
> +static DEFINE_MUTEX(msi_used_lock);
> +static phys_addr_t msi_doorbell_addr;
> +#endif
>  
>  /*
>   * In SMP mode:
> @@ -87,6 +100,144 @@ static void armada_370_xp_irq_unmask(struct irq_data *d)
>  				ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
>  }
>  
> +#ifdef CONFIG_PCI_MSI
> +
> +static int armada_370_xp_alloc_msi(void)
> +{
> +	int hwirq;
> +
> +	mutex_lock(&msi_used_lock);
> +	hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR);
> +	if (hwirq >= PCI_MSI_DOORBELL_NR)
> +		hwirq = -ENOSPC;
> +	else
> +		set_bit(hwirq, msi_used);
> +	mutex_unlock(&msi_used_lock);
> +
> +	return hwirq;
> +}
> +
> +static void armada_370_xp_free_msi(int hwirq)
> +{
> +	mutex_lock(&msi_used_lock);
> +	if (!test_bit(hwirq, msi_used))
> +		pr_err("trying to free unused MSI#%d\n", hwirq);
> +	else
> +		clear_bit(hwirq, msi_used);
> +	mutex_unlock(&msi_used_lock);
> +}
> +
> +static int armada_370_xp_setup_msi_irq(struct msi_chip *chip,
> +				       struct pci_dev *pdev,
> +				       struct msi_desc *desc)
> +{
> +	struct msi_msg msg;
> +	irq_hw_number_t hwirq;
> +	int virq;
> +
> +	hwirq = armada_370_xp_alloc_msi();
> +	if (hwirq < 0)
> +		return hwirq;
> +
> +	virq = irq_create_mapping(armada_370_xp_msi_domain, hwirq);
> +	if (!virq) {
> +		armada_370_xp_free_msi(hwirq);
> +		return -EINVAL;
> +	}
> +
> +	irq_set_msi_desc(virq, desc);
> +
> +	msg.address_lo = msi_doorbell_addr;
> +	msg.address_hi = 0;
> +	msg.data = 0xf00 | (hwirq + 16);
> +
> +	write_msi_msg(virq, &msg);
> +	return 0;
> +}
> +
> +static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip,
> +					   unsigned int irq)
> +{
> +	struct irq_data *d = irq_get_irq_data(irq);
> +	irq_dispose_mapping(irq);
> +	armada_370_xp_free_msi(d->hwirq);
> +}
> +
> +static struct irq_chip armada_370_xp_msi_irq_chip = {
> +	.name = "armada_370_xp_msi_irq",
> +	.irq_enable = unmask_msi_irq,
> +	.irq_disable = mask_msi_irq,
> +	.irq_mask = mask_msi_irq,
> +	.irq_unmask = unmask_msi_irq,
> +};
> +
> +static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq,
> +				 irq_hw_number_t hw)
> +{
> +	irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip,
> +				 handle_simple_irq);
> +	set_irq_flags(virq, IRQF_VALID);
> +
> +	return 0;
> +}
> +
> +static const struct irq_domain_ops armada_370_xp_msi_irq_ops = {
> +	.map = armada_370_xp_msi_map,
> +};
> +
> +static int armada_370_xp_msi_init(struct device_node *node,
> +				  phys_addr_t main_int_phys_base)
> +{
> +	struct msi_chip *msi_chip;
> +	u32 reg;
> +	int ret;
> +
> +	msi_doorbell_addr = main_int_phys_base +
> +		ARMADA_370_XP_SW_TRIG_INT_OFFS;
> +
> +	msi_chip = kzalloc(sizeof(*msi_chip), GFP_KERNEL);
> +	if (!msi_chip)
> +		return -ENOMEM;
> +
> +	msi_chip->setup_irq = armada_370_xp_setup_msi_irq;
> +	msi_chip->teardown_irq = armada_370_xp_teardown_msi_irq;
> +	msi_chip->of_node = node;
> +
> +	armada_370_xp_msi_domain =
> +		irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR,
> +				      &armada_370_xp_msi_irq_ops,
> +				      NULL);
> +	if (!armada_370_xp_msi_domain) {
> +		kfree(msi_chip);
> +		return -ENOMEM;
> +	}
> +
> +	ret = of_pci_msi_chip_add(msi_chip);
> +	if (ret < 0) {
> +		irq_domain_remove(armada_370_xp_msi_domain);
> +		kfree(msi_chip);
> +		return ret;
> +	}
> +
> +	reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS)
> +		| PCI_MSI_DOORBELL_MASK;
> +
> +	writel(reg, per_cpu_int_base +
> +	       ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
> +
> +	/* Unmask IPI interrupt */
> +	writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
> +
> +	return 0;
> +}
> +#else
> +static inline int armada_370_xp_msi_init(struct device_node *node,
> +					 phys_addr_t main_int_phys_base)
> +{
> +	return 0;
> +}
> +#endif
> +
>  #ifdef CONFIG_SMP
>  static int armada_xp_set_affinity(struct irq_data *d,
>  				  const struct cpumask *mask_val, bool force)
> @@ -214,12 +365,39 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
>  		if (irqnr > 1022)
>  			break;
>  
> -		if (irqnr > 0) {
> +		if (irqnr > 1) {
>  			irqnr =	irq_find_mapping(armada_370_xp_mpic_domain,
>  					irqnr);
>  			handle_IRQ(irqnr, regs);
>  			continue;
>  		}
> +
> +#ifdef CONFIG_PCI_MSI
> +		/* MSI handling */
> +		if (irqnr == 1) {
> +			u32 msimask, msinr;
> +
> +			msimask = readl_relaxed(per_cpu_int_base +
> +						ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
> +				& PCI_MSI_DOORBELL_MASK;
> +
> +			writel(~PCI_MSI_DOORBELL_MASK, per_cpu_int_base +
> +			       ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
> +
> +			for (msinr = PCI_MSI_DOORBELL_START;
> +			     msinr < PCI_MSI_DOORBELL_END; msinr++) {
> +				int irq;
> +
> +				if (!(msimask & BIT(msinr)))
> +					continue;
> +
> +				irq = irq_find_mapping(armada_370_xp_msi_domain,
> +						       msinr - 16);
> +				handle_IRQ(irq, regs);
> +			}
> +		}
> +#endif
> +
>  #ifdef CONFIG_SMP
>  		/* IPI Handling */
>  		if (irqnr == 0) {
> @@ -292,6 +470,8 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
>  
>  #endif
>  
> +	armada_370_xp_msi_init(node, main_int_res.start);
> +
>  	set_handle_irq(armada_370_xp_handle_irq);
>  
>  	return 0;
> 


-- 
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com



More information about the linux-arm-kernel mailing list