diff mbox series

[v1,2/2] gpio: ds4520: Add ADI DS4520 Regulator Support

Message ID 20230327130010.8342-3-okan.sahin@analog.com
State New
Headers show
Series Add DS4520 GPIO Expander Support | expand

Commit Message

Sahin, Okan March 27, 2023, 1 p.m. UTC
Gpio I/O expander.

Signed-off-by: Okan Sahin <okan.sahin@analog.com>
---
 drivers/gpio/Kconfig       |  10 ++
 drivers/gpio/Makefile      |   1 +
 drivers/gpio/gpio-ds4520.c | 198 +++++++++++++++++++++++++++++++++++++
 3 files changed, 209 insertions(+)
 create mode 100644 drivers/gpio/gpio-ds4520.c

Comments

Linus Walleij March 31, 2023, 9:41 a.m. UTC | #1
Hi Okan,

thanks for your patch!

First: why is the word "Regulator" in the subject? I don't quite get it.

On Mon, Mar 27, 2023 at 3:01 PM Okan Sahin <okan.sahin@analog.com> wrote:
>
> Gpio I/O expander.
>
> Signed-off-by: Okan Sahin <okan.sahin@analog.com>

This commit log is too terse. Write a bit about what this hardware is.

> +config GPIO_DS4520
> +       tristate "DS4520 I2C GPIO expander"
> +       select REGMAP_I2C
> +       help
> +         GPIO driver for Maxim MAX7300 I2C-based GPIO expander.

Is it MAX7300, I don't get this, it seems super-confused.

> +         Say yes here to enable the GPIO driver for the ADI DS4520 chip.
> +
> +         To compile this driver as a module, choose M here: the module will
> +         be called gpio-ds4520.

(...)

The driver is pretty straight-forward, but I think this can use the
generic GPIO_REGMAP helpers in
drivers/gpio/gpio-regmap.c
check other drivers selecting this helper library for inspiration.

Yours,
Linus Walleij
Sahin, Okan April 4, 2023, 2:35 p.m. UTC | #2
>Hi Okan,
>
>thanks for your patch!
>
>First: why is the word "Regulator" in the subject? I don't quite get it.
>
>On Mon, Mar 27, 2023 at 3:01 PM Okan Sahin <okan.sahin@analog.com> wrote:
>>
>> Gpio I/O expander.
>>
>> Signed-off-by: Okan Sahin <okan.sahin@analog.com>
>
>This commit log is too terse. Write a bit about what this hardware is.
>
>> +config GPIO_DS4520
>> +       tristate "DS4520 I2C GPIO expander"
>> +       select REGMAP_I2C
>> +       help
>> +         GPIO driver for Maxim MAX7300 I2C-based GPIO expander.
>
>Is it MAX7300, I don't get this, it seems super-confused.
>
>> +         Say yes here to enable the GPIO driver for the ADI DS4520 chip.
>> +
>> +         To compile this driver as a module, choose M here: the module will
>> +         be called gpio-ds4520.
>
>(...)
>
>The driver is pretty straight-forward, but I think this can use the generic
>GPIO_REGMAP helpers in drivers/gpio/gpio-regmap.c check other drivers selecting
>this helper library for inspiration.
>
>Yours,
>Linus Walleij

Hi Linus,

Thank you for your contribution. Should I add select GPIO_REGMAP into Kconfig?
I am trying to understand completely before sending new patch.
I will fix your other comments.

Regards,
Okan Sahin
Linus Walleij April 5, 2023, 1:20 p.m. UTC | #3
On Tue, Apr 4, 2023 at 4:36 PM Sahin, Okan <Okan.Sahin@analog.com> wrote:

> >The driver is pretty straight-forward, but I think this can use the generic
> >GPIO_REGMAP helpers in drivers/gpio/gpio-regmap.c check other drivers selecting
> >this helper library for inspiration.
(..)
> Thank you for your contribution. Should I add select GPIO_REGMAP into Kconfig?

Yes but that is not all, you also need to make use of the library helpers
provided in include/linux/gpio/regmap.h.

Find examples of other drivers doing this by e.g.:
git grep gpio_regmap_register

drivers/gpio/gpio-sl28cpld.c:   return
PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&pdev->dev, &config));
drivers/gpio/gpio-tn48m.c:      return
PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&pdev->dev, &config));
drivers/pinctrl/bcm/pinctrl-bcm63xx.c:  return
PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &grc));

^Look what these are doing

