[v2,2/3] counter: 104-quad-8: Refactor to buffer states for CMR, IOR, and IDR

Message ID c5adb13b4b0887beb1df40b34d2ef03d63a2860d.1679605919.git.william.gray@linaro.org
State New
Headers
Series Refactor 104-quad-8 to match device operations |

Commit Message

William Breathitt Gray March 23, 2023, 9:25 p.m. UTC
  The 104-quad-8 driver buffers the device configuration states
separately, however each device has only three control registers: CMR,
IOR, and IDR. Refactoring to buffer the states of these control
registers rather than each configuration separately results in succinct
code that more closely matches what is happening on the device.

Signed-off-by: William Breathitt Gray <william.gray@linaro.org>
---
Changes in v2:
 - Replace FIELD_PREP() and FIELD_GET() with u8_encode_bits() and
   u8_get_bits()
 - Replace FIELD_MODIFY() with u8p_replace_bits()
 - Wrap up control register update in quad8_control_register_update()

 drivers/counter/104-quad-8.c | 287 +++++++++++++----------------------
 1 file changed, 104 insertions(+), 183 deletions(-)
  

Comments

Andy Shevchenko March 24, 2023, 11:48 a.m. UTC | #1
On Thu, Mar 23, 2023 at 05:25:28PM -0400, William Breathitt Gray wrote:
> The 104-quad-8 driver buffers the device configuration states
> separately, however each device has only three control registers: CMR,
> IOR, and IDR. Refactoring to buffer the states of these control
> registers rather than each configuration separately results in succinct
> code that more closely matches what is happening on the device.

...

> +static void quad8_control_register_update(struct quad8 *const priv, u8 *const buf,
> +					  const size_t channel, const u8 val, const u8 field)
> +{
> +	u8p_replace_bits(&buf[channel], val, field);
> +	iowrite8(buf[channel], &priv->reg->channel[channel].control);
> +}

How did you compile this?
Due to nature of *_replace_bits() this may only be a macro.

That's what LKP is telling about I think.
  
Andy Shevchenko March 24, 2023, 11:50 a.m. UTC | #2
On Fri, Mar 24, 2023 at 01:48:43PM +0200, Andy Shevchenko wrote:
> On Thu, Mar 23, 2023 at 05:25:28PM -0400, William Breathitt Gray wrote:

...

> > +static void quad8_control_register_update(struct quad8 *const priv, u8 *const buf,
> > +					  const size_t channel, const u8 val, const u8 field)
> > +{
> > +	u8p_replace_bits(&buf[channel], val, field);
> > +	iowrite8(buf[channel], &priv->reg->channel[channel].control);
> > +}
> 
> How did you compile this?
> Due to nature of *_replace_bits() this may only be a macro.
> 
> That's what LKP is telling about I think.

Ah, no, that's because the last parameter is not constant in the last patch in
the series.
  
William Breathitt Gray March 24, 2023, 1:26 p.m. UTC | #3
On Fri, Mar 24, 2023 at 01:50:07PM +0200, Andy Shevchenko wrote:
> On Fri, Mar 24, 2023 at 01:48:43PM +0200, Andy Shevchenko wrote:
> > On Thu, Mar 23, 2023 at 05:25:28PM -0400, William Breathitt Gray wrote:
> 
> ...
> 
> > > +static void quad8_control_register_update(struct quad8 *const priv, u8 *const buf,
> > > +					  const size_t channel, const u8 val, const u8 field)
> > > +{
> > > +	u8p_replace_bits(&buf[channel], val, field);
> > > +	iowrite8(buf[channel], &priv->reg->channel[channel].control);
> > > +}
> > 
> > How did you compile this?
> > Due to nature of *_replace_bits() this may only be a macro.
> > 
> > That's what LKP is telling about I think.
> 
> Ah, no, that's because the last parameter is not constant in the last patch in
> the series.
> 
> -- 
> With Best Regards,
> Andy Shevchenko

I'm having trouble cross-compiling for riscv, but I'm unable to recreate
the build error when I compile for x86_64. However, I'd like to
understand this error so I can fix it properly.

Is the problem here due to the "const u8 field" parameter? Instead of a
constant variable, does this need to be a constant literal value for
u8p_replace_bits()? I don't think that parameter changed in the last
patch of the series, so why is the build error occurring for the last
patch and not this penultimate patch here? Would qualifying the
quad8_control_register_update() function with "__always_inline" resolve
this issue?

William Breathitt Gray
  
Andy Shevchenko March 24, 2023, 1:48 p.m. UTC | #4
On Fri, Mar 24, 2023 at 09:26:14AM -0400, William Breathitt Gray wrote:
> On Fri, Mar 24, 2023 at 01:50:07PM +0200, Andy Shevchenko wrote:
> > On Fri, Mar 24, 2023 at 01:48:43PM +0200, Andy Shevchenko wrote:
> > > On Thu, Mar 23, 2023 at 05:25:28PM -0400, William Breathitt Gray wrote:

...

> > > > +static void quad8_control_register_update(struct quad8 *const priv, u8 *const buf,
> > > > +					  const size_t channel, const u8 val, const u8 field)
> > > > +{
> > > > +	u8p_replace_bits(&buf[channel], val, field);
> > > > +	iowrite8(buf[channel], &priv->reg->channel[channel].control);
> > > > +}
> > > 
> > > How did you compile this?
> > > Due to nature of *_replace_bits() this may only be a macro.
> > > 
> > > That's what LKP is telling about I think.
> > 
> > Ah, no, that's because the last parameter is not constant in the last patch in
> > the series.

> I'm having trouble cross-compiling for riscv, but I'm unable to recreate
> the build error when I compile for x86_64. However, I'd like to
> understand this error so I can fix it properly.
> 
> Is the problem here due to the "const u8 field" parameter? Instead of a
> constant variable, does this need to be a constant literal value for
> u8p_replace_bits()? I don't think that parameter changed in the last
> patch of the series, so why is the build error occurring for the last
> patch and not this penultimate patch here?

