diff mbox series

[v6] arm pl011 serial: support multi-irq request

Message ID 1628825490-18937-1-git-send-email-hptsfb@gmail.com
State New
Headers show
Series [v6] arm pl011 serial: support multi-irq request | expand

Commit Message

Bing Fan Aug. 13, 2021, 3:31 a.m. UTC
From: Bing Fan <tombinfan@tencent.com>

In order to make pl011 work better, multiple interrupts are
required, such as TXIM, RXIM, RTIM, error interrupt(FE/PE/BE/OE);
at the same time, pl011 to GIC does not merge the interrupt
lines(each serial-interrupt corresponding to different GIC hardware
interrupt), so need to enable and request multiple gic interrupt
numbers in the driver.

Signed-off-by: Bing Fan <tombinfan@tencent.com>
---
 drivers/tty/serial/amba-pl011.c | 39 +++++++++++++++++++++++++++++++--
 1 file changed, 37 insertions(+), 2 deletions(-)

Comments

Greg Kroah-Hartman Aug. 13, 2021, 7:19 a.m. UTC | #1
On Fri, Aug 13, 2021 at 11:31:30AM +0800, Bing Fan wrote:
> From: Bing Fan <tombinfan@tencent.com>
> 
> In order to make pl011 work better, multiple interrupts are
> required, such as TXIM, RXIM, RTIM, error interrupt(FE/PE/BE/OE);
> at the same time, pl011 to GIC does not merge the interrupt
> lines(each serial-interrupt corresponding to different GIC hardware
> interrupt), so need to enable and request multiple gic interrupt
> numbers in the driver.
> 
> Signed-off-by: Bing Fan <tombinfan@tencent.com>
> ---
>  drivers/tty/serial/amba-pl011.c | 39 +++++++++++++++++++++++++++++++--
>  1 file changed, 37 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
> index e14f3378b8a0..eaac3431459c 100644
> --- a/drivers/tty/serial/amba-pl011.c
> +++ b/drivers/tty/serial/amba-pl011.c
> @@ -1701,6 +1701,41 @@ static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h)
>  	}
>  }
>  
> +static void pl011_release_multi_irqs(struct uart_amba_port *uap, unsigned int max_cnt)
> +{
> +	struct amba_device *amba_dev = container_of(uap->port.dev, struct amba_device, dev);
> +	int i;
> +
> +	for (i = 0; i < max_cnt; i++)
> +		if (amba_dev->irq[i])
> +			free_irq(amba_dev->irq[i], uap);
> +}
> +
> +static int pl011_allocate_multi_irqs(struct uart_amba_port *uap)
> +{
> +	int ret = 0;
> +	int i;
> +	unsigned int virq;
> +	struct amba_device *amba_dev = container_of(uap->port.dev, struct amba_device, dev);
> +
> +	pl011_write(uap->im, uap, REG_IMSC);
> +
> +	for (i = 0; i < AMBA_NR_IRQS; i++) {
> +		virq = amba_dev->irq[i];
> +		if (virq == 0)
> +			break;
> +
> +		ret = request_irq(virq, pl011_int, IRQF_SHARED, dev_name(&amba_dev->dev), uap);
> +		if (ret) {
> +			dev_err(uap->port.dev, "request %u interrupt failed\n", virq);
> +			pl011_release_multi_irqs(uap, i - 1);
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}

This function looks identical to pl011_allocate_irq(), so what is the
difference here?  Why is this still needed at all?  What does it do that
is different from pl011_allocate_irq()?

confused,

greg k-h
Greg Kroah-Hartman Aug. 13, 2021, 8:17 a.m. UTC | #2
On Fri, Aug 13, 2021 at 03:56:01PM +0800, Bing Fan wrote:
> 
> 在 8/13/2021 15:19, Greg KH 写道:
> > On Fri, Aug 13, 2021 at 11:31:30AM +0800, Bing Fan wrote:
> > > From: Bing Fan <tombinfan@tencent.com>
> > > 
> > > In order to make pl011 work better, multiple interrupts are
> > > required, such as TXIM, RXIM, RTIM, error interrupt(FE/PE/BE/OE);
> > > at the same time, pl011 to GIC does not merge the interrupt
> > > lines(each serial-interrupt corresponding to different GIC hardware
> > > interrupt), so need to enable and request multiple gic interrupt
> > > numbers in the driver.
> > > 
> > > Signed-off-by: Bing Fan <tombinfan@tencent.com>
> > > ---
> > >   drivers/tty/serial/amba-pl011.c | 39 +++++++++++++++++++++++++++++++--
> > >   1 file changed, 37 insertions(+), 2 deletions(-)
> > > 
> > > diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
> > > index e14f3378b8a0..eaac3431459c 100644
> > > --- a/drivers/tty/serial/amba-pl011.c
> > > +++ b/drivers/tty/serial/amba-pl011.c
> > > @@ -1701,6 +1701,41 @@ static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h)
> > >   	}
> > >   }
> > > +static void pl011_release_multi_irqs(struct uart_amba_port *uap, unsigned int max_cnt)
> > > +{
> > > +	struct amba_device *amba_dev = container_of(uap->port.dev, struct amba_device, dev);
> > > +	int i;
> > > +
> > > +	for (i = 0; i < max_cnt; i++)
> > > +		if (amba_dev->irq[i])
> > > +			free_irq(amba_dev->irq[i], uap);
> > > +}
> > > +
> > > +static int pl011_allocate_multi_irqs(struct uart_amba_port *uap)
> > > +{
> > > +	int ret = 0;
> > > +	int i;
> > > +	unsigned int virq;
> > > +	struct amba_device *amba_dev = container_of(uap->port.dev, struct amba_device, dev);
> > > +
> > > +	pl011_write(uap->im, uap, REG_IMSC);
> > > +
> > > +	for (i = 0; i < AMBA_NR_IRQS; i++) {
> > > +		virq = amba_dev->irq[i];
> > > +		if (virq == 0)
> > > +			break;
> > > +
> > > +		ret = request_irq(virq, pl011_int, IRQF_SHARED, dev_name(&amba_dev->dev), uap);
> > > +		if (ret) {
> > > +			dev_err(uap->port.dev, "request %u interrupt failed\n", virq);
> > > +			pl011_release_multi_irqs(uap, i - 1);
> > > +			break;
> > > +		}
> > > +	}
> > > +
> > > +	return ret;
> > > +}
> > This function looks identical to pl011_allocate_irq(), so what is the
> > difference here?  Why is this still needed at all?  What does it do that
> > is different from pl011_allocate_irq()?
> 
> The v6-patch is based on master of
> git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git, not tty-next.

