All of lore.kernel.org
 help / color / mirror / Atom feed
From: AngeloGioacchino Del Regno  <angelogioacchino.delregno@somainline.org>
To: Linus Walleij <linus.walleij@linaro.org>
Cc: "open list:GPIO SUBSYSTEM" <linux-gpio@vger.kernel.org>,
	konrad.dybcio@somainline.org, marijn.suijten@somainline.org,
	martin.botka@somainline.org, phone-devel@vger.kernel.org,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	"open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS" 
	<devicetree@vger.kernel.org>, Rob Herring <robh+dt@kernel.org>
Subject: Re: [PATCH 1/2] pinctrl: Add driver for Awinic AW9523/B I2C GPIO Expander
Date: Sun, 10 Jan 2021 00:11:57 +0100	[thread overview]
Message-ID: <111b918d-2b43-be81-2dbf-e984750b0ef7@somainline.org> (raw)
In-Reply-To: <CACRpkdbETKnhgR2-T+s3ChY4v-C5ErdPEp2WcMSZHzJ=O-fHig@mail.gmail.com>

Il 09/01/21 23:11, Linus Walleij ha scritto:
> On Sat, Jan 9, 2021 at 3:02 PM AngeloGioacchino Del Regno
> <angelogioacchino.delregno@somainline.org> wrote:
> 
>> The Awinic AW9523(B) is a multi-function I2C gpio expander in a
>> TQFN-24L package, featuring PWM (max 37mA per pin, or total max
>> power 3.2Watts) for LED driving capability.
>>
>> It has two ports with 8 pins per port (for a total of 16 pins),
>> configurable as either PWM with 1/256 stepping or GPIO input/output,
>> 1.8V logic input; each GPIO can be configured as input or output
>> independently from each other.
>>
>> This IC also has an internal interrupt controller, which is capable
>> of generating an interrupt for each GPIO, depending on the
>> configuration, and will raise an interrupt on the INTN pin to
>> advertise this to an external interrupt controller.
>>
>> Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
> 
> Okay!
> 
> Overall this driver is in good shape.
> 
> The major review comment is that it'd be nice if you look into
> using regmaps register cache instead of rolling your own,
> and also possibly using regmaps locking rather than your own
> as a result of that.
> 
Actually, I really tried to use regmap's FLAT register cache and after 
many, many tries... I had to give up. I just couldn't get it working. :(

>> +config PINCTRL_AW9523
>> +       bool "Awinic AW9523/AW9523B I2C GPIO expander pinctrl driver"
>> +       depends on OF && I2C
>> +       select PINMUX
>> +       select PINCONF
>> +       select GENERIC_PINCONF
>> +       select GPIOLIB
>> +       select GPIOLIB_IRQCHIP
>> +       select REGMAP
>> +       help
>> +         The Awinic AW9523/AW9523B is a multi-function I2C GPIO
>> +         expander with PWM functionality. This driver bundles a
>> +         pinctrl driver to select the function muxing and a GPIO
>> +         driver to handle GPIO, when the GPIO function is selected.
>> +
>> +         Say yes to enable pinctrl and GPIO support for the AW9523(B).
> 
> This:
> 
> +       DECLARE_BITMAP(old_masked[AW9523_NUM_PORTS], AW9523_PINS_PER_PORT);
> +       DECLARE_BITMAP(masked[AW9523_NUM_PORTS], AW9523_PINS_PER_PORT)
> (...)
> +       DECLARE_BITMAP(direction_in[AW9523_NUM_PORTS], AW9523_PINS_PER_PORT);
> 
> And this looks like a reimplementation of the existing register cache
> in regmap. So use regmaps regcache instead. (More notes on that
> below.)
> 
> This looks good. Right dependencies and helpers.
> 
>> +       int hw_pin = pin % AW9523_PINS_PER_PORT;
> 
> This makes me a bit wary.
> 
> Is that really the "hardware pin" as it looks? It looks more like
> the bit number 0..7 in the register for that port. I would just name these
> "regbit" or just "n" like you do in the irq code.
> 
Yes this is the bit number 0..7, you've understood that right. I guess 
renaming it to "regbit" is a good choice, makes it more understandable!

>> +/*
>> + * __aw9523_gpio_get_direction - Get pin direction
>> + * @regmap: Regmap structure
>> + * @pin: gpiolib pin number
>> + * @hwp: pin index in port register
>> + *
>> + * Return: Pin direction for success or negative number for error
>> + */
>> +static int __aw9523_gpio_get_direction(struct regmap *regmap, u8 pin, u8 hwp)
> 
> Nitpick: I kind of dislike __underscore functions because they have
> ambiguous semantics. Sometimes it is a compiler thing. Sometimes
> it is an inner function from something wrapped, i.e. it depends on
> context what these underscores
> mean. What about finding a better name that says what the function
> is doing?
> 
My initial idea was aw9523_get_pin_direction... then I changed it to 
include the word "gpio" in an attempt to make it less confusing. Let's 
go for the initial one then!

