diff mbox

[2/4] irqchip: irq-armada-370-xp: use the generic MSI infrastructure

Message ID 1450707546-15060-3-git-send-email-thomas.petazzoni@free-electrons.com (mailing list archive)
State New, archived
Headers show

Commit Message

Thomas Petazzoni Dec. 21, 2015, 2:19 p.m. UTC
This commit moves the irq-armada-370-xp driver from using the
PCI-specific MSI infrastructure to the generic MSI infrastructure, to
which drivers are progressively converted.

In this hardware, the MSI controller is directly bundled inside the
interrupt controller, so we have a single Device Tree node to which
multiple IRQ domaines are attached: the wired interrupt domain and the
MSI interrupt domain. In order to ensure that they can be
differentiated, we have to force the bus_token of the wired interrupt
domain to be DOMAIN_BUS_WIRED. The MSI domain bus_token is
automatically set to the appropriate value by
pci_msi_create_irq_domain().

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Suggested-by: Marc Zyngier <marc.zyngier@arm.com>
---
 drivers/irqchip/Kconfig             |   1 +
 drivers/irqchip/irq-armada-370-xp.c | 151 +++++++++++++++---------------------
 2 files changed, 65 insertions(+), 87 deletions(-)

Comments

Gregory CLEMENT Dec. 23, 2015, 11:37 a.m. UTC | #1
Hi Thomas,
 
 On lun., déc. 21 2015, Thomas Petazzoni <thomas.petazzoni@free-electrons.com> wrote:

> This commit moves the irq-armada-370-xp driver from using the
> PCI-specific MSI infrastructure to the generic MSI infrastructure, to
> which drivers are progressively converted.
>
> In this hardware, the MSI controller is directly bundled inside the
> interrupt controller, so we have a single Device Tree node to which
> multiple IRQ domaines are attached: the wired interrupt domain and the
> MSI interrupt domain. In order to ensure that they can be
> differentiated, we have to force the bus_token of the wired interrupt
> domain to be DOMAIN_BUS_WIRED. The MSI domain bus_token is
> automatically set to the appropriate value by
> pci_msi_create_irq_domain().

the way git generated the patch make it difficult to read, furthermore I
don't know too much about MSI. So for the bulk of this patch I trust you
and I let the irq experts comment if needed. I still have a minor remark,
see below:

>
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Suggested-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
>  drivers/irqchip/Kconfig             |   1 +
>  drivers/irqchip/irq-armada-370-xp.c | 151 +++++++++++++++---------------------
>  2 files changed, 65 insertions(+), 87 deletions(-)
>
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index ab672b0..8ccb60e 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -51,6 +51,7 @@ config ARMADA_370_XP_IRQ
>  	bool
>  	default y if ARCH_MVEBU
>  	select GENERIC_IRQ_CHIP
> +	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>  
>  config ATMEL_AIC_IRQ
>  	bool
> diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
> index 3f3a8c3..304166b 100644
> --- a/drivers/irqchip/irq-armada-370-xp.c
> +++ b/drivers/irqchip/irq-armada-370-xp.c
> @@ -71,6 +71,7 @@ static u32 doorbell_mask_reg;
>  static int parent_irq;
>  #ifdef CONFIG_PCI_MSI
>  static struct irq_domain *armada_370_xp_msi_domain;
> +static struct irq_domain *armada_370_xp_msi_inner_domain;
>  static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
>  static DEFINE_MUTEX(msi_used_lock);
>  static phys_addr_t msi_doorbell_addr;
> @@ -115,127 +116,102 @@ static void armada_370_xp_irq_unmask(struct irq_data *d)
>  
>  #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);
> +static struct irq_chip armada_370_xp_msi_irq_chip = {
> +	.name = "armada_370_xp_msi_irq",
> +	.irq_enable = pci_msi_unmask_irq,
> +	.irq_disable = pci_msi_mask_irq,
> +	.irq_mask = pci_msi_mask_irq,
> +	.irq_unmask = pci_msi_unmask_irq,
> +};
>  
> -	return hwirq;
> -}
> +static struct msi_domain_info armada_370_xp_msi_domain_info = {
> +	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +		   MSI_FLAG_MULTI_PCI_MSI),
> +	.chip	= &armada_370_xp_msi_irq_chip,
> +};
>  
> -static void armada_370_xp_free_msi(int hwirq)
> +static void armada_370_xp_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
>  {
> -	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);
> +	msg->address_lo = lower_32_bits(msi_doorbell_addr);
> +	msg->address_hi = upper_32_bits(msi_doorbell_addr);
> +	msg->data = 0xf00 | (data->hwirq + PCI_MSI_DOORBELL_START);