Yours,
Linus Walleij
Michael Walle April 5, 2023, 1:57 p.m. UTC | #4
Am 2023-04-05 15:20, schrieb Linus Walleij:
> On Tue, Apr 4, 2023 at 4:36 PM Sahin, Okan <Okan.Sahin@analog.com> 
> wrote:
> 
>> >The driver is pretty straight-forward, but I think this can use the generic
>> >GPIO_REGMAP helpers in drivers/gpio/gpio-regmap.c check other drivers selecting
>> >this helper library for inspiration.
> (..)
>> Thank you for your contribution. Should I add select GPIO_REGMAP into 
>> Kconfig?
> 
> Yes but that is not all, you also need to make use of the library 
> helpers
> provided in include/linux/gpio/regmap.h.
> 
> Find examples of other drivers doing this by e.g.:
> git grep gpio_regmap_register
> 
> drivers/gpio/gpio-sl28cpld.c:   return
> PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&pdev->dev, &config));
> drivers/gpio/gpio-tn48m.c:      return
> PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&pdev->dev, &config));
> drivers/pinctrl/bcm/pinctrl-bcm63xx.c:  return
> PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &grc));
> 
> ^Look what these are doing

This driver is doing two register writes/reads for setting the
direction, that's something which isn't supported in GPIO_REGMAP.
OTOH I'm not sure the driver is doing it correctly, because it also
seems to switch the pullup resisters together with the direction.
I'm not sure that is correct. So there might be just one register
involved after all and the GPIO_REGMAP should work again.

Also, according to the datasheet this has some nv memory (to set the
initial state of the GPIOs [?]). So it should really be a multi-function
device. I'm not sure if this has to be considered right from the
beginning or if the device support can start with GPIO only and later
be transitioned to a full featured MFD (probably with nvmem support).

-michael
Linus Walleij April 7, 2023, 1:48 p.m. UTC | #5
On Wed, Apr 5, 2023 at 3:57 PM Michael Walle <michael@walle.cc> wrote:

> OTOH I'm not sure the driver is doing it correctly, because it also
> seems to switch the pullup resisters together with the direction.
> I'm not sure that is correct. So there might be just one register
> involved after all and the GPIO_REGMAP should work again.

I'm pretty sure that should be in the .set_config() callback.

> Also, according to the datasheet this has some nv memory (to set the
> initial state of the GPIOs [?]). So it should really be a multi-function
> device. I'm not sure if this has to be considered right from the
> beginning or if the device support can start with GPIO only and later
> be transitioned to a full featured MFD (probably with nvmem support).

That's a bit of a soft definition.

If the chip is *only* doing GPIO and nvram it can be a GPIO-only
device I think.

The precedent is a ton of ethernet drivers with nvram for storing
e.g. the MAC address. We don't make all of those into MFDs,
as the nvram is closely tied to the one and only function of the
block.

Yours,
Linus Walleij
Andy Shevchenko April 7, 2023, 6:36 p.m. UTC | #6
Fri, Apr 07, 2023 at 03:48:25PM +0200, Linus Walleij kirjoitti:
> On Wed, Apr 5, 2023 at 3:57 PM Michael Walle <michael@walle.cc> wrote:
> 
> > OTOH I'm not sure the driver is doing it correctly, because it also
> > seems to switch the pullup resisters together with the direction.
> > I'm not sure that is correct. So there might be just one register
> > involved after all and the GPIO_REGMAP should work again.
> 
> I'm pretty sure that should be in the .set_config() callback.
> 
> > Also, according to the datasheet this has some nv memory (to set the
> > initial state of the GPIOs [?]). So it should really be a multi-function
> > device. I'm not sure if this has to be considered right from the
> > beginning or if the device support can start with GPIO only and later
> > be transitioned to a full featured MFD (probably with nvmem support).
> 
> That's a bit of a soft definition.
> 
> If the chip is *only* doing GPIO and nvram it can be a GPIO-only
> device I think.
> 
> The precedent is a ton of ethernet drivers with nvram for storing
> e.g. the MAC address. We don't make all of those into MFDs,
> as the nvram is closely tied to the one and only function of the
> block.

