mbox series

[v5,0/8] RTL8231 GPIO expander support

Message ID cover.1623532208.git.sander@svanheule.net
Headers show
Series RTL8231 GPIO expander support | expand

Message

Sander Vanheule June 12, 2021, 9:12 p.m. UTC
The RTL8231 GPIO and LED expander can be configured for use as an MDIO or SMI
bus device. Currently only the MDIO mode is supported, although SMI mode
support should be fairly straightforward, once an SMI bus driver is available.

Provided features by the RTL8231:
  - Up to 37 GPIOs
    - Configurable drive strength: 8mA or 4mA (currently unsupported)
    - Input debouncing on GPIOs 31-36
  - Up to 88 LEDs in multiple scan matrix groups
    - On, off, or one of six toggling intervals
    - "single-color mode": 2×36 single color LEDs + 8 bi-color LEDs
    - "bi-color mode": (12 + 2×6) bi-color LEDs + 24 single color LEDs
  - Up to one PWM output (currently unsupported)
    - Fixed duty cycle, 8 selectable frequencies (1.2kHz - 4.8kHz)

The GPIO controller uses gpio-regmap. To support the aliased data input and
output registers, the regmap interface is extended to supported atomic,
uncached register reads. This is then used with a new quirk for gpio-regmap.

Register access is provided through a new MDIO regmap provider. The required
MDIO regmap support was merged in Mark Brown's regmap repository, and can be
found under the regmap-mdio tag:
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git/tag/?h=regmap-mdio

---
Another revision of this patch series, now without (virtual) register paging.
After a few other (failed) attemps, I added a call to the regmap interface to
perform atomic, uncached register reads. Combined with the appropriate caching
of the register values, this can provide a split view of the data registers for
gpio-regmap. See patches 1/7 and 2/7.

These additions allowed the MFD core driver to be a bit less complex. The GPIO
support didn't see significant changes, so I've kept the review tags. The
bindings and LED driver are unchanged.

With this patch series (hopefully) nearing its final form, I was wondering if
this could be merged via the MFD tree, when all the necessary reviews and/or
acks are present. Would that be OK for everyone?

Changes since v4:
  - List myself as maintainer for this chip
  - Add uncached register reads to regmap; replaces virtual registers
Link: https://lore.kernel.org/lkml/cover.1622713678.git.sander@svanheule.net/

Changes since v3:
  - Drop gpio-regmap direction-before-value quirk
  - Use secondary virtual register range to enable proper read-modify-write
    behaviour on GPIO output values
  - Add pin debounce support
  - Switch to generic pinmux functions

Changes since v2:
  - MDIO regmap support was merged, so patch is dropped here
  - Implement feedback for DT bindings
  - Use correct module names in Kconfigs
  - Fix k*alloc return value checks
  - Introduce GPIO regmap quirks to set output direction first
  - pinctrl: Use static pin descriptions for pin controller
  - pinctrl: Fix gpio consumer resource leak
  - mfd: Replace CONFIG_PM-ifdef'ery
  - leds: Rename interval to interval_ms

Changes since v1:
  - Reintroduce MDIO regmap, with fixed Kconfig dependencies
  - Add configurable dir/value order for gpio-regmap direction_out call
  - Drop allocations for regmap fields that are used only on init
  - Move some definitions to MFD header
  - Add PM ops to replace driver remove for MFD
  - Change pinctrl driver to (modified) gpio-regmap
  - Change leds driver to use fwnode

Changes since RFC:
  - Dropped MDIO regmap interface. I was unable to resolve the Kconfig
    dependency issue, so have reverted to using regmap_config.reg_read/write.
  - Added pinctrl support
  - Added LED support
  - Changed root device to MFD, with pinctrl and leds child devices. Root
    device is now an mdio_device driver.