So here instead of using the 16 value you use PCI_MSI_DOORBELL_START
which seems better. What about using it also in
armada_370_xp_handle_msi_irq?

>  }
>  
> -static int armada_370_xp_setup_msi_irq(struct msi_controller *chip,
> -				       struct pci_dev *pdev,
> -				       struct msi_desc *desc)
> +static int armada_370_xp_msi_set_affinity(struct irq_data *irq_data,
> +					  const struct cpumask *mask, bool force)
>  {
> -	struct msi_msg msg;
> -	int virq, hwirq;
> +	 return -EINVAL;
> +}
>  
> -	/* We support MSI, but not MSI-X */
> -	if (desc->msi_attrib.is_msix)
> -		return -EINVAL;
> +static struct irq_chip armada_370_xp_msi_bottom_irq_chip = {
> +	.name			= "MPIC MSI",
> +	.irq_compose_msi_msg	= armada_370_xp_compose_msi_msg,
> +	.irq_set_affinity	= armada_370_xp_msi_set_affinity,
> +};
>  
> -	hwirq = armada_370_xp_alloc_msi();
> -	if (hwirq < 0)
> -		return hwirq;
> +static int armada_370_xp_msi_alloc(struct irq_domain *domain, unsigned int virq,
> +				   unsigned int nr_irqs, void *args)
> +{
> +	int hwirq;
>  
> -	virq = irq_create_mapping(armada_370_xp_msi_domain, hwirq);
> -	if (!virq) {
> -		armada_370_xp_free_msi(hwirq);
> -		return -EINVAL;
> +	mutex_lock(&msi_used_lock);
> +	hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR);
> +	if (hwirq >= PCI_MSI_DOORBELL_NR) {
> +		mutex_unlock(&msi_used_lock);
> +		return -ENOSPC;
>  	}
>  
> -	irq_set_msi_desc(virq, desc);
> +	set_bit(hwirq, msi_used);
> +	mutex_unlock(&msi_used_lock);
>  
> -	msg.address_lo = msi_doorbell_addr;
> -	msg.address_hi = 0;
> -	msg.data = 0xf00 | (hwirq + 16);
> +	irq_domain_set_info(domain, virq, hwirq, &armada_370_xp_msi_bottom_irq_chip,
> +			    domain->host_data, handle_simple_irq,
> +			    NULL, NULL);
>  
> -	pci_write_msi_msg(virq, &msg);
> -	return 0;
> +	return hwirq;
>  }
>  
> -static void armada_370_xp_teardown_msi_irq(struct msi_controller *chip,
> -					   unsigned int irq)
> +static void armada_370_xp_msi_free(struct irq_domain *domain,
> +				   unsigned int virq, unsigned int nr_irqs)
>  {
> -	struct irq_data *d = irq_get_irq_data(irq);
> -	unsigned long hwirq = d->hwirq;
> +	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>  
> -	irq_dispose_mapping(irq);
> -	armada_370_xp_free_msi(hwirq);
> -}
> -
> -static struct irq_chip armada_370_xp_msi_irq_chip = {
> -	.name = "armada_370_xp_msi_irq",
> -	.irq_enable = pci_msi_unmask_irq,
> -	.irq_disable = pci_msi_mask_irq,
> -	.irq_mask = pci_msi_mask_irq,
> -	.irq_unmask = pci_msi_unmask_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);
> -
> -	return 0;
> +	mutex_lock(&msi_used_lock);
> +	if (!test_bit(d->hwirq, msi_used))
> +		pr_err("trying to free unused MSI#%lu\n", d->hwirq);
> +	else
> +		clear_bit(d->hwirq, msi_used);
> +	mutex_unlock(&msi_used_lock);
>  }
>  
> -static const struct irq_domain_ops armada_370_xp_msi_irq_ops = {
> -	.map = armada_370_xp_msi_map,
> +static const struct irq_domain_ops armada_370_xp_msi_domain_ops = {
> +	.alloc	= armada_370_xp_msi_alloc,
> +	.free	= armada_370_xp_msi_free,
>  };
>  
>  static int armada_370_xp_msi_init(struct device_node *node,
>  				  phys_addr_t main_int_phys_base)
>  {
> -	struct msi_controller *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)
> +	armada_370_xp_msi_inner_domain =
> +		irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR,
> +				      &armada_370_xp_msi_domain_ops, NULL);
> +	if (!armada_370_xp_msi_inner_domain)
>  		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);
> +		pci_msi_create_irq_domain(of_node_to_fwnode(node),
> +					  &armada_370_xp_msi_domain_info,
> +					  armada_370_xp_msi_inner_domain);
>  	if (!armada_370_xp_msi_domain) {
> -		kfree(msi_chip);
> +		irq_domain_remove(armada_370_xp_msi_inner_domain);
>  		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;
>  
> @@ -427,12 +403,12 @@ static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained)
>  			continue;
>  
>  		if (is_chained) {
> -			irq = irq_find_mapping(armada_370_xp_msi_domain,
> +			irq = irq_find_mapping(armada_370_xp_msi_inner_domain,
>  					       msinr - 16);
Could we use also PCI_MSI_DOORBELL_START here?

>  			generic_handle_irq(irq);
>  		} else {
>  			irq = msinr - 16;
and here?

> -			handle_domain_irq(armada_370_xp_msi_domain,
> +			handle_domain_irq(armada_370_xp_msi_inner_domain,
>  					  irq, regs);
>  		}
>  	}
> @@ -604,6 +580,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
>  	armada_370_xp_mpic_domain =
>  		irq_domain_add_linear(node, nr_irqs,
>  				&armada_370_xp_mpic_irq_ops, NULL);
> +	armada_370_xp_mpic_domain->bus_token = DOMAIN_BUS_WIRED;
>  
>  	BUG_ON(!armada_370_xp_mpic_domain);
>  
> -- 
> 2.6.4
>
Thanks,

Gregory
Marc Zyngier Jan. 26, 2016, 2:12 p.m. UTC | #2
Hi Thomas,

I finally found some bandwidth to have a look at this. A few comments below:

On 21/12/15 14:19, Thomas Petazzoni wrote:
> This commit moves the irq-armada-370-xp driver from using the
> PCI-specific MSI infrastructure to the generic MSI infrastructure, to
> which drivers are progressively converted.
> 
> In this hardware, the MSI controller is directly bundled inside the
> interrupt controller, so we have a single Device Tree node to which
> multiple IRQ domaines are attached: the wired interrupt domain and the
> MSI interrupt domain. In order to ensure that they can be
> differentiated, we have to force the bus_token of the wired interrupt
> domain to be DOMAIN_BUS_WIRED. The MSI domain bus_token is
> automatically set to the appropriate value by
> pci_msi_create_irq_domain().
> 
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Suggested-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
>  drivers/irqchip/Kconfig             |   1 +
>  drivers/irqchip/irq-armada-370-xp.c | 151 +++++++++++++++---------------------
>  2 files changed, 65 insertions(+), 87 deletions(-)
> 
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index ab672b0..8ccb60e 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -51,6 +51,7 @@ config ARMADA_370_XP_IRQ
>  	bool
>  	default y if ARCH_MVEBU
>  	select GENERIC_IRQ_CHIP
> +	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
>  
>  config ATMEL_AIC_IRQ
>  	bool
> diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
> index 3f3a8c3..304166b 100644
> --- a/drivers/irqchip/irq-armada-370-xp.c
> +++ b/drivers/irqchip/irq-armada-370-xp.c
> @@ -71,6 +71,7 @@ static u32 doorbell_mask_reg;
>  static int parent_irq;
>  #ifdef CONFIG_PCI_MSI
>  static struct irq_domain *armada_370_xp_msi_domain;
> +static struct irq_domain *armada_370_xp_msi_inner_domain;
>  static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
>  static DEFINE_MUTEX(msi_used_lock);
>  static phys_addr_t msi_doorbell_addr;
> @@ -115,127 +116,102 @@ static void armada_370_xp_irq_unmask(struct irq_data *d)
>  
>  #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);
> +static struct irq_chip armada_370_xp_msi_irq_chip = {
> +	.name = "armada_370_xp_msi_irq",
> +	.irq_enable = pci_msi_unmask_irq,
> +	.irq_disable = pci_msi_mask_irq,
> +	.irq_mask = pci_msi_mask_irq,
> +	.irq_unmask = pci_msi_unmask_irq,

Having only mask/unmask ought to be enough.

> +};
>  
> -	return hwirq;
> -}
> +static struct msi_domain_info armada_370_xp_msi_domain_info = {
> +	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +		   MSI_FLAG_MULTI_PCI_MSI),

