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
>
next prev parent reply other threads:[~2021-01-09 23:12 UTC|newest]
Thread overview: 18+ 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 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®