Always submit patches based on tty-next if you want them accepted into
that tree.

> As below, the pl011_allocate_irq function supports single irq request only,
> which different from pl011_allocate_multi_irqs.
> 
> static int pl011_allocate_irq(struct uart_amba_port *uap)
> {
>     pl011_write(uap->im, uap, REG_IMSC);
> 
>     return request_irq(uap->port.irq, pl011_int, IRQF_SHARED, "uart-pl011",
> uap);
> }

Ok, but that does not reflect what is in my tree to be merged for
5.15-rc1.  What is wrong with the code in there that is incorrect and
needs to be changed?

thanks,

greg k-h
Greg Kroah-Hartman Aug. 13, 2021, 3:04 p.m. UTC | #3
On Fri, Aug 13, 2021 at 03:08:48PM +0100, Robin Murphy wrote:
> Hi Greg,
> 
> On 2021-08-13 09:17, Greg KH wrote:
> > On Fri, Aug 13, 2021 at 03:56:01PM +0800, Bing Fan wrote:
> > > 
> > > 在 8/13/2021 15:19, Greg KH 写道:
> > > > On Fri, Aug 13, 2021 at 11:31:30AM +0800, Bing Fan wrote:
> > > > > From: Bing Fan <tombinfan@tencent.com>
> > > > > 
> > > > > In order to make pl011 work better, multiple interrupts are
> > > > > required, such as TXIM, RXIM, RTIM, error interrupt(FE/PE/BE/OE);
> > > > > at the same time, pl011 to GIC does not merge the interrupt
> > > > > lines(each serial-interrupt corresponding to different GIC hardware
> > > > > interrupt), so need to enable and request multiple gic interrupt
> > > > > numbers in the driver.
> > > > > 
> > > > > Signed-off-by: Bing Fan <tombinfan@tencent.com>
> > > > > ---
> > > > >    drivers/tty/serial/amba-pl011.c | 39 +++++++++++++++++++++++++++++++--
> > > > >    1 file changed, 37 insertions(+), 2 deletions(-)
> > > > > 
> > > > > diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
> > > > > index e14f3378b8a0..eaac3431459c 100644
> > > > > --- a/drivers/tty/serial/amba-pl011.c
> > > > > +++ b/drivers/tty/serial/amba-pl011.c
> > > > > @@ -1701,6 +1701,41 @@ static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h)
> > > > >    	}
> > > > >    }
> > > > > +static void pl011_release_multi_irqs(struct uart_amba_port *uap, unsigned int max_cnt)
> > > > > +{
> > > > > +	struct amba_device *amba_dev = container_of(uap->port.dev, struct amba_device, dev);
> > > > > +	int i;
> > > > > +
> > > > > +	for (i = 0; i < max_cnt; i++)
> > > > > +		if (amba_dev->irq[i])
> > > > > +			free_irq(amba_dev->irq[i], uap);
> > > > > +}
> > > > > +
> > > > > +static int pl011_allocate_multi_irqs(struct uart_amba_port *uap)
> > > > > +{
> > > > > +	int ret = 0;
> > > > > +	int i;
> > > > > +	unsigned int virq;
> > > > > +	struct amba_device *amba_dev = container_of(uap->port.dev, struct amba_device, dev);
> > > > > +
> > > > > +	pl011_write(uap->im, uap, REG_IMSC);
> > > > > +
> > > > > +	for (i = 0; i < AMBA_NR_IRQS; i++) {
> > > > > +		virq = amba_dev->irq[i];
> > > > > +		if (virq == 0)
> > > > > +			break;
> > > > > +
> > > > > +		ret = request_irq(virq, pl011_int, IRQF_SHARED, dev_name(&amba_dev->dev), uap);
> > > > > +		if (ret) {
> > > > > +			dev_err(uap->port.dev, "request %u interrupt failed\n", virq);
> > > > > +			pl011_release_multi_irqs(uap, i - 1);
> > > > > +			break;
> > > > > +		}
> > > > > +	}
> > > > > +
> > > > > +	return ret;
> > > > > +}
> > > > This function looks identical to pl011_allocate_irq(), so what is the
> > > > difference here?  Why is this still needed at all?  What does it do that
> > > > is different from pl011_allocate_irq()?
> > > 
> > > The v6-patch is based on master of
> > > git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git, not tty-next.
> > 
> > Always submit patches based on tty-next if you want them accepted into
> > that tree.
> > 
> > > As below, the pl011_allocate_irq function supports single irq request only,
> > > which different from pl011_allocate_multi_irqs.
> > > 
> > > static int pl011_allocate_irq(struct uart_amba_port *uap)
> > > {
> > >      pl011_write(uap->im, uap, REG_IMSC);
> > > 
> > >      return request_irq(uap->port.irq, pl011_int, IRQF_SHARED, "uart-pl011",
> > > uap);
> > > }
> > 
> > Ok, but that does not reflect what is in my tree to be merged for
> > 5.15-rc1.  What is wrong with the code in there that is incorrect and
> > needs to be changed?
> 
> As reported by Qian Cai, it blows up on ACPI-based systems by assuming
> port.dev is an amba_device when in fact in that situation it's a
> platform_device. If you're able to drop the current patch from your tree
> that would probably be the best thing to do for the moment.