I agree with Linus. This should be part of the actual (main) driver for
the chip as many do (like USB to serial adapters that have GPIO capability).
Also this code lacks of proper locking and has style issues.
Sahin, Okan April 9, 2023, 2:25 p.m. UTC | #7
>Fri, Apr 07, 2023 at 03:48:25PM +0200, Linus Walleij kirjoitti:
>> On Wed, Apr 5, 2023 at 3:57 PM Michael Walle <michael@walle.cc> wrote:
>>
>> > OTOH I'm not sure the driver is doing it correctly, because it also
>> > seems to switch the pullup resisters together with the direction.
>> > I'm not sure that is correct. So there might be just one register
>> > involved after all and the GPIO_REGMAP should work again.
>>
>> I'm pretty sure that should be in the .set_config() callback.
>>
>> > Also, according to the datasheet this has some nv memory (to set the
>> > initial state of the GPIOs [?]). So it should really be a
>> > multi-function device. I'm not sure if this has to be considered
>> > right from the beginning or if the device support can start with
>> > GPIO only and later be transitioned to a full featured MFD (probably with nvmem
>support).
>>
>> That's a bit of a soft definition.
>>
>> If the chip is *only* doing GPIO and nvram it can be a GPIO-only
>> device I think.
>>
>> The precedent is a ton of ethernet drivers with nvram for storing e.g.
>> the MAC address. We don't make all of those into MFDs, as the nvram is
>> closely tied to the one and only function of the block.
>
>I agree with Linus. This should be part of the actual (main) driver for the chip as many
>do (like USB to serial adapters that have GPIO capability).
>Also this code lacks of proper locking and has style issues.
>
>--
>With Best Regards,
>Andy Shevchenko
>

Hi Linus,

I think gpio_regmap is not suitable for this driver as Michael stated. 
https://www.analog.com/media/en/technical-documentation/data-sheets/ds4520.pdf
Please check block diagram. There are two input registers that control gpio state
so gpio_regmap does not look ok for this. Am I missing something?

Hi Michael,

I tested driver for both writing and reading, it seems fine. Initially, this question
confused me too, but after examining other drivers with nvmem, my opinion is 
same as both Linus and Andy. Also, at this point I am not planning to add
nvmem support.

Hi Andy,

Could you give more detail related to locking and style issues?

Regards,
Okan Sahin
Michael Walle April 11, 2023, 1:42 p.m. UTC | #8
Am 2023-04-09 16:25, schrieb Sahin, Okan:
>> Fri, Apr 07, 2023 at 03:48:25PM +0200, Linus Walleij kirjoitti:
>>> On Wed, Apr 5, 2023 at 3:57 PM Michael Walle <michael@walle.cc> 
>>> wrote:
>>> 
>>> > OTOH I'm not sure the driver is doing it correctly, because it also
>>> > seems to switch the pullup resisters together with the direction.
>>> > I'm not sure that is correct. So there might be just one register
>>> > involved after all and the GPIO_REGMAP should work again.
>>> 
>>> I'm pretty sure that should be in the .set_config() callback.
>>> 
>>> > Also, according to the datasheet this has some nv memory (to set the
>>> > initial state of the GPIOs [?]). So it should really be a
>>> > multi-function device. I'm not sure if this has to be considered
>>> > right from the beginning or if the device support can start with
>>> > GPIO only and later be transitioned to a full featured MFD (probably with nvmem
>> support).
>>> 
>>> That's a bit of a soft definition.
>>> 
>>> If the chip is *only* doing GPIO and nvram it can be a GPIO-only
>>> device I think.
>>> 
>>> The precedent is a ton of ethernet drivers with nvram for storing 
>>> e.g.
>>> the MAC address. We don't make all of those into MFDs, as the nvram 
>>> is
>>> closely tied to the one and only function of the block.
>> 
>> I agree with Linus. This should be part of the actual (main) driver 
>> for the chip as many
>> do (like USB to serial adapters that have GPIO capability).

You mean the gpio driver is calling nvmem_register()? Yeah I agree, that
should work.

> I think gpio_regmap is not suitable for this driver as Michael stated.
> https://www.analog.com/media/en/technical-documentation/data-sheets/ds4520.pdf
> Please check block diagram. There are two input registers that control
> gpio state
> so gpio_regmap does not look ok for this. Am I missing something?

You mean F8/F9? That will work as they are for different GPIOs. What
doesn't work with gpio-regmap is when you need to modify two different
registers for one GPIO. Have a look at gpio_regmap_get() and
gpio_regmap_set(). If the default gpio_regmap_simple_xlate() doesn't 
work
you can use your own .xlate() op.

> Also, at this point I am not planning to add nvmem support.

That is a pity, because that is the whole use case for this gpio 
expander,
no? "Programmable Replacement for Mechanical Jumpers and Switches"

