diff mbox series

[v5,09/10] gpio: Add a driver for Cadence I3C GPIO expander

Message ID 20180622104930.32050-10-boris.brezillon@bootlin.com
State New
Headers show
Series Add the I3C subsystem | expand

Commit Message

Boris Brezillon June 22, 2018, 10:49 a.m. UTC
Add a driver for Cadence I3C GPIO expander.

Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
---
Hi Linus,

I intentionally did not report your Acked-by because of the scract
buffer changes I added in this version (needed to follow the
"buffer should be DMA-able" rule)

Feel free to add it back if you're okay with this version.

Regards,

Boris

Changes in v5:
- Use the !! operator to return 0 or 1 in cdns_i3c_gpio_get_direction()
- Use a scratch buffer to make sure the buffers passed to
  i3c_device_do_priv_xfers() are DMA-able
- Fix errors reported by checkpatch

Changes in v4:
- none

Changes in v3:
- new
---
 drivers/gpio/Kconfig         |  11 ++
 drivers/gpio/Makefile        |   1 +
 drivers/gpio/gpio-cdns-i3c.c | 411 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 423 insertions(+)
 create mode 100644 drivers/gpio/gpio-cdns-i3c.c

Comments

Randy Dunlap June 22, 2018, 4:04 p.m. UTC | #1
Hi,

On 06/22/2018 03:49 AM, Boris Brezillon wrote:
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 71c0ab46f216..19ed6006aea1 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -898,6 +898,17 @@ config GPIO_TS4900
>  
>  endmenu
>  
> +menu "I3C GPIO expanders"
> +	depends on I3C
> +
> +config GPIO_CDNS_I3C
> +	tristate "Cadence I3C GPIO expander"
> +	select GPIOLIB_IRQCHIP
> +	help
> +	  Say yes here to enabled the driver for Cadence I3C GPIO expander.

	               to enable the driver

> +
> +endmenu
Boris Brezillon June 22, 2018, 6:35 p.m. UTC | #2
On Fri, 22 Jun 2018 09:04:47 -0700
Randy Dunlap <rdunlap@infradead.org> wrote:

> Hi,
> 
> On 06/22/2018 03:49 AM, Boris Brezillon wrote:
> > diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> > index 71c0ab46f216..19ed6006aea1 100644
> > --- a/drivers/gpio/Kconfig
> > +++ b/drivers/gpio/Kconfig
> > @@ -898,6 +898,17 @@ config GPIO_TS4900
> >  
> >  endmenu
> >  
> > +menu "I3C GPIO expanders"
> > +	depends on I3C
> > +
> > +config GPIO_CDNS_I3C
> > +	tristate "Cadence I3C GPIO expander"
> > +	select GPIOLIB_IRQCHIP
> > +	help
> > +	  Say yes here to enabled the driver for Cadence I3C GPIO expander.  
> 
> 	               to enable the driver

I'll fix this typo

Thanks,

Boris

--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andy Shevchenko June 26, 2018, 7:07 p.m. UTC | #3
On Fri, Jun 22, 2018 at 1:49 PM, Boris Brezillon
<boris.brezillon@bootlin.com> wrote:
> Add a driver for Cadence I3C GPIO expander.
>
> Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>

> +       scratchbuf = kmalloc(sizeof(*scratchbuf) * 2, GFP_KERNEL);

kmalloc_array() ?

> +       ret = i3c_device_do_priv_xfers(gpioc->i3cdev, xfers,
> +                                      ARRAY_SIZE(xfers));

One line?


> +static void cdns_i3c_gpio_set_multiple(struct gpio_chip *g,
> +                                      unsigned long *mask,
> +                                      unsigned long *bits)
> +{
> +       struct cdns_i3c_gpio *gpioc = gpioc_to_cdns_gpioc(g);
> +       u8 newovr;
> +       int ret;
> +
> +       newovr = (gpioc->ovr & ~(*mask)) | (*bits & *mask);
> +       if (newovr == gpioc->ovr)
> +               return;
> +
> +       ret = cdns_i3c_gpio_write_reg(gpioc, OVR, newovr);
> +       if (!ret)
> +               gpioc->ovr = newovr;

You don't change mask here, why do you need a pointer to it?

> +}

> +static int cdns_i3c_gpio_get_multiple(struct gpio_chip *g,
> +                                     unsigned long *mask,
> +                                     unsigned long *bits)
> +{
> +       struct cdns_i3c_gpio *gpioc = gpioc_to_cdns_gpioc(g);
> +       int ret;
> +       u8 ivr;
> +
> +       ret = cdns_i3c_gpio_read_reg(gpioc, IVR, &ivr);
> +       if (ret)
> +               return ret;
> +
> +       *bits = ivr & *mask & gpioc->dir;
> +       *bits |= gpioc->ovr & *mask & ~gpioc->dir;

Ditto.

> +
> +       return 0;
> +}

> +static void cdns_i3c_gpio_ibi_handler(struct i3c_device *i3cdev,
> +                                     const struct i3c_ibi_payload *payload)
> +{
> +       struct cdns_i3c_gpio *gpioc = i3cdev_get_drvdata(i3cdev);

> +       u8 isr = 0;

Perhaps you need to check the result of _read_reg() below instead of
dummy assignment?

> +       int i;
> +
> +       cdns_i3c_gpio_read_reg(gpioc, ISR, &isr);

> +       for (i = 0; i < 8; i++) {
> +               unsigned int irq;
> +
> +               if (!(BIT(i) & isr & gpioc->imr))
> +                       continue;

for_each_set_bit() ?

> +
> +               irq = irq_find_mapping(gpioc->gpioc.irq.domain, i);
> +               handle_nested_irq(irq);
> +       }
> +}