And not MSI-X? That seems rather strange (I'm pretty sure it just works).

> +	.chip	= &armada_370_xp_msi_irq_chip,
> +};
>  
> -static void armada_370_xp_free_msi(int hwirq)
> +static void armada_370_xp_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
>  {
> -	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);
> +	msg->address_lo = lower_32_bits(msi_doorbell_addr);
> +	msg->address_hi = upper_32_bits(msi_doorbell_addr);
> +	msg->data = 0xf00 | (data->hwirq + PCI_MSI_DOORBELL_START);
>  }
>  
> -static int armada_370_xp_setup_msi_irq(struct msi_controller *chip,
> -				       struct pci_dev *pdev,
> -				       struct msi_desc *desc)
> +static int armada_370_xp_msi_set_affinity(struct irq_data *irq_data,
> +					  const struct cpumask *mask, bool force)
>  {
> -	struct msi_msg msg;
> -	int virq, hwirq;
> +	 return -EINVAL;
> +}
>  
> -	/* We support MSI, but not MSI-X */
> -	if (desc->msi_attrib.is_msix)
> -		return -EINVAL;
> +static struct irq_chip armada_370_xp_msi_bottom_irq_chip = {
> +	.name			= "MPIC MSI",
> +	.irq_compose_msi_msg	= armada_370_xp_compose_msi_msg,
> +	.irq_set_affinity	= armada_370_xp_msi_set_affinity,
> +};
>  
> -	hwirq = armada_370_xp_alloc_msi();
> -	if (hwirq < 0)
> -		return hwirq;
> +static int armada_370_xp_msi_alloc(struct irq_domain *domain, unsigned int virq,
> +				   unsigned int nr_irqs, void *args)
> +{
> +	int hwirq;
>  
> -	virq = irq_create_mapping(armada_370_xp_msi_domain, hwirq);
> -	if (!virq) {
> -		armada_370_xp_free_msi(hwirq);
> -		return -EINVAL;
> +	mutex_lock(&msi_used_lock);
> +	hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR);
> +	if (hwirq >= PCI_MSI_DOORBELL_NR) {
> +		mutex_unlock(&msi_used_lock);
> +		return -ENOSPC;
>  	}
>  
> -	irq_set_msi_desc(virq, desc);
> +	set_bit(hwirq, msi_used);
> +	mutex_unlock(&msi_used_lock);
>  
> -	msg.address_lo = msi_doorbell_addr;
> -	msg.address_hi = 0;
> -	msg.data = 0xf00 | (hwirq + 16);
> +	irq_domain_set_info(domain, virq, hwirq, &armada_370_xp_msi_bottom_irq_chip,
> +			    domain->host_data, handle_simple_irq,
> +			    NULL, NULL);
>  
> -	pci_write_msi_msg(virq, &msg);
> -	return 0;
> +	return hwirq;

So you are allocating one bit at a time, irrespective of nr_irqs. This
implies that you can't support MULTI_MSI here (you'd need to guarantee a
contiguous allocation of nr_irqs bits). So either you amend your
allocator to deal with those (pretty easy), or you drop MULTI_MSI from
your msi_domain_info.