-michael
Andy Shevchenko April 11, 2023, 2:11 p.m. UTC | #9
On Sun, Apr 9, 2023 at 5:25 PM Sahin, Okan <Okan.Sahin@analog.com> wrote:
> >Fri, Apr 07, 2023 at 03:48:25PM +0200, Linus Walleij kirjoitti:
> >> On Wed, Apr 5, 2023 at 3:57 PM Michael Walle <michael@walle.cc> wrote:

...

> >> If the chip is *only* doing GPIO and nvram it can be a GPIO-only
> >> device I think.

I have read a datasheet, it has the pre-boot settings, but it doesn't
matter from the Linux point of view. The only thing which we need to
take care of is to have the EEPROM disabled during GPIO interaction.
However, there is a question as to how this device should actually
commit into EEPROM the state to survive the cold/warm power cycle.
What is the persistent flag for, btw, I haven't been familiar with it?

> >> The precedent is a ton of ethernet drivers with nvram for storing e.g.
> >> the MAC address. We don't make all of those into MFDs, as the nvram is
> >> closely tied to the one and only function of the block.
> >
> >I agree with Linus. This should be part of the actual (main) driver for the chip as many
> >do (like USB to serial adapters that have GPIO capability).
> >Also this code lacks of proper locking and has style issues.

...

> Could you give more detail related to locking and style issues?

You use a few IO accessors in a row without locking, which means at
any point of this flow some other CPUs may access the chip using the
same or other APIs of this driver.
Sahin, Okan April 24, 2023, 3:39 p.m. UTC | #10
>Am 2023-04-09 16:25, schrieb Sahin, Okan:
>>> Fri, Apr 07, 2023 at 03:48:25PM +0200, Linus Walleij kirjoitti:
>>>> On Wed, Apr 5, 2023 at 3:57 PM Michael Walle <michael@walle.cc>
>>>> wrote:
>>>>
>>>> > OTOH I'm not sure the driver is doing it correctly, because it also
>>>> > seems to switch the pullup resisters together with the direction.
>>>> > I'm not sure that is correct. So there might be just one register
>>>> > involved after all and the GPIO_REGMAP should work again.
>>>>
>>>> I'm pretty sure that should be in the .set_config() callback.
>>>>
>>>> > Also, according to the datasheet this has some nv memory (to set the
>>>> > initial state of the GPIOs [?]). So it should really be a
>>>> > multi-function device. I'm not sure if this has to be considered
>>>> > right from the beginning or if the device support can start with
>>>> > GPIO only and later be transitioned to a full featured MFD (probably with
>nvmem
>>> support).
>>>>
>>>> That's a bit of a soft definition.
>>>>
>>>> If the chip is *only* doing GPIO and nvram it can be a GPIO-only
>>>> device I think.
>>>>
>>>> The precedent is a ton of ethernet drivers with nvram for storing
>>>> e.g.
>>>> the MAC address. We don't make all of those into MFDs, as the nvram
>>>> is
>>>> closely tied to the one and only function of the block.
>>>
>>> I agree with Linus. This should be part of the actual (main) driver
>>> for the chip as many
>>> do (like USB to serial adapters that have GPIO capability).
>
>You mean the gpio driver is calling nvmem_register()? Yeah I agree, that
>should work.
>
>> I think gpio_regmap is not suitable for this driver as Michael stated.
>> https://www.analog.com/media/en/technical-documentation/data-
>sheets/ds4520.pdf
>> Please check block diagram. There are two input registers that control
>> gpio state
>> so gpio_regmap does not look ok for this. Am I missing something?
>
>You mean F8/F9? That will work as they are for different GPIOs. What
>doesn't work with gpio-regmap is when you need to modify two different
>registers for one GPIO. Have a look at gpio_regmap_get() and
>gpio_regmap_set(). If the default gpio_regmap_simple_xlate() doesn't
>work
>you can use your own .xlate() op.
>

Actually, I checked the functions that you suggested, but as far as I understand
they might work if there would be one bit to set direction or value. However,
this is not the case for ds4520. In other words, if I want to set the gpio direction
as output, I need to set a corresponding bit for both F0 and F1 registers.
In the document, you can see block diagram. I do not know why, but design is
not standard that’s why I think I can not use gpio-regmap.

>> Also, at this point I am not planning to add nvmem support.
>
>That is a pity, because that is the whole use case for this gpio
>expander,
>no? "Programmable Replacement for Mechanical Jumpers and Switches"

I can set "SEE" bit as "0" in the Configuration Register to write EEPROM so it might solve
issue.

>
>-michael