Sander Vanheule (8):
  regmap: Support atomic forced uncached reads
  gpio: regmap: Add quirk for aliased data registers
  dt-bindings: leds: Binding for RTL8231 scan matrix
  dt-bindings: mfd: Binding for RTL8231
  mfd: Add RTL8231 core device
  pinctrl: Add RTL8231 pin control and GPIO support
  leds: Add support for RTL8231 LED scan matrix
  MAINTAINERS: Add RTL8231 MFD driver

 .../bindings/leds/realtek,rtl8231-leds.yaml   | 166 +++++++
 .../bindings/mfd/realtek,rtl8231.yaml         | 190 ++++++++
 MAINTAINERS                                   |  10 +
 drivers/base/regmap/regmap.c                  |  33 ++
 drivers/gpio/gpio-regmap.c                    |   7 +-
 drivers/leds/Kconfig                          |  10 +
 drivers/leds/Makefile                         |   1 +
 drivers/leds/leds-rtl8231.c                   | 291 ++++++++++++
 drivers/mfd/Kconfig                           |   9 +
 drivers/mfd/Makefile                          |   1 +
 drivers/mfd/rtl8231.c                         | 186 ++++++++
 drivers/pinctrl/Kconfig                       |  11 +
 drivers/pinctrl/Makefile                      |   1 +
 drivers/pinctrl/pinctrl-rtl8231.c             | 438 ++++++++++++++++++
 include/linux/gpio/regmap.h                   |  13 +
 include/linux/mfd/rtl8231.h                   |  71 +++
 include/linux/regmap.h                        |   8 +
 17 files changed, 1445 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
 create mode 100644 Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
 create mode 100644 drivers/leds/leds-rtl8231.c
 create mode 100644 drivers/mfd/rtl8231.c
 create mode 100644 drivers/pinctrl/pinctrl-rtl8231.c
 create mode 100644 include/linux/mfd/rtl8231.h

Comments

Andy Shevchenko June 12, 2021, 9:23 p.m. UTC | #1
On Sun, Jun 13, 2021 at 12:13 AM Sander Vanheule <sander@svanheule.net> wrote:
>
> When a user wants to read a single uncached register, cache bypassing
> can be enabled. However, this is not atomic unless an external lock is
> used for the regmap. When using regcache_cache_bypass, the original
> bypass state also cannot be restored.
>
> Add support to atomically read a single uncached value, bypassing any
> regmap cache.

> +int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val)

If this is acceptable in general, I will rather name the function like
regmap_nocache_read() to be aligned with the other API naming pattern
(see below).

>  int regmap_raw_write_async(struct regmap *map, unsigned int reg,
>                            const void *val, size_t val_len);
>  int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
> +int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val);
>  int regmap_raw_read(struct regmap *map, unsigned int reg,
>                     void *val, size_t val_len);
>  int regmap_noinc_read(struct regmap *map, unsigned int reg,
Andy Shevchenko June 12, 2021, 9:29 p.m. UTC | #2
On Sun, Jun 13, 2021 at 12:13 AM Sander Vanheule <sander@svanheule.net> wrote:
>
> Some chips have the read-only input and write-only output data registers
> aliased to the same offset. As a result it is not possible to perform
> read-modify-writes on the output values, when a line is still configured
> as input.
>
> Add a quirk for aliased data registers, and document how the regmap
> should be set up for correct operation.

I still believe that there is no issue with gpio-regmap and we don't
need any quirk there.

The issue is in the regmap APIs (implementation) itself. Hardware with
the concept of reading X / writing Y at the same offset is okay per
se. regmaps doesn't support it properly and should be fixed (amended)
in a way that you provide this kind of register description thru
regmap configuration or so.

I expressed the idea of trying to implement regmap-8250 as an example
of the support for such hardware. And OTOH that this kind of hardware
is not unusual.
Mark Brown June 14, 2021, 10:33 a.m. UTC | #3
On Sat, Jun 12, 2021 at 11:12:31PM +0200, Sander Vanheule wrote:

> When a user wants to read a single uncached register, cache bypassing
> can be enabled. However, this is not atomic unless an external lock is
> used for the regmap. When using regcache_cache_bypass, the original
> bypass state also cannot be restored.

> Add support to atomically read a single uncached value, bypassing any
> regmap cache.

The expectation here is that if there is a need to do this for some
reason the user can arrange to do this for itself - if something is
happening that makes a normally non-volatile register volatile then 
it probably needs higher level coordination.  What's the use case?

> +int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val)

Bypassed what?  I think Andy's naming suggestion was much better.