I have not seen any such bug report.

If something needs to be reverted in linux-next, (i.e. in my tty-next
branch), please let me know.  Ideally by sending a pathc to do so...

thanks,

greg k-h
Robin Murphy Aug. 13, 2021, 3:17 p.m. UTC | #4
On 2021-08-13 16:04, Greg KH wrote:
> On Fri, Aug 13, 2021 at 03:08:48PM +0100, Robin Murphy wrote:
>> Hi Greg,
>>
>> On 2021-08-13 09:17, Greg KH wrote:
>>> On Fri, Aug 13, 2021 at 03:56:01PM +0800, Bing Fan wrote:
>>>>
>>>> 在 8/13/2021 15:19, Greg KH 写道:
>>>>> On Fri, Aug 13, 2021 at 11:31:30AM +0800, Bing Fan wrote:
>>>>>> From: Bing Fan <tombinfan@tencent.com>
>>>>>>
>>>>>> In order to make pl011 work better, multiple interrupts are
>>>>>> required, such as TXIM, RXIM, RTIM, error interrupt(FE/PE/BE/OE);
>>>>>> at the same time, pl011 to GIC does not merge the interrupt
>>>>>> lines(each serial-interrupt corresponding to different GIC hardware
>>>>>> interrupt), so need to enable and request multiple gic interrupt
>>>>>> numbers in the driver.
>>>>>>
>>>>>> Signed-off-by: Bing Fan <tombinfan@tencent.com>
>>>>>> ---
>>>>>>     drivers/tty/serial/amba-pl011.c | 39 +++++++++++++++++++++++++++++++--
>>>>>>     1 file changed, 37 insertions(+), 2 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
>>>>>> index e14f3378b8a0..eaac3431459c 100644
>>>>>> --- a/drivers/tty/serial/amba-pl011.c
>>>>>> +++ b/drivers/tty/serial/amba-pl011.c
>>>>>> @@ -1701,6 +1701,41 @@ static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h)
>>>>>>     	}
>>>>>>     }
>>>>>> +static void pl011_release_multi_irqs(struct uart_amba_port *uap, unsigned int max_cnt)
>>>>>> +{
>>>>>> +	struct amba_device *amba_dev = container_of(uap->port.dev, struct amba_device, dev);
>>>>>> +	int i;
>>>>>> +
>>>>>> +	for (i = 0; i < max_cnt; i++)
>>>>>> +		if (amba_dev->irq[i])
>>>>>> +			free_irq(amba_dev->irq[i], uap);
>>>>>> +}
>>>>>> +
>>>>>> +static int pl011_allocate_multi_irqs(struct uart_amba_port *uap)
>>>>>> +{
>>>>>> +	int ret = 0;
>>>>>> +	int i;
>>>>>> +	unsigned int virq;
>>>>>> +	struct amba_device *amba_dev = container_of(uap->port.dev, struct amba_device, dev);
>>>>>> +
>>>>>> +	pl011_write(uap->im, uap, REG_IMSC);
>>>>>> +
>>>>>> +	for (i = 0; i < AMBA_NR_IRQS; i++) {
>>>>>> +		virq = amba_dev->irq[i];
>>>>>> +		if (virq == 0)
>>>>>> +			break;
>>>>>> +
>>>>>> +		ret = request_irq(virq, pl011_int, IRQF_SHARED, dev_name(&amba_dev->dev), uap);
>>>>>> +		if (ret) {
>>>>>> +			dev_err(uap->port.dev, "request %u interrupt failed\n", virq);
>>>>>> +			pl011_release_multi_irqs(uap, i - 1);
>>>>>> +			break;
>>>>>> +		}
>>>>>> +	}
>>>>>> +
>>>>>> +	return ret;
>>>>>> +}
>>>>> This function looks identical to pl011_allocate_irq(), so what is the
>>>>> difference here?  Why is this still needed at all?  What does it do that
>>>>> is different from pl011_allocate_irq()?
>>>>
>>>> The v6-patch is based on master of
>>>> git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git, not tty-next.
>>>
>>> Always submit patches based on tty-next if you want them accepted into
>>> that tree.
>>>
>>>> As below, the pl011_allocate_irq function supports single irq request only,
>>>> which different from pl011_allocate_multi_irqs.
>>>>
>>>> static int pl011_allocate_irq(struct uart_amba_port *uap)
>>>> {
>>>>       pl011_write(uap->im, uap, REG_IMSC);
>>>>
>>>>       return request_irq(uap->port.irq, pl011_int, IRQF_SHARED, "uart-pl011",
>>>> uap);
>>>> }
>>>
>>> Ok, but that does not reflect what is in my tree to be merged for
>>> 5.15-rc1.  What is wrong with the code in there that is incorrect and
>>> needs to be changed?
>>
>> As reported by Qian Cai, it blows up on ACPI-based systems by assuming
>> port.dev is an amba_device when in fact in that situation it's a
>> platform_device. If you're able to drop the current patch from your tree
>> that would probably be the best thing to do for the moment.
> 
> I have not seen any such bug report.