Regards,
Okan Sahin
Michael Walle April 25, 2023, 7:05 a.m. UTC | #11
Am 2023-04-24 17:39, schrieb Sahin, Okan:
>> Am 2023-04-09 16:25, schrieb Sahin, Okan:
>>>> Fri, Apr 07, 2023 at 03:48:25PM +0200, Linus Walleij kirjoitti:
>>>>> On Wed, Apr 5, 2023 at 3:57 PM Michael Walle <michael@walle.cc>
>>>>> wrote:
>>>>> 
>>>>> > OTOH I'm not sure the driver is doing it correctly, because it also
>>>>> > seems to switch the pullup resisters together with the direction.
>>>>> > I'm not sure that is correct. So there might be just one register
>>>>> > involved after all and the GPIO_REGMAP should work again.
>>>>> 
>>>>> I'm pretty sure that should be in the .set_config() callback.
>>>>> 
>>>>> > Also, according to the datasheet this has some nv memory (to set the
>>>>> > initial state of the GPIOs [?]). So it should really be a
>>>>> > multi-function device. I'm not sure if this has to be considered
>>>>> > right from the beginning or if the device support can start with
>>>>> > GPIO only and later be transitioned to a full featured MFD (probably with
>> nvmem
>>>> support).
>>>>> 
>>>>> That's a bit of a soft definition.
>>>>> 
>>>>> If the chip is *only* doing GPIO and nvram it can be a GPIO-only
>>>>> device I think.
>>>>> 
>>>>> The precedent is a ton of ethernet drivers with nvram for storing
>>>>> e.g.
>>>>> the MAC address. We don't make all of those into MFDs, as the nvram
>>>>> is
>>>>> closely tied to the one and only function of the block.
>>>> 
>>>> I agree with Linus. This should be part of the actual (main) driver
>>>> for the chip as many
>>>> do (like USB to serial adapters that have GPIO capability).
>> 
>> You mean the gpio driver is calling nvmem_register()? Yeah I agree, 
>> that
>> should work.
>> 
>>> I think gpio_regmap is not suitable for this driver as Michael 
>>> stated.
>>> https://www.analog.com/media/en/technical-documentation/data-
>> sheets/ds4520.pdf
>>> Please check block diagram. There are two input registers that 
>>> control
>>> gpio state
>>> so gpio_regmap does not look ok for this. Am I missing something?
>> 
>> You mean F8/F9? That will work as they are for different GPIOs. What
>> doesn't work with gpio-regmap is when you need to modify two different
>> registers for one GPIO. Have a look at gpio_regmap_get() and
>> gpio_regmap_set(). If the default gpio_regmap_simple_xlate() doesn't
>> work
>> you can use your own .xlate() op.
>> 
> 
> Actually, I checked the functions that you suggested, but as far as I 
> understand
> they might work if there would be one bit to set direction or value. 
> However,
> this is not the case for ds4520. In other words, if I want to set the
> gpio direction
> as output, I need to set a corresponding bit for both F0 and F1 
> registers.

I can't follow. F0/F1 is for the pull-up. That was actually my initial
question and Linus said, that should probably be done in a seperate
.set_config operation not together with a direction change.

> In the document, you can see block diagram. I do not know why, but 
> design is
> not standard that’s why I think I can not use gpio-regmap.
> 
>>> Also, at this point I am not planning to add nvmem support.
>> 
>> That is a pity, because that is the whole use case for this gpio
>> expander,
>> no? "Programmable Replacement for Mechanical Jumpers and Switches"
> 
> I can set "SEE" bit as "0" in the Configuration Register to write
> EEPROM so it might solve
> issue.

If you do that unconditionally, that might wear out the EEPROM,
though.