>  }
>  
> -static void armada_370_xp_teardown_msi_irq(struct msi_controller *chip,
> -					   unsigned int irq)
> +static void armada_370_xp_msi_free(struct irq_domain *domain,
> +				   unsigned int virq, unsigned int nr_irqs)
>  {
> -	struct irq_data *d = irq_get_irq_data(irq);
> -	unsigned long hwirq = d->hwirq;
> +	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>  
> -	irq_dispose_mapping(irq);
> -	armada_370_xp_free_msi(hwirq);
> -}
> -
> -static struct irq_chip armada_370_xp_msi_irq_chip = {
> -	.name = "armada_370_xp_msi_irq",
> -	.irq_enable = pci_msi_unmask_irq,
> -	.irq_disable = pci_msi_mask_irq,
> -	.irq_mask = pci_msi_mask_irq,
> -	.irq_unmask = pci_msi_unmask_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);
> -
> -	return 0;
> +	mutex_lock(&msi_used_lock);
> +	if (!test_bit(d->hwirq, msi_used))
> +		pr_err("trying to free unused MSI#%lu\n", d->hwirq);
> +	else
> +		clear_bit(d->hwirq, msi_used);
> +	mutex_unlock(&msi_used_lock);
>  }
>  
> -static const struct irq_domain_ops armada_370_xp_msi_irq_ops = {
> -	.map = armada_370_xp_msi_map,
> +static const struct irq_domain_ops armada_370_xp_msi_domain_ops = {
> +	.alloc	= armada_370_xp_msi_alloc,
> +	.free	= armada_370_xp_msi_free,
>  };
>  
>  static int armada_370_xp_msi_init(struct device_node *node,
>  				  phys_addr_t main_int_phys_base)
>  {
> -	struct msi_controller *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)
> +	armada_370_xp_msi_inner_domain =
> +		irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR,
> +				      &armada_370_xp_msi_domain_ops, NULL);