Good question. Perhaps my understanding is incorrect.

> Would qualifying the
> quad8_control_register_update() function with "__always_inline" resolve
> this issue?

Hmm... Don't know. You can always download a toolchain specifically build for
building kernels: https://mirrors.edge.kernel.org/pub/tools/crosstool/.
  
William Breathitt Gray March 24, 2023, 3:35 p.m. UTC | #5
On Fri, Mar 24, 2023 at 03:48:19PM +0200, Andy Shevchenko wrote:
> On Fri, Mar 24, 2023 at 09:26:14AM -0400, William Breathitt Gray wrote:
> > On Fri, Mar 24, 2023 at 01:50:07PM +0200, Andy Shevchenko wrote:
> > > On Fri, Mar 24, 2023 at 01:48:43PM +0200, Andy Shevchenko wrote:
> > > > On Thu, Mar 23, 2023 at 05:25:28PM -0400, William Breathitt Gray wrote:
> 
> ...
> 
> > > > > +static void quad8_control_register_update(struct quad8 *const priv, u8 *const buf,
> > > > > +					  const size_t channel, const u8 val, const u8 field)
> > > > > +{
> > > > > +	u8p_replace_bits(&buf[channel], val, field);
> > > > > +	iowrite8(buf[channel], &priv->reg->channel[channel].control);
> > > > > +}
> > > > 
> > > > How did you compile this?
> > > > Due to nature of *_replace_bits() this may only be a macro.
> > > > 
> > > > That's what LKP is telling about I think.
> > > 
> > > Ah, no, that's because the last parameter is not constant in the last patch in
> > > the series.
> 
> > I'm having trouble cross-compiling for riscv, but I'm unable to recreate
> > the build error when I compile for x86_64. However, I'd like to
> > understand this error so I can fix it properly.
> > 
> > Is the problem here due to the "const u8 field" parameter? Instead of a
> > constant variable, does this need to be a constant literal value for
> > u8p_replace_bits()? I don't think that parameter changed in the last
> > patch of the series, so why is the build error occurring for the last
> > patch and not this penultimate patch here?
> 
> Good question. Perhaps my understanding is incorrect.
> 
> > Would qualifying the
> > quad8_control_register_update() function with "__always_inline" resolve
> > this issue?
> 
> Hmm... Don't know. You can always download a toolchain specifically build for
> building kernels: https://mirrors.edge.kernel.org/pub/tools/crosstool/.
> 
> -- 
> With Best Regards,
> Andy Shevchenko
> 
> 

I tried to build using crosstools, so I followed the reproduce sequence
from the kernel test robot message: download make.cross, fetch branch
and checkout commit, and then I executed the following (I'm on an arm64
system):

        URL=https://cdn.kernel.org/pub/tools/crosstool/files/bin/arm64 COMPILER_INSTALL_PATH=$HOME/Projects/Linux/testwilliam/0day COMPILER=gcc-12.1.0 ../make.cross W=1 O=build_dir ARCH=riscv olddefconfig
        URL=https://cdn.kernel.org/pub/tools/crosstool/files/bin/arm64 COMPILER_INSTALL_PATH=$HOME/Projects/Linux/testwilliam/0day COMPILER=gcc-12.1.0 ../make.cross W=1 O=build_dir ARCH=riscv SHELL=/bin/bash drivers/

I encountered the following errors regarding GCC plugins:

        cc1: error: cannot load plugin ./scripts/gcc-plugins/randomize_layout_plugin.so: ./scripts/gcc-plugins/randomize_layout_plugin.so: undefined symbol: _ZN8opt_pass14set_pass_paramEjb
        cc1: error: cannot load plugin ./scripts/gcc-plugins/latent_entropy_plugin.so: ./scripts/gcc-plugins/latent_entropy_plugin.so: undefined symbol: _ZN8opt_pass14set_pass_paramEjb
        cc1: error: cannot load plugin ./scripts/gcc-plugins/randomize_layout_plugin.so: ./scripts/gcc-plugins/randomize_layout_plugin.so: undefined symbol: _ZN8opt_pass14set_pass_paramEjb
        make[2]: *** [../scripts/Makefile.build:252: scripts/mod/empty.o] Error 1
        cc1: error: cannot load plugin ./scripts/gcc-plugins/latent_entropy_plugin.so: ./scripts/gcc-plugins/latent_entropy_plugin.so: undefined symbol: _ZN8opt_pass14set_pass_paramEjb

I'm not quite sure how to resolve that plugin issue, but regardless I
continued investigating the original build error reported by the kernel
test robot.

There are eight calls to quad8_control_register_update() in 104-quad-8:

        quad8_control_register_update(priv, priv->idr, id, DISABLE_INDEX_MODE, INDEX_MODE);
        quad8_control_register_update(priv, priv->cmr, id, mode_cfg, QUADRATURE_MODE);
        quad8_control_register_update(priv, priv->ior, event_node->channel, flg_pins, FLG_PINS);
        quad8_control_register_update(priv, priv->idr, channel_id, index_polarity, INDEX_POLARITY);
        quad8_control_register_update(priv, priv->idr, channel_id, synchronous_mode, INDEX_MODE);
        quad8_control_register_update(priv, priv->cmr, count->id, count_mode, COUNT_MODE);
        quad8_control_register_update(priv, priv->ior, count->id, enable, AB_GATE);
        quad8_control_register_update(priv, priv->ior, count->id, !preset_enable, LOAD_PIN);

The field arguments for these calls are all preprocessor defines:

        #define INDEX_MODE BIT(0)
        #define QUADRATURE_MODE GENMASK(4, 3)
        #define FLG_PINS GENMASK(4, 3)
        #define INDEX_POLARITY BIT(1)
        #define COUNT_MODE GENMASK(2, 1)
        #define AB_GATE BIT(0)
        #define LOAD_PIN BIT(1)