-michael
Sahin, Okan April 26, 2023, 11:28 a.m. UTC | #12
>Am 2023-04-24 17:39, schrieb Sahin, Okan:
>>> Am 2023-04-09 16:25, schrieb Sahin, Okan:
>>>>> Fri, Apr 07, 2023 at 03:48:25PM +0200, Linus Walleij kirjoitti:
>>>>>> On Wed, Apr 5, 2023 at 3:57 PM Michael Walle <michael@walle.cc>
>>>>>> wrote:
>>>>>>
>>>>>> > OTOH I'm not sure the driver is doing it correctly, because it also
>>>>>> > seems to switch the pullup resisters together with the direction.
>>>>>> > I'm not sure that is correct. So there might be just one register
>>>>>> > involved after all and the GPIO_REGMAP should work again.
>>>>>>
>>>>>> I'm pretty sure that should be in the .set_config() callback.
>>>>>>
>>>>>> > Also, according to the datasheet this has some nv memory (to set the
>>>>>> > initial state of the GPIOs [?]). So it should really be a
>>>>>> > multi-function device. I'm not sure if this has to be considered
>>>>>> > right from the beginning or if the device support can start with
>>>>>> > GPIO only and later be transitioned to a full featured MFD (probably with
>>> nvmem
>>>>> support).
>>>>>>
>>>>>> That's a bit of a soft definition.
>>>>>>
>>>>>> If the chip is *only* doing GPIO and nvram it can be a GPIO-only
>>>>>> device I think.
>>>>>>
>>>>>> The precedent is a ton of ethernet drivers with nvram for storing
>>>>>> e.g.
>>>>>> the MAC address. We don't make all of those into MFDs, as the nvram
>>>>>> is
>>>>>> closely tied to the one and only function of the block.
>>>>>
>>>>> I agree with Linus. This should be part of the actual (main) driver
>>>>> for the chip as many
>>>>> do (like USB to serial adapters that have GPIO capability).
>>>
>>> You mean the gpio driver is calling nvmem_register()? Yeah I agree,
>>> that
>>> should work.
>>>
>>>> I think gpio_regmap is not suitable for this driver as Michael
>>>> stated.
>>>> https://www.analog.com/media/en/technical-documentation/data-
>>> sheets/ds4520.pdf
>>>> Please check block diagram. There are two input registers that
>>>> control
>>>> gpio state
>>>> so gpio_regmap does not look ok for this. Am I missing something?
>>>
>>> You mean F8/F9? That will work as they are for different GPIOs. What
>>> doesn't work with gpio-regmap is when you need to modify two different
>>> registers for one GPIO. Have a look at gpio_regmap_get() and
>>> gpio_regmap_set(). If the default gpio_regmap_simple_xlate() doesn't
>>> work
>>> you can use your own .xlate() op.
>>>
>>
>> Actually, I checked the functions that you suggested, but as far as I
>> understand
>> they might work if there would be one bit to set direction or value.
>> However,
>> this is not the case for ds4520. In other words, if I want to set the
>> gpio direction
>> as output, I need to set a corresponding bit for both F0 and F1
>> registers.
>
>I can't follow. F0/F1 is for the pull-up. That was actually my initial
>question and Linus said, that should probably be done in a seperate
>.set_config operation not together with a direction change.

I think I understand what you are trying to say so far. I did not have too much
experience related to gpio. I will set pull_up register in .set_config
However, I did not understand where its parameters come from.
set_config(struct gpio_chip *chip, unsigned int offset,
	      unsigned long config)
It might be trivial question, but Where does config come from?

At the end, I should rewrite the code using regmap_gpio, right? So if I rewrite 
code using regmap_gpio, how can I replace set_config(...)?

>
>> In the document, you can see block diagram. I do not know why, but
>> design is
>> not standard that’s why I think I can not use gpio-regmap.
>>
>>>> Also, at this point I am not planning to add nvmem support.
>>>
>>> That is a pity, because that is the whole use case for this gpio
>>> expander,
>>> no? "Programmable Replacement for Mechanical Jumpers and Switches"
>>
>> I can set "SEE" bit as "0" in the Configuration Register to write
>> EEPROM so it might solve
>> issue.
>
>If you do that unconditionally, that might wear out the EEPROM,
>though.
>
>-michael

Hi Michael,

Thank you for your support.

Regards,
Okan Sahin
Michael Walle April 26, 2023, 11:53 a.m. UTC | #13
Hi,

> I think I understand what you are trying to say so far. I did not have 
> too much
> experience related to gpio. I will set pull_up register in .set_config
> However, I did not understand where its parameters come from.
> set_config(struct gpio_chip *chip, unsigned int offset,
> 	      unsigned long config)
> It might be trivial question, but Where does config come from?

Others have to answer that one as I don't have that much experience 
either.

> At the end, I should rewrite the code using regmap_gpio, right? So if I 
> rewrite
> code using regmap_gpio, how can I replace set_config(...)?

You'd have to add a .set_config to gpio_regmap_config and then in

gpio_regmap_register():
   gpio->set_config = config->set_config;

I don't think it makes sense to have a default implementation in 
gpio-regmap,
the variances between "simple" gpio controllers might be too broad.