It's the thread on the v5 patch that you've just replied to ;)

> If something needs to be reverted in linux-next, (i.e. in my tty-next
> branch), please let me know.  Ideally by sending a pathc to do so...

Since I'm only really involved here off the back of a drive-by review 
comment, I'll leave that up to Qian.

Cheers,
Robin.
Robin Murphy Aug. 16, 2021, 10:19 a.m. UTC | #5
On 2021-08-16 08:42, Bing Fan wrote:
> 

> At present, i think a focus of our discussion is whether this patch is 

> necessary.

> 

> As for the other points you mentioned, I think they can be used as code 

> review comments.

> 

> 

> Yes, as you described below, most dts files have only one interrupt, but 

> not all platforms are like this.

> 

> The scene I'm encountering now is the latter: the interrupt lines of the 

> uart is connected to the gic separately

> 

> so the dts should be define like this:

> 

>                 duart1: serial@5E139000 {

>                          compatible = "arm,pl011", "arm,primecell";

>                          reg = <0x00 0x5E139000 0x0 0x1000>;

>                          interrupts = <GIC_SPI 178 IRQ_TYPE_LEVEL_HIGH>,

>                                  <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH>,

>                                  <GIC_SPI 180 IRQ_TYPE_LEVEL_HIGH>,

>                                  <GIC_SPI 181 IRQ_TYPE_LEVEL_HIGH>;

>                          clocks = <&sysclk>;

>                          clock-names = "apb_pclk";

>                  };