> +static const struct i3c_device_id cdns_i3c_gpio_ids[] = {
> +       I3C_DEVICE(0x1c9, 0x0, NULL),

> +       { /* sentinel */ },

Slightly better without comma.

> +};
> +MODULE_DEVICE_TABLE(i3c, cdns_i3c_gpio_ids);
Boris Brezillon June 26, 2018, 7:56 p.m. UTC | #4
Hi Andy,

On Tue, 26 Jun 2018 22:07:03 +0300
Andy Shevchenko <andy.shevchenko@gmail.com> wrote:

> On Fri, Jun 22, 2018 at 1:49 PM, Boris Brezillon
> <boris.brezillon@bootlin.com> wrote:
> > Add a driver for Cadence I3C GPIO expander.
> >
> > Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>  
> 
> > +       scratchbuf = kmalloc(sizeof(*scratchbuf) * 2, GFP_KERNEL);  
> 
> kmalloc_array() ?
> 
> > +       ret = i3c_device_do_priv_xfers(gpioc->i3cdev, xfers,
> > +                                      ARRAY_SIZE(xfers));  
> 
> One line?
> 
> 
> > +static void cdns_i3c_gpio_set_multiple(struct gpio_chip *g,
> > +                                      unsigned long *mask,
> > +                                      unsigned long *bits)
> > +{
> > +       struct cdns_i3c_gpio *gpioc = gpioc_to_cdns_gpioc(g);
> > +       u8 newovr;
> > +       int ret;
> > +
> > +       newovr = (gpioc->ovr & ~(*mask)) | (*bits & *mask);
> > +       if (newovr == gpioc->ovr)
> > +               return;
> > +
> > +       ret = cdns_i3c_gpio_write_reg(gpioc, OVR, newovr);
> > +       if (!ret)
> > +               gpioc->ovr = newovr;  
> 
> You don't change mask here, why do you need a pointer to it?

What are you talking about??!!! This is part of the gpio_chip interface
that drivers have to implement. You can't decide that mask should not be
a pointer. And if you actually look at the code, you'll probably see
that the reason we're passed a pointer here is that the GPIO chip might
expose more GPIOs than can be represented by an unsigned long, hence
the array of unsigned long.

I'll say it here because this is not the first time I'm pissed off by
one of your review. You seem to review everything that is posted on the
LKML, and most of the time your reviews are superficial (focused on tiny
details or coding style issues). Don't you have better things to do?

I mean, I'm fine when I get such comments from people that also do
useful comments, but yours are most of the time useless and sometime
even wrong because you didn't time to look at the code in details.

> 
> > +}  
> 
> > +static int cdns_i3c_gpio_get_multiple(struct gpio_chip *g,
> > +                                     unsigned long *mask,
> > +                                     unsigned long *bits)
> > +{
> > +       struct cdns_i3c_gpio *gpioc = gpioc_to_cdns_gpioc(g);
> > +       int ret;
> > +       u8 ivr;
> > +
> > +       ret = cdns_i3c_gpio_read_reg(gpioc, IVR, &ivr);
> > +       if (ret)
> > +               return ret;
> > +
> > +       *bits = ivr & *mask & gpioc->dir;
> > +       *bits |= gpioc->ovr & *mask & ~gpioc->dir;  
> 
> Ditto.
> 
> > +
> > +       return 0;
> > +}  
> 
> > +static void cdns_i3c_gpio_ibi_handler(struct i3c_device *i3cdev,
> > +                                     const struct i3c_ibi_payload *payload)
> > +{
> > +       struct cdns_i3c_gpio *gpioc = i3cdev_get_drvdata(i3cdev);  
> 
> > +       u8 isr = 0;  
> 
> Perhaps you need to check the result of _read_reg() below instead of
> dummy assignment?
> 
> > +       int i;
> > +
> > +       cdns_i3c_gpio_read_reg(gpioc, ISR, &isr);  
> 
> > +       for (i = 0; i < 8; i++) {
> > +               unsigned int irq;
> > +
> > +               if (!(BIT(i) & isr & gpioc->imr))
> > +                       continue;  
> 
> for_each_set_bit() ?
> 
> > +
> > +               irq = irq_find_mapping(gpioc->gpioc.irq.domain, i);
> > +               handle_nested_irq(irq);
> > +       }
> > +}  
> 
> > +static const struct i3c_device_id cdns_i3c_gpio_ids[] = {
> > +       I3C_DEVICE(0x1c9, 0x0, NULL),  
> 
> > +       { /* sentinel */ },  
> 
> Slightly better without comma.
> 
> > +};
> > +MODULE_DEVICE_TABLE(i3c, cdns_i3c_gpio_ids);  
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andy Shevchenko June 26, 2018, 8:44 p.m. UTC | #5
On Tue, Jun 26, 2018 at 10:56 PM, Boris Brezillon
<boris.brezillon@bootlin.com> wrote:
> On Tue, 26 Jun 2018 22:07:03 +0300
> Andy Shevchenko <andy.shevchenko@gmail.com> wrote:
>> On Fri, Jun 22, 2018 at 1:49 PM, Boris Brezillon
>> <boris.brezillon@bootlin.com> wrote:
>> > Add a driver for Cadence I3C GPIO expander.
>> >
>> > Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
>>
>> > +       scratchbuf = kmalloc(sizeof(*scratchbuf) * 2, GFP_KERNEL);
>>
>> kmalloc_array() ?
>>
>> > +       ret = i3c_device_do_priv_xfers(gpioc->i3cdev, xfers,
>> > +                                      ARRAY_SIZE(xfers));
>>
>> One line?
>>

>> You don't change mask here, why do you need a pointer to it?
>
> What are you talking about??!!! This is part of the gpio_chip interface
> that drivers have to implement. You can't decide that mask should not be
> a pointer. And if you actually look at the code, you'll probably see
> that the reason we're passed a pointer here is that the GPIO chip might
> expose more GPIOs than can be represented by an unsigned long, hence
> the array of unsigned long.
>