-michael
Sahin, Okan April 26, 2023, 1:39 p.m. UTC | #14
>> I think I understand what you are trying to say so far. I did not have
>> too much
>> experience related to gpio. I will set pull_up register in .set_config
>> However, I did not understand where its parameters come from.
>> set_config(struct gpio_chip *chip, unsigned int offset,
>> 	      unsigned long config)
>> It might be trivial question, but Where does config come from?
>
>Others have to answer that one as I don't have that much experience
>either.
>
>> At the end, I should rewrite the code using regmap_gpio, right? So if I
>> rewrite
>> code using regmap_gpio, how can I replace set_config(...)?
>
>You'd have to add a .set_config to gpio_regmap_config and then in
>
>gpio_regmap_register():
>   gpio->set_config = config->set_config;
>
>I don't think it makes sense to have a default implementation in
>gpio-regmap,
>the variances between "simple" gpio controllers might be too broad.
>
>-michael

Hi,

One last question, as ds4520 IC has 9 I/O pins so I need to set registers like 
below
struct gpio_regmap *gpio;
config.reg_dir_out_base = IO_CONTROL0; (get_direction and setting direction)
config.reg_dat_base = IO_STATUS0; (for .get)
config.reg_set_base = IO_STATUS0; (for .set)

As I have both directions, I must set both reg_dat_base and
reg_set_base.

https://elixir.bootlin.com/linux/latest/source/include/linux/gpio/regmap.h#L52

In this case, I am able to use only pull_up register to set output value to high
as default. Is that okay? I am asking again and again to minimize number of 
patch. I want to be sure before sending new patch. Thank you for your contribution.

Regards,
Okan Sahin
diff mbox series

Patch

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 13be729710f2..20aa28e208d5 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1000,6 +1000,16 @@  config GPIO_ADNP
 	  enough to represent all pins, but the driver will assume a
 	  register layout for 64 pins (8 registers).
 
+config GPIO_DS4520
+	tristate "DS4520 I2C GPIO expander"
+	select REGMAP_I2C
+	help
+	  GPIO driver for Maxim MAX7300 I2C-based GPIO expander.
+	  Say yes here to enable the GPIO driver for the ADI DS4520 chip.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called gpio-ds4520.
+
 config GPIO_GW_PLD
 	tristate "Gateworks PLD GPIO Expander"
 	depends on OF_GPIO
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index c048ba003367..6f8656d5d617 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -52,6 +52,7 @@  obj-$(CONFIG_GPIO_DA9052)		+= gpio-da9052.o
 obj-$(CONFIG_GPIO_DA9055)		+= gpio-da9055.o
 obj-$(CONFIG_GPIO_DAVINCI)		+= gpio-davinci.o
 obj-$(CONFIG_GPIO_DLN2)			+= gpio-dln2.o
+obj-$(CONFIG_GPIO_DS4520)		+= gpio-ds4520.o
 obj-$(CONFIG_GPIO_DWAPB)		+= gpio-dwapb.o
 obj-$(CONFIG_GPIO_EIC_SPRD)		+= gpio-eic-sprd.o
 obj-$(CONFIG_GPIO_EM)			+= gpio-em.o