nit: Please keep the assignment on a single line.

> +	if (!armada_370_xp_msi_inner_domain)
>  		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);
> +		pci_msi_create_irq_domain(of_node_to_fwnode(node),
> +					  &armada_370_xp_msi_domain_info,
> +					  armada_370_xp_msi_inner_domain);

Same here.

>  	if (!armada_370_xp_msi_domain) {
> -		kfree(msi_chip);
> +		irq_domain_remove(armada_370_xp_msi_inner_domain);
>  		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;
>  
> @@ -427,12 +403,12 @@ static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained)
>  			continue;
>  
>  		if (is_chained) {
> -			irq = irq_find_mapping(armada_370_xp_msi_domain,
> +			irq = irq_find_mapping(armada_370_xp_msi_inner_domain,
>  					       msinr - 16);
>  			generic_handle_irq(irq);
>  		} else {
>  			irq = msinr - 16;
> -			handle_domain_irq(armada_370_xp_msi_domain,
> +			handle_domain_irq(armada_370_xp_msi_inner_domain,
>  					  irq, regs);
>  		}
>  	}
> @@ -604,6 +580,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
>  	armada_370_xp_mpic_domain =
>  		irq_domain_add_linear(node, nr_irqs,
>  				&armada_370_xp_mpic_irq_ops, NULL);
> +	armada_370_xp_mpic_domain->bus_token = DOMAIN_BUS_WIRED;
>  
>  	BUG_ON(!armada_370_xp_mpic_domain);

Given the assignment just above, this BUG_ON has become pretty useless...

Thanks,

	M.
Thomas Petazzoni Jan. 26, 2016, 3:41 p.m. UTC | #3
Gregory,

On Wed, 23 Dec 2015 12:37:43 +0100, Gregory CLEMENT wrote:

> > -	mutex_unlock(&msi_used_lock);
> > +	msg->address_lo = lower_32_bits(msi_doorbell_addr);
> > +	msg->address_hi = upper_32_bits(msi_doorbell_addr);
> > +	msg->data = 0xf00 | (data->hwirq + PCI_MSI_DOORBELL_START);
> 
> So here instead of using the 16 value you use PCI_MSI_DOORBELL_START
> which seems better. What about using it also in
> armada_370_xp_handle_msi_irq?

Good idea, I've fixed this. Since it's not related to the conversion to
the generic MSI infrastructure, I made this change as part of a
separate commit (this one is already quite difficult to read as a
patch, as you noted).

Thanks for the feedback!

Thomas
Thomas Petazzoni Jan. 26, 2016, 3:52 p.m. UTC | #4
Marc,

On Tue, 26 Jan 2016 14:12:06 +0000, Marc Zyngier wrote:

> I finally found some bandwidth to have a look at this. A few comments below:

Great, thanks! Since 4.5-rc1 is out, I was about to resend this patch
series, so your review comes exactly at the right time. Some comments
below.

> > +static struct irq_chip armada_370_xp_msi_irq_chip = {
> > +	.name = "armada_370_xp_msi_irq",
> > +	.irq_enable = pci_msi_unmask_irq,
> > +	.irq_disable = pci_msi_mask_irq,
> > +	.irq_mask = pci_msi_mask_irq,
> > +	.irq_unmask = pci_msi_unmask_irq,
> 
> Having only mask/unmask ought to be enough.

OK, will fix.

> > -	return hwirq;
> > -}
> > +static struct msi_domain_info armada_370_xp_msi_domain_info = {
> > +	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> > +		   MSI_FLAG_MULTI_PCI_MSI),
> 
> And not MSI-X? That seems rather strange (I'm pretty sure it just works).

See commit 830cbe4b7a918613276aa3d3b28d24410623f92c ("irqchip:
armada-370-xp: implement the ->check_device() msi_chip operation") in
which we changed the driver to explicitly disable MSI-X support. The HW
does support it, but we haven't enabled it yet in the irqchip driver,
and we a PCI device driver tries to use MSI-X, it fails badly.

So, I'd like to keep the enabling of MSI-X as a separate exercise, if
you don't mind :-)

> > -	msg.address_lo = msi_doorbell_addr;
> > -	msg.address_hi = 0;
> > -	msg.data = 0xf00 | (hwirq + 16);
> > +	irq_domain_set_info(domain, virq, hwirq, &armada_370_xp_msi_bottom_irq_chip,
> > +			    domain->host_data, handle_simple_irq,
> > +			    NULL, NULL);
> >  
> > -	pci_write_msi_msg(virq, &msg);
> > -	return 0;
> > +	return hwirq;
> 
> So you are allocating one bit at a time, irrespective of nr_irqs. This
> implies that you can't support MULTI_MSI here (you'd need to guarantee a
> contiguous allocation of nr_irqs bits). So either you amend your
> allocator to deal with those (pretty easy), or you drop MULTI_MSI from
> your msi_domain_info.

Correct. Since the patch is already a bit complicated, I'm going to
drop MULTI_MSI from this patch, and then add another patch which adds
MULTI_MSI support (after adapting the alloc/free functions accordingly).


> > +	armada_370_xp_msi_inner_domain =
> > +		irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR,
> > +				      &armada_370_xp_msi_domain_ops, NULL);
> 
> nit: Please keep the assignment on a single line.

Unfortunately, due to the long name of the variable and the function,
keeping the assignment on a single line quickly reaches the 80 columns
limit, and each argument has to be put on its own line, making the
thing even less pretty. I tend to prefer splitting the assignment on
several lines like done here in such cases, but if you really don't
like, then I don't mind changing that.

> > @@ -604,6 +580,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
> >  	armada_370_xp_mpic_domain =
> >  		irq_domain_add_linear(node, nr_irqs,
> >  				&armada_370_xp_mpic_irq_ops, NULL);
> > +	armada_370_xp_mpic_domain->bus_token = DOMAIN_BUS_WIRED;
> >  
> >  	BUG_ON(!armada_370_xp_mpic_domain);
> 
> Given the assignment just above, this BUG_ON has become pretty useless...

Absolutely. I've changed the BUG_ON() location to make it somewhat more
useful.

Thanks again for the review!

Thomas
Marc Zyngier Jan. 26, 2016, 4:33 p.m. UTC | #5
On 26/01/16 15:52, Thomas Petazzoni wrote:
> Marc,
> 
> On Tue, 26 Jan 2016 14:12:06 +0000, Marc Zyngier wrote:
> 
>> I finally found some bandwidth to have a look at this. A few comments below:
> 
> Great, thanks! Since 4.5-rc1 is out, I was about to resend this patch
> series, so your review comes exactly at the right time. Some comments
> below.
> 
>>> +static struct irq_chip armada_370_xp_msi_irq_chip = {
>>> +	.name = "armada_370_xp_msi_irq",
>>> +	.irq_enable = pci_msi_unmask_irq,
>>> +	.irq_disable = pci_msi_mask_irq,
>>> +	.irq_mask = pci_msi_mask_irq,
>>> +	.irq_unmask = pci_msi_unmask_irq,
>>
>> Having only mask/unmask ought to be enough.
> 
> OK, will fix.
> 
>>> -	return hwirq;
>>> -}
>>> +static struct msi_domain_info armada_370_xp_msi_domain_info = {
>>> +	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>>> +		   MSI_FLAG_MULTI_PCI_MSI),
>>
>> And not MSI-X? That seems rather strange (I'm pretty sure it just works).
> 
> See commit 830cbe4b7a918613276aa3d3b28d24410623f92c ("irqchip:
> armada-370-xp: implement the ->check_device() msi_chip operation") in
> which we changed the driver to explicitly disable MSI-X support. The HW
> does support it, but we haven't enabled it yet in the irqchip driver,
> and we a PCI device driver tries to use MSI-X, it fails badly.
> 
> So, I'd like to keep the enabling of MSI-X as a separate exercise, if
> you don't mind :-)

Sure. It would be interesting to find out what is triggering the issue,
so having that as a separate patch is fine.

> 
>>> -	msg.address_lo = msi_doorbell_addr;
>>> -	msg.address_hi = 0;
>>> -	msg.data = 0xf00 | (hwirq + 16);
>>> +	irq_domain_set_info(domain, virq, hwirq, &armada_370_xp_msi_bottom_irq_chip,
>>> +			    domain->host_data, handle_simple_irq,
>>> +			    NULL, NULL);
>>>  
>>> -	pci_write_msi_msg(virq, &msg);
>>> -	return 0;
>>> +	return hwirq;
>>
>> So you are allocating one bit at a time, irrespective of nr_irqs. This
>> implies that you can't support MULTI_MSI here (you'd need to guarantee a
>> contiguous allocation of nr_irqs bits). So either you amend your
>> allocator to deal with those (pretty easy), or you drop MULTI_MSI from
>> your msi_domain_info.
> 
> Correct. Since the patch is already a bit complicated, I'm going to
> drop MULTI_MSI from this patch, and then add another patch which adds
> MULTI_MSI support (after adapting the alloc/free functions accordingly).

OK.

> 
>>> +	armada_370_xp_msi_inner_domain =
>>> +		irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR,
>>> +				      &armada_370_xp_msi_domain_ops, NULL);
>>
>> nit: Please keep the assignment on a single line.
> 
> Unfortunately, due to the long name of the variable and the function,
> keeping the assignment on a single line quickly reaches the 80 columns
> limit, and each argument has to be put on its own line, making the
> thing even less pretty. I tend to prefer splitting the assignment on
> several lines like done here in such cases, but if you really don't
> like, then I don't mind changing that.

I definitely don't mind having lines larger than 80 chars (I've retired
my vt100 a while ago), but that's your call in the end.