Removing the duplicates, we get the following four field masks:

        BIT(0)
        BIT(1)
        GENMASK(2, 1)
        GENMASK(4, 3)

I don't think there's a problem with these masks (unless there's
something broken in the BIT() or GENMASK() implementations for riscv) so
I'm suspecting something is wrong in bitfields.h. Here's the relevant
function:

        extern void __compiletime_error("bad bitfield mask")
        __bad_mask(void);
        static __always_inline u64 field_multiplier(u64 field)
        {
        	if ((field | (field - 1)) & ((field | (field - 1)) + 1))
        		__bad_mask();
        	return field & -field;
        }

If I compute the conditional check by hand, it evaluates to false for
all four possible field masks. Is it possible the compiler is ignoring
the if statement evaluation and attempting the __bad_mask() compilation
regardless of the field passed in?

William Breathitt Gray
  
William Breathitt Gray March 27, 2023, 12:01 a.m. UTC | #6
On Fri, Mar 24, 2023 at 11:35:02AM -0400, William Breathitt Gray wrote:
> There are eight calls to quad8_control_register_update() in 104-quad-8:
> 
>         quad8_control_register_update(priv, priv->idr, id, DISABLE_INDEX_MODE, INDEX_MODE);
>         quad8_control_register_update(priv, priv->cmr, id, mode_cfg, QUADRATURE_MODE);
>         quad8_control_register_update(priv, priv->ior, event_node->channel, flg_pins, FLG_PINS);
>         quad8_control_register_update(priv, priv->idr, channel_id, index_polarity, INDEX_POLARITY);
>         quad8_control_register_update(priv, priv->idr, channel_id, synchronous_mode, INDEX_MODE);
>         quad8_control_register_update(priv, priv->cmr, count->id, count_mode, COUNT_MODE);
>         quad8_control_register_update(priv, priv->ior, count->id, enable, AB_GATE);
>         quad8_control_register_update(priv, priv->ior, count->id, !preset_enable, LOAD_PIN);

I attempted the cross-compiling using an x86-64 system and I was able to
recreate the build error. I tried to isolate the problem line by
commenting out quad8_control_register_update() calls and discover that
this appears to be an inline issue after all: if there are more than six
calls to quad8_control_register_update() are in the code, then the
'__bad_mask' build error occurs.

The build error doesn't occur if I force the inline via __always_inline,
so I'll add that to quad8_control_register_update() to resolve this
issue and submit a v3 patchset later this week.

William Breathitt Gray
  
Andy Shevchenko March 27, 2023, 9:55 a.m. UTC | #7
+Cc clang (for the ideas you might have, while the issue seems related to GCC[?] )

On Sun, Mar 26, 2023 at 08:01:23PM -0400, William Breathitt Gray wrote:
> On Fri, Mar 24, 2023 at 11:35:02AM -0400, William Breathitt Gray wrote:
> > There are eight calls to quad8_control_register_update() in 104-quad-8:
> > 
> >         quad8_control_register_update(priv, priv->idr, id, DISABLE_INDEX_MODE, INDEX_MODE);
> >         quad8_control_register_update(priv, priv->cmr, id, mode_cfg, QUADRATURE_MODE);
> >         quad8_control_register_update(priv, priv->ior, event_node->channel, flg_pins, FLG_PINS);
> >         quad8_control_register_update(priv, priv->idr, channel_id, index_polarity, INDEX_POLARITY);
> >         quad8_control_register_update(priv, priv->idr, channel_id, synchronous_mode, INDEX_MODE);
> >         quad8_control_register_update(priv, priv->cmr, count->id, count_mode, COUNT_MODE);
> >         quad8_control_register_update(priv, priv->ior, count->id, enable, AB_GATE);
> >         quad8_control_register_update(priv, priv->ior, count->id, !preset_enable, LOAD_PIN);
> 
> I attempted the cross-compiling using an x86-64 system and I was able to
> recreate the build error. I tried to isolate the problem line by
> commenting out quad8_control_register_update() calls and discover that
> this appears to be an inline issue after all: if there are more than six
> calls to quad8_control_register_update() are in the code, then the
> '__bad_mask' build error occurs.
> 
> The build error doesn't occur if I force the inline via __always_inline,
> so I'll add that to quad8_control_register_update() to resolve this
> issue and submit a v3 patchset later this week.

Doe it mean it's a compiler error? Or is it a code error?

I'm wondering if clang also fails here.
  
William Breathitt Gray March 31, 2023, 6:24 p.m. UTC | #8
On Mon, Mar 27, 2023 at 12:55:04PM +0300, Andy Shevchenko wrote:
> +Cc clang (for the ideas you might have, while the issue seems related to GCC[?] )
> 
> On Sun, Mar 26, 2023 at 08:01:23PM -0400, William Breathitt Gray wrote:
> > On Fri, Mar 24, 2023 at 11:35:02AM -0400, William Breathitt Gray wrote:
> > > There are eight calls to quad8_control_register_update() in 104-quad-8:
> > > 
> > >         quad8_control_register_update(priv, priv->idr, id, DISABLE_INDEX_MODE, INDEX_MODE);
> > >         quad8_control_register_update(priv, priv->cmr, id, mode_cfg, QUADRATURE_MODE);
> > >         quad8_control_register_update(priv, priv->ior, event_node->channel, flg_pins, FLG_PINS);
> > >         quad8_control_register_update(priv, priv->idr, channel_id, index_polarity, INDEX_POLARITY);
> > >         quad8_control_register_update(priv, priv->idr, channel_id, synchronous_mode, INDEX_MODE);
> > >         quad8_control_register_update(priv, priv->cmr, count->id, count_mode, COUNT_MODE);
> > >         quad8_control_register_update(priv, priv->ior, count->id, enable, AB_GATE);
> > >         quad8_control_register_update(priv, priv->ior, count->id, !preset_enable, LOAD_PIN);
> > 
> > I attempted the cross-compiling using an x86-64 system and I was able to
> > recreate the build error. I tried to isolate the problem line by
> > commenting out quad8_control_register_update() calls and discover that
> > this appears to be an inline issue after all: if there are more than six
> > calls to quad8_control_register_update() are in the code, then the
> > '__bad_mask' build error occurs.
> > 
> > The build error doesn't occur if I force the inline via __always_inline,
> > so I'll add that to quad8_control_register_update() to resolve this
> > issue and submit a v3 patchset later this week.
> 
> Doe it mean it's a compiler error? Or is it a code error?
> 
> I'm wondering if clang also fails here.
> 
> -- 
> With Best Regards,
> Andy Shevchenko