> I'll say it here because this is not the first time I'm pissed off by
> one of your review.

Like 'I like offending people, because I think people who get offended
should be offended.' ?
I'm not that guy, relax.

>  You seem to review everything that is posted on the LKML,

That's not true.

> and most of the time your reviews are superficial (focused on tiny
> details or coding style issues). Don't you have better things to do?

If your code is written using ancient style and / or API it's not good
to start with.
Isn't it? Otherwise, why we do introduce new internal APIs, for what purpose?

On top of that you might find more interesting reviews where I pointed
out to a memory leak or other nasty stuff. But of course you prefer
not to norice that.
I understand you.

> I mean, I'm fine when I get such comments from people that also do
> useful comments, but yours are most of the time useless and sometime
> even wrong because you didn't time to look at the code in details.

Calm down, please.
You would simple ignore them. Your choice.

But okay, I would try to avoid your stuff to make you happier. Sorry
for disturbing.

>> Ditto.

In these two comments I'm indeed wrong.
Boris Brezillon June 26, 2018, 9:46 p.m. UTC | #6
On Tue, 26 Jun 2018 23:44:36 +0300
Andy Shevchenko <andy.shevchenko@gmail.com> wrote:

> On Tue, Jun 26, 2018 at 10:56 PM, Boris Brezillon
> <boris.brezillon@bootlin.com> wrote:
> > On Tue, 26 Jun 2018 22:07:03 +0300
> > Andy Shevchenko <andy.shevchenko@gmail.com> wrote:  
> >> On Fri, Jun 22, 2018 at 1:49 PM, Boris Brezillon
> >> <boris.brezillon@bootlin.com> wrote:  
> >> > Add a driver for Cadence I3C GPIO expander.
> >> >
> >> > Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>  
> >>  
> >> > +       scratchbuf = kmalloc(sizeof(*scratchbuf) * 2, GFP_KERNEL);  
> >>
> >> kmalloc_array() ?
> >>  
> >> > +       ret = i3c_device_do_priv_xfers(gpioc->i3cdev, xfers,
> >> > +                                      ARRAY_SIZE(xfers));  
> >>
> >> One line?
> >>  
> 
> >> You don't change mask here, why do you need a pointer to it?  
> >
> > What are you talking about??!!! This is part of the gpio_chip interface
> > that drivers have to implement. You can't decide that mask should not be
> > a pointer. And if you actually look at the code, you'll probably see
> > that the reason we're passed a pointer here is that the GPIO chip might
> > expose more GPIOs than can be represented by an unsigned long, hence
> > the array of unsigned long.
> >  
> 
> > I'll say it here because this is not the first time I'm pissed off by
> > one of your review.  
> 
> Like 'I like offending people, because I think people who get offended
> should be offended.' ?
> I'm not that guy, relax.

I'm not offended, just annoyed, and not because I have things to change
in my patchset (I'm used to that and that's something I comply with
most of the time), but because the only reviews I had from you were of
this kind (nitpicking on minor stuff).

> 
> >  You seem to review everything that is posted on the LKML,  
> 
> That's not true.
> 
> > and most of the time your reviews are superficial (focused on tiny
> > details or coding style issues). Don't you have better things to do?  
> 
> If your code is written using ancient style and / or API it's not good
> to start with.
> Isn't it? Otherwise, why we do introduce new internal APIs, for what purpose?

Come on!

- kzalloc() vs kmalloc_array()
- for (i = 0; i < n, i++) { if (BIT(x) & var) ... }
  vs
  for_each_bit_set() (which is not even applicable here because I'm
  not manipulating an unsigned long)

Where do you see ancient style APIs here?

And that's not even the problem. I'm okay to fix those things, but you
only ever do these kind of reviews, and sometime you even act like you
know better than the developer or the maintainer how the code should be
organized without even digging enough to have a clue (your comment on
the fact that mask should not be a pointer is a good example of such
situations).

> 
> On top of that you might find more interesting reviews where I pointed
> out to a memory leak or other nasty stuff. But of course you prefer
> not to norice that.

Yes, sometime you have valid point, but it gets lost in all the noise.

> I understand you.
> 
> > I mean, I'm fine when I get such comments from people that also do
> > useful comments, but yours are most of the time useless and sometime
> > even wrong because you didn't time to look at the code in details.  
> 
> Calm down, please.

Why? Because I say you didn't look at the code in details? Isn't it
true? Did you look at what I3C is, how it works or how the I3C framework
is architectured? Because that's the kind of reviews I'd love to have on
this series.

> You would simple ignore them. Your choice.

That's not my point. My point is, maybe you should do less reviews but
spend more time on each of them so you don't just scratch the surface
with comments I could get from a tool like checkpatch.
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andy Shevchenko June 27, 2018, 5:53 p.m. UTC | #7
On Wed, Jun 27, 2018 at 12:46 AM, Boris Brezillon
<boris.brezillon@bootlin.com> wrote:
> On Tue, 26 Jun 2018 23:44:36 +0300
> Andy Shevchenko <andy.shevchenko@gmail.com> wrote:
>> On Tue, Jun 26, 2018 at 10:56 PM, Boris Brezillon
>> <boris.brezillon@bootlin.com> wrote:
>> > On Tue, 26 Jun 2018 22:07:03 +0300
>> > Andy Shevchenko <andy.shevchenko@gmail.com> wrote:
>> >> On Fri, Jun 22, 2018 at 1:49 PM, Boris Brezillon
>> >> <boris.brezillon@bootlin.com> wrote:

