[PATCH] irqchip/ls-scfg-msi: update Layerscape SCFG MSI driver

Marc Zyngier marc.zyngier at arm.com
Tue Oct 25 06:11:00 PDT 2016


On 25/10/16 13:39, Minghuan Lian wrote:
> 1. The patch uses soc_device_match() to match the SoC family
> and revision instead of DTS compatible, because compatible cannot
> describe the SoC revision information.
> 2. The patch provides a new method to support Layerscape
> SCFG MSI. It tries to assign a dedicated MSIR to every core.
> When changing a MSI interrupt affinity, the MSI message
> data will be changed to refer to a new MSIR that has
> been associated with the core.
> 
> Signed-off-by: Minghuan Lian <Minghuan.Lian at nxp.com>
> ---
> The patch depends on https://patchwork.kernel.org/patch/9342915/

What is the status of this patch? This is the first time I hear about
it, and I can't find it in -rc2.

> 
>  drivers/irqchip/irq-ls-scfg-msi.c | 444 +++++++++++++++++++++++++++++++-------
>  1 file changed, 367 insertions(+), 77 deletions(-)
> 
> diff --git a/drivers/irqchip/irq-ls-scfg-msi.c b/drivers/irqchip/irq-ls-scfg-msi.c
> index 02cca74c..0245d8a 100644
> --- a/drivers/irqchip/irq-ls-scfg-msi.c
> +++ b/drivers/irqchip/irq-ls-scfg-msi.c
> @@ -10,6 +10,7 @@
>   * published by the Free Software Foundation.
>   */
>  
> +#include <linux/bitmap.h>
>  #include <linux/kernel.h>
>  #include <linux/module.h>
>  #include <linux/msi.h>
> @@ -17,23 +18,91 @@
>  #include <linux/irq.h>
>  #include <linux/irqchip/chained_irq.h>
>  #include <linux/irqdomain.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
>  #include <linux/of_pci.h>
>  #include <linux/of_platform.h>
>  #include <linux/spinlock.h>
> +#include <linux/sys_soc.h>
>  
> -#define MSI_MAX_IRQS	32
> -#define MSI_IBS_SHIFT	3
> -#define MSIR		4
> +#define LS_MSIR_NUM_MAX		4 /* MSIIR can index 4 MSI registers */
> +#define IRQS_32_PER_MSIR	32
> +#define IRQS_8_PER_MSIR		8
> +
> +#define MSIR_OFFSET(idx)	((idx) * 0x4)
> +
> +enum msi_affinity_flag {
> +	MSI_GROUP_AFFINITY_FLAG,
> +	MSI_AFFINITY_FLAG
> +};
> +
> +struct ls_scfg_msi;
> +struct ls_scfg_msi_ctrl;
> +
> +struct ls_scfg_msi_cfg {
> +	u32 ibs_shift; /* Shift of interrupt bit select */
> +	u32 msir_irqs; /* The irq number per MSIR */
> +	u32 msir_base; /* The base address of MSIR */
> +};
> +
> +struct ls_scfg_msir {
> +	struct ls_scfg_msi_ctrl	*ctrl;
> +	void __iomem		*addr;
> +	int			index;
> +	int			virq;
> +};
> +
> +struct ls_scfg_msi_ctrl {
> +	struct list_head		list;
> +	struct ls_scfg_msi		*msi_data;
> +	void __iomem			*regs;
> +	phys_addr_t			msiir_addr;
> +	enum msi_affinity_flag		flag;
> +	int				irq_base;
> +	spinlock_t			lock;
> +	struct ls_scfg_msir		*msir;
> +	unsigned long			*bm;
> +};
>  
>  struct ls_scfg_msi {
> -	spinlock_t		lock;
> -	struct platform_device	*pdev;
> -	struct irq_domain	*parent;
> -	struct irq_domain	*msi_domain;
> -	void __iomem		*regs;
> -	phys_addr_t		msiir_addr;
> -	int			irq;
> -	DECLARE_BITMAP(used, MSI_MAX_IRQS);
> +	struct platform_device		*pdev;
> +	struct irq_domain		*parent;
> +	struct irq_domain		*msi_domain;
> +	struct list_head		ctrl_list;
> +	const struct ls_scfg_msi_cfg	*cfg;
> +	u32				cpu_num;
> +};
> +
> +static struct ls_scfg_msi_cfg ls1021_msi_cfg = {
> +	.ibs_shift = 3,
> +	.msir_irqs = IRQS_32_PER_MSIR,
> +	.msir_base = 0x4,
> +};
> +
> +static struct ls_scfg_msi_cfg ls1043_rev11_msi_cfg = {
> +	.ibs_shift = 2,
> +	.msir_irqs = IRQS_8_PER_MSIR,
> +	.msir_base = 0x10,
> +};
> +
> +static struct ls_scfg_msi_cfg ls1046_msi_cfg = {
> +	.ibs_shift = 2,
> +	.msir_irqs = IRQS_32_PER_MSIR,
> +	.msir_base = 0x4,
> +};
> +
> +static struct soc_device_attribute soc_msi_matches[] = {
> +	{ .family = "QorIQ LS1021A",
> +	  .data = &ls1021_msi_cfg },
> +	{ .family = "QorIQ LS1012A",
> +	  .data = &ls1021_msi_cfg },
> +	{ .family = "QorIQ LS1043A", .revision = "1.0",
> +	  .data = &ls1021_msi_cfg },
> +	{ .family = "QorIQ LS1043A", .revision = "1.1",
> +	  .data = &ls1043_rev11_msi_cfg },
> +	{ .family = "QorIQ LS1046A",
> +	  .data = &ls1046_msi_cfg },
> +	{ },

Right. So after spending about 5 years getting rid of board files, they
are now back, just spread over a zillion drivers? Why isn't that
described in DT?

>  };
>  
>  static struct irq_chip ls_scfg_msi_irq_chip = {
> @@ -49,19 +118,53 @@ struct ls_scfg_msi {
>  	.chip	= &ls_scfg_msi_irq_chip,
>  };
>  
> +static int ctrl_num;
> +
> +static irqreturn_t (*ls_scfg_msi_irq_handler)(int irq, void *arg);
> +
>  static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
>  {
> -	struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(data);
> +	struct ls_scfg_msi_ctrl *ctrl = irq_data_get_irq_chip_data(data);
> +	u32 ibs, srs;
>  
> -	msg->address_hi = upper_32_bits(msi_data->msiir_addr);
> -	msg->address_lo = lower_32_bits(msi_data->msiir_addr);
> -	msg->data = data->hwirq << MSI_IBS_SHIFT;
> +	msg->address_hi = upper_32_bits(ctrl->msiir_addr);
> +	msg->address_lo = lower_32_bits(ctrl->msiir_addr);
> +
> +	ibs = data->hwirq - ctrl->irq_base;
> +
> +	srs = cpumask_first(irq_data_get_affinity_mask(data));
> +	if (srs >= ctrl->msi_data->cpu_num)
> +		srs = 0;
> +
> +	msg->data = ibs << ctrl->msi_data->cfg->ibs_shift | srs;
> +
> +	pr_debug("%s: ibs %d srs %d address0x%x-0x%x data 0x%x\n",
> +		 __func__, ibs, srs, msg->address_hi,
> +		 msg->address_lo, msg->data);
>  }
>  
> -static int ls_scfg_msi_set_affinity(struct irq_data *irq_data,
> -				    const struct cpumask *mask, bool force)
> +static int ls_scfg_msi_set_affinity(struct irq_data *data,
> +				const struct cpumask *mask, bool force)
>  {
> -	return -EINVAL;
> +	struct ls_scfg_msi_ctrl *ctrl = irq_data_get_irq_chip_data(data);
> +	u32 cpu;
> +
> +	if (!force)
> +		cpu = cpumask_any_and(mask, cpu_online_mask);
> +	else
> +		cpu = cpumask_first(mask);
> +
> +	if (cpu >= ctrl->msi_data->cpu_num)
> +		return -EINVAL;
> +
> +	if (ctrl->msir[cpu].virq <= 0) {
> +		pr_warn("cannot bind the irq to cpu%d\n", cpu);
> +		return -EINVAL;
> +	}
> +
> +	cpumask_copy(irq_data_get_affinity_mask(data), mask);
> +
> +	return IRQ_SET_MASK_OK_NOCOPY;
>  }
>  
>  static struct irq_chip ls_scfg_msi_parent_chip = {
> @@ -76,44 +179,57 @@ static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain,
>  					void *args)
>  {
>  	struct ls_scfg_msi *msi_data = domain->host_data;
> -	int pos, err = 0;
> +	static struct list_head *current_entry;
> +	struct ls_scfg_msi_ctrl *ctrl;
> +	int i, hwirq = -ENOMEM;
> +
> +	if (!current_entry || current_entry->next == &msi_data->ctrl_list)
> +		current_entry = &msi_data->ctrl_list;
> +
> +	list_for_each_entry(ctrl, current_entry, list) {
> +		spin_lock(&ctrl->lock);
> +		hwirq = bitmap_find_free_region(ctrl->bm,
> +						msi_data->cfg->msir_irqs,
> +						order_base_2(nr_irqs));
> +		spin_unlock(&ctrl->lock);
> +
> +		if (hwirq >= 0)
> +			break;
> +	}
>  
> -	WARN_ON(nr_irqs != 1);
> +	if (hwirq < 0)
> +		return hwirq;
>  
> -	spin_lock(&msi_data->lock);
> -	pos = find_first_zero_bit(msi_data->used, MSI_MAX_IRQS);
> -	if (pos < MSI_MAX_IRQS)
> -		__set_bit(pos, msi_data->used);
> -	else
> -		err = -ENOSPC;
> -	spin_unlock(&msi_data->lock);
> +	hwirq = hwirq + ctrl->irq_base;
>  
> -	if (err)
> -		return err;
> +	for (i = 0; i < nr_irqs; i++)
> +		irq_domain_set_info(domain, virq + i, hwirq + i,
> +				    &ls_scfg_msi_parent_chip, ctrl,
> +				    handle_simple_irq, NULL, NULL);
>  
> -	irq_domain_set_info(domain, virq, pos,
> -			    &ls_scfg_msi_parent_chip, msi_data,
> -			    handle_simple_irq, NULL, NULL);
> +	current_entry = &ctrl->list;
>  
>  	return 0;
>  }
>  
>  static void ls_scfg_msi_domain_irq_free(struct irq_domain *domain,
> -				   unsigned int virq, unsigned int nr_irqs)
> +					unsigned int virq,
> +					unsigned int nr_irqs)
>  {
>  	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> -	struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(d);
> +	struct ls_scfg_msi_ctrl *ctrl = irq_data_get_irq_chip_data(d);
>  	int pos;
>  
> -	pos = d->hwirq;
> -	if (pos < 0 || pos >= MSI_MAX_IRQS) {
> -		pr_err("failed to teardown msi. Invalid hwirq %d\n", pos);
> +	pos = d->hwirq - ctrl->irq_base;
> +
> +	if (pos < 0 || pos >= ctrl->msi_data->cfg->msir_irqs) {
> +		pr_err("Failed to teardown msi. Invalid hwirq %d\n", pos);
>  		return;
>  	}
>  
> -	spin_lock(&msi_data->lock);
> -	__clear_bit(pos, msi_data->used);
> -	spin_unlock(&msi_data->lock);
> +	spin_lock(&ctrl->lock);
> +	bitmap_release_region(ctrl->bm, pos, order_base_2(nr_irqs));
> +	spin_unlock(&ctrl->lock);
>  }
>  
>  static const struct irq_domain_ops ls_scfg_msi_domain_ops = {
> @@ -121,29 +237,198 @@ static void ls_scfg_msi_domain_irq_free(struct irq_domain *domain,
>  	.free	= ls_scfg_msi_domain_irq_free,
>  };
>  
> -static void ls_scfg_msi_irq_handler(struct irq_desc *desc)
> +static irqreturn_t ls_scfg_msi_irqs32_handler(int irq, void *arg)
>  {
> -	struct ls_scfg_msi *msi_data = irq_desc_get_handler_data(desc);
> +	struct ls_scfg_msir *msir = arg;
> +	struct ls_scfg_msi_ctrl *ctrl = msir->ctrl;
> +	struct ls_scfg_msi *msi_data = ctrl->msi_data;
>  	unsigned long val;
> -	int pos, virq;
> +	int pos = 0, hwirq, virq;
> +	irqreturn_t ret = IRQ_NONE;
>  
> -	chained_irq_enter(irq_desc_get_chip(desc), desc);
> +	val = ioread32be(msir->addr);
>  
> -	val = ioread32be(msi_data->regs + MSIR);
> -	for_each_set_bit(pos, &val, MSI_MAX_IRQS) {
> -		virq = irq_find_mapping(msi_data->parent, (31 - pos));
> -		if (virq)
> +	for_each_set_bit(pos, &val, IRQS_32_PER_MSIR) {
> +		hwirq = (IRQS_32_PER_MSIR - 1 - pos) + ctrl->irq_base;
> +		virq = irq_find_mapping(msi_data->parent, hwirq);
> +		if (virq) {
>  			generic_handle_irq(virq);
> +			ret = IRQ_HANDLED;
> +		}
> +	}
> +
> +	return ret;
> +}

NAK. This is not an interrupt handler. This is a flow handler.

> +
> +static irqreturn_t ls_scfg_msi_irqs8_handler(int irq, void *arg)
> +{
> +	struct ls_scfg_msir *msir = arg;
> +	struct ls_scfg_msi_ctrl *ctrl = msir->ctrl;
> +	struct ls_scfg_msi *msi_data = ctrl->msi_data;
> +	unsigned long val;
> +	int pos = 0, hwirq, virq;
> +	irqreturn_t ret = IRQ_NONE;
> +
> +	val = ioread32be(msir->addr);
> +	val = (val << (msir->index * 8)) & 0xff000000;
> +
> +	for_each_set_bit(pos, &val, IRQS_32_PER_MSIR) {
> +		hwirq = (IRQS_32_PER_MSIR - 1 - pos) + ctrl->irq_base;
> +		virq = irq_find_mapping(msi_data->parent, hwirq);
> +		if (virq) {
> +			generic_handle_irq(virq);
> +			ret = IRQ_HANDLED;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static void ls_scfg_msi_cascade(struct irq_desc *desc)
> +{
> +	struct ls_scfg_msir *msir = irq_desc_get_handler_data(desc);
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +
> +	chained_irq_enter(chip, desc);
> +	ls_scfg_msi_irq_handler(desc->irq_data.irq, msir);
> +	chained_irq_exit(chip, desc);
> +}

NAK.

> +
> +static int ls_scfg_msi_setup_hwirq(struct ls_scfg_msi_ctrl *ctrl,
> +				   struct device_node *node,
> +				   int index)
> +{
> +	struct ls_scfg_msir *msir = &ctrl->msir[index];
> +	int ret;
> +
> +	msir->virq = of_irq_get(node, index);
> +	if (msir->virq <= 0)
> +		return -ENODEV;
> +
> +	msir->index = index;
> +	msir->ctrl = ctrl;
> +	msir->addr = ctrl->regs + ctrl->msi_data->cfg->msir_base +
> +		     MSIR_OFFSET(msir->index);
> +
> +	if (ctrl->flag == MSI_GROUP_AFFINITY_FLAG) {
> +		ret = request_irq(msir->virq, ls_scfg_msi_irq_handler,
> +				  IRQF_NO_THREAD, "MSI-GROUP", msir);
> +		if (ret) {
> +			pr_err("failed to request irq %d\n", msir->virq);
> +			msir->virq = 0;
> +			return -ENODEV;
> +		}
> +	} else {
> +		irq_set_chained_handler(msir->virq, ls_scfg_msi_cascade);
> +		irq_set_handler_data(msir->virq, msir);
> +		irq_set_affinity(msir->virq, get_cpu_mask(index));
> +	}
> +
> +	return 0;
> +}
> +
> +static void ls_scfg_msi_ctrl_remove(struct ls_scfg_msi_ctrl *ctrl)
> +{
> +	struct ls_scfg_msir *msir;
> +	int i;
> +
> +	if (!ctrl)
> +		return;
> +
> +	if (ctrl->msir) {
> +		for (i = 0; i < ctrl->msi_data->cpu_num; i++) {
> +			msir = &ctrl->msir[i];
> +
> +			if (msir->virq <= 0)
> +				continue;
> +
> +			if (ctrl->flag == MSI_GROUP_AFFINITY_FLAG)
> +				free_irq(msir->virq, msir);
> +			else
> +				irq_set_chained_handler_and_data(msir->virq,
> +								 NULL, NULL);
> +		}
> +
> +		kfree(ctrl->msir);
>  	}
>  
> -	chained_irq_exit(irq_desc_get_chip(desc), desc);
> +	if (ctrl->regs)
> +		iounmap(ctrl->regs);
> +
> +	kfree(ctrl->bm);
> +	kfree(ctrl);
> +}
> +
> +static int ls_scfg_msi_ctrl_probe(struct device_node *node,
> +				  struct ls_scfg_msi *msi_data)
> +{
> +	struct ls_scfg_msi_ctrl *ctrl;
> +	struct resource res;
> +	int err, irqs, i;
> +
> +	err = of_address_to_resource(node, 0, &res);
> +	if (err) {
> +		pr_warn("%s: no regs\n", node->full_name);
> +		return -ENXIO;
> +	}
> +
> +	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
> +	if (!ctrl)
> +		return  -ENOMEM;
> +
> +	ctrl->msi_data = msi_data;
> +	ctrl->msiir_addr = res.start;
> +	spin_lock_init(&ctrl->lock);
> +
> +	ctrl->regs = ioremap(res.start, resource_size(&res));
> +	if (!ctrl->regs) {
> +		pr_err("%s: unable to map registers\n", node->full_name);
> +		err = -ENOMEM;
> +		goto _err;
> +	}
> +
> +	ctrl->msir = kcalloc(msi_data->cpu_num, sizeof(struct ls_scfg_msir),
> +			     GFP_KERNEL);
> +	if (!ctrl->msir) {
> +		err = -ENOMEM;
> +		goto _err;
> +	}
> +
> +	ctrl->bm = kcalloc(BITS_TO_LONGS(msi_data->cfg->msir_irqs),
> +			   sizeof(long), GFP_KERNEL);
> +	if (!ctrl->bm) {
> +		err = -ENOMEM;
> +		goto _err;
> +	}
> +
> +	ctrl->irq_base = msi_data->cfg->msir_irqs * ctrl_num;
> +	ctrl_num++;
> +
> +	irqs = of_irq_count(node);
> +	if (irqs >= msi_data->cpu_num)
> +		ctrl->flag = MSI_AFFINITY_FLAG;
> +	else
> +		ctrl->flag = MSI_GROUP_AFFINITY_FLAG;
> +
> +	for (i = 0; i < msi_data->cpu_num; i++)
> +		ls_scfg_msi_setup_hwirq(ctrl, node, i);
> +
> +	list_add_tail(&ctrl->list, &msi_data->ctrl_list);
> +
> +	return 0;
> +
> +_err:
> +	ls_scfg_msi_ctrl_remove(ctrl);
> +	pr_err("MSI: failed probing %s (%d)\n", node->full_name, err);
> +	return err;
>  }
>  
>  static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data)
>  {
>  	/* Initialize MSI domain parent */
>  	msi_data->parent = irq_domain_add_linear(NULL,
> -						 MSI_MAX_IRQS,
> +						 msi_data->cfg->msir_irqs *
> +						 ctrl_num,
>  						 &ls_scfg_msi_domain_ops,
>  						 msi_data);
>  	if (!msi_data->parent) {
> @@ -167,51 +452,57 @@ static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data)
>  static int ls_scfg_msi_probe(struct platform_device *pdev)
>  {
>  	struct ls_scfg_msi *msi_data;
> -	struct resource *res;
> -	int ret;
> +	const struct soc_device_attribute *match;
> +	struct device_node *child;
>  
>  	msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL);
>  	if (!msi_data)
>  		return -ENOMEM;
>  
> -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> -	msi_data->regs = devm_ioremap_resource(&pdev->dev, res);
> -	if (IS_ERR(msi_data->regs)) {
> -		dev_err(&pdev->dev, "failed to initialize 'regs'\n");
> -		return PTR_ERR(msi_data->regs);
> -	}
> -	msi_data->msiir_addr = res->start;
> -
> -	msi_data->irq = platform_get_irq(pdev, 0);
> -	if (msi_data->irq <= 0) {
> -		dev_err(&pdev->dev, "failed to get MSI irq\n");
> -		return -ENODEV;
> -	}
> +	INIT_LIST_HEAD(&msi_data->ctrl_list);
>  
>  	msi_data->pdev = pdev;
> -	spin_lock_init(&msi_data->lock);
> +	msi_data->cpu_num = num_possible_cpus();
> +
> +	match = soc_device_match(soc_msi_matches);
> +	if (match)
> +		msi_data->cfg = match->data;
> +	else
> +		msi_data->cfg = &ls1046_msi_cfg;
> +
> +	if (msi_data->cfg->msir_irqs == IRQS_8_PER_MSIR)
> +		ls_scfg_msi_irq_handler = ls_scfg_msi_irqs8_handler;
> +	else
> +		ls_scfg_msi_irq_handler = ls_scfg_msi_irqs32_handler;
>  
> -	ret = ls_scfg_msi_domains_init(msi_data);
> -	if (ret)
> -		return ret;
> +	for_each_child_of_node(msi_data->pdev->dev.of_node, child)
> +		ls_scfg_msi_ctrl_probe(child, msi_data);
>  
> -	irq_set_chained_handler_and_data(msi_data->irq,
> -					 ls_scfg_msi_irq_handler,
> -					 msi_data);
> +	ls_scfg_msi_domains_init(msi_data);
>  
>  	platform_set_drvdata(pdev, msi_data);
>  
> +	dev_info(&pdev->dev, "irqs:%dx%d ibs_shift:%d msir_base:0x%x\n",
> +		 msi_data->cfg->msir_irqs, ctrl_num,
> +		 msi_data->cfg->ibs_shift, msi_data->cfg->msir_base);
> +
>  	return 0;
>  }
>  
>  static int ls_scfg_msi_remove(struct platform_device *pdev)
>  {
>  	struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev);
> +	struct ls_scfg_msi_ctrl *ctrl, *temp;
>  
> -	irq_set_chained_handler_and_data(msi_data->irq, NULL, NULL);
> +	list_for_each_entry_safe(ctrl, temp, &msi_data->ctrl_list, list) {
> +		list_move_tail(&ctrl->list, &msi_data->ctrl_list);
> +		ls_scfg_msi_ctrl_remove(ctrl);
> +	}
>  
> -	irq_domain_remove(msi_data->msi_domain);
> -	irq_domain_remove(msi_data->parent);
> +	if (msi_data->msi_domain)
> +		irq_domain_remove(msi_data->msi_domain);
> +	if (msi_data->parent)
> +		irq_domain_remove(msi_data->parent);
>  
>  	platform_set_drvdata(pdev, NULL);
>  
> @@ -219,8 +510,7 @@ static int ls_scfg_msi_remove(struct platform_device *pdev)
>  }
>  
>  static const struct of_device_id ls_scfg_msi_id[] = {
> -	{ .compatible = "fsl,1s1021a-msi", },
> -	{ .compatible = "fsl,1s1043a-msi", },
> +	{ .compatible = "fsl,ls-scfg-msi" },

And now you've broken all the previous DTs that previously existed.
What's the plan?

>  	{},
>  };
>  
> 

Overall, I hate this patch. It is broken from an irqchip PoV, it creates
a new range of board files, just even less maintainable, it breaks
compatibility with previous DTs.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...



More information about the linux-arm-kernel mailing list