[v4,2/2] irqchip/loongson-eiointc: Simplify irq routing on some platforms

Message ID 20230713032243.2046205-3-maobibo@loongson.cn
State New
Headers
Series irqchip/loongson-eiointc: Add simple irq routing method |

Commit Message

maobibo July 13, 2023, 3:22 a.m. UTC
  Some LoongArch systems have only one eiointc node such as 3A5000/2K2000
and qemu virt-machine. If there is only one eiointc node, all cpus can
access eiointc registers directly; if there is multiple eiointc nodes, each
cpu can only access eiointc belonging to specified node group, so anysend
or ipi needs to be used to configure irq routing. IRQ routing is simple on
such systems with one node, hacking method like anysend is not necessary.

This patch provides simpile IRQ routing method for systems with one eiointc
node, and is tested on 3A5000 board and qemu virt-machine.

Signed-off-by: Bibo Mao <maobibo@loongson.cn>
---
 drivers/irqchip/irq-loongson-eiointc.c | 80 ++++++++++++++++++++++++--
 1 file changed, 74 insertions(+), 6 deletions(-)
  

Comments

Huacai Chen July 14, 2023, 3:01 a.m. UTC | #1
Hi, Bibo,

On Thu, Jul 13, 2023 at 11:23 AM Bibo Mao <maobibo@loongson.cn> wrote:
>
> Some LoongArch systems have only one eiointc node such as 3A5000/2K2000
> and qemu virt-machine. If there is only one eiointc node, all cpus can
> access eiointc registers directly; if there is multiple eiointc nodes, each
> cpu can only access eiointc belonging to specified node group, so anysend
> or ipi needs to be used to configure irq routing. IRQ routing is simple on
> such systems with one node, hacking method like anysend is not necessary.
>
> This patch provides simpile IRQ routing method for systems with one eiointc
> node, and is tested on 3A5000 board and qemu virt-machine.
>
> Signed-off-by: Bibo Mao <maobibo@loongson.cn>
> ---
>  drivers/irqchip/irq-loongson-eiointc.c | 80 ++++++++++++++++++++++++--
>  1 file changed, 74 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c
> index 603d323b8f8b..e6be9d6a18c8 100644
> --- a/drivers/irqchip/irq-loongson-eiointc.c
> +++ b/drivers/irqchip/irq-loongson-eiointc.c
> @@ -127,6 +127,48 @@ static int eiointc_set_irq_affinity(struct irq_data *d, const struct cpumask *af
>         return IRQ_SET_MASK_OK;
>  }
>
> +static int eiointc_single_set_irq_affinity(struct irq_data *d,
> +                               const struct cpumask *affinity, bool force)
> +{
> +       unsigned int cpu;
> +       unsigned long flags;
> +       uint32_t vector, regaddr, data, coremap;
> +       struct cpumask mask;
> +       struct eiointc_priv *priv = d->domain->host_data;
> +
> +       cpumask_and(&mask, affinity, cpu_online_mask);
> +       cpumask_and(&mask, &mask, &priv->cpuspan_map);
> +       if (cpumask_empty(&mask))
> +               return -EINVAL;
> +
> +       cpu = cpumask_first(&mask);
> +       vector = d->hwirq;
> +       regaddr = EIOINTC_REG_ENABLE + ((vector >> 5) << 2);
> +       data = ~BIT(vector & 0x1F);
> +       coremap = BIT(cpu_logical_map(cpu) % CORES_PER_EIO_NODE);
> +
> +       /*
> +        * simplify for platform with only one eiointc node
> +        * access eiointc registers directly rather than
> +        * use any_send method here
> +        */
> +       raw_spin_lock_irqsave(&affinity_lock, flags);
> +       iocsr_write32(EIOINTC_ALL_ENABLE & data, regaddr);
> +       /*
> +        * get irq route info for continuous 4 vectors
> +        * and set affinity for specified vector
> +        */
> +       data = iocsr_read32(EIOINTC_REG_ROUTE + (vector & ~3));
> +       data &=  ~(0xff << ((vector & 3) * 8));
> +       data |= coremap << ((vector & 3) * 8);
> +       iocsr_write32(data, EIOINTC_REG_ROUTE + (vector & ~3));
> +       iocsr_write32(EIOINTC_ALL_ENABLE, regaddr);
> +       raw_spin_unlock_irqrestore(&affinity_lock, flags);
> +
> +       irq_data_update_effective_affinity(d, cpumask_of(cpu));
> +       return IRQ_SET_MASK_OK;
> +}
> +
>  static int eiointc_index(int node)
>  {
>         int i;
> @@ -238,22 +280,39 @@ static struct irq_chip eiointc_irq_chip = {
>         .irq_set_affinity       = eiointc_set_irq_affinity,
>  };
>
> +static struct irq_chip eiointc_irq_chipi_single = {
What does "chipi" mean? Maybe "chip"?

And from my point of view, the main goal of this patch is to add
"virtual eiointc" support which is different from "real eiointc". So I
think it is better to not touch the "real eiointc" logic, but add a
variant for "virtual eiointc", and then, the name should be
"EIOINTC-V", not "EIOINTC-S".

Huacai

> +       .name                   = "EIOINTC-S",
> +       .irq_ack                = eiointc_ack_irq,
> +       .irq_mask               = eiointc_mask_irq,
> +       .irq_unmask             = eiointc_unmask_irq,
> +#ifdef CONFIG_SMP
> +       .irq_set_affinity       = eiointc_single_set_irq_affinity,
> +#endif
> +};
> +
>  static int eiointc_domain_alloc(struct irq_domain *domain, unsigned int virq,
>                                 unsigned int nr_irqs, void *arg)
>  {
>         int ret;
>         unsigned int i, type;
>         unsigned long hwirq = 0;
> -       struct eiointc *priv = domain->host_data;
> +       struct eiointc_priv *priv = domain->host_data;
> +       struct irq_chip *chip;
>
>         ret = irq_domain_translate_onecell(domain, arg, &hwirq, &type);
>         if (ret)
>                 return ret;
>
> -       for (i = 0; i < nr_irqs; i++) {
> -               irq_domain_set_info(domain, virq + i, hwirq + i, &eiointc_irq_chip,
> +       /*
> +        * use simple irq routing method on single eiointc node
> +        */
> +       if ((nr_pics == 1) && (nodes_weight(priv->node_map) == 1))
> +               chip = &eiointc_irq_chipi_single;
> +       else
> +               chip = &eiointc_irq_chip;
> +       for (i = 0; i < nr_irqs; i++)
> +               irq_domain_set_info(domain, virq + i, hwirq + i, chip,
>                                         priv, handle_edge_irq, NULL, NULL);
> -       }
>
>         return 0;
>  }
> @@ -310,6 +369,7 @@ static void eiointc_resume(void)
>         int i, j;
>         struct irq_desc *desc;
>         struct irq_data *irq_data;
> +       struct irq_chip *chip;
>
>         eiointc_router_init(0);
>
> @@ -319,7 +379,8 @@ static void eiointc_resume(void)
>                         if (desc && desc->handle_irq && desc->handle_irq != handle_bad_irq) {
>                                 raw_spin_lock(&desc->lock);
>                                 irq_data = irq_domain_get_irq_data(eiointc_priv[i]->eiointc_domain, irq_desc_get_irq(desc));
> -                               eiointc_set_irq_affinity(irq_data, irq_data->common->affinity, 0);
> +                               chip = irq_data_get_irq_chip(irq_data);
> +                               chip->irq_set_affinity(irq_data, irq_data->common->affinity, 0);
>                                 raw_spin_unlock(&desc->lock);
>                         }
>                 }
> @@ -497,7 +558,14 @@ static int __init eiointc_of_init(struct device_node *of_node,
>         priv->node = 0;
>         priv->domain_handle = of_node_to_fwnode(of_node);
>
> -       ret = eiointc_init(priv, parent_irq, 0);
> +       /*
> +        * 2k0500 and 2k2000 has only one eiointc node
> +        * set nodemap as 1 for simple irq routing
> +        *
> +        * Fixme: what about future embedded boards with more than 4 cpus?
> +        * nodemap and node need be added in dts like acpi table
> +        */
> +       ret = eiointc_init(priv, parent_irq, 1);
>         if (ret < 0)
>                 goto out_free_priv;
>
> --
> 2.27.0
>
  
maobibo July 14, 2023, 3:24 a.m. UTC | #2
在 2023/7/14 11:01, Huacai Chen 写道:
> Hi, Bibo,
> 
> On Thu, Jul 13, 2023 at 11:23 AM Bibo Mao <maobibo@loongson.cn> wrote:
>>
>> Some LoongArch systems have only one eiointc node such as 3A5000/2K2000
>> and qemu virt-machine. If there is only one eiointc node, all cpus can
>> access eiointc registers directly; if there is multiple eiointc nodes, each
>> cpu can only access eiointc belonging to specified node group, so anysend
>> or ipi needs to be used to configure irq routing. IRQ routing is simple on
>> such systems with one node, hacking method like anysend is not necessary.
>>
>> This patch provides simpile IRQ routing method for systems with one eiointc
>> node, and is tested on 3A5000 board and qemu virt-machine.
>>
>> Signed-off-by: Bibo Mao <maobibo@loongson.cn>
>> ---
>>  drivers/irqchip/irq-loongson-eiointc.c | 80 ++++++++++++++++++++++++--
>>  1 file changed, 74 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c
>> index 603d323b8f8b..e6be9d6a18c8 100644
>> --- a/drivers/irqchip/irq-loongson-eiointc.c
>> +++ b/drivers/irqchip/irq-loongson-eiointc.c
>> @@ -127,6 +127,48 @@ static int eiointc_set_irq_affinity(struct irq_data *d, const struct cpumask *af
>>         return IRQ_SET_MASK_OK;
>>  }
>>
>> +static int eiointc_single_set_irq_affinity(struct irq_data *d,
>> +                               const struct cpumask *affinity, bool force)
>> +{
>> +       unsigned int cpu;
>> +       unsigned long flags;
>> +       uint32_t vector, regaddr, data, coremap;
>> +       struct cpumask mask;
>> +       struct eiointc_priv *priv = d->domain->host_data;
>> +
>> +       cpumask_and(&mask, affinity, cpu_online_mask);
>> +       cpumask_and(&mask, &mask, &priv->cpuspan_map);
>> +       if (cpumask_empty(&mask))
>> +               return -EINVAL;
>> +
>> +       cpu = cpumask_first(&mask);
>> +       vector = d->hwirq;
>> +       regaddr = EIOINTC_REG_ENABLE + ((vector >> 5) << 2);
>> +       data = ~BIT(vector & 0x1F);
>> +       coremap = BIT(cpu_logical_map(cpu) % CORES_PER_EIO_NODE);
>> +
>> +       /*
>> +        * simplify for platform with only one eiointc node
>> +        * access eiointc registers directly rather than
>> +        * use any_send method here
>> +        */
>> +       raw_spin_lock_irqsave(&affinity_lock, flags);
>> +       iocsr_write32(EIOINTC_ALL_ENABLE & data, regaddr);
>> +       /*
>> +        * get irq route info for continuous 4 vectors
>> +        * and set affinity for specified vector
>> +        */
>> +       data = iocsr_read32(EIOINTC_REG_ROUTE + (vector & ~3));
>> +       data &=  ~(0xff << ((vector & 3) * 8));
>> +       data |= coremap << ((vector & 3) * 8);
>> +       iocsr_write32(data, EIOINTC_REG_ROUTE + (vector & ~3));
>> +       iocsr_write32(EIOINTC_ALL_ENABLE, regaddr);
>> +       raw_spin_unlock_irqrestore(&affinity_lock, flags);
>> +
>> +       irq_data_update_effective_affinity(d, cpumask_of(cpu));
>> +       return IRQ_SET_MASK_OK;
>> +}
>> +
>>  static int eiointc_index(int node)
>>  {
>>         int i;
>> @@ -238,22 +280,39 @@ static struct irq_chip eiointc_irq_chip = {
>>         .irq_set_affinity       = eiointc_set_irq_affinity,
>>  };
>>
>> +static struct irq_chip eiointc_irq_chipi_single = {
> What does "chipi" mean? Maybe "chip"?
it is a typo, it should chip.

> 
> And from my point of view, the main goal of this patch is to add
> "virtual eiointc" support which is different from "real eiointc". So I
> think it is better to not touch the "real eiointc" logic, but add a
> variant for "virtual eiointc", and then, the name should be
> "EIOINTC-V", not "EIOINTC-S".
it is not for virt-machine only, it is ok for 3A5000 and embedded boards also.

If it is possible to add special eiointc for virt-machine, irq should be routed
to all vcpus rather than only vcpu 0-3 for virt eiointc model.

Regards
Bibo Mao
Only server h
> 
> Huacai
> 
>> +       .name                   = "EIOINTC-S",
>> +       .irq_ack                = eiointc_ack_irq,
>> +       .irq_mask               = eiointc_mask_irq,
>> +       .irq_unmask             = eiointc_unmask_irq,
>> +#ifdef CONFIG_SMP
>> +       .irq_set_affinity       = eiointc_single_set_irq_affinity,
>> +#endif
>> +};
>> +
>>  static int eiointc_domain_alloc(struct irq_domain *domain, unsigned int virq,
>>                                 unsigned int nr_irqs, void *arg)
>>  {
>>         int ret;
>>         unsigned int i, type;
>>         unsigned long hwirq = 0;
>> -       struct eiointc *priv = domain->host_data;
>> +       struct eiointc_priv *priv = domain->host_data;
>> +       struct irq_chip *chip;
>>
>>         ret = irq_domain_translate_onecell(domain, arg, &hwirq, &type);
>>         if (ret)
>>                 return ret;
>>
>> -       for (i = 0; i < nr_irqs; i++) {
>> -               irq_domain_set_info(domain, virq + i, hwirq + i, &eiointc_irq_chip,
>> +       /*
>> +        * use simple irq routing method on single eiointc node
>> +        */
>> +       if ((nr_pics == 1) && (nodes_weight(priv->node_map) == 1))
>> +               chip = &eiointc_irq_chipi_single;
>> +       else
>> +               chip = &eiointc_irq_chip;
>> +       for (i = 0; i < nr_irqs; i++)
>> +               irq_domain_set_info(domain, virq + i, hwirq + i, chip,
>>                                         priv, handle_edge_irq, NULL, NULL);
>> -       }
>>
>>         return 0;
>>  }
>> @@ -310,6 +369,7 @@ static void eiointc_resume(void)
>>         int i, j;
>>         struct irq_desc *desc;
>>         struct irq_data *irq_data;
>> +       struct irq_chip *chip;
>>
>>         eiointc_router_init(0);
>>
>> @@ -319,7 +379,8 @@ static void eiointc_resume(void)
>>                         if (desc && desc->handle_irq && desc->handle_irq != handle_bad_irq) {
>>                                 raw_spin_lock(&desc->lock);
>>                                 irq_data = irq_domain_get_irq_data(eiointc_priv[i]->eiointc_domain, irq_desc_get_irq(desc));
>> -                               eiointc_set_irq_affinity(irq_data, irq_data->common->affinity, 0);
>> +                               chip = irq_data_get_irq_chip(irq_data);
>> +                               chip->irq_set_affinity(irq_data, irq_data->common->affinity, 0);
>>                                 raw_spin_unlock(&desc->lock);
>>                         }
>>                 }
>> @@ -497,7 +558,14 @@ static int __init eiointc_of_init(struct device_node *of_node,
>>         priv->node = 0;
>>         priv->domain_handle = of_node_to_fwnode(of_node);
>>
>> -       ret = eiointc_init(priv, parent_irq, 0);
>> +       /*
>> +        * 2k0500 and 2k2000 has only one eiointc node
>> +        * set nodemap as 1 for simple irq routing
>> +        *
>> +        * Fixme: what about future embedded boards with more than 4 cpus?
>> +        * nodemap and node need be added in dts like acpi table
>> +        */
>> +       ret = eiointc_init(priv, parent_irq, 1);
>>         if (ret < 0)
>>                 goto out_free_priv;
>>
>> --
>> 2.27.0
>>
  
maobibo July 18, 2023, 7:01 a.m. UTC | #3
Jianmin,

Do you have any comments so that I can update in the next version together?
since you are original author of eiointc driver.

Is it possible to add extra specific version in eiointc acpi table for
virt-machine in future? For specific eiointc, irq can be routed to all
cpus rather cpu 0-3, I do not know whether it is deserved for qemu
virt-machine alone.

Regards
Bibo Mao

在 2023/7/13 11:22, Bibo Mao 写道:
> Some LoongArch systems have only one eiointc node such as 3A5000/2K2000
> and qemu virt-machine. If there is only one eiointc node, all cpus can
> access eiointc registers directly; if there is multiple eiointc nodes, each
> cpu can only access eiointc belonging to specified node group, so anysend
> or ipi needs to be used to configure irq routing. IRQ routing is simple on
> such systems with one node, hacking method like anysend is not necessary.
> 
> This patch provides simpile IRQ routing method for systems with one eiointc
> node, and is tested on 3A5000 board and qemu virt-machine.
> 
> Signed-off-by: Bibo Mao <maobibo@loongson.cn>
> ---
>  drivers/irqchip/irq-loongson-eiointc.c | 80 ++++++++++++++++++++++++--
>  1 file changed, 74 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c
> index 603d323b8f8b..e6be9d6a18c8 100644
> --- a/drivers/irqchip/irq-loongson-eiointc.c
> +++ b/drivers/irqchip/irq-loongson-eiointc.c
> @@ -127,6 +127,48 @@ static int eiointc_set_irq_affinity(struct irq_data *d, const struct cpumask *af
>  	return IRQ_SET_MASK_OK;
>  }
>  
> +static int eiointc_single_set_irq_affinity(struct irq_data *d,
> +				const struct cpumask *affinity, bool force)
> +{
> +	unsigned int cpu;
> +	unsigned long flags;
> +	uint32_t vector, regaddr, data, coremap;
> +	struct cpumask mask;
> +	struct eiointc_priv *priv = d->domain->host_data;
> +
> +	cpumask_and(&mask, affinity, cpu_online_mask);
> +	cpumask_and(&mask, &mask, &priv->cpuspan_map);
> +	if (cpumask_empty(&mask))
> +		return -EINVAL;
> +
> +	cpu = cpumask_first(&mask);
> +	vector = d->hwirq;
> +	regaddr = EIOINTC_REG_ENABLE + ((vector >> 5) << 2);
> +	data = ~BIT(vector & 0x1F);
> +	coremap = BIT(cpu_logical_map(cpu) % CORES_PER_EIO_NODE);
> +
> +	/*
> +	 * simplify for platform with only one eiointc node
> +	 * access eiointc registers directly rather than
> +	 * use any_send method here
> +	 */
> +	raw_spin_lock_irqsave(&affinity_lock, flags);
> +	iocsr_write32(EIOINTC_ALL_ENABLE & data, regaddr);
> +	/*
> +	 * get irq route info for continuous 4 vectors
> +	 * and set affinity for specified vector
> +	 */
> +	data = iocsr_read32(EIOINTC_REG_ROUTE + (vector & ~3));
> +	data &=  ~(0xff << ((vector & 3) * 8));
> +	data |= coremap << ((vector & 3) * 8);
> +	iocsr_write32(data, EIOINTC_REG_ROUTE + (vector & ~3));
> +	iocsr_write32(EIOINTC_ALL_ENABLE, regaddr);
> +	raw_spin_unlock_irqrestore(&affinity_lock, flags);
> +
> +	irq_data_update_effective_affinity(d, cpumask_of(cpu));
> +	return IRQ_SET_MASK_OK;
> +}
> +
>  static int eiointc_index(int node)
>  {
>  	int i;
> @@ -238,22 +280,39 @@ static struct irq_chip eiointc_irq_chip = {
>  	.irq_set_affinity	= eiointc_set_irq_affinity,
>  };
>  
> +static struct irq_chip eiointc_irq_chipi_single = {
> +	.name			= "EIOINTC-S",
> +	.irq_ack		= eiointc_ack_irq,
> +	.irq_mask		= eiointc_mask_irq,
> +	.irq_unmask		= eiointc_unmask_irq,
> +#ifdef CONFIG_SMP
> +	.irq_set_affinity       = eiointc_single_set_irq_affinity,
> +#endif
> +};
> +
>  static int eiointc_domain_alloc(struct irq_domain *domain, unsigned int virq,
>  				unsigned int nr_irqs, void *arg)
>  {
>  	int ret;
>  	unsigned int i, type;
>  	unsigned long hwirq = 0;
> -	struct eiointc *priv = domain->host_data;
> +	struct eiointc_priv *priv = domain->host_data;
> +	struct irq_chip *chip;
>  
>  	ret = irq_domain_translate_onecell(domain, arg, &hwirq, &type);
>  	if (ret)
>  		return ret;
>  
> -	for (i = 0; i < nr_irqs; i++) {
> -		irq_domain_set_info(domain, virq + i, hwirq + i, &eiointc_irq_chip,
> +	/*
> +	 * use simple irq routing method on single eiointc node
> +	 */
> +	if ((nr_pics == 1) && (nodes_weight(priv->node_map) == 1))
> +		chip = &eiointc_irq_chipi_single;
> +	else
> +		chip = &eiointc_irq_chip;
> +	for (i = 0; i < nr_irqs; i++)
> +		irq_domain_set_info(domain, virq + i, hwirq + i, chip,
>  					priv, handle_edge_irq, NULL, NULL);
> -	}
>  
>  	return 0;
>  }
> @@ -310,6 +369,7 @@ static void eiointc_resume(void)
>  	int i, j;
>  	struct irq_desc *desc;
>  	struct irq_data *irq_data;
> +	struct irq_chip *chip;
>  
>  	eiointc_router_init(0);
>  
> @@ -319,7 +379,8 @@ static void eiointc_resume(void)
>  			if (desc && desc->handle_irq && desc->handle_irq != handle_bad_irq) {
>  				raw_spin_lock(&desc->lock);
>  				irq_data = irq_domain_get_irq_data(eiointc_priv[i]->eiointc_domain, irq_desc_get_irq(desc));
> -				eiointc_set_irq_affinity(irq_data, irq_data->common->affinity, 0);
> +				chip = irq_data_get_irq_chip(irq_data);
> +				chip->irq_set_affinity(irq_data, irq_data->common->affinity, 0);
>  				raw_spin_unlock(&desc->lock);
>  			}
>  		}
> @@ -497,7 +558,14 @@ static int __init eiointc_of_init(struct device_node *of_node,
>  	priv->node = 0;
>  	priv->domain_handle = of_node_to_fwnode(of_node);
>  
> -	ret = eiointc_init(priv, parent_irq, 0);
> +	/*
> +	 * 2k0500 and 2k2000 has only one eiointc node
> +	 * set nodemap as 1 for simple irq routing
> +	 *
> +	 * Fixme: what about future embedded boards with more than 4 cpus?
> +	 * nodemap and node need be added in dts like acpi table
> +	 */
> +	ret = eiointc_init(priv, parent_irq, 1);
>  	if (ret < 0)
>  		goto out_free_priv;
>
  
吕建民 July 19, 2023, 3:33 a.m. UTC | #4
Hi, bibo

I think the patch has been it as we discussed offline, I have no further 
comments. And there's no plan to update EIOPIC structure in ACPI table 
since new advanced feature for LoongArch irq chip will be available in 
future.

Thanks,
Jianmin.

On 2023/7/18 下午3:01, bibo mao wrote:
> Jianmin,
>
> Do you have any comments so that I can update in the next version together?
> since you are original author of eiointc driver.
>
> Is it possible to add extra specific version in eiointc acpi table for
> virt-machine in future? For specific eiointc, irq can be routed to all
> cpus rather cpu 0-3, I do not know whether it is deserved for qemu
> virt-machine alone.
>
> Regards
> Bibo Mao
>
> 在 2023/7/13 11:22, Bibo Mao 写道:
>> Some LoongArch systems have only one eiointc node such as 3A5000/2K2000
>> and qemu virt-machine. If there is only one eiointc node, all cpus can
>> access eiointc registers directly; if there is multiple eiointc nodes, each
>> cpu can only access eiointc belonging to specified node group, so anysend
>> or ipi needs to be used to configure irq routing. IRQ routing is simple on
>> such systems with one node, hacking method like anysend is not necessary.
>>
>> This patch provides simpile IRQ routing method for systems with one eiointc
>> node, and is tested on 3A5000 board and qemu virt-machine.
>>
>> Signed-off-by: Bibo Mao <maobibo@loongson.cn>
>> ---
>>   drivers/irqchip/irq-loongson-eiointc.c | 80 ++++++++++++++++++++++++--
>>   1 file changed, 74 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c
>> index 603d323b8f8b..e6be9d6a18c8 100644
>> --- a/drivers/irqchip/irq-loongson-eiointc.c
>> +++ b/drivers/irqchip/irq-loongson-eiointc.c
>> @@ -127,6 +127,48 @@ static int eiointc_set_irq_affinity(struct irq_data *d, const struct cpumask *af
>>   	return IRQ_SET_MASK_OK;
>>   }
>>   
>> +static int eiointc_single_set_irq_affinity(struct irq_data *d,
>> +				const struct cpumask *affinity, bool force)
>> +{
>> +	unsigned int cpu;
>> +	unsigned long flags;
>> +	uint32_t vector, regaddr, data, coremap;
>> +	struct cpumask mask;
>> +	struct eiointc_priv *priv = d->domain->host_data;
>> +
>> +	cpumask_and(&mask, affinity, cpu_online_mask);
>> +	cpumask_and(&mask, &mask, &priv->cpuspan_map);
>> +	if (cpumask_empty(&mask))
>> +		return -EINVAL;
>> +
>> +	cpu = cpumask_first(&mask);
>> +	vector = d->hwirq;
>> +	regaddr = EIOINTC_REG_ENABLE + ((vector >> 5) << 2);
>> +	data = ~BIT(vector & 0x1F);
>> +	coremap = BIT(cpu_logical_map(cpu) % CORES_PER_EIO_NODE);
>> +
>> +	/*
>> +	 * simplify for platform with only one eiointc node
>> +	 * access eiointc registers directly rather than
>> +	 * use any_send method here
>> +	 */
>> +	raw_spin_lock_irqsave(&affinity_lock, flags);
>> +	iocsr_write32(EIOINTC_ALL_ENABLE & data, regaddr);
>> +	/*
>> +	 * get irq route info for continuous 4 vectors
>> +	 * and set affinity for specified vector
>> +	 */
>> +	data = iocsr_read32(EIOINTC_REG_ROUTE + (vector & ~3));
>> +	data &=  ~(0xff << ((vector & 3) * 8));
>> +	data |= coremap << ((vector & 3) * 8);
>> +	iocsr_write32(data, EIOINTC_REG_ROUTE + (vector & ~3));
>> +	iocsr_write32(EIOINTC_ALL_ENABLE, regaddr);
>> +	raw_spin_unlock_irqrestore(&affinity_lock, flags);
>> +
>> +	irq_data_update_effective_affinity(d, cpumask_of(cpu));
>> +	return IRQ_SET_MASK_OK;
>> +}
>> +
>>   static int eiointc_index(int node)
>>   {
>>   	int i;
>> @@ -238,22 +280,39 @@ static struct irq_chip eiointc_irq_chip = {
>>   	.irq_set_affinity	= eiointc_set_irq_affinity,
>>   };
>>   
>> +static struct irq_chip eiointc_irq_chipi_single = {
>> +	.name			= "EIOINTC-S",
>> +	.irq_ack		= eiointc_ack_irq,
>> +	.irq_mask		= eiointc_mask_irq,
>> +	.irq_unmask		= eiointc_unmask_irq,
>> +#ifdef CONFIG_SMP
>> +	.irq_set_affinity       = eiointc_single_set_irq_affinity,
>> +#endif
>> +};
>> +
>>   static int eiointc_domain_alloc(struct irq_domain *domain, unsigned int virq,
>>   				unsigned int nr_irqs, void *arg)
>>   {
>>   	int ret;
>>   	unsigned int i, type;
>>   	unsigned long hwirq = 0;
>> -	struct eiointc *priv = domain->host_data;
>> +	struct eiointc_priv *priv = domain->host_data;
>> +	struct irq_chip *chip;
>>   
>>   	ret = irq_domain_translate_onecell(domain, arg, &hwirq, &type);
>>   	if (ret)
>>   		return ret;
>>   
>> -	for (i = 0; i < nr_irqs; i++) {
>> -		irq_domain_set_info(domain, virq + i, hwirq + i, &eiointc_irq_chip,
>> +	/*
>> +	 * use simple irq routing method on single eiointc node
>> +	 */
>> +	if ((nr_pics == 1) && (nodes_weight(priv->node_map) == 1))
>> +		chip = &eiointc_irq_chipi_single;
>> +	else
>> +		chip = &eiointc_irq_chip;
>> +	for (i = 0; i < nr_irqs; i++)
>> +		irq_domain_set_info(domain, virq + i, hwirq + i, chip,
>>   					priv, handle_edge_irq, NULL, NULL);
>> -	}
>>   
>>   	return 0;
>>   }
>> @@ -310,6 +369,7 @@ static void eiointc_resume(void)
>>   	int i, j;
>>   	struct irq_desc *desc;
>>   	struct irq_data *irq_data;
>> +	struct irq_chip *chip;
>>   
>>   	eiointc_router_init(0);
>>   
>> @@ -319,7 +379,8 @@ static void eiointc_resume(void)
>>   			if (desc && desc->handle_irq && desc->handle_irq != handle_bad_irq) {
>>   				raw_spin_lock(&desc->lock);
>>   				irq_data = irq_domain_get_irq_data(eiointc_priv[i]->eiointc_domain, irq_desc_get_irq(desc));
>> -				eiointc_set_irq_affinity(irq_data, irq_data->common->affinity, 0);
>> +				chip = irq_data_get_irq_chip(irq_data);
>> +				chip->irq_set_affinity(irq_data, irq_data->common->affinity, 0);
>>   				raw_spin_unlock(&desc->lock);
>>   			}
>>   		}
>> @@ -497,7 +558,14 @@ static int __init eiointc_of_init(struct device_node *of_node,
>>   	priv->node = 0;
>>   	priv->domain_handle = of_node_to_fwnode(of_node);
>>   
>> -	ret = eiointc_init(priv, parent_irq, 0);
>> +	/*
>> +	 * 2k0500 and 2k2000 has only one eiointc node
>> +	 * set nodemap as 1 for simple irq routing
>> +	 *
>> +	 * Fixme: what about future embedded boards with more than 4 cpus?
>> +	 * nodemap and node need be added in dts like acpi table
>> +	 */
>> +	ret = eiointc_init(priv, parent_irq, 1);
>>   	if (ret < 0)
>>   		goto out_free_priv;
>>
  
maobibo July 19, 2023, 3:54 a.m. UTC | #5
Jianmin,

Thank for your reply.

OK, qemu virt-machine eiointc model will be consistent with physical machine.
And let's wait for new LoongArch irqchip in future.

Regards
Bibo Mao

在 2023/7/19 11:33, Jianmin Lv 写道:
> Hi, bibo
> 
> I think the patch has been it as we discussed offline, I have no further comments. And there's no plan to update EIOPIC structure in ACPI table since new advanced feature for LoongArch irq chip will be available in future.
> 
> Thanks,
> Jianmin.
> 
> On 2023/7/18 下午3:01, bibo mao wrote:
>> Jianmin,
>>
>> Do you have any comments so that I can update in the next version together?
>> since you are original author of eiointc driver.
>>
>> Is it possible to add extra specific version in eiointc acpi table for
>> virt-machine in future? For specific eiointc, irq can be routed to all
>> cpus rather cpu 0-3, I do not know whether it is deserved for qemu
>> virt-machine alone.
>>
>> Regards
>> Bibo Mao
>>
>> 在 2023/7/13 11:22, Bibo Mao 写道:
>>> Some LoongArch systems have only one eiointc node such as 3A5000/2K2000
>>> and qemu virt-machine. If there is only one eiointc node, all cpus can
>>> access eiointc registers directly; if there is multiple eiointc nodes, each
>>> cpu can only access eiointc belonging to specified node group, so anysend
>>> or ipi needs to be used to configure irq routing. IRQ routing is simple on
>>> such systems with one node, hacking method like anysend is not necessary.
>>>
>>> This patch provides simpile IRQ routing method for systems with one eiointc
>>> node, and is tested on 3A5000 board and qemu virt-machine.
>>>
>>> Signed-off-by: Bibo Mao <maobibo@loongson.cn>
>>> ---
>>>   drivers/irqchip/irq-loongson-eiointc.c | 80 ++++++++++++++++++++++++--
>>>   1 file changed, 74 insertions(+), 6 deletions(-)
>>>
>>> diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c
>>> index 603d323b8f8b..e6be9d6a18c8 100644
>>> --- a/drivers/irqchip/irq-loongson-eiointc.c
>>> +++ b/drivers/irqchip/irq-loongson-eiointc.c
>>> @@ -127,6 +127,48 @@ static int eiointc_set_irq_affinity(struct irq_data *d, const struct cpumask *af
>>>       return IRQ_SET_MASK_OK;
>>>   }
>>>   +static int eiointc_single_set_irq_affinity(struct irq_data *d,
>>> +                const struct cpumask *affinity, bool force)
>>> +{
>>> +    unsigned int cpu;
>>> +    unsigned long flags;
>>> +    uint32_t vector, regaddr, data, coremap;
>>> +    struct cpumask mask;
>>> +    struct eiointc_priv *priv = d->domain->host_data;
>>> +
>>> +    cpumask_and(&mask, affinity, cpu_online_mask);
>>> +    cpumask_and(&mask, &mask, &priv->cpuspan_map);
>>> +    if (cpumask_empty(&mask))
>>> +        return -EINVAL;
>>> +
>>> +    cpu = cpumask_first(&mask);
>>> +    vector = d->hwirq;
>>> +    regaddr = EIOINTC_REG_ENABLE + ((vector >> 5) << 2);
>>> +    data = ~BIT(vector & 0x1F);
>>> +    coremap = BIT(cpu_logical_map(cpu) % CORES_PER_EIO_NODE);
>>> +
>>> +    /*
>>> +     * simplify for platform with only one eiointc node
>>> +     * access eiointc registers directly rather than
>>> +     * use any_send method here
>>> +     */
>>> +    raw_spin_lock_irqsave(&affinity_lock, flags);
>>> +    iocsr_write32(EIOINTC_ALL_ENABLE & data, regaddr);
>>> +    /*
>>> +     * get irq route info for continuous 4 vectors
>>> +     * and set affinity for specified vector
>>> +     */
>>> +    data = iocsr_read32(EIOINTC_REG_ROUTE + (vector & ~3));
>>> +    data &=  ~(0xff << ((vector & 3) * 8));
>>> +    data |= coremap << ((vector & 3) * 8);
>>> +    iocsr_write32(data, EIOINTC_REG_ROUTE + (vector & ~3));
>>> +    iocsr_write32(EIOINTC_ALL_ENABLE, regaddr);
>>> +    raw_spin_unlock_irqrestore(&affinity_lock, flags);
>>> +
>>> +    irq_data_update_effective_affinity(d, cpumask_of(cpu));
>>> +    return IRQ_SET_MASK_OK;
>>> +}
>>> +
>>>   static int eiointc_index(int node)
>>>   {
>>>       int i;
>>> @@ -238,22 +280,39 @@ static struct irq_chip eiointc_irq_chip = {
>>>       .irq_set_affinity    = eiointc_set_irq_affinity,
>>>   };
>>>   +static struct irq_chip eiointc_irq_chipi_single = {
>>> +    .name            = "EIOINTC-S",
>>> +    .irq_ack        = eiointc_ack_irq,
>>> +    .irq_mask        = eiointc_mask_irq,
>>> +    .irq_unmask        = eiointc_unmask_irq,
>>> +#ifdef CONFIG_SMP
>>> +    .irq_set_affinity       = eiointc_single_set_irq_affinity,
>>> +#endif
>>> +};
>>> +
>>>   static int eiointc_domain_alloc(struct irq_domain *domain, unsigned int virq,
>>>                   unsigned int nr_irqs, void *arg)
>>>   {
>>>       int ret;
>>>       unsigned int i, type;
>>>       unsigned long hwirq = 0;
>>> -    struct eiointc *priv = domain->host_data;
>>> +    struct eiointc_priv *priv = domain->host_data;
>>> +    struct irq_chip *chip;
>>>         ret = irq_domain_translate_onecell(domain, arg, &hwirq, &type);
>>>       if (ret)
>>>           return ret;
>>>   -    for (i = 0; i < nr_irqs; i++) {
>>> -        irq_domain_set_info(domain, virq + i, hwirq + i, &eiointc_irq_chip,
>>> +    /*
>>> +     * use simple irq routing method on single eiointc node
>>> +     */
>>> +    if ((nr_pics == 1) && (nodes_weight(priv->node_map) == 1))
>>> +        chip = &eiointc_irq_chipi_single;
>>> +    else
>>> +        chip = &eiointc_irq_chip;
>>> +    for (i = 0; i < nr_irqs; i++)
>>> +        irq_domain_set_info(domain, virq + i, hwirq + i, chip,
>>>                       priv, handle_edge_irq, NULL, NULL);
>>> -    }
>>>         return 0;
>>>   }
>>> @@ -310,6 +369,7 @@ static void eiointc_resume(void)
>>>       int i, j;
>>>       struct irq_desc *desc;
>>>       struct irq_data *irq_data;
>>> +    struct irq_chip *chip;
>>>         eiointc_router_init(0);
>>>   @@ -319,7 +379,8 @@ static void eiointc_resume(void)
>>>               if (desc && desc->handle_irq && desc->handle_irq != handle_bad_irq) {
>>>                   raw_spin_lock(&desc->lock);
>>>                   irq_data = irq_domain_get_irq_data(eiointc_priv[i]->eiointc_domain, irq_desc_get_irq(desc));
>>> -                eiointc_set_irq_affinity(irq_data, irq_data->common->affinity, 0);
>>> +                chip = irq_data_get_irq_chip(irq_data);
>>> +                chip->irq_set_affinity(irq_data, irq_data->common->affinity, 0);
>>>                   raw_spin_unlock(&desc->lock);
>>>               }
>>>           }
>>> @@ -497,7 +558,14 @@ static int __init eiointc_of_init(struct device_node *of_node,
>>>       priv->node = 0;
>>>       priv->domain_handle = of_node_to_fwnode(of_node);
>>>   -    ret = eiointc_init(priv, parent_irq, 0);
>>> +    /*
>>> +     * 2k0500 and 2k2000 has only one eiointc node
>>> +     * set nodemap as 1 for simple irq routing
>>> +     *
>>> +     * Fixme: what about future embedded boards with more than 4 cpus?
>>> +     * nodemap and node need be added in dts like acpi table
>>> +     */
>>> +    ret = eiointc_init(priv, parent_irq, 1);
>>>       if (ret < 0)
>>>           goto out_free_priv;
>>>   
>
  

Patch

diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c
index 603d323b8f8b..e6be9d6a18c8 100644
--- a/drivers/irqchip/irq-loongson-eiointc.c
+++ b/drivers/irqchip/irq-loongson-eiointc.c
@@ -127,6 +127,48 @@  static int eiointc_set_irq_affinity(struct irq_data *d, const struct cpumask *af
 	return IRQ_SET_MASK_OK;
 }
 
+static int eiointc_single_set_irq_affinity(struct irq_data *d,
+				const struct cpumask *affinity, bool force)
+{
+	unsigned int cpu;
+	unsigned long flags;
+	uint32_t vector, regaddr, data, coremap;
+	struct cpumask mask;
+	struct eiointc_priv *priv = d->domain->host_data;
+
+	cpumask_and(&mask, affinity, cpu_online_mask);
+	cpumask_and(&mask, &mask, &priv->cpuspan_map);
+	if (cpumask_empty(&mask))
+		return -EINVAL;
+
+	cpu = cpumask_first(&mask);
+	vector = d->hwirq;
+	regaddr = EIOINTC_REG_ENABLE + ((vector >> 5) << 2);
+	data = ~BIT(vector & 0x1F);
+	coremap = BIT(cpu_logical_map(cpu) % CORES_PER_EIO_NODE);
+
+	/*
+	 * simplify for platform with only one eiointc node
+	 * access eiointc registers directly rather than
+	 * use any_send method here
+	 */
+	raw_spin_lock_irqsave(&affinity_lock, flags);
+	iocsr_write32(EIOINTC_ALL_ENABLE & data, regaddr);
+	/*
+	 * get irq route info for continuous 4 vectors
+	 * and set affinity for specified vector
+	 */
+	data = iocsr_read32(EIOINTC_REG_ROUTE + (vector & ~3));
+	data &=  ~(0xff << ((vector & 3) * 8));
+	data |= coremap << ((vector & 3) * 8);
+	iocsr_write32(data, EIOINTC_REG_ROUTE + (vector & ~3));
+	iocsr_write32(EIOINTC_ALL_ENABLE, regaddr);
+	raw_spin_unlock_irqrestore(&affinity_lock, flags);
+
+	irq_data_update_effective_affinity(d, cpumask_of(cpu));
+	return IRQ_SET_MASK_OK;
+}
+
 static int eiointc_index(int node)
 {
 	int i;
@@ -238,22 +280,39 @@  static struct irq_chip eiointc_irq_chip = {
 	.irq_set_affinity	= eiointc_set_irq_affinity,
 };
 
+static struct irq_chip eiointc_irq_chipi_single = {
+	.name			= "EIOINTC-S",
+	.irq_ack		= eiointc_ack_irq,
+	.irq_mask		= eiointc_mask_irq,
+	.irq_unmask		= eiointc_unmask_irq,
+#ifdef CONFIG_SMP
+	.irq_set_affinity       = eiointc_single_set_irq_affinity,
+#endif
+};
+
 static int eiointc_domain_alloc(struct irq_domain *domain, unsigned int virq,
 				unsigned int nr_irqs, void *arg)
 {
 	int ret;
 	unsigned int i, type;
 	unsigned long hwirq = 0;
-	struct eiointc *priv = domain->host_data;
+	struct eiointc_priv *priv = domain->host_data;
+	struct irq_chip *chip;
 
 	ret = irq_domain_translate_onecell(domain, arg, &hwirq, &type);
 	if (ret)
 		return ret;
 
-	for (i = 0; i < nr_irqs; i++) {
-		irq_domain_set_info(domain, virq + i, hwirq + i, &eiointc_irq_chip,
+	/*
+	 * use simple irq routing method on single eiointc node
+	 */
+	if ((nr_pics == 1) && (nodes_weight(priv->node_map) == 1))
+		chip = &eiointc_irq_chipi_single;
+	else
+		chip = &eiointc_irq_chip;
+	for (i = 0; i < nr_irqs; i++)
+		irq_domain_set_info(domain, virq + i, hwirq + i, chip,
 					priv, handle_edge_irq, NULL, NULL);
-	}
 
 	return 0;
 }
@@ -310,6 +369,7 @@  static void eiointc_resume(void)
 	int i, j;
 	struct irq_desc *desc;
 	struct irq_data *irq_data;
+	struct irq_chip *chip;
 
 	eiointc_router_init(0);
 
@@ -319,7 +379,8 @@  static void eiointc_resume(void)
 			if (desc && desc->handle_irq && desc->handle_irq != handle_bad_irq) {
 				raw_spin_lock(&desc->lock);
 				irq_data = irq_domain_get_irq_data(eiointc_priv[i]->eiointc_domain, irq_desc_get_irq(desc));
-				eiointc_set_irq_affinity(irq_data, irq_data->common->affinity, 0);
+				chip = irq_data_get_irq_chip(irq_data);
+				chip->irq_set_affinity(irq_data, irq_data->common->affinity, 0);
 				raw_spin_unlock(&desc->lock);
 			}
 		}
@@ -497,7 +558,14 @@  static int __init eiointc_of_init(struct device_node *of_node,
 	priv->node = 0;
 	priv->domain_handle = of_node_to_fwnode(of_node);
 
-	ret = eiointc_init(priv, parent_irq, 0);
+	/*
+	 * 2k0500 and 2k2000 has only one eiointc node
+	 * set nodemap as 1 for simple irq routing
+	 *
+	 * Fixme: what about future embedded boards with more than 4 cpus?
+	 * nodemap and node need be added in dts like acpi table
+	 */
+	ret = eiointc_init(priv, parent_irq, 1);
 	if (ret < 0)
 		goto out_free_priv;