Thanks,

	M.
diff mbox

Patch

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index ab672b0..8ccb60e 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -51,6 +51,7 @@  config ARMADA_370_XP_IRQ
 	bool
 	default y if ARCH_MVEBU
 	select GENERIC_IRQ_CHIP
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
 
 config ATMEL_AIC_IRQ
 	bool
diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
index 3f3a8c3..304166b 100644
--- a/drivers/irqchip/irq-armada-370-xp.c
+++ b/drivers/irqchip/irq-armada-370-xp.c
@@ -71,6 +71,7 @@  static u32 doorbell_mask_reg;
 static int parent_irq;
 #ifdef CONFIG_PCI_MSI
 static struct irq_domain *armada_370_xp_msi_domain;
+static struct irq_domain *armada_370_xp_msi_inner_domain;
 static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
 static DEFINE_MUTEX(msi_used_lock);
 static phys_addr_t msi_doorbell_addr;
@@ -115,127 +116,102 @@  static void armada_370_xp_irq_unmask(struct irq_data *d)
 
 #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);
+static struct irq_chip armada_370_xp_msi_irq_chip = {
+	.name = "armada_370_xp_msi_irq",
+	.irq_enable = pci_msi_unmask_irq,
+	.irq_disable = pci_msi_mask_irq,
+	.irq_mask = pci_msi_mask_irq,
+	.irq_unmask = pci_msi_unmask_irq,
+};
 