Please also keep to 80 columns if you can, I know the requirements got
relaxed a bit but no need to do it excessively.
Sander Vanheule July 7, 2021, 8:53 p.m. UTC | #4
Hi Andy, Mark,

My apologies for the delay in replying, work has been keeping me a bit busy.

On Sun, 2021-06-13 at 00:29 +0300, Andy Shevchenko wrote:
> On Sun, Jun 13, 2021 at 12:13 AM Sander Vanheule <sander@svanheule.net> wrote:
> > 
> > Some chips have the read-only input and write-only output data registers
> > aliased to the same offset. As a result it is not possible to perform
> > read-modify-writes on the output values, when a line is still configured
> > as input.
> > 
> > Add a quirk for aliased data registers, and document how the regmap
> > should be set up for correct operation.
> 
> I still believe that there is no issue with gpio-regmap and we don't
> need any quirk there.
> 
> The issue is in the regmap APIs (implementation) itself. Hardware with
> the concept of reading X / writing Y at the same offset is okay per
> se. regmaps doesn't support it properly and should be fixed (amended)
> in a way that you provide this kind of register description thru
> regmap configuration or so.

I've made an attempt at implementing a "regmap_aliased()", similar to
"regmap_volatile()". However, this meant I had to make _regmap_read() aware of
wether the read- or write-alias was being read (from cache), touching some parts
of the regmap code I'm not using myself. Furthermore, this "aliased" property
isn't really perpendicular to "volatile", since writes should never be volatile,
and non-volatile reads don't make much sense (to me) on a read-only register.

In addition to entire registers having different meanings on reads or writes,
individual bitfields could also have different properties. Some bits of a
register could be aliased, while other bits are just 'plain' volatile. This is
the case for the last register of the RTL8231, where the low bits are GPIO data
(so aliased), and the highest bit is a self-clearing "latch new data" command
bit (i.e. RW-volatile).

If a regmap_field could overwrite the specifiers of it's parent register, I
think this may provide quite a natural solution to the aliasing problem: just
create two regmap_field defintions. One definition would be 'write-only' (and
cached for RMW), the other 'volatile read-only'. All regmap_fields could still
rely on a single cached register value, I think. I didn't try to implement this
though, so maybe I'm missing some behaviour that would disqualify this solution.
Would you think this could be an acceptable way to move forward here?


> I expressed the idea of trying to implement regmap-8250 as an example
> of the support for such hardware. And OTOH that this kind of hardware
> is not unusual.

This implementation indeed requires the same aliasing support, in addition to
register paging even. I've never touched that subsystem before though, so I
would need some more time if I wanted to try this.

Best,
Sander
Mark Brown July 8, 2021, 2:23 p.m. UTC | #5
On Wed, Jul 07, 2021 at 10:53:19PM +0200, Sander Vanheule wrote:

> I've made an attempt at implementing a "regmap_aliased()", similar to
> "regmap_volatile()". However, this meant I had to make _regmap_read() aware of
> wether the read- or write-alias was being read (from cache), touching some parts
> of the regmap code I'm not using myself. Furthermore, this "aliased" property
> isn't really perpendicular to "volatile", since writes should never be volatile,
> and non-volatile reads don't make much sense (to me) on a read-only register.

As far as the abstractions in regmap are concerned these registers are
volatile, that's currently how regmap undertands registers where
readback won't give you the last written value.  Trying to convince the
framework to handle these registers as anything other than volatile is
going to of need be an invasive change.

> If a regmap_field could overwrite the specifiers of it's parent register, I
> think this may provide quite a natural solution to the aliasing problem: just
> create two regmap_field defintions. One definition would be 'write-only' (and
> cached for RMW), the other 'volatile read-only'. All regmap_fields could still
> rely on a single cached register value, I think. I didn't try to implement this
> though, so maybe I'm missing some behaviour that would disqualify this solution.
> Would you think this could be an acceptable way to move forward here?

This feels like a painful and potentially high overhead approach to
things - at the minute fields are layered on top of registers and are
totally invisible at the register level, pulling the two together would
touch a lot of places and make things tense, especially if we ended up
with two different fields aliasing each other.  I'd need to see code but
it feels like a difficult approach to take.