Apologies for being unclear - the point I was implying is that of course 
you can do that in practice, but if you run that DTS through `make 
dtbs_check` it will fail. The binding needs extending to make it valid 
to specify more than one interrupt, and that's a separate patch and 
discussion in itself (simply increasing "maxitems" for the "interrupts" 
property is not enough to be robust).

Robin.

> The current tty-master code cannot meet this scenario, so I submitted 

> this patch.

> 

> 

> 

> 

> 

> 在 2021/8/13 下午10:37, Robin Murphy 写道:

>> [ +Russell as the listed PL011 maintainer ]

>>

>> On 2021-08-13 04:31, Bing Fan wrote:

>>> From: Bing Fan <tombinfan@tencent.com>

>>>

>>> In order to make pl011 work better, multiple interrupts are

>>> required, such as TXIM, RXIM, RTIM, error interrupt(FE/PE/BE/OE);

>>> at the same time, pl011 to GIC does not merge the interrupt

>>> lines(each serial-interrupt corresponding to different GIC hardware

>>> interrupt), so need to enable and request multiple gic interrupt

>>> numbers in the driver.

>>>

>>> Signed-off-by: Bing Fan <tombinfan@tencent.com>

>>> ---

>>>   drivers/tty/serial/amba-pl011.c | 39 +++++++++++++++++++++++++++++++--

>>>   1 file changed, 37 insertions(+), 2 deletions(-)

>>>

>>> diff --git a/drivers/tty/serial/amba-pl011.c 

>>> b/drivers/tty/serial/amba-pl011.c

>>> index e14f3378b8a0..eaac3431459c 100644

>>> --- a/drivers/tty/serial/amba-pl011.c

>>> +++ b/drivers/tty/serial/amba-pl011.c

>>> @@ -1701,6 +1701,41 @@ static void pl011_write_lcr_h(struct 

>>> uart_amba_port *uap, unsigned int lcr_h)

>>>       }

>>>   }