Al, I think you were the one who introduced the field_multiplier()
implementation in commit 00b0c9b82663ac ("Add primitives for
manipulating bitfields both in host- and fixed-endian."). Is this build
error [0] expected in your opinion?

I see that the field specification must be a constant according to the
commit description, so does that mean a "const u8 field" parameter is
valid? Does the field_multiplier() implementation have an expectation
that the condition check will be evaluated by the compiler during the
build and bypass the __bad_mask() compile time error so that it doesn't
appear?

William Breathitt Gray

[0] https://lore.kernel.org/all/202303241128.WBKc4LIy-lkp@intel.com/
  

Patch

diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c
index f07e4a9b946d..fe0887e6185d 100644
--- a/drivers/counter/104-quad-8.c
+++ b/drivers/counter/104-quad-8.c
@@ -68,32 +68,21 @@  struct quad8_reg {
 /**
  * struct quad8 - device private data structure
  * @lock:		lock to prevent clobbering device states during R/W ops
- * @counter:		instance of the counter_device
+ * @cmr:		array of Counter Mode Register states
+ * @ior:		array of Input / Output Control Register states
+ * @idr:		array of Index Control Register states
  * @fck_prescaler:	array of filter clock prescaler configurations
  * @preset:		array of preset values
- * @count_mode:		array of count mode configurations
- * @quadrature_mode:	array of quadrature mode configurations
- * @quadrature_scale:	array of quadrature mode scale configurations
- * @ab_enable:		array of A and B inputs enable configurations
- * @preset_enable:	array of set_to_preset_on_index attribute configurations
- * @irq_trigger:	array of current IRQ trigger function configurations
- * @synchronous_mode:	array of index function synchronous mode configurations
- * @index_polarity:	array of index function polarity configurations
  * @cable_fault_enable:	differential encoder cable status enable configurations
  * @reg:		I/O address offset for the device registers
  */
 struct quad8 {
 	spinlock_t lock;
+	u8 cmr[QUAD8_NUM_COUNTERS];
+	u8 ior[QUAD8_NUM_COUNTERS];
+	u8 idr[QUAD8_NUM_COUNTERS];
 	unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
 	unsigned int preset[QUAD8_NUM_COUNTERS];
-	unsigned int count_mode[QUAD8_NUM_COUNTERS];
-	unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
-	unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
-	unsigned int ab_enable[QUAD8_NUM_COUNTERS];
-	unsigned int preset_enable[QUAD8_NUM_COUNTERS];
-	unsigned int irq_trigger[QUAD8_NUM_COUNTERS];
-	unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
-	unsigned int index_polarity[QUAD8_NUM_COUNTERS];
 	unsigned int cable_fault_enable;
 	struct quad8_reg __iomem *reg;
 };
@@ -102,6 +91,8 @@  struct quad8 {
 #define FLAG_E BIT(4)
 /* Up/Down flag */
 #define FLAG_UD BIT(5)
+/* Counting up */
+#define UP 0x1
 
 #define REGISTER_SELECTION GENMASK(6, 5)
 
@@ -183,8 +174,12 @@  struct quad8 {
 #define INDEX_POLARITY BIT(1)
 /* Disable Index mode */
 #define DISABLE_INDEX_MODE 0x0
+/* Enable Index mode */
+#define ENABLE_INDEX_MODE 0x1
 /* Negative Index Polarity */
 #define NEGATIVE_INDEX_POLARITY 0x0
+/* Positive Index Polarity */
+#define POSITIVE_INDEX_POLARITY 0x1
 
 /*
  * Channel Operation Register
@@ -205,6 +200,13 @@  struct quad8 {
 /* Each Counter is 24 bits wide */
 #define LS7267_CNTR_MAX GENMASK(23, 0)
 
+static void quad8_control_register_update(struct quad8 *const priv, u8 *const buf,
+					  const size_t channel, const u8 val, const u8 field)
+{
+	u8p_replace_bits(&buf[channel], val, field);
+	iowrite8(buf[channel], &priv->reg->channel[channel].control);
+}
+
 static int quad8_signal_read(struct counter_device *counter,
 			     struct counter_signal *signal,
 			     enum counter_signal_level *level)
@@ -291,19 +293,17 @@  static const enum counter_function quad8_count_functions_list[] = {
 static int quad8_function_get(const struct quad8 *const priv, const size_t id,
 			      enum counter_function *const function)
 {
-	if (!priv->quadrature_mode[id]) {
+	switch (u8_get_bits(priv->cmr[id], QUADRATURE_MODE)) {
+	case NON_QUADRATURE:
 		*function = COUNTER_FUNCTION_PULSE_DIRECTION;
 		return 0;
-	}
-
-	switch (priv->quadrature_scale[id]) {
-	case 0:
+	case QUADRATURE_X1:
 		*function = COUNTER_FUNCTION_QUADRATURE_X1_A;
 		return 0;
-	case 1:
+	case QUADRATURE_X2:
 		*function = COUNTER_FUNCTION_QUADRATURE_X2_A;
 		return 0;
-	case 2:
+	case QUADRATURE_X4:
 		*function = COUNTER_FUNCTION_QUADRATURE_X4;
 		return 0;
 	default:
@@ -335,59 +335,36 @@  static int quad8_function_write(struct counter_device *counter,
 {
 	struct quad8 *const priv = counter_priv(counter);
 	const int id = count->id;
-	unsigned int *const quadrature_mode = priv->quadrature_mode + id;
-	unsigned int *const scale = priv->quadrature_scale + id;
-	unsigned int *const synchronous_mode = priv->synchronous_mode + id;
-	u8 __iomem *const control = &priv->reg->channel[id].control;
 	unsigned long irqflags;
 	unsigned int mode_cfg;
-	unsigned int idr_cfg;
-
-	spin_lock_irqsave(&priv->lock, irqflags);
-
-	mode_cfg = u8_encode_bits(priv->count_mode[id], COUNT_MODE);
-	idr_cfg = u8_encode_bits(priv->index_polarity[id], INDEX_POLARITY);
+	bool synchronous_mode;
 
-	if (function == COUNTER_FUNCTION_PULSE_DIRECTION) {
-		*quadrature_mode = 0;
-
-		/* Quadrature scaling only available in quadrature mode */
-		*scale = 0;
-
-		mode_cfg |= u8_encode_bits(NON_QUADRATURE, QUADRATURE_MODE);
+	switch (function) {
+	case COUNTER_FUNCTION_PULSE_DIRECTION:
+		mode_cfg = NON_QUADRATURE;
+		break;
+	case COUNTER_FUNCTION_QUADRATURE_X1_A:
+		mode_cfg = QUADRATURE_X1;
+		break;
+	case COUNTER_FUNCTION_QUADRATURE_X2_A:
+		mode_cfg = QUADRATURE_X2;
+		break;
+	case COUNTER_FUNCTION_QUADRATURE_X4:
+		mode_cfg = QUADRATURE_X4;
+		break;
+	default:
+		/* should never reach this path */
+		return -EINVAL;
+	}
 
-		/* Synchronous function not supported in non-quadrature mode */
-		if (*synchronous_mode) {
-			*synchronous_mode = 0;
-			/* Disable synchronous function mode */
-			idr_cfg |= u8_encode_bits(*synchronous_mode, INDEX_MODE);
-			iowrite8(SELECT_IDR | idr_cfg, control);
-		}
-	} else {
-		*quadrature_mode = 1;
+	spin_lock_irqsave(&priv->lock, irqflags);
 
-		switch (function) {
-		case COUNTER_FUNCTION_QUADRATURE_X1_A:
-			*scale = 0;
-			mode_cfg |= u8_encode_bits(QUADRATURE_X1, QUADRATURE_MODE);
-			break;
-		case COUNTER_FUNCTION_QUADRATURE_X2_A:
-			*scale = 1;
-			mode_cfg |= u8_encode_bits(QUADRATURE_X2, QUADRATURE_MODE);
-			break;
-		case COUNTER_FUNCTION_QUADRATURE_X4:
-			*scale = 2;
-			mode_cfg |= u8_encode_bits(QUADRATURE_X4, QUADRATURE_MODE);
-			break;
-		default:
-			/* should never reach this path */
-			spin_unlock_irqrestore(&priv->lock, irqflags);
-			return -EINVAL;
-		}
-	}
+	/* Synchronous function not supported in non-quadrature mode */
+	synchronous_mode = u8_get_bits(priv->idr[id], INDEX_MODE) == ENABLE_INDEX_MODE;
+	if (synchronous_mode && mode_cfg == NON_QUADRATURE)
+		quad8_control_register_update(priv, priv->idr, id, DISABLE_INDEX_MODE, INDEX_MODE);
 
-	/* Load mode configuration to Counter Mode Register */
-	iowrite8(SELECT_CMR | mode_cfg, control);
+	quad8_control_register_update(priv, priv->cmr, id, mode_cfg, QUADRATURE_MODE);
 
 	spin_unlock_irqrestore(&priv->lock, irqflags);
 
@@ -399,15 +376,11 @@  static int quad8_direction_read(struct counter_device *counter,
 				enum counter_count_direction *direction)
 {
 	const struct quad8 *const priv = counter_priv(counter);
-	unsigned int ud_flag;
 	u8 __iomem *const flag_addr = &priv->reg->channel[count->id].control;
 	u8 flag;
 
 	flag = ioread8(flag_addr);
-	/* U/D flag: nonzero = up, zero = down */
-	ud_flag = u8_get_bits(flag, FLAG_UD);
-
-	*direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD :
+	*direction = (u8_get_bits(flag, FLAG_UD) == UP) ? COUNTER_COUNT_DIRECTION_FORWARD :
 		COUNTER_COUNT_DIRECTION_BACKWARD;
 
 	return 0;
@@ -437,13 +410,13 @@  static int quad8_action_read(struct counter_device *counter,
 	const size_t signal_a_id = count->synapses[0].signal->id;
 	enum counter_count_direction direction;
 
+	/* Default action mode */
+	*action = COUNTER_SYNAPSE_ACTION_NONE;
+
 	/* Handle Index signals */
 	if (synapse->signal->id >= 16) {
-		if (!priv->preset_enable[count->id])
+		if (u8_get_bits(priv->ior[count->id], LOAD_PIN) == LOAD_CNTR)
 			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
-		else
-			*action = COUNTER_SYNAPSE_ACTION_NONE;
-
 		return 0;
 	}
 
@@ -463,9 +436,6 @@  static int quad8_action_read(struct counter_device *counter,
 
 	spin_unlock_irqrestore(&priv->lock, irqflags);
 
-	/* Default action mode */
-	*action = COUNTER_SYNAPSE_ACTION_NONE;
-
 	/* Determine action mode based on current count function mode */
 	switch (function) {
 	case COUNTER_FUNCTION_PULSE_DIRECTION:
@@ -493,37 +463,29 @@  static int quad8_action_read(struct counter_device *counter,
 	}
 }
 
-enum {
-	QUAD8_EVENT_CARRY = FLG1_CARRY_FLG2_BORROW,
-	QUAD8_EVENT_COMPARE = FLG1_COMPARE_FLG2_BORROW,
-	QUAD8_EVENT_CARRY_BORROW = FLG1_CARRYBORROW_FLG2_UD,
-	QUAD8_EVENT_INDEX = FLG1_INDX_FLG2_E,
-};
-
 static int quad8_events_configure(struct counter_device *counter)
 {
 	struct quad8 *const priv = counter_priv(counter);
 	unsigned long irq_enabled = 0;
 	unsigned long irqflags;
 	struct counter_event_node *event_node;
-	unsigned int next_irq_trigger;
-	unsigned long ior_cfg;
+	u8 flg_pins;
 
 	spin_lock_irqsave(&priv->lock, irqflags);
 
 	list_for_each_entry(event_node, &counter->events_list, l) {
 		switch (event_node->event) {
 		case COUNTER_EVENT_OVERFLOW:
-			next_irq_trigger = QUAD8_EVENT_CARRY;
+			flg_pins = FLG1_CARRY_FLG2_BORROW;
 			break;
 		case COUNTER_EVENT_THRESHOLD:
-			next_irq_trigger = QUAD8_EVENT_COMPARE;
+			flg_pins = FLG1_COMPARE_FLG2_BORROW;
 			break;
 		case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
-			next_irq_trigger = QUAD8_EVENT_CARRY_BORROW;
+			flg_pins = FLG1_CARRYBORROW_FLG2_UD;
 			break;
 		case COUNTER_EVENT_INDEX:
-			next_irq_trigger = QUAD8_EVENT_INDEX;
+			flg_pins = FLG1_INDX_FLG2_E;
 			break;
 		default:
 			/* should never reach this path */
@@ -535,18 +497,12 @@  static int quad8_events_configure(struct counter_device *counter)
 		irq_enabled |= BIT(event_node->channel);
 
 		/* Skip configuration if it is the same as previously set */
-		if (priv->irq_trigger[event_node->channel] == next_irq_trigger)
+		if (flg_pins == u8_get_bits(priv->ior[event_node->channel], FLG_PINS))
 			continue;
 
 		/* Save new IRQ function configuration */
-		priv->irq_trigger[event_node->channel] = next_irq_trigger;
-
-		/* Load configuration to I/O Control Register */
-		ior_cfg = u8_encode_bits(priv->ab_enable[event_node->channel], AB_GATE) |
-			  u8_encode_bits(priv->preset_enable[event_node->channel], LOAD_PIN) |
-			  u8_encode_bits(priv->irq_trigger[event_node->channel], FLG_PINS);
-		iowrite8(SELECT_IOR | ior_cfg,
-			 &priv->reg->channel[event_node->channel].control);
+		quad8_control_register_update(priv, priv->ior, event_node->channel, flg_pins,
+					      FLG_PINS);
 	}
 
 	iowrite8(irq_enabled, &priv->reg->index_interrupt);
@@ -602,7 +558,7 @@  static int quad8_index_polarity_get(struct counter_device *counter,
 	const struct quad8 *const priv = counter_priv(counter);
 	const size_t channel_id = signal->id - 16;
 
-	*index_polarity = priv->index_polarity[channel_id];
+	*index_polarity = u8_get_bits(priv->idr[channel_id], INDEX_POLARITY);
 
 	return 0;
 }
@@ -613,18 +569,11 @@  static int quad8_index_polarity_set(struct counter_device *counter,
 {
 	struct quad8 *const priv = counter_priv(counter);
 	const size_t channel_id = signal->id - 16;
-	u8 __iomem *const control = &priv->reg->channel[channel_id].control;
 	unsigned long irqflags;
-	unsigned int idr_cfg = u8_encode_bits(index_polarity, INDEX_POLARITY);
 
 	spin_lock_irqsave(&priv->lock, irqflags);
 
-	idr_cfg |= u8_encode_bits(priv->synchronous_mode[channel_id], INDEX_MODE);
-
-	priv->index_polarity[channel_id] = index_polarity;
-
-	/* Load Index Control configuration to Index Control Register */
-	iowrite8(SELECT_IDR | idr_cfg, control);
+	quad8_control_register_update(priv, priv->idr, channel_id, index_polarity, INDEX_POLARITY);
 
 	spin_unlock_irqrestore(&priv->lock, irqflags);
 
@@ -642,7 +591,7 @@  static int quad8_polarity_read(struct counter_device *counter,
 	if (err)
 		return err;
 
-	*polarity = (index_polarity) ? COUNTER_SIGNAL_POLARITY_POSITIVE :
+	*polarity = (index_polarity == POSITIVE_INDEX_POLARITY) ? COUNTER_SIGNAL_POLARITY_POSITIVE :
 		COUNTER_SIGNAL_POLARITY_NEGATIVE;
 
 	return 0;
@@ -652,7 +601,8 @@  static int quad8_polarity_write(struct counter_device *counter,
 				struct counter_signal *signal,
 				enum counter_signal_polarity polarity)
 {
-	const u32 pol = (polarity == COUNTER_SIGNAL_POLARITY_POSITIVE) ? 1 : 0;
+	const u32 pol = (polarity == COUNTER_SIGNAL_POLARITY_POSITIVE) ? POSITIVE_INDEX_POLARITY :
+									 NEGATIVE_INDEX_POLARITY;
 
 	return quad8_index_polarity_set(counter, signal, pol);
 }
@@ -669,7 +619,7 @@  static int quad8_synchronous_mode_get(struct counter_device *counter,
 	const struct quad8 *const priv = counter_priv(counter);
 	const size_t channel_id = signal->id - 16;
 
-	*synchronous_mode = priv->synchronous_mode[channel_id];
+	*synchronous_mode = u8_get_bits(priv->idr[channel_id], INDEX_MODE);
 
 	return 0;
 }
@@ -680,24 +630,19 @@  static int quad8_synchronous_mode_set(struct counter_device *counter,
 {
 	struct quad8 *const priv = counter_priv(counter);
 	const size_t channel_id = signal->id - 16;
-	u8 __iomem *const control = &priv->reg->channel[channel_id].control;
+	u8 quadrature_mode;
 	unsigned long irqflags;
-	unsigned int idr_cfg = u8_encode_bits(synchronous_mode, INDEX_MODE);
 
 	spin_lock_irqsave(&priv->lock, irqflags);
 
-	idr_cfg |= u8_encode_bits(priv->index_polarity[channel_id], INDEX_POLARITY);
-
 	/* Index function must be non-synchronous in non-quadrature mode */
-	if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
+	quadrature_mode = u8_get_bits(priv->idr[channel_id], QUADRATURE_MODE);
+	if (synchronous_mode && quadrature_mode == NON_QUADRATURE) {
 		spin_unlock_irqrestore(&priv->lock, irqflags);
 		return -EINVAL;
 	}
 
-	priv->synchronous_mode[channel_id] = synchronous_mode;
-
-	/* Load Index Control configuration to Index Control Register */
-	iowrite8(SELECT_IDR | idr_cfg, control);
+	quad8_control_register_update(priv, priv->idr, channel_id, synchronous_mode, INDEX_MODE);
 
 	spin_unlock_irqrestore(&priv->lock, irqflags);
 
@@ -719,7 +664,7 @@  static int quad8_count_mode_read(struct counter_device *counter,
 {
 	const struct quad8 *const priv = counter_priv(counter);
 
-	switch (priv->count_mode[count->id]) {
+	switch (u8_get_bits(priv->cmr[count->id], COUNT_MODE)) {
 	case NORMAL_COUNT:
 		*cnt_mode = COUNTER_COUNT_MODE_NORMAL;
 		break;
@@ -743,8 +688,6 @@  static int quad8_count_mode_write(struct counter_device *counter,
 {
 	struct quad8 *const priv = counter_priv(counter);
 	unsigned int count_mode;
-	unsigned int mode_cfg;
-	u8 __iomem *const control = &priv->reg->channel[count->id].control;
 	unsigned long irqflags;
 
 	switch (cnt_mode) {
@@ -767,19 +710,7 @@  static int quad8_count_mode_write(struct counter_device *counter,
 
 	spin_lock_irqsave(&priv->lock, irqflags);
 
-	priv->count_mode[count->id] = count_mode;
-
-	/* Set count mode configuration value */
-	mode_cfg = u8_encode_bits(count_mode, COUNT_MODE);
-
-	/* Add quadrature mode configuration */
-	if (priv->quadrature_mode[count->id])
-		mode_cfg |= u8_encode_bits(priv->quadrature_scale[count->id] + 1, QUADRATURE_MODE);
-	else
-		mode_cfg |= u8_encode_bits(NON_QUADRATURE, QUADRATURE_MODE);
-
-	/* Load mode configuration to Counter Mode Register */
-	iowrite8(SELECT_CMR | mode_cfg, control);
+	quad8_control_register_update(priv, priv->cmr, count->id, count_mode, COUNT_MODE);
 
 	spin_unlock_irqrestore(&priv->lock, irqflags);
 
@@ -791,7 +722,7 @@  static int quad8_count_enable_read(struct counter_device *counter,
 {
 	const struct quad8 *const priv = counter_priv(counter);
 
-	*enable = priv->ab_enable[count->id];
+	*enable = u8_get_bits(priv->ior[count->id], AB_GATE);
 
 	return 0;
 }
@@ -800,20 +731,11 @@  static int quad8_count_enable_write(struct counter_device *counter,
 				    struct counter_count *count, u8 enable)
 {
 	struct quad8 *const priv = counter_priv(counter);
-	u8 __iomem *const control = &priv->reg->channel[count->id].control;
 	unsigned long irqflags;
-	unsigned int ior_cfg;
 
 	spin_lock_irqsave(&priv->lock, irqflags);
 
-	priv->ab_enable[count->id] = enable;
-
-	ior_cfg = u8_encode_bits(enable, AB_GATE) |
-		  u8_encode_bits(priv->preset_enable[count->id], LOAD_PIN) |
-		  u8_encode_bits(priv->irq_trigger[count->id], FLG_PINS);
-
-	/* Load I/O control configuration */
-	iowrite8(SELECT_IOR | ior_cfg, control);
+	quad8_control_register_update(priv, priv->ior, count->id, enable, AB_GATE);
 
 	spin_unlock_irqrestore(&priv->lock, irqflags);
 
@@ -890,7 +812,7 @@  static int quad8_count_ceiling_read(struct counter_device *counter,
 	spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
-	switch (priv->count_mode[count->id]) {
+	switch (u8_get_bits(priv->cmr[count->id], COUNT_MODE)) {
 	case RANGE_LIMIT:
 	case MODULO_N:
 		*ceiling = priv->preset[count->id];
@@ -917,7 +839,7 @@  static int quad8_count_ceiling_write(struct counter_device *counter,
 	spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
-	switch (priv->count_mode[count->id]) {
+	switch (u8_get_bits(priv->cmr[count->id], COUNT_MODE)) {
 	case RANGE_LIMIT:
 	case MODULO_N:
 		quad8_preset_register_set(priv, count->id, ceiling);
@@ -936,7 +858,8 @@  static int quad8_count_preset_enable_read(struct counter_device *counter,
 {
 	const struct quad8 *const priv = counter_priv(counter);
 
-	*preset_enable = !priv->preset_enable[count->id];
+	/* Preset enable is active low in Input/Output Control register */
+	*preset_enable = !u8_get_bits(priv->ior[count->id], LOAD_PIN);
 
 	return 0;
 }
@@ -946,23 +869,12 @@  static int quad8_count_preset_enable_write(struct counter_device *counter,
 					   u8 preset_enable)
 {
 	struct quad8 *const priv = counter_priv(counter);
-	u8 __iomem *const control = &priv->reg->channel[count->id].control;
 	unsigned long irqflags;
-	unsigned int ior_cfg;
-
-	/* Preset enable is active low in Input/Output Control register */
-	preset_enable = !preset_enable;
 
 	spin_lock_irqsave(&priv->lock, irqflags);
 
-	priv->preset_enable[count->id] = preset_enable;
-
-	ior_cfg = u8_encode_bits(priv->ab_enable[count->id], AB_GATE) |
-		  u8_encode_bits(preset_enable, LOAD_PIN) |
-		  u8_encode_bits(priv->irq_trigger[count->id], FLG_PINS);
-
-	/* Load I/O control configuration to Input / Output Control Register */
-	iowrite8(SELECT_IOR | ior_cfg, control);
+	/* Preset enable is active low in Input/Output Control register */
+	quad8_control_register_update(priv, priv->ior, count->id, !preset_enable, LOAD_PIN);
 
 	spin_unlock_irqrestore(&priv->lock, irqflags);
 
@@ -1224,6 +1136,7 @@  static irqreturn_t quad8_irq_handler(int irq, void *private)
 	struct quad8 *const priv = counter_priv(counter);
 	unsigned long irq_status;
 	unsigned long channel;
+	unsigned int flg_pins;
 	u8 event;
 
 	irq_status = ioread8(&priv->reg->interrupt_status);
@@ -1231,23 +1144,24 @@  static irqreturn_t quad8_irq_handler(int irq, void *private)
 		return IRQ_NONE;
 
 	for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS) {
-		switch (priv->irq_trigger[channel]) {
-		case QUAD8_EVENT_CARRY:
+		flg_pins = u8_get_bits(priv->ior[channel], FLG_PINS);
+		switch (flg_pins) {
+		case FLG1_CARRY_FLG2_BORROW:
 			event = COUNTER_EVENT_OVERFLOW;
 				break;
-		case QUAD8_EVENT_COMPARE:
+		case FLG1_COMPARE_FLG2_BORROW:
 			event = COUNTER_EVENT_THRESHOLD;
 				break;
-		case QUAD8_EVENT_CARRY_BORROW:
+		case FLG1_CARRYBORROW_FLG2_UD:
 			event = COUNTER_EVENT_OVERFLOW_UNDERFLOW;
 				break;
-		case QUAD8_EVENT_INDEX:
+		case FLG1_INDX_FLG2_E:
 			event = COUNTER_EVENT_INDEX;
 				break;
 		default:
 			/* should never reach this path */
 			WARN_ONCE(true, "invalid interrupt trigger function %u configured for channel %lu\n",
-				  priv->irq_trigger[channel], channel);
+				  flg_pins, channel);
 			continue;
 		}
 
@@ -1260,8 +1174,9 @@  static irqreturn_t quad8_irq_handler(int irq, void *private)
 	return IRQ_HANDLED;
 }
 
-static void quad8_init_counter(struct channel_reg __iomem *const chan)
+static void quad8_init_counter(struct quad8 *const priv, const size_t channel)
 {
+	struct channel_reg __iomem *const chan = priv->reg->channel + channel;
 	unsigned long i;
 
 	iowrite8(SELECT_RLD | RESET_BP, &chan->control);
@@ -1274,15 +1189,21 @@  static void quad8_init_counter(struct channel_reg __iomem *const chan)
 		iowrite8(0x00, &chan->data);
 	iowrite8(SELECT_RLD | RESET_BT_CT_CPT_S_IDX, &chan->control);
 	iowrite8(SELECT_RLD | RESET_E, &chan->control);
+
 	/* Binary encoding; Normal count; non-quadrature mode */
-	iowrite8(SELECT_CMR | BINARY | u8_encode_bits(NORMAL_COUNT, COUNT_MODE) |
-		 u8_encode_bits(NON_QUADRATURE, QUADRATURE_MODE), &chan->control);
+	priv->cmr[channel] = SELECT_CMR | BINARY | u8_encode_bits(NORMAL_COUNT, COUNT_MODE) |
+			     u8_encode_bits(NON_QUADRATURE, QUADRATURE_MODE);
+	iowrite8(priv->cmr[channel], &chan->control);
+
 	/* Disable A and B inputs; preset on index; FLG1 as Carry */
-	iowrite8(SELECT_IOR | DISABLE_AB | u8_encode_bits(LOAD_CNTR, LOAD_PIN) |
-		 u8_encode_bits(FLG1_CARRY_FLG2_BORROW, FLG_PINS), &chan->control);
+	priv->ior[channel] = SELECT_IOR | DISABLE_AB | u8_encode_bits(LOAD_CNTR, LOAD_PIN) |
+			     u8_encode_bits(FLG1_CARRY_FLG2_BORROW, FLG_PINS);
+	iowrite8(priv->ior[channel], &chan->control);
+
 	/* Disable index function; negative index polarity */
-	iowrite8(SELECT_IDR | u8_encode_bits(DISABLE_INDEX_MODE, INDEX_MODE) |
-		 u8_encode_bits(NEGATIVE_INDEX_POLARITY, INDEX_POLARITY), &chan->control);
+	priv->idr[channel] = SELECT_IDR | u8_encode_bits(DISABLE_INDEX_MODE, INDEX_MODE) |
+			     u8_encode_bits(NEGATIVE_INDEX_POLARITY, INDEX_POLARITY);
+	iowrite8(priv->idr[channel], &chan->control);
 }
 
 static int quad8_probe(struct device *dev, unsigned int id)
@@ -1324,7 +1245,7 @@  static int quad8_probe(struct device *dev, unsigned int id)
 	iowrite8(RESET_COUNTERS | DISABLE_INTERRUPT_FUNCTION, &priv->reg->channel_oper);
 	/* Set initial configuration for all counters */
 	for (i = 0; i < QUAD8_NUM_COUNTERS; i++)
-		quad8_init_counter(priv->reg->channel + i);
+		quad8_init_counter(priv, i);
 	/* Disable Differential Encoder Cable Status for all channels */
 	iowrite8(0xFF, &priv->reg->cable_status);
 	/* Enable all counters and enable interrupt function */