diff --git a/drivers/gpio/gpio-ds4520.c b/drivers/gpio/gpio-ds4520.c
new file mode 100644
index 000000000000..8f878e7824b8
--- /dev/null
+++ b/drivers/gpio/gpio-ds4520.c
@@ -0,0 +1,198 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 Analog Devices, Inc.
+ * Driver for the DS4520 I/O Expander
+ */
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/gpio/driver.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#define NUMBER_OF_GPIO	9
+
+#define PULLUP0		0xF0
+#define IO_CONTROL0	0xF2
+#define IO_STATUS0	0xF8
+
+#define REG_SIZE	8
+
+struct ds4520_gpio_priv {
+	struct regmap *regmap;
+	struct gpio_chip gpio_chip;
+};
+
+static int ds4520_gpio_get_direction(struct gpio_chip *chip,
+				     unsigned int offset)
+{
+	struct ds4520_gpio_priv *priv = gpiochip_get_data(chip);
+	u32 val_io_control = 0, val_pullup = 0;
+	u8 reg_offset = (offset / REG_SIZE);
+	int ret;
+
+	ret = regmap_set_bits(priv->regmap, 0xF4, 0x01);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(priv->regmap, IO_CONTROL0 + reg_offset,
+			  &val_io_control);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(priv->regmap, PULLUP0 + reg_offset, &val_pullup);
+	if (ret)
+		return ret;
+
+	if ((val_io_control & (1 << (offset % 8)))
+	    == (val_pullup & (1 << (offset % 8))))
+		return GPIO_LINE_DIRECTION_OUT;
+	else
+		return GPIO_LINE_DIRECTION_IN;
+}
+
+static int ds4520_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned int offset)
+{
+	struct ds4520_gpio_priv *priv = gpiochip_get_data(chip);
+	u8 reg_offset = (offset / REG_SIZE);
+	u8 mask = BIT(offset % 8);
+	int ret;
+
+	ret = regmap_clear_bits(priv->regmap, IO_CONTROL0 + reg_offset, mask);
+	if (ret)
+		return ret;
+
+	ret = regmap_set_bits(priv->regmap, PULLUP0 + reg_offset, mask);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int ds4520_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+	struct ds4520_gpio_priv *priv = gpiochip_get_data(chip);
+	u8 reg_offset = (offset / REG_SIZE);
+	u8 mask = BIT(offset % 8);
+	u32 val = 0;
+	int ret;
+
+	ret = regmap_read(priv->regmap, IO_STATUS0 + reg_offset, &val);
+	if (ret)
+		return ret;
+
+	return !!(val & mask);
+}
+
+static void ds4520_gpio_set(struct gpio_chip *chip, unsigned int offset,
+			    int value)
+{
+	struct ds4520_gpio_priv *priv = gpiochip_get_data(chip);
+	u8 reg_offset = (offset / REG_SIZE);
+	u8 mask = BIT(offset % 8);
+
+	regmap_update_bits(priv->regmap, PULLUP0 + reg_offset, mask,
+			   value ? mask : 0);
+	regmap_update_bits(priv->regmap, IO_CONTROL0 + reg_offset, mask,
+			   value ? mask : 0);
+}
+
+static int ds4520_gpio_direction_output(struct gpio_chip *chip,
+					unsigned int offset, int value)
+{
+	struct ds4520_gpio_priv *priv = gpiochip_get_data(chip);
+	u8 reg_offset = (offset / REG_SIZE);
+	u8 mask = BIT(offset % 8);
+	int ret;
+
+	ret = regmap_clear_bits(priv->regmap, IO_CONTROL0 + reg_offset, mask);
+	if (ret)
+		return ret;
+
+	ds4520_gpio_set(chip, offset, value);
+
+	return 0;
+}
+
+static const struct regmap_config ds4520_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+static const struct gpio_chip ds4520_chip = {
+	.label			= "ds4520-gpio",
+	.owner			= THIS_MODULE,
+	.get_direction		= ds4520_gpio_get_direction,
+	.direction_input	= ds4520_gpio_direction_input,
+	.direction_output	= ds4520_gpio_direction_output,
+	.get			= ds4520_gpio_get,
+	.set			= ds4520_gpio_set,
+	.base			= -1,
+	.can_sleep		= true,
+};
+
+static int ds4520_gpio_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct ds4520_gpio_priv *priv;
+	u32 ngpio;
+	int ret;
+
+	ngpio = NUMBER_OF_GPIO;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->gpio_chip = ds4520_chip;
+	priv->gpio_chip.label = "ds4520-gpio";
+	priv->gpio_chip.ngpio = ngpio;
+	priv->gpio_chip.parent = dev;
+
+	priv->regmap = devm_regmap_init_i2c(client, &ds4520_regmap_config);
+	if (IS_ERR(priv->regmap)) {
+		ret = PTR_ERR(priv->regmap);
+		dev_err_probe(dev, ret,
+			      "Failed to allocate register map\n");
+		return ret;
+	}
+
+	ret = devm_gpiochip_add_data(dev, &priv->gpio_chip, priv);
+	if (ret) {
+		dev_err_probe(dev, ret, "Unable to register gpiochip\n");
+		return ret;
+	}
+
+	i2c_set_clientdata(client, priv);
+
+	return 0;
+}
+
+static const struct of_device_id ds4520_gpio_of_match_table[] = {
+	{
+		.compatible = "adi,ds4520-gpio"
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ds4520_gpio_of_match_table);
+
+static const struct i2c_device_id ds4520_gpio_id_table[] = {
+	{ "ds4520-gpio" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, ds4520_gpio_id_table);
+
+static struct i2c_driver ds4520_gpio_driver = {
+	.driver = {
+		.name = "ds4520-gpio",
+		.of_match_table = ds4520_gpio_of_match_table,
+	},
+	.probe_new = ds4520_gpio_probe,
+	.id_table = ds4520_gpio_id_table,
+};
+module_i2c_driver(ds4520_gpio_driver);
+
+MODULE_DESCRIPTION("DS4520 I/O Expander");
+MODULE_AUTHOR("Okan Sahin <okan.sahin@analog.com>");
+MODULE_LICENSE("GPL");