>>>   +static void pl011_release_multi_irqs(struct uart_amba_port *uap, 

>>> unsigned int max_cnt)

>>> +{

>>> +    struct amba_device *amba_dev = container_of(uap->port.dev, 

>>> struct amba_device, dev);

>>> +    int i;

>>> +

>>> +    for (i = 0; i < max_cnt; i++)

>>> +        if (amba_dev->irq[i])

>>> +            free_irq(amba_dev->irq[i], uap);

>>

>> When you request the IRQs you break at the first zero, so this could 

>> potentially try to free IRQs that you haven't requested, if there 

>> happen to be any nonzero values beyond that. Maybe that can never 

>> happen, but there seems little need for deliberate inconsistency here.

>>

>>> +}

>>> +

>>> +static int pl011_allocate_multi_irqs(struct uart_amba_port *uap)

>>> +{

>>> +    int ret = 0;

>>> +    int i;

>>> +    unsigned int virq;

>>> +    struct amba_device *amba_dev = container_of(uap->port.dev, 

>>> struct amba_device, dev);

>>> +

>>> +    pl011_write(uap->im, uap, REG_IMSC);

>>> +

>>> +    for (i = 0; i < AMBA_NR_IRQS; i++) {

>>

>> It's not clear where these extra IRQs are expected to come from given 

>> that the DT binding explicitly defines only one :/

>>

>>> +        virq = amba_dev->irq[i];

>>> +        if (virq == 0)

>>> +            break;

>>> +

>>> +        ret = request_irq(virq, pl011_int, IRQF_SHARED, 

>>> dev_name(&amba_dev->dev), uap);

>>

>> Note that using dev_name() here technically breaks user ABI - scripts 

>> looking in /proc for an irq named "uart-pl011" will no longer find it.

>>

>> Furthermore, the "dev" cookie passed to request_irq is supposed to be 

>> globally unique, which "uap" isn't once you start registering it 

>> multiple times. If firmware did describe all the individual PL011 IRQ 

>> outputs on a system where they are muxed to the same physical IRQ 

>> anyway, you'd end up registering ambiguous IRQ actions here. Of course 

>> in practice you might still get away with that, but it is technically 

>> wrong.

>>

>> Robin.

>>

>>> +        if (ret) {

>>> +            dev_err(uap->port.dev, "request %u interrupt failed\n", 

>>> virq);

>>> +            pl011_release_multi_irqs(uap, i - 1);

>>> +            break;

>>> +        }

>>> +    }

>>> +

>>> +    return ret;

>>> +}

>>> +

>>>   static int pl011_allocate_irq(struct uart_amba_port *uap)