-	return hwirq;
-}
+static struct msi_domain_info armada_370_xp_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		   MSI_FLAG_MULTI_PCI_MSI),
+	.chip	= &armada_370_xp_msi_irq_chip,
+};
 
-static void armada_370_xp_free_msi(int hwirq)
+static void armada_370_xp_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
 {
-	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);
+	msg->address_lo = lower_32_bits(msi_doorbell_addr);
+	msg->address_hi = upper_32_bits(msi_doorbell_addr);
+	msg->data = 0xf00 | (data->hwirq + PCI_MSI_DOORBELL_START);
 }
 
-static int armada_370_xp_setup_msi_irq(struct msi_controller *chip,
-				       struct pci_dev *pdev,
-				       struct msi_desc *desc)
+static int armada_370_xp_msi_set_affinity(struct irq_data *irq_data,
+					  const struct cpumask *mask, bool force)
 {
-	struct msi_msg msg;
-	int virq, hwirq;
+	 return -EINVAL;
+}
 
-	/* We support MSI, but not MSI-X */
-	if (desc->msi_attrib.is_msix)
-		return -EINVAL;
+static struct irq_chip armada_370_xp_msi_bottom_irq_chip = {
+	.name			= "MPIC MSI",
+	.irq_compose_msi_msg	= armada_370_xp_compose_msi_msg,
+	.irq_set_affinity	= armada_370_xp_msi_set_affinity,
+};
 