>> > I'll say it here because this is not the first time I'm pissed off by
>> > one of your review.
>>
>> Like 'I like offending people, because I think people who get offended
>> should be offended.' ?
>> I'm not that guy, relax.
>
> I'm not offended, just annoyed, and not because I have things to change
> in my patchset (I'm used to that and that's something I comply with
> most of the time), but because the only reviews I had from you were of
> this kind (nitpicking on minor stuff).

OK, point taken.

>> > and most of the time your reviews are superficial (focused on tiny
>> > details or coding style issues). Don't you have better things to do?
>>
>> If your code is written using ancient style and / or API it's not good
>> to start with.
>> Isn't it? Otherwise, why we do introduce new internal APIs, for what purpose?
>
> Come on!
>
> - kzalloc() vs kmalloc_array()
> - for (i = 0; i < n, i++) { if (BIT(x) & var) ... }
>   vs
>   for_each_bit_set() (which is not even applicable here because I'm
>   not manipulating an unsigned long)
>
> Where do you see ancient style APIs here?

Both. kmalloc_array() and for_each_set_bit() were introduced quite
long time ago.

On top of that you are open coding, if I'm not mistaken, cpu_to_be32()
and be32_to_cpu().
...and more I believe.

> And that's not even the problem. I'm okay to fix those things, but you
> only ever do these kind of reviews, and sometime you even act like you
> know better than the developer or the maintainer how the code should be
> organized without even digging enough to have a clue (your comment on
> the fact that mask should not be a pointer is a good example of such
> situations).

"sometime". Yes, I admit my mistakes.

>> On top of that you might find more interesting reviews where I pointed
>> out to a memory leak or other nasty stuff. But of course you prefer
>> not to norice that.
>
> Yes, sometime you have valid point, but it gets lost in all the noise.

Btw, you forgot to call of_node_put() on error path in one case. Did I
again missed the details?

>> > I mean, I'm fine when I get such comments from people that also do
>> > useful comments, but yours are most of the time useless and sometime
>> > even wrong because you didn't time to look at the code in details.
>>
>> Calm down, please.
>
> Why? Because I say you didn't look at the code in details? Isn't it
> true? Did you look at what I3C is, how it works or how the I3C framework
> is architectured? Because that's the kind of reviews I'd love to have on
> this series.

You got me.
I have a copy of the spec, this require a bit of time to go through.

For now I can offer you to consider IBI implementation using IRQ chip framework.
It would give few advantages (like we do this for GPIO), such as IRQ
statistics or wake up capabilities.

>> You would simple ignore them. Your choice.
>
> That's not my point. My point is, maybe you should do less reviews but
> spend more time on each of them

Good point.

> so you don't just scratch the surface
> with comments I could get from a tool like checkpatch.

Perhaps you should run checkpatch yourself then?
Boris Brezillon June 27, 2018, 7:36 p.m. UTC | #8
Hi Andy,

On Wed, 27 Jun 2018 20:53:51 +0300
Andy Shevchenko <andy.shevchenko@gmail.com> wrote:

> On Wed, Jun 27, 2018 at 12:46 AM, Boris Brezillon
> <boris.brezillon@bootlin.com> wrote:
> > On Tue, 26 Jun 2018 23:44:36 +0300
> > Andy Shevchenko <andy.shevchenko@gmail.com> wrote:  
> >> On Tue, Jun 26, 2018 at 10:56 PM, Boris Brezillon
> >> <boris.brezillon@bootlin.com> wrote:  
> >> > On Tue, 26 Jun 2018 22:07:03 +0300
> >> > Andy Shevchenko <andy.shevchenko@gmail.com> wrote:  
> >> >> On Fri, Jun 22, 2018 at 1:49 PM, Boris Brezillon
> >> >> <boris.brezillon@bootlin.com> wrote:  
> 
> >> > I'll say it here because this is not the first time I'm pissed off by
> >> > one of your review.  
> >>
> >> Like 'I like offending people, because I think people who get offended
> >> should be offended.' ?
> >> I'm not that guy, relax.  
> >
> > I'm not offended, just annoyed, and not because I have things to change
> > in my patchset (I'm used to that and that's something I comply with
> > most of the time), but because the only reviews I had from you were of
> > this kind (nitpicking on minor stuff).  
> 
> OK, point taken.
> 
> >> > and most of the time your reviews are superficial (focused on tiny
> >> > details or coding style issues). Don't you have better things to do?  
> >>
> >> If your code is written using ancient style and / or API it's not good
> >> to start with.
> >> Isn't it? Otherwise, why we do introduce new internal APIs, for what purpose?  
> >
> > Come on!
> >
> > - kzalloc() vs kmalloc_array()
> > - for (i = 0; i < n, i++) { if (BIT(x) & var) ... }
> >   vs
> >   for_each_bit_set() (which is not even applicable here because I'm
> >   not manipulating an unsigned long)
> >
> > Where do you see ancient style APIs here?  
> 
> Both. kmalloc_array() and for_each_set_bit() were introduced quite
> long time ago.

I mean, kzalloc() is not deprecated AFAIK and I don't really see the
benefit of using kmalloc_array(), but if that makes you happy, let's go
for kmalloc_array().

For for_each_bit_set() it would require changing the type of isr +
having a temporary variable to get the reg value into an u8. Again, I
can do the change, but I don't really see how it improves the code. 

> 
> On top of that you are open coding, if I'm not mistaken, cpu_to_be32()
> and be32_to_cpu().

Care to point where?

> ...and more I believe.

Care to point what?
 
> >> On top of that you might find more interesting reviews where I pointed
> >> out to a memory leak or other nasty stuff. But of course you prefer
> >> not to norice that.  
> >
> > Yes, sometime you have valid point, but it gets lost in all the noise.  
> 
> Btw, you forgot to call of_node_put() on error path in one case.