>>>   {

>>>       pl011_write(uap->im, uap, REG_IMSC);

>>> @@ -1753,7 +1788,7 @@ static int pl011_startup(struct uart_port *port)

>>>       if (retval)

>>>           goto clk_dis;

>>>   -    retval = pl011_allocate_irq(uap);

>>> +    retval = pl011_allocate_multi_irqs(uap);

>>>       if (retval)

>>>           goto clk_dis;

>>>   @@ -1864,7 +1899,7 @@ static void pl011_shutdown(struct uart_port 

>>> *port)

>>>         pl011_dma_shutdown(uap);

>>>   -    free_irq(uap->port.irq, uap);

>>> +    pl011_release_multi_irqs(uap, AMBA_NR_IRQS);

>>>         pl011_disable_uart(uap);

>>>
Russell King (Oracle) Aug. 16, 2021, 10:34 a.m. UTC | #6
On Fri, Aug 13, 2021 at 03:37:16PM +0100, Robin Murphy wrote:
> > +static int pl011_allocate_multi_irqs(struct uart_amba_port *uap)

> > +{

> > +	int ret = 0;

> > +	int i;

> > +	unsigned int virq;

> > +	struct amba_device *amba_dev = container_of(uap->port.dev, struct amba_device, dev);

> > +

> > +	pl011_write(uap->im, uap, REG_IMSC);

> > +

> > +	for (i = 0; i < AMBA_NR_IRQS; i++) {

> 

> It's not clear where these extra IRQs are expected to come from given that

> the DT binding explicitly defines only one :/


The DT binding (and driver) was written assuming that people wouldn't
use the individual interrupts - but I guess someone decided it was a
good idea to have a bazillion interrupt signals going to your interrupt
controller from something as simple as a UART (which is permitted by
the PL011 TRM.) It's only taken about 20 years for this to happen, so
I think we should think we're lucky this hasn't come up before! :D

> > +		virq = amba_dev->irq[i];

> > +		if (virq == 0)

> > +			break;

> > +

> > +		ret = request_irq(virq, pl011_int, IRQF_SHARED, dev_name(&amba_dev->dev), uap);

> 

> Note that using dev_name() here technically breaks user ABI - scripts

> looking in /proc for an irq named "uart-pl011" will no longer find it.

> 

> Furthermore, the "dev" cookie passed to request_irq is supposed to be

> globally unique, which "uap" isn't once you start registering it multiple

> times.


There's no difference there.

First, the "private" used with request_irq() only has to be globally
unique for the interrupt number being requested. Secondly, there is
no way for two UARTs to share the same "uap" structure, and finally
there is a 1:1 model between "uap" and "dev". So, I don't see a problem
as far as whether we use "uap" or "dev" here.

> If firmware did describe all the individual PL011 IRQ outputs on a

> system where they are muxed to the same physical IRQ anyway, you'd end up

> registering ambiguous IRQ actions here. Of course in practice you might

> still get away with that, but it is technically wrong.


Yes. This would also make a total nonsense of using multiple interrupt
lines.

The whole point of using multiple interrupt lines from the UART is so
the interrupt demultiplexing can be handled at the interrupt controller
and their priorities can be decided there. If we adopt a software
structure where we effectively register our "merged" interrupt handler
for all these signals, then there is absolutely no benefit to using
multiple interrupt signals, since that will override any priority, and
we will still have the extra overhead of decoding which interrupt fired
at the UART level.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!
diff mbox series

Patch

diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index e14f3378b8a0..eaac3431459c 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -1701,6 +1701,41 @@  static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h)
 	}
 }
 
+static void pl011_release_multi_irqs(struct uart_amba_port *uap, unsigned int max_cnt)
+{
+	struct amba_device *amba_dev = container_of(uap->port.dev, struct amba_device, dev);
+	int i;
+
+	for (i = 0; i < max_cnt; i++)
+		if (amba_dev->irq[i])
+			free_irq(amba_dev->irq[i], uap);
+}
+
+static int pl011_allocate_multi_irqs(struct uart_amba_port *uap)
+{
+	int ret = 0;
+	int i;
+	unsigned int virq;
+	struct amba_device *amba_dev = container_of(uap->port.dev, struct amba_device, dev);
+
+	pl011_write(uap->im, uap, REG_IMSC);
+
+	for (i = 0; i < AMBA_NR_IRQS; i++) {
+		virq = amba_dev->irq[i];
+		if (virq == 0)
+			break;
+
+		ret = request_irq(virq, pl011_int, IRQF_SHARED, dev_name(&amba_dev->dev), uap);
+		if (ret) {
+			dev_err(uap->port.dev, "request %u interrupt failed\n", virq);
+			pl011_release_multi_irqs(uap, i - 1);
+			break;
+		}
+	}
+
+	return ret;
+}
+
 static int pl011_allocate_irq(struct uart_amba_port *uap)
 {
 	pl011_write(uap->im, uap, REG_IMSC);
@@ -1753,7 +1788,7 @@  static int pl011_startup(struct uart_port *port)
 	if (retval)
 		goto clk_dis;
 
-	retval = pl011_allocate_irq(uap);
+	retval = pl011_allocate_multi_irqs(uap);
 	if (retval)
 		goto clk_dis;
 
@@ -1864,7 +1899,7 @@  static void pl011_shutdown(struct uart_port *port)
 
 	pl011_dma_shutdown(uap);
 
-	free_irq(uap->port.irq, uap);
+	pl011_release_multi_irqs(uap, AMBA_NR_IRQS);
 
 	pl011_disable_uart(uap);