diff mbox series

[v5,2/8] gpio: regmap: Add quirk for aliased data registers

Message ID 5d8e5e8a29ecf39da48beb94c42003a5c686ec4e.1623532208.git.sander@svanheule.net
State New
Headers show
Series RTL8231 GPIO expander support | expand

Commit Message

Sander Vanheule June 12, 2021, 9:12 p.m. UTC
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.

Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
 drivers/gpio/gpio-regmap.c  |  7 ++++++-
 include/linux/gpio/regmap.h | 13 +++++++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)

Comments

Sander Vanheule July 7, 2021, 8:53 p.m. UTC | #1
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 | #2
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.
diff mbox series

Patch

diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c
index 134cedf151a7..3a3d0d9a945c 100644
--- a/drivers/gpio/gpio-regmap.c
+++ b/drivers/gpio/gpio-regmap.c
@@ -15,6 +15,7 @@  struct gpio_regmap {
 	struct device *parent;
 	struct regmap *regmap;
 	struct gpio_chip gpio_chip;
+	unsigned int quirks;
 
 	int reg_stride;
 	int ngpio_per_reg;
@@ -68,7 +69,10 @@  static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset)
 	if (ret)
 		return ret;
 
-	ret = regmap_read(gpio->regmap, reg, &val);
+	if (gpio->quirks & GPIO_REGMAP_QUIRK_ALIASED_DATA)
+		ret = regmap_read_bypassed(gpio->regmap, reg, &val);
+	else
+		ret = regmap_read(gpio->regmap, reg, &val);
 	if (ret)
 		return ret;
 
@@ -227,6 +231,7 @@  struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
 
 	gpio->parent = config->parent;
 	gpio->regmap = config->regmap;
+	gpio->quirks = config->quirks;
 	gpio->ngpio_per_reg = config->ngpio_per_reg;
 	gpio->reg_stride = config->reg_stride;
 	gpio->reg_mask_xlate = config->reg_mask_xlate;
diff --git a/include/linux/gpio/regmap.h b/include/linux/gpio/regmap.h
index 334dd928042b..b0751a10fa4a 100644
--- a/include/linux/gpio/regmap.h
+++ b/include/linux/gpio/regmap.h
@@ -12,6 +12,17 @@  struct regmap;
 #define GPIO_REGMAP_ADDR_ZERO ((unsigned int)(-1))
 #define GPIO_REGMAP_ADDR(addr) ((addr) ? : GPIO_REGMAP_ADDR_ZERO)
 
+enum gpio_regmap_quirk {
+	/*
+	 * For hardware where the (read-only) input and (write-only) output
+	 * registers are aliased to the same offset. In this case the register
+	 * must not be marked as volatile and a regcache must be used, to cache
+	 * the write-only output values. Register reads for the input values
+	 * will be performed by bypassing the cache.
+	 */
+	GPIO_REGMAP_QUIRK_ALIASED_DATA = BIT(0),
+};
+
 /**
  * struct gpio_regmap_config - Description of a generic regmap gpio_chip.
  * @parent:		The parent device
@@ -31,6 +42,7 @@  struct regmap;
  * @reg_stride:		(Optional) May be set if the registers (of the
  *			same type, dat, set, etc) are not consecutive.
  * @ngpio_per_reg:	Number of GPIOs per register
+ * @quirks:		Flags indicating GPIO chip hardware issues
  * @irq_domain:		(Optional) IRQ domain if the controller is
  *			interrupt-capable
  * @reg_mask_xlate:     (Optional) Translates base address and GPIO
@@ -73,6 +85,7 @@  struct gpio_regmap_config {
 	unsigned int reg_dir_out_base;
 	int reg_stride;
 	int ngpio_per_reg;
+	unsigned int quirks;
 	struct irq_domain *irq_domain;
 
 	int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base,