In this driver or another patch of the series? I don't see any
of_node_get() call in this GPIO driver, but maybe it's something done
in one of the GPIO helper.

> Did I
> again missed the details?
> 
> >> > I mean, I'm fine when I get such comments from people that also do
> >> > useful comments, but yours are most of the time useless and sometime
> >> > even wrong because you didn't time to look at the code in details.  
> >>
> >> Calm down, please.  
> >
> > Why? Because I say you didn't look at the code in details? Isn't it
> > true? Did you look at what I3C is, how it works or how the I3C framework
> > is architectured? Because that's the kind of reviews I'd love to have on
> > this series.  
> 
> You got me.
> I have a copy of the spec, this require a bit of time to go through.

Cool!

> 
> For now I can offer you to consider IBI implementation using IRQ chip framework.
> It would give few advantages (like we do this for GPIO), such as IRQ
> statistics or wake up capabilities.

Just pasting the comment I made in my cover letter:

"
Main changes between the initial RFC and this v2 are:
- Add a generic infrastructure to support IBIs. It's worth mentioning
  that I tried exposing IBIs as a regular IRQs, but after several
  attempts and a discussion with Mark Zyngier, it appeared that it was
  not really fitting in the Linux IRQ model (the fact that you have
  payload attached to IBIs, the fact that most of the time an IBI will
  generate a transfer on the bus which has to be done in an atomic
  context, ...)
  The counterpart of this decision is the latency induced by the
  workqueue approach, but since I don't have real use cases, I don't
  know if this can be a problem or not. 
"

To sum-up, yes I tried implementing what you suggest, and it ended
being messy for 2 main reasons:

1/ IBIs have payload attached to them, and exposing IBIs are regular
IRQs meant adding a payload queuing mechanism to the i3c_device so that
the I3C device driver could dequeue each payload independently. Clearly
not impossible to deal with, but conceptually far from the concept of
IRQs we have in Linux.

2/ The I3C APIs are meant to be used in non-atomic context, but if you
expose IBIs are regular IRQs, the irqchip will have to mask/unmask the
IRQs from an atomic context, and masking/unmasking IRQs almost always
implies doing a CCC or priv SDR transfer. Plus, most of the time you'll
have to retrieve extra info from the IRQ handler, which again means
sending frames on the I3C bus. We could make that work by either
supporting async transfers or having the irq chip handle the IRQ
handlers from a thread. But both options add extra complexity for no
real benefit given that IBIs are already not exactly fitting in the
Linux IRQ model.

The discussion I had with Mark Zyngier finished convincing me that
IBIs would be better exposed with their own API.

> 
> >> You would simple ignore them. Your choice.  
> >
> > That's not my point. My point is, maybe you should do less reviews but
> > spend more time on each of them  
> 
> Good point.
> 
> > so you don't just scratch the surface
> > with comments I could get from a tool like checkpatch.  
> 
> Perhaps you should run checkpatch yourself then?
> 

I do run checkpatch --strict and fix most of the thing reported except
those hurting readability. I don't remember seeing checkpatch complain
about kzalloc() usage, and I guess it's not smart enough to detect that
for_each_bit_set() can be used to replace the "for() if (BIT(x) & val)"
pattern.

Anyway, thanks for having a look at the I3C spec and starting the
discussion on IBIs. I guess I owe you an apology.

Regards,

Boris
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Linus Walleij June 27, 2018, 10:14 p.m. UTC | #9
On Fri, Jun 22, 2018 at 12:49 PM Boris Brezillon
<boris.brezillon@bootlin.com> wrote:

> Add a driver for Cadence I3C GPIO expander.
>
> Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>

Acked-by: Linus Walleij <linus.walleij@linaro.org>

If you need to merge this through the subsystem merge.

I see there was some heat in this discussion between you
and Andy, and it seems to be resolved. Which is good
because you are both senior contributors that I value
a lot.

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Joe Perches June 27, 2018, 10:54 p.m. UTC | #10
On Wed, 2018-06-27 at 21:36 +0200, Boris Brezillon wrote:
> I mean, kzalloc() is not deprecated AFAIK and I don't really see the
> benefit of using kmalloc_array(), but if that makes you happy, let's go
> for kmalloc_array().

kcalloc

> I do run checkpatch --strict and fix most of the thing reported except
> those hurting readability. I don't remember seeing checkpatch complain
> about kzalloc() usage, and I guess it's not smart enough to detect that
> for_each_bit_set() can be used to replace the "for() if (BIT(x) & val)"
> pattern.

That would not an appropriate conversion suggestion in any case.
coccinelle could at least look at whether or not x is allocated
as a bitmap via DECLARE_BITMAP or bitmap_alloc


--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andy Shevchenko June 28, 2018, midnight UTC | #11
On Thu, Jun 28, 2018 at 1:54 AM, Joe Perches <joe@perches.com> wrote:
> On Wed, 2018-06-27 at 21:36 +0200, Boris Brezillon wrote:
>> I mean, kzalloc() is not deprecated AFAIK and I don't really see the
>> benefit of using kmalloc_array(), but if that makes you happy, let's go
>> for kmalloc_array().
>
> kcalloc

IIRC in the original code kmalloc(x*y) is used.

>> I do run checkpatch --strict and fix most of the thing reported except
>> those hurting readability. I don't remember seeing checkpatch complain
>> about kzalloc() usage, and I guess it's not smart enough to detect that
>> for_each_bit_set() can be used to replace the "for() if (BIT(x) & val)"
>> pattern.
>
> That would not an appropriate conversion suggestion in any case.
> coccinelle could at least look at whether or not x is allocated
> as a bitmap via DECLARE_BITMAP or bitmap_alloc

Huh?!