-	hwirq = armada_370_xp_alloc_msi();
-	if (hwirq < 0)
-		return hwirq;
+static int armada_370_xp_msi_alloc(struct irq_domain *domain, unsigned int virq,
+				   unsigned int nr_irqs, void *args)
+{
+	int hwirq;
 
-	virq = irq_create_mapping(armada_370_xp_msi_domain, hwirq);
-	if (!virq) {
-		armada_370_xp_free_msi(hwirq);
-		return -EINVAL;
+	mutex_lock(&msi_used_lock);
+	hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR);
+	if (hwirq >= PCI_MSI_DOORBELL_NR) {
+		mutex_unlock(&msi_used_lock);
+		return -ENOSPC;
 	}
 
-	irq_set_msi_desc(virq, desc);
+	set_bit(hwirq, msi_used);
+	mutex_unlock(&msi_used_lock);
 
-	msg.address_lo = msi_doorbell_addr;
-	msg.address_hi = 0;
-	msg.data = 0xf00 | (hwirq + 16);
+	irq_domain_set_info(domain, virq, hwirq, &armada_370_xp_msi_bottom_irq_chip,
+			    domain->host_data, handle_simple_irq,
+			    NULL, NULL);
 
-	pci_write_msi_msg(virq, &msg);
-	return 0;
+	return hwirq;
 }
 
-static void armada_370_xp_teardown_msi_irq(struct msi_controller *chip,
-					   unsigned int irq)
+static void armada_370_xp_msi_free(struct irq_domain *domain,
+				   unsigned int virq, unsigned int nr_irqs)
 {
-	struct irq_data *d = irq_get_irq_data(irq);
-	unsigned long hwirq = d->hwirq;
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
 
-	irq_dispose_mapping(irq);
-	armada_370_xp_free_msi(hwirq);
-}
-
-static struct irq_chip armada_370_xp_msi_irq_chip = {
-	.name = "armada_370_xp_msi_irq",
-	.irq_enable = pci_msi_unmask_irq,
-	.irq_disable = pci_msi_mask_irq,
-	.irq_mask = pci_msi_mask_irq,
-	.irq_unmask = pci_msi_unmask_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);
-
-	return 0;
+	mutex_lock(&msi_used_lock);
+	if (!test_bit(d->hwirq, msi_used))
+		pr_err("trying to free unused MSI#%lu\n", d->hwirq);
+	else
+		clear_bit(d->hwirq, msi_used);
+	mutex_unlock(&msi_used_lock);
 }
 
-static const struct irq_domain_ops armada_370_xp_msi_irq_ops = {
-	.map = armada_370_xp_msi_map,
+static const struct irq_domain_ops armada_370_xp_msi_domain_ops = {
+	.alloc	= armada_370_xp_msi_alloc,
+	.free	= armada_370_xp_msi_free,
 };
 
 static int armada_370_xp_msi_init(struct device_node *node,
 				  phys_addr_t main_int_phys_base)
 {
-	struct msi_controller *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)
+	armada_370_xp_msi_inner_domain =
+		irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR,
+				      &armada_370_xp_msi_domain_ops, NULL);
+	if (!armada_370_xp_msi_inner_domain)
 		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);
+		pci_msi_create_irq_domain(of_node_to_fwnode(node),
+					  &armada_370_xp_msi_domain_info,
+					  armada_370_xp_msi_inner_domain);
 	if (!armada_370_xp_msi_domain) {
-		kfree(msi_chip);
+		irq_domain_remove(armada_370_xp_msi_inner_domain);
 		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;
 
@@ -427,12 +403,12 @@  static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained)
 			continue;
 
 		if (is_chained) {
-			irq = irq_find_mapping(armada_370_xp_msi_domain,
+			irq = irq_find_mapping(armada_370_xp_msi_inner_domain,
 					       msinr - 16);
 			generic_handle_irq(irq);
 		} else {
 			irq = msinr - 16;
-			handle_domain_irq(armada_370_xp_msi_domain,
+			handle_domain_irq(armada_370_xp_msi_inner_domain,
 					  irq, regs);
 		}
 	}
@@ -604,6 +580,7 @@  static int __init armada_370_xp_mpic_of_init(struct device_node *node,
 	armada_370_xp_mpic_domain =
 		irq_domain_add_linear(node, nr_irqs,
 				&armada_370_xp_mpic_irq_ops, NULL);
+	armada_370_xp_mpic_domain->bus_token = DOMAIN_BUS_WIRED;
 
 	BUG_ON(!armada_370_xp_mpic_domain);