>> +static int __aw9523_get_port_state(struct regmap *regmap, u8 pin,
>> +                                  u8 hw_pin, unsigned int *state)
> 
> Same.
> 
...And here I had another function without __prefix, which was then 
merged into another one as having it separated made no sense, then I 
forgot to remove the underscores. Oops! Removed!

>> +static int aw9523_gpio_irq_type(struct irq_data *d, unsigned int type)
>> +{
>> +       switch (type) {
>> +       case IRQ_TYPE_NONE:
>> +       case IRQ_TYPE_LEVEL_MASK:
>> +       case IRQ_TYPE_LEVEL_HIGH:
>> +       case IRQ_TYPE_LEVEL_LOW:
>> +       case IRQ_TYPE_EDGE_BOTH:
>> +       case IRQ_TYPE_EDGE_RISING:
>> +       case IRQ_TYPE_EDGE_FALLING:
>> +               return 0;
> 
> Does this hardware really support all these edge types without any
> software configuration whatsoever. That looks weird.
> 
And it would indeed be weird: I've rechecked the datasheet again and 
only LEVEL interrupts are supported. As stated there: "When AW9523B 
detect port change, any input state from high-level to low-level or from

low-level to high-level will generate interrupt after 8us internal 
deglitch."
I wonder what happened with my brain, there...

>> +static irqreturn_t aw9523_irq_thread_func(int irq, void *dev_id)
>> +{
>> +       struct aw9523 *awi = (struct aw9523 *)dev_id;
>> +       unsigned long n, val = 0;
>> +       unsigned long changed_gpio;
>> +       unsigned int tmp, port_pin, i, ret;
>> +
>> +       for (i = 0; i < AW9523_NUM_PORTS; i++) {
>> +               port_pin = i * AW9523_PINS_PER_PORT;
>> +               ret = regmap_read(awi->regmap,
>> +                                 AW9523_REG_IN_STATE(port_pin),
>> +                                 &tmp);
>> +               if (ret)
>> +                       return ret;
>> +
>> +               val |= (u8)tmp << (i * 8);
>> +       }
> 
> Can you convince me that these are not just consecutive registers
> that could be read in one go with regmap_bulk_read()?
> (I could not unwind the macros in my head, and you have the
> datasheet I suppose.)
> 
I cannot and I would never convince you of something wrong: yes, this is 
a read of two (and only two) consecutive registers. Here, I didn't go 
for regmap_bulk_read in favor of a "paranoid" performance optimization 
of this operation: in regmap_bulk_read we have 2 if branches, 1 if-else 
branch, plus another "implicit" (regmap_get_offset) if-else branch, and 
a switch. That's exactly what I'm avoiding with this for loop... for 1.5 
times.

...And that's the full story: all about keeping overhead as minimal as 
possible.
However, if it's really necessary to get that (even if very small) 
overhead, I can switch that to a regmap_bulk_read call... but from my 
perspective, having less instructions is better for many reasons.
A typical case of "less is more", I guess?

>> +/*
>> + * aw9523_irq_bus_sync_unlock - Synchronize state and unlock
>> + * @d: irq data
>> + *
>> + * Writes the interrupt mask bits (found in the bit map) to the
>> + * hardware, then unlocks the bus.
>> + */
>> +static void aw9523_irq_bus_sync_unlock(struct irq_data *d)
>> +{
>> +       struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d));
>> +       int i;
>> +
>> +       for (i = 0; i < AW9523_NUM_PORTS; i++) {
>> +               if (bitmap_equal(awi->irq->masked[i], awi->irq->old_masked[i],
>> +                                AW9523_PINS_PER_PORT))
>> +                       continue;
>> +               regmap_write(awi->regmap,
>> +                            AW9523_REG_INTR_DIS(AW9523_PINS_PER_PORT * i),
>> +                            *awi->irq->masked[i]);
>> +               bitmap_copy(awi->irq->old_masked[i], awi->irq->masked[i],
>> +                           AW9523_PINS_PER_PORT);
>> +       }
>> +       mutex_unlock(&awi->irq->lock);
>> +}
> 
> These copies in the state that you write out at sync unlock.
> 
> Can this not be done using the async facility in regmap?
> 
> regmap_write_async()/regcache_mark_dirty() in all the IRQ
> config etc functions, followed by a simple
> regcache_sync() here makes it unnecessary to keep your
> own register cache I believe?
> 
> At least that is how I always thought it was supposed to be
> used.
> 
As I wrote earlier, unfortunately I tried hard... but I couldn't succeed...

>> +static int aw9523_direction_input(struct gpio_chip *chip, unsigned int offset)
>> +{
>> +       struct aw9523 *awi = gpiochip_get_data(chip);
>> +       u8 hw_pin = offset % AW9523_PINS_PER_PORT;
>> +       int port = AW9523_PIN_TO_PORT(offset);
>> +
>> +       set_bit(offset, awi->direction_in[port]);
> 
> This direction_in state seems to be another reimplementation of regmaps
> register cache.
> 
>> +static int aw9523_hw_reset(struct aw9523 *awi)
>> +{
>> +       int ret, max_retries = 2;
>> +
>> +       /* Sometimes the chip needs more than one reset cycle */
>> +       do {
>> +               ret = __aw9523_hw_reset(awi);
> 
> Please give a better name to the inner function. Like
> aw9523_drive_reset_gpio() or so.
> 
I like it. aw9523_drive_reset_gpio it is!

>> +       for (i = 0; i < AW9523_NUM_PORTS; i++) {
>> +               bitmap_fill(awi->irq->masked[i], AW9523_PINS_PER_PORT);
>> +               bitmap_fill(awi->irq->old_masked[i], AW9523_PINS_PER_PORT);
>> +       }
> 
> This is another of these complications of reimplementing regmaps
> register cache.
> 
>> +static const struct regmap_config aw9523_regmap = {
>> +       .reg_bits = 8,
>> +       .val_bits = 8,
>> +
>> +       .cache_type = REGCACHE_NONE,
> 
> By using some elaborate caching here instead of implementing
> your own, the driver can be simplified.
> 
>> +       .disable_locking = true,
> 
> Are you sure you are not just reimplementing this locking
> with your mutex?
> 
Yes, I am using more specialized locking, which results in less 
lock-unlock operations in many cases, bringing *a lot* less overhead.
Using the regmap locking, my keyboard matrix was a lot slower: I really 
had the need to optimize this driver's performance as much as possible.

>> +static struct i2c_driver aw9523_driver = {
>> +       .driver = {
>> +               .name = "aw9523-pinctrl",
>> +               .of_match_table = of_aw9523_i2c_match,
>> +       },
>> +       .probe = aw9523_probe,
> 
> A lot of people (especially on Qualcomm platforms, which is used in the
> DT binding example) are working to modularize pin controllers.
> 
> This controller on a slow bus should be able to support .remove() I
> think?
> 
> You should even be able to insmod/rmmod it at runtime for testing.
> 
Actually, yes. I will add a .remove callback.
You will get a V2 of this driver tomorrow!

-- Angelo
> Yours,
> Linus Walleij
> 


  reply	other threads:[~2021-01-09 23:12 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-01-09 14:02 [PATCH 0/2] Add Awinic AW9523(B) I2C GPIO Expander driver AngeloGioacchino Del Regno
2021-01-09 14:02 ` [PATCH 1/2] pinctrl: Add driver for Awinic AW9523/B I2C GPIO Expander AngeloGioacchino Del Regno
2021-01-09 17:24   ` kernel test robot
2021-01-09 17:24     ` kernel test robot
2021-01-09 22:12     ` Linus Walleij
2021-01-09 22:12       ` Linus Walleij
2021-01-09 23:12       ` AngeloGioacchino Del Regno
2021-01-09 22:11   ` Linus Walleij
2021-01-09 23:11     ` AngeloGioacchino Del Regno [this message]
2021-01-10  0:24       ` Linus Walleij
2021-01-10 14:32         ` AngeloGioacchino Del Regno
2021-01-10 14:56           ` AngeloGioacchino Del Regno
2021-01-10 19:35           ` Linus Walleij
2021-01-11 17:54             ` AngeloGioacchino Del Regno
2021-01-11 13:10           ` Mark Brown
2021-01-09 14:02 ` [PATCH 2/2] dt-bindings: pinctrl: Add bindings for Awinic AW9523/AW9523B AngeloGioacchino Del Regno
2021-01-09 22:14   ` Linus Walleij
2021-01-09 23:13     ` AngeloGioacchino Del Regno
2021-01-10 17:18   ` Rob Herring
2021-01-25 18:21 [PATCH 1/2] pinctrl: Add driver for Awinic AW9523/B I2C GPIO Expander AngeloGioacchino Del Regno

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=111b918d-2b43-be81-2dbf-e984750b0ef7@somainline.org \
    --to=angelogioacchino.delregno@somainline.org \
    --cc=devicetree@vger.kernel.org \
    --cc=konrad.dybcio@somainline.org \
    --cc=linus.walleij@linaro.org \
    --cc=linux-gpio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=marijn.suijten@somainline.org \
    --cc=martin.botka@somainline.org \
    --cc=phone-devel@vger.kernel.org \
    --cc=robh+dt@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.