lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <111b918d-2b43-be81-2dbf-e984750b0ef7@somainline.org>
Date:   Sun, 10 Jan 2021 00:11:57 +0100
From:   AngeloGioacchino Del Regno 
        <angelogioacchino.delregno@...ainline.org>
To:     Linus Walleij <linus.walleij@...aro.org>
Cc:     "open list:GPIO SUBSYSTEM" <linux-gpio@...r.kernel.org>,
        konrad.dybcio@...ainline.org, marijn.suijten@...ainline.org,
        martin.botka@...ainline.org, phone-devel@...r.kernel.org,
        "linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
        "open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS" 
        <devicetree@...r.kernel.org>, Rob Herring <robh+dt@...nel.org>
Subject: Re: [PATCH 1/2] pinctrl: Add driver for Awinic AW9523/B I2C GPIO
 Expander

Il 09/01/21 23:11, Linus Walleij ha scritto:
> On Sat, Jan 9, 2021 at 3:02 PM AngeloGioacchino Del Regno
> <angelogioacchino.delregno@...ainline.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@...ainline.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
> 

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