bitmap operations are working against unsigned long *. one long is
also a bitmap.
So, that coccinelle scripts must be fixed accordingly.
Joe Perches June 28, 2018, 12:50 a.m. UTC | #12
On Thu, 2018-06-28 at 03:00 +0300, Andy Shevchenko wrote:
> bitmap operations are working against unsigned long *. one long is
> also a bitmap.

Should only be so if declared via DECLARE_BITMAP

> So, that coccinelle scripts must be fixed accordingly.

We disagree about a non-existent script.
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Wolfram Sang June 28, 2018, 4:08 a.m. UTC | #13
> I see there was some heat in this discussion between you
> and Andy, and it seems to be resolved. Which is good
> because you are both senior contributors that I value
> a lot.

+1
diff mbox series

Patch

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 71c0ab46f216..19ed6006aea1 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -898,6 +898,17 @@  config GPIO_TS4900
 
 endmenu
 
+menu "I3C GPIO expanders"
+	depends on I3C
+
+config GPIO_CDNS_I3C
+	tristate "Cadence I3C GPIO expander"
+	select GPIOLIB_IRQCHIP
+	help
+	  Say yes here to enabled the driver for Cadence I3C GPIO expander.
+
+endmenu
+
 menu "MFD GPIO expanders"
 
 config GPIO_ADP5520
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 1324c8f966a7..020b9171223b 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -37,6 +37,7 @@  obj-$(CONFIG_GPIO_BCM_KONA)	+= gpio-bcm-kona.o
 obj-$(CONFIG_GPIO_BD9571MWV)	+= gpio-bd9571mwv.o
 obj-$(CONFIG_GPIO_BRCMSTB)	+= gpio-brcmstb.o
 obj-$(CONFIG_GPIO_BT8XX)	+= gpio-bt8xx.o
+obj-$(CONFIG_GPIO_CDNS_I3C)	+= gpio-cdns-i3c.o
 obj-$(CONFIG_GPIO_CLPS711X)	+= gpio-clps711x.o
 obj-$(CONFIG_GPIO_CS5535)	+= gpio-cs5535.o
 obj-$(CONFIG_GPIO_CRYSTAL_COVE)	+= gpio-crystalcove.o
diff --git a/drivers/gpio/gpio-cdns-i3c.c b/drivers/gpio/gpio-cdns-i3c.c
new file mode 100644
index 000000000000..028336754215
--- /dev/null
+++ b/drivers/gpio/gpio-cdns-i3c.c
@@ -0,0 +1,411 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Cadence Design Systems Inc.
+ *
+ * Author: Boris Brezillon <boris.brezillon@bootlin.com>
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/i3c/device.h>
+#include <linux/module.h>
+
+#define OVR		0x0
+#define IVR		0x1
+#define DIR_MODE	0x2
+#define IMR		0x3
+#define ISR		0x4
+#define ITR(x)		(0x5 + (x))
+
+struct cdns_i3c_gpio {
+	struct gpio_chip gpioc;
+	struct irq_chip irqc;
+	struct i3c_device *i3cdev;
+	struct mutex irq_lock;
+	u8 dir;
+	u8 ovr;
+	u8 imr;
+	u8 itr[3];
+};
+
+static struct cdns_i3c_gpio *gpioc_to_cdns_gpioc(struct gpio_chip *gpioc)
+{
+	return container_of(gpioc, struct cdns_i3c_gpio, gpioc);
+}
+
+static int cdns_i3c_gpio_read_reg(struct cdns_i3c_gpio *gpioc, u8 reg,
+				  u8 *val)
+{
+	struct i3c_priv_xfer xfers[2] = { };
+	u8 *scratchbuf;
+	int ret;
+
+	/*
+	 * i3c_device_do_priv_xfers() mandates that buffers passed in xfers be
+	 * DMA-able. This prevents us from using reg and val directly since
+	 * reg is on the stack, and val might be too.
+	 * Allocate a temporary buffer with kmalloc() to solve the problem.
+	 */
+	scratchbuf = kmalloc(sizeof(*scratchbuf), GFP_KERNEL);
+	if (!scratchbuf)
+		return -ENOMEM;
+
+	scratchbuf[0] = reg;
+	xfers[0].data.out = scratchbuf;
+	xfers[0].len = 1;
+	xfers[1].data.in = scratchbuf;
+	xfers[1].len = 1;
+	xfers[1].rnw = true;
+
+	ret = i3c_device_do_priv_xfers(gpioc->i3cdev, xfers,
+				       ARRAY_SIZE(xfers));
+	if (!ret)
+		*val = *scratchbuf;
+
+	kfree(scratchbuf);
+
+	return ret;
+}
+
+static int cdns_i3c_gpio_write_reg(struct cdns_i3c_gpio *gpioc, u8 reg,
+				   u8 val)
+{
+	struct i3c_priv_xfer xfers[2] = { };
+	u8 *scratchbuf;
+	int ret;
+
+	/*
+	 * i3c_device_do_priv_xfers() mandates that buffers passed in xfers be
+	 * DMA-able. This prevents us from using reg and val directly since
+	 * reg is on the stack, and val might be too.
+	 * Allocate a temporary buffer with kmalloc() to solve the problem.
+	 */
+	scratchbuf = kmalloc(sizeof(*scratchbuf) * 2, GFP_KERNEL);
+	if (!scratchbuf)
+		return -ENOMEM;
+
+	scratchbuf[0] = reg;
+	scratchbuf[1] = val;
+	xfers[0].data.out = scratchbuf;
+	xfers[0].len = 1;
+	xfers[1].data.out = scratchbuf + 1;
+	xfers[1].len = 1;
+
+	ret = i3c_device_do_priv_xfers(gpioc->i3cdev, xfers,
+				       ARRAY_SIZE(xfers));
+
+	kfree(scratchbuf);
+
+	return ret;
+}
+
+static int cdns_i3c_gpio_get_direction(struct gpio_chip *g,
+				       unsigned int offset)
+{
+	struct cdns_i3c_gpio *gpioc = gpioc_to_cdns_gpioc(g);
+
+	return !!(gpioc->dir & BIT(offset));
+}
+
+static void cdns_i3c_gpio_set_multiple(struct gpio_chip *g,
+				       unsigned long *mask,
+				       unsigned long *bits)
+{
+	struct cdns_i3c_gpio *gpioc = gpioc_to_cdns_gpioc(g);
+	u8 newovr;
+	int ret;
+
+	newovr = (gpioc->ovr & ~(*mask)) | (*bits & *mask);
+	if (newovr == gpioc->ovr)
+		return;
+
+	ret = cdns_i3c_gpio_write_reg(gpioc, OVR, newovr);
+	if (!ret)
+		gpioc->ovr = newovr;
+}
+
+static void cdns_i3c_gpio_set(struct gpio_chip *g, unsigned int offset,
+			      int value)
+{
+	unsigned long mask = BIT(offset), bits = value ? BIT(offset) : 0;
+
+	cdns_i3c_gpio_set_multiple(g, &mask, &bits);
+}
+
+static int cdns_i3c_gpio_set_dir(struct cdns_i3c_gpio *gpioc, unsigned int pin,
+				 bool in)
+{
+	u8 newdir;
+	int ret;
+
+	newdir = gpioc->dir;
+	if (in)
+		newdir |= BIT(pin);
+	else
+		newdir &= ~BIT(pin);
+
+	if (newdir == gpioc->dir)
+		return 0;
+
+	gpioc->dir = newdir;
+	ret = cdns_i3c_gpio_write_reg(gpioc, DIR_MODE, newdir);
+	if (!ret)
+		gpioc->dir = newdir;
+
+	return ret;
+}
+
+static int cdns_i3c_gpio_dir_input(struct gpio_chip *g, unsigned int offset)
+{
+	struct cdns_i3c_gpio *gpioc = gpioc_to_cdns_gpioc(g);
+
+	return cdns_i3c_gpio_set_dir(gpioc, offset, true);
+}
+
+static int cdns_i3c_gpio_dir_output(struct gpio_chip *g, unsigned int offset,
+				    int val)
+{
+	struct cdns_i3c_gpio *gpioc = gpioc_to_cdns_gpioc(g);
+
+	cdns_i3c_gpio_set(g, offset, val);
+
+	return cdns_i3c_gpio_set_dir(gpioc, offset, true);
+}
+
+static int cdns_i3c_gpio_get_multiple(struct gpio_chip *g,
+				      unsigned long *mask,
+				      unsigned long *bits)
+{
+	struct cdns_i3c_gpio *gpioc = gpioc_to_cdns_gpioc(g);
+	int ret;
+	u8 ivr;
+
+	ret = cdns_i3c_gpio_read_reg(gpioc, IVR, &ivr);
+	if (ret)
+		return ret;
+
+	*bits = ivr & *mask & gpioc->dir;
+	*bits |= gpioc->ovr & *mask & ~gpioc->dir;
+
+	return 0;
+}
+
+static int cdns_i3c_gpio_get(struct gpio_chip *g, unsigned int offset)
+{
+	unsigned long mask = BIT(offset), bits = 0;
+	int ret;
+
+	ret = cdns_i3c_gpio_get_multiple(g, &mask, &bits);
+	if (ret)
+		return ret;
+
+	return mask & bits;
+}
+
+static void cdns_i3c_gpio_ibi_handler(struct i3c_device *i3cdev,
+				      const struct i3c_ibi_payload *payload)
+{
+	struct cdns_i3c_gpio *gpioc = i3cdev_get_drvdata(i3cdev);
+	u8 isr = 0;
+	int i;
+
+	cdns_i3c_gpio_read_reg(gpioc, ISR, &isr);
+	for (i = 0; i < 8; i++) {
+		unsigned int irq;
+
+		if (!(BIT(i) & isr & gpioc->imr))
+			continue;
+
+		irq = irq_find_mapping(gpioc->gpioc.irq.domain, i);
+		handle_nested_irq(irq);
+	}
+}
+
+static void cdns_i3c_gpio_irq_lock(struct irq_data *data)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+	struct cdns_i3c_gpio *gpioc = gpiochip_get_data(gc);
+
+	mutex_lock(&gpioc->irq_lock);
+}
+
+static void cdns_i3c_gpio_irq_sync_unlock(struct irq_data *data)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+	struct cdns_i3c_gpio *gpioc = gpiochip_get_data(gc);
+	int i;
+
+	cdns_i3c_gpio_write_reg(gpioc, IMR, gpioc->imr);
+	for (i = 0; i < 3; i++)
+		cdns_i3c_gpio_write_reg(gpioc, ITR(i), gpioc->itr[i]);
+
+	mutex_unlock(&gpioc->irq_lock);
+}
+
+static void cdns_i3c_gpio_irq_unmask(struct irq_data *data)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+	struct cdns_i3c_gpio *gpioc = gpiochip_get_data(gc);
+
+	gpioc->imr |= BIT(data->hwirq);
+}
+
+static void cdns_i3c_gpio_irq_mask(struct irq_data *data)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+	struct cdns_i3c_gpio *gpioc = gpiochip_get_data(gc);
+
+	gpioc->imr &= ~BIT(data->hwirq);
+}
+
+static int cdns_i3c_gpio_irq_set_type(struct irq_data *data, unsigned int type)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+	struct cdns_i3c_gpio *gpioc = gpiochip_get_data(gc);
+
+	switch (type) {
+	case IRQ_TYPE_LEVEL_HIGH:
+		gpioc->itr[0] |= BIT(data->hwirq);
+		gpioc->itr[1] |= BIT(data->hwirq);
+		break;
+
+	case IRQ_TYPE_LEVEL_LOW:
+		gpioc->itr[0] |= BIT(data->hwirq);
+		gpioc->itr[1] &= ~BIT(data->hwirq);
+		break;
+
+	case IRQ_TYPE_EDGE_BOTH:
+		gpioc->itr[0] &= ~BIT(data->hwirq);
+		gpioc->itr[2] |= BIT(data->hwirq);
+		break;
+
+	case IRQ_TYPE_EDGE_RISING:
+		gpioc->itr[0] &= ~BIT(data->hwirq);
+		gpioc->itr[1] |= BIT(data->hwirq);
+		gpioc->itr[2] &= ~BIT(data->hwirq);
+		break;
+
+	case IRQ_TYPE_EDGE_FALLING:
+		gpioc->itr[0] &= ~BIT(data->hwirq);
+		gpioc->itr[1] &= ~BIT(data->hwirq);
+		gpioc->itr[2] &= ~BIT(data->hwirq);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cdns_i3c_gpio_probe(struct i3c_device *i3cdev)
+{
+	struct cdns_i3c_gpio *gpioc;
+	struct device *parent = i3cdev_to_dev(i3cdev);
+	struct i3c_ibi_setup ibisetup = {
+		.max_payload_len = 2,
+		.num_slots = 1,
+		.handler = cdns_i3c_gpio_ibi_handler,
+	};
+	int ret;
+
+	gpioc = devm_kzalloc(parent, sizeof(*gpioc), GFP_KERNEL);
+	if (!gpioc)
+		return -ENOMEM;
+
+	gpioc->i3cdev = i3cdev;
+	i3cdev_set_drvdata(i3cdev, gpioc);
+
+	/* Mask all interrupts. */
+	ret = cdns_i3c_gpio_write_reg(gpioc, IMR, 0);
+	if (ret)
+		return ret;
+
+	/*
+	 * Clear the ISR after reading it, not when the IBI is is Acked by the
+	 * I3C master. This way we make sure we don't lose events.
+	 */
+	ret = cdns_i3c_gpio_write_reg(gpioc, ITR(3), 0xff);
+	if (ret)
+		return ret;
+
+	ret = cdns_i3c_gpio_read_reg(gpioc, DIR_MODE, &gpioc->dir);
+	if (ret)
+		return ret;
+
+	ret = cdns_i3c_gpio_read_reg(gpioc, OVR, &gpioc->ovr);
+	if (ret)
+		return ret;
+
+	ret = i3c_device_request_ibi(i3cdev, &ibisetup);
+	if (ret)
+		return ret;
+
+	gpioc->gpioc.label = dev_name(parent);
+	gpioc->gpioc.owner = THIS_MODULE;
+	gpioc->gpioc.parent = parent;
+	gpioc->gpioc.base = -1;
+	gpioc->gpioc.ngpio = 8;
+	gpioc->gpioc.can_sleep = true;
+	gpioc->gpioc.get_direction = cdns_i3c_gpio_get_direction;
+	gpioc->gpioc.direction_input = cdns_i3c_gpio_dir_input;
+	gpioc->gpioc.direction_output = cdns_i3c_gpio_dir_output;
+	gpioc->gpioc.get = cdns_i3c_gpio_get;
+	gpioc->gpioc.get_multiple = cdns_i3c_gpio_get_multiple;
+	gpioc->gpioc.set = cdns_i3c_gpio_set;
+	gpioc->gpioc.set_multiple = cdns_i3c_gpio_set_multiple;
+
+	ret = devm_gpiochip_add_data(parent, &gpioc->gpioc, gpioc);
+	if (ret)
+		return ret;
+
+	gpioc->irqc.name = dev_name(parent);
+	gpioc->irqc.parent_device = parent;
+	gpioc->irqc.irq_unmask = cdns_i3c_gpio_irq_unmask;
+	gpioc->irqc.irq_mask = cdns_i3c_gpio_irq_mask;
+	gpioc->irqc.irq_bus_lock = cdns_i3c_gpio_irq_lock;
+	gpioc->irqc.irq_bus_sync_unlock = cdns_i3c_gpio_irq_sync_unlock;
+	gpioc->irqc.irq_set_type = cdns_i3c_gpio_irq_set_type;
+	gpioc->irqc.flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND;
+
+	ret = gpiochip_irqchip_add_nested(&gpioc->gpioc, &gpioc->irqc, 0,
+					  handle_simple_irq, IRQ_TYPE_NONE);
+	if (ret)
+		goto err_free_ibi;
+
+	ret = i3c_device_enable_ibi(i3cdev);
+	if (ret)
+		goto err_free_ibi;
+
+	return 0;
+
+err_free_ibi:
+	i3c_device_free_ibi(i3cdev);
+
+	return ret;
+}
+
+static int cdns_i3c_gpio_remove(struct i3c_device *i3cdev)
+{
+	i3c_device_disable_ibi(i3cdev);
+	i3c_device_free_ibi(i3cdev);
+
+	return 0;
+}
+
+static const struct i3c_device_id cdns_i3c_gpio_ids[] = {
+	I3C_DEVICE(0x1c9, 0x0, NULL),
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(i3c, cdns_i3c_gpio_ids);
+
+static struct i3c_driver cdns_i3c_gpio = {
+	.driver.name = "cdns-i3c-gpio",
+	.id_table = cdns_i3c_gpio_ids,
+	.probe = cdns_i3c_gpio_probe,
+	.remove = cdns_i3c_gpio_remove,
+};
+module_i3c_driver(cdns_i3c_gpio);
+
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@bootlin.com>");
+MODULE_DESCRIPTION("Driver for Cadence I3C GPIO expander");
+MODULE_LICENSE("GPL v2");