All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v16 00/14] Introduce the Counter character device interface
@ 2021-08-27  3:47 ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray

Changes in v16:
 - Define magic numbers for stm32-lptimer-cnt clock polarities
 - Define magic numbers for stm32-timer-cnt encoder modes
 - Bump KernelVersion to 5.16 in sysfs-bus-counter ABI documentation
 - Fix typos in driver API generic-counter.rst documentation file

For convenience, this patchset is also available on my personal git
repo: https://gitlab.com/vilhelmgray/iio/-/tree/counter_chrdev_v16

The patches preceding "counter: Internalize sysfs interface code" are
primarily cleanup and fixes that can be picked up and applied now to the
IIO tree if so desired. The "counter: Internalize sysfs interface code"
patch as well may be considered for pickup because it is relatively safe
and makes no changes to the userspace interface.

To summarize the main points of this patchset: there are no changes to
the existing Counter sysfs userspace interface; a Counter character
device interface is introduced that allows Counter events and associated
data to be read() by userspace; the events_configure() and
watch_validate() driver callbacks are introduced to support Counter
events; and IRQ support is added to the 104-QUAD-8 driver, serving as an
example of how to support the new Counter events functionality.

William Breathitt Gray (14):
  counter: stm32-lptimer-cnt: Provide defines for clock polarities
  counter: stm32-timer-cnt: Provide defines for slave mode selection
  counter: Internalize sysfs interface code
  counter: Update counter.h comments to reflect sysfs internalization
  docs: counter: Update to reflect sysfs internalization
  counter: Move counter enums to uapi header
  counter: Add character device interface
  docs: counter: Document character device interface
  tools/counter: Create Counter tools
  counter: Implement signalZ_action_component_id sysfs attribute
  counter: Implement *_component_id sysfs attributes
  counter: Implement events_queue_size sysfs attribute
  counter: 104-quad-8: Replace mutex with spinlock
  counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8

 Documentation/ABI/testing/sysfs-bus-counter   |   38 +-
 Documentation/driver-api/generic-counter.rst  |  358 +++-
 .../userspace-api/ioctl/ioctl-number.rst      |    1 +
 MAINTAINERS                                   |    3 +-
 drivers/counter/104-quad-8.c                  |  699 ++++----
 drivers/counter/Kconfig                       |    6 +-
 drivers/counter/Makefile                      |    1 +
 drivers/counter/counter-chrdev.c              |  553 ++++++
 drivers/counter/counter-chrdev.h              |   14 +
 drivers/counter/counter-core.c                |  191 +++
 drivers/counter/counter-sysfs.c               |  960 +++++++++++
 drivers/counter/counter-sysfs.h               |   13 +
 drivers/counter/counter.c                     | 1496 -----------------
 drivers/counter/ftm-quaddec.c                 |   60 +-
 drivers/counter/intel-qep.c                   |  144 +-
 drivers/counter/interrupt-cnt.c               |   62 +-
 drivers/counter/microchip-tcb-capture.c       |   91 +-
 drivers/counter/stm32-lptimer-cnt.c           |  212 ++-
 drivers/counter/stm32-timer-cnt.c             |  195 +--
 drivers/counter/ti-eqep.c                     |  180 +-
 include/linux/counter.h                       |  715 ++++----
 include/linux/counter_enum.h                  |   45 -
 include/linux/mfd/stm32-lptimer.h             |    5 +
 include/linux/mfd/stm32-timers.h              |    4 +
 include/uapi/linux/counter.h                  |  154 ++
 tools/Makefile                                |   13 +-
 tools/counter/Build                           |    1 +
 tools/counter/Makefile                        |   53 +
 tools/counter/counter_example.c               |   93 +
 29 files changed, 3569 insertions(+), 2791 deletions(-)
 create mode 100644 drivers/counter/counter-chrdev.c
 create mode 100644 drivers/counter/counter-chrdev.h
 create mode 100644 drivers/counter/counter-core.c
 create mode 100644 drivers/counter/counter-sysfs.c
 create mode 100644 drivers/counter/counter-sysfs.h
 delete mode 100644 drivers/counter/counter.c
 delete mode 100644 include/linux/counter_enum.h
 create mode 100644 include/uapi/linux/counter.h
 create mode 100644 tools/counter/Build
 create mode 100644 tools/counter/Makefile
 create mode 100644 tools/counter/counter_example.c


base-commit: 5ffeb17c0d3dd44704b4aee83e297ec07666e4d6
-- 
2.32.0


^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v16 00/14] Introduce the Counter character device interface
@ 2021-08-27  3:47 ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray

Changes in v16:
 - Define magic numbers for stm32-lptimer-cnt clock polarities
 - Define magic numbers for stm32-timer-cnt encoder modes
 - Bump KernelVersion to 5.16 in sysfs-bus-counter ABI documentation
 - Fix typos in driver API generic-counter.rst documentation file

For convenience, this patchset is also available on my personal git
repo: https://gitlab.com/vilhelmgray/iio/-/tree/counter_chrdev_v16

The patches preceding "counter: Internalize sysfs interface code" are
primarily cleanup and fixes that can be picked up and applied now to the
IIO tree if so desired. The "counter: Internalize sysfs interface code"
patch as well may be considered for pickup because it is relatively safe
and makes no changes to the userspace interface.

To summarize the main points of this patchset: there are no changes to
the existing Counter sysfs userspace interface; a Counter character
device interface is introduced that allows Counter events and associated
data to be read() by userspace; the events_configure() and
watch_validate() driver callbacks are introduced to support Counter
events; and IRQ support is added to the 104-QUAD-8 driver, serving as an
example of how to support the new Counter events functionality.

William Breathitt Gray (14):
  counter: stm32-lptimer-cnt: Provide defines for clock polarities
  counter: stm32-timer-cnt: Provide defines for slave mode selection
  counter: Internalize sysfs interface code
  counter: Update counter.h comments to reflect sysfs internalization
  docs: counter: Update to reflect sysfs internalization
  counter: Move counter enums to uapi header
  counter: Add character device interface
  docs: counter: Document character device interface
  tools/counter: Create Counter tools
  counter: Implement signalZ_action_component_id sysfs attribute
  counter: Implement *_component_id sysfs attributes
  counter: Implement events_queue_size sysfs attribute
  counter: 104-quad-8: Replace mutex with spinlock
  counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8

 Documentation/ABI/testing/sysfs-bus-counter   |   38 +-
 Documentation/driver-api/generic-counter.rst  |  358 +++-
 .../userspace-api/ioctl/ioctl-number.rst      |    1 +
 MAINTAINERS                                   |    3 +-
 drivers/counter/104-quad-8.c                  |  699 ++++----
 drivers/counter/Kconfig                       |    6 +-
 drivers/counter/Makefile                      |    1 +
 drivers/counter/counter-chrdev.c              |  553 ++++++
 drivers/counter/counter-chrdev.h              |   14 +
 drivers/counter/counter-core.c                |  191 +++
 drivers/counter/counter-sysfs.c               |  960 +++++++++++
 drivers/counter/counter-sysfs.h               |   13 +
 drivers/counter/counter.c                     | 1496 -----------------
 drivers/counter/ftm-quaddec.c                 |   60 +-
 drivers/counter/intel-qep.c                   |  144 +-
 drivers/counter/interrupt-cnt.c               |   62 +-
 drivers/counter/microchip-tcb-capture.c       |   91 +-
 drivers/counter/stm32-lptimer-cnt.c           |  212 ++-
 drivers/counter/stm32-timer-cnt.c             |  195 +--
 drivers/counter/ti-eqep.c                     |  180 +-
 include/linux/counter.h                       |  715 ++++----
 include/linux/counter_enum.h                  |   45 -
 include/linux/mfd/stm32-lptimer.h             |    5 +
 include/linux/mfd/stm32-timers.h              |    4 +
 include/uapi/linux/counter.h                  |  154 ++
 tools/Makefile                                |   13 +-
 tools/counter/Build                           |    1 +
 tools/counter/Makefile                        |   53 +
 tools/counter/counter_example.c               |   93 +
 29 files changed, 3569 insertions(+), 2791 deletions(-)
 create mode 100644 drivers/counter/counter-chrdev.c
 create mode 100644 drivers/counter/counter-chrdev.h
 create mode 100644 drivers/counter/counter-core.c
 create mode 100644 drivers/counter/counter-sysfs.c
 create mode 100644 drivers/counter/counter-sysfs.h
 delete mode 100644 drivers/counter/counter.c
 delete mode 100644 include/linux/counter_enum.h
 create mode 100644 include/uapi/linux/counter.h
 create mode 100644 tools/counter/Build
 create mode 100644 tools/counter/Makefile
 create mode 100644 tools/counter/counter_example.c


base-commit: 5ffeb17c0d3dd44704b4aee83e297ec07666e4d6
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v16 01/14] counter: stm32-lptimer-cnt: Provide defines for clock polarities
  2021-08-27  3:47 ` William Breathitt Gray
@ 2021-08-27  3:47   ` William Breathitt Gray
  -1 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray, Fabrice Gasnier

The STM32 low-power timer permits configuration of the clock polarity
via the LPTIMX_CFGR register CKPOL bits. This patch provides
preprocessor defines for the supported clock polarities.

Cc: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/counter/stm32-lptimer-cnt.c | 6 +++---
 include/linux/mfd/stm32-lptimer.h   | 5 +++++
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c
index 13656957c45f..7367f46c6f91 100644
--- a/drivers/counter/stm32-lptimer-cnt.c
+++ b/drivers/counter/stm32-lptimer-cnt.c
@@ -140,9 +140,9 @@ static const enum counter_function stm32_lptim_cnt_functions[] = {
 };
 
 enum stm32_lptim_synapse_action {
-	STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE,
-	STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE,
-	STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES,
+	STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE = STM32_LPTIM_CKPOL_RISING_EDGE,
+	STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE = STM32_LPTIM_CKPOL_FALLING_EDGE,
+	STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES = STM32_LPTIM_CKPOL_BOTH_EDGES,
 	STM32_LPTIM_SYNAPSE_ACTION_NONE,
 };
 
diff --git a/include/linux/mfd/stm32-lptimer.h b/include/linux/mfd/stm32-lptimer.h
index 90b20550c1c8..06d3f11dc3c9 100644
--- a/include/linux/mfd/stm32-lptimer.h
+++ b/include/linux/mfd/stm32-lptimer.h
@@ -45,6 +45,11 @@
 #define STM32_LPTIM_PRESC	GENMASK(11, 9)
 #define STM32_LPTIM_CKPOL	GENMASK(2, 1)
 
+/* STM32_LPTIM_CKPOL */
+#define STM32_LPTIM_CKPOL_RISING_EDGE	0
+#define STM32_LPTIM_CKPOL_FALLING_EDGE	1
+#define STM32_LPTIM_CKPOL_BOTH_EDGES	2
+
 /* STM32_LPTIM_ARR */
 #define STM32_LPTIM_MAX_ARR	0xFFFF
 
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 01/14] counter: stm32-lptimer-cnt: Provide defines for clock polarities
@ 2021-08-27  3:47   ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray, Fabrice Gasnier

The STM32 low-power timer permits configuration of the clock polarity
via the LPTIMX_CFGR register CKPOL bits. This patch provides
preprocessor defines for the supported clock polarities.

Cc: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/counter/stm32-lptimer-cnt.c | 6 +++---
 include/linux/mfd/stm32-lptimer.h   | 5 +++++
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c
index 13656957c45f..7367f46c6f91 100644
--- a/drivers/counter/stm32-lptimer-cnt.c
+++ b/drivers/counter/stm32-lptimer-cnt.c
@@ -140,9 +140,9 @@ static const enum counter_function stm32_lptim_cnt_functions[] = {
 };
 
 enum stm32_lptim_synapse_action {
-	STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE,
-	STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE,
-	STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES,
+	STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE = STM32_LPTIM_CKPOL_RISING_EDGE,
+	STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE = STM32_LPTIM_CKPOL_FALLING_EDGE,
+	STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES = STM32_LPTIM_CKPOL_BOTH_EDGES,
 	STM32_LPTIM_SYNAPSE_ACTION_NONE,
 };
 
diff --git a/include/linux/mfd/stm32-lptimer.h b/include/linux/mfd/stm32-lptimer.h
index 90b20550c1c8..06d3f11dc3c9 100644
--- a/include/linux/mfd/stm32-lptimer.h
+++ b/include/linux/mfd/stm32-lptimer.h
@@ -45,6 +45,11 @@
 #define STM32_LPTIM_PRESC	GENMASK(11, 9)
 #define STM32_LPTIM_CKPOL	GENMASK(2, 1)
 
+/* STM32_LPTIM_CKPOL */
+#define STM32_LPTIM_CKPOL_RISING_EDGE	0
+#define STM32_LPTIM_CKPOL_FALLING_EDGE	1
+#define STM32_LPTIM_CKPOL_BOTH_EDGES	2
+
 /* STM32_LPTIM_ARR */
 #define STM32_LPTIM_MAX_ARR	0xFFFF
 
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 02/14] counter: stm32-timer-cnt: Provide defines for slave mode selection
  2021-08-27  3:47 ` William Breathitt Gray
@ 2021-08-27  3:47   ` William Breathitt Gray
  -1 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray, Fabrice Gasnier

The STM32 timer permits configuration of the counter encoder mode via
the slave mode control register (SMCR) slave mode selection (SMS) bits.
This patch provides preprocessor defines for the supported encoder
modes.

Cc: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/counter/stm32-timer-cnt.c | 16 ++++++++--------
 include/linux/mfd/stm32-timers.h  |  4 ++++
 2 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c
index 3fb0debd7425..1fbc46f4ee66 100644
--- a/drivers/counter/stm32-timer-cnt.c
+++ b/drivers/counter/stm32-timer-cnt.c
@@ -93,16 +93,16 @@ static int stm32_count_function_get(struct counter_device *counter,
 	regmap_read(priv->regmap, TIM_SMCR, &smcr);
 
 	switch (smcr & TIM_SMCR_SMS) {
-	case 0:
+	case TIM_SMCR_SMS_SLAVE_MODE_DISABLED:
 		*function = STM32_COUNT_SLAVE_MODE_DISABLED;
 		return 0;
-	case 1:
+	case TIM_SMCR_SMS_ENCODER_MODE_1:
 		*function = STM32_COUNT_ENCODER_MODE_1;
 		return 0;
-	case 2:
+	case TIM_SMCR_SMS_ENCODER_MODE_2:
 		*function = STM32_COUNT_ENCODER_MODE_2;
 		return 0;
-	case 3:
+	case TIM_SMCR_SMS_ENCODER_MODE_3:
 		*function = STM32_COUNT_ENCODER_MODE_3;
 		return 0;
 	default:
@@ -119,16 +119,16 @@ static int stm32_count_function_set(struct counter_device *counter,
 
 	switch (function) {
 	case STM32_COUNT_SLAVE_MODE_DISABLED:
-		sms = 0;
+		sms = TIM_SMCR_SMS_SLAVE_MODE_DISABLED;
 		break;
 	case STM32_COUNT_ENCODER_MODE_1:
-		sms = 1;
+		sms = TIM_SMCR_SMS_ENCODER_MODE_1;
 		break;
 	case STM32_COUNT_ENCODER_MODE_2:
-		sms = 2;
+		sms = TIM_SMCR_SMS_ENCODER_MODE_2;
 		break;
 	case STM32_COUNT_ENCODER_MODE_3:
-		sms = 3;
+		sms = TIM_SMCR_SMS_ENCODER_MODE_3;
 		break;
 	default:
 		return -EINVAL;
diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h
index f8db83aedb2b..5f5c43fd69dd 100644
--- a/include/linux/mfd/stm32-timers.h
+++ b/include/linux/mfd/stm32-timers.h
@@ -82,6 +82,10 @@
 #define MAX_TIM_ICPSC		0x3
 #define TIM_CR2_MMS_SHIFT	4
 #define TIM_CR2_MMS2_SHIFT	20
+#define TIM_SMCR_SMS_SLAVE_MODE_DISABLED	0 /* counts on internal clock when CEN=1 */
+#define TIM_SMCR_SMS_ENCODER_MODE_1		1 /* counts TI1FP1 edges, depending on TI2FP2 level */
+#define TIM_SMCR_SMS_ENCODER_MODE_2		2 /* counts TI2FP2 edges, depending on TI1FP1 level */
+#define TIM_SMCR_SMS_ENCODER_MODE_3		3 /* counts on both TI1FP1 and TI2FP2 edges */
 #define TIM_SMCR_TS_SHIFT	4
 #define TIM_BDTR_BKF_MASK	0xF
 #define TIM_BDTR_BKF_SHIFT(x)	(16 + (x) * 4)
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 02/14] counter: stm32-timer-cnt: Provide defines for slave mode selection
@ 2021-08-27  3:47   ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray, Fabrice Gasnier

The STM32 timer permits configuration of the counter encoder mode via
the slave mode control register (SMCR) slave mode selection (SMS) bits.
This patch provides preprocessor defines for the supported encoder
modes.

Cc: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/counter/stm32-timer-cnt.c | 16 ++++++++--------
 include/linux/mfd/stm32-timers.h  |  4 ++++
 2 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c
index 3fb0debd7425..1fbc46f4ee66 100644
--- a/drivers/counter/stm32-timer-cnt.c
+++ b/drivers/counter/stm32-timer-cnt.c
@@ -93,16 +93,16 @@ static int stm32_count_function_get(struct counter_device *counter,
 	regmap_read(priv->regmap, TIM_SMCR, &smcr);
 
 	switch (smcr & TIM_SMCR_SMS) {
-	case 0:
+	case TIM_SMCR_SMS_SLAVE_MODE_DISABLED:
 		*function = STM32_COUNT_SLAVE_MODE_DISABLED;
 		return 0;
-	case 1:
+	case TIM_SMCR_SMS_ENCODER_MODE_1:
 		*function = STM32_COUNT_ENCODER_MODE_1;
 		return 0;
-	case 2:
+	case TIM_SMCR_SMS_ENCODER_MODE_2:
 		*function = STM32_COUNT_ENCODER_MODE_2;
 		return 0;
-	case 3:
+	case TIM_SMCR_SMS_ENCODER_MODE_3:
 		*function = STM32_COUNT_ENCODER_MODE_3;
 		return 0;
 	default:
@@ -119,16 +119,16 @@ static int stm32_count_function_set(struct counter_device *counter,
 
 	switch (function) {
 	case STM32_COUNT_SLAVE_MODE_DISABLED:
-		sms = 0;
+		sms = TIM_SMCR_SMS_SLAVE_MODE_DISABLED;
 		break;
 	case STM32_COUNT_ENCODER_MODE_1:
-		sms = 1;
+		sms = TIM_SMCR_SMS_ENCODER_MODE_1;
 		break;
 	case STM32_COUNT_ENCODER_MODE_2:
-		sms = 2;
+		sms = TIM_SMCR_SMS_ENCODER_MODE_2;
 		break;
 	case STM32_COUNT_ENCODER_MODE_3:
-		sms = 3;
+		sms = TIM_SMCR_SMS_ENCODER_MODE_3;
 		break;
 	default:
 		return -EINVAL;
diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h
index f8db83aedb2b..5f5c43fd69dd 100644
--- a/include/linux/mfd/stm32-timers.h
+++ b/include/linux/mfd/stm32-timers.h
@@ -82,6 +82,10 @@
 #define MAX_TIM_ICPSC		0x3
 #define TIM_CR2_MMS_SHIFT	4
 #define TIM_CR2_MMS2_SHIFT	20
+#define TIM_SMCR_SMS_SLAVE_MODE_DISABLED	0 /* counts on internal clock when CEN=1 */
+#define TIM_SMCR_SMS_ENCODER_MODE_1		1 /* counts TI1FP1 edges, depending on TI2FP2 level */
+#define TIM_SMCR_SMS_ENCODER_MODE_2		2 /* counts TI2FP2 edges, depending on TI1FP1 level */
+#define TIM_SMCR_SMS_ENCODER_MODE_3		3 /* counts on both TI1FP1 and TI2FP2 edges */
 #define TIM_SMCR_TS_SHIFT	4
 #define TIM_BDTR_BKF_MASK	0xF
 #define TIM_BDTR_BKF_SHIFT(x)	(16 + (x) * 4)
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 03/14] counter: Internalize sysfs interface code
  2021-08-27  3:47 ` William Breathitt Gray
                   ` (2 preceding siblings ...)
  (?)
@ 2021-08-27  3:47 ` William Breathitt Gray
  2021-08-27 14:09   ` kernel test robot
                     ` (2 more replies)
  -1 siblings, 3 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray, Dan Carpenter

This is a reimplementation of the Generic Counter driver interface.
There are no modifications to the Counter subsystem userspace interface,
so existing userspace applications should continue to run seamlessly.

The purpose of this patch is to internalize the sysfs interface code
among the various counter drivers into a shared module. Counter drivers
pass and take data natively (i.e. u8, u64, etc.) and the shared counter
module handles the translation between the sysfs interface and the
device drivers. This guarantees a standard userspace interface for all
counter drivers, and helps generalize the Generic Counter driver ABI in
order to support the Generic Counter chrdev interface (introduced in a
subsequent patch) without significant changes to the existing counter
drivers.

Note, Counter device registration is the same as before: drivers
populate a struct counter_device with components and callbacks, then
pass the structure to the devm_counter_register function. However,
what's different now is how the Counter subsystem code handles this
registration internally.

Whereas before callbacks would interact directly with sysfs data, this
interaction is now abstracted and instead callbacks interact with native
C data types. The counter_comp structure forms the basis for Counter
extensions.

The counter-sysfs.c file contains the code to parse through the
counter_device structure and register the requested components and
extensions. Attributes are created and populated based on type, with
respective translation functions to handle the mapping between sysfs and
the counter driver callbacks.

The translation performed for each attribute is straightforward: the
attribute type and data is parsed from the counter_attribute structure,
the respective counter driver read/write callback is called, and sysfs
I/O is handled before or after the driver read/write function is called.

Cc: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Cc: Patrick Havelange <patrick.havelange@essensium.com>
Cc: Kamel Bouhara <kamel.bouhara@bootlin.com>
Cc: Fabrice Gasnier <fabrice.gasnier@st.com>
Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com>
Cc: Alexandre Torgue <alexandre.torgue@st.com>
Cc: Dan Carpenter <dan.carpenter@oracle.com>
Acked-by: Syed Nayyar Waris <syednwaris@gmail.com>
Reviewed-by: David Lechner <david@lechnology.com>
Tested-by: David Lechner <david@lechnology.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 MAINTAINERS                             |    1 -
 drivers/counter/104-quad-8.c            |  449 +++----
 drivers/counter/Makefile                |    1 +
 drivers/counter/counter-core.c          |  142 +++
 drivers/counter/counter-sysfs.c         |  849 +++++++++++++
 drivers/counter/counter-sysfs.h         |   13 +
 drivers/counter/counter.c               | 1496 -----------------------
 drivers/counter/ftm-quaddec.c           |   60 +-
 drivers/counter/intel-qep.c             |  144 +--
 drivers/counter/interrupt-cnt.c         |   62 +-
 drivers/counter/microchip-tcb-capture.c |   91 +-
 drivers/counter/stm32-lptimer-cnt.c     |  212 ++--
 drivers/counter/stm32-timer-cnt.c       |  179 ++-
 drivers/counter/ti-eqep.c               |  180 +--
 include/linux/counter.h                 |  658 +++++-----
 include/linux/counter_enum.h            |   45 -
 16 files changed, 1949 insertions(+), 2633 deletions(-)
 create mode 100644 drivers/counter/counter-core.c
 create mode 100644 drivers/counter/counter-sysfs.c
 create mode 100644 drivers/counter/counter-sysfs.h
 delete mode 100644 drivers/counter/counter.c
 delete mode 100644 include/linux/counter_enum.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 212bad85e07a..b0cdb9aaca85 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4781,7 +4781,6 @@ F:	Documentation/ABI/testing/sysfs-bus-counter
 F:	Documentation/driver-api/generic-counter.rst
 F:	drivers/counter/
 F:	include/linux/counter.h
-F:	include/linux/counter_enum.h
 
 CP2615 I2C DRIVER
 M:	Bence Csókás <bence98@sch.bme.hu>
diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c
index 0caa60537b14..c587f295d720 100644
--- a/drivers/counter/104-quad-8.c
+++ b/drivers/counter/104-quad-8.c
@@ -117,7 +117,7 @@ static int quad8_signal_read(struct counter_device *counter,
 }
 
 static int quad8_count_read(struct counter_device *counter,
-	struct counter_count *count, unsigned long *val)
+			    struct counter_count *count, u64 *val)
 {
 	struct quad8 *const priv = counter->priv;
 	const int base_offset = priv->base + 2 * count->id;
@@ -148,7 +148,7 @@ static int quad8_count_read(struct counter_device *counter,
 }
 
 static int quad8_count_write(struct counter_device *counter,
-	struct counter_count *count, unsigned long val)
+			     struct counter_count *count, u64 val)
 {
 	struct quad8 *const priv = counter->priv;
 	const int base_offset = priv->base + 2 * count->id;
@@ -188,22 +188,16 @@ static int quad8_count_write(struct counter_device *counter,
 	return 0;
 }
 
-enum quad8_count_function {
-	QUAD8_COUNT_FUNCTION_PULSE_DIRECTION = 0,
-	QUAD8_COUNT_FUNCTION_QUADRATURE_X1,
-	QUAD8_COUNT_FUNCTION_QUADRATURE_X2,
-	QUAD8_COUNT_FUNCTION_QUADRATURE_X4
-};
-
 static const enum counter_function quad8_count_functions_list[] = {
-	[QUAD8_COUNT_FUNCTION_PULSE_DIRECTION] = COUNTER_FUNCTION_PULSE_DIRECTION,
-	[QUAD8_COUNT_FUNCTION_QUADRATURE_X1] = COUNTER_FUNCTION_QUADRATURE_X1_A,
-	[QUAD8_COUNT_FUNCTION_QUADRATURE_X2] = COUNTER_FUNCTION_QUADRATURE_X2_A,
-	[QUAD8_COUNT_FUNCTION_QUADRATURE_X4] = COUNTER_FUNCTION_QUADRATURE_X4
+	COUNTER_FUNCTION_PULSE_DIRECTION,
+	COUNTER_FUNCTION_QUADRATURE_X1_A,
+	COUNTER_FUNCTION_QUADRATURE_X2_A,
+	COUNTER_FUNCTION_QUADRATURE_X4,
 };
 
-static int quad8_function_get(struct counter_device *counter,
-	struct counter_count *count, size_t *function)
+static int quad8_function_read(struct counter_device *counter,
+			       struct counter_count *count,
+			       enum counter_function *function)
 {
 	struct quad8 *const priv = counter->priv;
 	const int id = count->id;
@@ -213,25 +207,26 @@ static int quad8_function_get(struct counter_device *counter,
 	if (priv->quadrature_mode[id])
 		switch (priv->quadrature_scale[id]) {
 		case 0:
-			*function = QUAD8_COUNT_FUNCTION_QUADRATURE_X1;
+			*function = COUNTER_FUNCTION_QUADRATURE_X1_A;
 			break;
 		case 1:
-			*function = QUAD8_COUNT_FUNCTION_QUADRATURE_X2;
+			*function = COUNTER_FUNCTION_QUADRATURE_X2_A;
 			break;
 		case 2:
-			*function = QUAD8_COUNT_FUNCTION_QUADRATURE_X4;
+			*function = COUNTER_FUNCTION_QUADRATURE_X4;
 			break;
 		}
 	else
-		*function = QUAD8_COUNT_FUNCTION_PULSE_DIRECTION;
+		*function = COUNTER_FUNCTION_PULSE_DIRECTION;
 
 	mutex_unlock(&priv->lock);
 
 	return 0;
 }
 
-static int quad8_function_set(struct counter_device *counter,
-	struct counter_count *count, size_t function)
+static int quad8_function_write(struct counter_device *counter,
+				struct counter_count *count,
+				enum counter_function function)
 {
 	struct quad8 *const priv = counter->priv;
 	const int id = count->id;
@@ -247,7 +242,7 @@ static int quad8_function_set(struct counter_device *counter,
 	mode_cfg = priv->count_mode[id] << 1;
 	idr_cfg = priv->index_polarity[id] << 1;
 
-	if (function == QUAD8_COUNT_FUNCTION_PULSE_DIRECTION) {
+	if (function == COUNTER_FUNCTION_PULSE_DIRECTION) {
 		*quadrature_mode = 0;
 
 		/* Quadrature scaling only available in quadrature mode */
@@ -263,15 +258,15 @@ static int quad8_function_set(struct counter_device *counter,
 		*quadrature_mode = 1;
 
 		switch (function) {
-		case QUAD8_COUNT_FUNCTION_QUADRATURE_X1:
+		case COUNTER_FUNCTION_QUADRATURE_X1_A:
 			*scale = 0;
 			mode_cfg |= QUAD8_CMR_QUADRATURE_X1;
 			break;
-		case QUAD8_COUNT_FUNCTION_QUADRATURE_X2:
+		case COUNTER_FUNCTION_QUADRATURE_X2_A:
 			*scale = 1;
 			mode_cfg |= QUAD8_CMR_QUADRATURE_X2;
 			break;
-		case QUAD8_COUNT_FUNCTION_QUADRATURE_X4:
+		case COUNTER_FUNCTION_QUADRATURE_X4:
 			*scale = 2;
 			mode_cfg |= QUAD8_CMR_QUADRATURE_X4;
 			break;
@@ -290,8 +285,9 @@ static int quad8_function_set(struct counter_device *counter,
 	return 0;
 }
 
-static void quad8_direction_get(struct counter_device *counter,
-	struct counter_count *count, enum counter_count_direction *direction)
+static int quad8_direction_read(struct counter_device *counter,
+				struct counter_count *count,
+				enum counter_count_direction *direction)
 {
 	const struct quad8 *const priv = counter->priv;
 	unsigned int ud_flag;
@@ -302,76 +298,74 @@ static void quad8_direction_get(struct counter_device *counter,
 
 	*direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD :
 		COUNTER_COUNT_DIRECTION_BACKWARD;
-}
 
-enum quad8_synapse_action {
-	QUAD8_SYNAPSE_ACTION_NONE = 0,
-	QUAD8_SYNAPSE_ACTION_RISING_EDGE,
-	QUAD8_SYNAPSE_ACTION_FALLING_EDGE,
-	QUAD8_SYNAPSE_ACTION_BOTH_EDGES
-};
+	return 0;
+}
 
 static const enum counter_synapse_action quad8_index_actions_list[] = {
-	[QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
-	[QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE
+	COUNTER_SYNAPSE_ACTION_NONE,
+	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
 };
 
 static const enum counter_synapse_action quad8_synapse_actions_list[] = {
-	[QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
-	[QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
-	[QUAD8_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
-	[QUAD8_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES
+	COUNTER_SYNAPSE_ACTION_NONE,
+	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
+	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
+	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
 };
 
-static int quad8_action_get(struct counter_device *counter,
-	struct counter_count *count, struct counter_synapse *synapse,
-	size_t *action)
+static int quad8_action_read(struct counter_device *counter,
+			     struct counter_count *count,
+			     struct counter_synapse *synapse,
+			     enum counter_synapse_action *action)
 {
 	struct quad8 *const priv = counter->priv;
 	int err;
-	size_t function = 0;
+	enum counter_function function;
 	const size_t signal_a_id = count->synapses[0].signal->id;
 	enum counter_count_direction direction;
 
 	/* Handle Index signals */
 	if (synapse->signal->id >= 16) {
 		if (priv->preset_enable[count->id])
-			*action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
+			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
 		else
-			*action = QUAD8_SYNAPSE_ACTION_NONE;
+			*action = COUNTER_SYNAPSE_ACTION_NONE;
 
 		return 0;
 	}
 
-	err = quad8_function_get(counter, count, &function);
+	err = quad8_function_read(counter, count, &function);
 	if (err)
 		return err;
 
 	/* Default action mode */
-	*action = QUAD8_SYNAPSE_ACTION_NONE;
+	*action = COUNTER_SYNAPSE_ACTION_NONE;
 
 	/* Determine action mode based on current count function mode */
 	switch (function) {
-	case QUAD8_COUNT_FUNCTION_PULSE_DIRECTION:
+	case COUNTER_FUNCTION_PULSE_DIRECTION:
 		if (synapse->signal->id == signal_a_id)
-			*action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
+			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
 		return 0;
-	case QUAD8_COUNT_FUNCTION_QUADRATURE_X1:
+	case COUNTER_FUNCTION_QUADRATURE_X1_A:
 		if (synapse->signal->id == signal_a_id) {
-			quad8_direction_get(counter, count, &direction);
+			err = quad8_direction_read(counter, count, &direction);
+			if (err)
+				return err;
 
 			if (direction == COUNTER_COUNT_DIRECTION_FORWARD)
-				*action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
+				*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
 			else
-				*action = QUAD8_SYNAPSE_ACTION_FALLING_EDGE;
+				*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
 		}
 		return 0;
-	case QUAD8_COUNT_FUNCTION_QUADRATURE_X2:
+	case COUNTER_FUNCTION_QUADRATURE_X2_A:
 		if (synapse->signal->id == signal_a_id)
-			*action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES;
+			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
 		return 0;
-	case QUAD8_COUNT_FUNCTION_QUADRATURE_X4:
-		*action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES;
+	case COUNTER_FUNCTION_QUADRATURE_X4:
+		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
 		return 0;
 	default:
 		/* should never reach this path */
@@ -383,9 +377,9 @@ static const struct counter_ops quad8_ops = {
 	.signal_read = quad8_signal_read,
 	.count_read = quad8_count_read,
 	.count_write = quad8_count_write,
-	.function_get = quad8_function_get,
-	.function_set = quad8_function_set,
-	.action_get = quad8_action_get
+	.function_read = quad8_function_read,
+	.function_write = quad8_function_write,
+	.action_read = quad8_action_read
 };
 
 static const char *const quad8_index_polarity_modes[] = {
@@ -394,7 +388,8 @@ static const char *const quad8_index_polarity_modes[] = {
 };
 
 static int quad8_index_polarity_get(struct counter_device *counter,
-	struct counter_signal *signal, size_t *index_polarity)
+				    struct counter_signal *signal,
+				    u32 *index_polarity)
 {
 	const struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id - 16;
@@ -405,7 +400,8 @@ static int quad8_index_polarity_get(struct counter_device *counter,
 }
 
 static int quad8_index_polarity_set(struct counter_device *counter,
-	struct counter_signal *signal, size_t index_polarity)
+				    struct counter_signal *signal,
+				    u32 index_polarity)
 {
 	struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id - 16;
@@ -426,20 +422,14 @@ static int quad8_index_polarity_set(struct counter_device *counter,
 	return 0;
 }
 
-static struct counter_signal_enum_ext quad8_index_pol_enum = {
-	.items = quad8_index_polarity_modes,
-	.num_items = ARRAY_SIZE(quad8_index_polarity_modes),
-	.get = quad8_index_polarity_get,
-	.set = quad8_index_polarity_set
-};
-
 static const char *const quad8_synchronous_modes[] = {
 	"non-synchronous",
 	"synchronous"
 };
 
 static int quad8_synchronous_mode_get(struct counter_device *counter,
-	struct counter_signal *signal, size_t *synchronous_mode)
+				      struct counter_signal *signal,
+				      u32 *synchronous_mode)
 {
 	const struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id - 16;
@@ -450,7 +440,8 @@ static int quad8_synchronous_mode_get(struct counter_device *counter,
 }
 
 static int quad8_synchronous_mode_set(struct counter_device *counter,
-	struct counter_signal *signal, size_t synchronous_mode)
+				      struct counter_signal *signal,
+				      u32 synchronous_mode)
 {
 	struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id - 16;
@@ -477,22 +468,18 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,
 	return 0;
 }
 
-static struct counter_signal_enum_ext quad8_syn_mode_enum = {
-	.items = quad8_synchronous_modes,
-	.num_items = ARRAY_SIZE(quad8_synchronous_modes),
-	.get = quad8_synchronous_mode_get,
-	.set = quad8_synchronous_mode_set
-};
-
-static ssize_t quad8_count_floor_read(struct counter_device *counter,
-	struct counter_count *count, void *private, char *buf)
+static int quad8_count_floor_read(struct counter_device *counter,
+				  struct counter_count *count, u64 *floor)
 {
 	/* Only a floor of 0 is supported */
-	return sprintf(buf, "0\n");
+	*floor = 0;
+
+	return 0;
 }
 
-static int quad8_count_mode_get(struct counter_device *counter,
-	struct counter_count *count, size_t *cnt_mode)
+static int quad8_count_mode_read(struct counter_device *counter,
+				 struct counter_count *count,
+				 enum counter_count_mode *cnt_mode)
 {
 	const struct quad8 *const priv = counter->priv;
 
@@ -515,26 +502,28 @@ static int quad8_count_mode_get(struct counter_device *counter,
 	return 0;
 }
 
-static int quad8_count_mode_set(struct counter_device *counter,
-	struct counter_count *count, size_t cnt_mode)
+static int quad8_count_mode_write(struct counter_device *counter,
+				  struct counter_count *count,
+				  enum counter_count_mode cnt_mode)
 {
 	struct quad8 *const priv = counter->priv;
+	unsigned int count_mode;
 	unsigned int mode_cfg;
 	const int base_offset = priv->base + 2 * count->id + 1;
 
 	/* Map Generic Counter count mode to 104-QUAD-8 count mode */
 	switch (cnt_mode) {
 	case COUNTER_COUNT_MODE_NORMAL:
-		cnt_mode = 0;
+		count_mode = 0;
 		break;
 	case COUNTER_COUNT_MODE_RANGE_LIMIT:
-		cnt_mode = 1;
+		count_mode = 1;
 		break;
 	case COUNTER_COUNT_MODE_NON_RECYCLE:
-		cnt_mode = 2;
+		count_mode = 2;
 		break;
 	case COUNTER_COUNT_MODE_MODULO_N:
-		cnt_mode = 3;
+		count_mode = 3;
 		break;
 	default:
 		/* should never reach this path */
@@ -543,10 +532,10 @@ static int quad8_count_mode_set(struct counter_device *counter,
 
 	mutex_lock(&priv->lock);
 
-	priv->count_mode[count->id] = cnt_mode;
+	priv->count_mode[count->id] = count_mode;
 
 	/* Set count mode configuration value */
-	mode_cfg = cnt_mode << 1;
+	mode_cfg = count_mode << 1;
 
 	/* Add quadrature mode configuration */
 	if (priv->quadrature_mode[count->id])
@@ -560,56 +549,35 @@ static int quad8_count_mode_set(struct counter_device *counter,
 	return 0;
 }
 
-static struct counter_count_enum_ext quad8_cnt_mode_enum = {
-	.items = counter_count_mode_str,
-	.num_items = ARRAY_SIZE(counter_count_mode_str),
-	.get = quad8_count_mode_get,
-	.set = quad8_count_mode_set
-};
-
-static ssize_t quad8_count_direction_read(struct counter_device *counter,
-	struct counter_count *count, void *priv, char *buf)
-{
-	enum counter_count_direction dir;
-
-	quad8_direction_get(counter, count, &dir);
-
-	return sprintf(buf, "%s\n", counter_count_direction_str[dir]);
-}
-
-static ssize_t quad8_count_enable_read(struct counter_device *counter,
-	struct counter_count *count, void *private, char *buf)
+static int quad8_count_enable_read(struct counter_device *counter,
+				   struct counter_count *count, u8 *enable)
 {
 	const struct quad8 *const priv = counter->priv;
 
-	return sprintf(buf, "%u\n", priv->ab_enable[count->id]);
+	*enable = priv->ab_enable[count->id];
+
+	return 0;
 }
 
-static ssize_t quad8_count_enable_write(struct counter_device *counter,
-	struct counter_count *count, void *private, const char *buf, size_t len)
+static int quad8_count_enable_write(struct counter_device *counter,
+				    struct counter_count *count, u8 enable)
 {
 	struct quad8 *const priv = counter->priv;
 	const int base_offset = priv->base + 2 * count->id;
-	int err;
-	bool ab_enable;
 	unsigned int ior_cfg;
 
-	err = kstrtobool(buf, &ab_enable);
-	if (err)
-		return err;
-
 	mutex_lock(&priv->lock);
 
-	priv->ab_enable[count->id] = ab_enable;
+	priv->ab_enable[count->id] = enable;
 
-	ior_cfg = ab_enable | priv->preset_enable[count->id] << 1;
+	ior_cfg = enable | priv->preset_enable[count->id] << 1;
 
 	/* Load I/O control configuration */
 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
 
 	mutex_unlock(&priv->lock);
 
-	return len;
+	return 0;
 }
 
 static const char *const quad8_noise_error_states[] = {
@@ -618,7 +586,7 @@ static const char *const quad8_noise_error_states[] = {
 };
 
 static int quad8_error_noise_get(struct counter_device *counter,
-	struct counter_count *count, size_t *noise_error)
+				 struct counter_count *count, u32 *noise_error)
 {
 	const struct quad8 *const priv = counter->priv;
 	const int base_offset = priv->base + 2 * count->id + 1;
@@ -628,18 +596,14 @@ static int quad8_error_noise_get(struct counter_device *counter,
 	return 0;
 }
 
-static struct counter_count_enum_ext quad8_error_noise_enum = {
-	.items = quad8_noise_error_states,
-	.num_items = ARRAY_SIZE(quad8_noise_error_states),
-	.get = quad8_error_noise_get
-};
-
-static ssize_t quad8_count_preset_read(struct counter_device *counter,
-	struct counter_count *count, void *private, char *buf)
+static int quad8_count_preset_read(struct counter_device *counter,
+				   struct counter_count *count, u64 *preset)
 {
 	const struct quad8 *const priv = counter->priv;
 
-	return sprintf(buf, "%u\n", priv->preset[count->id]);
+	*preset = priv->preset[count->id];
+
+	return 0;
 }
 
 static void quad8_preset_register_set(struct quad8 *const priv, const int id,
@@ -658,16 +622,10 @@ static void quad8_preset_register_set(struct quad8 *const priv, const int id,
 		outb(preset >> (8 * i), base_offset);
 }
 
-static ssize_t quad8_count_preset_write(struct counter_device *counter,
-	struct counter_count *count, void *private, const char *buf, size_t len)
+static int quad8_count_preset_write(struct counter_device *counter,
+				    struct counter_count *count, u64 preset)
 {
 	struct quad8 *const priv = counter->priv;
-	unsigned int preset;
-	int ret;
-
-	ret = kstrtouint(buf, 0, &preset);
-	if (ret)
-		return ret;
 
 	/* Only 24-bit values are supported */
 	if (preset > 0xFFFFFF)
@@ -679,11 +637,11 @@ static ssize_t quad8_count_preset_write(struct counter_device *counter,
 
 	mutex_unlock(&priv->lock);
 
-	return len;
+	return 0;
 }
 
-static ssize_t quad8_count_ceiling_read(struct counter_device *counter,
-	struct counter_count *count, void *private, char *buf)
+static int quad8_count_ceiling_read(struct counter_device *counter,
+				    struct counter_count *count, u64 *ceiling)
 {
 	struct quad8 *const priv = counter->priv;
 
@@ -693,26 +651,23 @@ static ssize_t quad8_count_ceiling_read(struct counter_device *counter,
 	switch (priv->count_mode[count->id]) {
 	case 1:
 	case 3:
-		mutex_unlock(&priv->lock);
-		return sprintf(buf, "%u\n", priv->preset[count->id]);
+		*ceiling = priv->preset[count->id];
+		break;
+	default:
+		/* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
+		*ceiling = 0x1FFFFFF;
+		break;
 	}
 
 	mutex_unlock(&priv->lock);
 
-	/* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
-	return sprintf(buf, "33554431\n");
+	return 0;
 }
 
-static ssize_t quad8_count_ceiling_write(struct counter_device *counter,
-	struct counter_count *count, void *private, const char *buf, size_t len)
+static int quad8_count_ceiling_write(struct counter_device *counter,
+				     struct counter_count *count, u64 ceiling)
 {
 	struct quad8 *const priv = counter->priv;
-	unsigned int ceiling;
-	int ret;
-
-	ret = kstrtouint(buf, 0, &ceiling);
-	if (ret)
-		return ret;
 
 	/* Only 24-bit values are supported */
 	if (ceiling > 0xFFFFFF)
@@ -726,7 +681,7 @@ static ssize_t quad8_count_ceiling_write(struct counter_device *counter,
 	case 3:
 		quad8_preset_register_set(priv, count->id, ceiling);
 		mutex_unlock(&priv->lock);
-		return len;
+		return 0;
 	}
 
 	mutex_unlock(&priv->lock);
@@ -734,27 +689,25 @@ static ssize_t quad8_count_ceiling_write(struct counter_device *counter,
 	return -EINVAL;
 }
 
-static ssize_t quad8_count_preset_enable_read(struct counter_device *counter,
-	struct counter_count *count, void *private, char *buf)
+static int quad8_count_preset_enable_read(struct counter_device *counter,
+					  struct counter_count *count,
+					  u8 *preset_enable)
 {
 	const struct quad8 *const priv = counter->priv;
 
-	return sprintf(buf, "%u\n", !priv->preset_enable[count->id]);
+	*preset_enable = !priv->preset_enable[count->id];
+
+	return 0;
 }
 
-static ssize_t quad8_count_preset_enable_write(struct counter_device *counter,
-	struct counter_count *count, void *private, const char *buf, size_t len)
+static int quad8_count_preset_enable_write(struct counter_device *counter,
+					   struct counter_count *count,
+					   u8 preset_enable)
 {
 	struct quad8 *const priv = counter->priv;
 	const int base_offset = priv->base + 2 * count->id + 1;
-	bool preset_enable;
-	int ret;
 	unsigned int ior_cfg;
 
-	ret = kstrtobool(buf, &preset_enable);
-	if (ret)
-		return ret;
-
 	/* Preset enable is active low in Input/Output Control register */
 	preset_enable = !preset_enable;
 
@@ -762,25 +715,24 @@ static ssize_t quad8_count_preset_enable_write(struct counter_device *counter,
 
 	priv->preset_enable[count->id] = preset_enable;
 
-	ior_cfg = priv->ab_enable[count->id] | (unsigned int)preset_enable << 1;
+	ior_cfg = priv->ab_enable[count->id] | preset_enable << 1;
 
 	/* Load I/O control configuration to Input / Output Control Register */
 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
 
 	mutex_unlock(&priv->lock);
 
-	return len;
+	return 0;
 }
 
-static ssize_t quad8_signal_cable_fault_read(struct counter_device *counter,
-					     struct counter_signal *signal,
-					     void *private, char *buf)
+static int quad8_signal_cable_fault_read(struct counter_device *counter,
+					 struct counter_signal *signal,
+					 u8 *cable_fault)
 {
 	struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id / 2;
 	bool disabled;
 	unsigned int status;
-	unsigned int fault;
 
 	mutex_lock(&priv->lock);
 
@@ -797,36 +749,31 @@ static ssize_t quad8_signal_cable_fault_read(struct counter_device *counter,
 	mutex_unlock(&priv->lock);
 
 	/* Mask respective channel and invert logic */
-	fault = !(status & BIT(channel_id));
+	*cable_fault = !(status & BIT(channel_id));
 
-	return sprintf(buf, "%u\n", fault);
+	return 0;
 }
 
-static ssize_t quad8_signal_cable_fault_enable_read(
-	struct counter_device *counter, struct counter_signal *signal,
-	void *private, char *buf)
+static int quad8_signal_cable_fault_enable_read(struct counter_device *counter,
+						struct counter_signal *signal,
+						u8 *enable)
 {
 	const struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id / 2;
-	const unsigned int enb = !!(priv->cable_fault_enable & BIT(channel_id));
 
-	return sprintf(buf, "%u\n", enb);
+	*enable = !!(priv->cable_fault_enable & BIT(channel_id));
+
+	return 0;
 }
 
-static ssize_t quad8_signal_cable_fault_enable_write(
-	struct counter_device *counter, struct counter_signal *signal,
-	void *private, const char *buf, size_t len)
+static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
+						 struct counter_signal *signal,
+						 u8 enable)
 {
 	struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id / 2;
-	bool enable;
-	int ret;
 	unsigned int cable_fault_enable;
 
-	ret = kstrtobool(buf, &enable);
-	if (ret)
-		return ret;
-
 	mutex_lock(&priv->lock);
 
 	if (enable)
@@ -841,31 +788,27 @@ static ssize_t quad8_signal_cable_fault_enable_write(
 
 	mutex_unlock(&priv->lock);
 
-	return len;
+	return 0;
 }
 
-static ssize_t quad8_signal_fck_prescaler_read(struct counter_device *counter,
-	struct counter_signal *signal, void *private, char *buf)
+static int quad8_signal_fck_prescaler_read(struct counter_device *counter,
+					   struct counter_signal *signal,
+					   u8 *prescaler)
 {
 	const struct quad8 *const priv = counter->priv;
-	const size_t channel_id = signal->id / 2;
 
-	return sprintf(buf, "%u\n", priv->fck_prescaler[channel_id]);
+	*prescaler = priv->fck_prescaler[signal->id / 2];
+
+	return 0;
 }
 
-static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter,
-	struct counter_signal *signal, void *private, const char *buf,
-	size_t len)
+static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
+					    struct counter_signal *signal,
+					    u8 prescaler)
 {
 	struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id / 2;
 	const int base_offset = priv->base + 2 * channel_id;
-	u8 prescaler;
-	int ret;
-
-	ret = kstrtou8(buf, 0, &prescaler);
-	if (ret)
-		return ret;
 
 	mutex_lock(&priv->lock);
 
@@ -881,31 +824,30 @@ static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter,
 
 	mutex_unlock(&priv->lock);
 
-	return len;
+	return 0;
 }
 
-static const struct counter_signal_ext quad8_signal_ext[] = {
-	{
-		.name = "cable_fault",
-		.read = quad8_signal_cable_fault_read
-	},
-	{
-		.name = "cable_fault_enable",
-		.read = quad8_signal_cable_fault_enable_read,
-		.write = quad8_signal_cable_fault_enable_write
-	},
-	{
-		.name = "filter_clock_prescaler",
-		.read = quad8_signal_fck_prescaler_read,
-		.write = quad8_signal_fck_prescaler_write
-	}
+static struct counter_comp quad8_signal_ext[] = {
+	COUNTER_COMP_SIGNAL_BOOL("cable_fault", quad8_signal_cable_fault_read,
+				 NULL),
+	COUNTER_COMP_SIGNAL_BOOL("cable_fault_enable",
+				 quad8_signal_cable_fault_enable_read,
+				 quad8_signal_cable_fault_enable_write),
+	COUNTER_COMP_SIGNAL_U8("filter_clock_prescaler",
+			       quad8_signal_fck_prescaler_read,
+			       quad8_signal_fck_prescaler_write)
 };
 
-static const struct counter_signal_ext quad8_index_ext[] = {
-	COUNTER_SIGNAL_ENUM("index_polarity", &quad8_index_pol_enum),
-	COUNTER_SIGNAL_ENUM_AVAILABLE("index_polarity",	&quad8_index_pol_enum),
-	COUNTER_SIGNAL_ENUM("synchronous_mode", &quad8_syn_mode_enum),
-	COUNTER_SIGNAL_ENUM_AVAILABLE("synchronous_mode", &quad8_syn_mode_enum)
+static DEFINE_COUNTER_ENUM(quad8_index_pol_enum, quad8_index_polarity_modes);
+static DEFINE_COUNTER_ENUM(quad8_synch_mode_enum, quad8_synchronous_modes);
+
+static struct counter_comp quad8_index_ext[] = {
+	COUNTER_COMP_SIGNAL_ENUM("index_polarity", quad8_index_polarity_get,
+				 quad8_index_polarity_set,
+				 quad8_index_pol_enum),
+	COUNTER_COMP_SIGNAL_ENUM("synchronous_mode", quad8_synchronous_mode_get,
+				 quad8_synchronous_mode_set,
+				 quad8_synch_mode_enum),
 };
 
 #define QUAD8_QUAD_SIGNAL(_id, _name) {		\
@@ -974,39 +916,30 @@ static struct counter_synapse quad8_count_synapses[][3] = {
 	QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7)
 };
 
-static const struct counter_count_ext quad8_count_ext[] = {
-	{
-		.name = "ceiling",
-		.read = quad8_count_ceiling_read,
-		.write = quad8_count_ceiling_write
-	},
-	{
-		.name = "floor",
-		.read = quad8_count_floor_read
-	},
-	COUNTER_COUNT_ENUM("count_mode", &quad8_cnt_mode_enum),
-	COUNTER_COUNT_ENUM_AVAILABLE("count_mode", &quad8_cnt_mode_enum),
-	{
-		.name = "direction",
-		.read = quad8_count_direction_read
-	},
-	{
-		.name = "enable",
-		.read = quad8_count_enable_read,
-		.write = quad8_count_enable_write
-	},
-	COUNTER_COUNT_ENUM("error_noise", &quad8_error_noise_enum),
-	COUNTER_COUNT_ENUM_AVAILABLE("error_noise", &quad8_error_noise_enum),
-	{
-		.name = "preset",
-		.read = quad8_count_preset_read,
-		.write = quad8_count_preset_write
-	},
-	{
-		.name = "preset_enable",
-		.read = quad8_count_preset_enable_read,
-		.write = quad8_count_preset_enable_write
-	}
+static const enum counter_count_mode quad8_cnt_modes[] = {
+	COUNTER_COUNT_MODE_NORMAL,
+	COUNTER_COUNT_MODE_RANGE_LIMIT,
+	COUNTER_COUNT_MODE_NON_RECYCLE,
+	COUNTER_COUNT_MODE_MODULO_N,
+};
+
+static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes);
+
+static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states);
+
+static struct counter_comp quad8_count_ext[] = {
+	COUNTER_COMP_CEILING(quad8_count_ceiling_read,
+			     quad8_count_ceiling_write),
+	COUNTER_COMP_FLOOR(quad8_count_floor_read, NULL),
+	COUNTER_COMP_COUNT_MODE(quad8_count_mode_read, quad8_count_mode_write,
+				quad8_count_mode_available),
+	COUNTER_COMP_DIRECTION(quad8_direction_read),
+	COUNTER_COMP_ENABLE(quad8_count_enable_read, quad8_count_enable_write),
+	COUNTER_COMP_COUNT_ENUM("error_noise", quad8_error_noise_get, NULL,
+				quad8_error_noise_enum),
+	COUNTER_COMP_PRESET(quad8_count_preset_read, quad8_count_preset_write),
+	COUNTER_COMP_PRESET_ENABLE(quad8_count_preset_enable_read,
+				   quad8_count_preset_enable_write),
 };
 
 #define QUAD8_COUNT(_id, _cntname) {					\
diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
index 19742e6f5e3e..1ab7e087fdc2 100644
--- a/drivers/counter/Makefile
+++ b/drivers/counter/Makefile
@@ -4,6 +4,7 @@
 #
 
 obj-$(CONFIG_COUNTER) += counter.o
+counter-y := counter-core.o counter-sysfs.o
 
 obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
 obj-$(CONFIG_INTERRUPT_CNT)		+= interrupt-cnt.o
diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
new file mode 100644
index 000000000000..c533a6ff12cf
--- /dev/null
+++ b/drivers/counter/counter-core.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic Counter interface
+ * Copyright (C) 2020 William Breathitt Gray
+ */
+#include <linux/counter.h>
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/gfp.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "counter-sysfs.h"
+
+/* Provides a unique ID for each counter device */
+static DEFINE_IDA(counter_ida);
+
+static void counter_device_release(struct device *dev)
+{
+	ida_free(&counter_ida, dev->id);
+}
+
+static struct device_type counter_device_type = {
+	.name = "counter_device",
+	.release = counter_device_release,
+};
+
+static struct bus_type counter_bus_type = {
+	.name = "counter",
+	.dev_name = "counter",
+};
+
+/**
+ * counter_register - register Counter to the system
+ * @counter:	pointer to Counter to register
+ *
+ * This function registers a Counter to the system. A sysfs "counter" directory
+ * will be created and populated with sysfs attributes correlating with the
+ * Counter Signals, Synapses, and Counts respectively.
+ */
+int counter_register(struct counter_device *const counter)
+{
+	struct device *const dev = &counter->dev;
+	int id;
+	int err;
+
+	/* Acquire unique ID */
+	id = ida_alloc(&counter_ida, GFP_KERNEL);
+	if (id < 0)
+		return id;
+
+	/* Configure device structure for Counter */
+	dev->id = id;
+	dev->type = &counter_device_type;
+	dev->bus = &counter_bus_type;
+	if (counter->parent) {
+		dev->parent = counter->parent;
+		dev->of_node = counter->parent->of_node;
+	}
+	device_initialize(dev);
+	dev_set_drvdata(dev, counter);
+
+	/* Add Counter sysfs attributes */
+	err = counter_sysfs_add(counter);
+	if (err < 0)
+		goto err_free_id;
+
+	/* Add device to system */
+	err = device_add(dev);
+	if (err < 0)
+		goto err_free_id;
+
+	return 0;
+
+err_free_id:
+	put_device(dev);
+	return err;
+}
+EXPORT_SYMBOL_GPL(counter_register);
+
+/**
+ * counter_unregister - unregister Counter from the system
+ * @counter:	pointer to Counter to unregister
+ *
+ * The Counter is unregistered from the system.
+ */
+void counter_unregister(struct counter_device *const counter)
+{
+	if (!counter)
+		return;
+
+	device_unregister(&counter->dev);
+}
+EXPORT_SYMBOL_GPL(counter_unregister);
+
+static void devm_counter_release(void *counter)
+{
+	counter_unregister(counter);
+}
+
+/**
+ * devm_counter_register - Resource-managed counter_register
+ * @dev:	device to allocate counter_device for
+ * @counter:	pointer to Counter to register
+ *
+ * Managed counter_register. The Counter registered with this function is
+ * automatically unregistered on driver detach. This function calls
+ * counter_register internally. Refer to that function for more information.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int devm_counter_register(struct device *dev,
+			  struct counter_device *const counter)
+{
+	int err;
+
+	err = counter_register(counter);
+	if (err < 0)
+		return err;
+
+	return devm_add_action_or_reset(dev, devm_counter_release, counter);
+}
+EXPORT_SYMBOL_GPL(devm_counter_register);
+
+static int __init counter_init(void)
+{
+	return bus_register(&counter_bus_type);
+}
+
+static void __exit counter_exit(void)
+{
+	bus_unregister(&counter_bus_type);
+}
+
+subsys_initcall(counter_init);
+module_exit(counter_exit);
+
+MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
+MODULE_DESCRIPTION("Generic Counter interface");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
new file mode 100644
index 000000000000..108cbd838eb9
--- /dev/null
+++ b/drivers/counter/counter-sysfs.c
@@ -0,0 +1,849 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic Counter sysfs interface
+ * Copyright (C) 2020 William Breathitt Gray
+ */
+#include <linux/counter.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/kstrtox.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+
+#include "counter-sysfs.h"
+
+/**
+ * struct counter_attribute - Counter sysfs attribute
+ * @dev_attr:	device attribute for sysfs
+ * @l:		node to add Counter attribute to attribute group list
+ * @comp:	Counter component callbacks and data
+ * @scope:	Counter scope of the attribute
+ * @parent:	pointer to the parent component
+ */
+struct counter_attribute {
+	struct device_attribute dev_attr;
+	struct list_head l;
+
+	struct counter_comp comp;
+	enum counter_scope scope;
+	void *parent;
+};
+
+#define to_counter_attribute(_dev_attr) \
+	container_of(_dev_attr, struct counter_attribute, dev_attr)
+
+/**
+ * struct counter_attribute_group - container for attribute group
+ * @name:	name of the attribute group
+ * @attr_list:	list to keep track of created attributes
+ * @num_attr:	number of attributes
+ */
+struct counter_attribute_group {
+	const char *name;
+	struct list_head attr_list;
+	size_t num_attr;
+};
+
+static const char *const counter_function_str[] = {
+	[COUNTER_FUNCTION_INCREASE] = "increase",
+	[COUNTER_FUNCTION_DECREASE] = "decrease",
+	[COUNTER_FUNCTION_PULSE_DIRECTION] = "pulse-direction",
+	[COUNTER_FUNCTION_QUADRATURE_X1_A] = "quadrature x1 a",
+	[COUNTER_FUNCTION_QUADRATURE_X1_B] = "quadrature x1 b",
+	[COUNTER_FUNCTION_QUADRATURE_X2_A] = "quadrature x2 a",
+	[COUNTER_FUNCTION_QUADRATURE_X2_B] = "quadrature x2 b",
+	[COUNTER_FUNCTION_QUADRATURE_X4] = "quadrature x4"
+};
+
+static const char *const counter_signal_value_str[] = {
+	[COUNTER_SIGNAL_LEVEL_LOW] = "low",
+	[COUNTER_SIGNAL_LEVEL_HIGH] = "high"
+};
+
+static const char *const counter_synapse_action_str[] = {
+	[COUNTER_SYNAPSE_ACTION_NONE] = "none",
+	[COUNTER_SYNAPSE_ACTION_RISING_EDGE] = "rising edge",
+	[COUNTER_SYNAPSE_ACTION_FALLING_EDGE] = "falling edge",
+	[COUNTER_SYNAPSE_ACTION_BOTH_EDGES] = "both edges"
+};
+
+static const char *const counter_count_direction_str[] = {
+	[COUNTER_COUNT_DIRECTION_FORWARD] = "forward",
+	[COUNTER_COUNT_DIRECTION_BACKWARD] = "backward"
+};
+
+static const char *const counter_count_mode_str[] = {
+	[COUNTER_COUNT_MODE_NORMAL] = "normal",
+	[COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit",
+	[COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle",
+	[COUNTER_COUNT_MODE_MODULO_N] = "modulo-n"
+};
+
+static ssize_t counter_comp_u8_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	const struct counter_attribute *const a = to_counter_attribute(attr);
+	struct counter_device *const counter = dev_get_drvdata(dev);
+	int err;
+	u8 data = 0;
+
+	switch (a->scope) {
+	case COUNTER_SCOPE_DEVICE:
+		err = a->comp.device_u8_read(counter, &data);
+		break;
+	case COUNTER_SCOPE_SIGNAL:
+		err = a->comp.signal_u8_read(counter, a->parent, &data);
+		break;
+	case COUNTER_SCOPE_COUNT:
+		err = a->comp.count_u8_read(counter, a->parent, &data);
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (err < 0)
+		return err;
+
+	if (a->comp.type == COUNTER_COMP_BOOL)
+		/* data should already be boolean but ensure just to be safe */
+		data = !!data;
+
+	return sprintf(buf, "%u\n", (unsigned int)data);
+}
+
+static ssize_t counter_comp_u8_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t len)
+{
+	const struct counter_attribute *const a = to_counter_attribute(attr);
+	struct counter_device *const counter = dev_get_drvdata(dev);
+	int err;
+	bool bool_data = 0;
+	u8 data = 0;
+
+	if (a->comp.type == COUNTER_COMP_BOOL) {
+		err = kstrtobool(buf, &bool_data);
+		data = bool_data;
+	} else
+		err = kstrtou8(buf, 0, &data);
+	if (err < 0)
+		return err;
+
+	switch (a->scope) {
+	case COUNTER_SCOPE_DEVICE:
+		err = a->comp.device_u8_write(counter, data);
+		break;
+	case COUNTER_SCOPE_SIGNAL:
+		err = a->comp.signal_u8_write(counter, a->parent, data);
+		break;
+	case COUNTER_SCOPE_COUNT:
+		err = a->comp.count_u8_write(counter, a->parent, data);
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (err < 0)
+		return err;
+
+	return len;
+}
+
+static ssize_t counter_comp_u32_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	const struct counter_attribute *const a = to_counter_attribute(attr);
+	struct counter_device *const counter = dev_get_drvdata(dev);
+	const struct counter_available *const avail = a->comp.priv;
+	int err;
+	u32 data = 0;
+
+	switch (a->scope) {
+	case COUNTER_SCOPE_DEVICE:
+		err = a->comp.device_u32_read(counter, &data);
+		break;
+	case COUNTER_SCOPE_SIGNAL:
+		err = a->comp.signal_u32_read(counter, a->parent, &data);
+		break;
+	case COUNTER_SCOPE_COUNT:
+		if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)
+			err = a->comp.action_read(counter, a->parent,
+						  a->comp.priv, &data);
+		else
+			err = a->comp.count_u32_read(counter, a->parent, &data);
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (err < 0)
+		return err;
+
+	switch (a->comp.type) {
+	case COUNTER_COMP_FUNCTION:
+		return sysfs_emit(buf, "%s\n", counter_function_str[data]);
+	case COUNTER_COMP_SIGNAL_LEVEL:
+		return sysfs_emit(buf, "%s\n", counter_signal_value_str[data]);
+	case COUNTER_COMP_SYNAPSE_ACTION:
+		return sysfs_emit(buf, "%s\n", counter_synapse_action_str[data]);
+	case COUNTER_COMP_ENUM:
+		return sysfs_emit(buf, "%s\n", avail->strs[data]);
+	case COUNTER_COMP_COUNT_DIRECTION:
+		return sysfs_emit(buf, "%s\n", counter_count_direction_str[data]);
+	case COUNTER_COMP_COUNT_MODE:
+		return sysfs_emit(buf, "%s\n", counter_count_mode_str[data]);
+	default:
+		return sprintf(buf, "%u\n", (unsigned int)data);
+	}
+}
+
+static int counter_find_enum(u32 *const enum_item, const u32 *const enums,
+			     const size_t num_enums, const char *const buf,
+			     const char *const string_array[])
+{
+	size_t index;
+
+	for (index = 0; index < num_enums; index++) {
+		*enum_item = enums[index];
+		if (sysfs_streq(buf, string_array[*enum_item]))
+			return 0;
+	}
+
+	return -EINVAL;
+}
+
+static ssize_t counter_comp_u32_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t len)
+{
+	const struct counter_attribute *const a = to_counter_attribute(attr);
+	struct counter_device *const counter = dev_get_drvdata(dev);
+	struct counter_count *const count = a->parent;
+	struct counter_synapse *const synapse = a->comp.priv;
+	const struct counter_available *const avail = a->comp.priv;
+	int err;
+	u32 data = 0;
+
+	switch (a->comp.type) {
+	case COUNTER_COMP_FUNCTION:
+		err = counter_find_enum(&data, count->functions_list,
+					count->num_functions, buf,
+					counter_function_str);
+		break;
+	case COUNTER_COMP_SYNAPSE_ACTION:
+		err = counter_find_enum(&data, synapse->actions_list,
+					synapse->num_actions, buf,
+					counter_synapse_action_str);
+		break;
+	case COUNTER_COMP_ENUM:
+		err = __sysfs_match_string(avail->strs, avail->num_items, buf);
+		data = err;
+		break;
+	case COUNTER_COMP_COUNT_MODE:
+		err = counter_find_enum(&data, avail->enums, avail->num_items,
+					buf, counter_count_mode_str);
+		break;
+	default:
+		err = kstrtou32(buf, 0, &data);
+		break;
+	}
+	if (err < 0)
+		return err;
+
+	switch (a->scope) {
+	case COUNTER_SCOPE_DEVICE:
+		err = a->comp.device_u32_write(counter, data);
+		break;
+	case COUNTER_SCOPE_SIGNAL:
+		err = a->comp.signal_u32_write(counter, a->parent, data);
+		break;
+	case COUNTER_SCOPE_COUNT:
+		if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)
+			err = a->comp.action_write(counter, count, synapse,
+						   data);
+		else
+			err = a->comp.count_u32_write(counter, count, data);
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (err < 0)
+		return err;
+
+	return len;
+}
+
+static ssize_t counter_comp_u64_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	const struct counter_attribute *const a = to_counter_attribute(attr);
+	struct counter_device *const counter = dev_get_drvdata(dev);
+	int err;
+	u64 data = 0;
+
+	switch (a->scope) {
+	case COUNTER_SCOPE_DEVICE:
+		err = a->comp.device_u64_read(counter, &data);
+		break;
+	case COUNTER_SCOPE_SIGNAL:
+		err = a->comp.signal_u64_read(counter, a->parent, &data);
+		break;
+	case COUNTER_SCOPE_COUNT:
+		err = a->comp.count_u64_read(counter, a->parent, &data);
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (err < 0)
+		return err;
+
+	return sprintf(buf, "%llu\n", (unsigned long long)data);
+}
+
+static ssize_t counter_comp_u64_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t len)
+{
+	const struct counter_attribute *const a = to_counter_attribute(attr);
+	struct counter_device *const counter = dev_get_drvdata(dev);
+	int err;
+	u64 data = 0;
+
+	err = kstrtou64(buf, 0, &data);
+	if (err < 0)
+		return err;
+
+	switch (a->scope) {
+	case COUNTER_SCOPE_DEVICE:
+		err = a->comp.device_u64_write(counter, data);
+		break;
+	case COUNTER_SCOPE_SIGNAL:
+		err = a->comp.signal_u64_write(counter, a->parent, data);
+		break;
+	case COUNTER_SCOPE_COUNT:
+		err = a->comp.count_u64_write(counter, a->parent, data);
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (err < 0)
+		return err;
+
+	return len;
+}
+
+static ssize_t enums_available_show(const u32 *const enums,
+				    const size_t num_enums,
+				    const char *const strs[], char *buf)
+{
+	size_t len = 0;
+	size_t index;
+
+	for (index = 0; index < num_enums; index++)
+		len += sysfs_emit_at(buf, len, "%s\n", strs[enums[index]]);
+
+	return len;
+}
+
+static ssize_t strs_available_show(const struct counter_available *const avail,
+				   char *buf)
+{
+	size_t len = 0;
+	size_t index;
+
+	for (index = 0; index < avail->num_items; index++)
+		len += sysfs_emit_at(buf, len, "%s\n", avail->strs[index]);
+
+	return len;
+}
+
+static ssize_t counter_comp_available_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	const struct counter_attribute *const a = to_counter_attribute(attr);
+	const struct counter_count *const count = a->parent;
+	const struct counter_synapse *const synapse = a->comp.priv;
+	const struct counter_available *const avail = a->comp.priv;
+
+	switch (a->comp.type) {
+	case COUNTER_COMP_FUNCTION:
+		return enums_available_show(count->functions_list,
+					    count->num_functions,
+					    counter_function_str, buf);
+	case COUNTER_COMP_SYNAPSE_ACTION:
+		return enums_available_show(synapse->actions_list,
+					    synapse->num_actions,
+					    counter_synapse_action_str, buf);
+	case COUNTER_COMP_ENUM:
+		return strs_available_show(avail, buf);
+	case COUNTER_COMP_COUNT_MODE:
+		return enums_available_show(avail->enums, avail->num_items,
+					    counter_count_mode_str, buf);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int counter_avail_attr_create(struct device *const dev,
+	struct counter_attribute_group *const group,
+	const struct counter_comp *const comp, void *const parent)
+{
+	struct counter_attribute *counter_attr;
+	struct device_attribute *dev_attr;
+
+	counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
+	if (!counter_attr)
+		return -ENOMEM;
+
+	/* Configure Counter attribute */
+	counter_attr->comp.type = comp->type;
+	counter_attr->comp.priv = comp->priv;
+	counter_attr->parent = parent;
+
+	/* Initialize sysfs attribute */
+	dev_attr = &counter_attr->dev_attr;
+	sysfs_attr_init(&dev_attr->attr);
+
+	/* Configure device attribute */
+	dev_attr->attr.name = devm_kasprintf(dev, GFP_KERNEL, "%s_available",
+					     comp->name);
+	if (!dev_attr->attr.name)
+		return -ENOMEM;
+	dev_attr->attr.mode = 0444;
+	dev_attr->show = counter_comp_available_show;
+
+	/* Store list node */
+	list_add(&counter_attr->l, &group->attr_list);
+	group->num_attr++;
+
+	return 0;
+}
+
+static int counter_attr_create(struct device *const dev,
+			       struct counter_attribute_group *const group,
+			       const struct counter_comp *const comp,
+			       const enum counter_scope scope,
+			       void *const parent)
+{
+	struct counter_attribute *counter_attr;
+	struct device_attribute *dev_attr;
+
+	counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
+	if (!counter_attr)
+		return -ENOMEM;
+
+	/* Configure Counter attribute */
+	counter_attr->comp = *comp;
+	counter_attr->scope = scope;
+	counter_attr->parent = parent;
+
+	/* Configure device attribute */
+	dev_attr = &counter_attr->dev_attr;
+	sysfs_attr_init(&dev_attr->attr);
+	dev_attr->attr.name = comp->name;
+	switch (comp->type) {
+	case COUNTER_COMP_U8:
+	case COUNTER_COMP_BOOL:
+		if (comp->device_u8_read) {
+			dev_attr->attr.mode |= 0444;
+			dev_attr->show = counter_comp_u8_show;
+		}
+		if (comp->device_u8_write) {
+			dev_attr->attr.mode |= 0200;
+			dev_attr->store = counter_comp_u8_store;
+		}
+		break;
+	case COUNTER_COMP_SIGNAL_LEVEL:
+	case COUNTER_COMP_FUNCTION:
+	case COUNTER_COMP_SYNAPSE_ACTION:
+	case COUNTER_COMP_ENUM:
+	case COUNTER_COMP_COUNT_DIRECTION:
+	case COUNTER_COMP_COUNT_MODE:
+		if (comp->device_u32_read) {
+			dev_attr->attr.mode |= 0444;
+			dev_attr->show = counter_comp_u32_show;
+		}
+		if (comp->device_u32_write) {
+			dev_attr->attr.mode |= 0200;
+			dev_attr->store = counter_comp_u32_store;
+		}
+		break;
+	case COUNTER_COMP_U64:
+		if (comp->device_u64_read) {
+			dev_attr->attr.mode |= 0444;
+			dev_attr->show = counter_comp_u64_show;
+		}
+		if (comp->device_u64_write) {
+			dev_attr->attr.mode |= 0200;
+			dev_attr->store = counter_comp_u64_store;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Store list node */
+	list_add(&counter_attr->l, &group->attr_list);
+	group->num_attr++;
+
+	/* Create "*_available" attribute if needed */
+	switch (comp->type) {
+	case COUNTER_COMP_FUNCTION:
+	case COUNTER_COMP_SYNAPSE_ACTION:
+	case COUNTER_COMP_ENUM:
+	case COUNTER_COMP_COUNT_MODE:
+		return counter_avail_attr_create(dev, group, comp, parent);
+	default:
+		return 0;
+	}
+}
+
+static ssize_t counter_comp_name_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	return sysfs_emit(buf, "%s\n", to_counter_attribute(attr)->comp.name);
+}
+
+static int counter_name_attr_create(struct device *const dev,
+				    struct counter_attribute_group *const group,
+				    const char *const name)
+{
+	struct counter_attribute *counter_attr;
+
+	counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
+	if (!counter_attr)
+		return -ENOMEM;
+
+	/* Configure Counter attribute */
+	counter_attr->comp.name = name;
+
+	/* Configure device attribute */
+	sysfs_attr_init(&counter_attr->dev_attr.attr);
+	counter_attr->dev_attr.attr.name = "name";
+	counter_attr->dev_attr.attr.mode = 0444;
+	counter_attr->dev_attr.show = counter_comp_name_show;
+
+	/* Store list node */
+	list_add(&counter_attr->l, &group->attr_list);
+	group->num_attr++;
+
+	return 0;
+}
+
+static struct counter_comp counter_signal_comp = {
+	.type = COUNTER_COMP_SIGNAL_LEVEL,
+	.name = "signal",
+};
+
+static int counter_signal_attrs_create(struct counter_device *const counter,
+	struct counter_attribute_group *const cattr_group,
+	struct counter_signal *const signal)
+{
+	const enum counter_scope scope = COUNTER_SCOPE_SIGNAL;
+	struct device *const dev = &counter->dev;
+	int err;
+	struct counter_comp comp;
+	size_t i;
+
+	/* Create main Signal attribute */
+	comp = counter_signal_comp;
+	comp.signal_u32_read = counter->ops->signal_read;
+	err = counter_attr_create(dev, cattr_group, &comp, scope, signal);
+	if (err < 0)
+		return err;
+
+	/* Create Signal name attribute */
+	err = counter_name_attr_create(dev, cattr_group, signal->name);
+	if (err < 0)
+		return err;
+
+	/* Create an attribute for each extension */
+	for (i = 0; i < signal->num_ext; i++) {
+		err = counter_attr_create(dev, cattr_group, signal->ext + i,
+					  scope, signal);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static int counter_sysfs_signals_add(struct counter_device *const counter,
+	struct counter_attribute_group *const groups)
+{
+	size_t i;
+	int err;
+
+	/* Add each Signal */
+	for (i = 0; i < counter->num_signals; i++) {
+		/* Generate Signal attribute directory name */
+		groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL,
+						"signal%zu", i);
+		if (!groups[i].name)
+			return -ENOMEM;
+
+		/* Create all attributes associated with Signal */
+		err = counter_signal_attrs_create(counter, groups + i,
+						  counter->signals + i);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static int counter_sysfs_synapses_add(struct counter_device *const counter,
+	struct counter_attribute_group *const group,
+	struct counter_count *const count)
+{
+	size_t i;
+
+	/* Add each Synapse */
+	for (i = 0; i < count->num_synapses; i++) {
+		struct device *const dev = &counter->dev;
+		struct counter_synapse *synapse;
+		size_t id;
+		struct counter_comp comp;
+		int err;
+
+		synapse = count->synapses + i;
+
+		/* Generate Synapse action name */
+		id = synapse->signal - counter->signals;
+		comp.name = devm_kasprintf(dev, GFP_KERNEL, "signal%zu_action",
+					   id);
+		if (!comp.name)
+			return -ENOMEM;
+
+		/* Create action attribute */
+		comp.type = COUNTER_COMP_SYNAPSE_ACTION;
+		comp.action_read = counter->ops->action_read;
+		comp.action_write = counter->ops->action_write;
+		comp.priv = synapse;
+		err = counter_attr_create(dev, group, &comp,
+					  COUNTER_SCOPE_COUNT, count);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static struct counter_comp counter_count_comp =
+	COUNTER_COMP_COUNT_U64("count", NULL, NULL);
+
+static struct counter_comp counter_function_comp = {
+	.type = COUNTER_COMP_FUNCTION,
+	.name = "function",
+};
+
+static int counter_count_attrs_create(struct counter_device *const counter,
+	struct counter_attribute_group *const cattr_group,
+	struct counter_count *const count)
+{
+	const enum counter_scope scope = COUNTER_SCOPE_COUNT;
+	struct device *const dev = &counter->dev;
+	int err;
+	struct counter_comp comp;
+	size_t i;
+
+	/* Create main Count attribute */
+	comp = counter_count_comp;
+	comp.count_u64_read = counter->ops->count_read;
+	comp.count_u64_write = counter->ops->count_write;
+	err = counter_attr_create(dev, cattr_group, &comp, scope, count);
+	if (err < 0)
+		return err;
+
+	/* Create Count name attribute */
+	err = counter_name_attr_create(dev, cattr_group, count->name);
+	if (err < 0)
+		return err;
+
+	/* Create Count function attribute */
+	comp = counter_function_comp;
+	comp.count_u32_read = counter->ops->function_read;
+	comp.count_u32_write = counter->ops->function_write;
+	err = counter_attr_create(dev, cattr_group, &comp, scope, count);
+	if (err < 0)
+		return err;
+
+	/* Create an attribute for each extension */
+	for (i = 0; i < count->num_ext; i++) {
+		err = counter_attr_create(dev, cattr_group, count->ext + i,
+					  scope, count);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static int counter_sysfs_counts_add(struct counter_device *const counter,
+	struct counter_attribute_group *const groups)
+{
+	size_t i;
+	struct counter_count *count;
+	int err;
+
+	/* Add each Count */
+	for (i = 0; i < counter->num_counts; i++) {
+		count = counter->counts + i;
+
+		/* Generate Count attribute directory name */
+		groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL,
+						"count%zu", i);
+		if (!groups[i].name)
+			return -ENOMEM;
+
+		/* Add sysfs attributes of the Synapses */
+		err = counter_sysfs_synapses_add(counter, groups + i, count);
+		if (err < 0)
+			return err;
+
+		/* Create all attributes associated with Count */
+		err = counter_count_attrs_create(counter, groups + i, count);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static int counter_num_signals_read(struct counter_device *counter, u8 *val)
+{
+	*val = counter->num_signals;
+	return 0;
+}
+
+static int counter_num_counts_read(struct counter_device *counter, u8 *val)
+{
+	*val = counter->num_counts;
+	return 0;
+}
+
+static struct counter_comp counter_num_signals_comp =
+	COUNTER_COMP_DEVICE_U8("num_signals", counter_num_signals_read, NULL);
+
+static struct counter_comp counter_num_counts_comp =
+	COUNTER_COMP_DEVICE_U8("num_counts", counter_num_counts_read, NULL);
+
+static int counter_sysfs_attr_add(struct counter_device *const counter,
+				  struct counter_attribute_group *cattr_group)
+{
+	const enum counter_scope scope = COUNTER_SCOPE_DEVICE;
+	struct device *const dev = &counter->dev;
+	int err;
+	size_t i;
+
+	/* Add Signals sysfs attributes */
+	err = counter_sysfs_signals_add(counter, cattr_group);
+	if (err < 0)
+		return err;
+	cattr_group += counter->num_signals;
+
+	/* Add Counts sysfs attributes */
+	err = counter_sysfs_counts_add(counter, cattr_group);
+	if (err < 0)
+		return err;
+	cattr_group += counter->num_counts;
+
+	/* Create name attribute */
+	err = counter_name_attr_create(dev, cattr_group, counter->name);
+	if (err < 0)
+		return err;
+
+	/* Create num_signals attribute */
+	err = counter_attr_create(dev, cattr_group, &counter_num_signals_comp,
+				  scope, NULL);
+	if (err < 0)
+		return err;
+
+	/* Create num_counts attribute */
+	err = counter_attr_create(dev, cattr_group, &counter_num_counts_comp,
+				  scope, NULL);
+	if (err < 0)
+		return err;
+
+	/* Create an attribute for each extension */
+	for (i = 0; i < counter->num_ext; i++) {
+		err = counter_attr_create(dev, cattr_group, counter->ext + i,
+					  scope, NULL);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/**
+ * counter_sysfs_add - Adds Counter sysfs attributes to the device structure
+ * @counter:	Pointer to the Counter device structure
+ *
+ * Counter sysfs attributes are created and added to the respective device
+ * structure for later registration to the system. Resource-managed memory
+ * allocation is performed by this function, and this memory should be freed
+ * when no longer needed (automatically by a device_unregister call, or
+ * manually by a devres_release_all call).
+ */
+int counter_sysfs_add(struct counter_device *const counter)
+{
+	struct device *const dev = &counter->dev;
+	const size_t num_groups = counter->num_signals + counter->num_counts + 1;
+	struct counter_attribute_group *cattr_groups;
+	size_t i, j;
+	int err;
+	struct attribute_group *groups;
+	struct counter_attribute *p;
+
+	/* Allocate space for attribute groups (signals, counts, and ext) */
+	cattr_groups = devm_kcalloc(dev, num_groups, sizeof(*cattr_groups),
+				    GFP_KERNEL);
+	if (!cattr_groups)
+		return -ENOMEM;
+
+	/* Initialize attribute lists */
+	for (i = 0; i < num_groups; i++)
+		INIT_LIST_HEAD(&cattr_groups[i].attr_list);
+
+	/* Add Counter device sysfs attributes */
+	err = counter_sysfs_attr_add(counter, cattr_groups);
+	if (err < 0)
+		return err;
+
+	/* Allocate attribute group pointers for association with device */
+	dev->groups = devm_kcalloc(dev, num_groups + 1, sizeof(*dev->groups),
+				   GFP_KERNEL);
+	if (!dev->groups)
+		return -ENOMEM;
+
+	/* Allocate space for attribute groups */
+	groups = devm_kcalloc(dev, num_groups, sizeof(*groups), GFP_KERNEL);
+	if (!groups)
+		return -ENOMEM;
+
+	/* Prepare each group of attributes for association */
+	for (i = 0; i < num_groups; i++) {
+		groups[i].name = cattr_groups[i].name;
+
+		/* Allocate space for attribute pointers */
+		groups[i].attrs = devm_kcalloc(dev,
+					       cattr_groups[i].num_attr + 1,
+					       sizeof(*groups[i].attrs),
+					       GFP_KERNEL);
+		if (!groups[i].attrs)
+			return -ENOMEM;
+
+		/* Add attribute pointers to attribute group */
+		j = 0;
+		list_for_each_entry(p, &cattr_groups[i].attr_list, l)
+			groups[i].attrs[j++] = &p->dev_attr.attr;
+
+		/* Associate attribute group */
+		dev->groups[i] = &groups[i];
+	}
+
+	return 0;
+}
diff --git a/drivers/counter/counter-sysfs.h b/drivers/counter/counter-sysfs.h
new file mode 100644
index 000000000000..14fe566aca0e
--- /dev/null
+++ b/drivers/counter/counter-sysfs.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Counter sysfs interface
+ * Copyright (C) 2020 William Breathitt Gray
+ */
+#ifndef _COUNTER_SYSFS_H_
+#define _COUNTER_SYSFS_H_
+
+#include <linux/counter.h>
+
+int counter_sysfs_add(struct counter_device *const counter);
+
+#endif /* _COUNTER_SYSFS_H_ */
diff --git a/drivers/counter/counter.c b/drivers/counter/counter.c
deleted file mode 100644
index de921e8a3f72..000000000000
--- a/drivers/counter/counter.c
+++ /dev/null
@@ -1,1496 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Generic Counter interface
- * Copyright (C) 2018 William Breathitt Gray
- */
-#include <linux/counter.h>
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/export.h>
-#include <linux/fs.h>
-#include <linux/gfp.h>
-#include <linux/idr.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/printk.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/sysfs.h>
-#include <linux/types.h>
-
-const char *const counter_count_direction_str[2] = {
-	[COUNTER_COUNT_DIRECTION_FORWARD] = "forward",
-	[COUNTER_COUNT_DIRECTION_BACKWARD] = "backward"
-};
-EXPORT_SYMBOL_GPL(counter_count_direction_str);
-
-const char *const counter_count_mode_str[4] = {
-	[COUNTER_COUNT_MODE_NORMAL] = "normal",
-	[COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit",
-	[COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle",
-	[COUNTER_COUNT_MODE_MODULO_N] = "modulo-n"
-};
-EXPORT_SYMBOL_GPL(counter_count_mode_str);
-
-ssize_t counter_signal_enum_read(struct counter_device *counter,
-				 struct counter_signal *signal, void *priv,
-				 char *buf)
-{
-	const struct counter_signal_enum_ext *const e = priv;
-	int err;
-	size_t index;
-
-	if (!e->get)
-		return -EINVAL;
-
-	err = e->get(counter, signal, &index);
-	if (err)
-		return err;
-
-	if (index >= e->num_items)
-		return -EINVAL;
-
-	return sprintf(buf, "%s\n", e->items[index]);
-}
-EXPORT_SYMBOL_GPL(counter_signal_enum_read);
-
-ssize_t counter_signal_enum_write(struct counter_device *counter,
-				  struct counter_signal *signal, void *priv,
-				  const char *buf, size_t len)
-{
-	const struct counter_signal_enum_ext *const e = priv;
-	ssize_t index;
-	int err;
-
-	if (!e->set)
-		return -EINVAL;
-
-	index = __sysfs_match_string(e->items, e->num_items, buf);
-	if (index < 0)
-		return index;
-
-	err = e->set(counter, signal, index);
-	if (err)
-		return err;
-
-	return len;
-}
-EXPORT_SYMBOL_GPL(counter_signal_enum_write);
-
-ssize_t counter_signal_enum_available_read(struct counter_device *counter,
-					   struct counter_signal *signal,
-					   void *priv, char *buf)
-{
-	const struct counter_signal_enum_ext *const e = priv;
-	size_t i;
-	size_t len = 0;
-
-	if (!e->num_items)
-		return 0;
-
-	for (i = 0; i < e->num_items; i++)
-		len += sprintf(buf + len, "%s\n", e->items[i]);
-
-	return len;
-}
-EXPORT_SYMBOL_GPL(counter_signal_enum_available_read);
-
-ssize_t counter_count_enum_read(struct counter_device *counter,
-				struct counter_count *count, void *priv,
-				char *buf)
-{
-	const struct counter_count_enum_ext *const e = priv;
-	int err;
-	size_t index;
-
-	if (!e->get)
-		return -EINVAL;
-
-	err = e->get(counter, count, &index);
-	if (err)
-		return err;
-
-	if (index >= e->num_items)
-		return -EINVAL;
-
-	return sprintf(buf, "%s\n", e->items[index]);
-}
-EXPORT_SYMBOL_GPL(counter_count_enum_read);
-
-ssize_t counter_count_enum_write(struct counter_device *counter,
-				 struct counter_count *count, void *priv,
-				 const char *buf, size_t len)
-{
-	const struct counter_count_enum_ext *const e = priv;
-	ssize_t index;
-	int err;
-
-	if (!e->set)
-		return -EINVAL;
-
-	index = __sysfs_match_string(e->items, e->num_items, buf);
-	if (index < 0)
-		return index;
-
-	err = e->set(counter, count, index);
-	if (err)
-		return err;
-
-	return len;
-}
-EXPORT_SYMBOL_GPL(counter_count_enum_write);
-
-ssize_t counter_count_enum_available_read(struct counter_device *counter,
-					  struct counter_count *count,
-					  void *priv, char *buf)
-{
-	const struct counter_count_enum_ext *const e = priv;
-	size_t i;
-	size_t len = 0;
-
-	if (!e->num_items)
-		return 0;
-
-	for (i = 0; i < e->num_items; i++)
-		len += sprintf(buf + len, "%s\n", e->items[i]);
-
-	return len;
-}
-EXPORT_SYMBOL_GPL(counter_count_enum_available_read);
-
-ssize_t counter_device_enum_read(struct counter_device *counter, void *priv,
-				 char *buf)
-{
-	const struct counter_device_enum_ext *const e = priv;
-	int err;
-	size_t index;
-
-	if (!e->get)
-		return -EINVAL;
-
-	err = e->get(counter, &index);
-	if (err)
-		return err;
-
-	if (index >= e->num_items)
-		return -EINVAL;
-
-	return sprintf(buf, "%s\n", e->items[index]);
-}
-EXPORT_SYMBOL_GPL(counter_device_enum_read);
-
-ssize_t counter_device_enum_write(struct counter_device *counter, void *priv,
-				  const char *buf, size_t len)
-{
-	const struct counter_device_enum_ext *const e = priv;
-	ssize_t index;
-	int err;
-
-	if (!e->set)
-		return -EINVAL;
-
-	index = __sysfs_match_string(e->items, e->num_items, buf);
-	if (index < 0)
-		return index;
-
-	err = e->set(counter, index);
-	if (err)
-		return err;
-
-	return len;
-}
-EXPORT_SYMBOL_GPL(counter_device_enum_write);
-
-ssize_t counter_device_enum_available_read(struct counter_device *counter,
-					   void *priv, char *buf)
-{
-	const struct counter_device_enum_ext *const e = priv;
-	size_t i;
-	size_t len = 0;
-
-	if (!e->num_items)
-		return 0;
-
-	for (i = 0; i < e->num_items; i++)
-		len += sprintf(buf + len, "%s\n", e->items[i]);
-
-	return len;
-}
-EXPORT_SYMBOL_GPL(counter_device_enum_available_read);
-
-struct counter_attr_parm {
-	struct counter_device_attr_group *group;
-	const char *prefix;
-	const char *name;
-	ssize_t (*show)(struct device *dev, struct device_attribute *attr,
-			char *buf);
-	ssize_t (*store)(struct device *dev, struct device_attribute *attr,
-			 const char *buf, size_t len);
-	void *component;
-};
-
-struct counter_device_attr {
-	struct device_attribute dev_attr;
-	struct list_head l;
-	void *component;
-};
-
-static int counter_attribute_create(const struct counter_attr_parm *const parm)
-{
-	struct counter_device_attr *counter_attr;
-	struct device_attribute *dev_attr;
-	int err;
-	struct list_head *const attr_list = &parm->group->attr_list;
-
-	/* Allocate a Counter device attribute */
-	counter_attr = kzalloc(sizeof(*counter_attr), GFP_KERNEL);
-	if (!counter_attr)
-		return -ENOMEM;
-	dev_attr = &counter_attr->dev_attr;
-
-	sysfs_attr_init(&dev_attr->attr);
-
-	/* Configure device attribute */
-	dev_attr->attr.name = kasprintf(GFP_KERNEL, "%s%s", parm->prefix,
-					parm->name);
-	if (!dev_attr->attr.name) {
-		err = -ENOMEM;
-		goto err_free_counter_attr;
-	}
-	if (parm->show) {
-		dev_attr->attr.mode |= 0444;
-		dev_attr->show = parm->show;
-	}
-	if (parm->store) {
-		dev_attr->attr.mode |= 0200;
-		dev_attr->store = parm->store;
-	}
-
-	/* Store associated Counter component with attribute */
-	counter_attr->component = parm->component;
-
-	/* Keep track of the attribute for later cleanup */
-	list_add(&counter_attr->l, attr_list);
-	parm->group->num_attr++;
-
-	return 0;
-
-err_free_counter_attr:
-	kfree(counter_attr);
-	return err;
-}
-
-#define to_counter_attr(_dev_attr) \
-	container_of(_dev_attr, struct counter_device_attr, dev_attr)
-
-struct counter_signal_unit {
-	struct counter_signal *signal;
-};
-
-static const char *const counter_signal_level_str[] = {
-	[COUNTER_SIGNAL_LEVEL_LOW] = "low",
-	[COUNTER_SIGNAL_LEVEL_HIGH] = "high"
-};
-
-static ssize_t counter_signal_show(struct device *dev,
-				   struct device_attribute *attr, char *buf)
-{
-	struct counter_device *const counter = dev_get_drvdata(dev);
-	const struct counter_device_attr *const devattr = to_counter_attr(attr);
-	const struct counter_signal_unit *const component = devattr->component;
-	struct counter_signal *const signal = component->signal;
-	int err;
-	enum counter_signal_level level;
-
-	err = counter->ops->signal_read(counter, signal, &level);
-	if (err)
-		return err;
-
-	return sprintf(buf, "%s\n", counter_signal_level_str[level]);
-}
-
-struct counter_name_unit {
-	const char *name;
-};
-
-static ssize_t counter_device_attr_name_show(struct device *dev,
-					     struct device_attribute *attr,
-					     char *buf)
-{
-	const struct counter_name_unit *const comp = to_counter_attr(attr)->component;
-
-	return sprintf(buf, "%s\n", comp->name);
-}
-
-static int counter_name_attribute_create(
-	struct counter_device_attr_group *const group,
-	const char *const name)
-{
-	struct counter_name_unit *name_comp;
-	struct counter_attr_parm parm;
-	int err;
-
-	/* Skip if no name */
-	if (!name)
-		return 0;
-
-	/* Allocate name attribute component */
-	name_comp = kmalloc(sizeof(*name_comp), GFP_KERNEL);
-	if (!name_comp)
-		return -ENOMEM;
-	name_comp->name = name;
-
-	/* Allocate Signal name attribute */
-	parm.group = group;
-	parm.prefix = "";
-	parm.name = "name";
-	parm.show = counter_device_attr_name_show;
-	parm.store = NULL;
-	parm.component = name_comp;
-	err = counter_attribute_create(&parm);
-	if (err)
-		goto err_free_name_comp;
-
-	return 0;
-
-err_free_name_comp:
-	kfree(name_comp);
-	return err;
-}
-
-struct counter_signal_ext_unit {
-	struct counter_signal *signal;
-	const struct counter_signal_ext *ext;
-};
-
-static ssize_t counter_signal_ext_show(struct device *dev,
-				       struct device_attribute *attr, char *buf)
-{
-	const struct counter_device_attr *const devattr = to_counter_attr(attr);
-	const struct counter_signal_ext_unit *const comp = devattr->component;
-	const struct counter_signal_ext *const ext = comp->ext;
-
-	return ext->read(dev_get_drvdata(dev), comp->signal, ext->priv, buf);
-}
-
-static ssize_t counter_signal_ext_store(struct device *dev,
-					struct device_attribute *attr,
-					const char *buf, size_t len)
-{
-	const struct counter_device_attr *const devattr = to_counter_attr(attr);
-	const struct counter_signal_ext_unit *const comp = devattr->component;
-	const struct counter_signal_ext *const ext = comp->ext;
-
-	return ext->write(dev_get_drvdata(dev), comp->signal, ext->priv, buf,
-		len);
-}
-
-static void counter_device_attr_list_free(struct list_head *attr_list)
-{
-	struct counter_device_attr *p, *n;
-
-	list_for_each_entry_safe(p, n, attr_list, l) {
-		/* free attribute name and associated component memory */
-		kfree(p->dev_attr.attr.name);
-		kfree(p->component);
-		list_del(&p->l);
-		kfree(p);
-	}
-}
-
-static int counter_signal_ext_register(
-	struct counter_device_attr_group *const group,
-	struct counter_signal *const signal)
-{
-	const size_t num_ext = signal->num_ext;
-	size_t i;
-	const struct counter_signal_ext *ext;
-	struct counter_signal_ext_unit *signal_ext_comp;
-	struct counter_attr_parm parm;
-	int err;
-
-	/* Create an attribute for each extension */
-	for (i = 0 ; i < num_ext; i++) {
-		ext = signal->ext + i;
-
-		/* Allocate signal_ext attribute component */
-		signal_ext_comp = kmalloc(sizeof(*signal_ext_comp), GFP_KERNEL);
-		if (!signal_ext_comp) {
-			err = -ENOMEM;
-			goto err_free_attr_list;
-		}
-		signal_ext_comp->signal = signal;
-		signal_ext_comp->ext = ext;
-
-		/* Allocate a Counter device attribute */
-		parm.group = group;
-		parm.prefix = "";
-		parm.name = ext->name;
-		parm.show = (ext->read) ? counter_signal_ext_show : NULL;
-		parm.store = (ext->write) ? counter_signal_ext_store : NULL;
-		parm.component = signal_ext_comp;
-		err = counter_attribute_create(&parm);
-		if (err) {
-			kfree(signal_ext_comp);
-			goto err_free_attr_list;
-		}
-	}
-
-	return 0;
-
-err_free_attr_list:
-	counter_device_attr_list_free(&group->attr_list);
-	return err;
-}
-
-static int counter_signal_attributes_create(
-	struct counter_device_attr_group *const group,
-	const struct counter_device *const counter,
-	struct counter_signal *const signal)
-{
-	struct counter_signal_unit *signal_comp;
-	struct counter_attr_parm parm;
-	int err;
-
-	/* Allocate Signal attribute component */
-	signal_comp = kmalloc(sizeof(*signal_comp), GFP_KERNEL);
-	if (!signal_comp)
-		return -ENOMEM;
-	signal_comp->signal = signal;
-
-	/* Create main Signal attribute */
-	parm.group = group;
-	parm.prefix = "";
-	parm.name = "signal";
-	parm.show = (counter->ops->signal_read) ? counter_signal_show : NULL;
-	parm.store = NULL;
-	parm.component = signal_comp;
-	err = counter_attribute_create(&parm);
-	if (err) {
-		kfree(signal_comp);
-		return err;
-	}
-
-	/* Create Signal name attribute */
-	err = counter_name_attribute_create(group, signal->name);
-	if (err)
-		goto err_free_attr_list;
-
-	/* Register Signal extension attributes */
-	err = counter_signal_ext_register(group, signal);
-	if (err)
-		goto err_free_attr_list;
-
-	return 0;
-
-err_free_attr_list:
-	counter_device_attr_list_free(&group->attr_list);
-	return err;
-}
-
-static int counter_signals_register(
-	struct counter_device_attr_group *const groups_list,
-	const struct counter_device *const counter)
-{
-	const size_t num_signals = counter->num_signals;
-	size_t i;
-	struct counter_signal *signal;
-	const char *name;
-	int err;
-
-	/* Register each Signal */
-	for (i = 0; i < num_signals; i++) {
-		signal = counter->signals + i;
-
-		/* Generate Signal attribute directory name */
-		name = kasprintf(GFP_KERNEL, "signal%d", signal->id);
-		if (!name) {
-			err = -ENOMEM;
-			goto err_free_attr_groups;
-		}
-		groups_list[i].attr_group.name = name;
-
-		/* Create all attributes associated with Signal */
-		err = counter_signal_attributes_create(groups_list + i, counter,
-						       signal);
-		if (err)
-			goto err_free_attr_groups;
-	}
-
-	return 0;
-
-err_free_attr_groups:
-	do {
-		kfree(groups_list[i].attr_group.name);
-		counter_device_attr_list_free(&groups_list[i].attr_list);
-	} while (i--);
-	return err;
-}
-
-static const char *const counter_synapse_action_str[] = {
-	[COUNTER_SYNAPSE_ACTION_NONE] = "none",
-	[COUNTER_SYNAPSE_ACTION_RISING_EDGE] = "rising edge",
-	[COUNTER_SYNAPSE_ACTION_FALLING_EDGE] = "falling edge",
-	[COUNTER_SYNAPSE_ACTION_BOTH_EDGES] = "both edges"
-};
-
-struct counter_action_unit {
-	struct counter_synapse *synapse;
-	struct counter_count *count;
-};
-
-static ssize_t counter_action_show(struct device *dev,
-				   struct device_attribute *attr, char *buf)
-{
-	const struct counter_device_attr *const devattr = to_counter_attr(attr);
-	int err;
-	struct counter_device *const counter = dev_get_drvdata(dev);
-	const struct counter_action_unit *const component = devattr->component;
-	struct counter_count *const count = component->count;
-	struct counter_synapse *const synapse = component->synapse;
-	size_t action_index;
-	enum counter_synapse_action action;
-
-	err = counter->ops->action_get(counter, count, synapse, &action_index);
-	if (err)
-		return err;
-
-	synapse->action = action_index;
-
-	action = synapse->actions_list[action_index];
-	return sprintf(buf, "%s\n", counter_synapse_action_str[action]);
-}
-
-static ssize_t counter_action_store(struct device *dev,
-				    struct device_attribute *attr,
-				    const char *buf, size_t len)
-{
-	const struct counter_device_attr *const devattr = to_counter_attr(attr);
-	const struct counter_action_unit *const component = devattr->component;
-	struct counter_synapse *const synapse = component->synapse;
-	size_t action_index;
-	const size_t num_actions = synapse->num_actions;
-	enum counter_synapse_action action;
-	int err;
-	struct counter_device *const counter = dev_get_drvdata(dev);
-	struct counter_count *const count = component->count;
-
-	/* Find requested action mode */
-	for (action_index = 0; action_index < num_actions; action_index++) {
-		action = synapse->actions_list[action_index];
-		if (sysfs_streq(buf, counter_synapse_action_str[action]))
-			break;
-	}
-	/* If requested action mode not found */
-	if (action_index >= num_actions)
-		return -EINVAL;
-
-	err = counter->ops->action_set(counter, count, synapse, action_index);
-	if (err)
-		return err;
-
-	synapse->action = action_index;
-
-	return len;
-}
-
-struct counter_action_avail_unit {
-	const enum counter_synapse_action *actions_list;
-	size_t num_actions;
-};
-
-static ssize_t counter_synapse_action_available_show(struct device *dev,
-	struct device_attribute *attr, char *buf)
-{
-	const struct counter_device_attr *const devattr = to_counter_attr(attr);
-	const struct counter_action_avail_unit *const component = devattr->component;
-	size_t i;
-	enum counter_synapse_action action;
-	ssize_t len = 0;
-
-	for (i = 0; i < component->num_actions; i++) {
-		action = component->actions_list[i];
-		len += sprintf(buf + len, "%s\n",
-			       counter_synapse_action_str[action]);
-	}
-
-	return len;
-}
-
-static int counter_synapses_register(
-	struct counter_device_attr_group *const group,
-	const struct counter_device *const counter,
-	struct counter_count *const count, const char *const count_attr_name)
-{
-	size_t i;
-	struct counter_synapse *synapse;
-	const char *prefix;
-	struct counter_action_unit *action_comp;
-	struct counter_attr_parm parm;
-	int err;
-	struct counter_action_avail_unit *avail_comp;
-
-	/* Register each Synapse */
-	for (i = 0; i < count->num_synapses; i++) {
-		synapse = count->synapses + i;
-
-		/* Generate attribute prefix */
-		prefix = kasprintf(GFP_KERNEL, "signal%d_",
-				   synapse->signal->id);
-		if (!prefix) {
-			err = -ENOMEM;
-			goto err_free_attr_list;
-		}
-
-		/* Allocate action attribute component */
-		action_comp = kmalloc(sizeof(*action_comp), GFP_KERNEL);
-		if (!action_comp) {
-			err = -ENOMEM;
-			goto err_free_prefix;
-		}
-		action_comp->synapse = synapse;
-		action_comp->count = count;
-
-		/* Create action attribute */
-		parm.group = group;
-		parm.prefix = prefix;
-		parm.name = "action";
-		parm.show = (counter->ops->action_get) ? counter_action_show : NULL;
-		parm.store = (counter->ops->action_set) ? counter_action_store : NULL;
-		parm.component = action_comp;
-		err = counter_attribute_create(&parm);
-		if (err) {
-			kfree(action_comp);
-			goto err_free_prefix;
-		}
-
-		/* Allocate action available attribute component */
-		avail_comp = kmalloc(sizeof(*avail_comp), GFP_KERNEL);
-		if (!avail_comp) {
-			err = -ENOMEM;
-			goto err_free_prefix;
-		}
-		avail_comp->actions_list = synapse->actions_list;
-		avail_comp->num_actions = synapse->num_actions;
-
-		/* Create action_available attribute */
-		parm.group = group;
-		parm.prefix = prefix;
-		parm.name = "action_available";
-		parm.show = counter_synapse_action_available_show;
-		parm.store = NULL;
-		parm.component = avail_comp;
-		err = counter_attribute_create(&parm);
-		if (err) {
-			kfree(avail_comp);
-			goto err_free_prefix;
-		}
-
-		kfree(prefix);
-	}
-
-	return 0;
-
-err_free_prefix:
-	kfree(prefix);
-err_free_attr_list:
-	counter_device_attr_list_free(&group->attr_list);
-	return err;
-}
-
-struct counter_count_unit {
-	struct counter_count *count;
-};
-
-static ssize_t counter_count_show(struct device *dev,
-				  struct device_attribute *attr,
-				  char *buf)
-{
-	struct counter_device *const counter = dev_get_drvdata(dev);
-	const struct counter_device_attr *const devattr = to_counter_attr(attr);
-	const struct counter_count_unit *const component = devattr->component;
-	struct counter_count *const count = component->count;
-	int err;
-	unsigned long val;
-
-	err = counter->ops->count_read(counter, count, &val);
-	if (err)
-		return err;
-
-	return sprintf(buf, "%lu\n", val);
-}
-
-static ssize_t counter_count_store(struct device *dev,
-				   struct device_attribute *attr,
-				   const char *buf, size_t len)
-{
-	struct counter_device *const counter = dev_get_drvdata(dev);
-	const struct counter_device_attr *const devattr = to_counter_attr(attr);
-	const struct counter_count_unit *const component = devattr->component;
-	struct counter_count *const count = component->count;
-	int err;
-	unsigned long val;
-
-	err = kstrtoul(buf, 0, &val);
-	if (err)
-		return err;
-
-	err = counter->ops->count_write(counter, count, val);
-	if (err)
-		return err;
-
-	return len;
-}
-
-static const char *const counter_function_str[] = {
-	[COUNTER_FUNCTION_INCREASE] = "increase",
-	[COUNTER_FUNCTION_DECREASE] = "decrease",
-	[COUNTER_FUNCTION_PULSE_DIRECTION] = "pulse-direction",
-	[COUNTER_FUNCTION_QUADRATURE_X1_A] = "quadrature x1 a",
-	[COUNTER_FUNCTION_QUADRATURE_X1_B] = "quadrature x1 b",
-	[COUNTER_FUNCTION_QUADRATURE_X2_A] = "quadrature x2 a",
-	[COUNTER_FUNCTION_QUADRATURE_X2_B] = "quadrature x2 b",
-	[COUNTER_FUNCTION_QUADRATURE_X4] = "quadrature x4"
-};
-
-static ssize_t counter_function_show(struct device *dev,
-				     struct device_attribute *attr, char *buf)
-{
-	int err;
-	struct counter_device *const counter = dev_get_drvdata(dev);
-	const struct counter_device_attr *const devattr = to_counter_attr(attr);
-	const struct counter_count_unit *const component = devattr->component;
-	struct counter_count *const count = component->count;
-	size_t func_index;
-	enum counter_function function;
-
-	err = counter->ops->function_get(counter, count, &func_index);
-	if (err)
-		return err;
-
-	count->function = func_index;
-
-	function = count->functions_list[func_index];
-	return sprintf(buf, "%s\n", counter_function_str[function]);
-}
-
-static ssize_t counter_function_store(struct device *dev,
-				      struct device_attribute *attr,
-				      const char *buf, size_t len)
-{
-	const struct counter_device_attr *const devattr = to_counter_attr(attr);
-	const struct counter_count_unit *const component = devattr->component;
-	struct counter_count *const count = component->count;
-	const size_t num_functions = count->num_functions;
-	size_t func_index;
-	enum counter_function function;
-	int err;
-	struct counter_device *const counter = dev_get_drvdata(dev);
-
-	/* Find requested Count function mode */
-	for (func_index = 0; func_index < num_functions; func_index++) {
-		function = count->functions_list[func_index];
-		if (sysfs_streq(buf, counter_function_str[function]))
-			break;
-	}
-	/* Return error if requested Count function mode not found */
-	if (func_index >= num_functions)
-		return -EINVAL;
-
-	err = counter->ops->function_set(counter, count, func_index);
-	if (err)
-		return err;
-
-	count->function = func_index;
-
-	return len;
-}
-
-struct counter_count_ext_unit {
-	struct counter_count *count;
-	const struct counter_count_ext *ext;
-};
-
-static ssize_t counter_count_ext_show(struct device *dev,
-				      struct device_attribute *attr, char *buf)
-{
-	const struct counter_device_attr *const devattr = to_counter_attr(attr);
-	const struct counter_count_ext_unit *const comp = devattr->component;
-	const struct counter_count_ext *const ext = comp->ext;
-
-	return ext->read(dev_get_drvdata(dev), comp->count, ext->priv, buf);
-}
-
-static ssize_t counter_count_ext_store(struct device *dev,
-				       struct device_attribute *attr,
-				       const char *buf, size_t len)
-{
-	const struct counter_device_attr *const devattr = to_counter_attr(attr);
-	const struct counter_count_ext_unit *const comp = devattr->component;
-	const struct counter_count_ext *const ext = comp->ext;
-
-	return ext->write(dev_get_drvdata(dev), comp->count, ext->priv, buf,
-		len);
-}
-
-static int counter_count_ext_register(
-	struct counter_device_attr_group *const group,
-	struct counter_count *const count)
-{
-	size_t i;
-	const struct counter_count_ext *ext;
-	struct counter_count_ext_unit *count_ext_comp;
-	struct counter_attr_parm parm;
-	int err;
-
-	/* Create an attribute for each extension */
-	for (i = 0 ; i < count->num_ext; i++) {
-		ext = count->ext + i;
-
-		/* Allocate count_ext attribute component */
-		count_ext_comp = kmalloc(sizeof(*count_ext_comp), GFP_KERNEL);
-		if (!count_ext_comp) {
-			err = -ENOMEM;
-			goto err_free_attr_list;
-		}
-		count_ext_comp->count = count;
-		count_ext_comp->ext = ext;
-
-		/* Allocate count_ext attribute */
-		parm.group = group;
-		parm.prefix = "";
-		parm.name = ext->name;
-		parm.show = (ext->read) ? counter_count_ext_show : NULL;
-		parm.store = (ext->write) ? counter_count_ext_store : NULL;
-		parm.component = count_ext_comp;
-		err = counter_attribute_create(&parm);
-		if (err) {
-			kfree(count_ext_comp);
-			goto err_free_attr_list;
-		}
-	}
-
-	return 0;
-
-err_free_attr_list:
-	counter_device_attr_list_free(&group->attr_list);
-	return err;
-}
-
-struct counter_func_avail_unit {
-	const enum counter_function *functions_list;
-	size_t num_functions;
-};
-
-static ssize_t counter_function_available_show(struct device *dev,
-	struct device_attribute *attr, char *buf)
-{
-	const struct counter_device_attr *const devattr = to_counter_attr(attr);
-	const struct counter_func_avail_unit *const component = devattr->component;
-	const enum counter_function *const func_list = component->functions_list;
-	const size_t num_functions = component->num_functions;
-	size_t i;
-	enum counter_function function;
-	ssize_t len = 0;
-
-	for (i = 0; i < num_functions; i++) {
-		function = func_list[i];
-		len += sprintf(buf + len, "%s\n",
-			       counter_function_str[function]);
-	}
-
-	return len;
-}
-
-static int counter_count_attributes_create(
-	struct counter_device_attr_group *const group,
-	const struct counter_device *const counter,
-	struct counter_count *const count)
-{
-	struct counter_count_unit *count_comp;
-	struct counter_attr_parm parm;
-	int err;
-	struct counter_count_unit *func_comp;
-	struct counter_func_avail_unit *avail_comp;
-
-	/* Allocate count attribute component */
-	count_comp = kmalloc(sizeof(*count_comp), GFP_KERNEL);
-	if (!count_comp)
-		return -ENOMEM;
-	count_comp->count = count;
-
-	/* Create main Count attribute */
-	parm.group = group;
-	parm.prefix = "";
-	parm.name = "count";
-	parm.show = (counter->ops->count_read) ? counter_count_show : NULL;
-	parm.store = (counter->ops->count_write) ? counter_count_store : NULL;
-	parm.component = count_comp;
-	err = counter_attribute_create(&parm);
-	if (err) {
-		kfree(count_comp);
-		return err;
-	}
-
-	/* Allocate function attribute component */
-	func_comp = kmalloc(sizeof(*func_comp), GFP_KERNEL);
-	if (!func_comp) {
-		err = -ENOMEM;
-		goto err_free_attr_list;
-	}
-	func_comp->count = count;
-
-	/* Create Count function attribute */
-	parm.group = group;
-	parm.prefix = "";
-	parm.name = "function";
-	parm.show = (counter->ops->function_get) ? counter_function_show : NULL;
-	parm.store = (counter->ops->function_set) ? counter_function_store : NULL;
-	parm.component = func_comp;
-	err = counter_attribute_create(&parm);
-	if (err) {
-		kfree(func_comp);
-		goto err_free_attr_list;
-	}
-
-	/* Allocate function available attribute component */
-	avail_comp = kmalloc(sizeof(*avail_comp), GFP_KERNEL);
-	if (!avail_comp) {
-		err = -ENOMEM;
-		goto err_free_attr_list;
-	}
-	avail_comp->functions_list = count->functions_list;
-	avail_comp->num_functions = count->num_functions;
-
-	/* Create Count function_available attribute */
-	parm.group = group;
-	parm.prefix = "";
-	parm.name = "function_available";
-	parm.show = counter_function_available_show;
-	parm.store = NULL;
-	parm.component = avail_comp;
-	err = counter_attribute_create(&parm);
-	if (err) {
-		kfree(avail_comp);
-		goto err_free_attr_list;
-	}
-
-	/* Create Count name attribute */
-	err = counter_name_attribute_create(group, count->name);
-	if (err)
-		goto err_free_attr_list;
-
-	/* Register Count extension attributes */
-	err = counter_count_ext_register(group, count);
-	if (err)
-		goto err_free_attr_list;
-
-	return 0;
-
-err_free_attr_list:
-	counter_device_attr_list_free(&group->attr_list);
-	return err;
-}
-
-static int counter_counts_register(
-	struct counter_device_attr_group *const groups_list,
-	const struct counter_device *const counter)
-{
-	size_t i;
-	struct counter_count *count;
-	const char *name;
-	int err;
-
-	/* Register each Count */
-	for (i = 0; i < counter->num_counts; i++) {
-		count = counter->counts + i;
-
-		/* Generate Count attribute directory name */
-		name = kasprintf(GFP_KERNEL, "count%d", count->id);
-		if (!name) {
-			err = -ENOMEM;
-			goto err_free_attr_groups;
-		}
-		groups_list[i].attr_group.name = name;
-
-		/* Register the Synapses associated with each Count */
-		err = counter_synapses_register(groups_list + i, counter, count,
-						name);
-		if (err)
-			goto err_free_attr_groups;
-
-		/* Create all attributes associated with Count */
-		err = counter_count_attributes_create(groups_list + i, counter,
-						      count);
-		if (err)
-			goto err_free_attr_groups;
-	}
-
-	return 0;
-
-err_free_attr_groups:
-	do {
-		kfree(groups_list[i].attr_group.name);
-		counter_device_attr_list_free(&groups_list[i].attr_list);
-	} while (i--);
-	return err;
-}
-
-struct counter_size_unit {
-	size_t size;
-};
-
-static ssize_t counter_device_attr_size_show(struct device *dev,
-					     struct device_attribute *attr,
-					     char *buf)
-{
-	const struct counter_size_unit *const comp = to_counter_attr(attr)->component;
-
-	return sprintf(buf, "%zu\n", comp->size);
-}
-
-static int counter_size_attribute_create(
-	struct counter_device_attr_group *const group,
-	const size_t size, const char *const name)
-{
-	struct counter_size_unit *size_comp;
-	struct counter_attr_parm parm;
-	int err;
-
-	/* Allocate size attribute component */
-	size_comp = kmalloc(sizeof(*size_comp), GFP_KERNEL);
-	if (!size_comp)
-		return -ENOMEM;
-	size_comp->size = size;
-
-	parm.group = group;
-	parm.prefix = "";
-	parm.name = name;
-	parm.show = counter_device_attr_size_show;
-	parm.store = NULL;
-	parm.component = size_comp;
-	err = counter_attribute_create(&parm);
-	if (err)
-		goto err_free_size_comp;
-
-	return 0;
-
-err_free_size_comp:
-	kfree(size_comp);
-	return err;
-}
-
-struct counter_ext_unit {
-	const struct counter_device_ext *ext;
-};
-
-static ssize_t counter_device_ext_show(struct device *dev,
-				       struct device_attribute *attr, char *buf)
-{
-	const struct counter_device_attr *const devattr = to_counter_attr(attr);
-	const struct counter_ext_unit *const component = devattr->component;
-	const struct counter_device_ext *const ext = component->ext;
-
-	return ext->read(dev_get_drvdata(dev), ext->priv, buf);
-}
-
-static ssize_t counter_device_ext_store(struct device *dev,
-					struct device_attribute *attr,
-					const char *buf, size_t len)
-{
-	const struct counter_device_attr *const devattr = to_counter_attr(attr);
-	const struct counter_ext_unit *const component = devattr->component;
-	const struct counter_device_ext *const ext = component->ext;
-
-	return ext->write(dev_get_drvdata(dev), ext->priv, buf, len);
-}
-
-static int counter_device_ext_register(
-	struct counter_device_attr_group *const group,
-	struct counter_device *const counter)
-{
-	size_t i;
-	struct counter_ext_unit *ext_comp;
-	struct counter_attr_parm parm;
-	int err;
-
-	/* Create an attribute for each extension */
-	for (i = 0 ; i < counter->num_ext; i++) {
-		/* Allocate extension attribute component */
-		ext_comp = kmalloc(sizeof(*ext_comp), GFP_KERNEL);
-		if (!ext_comp) {
-			err = -ENOMEM;
-			goto err_free_attr_list;
-		}
-
-		ext_comp->ext = counter->ext + i;
-
-		/* Allocate extension attribute */
-		parm.group = group;
-		parm.prefix = "";
-		parm.name = counter->ext[i].name;
-		parm.show = (counter->ext[i].read) ? counter_device_ext_show : NULL;
-		parm.store = (counter->ext[i].write) ? counter_device_ext_store : NULL;
-		parm.component = ext_comp;
-		err = counter_attribute_create(&parm);
-		if (err) {
-			kfree(ext_comp);
-			goto err_free_attr_list;
-		}
-	}
-
-	return 0;
-
-err_free_attr_list:
-	counter_device_attr_list_free(&group->attr_list);
-	return err;
-}
-
-static int counter_global_attr_register(
-	struct counter_device_attr_group *const group,
-	struct counter_device *const counter)
-{
-	int err;
-
-	/* Create name attribute */
-	err = counter_name_attribute_create(group, counter->name);
-	if (err)
-		return err;
-
-	/* Create num_counts attribute */
-	err = counter_size_attribute_create(group, counter->num_counts,
-					    "num_counts");
-	if (err)
-		goto err_free_attr_list;
-
-	/* Create num_signals attribute */
-	err = counter_size_attribute_create(group, counter->num_signals,
-					    "num_signals");
-	if (err)
-		goto err_free_attr_list;
-
-	/* Register Counter device extension attributes */
-	err = counter_device_ext_register(group, counter);
-	if (err)
-		goto err_free_attr_list;
-
-	return 0;
-
-err_free_attr_list:
-	counter_device_attr_list_free(&group->attr_list);
-	return err;
-}
-
-static void counter_device_groups_list_free(
-	struct counter_device_attr_group *const groups_list,
-	const size_t num_groups)
-{
-	struct counter_device_attr_group *group;
-	size_t i;
-
-	/* loop through all attribute groups (signals, counts, global, etc.) */
-	for (i = 0; i < num_groups; i++) {
-		group = groups_list + i;
-
-		/* free all attribute group and associated attributes memory */
-		kfree(group->attr_group.name);
-		kfree(group->attr_group.attrs);
-		counter_device_attr_list_free(&group->attr_list);
-	}
-
-	kfree(groups_list);
-}
-
-static int counter_device_groups_list_prepare(
-	struct counter_device *const counter)
-{
-	const size_t total_num_groups =
-		counter->num_signals + counter->num_counts + 1;
-	struct counter_device_attr_group *groups_list;
-	size_t i;
-	int err;
-	size_t num_groups = 0;
-
-	/* Allocate space for attribute groups (signals, counts, and ext) */
-	groups_list = kcalloc(total_num_groups, sizeof(*groups_list),
-			      GFP_KERNEL);
-	if (!groups_list)
-		return -ENOMEM;
-
-	/* Initialize attribute lists */
-	for (i = 0; i < total_num_groups; i++)
-		INIT_LIST_HEAD(&groups_list[i].attr_list);
-
-	/* Register Signals */
-	err = counter_signals_register(groups_list, counter);
-	if (err)
-		goto err_free_groups_list;
-	num_groups += counter->num_signals;
-
-	/* Register Counts and respective Synapses */
-	err = counter_counts_register(groups_list + num_groups, counter);
-	if (err)
-		goto err_free_groups_list;
-	num_groups += counter->num_counts;
-
-	/* Register Counter global attributes */
-	err = counter_global_attr_register(groups_list + num_groups, counter);
-	if (err)
-		goto err_free_groups_list;
-	num_groups++;
-
-	/* Store groups_list in device_state */
-	counter->device_state->groups_list = groups_list;
-	counter->device_state->num_groups = num_groups;
-
-	return 0;
-
-err_free_groups_list:
-	counter_device_groups_list_free(groups_list, num_groups);
-	return err;
-}
-
-static int counter_device_groups_prepare(
-	struct counter_device_state *const device_state)
-{
-	size_t i, j;
-	struct counter_device_attr_group *group;
-	int err;
-	struct counter_device_attr *p;
-
-	/* Allocate attribute groups for association with device */
-	device_state->groups = kcalloc(device_state->num_groups + 1,
-				       sizeof(*device_state->groups),
-				       GFP_KERNEL);
-	if (!device_state->groups)
-		return -ENOMEM;
-
-	/* Prepare each group of attributes for association */
-	for (i = 0; i < device_state->num_groups; i++) {
-		group = device_state->groups_list + i;
-
-		/* Allocate space for attribute pointers in attribute group */
-		group->attr_group.attrs = kcalloc(group->num_attr + 1,
-			sizeof(*group->attr_group.attrs), GFP_KERNEL);
-		if (!group->attr_group.attrs) {
-			err = -ENOMEM;
-			goto err_free_groups;
-		}
-
-		/* Add attribute pointers to attribute group */
-		j = 0;
-		list_for_each_entry(p, &group->attr_list, l)
-			group->attr_group.attrs[j++] = &p->dev_attr.attr;
-
-		/* Group attributes in attribute group */
-		device_state->groups[i] = &group->attr_group;
-	}
-	/* Associate attributes with device */
-	device_state->dev.groups = device_state->groups;
-
-	return 0;
-
-err_free_groups:
-	do {
-		group = device_state->groups_list + i;
-		kfree(group->attr_group.attrs);
-		group->attr_group.attrs = NULL;
-	} while (i--);
-	kfree(device_state->groups);
-	return err;
-}
-
-/* Provides a unique ID for each counter device */
-static DEFINE_IDA(counter_ida);
-
-static void counter_device_release(struct device *dev)
-{
-	struct counter_device *const counter = dev_get_drvdata(dev);
-	struct counter_device_state *const device_state = counter->device_state;
-
-	kfree(device_state->groups);
-	counter_device_groups_list_free(device_state->groups_list,
-					device_state->num_groups);
-	ida_simple_remove(&counter_ida, device_state->id);
-	kfree(device_state);
-}
-
-static struct device_type counter_device_type = {
-	.name = "counter_device",
-	.release = counter_device_release
-};
-
-static struct bus_type counter_bus_type = {
-	.name = "counter"
-};
-
-/**
- * counter_register - register Counter to the system
- * @counter:	pointer to Counter to register
- *
- * This function registers a Counter to the system. A sysfs "counter" directory
- * will be created and populated with sysfs attributes correlating with the
- * Counter Signals, Synapses, and Counts respectively.
- */
-int counter_register(struct counter_device *const counter)
-{
-	struct counter_device_state *device_state;
-	int err;
-
-	/* Allocate internal state container for Counter device */
-	device_state = kzalloc(sizeof(*device_state), GFP_KERNEL);
-	if (!device_state)
-		return -ENOMEM;
-	counter->device_state = device_state;
-
-	/* Acquire unique ID */
-	device_state->id = ida_simple_get(&counter_ida, 0, 0, GFP_KERNEL);
-	if (device_state->id < 0) {
-		err = device_state->id;
-		goto err_free_device_state;
-	}
-
-	/* Configure device structure for Counter */
-	device_state->dev.type = &counter_device_type;
-	device_state->dev.bus = &counter_bus_type;
-	if (counter->parent) {
-		device_state->dev.parent = counter->parent;
-		device_state->dev.of_node = counter->parent->of_node;
-	}
-	dev_set_name(&device_state->dev, "counter%d", device_state->id);
-	device_initialize(&device_state->dev);
-	dev_set_drvdata(&device_state->dev, counter);
-
-	/* Prepare device attributes */
-	err = counter_device_groups_list_prepare(counter);
-	if (err)
-		goto err_free_id;
-
-	/* Organize device attributes to groups and match to device */
-	err = counter_device_groups_prepare(device_state);
-	if (err)
-		goto err_free_groups_list;
-
-	/* Add device to system */
-	err = device_add(&device_state->dev);
-	if (err)
-		goto err_free_groups;
-
-	return 0;
-
-err_free_groups:
-	kfree(device_state->groups);
-err_free_groups_list:
-	counter_device_groups_list_free(device_state->groups_list,
-					device_state->num_groups);
-err_free_id:
-	ida_simple_remove(&counter_ida, device_state->id);
-err_free_device_state:
-	kfree(device_state);
-	return err;
-}
-EXPORT_SYMBOL_GPL(counter_register);
-
-/**
- * counter_unregister - unregister Counter from the system
- * @counter:	pointer to Counter to unregister
- *
- * The Counter is unregistered from the system; all allocated memory is freed.
- */
-void counter_unregister(struct counter_device *const counter)
-{
-	if (counter)
-		device_del(&counter->device_state->dev);
-}
-EXPORT_SYMBOL_GPL(counter_unregister);
-
-static void devm_counter_unreg(struct device *dev, void *res)
-{
-	counter_unregister(*(struct counter_device **)res);
-}
-
-/**
- * devm_counter_register - Resource-managed counter_register
- * @dev:	device to allocate counter_device for
- * @counter:	pointer to Counter to register
- *
- * Managed counter_register. The Counter registered with this function is
- * automatically unregistered on driver detach. This function calls
- * counter_register internally. Refer to that function for more information.
- *
- * If an Counter registered with this function needs to be unregistered
- * separately, devm_counter_unregister must be used.
- *
- * RETURNS:
- * 0 on success, negative error number on failure.
- */
-int devm_counter_register(struct device *dev,
-			  struct counter_device *const counter)
-{
-	struct counter_device **ptr;
-	int ret;
-
-	ptr = devres_alloc(devm_counter_unreg, sizeof(*ptr), GFP_KERNEL);
-	if (!ptr)
-		return -ENOMEM;
-
-	ret = counter_register(counter);
-	if (!ret) {
-		*ptr = counter;
-		devres_add(dev, ptr);
-	} else {
-		devres_free(ptr);
-	}
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(devm_counter_register);
-
-static int devm_counter_match(struct device *dev, void *res, void *data)
-{
-	struct counter_device **r = res;
-
-	if (!r || !*r) {
-		WARN_ON(!r || !*r);
-		return 0;
-	}
-
-	return *r == data;
-}
-
-/**
- * devm_counter_unregister - Resource-managed counter_unregister
- * @dev:	device this counter_device belongs to
- * @counter:	pointer to Counter associated with the device
- *
- * Unregister Counter registered with devm_counter_register.
- */
-void devm_counter_unregister(struct device *dev,
-			     struct counter_device *const counter)
-{
-	int rc;
-
-	rc = devres_release(dev, devm_counter_unreg, devm_counter_match,
-			    counter);
-	WARN_ON(rc);
-}
-EXPORT_SYMBOL_GPL(devm_counter_unregister);
-
-static int __init counter_init(void)
-{
-	return bus_register(&counter_bus_type);
-}
-
-static void __exit counter_exit(void)
-{
-	bus_unregister(&counter_bus_type);
-}
-
-subsys_initcall(counter_init);
-module_exit(counter_exit);
-
-MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
-MODULE_DESCRIPTION("Generic Counter interface");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/counter/ftm-quaddec.c b/drivers/counter/ftm-quaddec.c
index 53c15f84909b..5ef0478709cd 100644
--- a/drivers/counter/ftm-quaddec.c
+++ b/drivers/counter/ftm-quaddec.c
@@ -14,6 +14,7 @@
 #include <linux/mutex.h>
 #include <linux/counter.h>
 #include <linux/bitfield.h>
+#include <linux/types.h>
 
 #define FTM_FIELD_UPDATE(ftm, offset, mask, val)			\
 	({								\
@@ -115,8 +116,7 @@ static void ftm_quaddec_disable(void *ftm)
 }
 
 static int ftm_quaddec_get_prescaler(struct counter_device *counter,
-				     struct counter_count *count,
-				     size_t *cnt_mode)
+				     struct counter_count *count, u32 *cnt_mode)
 {
 	struct ftm_quaddec *ftm = counter->priv;
 	uint32_t scflags;
@@ -129,8 +129,7 @@ static int ftm_quaddec_get_prescaler(struct counter_device *counter,
 }
 
 static int ftm_quaddec_set_prescaler(struct counter_device *counter,
-				     struct counter_count *count,
-				     size_t cnt_mode)
+				     struct counter_count *count, u32 cnt_mode)
 {
 	struct ftm_quaddec *ftm = counter->priv;
 
@@ -151,33 +150,17 @@ static const char * const ftm_quaddec_prescaler[] = {
 	"1", "2", "4", "8", "16", "32", "64", "128"
 };
 
-static struct counter_count_enum_ext ftm_quaddec_prescaler_enum = {
-	.items = ftm_quaddec_prescaler,
-	.num_items = ARRAY_SIZE(ftm_quaddec_prescaler),
-	.get = ftm_quaddec_get_prescaler,
-	.set = ftm_quaddec_set_prescaler
-};
-
-enum ftm_quaddec_synapse_action {
-	FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES,
-};
-
 static const enum counter_synapse_action ftm_quaddec_synapse_actions[] = {
-	[FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES] =
 	COUNTER_SYNAPSE_ACTION_BOTH_EDGES
 };
 
-enum ftm_quaddec_count_function {
-	FTM_QUADDEC_COUNT_ENCODER_MODE_1,
-};
-
 static const enum counter_function ftm_quaddec_count_functions[] = {
-	[FTM_QUADDEC_COUNT_ENCODER_MODE_1] = COUNTER_FUNCTION_QUADRATURE_X4
+	COUNTER_FUNCTION_QUADRATURE_X4
 };
 
 static int ftm_quaddec_count_read(struct counter_device *counter,
 				  struct counter_count *count,
-				  unsigned long *val)
+				  u64 *val)
 {
 	struct ftm_quaddec *const ftm = counter->priv;
 	uint32_t cntval;
@@ -191,7 +174,7 @@ static int ftm_quaddec_count_read(struct counter_device *counter,
 
 static int ftm_quaddec_count_write(struct counter_device *counter,
 				   struct counter_count *count,
-				   const unsigned long val)
+				   const u64 val)
 {
 	struct ftm_quaddec *const ftm = counter->priv;
 
@@ -205,21 +188,21 @@ static int ftm_quaddec_count_write(struct counter_device *counter,
 	return 0;
 }
 
-static int ftm_quaddec_count_function_get(struct counter_device *counter,
-					  struct counter_count *count,
-					  size_t *function)
+static int ftm_quaddec_count_function_read(struct counter_device *counter,
+					   struct counter_count *count,
+					   enum counter_function *function)
 {
-	*function = FTM_QUADDEC_COUNT_ENCODER_MODE_1;
+	*function = COUNTER_FUNCTION_QUADRATURE_X4;
 
 	return 0;
 }
 
-static int ftm_quaddec_action_get(struct counter_device *counter,
-				  struct counter_count *count,
-				  struct counter_synapse *synapse,
-				  size_t *action)
+static int ftm_quaddec_action_read(struct counter_device *counter,
+				   struct counter_count *count,
+				   struct counter_synapse *synapse,
+				   enum counter_synapse_action *action)
 {
-	*action = FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES;
+	*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
 
 	return 0;
 }
@@ -227,8 +210,8 @@ static int ftm_quaddec_action_get(struct counter_device *counter,
 static const struct counter_ops ftm_quaddec_cnt_ops = {
 	.count_read = ftm_quaddec_count_read,
 	.count_write = ftm_quaddec_count_write,
-	.function_get = ftm_quaddec_count_function_get,
-	.action_get = ftm_quaddec_action_get,
+	.function_read = ftm_quaddec_count_function_read,
+	.action_read = ftm_quaddec_action_read,
 };
 
 static struct counter_signal ftm_quaddec_signals[] = {
@@ -255,9 +238,12 @@ static struct counter_synapse ftm_quaddec_count_synapses[] = {
 	}
 };
 
-static const struct counter_count_ext ftm_quaddec_count_ext[] = {
-	COUNTER_COUNT_ENUM("prescaler", &ftm_quaddec_prescaler_enum),
-	COUNTER_COUNT_ENUM_AVAILABLE("prescaler", &ftm_quaddec_prescaler_enum),
+static DEFINE_COUNTER_ENUM(ftm_quaddec_prescaler_enum, ftm_quaddec_prescaler);
+
+static struct counter_comp ftm_quaddec_count_ext[] = {
+	COUNTER_COMP_COUNT_ENUM("prescaler", ftm_quaddec_get_prescaler,
+				ftm_quaddec_set_prescaler,
+				ftm_quaddec_prescaler_enum),
 };
 
 static struct counter_count ftm_quaddec_counts = {
diff --git a/drivers/counter/intel-qep.c b/drivers/counter/intel-qep.c
index 8a6847d5fb2b..696f68bc93bc 100644
--- a/drivers/counter/intel-qep.c
+++ b/drivers/counter/intel-qep.c
@@ -62,13 +62,6 @@
 
 #define INTEL_QEP_CLK_PERIOD_NS		10
 
-#define INTEL_QEP_COUNTER_EXT_RW(_name)				\
-{								\
-	.name = #_name,						\
-	.read = _name##_read,					\
-	.write = _name##_write,					\
-}
-
 struct intel_qep {
 	struct counter_device counter;
 	struct mutex lock;
@@ -114,8 +107,7 @@ static void intel_qep_init(struct intel_qep *qep)
 }
 
 static int intel_qep_count_read(struct counter_device *counter,
-				struct counter_count *count,
-				unsigned long *val)
+				struct counter_count *count, u64 *val)
 {
 	struct intel_qep *const qep = counter->priv;
 
@@ -130,11 +122,11 @@ static const enum counter_function intel_qep_count_functions[] = {
 	COUNTER_FUNCTION_QUADRATURE_X4,
 };
 
-static int intel_qep_function_get(struct counter_device *counter,
-				  struct counter_count *count,
-				  size_t *function)
+static int intel_qep_function_read(struct counter_device *counter,
+				   struct counter_count *count,
+				   enum counter_function *function)
 {
-	*function = 0;
+	*function = COUNTER_FUNCTION_QUADRATURE_X4;
 
 	return 0;
 }
@@ -143,19 +135,19 @@ static const enum counter_synapse_action intel_qep_synapse_actions[] = {
 	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
 };
 
-static int intel_qep_action_get(struct counter_device *counter,
-				struct counter_count *count,
-				struct counter_synapse *synapse,
-				size_t *action)
+static int intel_qep_action_read(struct counter_device *counter,
+				 struct counter_count *count,
+				 struct counter_synapse *synapse,
+				 enum counter_synapse_action *action)
 {
-	*action = 0;
+	*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
 	return 0;
 }
 
 static const struct counter_ops intel_qep_counter_ops = {
 	.count_read = intel_qep_count_read,
-	.function_get = intel_qep_function_get,
-	.action_get = intel_qep_action_get,
+	.function_read = intel_qep_function_read,
+	.action_read = intel_qep_action_read,
 };
 
 #define INTEL_QEP_SIGNAL(_id, _name) {				\
@@ -181,31 +173,27 @@ static struct counter_synapse intel_qep_count_synapses[] = {
 	INTEL_QEP_SYNAPSE(2),
 };
 
-static ssize_t ceiling_read(struct counter_device *counter,
-			    struct counter_count *count,
-			    void *priv, char *buf)
+static int intel_qep_ceiling_read(struct counter_device *counter,
+				  struct counter_count *count, u64 *ceiling)
 {
 	struct intel_qep *qep = counter->priv;
-	u32 reg;
 
 	pm_runtime_get_sync(qep->dev);
-	reg = intel_qep_readl(qep, INTEL_QEPMAX);
+	*ceiling = intel_qep_readl(qep, INTEL_QEPMAX);
 	pm_runtime_put(qep->dev);
 
-	return sysfs_emit(buf, "%u\n", reg);
+	return 0;
 }
 
-static ssize_t ceiling_write(struct counter_device *counter,
-			     struct counter_count *count,
-			     void *priv, const char *buf, size_t len)
+static int intel_qep_ceiling_write(struct counter_device *counter,
+				   struct counter_count *count, u64 max)
 {
 	struct intel_qep *qep = counter->priv;
-	u32 max;
-	int ret;
+	int ret = 0;
 
-	ret = kstrtou32(buf, 0, &max);
-	if (ret < 0)
-		return ret;
+	/* Intel QEP ceiling configuration only supports 32-bit values */
+	if (max != (u32)max)
+		return -ERANGE;
 
 	mutex_lock(&qep->lock);
 	if (qep->enabled) {
@@ -216,34 +204,28 @@ static ssize_t ceiling_write(struct counter_device *counter,
 	pm_runtime_get_sync(qep->dev);
 	intel_qep_writel(qep, INTEL_QEPMAX, max);
 	pm_runtime_put(qep->dev);
-	ret = len;
 
 out:
 	mutex_unlock(&qep->lock);
 	return ret;
 }
 
-static ssize_t enable_read(struct counter_device *counter,
-			   struct counter_count *count,
-			   void *priv, char *buf)
+static int intel_qep_enable_read(struct counter_device *counter,
+				 struct counter_count *count, u8 *enable)
 {
 	struct intel_qep *qep = counter->priv;
 
-	return sysfs_emit(buf, "%u\n", qep->enabled);
+	*enable = qep->enabled;
+
+	return 0;
 }
 
-static ssize_t enable_write(struct counter_device *counter,
-			    struct counter_count *count,
-			    void *priv, const char *buf, size_t len)
+static int intel_qep_enable_write(struct counter_device *counter,
+				  struct counter_count *count, u8 val)
 {
 	struct intel_qep *qep = counter->priv;
 	u32 reg;
-	bool val, changed;
-	int ret;
-
-	ret = kstrtobool(buf, &val);
-	if (ret)
-		return ret;
+	bool changed;
 
 	mutex_lock(&qep->lock);
 	changed = val ^ qep->enabled;
@@ -267,12 +249,12 @@ static ssize_t enable_write(struct counter_device *counter,
 
 out:
 	mutex_unlock(&qep->lock);
-	return len;
+	return 0;
 }
 
-static ssize_t spike_filter_ns_read(struct counter_device *counter,
-				    struct counter_count *count,
-				    void *priv, char *buf)
+static int intel_qep_spike_filter_ns_read(struct counter_device *counter,
+					  struct counter_count *count,
+					  u64 *length)
 {
 	struct intel_qep *qep = counter->priv;
 	u32 reg;
@@ -281,26 +263,24 @@ static ssize_t spike_filter_ns_read(struct counter_device *counter,
 	reg = intel_qep_readl(qep, INTEL_QEPCON);
 	if (!(reg & INTEL_QEPCON_FLT_EN)) {
 		pm_runtime_put(qep->dev);
-		return sysfs_emit(buf, "0\n");
+		return 0;
 	}
 	reg = INTEL_QEPFLT_MAX_COUNT(intel_qep_readl(qep, INTEL_QEPFLT));
 	pm_runtime_put(qep->dev);
 
-	return sysfs_emit(buf, "%u\n", (reg + 2) * INTEL_QEP_CLK_PERIOD_NS);
+	*length = (reg + 2) * INTEL_QEP_CLK_PERIOD_NS;
+
+	return 0;
 }
 
-static ssize_t spike_filter_ns_write(struct counter_device *counter,
-				     struct counter_count *count,
-				     void *priv, const char *buf, size_t len)
+static int intel_qep_spike_filter_ns_write(struct counter_device *counter,
+					   struct counter_count *count,
+					   u64 length)
 {
 	struct intel_qep *qep = counter->priv;
-	u32 reg, length;
+	u32 reg;
 	bool enable;
-	int ret;
-
-	ret = kstrtou32(buf, 0, &length);
-	if (ret < 0)
-		return ret;
+	int ret = 0;
 
 	/*
 	 * Spike filter length is (MAX_COUNT + 2) clock periods.
@@ -336,16 +316,15 @@ static ssize_t spike_filter_ns_write(struct counter_device *counter,
 	intel_qep_writel(qep, INTEL_QEPFLT, length);
 	intel_qep_writel(qep, INTEL_QEPCON, reg);
 	pm_runtime_put(qep->dev);
-	ret = len;
 
 out:
 	mutex_unlock(&qep->lock);
 	return ret;
 }
 
-static ssize_t preset_enable_read(struct counter_device *counter,
-				  struct counter_count *count,
-				  void *priv, char *buf)
+static int intel_qep_preset_enable_read(struct counter_device *counter,
+					struct counter_count *count,
+					u8 *preset_enable)
 {
 	struct intel_qep *qep = counter->priv;
 	u32 reg;
@@ -353,21 +332,18 @@ static ssize_t preset_enable_read(struct counter_device *counter,
 	pm_runtime_get_sync(qep->dev);
 	reg = intel_qep_readl(qep, INTEL_QEPCON);
 	pm_runtime_put(qep->dev);
-	return sysfs_emit(buf, "%u\n", !(reg & INTEL_QEPCON_COUNT_RST_MODE));
+
+	*preset_enable = !(reg & INTEL_QEPCON_COUNT_RST_MODE);
+
+	return 0;
 }
 
-static ssize_t preset_enable_write(struct counter_device *counter,
-				   struct counter_count *count,
-				   void *priv, const char *buf, size_t len)
+static int intel_qep_preset_enable_write(struct counter_device *counter,
+					 struct counter_count *count, u8 val)
 {
 	struct intel_qep *qep = counter->priv;
 	u32 reg;
-	bool val;
-	int ret;
-
-	ret = kstrtobool(buf, &val);
-	if (ret)
-		return ret;
+	int ret = 0;
 
 	mutex_lock(&qep->lock);
 	if (qep->enabled) {
@@ -384,7 +360,6 @@ static ssize_t preset_enable_write(struct counter_device *counter,
 
 	intel_qep_writel(qep, INTEL_QEPCON, reg);
 	pm_runtime_put(qep->dev);
-	ret = len;
 
 out:
 	mutex_unlock(&qep->lock);
@@ -392,11 +367,14 @@ static ssize_t preset_enable_write(struct counter_device *counter,
 	return ret;
 }
 
-static const struct counter_count_ext intel_qep_count_ext[] = {
-	INTEL_QEP_COUNTER_EXT_RW(ceiling),
-	INTEL_QEP_COUNTER_EXT_RW(enable),
-	INTEL_QEP_COUNTER_EXT_RW(spike_filter_ns),
-	INTEL_QEP_COUNTER_EXT_RW(preset_enable)
+static struct counter_comp intel_qep_count_ext[] = {
+	COUNTER_COMP_ENABLE(intel_qep_enable_read, intel_qep_enable_write),
+	COUNTER_COMP_CEILING(intel_qep_ceiling_read, intel_qep_ceiling_write),
+	COUNTER_COMP_PRESET_ENABLE(intel_qep_preset_enable_read,
+				   intel_qep_preset_enable_write),
+	COUNTER_COMP_COUNT_U64("spike_filter_ns",
+			       intel_qep_spike_filter_ns_read,
+			       intel_qep_spike_filter_ns_write),
 };
 
 static struct counter_count intel_qep_counter_count[] = {
diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c
index 1de4243db488..8514a87fcbee 100644
--- a/drivers/counter/interrupt-cnt.c
+++ b/drivers/counter/interrupt-cnt.c
@@ -10,6 +10,7 @@
 #include <linux/mod_devicetable.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/types.h>
 
 #define INTERRUPT_CNT_NAME "interrupt-cnt"
 
@@ -33,30 +34,23 @@ static irqreturn_t interrupt_cnt_isr(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
-static ssize_t interrupt_cnt_enable_read(struct counter_device *counter,
-					 struct counter_count *count,
-					 void *private, char *buf)
+static int interrupt_cnt_enable_read(struct counter_device *counter,
+				     struct counter_count *count, u8 *enable)
 {
 	struct interrupt_cnt_priv *priv = counter->priv;
 
-	return sysfs_emit(buf, "%d\n", priv->enabled);
+	*enable = priv->enabled;
+
+	return 0;
 }
 
-static ssize_t interrupt_cnt_enable_write(struct counter_device *counter,
-					  struct counter_count *count,
-					  void *private, const char *buf,
-					  size_t len)
+static int interrupt_cnt_enable_write(struct counter_device *counter,
+				      struct counter_count *count, u8 enable)
 {
 	struct interrupt_cnt_priv *priv = counter->priv;
-	bool enable;
-	ssize_t ret;
-
-	ret = kstrtobool(buf, &enable);
-	if (ret)
-		return ret;
 
 	if (priv->enabled == enable)
-		return len;
+		return 0;
 
 	if (enable) {
 		priv->enabled = true;
@@ -66,33 +60,30 @@ static ssize_t interrupt_cnt_enable_write(struct counter_device *counter,
 		priv->enabled = false;
 	}
 
-	return len;
+	return 0;
 }
 
-static const struct counter_count_ext interrupt_cnt_ext[] = {
-	{
-		.name = "enable",
-		.read = interrupt_cnt_enable_read,
-		.write = interrupt_cnt_enable_write,
-	},
+static struct counter_comp interrupt_cnt_ext[] = {
+	COUNTER_COMP_ENABLE(interrupt_cnt_enable_read,
+			    interrupt_cnt_enable_write),
 };
 
 static const enum counter_synapse_action interrupt_cnt_synapse_actions[] = {
 	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
 };
 
-static int interrupt_cnt_action_get(struct counter_device *counter,
-				    struct counter_count *count,
-				    struct counter_synapse *synapse,
-				    size_t *action)
+static int interrupt_cnt_action_read(struct counter_device *counter,
+				     struct counter_count *count,
+				     struct counter_synapse *synapse,
+				     enum counter_synapse_action *action)
 {
-	*action = 0;
+	*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
 
 	return 0;
 }
 
 static int interrupt_cnt_read(struct counter_device *counter,
-			      struct counter_count *count, unsigned long *val)
+			      struct counter_count *count, u64 *val)
 {
 	struct interrupt_cnt_priv *priv = counter->priv;
 
@@ -102,8 +93,7 @@ static int interrupt_cnt_read(struct counter_device *counter,
 }
 
 static int interrupt_cnt_write(struct counter_device *counter,
-			       struct counter_count *count,
-			       const unsigned long val)
+			       struct counter_count *count, const u64 val)
 {
 	struct interrupt_cnt_priv *priv = counter->priv;
 
@@ -119,11 +109,11 @@ static const enum counter_function interrupt_cnt_functions[] = {
 	COUNTER_FUNCTION_INCREASE,
 };
 
-static int interrupt_cnt_function_get(struct counter_device *counter,
-				      struct counter_count *count,
-				      size_t *function)
+static int interrupt_cnt_function_read(struct counter_device *counter,
+				       struct counter_count *count,
+				       enum counter_function *function)
 {
-	*function = 0;
+	*function = COUNTER_FUNCTION_INCREASE;
 
 	return 0;
 }
@@ -148,10 +138,10 @@ static int interrupt_cnt_signal_read(struct counter_device *counter,
 }
 
 static const struct counter_ops interrupt_cnt_ops = {
-	.action_get = interrupt_cnt_action_get,
+	.action_read = interrupt_cnt_action_read,
 	.count_read = interrupt_cnt_read,
 	.count_write = interrupt_cnt_write,
-	.function_get = interrupt_cnt_function_get,
+	.function_read = interrupt_cnt_function_read,
 	.signal_read  = interrupt_cnt_signal_read,
 };
 
diff --git a/drivers/counter/microchip-tcb-capture.c b/drivers/counter/microchip-tcb-capture.c
index 1aa70b9c4833..79e0c84a3b81 100644
--- a/drivers/counter/microchip-tcb-capture.c
+++ b/drivers/counter/microchip-tcb-capture.c
@@ -32,28 +32,16 @@ struct mchp_tc_data {
 	bool trig_inverted;
 };
 
-enum mchp_tc_count_function {
-	MCHP_TC_FUNCTION_INCREASE,
-	MCHP_TC_FUNCTION_QUADRATURE,
-};
-
 static const enum counter_function mchp_tc_count_functions[] = {
-	[MCHP_TC_FUNCTION_INCREASE] = COUNTER_FUNCTION_INCREASE,
-	[MCHP_TC_FUNCTION_QUADRATURE] = COUNTER_FUNCTION_QUADRATURE_X4,
-};
-
-enum mchp_tc_synapse_action {
-	MCHP_TC_SYNAPSE_ACTION_NONE = 0,
-	MCHP_TC_SYNAPSE_ACTION_RISING_EDGE,
-	MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE,
-	MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE
+	COUNTER_FUNCTION_INCREASE,
+	COUNTER_FUNCTION_QUADRATURE_X4,
 };
 
 static const enum counter_synapse_action mchp_tc_synapse_actions[] = {
-	[MCHP_TC_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
-	[MCHP_TC_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
-	[MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
-	[MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
+	COUNTER_SYNAPSE_ACTION_NONE,
+	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
+	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
+	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
 };
 
 static struct counter_signal mchp_tc_count_signals[] = {
@@ -80,23 +68,23 @@ static struct counter_synapse mchp_tc_count_synapses[] = {
 	}
 };
 
-static int mchp_tc_count_function_get(struct counter_device *counter,
-				      struct counter_count *count,
-				      size_t *function)
+static int mchp_tc_count_function_read(struct counter_device *counter,
+				       struct counter_count *count,
+				       enum counter_function *function)
 {
 	struct mchp_tc_data *const priv = counter->priv;
 
 	if (priv->qdec_mode)
-		*function = MCHP_TC_FUNCTION_QUADRATURE;
+		*function = COUNTER_FUNCTION_QUADRATURE_X4;
 	else
-		*function = MCHP_TC_FUNCTION_INCREASE;
+		*function = COUNTER_FUNCTION_INCREASE;
 
 	return 0;
 }
 
-static int mchp_tc_count_function_set(struct counter_device *counter,
-				      struct counter_count *count,
-				      size_t function)
+static int mchp_tc_count_function_write(struct counter_device *counter,
+					struct counter_count *count,
+					enum counter_function function)
 {
 	struct mchp_tc_data *const priv = counter->priv;
 	u32 bmr, cmr;
@@ -108,7 +96,7 @@ static int mchp_tc_count_function_set(struct counter_device *counter,
 	cmr &= ~ATMEL_TC_WAVE;
 
 	switch (function) {
-	case MCHP_TC_FUNCTION_INCREASE:
+	case COUNTER_FUNCTION_INCREASE:
 		priv->qdec_mode = 0;
 		/* Set highest rate based on whether soc has gclk or not */
 		bmr &= ~(ATMEL_TC_QDEN | ATMEL_TC_POSEN);
@@ -120,7 +108,7 @@ static int mchp_tc_count_function_set(struct counter_device *counter,
 		cmr |=  ATMEL_TC_CMR_MASK;
 		cmr &= ~(ATMEL_TC_ABETRG | ATMEL_TC_XC0);
 		break;
-	case MCHP_TC_FUNCTION_QUADRATURE:
+	case COUNTER_FUNCTION_QUADRATURE_X4:
 		if (!priv->tc_cfg->has_qdec)
 			return -EINVAL;
 		/* In QDEC mode settings both channels 0 and 1 are required */
@@ -176,10 +164,10 @@ static int mchp_tc_count_signal_read(struct counter_device *counter,
 	return 0;
 }
 
-static int mchp_tc_count_action_get(struct counter_device *counter,
-				    struct counter_count *count,
-				    struct counter_synapse *synapse,
-				    size_t *action)
+static int mchp_tc_count_action_read(struct counter_device *counter,
+				     struct counter_count *count,
+				     struct counter_synapse *synapse,
+				     enum counter_synapse_action *action)
 {
 	struct mchp_tc_data *const priv = counter->priv;
 	u32 cmr;
@@ -188,26 +176,26 @@ static int mchp_tc_count_action_get(struct counter_device *counter,
 
 	switch (cmr & ATMEL_TC_ETRGEDG) {
 	default:
-		*action = MCHP_TC_SYNAPSE_ACTION_NONE;
+		*action = COUNTER_SYNAPSE_ACTION_NONE;
 		break;
 	case ATMEL_TC_ETRGEDG_RISING:
-		*action = MCHP_TC_SYNAPSE_ACTION_RISING_EDGE;
+		*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
 		break;
 	case ATMEL_TC_ETRGEDG_FALLING:
-		*action = MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE;
+		*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
 		break;
 	case ATMEL_TC_ETRGEDG_BOTH:
-		*action = MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE;
+		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
 		break;
 	}
 
 	return 0;
 }
 
-static int mchp_tc_count_action_set(struct counter_device *counter,
-				    struct counter_count *count,
-				    struct counter_synapse *synapse,
-				    size_t action)
+static int mchp_tc_count_action_write(struct counter_device *counter,
+				      struct counter_count *count,
+				      struct counter_synapse *synapse,
+				      enum counter_synapse_action action)
 {
 	struct mchp_tc_data *const priv = counter->priv;
 	u32 edge = ATMEL_TC_ETRGEDG_NONE;
@@ -217,16 +205,16 @@ static int mchp_tc_count_action_set(struct counter_device *counter,
 		return -EINVAL;
 
 	switch (action) {
-	case MCHP_TC_SYNAPSE_ACTION_NONE:
+	case COUNTER_SYNAPSE_ACTION_NONE:
 		edge = ATMEL_TC_ETRGEDG_NONE;
 		break;
-	case MCHP_TC_SYNAPSE_ACTION_RISING_EDGE:
+	case COUNTER_SYNAPSE_ACTION_RISING_EDGE:
 		edge = ATMEL_TC_ETRGEDG_RISING;
 		break;
-	case MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE:
+	case COUNTER_SYNAPSE_ACTION_FALLING_EDGE:
 		edge = ATMEL_TC_ETRGEDG_FALLING;
 		break;
-	case MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE:
+	case COUNTER_SYNAPSE_ACTION_BOTH_EDGES:
 		edge = ATMEL_TC_ETRGEDG_BOTH;
 		break;
 	default:
@@ -240,8 +228,7 @@ static int mchp_tc_count_action_set(struct counter_device *counter,
 }
 
 static int mchp_tc_count_read(struct counter_device *counter,
-			      struct counter_count *count,
-			      unsigned long *val)
+			      struct counter_count *count, u64 *val)
 {
 	struct mchp_tc_data *const priv = counter->priv;
 	u32 cnt;
@@ -264,12 +251,12 @@ static struct counter_count mchp_tc_counts[] = {
 };
 
 static const struct counter_ops mchp_tc_ops = {
-	.signal_read  = mchp_tc_count_signal_read,
-	.count_read   = mchp_tc_count_read,
-	.function_get = mchp_tc_count_function_get,
-	.function_set = mchp_tc_count_function_set,
-	.action_get   = mchp_tc_count_action_get,
-	.action_set   = mchp_tc_count_action_set
+	.signal_read    = mchp_tc_count_signal_read,
+	.count_read     = mchp_tc_count_read,
+	.function_read  = mchp_tc_count_function_read,
+	.function_write = mchp_tc_count_function_write,
+	.action_read    = mchp_tc_count_action_read,
+	.action_write   = mchp_tc_count_action_write
 };
 
 static const struct atmel_tcb_config tcb_rm9200_config = {
diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c
index 7367f46c6f91..5168833b1fdf 100644
--- a/drivers/counter/stm32-lptimer-cnt.c
+++ b/drivers/counter/stm32-lptimer-cnt.c
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
+#include <linux/types.h>
 
 struct stm32_lptim_cnt {
 	struct counter_device counter;
@@ -107,11 +108,7 @@ static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
 	return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
 }
 
-/**
- * enum stm32_lptim_cnt_function - enumerates LPTimer counter & encoder modes
- * @STM32_LPTIM_COUNTER_INCREASE: up count on IN1 rising, falling or both edges
- * @STM32_LPTIM_ENCODER_BOTH_EDGE: count on both edges (IN1 & IN2 quadrature)
- *
+/*
  * In non-quadrature mode, device counts up on active edge.
  * In quadrature mode, encoder counting scenarios are as follows:
  * +---------+----------+--------------------+--------------------+
@@ -129,33 +126,20 @@ static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
  * | edges   | Low  ->  |   Up     |   Down  |   Down   |   Up    |
  * +---------+----------+----------+---------+----------+---------+
  */
-enum stm32_lptim_cnt_function {
-	STM32_LPTIM_COUNTER_INCREASE,
-	STM32_LPTIM_ENCODER_BOTH_EDGE,
-};
-
 static const enum counter_function stm32_lptim_cnt_functions[] = {
-	[STM32_LPTIM_COUNTER_INCREASE] = COUNTER_FUNCTION_INCREASE,
-	[STM32_LPTIM_ENCODER_BOTH_EDGE] = COUNTER_FUNCTION_QUADRATURE_X4,
-};
-
-enum stm32_lptim_synapse_action {
-	STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE = STM32_LPTIM_CKPOL_RISING_EDGE,
-	STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE = STM32_LPTIM_CKPOL_FALLING_EDGE,
-	STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES = STM32_LPTIM_CKPOL_BOTH_EDGES,
-	STM32_LPTIM_SYNAPSE_ACTION_NONE,
+	COUNTER_FUNCTION_INCREASE,
+	COUNTER_FUNCTION_QUADRATURE_X4,
 };
 
 static const enum counter_synapse_action stm32_lptim_cnt_synapse_actions[] = {
-	/* Index must match with stm32_lptim_cnt_polarity[] (priv->polarity) */
-	[STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
-	[STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
-	[STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
-	[STM32_LPTIM_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
+	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
+	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
+	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
+	COUNTER_SYNAPSE_ACTION_NONE,
 };
 
 static int stm32_lptim_cnt_read(struct counter_device *counter,
-				struct counter_count *count, unsigned long *val)
+				struct counter_count *count, u64 *val)
 {
 	struct stm32_lptim_cnt *const priv = counter->priv;
 	u32 cnt;
@@ -170,28 +154,28 @@ static int stm32_lptim_cnt_read(struct counter_device *counter,
 	return 0;
 }
 
-static int stm32_lptim_cnt_function_get(struct counter_device *counter,
-					struct counter_count *count,
-					size_t *function)
+static int stm32_lptim_cnt_function_read(struct counter_device *counter,
+					 struct counter_count *count,
+					 enum counter_function *function)
 {
 	struct stm32_lptim_cnt *const priv = counter->priv;
 
 	if (!priv->quadrature_mode) {
-		*function = STM32_LPTIM_COUNTER_INCREASE;
+		*function = COUNTER_FUNCTION_INCREASE;
 		return 0;
 	}
 
-	if (priv->polarity == STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES) {
-		*function = STM32_LPTIM_ENCODER_BOTH_EDGE;
+	if (priv->polarity == STM32_LPTIM_CKPOL_BOTH_EDGES) {
+		*function = COUNTER_FUNCTION_QUADRATURE_X4;
 		return 0;
 	}
 
 	return -EINVAL;
 }
 
-static int stm32_lptim_cnt_function_set(struct counter_device *counter,
-					struct counter_count *count,
-					size_t function)
+static int stm32_lptim_cnt_function_write(struct counter_device *counter,
+					  struct counter_count *count,
+					  enum counter_function function)
 {
 	struct stm32_lptim_cnt *const priv = counter->priv;
 
@@ -199,12 +183,12 @@ static int stm32_lptim_cnt_function_set(struct counter_device *counter,
 		return -EBUSY;
 
 	switch (function) {
-	case STM32_LPTIM_COUNTER_INCREASE:
+	case COUNTER_FUNCTION_INCREASE:
 		priv->quadrature_mode = 0;
 		return 0;
-	case STM32_LPTIM_ENCODER_BOTH_EDGE:
+	case COUNTER_FUNCTION_QUADRATURE_X4:
 		priv->quadrature_mode = 1;
-		priv->polarity = STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES;
+		priv->polarity = STM32_LPTIM_CKPOL_BOTH_EDGES;
 		return 0;
 	default:
 		/* should never reach this path */
@@ -212,9 +196,9 @@ static int stm32_lptim_cnt_function_set(struct counter_device *counter,
 	}
 }
 
-static ssize_t stm32_lptim_cnt_enable_read(struct counter_device *counter,
-					   struct counter_count *count,
-					   void *private, char *buf)
+static int stm32_lptim_cnt_enable_read(struct counter_device *counter,
+				       struct counter_count *count,
+				       u8 *enable)
 {
 	struct stm32_lptim_cnt *const priv = counter->priv;
 	int ret;
@@ -223,22 +207,18 @@ static ssize_t stm32_lptim_cnt_enable_read(struct counter_device *counter,
 	if (ret < 0)
 		return ret;
 
-	return scnprintf(buf, PAGE_SIZE, "%u\n", ret);
+	*enable = ret;
+
+	return 0;
 }
 
-static ssize_t stm32_lptim_cnt_enable_write(struct counter_device *counter,
-					    struct counter_count *count,
-					    void *private,
-					    const char *buf, size_t len)
+static int stm32_lptim_cnt_enable_write(struct counter_device *counter,
+					struct counter_count *count,
+					u8 enable)
 {
 	struct stm32_lptim_cnt *const priv = counter->priv;
-	bool enable;
 	int ret;
 
-	ret = kstrtobool(buf, &enable);
-	if (ret)
-		return ret;
-
 	/* Check nobody uses the timer, or already disabled/enabled */
 	ret = stm32_lptim_is_enabled(priv);
 	if ((ret < 0) || (!ret && !enable))
@@ -254,78 +234,81 @@ static ssize_t stm32_lptim_cnt_enable_write(struct counter_device *counter,
 	if (ret)
 		return ret;
 
-	return len;
+	return 0;
 }
 
-static ssize_t stm32_lptim_cnt_ceiling_read(struct counter_device *counter,
-					    struct counter_count *count,
-					    void *private, char *buf)
+static int stm32_lptim_cnt_ceiling_read(struct counter_device *counter,
+					struct counter_count *count,
+					u64 *ceiling)
 {
 	struct stm32_lptim_cnt *const priv = counter->priv;
 
-	return snprintf(buf, PAGE_SIZE, "%u\n", priv->ceiling);
+	*ceiling = priv->ceiling;
+
+	return 0;
 }
 
-static ssize_t stm32_lptim_cnt_ceiling_write(struct counter_device *counter,
-					     struct counter_count *count,
-					     void *private,
-					     const char *buf, size_t len)
+static int stm32_lptim_cnt_ceiling_write(struct counter_device *counter,
+					 struct counter_count *count,
+					 u64 ceiling)
 {
 	struct stm32_lptim_cnt *const priv = counter->priv;
-	unsigned int ceiling;
-	int ret;
 
 	if (stm32_lptim_is_enabled(priv))
 		return -EBUSY;
 
-	ret = kstrtouint(buf, 0, &ceiling);
-	if (ret)
-		return ret;
-
 	if (ceiling > STM32_LPTIM_MAX_ARR)
 		return -ERANGE;
 
 	priv->ceiling = ceiling;
 
-	return len;
+	return 0;
 }
 
-static const struct counter_count_ext stm32_lptim_cnt_ext[] = {
-	{
-		.name = "enable",
-		.read = stm32_lptim_cnt_enable_read,
-		.write = stm32_lptim_cnt_enable_write
-	},
-	{
-		.name = "ceiling",
-		.read = stm32_lptim_cnt_ceiling_read,
-		.write = stm32_lptim_cnt_ceiling_write
-	},
+static struct counter_comp stm32_lptim_cnt_ext[] = {
+	COUNTER_COMP_ENABLE(stm32_lptim_cnt_enable_read,
+			    stm32_lptim_cnt_enable_write),
+	COUNTER_COMP_CEILING(stm32_lptim_cnt_ceiling_read,
+			     stm32_lptim_cnt_ceiling_write),
 };
 
-static int stm32_lptim_cnt_action_get(struct counter_device *counter,
-				      struct counter_count *count,
-				      struct counter_synapse *synapse,
-				      size_t *action)
+static int stm32_lptim_cnt_action_read(struct counter_device *counter,
+				       struct counter_count *count,
+				       struct counter_synapse *synapse,
+				       enum counter_synapse_action *action)
 {
 	struct stm32_lptim_cnt *const priv = counter->priv;
-	size_t function;
+	enum counter_function function;
 	int err;
 
-	err = stm32_lptim_cnt_function_get(counter, count, &function);
+	err = stm32_lptim_cnt_function_read(counter, count, &function);
 	if (err)
 		return err;
 
 	switch (function) {
-	case STM32_LPTIM_COUNTER_INCREASE:
+	case COUNTER_FUNCTION_INCREASE:
 		/* LP Timer acts as up-counter on input 1 */
-		if (synapse->signal->id == count->synapses[0].signal->id)
-			*action = priv->polarity;
-		else
-			*action = STM32_LPTIM_SYNAPSE_ACTION_NONE;
-		return 0;
-	case STM32_LPTIM_ENCODER_BOTH_EDGE:
-		*action = priv->polarity;
+		if (synapse->signal->id != count->synapses[0].signal->id) {
+			*action = COUNTER_SYNAPSE_ACTION_NONE;
+			return 0;
+		}
+
+		switch (priv->polarity) {
+		case STM32_LPTIM_CKPOL_RISING_EDGE:
+			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
+			return 0;
+		case STM32_LPTIM_CKPOL_FALLING_EDGE:
+			*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
+			return 0;
+		case STM32_LPTIM_CKPOL_BOTH_EDGES:
+			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
+			return 0;
+		default:
+			/* should never reach this path */
+			return -EINVAL;
+		}
+	case COUNTER_FUNCTION_QUADRATURE_X4:
+		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
 		return 0;
 	default:
 		/* should never reach this path */
@@ -333,43 +316,48 @@ static int stm32_lptim_cnt_action_get(struct counter_device *counter,
 	}
 }
 
-static int stm32_lptim_cnt_action_set(struct counter_device *counter,
-				      struct counter_count *count,
-				      struct counter_synapse *synapse,
-				      size_t action)
+static int stm32_lptim_cnt_action_write(struct counter_device *counter,
+					struct counter_count *count,
+					struct counter_synapse *synapse,
+					enum counter_synapse_action action)
 {
 	struct stm32_lptim_cnt *const priv = counter->priv;
-	size_t function;
+	enum counter_function function;
 	int err;
 
 	if (stm32_lptim_is_enabled(priv))
 		return -EBUSY;
 
-	err = stm32_lptim_cnt_function_get(counter, count, &function);
+	err = stm32_lptim_cnt_function_read(counter, count, &function);
 	if (err)
 		return err;
 
 	/* only set polarity when in counter mode (on input 1) */
-	if (function == STM32_LPTIM_COUNTER_INCREASE
-	    && synapse->signal->id == count->synapses[0].signal->id) {
-		switch (action) {
-		case STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE:
-		case STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE:
-		case STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES:
-			priv->polarity = action;
-			return 0;
-		}
-	}
+	if (function != COUNTER_FUNCTION_INCREASE
+	    || synapse->signal->id != count->synapses[0].signal->id)
+		return -EINVAL;
 
-	return -EINVAL;
+	switch (action) {
+	case COUNTER_SYNAPSE_ACTION_RISING_EDGE:
+		priv->polarity = STM32_LPTIM_CKPOL_RISING_EDGE;
+		return 0;
+	case COUNTER_SYNAPSE_ACTION_FALLING_EDGE:
+		priv->polarity = STM32_LPTIM_CKPOL_FALLING_EDGE;
+		return 0;
+	case COUNTER_SYNAPSE_ACTION_BOTH_EDGES:
+		priv->polarity = STM32_LPTIM_CKPOL_BOTH_EDGES;
+		return 0;
+	default:
+		return -EINVAL;
+	}
 }
 
 static const struct counter_ops stm32_lptim_cnt_ops = {
 	.count_read = stm32_lptim_cnt_read,
-	.function_get = stm32_lptim_cnt_function_get,
-	.function_set = stm32_lptim_cnt_function_set,
-	.action_get = stm32_lptim_cnt_action_get,
-	.action_set = stm32_lptim_cnt_action_set,
+	.function_read = stm32_lptim_cnt_function_read,
+	.function_write = stm32_lptim_cnt_function_write,
+	.action_read = stm32_lptim_cnt_action_read,
+	.action_write = stm32_lptim_cnt_action_write,
 };
 
 static struct counter_signal stm32_lptim_cnt_signals[] = {
diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c
index 1fbc46f4ee66..0546e932db0c 100644
--- a/drivers/counter/stm32-timer-cnt.c
+++ b/drivers/counter/stm32-timer-cnt.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
+#include <linux/types.h>
 
 #define TIM_CCMR_CCXS	(BIT(8) | BIT(0))
 #define TIM_CCMR_MASK	(TIM_CCMR_CC1S | TIM_CCMR_CC2S | \
@@ -36,29 +37,15 @@ struct stm32_timer_cnt {
 	struct stm32_timer_regs bak;
 };
 
-/**
- * enum stm32_count_function - enumerates stm32 timer counter encoder modes
- * @STM32_COUNT_SLAVE_MODE_DISABLED: counts on internal clock when CEN=1
- * @STM32_COUNT_ENCODER_MODE_1: counts TI1FP1 edges, depending on TI2FP2 level
- * @STM32_COUNT_ENCODER_MODE_2: counts TI2FP2 edges, depending on TI1FP1 level
- * @STM32_COUNT_ENCODER_MODE_3: counts on both TI1FP1 and TI2FP2 edges
- */
-enum stm32_count_function {
-	STM32_COUNT_SLAVE_MODE_DISABLED,
-	STM32_COUNT_ENCODER_MODE_1,
-	STM32_COUNT_ENCODER_MODE_2,
-	STM32_COUNT_ENCODER_MODE_3,
-};
-
 static const enum counter_function stm32_count_functions[] = {
-	[STM32_COUNT_SLAVE_MODE_DISABLED] = COUNTER_FUNCTION_INCREASE,
-	[STM32_COUNT_ENCODER_MODE_1] = COUNTER_FUNCTION_QUADRATURE_X2_A,
-	[STM32_COUNT_ENCODER_MODE_2] = COUNTER_FUNCTION_QUADRATURE_X2_B,
-	[STM32_COUNT_ENCODER_MODE_3] = COUNTER_FUNCTION_QUADRATURE_X4,
+	COUNTER_FUNCTION_INCREASE,
+	COUNTER_FUNCTION_QUADRATURE_X2_A,
+	COUNTER_FUNCTION_QUADRATURE_X2_B,
+	COUNTER_FUNCTION_QUADRATURE_X4,
 };
 
 static int stm32_count_read(struct counter_device *counter,
-			    struct counter_count *count, unsigned long *val)
+			    struct counter_count *count, u64 *val)
 {
 	struct stm32_timer_cnt *const priv = counter->priv;
 	u32 cnt;
@@ -70,8 +57,7 @@ static int stm32_count_read(struct counter_device *counter,
 }
 
 static int stm32_count_write(struct counter_device *counter,
-			     struct counter_count *count,
-			     const unsigned long val)
+			     struct counter_count *count, const u64 val)
 {
 	struct stm32_timer_cnt *const priv = counter->priv;
 	u32 ceiling;
@@ -83,9 +69,9 @@ static int stm32_count_write(struct counter_device *counter,
 	return regmap_write(priv->regmap, TIM_CNT, val);
 }
 
-static int stm32_count_function_get(struct counter_device *counter,
-				    struct counter_count *count,
-				    size_t *function)
+static int stm32_count_function_read(struct counter_device *counter,
+				     struct counter_count *count,
+				     enum counter_function *function)
 {
 	struct stm32_timer_cnt *const priv = counter->priv;
 	u32 smcr;
@@ -94,40 +80,40 @@ static int stm32_count_function_get(struct counter_device *counter,
 
 	switch (smcr & TIM_SMCR_SMS) {
 	case TIM_SMCR_SMS_SLAVE_MODE_DISABLED:
-		*function = STM32_COUNT_SLAVE_MODE_DISABLED;
+		*function = COUNTER_FUNCTION_INCREASE;
 		return 0;
 	case TIM_SMCR_SMS_ENCODER_MODE_1:
-		*function = STM32_COUNT_ENCODER_MODE_1;
+		*function = COUNTER_FUNCTION_QUADRATURE_X2_A;
 		return 0;
 	case TIM_SMCR_SMS_ENCODER_MODE_2:
-		*function = STM32_COUNT_ENCODER_MODE_2;
+		*function = COUNTER_FUNCTION_QUADRATURE_X2_B;
 		return 0;
 	case TIM_SMCR_SMS_ENCODER_MODE_3:
-		*function = STM32_COUNT_ENCODER_MODE_3;
+		*function = COUNTER_FUNCTION_QUADRATURE_X4;
 		return 0;
 	default:
 		return -EINVAL;
 	}
 }
 
-static int stm32_count_function_set(struct counter_device *counter,
-				    struct counter_count *count,
-				    size_t function)
+static int stm32_count_function_write(struct counter_device *counter,
+				      struct counter_count *count,
+				      enum counter_function function)
 {
 	struct stm32_timer_cnt *const priv = counter->priv;
 	u32 cr1, sms;
 
 	switch (function) {
-	case STM32_COUNT_SLAVE_MODE_DISABLED:
+	case COUNTER_FUNCTION_INCREASE:
 		sms = TIM_SMCR_SMS_SLAVE_MODE_DISABLED;
 		break;
-	case STM32_COUNT_ENCODER_MODE_1:
+	case COUNTER_FUNCTION_QUADRATURE_X2_A:
 		sms = TIM_SMCR_SMS_ENCODER_MODE_1;
 		break;
-	case STM32_COUNT_ENCODER_MODE_2:
+	case COUNTER_FUNCTION_QUADRATURE_X2_B:
 		sms = TIM_SMCR_SMS_ENCODER_MODE_2;
 		break;
-	case STM32_COUNT_ENCODER_MODE_3:
+	case COUNTER_FUNCTION_QUADRATURE_X4:
 		sms = TIM_SMCR_SMS_ENCODER_MODE_3;
 		break;
 	default:
@@ -150,44 +136,37 @@ static int stm32_count_function_set(struct counter_device *counter,
 	return 0;
 }
 
-static ssize_t stm32_count_direction_read(struct counter_device *counter,
+static int stm32_count_direction_read(struct counter_device *counter,
 				      struct counter_count *count,
-				      void *private, char *buf)
+				      enum counter_count_direction *direction)
 {
 	struct stm32_timer_cnt *const priv = counter->priv;
-	const char *direction;
 	u32 cr1;
 
 	regmap_read(priv->regmap, TIM_CR1, &cr1);
-	direction = (cr1 & TIM_CR1_DIR) ? "backward" : "forward";
+	*direction = (cr1 & TIM_CR1_DIR) ? COUNTER_COUNT_DIRECTION_BACKWARD :
+		COUNTER_COUNT_DIRECTION_FORWARD;
 
-	return scnprintf(buf, PAGE_SIZE, "%s\n", direction);
+	return 0;
 }
 
-static ssize_t stm32_count_ceiling_read(struct counter_device *counter,
-					struct counter_count *count,
-					void *private, char *buf)
+static int stm32_count_ceiling_read(struct counter_device *counter,
+				    struct counter_count *count, u64 *ceiling)
 {
 	struct stm32_timer_cnt *const priv = counter->priv;
 	u32 arr;
 
 	regmap_read(priv->regmap, TIM_ARR, &arr);
 
-	return snprintf(buf, PAGE_SIZE, "%u\n", arr);
+	*ceiling = arr;
+
+	return 0;
 }
 
-static ssize_t stm32_count_ceiling_write(struct counter_device *counter,
-					 struct counter_count *count,
-					 void *private,
-					 const char *buf, size_t len)
+static int stm32_count_ceiling_write(struct counter_device *counter,
+				     struct counter_count *count, u64 ceiling)
 {
 	struct stm32_timer_cnt *const priv = counter->priv;
-	unsigned int ceiling;
-	int ret;
-
-	ret = kstrtouint(buf, 0, &ceiling);
-	if (ret)
-		return ret;
 
 	if (ceiling > priv->max_arr)
 		return -ERANGE;
@@ -196,34 +175,27 @@ static ssize_t stm32_count_ceiling_write(struct counter_device *counter,
 	regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, 0);
 	regmap_write(priv->regmap, TIM_ARR, ceiling);
 
-	return len;
+	return 0;
 }
 
-static ssize_t stm32_count_enable_read(struct counter_device *counter,
-				       struct counter_count *count,
-				       void *private, char *buf)
+static int stm32_count_enable_read(struct counter_device *counter,
+				   struct counter_count *count, u8 *enable)
 {
 	struct stm32_timer_cnt *const priv = counter->priv;
 	u32 cr1;
 
 	regmap_read(priv->regmap, TIM_CR1, &cr1);
 
-	return scnprintf(buf, PAGE_SIZE, "%d\n", (bool)(cr1 & TIM_CR1_CEN));
+	*enable = cr1 & TIM_CR1_CEN;
+
+	return 0;
 }
 
-static ssize_t stm32_count_enable_write(struct counter_device *counter,
-					struct counter_count *count,
-					void *private,
-					const char *buf, size_t len)
+static int stm32_count_enable_write(struct counter_device *counter,
+				    struct counter_count *count, u8 enable)
 {
 	struct stm32_timer_cnt *const priv = counter->priv;
-	int err;
 	u32 cr1;
-	bool enable;
-
-	err = kstrtobool(buf, &enable);
-	if (err)
-		return err;
 
 	if (enable) {
 		regmap_read(priv->regmap, TIM_CR1, &cr1);
@@ -242,70 +214,55 @@ static ssize_t stm32_count_enable_write(struct counter_device *counter,
 	/* Keep enabled state to properly handle low power states */
 	priv->enabled = enable;
 
-	return len;
+	return 0;
 }
 
-static const struct counter_count_ext stm32_count_ext[] = {
-	{
-		.name = "direction",
-		.read = stm32_count_direction_read,
-	},
-	{
-		.name = "enable",
-		.read = stm32_count_enable_read,
-		.write = stm32_count_enable_write
-	},
-	{
-		.name = "ceiling",
-		.read = stm32_count_ceiling_read,
-		.write = stm32_count_ceiling_write
-	},
-};
-
-enum stm32_synapse_action {
-	STM32_SYNAPSE_ACTION_NONE,
-	STM32_SYNAPSE_ACTION_BOTH_EDGES
+static struct counter_comp stm32_count_ext[] = {
+	COUNTER_COMP_DIRECTION(stm32_count_direction_read),
+	COUNTER_COMP_ENABLE(stm32_count_enable_read, stm32_count_enable_write),
+	COUNTER_COMP_CEILING(stm32_count_ceiling_read,
+			     stm32_count_ceiling_write),
 };
 
 static const enum counter_synapse_action stm32_synapse_actions[] = {
-	[STM32_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
-	[STM32_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES
+	COUNTER_SYNAPSE_ACTION_NONE,
+	COUNTER_SYNAPSE_ACTION_BOTH_EDGES
 };
 
-static int stm32_action_get(struct counter_device *counter,
-			    struct counter_count *count,
-			    struct counter_synapse *synapse,
-			    size_t *action)
+static int stm32_action_read(struct counter_device *counter,
+			     struct counter_count *count,
+			     struct counter_synapse *synapse,
+			     enum counter_synapse_action *action)
 {
-	size_t function;
+	enum counter_function function;
 	int err;
 
-	err = stm32_count_function_get(counter, count, &function);
+	err = stm32_count_function_read(counter, count, &function);
 	if (err)
 		return err;
 
 	switch (function) {
-	case STM32_COUNT_SLAVE_MODE_DISABLED:
+	case COUNTER_FUNCTION_INCREASE:
 		/* counts on internal clock when CEN=1 */
-		*action = STM32_SYNAPSE_ACTION_NONE;
+		*action = COUNTER_SYNAPSE_ACTION_NONE;
 		return 0;
-	case STM32_COUNT_ENCODER_MODE_1:
+	case COUNTER_FUNCTION_QUADRATURE_X2_A:
 		/* counts up/down on TI1FP1 edge depending on TI2FP2 level */
 		if (synapse->signal->id == count->synapses[0].signal->id)
-			*action = STM32_SYNAPSE_ACTION_BOTH_EDGES;
+			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
 		else
-			*action = STM32_SYNAPSE_ACTION_NONE;
+			*action = COUNTER_SYNAPSE_ACTION_NONE;
 		return 0;
-	case STM32_COUNT_ENCODER_MODE_2:
+	case COUNTER_FUNCTION_QUADRATURE_X2_B:
 		/* counts up/down on TI2FP2 edge depending on TI1FP1 level */
 		if (synapse->signal->id == count->synapses[1].signal->id)
-			*action = STM32_SYNAPSE_ACTION_BOTH_EDGES;
+			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
 		else
-			*action = STM32_SYNAPSE_ACTION_NONE;
+			*action = COUNTER_SYNAPSE_ACTION_NONE;
 		return 0;
-	case STM32_COUNT_ENCODER_MODE_3:
+	case COUNTER_FUNCTION_QUADRATURE_X4:
 		/* counts up/down on both TI1FP1 and TI2FP2 edges */
-		*action = STM32_SYNAPSE_ACTION_BOTH_EDGES;
+		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
 		return 0;
 	default:
 		return -EINVAL;
@@ -315,9 +272,9 @@ static int stm32_action_get(struct counter_device *counter,
 static const struct counter_ops stm32_timer_cnt_ops = {
 	.count_read = stm32_count_read,
 	.count_write = stm32_count_write,
-	.function_get = stm32_count_function_get,
-	.function_set = stm32_count_function_set,
-	.action_get = stm32_action_get,
+	.function_read = stm32_count_function_read,
+	.function_write = stm32_count_function_write,
+	.action_read = stm32_action_read,
 };
 
 static struct counter_signal stm32_signals[] = {
diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c
index 94fe58bb3eab..09817c953f9a 100644
--- a/drivers/counter/ti-eqep.c
+++ b/drivers/counter/ti-eqep.c
@@ -13,6 +13,7 @@
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
+#include <linux/types.h>
 
 /* 32-bit registers */
 #define QPOSCNT		0x0
@@ -73,19 +74,13 @@ enum {
 };
 
 /* Position Counter Input Modes */
-enum {
+enum ti_eqep_count_func {
 	TI_EQEP_COUNT_FUNC_QUAD_COUNT,
 	TI_EQEP_COUNT_FUNC_DIR_COUNT,
 	TI_EQEP_COUNT_FUNC_UP_COUNT,
 	TI_EQEP_COUNT_FUNC_DOWN_COUNT,
 };
 
-enum {
-	TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES,
-	TI_EQEP_SYNAPSE_ACTION_RISING_EDGE,
-	TI_EQEP_SYNAPSE_ACTION_NONE,
-};
-
 struct ti_eqep_cnt {
 	struct counter_device counter;
 	struct regmap *regmap32;
@@ -93,7 +88,7 @@ struct ti_eqep_cnt {
 };
 
 static int ti_eqep_count_read(struct counter_device *counter,
-			      struct counter_count *count, unsigned long *val)
+			      struct counter_count *count, u64 *val)
 {
 	struct ti_eqep_cnt *priv = counter->priv;
 	u32 cnt;
@@ -105,7 +100,7 @@ static int ti_eqep_count_read(struct counter_device *counter,
 }
 
 static int ti_eqep_count_write(struct counter_device *counter,
-			       struct counter_count *count, unsigned long val)
+			       struct counter_count *count, u64 val)
 {
 	struct ti_eqep_cnt *priv = counter->priv;
 	u32 max;
@@ -117,64 +112,100 @@ static int ti_eqep_count_write(struct counter_device *counter,
 	return regmap_write(priv->regmap32, QPOSCNT, val);
 }
 
-static int ti_eqep_function_get(struct counter_device *counter,
-				struct counter_count *count, size_t *function)
+static int ti_eqep_function_read(struct counter_device *counter,
+				 struct counter_count *count,
+				 enum counter_function *function)
 {
 	struct ti_eqep_cnt *priv = counter->priv;
 	u32 qdecctl;
 
 	regmap_read(priv->regmap16, QDECCTL, &qdecctl);
-	*function = (qdecctl & QDECCTL_QSRC) >> QDECCTL_QSRC_SHIFT;
+
+	switch ((qdecctl & QDECCTL_QSRC) >> QDECCTL_QSRC_SHIFT) {
+	case TI_EQEP_COUNT_FUNC_QUAD_COUNT:
+		*function = COUNTER_FUNCTION_QUADRATURE_X4;
+		break;
+	case TI_EQEP_COUNT_FUNC_DIR_COUNT:
+		*function = COUNTER_FUNCTION_PULSE_DIRECTION;
+		break;
+	case TI_EQEP_COUNT_FUNC_UP_COUNT:
+		*function = COUNTER_FUNCTION_INCREASE;
+		break;
+	case TI_EQEP_COUNT_FUNC_DOWN_COUNT:
+		*function = COUNTER_FUNCTION_DECREASE;
+		break;
+	}
 
 	return 0;
 }
 
-static int ti_eqep_function_set(struct counter_device *counter,
-				struct counter_count *count, size_t function)
+static int ti_eqep_function_write(struct counter_device *counter,
+				  struct counter_count *count,
+				  enum counter_function function)
 {
 	struct ti_eqep_cnt *priv = counter->priv;
+	enum ti_eqep_count_func qsrc;
+
+	switch (function) {
+	case COUNTER_FUNCTION_QUADRATURE_X4:
+		qsrc = TI_EQEP_COUNT_FUNC_QUAD_COUNT;
+		break;
+	case COUNTER_FUNCTION_PULSE_DIRECTION:
+		qsrc = TI_EQEP_COUNT_FUNC_DIR_COUNT;
+		break;
+	case COUNTER_FUNCTION_INCREASE:
+		qsrc = TI_EQEP_COUNT_FUNC_UP_COUNT;
+		break;
+	case COUNTER_FUNCTION_DECREASE:
+		qsrc = TI_EQEP_COUNT_FUNC_DOWN_COUNT;
+		break;
+	default:
+		/* should never reach this path */
+		return -EINVAL;
+	}
 
 	return regmap_write_bits(priv->regmap16, QDECCTL, QDECCTL_QSRC,
-				 function << QDECCTL_QSRC_SHIFT);
+				 qsrc << QDECCTL_QSRC_SHIFT);
 }
 
-static int ti_eqep_action_get(struct counter_device *counter,
-			      struct counter_count *count,
-			      struct counter_synapse *synapse, size_t *action)
+static int ti_eqep_action_read(struct counter_device *counter,
+			       struct counter_count *count,
+			       struct counter_synapse *synapse,
+			       enum counter_synapse_action *action)
 {
 	struct ti_eqep_cnt *priv = counter->priv;
-	size_t function;
+	enum counter_function function;
 	u32 qdecctl;
 	int err;
 
-	err = ti_eqep_function_get(counter, count, &function);
+	err = ti_eqep_function_read(counter, count, &function);
 	if (err)
 		return err;
 
 	switch (function) {
-	case TI_EQEP_COUNT_FUNC_QUAD_COUNT:
+	case COUNTER_FUNCTION_QUADRATURE_X4:
 		/* In quadrature mode, the rising and falling edge of both
 		 * QEPA and QEPB trigger QCLK.
 		 */
-		*action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES;
+		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
 		return 0;
-	case TI_EQEP_COUNT_FUNC_DIR_COUNT:
+	case COUNTER_FUNCTION_PULSE_DIRECTION:
 		/* In direction-count mode only rising edge of QEPA is counted
 		 * and QEPB gives direction.
 		 */
 		switch (synapse->signal->id) {
 		case TI_EQEP_SIGNAL_QEPA:
-			*action = TI_EQEP_SYNAPSE_ACTION_RISING_EDGE;
+			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
 			return 0;
 		case TI_EQEP_SIGNAL_QEPB:
-			*action = TI_EQEP_SYNAPSE_ACTION_NONE;
+			*action = COUNTER_SYNAPSE_ACTION_NONE;
 			return 0;
 		default:
 			/* should never reach this path */
 			return -EINVAL;
 		}
-	case TI_EQEP_COUNT_FUNC_UP_COUNT:
-	case TI_EQEP_COUNT_FUNC_DOWN_COUNT:
+	case COUNTER_FUNCTION_INCREASE:
+	case COUNTER_FUNCTION_DECREASE:
 		/* In up/down-count modes only QEPA is counted and QEPB is not
 		 * used.
 		 */
@@ -185,12 +216,12 @@ static int ti_eqep_action_get(struct counter_device *counter,
 				return err;
 
 			if (qdecctl & QDECCTL_XCR)
-				*action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES;
+				*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
 			else
-				*action = TI_EQEP_SYNAPSE_ACTION_RISING_EDGE;
+				*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
 			return 0;
 		case TI_EQEP_SIGNAL_QEPB:
-			*action = TI_EQEP_SYNAPSE_ACTION_NONE;
+			*action = COUNTER_SYNAPSE_ACTION_NONE;
 			return 0;
 		default:
 			/* should never reach this path */
@@ -205,82 +236,67 @@ static int ti_eqep_action_get(struct counter_device *counter,
 static const struct counter_ops ti_eqep_counter_ops = {
 	.count_read	= ti_eqep_count_read,
 	.count_write	= ti_eqep_count_write,
-	.function_get	= ti_eqep_function_get,
-	.function_set	= ti_eqep_function_set,
-	.action_get	= ti_eqep_action_get,
+	.function_read	= ti_eqep_function_read,
+	.function_write	= ti_eqep_function_write,
+	.action_read	= ti_eqep_action_read,
 };
 
-static ssize_t ti_eqep_position_ceiling_read(struct counter_device *counter,
-					     struct counter_count *count,
-					     void *ext_priv, char *buf)
+static int ti_eqep_position_ceiling_read(struct counter_device *counter,
+					 struct counter_count *count,
+					 u64 *ceiling)
 {
 	struct ti_eqep_cnt *priv = counter->priv;
 	u32 qposmax;
 
 	regmap_read(priv->regmap32, QPOSMAX, &qposmax);
 
-	return sprintf(buf, "%u\n", qposmax);
+	*ceiling = qposmax;
+
+	return 0;
 }
 
-static ssize_t ti_eqep_position_ceiling_write(struct counter_device *counter,
-					      struct counter_count *count,
-					      void *ext_priv, const char *buf,
-					      size_t len)
+static int ti_eqep_position_ceiling_write(struct counter_device *counter,
+					  struct counter_count *count,
+					  u64 ceiling)
 {
 	struct ti_eqep_cnt *priv = counter->priv;
-	int err;
-	u32 res;
 
-	err = kstrtouint(buf, 0, &res);
-	if (err < 0)
-		return err;
+	if (ceiling != (u32)ceiling)
+		return -ERANGE;
 
-	regmap_write(priv->regmap32, QPOSMAX, res);
+	regmap_write(priv->regmap32, QPOSMAX, ceiling);
 
-	return len;
+	return 0;
 }
 
-static ssize_t ti_eqep_position_enable_read(struct counter_device *counter,
-					    struct counter_count *count,
-					    void *ext_priv, char *buf)
+static int ti_eqep_position_enable_read(struct counter_device *counter,
+					struct counter_count *count, u8 *enable)
 {
 	struct ti_eqep_cnt *priv = counter->priv;
 	u32 qepctl;
 
 	regmap_read(priv->regmap16, QEPCTL, &qepctl);
 
-	return sprintf(buf, "%u\n", !!(qepctl & QEPCTL_PHEN));
+	*enable = !!(qepctl & QEPCTL_PHEN);
+
+	return 0;
 }
 
-static ssize_t ti_eqep_position_enable_write(struct counter_device *counter,
-					     struct counter_count *count,
-					     void *ext_priv, const char *buf,
-					     size_t len)
+static int ti_eqep_position_enable_write(struct counter_device *counter,
+					 struct counter_count *count, u8 enable)
 {
 	struct ti_eqep_cnt *priv = counter->priv;
-	int err;
-	bool res;
-
-	err = kstrtobool(buf, &res);
-	if (err < 0)
-		return err;
 
-	regmap_write_bits(priv->regmap16, QEPCTL, QEPCTL_PHEN, res ? -1 : 0);
+	regmap_write_bits(priv->regmap16, QEPCTL, QEPCTL_PHEN, enable ? -1 : 0);
 
-	return len;
+	return 0;
 }
 
-static struct counter_count_ext ti_eqep_position_ext[] = {
-	{
-		.name	= "ceiling",
-		.read	= ti_eqep_position_ceiling_read,
-		.write	= ti_eqep_position_ceiling_write,
-	},
-	{
-		.name	= "enable",
-		.read	= ti_eqep_position_enable_read,
-		.write	= ti_eqep_position_enable_write,
-	},
+static struct counter_comp ti_eqep_position_ext[] = {
+	COUNTER_COMP_CEILING(ti_eqep_position_ceiling_read,
+			     ti_eqep_position_ceiling_write),
+	COUNTER_COMP_ENABLE(ti_eqep_position_enable_read,
+			    ti_eqep_position_enable_write),
 };
 
 static struct counter_signal ti_eqep_signals[] = {
@@ -295,16 +311,16 @@ static struct counter_signal ti_eqep_signals[] = {
 };
 
 static const enum counter_function ti_eqep_position_functions[] = {
-	[TI_EQEP_COUNT_FUNC_QUAD_COUNT]	= COUNTER_FUNCTION_QUADRATURE_X4,
-	[TI_EQEP_COUNT_FUNC_DIR_COUNT]	= COUNTER_FUNCTION_PULSE_DIRECTION,
-	[TI_EQEP_COUNT_FUNC_UP_COUNT]	= COUNTER_FUNCTION_INCREASE,
-	[TI_EQEP_COUNT_FUNC_DOWN_COUNT]	= COUNTER_FUNCTION_DECREASE,
+	COUNTER_FUNCTION_QUADRATURE_X4,
+	COUNTER_FUNCTION_PULSE_DIRECTION,
+	COUNTER_FUNCTION_INCREASE,
+	COUNTER_FUNCTION_DECREASE,
 };
 
 static const enum counter_synapse_action ti_eqep_position_synapse_actions[] = {
-	[TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES]	= COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
-	[TI_EQEP_SYNAPSE_ACTION_RISING_EDGE]	= COUNTER_SYNAPSE_ACTION_RISING_EDGE,
-	[TI_EQEP_SYNAPSE_ACTION_NONE]		= COUNTER_SYNAPSE_ACTION_NONE,
+	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
+	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
+	COUNTER_SYNAPSE_ACTION_NONE,
 };
 
 static struct counter_synapse ti_eqep_position_synapses[] = {
diff --git a/include/linux/counter.h b/include/linux/counter.h
index d16ce2819b48..b69277f5c4c5 100644
--- a/include/linux/counter.h
+++ b/include/linux/counter.h
@@ -6,42 +6,184 @@
 #ifndef _COUNTER_H_
 #define _COUNTER_H_
 
-#include <linux/counter_enum.h>
 #include <linux/device.h>
+#include <linux/kernel.h>
 #include <linux/types.h>
 
+struct counter_device;
+struct counter_count;
+struct counter_synapse;
+struct counter_signal;
+
+enum counter_comp_type {
+	COUNTER_COMP_U8,
+	COUNTER_COMP_U64,
+	COUNTER_COMP_BOOL,
+	COUNTER_COMP_SIGNAL_LEVEL,
+	COUNTER_COMP_FUNCTION,
+	COUNTER_COMP_SYNAPSE_ACTION,
+	COUNTER_COMP_ENUM,
+	COUNTER_COMP_COUNT_DIRECTION,
+	COUNTER_COMP_COUNT_MODE,
+};
+
+enum counter_scope {
+	COUNTER_SCOPE_DEVICE,
+	COUNTER_SCOPE_SIGNAL,
+	COUNTER_SCOPE_COUNT,
+};
+
 enum counter_count_direction {
-	COUNTER_COUNT_DIRECTION_FORWARD = 0,
-	COUNTER_COUNT_DIRECTION_BACKWARD
+	COUNTER_COUNT_DIRECTION_FORWARD,
+	COUNTER_COUNT_DIRECTION_BACKWARD,
 };
-extern const char *const counter_count_direction_str[2];
 
 enum counter_count_mode {
-	COUNTER_COUNT_MODE_NORMAL = 0,
+	COUNTER_COUNT_MODE_NORMAL,
 	COUNTER_COUNT_MODE_RANGE_LIMIT,
 	COUNTER_COUNT_MODE_NON_RECYCLE,
-	COUNTER_COUNT_MODE_MODULO_N
+	COUNTER_COUNT_MODE_MODULO_N,
 };
-extern const char *const counter_count_mode_str[4];
 
-struct counter_device;
-struct counter_signal;
+enum counter_function {
+	COUNTER_FUNCTION_INCREASE,
+	COUNTER_FUNCTION_DECREASE,
+	COUNTER_FUNCTION_PULSE_DIRECTION,
+	COUNTER_FUNCTION_QUADRATURE_X1_A,
+	COUNTER_FUNCTION_QUADRATURE_X1_B,
+	COUNTER_FUNCTION_QUADRATURE_X2_A,
+	COUNTER_FUNCTION_QUADRATURE_X2_B,
+	COUNTER_FUNCTION_QUADRATURE_X4,
+};
+
+enum counter_signal_level {
+	COUNTER_SIGNAL_LEVEL_LOW,
+	COUNTER_SIGNAL_LEVEL_HIGH,
+};
+
+enum counter_synapse_action {
+	COUNTER_SYNAPSE_ACTION_NONE,
+	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
+	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
+	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
+};
 
 /**
- * struct counter_signal_ext - Counter Signal extensions
- * @name:	attribute name
- * @read:	read callback for this attribute; may be NULL
- * @write:	write callback for this attribute; may be NULL
- * @priv:	data private to the driver
+ * struct counter_comp - Counter component node
+ * @type:		Counter component data type
+ * @name:		device-specific component name
+ * @priv:		component-relevant data
+ * @action_read		Synapse action mode read callback. The read value of the
+ *			respective Synapse action mode should be passed back via
+ *			the action parameter.
+ * @device_u8_read	Device u8 component read callback. The read value of the
+ *			respective Device u8 component should be passed back via
+ *			the val parameter.
+ * @count_u8_read	Count u8 component read callback. The read value of the
+ *			respective Count u8 component should be passed back via
+ *			the val parameter.
+ * @signal_u8_read	Signal u8 component read callback. The read value of the
+ *			respective Signal u8 component should be passed back via
+ *			the val parameter.
+ * @device_u32_read	Device u32 component read callback. The read value of
+ *			the respective Device u32 component should be passed
+ *			back via the val parameter.
+ * @count_u32_read	Count u32 component read callback. The read value of the
+ *			respective Count u32 component should be passed back via
+ *			the val parameter.
+ * @signal_u32_read	Signal u32 component read callback. The read value of
+ *			the respective Signal u32 component should be passed
+ *			back via the val parameter.
+ * @device_u64_read	Device u64 component read callback. The read value of
+ *			the respective Device u64 component should be passed
+ *			back via the val parameter.
+ * @count_u64_read	Count u64 component read callback. The read value of the
+ *			respective Count u64 component should be passed back via
+ *			the val parameter.
+ * @signal_u64_read	Signal u64 component read callback. The read value of
+ *			the respective Signal u64 component should be passed
+ *			back via the val parameter.
+ * @action_write	Synapse action mode write callback. The write value of
+ *			the respective Synapse action mode is passed via the
+ *			action parameter.
+ * @device_u8_write	Device u8 component write callback. The write value of
+ *			the respective Device u8 component is passed via the val
+ *			parameter.
+ * @count_u8_write	Count u8 component write callback. The write value of
+ *			the respective Count u8 component is passed via the val
+ *			parameter.
+ * @signal_u8_write	Signal u8 component write callback. The write value of
+ *			the respective Signal u8 component is passed via the val
+ *			parameter.
+ * @device_u32_write	Device u32 component write callback. The write value of
+ *			the respective Device u32 component is passed via the
+ *			val parameter.
+ * @count_u32_write	Count u32 component write callback. The write value of
+ *			the respective Count u32 component is passed via the val
+ *			parameter.
+ * @signal_u32_write	Signal u32 component write callback. The write value of
+ *			the respective Signal u32 component is passed via the
+ *			val parameter.
+ * @device_u64_write	Device u64 component write callback. The write value of
+ *			the respective Device u64 component is passed via the
+ *			val parameter.
+ * @count_u64_write	Count u64 component write callback. The write value of
+ *			the respective Count u64 component is passed via the val
+ *			parameter.
+ * @signal_u64_write	Signal u64 component write callback. The write value of
+ *			the respective Signal u64 component is passed via the
+ *			val parameter.
  */
-struct counter_signal_ext {
+struct counter_comp {
+	enum counter_comp_type type;
 	const char *name;
-	ssize_t (*read)(struct counter_device *counter,
-			struct counter_signal *signal, void *priv, char *buf);
-	ssize_t (*write)(struct counter_device *counter,
-			 struct counter_signal *signal, void *priv,
-			 const char *buf, size_t len);
 	void *priv;
+	union {
+		int (*action_read)(struct counter_device *counter,
+				   struct counter_count *count,
+				   struct counter_synapse *synapse,
+				   enum counter_synapse_action *action);
+		int (*device_u8_read)(struct counter_device *counter, u8 *val);
+		int (*count_u8_read)(struct counter_device *counter,
+				     struct counter_count *count, u8 *val);
+		int (*signal_u8_read)(struct counter_device *counter,
+				      struct counter_signal *signal, u8 *val);
+		int (*device_u32_read)(struct counter_device *counter,
+				       u32 *val);
+		int (*count_u32_read)(struct counter_device *counter,
+				      struct counter_count *count, u32 *val);
+		int (*signal_u32_read)(struct counter_device *counter,
+				       struct counter_signal *signal, u32 *val);
+		int (*device_u64_read)(struct counter_device *counter,
+				       u64 *val);
+		int (*count_u64_read)(struct counter_device *counter,
+				      struct counter_count *count, u64 *val);
+		int (*signal_u64_read)(struct counter_device *counter,
+				       struct counter_signal *signal, u64 *val);
+	};
+	union {
+		int (*action_write)(struct counter_device *counter,
+				    struct counter_count *count,
+				    struct counter_synapse *synapse,
+				    enum counter_synapse_action action);
+		int (*device_u8_write)(struct counter_device *counter, u8 val);
+		int (*count_u8_write)(struct counter_device *counter,
+				      struct counter_count *count, u8 val);
+		int (*signal_u8_write)(struct counter_device *counter,
+				       struct counter_signal *signal, u8 val);
+		int (*device_u32_write)(struct counter_device *counter,
+					u32 val);
+		int (*count_u32_write)(struct counter_device *counter,
+				       struct counter_count *count, u32 val);
+		int (*signal_u32_write)(struct counter_device *counter,
+					struct counter_signal *signal, u32 val);
+		int (*device_u64_write)(struct counter_device *counter,
+					u64 val);
+		int (*count_u64_write)(struct counter_device *counter,
+				       struct counter_count *count, u64 val);
+		int (*signal_u64_write)(struct counter_device *counter,
+					struct counter_signal *signal, u64 val);
+	};
 };
 
 /**
@@ -51,248 +193,52 @@ struct counter_signal_ext {
  *		as it appears in the datasheet documentation
  * @ext:	optional array of Counter Signal extensions
  * @num_ext:	number of Counter Signal extensions specified in @ext
- * @priv:	optional private data supplied by driver
  */
 struct counter_signal {
 	int id;
 	const char *name;
 
-	const struct counter_signal_ext *ext;
+	struct counter_comp *ext;
 	size_t num_ext;
-
-	void *priv;
-};
-
-/**
- * struct counter_signal_enum_ext - Signal enum extension attribute
- * @items:	Array of strings
- * @num_items:	Number of items specified in @items
- * @set:	Set callback function; may be NULL
- * @get:	Get callback function; may be NULL
- *
- * The counter_signal_enum_ext structure can be used to implement enum style
- * Signal extension attributes. Enum style attributes are those which have a set
- * of strings that map to unsigned integer values. The Generic Counter Signal
- * enum extension helper code takes care of mapping between value and string, as
- * well as generating a "_available" file which contains a list of all available
- * items. The get callback is used to query the currently active item; the index
- * of the item within the respective items array is returned via the 'item'
- * parameter. The set callback is called when the attribute is updated; the
- * 'item' parameter contains the index of the newly activated item within the
- * respective items array.
- */
-struct counter_signal_enum_ext {
-	const char * const *items;
-	size_t num_items;
-	int (*get)(struct counter_device *counter,
-		   struct counter_signal *signal, size_t *item);
-	int (*set)(struct counter_device *counter,
-		   struct counter_signal *signal, size_t item);
-};
-
-/**
- * COUNTER_SIGNAL_ENUM() - Initialize Signal enum extension
- * @_name:	Attribute name
- * @_e:		Pointer to a counter_signal_enum_ext structure
- *
- * This should usually be used together with COUNTER_SIGNAL_ENUM_AVAILABLE()
- */
-#define COUNTER_SIGNAL_ENUM(_name, _e) \
-{ \
-	.name = (_name), \
-	.read = counter_signal_enum_read, \
-	.write = counter_signal_enum_write, \
-	.priv = (_e) \
-}
-
-/**
- * COUNTER_SIGNAL_ENUM_AVAILABLE() - Initialize Signal enum available extension
- * @_name:	Attribute name ("_available" will be appended to the name)
- * @_e:		Pointer to a counter_signal_enum_ext structure
- *
- * Creates a read only attribute that lists all the available enum items in a
- * newline separated list. This should usually be used together with
- * COUNTER_SIGNAL_ENUM()
- */
-#define COUNTER_SIGNAL_ENUM_AVAILABLE(_name, _e) \
-{ \
-	.name = (_name "_available"), \
-	.read = counter_signal_enum_available_read, \
-	.priv = (_e) \
-}
-
-enum counter_synapse_action {
-	COUNTER_SYNAPSE_ACTION_NONE = 0,
-	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
-	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
-	COUNTER_SYNAPSE_ACTION_BOTH_EDGES
 };
 
 /**
  * struct counter_synapse - Counter Synapse node
- * @action:		index of current action mode
  * @actions_list:	array of available action modes
  * @num_actions:	number of action modes specified in @actions_list
  * @signal:		pointer to associated signal
  */
 struct counter_synapse {
-	size_t action;
 	const enum counter_synapse_action *actions_list;
 	size_t num_actions;
 
 	struct counter_signal *signal;
 };
 
-struct counter_count;
-
-/**
- * struct counter_count_ext - Counter Count extension
- * @name:	attribute name
- * @read:	read callback for this attribute; may be NULL
- * @write:	write callback for this attribute; may be NULL
- * @priv:	data private to the driver
- */
-struct counter_count_ext {
-	const char *name;
-	ssize_t (*read)(struct counter_device *counter,
-			struct counter_count *count, void *priv, char *buf);
-	ssize_t (*write)(struct counter_device *counter,
-			 struct counter_count *count, void *priv,
-			 const char *buf, size_t len);
-	void *priv;
-};
-
-enum counter_function {
-	COUNTER_FUNCTION_INCREASE = 0,
-	COUNTER_FUNCTION_DECREASE,
-	COUNTER_FUNCTION_PULSE_DIRECTION,
-	COUNTER_FUNCTION_QUADRATURE_X1_A,
-	COUNTER_FUNCTION_QUADRATURE_X1_B,
-	COUNTER_FUNCTION_QUADRATURE_X2_A,
-	COUNTER_FUNCTION_QUADRATURE_X2_B,
-	COUNTER_FUNCTION_QUADRATURE_X4
-};
-
 /**
  * struct counter_count - Counter Count node
  * @id:			unique ID used to identify Count
  * @name:		device-specific Count name; ideally, this should match
  *			the name as it appears in the datasheet documentation
- * @function:		index of current function mode
  * @functions_list:	array available function modes
  * @num_functions:	number of function modes specified in @functions_list
  * @synapses:		array of synapses for initialization
  * @num_synapses:	number of synapses specified in @synapses
  * @ext:		optional array of Counter Count extensions
  * @num_ext:		number of Counter Count extensions specified in @ext
- * @priv:		optional private data supplied by driver
  */
 struct counter_count {
 	int id;
 	const char *name;
 
-	size_t function;
 	const enum counter_function *functions_list;
 	size_t num_functions;
 
 	struct counter_synapse *synapses;
 	size_t num_synapses;
 
-	const struct counter_count_ext *ext;
+	struct counter_comp *ext;
 	size_t num_ext;
-
-	void *priv;
-};
-
-/**
- * struct counter_count_enum_ext - Count enum extension attribute
- * @items:	Array of strings
- * @num_items:	Number of items specified in @items
- * @set:	Set callback function; may be NULL
- * @get:	Get callback function; may be NULL
- *
- * The counter_count_enum_ext structure can be used to implement enum style
- * Count extension attributes. Enum style attributes are those which have a set
- * of strings that map to unsigned integer values. The Generic Counter Count
- * enum extension helper code takes care of mapping between value and string, as
- * well as generating a "_available" file which contains a list of all available
- * items. The get callback is used to query the currently active item; the index
- * of the item within the respective items array is returned via the 'item'
- * parameter. The set callback is called when the attribute is updated; the
- * 'item' parameter contains the index of the newly activated item within the
- * respective items array.
- */
-struct counter_count_enum_ext {
-	const char * const *items;
-	size_t num_items;
-	int (*get)(struct counter_device *counter, struct counter_count *count,
-		   size_t *item);
-	int (*set)(struct counter_device *counter, struct counter_count *count,
-		   size_t item);
-};
-
-/**
- * COUNTER_COUNT_ENUM() - Initialize Count enum extension
- * @_name:	Attribute name
- * @_e:		Pointer to a counter_count_enum_ext structure
- *
- * This should usually be used together with COUNTER_COUNT_ENUM_AVAILABLE()
- */
-#define COUNTER_COUNT_ENUM(_name, _e) \
-{ \
-	.name = (_name), \
-	.read = counter_count_enum_read, \
-	.write = counter_count_enum_write, \
-	.priv = (_e) \
-}
-
-/**
- * COUNTER_COUNT_ENUM_AVAILABLE() - Initialize Count enum available extension
- * @_name:	Attribute name ("_available" will be appended to the name)
- * @_e:		Pointer to a counter_count_enum_ext structure
- *
- * Creates a read only attribute that lists all the available enum items in a
- * newline separated list. This should usually be used together with
- * COUNTER_COUNT_ENUM()
- */
-#define COUNTER_COUNT_ENUM_AVAILABLE(_name, _e) \
-{ \
-	.name = (_name "_available"), \
-	.read = counter_count_enum_available_read, \
-	.priv = (_e) \
-}
-
-/**
- * struct counter_device_attr_group - internal container for attribute group
- * @attr_group:	Counter sysfs attributes group
- * @attr_list:	list to keep track of created Counter sysfs attributes
- * @num_attr:	number of Counter sysfs attributes
- */
-struct counter_device_attr_group {
-	struct attribute_group attr_group;
-	struct list_head attr_list;
-	size_t num_attr;
-};
-
-/**
- * struct counter_device_state - internal state container for a Counter device
- * @id:			unique ID used to identify the Counter
- * @dev:		internal device structure
- * @groups_list:	attribute groups list (for Signals, Counts, and ext)
- * @num_groups:		number of attribute groups containers
- * @groups:		Counter sysfs attribute groups (to populate @dev.groups)
- */
-struct counter_device_state {
-	int id;
-	struct device dev;
-	struct counter_device_attr_group *groups_list;
-	size_t num_groups;
-	const struct attribute_group **groups;
-};
-
-enum counter_signal_level {
-	COUNTER_SIGNAL_LEVEL_LOW,
-	COUNTER_SIGNAL_LEVEL_HIGH,
 };
 
 /**
@@ -306,117 +252,47 @@ enum counter_signal_level {
  * @count_write:	optional write callback for Count attribute. The write
  *			value for the respective Count is passed in via the val
  *			parameter.
- * @function_get:	function to get the current count function mode. Returns
- *			0 on success and negative error code on error. The index
- *			of the respective Count's returned function mode should
- *			be passed back via the function parameter.
- * @function_set:	function to set the count function mode. function is the
- *			index of the requested function mode from the respective
- *			Count's functions_list array.
- * @action_get:		function to get the current action mode. Returns 0 on
- *			success and negative error code on error. The index of
- *			the respective Synapse's returned action mode should be
- *			passed back via the action parameter.
- * @action_set:		function to set the action mode. action is the index of
- *			the requested action mode from the respective Synapse's
- *			actions_list array.
+ * @function_read:	read callback the Count function modes. The read
+ *			function mode of the respective Count should be passed
+ *			back via the function parameter.
+ * @function_write:	write callback for Count function modes. The function
+ *			mode to write for the respective Count is passed in via
+ *			the function parameter.
+ * @action_read:	read callback the Synapse action modes. The read action
+ *			mode of the respective Synapse should be passed back via
+ *			the action parameter.
+ * @action_write:	write callback for Synapse action modes. The action mode
+ *			to write for the respective Synapse is passed in via the
+ *			action parameter.
  */
 struct counter_ops {
 	int (*signal_read)(struct counter_device *counter,
 			   struct counter_signal *signal,
 			   enum counter_signal_level *level);
 	int (*count_read)(struct counter_device *counter,
-			  struct counter_count *count, unsigned long *val);
+			  struct counter_count *count, u64 *value);
 	int (*count_write)(struct counter_device *counter,
-			   struct counter_count *count, unsigned long val);
-	int (*function_get)(struct counter_device *counter,
-			    struct counter_count *count, size_t *function);
-	int (*function_set)(struct counter_device *counter,
-			    struct counter_count *count, size_t function);
-	int (*action_get)(struct counter_device *counter,
-			  struct counter_count *count,
-			  struct counter_synapse *synapse, size_t *action);
-	int (*action_set)(struct counter_device *counter,
-			  struct counter_count *count,
-			  struct counter_synapse *synapse, size_t action);
-};
-
-/**
- * struct counter_device_ext - Counter device extension
- * @name:	attribute name
- * @read:	read callback for this attribute; may be NULL
- * @write:	write callback for this attribute; may be NULL
- * @priv:	data private to the driver
- */
-struct counter_device_ext {
-	const char *name;
-	ssize_t (*read)(struct counter_device *counter, void *priv, char *buf);
-	ssize_t (*write)(struct counter_device *counter, void *priv,
-			 const char *buf, size_t len);
-	void *priv;
+			   struct counter_count *count, u64 value);
+	int (*function_read)(struct counter_device *counter,
+			     struct counter_count *count,
+			     enum counter_function *function);
+	int (*function_write)(struct counter_device *counter,
+			      struct counter_count *count,
+			      enum counter_function function);
+	int (*action_read)(struct counter_device *counter,
+			   struct counter_count *count,
+			   struct counter_synapse *synapse,
+			   enum counter_synapse_action *action);
+	int (*action_write)(struct counter_device *counter,
+			    struct counter_count *count,
+			    struct counter_synapse *synapse,
+			    enum counter_synapse_action action);
 };
 
-/**
- * struct counter_device_enum_ext - Counter enum extension attribute
- * @items:	Array of strings
- * @num_items:	Number of items specified in @items
- * @set:	Set callback function; may be NULL
- * @get:	Get callback function; may be NULL
- *
- * The counter_device_enum_ext structure can be used to implement enum style
- * Counter extension attributes. Enum style attributes are those which have a
- * set of strings that map to unsigned integer values. The Generic Counter enum
- * extension helper code takes care of mapping between value and string, as well
- * as generating a "_available" file which contains a list of all available
- * items. The get callback is used to query the currently active item; the index
- * of the item within the respective items array is returned via the 'item'
- * parameter. The set callback is called when the attribute is updated; the
- * 'item' parameter contains the index of the newly activated item within the
- * respective items array.
- */
-struct counter_device_enum_ext {
-	const char * const *items;
-	size_t num_items;
-	int (*get)(struct counter_device *counter, size_t *item);
-	int (*set)(struct counter_device *counter, size_t item);
-};
-
-/**
- * COUNTER_DEVICE_ENUM() - Initialize Counter enum extension
- * @_name:	Attribute name
- * @_e:		Pointer to a counter_device_enum_ext structure
- *
- * This should usually be used together with COUNTER_DEVICE_ENUM_AVAILABLE()
- */
-#define COUNTER_DEVICE_ENUM(_name, _e) \
-{ \
-	.name = (_name), \
-	.read = counter_device_enum_read, \
-	.write = counter_device_enum_write, \
-	.priv = (_e) \
-}
-
-/**
- * COUNTER_DEVICE_ENUM_AVAILABLE() - Initialize Counter enum available extension
- * @_name:	Attribute name ("_available" will be appended to the name)
- * @_e:		Pointer to a counter_device_enum_ext structure
- *
- * Creates a read only attribute that lists all the available enum items in a
- * newline separated list. This should usually be used together with
- * COUNTER_DEVICE_ENUM()
- */
-#define COUNTER_DEVICE_ENUM_AVAILABLE(_name, _e) \
-{ \
-	.name = (_name "_available"), \
-	.read = counter_device_enum_available_read, \
-	.priv = (_e) \
-}
-
 /**
  * struct counter_device - Counter data structure
  * @name:		name of the device as it appears in the datasheet
  * @parent:		optional parent device providing the counters
- * @device_state:	internal device state container
  * @ops:		callbacks from driver
  * @signals:		array of Signals
  * @num_signals:	number of Signals specified in @signals
@@ -425,11 +301,11 @@ struct counter_device_enum_ext {
  * @ext:		optional array of Counter device extensions
  * @num_ext:		number of Counter device extensions specified in @ext
  * @priv:		optional private data supplied by driver
+ * @dev:		internal device structure
  */
 struct counter_device {
 	const char *name;
 	struct device *parent;
-	struct counter_device_state *device_state;
 
 	const struct counter_ops *ops;
 
@@ -438,17 +314,159 @@ struct counter_device {
 	struct counter_count *counts;
 	size_t num_counts;
 
-	const struct counter_device_ext *ext;
+	struct counter_comp *ext;
 	size_t num_ext;
 
 	void *priv;
+
+	struct device dev;
 };
 
 int counter_register(struct counter_device *const counter);
 void counter_unregister(struct counter_device *const counter);
 int devm_counter_register(struct device *dev,
 			  struct counter_device *const counter);
-void devm_counter_unregister(struct device *dev,
-			     struct counter_device *const counter);
+
+#define COUNTER_COMP_DEVICE_U8(_name, _read, _write) \
+{ \
+	.type = COUNTER_COMP_U8, \
+	.name = (_name), \
+	.device_u8_read = (_read), \
+	.device_u8_write = (_write), \
+}
+#define COUNTER_COMP_COUNT_U8(_name, _read, _write) \
+{ \
+	.type = COUNTER_COMP_U8, \
+	.name = (_name), \
+	.count_u8_read = (_read), \
+	.count_u8_write = (_write), \
+}
+#define COUNTER_COMP_SIGNAL_U8(_name, _read, _write) \
+{ \
+	.type = COUNTER_COMP_U8, \
+	.name = (_name), \
+	.signal_u8_read = (_read), \
+	.signal_u8_write = (_write), \
+}
+
+#define COUNTER_COMP_DEVICE_U64(_name, _read, _write) \
+{ \
+	.type = COUNTER_COMP_U64, \
+	.name = (_name), \
+	.device_u64_read = (_read), \
+	.device_u64_write = (_write), \
+}
+#define COUNTER_COMP_COUNT_U64(_name, _read, _write) \
+{ \
+	.type = COUNTER_COMP_U64, \
+	.name = (_name), \
+	.count_u64_read = (_read), \
+	.count_u64_write = (_write), \
+}
+#define COUNTER_COMP_SIGNAL_U64(_name, _read, _write) \
+{ \
+	.type = COUNTER_COMP_U64, \
+	.name = (_name), \
+	.signal_u64_read = (_read), \
+	.signal_u64_write = (_write), \
+}
+
+#define COUNTER_COMP_DEVICE_BOOL(_name, _read, _write) \
+{ \
+	.type = COUNTER_COMP_BOOL, \
+	.name = (_name), \
+	.device_u8_read = (_read), \
+	.device_u8_write = (_write), \
+}
+#define COUNTER_COMP_COUNT_BOOL(_name, _read, _write) \
+{ \
+	.type = COUNTER_COMP_BOOL, \
+	.name = (_name), \
+	.count_u8_read = (_read), \
+	.count_u8_write = (_write), \
+}
+#define COUNTER_COMP_SIGNAL_BOOL(_name, _read, _write) \
+{ \
+	.type = COUNTER_COMP_BOOL, \
+	.name = (_name), \
+	.signal_u8_read = (_read), \
+	.signal_u8_write = (_write), \
+}
+
+struct counter_available {
+	union {
+		const u32 *enums;
+		const char *const *strs;
+	};
+	size_t num_items;
+};
+
+#define DEFINE_COUNTER_AVAILABLE(_name, _enums) \
+	struct counter_available _name = { \
+		.enums = (_enums), \
+		.num_items = ARRAY_SIZE(_enums), \
+	}
+
+#define DEFINE_COUNTER_ENUM(_name, _strs) \
+	struct counter_available _name = { \
+		.strs = (_strs), \
+		.num_items = ARRAY_SIZE(_strs), \
+	}
+
+#define COUNTER_COMP_DEVICE_ENUM(_name, _get, _set, _available) \
+{ \
+	.type = COUNTER_COMP_ENUM, \
+	.name = (_name), \
+	.device_u32_read = (_get), \
+	.device_u32_write = (_set), \
+	.priv = &(_available), \
+}
+#define COUNTER_COMP_COUNT_ENUM(_name, _get, _set, _available) \
+{ \
+	.type = COUNTER_COMP_ENUM, \
+	.name = (_name), \
+	.count_u32_read = (_get), \
+	.count_u32_write = (_set), \
+	.priv = &(_available), \
+}
+#define COUNTER_COMP_SIGNAL_ENUM(_name, _get, _set, _available) \
+{ \
+	.type = COUNTER_COMP_ENUM, \
+	.name = (_name), \
+	.signal_u32_read = (_get), \
+	.signal_u32_write = (_set), \
+	.priv = &(_available), \
+}
+
+#define COUNTER_COMP_CEILING(_read, _write) \
+	COUNTER_COMP_COUNT_U64("ceiling", _read, _write)
+
+#define COUNTER_COMP_COUNT_MODE(_read, _write, _available) \
+{ \
+	.type = COUNTER_COMP_COUNT_MODE, \
+	.name = "count_mode", \
+	.count_u32_read = (_read), \
+	.count_u32_write = (_write), \
+	.priv = &(_available), \
+}
+
+#define COUNTER_COMP_DIRECTION(_read) \
+{ \
+	.type = COUNTER_COMP_COUNT_DIRECTION, \
+	.name = "direction", \
+	.count_u32_read = (_read), \
+}
+
+#define COUNTER_COMP_ENABLE(_read, _write) \
+	COUNTER_COMP_COUNT_BOOL("enable", _read, _write)
+
+#define COUNTER_COMP_FLOOR(_read, _write) \
+	COUNTER_COMP_COUNT_U64("floor", _read, _write)
+
+#define COUNTER_COMP_PRESET(_read, _write) \
+	COUNTER_COMP_COUNT_U64("preset", _read, _write)
+
+#define COUNTER_COMP_PRESET_ENABLE(_read, _write) \
+	COUNTER_COMP_COUNT_BOOL("preset_enable", _read, _write)
 
 #endif /* _COUNTER_H_ */
diff --git a/include/linux/counter_enum.h b/include/linux/counter_enum.h
deleted file mode 100644
index 9f917298a88f..000000000000
--- a/include/linux/counter_enum.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Counter interface enum functions
- * Copyright (C) 2018 William Breathitt Gray
- */
-#ifndef _COUNTER_ENUM_H_
-#define _COUNTER_ENUM_H_
-
-#include <linux/types.h>
-
-struct counter_device;
-struct counter_signal;
-struct counter_count;
-
-ssize_t counter_signal_enum_read(struct counter_device *counter,
-				 struct counter_signal *signal, void *priv,
-				 char *buf);
-ssize_t counter_signal_enum_write(struct counter_device *counter,
-				  struct counter_signal *signal, void *priv,
-				  const char *buf, size_t len);
-
-ssize_t counter_signal_enum_available_read(struct counter_device *counter,
-					   struct counter_signal *signal,
-					   void *priv, char *buf);
-
-ssize_t counter_count_enum_read(struct counter_device *counter,
-				struct counter_count *count, void *priv,
-				char *buf);
-ssize_t counter_count_enum_write(struct counter_device *counter,
-				 struct counter_count *count, void *priv,
-				 const char *buf, size_t len);
-
-ssize_t counter_count_enum_available_read(struct counter_device *counter,
-					  struct counter_count *count,
-					  void *priv, char *buf);
-
-ssize_t counter_device_enum_read(struct counter_device *counter, void *priv,
-				 char *buf);
-ssize_t counter_device_enum_write(struct counter_device *counter, void *priv,
-				  const char *buf, size_t len);
-
-ssize_t counter_device_enum_available_read(struct counter_device *counter,
-					   void *priv, char *buf);
-
-#endif /* _COUNTER_ENUM_H_ */
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 04/14] counter: Update counter.h comments to reflect sysfs internalization
  2021-08-27  3:47 ` William Breathitt Gray
@ 2021-08-27  3:47   ` William Breathitt Gray
  -1 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray

The Counter subsystem architecture and driver implementations have
changed in order to handle Counter sysfs interactions in a more
consistent way. This patch updates the Generic Counter interface
header file comments to reflect the changes.

Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/counter/counter-core.c |  3 ++
 include/linux/counter.h        | 62 ++++++++++++++++------------------
 2 files changed, 33 insertions(+), 32 deletions(-)

diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
index c533a6ff12cf..3cda2c47bacb 100644
--- a/drivers/counter/counter-core.c
+++ b/drivers/counter/counter-core.c
@@ -38,6 +38,9 @@ static struct bus_type counter_bus_type = {
  * This function registers a Counter to the system. A sysfs "counter" directory
  * will be created and populated with sysfs attributes correlating with the
  * Counter Signals, Synapses, and Counts respectively.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
  */
 int counter_register(struct counter_device *const counter)
 {
diff --git a/include/linux/counter.h b/include/linux/counter.h
index b69277f5c4c5..445f22d8bfe2 100644
--- a/include/linux/counter.h
+++ b/include/linux/counter.h
@@ -188,11 +188,10 @@ struct counter_comp {
 
 /**
  * struct counter_signal - Counter Signal node
- * @id:		unique ID used to identify signal
- * @name:	device-specific Signal name; ideally, this should match the name
- *		as it appears in the datasheet documentation
- * @ext:	optional array of Counter Signal extensions
- * @num_ext:	number of Counter Signal extensions specified in @ext
+ * @id:		unique ID used to identify the Signal
+ * @name:	device-specific Signal name
+ * @ext:	optional array of Signal extensions
+ * @num_ext:	number of Signal extensions specified in @ext
  */
 struct counter_signal {
 	int id;
@@ -206,7 +205,7 @@ struct counter_signal {
  * struct counter_synapse - Counter Synapse node
  * @actions_list:	array of available action modes
  * @num_actions:	number of action modes specified in @actions_list
- * @signal:		pointer to associated signal
+ * @signal:		pointer to the associated Signal
  */
 struct counter_synapse {
 	const enum counter_synapse_action *actions_list;
@@ -217,15 +216,14 @@ struct counter_synapse {
 
 /**
  * struct counter_count - Counter Count node
- * @id:			unique ID used to identify Count
- * @name:		device-specific Count name; ideally, this should match
- *			the name as it appears in the datasheet documentation
- * @functions_list:	array available function modes
+ * @id:			unique ID used to identify the Count
+ * @name:		device-specific Count name
+ * @functions_list:	array of available function modes
  * @num_functions:	number of function modes specified in @functions_list
- * @synapses:		array of synapses for initialization
- * @num_synapses:	number of synapses specified in @synapses
- * @ext:		optional array of Counter Count extensions
- * @num_ext:		number of Counter Count extensions specified in @ext
+ * @synapses:		array of Synapses for initialization
+ * @num_synapses:	number of Synapses specified in @synapses
+ * @ext:		optional array of Count extensions
+ * @num_ext:		number of Count extensions specified in @ext
  */
 struct counter_count {
 	int id;
@@ -243,27 +241,27 @@ struct counter_count {
 
 /**
  * struct counter_ops - Callbacks from driver
- * @signal_read:	optional read callback for Signal attribute. The read
- *			level of the respective Signal should be passed back via
- *			the level parameter.
- * @count_read:		optional read callback for Count attribute. The read
- *			value of the respective Count should be passed back via
- *			the val parameter.
- * @count_write:	optional write callback for Count attribute. The write
- *			value for the respective Count is passed in via the val
+ * @signal_read:	optional read callback for Signals. The read level of
+ *			the respective Signal should be passed back via the
+ *			level parameter.
+ * @count_read:		read callback for Counts. The read value of the
+ *			respective Count should be passed back via the value
+ *			parameter.
+ * @count_write:	optional write callback for Counts. The write value for
+ *			the respective Count is passed in via the value
  *			parameter.
  * @function_read:	read callback the Count function modes. The read
  *			function mode of the respective Count should be passed
  *			back via the function parameter.
- * @function_write:	write callback for Count function modes. The function
- *			mode to write for the respective Count is passed in via
- *			the function parameter.
- * @action_read:	read callback the Synapse action modes. The read action
- *			mode of the respective Synapse should be passed back via
- *			the action parameter.
- * @action_write:	write callback for Synapse action modes. The action mode
- *			to write for the respective Synapse is passed in via the
- *			action parameter.
+ * @function_write:	optional write callback for Count function modes. The
+ *			function mode to write for the respective Count is
+ *			passed in via the function parameter.
+ * @action_read:	optional read callback the Synapse action modes. The
+ *			read action mode of the respective Synapse should be
+ *			passed back via the action parameter.
+ * @action_write:	optional write callback for Synapse action modes. The
+ *			action mode to write for the respective Synapse is
+ *			passed in via the action parameter.
  */
 struct counter_ops {
 	int (*signal_read)(struct counter_device *counter,
@@ -291,7 +289,7 @@ struct counter_ops {
 
 /**
  * struct counter_device - Counter data structure
- * @name:		name of the device as it appears in the datasheet
+ * @name:		name of the device
  * @parent:		optional parent device providing the counters
  * @ops:		callbacks from driver
  * @signals:		array of Signals
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 04/14] counter: Update counter.h comments to reflect sysfs internalization
@ 2021-08-27  3:47   ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray

The Counter subsystem architecture and driver implementations have
changed in order to handle Counter sysfs interactions in a more
consistent way. This patch updates the Generic Counter interface
header file comments to reflect the changes.

Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/counter/counter-core.c |  3 ++
 include/linux/counter.h        | 62 ++++++++++++++++------------------
 2 files changed, 33 insertions(+), 32 deletions(-)

diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
index c533a6ff12cf..3cda2c47bacb 100644
--- a/drivers/counter/counter-core.c
+++ b/drivers/counter/counter-core.c
@@ -38,6 +38,9 @@ static struct bus_type counter_bus_type = {
  * This function registers a Counter to the system. A sysfs "counter" directory
  * will be created and populated with sysfs attributes correlating with the
  * Counter Signals, Synapses, and Counts respectively.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
  */
 int counter_register(struct counter_device *const counter)
 {
diff --git a/include/linux/counter.h b/include/linux/counter.h
index b69277f5c4c5..445f22d8bfe2 100644
--- a/include/linux/counter.h
+++ b/include/linux/counter.h
@@ -188,11 +188,10 @@ struct counter_comp {
 
 /**
  * struct counter_signal - Counter Signal node
- * @id:		unique ID used to identify signal
- * @name:	device-specific Signal name; ideally, this should match the name
- *		as it appears in the datasheet documentation
- * @ext:	optional array of Counter Signal extensions
- * @num_ext:	number of Counter Signal extensions specified in @ext
+ * @id:		unique ID used to identify the Signal
+ * @name:	device-specific Signal name
+ * @ext:	optional array of Signal extensions
+ * @num_ext:	number of Signal extensions specified in @ext
  */
 struct counter_signal {
 	int id;
@@ -206,7 +205,7 @@ struct counter_signal {
  * struct counter_synapse - Counter Synapse node
  * @actions_list:	array of available action modes
  * @num_actions:	number of action modes specified in @actions_list
- * @signal:		pointer to associated signal
+ * @signal:		pointer to the associated Signal
  */
 struct counter_synapse {
 	const enum counter_synapse_action *actions_list;
@@ -217,15 +216,14 @@ struct counter_synapse {
 
 /**
  * struct counter_count - Counter Count node
- * @id:			unique ID used to identify Count
- * @name:		device-specific Count name; ideally, this should match
- *			the name as it appears in the datasheet documentation
- * @functions_list:	array available function modes
+ * @id:			unique ID used to identify the Count
+ * @name:		device-specific Count name
+ * @functions_list:	array of available function modes
  * @num_functions:	number of function modes specified in @functions_list
- * @synapses:		array of synapses for initialization
- * @num_synapses:	number of synapses specified in @synapses
- * @ext:		optional array of Counter Count extensions
- * @num_ext:		number of Counter Count extensions specified in @ext
+ * @synapses:		array of Synapses for initialization
+ * @num_synapses:	number of Synapses specified in @synapses
+ * @ext:		optional array of Count extensions
+ * @num_ext:		number of Count extensions specified in @ext
  */
 struct counter_count {
 	int id;
@@ -243,27 +241,27 @@ struct counter_count {
 
 /**
  * struct counter_ops - Callbacks from driver
- * @signal_read:	optional read callback for Signal attribute. The read
- *			level of the respective Signal should be passed back via
- *			the level parameter.
- * @count_read:		optional read callback for Count attribute. The read
- *			value of the respective Count should be passed back via
- *			the val parameter.
- * @count_write:	optional write callback for Count attribute. The write
- *			value for the respective Count is passed in via the val
+ * @signal_read:	optional read callback for Signals. The read level of
+ *			the respective Signal should be passed back via the
+ *			level parameter.
+ * @count_read:		read callback for Counts. The read value of the
+ *			respective Count should be passed back via the value
+ *			parameter.
+ * @count_write:	optional write callback for Counts. The write value for
+ *			the respective Count is passed in via the value
  *			parameter.
  * @function_read:	read callback the Count function modes. The read
  *			function mode of the respective Count should be passed
  *			back via the function parameter.
- * @function_write:	write callback for Count function modes. The function
- *			mode to write for the respective Count is passed in via
- *			the function parameter.
- * @action_read:	read callback the Synapse action modes. The read action
- *			mode of the respective Synapse should be passed back via
- *			the action parameter.
- * @action_write:	write callback for Synapse action modes. The action mode
- *			to write for the respective Synapse is passed in via the
- *			action parameter.
+ * @function_write:	optional write callback for Count function modes. The
+ *			function mode to write for the respective Count is
+ *			passed in via the function parameter.
+ * @action_read:	optional read callback the Synapse action modes. The
+ *			read action mode of the respective Synapse should be
+ *			passed back via the action parameter.
+ * @action_write:	optional write callback for Synapse action modes. The
+ *			action mode to write for the respective Synapse is
+ *			passed in via the action parameter.
  */
 struct counter_ops {
 	int (*signal_read)(struct counter_device *counter,
@@ -291,7 +289,7 @@ struct counter_ops {
 
 /**
  * struct counter_device - Counter data structure
- * @name:		name of the device as it appears in the datasheet
+ * @name:		name of the device
  * @parent:		optional parent device providing the counters
  * @ops:		callbacks from driver
  * @signals:		array of Signals
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 05/14] docs: counter: Update to reflect sysfs internalization
  2021-08-27  3:47 ` William Breathitt Gray
@ 2021-08-27  3:47   ` William Breathitt Gray
  -1 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray

The Counter subsystem architecture and driver implementations have
changed in order to handle Counter sysfs interactions in a more
consistent way. This patch updates the Generic Counter interface
documentation to reflect the changes.

Reviewed-by: David Lechner <david@lechnology.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 Documentation/ABI/testing/sysfs-bus-counter  |   9 +-
 Documentation/driver-api/generic-counter.rst | 243 ++++++++++++++-----
 2 files changed, 185 insertions(+), 67 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter
index 20fe5afd4f9e..dee79b606847 100644
--- a/Documentation/ABI/testing/sysfs-bus-counter
+++ b/Documentation/ABI/testing/sysfs-bus-counter
@@ -286,7 +286,14 @@ What:		/sys/bus/counter/devices/counterX/signalY/signal
 KernelVersion:	5.2
 Contact:	linux-iio@vger.kernel.org
 Description:
-		Signal data of Signal Y represented as a string.
+		Signal level state of Signal Y. The following signal level
+		states are available:
+
+		low:
+			Low level state.
+
+		high:
+			High level state.
 
 What:		/sys/bus/counter/devices/counterX/signalY/synchronous_mode
 KernelVersion:	5.2
diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst
index 64fe7db080e5..f6397218aa4c 100644
--- a/Documentation/driver-api/generic-counter.rst
+++ b/Documentation/driver-api/generic-counter.rst
@@ -250,8 +250,8 @@ for defining a counter device.
 .. kernel-doc:: drivers/counter/counter.c
    :export:
 
-Implementation
-==============
+Driver Implementation
+=====================
 
 To support a counter device, a driver must first allocate the available
 Counter Signals via counter_signal structures. These Signals should
@@ -267,25 +267,61 @@ respective counter_count structure. These counter_count structures are
 set to the counts array member of an allocated counter_device structure
 before the Counter is registered to the system.
 
-Driver callbacks should be provided to the counter_device structure via
-a constant counter_ops structure in order to communicate with the
-device: to read and write various Signals and Counts, and to set and get
-the "action mode" and "function mode" for various Synapses and Counts
-respectively.
+Driver callbacks must be provided to the counter_device structure in
+order to communicate with the device: to read and write various Signals
+and Counts, and to set and get the "action mode" and "function mode" for
+various Synapses and Counts respectively.
 
 A defined counter_device structure may be registered to the system by
 passing it to the counter_register function, and unregistered by passing
 it to the counter_unregister function. Similarly, the
-devm_counter_register and devm_counter_unregister functions may be used
-if device memory-managed registration is desired.
-
-Extension sysfs attributes can be created for auxiliary functionality
-and data by passing in defined counter_device_ext, counter_count_ext,
-and counter_signal_ext structures. In these cases, the
-counter_device_ext structure is used for global/miscellaneous exposure
-and configuration of the respective Counter device, while the
-counter_count_ext and counter_signal_ext structures allow for auxiliary
-exposure and configuration of a specific Count or Signal respectively.
+devm_counter_register function may be used if device memory-managed
+registration is desired.
+
+The struct counter_comp structure is used to define counter extensions
+for Signals, Synapses, and Counts.
+
+The "type" member specifies the type of high-level data (e.g. BOOL,
+COUNT_DIRECTION, etc.) handled by this extension. The "``*_read``" and
+"``*_write``" members can then be set by the counter device driver with
+callbacks to handle that data using native C data types (i.e. u8, u64,
+etc.).
+
+Convenience macros such as ``COUNTER_COMP_COUNT_U64`` are provided for
+use by driver authors. In particular, driver authors are expected to use
+the provided macros for standard Counter subsystem attributes in order
+to maintain a consistent interface for userspace. For example, a counter
+device driver may define several standard attributes like so::
+
+        struct counter_comp count_ext[] = {
+                COUNTER_COMP_DIRECTION(count_direction_read),
+                COUNTER_COMP_ENABLE(count_enable_read, count_enable_write),
+                COUNTER_COMP_CEILING(count_ceiling_read, count_ceiling_write),
+        };
+
+This makes it simple to see, add, and modify the attributes that are
+supported by this driver ("direction", "enable", and "ceiling") and to
+maintain this code without getting lost in a web of struct braces.
+
+Callbacks must match the function type expected for the respective
+component or extension. These function types are defined in the struct
+counter_comp structure as the "``*_read``" and "``*_write``" union
+members.
+
+The corresponding callback prototypes for the extensions mentioned in
+the previous example above would be::
+
+        int count_direction_read(struct counter_device *counter,
+                                 struct counter_count *count,
+                                 enum counter_count_direction *direction);
+        int count_enable_read(struct counter_device *counter,
+                              struct counter_count *count, u8 *enable);
+        int count_enable_write(struct counter_device *counter,
+                               struct counter_count *count, u8 enable);
+        int count_ceiling_read(struct counter_device *counter,
+                               struct counter_count *count, u64 *ceiling);
+        int count_ceiling_write(struct counter_device *counter,
+                                struct counter_count *count, u64 ceiling);
 
 Determining the type of extension to create is a matter of scope.
 
@@ -313,52 +349,127 @@ Determining the type of extension to create is a matter of scope.
   chip overheated via a device extension called "error_overtemp":
   /sys/bus/counter/devices/counterX/error_overtemp
 
-Architecture
-============
-
-When the Generic Counter interface counter module is loaded, the
-counter_init function is called which registers a bus_type named
-"counter" to the system. Subsequently, when the module is unloaded, the
-counter_exit function is called which unregisters the bus_type named
-"counter" from the system.
-
-Counter devices are registered to the system via the counter_register
-function, and later removed via the counter_unregister function. The
-counter_register function establishes a unique ID for the Counter
-device and creates a respective sysfs directory, where X is the
-mentioned unique ID:
-
-    /sys/bus/counter/devices/counterX
-
-Sysfs attributes are created within the counterX directory to expose
-functionality, configurations, and data relating to the Counts, Signals,
-and Synapses of the Counter device, as well as options and information
-for the Counter device itself.
-
-Each Signal has a directory created to house its relevant sysfs
-attributes, where Y is the unique ID of the respective Signal:
-
-    /sys/bus/counter/devices/counterX/signalY
-
-Similarly, each Count has a directory created to house its relevant
-sysfs attributes, where Y is the unique ID of the respective Count:
-
-    /sys/bus/counter/devices/counterX/countY
-
-For a more detailed breakdown of the available Generic Counter interface
-sysfs attributes, please refer to the
-Documentation/ABI/testing/sysfs-bus-counter file.
-
-The Signals and Counts associated with the Counter device are registered
-to the system as well by the counter_register function. The
-signal_read/signal_write driver callbacks are associated with their
-respective Signal attributes, while the count_read/count_write and
-function_get/function_set driver callbacks are associated with their
-respective Count attributes; similarly, the same is true for the
-action_get/action_set driver callbacks and their respective Synapse
-attributes. If a driver callback is left undefined, then the respective
-read/write permission is left disabled for the relevant attributes.
-
-Similarly, extension sysfs attributes are created for the defined
-counter_device_ext, counter_count_ext, and counter_signal_ext
-structures that are passed in.
+Subsystem Architecture
+======================
+
+Counter drivers pass and take data natively (i.e. ``u8``, ``u64``, etc.)
+and the shared counter module handles the translation between the sysfs
+interface. This guarantees a standard userspace interface for all
+counter drivers, and enables a Generic Counter chrdev interface via a
+generalized device driver ABI.
+
+A high-level view of how a count value is passed down from a counter
+driver is exemplified by the following. The driver callbacks are first
+registered to the Counter core component for use by the Counter
+userspace interface components::
+
+        Driver callbacks registration:
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        +----------------------------+
+                        | Counter device driver      |
+                        +----------------------------+
+                        | Processes data from device |
+                        +----------------------------+
+                                |
+                         -------------------
+                        / driver callbacks /
+                        -------------------
+                                |
+                                V
+                        +----------------------+
+                        | Counter core         |
+                        +----------------------+
+                        | Routes device driver |
+                        | callbacks to the     |
+                        | userspace interfaces |
+                        +----------------------+
+                                |
+                         -------------------
+                        / driver callbacks /
+                        -------------------
+                                |
+                +---------------+
+                |
+                V
+        +--------------------+
+        | Counter sysfs      |
+        +--------------------+
+        | Translates to the  |
+        | standard Counter   |
+        | sysfs output       |
+        +--------------------+
+
+Thereafter, data can be transferred directly between the Counter device
+driver and Counter userspace interface::
+
+        Count data request:
+        ~~~~~~~~~~~~~~~~~~~
+                         ----------------------
+                        / Counter device       \
+                        +----------------------+
+                        | Count register: 0x28 |
+                        +----------------------+
+                                |
+                         -----------------
+                        / raw count data /
+                        -----------------
+                                |
+                                V
+                        +----------------------------+
+                        | Counter device driver      |
+                        +----------------------------+
+                        | Processes data from device |
+                        |----------------------------|
+                        | Type: u64                  |
+                        | Value: 42                  |
+                        +----------------------------+
+                                |
+                         ----------
+                        / u64     /
+                        ----------
+                                |
+                +---------------+
+                |
+                V
+        +--------------------+
+        | Counter sysfs      |
+        +--------------------+
+        | Translates to the  |
+        | standard Counter   |
+        | sysfs output       |
+        |--------------------|
+        | Type: const char * |
+        | Value: "42"        |
+        +--------------------+
+                |
+         ---------------
+        / const char * /
+        ---------------
+                |
+                V
+        +--------------------------------------------------+
+        | `/sys/bus/counter/devices/counterX/countY/count` |
+        +--------------------------------------------------+
+        \ Count: "42"                                      /
+         --------------------------------------------------
+
+There are three primary components involved:
+
+Counter device driver
+---------------------
+Communicates with the hardware device to read/write data; e.g. counter
+drivers for quadrature encoders, timers, etc.
+
+Counter core
+------------
+Registers the counter device driver to the system so that the respective
+callbacks are called during userspace interaction.
+
+Counter sysfs
+-------------
+Translates counter data to the standard Counter sysfs interface format
+and vice versa.
+
+Please refer to the ``Documentation/ABI/testing/sysfs-bus-counter`` file
+for a detailed breakdown of the available Generic Counter interface
+sysfs attributes.
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 05/14] docs: counter: Update to reflect sysfs internalization
@ 2021-08-27  3:47   ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray

The Counter subsystem architecture and driver implementations have
changed in order to handle Counter sysfs interactions in a more
consistent way. This patch updates the Generic Counter interface
documentation to reflect the changes.

Reviewed-by: David Lechner <david@lechnology.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 Documentation/ABI/testing/sysfs-bus-counter  |   9 +-
 Documentation/driver-api/generic-counter.rst | 243 ++++++++++++++-----
 2 files changed, 185 insertions(+), 67 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter
index 20fe5afd4f9e..dee79b606847 100644
--- a/Documentation/ABI/testing/sysfs-bus-counter
+++ b/Documentation/ABI/testing/sysfs-bus-counter
@@ -286,7 +286,14 @@ What:		/sys/bus/counter/devices/counterX/signalY/signal
 KernelVersion:	5.2
 Contact:	linux-iio@vger.kernel.org
 Description:
-		Signal data of Signal Y represented as a string.
+		Signal level state of Signal Y. The following signal level
+		states are available:
+
+		low:
+			Low level state.
+
+		high:
+			High level state.
 
 What:		/sys/bus/counter/devices/counterX/signalY/synchronous_mode
 KernelVersion:	5.2
diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst
index 64fe7db080e5..f6397218aa4c 100644
--- a/Documentation/driver-api/generic-counter.rst
+++ b/Documentation/driver-api/generic-counter.rst
@@ -250,8 +250,8 @@ for defining a counter device.
 .. kernel-doc:: drivers/counter/counter.c
    :export:
 
-Implementation
-==============
+Driver Implementation
+=====================
 
 To support a counter device, a driver must first allocate the available
 Counter Signals via counter_signal structures. These Signals should
@@ -267,25 +267,61 @@ respective counter_count structure. These counter_count structures are
 set to the counts array member of an allocated counter_device structure
 before the Counter is registered to the system.
 
-Driver callbacks should be provided to the counter_device structure via
-a constant counter_ops structure in order to communicate with the
-device: to read and write various Signals and Counts, and to set and get
-the "action mode" and "function mode" for various Synapses and Counts
-respectively.
+Driver callbacks must be provided to the counter_device structure in
+order to communicate with the device: to read and write various Signals
+and Counts, and to set and get the "action mode" and "function mode" for
+various Synapses and Counts respectively.
 
 A defined counter_device structure may be registered to the system by
 passing it to the counter_register function, and unregistered by passing
 it to the counter_unregister function. Similarly, the
-devm_counter_register and devm_counter_unregister functions may be used
-if device memory-managed registration is desired.
-
-Extension sysfs attributes can be created for auxiliary functionality
-and data by passing in defined counter_device_ext, counter_count_ext,
-and counter_signal_ext structures. In these cases, the
-counter_device_ext structure is used for global/miscellaneous exposure
-and configuration of the respective Counter device, while the
-counter_count_ext and counter_signal_ext structures allow for auxiliary
-exposure and configuration of a specific Count or Signal respectively.
+devm_counter_register function may be used if device memory-managed
+registration is desired.
+
+The struct counter_comp structure is used to define counter extensions
+for Signals, Synapses, and Counts.
+
+The "type" member specifies the type of high-level data (e.g. BOOL,
+COUNT_DIRECTION, etc.) handled by this extension. The "``*_read``" and
+"``*_write``" members can then be set by the counter device driver with
+callbacks to handle that data using native C data types (i.e. u8, u64,
+etc.).
+
+Convenience macros such as ``COUNTER_COMP_COUNT_U64`` are provided for
+use by driver authors. In particular, driver authors are expected to use
+the provided macros for standard Counter subsystem attributes in order
+to maintain a consistent interface for userspace. For example, a counter
+device driver may define several standard attributes like so::
+
+        struct counter_comp count_ext[] = {
+                COUNTER_COMP_DIRECTION(count_direction_read),
+                COUNTER_COMP_ENABLE(count_enable_read, count_enable_write),
+                COUNTER_COMP_CEILING(count_ceiling_read, count_ceiling_write),
+        };
+
+This makes it simple to see, add, and modify the attributes that are
+supported by this driver ("direction", "enable", and "ceiling") and to
+maintain this code without getting lost in a web of struct braces.
+
+Callbacks must match the function type expected for the respective
+component or extension. These function types are defined in the struct
+counter_comp structure as the "``*_read``" and "``*_write``" union
+members.
+
+The corresponding callback prototypes for the extensions mentioned in
+the previous example above would be::
+
+        int count_direction_read(struct counter_device *counter,
+                                 struct counter_count *count,
+                                 enum counter_count_direction *direction);
+        int count_enable_read(struct counter_device *counter,
+                              struct counter_count *count, u8 *enable);
+        int count_enable_write(struct counter_device *counter,
+                               struct counter_count *count, u8 enable);
+        int count_ceiling_read(struct counter_device *counter,
+                               struct counter_count *count, u64 *ceiling);
+        int count_ceiling_write(struct counter_device *counter,
+                                struct counter_count *count, u64 ceiling);
 
 Determining the type of extension to create is a matter of scope.
 
@@ -313,52 +349,127 @@ Determining the type of extension to create is a matter of scope.
   chip overheated via a device extension called "error_overtemp":
   /sys/bus/counter/devices/counterX/error_overtemp
 
-Architecture
-============
-
-When the Generic Counter interface counter module is loaded, the
-counter_init function is called which registers a bus_type named
-"counter" to the system. Subsequently, when the module is unloaded, the
-counter_exit function is called which unregisters the bus_type named
-"counter" from the system.
-
-Counter devices are registered to the system via the counter_register
-function, and later removed via the counter_unregister function. The
-counter_register function establishes a unique ID for the Counter
-device and creates a respective sysfs directory, where X is the
-mentioned unique ID:
-
-    /sys/bus/counter/devices/counterX
-
-Sysfs attributes are created within the counterX directory to expose
-functionality, configurations, and data relating to the Counts, Signals,
-and Synapses of the Counter device, as well as options and information
-for the Counter device itself.
-
-Each Signal has a directory created to house its relevant sysfs
-attributes, where Y is the unique ID of the respective Signal:
-
-    /sys/bus/counter/devices/counterX/signalY
-
-Similarly, each Count has a directory created to house its relevant
-sysfs attributes, where Y is the unique ID of the respective Count:
-
-    /sys/bus/counter/devices/counterX/countY
-
-For a more detailed breakdown of the available Generic Counter interface
-sysfs attributes, please refer to the
-Documentation/ABI/testing/sysfs-bus-counter file.
-
-The Signals and Counts associated with the Counter device are registered
-to the system as well by the counter_register function. The
-signal_read/signal_write driver callbacks are associated with their
-respective Signal attributes, while the count_read/count_write and
-function_get/function_set driver callbacks are associated with their
-respective Count attributes; similarly, the same is true for the
-action_get/action_set driver callbacks and their respective Synapse
-attributes. If a driver callback is left undefined, then the respective
-read/write permission is left disabled for the relevant attributes.
-
-Similarly, extension sysfs attributes are created for the defined
-counter_device_ext, counter_count_ext, and counter_signal_ext
-structures that are passed in.
+Subsystem Architecture
+======================
+
+Counter drivers pass and take data natively (i.e. ``u8``, ``u64``, etc.)
+and the shared counter module handles the translation between the sysfs
+interface. This guarantees a standard userspace interface for all
+counter drivers, and enables a Generic Counter chrdev interface via a
+generalized device driver ABI.
+
+A high-level view of how a count value is passed down from a counter
+driver is exemplified by the following. The driver callbacks are first
+registered to the Counter core component for use by the Counter
+userspace interface components::
+
+        Driver callbacks registration:
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        +----------------------------+
+                        | Counter device driver      |
+                        +----------------------------+
+                        | Processes data from device |
+                        +----------------------------+
+                                |
+                         -------------------
+                        / driver callbacks /
+                        -------------------
+                                |
+                                V
+                        +----------------------+
+                        | Counter core         |
+                        +----------------------+
+                        | Routes device driver |
+                        | callbacks to the     |
+                        | userspace interfaces |
+                        +----------------------+
+                                |
+                         -------------------
+                        / driver callbacks /
+                        -------------------
+                                |
+                +---------------+
+                |
+                V
+        +--------------------+
+        | Counter sysfs      |
+        +--------------------+
+        | Translates to the  |
+        | standard Counter   |
+        | sysfs output       |
+        +--------------------+
+
+Thereafter, data can be transferred directly between the Counter device
+driver and Counter userspace interface::
+
+        Count data request:
+        ~~~~~~~~~~~~~~~~~~~
+                         ----------------------
+                        / Counter device       \
+                        +----------------------+
+                        | Count register: 0x28 |
+                        +----------------------+
+                                |
+                         -----------------
+                        / raw count data /
+                        -----------------
+                                |
+                                V
+                        +----------------------------+
+                        | Counter device driver      |
+                        +----------------------------+
+                        | Processes data from device |
+                        |----------------------------|
+                        | Type: u64                  |
+                        | Value: 42                  |
+                        +----------------------------+
+                                |
+                         ----------
+                        / u64     /
+                        ----------
+                                |
+                +---------------+
+                |
+                V
+        +--------------------+
+        | Counter sysfs      |
+        +--------------------+
+        | Translates to the  |
+        | standard Counter   |
+        | sysfs output       |
+        |--------------------|
+        | Type: const char * |
+        | Value: "42"        |
+        +--------------------+
+                |
+         ---------------
+        / const char * /
+        ---------------
+                |
+                V
+        +--------------------------------------------------+
+        | `/sys/bus/counter/devices/counterX/countY/count` |
+        +--------------------------------------------------+
+        \ Count: "42"                                      /
+         --------------------------------------------------
+
+There are three primary components involved:
+
+Counter device driver
+---------------------
+Communicates with the hardware device to read/write data; e.g. counter
+drivers for quadrature encoders, timers, etc.
+
+Counter core
+------------
+Registers the counter device driver to the system so that the respective
+callbacks are called during userspace interaction.
+
+Counter sysfs
+-------------
+Translates counter data to the standard Counter sysfs interface format
+and vice versa.
+
+Please refer to the ``Documentation/ABI/testing/sysfs-bus-counter`` file
+for a detailed breakdown of the available Generic Counter interface
+sysfs attributes.
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 06/14] counter: Move counter enums to uapi header
  2021-08-27  3:47 ` William Breathitt Gray
@ 2021-08-27  3:47   ` William Breathitt Gray
  -1 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray

This is in preparation for a subsequent patch implementing a character
device interface for the Counter subsystem.

Reviewed-by: David Lechner <david@lechnology.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 MAINTAINERS                  |  1 +
 include/linux/counter.h      | 42 +--------------------------
 include/uapi/linux/counter.h | 56 ++++++++++++++++++++++++++++++++++++
 3 files changed, 58 insertions(+), 41 deletions(-)
 create mode 100644 include/uapi/linux/counter.h

diff --git a/MAINTAINERS b/MAINTAINERS
index b0cdb9aaca85..f2fdd2202605 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4781,6 +4781,7 @@ F:	Documentation/ABI/testing/sysfs-bus-counter
 F:	Documentation/driver-api/generic-counter.rst
 F:	drivers/counter/
 F:	include/linux/counter.h
+F:	include/uapi/linux/counter.h
 
 CP2615 I2C DRIVER
 M:	Bence Csókás <bence98@sch.bme.hu>
diff --git a/include/linux/counter.h b/include/linux/counter.h
index 445f22d8bfe2..7c9f7e23953a 100644
--- a/include/linux/counter.h
+++ b/include/linux/counter.h
@@ -9,6 +9,7 @@
 #include <linux/device.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
+#include <uapi/linux/counter.h>
 
 struct counter_device;
 struct counter_count;
@@ -27,47 +28,6 @@ enum counter_comp_type {
 	COUNTER_COMP_COUNT_MODE,
 };
 
-enum counter_scope {
-	COUNTER_SCOPE_DEVICE,
-	COUNTER_SCOPE_SIGNAL,
-	COUNTER_SCOPE_COUNT,
-};
-
-enum counter_count_direction {
-	COUNTER_COUNT_DIRECTION_FORWARD,
-	COUNTER_COUNT_DIRECTION_BACKWARD,
-};
-
-enum counter_count_mode {
-	COUNTER_COUNT_MODE_NORMAL,
-	COUNTER_COUNT_MODE_RANGE_LIMIT,
-	COUNTER_COUNT_MODE_NON_RECYCLE,
-	COUNTER_COUNT_MODE_MODULO_N,
-};
-
-enum counter_function {
-	COUNTER_FUNCTION_INCREASE,
-	COUNTER_FUNCTION_DECREASE,
-	COUNTER_FUNCTION_PULSE_DIRECTION,
-	COUNTER_FUNCTION_QUADRATURE_X1_A,
-	COUNTER_FUNCTION_QUADRATURE_X1_B,
-	COUNTER_FUNCTION_QUADRATURE_X2_A,
-	COUNTER_FUNCTION_QUADRATURE_X2_B,
-	COUNTER_FUNCTION_QUADRATURE_X4,
-};
-
-enum counter_signal_level {
-	COUNTER_SIGNAL_LEVEL_LOW,
-	COUNTER_SIGNAL_LEVEL_HIGH,
-};
-
-enum counter_synapse_action {
-	COUNTER_SYNAPSE_ACTION_NONE,
-	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
-	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
-	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
-};
-
 /**
  * struct counter_comp - Counter component node
  * @type:		Counter component data type
diff --git a/include/uapi/linux/counter.h b/include/uapi/linux/counter.h
new file mode 100644
index 000000000000..6113938a6044
--- /dev/null
+++ b/include/uapi/linux/counter.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Userspace ABI for Counter character devices
+ * Copyright (C) 2020 William Breathitt Gray
+ */
+#ifndef _UAPI_COUNTER_H_
+#define _UAPI_COUNTER_H_
+
+/* Component scope definitions */
+enum counter_scope {
+	COUNTER_SCOPE_DEVICE,
+	COUNTER_SCOPE_SIGNAL,
+	COUNTER_SCOPE_COUNT,
+};
+
+/* Count direction values */
+enum counter_count_direction {
+	COUNTER_COUNT_DIRECTION_FORWARD,
+	COUNTER_COUNT_DIRECTION_BACKWARD,
+};
+
+/* Count mode values */
+enum counter_count_mode {
+	COUNTER_COUNT_MODE_NORMAL,
+	COUNTER_COUNT_MODE_RANGE_LIMIT,
+	COUNTER_COUNT_MODE_NON_RECYCLE,
+	COUNTER_COUNT_MODE_MODULO_N,
+};
+
+/* Count function values */
+enum counter_function {
+	COUNTER_FUNCTION_INCREASE,
+	COUNTER_FUNCTION_DECREASE,
+	COUNTER_FUNCTION_PULSE_DIRECTION,
+	COUNTER_FUNCTION_QUADRATURE_X1_A,
+	COUNTER_FUNCTION_QUADRATURE_X1_B,
+	COUNTER_FUNCTION_QUADRATURE_X2_A,
+	COUNTER_FUNCTION_QUADRATURE_X2_B,
+	COUNTER_FUNCTION_QUADRATURE_X4,
+};
+
+/* Signal values */
+enum counter_signal_level {
+	COUNTER_SIGNAL_LEVEL_LOW,
+	COUNTER_SIGNAL_LEVEL_HIGH,
+};
+
+/* Action mode values */
+enum counter_synapse_action {
+	COUNTER_SYNAPSE_ACTION_NONE,
+	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
+	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
+	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
+};
+
+#endif /* _UAPI_COUNTER_H_ */
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 06/14] counter: Move counter enums to uapi header
@ 2021-08-27  3:47   ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray

This is in preparation for a subsequent patch implementing a character
device interface for the Counter subsystem.

Reviewed-by: David Lechner <david@lechnology.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 MAINTAINERS                  |  1 +
 include/linux/counter.h      | 42 +--------------------------
 include/uapi/linux/counter.h | 56 ++++++++++++++++++++++++++++++++++++
 3 files changed, 58 insertions(+), 41 deletions(-)
 create mode 100644 include/uapi/linux/counter.h

diff --git a/MAINTAINERS b/MAINTAINERS
index b0cdb9aaca85..f2fdd2202605 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4781,6 +4781,7 @@ F:	Documentation/ABI/testing/sysfs-bus-counter
 F:	Documentation/driver-api/generic-counter.rst
 F:	drivers/counter/
 F:	include/linux/counter.h
+F:	include/uapi/linux/counter.h
 
 CP2615 I2C DRIVER
 M:	Bence Csókás <bence98@sch.bme.hu>
diff --git a/include/linux/counter.h b/include/linux/counter.h
index 445f22d8bfe2..7c9f7e23953a 100644
--- a/include/linux/counter.h
+++ b/include/linux/counter.h
@@ -9,6 +9,7 @@
 #include <linux/device.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
+#include <uapi/linux/counter.h>
 
 struct counter_device;
 struct counter_count;
@@ -27,47 +28,6 @@ enum counter_comp_type {
 	COUNTER_COMP_COUNT_MODE,
 };
 
-enum counter_scope {
-	COUNTER_SCOPE_DEVICE,
-	COUNTER_SCOPE_SIGNAL,
-	COUNTER_SCOPE_COUNT,
-};
-
-enum counter_count_direction {
-	COUNTER_COUNT_DIRECTION_FORWARD,
-	COUNTER_COUNT_DIRECTION_BACKWARD,
-};
-
-enum counter_count_mode {
-	COUNTER_COUNT_MODE_NORMAL,
-	COUNTER_COUNT_MODE_RANGE_LIMIT,
-	COUNTER_COUNT_MODE_NON_RECYCLE,
-	COUNTER_COUNT_MODE_MODULO_N,
-};
-
-enum counter_function {
-	COUNTER_FUNCTION_INCREASE,
-	COUNTER_FUNCTION_DECREASE,
-	COUNTER_FUNCTION_PULSE_DIRECTION,
-	COUNTER_FUNCTION_QUADRATURE_X1_A,
-	COUNTER_FUNCTION_QUADRATURE_X1_B,
-	COUNTER_FUNCTION_QUADRATURE_X2_A,
-	COUNTER_FUNCTION_QUADRATURE_X2_B,
-	COUNTER_FUNCTION_QUADRATURE_X4,
-};
-
-enum counter_signal_level {
-	COUNTER_SIGNAL_LEVEL_LOW,
-	COUNTER_SIGNAL_LEVEL_HIGH,
-};
-
-enum counter_synapse_action {
-	COUNTER_SYNAPSE_ACTION_NONE,
-	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
-	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
-	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
-};
-
 /**
  * struct counter_comp - Counter component node
  * @type:		Counter component data type
diff --git a/include/uapi/linux/counter.h b/include/uapi/linux/counter.h
new file mode 100644
index 000000000000..6113938a6044
--- /dev/null
+++ b/include/uapi/linux/counter.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Userspace ABI for Counter character devices
+ * Copyright (C) 2020 William Breathitt Gray
+ */
+#ifndef _UAPI_COUNTER_H_
+#define _UAPI_COUNTER_H_
+
+/* Component scope definitions */
+enum counter_scope {
+	COUNTER_SCOPE_DEVICE,
+	COUNTER_SCOPE_SIGNAL,
+	COUNTER_SCOPE_COUNT,
+};
+
+/* Count direction values */
+enum counter_count_direction {
+	COUNTER_COUNT_DIRECTION_FORWARD,
+	COUNTER_COUNT_DIRECTION_BACKWARD,
+};
+
+/* Count mode values */
+enum counter_count_mode {
+	COUNTER_COUNT_MODE_NORMAL,
+	COUNTER_COUNT_MODE_RANGE_LIMIT,
+	COUNTER_COUNT_MODE_NON_RECYCLE,
+	COUNTER_COUNT_MODE_MODULO_N,
+};
+
+/* Count function values */
+enum counter_function {
+	COUNTER_FUNCTION_INCREASE,
+	COUNTER_FUNCTION_DECREASE,
+	COUNTER_FUNCTION_PULSE_DIRECTION,
+	COUNTER_FUNCTION_QUADRATURE_X1_A,
+	COUNTER_FUNCTION_QUADRATURE_X1_B,
+	COUNTER_FUNCTION_QUADRATURE_X2_A,
+	COUNTER_FUNCTION_QUADRATURE_X2_B,
+	COUNTER_FUNCTION_QUADRATURE_X4,
+};
+
+/* Signal values */
+enum counter_signal_level {
+	COUNTER_SIGNAL_LEVEL_LOW,
+	COUNTER_SIGNAL_LEVEL_HIGH,
+};
+
+/* Action mode values */
+enum counter_synapse_action {
+	COUNTER_SYNAPSE_ACTION_NONE,
+	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
+	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
+	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
+};
+
+#endif /* _UAPI_COUNTER_H_ */
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 07/14] counter: Add character device interface
  2021-08-27  3:47 ` William Breathitt Gray
@ 2021-08-27  3:47   ` William Breathitt Gray
  -1 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray, Dan Carpenter

This patch introduces a character device interface for the Counter
subsystem. Device data is exposed through standard character device read
operations. Device data is gathered when a Counter event is pushed by
the respective Counter device driver. Configuration is handled via ioctl
operations on the respective Counter character device node.

Cc: David Lechner <david@lechnology.com>
Cc: Gwendal Grignou <gwendal@chromium.org>
Cc: Dan Carpenter <dan.carpenter@oracle.com>
Cc: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/counter/Makefile         |   2 +-
 drivers/counter/counter-chrdev.c | 553 +++++++++++++++++++++++++++++++
 drivers/counter/counter-chrdev.h |  14 +
 drivers/counter/counter-core.c   |  60 +++-
 include/linux/counter.h          |  51 +++
 include/uapi/linux/counter.h     |  98 ++++++
 6 files changed, 770 insertions(+), 8 deletions(-)
 create mode 100644 drivers/counter/counter-chrdev.c
 create mode 100644 drivers/counter/counter-chrdev.h

diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
index 1ab7e087fdc2..8fde6c100ebc 100644
--- a/drivers/counter/Makefile
+++ b/drivers/counter/Makefile
@@ -4,7 +4,7 @@
 #
 
 obj-$(CONFIG_COUNTER) += counter.o
-counter-y := counter-core.o counter-sysfs.o
+counter-y := counter-core.o counter-sysfs.o counter-chrdev.o
 
 obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
 obj-$(CONFIG_INTERRUPT_CNT)		+= interrupt-cnt.o
diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c
new file mode 100644
index 000000000000..30659b67d8f3
--- /dev/null
+++ b/drivers/counter/counter-chrdev.c
@@ -0,0 +1,553 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic Counter character device interface
+ * Copyright (C) 2020 William Breathitt Gray
+ */
+#include <linux/bitops.h>
+#include <linux/cdev.h>
+#include <linux/counter.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/kfifo.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/nospec.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/timekeeping.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+
+#include "counter-chrdev.h"
+
+struct counter_comp_node {
+	struct list_head l;
+	struct counter_component component;
+	struct counter_comp comp;
+	void *parent;
+};
+
+static ssize_t counter_chrdev_read(struct file *filp, char __user *buf,
+				   size_t len, loff_t *f_ps)
+{
+	struct counter_device *const counter = filp->private_data;
+	int err;
+	unsigned int copied;
+
+	if (!counter->ops)
+		return -ENODEV;
+
+	if (len < sizeof(struct counter_event))
+		return -EINVAL;
+
+	do {
+		if (kfifo_is_empty(&counter->events)) {
+			if (filp->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+
+			err = wait_event_interruptible(counter->events_wait,
+					!kfifo_is_empty(&counter->events) ||
+					!counter->ops);
+			if (err < 0)
+				return err;
+			if (!counter->ops)
+				return -ENODEV;
+		}
+
+		if (mutex_lock_interruptible(&counter->events_lock))
+			return -ERESTARTSYS;
+		err = kfifo_to_user(&counter->events, buf, len, &copied);
+		mutex_unlock(&counter->events_lock);
+		if (err < 0)
+			return err;
+	} while (!copied);
+
+	return copied;
+}
+
+static __poll_t counter_chrdev_poll(struct file *filp,
+				    struct poll_table_struct *pollt)
+{
+	struct counter_device *const counter = filp->private_data;
+	__poll_t events = 0;
+
+	if (!counter->ops)
+		return events;
+
+	poll_wait(filp, &counter->events_wait, pollt);
+
+	if (!kfifo_is_empty(&counter->events))
+		events = EPOLLIN | EPOLLRDNORM;
+
+	return events;
+}
+
+static void counter_events_list_free(struct list_head *const events_list)
+{
+	struct counter_event_node *p, *n;
+	struct counter_comp_node *q, *o;
+
+	list_for_each_entry_safe(p, n, events_list, l) {
+		/* Free associated component nodes */
+		list_for_each_entry_safe(q, o, &p->comp_list, l) {
+			list_del(&q->l);
+			kfree(q);
+		}
+
+		/* Free event node */
+		list_del(&p->l);
+		kfree(p);
+	}
+}
+
+static int counter_set_event_node(struct counter_device *const counter,
+				  struct counter_watch *const watch,
+				  const struct counter_comp_node *const cfg)
+{
+	struct counter_event_node *event_node;
+	int err = 0;
+	struct counter_comp_node *comp_node;
+
+	/* Search for event in the list */
+	list_for_each_entry(event_node, &counter->next_events_list, l)
+		if (event_node->event == watch->event &&
+		    event_node->channel == watch->channel)
+			break;
+
+	/* If event is not already in the list */
+	if (&event_node->l == &counter->next_events_list) {
+		/* Allocate new event node */
+		event_node = kmalloc(sizeof(*event_node), GFP_KERNEL);
+		if (!event_node)
+			return -ENOMEM;
+
+		/* Configure event node and add to the list */
+		event_node->event = watch->event;
+		event_node->channel = watch->channel;
+		INIT_LIST_HEAD(&event_node->comp_list);
+		list_add(&event_node->l, &counter->next_events_list);
+	}
+
+	/* Check if component watch has already been set before */
+	list_for_each_entry(comp_node, &event_node->comp_list, l)
+		if (comp_node->parent == cfg->parent &&
+		    comp_node->comp.count_u8_read == cfg->comp.count_u8_read) {
+			err = -EINVAL;
+			goto exit_free_event_node;
+		}
+
+	/* Allocate component node */
+	comp_node = kmalloc(sizeof(*comp_node), GFP_KERNEL);
+	if (!comp_node) {
+		err = -ENOMEM;
+		goto exit_free_event_node;
+	}
+	*comp_node = *cfg;
+
+	/* Add component node to event node */
+	list_add_tail(&comp_node->l, &event_node->comp_list);
+
+exit_free_event_node:
+	/* Free event node if no one else is watching */
+	if (list_empty(&event_node->comp_list)) {
+		list_del(&event_node->l);
+		kfree(event_node);
+	}
+
+	return err;
+}
+
+static int counter_enable_events(struct counter_device *const counter)
+{
+	unsigned long flags;
+	int err = 0;
+
+	mutex_lock(&counter->n_events_list_lock);
+	spin_lock_irqsave(&counter->events_list_lock, flags);
+
+	counter_events_list_free(&counter->events_list);
+	list_replace_init(&counter->next_events_list,
+			  &counter->events_list);
+
+	if (counter->ops->events_configure)
+		err = counter->ops->events_configure(counter);
+
+	spin_unlock_irqrestore(&counter->events_list_lock, flags);
+	mutex_unlock(&counter->n_events_list_lock);
+
+	return err;
+}
+
+static int counter_disable_events(struct counter_device *const counter)
+{
+	unsigned long flags;
+	int err = 0;
+
+	spin_lock_irqsave(&counter->events_list_lock, flags);
+
+	counter_events_list_free(&counter->events_list);
+
+	if (counter->ops->events_configure)
+		err = counter->ops->events_configure(counter);
+
+	spin_unlock_irqrestore(&counter->events_list_lock, flags);
+
+	mutex_lock(&counter->n_events_list_lock);
+
+	counter_events_list_free(&counter->next_events_list);
+
+	mutex_unlock(&counter->n_events_list_lock);
+
+	return err;
+}
+
+static int counter_add_watch(struct counter_device *const counter,
+			     const unsigned long arg)
+{
+	void __user *const uwatch = (void __user *)arg;
+	struct counter_watch watch;
+	struct counter_comp_node comp_node = {};
+	size_t parent, id;
+	struct counter_comp *ext;
+	size_t num_ext;
+	int err = 0;
+
+	if (copy_from_user(&watch, uwatch, sizeof(watch)))
+		return -EFAULT;
+
+	if (watch.component.type == COUNTER_COMPONENT_NONE)
+		goto no_component;
+
+	parent = watch.component.parent;
+
+	/* Configure parent component info for comp node */
+	switch (watch.component.scope) {
+	case COUNTER_SCOPE_DEVICE:
+		ext = counter->ext;
+		num_ext = counter->num_ext;
+		break;
+	case COUNTER_SCOPE_SIGNAL:
+		if (parent >= counter->num_signals)
+			return -EINVAL;
+		parent = array_index_nospec(parent, counter->num_signals);
+
+		comp_node.parent = counter->signals + parent;
+
+		ext = counter->signals[parent].ext;
+		num_ext = counter->signals[parent].num_ext;
+		break;
+	case COUNTER_SCOPE_COUNT:
+		if (parent >= counter->num_counts)
+			return -EINVAL;
+		parent = array_index_nospec(parent, counter->num_counts);
+
+		comp_node.parent = counter->counts + parent;
+
+		ext = counter->counts[parent].ext;
+		num_ext = counter->counts[parent].num_ext;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	id = watch.component.id;
+
+	/* Configure component info for comp node */
+	switch (watch.component.type) {
+	case COUNTER_COMPONENT_SIGNAL:
+		if (watch.component.scope != COUNTER_SCOPE_SIGNAL)
+			return -EINVAL;
+
+		comp_node.comp.type = COUNTER_COMP_SIGNAL_LEVEL;
+		comp_node.comp.signal_u32_read = counter->ops->signal_read;
+		break;
+	case COUNTER_COMPONENT_COUNT:
+		if (watch.component.scope != COUNTER_SCOPE_COUNT)
+			return -EINVAL;
+
+		comp_node.comp.type = COUNTER_COMP_U64;
+		comp_node.comp.count_u64_read = counter->ops->count_read;
+		break;
+	case COUNTER_COMPONENT_FUNCTION:
+		if (watch.component.scope != COUNTER_SCOPE_COUNT)
+			return -EINVAL;
+
+		comp_node.comp.type = COUNTER_COMP_FUNCTION;
+		comp_node.comp.count_u32_read = counter->ops->function_read;
+		break;
+	case COUNTER_COMPONENT_SYNAPSE_ACTION:
+		if (watch.component.scope != COUNTER_SCOPE_COUNT)
+			return -EINVAL;
+		if (id >= counter->counts[parent].num_synapses)
+			return -EINVAL;
+		id = array_index_nospec(id, counter->counts[parent].num_synapses);
+
+		comp_node.comp.type = COUNTER_COMP_SYNAPSE_ACTION;
+		comp_node.comp.action_read = counter->ops->action_read;
+		comp_node.comp.priv = counter->counts[parent].synapses + id;
+		break;
+	case COUNTER_COMPONENT_EXTENSION:
+		if (id >= num_ext)
+			return -EINVAL;
+		id = array_index_nospec(id, num_ext);
+
+		comp_node.comp = ext[id];
+		break;
+	default:
+		return -EINVAL;
+	}
+	/* Check if any read callback is set; this is part of a union */
+	if (!comp_node.comp.count_u8_read)
+		return -EOPNOTSUPP;
+
+no_component:
+	mutex_lock(&counter->n_events_list_lock);
+
+	if (counter->ops->watch_validate) {
+		err = counter->ops->watch_validate(counter, &watch);
+		if (err < 0)
+			goto err_exit;
+	}
+
+	comp_node.component = watch.component;
+
+	err = counter_set_event_node(counter, &watch, &comp_node);
+
+err_exit:
+	mutex_unlock(&counter->n_events_list_lock);
+
+	return err;
+}
+
+static long counter_chrdev_ioctl(struct file *filp, unsigned int cmd,
+				 unsigned long arg)
+{
+	struct counter_device *const counter = filp->private_data;
+	int ret = -ENODEV;
+
+	mutex_lock(&counter->ops_exist_lock);
+
+	if (!counter->ops)
+		goto out_unlock;
+
+	switch (cmd) {
+	case COUNTER_ADD_WATCH_IOCTL:
+		ret = counter_add_watch(counter, arg);
+		break;
+	case COUNTER_ENABLE_EVENTS_IOCTL:
+		ret = counter_enable_events(counter);
+		break;
+	case COUNTER_DISABLE_EVENTS_IOCTL:
+		ret = counter_disable_events(counter);
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+out_unlock:
+	mutex_unlock(&counter->ops_exist_lock);
+
+	return ret;
+}
+
+static int counter_chrdev_open(struct inode *inode, struct file *filp)
+{
+	struct counter_device *const counter = container_of(inode->i_cdev,
+							    typeof(*counter),
+							    chrdev);
+
+	if (test_and_set_bit_lock(0, counter->chrdev_lock))
+		return -EBUSY;
+
+	get_device(&counter->dev);
+	filp->private_data = counter;
+
+	return nonseekable_open(inode, filp);
+}
+
+static int counter_chrdev_release(struct inode *inode, struct file *filp)
+{
+	struct counter_device *const counter = filp->private_data;
+	int ret = 0;
+
+	mutex_lock(&counter->ops_exist_lock);
+
+	if (!counter->ops) {
+		counter_events_list_free(&counter->events_list);
+		counter_events_list_free(&counter->next_events_list);
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	ret = counter_disable_events(counter);
+	if (ret < 0) {
+		mutex_unlock(&counter->ops_exist_lock);
+		return ret;
+	}
+
+out_unlock:
+	mutex_unlock(&counter->ops_exist_lock);
+
+	put_device(&counter->dev);
+	clear_bit_unlock(0, counter->chrdev_lock);
+
+	return ret;
+}
+
+static const struct file_operations counter_fops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.read = counter_chrdev_read,
+	.poll = counter_chrdev_poll,
+	.unlocked_ioctl = counter_chrdev_ioctl,
+	.open = counter_chrdev_open,
+	.release = counter_chrdev_release,
+};
+
+int counter_chrdev_add(struct counter_device *const counter)
+{
+	/* Initialize Counter events lists */
+	INIT_LIST_HEAD(&counter->events_list);
+	INIT_LIST_HEAD(&counter->next_events_list);
+	spin_lock_init(&counter->events_list_lock);
+	mutex_init(&counter->n_events_list_lock);
+	init_waitqueue_head(&counter->events_wait);
+	mutex_init(&counter->events_lock);
+
+	/* Initialize character device */
+	clear_bit(0, counter->chrdev_lock);
+	cdev_init(&counter->chrdev, &counter_fops);
+
+	/* Allocate Counter events queue */
+	return kfifo_alloc(&counter->events, 64, GFP_KERNEL);
+}
+
+void counter_chrdev_remove(struct counter_device *const counter)
+{
+	kfifo_free(&counter->events);
+}
+
+static int counter_get_data(struct counter_device *const counter,
+			    const struct counter_comp_node *const comp_node,
+			    u64 *const value)
+{
+	const struct counter_comp *const comp = &comp_node->comp;
+	void *const parent = comp_node->parent;
+	u8 value_u8 = 0;
+	u32 value_u32 = 0;
+	int ret;
+
+	if (comp_node->component.type == COUNTER_COMPONENT_NONE)
+		return 0;
+
+	switch (comp->type) {
+	case COUNTER_COMP_U8:
+	case COUNTER_COMP_BOOL:
+		switch (comp_node->component.scope) {
+		case COUNTER_SCOPE_DEVICE:
+			ret = comp->device_u8_read(counter, &value_u8);
+			break;
+		case COUNTER_SCOPE_SIGNAL:
+			ret = comp->signal_u8_read(counter, parent, &value_u8);
+			break;
+		case COUNTER_SCOPE_COUNT:
+			ret = comp->count_u8_read(counter, parent, &value_u8);
+			break;
+		}
+		*value = value_u8;
+		return ret;
+	case COUNTER_COMP_SIGNAL_LEVEL:
+	case COUNTER_COMP_FUNCTION:
+	case COUNTER_COMP_ENUM:
+	case COUNTER_COMP_COUNT_DIRECTION:
+	case COUNTER_COMP_COUNT_MODE:
+		switch (comp_node->component.scope) {
+		case COUNTER_SCOPE_DEVICE:
+			ret = comp->device_u32_read(counter, &value_u32);
+			break;
+		case COUNTER_SCOPE_SIGNAL:
+			ret = comp->signal_u32_read(counter, parent,
+						    &value_u32);
+			break;
+		case COUNTER_SCOPE_COUNT:
+			ret = comp->count_u32_read(counter, parent, &value_u32);
+			break;
+		}
+		*value = value_u32;
+		return ret;
+	case COUNTER_COMP_U64:
+		switch (comp_node->component.scope) {
+		case COUNTER_SCOPE_DEVICE:
+			return comp->device_u64_read(counter, value);
+		case COUNTER_SCOPE_SIGNAL:
+			return comp->signal_u64_read(counter, parent, value);
+		case COUNTER_SCOPE_COUNT:
+			return comp->count_u64_read(counter, parent, value);
+		default:
+			return -EINVAL;
+		}
+	case COUNTER_COMP_SYNAPSE_ACTION:
+		ret = comp->action_read(counter, parent, comp->priv,
+					&value_u32);
+		*value = value_u32;
+		return ret;
+	default:
+		return -EINVAL;
+	}
+}
+
+/**
+ * counter_push_event - queue event for userspace reading
+ * @counter:	pointer to Counter structure
+ * @event:	triggered event
+ * @channel:	event channel
+ *
+ * Note: If no one is watching for the respective event, it is silently
+ * discarded.
+ */
+void counter_push_event(struct counter_device *const counter, const u8 event,
+			const u8 channel)
+{
+	struct counter_event ev;
+	unsigned int copied = 0;
+	unsigned long flags;
+	struct counter_event_node *event_node;
+	struct counter_comp_node *comp_node;
+
+	ev.timestamp = ktime_get_ns();
+	ev.watch.event = event;
+	ev.watch.channel = channel;
+
+	/* Could be in an interrupt context, so use a spin lock */
+	spin_lock_irqsave(&counter->events_list_lock, flags);
+
+	/* Search for event in the list */
+	list_for_each_entry(event_node, &counter->events_list, l)
+		if (event_node->event == event &&
+		    event_node->channel == channel)
+			break;
+
+	/* If event is not in the list */
+	if (&event_node->l == &counter->events_list)
+		goto exit_early;
+
+	/* Read and queue relevant comp for userspace */
+	list_for_each_entry(comp_node, &event_node->comp_list, l) {
+		ev.watch.component = comp_node->component;
+		ev.status = -counter_get_data(counter, comp_node, &ev.value);
+
+		copied += kfifo_in(&counter->events, &ev, 1);
+	}
+
+exit_early:
+	spin_unlock_irqrestore(&counter->events_list_lock, flags);
+
+	if (copied)
+		wake_up_poll(&counter->events_wait, EPOLLIN);
+}
+EXPORT_SYMBOL_GPL(counter_push_event);
diff --git a/drivers/counter/counter-chrdev.h b/drivers/counter/counter-chrdev.h
new file mode 100644
index 000000000000..5529d16703c4
--- /dev/null
+++ b/drivers/counter/counter-chrdev.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Counter character device interface
+ * Copyright (C) 2020 William Breathitt Gray
+ */
+#ifndef _COUNTER_CHRDEV_H_
+#define _COUNTER_CHRDEV_H_
+
+#include <linux/counter.h>
+
+int counter_chrdev_add(struct counter_device *const counter);
+void counter_chrdev_remove(struct counter_device *const counter);
+
+#endif /* _COUNTER_CHRDEV_H_ */
diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
index 3cda2c47bacb..28dc1ff63c94 100644
--- a/drivers/counter/counter-core.c
+++ b/drivers/counter/counter-core.c
@@ -3,14 +3,22 @@
  * Generic Counter interface
  * Copyright (C) 2020 William Breathitt Gray
  */
+#include <linux/cdev.h>
 #include <linux/counter.h>
 #include <linux/device.h>
+#include <linux/device/bus.h>
 #include <linux/export.h>
+#include <linux/fs.h>
 #include <linux/gfp.h>
 #include <linux/idr.h>
 #include <linux/init.h>
+#include <linux/kdev_t.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/wait.h>
 
+#include "counter-chrdev.h"
 #include "counter-sysfs.h"
 
 /* Provides a unique ID for each counter device */
@@ -18,6 +26,9 @@ static DEFINE_IDA(counter_ida);
 
 static void counter_device_release(struct device *dev)
 {
+	struct counter_device *const counter = dev_get_drvdata(dev);
+
+	counter_chrdev_remove(counter);
 	ida_free(&counter_ida, dev->id);
 }
 
@@ -31,6 +42,8 @@ static struct bus_type counter_bus_type = {
 	.dev_name = "counter",
 };
 
+static dev_t counter_devt;
+
 /**
  * counter_register - register Counter to the system
  * @counter:	pointer to Counter to register
@@ -53,10 +66,13 @@ int counter_register(struct counter_device *const counter)
 	if (id < 0)
 		return id;
 
+	mutex_init(&counter->ops_exist_lock);
+
 	/* Configure device structure for Counter */
 	dev->id = id;
 	dev->type = &counter_device_type;
 	dev->bus = &counter_bus_type;
+	dev->devt = MKDEV(MAJOR(counter_devt), id);
 	if (counter->parent) {
 		dev->parent = counter->parent;
 		dev->of_node = counter->parent->of_node;
@@ -64,18 +80,22 @@ int counter_register(struct counter_device *const counter)
 	device_initialize(dev);
 	dev_set_drvdata(dev, counter);
 
-	/* Add Counter sysfs attributes */
-	err = counter_sysfs_add(counter);
+	err = counter_chrdev_add(counter);
 	if (err < 0)
 		goto err_free_id;
 
-	/* Add device to system */
-	err = device_add(dev);
+	err = counter_sysfs_add(counter);
 	if (err < 0)
-		goto err_free_id;
+		goto err_remove_chrdev;
+
+	err = cdev_device_add(&counter->chrdev, dev);
+	if (err < 0)
+		goto err_remove_chrdev;
 
 	return 0;
 
+err_remove_chrdev:
+	counter_chrdev_remove(counter);
 err_free_id:
 	put_device(dev);
 	return err;
@@ -93,7 +113,16 @@ void counter_unregister(struct counter_device *const counter)
 	if (!counter)
 		return;
 
-	device_unregister(&counter->dev);
+	cdev_device_del(&counter->chrdev, &counter->dev);
+
+	mutex_lock(&counter->ops_exist_lock);
+
+	counter->ops = NULL;
+	wake_up(&counter->events_wait);
+
+	mutex_unlock(&counter->ops_exist_lock);
+
+	put_device(&counter->dev);
 }
 EXPORT_SYMBOL_GPL(counter_unregister);
 
@@ -127,13 +156,30 @@ int devm_counter_register(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(devm_counter_register);
 
+#define COUNTER_DEV_MAX 256
+
 static int __init counter_init(void)
 {
-	return bus_register(&counter_bus_type);
+	int err;
+
+	err = bus_register(&counter_bus_type);
+	if (err < 0)
+		return err;
+
+	err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter");
+	if (err < 0)
+		goto err_unregister_bus;
+
+	return 0;
+
+err_unregister_bus:
+	bus_unregister(&counter_bus_type);
+	return err;
 }
 
 static void __exit counter_exit(void)
 {
+	unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX);
 	bus_unregister(&counter_bus_type);
 }
 
diff --git a/include/linux/counter.h b/include/linux/counter.h
index 7c9f7e23953a..9c9984655f8c 100644
--- a/include/linux/counter.h
+++ b/include/linux/counter.h
@@ -6,9 +6,14 @@
 #ifndef _COUNTER_H_
 #define _COUNTER_H_
 
+#include <linux/cdev.h>
 #include <linux/device.h>
 #include <linux/kernel.h>
+#include <linux/kfifo.h>
+#include <linux/mutex.h>
+#include <linux/spinlock_types.h>
 #include <linux/types.h>
+#include <linux/wait.h>
 #include <uapi/linux/counter.h>
 
 struct counter_device;
@@ -199,6 +204,20 @@ struct counter_count {
 	size_t num_ext;
 };
 
+/**
+ * struct counter_event_node - Counter Event node
+ * @l:		list of current watching Counter events
+ * @event:	event that triggers
+ * @channel:	event channel
+ * @comp_list:	list of components to watch when event triggers
+ */
+struct counter_event_node {
+	struct list_head l;
+	u8 event;
+	u8 channel;
+	struct list_head comp_list;
+};
+
 /**
  * struct counter_ops - Callbacks from driver
  * @signal_read:	optional read callback for Signals. The read level of
@@ -222,6 +241,13 @@ struct counter_count {
  * @action_write:	optional write callback for Synapse action modes. The
  *			action mode to write for the respective Synapse is
  *			passed in via the action parameter.
+ * @events_configure:	optional write callback to configure events. The list of
+ *			struct counter_event_node may be accessed via the
+ *			events_list member of the counter parameter.
+ * @watch_validate:	optional callback to validate a watch. The Counter
+ *			component watch configuration is passed in via the watch
+ *			parameter. A return value of 0 indicates a valid Counter
+ *			component watch configuration.
  */
 struct counter_ops {
 	int (*signal_read)(struct counter_device *counter,
@@ -245,6 +271,9 @@ struct counter_ops {
 			    struct counter_count *count,
 			    struct counter_synapse *synapse,
 			    enum counter_synapse_action action);
+	int (*events_configure)(struct counter_device *counter);
+	int (*watch_validate)(struct counter_device *counter,
+			      const struct counter_watch *watch);
 };
 
 /**
@@ -260,6 +289,16 @@ struct counter_ops {
  * @num_ext:		number of Counter device extensions specified in @ext
  * @priv:		optional private data supplied by driver
  * @dev:		internal device structure
+ * @chrdev:		internal character device structure
+ * @events_list:	list of current watching Counter events
+ * @events_list_lock:	lock to protect Counter events list operations
+ * @next_events_list:	list of next watching Counter events
+ * @n_events_list_lock:	lock to protect Counter next events list operations
+ * @events:		queue of detected Counter events
+ * @events_wait:	wait queue to allow blocking reads of Counter events
+ * @events_lock:	lock to protect Counter events queue read operations
+ * @chrdev_lock:	lock to limit chrdev to a single open at a time
+ * @ops_exist_lock:	lock to prevent use during removal
  */
 struct counter_device {
 	const char *name;
@@ -278,12 +317,24 @@ struct counter_device {
 	void *priv;
 
 	struct device dev;
+	struct cdev chrdev;
+	struct list_head events_list;
+	spinlock_t events_list_lock;
+	struct list_head next_events_list;
+	struct mutex n_events_list_lock;
+	DECLARE_KFIFO_PTR(events, struct counter_event);
+	wait_queue_head_t events_wait;
+	struct mutex events_lock;
+	DECLARE_BITMAP(chrdev_lock, 1);
+	struct mutex ops_exist_lock;
 };
 
 int counter_register(struct counter_device *const counter);
 void counter_unregister(struct counter_device *const counter);
 int devm_counter_register(struct device *dev,
 			  struct counter_device *const counter);
+void counter_push_event(struct counter_device *const counter, const u8 event,
+			const u8 channel);
 
 #define COUNTER_COMP_DEVICE_U8(_name, _read, _write) \
 { \
diff --git a/include/uapi/linux/counter.h b/include/uapi/linux/counter.h
index 6113938a6044..d0aa95aeff7b 100644
--- a/include/uapi/linux/counter.h
+++ b/include/uapi/linux/counter.h
@@ -6,6 +6,19 @@
 #ifndef _UAPI_COUNTER_H_
 #define _UAPI_COUNTER_H_
 
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/* Component type definitions */
+enum counter_component_type {
+	COUNTER_COMPONENT_NONE,
+	COUNTER_COMPONENT_SIGNAL,
+	COUNTER_COMPONENT_COUNT,
+	COUNTER_COMPONENT_FUNCTION,
+	COUNTER_COMPONENT_SYNAPSE_ACTION,
+	COUNTER_COMPONENT_EXTENSION,
+};
+
 /* Component scope definitions */
 enum counter_scope {
 	COUNTER_SCOPE_DEVICE,
@@ -13,6 +26,91 @@ enum counter_scope {
 	COUNTER_SCOPE_COUNT,
 };
 
+/**
+ * struct counter_component - Counter component identification
+ * @type: component type (one of enum counter_component_type)
+ * @scope: component scope (one of enum counter_scope)
+ * @parent: parent ID (matching the ID suffix of the respective parent sysfs
+ *          path as described by the ABI documentation file
+ *          Documentation/ABI/testing/sysfs-bus-counter)
+ * @id: component ID (matching the ID provided by the respective *_component_id
+ *      sysfs attribute of the desired component)
+ *
+ * For example, if the Count 2 ceiling extension of Counter device 4 is desired,
+ * set type equal to COUNTER_COMPONENT_EXTENSION, scope equal to
+ * COUNTER_COUNT_SCOPE, parent equal to 2, and id equal to the value provided by
+ * the respective /sys/bus/counter/devices/counter4/count2/ceiling_component_id
+ * sysfs attribute.
+ */
+struct counter_component {
+	__u8 type;
+	__u8 scope;
+	__u8 parent;
+	__u8 id;
+};
+
+/* Event type definitions */
+enum counter_event_type {
+	/* Count value increased past ceiling */
+	COUNTER_EVENT_OVERFLOW,
+	/* Count value decreased past floor */
+	COUNTER_EVENT_UNDERFLOW,
+	/* Count value increased past ceiling, or decreased past floor */
+	COUNTER_EVENT_OVERFLOW_UNDERFLOW,
+	/* Count value reached threshold */
+	COUNTER_EVENT_THRESHOLD,
+	/* Index signal detected */
+	COUNTER_EVENT_INDEX,
+};
+
+/**
+ * struct counter_watch - Counter component watch configuration
+ * @component: component to watch when event triggers
+ * @event: event that triggers (one of enum counter_event_type)
+ * @channel: event channel (typically 0 unless the device supports concurrent
+ *	     events of the same type)
+ */
+struct counter_watch {
+	struct counter_component component;
+	__u8 event;
+	__u8 channel;
+};
+
+/*
+ * Queues a Counter watch for the specified event.
+ *
+ * The queued watches will not be applied until COUNTER_ENABLE_EVENTS_IOCTL is
+ * called.
+ */
+#define COUNTER_ADD_WATCH_IOCTL _IOW(0x3E, 0x00, struct counter_watch)
+/*
+ * Enables monitoring the events specified by the Counter watches that were
+ * queued by COUNTER_ADD_WATCH_IOCTL.
+ *
+ * If events are already enabled, the new set of watches replaces the old one.
+ * Calling this ioctl also has the effect of clearing the queue of watches added
+ * by COUNTER_ADD_WATCH_IOCTL.
+ */
+#define COUNTER_ENABLE_EVENTS_IOCTL _IO(0x3E, 0x01)
+/*
+ * Stops monitoring the previously enabled events.
+ */
+#define COUNTER_DISABLE_EVENTS_IOCTL _IO(0x3E, 0x02)
+
+/**
+ * struct counter_event - Counter event data
+ * @timestamp: best estimate of time of event occurrence, in nanoseconds
+ * @value: component value
+ * @watch: component watch configuration
+ * @status: return status (system error number)
+ */
+struct counter_event {
+	__aligned_u64 timestamp;
+	__aligned_u64 value;
+	struct counter_watch watch;
+	__u8 status;
+};
+
 /* Count direction values */
 enum counter_count_direction {
 	COUNTER_COUNT_DIRECTION_FORWARD,
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 07/14] counter: Add character device interface
@ 2021-08-27  3:47   ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray, Dan Carpenter

This patch introduces a character device interface for the Counter
subsystem. Device data is exposed through standard character device read
operations. Device data is gathered when a Counter event is pushed by
the respective Counter device driver. Configuration is handled via ioctl
operations on the respective Counter character device node.

Cc: David Lechner <david@lechnology.com>
Cc: Gwendal Grignou <gwendal@chromium.org>
Cc: Dan Carpenter <dan.carpenter@oracle.com>
Cc: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/counter/Makefile         |   2 +-
 drivers/counter/counter-chrdev.c | 553 +++++++++++++++++++++++++++++++
 drivers/counter/counter-chrdev.h |  14 +
 drivers/counter/counter-core.c   |  60 +++-
 include/linux/counter.h          |  51 +++
 include/uapi/linux/counter.h     |  98 ++++++
 6 files changed, 770 insertions(+), 8 deletions(-)
 create mode 100644 drivers/counter/counter-chrdev.c
 create mode 100644 drivers/counter/counter-chrdev.h

diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
index 1ab7e087fdc2..8fde6c100ebc 100644
--- a/drivers/counter/Makefile
+++ b/drivers/counter/Makefile
@@ -4,7 +4,7 @@
 #
 
 obj-$(CONFIG_COUNTER) += counter.o
-counter-y := counter-core.o counter-sysfs.o
+counter-y := counter-core.o counter-sysfs.o counter-chrdev.o
 
 obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
 obj-$(CONFIG_INTERRUPT_CNT)		+= interrupt-cnt.o
diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c
new file mode 100644
index 000000000000..30659b67d8f3
--- /dev/null
+++ b/drivers/counter/counter-chrdev.c
@@ -0,0 +1,553 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic Counter character device interface
+ * Copyright (C) 2020 William Breathitt Gray
+ */
+#include <linux/bitops.h>
+#include <linux/cdev.h>
+#include <linux/counter.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/kfifo.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/nospec.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/timekeeping.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+
+#include "counter-chrdev.h"
+
+struct counter_comp_node {
+	struct list_head l;
+	struct counter_component component;
+	struct counter_comp comp;
+	void *parent;
+};
+
+static ssize_t counter_chrdev_read(struct file *filp, char __user *buf,
+				   size_t len, loff_t *f_ps)
+{
+	struct counter_device *const counter = filp->private_data;
+	int err;
+	unsigned int copied;
+
+	if (!counter->ops)
+		return -ENODEV;
+
+	if (len < sizeof(struct counter_event))
+		return -EINVAL;
+
+	do {
+		if (kfifo_is_empty(&counter->events)) {
+			if (filp->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+
+			err = wait_event_interruptible(counter->events_wait,
+					!kfifo_is_empty(&counter->events) ||
+					!counter->ops);
+			if (err < 0)
+				return err;
+			if (!counter->ops)
+				return -ENODEV;
+		}
+
+		if (mutex_lock_interruptible(&counter->events_lock))
+			return -ERESTARTSYS;
+		err = kfifo_to_user(&counter->events, buf, len, &copied);
+		mutex_unlock(&counter->events_lock);
+		if (err < 0)
+			return err;
+	} while (!copied);
+
+	return copied;
+}
+
+static __poll_t counter_chrdev_poll(struct file *filp,
+				    struct poll_table_struct *pollt)
+{
+	struct counter_device *const counter = filp->private_data;
+	__poll_t events = 0;
+
+	if (!counter->ops)
+		return events;
+
+	poll_wait(filp, &counter->events_wait, pollt);
+
+	if (!kfifo_is_empty(&counter->events))
+		events = EPOLLIN | EPOLLRDNORM;
+
+	return events;
+}
+
+static void counter_events_list_free(struct list_head *const events_list)
+{
+	struct counter_event_node *p, *n;
+	struct counter_comp_node *q, *o;
+
+	list_for_each_entry_safe(p, n, events_list, l) {
+		/* Free associated component nodes */
+		list_for_each_entry_safe(q, o, &p->comp_list, l) {
+			list_del(&q->l);
+			kfree(q);
+		}
+
+		/* Free event node */
+		list_del(&p->l);
+		kfree(p);
+	}
+}
+
+static int counter_set_event_node(struct counter_device *const counter,
+				  struct counter_watch *const watch,
+				  const struct counter_comp_node *const cfg)
+{
+	struct counter_event_node *event_node;
+	int err = 0;
+	struct counter_comp_node *comp_node;
+
+	/* Search for event in the list */
+	list_for_each_entry(event_node, &counter->next_events_list, l)
+		if (event_node->event == watch->event &&
+		    event_node->channel == watch->channel)
+			break;
+
+	/* If event is not already in the list */
+	if (&event_node->l == &counter->next_events_list) {
+		/* Allocate new event node */
+		event_node = kmalloc(sizeof(*event_node), GFP_KERNEL);
+		if (!event_node)
+			return -ENOMEM;
+
+		/* Configure event node and add to the list */
+		event_node->event = watch->event;
+		event_node->channel = watch->channel;
+		INIT_LIST_HEAD(&event_node->comp_list);
+		list_add(&event_node->l, &counter->next_events_list);
+	}
+
+	/* Check if component watch has already been set before */
+	list_for_each_entry(comp_node, &event_node->comp_list, l)
+		if (comp_node->parent == cfg->parent &&
+		    comp_node->comp.count_u8_read == cfg->comp.count_u8_read) {
+			err = -EINVAL;
+			goto exit_free_event_node;
+		}
+
+	/* Allocate component node */
+	comp_node = kmalloc(sizeof(*comp_node), GFP_KERNEL);
+	if (!comp_node) {
+		err = -ENOMEM;
+		goto exit_free_event_node;
+	}
+	*comp_node = *cfg;
+
+	/* Add component node to event node */
+	list_add_tail(&comp_node->l, &event_node->comp_list);
+
+exit_free_event_node:
+	/* Free event node if no one else is watching */
+	if (list_empty(&event_node->comp_list)) {
+		list_del(&event_node->l);
+		kfree(event_node);
+	}
+
+	return err;
+}
+
+static int counter_enable_events(struct counter_device *const counter)
+{
+	unsigned long flags;
+	int err = 0;
+
+	mutex_lock(&counter->n_events_list_lock);
+	spin_lock_irqsave(&counter->events_list_lock, flags);
+
+	counter_events_list_free(&counter->events_list);
+	list_replace_init(&counter->next_events_list,
+			  &counter->events_list);
+
+	if (counter->ops->events_configure)
+		err = counter->ops->events_configure(counter);
+
+	spin_unlock_irqrestore(&counter->events_list_lock, flags);
+	mutex_unlock(&counter->n_events_list_lock);
+
+	return err;
+}
+
+static int counter_disable_events(struct counter_device *const counter)
+{
+	unsigned long flags;
+	int err = 0;
+
+	spin_lock_irqsave(&counter->events_list_lock, flags);
+
+	counter_events_list_free(&counter->events_list);
+
+	if (counter->ops->events_configure)
+		err = counter->ops->events_configure(counter);
+
+	spin_unlock_irqrestore(&counter->events_list_lock, flags);
+
+	mutex_lock(&counter->n_events_list_lock);
+
+	counter_events_list_free(&counter->next_events_list);
+
+	mutex_unlock(&counter->n_events_list_lock);
+
+	return err;
+}
+
+static int counter_add_watch(struct counter_device *const counter,
+			     const unsigned long arg)
+{
+	void __user *const uwatch = (void __user *)arg;
+	struct counter_watch watch;
+	struct counter_comp_node comp_node = {};
+	size_t parent, id;
+	struct counter_comp *ext;
+	size_t num_ext;
+	int err = 0;
+
+	if (copy_from_user(&watch, uwatch, sizeof(watch)))
+		return -EFAULT;
+
+	if (watch.component.type == COUNTER_COMPONENT_NONE)
+		goto no_component;
+
+	parent = watch.component.parent;
+
+	/* Configure parent component info for comp node */
+	switch (watch.component.scope) {
+	case COUNTER_SCOPE_DEVICE:
+		ext = counter->ext;
+		num_ext = counter->num_ext;
+		break;
+	case COUNTER_SCOPE_SIGNAL:
+		if (parent >= counter->num_signals)
+			return -EINVAL;
+		parent = array_index_nospec(parent, counter->num_signals);
+
+		comp_node.parent = counter->signals + parent;
+
+		ext = counter->signals[parent].ext;
+		num_ext = counter->signals[parent].num_ext;
+		break;
+	case COUNTER_SCOPE_COUNT:
+		if (parent >= counter->num_counts)
+			return -EINVAL;
+		parent = array_index_nospec(parent, counter->num_counts);
+
+		comp_node.parent = counter->counts + parent;
+
+		ext = counter->counts[parent].ext;
+		num_ext = counter->counts[parent].num_ext;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	id = watch.component.id;
+
+	/* Configure component info for comp node */
+	switch (watch.component.type) {
+	case COUNTER_COMPONENT_SIGNAL:
+		if (watch.component.scope != COUNTER_SCOPE_SIGNAL)
+			return -EINVAL;
+
+		comp_node.comp.type = COUNTER_COMP_SIGNAL_LEVEL;
+		comp_node.comp.signal_u32_read = counter->ops->signal_read;
+		break;
+	case COUNTER_COMPONENT_COUNT:
+		if (watch.component.scope != COUNTER_SCOPE_COUNT)
+			return -EINVAL;
+
+		comp_node.comp.type = COUNTER_COMP_U64;
+		comp_node.comp.count_u64_read = counter->ops->count_read;
+		break;
+	case COUNTER_COMPONENT_FUNCTION:
+		if (watch.component.scope != COUNTER_SCOPE_COUNT)
+			return -EINVAL;
+
+		comp_node.comp.type = COUNTER_COMP_FUNCTION;
+		comp_node.comp.count_u32_read = counter->ops->function_read;
+		break;
+	case COUNTER_COMPONENT_SYNAPSE_ACTION:
+		if (watch.component.scope != COUNTER_SCOPE_COUNT)
+			return -EINVAL;
+		if (id >= counter->counts[parent].num_synapses)
+			return -EINVAL;
+		id = array_index_nospec(id, counter->counts[parent].num_synapses);
+
+		comp_node.comp.type = COUNTER_COMP_SYNAPSE_ACTION;
+		comp_node.comp.action_read = counter->ops->action_read;
+		comp_node.comp.priv = counter->counts[parent].synapses + id;
+		break;
+	case COUNTER_COMPONENT_EXTENSION:
+		if (id >= num_ext)
+			return -EINVAL;
+		id = array_index_nospec(id, num_ext);
+
+		comp_node.comp = ext[id];
+		break;
+	default:
+		return -EINVAL;
+	}
+	/* Check if any read callback is set; this is part of a union */
+	if (!comp_node.comp.count_u8_read)
+		return -EOPNOTSUPP;
+
+no_component:
+	mutex_lock(&counter->n_events_list_lock);
+
+	if (counter->ops->watch_validate) {
+		err = counter->ops->watch_validate(counter, &watch);
+		if (err < 0)
+			goto err_exit;
+	}
+
+	comp_node.component = watch.component;
+
+	err = counter_set_event_node(counter, &watch, &comp_node);
+
+err_exit:
+	mutex_unlock(&counter->n_events_list_lock);
+
+	return err;
+}
+
+static long counter_chrdev_ioctl(struct file *filp, unsigned int cmd,
+				 unsigned long arg)
+{
+	struct counter_device *const counter = filp->private_data;
+	int ret = -ENODEV;
+
+	mutex_lock(&counter->ops_exist_lock);
+
+	if (!counter->ops)
+		goto out_unlock;
+
+	switch (cmd) {
+	case COUNTER_ADD_WATCH_IOCTL:
+		ret = counter_add_watch(counter, arg);
+		break;
+	case COUNTER_ENABLE_EVENTS_IOCTL:
+		ret = counter_enable_events(counter);
+		break;
+	case COUNTER_DISABLE_EVENTS_IOCTL:
+		ret = counter_disable_events(counter);
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+out_unlock:
+	mutex_unlock(&counter->ops_exist_lock);
+
+	return ret;
+}
+
+static int counter_chrdev_open(struct inode *inode, struct file *filp)
+{
+	struct counter_device *const counter = container_of(inode->i_cdev,
+							    typeof(*counter),
+							    chrdev);
+
+	if (test_and_set_bit_lock(0, counter->chrdev_lock))
+		return -EBUSY;
+
+	get_device(&counter->dev);
+	filp->private_data = counter;
+
+	return nonseekable_open(inode, filp);
+}
+
+static int counter_chrdev_release(struct inode *inode, struct file *filp)
+{
+	struct counter_device *const counter = filp->private_data;
+	int ret = 0;
+
+	mutex_lock(&counter->ops_exist_lock);
+
+	if (!counter->ops) {
+		counter_events_list_free(&counter->events_list);
+		counter_events_list_free(&counter->next_events_list);
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	ret = counter_disable_events(counter);
+	if (ret < 0) {
+		mutex_unlock(&counter->ops_exist_lock);
+		return ret;
+	}
+
+out_unlock:
+	mutex_unlock(&counter->ops_exist_lock);
+
+	put_device(&counter->dev);
+	clear_bit_unlock(0, counter->chrdev_lock);
+
+	return ret;
+}
+
+static const struct file_operations counter_fops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.read = counter_chrdev_read,
+	.poll = counter_chrdev_poll,
+	.unlocked_ioctl = counter_chrdev_ioctl,
+	.open = counter_chrdev_open,
+	.release = counter_chrdev_release,
+};
+
+int counter_chrdev_add(struct counter_device *const counter)
+{
+	/* Initialize Counter events lists */
+	INIT_LIST_HEAD(&counter->events_list);
+	INIT_LIST_HEAD(&counter->next_events_list);
+	spin_lock_init(&counter->events_list_lock);
+	mutex_init(&counter->n_events_list_lock);
+	init_waitqueue_head(&counter->events_wait);
+	mutex_init(&counter->events_lock);
+
+	/* Initialize character device */
+	clear_bit(0, counter->chrdev_lock);
+	cdev_init(&counter->chrdev, &counter_fops);
+
+	/* Allocate Counter events queue */
+	return kfifo_alloc(&counter->events, 64, GFP_KERNEL);
+}
+
+void counter_chrdev_remove(struct counter_device *const counter)
+{
+	kfifo_free(&counter->events);
+}
+
+static int counter_get_data(struct counter_device *const counter,
+			    const struct counter_comp_node *const comp_node,
+			    u64 *const value)
+{
+	const struct counter_comp *const comp = &comp_node->comp;
+	void *const parent = comp_node->parent;
+	u8 value_u8 = 0;
+	u32 value_u32 = 0;
+	int ret;
+
+	if (comp_node->component.type == COUNTER_COMPONENT_NONE)
+		return 0;
+
+	switch (comp->type) {
+	case COUNTER_COMP_U8:
+	case COUNTER_COMP_BOOL:
+		switch (comp_node->component.scope) {
+		case COUNTER_SCOPE_DEVICE:
+			ret = comp->device_u8_read(counter, &value_u8);
+			break;
+		case COUNTER_SCOPE_SIGNAL:
+			ret = comp->signal_u8_read(counter, parent, &value_u8);
+			break;
+		case COUNTER_SCOPE_COUNT:
+			ret = comp->count_u8_read(counter, parent, &value_u8);
+			break;
+		}
+		*value = value_u8;
+		return ret;
+	case COUNTER_COMP_SIGNAL_LEVEL:
+	case COUNTER_COMP_FUNCTION:
+	case COUNTER_COMP_ENUM:
+	case COUNTER_COMP_COUNT_DIRECTION:
+	case COUNTER_COMP_COUNT_MODE:
+		switch (comp_node->component.scope) {
+		case COUNTER_SCOPE_DEVICE:
+			ret = comp->device_u32_read(counter, &value_u32);
+			break;
+		case COUNTER_SCOPE_SIGNAL:
+			ret = comp->signal_u32_read(counter, parent,
+						    &value_u32);
+			break;
+		case COUNTER_SCOPE_COUNT:
+			ret = comp->count_u32_read(counter, parent, &value_u32);
+			break;
+		}
+		*value = value_u32;
+		return ret;
+	case COUNTER_COMP_U64:
+		switch (comp_node->component.scope) {
+		case COUNTER_SCOPE_DEVICE:
+			return comp->device_u64_read(counter, value);
+		case COUNTER_SCOPE_SIGNAL:
+			return comp->signal_u64_read(counter, parent, value);
+		case COUNTER_SCOPE_COUNT:
+			return comp->count_u64_read(counter, parent, value);
+		default:
+			return -EINVAL;
+		}
+	case COUNTER_COMP_SYNAPSE_ACTION:
+		ret = comp->action_read(counter, parent, comp->priv,
+					&value_u32);
+		*value = value_u32;
+		return ret;
+	default:
+		return -EINVAL;
+	}
+}
+
+/**
+ * counter_push_event - queue event for userspace reading
+ * @counter:	pointer to Counter structure
+ * @event:	triggered event
+ * @channel:	event channel
+ *
+ * Note: If no one is watching for the respective event, it is silently
+ * discarded.
+ */
+void counter_push_event(struct counter_device *const counter, const u8 event,
+			const u8 channel)
+{
+	struct counter_event ev;
+	unsigned int copied = 0;
+	unsigned long flags;
+	struct counter_event_node *event_node;
+	struct counter_comp_node *comp_node;
+
+	ev.timestamp = ktime_get_ns();
+	ev.watch.event = event;
+	ev.watch.channel = channel;
+
+	/* Could be in an interrupt context, so use a spin lock */
+	spin_lock_irqsave(&counter->events_list_lock, flags);
+
+	/* Search for event in the list */
+	list_for_each_entry(event_node, &counter->events_list, l)
+		if (event_node->event == event &&
+		    event_node->channel == channel)
+			break;
+
+	/* If event is not in the list */
+	if (&event_node->l == &counter->events_list)
+		goto exit_early;
+
+	/* Read and queue relevant comp for userspace */
+	list_for_each_entry(comp_node, &event_node->comp_list, l) {
+		ev.watch.component = comp_node->component;
+		ev.status = -counter_get_data(counter, comp_node, &ev.value);
+
+		copied += kfifo_in(&counter->events, &ev, 1);
+	}
+
+exit_early:
+	spin_unlock_irqrestore(&counter->events_list_lock, flags);
+
+	if (copied)
+		wake_up_poll(&counter->events_wait, EPOLLIN);
+}
+EXPORT_SYMBOL_GPL(counter_push_event);
diff --git a/drivers/counter/counter-chrdev.h b/drivers/counter/counter-chrdev.h
new file mode 100644
index 000000000000..5529d16703c4
--- /dev/null
+++ b/drivers/counter/counter-chrdev.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Counter character device interface
+ * Copyright (C) 2020 William Breathitt Gray
+ */
+#ifndef _COUNTER_CHRDEV_H_
+#define _COUNTER_CHRDEV_H_
+
+#include <linux/counter.h>
+
+int counter_chrdev_add(struct counter_device *const counter);
+void counter_chrdev_remove(struct counter_device *const counter);
+
+#endif /* _COUNTER_CHRDEV_H_ */
diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
index 3cda2c47bacb..28dc1ff63c94 100644
--- a/drivers/counter/counter-core.c
+++ b/drivers/counter/counter-core.c
@@ -3,14 +3,22 @@
  * Generic Counter interface
  * Copyright (C) 2020 William Breathitt Gray
  */
+#include <linux/cdev.h>
 #include <linux/counter.h>
 #include <linux/device.h>
+#include <linux/device/bus.h>
 #include <linux/export.h>
+#include <linux/fs.h>
 #include <linux/gfp.h>
 #include <linux/idr.h>
 #include <linux/init.h>
+#include <linux/kdev_t.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/wait.h>
 
+#include "counter-chrdev.h"
 #include "counter-sysfs.h"
 
 /* Provides a unique ID for each counter device */
@@ -18,6 +26,9 @@ static DEFINE_IDA(counter_ida);
 
 static void counter_device_release(struct device *dev)
 {
+	struct counter_device *const counter = dev_get_drvdata(dev);
+
+	counter_chrdev_remove(counter);
 	ida_free(&counter_ida, dev->id);
 }
 
@@ -31,6 +42,8 @@ static struct bus_type counter_bus_type = {
 	.dev_name = "counter",
 };
 
+static dev_t counter_devt;
+
 /**
  * counter_register - register Counter to the system
  * @counter:	pointer to Counter to register
@@ -53,10 +66,13 @@ int counter_register(struct counter_device *const counter)
 	if (id < 0)
 		return id;
 
+	mutex_init(&counter->ops_exist_lock);
+
 	/* Configure device structure for Counter */
 	dev->id = id;
 	dev->type = &counter_device_type;
 	dev->bus = &counter_bus_type;
+	dev->devt = MKDEV(MAJOR(counter_devt), id);
 	if (counter->parent) {
 		dev->parent = counter->parent;
 		dev->of_node = counter->parent->of_node;
@@ -64,18 +80,22 @@ int counter_register(struct counter_device *const counter)
 	device_initialize(dev);
 	dev_set_drvdata(dev, counter);
 
-	/* Add Counter sysfs attributes */
-	err = counter_sysfs_add(counter);
+	err = counter_chrdev_add(counter);
 	if (err < 0)
 		goto err_free_id;
 
-	/* Add device to system */
-	err = device_add(dev);
+	err = counter_sysfs_add(counter);
 	if (err < 0)
-		goto err_free_id;
+		goto err_remove_chrdev;
+
+	err = cdev_device_add(&counter->chrdev, dev);
+	if (err < 0)
+		goto err_remove_chrdev;
 
 	return 0;
 
+err_remove_chrdev:
+	counter_chrdev_remove(counter);
 err_free_id:
 	put_device(dev);
 	return err;
@@ -93,7 +113,16 @@ void counter_unregister(struct counter_device *const counter)
 	if (!counter)
 		return;
 
-	device_unregister(&counter->dev);
+	cdev_device_del(&counter->chrdev, &counter->dev);
+
+	mutex_lock(&counter->ops_exist_lock);
+
+	counter->ops = NULL;
+	wake_up(&counter->events_wait);
+
+	mutex_unlock(&counter->ops_exist_lock);
+
+	put_device(&counter->dev);
 }
 EXPORT_SYMBOL_GPL(counter_unregister);
 
@@ -127,13 +156,30 @@ int devm_counter_register(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(devm_counter_register);
 
+#define COUNTER_DEV_MAX 256
+
 static int __init counter_init(void)
 {
-	return bus_register(&counter_bus_type);
+	int err;
+
+	err = bus_register(&counter_bus_type);
+	if (err < 0)
+		return err;
+
+	err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter");
+	if (err < 0)
+		goto err_unregister_bus;
+
+	return 0;
+
+err_unregister_bus:
+	bus_unregister(&counter_bus_type);
+	return err;
 }
 
 static void __exit counter_exit(void)
 {
+	unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX);
 	bus_unregister(&counter_bus_type);
 }
 
diff --git a/include/linux/counter.h b/include/linux/counter.h
index 7c9f7e23953a..9c9984655f8c 100644
--- a/include/linux/counter.h
+++ b/include/linux/counter.h
@@ -6,9 +6,14 @@
 #ifndef _COUNTER_H_
 #define _COUNTER_H_
 
+#include <linux/cdev.h>
 #include <linux/device.h>
 #include <linux/kernel.h>
+#include <linux/kfifo.h>
+#include <linux/mutex.h>
+#include <linux/spinlock_types.h>
 #include <linux/types.h>
+#include <linux/wait.h>
 #include <uapi/linux/counter.h>
 
 struct counter_device;
@@ -199,6 +204,20 @@ struct counter_count {
 	size_t num_ext;
 };
 
+/**
+ * struct counter_event_node - Counter Event node
+ * @l:		list of current watching Counter events
+ * @event:	event that triggers
+ * @channel:	event channel
+ * @comp_list:	list of components to watch when event triggers
+ */
+struct counter_event_node {
+	struct list_head l;
+	u8 event;
+	u8 channel;
+	struct list_head comp_list;
+};
+
 /**
  * struct counter_ops - Callbacks from driver
  * @signal_read:	optional read callback for Signals. The read level of
@@ -222,6 +241,13 @@ struct counter_count {
  * @action_write:	optional write callback for Synapse action modes. The
  *			action mode to write for the respective Synapse is
  *			passed in via the action parameter.
+ * @events_configure:	optional write callback to configure events. The list of
+ *			struct counter_event_node may be accessed via the
+ *			events_list member of the counter parameter.
+ * @watch_validate:	optional callback to validate a watch. The Counter
+ *			component watch configuration is passed in via the watch
+ *			parameter. A return value of 0 indicates a valid Counter
+ *			component watch configuration.
  */
 struct counter_ops {
 	int (*signal_read)(struct counter_device *counter,
@@ -245,6 +271,9 @@ struct counter_ops {
 			    struct counter_count *count,
 			    struct counter_synapse *synapse,
 			    enum counter_synapse_action action);
+	int (*events_configure)(struct counter_device *counter);
+	int (*watch_validate)(struct counter_device *counter,
+			      const struct counter_watch *watch);
 };
 
 /**
@@ -260,6 +289,16 @@ struct counter_ops {
  * @num_ext:		number of Counter device extensions specified in @ext
  * @priv:		optional private data supplied by driver
  * @dev:		internal device structure
+ * @chrdev:		internal character device structure
+ * @events_list:	list of current watching Counter events
+ * @events_list_lock:	lock to protect Counter events list operations
+ * @next_events_list:	list of next watching Counter events
+ * @n_events_list_lock:	lock to protect Counter next events list operations
+ * @events:		queue of detected Counter events
+ * @events_wait:	wait queue to allow blocking reads of Counter events
+ * @events_lock:	lock to protect Counter events queue read operations
+ * @chrdev_lock:	lock to limit chrdev to a single open at a time
+ * @ops_exist_lock:	lock to prevent use during removal
  */
 struct counter_device {
 	const char *name;
@@ -278,12 +317,24 @@ struct counter_device {
 	void *priv;
 
 	struct device dev;
+	struct cdev chrdev;
+	struct list_head events_list;
+	spinlock_t events_list_lock;
+	struct list_head next_events_list;
+	struct mutex n_events_list_lock;
+	DECLARE_KFIFO_PTR(events, struct counter_event);
+	wait_queue_head_t events_wait;
+	struct mutex events_lock;
+	DECLARE_BITMAP(chrdev_lock, 1);
+	struct mutex ops_exist_lock;
 };
 
 int counter_register(struct counter_device *const counter);
 void counter_unregister(struct counter_device *const counter);
 int devm_counter_register(struct device *dev,
 			  struct counter_device *const counter);
+void counter_push_event(struct counter_device *const counter, const u8 event,
+			const u8 channel);
 
 #define COUNTER_COMP_DEVICE_U8(_name, _read, _write) \
 { \
diff --git a/include/uapi/linux/counter.h b/include/uapi/linux/counter.h
index 6113938a6044..d0aa95aeff7b 100644
--- a/include/uapi/linux/counter.h
+++ b/include/uapi/linux/counter.h
@@ -6,6 +6,19 @@
 #ifndef _UAPI_COUNTER_H_
 #define _UAPI_COUNTER_H_
 
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/* Component type definitions */
+enum counter_component_type {
+	COUNTER_COMPONENT_NONE,
+	COUNTER_COMPONENT_SIGNAL,
+	COUNTER_COMPONENT_COUNT,
+	COUNTER_COMPONENT_FUNCTION,
+	COUNTER_COMPONENT_SYNAPSE_ACTION,
+	COUNTER_COMPONENT_EXTENSION,
+};
+
 /* Component scope definitions */
 enum counter_scope {
 	COUNTER_SCOPE_DEVICE,
@@ -13,6 +26,91 @@ enum counter_scope {
 	COUNTER_SCOPE_COUNT,
 };
 
+/**
+ * struct counter_component - Counter component identification
+ * @type: component type (one of enum counter_component_type)
+ * @scope: component scope (one of enum counter_scope)
+ * @parent: parent ID (matching the ID suffix of the respective parent sysfs
+ *          path as described by the ABI documentation file
+ *          Documentation/ABI/testing/sysfs-bus-counter)
+ * @id: component ID (matching the ID provided by the respective *_component_id
+ *      sysfs attribute of the desired component)
+ *
+ * For example, if the Count 2 ceiling extension of Counter device 4 is desired,
+ * set type equal to COUNTER_COMPONENT_EXTENSION, scope equal to
+ * COUNTER_COUNT_SCOPE, parent equal to 2, and id equal to the value provided by
+ * the respective /sys/bus/counter/devices/counter4/count2/ceiling_component_id
+ * sysfs attribute.
+ */
+struct counter_component {
+	__u8 type;
+	__u8 scope;
+	__u8 parent;
+	__u8 id;
+};
+
+/* Event type definitions */
+enum counter_event_type {
+	/* Count value increased past ceiling */
+	COUNTER_EVENT_OVERFLOW,
+	/* Count value decreased past floor */
+	COUNTER_EVENT_UNDERFLOW,
+	/* Count value increased past ceiling, or decreased past floor */
+	COUNTER_EVENT_OVERFLOW_UNDERFLOW,
+	/* Count value reached threshold */
+	COUNTER_EVENT_THRESHOLD,
+	/* Index signal detected */
+	COUNTER_EVENT_INDEX,
+};
+
+/**
+ * struct counter_watch - Counter component watch configuration
+ * @component: component to watch when event triggers
+ * @event: event that triggers (one of enum counter_event_type)
+ * @channel: event channel (typically 0 unless the device supports concurrent
+ *	     events of the same type)
+ */
+struct counter_watch {
+	struct counter_component component;
+	__u8 event;
+	__u8 channel;
+};
+
+/*
+ * Queues a Counter watch for the specified event.
+ *
+ * The queued watches will not be applied until COUNTER_ENABLE_EVENTS_IOCTL is
+ * called.
+ */
+#define COUNTER_ADD_WATCH_IOCTL _IOW(0x3E, 0x00, struct counter_watch)
+/*
+ * Enables monitoring the events specified by the Counter watches that were
+ * queued by COUNTER_ADD_WATCH_IOCTL.
+ *
+ * If events are already enabled, the new set of watches replaces the old one.
+ * Calling this ioctl also has the effect of clearing the queue of watches added
+ * by COUNTER_ADD_WATCH_IOCTL.
+ */
+#define COUNTER_ENABLE_EVENTS_IOCTL _IO(0x3E, 0x01)
+/*
+ * Stops monitoring the previously enabled events.
+ */
+#define COUNTER_DISABLE_EVENTS_IOCTL _IO(0x3E, 0x02)
+
+/**
+ * struct counter_event - Counter event data
+ * @timestamp: best estimate of time of event occurrence, in nanoseconds
+ * @value: component value
+ * @watch: component watch configuration
+ * @status: return status (system error number)
+ */
+struct counter_event {
+	__aligned_u64 timestamp;
+	__aligned_u64 value;
+	struct counter_watch watch;
+	__u8 status;
+};
+
 /* Count direction values */
 enum counter_count_direction {
 	COUNTER_COUNT_DIRECTION_FORWARD,
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 08/14] docs: counter: Document character device interface
  2021-08-27  3:47 ` William Breathitt Gray
@ 2021-08-27  3:47   ` William Breathitt Gray
  -1 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray

This patch adds high-level documentation about the Counter subsystem
character device interface.

Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 Documentation/driver-api/generic-counter.rst  | 177 ++++++++++++++----
 .../userspace-api/ioctl/ioctl-number.rst      |   1 +
 2 files changed, 137 insertions(+), 41 deletions(-)

diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst
index f6397218aa4c..6ecb5d1942c7 100644
--- a/Documentation/driver-api/generic-counter.rst
+++ b/Documentation/driver-api/generic-counter.rst
@@ -223,19 +223,6 @@ whether an input line is differential or single-ended) and instead focus
 on the core idea of what the data and process represent (e.g. position
 as interpreted from quadrature encoding data).
 
-Userspace Interface
-===================
-
-Several sysfs attributes are generated by the Generic Counter interface,
-and reside under the /sys/bus/counter/devices/counterX directory, where
-counterX refers to the respective counter device. Please see
-Documentation/ABI/testing/sysfs-bus-counter for detailed
-information on each Generic Counter interface sysfs attribute.
-
-Through these sysfs attributes, programs and scripts may interact with
-the Generic Counter paradigm Counts, Signals, and Synapses of respective
-counter devices.
-
 Driver API
 ==========
 
@@ -388,16 +375,16 @@ userspace interface components::
                         / driver callbacks /
                         -------------------
                                 |
-                +---------------+
-                |
-                V
-        +--------------------+
-        | Counter sysfs      |
-        +--------------------+
-        | Translates to the  |
-        | standard Counter   |
-        | sysfs output       |
-        +--------------------+
+                +---------------+---------------+
+                |                               |
+                V                               V
+        +--------------------+          +---------------------+
+        | Counter sysfs      |          | Counter chrdev      |
+        +--------------------+          +---------------------+
+        | Translates to the  |          | Translates to the   |
+        | standard Counter   |          | standard Counter    |
+        | sysfs output       |          | character device    |
+        +--------------------+          +---------------------+
 
 Thereafter, data can be transferred directly between the Counter device
 driver and Counter userspace interface::
@@ -428,23 +415,30 @@ driver and Counter userspace interface::
                         / u64     /
                         ----------
                                 |
-                +---------------+
-                |
-                V
-        +--------------------+
-        | Counter sysfs      |
-        +--------------------+
-        | Translates to the  |
-        | standard Counter   |
-        | sysfs output       |
-        |--------------------|
-        | Type: const char * |
-        | Value: "42"        |
-        +--------------------+
-                |
-         ---------------
-        / const char * /
-        ---------------
+                +---------------+---------------+
+                |                               |
+                V                               V
+        +--------------------+          +---------------------+
+        | Counter sysfs      |          | Counter chrdev      |
+        +--------------------+          +---------------------+
+        | Translates to the  |          | Translates to the   |
+        | standard Counter   |          | standard Counter    |
+        | sysfs output       |          | character device    |
+        |--------------------|          |---------------------|
+        | Type: const char * |          | Type: u64           |
+        | Value: "42"        |          | Value: 42           |
+        +--------------------+          +---------------------+
+                |                               |
+         ---------------                 -----------------------
+        / const char * /                / struct counter_event /
+        ---------------                 -----------------------
+                |                               |
+                |                               V
+                |                       +-----------+
+                |                       | read      |
+                |                       +-----------+
+                |                       \ Count: 42 /
+                |                        -----------
                 |
                 V
         +--------------------------------------------------+
@@ -453,7 +447,7 @@ driver and Counter userspace interface::
         \ Count: "42"                                      /
          --------------------------------------------------
 
-There are three primary components involved:
+There are four primary components involved:
 
 Counter device driver
 ---------------------
@@ -473,3 +467,104 @@ and vice versa.
 Please refer to the ``Documentation/ABI/testing/sysfs-bus-counter`` file
 for a detailed breakdown of the available Generic Counter interface
 sysfs attributes.
+
+Counter chrdev
+--------------
+Translates counter data to the standard Counter character device; data
+is transferred via standard character device read calls, while Counter
+events are configured via ioctl calls.
+
+Sysfs Interface
+===============
+
+Several sysfs attributes are generated by the Generic Counter interface,
+and reside under the ``/sys/bus/counter/devices/counterX`` directory,
+where ``X`` is to the respective counter device id. Please see
+``Documentation/ABI/testing/sysfs-bus-counter`` for detailed information
+on each Generic Counter interface sysfs attribute.
+
+Through these sysfs attributes, programs and scripts may interact with
+the Generic Counter paradigm Counts, Signals, and Synapses of respective
+counter devices.
+
+Counter Character Device
+========================
+
+Counter character device nodes are created under the ``/dev`` directory
+as ``counterX``, where ``X`` is the respective counter device id.
+Defines for the standard Counter data types are exposed via the
+userspace ``include/uapi/linux/counter.h`` file.
+
+Counter events
+--------------
+Counter device drivers can support Counter events by utilizing the
+``counter_push_event`` function::
+
+        void counter_push_event(struct counter_device *const counter, const u8 event,
+                                const u8 channel);
+
+The event id is specified by the ``event`` parameter; the event channel
+id is specified by the ``channel`` parameter. When this function is
+called, the Counter data associated with the respective event is
+gathered, and a ``struct counter_event`` is generated for each datum and
+pushed to userspace.
+
+Counter events can be configured by users to report various Counter
+data of interest. This can be conceptualized as a list of Counter
+component read calls to perform. For example:
+
+        +------------------------+------------------------+
+        | COUNTER_EVENT_OVERFLOW | COUNTER_EVENT_INDEX    |
+        +========================+========================+
+        | Channel 0              | Channel 0              |
+        +------------------------+------------------------+
+        | * Count 0              | * Signal 0             |
+        | * Count 1              | * Signal 0 Extension 0 |
+        | * Signal 3             | * Extension 4          |
+        | * Count 4 Extension 2  +------------------------+
+        | * Signal 5 Extension 0 | Channel 1              |
+        |                        +------------------------+
+        |                        | * Signal 4             |
+        |                        | * Signal 4 Extension 0 |
+        |                        | * Count 7              |
+        +------------------------+------------------------+
+
+When ``counter_push_event(counter, COUNTER_EVENT_INDEX, 1)`` is called
+for example, it will go down the list for the ``COUNTER_EVENT_INDEX``
+event channel 1 and execute the read callbacks for Signal 4, Signal 4
+Extension 0, and Count 7 -- the data returned for each is pushed to a
+kfifo as a ``struct counter_event``, which userspace can retrieve via a
+standard read operation on the respective character device node.
+
+Userspace
+---------
+Userspace applications can configure Counter events via ioctl operations
+on the Counter character device node. There following ioctl codes are
+supported and provided by the ``linux/counter.h`` userspace header file:
+
+* :c:macro:`COUNTER_ADD_WATCH_IOCTL`
+
+* :c:macro:`COUNTER_ENABLE_EVENTS_IOCTL`
+
+* :c:macro:`COUNTER_DISABLE_EVENTS_IOCTL`
+
+To configure events to gather Counter data, users first populate a
+``struct counter_watch`` with the relevant event id, event channel id,
+and the information for the desired Counter component from which to
+read, and then pass it via the ``COUNTER_ADD_WATCH_IOCTL`` ioctl
+command.
+
+Note that an event can be watched without gathering Counter data by
+setting the ``component.type`` member equal to
+``COUNTER_COMPONENT_NONE``. With this configuration the Counter
+character device will simply populate the event timestamps for those
+respective ``struct counter_event`` elements and ignore the component
+value.
+
+The ``COUNTER_ADD_WATCH_IOCTL`` command will buffer these Counter
+watches. When ready, the ``COUNTER_ENABLE_EVENTS_IOCTL`` ioctl command
+may be used to activate these Counter watches.
+
+Userspace applications can then execute a ``read`` operation (optionally
+calling ``poll`` first) on the Counter character device node to retrieve
+``struct counter_event`` elements with the desired data.
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index 1409e40e6345..fc4ccc79b1b8 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -88,6 +88,7 @@ Code  Seq#    Include File                                           Comments
                                                                      <http://infiniband.sourceforge.net/>
 0x20  all    drivers/cdrom/cm206.h
 0x22  all    scsi/sg.h
+0x3E  00-0F  linux/counter.h                                         <mailto:linux-iio@vger.kernel.org>
 '!'   00-1F  uapi/linux/seccomp.h
 '#'   00-3F                                                          IEEE 1394 Subsystem
                                                                      Block for the entire subsystem
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 08/14] docs: counter: Document character device interface
@ 2021-08-27  3:47   ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray

This patch adds high-level documentation about the Counter subsystem
character device interface.

Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 Documentation/driver-api/generic-counter.rst  | 177 ++++++++++++++----
 .../userspace-api/ioctl/ioctl-number.rst      |   1 +
 2 files changed, 137 insertions(+), 41 deletions(-)

diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst
index f6397218aa4c..6ecb5d1942c7 100644
--- a/Documentation/driver-api/generic-counter.rst
+++ b/Documentation/driver-api/generic-counter.rst
@@ -223,19 +223,6 @@ whether an input line is differential or single-ended) and instead focus
 on the core idea of what the data and process represent (e.g. position
 as interpreted from quadrature encoding data).
 
-Userspace Interface
-===================
-
-Several sysfs attributes are generated by the Generic Counter interface,
-and reside under the /sys/bus/counter/devices/counterX directory, where
-counterX refers to the respective counter device. Please see
-Documentation/ABI/testing/sysfs-bus-counter for detailed
-information on each Generic Counter interface sysfs attribute.
-
-Through these sysfs attributes, programs and scripts may interact with
-the Generic Counter paradigm Counts, Signals, and Synapses of respective
-counter devices.
-
 Driver API
 ==========
 
@@ -388,16 +375,16 @@ userspace interface components::
                         / driver callbacks /
                         -------------------
                                 |
-                +---------------+
-                |
-                V
-        +--------------------+
-        | Counter sysfs      |
-        +--------------------+
-        | Translates to the  |
-        | standard Counter   |
-        | sysfs output       |
-        +--------------------+
+                +---------------+---------------+
+                |                               |
+                V                               V
+        +--------------------+          +---------------------+
+        | Counter sysfs      |          | Counter chrdev      |
+        +--------------------+          +---------------------+
+        | Translates to the  |          | Translates to the   |
+        | standard Counter   |          | standard Counter    |
+        | sysfs output       |          | character device    |
+        +--------------------+          +---------------------+
 
 Thereafter, data can be transferred directly between the Counter device
 driver and Counter userspace interface::
@@ -428,23 +415,30 @@ driver and Counter userspace interface::
                         / u64     /
                         ----------
                                 |
-                +---------------+
-                |
-                V
-        +--------------------+
-        | Counter sysfs      |
-        +--------------------+
-        | Translates to the  |
-        | standard Counter   |
-        | sysfs output       |
-        |--------------------|
-        | Type: const char * |
-        | Value: "42"        |
-        +--------------------+
-                |
-         ---------------
-        / const char * /
-        ---------------
+                +---------------+---------------+
+                |                               |
+                V                               V
+        +--------------------+          +---------------------+
+        | Counter sysfs      |          | Counter chrdev      |
+        +--------------------+          +---------------------+
+        | Translates to the  |          | Translates to the   |
+        | standard Counter   |          | standard Counter    |
+        | sysfs output       |          | character device    |
+        |--------------------|          |---------------------|
+        | Type: const char * |          | Type: u64           |
+        | Value: "42"        |          | Value: 42           |
+        +--------------------+          +---------------------+
+                |                               |
+         ---------------                 -----------------------
+        / const char * /                / struct counter_event /
+        ---------------                 -----------------------
+                |                               |
+                |                               V
+                |                       +-----------+
+                |                       | read      |
+                |                       +-----------+
+                |                       \ Count: 42 /
+                |                        -----------
                 |
                 V
         +--------------------------------------------------+
@@ -453,7 +447,7 @@ driver and Counter userspace interface::
         \ Count: "42"                                      /
          --------------------------------------------------
 
-There are three primary components involved:
+There are four primary components involved:
 
 Counter device driver
 ---------------------
@@ -473,3 +467,104 @@ and vice versa.
 Please refer to the ``Documentation/ABI/testing/sysfs-bus-counter`` file
 for a detailed breakdown of the available Generic Counter interface
 sysfs attributes.
+
+Counter chrdev
+--------------
+Translates counter data to the standard Counter character device; data
+is transferred via standard character device read calls, while Counter
+events are configured via ioctl calls.
+
+Sysfs Interface
+===============
+
+Several sysfs attributes are generated by the Generic Counter interface,
+and reside under the ``/sys/bus/counter/devices/counterX`` directory,
+where ``X`` is to the respective counter device id. Please see
+``Documentation/ABI/testing/sysfs-bus-counter`` for detailed information
+on each Generic Counter interface sysfs attribute.
+
+Through these sysfs attributes, programs and scripts may interact with
+the Generic Counter paradigm Counts, Signals, and Synapses of respective
+counter devices.
+
+Counter Character Device
+========================
+
+Counter character device nodes are created under the ``/dev`` directory
+as ``counterX``, where ``X`` is the respective counter device id.
+Defines for the standard Counter data types are exposed via the
+userspace ``include/uapi/linux/counter.h`` file.
+
+Counter events
+--------------
+Counter device drivers can support Counter events by utilizing the
+``counter_push_event`` function::
+
+        void counter_push_event(struct counter_device *const counter, const u8 event,
+                                const u8 channel);
+
+The event id is specified by the ``event`` parameter; the event channel
+id is specified by the ``channel`` parameter. When this function is
+called, the Counter data associated with the respective event is
+gathered, and a ``struct counter_event`` is generated for each datum and
+pushed to userspace.
+
+Counter events can be configured by users to report various Counter
+data of interest. This can be conceptualized as a list of Counter
+component read calls to perform. For example:
+
+        +------------------------+------------------------+
+        | COUNTER_EVENT_OVERFLOW | COUNTER_EVENT_INDEX    |
+        +========================+========================+
+        | Channel 0              | Channel 0              |
+        +------------------------+------------------------+
+        | * Count 0              | * Signal 0             |
+        | * Count 1              | * Signal 0 Extension 0 |
+        | * Signal 3             | * Extension 4          |
+        | * Count 4 Extension 2  +------------------------+
+        | * Signal 5 Extension 0 | Channel 1              |
+        |                        +------------------------+
+        |                        | * Signal 4             |
+        |                        | * Signal 4 Extension 0 |
+        |                        | * Count 7              |
+        +------------------------+------------------------+
+
+When ``counter_push_event(counter, COUNTER_EVENT_INDEX, 1)`` is called
+for example, it will go down the list for the ``COUNTER_EVENT_INDEX``
+event channel 1 and execute the read callbacks for Signal 4, Signal 4
+Extension 0, and Count 7 -- the data returned for each is pushed to a
+kfifo as a ``struct counter_event``, which userspace can retrieve via a
+standard read operation on the respective character device node.
+
+Userspace
+---------
+Userspace applications can configure Counter events via ioctl operations
+on the Counter character device node. There following ioctl codes are
+supported and provided by the ``linux/counter.h`` userspace header file:
+
+* :c:macro:`COUNTER_ADD_WATCH_IOCTL`
+
+* :c:macro:`COUNTER_ENABLE_EVENTS_IOCTL`
+
+* :c:macro:`COUNTER_DISABLE_EVENTS_IOCTL`
+
+To configure events to gather Counter data, users first populate a
+``struct counter_watch`` with the relevant event id, event channel id,
+and the information for the desired Counter component from which to
+read, and then pass it via the ``COUNTER_ADD_WATCH_IOCTL`` ioctl
+command.
+
+Note that an event can be watched without gathering Counter data by
+setting the ``component.type`` member equal to
+``COUNTER_COMPONENT_NONE``. With this configuration the Counter
+character device will simply populate the event timestamps for those
+respective ``struct counter_event`` elements and ignore the component
+value.
+
+The ``COUNTER_ADD_WATCH_IOCTL`` command will buffer these Counter
+watches. When ready, the ``COUNTER_ENABLE_EVENTS_IOCTL`` ioctl command
+may be used to activate these Counter watches.
+
+Userspace applications can then execute a ``read`` operation (optionally
+calling ``poll`` first) on the Counter character device node to retrieve
+``struct counter_event`` elements with the desired data.
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index 1409e40e6345..fc4ccc79b1b8 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -88,6 +88,7 @@ Code  Seq#    Include File                                           Comments
                                                                      <http://infiniband.sourceforge.net/>
 0x20  all    drivers/cdrom/cm206.h
 0x22  all    scsi/sg.h
+0x3E  00-0F  linux/counter.h                                         <mailto:linux-iio@vger.kernel.org>
 '!'   00-1F  uapi/linux/seccomp.h
 '#'   00-3F                                                          IEEE 1394 Subsystem
                                                                      Block for the entire subsystem
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 09/14] tools/counter: Create Counter tools
  2021-08-27  3:47 ` William Breathitt Gray
@ 2021-08-27  3:47   ` William Breathitt Gray
  -1 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray, Pavel Machek

This creates an example Counter program under tools/counter/*
to exemplify the Counter character device interface.

Cc: Pavel Machek <pavel@ucw.cz>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 MAINTAINERS                     |  1 +
 tools/Makefile                  | 13 ++---
 tools/counter/Build             |  1 +
 tools/counter/Makefile          | 53 +++++++++++++++++++
 tools/counter/counter_example.c | 93 +++++++++++++++++++++++++++++++++
 5 files changed, 155 insertions(+), 6 deletions(-)
 create mode 100644 tools/counter/Build
 create mode 100644 tools/counter/Makefile
 create mode 100644 tools/counter/counter_example.c

diff --git a/MAINTAINERS b/MAINTAINERS
index f2fdd2202605..57dc9b6ff82a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4782,6 +4782,7 @@ F:	Documentation/driver-api/generic-counter.rst
 F:	drivers/counter/
 F:	include/linux/counter.h
 F:	include/uapi/linux/counter.h
+F:	tools/counter/
 
 CP2615 I2C DRIVER
 M:	Bence Csókás <bence98@sch.bme.hu>
diff --git a/tools/Makefile b/tools/Makefile
index 7e9d34ddd74c..5da1fde03a9a 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -12,6 +12,7 @@ help:
 	@echo '  acpi                   - ACPI tools'
 	@echo '  bpf                    - misc BPF tools'
 	@echo '  cgroup                 - cgroup tools'
+	@echo '  counter                - counter tools'
 	@echo '  cpupower               - a tool for all things x86 CPU power'
 	@echo '  debugging              - tools for debugging'
 	@echo '  firewire               - the userspace part of nosy, an IEEE-1394 traffic sniffer'
@@ -65,7 +66,7 @@ acpi: FORCE
 cpupower: FORCE
 	$(call descend,power/$@)
 
-cgroup firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE
+cgroup counter firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE
 	$(call descend,$@)
 
 bpf/%: FORCE
@@ -100,7 +101,7 @@ freefall: FORCE
 kvm_stat: FORCE
 	$(call descend,kvm/$@)
 
-all: acpi cgroup cpupower gpio hv firewire liblockdep \
+all: acpi cgroup counter cpupower gpio hv firewire liblockdep \
 		perf selftests bootconfig spi turbostat usb \
 		virtio vm bpf x86_energy_perf_policy \
 		tmon freefall iio objtool kvm_stat wmi \
@@ -112,7 +113,7 @@ acpi_install:
 cpupower_install:
 	$(call descend,power/$(@:_install=),install)
 
-cgroup_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install:
+cgroup_install counter_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install:
 	$(call descend,$(@:_install=),install)
 
 liblockdep_install:
@@ -133,7 +134,7 @@ freefall_install:
 kvm_stat_install:
 	$(call descend,kvm/$(@:_install=),install)
 
-install: acpi_install cgroup_install cpupower_install gpio_install \
+install: acpi_install cgroup_install counter_install cpupower_install gpio_install \
 		hv_install firewire_install iio_install liblockdep_install \
 		perf_install selftests_install turbostat_install usb_install \
 		virtio_install vm_install bpf_install x86_energy_perf_policy_install \
@@ -147,7 +148,7 @@ acpi_clean:
 cpupower_clean:
 	$(call descend,power/cpupower,clean)
 
-cgroup_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean:
+cgroup_clean counter_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean:
 	$(call descend,$(@:_clean=),clean)
 
 liblockdep_clean:
@@ -181,7 +182,7 @@ freefall_clean:
 build_clean:
 	$(call descend,build,clean)
 
-clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
+clean: acpi_clean cgroup_clean counter_clean cpupower_clean hv_clean firewire_clean \
 		perf_clean selftests_clean turbostat_clean bootconfig_clean spi_clean usb_clean virtio_clean \
 		vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
 		freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
diff --git a/tools/counter/Build b/tools/counter/Build
new file mode 100644
index 000000000000..33f4a51d715e
--- /dev/null
+++ b/tools/counter/Build
@@ -0,0 +1 @@
+counter_example-y += counter_example.o
diff --git a/tools/counter/Makefile b/tools/counter/Makefile
new file mode 100644
index 000000000000..5ebc195fd9c0
--- /dev/null
+++ b/tools/counter/Makefile
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../scripts/Makefile.include
+
+bindir ?= /usr/bin
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(CURDIR)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+endif
+
+# Do not use make's built-in rules
+# (this improves performance and avoids hard-to-debug behaviour);
+MAKEFLAGS += -r
+
+override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
+
+ALL_TARGETS := counter_example
+ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
+
+all: $(ALL_PROGRAMS)
+
+export srctree OUTPUT CC LD CFLAGS
+include $(srctree)/tools/build/Makefile.include
+
+#
+# We need the following to be outside of kernel tree
+#
+$(OUTPUT)include/linux/counter.h: ../../include/uapi/linux/counter.h
+	mkdir -p $(OUTPUT)include/linux 2>&1 || true
+	ln -sf $(CURDIR)/../../include/uapi/linux/counter.h $@
+
+prepare: $(OUTPUT)include/linux/counter.h
+
+COUNTER_EXAMPLE := $(OUTPUT)counter_example.o
+$(COUNTER_EXAMPLE): prepare FORCE
+	$(Q)$(MAKE) $(build)=counter_example
+$(OUTPUT)counter_example: $(COUNTER_EXAMPLE)
+	$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
+
+clean:
+	rm -f $(ALL_PROGRAMS)
+	rm -rf $(OUTPUT)include/linux/counter.h
+	find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete
+
+install: $(ALL_PROGRAMS)
+	install -d -m 755 $(DESTDIR)$(bindir);		\
+	for program in $(ALL_PROGRAMS); do		\
+		install $$program $(DESTDIR)$(bindir);	\
+	done
+
+FORCE:
+
+.PHONY: all install clean FORCE prepare
diff --git a/tools/counter/counter_example.c b/tools/counter/counter_example.c
new file mode 100644
index 000000000000..90d69fb9463b
--- /dev/null
+++ b/tools/counter/counter_example.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Counter - example userspace application
+ *
+ * The userspace application opens /dev/counter0, configures the
+ * COUNTER_EVENT_INDEX event channel 0 to gather Count 0 count and Count
+ * 1 count, and prints out the data as it becomes available on the
+ * character device node.
+ *
+ * Copyright (C) 2021 William Breathitt Gray
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/counter.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+static struct counter_watch watches[2] = {
+	{
+		/* Component data: Count 0 count */
+		.component.type = COUNTER_COMPONENT_COUNT,
+		.component.scope = COUNTER_SCOPE_COUNT,
+		.component.parent = 0,
+		/* Event type: Index */
+		.event = COUNTER_EVENT_INDEX,
+		/* Device event channel 0 */
+		.channel = 0,
+	},
+	{
+		/* Component data: Count 1 count */
+		.component.type = COUNTER_COMPONENT_COUNT,
+		.component.scope = COUNTER_SCOPE_COUNT,
+		.component.parent = 1,
+		/* Event type: Index */
+		.event = COUNTER_EVENT_INDEX,
+		/* Device event channel 0 */
+		.channel = 0,
+	},
+};
+
+int main(void)
+{
+	int fd;
+	int ret;
+	struct counter_event event_data[2];
+
+	fd = open("/dev/counter0", O_RDWR);
+	if (fd == -1) {
+		perror("Unable to open /dev/counter0");
+		return -errno;
+	}
+
+	ret = ioctl(fd, COUNTER_ADD_WATCH_IOCTL, watches);
+	if (ret == -1) {
+		perror("Error adding watches[0]");
+		return -errno;
+	}
+	ret = ioctl(fd, COUNTER_ADD_WATCH_IOCTL, watches + 1);
+	if (ret == -1) {
+		perror("Error adding watches[1]");
+		return -errno;
+	}
+	ret = ioctl(fd, COUNTER_ENABLE_EVENTS_IOCTL);
+	if (ret == -1) {
+		perror("Error enabling events");
+		return -errno;
+	}
+
+	for (;;) {
+		ret = read(fd, event_data, sizeof(event_data));
+		if (ret == -1) {
+			perror("Failed to read event data");
+			return 1;
+		}
+
+		if (ret != sizeof(event_data)) {
+			fprintf(stderr, "Failed to read event data\n");
+			return -EIO;
+		}
+
+		printf("Timestamp 0: %llu\tCount 0: %llu\n"
+		       "Error Message 0: %s\n"
+		       "Timestamp 1: %llu\tCount 1: %llu\n"
+		       "Error Message 1: %s\n",
+		       event_data[0].timestamp, event_data[0].value,
+		       strerror(event_data[0].status),
+		       event_data[1].timestamp, event_data[1].value,
+		       strerror(event_data[1].status));
+	}
+
+	return 0;
+}
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 09/14] tools/counter: Create Counter tools
@ 2021-08-27  3:47   ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray, Pavel Machek

This creates an example Counter program under tools/counter/*
to exemplify the Counter character device interface.

Cc: Pavel Machek <pavel@ucw.cz>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 MAINTAINERS                     |  1 +
 tools/Makefile                  | 13 ++---
 tools/counter/Build             |  1 +
 tools/counter/Makefile          | 53 +++++++++++++++++++
 tools/counter/counter_example.c | 93 +++++++++++++++++++++++++++++++++
 5 files changed, 155 insertions(+), 6 deletions(-)
 create mode 100644 tools/counter/Build
 create mode 100644 tools/counter/Makefile
 create mode 100644 tools/counter/counter_example.c

diff --git a/MAINTAINERS b/MAINTAINERS
index f2fdd2202605..57dc9b6ff82a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4782,6 +4782,7 @@ F:	Documentation/driver-api/generic-counter.rst
 F:	drivers/counter/
 F:	include/linux/counter.h
 F:	include/uapi/linux/counter.h
+F:	tools/counter/
 
 CP2615 I2C DRIVER
 M:	Bence Csókás <bence98@sch.bme.hu>
diff --git a/tools/Makefile b/tools/Makefile
index 7e9d34ddd74c..5da1fde03a9a 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -12,6 +12,7 @@ help:
 	@echo '  acpi                   - ACPI tools'
 	@echo '  bpf                    - misc BPF tools'
 	@echo '  cgroup                 - cgroup tools'
+	@echo '  counter                - counter tools'
 	@echo '  cpupower               - a tool for all things x86 CPU power'
 	@echo '  debugging              - tools for debugging'
 	@echo '  firewire               - the userspace part of nosy, an IEEE-1394 traffic sniffer'
@@ -65,7 +66,7 @@ acpi: FORCE
 cpupower: FORCE
 	$(call descend,power/$@)
 
-cgroup firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE
+cgroup counter firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE
 	$(call descend,$@)
 
 bpf/%: FORCE
@@ -100,7 +101,7 @@ freefall: FORCE
 kvm_stat: FORCE
 	$(call descend,kvm/$@)
 
-all: acpi cgroup cpupower gpio hv firewire liblockdep \
+all: acpi cgroup counter cpupower gpio hv firewire liblockdep \
 		perf selftests bootconfig spi turbostat usb \
 		virtio vm bpf x86_energy_perf_policy \
 		tmon freefall iio objtool kvm_stat wmi \
@@ -112,7 +113,7 @@ acpi_install:
 cpupower_install:
 	$(call descend,power/$(@:_install=),install)
 
-cgroup_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install:
+cgroup_install counter_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install:
 	$(call descend,$(@:_install=),install)
 
 liblockdep_install:
@@ -133,7 +134,7 @@ freefall_install:
 kvm_stat_install:
 	$(call descend,kvm/$(@:_install=),install)
 
-install: acpi_install cgroup_install cpupower_install gpio_install \
+install: acpi_install cgroup_install counter_install cpupower_install gpio_install \
 		hv_install firewire_install iio_install liblockdep_install \
 		perf_install selftests_install turbostat_install usb_install \
 		virtio_install vm_install bpf_install x86_energy_perf_policy_install \
@@ -147,7 +148,7 @@ acpi_clean:
 cpupower_clean:
 	$(call descend,power/cpupower,clean)
 
-cgroup_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean:
+cgroup_clean counter_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean:
 	$(call descend,$(@:_clean=),clean)
 
 liblockdep_clean:
@@ -181,7 +182,7 @@ freefall_clean:
 build_clean:
 	$(call descend,build,clean)
 
-clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
+clean: acpi_clean cgroup_clean counter_clean cpupower_clean hv_clean firewire_clean \
 		perf_clean selftests_clean turbostat_clean bootconfig_clean spi_clean usb_clean virtio_clean \
 		vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
 		freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
diff --git a/tools/counter/Build b/tools/counter/Build
new file mode 100644
index 000000000000..33f4a51d715e
--- /dev/null
+++ b/tools/counter/Build
@@ -0,0 +1 @@
+counter_example-y += counter_example.o
diff --git a/tools/counter/Makefile b/tools/counter/Makefile
new file mode 100644
index 000000000000..5ebc195fd9c0
--- /dev/null
+++ b/tools/counter/Makefile
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../scripts/Makefile.include
+
+bindir ?= /usr/bin
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(CURDIR)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+endif
+
+# Do not use make's built-in rules
+# (this improves performance and avoids hard-to-debug behaviour);
+MAKEFLAGS += -r
+
+override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
+
+ALL_TARGETS := counter_example
+ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
+
+all: $(ALL_PROGRAMS)
+
+export srctree OUTPUT CC LD CFLAGS
+include $(srctree)/tools/build/Makefile.include
+
+#
+# We need the following to be outside of kernel tree
+#
+$(OUTPUT)include/linux/counter.h: ../../include/uapi/linux/counter.h
+	mkdir -p $(OUTPUT)include/linux 2>&1 || true
+	ln -sf $(CURDIR)/../../include/uapi/linux/counter.h $@
+
+prepare: $(OUTPUT)include/linux/counter.h
+
+COUNTER_EXAMPLE := $(OUTPUT)counter_example.o
+$(COUNTER_EXAMPLE): prepare FORCE
+	$(Q)$(MAKE) $(build)=counter_example
+$(OUTPUT)counter_example: $(COUNTER_EXAMPLE)
+	$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
+
+clean:
+	rm -f $(ALL_PROGRAMS)
+	rm -rf $(OUTPUT)include/linux/counter.h
+	find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete
+
+install: $(ALL_PROGRAMS)
+	install -d -m 755 $(DESTDIR)$(bindir);		\
+	for program in $(ALL_PROGRAMS); do		\
+		install $$program $(DESTDIR)$(bindir);	\
+	done
+
+FORCE:
+
+.PHONY: all install clean FORCE prepare
diff --git a/tools/counter/counter_example.c b/tools/counter/counter_example.c
new file mode 100644
index 000000000000..90d69fb9463b
--- /dev/null
+++ b/tools/counter/counter_example.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Counter - example userspace application
+ *
+ * The userspace application opens /dev/counter0, configures the
+ * COUNTER_EVENT_INDEX event channel 0 to gather Count 0 count and Count
+ * 1 count, and prints out the data as it becomes available on the
+ * character device node.
+ *
+ * Copyright (C) 2021 William Breathitt Gray
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/counter.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+static struct counter_watch watches[2] = {
+	{
+		/* Component data: Count 0 count */
+		.component.type = COUNTER_COMPONENT_COUNT,
+		.component.scope = COUNTER_SCOPE_COUNT,
+		.component.parent = 0,
+		/* Event type: Index */
+		.event = COUNTER_EVENT_INDEX,
+		/* Device event channel 0 */
+		.channel = 0,
+	},
+	{
+		/* Component data: Count 1 count */
+		.component.type = COUNTER_COMPONENT_COUNT,
+		.component.scope = COUNTER_SCOPE_COUNT,
+		.component.parent = 1,
+		/* Event type: Index */
+		.event = COUNTER_EVENT_INDEX,
+		/* Device event channel 0 */
+		.channel = 0,
+	},
+};
+
+int main(void)
+{
+	int fd;
+	int ret;
+	struct counter_event event_data[2];
+
+	fd = open("/dev/counter0", O_RDWR);
+	if (fd == -1) {
+		perror("Unable to open /dev/counter0");
+		return -errno;
+	}
+
+	ret = ioctl(fd, COUNTER_ADD_WATCH_IOCTL, watches);
+	if (ret == -1) {
+		perror("Error adding watches[0]");
+		return -errno;
+	}
+	ret = ioctl(fd, COUNTER_ADD_WATCH_IOCTL, watches + 1);
+	if (ret == -1) {
+		perror("Error adding watches[1]");
+		return -errno;
+	}
+	ret = ioctl(fd, COUNTER_ENABLE_EVENTS_IOCTL);
+	if (ret == -1) {
+		perror("Error enabling events");
+		return -errno;
+	}
+
+	for (;;) {
+		ret = read(fd, event_data, sizeof(event_data));
+		if (ret == -1) {
+			perror("Failed to read event data");
+			return 1;
+		}
+
+		if (ret != sizeof(event_data)) {
+			fprintf(stderr, "Failed to read event data\n");
+			return -EIO;
+		}
+
+		printf("Timestamp 0: %llu\tCount 0: %llu\n"
+		       "Error Message 0: %s\n"
+		       "Timestamp 1: %llu\tCount 1: %llu\n"
+		       "Error Message 1: %s\n",
+		       event_data[0].timestamp, event_data[0].value,
+		       strerror(event_data[0].status),
+		       event_data[1].timestamp, event_data[1].value,
+		       strerror(event_data[1].status));
+	}
+
+	return 0;
+}
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 10/14] counter: Implement signalZ_action_component_id sysfs attribute
  2021-08-27  3:47 ` William Breathitt Gray
@ 2021-08-27  3:47   ` William Breathitt Gray
  -1 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray, Dan Carpenter

The Generic Counter chrdev interface expects users to supply component
IDs in order to select Synapses for requests. In order for users to know
what component ID belongs to which Synapse this information must be
exposed. The signalZ_action_component_id attribute provides a way for
users to discover what component ID belongs to the respective Synapse.

Cc: Gwendal Grignou <gwendal@chromium.org>
Cc: Dan Carpenter <dan.carpenter@oracle.com>
Reviewed-by: David Lechner <david@lechnology.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 Documentation/ABI/testing/sysfs-bus-counter |  7 ++++
 drivers/counter/counter-sysfs.c             | 45 +++++++++++++++++++++
 2 files changed, 52 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter
index dee79b606847..aa2cd3c10d74 100644
--- a/Documentation/ABI/testing/sysfs-bus-counter
+++ b/Documentation/ABI/testing/sysfs-bus-counter
@@ -203,6 +203,13 @@ Description:
 		both edges:
 			Any state transition.
 
+What:		/sys/bus/counter/devices/counterX/countY/signalZ_action_component_id
+KernelVersion:	5.16
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Read-only attribute that indicates the component ID of the
+		respective Synapse of Count Y for Signal Z.
+
 What:		/sys/bus/counter/devices/counterX/countY/spike_filter_ns
 KernelVersion:	5.14
 Contact:	linux-iio@vger.kernel.org
diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
index 108cbd838eb9..ee7ff1cde454 100644
--- a/drivers/counter/counter-sysfs.c
+++ b/drivers/counter/counter-sysfs.c
@@ -532,6 +532,46 @@ static int counter_name_attr_create(struct device *const dev,
 	return 0;
 }
 
+static ssize_t counter_comp_id_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	const size_t id = (size_t)to_counter_attribute(attr)->comp.priv;
+
+	return sprintf(buf, "%zu\n", id);
+}
+
+static int counter_comp_id_attr_create(struct device *const dev,
+				       struct counter_attribute_group *const group,
+				       const char *name, const size_t id)
+{
+	struct counter_attribute *counter_attr;
+
+	/* Allocate Counter attribute */
+	counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
+	if (!counter_attr)
+		return -ENOMEM;
+
+	/* Generate component ID name */
+	name = devm_kasprintf(dev, GFP_KERNEL, "%s_component_id", name);
+	if (!name)
+		return -ENOMEM;
+
+	/* Configure Counter attribute */
+	counter_attr->comp.priv = (void *)id;
+
+	/* Configure device attribute */
+	sysfs_attr_init(&counter_attr->dev_attr.attr);
+	counter_attr->dev_attr.attr.name = name;
+	counter_attr->dev_attr.attr.mode = 0444;
+	counter_attr->dev_attr.show = counter_comp_id_show;
+
+	/* Store list node */
+	list_add(&counter_attr->l, &group->attr_list);
+	group->num_attr++;
+
+	return 0;
+}
+
 static struct counter_comp counter_signal_comp = {
 	.type = COUNTER_COMP_SIGNAL_LEVEL,
 	.name = "signal",
@@ -626,6 +666,11 @@ static int counter_sysfs_synapses_add(struct counter_device *const counter,
 					  COUNTER_SCOPE_COUNT, count);
 		if (err < 0)
 			return err;
+
+		/* Create Synapse component ID attribute */
+		err = counter_comp_id_attr_create(dev, group, comp.name, i);
+		if (err < 0)
+			return err;
 	}
 
 	return 0;
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 10/14] counter: Implement signalZ_action_component_id sysfs attribute
@ 2021-08-27  3:47   ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray, Dan Carpenter

The Generic Counter chrdev interface expects users to supply component
IDs in order to select Synapses for requests. In order for users to know
what component ID belongs to which Synapse this information must be
exposed. The signalZ_action_component_id attribute provides a way for
users to discover what component ID belongs to the respective Synapse.

Cc: Gwendal Grignou <gwendal@chromium.org>
Cc: Dan Carpenter <dan.carpenter@oracle.com>
Reviewed-by: David Lechner <david@lechnology.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 Documentation/ABI/testing/sysfs-bus-counter |  7 ++++
 drivers/counter/counter-sysfs.c             | 45 +++++++++++++++++++++
 2 files changed, 52 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter
index dee79b606847..aa2cd3c10d74 100644
--- a/Documentation/ABI/testing/sysfs-bus-counter
+++ b/Documentation/ABI/testing/sysfs-bus-counter
@@ -203,6 +203,13 @@ Description:
 		both edges:
 			Any state transition.
 
+What:		/sys/bus/counter/devices/counterX/countY/signalZ_action_component_id
+KernelVersion:	5.16
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Read-only attribute that indicates the component ID of the
+		respective Synapse of Count Y for Signal Z.
+
 What:		/sys/bus/counter/devices/counterX/countY/spike_filter_ns
 KernelVersion:	5.14
 Contact:	linux-iio@vger.kernel.org
diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
index 108cbd838eb9..ee7ff1cde454 100644
--- a/drivers/counter/counter-sysfs.c
+++ b/drivers/counter/counter-sysfs.c
@@ -532,6 +532,46 @@ static int counter_name_attr_create(struct device *const dev,
 	return 0;
 }
 
+static ssize_t counter_comp_id_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	const size_t id = (size_t)to_counter_attribute(attr)->comp.priv;
+
+	return sprintf(buf, "%zu\n", id);
+}
+
+static int counter_comp_id_attr_create(struct device *const dev,
+				       struct counter_attribute_group *const group,
+				       const char *name, const size_t id)
+{
+	struct counter_attribute *counter_attr;
+
+	/* Allocate Counter attribute */
+	counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
+	if (!counter_attr)
+		return -ENOMEM;
+
+	/* Generate component ID name */
+	name = devm_kasprintf(dev, GFP_KERNEL, "%s_component_id", name);
+	if (!name)
+		return -ENOMEM;
+
+	/* Configure Counter attribute */
+	counter_attr->comp.priv = (void *)id;
+
+	/* Configure device attribute */
+	sysfs_attr_init(&counter_attr->dev_attr.attr);
+	counter_attr->dev_attr.attr.name = name;
+	counter_attr->dev_attr.attr.mode = 0444;
+	counter_attr->dev_attr.show = counter_comp_id_show;
+
+	/* Store list node */
+	list_add(&counter_attr->l, &group->attr_list);
+	group->num_attr++;
+
+	return 0;
+}
+
 static struct counter_comp counter_signal_comp = {
 	.type = COUNTER_COMP_SIGNAL_LEVEL,
 	.name = "signal",
@@ -626,6 +666,11 @@ static int counter_sysfs_synapses_add(struct counter_device *const counter,
 					  COUNTER_SCOPE_COUNT, count);
 		if (err < 0)
 			return err;
+
+		/* Create Synapse component ID attribute */
+		err = counter_comp_id_attr_create(dev, group, comp.name, i);
+		if (err < 0)
+			return err;
 	}
 
 	return 0;
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 11/14] counter: Implement *_component_id sysfs attributes
  2021-08-27  3:47 ` William Breathitt Gray
@ 2021-08-27  3:47   ` William Breathitt Gray
  -1 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray, Dan Carpenter

The Generic Counter chrdev interface expects users to supply component
IDs in order to select extensions for requests. In order for users to
know what component ID belongs to which extension this information must
be exposed. The *_component_id attribute provides a way for users to
discover what component ID belongs to which respective extension.

Cc: David Lechner <david@lechnology.com>
Cc: Gwendal Grignou <gwendal@chromium.org>
Cc: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 Documentation/ABI/testing/sysfs-bus-counter | 16 +++++++++-
 drivers/counter/counter-sysfs.c             | 33 +++++++++++++++++----
 2 files changed, 42 insertions(+), 7 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter
index aa2cd3c10d74..0ce16669157e 100644
--- a/Documentation/ABI/testing/sysfs-bus-counter
+++ b/Documentation/ABI/testing/sysfs-bus-counter
@@ -203,12 +203,26 @@ Description:
 		both edges:
 			Any state transition.
 
+What:		/sys/bus/counter/devices/counterX/countY/ceiling_component_id
+What:		/sys/bus/counter/devices/counterX/countY/floor_component_id
+What:		/sys/bus/counter/devices/counterX/countY/count_mode_component_id
+What:		/sys/bus/counter/devices/counterX/countY/direction_component_id
+What:		/sys/bus/counter/devices/counterX/countY/enable_component_id
+What:		/sys/bus/counter/devices/counterX/countY/error_noise_component_id
+What:		/sys/bus/counter/devices/counterX/countY/prescaler_component_id
+What:		/sys/bus/counter/devices/counterX/countY/preset_component_id
+What:		/sys/bus/counter/devices/counterX/countY/preset_enable_component_id
 What:		/sys/bus/counter/devices/counterX/countY/signalZ_action_component_id
+What:		/sys/bus/counter/devices/counterX/signalY/cable_fault_component_id
+What:		/sys/bus/counter/devices/counterX/signalY/cable_fault_enable_component_id
+What:		/sys/bus/counter/devices/counterX/signalY/filter_clock_prescaler_component_id
+What:		/sys/bus/counter/devices/counterX/signalY/index_polarity_component_id
+What:		/sys/bus/counter/devices/counterX/signalY/synchronous_mode_component_id
 KernelVersion:	5.16
 Contact:	linux-iio@vger.kernel.org
 Description:
 		Read-only attribute that indicates the component ID of the
-		respective Synapse of Count Y for Signal Z.
+		respective extension or Synapse.
 
 What:		/sys/bus/counter/devices/counterX/countY/spike_filter_ns
 KernelVersion:	5.14
diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
index ee7ff1cde454..97d8d7c2a2b6 100644
--- a/drivers/counter/counter-sysfs.c
+++ b/drivers/counter/counter-sysfs.c
@@ -586,6 +586,7 @@ static int counter_signal_attrs_create(struct counter_device *const counter,
 	int err;
 	struct counter_comp comp;
 	size_t i;
+	struct counter_comp *ext;
 
 	/* Create main Signal attribute */
 	comp = counter_signal_comp;
@@ -601,8 +602,14 @@ static int counter_signal_attrs_create(struct counter_device *const counter,
 
 	/* Create an attribute for each extension */
 	for (i = 0; i < signal->num_ext; i++) {
-		err = counter_attr_create(dev, cattr_group, signal->ext + i,
-					  scope, signal);
+		ext = &signal->ext[i];
+
+		err = counter_attr_create(dev, cattr_group, ext, scope, signal);
+		if (err < 0)
+			return err;
+
+		err = counter_comp_id_attr_create(dev, cattr_group, ext->name,
+						  i);
 		if (err < 0)
 			return err;
 	}
@@ -693,6 +700,7 @@ static int counter_count_attrs_create(struct counter_device *const counter,
 	int err;
 	struct counter_comp comp;
 	size_t i;
+	struct counter_comp *ext;
 
 	/* Create main Count attribute */
 	comp = counter_count_comp;
@@ -717,8 +725,14 @@ static int counter_count_attrs_create(struct counter_device *const counter,
 
 	/* Create an attribute for each extension */
 	for (i = 0; i < count->num_ext; i++) {
-		err = counter_attr_create(dev, cattr_group, count->ext + i,
-					  scope, count);
+		ext = &count->ext[i];
+
+		err = counter_attr_create(dev, cattr_group, ext, scope, count);
+		if (err < 0)
+			return err;
+
+		err = counter_comp_id_attr_create(dev, cattr_group, ext->name,
+						  i);
 		if (err < 0)
 			return err;
 	}
@@ -782,6 +796,7 @@ static int counter_sysfs_attr_add(struct counter_device *const counter,
 	struct device *const dev = &counter->dev;
 	int err;
 	size_t i;
+	struct counter_comp *ext;
 
 	/* Add Signals sysfs attributes */
 	err = counter_sysfs_signals_add(counter, cattr_group);
@@ -814,8 +829,14 @@ static int counter_sysfs_attr_add(struct counter_device *const counter,
 
 	/* Create an attribute for each extension */
 	for (i = 0; i < counter->num_ext; i++) {
-		err = counter_attr_create(dev, cattr_group, counter->ext + i,
-					  scope, NULL);
+		ext = &counter->ext[i];
+
+		err = counter_attr_create(dev, cattr_group, ext, scope, NULL);
+		if (err < 0)
+			return err;
+
+		err = counter_comp_id_attr_create(dev, cattr_group, ext->name,
+						  i);
 		if (err < 0)
 			return err;
 	}
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 11/14] counter: Implement *_component_id sysfs attributes
@ 2021-08-27  3:47   ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray, Dan Carpenter

The Generic Counter chrdev interface expects users to supply component
IDs in order to select extensions for requests. In order for users to
know what component ID belongs to which extension this information must
be exposed. The *_component_id attribute provides a way for users to
discover what component ID belongs to which respective extension.

Cc: David Lechner <david@lechnology.com>
Cc: Gwendal Grignou <gwendal@chromium.org>
Cc: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 Documentation/ABI/testing/sysfs-bus-counter | 16 +++++++++-
 drivers/counter/counter-sysfs.c             | 33 +++++++++++++++++----
 2 files changed, 42 insertions(+), 7 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter
index aa2cd3c10d74..0ce16669157e 100644
--- a/Documentation/ABI/testing/sysfs-bus-counter
+++ b/Documentation/ABI/testing/sysfs-bus-counter
@@ -203,12 +203,26 @@ Description:
 		both edges:
 			Any state transition.
 
+What:		/sys/bus/counter/devices/counterX/countY/ceiling_component_id
+What:		/sys/bus/counter/devices/counterX/countY/floor_component_id
+What:		/sys/bus/counter/devices/counterX/countY/count_mode_component_id
+What:		/sys/bus/counter/devices/counterX/countY/direction_component_id
+What:		/sys/bus/counter/devices/counterX/countY/enable_component_id
+What:		/sys/bus/counter/devices/counterX/countY/error_noise_component_id
+What:		/sys/bus/counter/devices/counterX/countY/prescaler_component_id
+What:		/sys/bus/counter/devices/counterX/countY/preset_component_id
+What:		/sys/bus/counter/devices/counterX/countY/preset_enable_component_id
 What:		/sys/bus/counter/devices/counterX/countY/signalZ_action_component_id
+What:		/sys/bus/counter/devices/counterX/signalY/cable_fault_component_id
+What:		/sys/bus/counter/devices/counterX/signalY/cable_fault_enable_component_id
+What:		/sys/bus/counter/devices/counterX/signalY/filter_clock_prescaler_component_id
+What:		/sys/bus/counter/devices/counterX/signalY/index_polarity_component_id
+What:		/sys/bus/counter/devices/counterX/signalY/synchronous_mode_component_id
 KernelVersion:	5.16
 Contact:	linux-iio@vger.kernel.org
 Description:
 		Read-only attribute that indicates the component ID of the
-		respective Synapse of Count Y for Signal Z.
+		respective extension or Synapse.
 
 What:		/sys/bus/counter/devices/counterX/countY/spike_filter_ns
 KernelVersion:	5.14
diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
index ee7ff1cde454..97d8d7c2a2b6 100644
--- a/drivers/counter/counter-sysfs.c
+++ b/drivers/counter/counter-sysfs.c
@@ -586,6 +586,7 @@ static int counter_signal_attrs_create(struct counter_device *const counter,
 	int err;
 	struct counter_comp comp;
 	size_t i;
+	struct counter_comp *ext;
 
 	/* Create main Signal attribute */
 	comp = counter_signal_comp;
@@ -601,8 +602,14 @@ static int counter_signal_attrs_create(struct counter_device *const counter,
 
 	/* Create an attribute for each extension */
 	for (i = 0; i < signal->num_ext; i++) {
-		err = counter_attr_create(dev, cattr_group, signal->ext + i,
-					  scope, signal);
+		ext = &signal->ext[i];
+
+		err = counter_attr_create(dev, cattr_group, ext, scope, signal);
+		if (err < 0)
+			return err;
+
+		err = counter_comp_id_attr_create(dev, cattr_group, ext->name,
+						  i);
 		if (err < 0)
 			return err;
 	}
@@ -693,6 +700,7 @@ static int counter_count_attrs_create(struct counter_device *const counter,
 	int err;
 	struct counter_comp comp;
 	size_t i;
+	struct counter_comp *ext;
 
 	/* Create main Count attribute */
 	comp = counter_count_comp;
@@ -717,8 +725,14 @@ static int counter_count_attrs_create(struct counter_device *const counter,
 
 	/* Create an attribute for each extension */
 	for (i = 0; i < count->num_ext; i++) {
-		err = counter_attr_create(dev, cattr_group, count->ext + i,
-					  scope, count);
+		ext = &count->ext[i];
+
+		err = counter_attr_create(dev, cattr_group, ext, scope, count);
+		if (err < 0)
+			return err;
+
+		err = counter_comp_id_attr_create(dev, cattr_group, ext->name,
+						  i);
 		if (err < 0)
 			return err;
 	}
@@ -782,6 +796,7 @@ static int counter_sysfs_attr_add(struct counter_device *const counter,
 	struct device *const dev = &counter->dev;
 	int err;
 	size_t i;
+	struct counter_comp *ext;
 
 	/* Add Signals sysfs attributes */
 	err = counter_sysfs_signals_add(counter, cattr_group);
@@ -814,8 +829,14 @@ static int counter_sysfs_attr_add(struct counter_device *const counter,
 
 	/* Create an attribute for each extension */
 	for (i = 0; i < counter->num_ext; i++) {
-		err = counter_attr_create(dev, cattr_group, counter->ext + i,
-					  scope, NULL);
+		ext = &counter->ext[i];
+
+		err = counter_attr_create(dev, cattr_group, ext, scope, NULL);
+		if (err < 0)
+			return err;
+
+		err = counter_comp_id_attr_create(dev, cattr_group, ext->name,
+						  i);
 		if (err < 0)
 			return err;
 	}
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 12/14] counter: Implement events_queue_size sysfs attribute
  2021-08-27  3:47 ` William Breathitt Gray
@ 2021-08-27  3:47   ` William Breathitt Gray
  -1 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray

The events_queue_size sysfs attribute provides a way for users to
dynamically configure the Counter events queue size for the Counter
character device interface. The size is in number of struct
counter_event data structures. The number of elements will be rounded-up
to a power of 2 due to a requirement of the kfifo_alloc function called
during reallocation of the queue.

Cc: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 Documentation/ABI/testing/sysfs-bus-counter |  8 ++++
 drivers/counter/counter-sysfs.c             | 45 +++++++++++++++++++++
 2 files changed, 53 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter
index 0ce16669157e..06c2b3e27e0b 100644
--- a/Documentation/ABI/testing/sysfs-bus-counter
+++ b/Documentation/ABI/testing/sysfs-bus-counter
@@ -233,6 +233,14 @@ Description:
 		shorter or equal to configured value are ignored. Value 0 means
 		filter is disabled.
 
+What:		/sys/bus/counter/devices/counterX/events_queue_size
+KernelVersion:	5.16
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Size of the Counter events queue in number of struct
+		counter_event data structures. The number of elements will be
+		rounded-up to a power of 2.
+
 What:		/sys/bus/counter/devices/counterX/name
 KernelVersion:	5.2
 Contact:	linux-iio@vger.kernel.org
diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
index 97d8d7c2a2b6..b90f404b1fa1 100644
--- a/drivers/counter/counter-sysfs.c
+++ b/drivers/counter/counter-sysfs.c
@@ -3,11 +3,13 @@
  * Generic Counter sysfs interface
  * Copyright (C) 2020 William Breathitt Gray
  */
+#include <linux/bitops.h>
 #include <linux/counter.h>
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/gfp.h>
 #include <linux/kernel.h>
+#include <linux/kfifo.h>
 #include <linux/kstrtox.h>
 #include <linux/list.h>
 #include <linux/string.h>
@@ -783,12 +785,49 @@ static int counter_num_counts_read(struct counter_device *counter, u8 *val)
 	return 0;
 }
 
+static int counter_events_queue_size_read(struct counter_device *counter,
+					  u64 *val)
+{
+	*val = kfifo_size(&counter->events);
+	return 0;
+}
+
+static int counter_events_queue_size_write(struct counter_device *counter,
+					   u64 val)
+{
+	DECLARE_KFIFO_PTR(events, struct counter_event);
+	int err = 0;
+
+	/* Verify chrdev is not currently being used */
+	if (test_and_set_bit_lock(0, counter->chrdev_lock))
+		return -EBUSY;
+
+	/* Allocate new events queue */
+	err = kfifo_alloc(&events, val, GFP_KERNEL);
+	if (err)
+		goto exit_early;
+
+	/* Swap in new events queue */
+	kfifo_free(&counter->events);
+	counter->events.kfifo = events.kfifo;
+
+exit_early:
+	clear_bit_unlock(0, counter->chrdev_lock);
+
+	return err;
+}
+
 static struct counter_comp counter_num_signals_comp =
 	COUNTER_COMP_DEVICE_U8("num_signals", counter_num_signals_read, NULL);
 
 static struct counter_comp counter_num_counts_comp =
 	COUNTER_COMP_DEVICE_U8("num_counts", counter_num_counts_read, NULL);
 
+static struct counter_comp counter_events_queue_size_comp =
+	COUNTER_COMP_DEVICE_U64("events_queue_size",
+				counter_events_queue_size_read,
+				counter_events_queue_size_write);
+
 static int counter_sysfs_attr_add(struct counter_device *const counter,
 				  struct counter_attribute_group *cattr_group)
 {
@@ -827,6 +866,12 @@ static int counter_sysfs_attr_add(struct counter_device *const counter,
 	if (err < 0)
 		return err;
 
+	/* Create events_queue_size attribute */
+	err = counter_attr_create(dev, cattr_group,
+				  &counter_events_queue_size_comp, scope, NULL);
+	if (err < 0)
+		return err;
+
 	/* Create an attribute for each extension */
 	for (i = 0; i < counter->num_ext; i++) {
 		ext = &counter->ext[i];
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 12/14] counter: Implement events_queue_size sysfs attribute
@ 2021-08-27  3:47   ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray

The events_queue_size sysfs attribute provides a way for users to
dynamically configure the Counter events queue size for the Counter
character device interface. The size is in number of struct
counter_event data structures. The number of elements will be rounded-up
to a power of 2 due to a requirement of the kfifo_alloc function called
during reallocation of the queue.

Cc: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 Documentation/ABI/testing/sysfs-bus-counter |  8 ++++
 drivers/counter/counter-sysfs.c             | 45 +++++++++++++++++++++
 2 files changed, 53 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter
index 0ce16669157e..06c2b3e27e0b 100644
--- a/Documentation/ABI/testing/sysfs-bus-counter
+++ b/Documentation/ABI/testing/sysfs-bus-counter
@@ -233,6 +233,14 @@ Description:
 		shorter or equal to configured value are ignored. Value 0 means
 		filter is disabled.
 
+What:		/sys/bus/counter/devices/counterX/events_queue_size
+KernelVersion:	5.16
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Size of the Counter events queue in number of struct
+		counter_event data structures. The number of elements will be
+		rounded-up to a power of 2.
+
 What:		/sys/bus/counter/devices/counterX/name
 KernelVersion:	5.2
 Contact:	linux-iio@vger.kernel.org
diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
index 97d8d7c2a2b6..b90f404b1fa1 100644
--- a/drivers/counter/counter-sysfs.c
+++ b/drivers/counter/counter-sysfs.c
@@ -3,11 +3,13 @@
  * Generic Counter sysfs interface
  * Copyright (C) 2020 William Breathitt Gray
  */
+#include <linux/bitops.h>
 #include <linux/counter.h>
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/gfp.h>
 #include <linux/kernel.h>
+#include <linux/kfifo.h>
 #include <linux/kstrtox.h>
 #include <linux/list.h>
 #include <linux/string.h>
@@ -783,12 +785,49 @@ static int counter_num_counts_read(struct counter_device *counter, u8 *val)
 	return 0;
 }
 
+static int counter_events_queue_size_read(struct counter_device *counter,
+					  u64 *val)
+{
+	*val = kfifo_size(&counter->events);
+	return 0;
+}
+
+static int counter_events_queue_size_write(struct counter_device *counter,
+					   u64 val)
+{
+	DECLARE_KFIFO_PTR(events, struct counter_event);
+	int err = 0;
+
+	/* Verify chrdev is not currently being used */
+	if (test_and_set_bit_lock(0, counter->chrdev_lock))
+		return -EBUSY;
+
+	/* Allocate new events queue */
+	err = kfifo_alloc(&events, val, GFP_KERNEL);
+	if (err)
+		goto exit_early;
+
+	/* Swap in new events queue */
+	kfifo_free(&counter->events);
+	counter->events.kfifo = events.kfifo;
+
+exit_early:
+	clear_bit_unlock(0, counter->chrdev_lock);
+
+	return err;
+}
+
 static struct counter_comp counter_num_signals_comp =
 	COUNTER_COMP_DEVICE_U8("num_signals", counter_num_signals_read, NULL);
 
 static struct counter_comp counter_num_counts_comp =
 	COUNTER_COMP_DEVICE_U8("num_counts", counter_num_counts_read, NULL);
 
+static struct counter_comp counter_events_queue_size_comp =
+	COUNTER_COMP_DEVICE_U64("events_queue_size",
+				counter_events_queue_size_read,
+				counter_events_queue_size_write);
+
 static int counter_sysfs_attr_add(struct counter_device *const counter,
 				  struct counter_attribute_group *cattr_group)
 {
@@ -827,6 +866,12 @@ static int counter_sysfs_attr_add(struct counter_device *const counter,
 	if (err < 0)
 		return err;
 
+	/* Create events_queue_size attribute */
+	err = counter_attr_create(dev, cattr_group,
+				  &counter_events_queue_size_comp, scope, NULL);
+	if (err < 0)
+		return err;
+
 	/* Create an attribute for each extension */
 	for (i = 0; i < counter->num_ext; i++) {
 		ext = &counter->ext[i];
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 13/14] counter: 104-quad-8: Replace mutex with spinlock
  2021-08-27  3:47 ` William Breathitt Gray
@ 2021-08-27  3:47   ` William Breathitt Gray
  -1 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray

This patch replaces the mutex I/O lock with a spinlock. This is in
preparation for a subsequent patch adding IRQ support for 104-QUAD-8
devices; we can't sleep in an interrupt context, so we'll need to use a
spinlock instead.

Acked-by: Syed Nayyar Waris <syednwaris@gmail.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/counter/104-quad-8.c | 89 +++++++++++++++++++++---------------
 1 file changed, 52 insertions(+), 37 deletions(-)

diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c
index c587f295d720..a56751bf1e9b 100644
--- a/drivers/counter/104-quad-8.c
+++ b/drivers/counter/104-quad-8.c
@@ -16,6 +16,7 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/types.h>
+#include <linux/spinlock.h>
 
 #define QUAD8_EXTENT 32
 
@@ -43,7 +44,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
  * @base:		base port address of the device
  */
 struct quad8 {
-	struct mutex lock;
+	spinlock_t lock;
 	struct counter_device counter;
 	unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
 	unsigned int preset[QUAD8_NUM_COUNTERS];
@@ -124,6 +125,7 @@ static int quad8_count_read(struct counter_device *counter,
 	unsigned int flags;
 	unsigned int borrow;
 	unsigned int carry;
+	unsigned long irqflags;
 	int i;
 
 	flags = inb(base_offset + 1);
@@ -133,7 +135,7 @@ static int quad8_count_read(struct counter_device *counter,
 	/* Borrow XOR Carry effectively doubles count range */
 	*val = (unsigned long)(borrow ^ carry) << 24;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Reset Byte Pointer; transfer Counter to Output Latch */
 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
@@ -142,7 +144,7 @@ static int quad8_count_read(struct counter_device *counter,
 	for (i = 0; i < 3; i++)
 		*val |= (unsigned long)inb(base_offset) << (8 * i);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -152,13 +154,14 @@ static int quad8_count_write(struct counter_device *counter,
 {
 	struct quad8 *const priv = counter->priv;
 	const int base_offset = priv->base + 2 * count->id;
+	unsigned long irqflags;
 	int i;
 
 	/* Only 24-bit values are supported */
 	if (val > 0xFFFFFF)
 		return -ERANGE;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Reset Byte Pointer */
 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
@@ -183,7 +186,7 @@ static int quad8_count_write(struct counter_device *counter,
 	/* Reset Error flag */
 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -201,8 +204,9 @@ static int quad8_function_read(struct counter_device *counter,
 {
 	struct quad8 *const priv = counter->priv;
 	const int id = count->id;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	if (priv->quadrature_mode[id])
 		switch (priv->quadrature_scale[id]) {
@@ -219,7 +223,7 @@ static int quad8_function_read(struct counter_device *counter,
 	else
 		*function = COUNTER_FUNCTION_PULSE_DIRECTION;
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -234,10 +238,11 @@ static int quad8_function_write(struct counter_device *counter,
 	unsigned int *const scale = priv->quadrature_scale + id;
 	unsigned int *const synchronous_mode = priv->synchronous_mode + id;
 	const int base_offset = priv->base + 2 * id + 1;
+	unsigned long irqflags;
 	unsigned int mode_cfg;
 	unsigned int idr_cfg;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	mode_cfg = priv->count_mode[id] << 1;
 	idr_cfg = priv->index_polarity[id] << 1;
@@ -272,7 +277,7 @@ static int quad8_function_write(struct counter_device *counter,
 			break;
 		default:
 			/* should never reach this path */
-			mutex_unlock(&priv->lock);
+			spin_unlock_irqrestore(&priv->lock, irqflags);
 			return -EINVAL;
 		}
 	}
@@ -280,7 +285,7 @@ static int quad8_function_write(struct counter_device *counter,
 	/* Load mode configuration to Counter Mode Register */
 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -406,9 +411,10 @@ static int quad8_index_polarity_set(struct counter_device *counter,
 	struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id - 16;
 	const int base_offset = priv->base + 2 * channel_id + 1;
+	unsigned long irqflags;
 	unsigned int idr_cfg = index_polarity << 1;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	idr_cfg |= priv->synchronous_mode[channel_id];
 
@@ -417,7 +423,7 @@ static int quad8_index_polarity_set(struct counter_device *counter,
 	/* Load Index Control configuration to Index Control Register */
 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -446,15 +452,16 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,
 	struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id - 16;
 	const int base_offset = priv->base + 2 * channel_id + 1;
+	unsigned long irqflags;
 	unsigned int idr_cfg = synchronous_mode;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	idr_cfg |= priv->index_polarity[channel_id] << 1;
 
 	/* Index function must be non-synchronous in non-quadrature mode */
 	if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
-		mutex_unlock(&priv->lock);
+		spin_unlock_irqrestore(&priv->lock, irqflags);
 		return -EINVAL;
 	}
 
@@ -463,7 +470,7 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,
 	/* Load Index Control configuration to Index Control Register */
 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -510,6 +517,7 @@ static int quad8_count_mode_write(struct counter_device *counter,
 	unsigned int count_mode;
 	unsigned int mode_cfg;
 	const int base_offset = priv->base + 2 * count->id + 1;
+	unsigned long irqflags;
 
 	/* Map Generic Counter count mode to 104-QUAD-8 count mode */
 	switch (cnt_mode) {
@@ -530,7 +538,7 @@ static int quad8_count_mode_write(struct counter_device *counter,
 		return -EINVAL;
 	}
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->count_mode[count->id] = count_mode;
 
@@ -544,7 +552,7 @@ static int quad8_count_mode_write(struct counter_device *counter,
 	/* Load mode configuration to Counter Mode Register */
 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -564,9 +572,10 @@ static int quad8_count_enable_write(struct counter_device *counter,
 {
 	struct quad8 *const priv = counter->priv;
 	const int base_offset = priv->base + 2 * count->id;
+	unsigned long irqflags;
 	unsigned int ior_cfg;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->ab_enable[count->id] = enable;
 
@@ -575,7 +584,7 @@ static int quad8_count_enable_write(struct counter_device *counter,
 	/* Load I/O control configuration */
 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -626,16 +635,17 @@ static int quad8_count_preset_write(struct counter_device *counter,
 				    struct counter_count *count, u64 preset)
 {
 	struct quad8 *const priv = counter->priv;
+	unsigned long irqflags;
 
 	/* Only 24-bit values are supported */
 	if (preset > 0xFFFFFF)
 		return -ERANGE;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	quad8_preset_register_set(priv, count->id, preset);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -644,8 +654,9 @@ static int quad8_count_ceiling_read(struct counter_device *counter,
 				    struct counter_count *count, u64 *ceiling)
 {
 	struct quad8 *const priv = counter->priv;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
 	switch (priv->count_mode[count->id]) {
@@ -659,7 +670,7 @@ static int quad8_count_ceiling_read(struct counter_device *counter,
 		break;
 	}
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -668,23 +679,24 @@ static int quad8_count_ceiling_write(struct counter_device *counter,
 				     struct counter_count *count, u64 ceiling)
 {
 	struct quad8 *const priv = counter->priv;
+	unsigned long irqflags;
 
 	/* Only 24-bit values are supported */
 	if (ceiling > 0xFFFFFF)
 		return -ERANGE;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
 	switch (priv->count_mode[count->id]) {
 	case 1:
 	case 3:
 		quad8_preset_register_set(priv, count->id, ceiling);
-		mutex_unlock(&priv->lock);
+		spin_unlock_irqrestore(&priv->lock, irqflags);
 		return 0;
 	}
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return -EINVAL;
 }
@@ -706,12 +718,13 @@ static int quad8_count_preset_enable_write(struct counter_device *counter,
 {
 	struct quad8 *const priv = counter->priv;
 	const int base_offset = priv->base + 2 * count->id + 1;
+	unsigned long irqflags;
 	unsigned int ior_cfg;
 
 	/* Preset enable is active low in Input/Output Control register */
 	preset_enable = !preset_enable;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->preset_enable[count->id] = preset_enable;
 
@@ -720,7 +733,7 @@ static int quad8_count_preset_enable_write(struct counter_device *counter,
 	/* Load I/O control configuration to Input / Output Control Register */
 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -731,22 +744,23 @@ static int quad8_signal_cable_fault_read(struct counter_device *counter,
 {
 	struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id / 2;
+	unsigned long irqflags;
 	bool disabled;
 	unsigned int status;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	disabled = !(priv->cable_fault_enable & BIT(channel_id));
 
 	if (disabled) {
-		mutex_unlock(&priv->lock);
+		spin_unlock_irqrestore(&priv->lock, irqflags);
 		return -EINVAL;
 	}
 
 	/* Logic 0 = cable fault */
 	status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	/* Mask respective channel and invert logic */
 	*cable_fault = !(status & BIT(channel_id));
@@ -772,9 +786,10 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
 {
 	struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id / 2;
+	unsigned long irqflags;
 	unsigned int cable_fault_enable;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	if (enable)
 		priv->cable_fault_enable |= BIT(channel_id);
@@ -786,7 +801,7 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
 
 	outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -809,8 +824,9 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
 	struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id / 2;
 	const int base_offset = priv->base + 2 * channel_id;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->fck_prescaler[channel_id] = prescaler;
 
@@ -822,7 +838,7 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
 	     base_offset + 1);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -991,8 +1007,7 @@ static int quad8_probe(struct device *dev, unsigned int id)
 	priv->counter.priv = priv;
 	priv->base = base[id];
 
-	/* Initialize mutex */
-	mutex_init(&priv->lock);
+	spin_lock_init(&priv->lock);
 
 	/* Reset all counters and disable interrupt function */
 	outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 13/14] counter: 104-quad-8: Replace mutex with spinlock
@ 2021-08-27  3:47   ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray

This patch replaces the mutex I/O lock with a spinlock. This is in
preparation for a subsequent patch adding IRQ support for 104-QUAD-8
devices; we can't sleep in an interrupt context, so we'll need to use a
spinlock instead.

Acked-by: Syed Nayyar Waris <syednwaris@gmail.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/counter/104-quad-8.c | 89 +++++++++++++++++++++---------------
 1 file changed, 52 insertions(+), 37 deletions(-)

diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c
index c587f295d720..a56751bf1e9b 100644
--- a/drivers/counter/104-quad-8.c
+++ b/drivers/counter/104-quad-8.c
@@ -16,6 +16,7 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/types.h>
+#include <linux/spinlock.h>
 
 #define QUAD8_EXTENT 32
 
@@ -43,7 +44,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
  * @base:		base port address of the device
  */
 struct quad8 {
-	struct mutex lock;
+	spinlock_t lock;
 	struct counter_device counter;
 	unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
 	unsigned int preset[QUAD8_NUM_COUNTERS];
@@ -124,6 +125,7 @@ static int quad8_count_read(struct counter_device *counter,
 	unsigned int flags;
 	unsigned int borrow;
 	unsigned int carry;
+	unsigned long irqflags;
 	int i;
 
 	flags = inb(base_offset + 1);
@@ -133,7 +135,7 @@ static int quad8_count_read(struct counter_device *counter,
 	/* Borrow XOR Carry effectively doubles count range */
 	*val = (unsigned long)(borrow ^ carry) << 24;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Reset Byte Pointer; transfer Counter to Output Latch */
 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
@@ -142,7 +144,7 @@ static int quad8_count_read(struct counter_device *counter,
 	for (i = 0; i < 3; i++)
 		*val |= (unsigned long)inb(base_offset) << (8 * i);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -152,13 +154,14 @@ static int quad8_count_write(struct counter_device *counter,
 {
 	struct quad8 *const priv = counter->priv;
 	const int base_offset = priv->base + 2 * count->id;
+	unsigned long irqflags;
 	int i;
 
 	/* Only 24-bit values are supported */
 	if (val > 0xFFFFFF)
 		return -ERANGE;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Reset Byte Pointer */
 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
@@ -183,7 +186,7 @@ static int quad8_count_write(struct counter_device *counter,
 	/* Reset Error flag */
 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -201,8 +204,9 @@ static int quad8_function_read(struct counter_device *counter,
 {
 	struct quad8 *const priv = counter->priv;
 	const int id = count->id;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	if (priv->quadrature_mode[id])
 		switch (priv->quadrature_scale[id]) {
@@ -219,7 +223,7 @@ static int quad8_function_read(struct counter_device *counter,
 	else
 		*function = COUNTER_FUNCTION_PULSE_DIRECTION;
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -234,10 +238,11 @@ static int quad8_function_write(struct counter_device *counter,
 	unsigned int *const scale = priv->quadrature_scale + id;
 	unsigned int *const synchronous_mode = priv->synchronous_mode + id;
 	const int base_offset = priv->base + 2 * id + 1;
+	unsigned long irqflags;
 	unsigned int mode_cfg;
 	unsigned int idr_cfg;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	mode_cfg = priv->count_mode[id] << 1;
 	idr_cfg = priv->index_polarity[id] << 1;
@@ -272,7 +277,7 @@ static int quad8_function_write(struct counter_device *counter,
 			break;
 		default:
 			/* should never reach this path */
-			mutex_unlock(&priv->lock);
+			spin_unlock_irqrestore(&priv->lock, irqflags);
 			return -EINVAL;
 		}
 	}
@@ -280,7 +285,7 @@ static int quad8_function_write(struct counter_device *counter,
 	/* Load mode configuration to Counter Mode Register */
 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -406,9 +411,10 @@ static int quad8_index_polarity_set(struct counter_device *counter,
 	struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id - 16;
 	const int base_offset = priv->base + 2 * channel_id + 1;
+	unsigned long irqflags;
 	unsigned int idr_cfg = index_polarity << 1;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	idr_cfg |= priv->synchronous_mode[channel_id];
 
@@ -417,7 +423,7 @@ static int quad8_index_polarity_set(struct counter_device *counter,
 	/* Load Index Control configuration to Index Control Register */
 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -446,15 +452,16 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,
 	struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id - 16;
 	const int base_offset = priv->base + 2 * channel_id + 1;
+	unsigned long irqflags;
 	unsigned int idr_cfg = synchronous_mode;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	idr_cfg |= priv->index_polarity[channel_id] << 1;
 
 	/* Index function must be non-synchronous in non-quadrature mode */
 	if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
-		mutex_unlock(&priv->lock);
+		spin_unlock_irqrestore(&priv->lock, irqflags);
 		return -EINVAL;
 	}
 
@@ -463,7 +470,7 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,
 	/* Load Index Control configuration to Index Control Register */
 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -510,6 +517,7 @@ static int quad8_count_mode_write(struct counter_device *counter,
 	unsigned int count_mode;
 	unsigned int mode_cfg;
 	const int base_offset = priv->base + 2 * count->id + 1;
+	unsigned long irqflags;
 
 	/* Map Generic Counter count mode to 104-QUAD-8 count mode */
 	switch (cnt_mode) {
@@ -530,7 +538,7 @@ static int quad8_count_mode_write(struct counter_device *counter,
 		return -EINVAL;
 	}
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->count_mode[count->id] = count_mode;
 
@@ -544,7 +552,7 @@ static int quad8_count_mode_write(struct counter_device *counter,
 	/* Load mode configuration to Counter Mode Register */
 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -564,9 +572,10 @@ static int quad8_count_enable_write(struct counter_device *counter,
 {
 	struct quad8 *const priv = counter->priv;
 	const int base_offset = priv->base + 2 * count->id;
+	unsigned long irqflags;
 	unsigned int ior_cfg;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->ab_enable[count->id] = enable;
 
@@ -575,7 +584,7 @@ static int quad8_count_enable_write(struct counter_device *counter,
 	/* Load I/O control configuration */
 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -626,16 +635,17 @@ static int quad8_count_preset_write(struct counter_device *counter,
 				    struct counter_count *count, u64 preset)
 {
 	struct quad8 *const priv = counter->priv;
+	unsigned long irqflags;
 
 	/* Only 24-bit values are supported */
 	if (preset > 0xFFFFFF)
 		return -ERANGE;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	quad8_preset_register_set(priv, count->id, preset);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -644,8 +654,9 @@ static int quad8_count_ceiling_read(struct counter_device *counter,
 				    struct counter_count *count, u64 *ceiling)
 {
 	struct quad8 *const priv = counter->priv;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
 	switch (priv->count_mode[count->id]) {
@@ -659,7 +670,7 @@ static int quad8_count_ceiling_read(struct counter_device *counter,
 		break;
 	}
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -668,23 +679,24 @@ static int quad8_count_ceiling_write(struct counter_device *counter,
 				     struct counter_count *count, u64 ceiling)
 {
 	struct quad8 *const priv = counter->priv;
+	unsigned long irqflags;
 
 	/* Only 24-bit values are supported */
 	if (ceiling > 0xFFFFFF)
 		return -ERANGE;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
 	switch (priv->count_mode[count->id]) {
 	case 1:
 	case 3:
 		quad8_preset_register_set(priv, count->id, ceiling);
-		mutex_unlock(&priv->lock);
+		spin_unlock_irqrestore(&priv->lock, irqflags);
 		return 0;
 	}
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return -EINVAL;
 }
@@ -706,12 +718,13 @@ static int quad8_count_preset_enable_write(struct counter_device *counter,
 {
 	struct quad8 *const priv = counter->priv;
 	const int base_offset = priv->base + 2 * count->id + 1;
+	unsigned long irqflags;
 	unsigned int ior_cfg;
 
 	/* Preset enable is active low in Input/Output Control register */
 	preset_enable = !preset_enable;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->preset_enable[count->id] = preset_enable;
 
@@ -720,7 +733,7 @@ static int quad8_count_preset_enable_write(struct counter_device *counter,
 	/* Load I/O control configuration to Input / Output Control Register */
 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -731,22 +744,23 @@ static int quad8_signal_cable_fault_read(struct counter_device *counter,
 {
 	struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id / 2;
+	unsigned long irqflags;
 	bool disabled;
 	unsigned int status;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	disabled = !(priv->cable_fault_enable & BIT(channel_id));
 
 	if (disabled) {
-		mutex_unlock(&priv->lock);
+		spin_unlock_irqrestore(&priv->lock, irqflags);
 		return -EINVAL;
 	}
 
 	/* Logic 0 = cable fault */
 	status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	/* Mask respective channel and invert logic */
 	*cable_fault = !(status & BIT(channel_id));
@@ -772,9 +786,10 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
 {
 	struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id / 2;
+	unsigned long irqflags;
 	unsigned int cable_fault_enable;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	if (enable)
 		priv->cable_fault_enable |= BIT(channel_id);
@@ -786,7 +801,7 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
 
 	outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -809,8 +824,9 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
 	struct quad8 *const priv = counter->priv;
 	const size_t channel_id = signal->id / 2;
 	const int base_offset = priv->base + 2 * channel_id;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->fck_prescaler[channel_id] = prescaler;
 
@@ -822,7 +838,7 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
 	     base_offset + 1);
 
-	mutex_unlock(&priv->lock);
+	spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -991,8 +1007,7 @@ static int quad8_probe(struct device *dev, unsigned int id)
 	priv->counter.priv = priv;
 	priv->base = base[id];
 
-	/* Initialize mutex */
-	mutex_init(&priv->lock);
+	spin_lock_init(&priv->lock);
 
 	/* Reset all counters and disable interrupt function */
 	outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 14/14] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
  2021-08-27  3:47 ` William Breathitt Gray
@ 2021-08-27  3:47   ` William Breathitt Gray
  -1 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray

The LSI/CSI LS7266R1 chip provides programmable output via the FLG pins.
When interrupts are enabled on the ACCES 104-QUAD-8, they occur whenever
FLG1 is active. Four functions are available for the FLG1 signal: Carry,
Compare, Carry-Borrow, and Index.

	Carry:
		Interrupt generated on active low Carry signal. Carry
		signal toggles every time the respective channel's
		counter overflows.

	Compare:
		Interrupt generated on active low Compare signal.
		Compare signal toggles every time respective channel's
		preset register is equal to the respective channel's
		counter.

	Carry-Borrow:
		Interrupt generated on active low Carry signal and
		active low Borrow signal. Carry signal toggles every
		time the respective channel's counter overflows. Borrow
		signal toggles every time the respective channel's
		counter underflows.

	Index:
		Interrupt generated on active high Index signal.

These four functions correspond respectivefly to the following four
Counter event types: COUNTER_EVENT_OVERFLOW, COUNTER_EVENT_THRESHOLD,
COUNTER_EVENT_OVERFLOW_UNDERFLOW, and COUNTER_EVENT_INDEX. Interrupts
push Counter events to event channel X, where 'X' is the respective
channel whose FLG1 activated.

This patch adds IRQ support for the ACCES 104-QUAD-8. The interrupt line
numbers for the devices may be configured via the irq array module
parameter.

Acked-by: Syed Nayyar Waris <syednwaris@gmail.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/counter/104-quad-8.c | 167 +++++++++++++++++++++++++++++++++--
 drivers/counter/Kconfig      |   6 +-
 2 files changed, 164 insertions(+), 9 deletions(-)

diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c
index a56751bf1e9b..1cbd60aaed69 100644
--- a/drivers/counter/104-quad-8.c
+++ b/drivers/counter/104-quad-8.c
@@ -11,6 +11,7 @@
 #include <linux/errno.h>
 #include <linux/io.h>
 #include <linux/ioport.h>
+#include <linux/interrupt.h>
 #include <linux/isa.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -25,6 +26,10 @@ static unsigned int num_quad8;
 module_param_hw_array(base, uint, ioport, &num_quad8, 0);
 MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
 
+static unsigned int irq[max_num_isa_dev(QUAD8_EXTENT)];
+module_param_hw_array(irq, uint, irq, NULL, 0);
+MODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers");
+
 #define QUAD8_NUM_COUNTERS 8
 
 /**
@@ -38,6 +43,8 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
  * @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
+ * @next_irq_trigger:	array of next 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
@@ -53,13 +60,17 @@ struct quad8 {
 	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 next_irq_trigger[QUAD8_NUM_COUNTERS];
 	unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
 	unsigned int index_polarity[QUAD8_NUM_COUNTERS];
 	unsigned int cable_fault_enable;
 	unsigned int base;
 };
 
+#define QUAD8_REG_INTERRUPT_STATUS 0x10
 #define QUAD8_REG_CHAN_OP 0x11
+#define QUAD8_REG_INDEX_INTERRUPT 0x12
 #define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
 #define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17
 /* Borrow Toggle flip-flop */
@@ -92,8 +103,8 @@ struct quad8 {
 #define QUAD8_RLD_CNTR_OUT 0x10
 /* Transfer Preset Register LSB to FCK Prescaler */
 #define QUAD8_RLD_PRESET_PSC 0x18
-#define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00
 #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
+#define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04
 #define QUAD8_CMR_QUADRATURE_X1 0x08
 #define QUAD8_CMR_QUADRATURE_X2 0x10
 #define QUAD8_CMR_QUADRATURE_X4 0x18
@@ -378,13 +389,103 @@ static int quad8_action_read(struct counter_device *counter,
 	}
 }
 
+enum {
+	QUAD8_EVENT_NONE = -1,
+	QUAD8_EVENT_CARRY = 0,
+	QUAD8_EVENT_COMPARE = 1,
+	QUAD8_EVENT_CARRY_BORROW = 2,
+	QUAD8_EVENT_INDEX = 3,
+};
+
+static int quad8_events_configure(struct counter_device *counter)
+{
+	struct quad8 *const priv = counter->priv;
+	unsigned long irq_enabled = 0;
+	unsigned long irqflags;
+	size_t channel;
+	unsigned long ior_cfg;
+	unsigned long base_offset;
+
+	spin_lock_irqsave(&priv->lock, irqflags);
+
+	/* Enable interrupts for the requested channels, disable for the rest */
+	for (channel = 0; channel < QUAD8_NUM_COUNTERS; channel++) {
+		if (priv->next_irq_trigger[channel] == QUAD8_EVENT_NONE)
+			continue;
+
+		if (priv->irq_trigger[channel] != priv->next_irq_trigger[channel]) {
+			/* Save new IRQ function configuration */
+			priv->irq_trigger[channel] = priv->next_irq_trigger[channel];
+
+			/* Load configuration to I/O Control Register */
+			ior_cfg = priv->ab_enable[channel] |
+				  priv->preset_enable[channel] << 1 |
+				  priv->irq_trigger[channel] << 3;
+			base_offset = priv->base + 2 * channel + 1;
+			outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
+		}
+
+		/* Reset next IRQ trigger function configuration */
+		priv->next_irq_trigger[channel] = QUAD8_EVENT_NONE;
+
+		/* Enable IRQ line */
+		irq_enabled |= BIT(channel);
+	}
+
+	outb(irq_enabled, priv->base + QUAD8_REG_INDEX_INTERRUPT);
+
+	spin_unlock_irqrestore(&priv->lock, irqflags);
+
+	return 0;
+}
+
+static int quad8_watch_validate(struct counter_device *counter,
+				const struct counter_watch *watch)
+{
+	struct quad8 *const priv = counter->priv;
+
+	if (watch->channel > QUAD8_NUM_COUNTERS - 1)
+		return -EINVAL;
+
+	switch (watch->event) {
+	case COUNTER_EVENT_OVERFLOW:
+		if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE)
+			priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_CARRY;
+		else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_CARRY)
+			return -EINVAL;
+		return 0;
+	case COUNTER_EVENT_THRESHOLD:
+		if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE)
+			priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_COMPARE;
+		else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_COMPARE)
+			return -EINVAL;
+		return 0;
+	case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
+		if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE)
+			priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_CARRY_BORROW;
+		else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_CARRY_BORROW)
+			return -EINVAL;
+		return 0;
+	case COUNTER_EVENT_INDEX:
+		if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE)
+			priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_INDEX;
+		else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_INDEX)
+			return -EINVAL;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
 static const struct counter_ops quad8_ops = {
 	.signal_read = quad8_signal_read,
 	.count_read = quad8_count_read,
 	.count_write = quad8_count_write,
 	.function_read = quad8_function_read,
 	.function_write = quad8_function_write,
-	.action_read = quad8_action_read
+	.action_read = quad8_action_read,
+	.events_configure = quad8_events_configure,
+	.watch_validate = quad8_watch_validate,
 };
 
 static const char *const quad8_index_polarity_modes[] = {
@@ -579,7 +680,8 @@ static int quad8_count_enable_write(struct counter_device *counter,
 
 	priv->ab_enable[count->id] = enable;
 
-	ior_cfg = enable | priv->preset_enable[count->id] << 1;
+	ior_cfg = enable | priv->preset_enable[count->id] << 1 |
+		  priv->irq_trigger[count->id] << 3;
 
 	/* Load I/O control configuration */
 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
@@ -728,7 +830,8 @@ static int quad8_count_preset_enable_write(struct counter_device *counter,
 
 	priv->preset_enable[count->id] = preset_enable;
 
-	ior_cfg = priv->ab_enable[count->id] | preset_enable << 1;
+	ior_cfg = priv->ab_enable[count->id] | preset_enable << 1 |
+		  priv->irq_trigger[count->id] << 3;
 
 	/* Load I/O control configuration to Input / Output Control Register */
 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
@@ -980,11 +1083,54 @@ static struct counter_count quad8_counts[] = {
 	QUAD8_COUNT(7, "Channel 8 Count")
 };
 
+static irqreturn_t quad8_irq_handler(int irq, void *private)
+{
+	struct quad8 *const priv = private;
+	const unsigned long base = priv->base;
+	unsigned long irq_status;
+	unsigned long channel;
+	u8 event;
+
+	irq_status = inb(base + QUAD8_REG_INTERRUPT_STATUS);
+	if (!irq_status)
+		return IRQ_NONE;
+
+	for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS) {
+		switch (priv->irq_trigger[channel]) {
+		case QUAD8_EVENT_CARRY:
+			event = COUNTER_EVENT_OVERFLOW;
+				break;
+		case QUAD8_EVENT_COMPARE:
+			event = COUNTER_EVENT_THRESHOLD;
+				break;
+		case QUAD8_EVENT_CARRY_BORROW:
+			event = COUNTER_EVENT_OVERFLOW_UNDERFLOW;
+				break;
+		case QUAD8_EVENT_INDEX:
+			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);
+			continue;
+		}
+
+		counter_push_event(&priv->counter, event, channel);
+	}
+
+	/* Clear pending interrupts on device */
+	outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base + QUAD8_REG_CHAN_OP);
+
+	return IRQ_HANDLED;
+}
+
 static int quad8_probe(struct device *dev, unsigned int id)
 {
 	struct quad8 *priv;
 	int i, j;
 	unsigned int base_offset;
+	int err;
 
 	if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
@@ -1009,6 +1155,8 @@ static int quad8_probe(struct device *dev, unsigned int id)
 
 	spin_lock_init(&priv->lock);
 
+	/* Reset Index/Interrupt Register */
+	outb(0x00, base[id] + QUAD8_REG_INDEX_INTERRUPT);
 	/* Reset all counters and disable interrupt function */
 	outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
 	/* Set initial configuration for all counters */
@@ -1035,11 +1183,18 @@ static int quad8_probe(struct device *dev, unsigned int id)
 		outb(QUAD8_CTR_IOR, base_offset + 1);
 		/* Disable index function; negative index polarity */
 		outb(QUAD8_CTR_IDR, base_offset + 1);
+		/* Initialize next IRQ trigger function configuration */
+		priv->next_irq_trigger[i] = QUAD8_EVENT_NONE;
 	}
 	/* Disable Differential Encoder Cable Status for all channels */
 	outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS);
-	/* Enable all counters */
-	outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
+	/* Enable all counters and enable interrupt function */
+	outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base[id] + QUAD8_REG_CHAN_OP);
+
+	err = devm_request_irq(dev, irq[id], quad8_irq_handler, IRQF_SHARED,
+			       priv->counter.name, priv);
+	if (err)
+		return err;
 
 	return devm_counter_register(dev, &priv->counter);
 }
diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
index d5d2540b30c2..3dcdb681c4e4 100644
--- a/drivers/counter/Kconfig
+++ b/drivers/counter/Kconfig
@@ -23,11 +23,11 @@ config 104_QUAD_8
 	  A counter's respective error flag may be cleared by performing a write
 	  operation on the respective count value attribute. Although the
 	  104-QUAD-8 counters have a 25-bit range, only the lower 24 bits may be
-	  set, either directly or via the counter's preset attribute. Interrupts
-	  are not supported by this driver.
+	  set, either directly or via the counter's preset attribute.
 
 	  The base port addresses for the devices may be configured via the base
-	  array module parameter.
+	  array module parameter. The interrupt line numbers for the devices may
+	  be configured via the irq array module parameter.
 
 config INTERRUPT_CNT
 	tristate "Interrupt counter driver"
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v16 14/14] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
@ 2021-08-27  3:47   ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-27  3:47 UTC (permalink / raw)
  To: jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	William Breathitt Gray

The LSI/CSI LS7266R1 chip provides programmable output via the FLG pins.
When interrupts are enabled on the ACCES 104-QUAD-8, they occur whenever
FLG1 is active. Four functions are available for the FLG1 signal: Carry,
Compare, Carry-Borrow, and Index.

	Carry:
		Interrupt generated on active low Carry signal. Carry
		signal toggles every time the respective channel's
		counter overflows.

	Compare:
		Interrupt generated on active low Compare signal.
		Compare signal toggles every time respective channel's
		preset register is equal to the respective channel's
		counter.

	Carry-Borrow:
		Interrupt generated on active low Carry signal and
		active low Borrow signal. Carry signal toggles every
		time the respective channel's counter overflows. Borrow
		signal toggles every time the respective channel's
		counter underflows.

	Index:
		Interrupt generated on active high Index signal.

These four functions correspond respectivefly to the following four
Counter event types: COUNTER_EVENT_OVERFLOW, COUNTER_EVENT_THRESHOLD,
COUNTER_EVENT_OVERFLOW_UNDERFLOW, and COUNTER_EVENT_INDEX. Interrupts
push Counter events to event channel X, where 'X' is the respective
channel whose FLG1 activated.

This patch adds IRQ support for the ACCES 104-QUAD-8. The interrupt line
numbers for the devices may be configured via the irq array module
parameter.

Acked-by: Syed Nayyar Waris <syednwaris@gmail.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/counter/104-quad-8.c | 167 +++++++++++++++++++++++++++++++++--
 drivers/counter/Kconfig      |   6 +-
 2 files changed, 164 insertions(+), 9 deletions(-)

diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c
index a56751bf1e9b..1cbd60aaed69 100644
--- a/drivers/counter/104-quad-8.c
+++ b/drivers/counter/104-quad-8.c
@@ -11,6 +11,7 @@
 #include <linux/errno.h>
 #include <linux/io.h>
 #include <linux/ioport.h>
+#include <linux/interrupt.h>
 #include <linux/isa.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -25,6 +26,10 @@ static unsigned int num_quad8;
 module_param_hw_array(base, uint, ioport, &num_quad8, 0);
 MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
 
+static unsigned int irq[max_num_isa_dev(QUAD8_EXTENT)];
+module_param_hw_array(irq, uint, irq, NULL, 0);
+MODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers");
+
 #define QUAD8_NUM_COUNTERS 8
 
 /**
@@ -38,6 +43,8 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
  * @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
+ * @next_irq_trigger:	array of next 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
@@ -53,13 +60,17 @@ struct quad8 {
 	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 next_irq_trigger[QUAD8_NUM_COUNTERS];
 	unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
 	unsigned int index_polarity[QUAD8_NUM_COUNTERS];
 	unsigned int cable_fault_enable;
 	unsigned int base;
 };
 
+#define QUAD8_REG_INTERRUPT_STATUS 0x10
 #define QUAD8_REG_CHAN_OP 0x11
+#define QUAD8_REG_INDEX_INTERRUPT 0x12
 #define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
 #define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17
 /* Borrow Toggle flip-flop */
@@ -92,8 +103,8 @@ struct quad8 {
 #define QUAD8_RLD_CNTR_OUT 0x10
 /* Transfer Preset Register LSB to FCK Prescaler */
 #define QUAD8_RLD_PRESET_PSC 0x18
-#define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00
 #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
+#define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04
 #define QUAD8_CMR_QUADRATURE_X1 0x08
 #define QUAD8_CMR_QUADRATURE_X2 0x10
 #define QUAD8_CMR_QUADRATURE_X4 0x18
@@ -378,13 +389,103 @@ static int quad8_action_read(struct counter_device *counter,
 	}
 }
 
+enum {
+	QUAD8_EVENT_NONE = -1,
+	QUAD8_EVENT_CARRY = 0,
+	QUAD8_EVENT_COMPARE = 1,
+	QUAD8_EVENT_CARRY_BORROW = 2,
+	QUAD8_EVENT_INDEX = 3,
+};
+
+static int quad8_events_configure(struct counter_device *counter)
+{
+	struct quad8 *const priv = counter->priv;
+	unsigned long irq_enabled = 0;
+	unsigned long irqflags;
+	size_t channel;
+	unsigned long ior_cfg;
+	unsigned long base_offset;
+
+	spin_lock_irqsave(&priv->lock, irqflags);
+
+	/* Enable interrupts for the requested channels, disable for the rest */
+	for (channel = 0; channel < QUAD8_NUM_COUNTERS; channel++) {
+		if (priv->next_irq_trigger[channel] == QUAD8_EVENT_NONE)
+			continue;
+
+		if (priv->irq_trigger[channel] != priv->next_irq_trigger[channel]) {
+			/* Save new IRQ function configuration */
+			priv->irq_trigger[channel] = priv->next_irq_trigger[channel];
+
+			/* Load configuration to I/O Control Register */
+			ior_cfg = priv->ab_enable[channel] |
+				  priv->preset_enable[channel] << 1 |
+				  priv->irq_trigger[channel] << 3;
+			base_offset = priv->base + 2 * channel + 1;
+			outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
+		}
+
+		/* Reset next IRQ trigger function configuration */
+		priv->next_irq_trigger[channel] = QUAD8_EVENT_NONE;
+
+		/* Enable IRQ line */
+		irq_enabled |= BIT(channel);
+	}
+
+	outb(irq_enabled, priv->base + QUAD8_REG_INDEX_INTERRUPT);
+
+	spin_unlock_irqrestore(&priv->lock, irqflags);
+
+	return 0;
+}
+
+static int quad8_watch_validate(struct counter_device *counter,
+				const struct counter_watch *watch)
+{
+	struct quad8 *const priv = counter->priv;
+
+	if (watch->channel > QUAD8_NUM_COUNTERS - 1)
+		return -EINVAL;
+
+	switch (watch->event) {
+	case COUNTER_EVENT_OVERFLOW:
+		if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE)
+			priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_CARRY;
+		else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_CARRY)
+			return -EINVAL;
+		return 0;
+	case COUNTER_EVENT_THRESHOLD:
+		if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE)
+			priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_COMPARE;
+		else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_COMPARE)
+			return -EINVAL;
+		return 0;
+	case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
+		if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE)
+			priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_CARRY_BORROW;
+		else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_CARRY_BORROW)
+			return -EINVAL;
+		return 0;
+	case COUNTER_EVENT_INDEX:
+		if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE)
+			priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_INDEX;
+		else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_INDEX)
+			return -EINVAL;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
 static const struct counter_ops quad8_ops = {
 	.signal_read = quad8_signal_read,
 	.count_read = quad8_count_read,
 	.count_write = quad8_count_write,
 	.function_read = quad8_function_read,
 	.function_write = quad8_function_write,
-	.action_read = quad8_action_read
+	.action_read = quad8_action_read,
+	.events_configure = quad8_events_configure,
+	.watch_validate = quad8_watch_validate,
 };
 
 static const char *const quad8_index_polarity_modes[] = {
@@ -579,7 +680,8 @@ static int quad8_count_enable_write(struct counter_device *counter,
 
 	priv->ab_enable[count->id] = enable;
 
-	ior_cfg = enable | priv->preset_enable[count->id] << 1;
+	ior_cfg = enable | priv->preset_enable[count->id] << 1 |
+		  priv->irq_trigger[count->id] << 3;
 
 	/* Load I/O control configuration */
 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
@@ -728,7 +830,8 @@ static int quad8_count_preset_enable_write(struct counter_device *counter,
 
 	priv->preset_enable[count->id] = preset_enable;
 
-	ior_cfg = priv->ab_enable[count->id] | preset_enable << 1;
+	ior_cfg = priv->ab_enable[count->id] | preset_enable << 1 |
+		  priv->irq_trigger[count->id] << 3;
 
 	/* Load I/O control configuration to Input / Output Control Register */
 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
@@ -980,11 +1083,54 @@ static struct counter_count quad8_counts[] = {
 	QUAD8_COUNT(7, "Channel 8 Count")
 };
 
+static irqreturn_t quad8_irq_handler(int irq, void *private)
+{
+	struct quad8 *const priv = private;
+	const unsigned long base = priv->base;
+	unsigned long irq_status;
+	unsigned long channel;
+	u8 event;
+
+	irq_status = inb(base + QUAD8_REG_INTERRUPT_STATUS);
+	if (!irq_status)
+		return IRQ_NONE;
+
+	for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS) {
+		switch (priv->irq_trigger[channel]) {
+		case QUAD8_EVENT_CARRY:
+			event = COUNTER_EVENT_OVERFLOW;
+				break;
+		case QUAD8_EVENT_COMPARE:
+			event = COUNTER_EVENT_THRESHOLD;
+				break;
+		case QUAD8_EVENT_CARRY_BORROW:
+			event = COUNTER_EVENT_OVERFLOW_UNDERFLOW;
+				break;
+		case QUAD8_EVENT_INDEX:
+			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);
+			continue;
+		}
+
+		counter_push_event(&priv->counter, event, channel);
+	}
+
+	/* Clear pending interrupts on device */
+	outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base + QUAD8_REG_CHAN_OP);
+
+	return IRQ_HANDLED;
+}
+
 static int quad8_probe(struct device *dev, unsigned int id)
 {
 	struct quad8 *priv;
 	int i, j;
 	unsigned int base_offset;
+	int err;
 
 	if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
@@ -1009,6 +1155,8 @@ static int quad8_probe(struct device *dev, unsigned int id)
 
 	spin_lock_init(&priv->lock);
 
+	/* Reset Index/Interrupt Register */
+	outb(0x00, base[id] + QUAD8_REG_INDEX_INTERRUPT);
 	/* Reset all counters and disable interrupt function */
 	outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
 	/* Set initial configuration for all counters */
@@ -1035,11 +1183,18 @@ static int quad8_probe(struct device *dev, unsigned int id)
 		outb(QUAD8_CTR_IOR, base_offset + 1);
 		/* Disable index function; negative index polarity */
 		outb(QUAD8_CTR_IDR, base_offset + 1);
+		/* Initialize next IRQ trigger function configuration */
+		priv->next_irq_trigger[i] = QUAD8_EVENT_NONE;
 	}
 	/* Disable Differential Encoder Cable Status for all channels */
 	outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS);
-	/* Enable all counters */
-	outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
+	/* Enable all counters and enable interrupt function */
+	outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base[id] + QUAD8_REG_CHAN_OP);
+
+	err = devm_request_irq(dev, irq[id], quad8_irq_handler, IRQF_SHARED,
+			       priv->counter.name, priv);
+	if (err)
+		return err;
 
 	return devm_counter_register(dev, &priv->counter);
 }
diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
index d5d2540b30c2..3dcdb681c4e4 100644
--- a/drivers/counter/Kconfig
+++ b/drivers/counter/Kconfig
@@ -23,11 +23,11 @@ config 104_QUAD_8
 	  A counter's respective error flag may be cleared by performing a write
 	  operation on the respective count value attribute. Although the
 	  104-QUAD-8 counters have a 25-bit range, only the lower 24 bits may be
-	  set, either directly or via the counter's preset attribute. Interrupts
-	  are not supported by this driver.
+	  set, either directly or via the counter's preset attribute.
 
 	  The base port addresses for the devices may be configured via the base
-	  array module parameter.
+	  array module parameter. The interrupt line numbers for the devices may
+	  be configured via the irq array module parameter.
 
 config INTERRUPT_CNT
 	tristate "Interrupt counter driver"
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 03/14] counter: Internalize sysfs interface code
  2021-08-27  3:47 ` [PATCH v16 03/14] counter: Internalize sysfs interface code William Breathitt Gray
@ 2021-08-27 14:09   ` kernel test robot
  2021-08-28  4:56   ` kernel test robot
  2021-08-31 13:44   ` [Linux-stm32] " Fabrice Gasnier
  2 siblings, 0 replies; 69+ messages in thread
From: kernel test robot @ 2021-08-27 14:09 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 5259 bytes --]

Hi William,

I love your patch! Yet something to improve:

[auto build test ERROR on 5ffeb17c0d3dd44704b4aee83e297ec07666e4d6]

url:    https://github.com/0day-ci/linux/commits/William-Breathitt-Gray/Introduce-the-Counter-character-device-interface/20210827-115154
base:   5ffeb17c0d3dd44704b4aee83e297ec07666e4d6
config: i386-randconfig-c001-20210827 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce (this is a W=1 build):
        # https://github.com/0day-ci/linux/commit/4eb2e2f3fe9da9dddf9d8bbb82ff16b1329131ce
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review William-Breathitt-Gray/Introduce-the-Counter-character-device-interface/20210827-115154
        git checkout 4eb2e2f3fe9da9dddf9d8bbb82ff16b1329131ce
        # save the attached .config to linux build tree
        mkdir build_dir
        make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   ld: drivers/counter/intel-qep.o: in function `intel_qep_spike_filter_ns_write':
>> drivers/counter/intel-qep.c:290: undefined reference to `__udivdi3'


vim +290 drivers/counter/intel-qep.c

b711f687a1c1c1 Jarkko Nikula          2021-06-02  275  
4eb2e2f3fe9da9 William Breathitt Gray 2021-08-27  276  static int intel_qep_spike_filter_ns_write(struct counter_device *counter,
b711f687a1c1c1 Jarkko Nikula          2021-06-02  277  					   struct counter_count *count,
4eb2e2f3fe9da9 William Breathitt Gray 2021-08-27  278  					   u64 length)
b711f687a1c1c1 Jarkko Nikula          2021-06-02  279  {
b711f687a1c1c1 Jarkko Nikula          2021-06-02  280  	struct intel_qep *qep = counter->priv;
4eb2e2f3fe9da9 William Breathitt Gray 2021-08-27  281  	u32 reg;
b711f687a1c1c1 Jarkko Nikula          2021-06-02  282  	bool enable;
4eb2e2f3fe9da9 William Breathitt Gray 2021-08-27  283  	int ret = 0;
b711f687a1c1c1 Jarkko Nikula          2021-06-02  284  
b711f687a1c1c1 Jarkko Nikula          2021-06-02  285  	/*
b711f687a1c1c1 Jarkko Nikula          2021-06-02  286  	 * Spike filter length is (MAX_COUNT + 2) clock periods.
b711f687a1c1c1 Jarkko Nikula          2021-06-02  287  	 * Disable filter when userspace writes 0, enable for valid
b711f687a1c1c1 Jarkko Nikula          2021-06-02  288  	 * nanoseconds values and error out otherwise.
b711f687a1c1c1 Jarkko Nikula          2021-06-02  289  	 */
b711f687a1c1c1 Jarkko Nikula          2021-06-02 @290  	length /= INTEL_QEP_CLK_PERIOD_NS;
b711f687a1c1c1 Jarkko Nikula          2021-06-02  291  	if (length == 0) {
b711f687a1c1c1 Jarkko Nikula          2021-06-02  292  		enable = false;
b711f687a1c1c1 Jarkko Nikula          2021-06-02  293  		length = 0;
b711f687a1c1c1 Jarkko Nikula          2021-06-02  294  	} else if (length >= 2) {
b711f687a1c1c1 Jarkko Nikula          2021-06-02  295  		enable = true;
b711f687a1c1c1 Jarkko Nikula          2021-06-02  296  		length -= 2;
b711f687a1c1c1 Jarkko Nikula          2021-06-02  297  	} else {
b711f687a1c1c1 Jarkko Nikula          2021-06-02  298  		return -EINVAL;
b711f687a1c1c1 Jarkko Nikula          2021-06-02  299  	}
b711f687a1c1c1 Jarkko Nikula          2021-06-02  300  
b711f687a1c1c1 Jarkko Nikula          2021-06-02  301  	if (length > INTEL_QEPFLT_MAX_COUNT(length))
e2ff3198c580a3 William Breathitt Gray 2021-08-03  302  		return -ERANGE;
b711f687a1c1c1 Jarkko Nikula          2021-06-02  303  
b711f687a1c1c1 Jarkko Nikula          2021-06-02  304  	mutex_lock(&qep->lock);
b711f687a1c1c1 Jarkko Nikula          2021-06-02  305  	if (qep->enabled) {
b711f687a1c1c1 Jarkko Nikula          2021-06-02  306  		ret = -EBUSY;
b711f687a1c1c1 Jarkko Nikula          2021-06-02  307  		goto out;
b711f687a1c1c1 Jarkko Nikula          2021-06-02  308  	}
b711f687a1c1c1 Jarkko Nikula          2021-06-02  309  
b711f687a1c1c1 Jarkko Nikula          2021-06-02  310  	pm_runtime_get_sync(qep->dev);
b711f687a1c1c1 Jarkko Nikula          2021-06-02  311  	reg = intel_qep_readl(qep, INTEL_QEPCON);
b711f687a1c1c1 Jarkko Nikula          2021-06-02  312  	if (enable)
b711f687a1c1c1 Jarkko Nikula          2021-06-02  313  		reg |= INTEL_QEPCON_FLT_EN;
b711f687a1c1c1 Jarkko Nikula          2021-06-02  314  	else
b711f687a1c1c1 Jarkko Nikula          2021-06-02  315  		reg &= ~INTEL_QEPCON_FLT_EN;
b711f687a1c1c1 Jarkko Nikula          2021-06-02  316  	intel_qep_writel(qep, INTEL_QEPFLT, length);
b711f687a1c1c1 Jarkko Nikula          2021-06-02  317  	intel_qep_writel(qep, INTEL_QEPCON, reg);
b711f687a1c1c1 Jarkko Nikula          2021-06-02  318  	pm_runtime_put(qep->dev);
b711f687a1c1c1 Jarkko Nikula          2021-06-02  319  
b711f687a1c1c1 Jarkko Nikula          2021-06-02  320  out:
b711f687a1c1c1 Jarkko Nikula          2021-06-02  321  	mutex_unlock(&qep->lock);
b711f687a1c1c1 Jarkko Nikula          2021-06-02  322  	return ret;
b711f687a1c1c1 Jarkko Nikula          2021-06-02  323  }
b711f687a1c1c1 Jarkko Nikula          2021-06-02  324  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 29020 bytes --]

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 03/14] counter: Internalize sysfs interface code
  2021-08-27  3:47 ` [PATCH v16 03/14] counter: Internalize sysfs interface code William Breathitt Gray
  2021-08-27 14:09   ` kernel test robot
@ 2021-08-28  4:56   ` kernel test robot
  2021-08-31 13:44   ` [Linux-stm32] " Fabrice Gasnier
  2 siblings, 0 replies; 69+ messages in thread
From: kernel test robot @ 2021-08-28  4:56 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 1590 bytes --]

Hi William,

I love your patch! Yet something to improve:

[auto build test ERROR on 5ffeb17c0d3dd44704b4aee83e297ec07666e4d6]

url:    https://github.com/0day-ci/linux/commits/William-Breathitt-Gray/Introduce-the-Counter-character-device-interface/20210827-115154
base:   5ffeb17c0d3dd44704b4aee83e297ec07666e4d6
config: arm-allyesconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/4eb2e2f3fe9da9dddf9d8bbb82ff16b1329131ce
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review William-Breathitt-Gray/Introduce-the-Counter-character-device-interface/20210827-115154
        git checkout 4eb2e2f3fe9da9dddf9d8bbb82ff16b1329131ce
        # save the attached .config to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=arm SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   arm-linux-gnueabi-ld: drivers/counter/intel-qep.o: in function `intel_qep_spike_filter_ns_write':
>> intel-qep.c:(.text+0x540): undefined reference to `__aeabi_uldivmod'

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 78514 bytes --]

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 00/14] Introduce the Counter character device interface
  2021-08-27  3:47 ` William Breathitt Gray
@ 2021-08-30 17:17   ` Jonathan Cameron
  -1 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-08-30 17:17 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula

On Fri, 27 Aug 2021 12:47:44 +0900
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> Changes in v16:
>  - Define magic numbers for stm32-lptimer-cnt clock polarities
>  - Define magic numbers for stm32-timer-cnt encoder modes
>  - Bump KernelVersion to 5.16 in sysfs-bus-counter ABI documentation
>  - Fix typos in driver API generic-counter.rst documentation file
> 
> For convenience, this patchset is also available on my personal git
> repo: https://gitlab.com/vilhelmgray/iio/-/tree/counter_chrdev_v16
> 
> The patches preceding "counter: Internalize sysfs interface code" are
> primarily cleanup and fixes that can be picked up and applied now to the
> IIO tree if so desired. The "counter: Internalize sysfs interface code"
> patch as well may be considered for pickup because it is relatively safe
> and makes no changes to the userspace interface.
> 
> To summarize the main points of this patchset: there are no changes to
> the existing Counter sysfs userspace interface; a Counter character
> device interface is introduced that allows Counter events and associated
> data to be read() by userspace; the events_configure() and
> watch_validate() driver callbacks are introduced to support Counter
> events; and IRQ support is added to the 104-QUAD-8 driver, serving as an
> example of how to support the new Counter events functionality.

Hi William,

I'll aim to pick up the first part in a week (too tired today after a lot
of reviewing to even manage the basic sanity check on the changes).

For the rest...

What I'd really like to know is if anyone other than William and I is planning
to review them in depth? (particularly 7 and 8 which are the new interface
patch and docs)

So if anyone reading this is in that category please let me know.  We can wait,
but conversely if no one is going to get time / inclination to do it then I
don't want to hold these up any longer and maximum time in linux-next may
be more useful than sitting unloved on the mailing list.

Jonathan

> 
> William Breathitt Gray (14):
>   counter: stm32-lptimer-cnt: Provide defines for clock polarities
>   counter: stm32-timer-cnt: Provide defines for slave mode selection
>   counter: Internalize sysfs interface code
>   counter: Update counter.h comments to reflect sysfs internalization
>   docs: counter: Update to reflect sysfs internalization
>   counter: Move counter enums to uapi header
>   counter: Add character device interface
>   docs: counter: Document character device interface
>   tools/counter: Create Counter tools
>   counter: Implement signalZ_action_component_id sysfs attribute
>   counter: Implement *_component_id sysfs attributes
>   counter: Implement events_queue_size sysfs attribute
>   counter: 104-quad-8: Replace mutex with spinlock
>   counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
> 
>  Documentation/ABI/testing/sysfs-bus-counter   |   38 +-
>  Documentation/driver-api/generic-counter.rst  |  358 +++-
>  .../userspace-api/ioctl/ioctl-number.rst      |    1 +
>  MAINTAINERS                                   |    3 +-
>  drivers/counter/104-quad-8.c                  |  699 ++++----
>  drivers/counter/Kconfig                       |    6 +-
>  drivers/counter/Makefile                      |    1 +
>  drivers/counter/counter-chrdev.c              |  553 ++++++
>  drivers/counter/counter-chrdev.h              |   14 +
>  drivers/counter/counter-core.c                |  191 +++
>  drivers/counter/counter-sysfs.c               |  960 +++++++++++
>  drivers/counter/counter-sysfs.h               |   13 +
>  drivers/counter/counter.c                     | 1496 -----------------
>  drivers/counter/ftm-quaddec.c                 |   60 +-
>  drivers/counter/intel-qep.c                   |  144 +-
>  drivers/counter/interrupt-cnt.c               |   62 +-
>  drivers/counter/microchip-tcb-capture.c       |   91 +-
>  drivers/counter/stm32-lptimer-cnt.c           |  212 ++-
>  drivers/counter/stm32-timer-cnt.c             |  195 +--
>  drivers/counter/ti-eqep.c                     |  180 +-
>  include/linux/counter.h                       |  715 ++++----
>  include/linux/counter_enum.h                  |   45 -
>  include/linux/mfd/stm32-lptimer.h             |    5 +
>  include/linux/mfd/stm32-timers.h              |    4 +
>  include/uapi/linux/counter.h                  |  154 ++
>  tools/Makefile                                |   13 +-
>  tools/counter/Build                           |    1 +
>  tools/counter/Makefile                        |   53 +
>  tools/counter/counter_example.c               |   93 +
>  29 files changed, 3569 insertions(+), 2791 deletions(-)
>  create mode 100644 drivers/counter/counter-chrdev.c
>  create mode 100644 drivers/counter/counter-chrdev.h
>  create mode 100644 drivers/counter/counter-core.c
>  create mode 100644 drivers/counter/counter-sysfs.c
>  create mode 100644 drivers/counter/counter-sysfs.h
>  delete mode 100644 drivers/counter/counter.c
>  delete mode 100644 include/linux/counter_enum.h
>  create mode 100644 include/uapi/linux/counter.h
>  create mode 100644 tools/counter/Build
>  create mode 100644 tools/counter/Makefile
>  create mode 100644 tools/counter/counter_example.c
> 
> 
> base-commit: 5ffeb17c0d3dd44704b4aee83e297ec07666e4d6


^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 00/14] Introduce the Counter character device interface
@ 2021-08-30 17:17   ` Jonathan Cameron
  0 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-08-30 17:17 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula

On Fri, 27 Aug 2021 12:47:44 +0900
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> Changes in v16:
>  - Define magic numbers for stm32-lptimer-cnt clock polarities
>  - Define magic numbers for stm32-timer-cnt encoder modes
>  - Bump KernelVersion to 5.16 in sysfs-bus-counter ABI documentation
>  - Fix typos in driver API generic-counter.rst documentation file
> 
> For convenience, this patchset is also available on my personal git
> repo: https://gitlab.com/vilhelmgray/iio/-/tree/counter_chrdev_v16
> 
> The patches preceding "counter: Internalize sysfs interface code" are
> primarily cleanup and fixes that can be picked up and applied now to the
> IIO tree if so desired. The "counter: Internalize sysfs interface code"
> patch as well may be considered for pickup because it is relatively safe
> and makes no changes to the userspace interface.
> 
> To summarize the main points of this patchset: there are no changes to
> the existing Counter sysfs userspace interface; a Counter character
> device interface is introduced that allows Counter events and associated
> data to be read() by userspace; the events_configure() and
> watch_validate() driver callbacks are introduced to support Counter
> events; and IRQ support is added to the 104-QUAD-8 driver, serving as an
> example of how to support the new Counter events functionality.

Hi William,

I'll aim to pick up the first part in a week (too tired today after a lot
of reviewing to even manage the basic sanity check on the changes).

For the rest...

What I'd really like to know is if anyone other than William and I is planning
to review them in depth? (particularly 7 and 8 which are the new interface
patch and docs)

So if anyone reading this is in that category please let me know.  We can wait,
but conversely if no one is going to get time / inclination to do it then I
don't want to hold these up any longer and maximum time in linux-next may
be more useful than sitting unloved on the mailing list.

Jonathan

> 
> William Breathitt Gray (14):
>   counter: stm32-lptimer-cnt: Provide defines for clock polarities
>   counter: stm32-timer-cnt: Provide defines for slave mode selection
>   counter: Internalize sysfs interface code
>   counter: Update counter.h comments to reflect sysfs internalization
>   docs: counter: Update to reflect sysfs internalization
>   counter: Move counter enums to uapi header
>   counter: Add character device interface
>   docs: counter: Document character device interface
>   tools/counter: Create Counter tools
>   counter: Implement signalZ_action_component_id sysfs attribute
>   counter: Implement *_component_id sysfs attributes
>   counter: Implement events_queue_size sysfs attribute
>   counter: 104-quad-8: Replace mutex with spinlock
>   counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
> 
>  Documentation/ABI/testing/sysfs-bus-counter   |   38 +-
>  Documentation/driver-api/generic-counter.rst  |  358 +++-
>  .../userspace-api/ioctl/ioctl-number.rst      |    1 +
>  MAINTAINERS                                   |    3 +-
>  drivers/counter/104-quad-8.c                  |  699 ++++----
>  drivers/counter/Kconfig                       |    6 +-
>  drivers/counter/Makefile                      |    1 +
>  drivers/counter/counter-chrdev.c              |  553 ++++++
>  drivers/counter/counter-chrdev.h              |   14 +
>  drivers/counter/counter-core.c                |  191 +++
>  drivers/counter/counter-sysfs.c               |  960 +++++++++++
>  drivers/counter/counter-sysfs.h               |   13 +
>  drivers/counter/counter.c                     | 1496 -----------------
>  drivers/counter/ftm-quaddec.c                 |   60 +-
>  drivers/counter/intel-qep.c                   |  144 +-
>  drivers/counter/interrupt-cnt.c               |   62 +-
>  drivers/counter/microchip-tcb-capture.c       |   91 +-
>  drivers/counter/stm32-lptimer-cnt.c           |  212 ++-
>  drivers/counter/stm32-timer-cnt.c             |  195 +--
>  drivers/counter/ti-eqep.c                     |  180 +-
>  include/linux/counter.h                       |  715 ++++----
>  include/linux/counter_enum.h                  |   45 -
>  include/linux/mfd/stm32-lptimer.h             |    5 +
>  include/linux/mfd/stm32-timers.h              |    4 +
>  include/uapi/linux/counter.h                  |  154 ++
>  tools/Makefile                                |   13 +-
>  tools/counter/Build                           |    1 +
>  tools/counter/Makefile                        |   53 +
>  tools/counter/counter_example.c               |   93 +
>  29 files changed, 3569 insertions(+), 2791 deletions(-)
>  create mode 100644 drivers/counter/counter-chrdev.c
>  create mode 100644 drivers/counter/counter-chrdev.h
>  create mode 100644 drivers/counter/counter-core.c
>  create mode 100644 drivers/counter/counter-sysfs.c
>  create mode 100644 drivers/counter/counter-sysfs.h
>  delete mode 100644 drivers/counter/counter.c
>  delete mode 100644 include/linux/counter_enum.h
>  create mode 100644 include/uapi/linux/counter.h
>  create mode 100644 tools/counter/Build
>  create mode 100644 tools/counter/Makefile
>  create mode 100644 tools/counter/counter_example.c
> 
> 
> base-commit: 5ffeb17c0d3dd44704b4aee83e297ec07666e4d6


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 01/14] counter: stm32-lptimer-cnt: Provide defines for clock polarities
  2021-08-27  3:47   ` William Breathitt Gray
@ 2021-08-31 13:38     ` Fabrice Gasnier
  -1 siblings, 0 replies; 69+ messages in thread
From: Fabrice Gasnier @ 2021-08-31 13:38 UTC (permalink / raw)
  To: William Breathitt Gray, jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula



On 8/27/21 5:47 AM, William Breathitt Gray wrote:
> The STM32 low-power timer permits configuration of the clock polarity
> via the LPTIMX_CFGR register CKPOL bits. This patch provides
> preprocessor defines for the supported clock polarities.
> 
> Cc: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> ---
>  drivers/counter/stm32-lptimer-cnt.c | 6 +++---
>  include/linux/mfd/stm32-lptimer.h   | 5 +++++
>  2 files changed, 8 insertions(+), 3 deletions(-)

Hi William,

You can add my:
Reviewed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>

Thanks,
Fabrice

> 
> diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c
> index 13656957c45f..7367f46c6f91 100644
> --- a/drivers/counter/stm32-lptimer-cnt.c
> +++ b/drivers/counter/stm32-lptimer-cnt.c
> @@ -140,9 +140,9 @@ static const enum counter_function stm32_lptim_cnt_functions[] = {
>  };
>  
>  enum stm32_lptim_synapse_action {
> -	STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE,
> -	STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE,
> -	STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES,
> +	STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE = STM32_LPTIM_CKPOL_RISING_EDGE,
> +	STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE = STM32_LPTIM_CKPOL_FALLING_EDGE,
> +	STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES = STM32_LPTIM_CKPOL_BOTH_EDGES,
>  	STM32_LPTIM_SYNAPSE_ACTION_NONE,
>  };
>  
> diff --git a/include/linux/mfd/stm32-lptimer.h b/include/linux/mfd/stm32-lptimer.h
> index 90b20550c1c8..06d3f11dc3c9 100644
> --- a/include/linux/mfd/stm32-lptimer.h
> +++ b/include/linux/mfd/stm32-lptimer.h
> @@ -45,6 +45,11 @@
>  #define STM32_LPTIM_PRESC	GENMASK(11, 9)
>  #define STM32_LPTIM_CKPOL	GENMASK(2, 1)
>  
> +/* STM32_LPTIM_CKPOL */
> +#define STM32_LPTIM_CKPOL_RISING_EDGE	0
> +#define STM32_LPTIM_CKPOL_FALLING_EDGE	1
> +#define STM32_LPTIM_CKPOL_BOTH_EDGES	2
> +
>  /* STM32_LPTIM_ARR */
>  #define STM32_LPTIM_MAX_ARR	0xFFFF
>  
> 

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 01/14] counter: stm32-lptimer-cnt: Provide defines for clock polarities
@ 2021-08-31 13:38     ` Fabrice Gasnier
  0 siblings, 0 replies; 69+ messages in thread
From: Fabrice Gasnier @ 2021-08-31 13:38 UTC (permalink / raw)
  To: William Breathitt Gray, jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula



On 8/27/21 5:47 AM, William Breathitt Gray wrote:
> The STM32 low-power timer permits configuration of the clock polarity
> via the LPTIMX_CFGR register CKPOL bits. This patch provides
> preprocessor defines for the supported clock polarities.
> 
> Cc: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> ---
>  drivers/counter/stm32-lptimer-cnt.c | 6 +++---
>  include/linux/mfd/stm32-lptimer.h   | 5 +++++
>  2 files changed, 8 insertions(+), 3 deletions(-)

Hi William,

You can add my:
Reviewed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>

Thanks,
Fabrice

> 
> diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c
> index 13656957c45f..7367f46c6f91 100644
> --- a/drivers/counter/stm32-lptimer-cnt.c
> +++ b/drivers/counter/stm32-lptimer-cnt.c
> @@ -140,9 +140,9 @@ static const enum counter_function stm32_lptim_cnt_functions[] = {
>  };
>  
>  enum stm32_lptim_synapse_action {
> -	STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE,
> -	STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE,
> -	STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES,
> +	STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE = STM32_LPTIM_CKPOL_RISING_EDGE,
> +	STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE = STM32_LPTIM_CKPOL_FALLING_EDGE,
> +	STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES = STM32_LPTIM_CKPOL_BOTH_EDGES,
>  	STM32_LPTIM_SYNAPSE_ACTION_NONE,
>  };
>  
> diff --git a/include/linux/mfd/stm32-lptimer.h b/include/linux/mfd/stm32-lptimer.h
> index 90b20550c1c8..06d3f11dc3c9 100644
> --- a/include/linux/mfd/stm32-lptimer.h
> +++ b/include/linux/mfd/stm32-lptimer.h
> @@ -45,6 +45,11 @@
>  #define STM32_LPTIM_PRESC	GENMASK(11, 9)
>  #define STM32_LPTIM_CKPOL	GENMASK(2, 1)
>  
> +/* STM32_LPTIM_CKPOL */
> +#define STM32_LPTIM_CKPOL_RISING_EDGE	0
> +#define STM32_LPTIM_CKPOL_FALLING_EDGE	1
> +#define STM32_LPTIM_CKPOL_BOTH_EDGES	2
> +
>  /* STM32_LPTIM_ARR */
>  #define STM32_LPTIM_MAX_ARR	0xFFFF
>  
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 02/14] counter: stm32-timer-cnt: Provide defines for slave mode selection
  2021-08-27  3:47   ` William Breathitt Gray
@ 2021-08-31 13:40     ` Fabrice Gasnier
  -1 siblings, 0 replies; 69+ messages in thread
From: Fabrice Gasnier @ 2021-08-31 13:40 UTC (permalink / raw)
  To: William Breathitt Gray, jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula

On 8/27/21 5:47 AM, William Breathitt Gray wrote:
> The STM32 timer permits configuration of the counter encoder mode via
> the slave mode control register (SMCR) slave mode selection (SMS) bits.
> This patch provides preprocessor defines for the supported encoder
> modes.
> 
> Cc: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> ---
>  drivers/counter/stm32-timer-cnt.c | 16 ++++++++--------
>  include/linux/mfd/stm32-timers.h  |  4 ++++
>  2 files changed, 12 insertions(+), 8 deletions(-)

Hi William,

You can add my:
Reviewed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>

Thanks,
Fabrice

> 
> diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c
> index 3fb0debd7425..1fbc46f4ee66 100644
> --- a/drivers/counter/stm32-timer-cnt.c
> +++ b/drivers/counter/stm32-timer-cnt.c
> @@ -93,16 +93,16 @@ static int stm32_count_function_get(struct counter_device *counter,
>  	regmap_read(priv->regmap, TIM_SMCR, &smcr);
>  
>  	switch (smcr & TIM_SMCR_SMS) {
> -	case 0:
> +	case TIM_SMCR_SMS_SLAVE_MODE_DISABLED:
>  		*function = STM32_COUNT_SLAVE_MODE_DISABLED;
>  		return 0;
> -	case 1:
> +	case TIM_SMCR_SMS_ENCODER_MODE_1:
>  		*function = STM32_COUNT_ENCODER_MODE_1;
>  		return 0;
> -	case 2:
> +	case TIM_SMCR_SMS_ENCODER_MODE_2:
>  		*function = STM32_COUNT_ENCODER_MODE_2;
>  		return 0;
> -	case 3:
> +	case TIM_SMCR_SMS_ENCODER_MODE_3:
>  		*function = STM32_COUNT_ENCODER_MODE_3;
>  		return 0;
>  	default:
> @@ -119,16 +119,16 @@ static int stm32_count_function_set(struct counter_device *counter,
>  
>  	switch (function) {
>  	case STM32_COUNT_SLAVE_MODE_DISABLED:
> -		sms = 0;
> +		sms = TIM_SMCR_SMS_SLAVE_MODE_DISABLED;
>  		break;
>  	case STM32_COUNT_ENCODER_MODE_1:
> -		sms = 1;
> +		sms = TIM_SMCR_SMS_ENCODER_MODE_1;
>  		break;
>  	case STM32_COUNT_ENCODER_MODE_2:
> -		sms = 2;
> +		sms = TIM_SMCR_SMS_ENCODER_MODE_2;
>  		break;
>  	case STM32_COUNT_ENCODER_MODE_3:
> -		sms = 3;
> +		sms = TIM_SMCR_SMS_ENCODER_MODE_3;
>  		break;
>  	default:
>  		return -EINVAL;
> diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h
> index f8db83aedb2b..5f5c43fd69dd 100644
> --- a/include/linux/mfd/stm32-timers.h
> +++ b/include/linux/mfd/stm32-timers.h
> @@ -82,6 +82,10 @@
>  #define MAX_TIM_ICPSC		0x3
>  #define TIM_CR2_MMS_SHIFT	4
>  #define TIM_CR2_MMS2_SHIFT	20
> +#define TIM_SMCR_SMS_SLAVE_MODE_DISABLED	0 /* counts on internal clock when CEN=1 */
> +#define TIM_SMCR_SMS_ENCODER_MODE_1		1 /* counts TI1FP1 edges, depending on TI2FP2 level */
> +#define TIM_SMCR_SMS_ENCODER_MODE_2		2 /* counts TI2FP2 edges, depending on TI1FP1 level */
> +#define TIM_SMCR_SMS_ENCODER_MODE_3		3 /* counts on both TI1FP1 and TI2FP2 edges */
>  #define TIM_SMCR_TS_SHIFT	4
>  #define TIM_BDTR_BKF_MASK	0xF
>  #define TIM_BDTR_BKF_SHIFT(x)	(16 + (x) * 4)
> 

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 02/14] counter: stm32-timer-cnt: Provide defines for slave mode selection
@ 2021-08-31 13:40     ` Fabrice Gasnier
  0 siblings, 0 replies; 69+ messages in thread
From: Fabrice Gasnier @ 2021-08-31 13:40 UTC (permalink / raw)
  To: William Breathitt Gray, jic23
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula

On 8/27/21 5:47 AM, William Breathitt Gray wrote:
> The STM32 timer permits configuration of the counter encoder mode via
> the slave mode control register (SMCR) slave mode selection (SMS) bits.
> This patch provides preprocessor defines for the supported encoder
> modes.
> 
> Cc: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> ---
>  drivers/counter/stm32-timer-cnt.c | 16 ++++++++--------
>  include/linux/mfd/stm32-timers.h  |  4 ++++
>  2 files changed, 12 insertions(+), 8 deletions(-)

Hi William,

You can add my:
Reviewed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>

Thanks,
Fabrice

> 
> diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c
> index 3fb0debd7425..1fbc46f4ee66 100644
> --- a/drivers/counter/stm32-timer-cnt.c
> +++ b/drivers/counter/stm32-timer-cnt.c
> @@ -93,16 +93,16 @@ static int stm32_count_function_get(struct counter_device *counter,
>  	regmap_read(priv->regmap, TIM_SMCR, &smcr);
>  
>  	switch (smcr & TIM_SMCR_SMS) {
> -	case 0:
> +	case TIM_SMCR_SMS_SLAVE_MODE_DISABLED:
>  		*function = STM32_COUNT_SLAVE_MODE_DISABLED;
>  		return 0;
> -	case 1:
> +	case TIM_SMCR_SMS_ENCODER_MODE_1:
>  		*function = STM32_COUNT_ENCODER_MODE_1;
>  		return 0;
> -	case 2:
> +	case TIM_SMCR_SMS_ENCODER_MODE_2:
>  		*function = STM32_COUNT_ENCODER_MODE_2;
>  		return 0;
> -	case 3:
> +	case TIM_SMCR_SMS_ENCODER_MODE_3:
>  		*function = STM32_COUNT_ENCODER_MODE_3;
>  		return 0;
>  	default:
> @@ -119,16 +119,16 @@ static int stm32_count_function_set(struct counter_device *counter,
>  
>  	switch (function) {
>  	case STM32_COUNT_SLAVE_MODE_DISABLED:
> -		sms = 0;
> +		sms = TIM_SMCR_SMS_SLAVE_MODE_DISABLED;
>  		break;
>  	case STM32_COUNT_ENCODER_MODE_1:
> -		sms = 1;
> +		sms = TIM_SMCR_SMS_ENCODER_MODE_1;
>  		break;
>  	case STM32_COUNT_ENCODER_MODE_2:
> -		sms = 2;
> +		sms = TIM_SMCR_SMS_ENCODER_MODE_2;
>  		break;
>  	case STM32_COUNT_ENCODER_MODE_3:
> -		sms = 3;
> +		sms = TIM_SMCR_SMS_ENCODER_MODE_3;
>  		break;
>  	default:
>  		return -EINVAL;
> diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h
> index f8db83aedb2b..5f5c43fd69dd 100644
> --- a/include/linux/mfd/stm32-timers.h
> +++ b/include/linux/mfd/stm32-timers.h
> @@ -82,6 +82,10 @@
>  #define MAX_TIM_ICPSC		0x3
>  #define TIM_CR2_MMS_SHIFT	4
>  #define TIM_CR2_MMS2_SHIFT	20
> +#define TIM_SMCR_SMS_SLAVE_MODE_DISABLED	0 /* counts on internal clock when CEN=1 */
> +#define TIM_SMCR_SMS_ENCODER_MODE_1		1 /* counts TI1FP1 edges, depending on TI2FP2 level */
> +#define TIM_SMCR_SMS_ENCODER_MODE_2		2 /* counts TI2FP2 edges, depending on TI1FP1 level */
> +#define TIM_SMCR_SMS_ENCODER_MODE_3		3 /* counts on both TI1FP1 and TI2FP2 edges */
>  #define TIM_SMCR_TS_SHIFT	4
>  #define TIM_BDTR_BKF_MASK	0xF
>  #define TIM_BDTR_BKF_SHIFT(x)	(16 + (x) * 4)
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Linux-stm32] [PATCH v16 03/14] counter: Internalize sysfs interface code
  2021-08-27  3:47 ` [PATCH v16 03/14] counter: Internalize sysfs interface code William Breathitt Gray
  2021-08-27 14:09   ` kernel test robot
  2021-08-28  4:56   ` kernel test robot
@ 2021-08-31 13:44   ` Fabrice Gasnier
  2021-08-31 14:16       ` William Breathitt Gray
  2021-09-08 17:44     ` Jonathan Cameron
  2 siblings, 2 replies; 69+ messages in thread
From: Fabrice Gasnier @ 2021-08-31 13:44 UTC (permalink / raw)
  To: William Breathitt Gray, jic23
  Cc: kamel.bouhara, gwendal, david, linux-iio, patrick.havelange,
	alexandre.belloni, mcoquelin.stm32, linux-kernel, o.rempel,
	jarkko.nikula, Dan Carpenter, kernel, fabrice.gasnier,
	syednwaris, linux-stm32, linux-arm-kernel, alexandre.torgue

On 8/27/21 5:47 AM, William Breathitt Gray wrote:
> This is a reimplementation of the Generic Counter driver interface.
> There are no modifications to the Counter subsystem userspace interface,
> so existing userspace applications should continue to run seamlessly.
> 
> The purpose of this patch is to internalize the sysfs interface code
> among the various counter drivers into a shared module. Counter drivers
> pass and take data natively (i.e. u8, u64, etc.) and the shared counter
> module handles the translation between the sysfs interface and the
> device drivers. This guarantees a standard userspace interface for all
> counter drivers, and helps generalize the Generic Counter driver ABI in
> order to support the Generic Counter chrdev interface (introduced in a
> subsequent patch) without significant changes to the existing counter
> drivers.
> 
> Note, Counter device registration is the same as before: drivers
> populate a struct counter_device with components and callbacks, then
> pass the structure to the devm_counter_register function. However,
> what's different now is how the Counter subsystem code handles this
> registration internally.
> 
> Whereas before callbacks would interact directly with sysfs data, this
> interaction is now abstracted and instead callbacks interact with native
> C data types. The counter_comp structure forms the basis for Counter
> extensions.
> 
> The counter-sysfs.c file contains the code to parse through the
> counter_device structure and register the requested components and
> extensions. Attributes are created and populated based on type, with
> respective translation functions to handle the mapping between sysfs and
> the counter driver callbacks.
> 
> The translation performed for each attribute is straightforward: the
> attribute type and data is parsed from the counter_attribute structure,
> the respective counter driver read/write callback is called, and sysfs
> I/O is handled before or after the driver read/write function is called.
> 
> Cc: Jarkko Nikula <jarkko.nikula@linux.intel.com>
> Cc: Patrick Havelange <patrick.havelange@essensium.com>
> Cc: Kamel Bouhara <kamel.bouhara@bootlin.com>
> Cc: Fabrice Gasnier <fabrice.gasnier@st.com>
> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com>
> Cc: Alexandre Torgue <alexandre.torgue@st.com>
> Cc: Dan Carpenter <dan.carpenter@oracle.com>
> Acked-by: Syed Nayyar Waris <syednwaris@gmail.com>
> Reviewed-by: David Lechner <david@lechnology.com>
> Tested-by: David Lechner <david@lechnology.com>
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> ---
>  MAINTAINERS                             |    1 -
>  drivers/counter/104-quad-8.c            |  449 +++----
>  drivers/counter/Makefile                |    1 +
>  drivers/counter/counter-core.c          |  142 +++
>  drivers/counter/counter-sysfs.c         |  849 +++++++++++++
>  drivers/counter/counter-sysfs.h         |   13 +
>  drivers/counter/counter.c               | 1496 -----------------------
>  drivers/counter/ftm-quaddec.c           |   60 +-
>  drivers/counter/intel-qep.c             |  144 +--
>  drivers/counter/interrupt-cnt.c         |   62 +-
>  drivers/counter/microchip-tcb-capture.c |   91 +-
>  drivers/counter/stm32-lptimer-cnt.c     |  212 ++--
>  drivers/counter/stm32-timer-cnt.c       |  179 ++-
>  drivers/counter/ti-eqep.c               |  180 +--
>  include/linux/counter.h                 |  658 +++++-----
>  include/linux/counter_enum.h            |   45 -
>  16 files changed, 1949 insertions(+), 2633 deletions(-)
>  create mode 100644 drivers/counter/counter-core.c
>  create mode 100644 drivers/counter/counter-sysfs.c
>  create mode 100644 drivers/counter/counter-sysfs.h
>  delete mode 100644 drivers/counter/counter.c
>  delete mode 100644 include/linux/counter_enum.h
> 

Hi William,

For the STM32 drivers, you can add my:
Reviewed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>

Thanks,
Fabrice



> diff --git a/MAINTAINERS b/MAINTAINERS
> index 212bad85e07a..b0cdb9aaca85 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4781,7 +4781,6 @@ F:	Documentation/ABI/testing/sysfs-bus-counter
>  F:	Documentation/driver-api/generic-counter.rst
>  F:	drivers/counter/
>  F:	include/linux/counter.h
> -F:	include/linux/counter_enum.h
>  
>  CP2615 I2C DRIVER
>  M:	Bence Csókás <bence98@sch.bme.hu>
> diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c
> index 0caa60537b14..c587f295d720 100644
> --- a/drivers/counter/104-quad-8.c
> +++ b/drivers/counter/104-quad-8.c
> @@ -117,7 +117,7 @@ static int quad8_signal_read(struct counter_device *counter,
>  }
>  
>  static int quad8_count_read(struct counter_device *counter,
> -	struct counter_count *count, unsigned long *val)
> +			    struct counter_count *count, u64 *val)
>  {
>  	struct quad8 *const priv = counter->priv;
>  	const int base_offset = priv->base + 2 * count->id;
> @@ -148,7 +148,7 @@ static int quad8_count_read(struct counter_device *counter,
>  }
>  
>  static int quad8_count_write(struct counter_device *counter,
> -	struct counter_count *count, unsigned long val)
> +			     struct counter_count *count, u64 val)
>  {
>  	struct quad8 *const priv = counter->priv;
>  	const int base_offset = priv->base + 2 * count->id;
> @@ -188,22 +188,16 @@ static int quad8_count_write(struct counter_device *counter,
>  	return 0;
>  }
>  
> -enum quad8_count_function {
> -	QUAD8_COUNT_FUNCTION_PULSE_DIRECTION = 0,
> -	QUAD8_COUNT_FUNCTION_QUADRATURE_X1,
> -	QUAD8_COUNT_FUNCTION_QUADRATURE_X2,
> -	QUAD8_COUNT_FUNCTION_QUADRATURE_X4
> -};
> -
>  static const enum counter_function quad8_count_functions_list[] = {
> -	[QUAD8_COUNT_FUNCTION_PULSE_DIRECTION] = COUNTER_FUNCTION_PULSE_DIRECTION,
> -	[QUAD8_COUNT_FUNCTION_QUADRATURE_X1] = COUNTER_FUNCTION_QUADRATURE_X1_A,
> -	[QUAD8_COUNT_FUNCTION_QUADRATURE_X2] = COUNTER_FUNCTION_QUADRATURE_X2_A,
> -	[QUAD8_COUNT_FUNCTION_QUADRATURE_X4] = COUNTER_FUNCTION_QUADRATURE_X4
> +	COUNTER_FUNCTION_PULSE_DIRECTION,
> +	COUNTER_FUNCTION_QUADRATURE_X1_A,
> +	COUNTER_FUNCTION_QUADRATURE_X2_A,
> +	COUNTER_FUNCTION_QUADRATURE_X4,
>  };
>  
> -static int quad8_function_get(struct counter_device *counter,
> -	struct counter_count *count, size_t *function)
> +static int quad8_function_read(struct counter_device *counter,
> +			       struct counter_count *count,
> +			       enum counter_function *function)
>  {
>  	struct quad8 *const priv = counter->priv;
>  	const int id = count->id;
> @@ -213,25 +207,26 @@ static int quad8_function_get(struct counter_device *counter,
>  	if (priv->quadrature_mode[id])
>  		switch (priv->quadrature_scale[id]) {
>  		case 0:
> -			*function = QUAD8_COUNT_FUNCTION_QUADRATURE_X1;
> +			*function = COUNTER_FUNCTION_QUADRATURE_X1_A;
>  			break;
>  		case 1:
> -			*function = QUAD8_COUNT_FUNCTION_QUADRATURE_X2;
> +			*function = COUNTER_FUNCTION_QUADRATURE_X2_A;
>  			break;
>  		case 2:
> -			*function = QUAD8_COUNT_FUNCTION_QUADRATURE_X4;
> +			*function = COUNTER_FUNCTION_QUADRATURE_X4;
>  			break;
>  		}
>  	else
> -		*function = QUAD8_COUNT_FUNCTION_PULSE_DIRECTION;
> +		*function = COUNTER_FUNCTION_PULSE_DIRECTION;
>  
>  	mutex_unlock(&priv->lock);
>  
>  	return 0;
>  }
>  
> -static int quad8_function_set(struct counter_device *counter,
> -	struct counter_count *count, size_t function)
> +static int quad8_function_write(struct counter_device *counter,
> +				struct counter_count *count,
> +				enum counter_function function)
>  {
>  	struct quad8 *const priv = counter->priv;
>  	const int id = count->id;
> @@ -247,7 +242,7 @@ static int quad8_function_set(struct counter_device *counter,
>  	mode_cfg = priv->count_mode[id] << 1;
>  	idr_cfg = priv->index_polarity[id] << 1;
>  
> -	if (function == QUAD8_COUNT_FUNCTION_PULSE_DIRECTION) {
> +	if (function == COUNTER_FUNCTION_PULSE_DIRECTION) {
>  		*quadrature_mode = 0;
>  
>  		/* Quadrature scaling only available in quadrature mode */
> @@ -263,15 +258,15 @@ static int quad8_function_set(struct counter_device *counter,
>  		*quadrature_mode = 1;
>  
>  		switch (function) {
> -		case QUAD8_COUNT_FUNCTION_QUADRATURE_X1:
> +		case COUNTER_FUNCTION_QUADRATURE_X1_A:
>  			*scale = 0;
>  			mode_cfg |= QUAD8_CMR_QUADRATURE_X1;
>  			break;
> -		case QUAD8_COUNT_FUNCTION_QUADRATURE_X2:
> +		case COUNTER_FUNCTION_QUADRATURE_X2_A:
>  			*scale = 1;
>  			mode_cfg |= QUAD8_CMR_QUADRATURE_X2;
>  			break;
> -		case QUAD8_COUNT_FUNCTION_QUADRATURE_X4:
> +		case COUNTER_FUNCTION_QUADRATURE_X4:
>  			*scale = 2;
>  			mode_cfg |= QUAD8_CMR_QUADRATURE_X4;
>  			break;
> @@ -290,8 +285,9 @@ static int quad8_function_set(struct counter_device *counter,
>  	return 0;
>  }
>  
> -static void quad8_direction_get(struct counter_device *counter,
> -	struct counter_count *count, enum counter_count_direction *direction)
> +static int quad8_direction_read(struct counter_device *counter,
> +				struct counter_count *count,
> +				enum counter_count_direction *direction)
>  {
>  	const struct quad8 *const priv = counter->priv;
>  	unsigned int ud_flag;
> @@ -302,76 +298,74 @@ static void quad8_direction_get(struct counter_device *counter,
>  
>  	*direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD :
>  		COUNTER_COUNT_DIRECTION_BACKWARD;
> -}
>  
> -enum quad8_synapse_action {
> -	QUAD8_SYNAPSE_ACTION_NONE = 0,
> -	QUAD8_SYNAPSE_ACTION_RISING_EDGE,
> -	QUAD8_SYNAPSE_ACTION_FALLING_EDGE,
> -	QUAD8_SYNAPSE_ACTION_BOTH_EDGES
> -};
> +	return 0;
> +}
>  
>  static const enum counter_synapse_action quad8_index_actions_list[] = {
> -	[QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
> -	[QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE
> +	COUNTER_SYNAPSE_ACTION_NONE,
> +	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
>  };
>  
>  static const enum counter_synapse_action quad8_synapse_actions_list[] = {
> -	[QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
> -	[QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> -	[QUAD8_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
> -	[QUAD8_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES
> +	COUNTER_SYNAPSE_ACTION_NONE,
> +	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> +	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
> +	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
>  };
>  
> -static int quad8_action_get(struct counter_device *counter,
> -	struct counter_count *count, struct counter_synapse *synapse,
> -	size_t *action)
> +static int quad8_action_read(struct counter_device *counter,
> +			     struct counter_count *count,
> +			     struct counter_synapse *synapse,
> +			     enum counter_synapse_action *action)
>  {
>  	struct quad8 *const priv = counter->priv;
>  	int err;
> -	size_t function = 0;
> +	enum counter_function function;
>  	const size_t signal_a_id = count->synapses[0].signal->id;
>  	enum counter_count_direction direction;
>  
>  	/* Handle Index signals */
>  	if (synapse->signal->id >= 16) {
>  		if (priv->preset_enable[count->id])
> -			*action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
> +			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
>  		else
> -			*action = QUAD8_SYNAPSE_ACTION_NONE;
> +			*action = COUNTER_SYNAPSE_ACTION_NONE;
>  
>  		return 0;
>  	}
>  
> -	err = quad8_function_get(counter, count, &function);
> +	err = quad8_function_read(counter, count, &function);
>  	if (err)
>  		return err;
>  
>  	/* Default action mode */
> -	*action = QUAD8_SYNAPSE_ACTION_NONE;
> +	*action = COUNTER_SYNAPSE_ACTION_NONE;
>  
>  	/* Determine action mode based on current count function mode */
>  	switch (function) {
> -	case QUAD8_COUNT_FUNCTION_PULSE_DIRECTION:
> +	case COUNTER_FUNCTION_PULSE_DIRECTION:
>  		if (synapse->signal->id == signal_a_id)
> -			*action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
> +			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
>  		return 0;
> -	case QUAD8_COUNT_FUNCTION_QUADRATURE_X1:
> +	case COUNTER_FUNCTION_QUADRATURE_X1_A:
>  		if (synapse->signal->id == signal_a_id) {
> -			quad8_direction_get(counter, count, &direction);
> +			err = quad8_direction_read(counter, count, &direction);
> +			if (err)
> +				return err;
>  
>  			if (direction == COUNTER_COUNT_DIRECTION_FORWARD)
> -				*action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
> +				*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
>  			else
> -				*action = QUAD8_SYNAPSE_ACTION_FALLING_EDGE;
> +				*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
>  		}
>  		return 0;
> -	case QUAD8_COUNT_FUNCTION_QUADRATURE_X2:
> +	case COUNTER_FUNCTION_QUADRATURE_X2_A:
>  		if (synapse->signal->id == signal_a_id)
> -			*action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES;
> +			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
>  		return 0;
> -	case QUAD8_COUNT_FUNCTION_QUADRATURE_X4:
> -		*action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES;
> +	case COUNTER_FUNCTION_QUADRATURE_X4:
> +		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
>  		return 0;
>  	default:
>  		/* should never reach this path */
> @@ -383,9 +377,9 @@ static const struct counter_ops quad8_ops = {
>  	.signal_read = quad8_signal_read,
>  	.count_read = quad8_count_read,
>  	.count_write = quad8_count_write,
> -	.function_get = quad8_function_get,
> -	.function_set = quad8_function_set,
> -	.action_get = quad8_action_get
> +	.function_read = quad8_function_read,
> +	.function_write = quad8_function_write,
> +	.action_read = quad8_action_read
>  };
>  
>  static const char *const quad8_index_polarity_modes[] = {
> @@ -394,7 +388,8 @@ static const char *const quad8_index_polarity_modes[] = {
>  };
>  
>  static int quad8_index_polarity_get(struct counter_device *counter,
> -	struct counter_signal *signal, size_t *index_polarity)
> +				    struct counter_signal *signal,
> +				    u32 *index_polarity)
>  {
>  	const struct quad8 *const priv = counter->priv;
>  	const size_t channel_id = signal->id - 16;
> @@ -405,7 +400,8 @@ static int quad8_index_polarity_get(struct counter_device *counter,
>  }
>  
>  static int quad8_index_polarity_set(struct counter_device *counter,
> -	struct counter_signal *signal, size_t index_polarity)
> +				    struct counter_signal *signal,
> +				    u32 index_polarity)
>  {
>  	struct quad8 *const priv = counter->priv;
>  	const size_t channel_id = signal->id - 16;
> @@ -426,20 +422,14 @@ static int quad8_index_polarity_set(struct counter_device *counter,
>  	return 0;
>  }
>  
> -static struct counter_signal_enum_ext quad8_index_pol_enum = {
> -	.items = quad8_index_polarity_modes,
> -	.num_items = ARRAY_SIZE(quad8_index_polarity_modes),
> -	.get = quad8_index_polarity_get,
> -	.set = quad8_index_polarity_set
> -};
> -
>  static const char *const quad8_synchronous_modes[] = {
>  	"non-synchronous",
>  	"synchronous"
>  };
>  
>  static int quad8_synchronous_mode_get(struct counter_device *counter,
> -	struct counter_signal *signal, size_t *synchronous_mode)
> +				      struct counter_signal *signal,
> +				      u32 *synchronous_mode)
>  {
>  	const struct quad8 *const priv = counter->priv;
>  	const size_t channel_id = signal->id - 16;
> @@ -450,7 +440,8 @@ static int quad8_synchronous_mode_get(struct counter_device *counter,
>  }
>  
>  static int quad8_synchronous_mode_set(struct counter_device *counter,
> -	struct counter_signal *signal, size_t synchronous_mode)
> +				      struct counter_signal *signal,
> +				      u32 synchronous_mode)
>  {
>  	struct quad8 *const priv = counter->priv;
>  	const size_t channel_id = signal->id - 16;
> @@ -477,22 +468,18 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,
>  	return 0;
>  }
>  
> -static struct counter_signal_enum_ext quad8_syn_mode_enum = {
> -	.items = quad8_synchronous_modes,
> -	.num_items = ARRAY_SIZE(quad8_synchronous_modes),
> -	.get = quad8_synchronous_mode_get,
> -	.set = quad8_synchronous_mode_set
> -};
> -
> -static ssize_t quad8_count_floor_read(struct counter_device *counter,
> -	struct counter_count *count, void *private, char *buf)
> +static int quad8_count_floor_read(struct counter_device *counter,
> +				  struct counter_count *count, u64 *floor)
>  {
>  	/* Only a floor of 0 is supported */
> -	return sprintf(buf, "0\n");
> +	*floor = 0;
> +
> +	return 0;
>  }
>  
> -static int quad8_count_mode_get(struct counter_device *counter,
> -	struct counter_count *count, size_t *cnt_mode)
> +static int quad8_count_mode_read(struct counter_device *counter,
> +				 struct counter_count *count,
> +				 enum counter_count_mode *cnt_mode)
>  {
>  	const struct quad8 *const priv = counter->priv;
>  
> @@ -515,26 +502,28 @@ static int quad8_count_mode_get(struct counter_device *counter,
>  	return 0;
>  }
>  
> -static int quad8_count_mode_set(struct counter_device *counter,
> -	struct counter_count *count, size_t cnt_mode)
> +static int quad8_count_mode_write(struct counter_device *counter,
> +				  struct counter_count *count,
> +				  enum counter_count_mode cnt_mode)
>  {
>  	struct quad8 *const priv = counter->priv;
> +	unsigned int count_mode;
>  	unsigned int mode_cfg;
>  	const int base_offset = priv->base + 2 * count->id + 1;
>  
>  	/* Map Generic Counter count mode to 104-QUAD-8 count mode */
>  	switch (cnt_mode) {
>  	case COUNTER_COUNT_MODE_NORMAL:
> -		cnt_mode = 0;
> +		count_mode = 0;
>  		break;
>  	case COUNTER_COUNT_MODE_RANGE_LIMIT:
> -		cnt_mode = 1;
> +		count_mode = 1;
>  		break;
>  	case COUNTER_COUNT_MODE_NON_RECYCLE:
> -		cnt_mode = 2;
> +		count_mode = 2;
>  		break;
>  	case COUNTER_COUNT_MODE_MODULO_N:
> -		cnt_mode = 3;
> +		count_mode = 3;
>  		break;
>  	default:
>  		/* should never reach this path */
> @@ -543,10 +532,10 @@ static int quad8_count_mode_set(struct counter_device *counter,
>  
>  	mutex_lock(&priv->lock);
>  
> -	priv->count_mode[count->id] = cnt_mode;
> +	priv->count_mode[count->id] = count_mode;
>  
>  	/* Set count mode configuration value */
> -	mode_cfg = cnt_mode << 1;
> +	mode_cfg = count_mode << 1;
>  
>  	/* Add quadrature mode configuration */
>  	if (priv->quadrature_mode[count->id])
> @@ -560,56 +549,35 @@ static int quad8_count_mode_set(struct counter_device *counter,
>  	return 0;
>  }
>  
> -static struct counter_count_enum_ext quad8_cnt_mode_enum = {
> -	.items = counter_count_mode_str,
> -	.num_items = ARRAY_SIZE(counter_count_mode_str),
> -	.get = quad8_count_mode_get,
> -	.set = quad8_count_mode_set
> -};
> -
> -static ssize_t quad8_count_direction_read(struct counter_device *counter,
> -	struct counter_count *count, void *priv, char *buf)
> -{
> -	enum counter_count_direction dir;
> -
> -	quad8_direction_get(counter, count, &dir);
> -
> -	return sprintf(buf, "%s\n", counter_count_direction_str[dir]);
> -}
> -
> -static ssize_t quad8_count_enable_read(struct counter_device *counter,
> -	struct counter_count *count, void *private, char *buf)
> +static int quad8_count_enable_read(struct counter_device *counter,
> +				   struct counter_count *count, u8 *enable)
>  {
>  	const struct quad8 *const priv = counter->priv;
>  
> -	return sprintf(buf, "%u\n", priv->ab_enable[count->id]);
> +	*enable = priv->ab_enable[count->id];
> +
> +	return 0;
>  }
>  
> -static ssize_t quad8_count_enable_write(struct counter_device *counter,
> -	struct counter_count *count, void *private, const char *buf, size_t len)
> +static int quad8_count_enable_write(struct counter_device *counter,
> +				    struct counter_count *count, u8 enable)
>  {
>  	struct quad8 *const priv = counter->priv;
>  	const int base_offset = priv->base + 2 * count->id;
> -	int err;
> -	bool ab_enable;
>  	unsigned int ior_cfg;
>  
> -	err = kstrtobool(buf, &ab_enable);
> -	if (err)
> -		return err;
> -
>  	mutex_lock(&priv->lock);
>  
> -	priv->ab_enable[count->id] = ab_enable;
> +	priv->ab_enable[count->id] = enable;
>  
> -	ior_cfg = ab_enable | priv->preset_enable[count->id] << 1;
> +	ior_cfg = enable | priv->preset_enable[count->id] << 1;
>  
>  	/* Load I/O control configuration */
>  	outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
>  
>  	mutex_unlock(&priv->lock);
>  
> -	return len;
> +	return 0;
>  }
>  
>  static const char *const quad8_noise_error_states[] = {
> @@ -618,7 +586,7 @@ static const char *const quad8_noise_error_states[] = {
>  };
>  
>  static int quad8_error_noise_get(struct counter_device *counter,
> -	struct counter_count *count, size_t *noise_error)
> +				 struct counter_count *count, u32 *noise_error)
>  {
>  	const struct quad8 *const priv = counter->priv;
>  	const int base_offset = priv->base + 2 * count->id + 1;
> @@ -628,18 +596,14 @@ static int quad8_error_noise_get(struct counter_device *counter,
>  	return 0;
>  }
>  
> -static struct counter_count_enum_ext quad8_error_noise_enum = {
> -	.items = quad8_noise_error_states,
> -	.num_items = ARRAY_SIZE(quad8_noise_error_states),
> -	.get = quad8_error_noise_get
> -};
> -
> -static ssize_t quad8_count_preset_read(struct counter_device *counter,
> -	struct counter_count *count, void *private, char *buf)
> +static int quad8_count_preset_read(struct counter_device *counter,
> +				   struct counter_count *count, u64 *preset)
>  {
>  	const struct quad8 *const priv = counter->priv;
>  
> -	return sprintf(buf, "%u\n", priv->preset[count->id]);
> +	*preset = priv->preset[count->id];
> +
> +	return 0;
>  }
>  
>  static void quad8_preset_register_set(struct quad8 *const priv, const int id,
> @@ -658,16 +622,10 @@ static void quad8_preset_register_set(struct quad8 *const priv, const int id,
>  		outb(preset >> (8 * i), base_offset);
>  }
>  
> -static ssize_t quad8_count_preset_write(struct counter_device *counter,
> -	struct counter_count *count, void *private, const char *buf, size_t len)
> +static int quad8_count_preset_write(struct counter_device *counter,
> +				    struct counter_count *count, u64 preset)
>  {
>  	struct quad8 *const priv = counter->priv;
> -	unsigned int preset;
> -	int ret;
> -
> -	ret = kstrtouint(buf, 0, &preset);
> -	if (ret)
> -		return ret;
>  
>  	/* Only 24-bit values are supported */
>  	if (preset > 0xFFFFFF)
> @@ -679,11 +637,11 @@ static ssize_t quad8_count_preset_write(struct counter_device *counter,
>  
>  	mutex_unlock(&priv->lock);
>  
> -	return len;
> +	return 0;
>  }
>  
> -static ssize_t quad8_count_ceiling_read(struct counter_device *counter,
> -	struct counter_count *count, void *private, char *buf)
> +static int quad8_count_ceiling_read(struct counter_device *counter,
> +				    struct counter_count *count, u64 *ceiling)
>  {
>  	struct quad8 *const priv = counter->priv;
>  
> @@ -693,26 +651,23 @@ static ssize_t quad8_count_ceiling_read(struct counter_device *counter,
>  	switch (priv->count_mode[count->id]) {
>  	case 1:
>  	case 3:
> -		mutex_unlock(&priv->lock);
> -		return sprintf(buf, "%u\n", priv->preset[count->id]);
> +		*ceiling = priv->preset[count->id];
> +		break;
> +	default:
> +		/* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
> +		*ceiling = 0x1FFFFFF;
> +		break;
>  	}
>  
>  	mutex_unlock(&priv->lock);
>  
> -	/* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
> -	return sprintf(buf, "33554431\n");
> +	return 0;
>  }
>  
> -static ssize_t quad8_count_ceiling_write(struct counter_device *counter,
> -	struct counter_count *count, void *private, const char *buf, size_t len)
> +static int quad8_count_ceiling_write(struct counter_device *counter,
> +				     struct counter_count *count, u64 ceiling)
>  {
>  	struct quad8 *const priv = counter->priv;
> -	unsigned int ceiling;
> -	int ret;
> -
> -	ret = kstrtouint(buf, 0, &ceiling);
> -	if (ret)
> -		return ret;
>  
>  	/* Only 24-bit values are supported */
>  	if (ceiling > 0xFFFFFF)
> @@ -726,7 +681,7 @@ static ssize_t quad8_count_ceiling_write(struct counter_device *counter,
>  	case 3:
>  		quad8_preset_register_set(priv, count->id, ceiling);
>  		mutex_unlock(&priv->lock);
> -		return len;
> +		return 0;
>  	}
>  
>  	mutex_unlock(&priv->lock);
> @@ -734,27 +689,25 @@ static ssize_t quad8_count_ceiling_write(struct counter_device *counter,
>  	return -EINVAL;
>  }
>  
> -static ssize_t quad8_count_preset_enable_read(struct counter_device *counter,
> -	struct counter_count *count, void *private, char *buf)
> +static int quad8_count_preset_enable_read(struct counter_device *counter,
> +					  struct counter_count *count,
> +					  u8 *preset_enable)
>  {
>  	const struct quad8 *const priv = counter->priv;
>  
> -	return sprintf(buf, "%u\n", !priv->preset_enable[count->id]);
> +	*preset_enable = !priv->preset_enable[count->id];
> +
> +	return 0;
>  }
>  
> -static ssize_t quad8_count_preset_enable_write(struct counter_device *counter,
> -	struct counter_count *count, void *private, const char *buf, size_t len)
> +static int quad8_count_preset_enable_write(struct counter_device *counter,
> +					   struct counter_count *count,
> +					   u8 preset_enable)
>  {
>  	struct quad8 *const priv = counter->priv;
>  	const int base_offset = priv->base + 2 * count->id + 1;
> -	bool preset_enable;
> -	int ret;
>  	unsigned int ior_cfg;
>  
> -	ret = kstrtobool(buf, &preset_enable);
> -	if (ret)
> -		return ret;
> -
>  	/* Preset enable is active low in Input/Output Control register */
>  	preset_enable = !preset_enable;
>  
> @@ -762,25 +715,24 @@ static ssize_t quad8_count_preset_enable_write(struct counter_device *counter,
>  
>  	priv->preset_enable[count->id] = preset_enable;
>  
> -	ior_cfg = priv->ab_enable[count->id] | (unsigned int)preset_enable << 1;
> +	ior_cfg = priv->ab_enable[count->id] | preset_enable << 1;
>  
>  	/* Load I/O control configuration to Input / Output Control Register */
>  	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
>  
>  	mutex_unlock(&priv->lock);
>  
> -	return len;
> +	return 0;
>  }
>  
> -static ssize_t quad8_signal_cable_fault_read(struct counter_device *counter,
> -					     struct counter_signal *signal,
> -					     void *private, char *buf)
> +static int quad8_signal_cable_fault_read(struct counter_device *counter,
> +					 struct counter_signal *signal,
> +					 u8 *cable_fault)
>  {
>  	struct quad8 *const priv = counter->priv;
>  	const size_t channel_id = signal->id / 2;
>  	bool disabled;
>  	unsigned int status;
> -	unsigned int fault;
>  
>  	mutex_lock(&priv->lock);
>  
> @@ -797,36 +749,31 @@ static ssize_t quad8_signal_cable_fault_read(struct counter_device *counter,
>  	mutex_unlock(&priv->lock);
>  
>  	/* Mask respective channel and invert logic */
> -	fault = !(status & BIT(channel_id));
> +	*cable_fault = !(status & BIT(channel_id));
>  
> -	return sprintf(buf, "%u\n", fault);
> +	return 0;
>  }
>  
> -static ssize_t quad8_signal_cable_fault_enable_read(
> -	struct counter_device *counter, struct counter_signal *signal,
> -	void *private, char *buf)
> +static int quad8_signal_cable_fault_enable_read(struct counter_device *counter,
> +						struct counter_signal *signal,
> +						u8 *enable)
>  {
>  	const struct quad8 *const priv = counter->priv;
>  	const size_t channel_id = signal->id / 2;
> -	const unsigned int enb = !!(priv->cable_fault_enable & BIT(channel_id));
>  
> -	return sprintf(buf, "%u\n", enb);
> +	*enable = !!(priv->cable_fault_enable & BIT(channel_id));
> +
> +	return 0;
>  }
>  
> -static ssize_t quad8_signal_cable_fault_enable_write(
> -	struct counter_device *counter, struct counter_signal *signal,
> -	void *private, const char *buf, size_t len)
> +static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
> +						 struct counter_signal *signal,
> +						 u8 enable)
>  {
>  	struct quad8 *const priv = counter->priv;
>  	const size_t channel_id = signal->id / 2;
> -	bool enable;
> -	int ret;
>  	unsigned int cable_fault_enable;
>  
> -	ret = kstrtobool(buf, &enable);
> -	if (ret)
> -		return ret;
> -
>  	mutex_lock(&priv->lock);
>  
>  	if (enable)
> @@ -841,31 +788,27 @@ static ssize_t quad8_signal_cable_fault_enable_write(
>  
>  	mutex_unlock(&priv->lock);
>  
> -	return len;
> +	return 0;
>  }
>  
> -static ssize_t quad8_signal_fck_prescaler_read(struct counter_device *counter,
> -	struct counter_signal *signal, void *private, char *buf)
> +static int quad8_signal_fck_prescaler_read(struct counter_device *counter,
> +					   struct counter_signal *signal,
> +					   u8 *prescaler)
>  {
>  	const struct quad8 *const priv = counter->priv;
> -	const size_t channel_id = signal->id / 2;
>  
> -	return sprintf(buf, "%u\n", priv->fck_prescaler[channel_id]);
> +	*prescaler = priv->fck_prescaler[signal->id / 2];
> +
> +	return 0;
>  }
>  
> -static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter,
> -	struct counter_signal *signal, void *private, const char *buf,
> -	size_t len)
> +static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
> +					    struct counter_signal *signal,
> +					    u8 prescaler)
>  {
>  	struct quad8 *const priv = counter->priv;
>  	const size_t channel_id = signal->id / 2;
>  	const int base_offset = priv->base + 2 * channel_id;
> -	u8 prescaler;
> -	int ret;
> -
> -	ret = kstrtou8(buf, 0, &prescaler);
> -	if (ret)
> -		return ret;
>  
>  	mutex_lock(&priv->lock);
>  
> @@ -881,31 +824,30 @@ static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter,
>  
>  	mutex_unlock(&priv->lock);
>  
> -	return len;
> +	return 0;
>  }
>  
> -static const struct counter_signal_ext quad8_signal_ext[] = {
> -	{
> -		.name = "cable_fault",
> -		.read = quad8_signal_cable_fault_read
> -	},
> -	{
> -		.name = "cable_fault_enable",
> -		.read = quad8_signal_cable_fault_enable_read,
> -		.write = quad8_signal_cable_fault_enable_write
> -	},
> -	{
> -		.name = "filter_clock_prescaler",
> -		.read = quad8_signal_fck_prescaler_read,
> -		.write = quad8_signal_fck_prescaler_write
> -	}
> +static struct counter_comp quad8_signal_ext[] = {
> +	COUNTER_COMP_SIGNAL_BOOL("cable_fault", quad8_signal_cable_fault_read,
> +				 NULL),
> +	COUNTER_COMP_SIGNAL_BOOL("cable_fault_enable",
> +				 quad8_signal_cable_fault_enable_read,
> +				 quad8_signal_cable_fault_enable_write),
> +	COUNTER_COMP_SIGNAL_U8("filter_clock_prescaler",
> +			       quad8_signal_fck_prescaler_read,
> +			       quad8_signal_fck_prescaler_write)
>  };
>  
> -static const struct counter_signal_ext quad8_index_ext[] = {
> -	COUNTER_SIGNAL_ENUM("index_polarity", &quad8_index_pol_enum),
> -	COUNTER_SIGNAL_ENUM_AVAILABLE("index_polarity",	&quad8_index_pol_enum),
> -	COUNTER_SIGNAL_ENUM("synchronous_mode", &quad8_syn_mode_enum),
> -	COUNTER_SIGNAL_ENUM_AVAILABLE("synchronous_mode", &quad8_syn_mode_enum)
> +static DEFINE_COUNTER_ENUM(quad8_index_pol_enum, quad8_index_polarity_modes);
> +static DEFINE_COUNTER_ENUM(quad8_synch_mode_enum, quad8_synchronous_modes);
> +
> +static struct counter_comp quad8_index_ext[] = {
> +	COUNTER_COMP_SIGNAL_ENUM("index_polarity", quad8_index_polarity_get,
> +				 quad8_index_polarity_set,
> +				 quad8_index_pol_enum),
> +	COUNTER_COMP_SIGNAL_ENUM("synchronous_mode", quad8_synchronous_mode_get,
> +				 quad8_synchronous_mode_set,
> +				 quad8_synch_mode_enum),
>  };
>  
>  #define QUAD8_QUAD_SIGNAL(_id, _name) {		\
> @@ -974,39 +916,30 @@ static struct counter_synapse quad8_count_synapses[][3] = {
>  	QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7)
>  };
>  
> -static const struct counter_count_ext quad8_count_ext[] = {
> -	{
> -		.name = "ceiling",
> -		.read = quad8_count_ceiling_read,
> -		.write = quad8_count_ceiling_write
> -	},
> -	{
> -		.name = "floor",
> -		.read = quad8_count_floor_read
> -	},
> -	COUNTER_COUNT_ENUM("count_mode", &quad8_cnt_mode_enum),
> -	COUNTER_COUNT_ENUM_AVAILABLE("count_mode", &quad8_cnt_mode_enum),
> -	{
> -		.name = "direction",
> -		.read = quad8_count_direction_read
> -	},
> -	{
> -		.name = "enable",
> -		.read = quad8_count_enable_read,
> -		.write = quad8_count_enable_write
> -	},
> -	COUNTER_COUNT_ENUM("error_noise", &quad8_error_noise_enum),
> -	COUNTER_COUNT_ENUM_AVAILABLE("error_noise", &quad8_error_noise_enum),
> -	{
> -		.name = "preset",
> -		.read = quad8_count_preset_read,
> -		.write = quad8_count_preset_write
> -	},
> -	{
> -		.name = "preset_enable",
> -		.read = quad8_count_preset_enable_read,
> -		.write = quad8_count_preset_enable_write
> -	}
> +static const enum counter_count_mode quad8_cnt_modes[] = {
> +	COUNTER_COUNT_MODE_NORMAL,
> +	COUNTER_COUNT_MODE_RANGE_LIMIT,
> +	COUNTER_COUNT_MODE_NON_RECYCLE,
> +	COUNTER_COUNT_MODE_MODULO_N,
> +};
> +
> +static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes);
> +
> +static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states);
> +
> +static struct counter_comp quad8_count_ext[] = {
> +	COUNTER_COMP_CEILING(quad8_count_ceiling_read,
> +			     quad8_count_ceiling_write),
> +	COUNTER_COMP_FLOOR(quad8_count_floor_read, NULL),
> +	COUNTER_COMP_COUNT_MODE(quad8_count_mode_read, quad8_count_mode_write,
> +				quad8_count_mode_available),
> +	COUNTER_COMP_DIRECTION(quad8_direction_read),
> +	COUNTER_COMP_ENABLE(quad8_count_enable_read, quad8_count_enable_write),
> +	COUNTER_COMP_COUNT_ENUM("error_noise", quad8_error_noise_get, NULL,
> +				quad8_error_noise_enum),
> +	COUNTER_COMP_PRESET(quad8_count_preset_read, quad8_count_preset_write),
> +	COUNTER_COMP_PRESET_ENABLE(quad8_count_preset_enable_read,
> +				   quad8_count_preset_enable_write),
>  };
>  
>  #define QUAD8_COUNT(_id, _cntname) {					\
> diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
> index 19742e6f5e3e..1ab7e087fdc2 100644
> --- a/drivers/counter/Makefile
> +++ b/drivers/counter/Makefile
> @@ -4,6 +4,7 @@
>  #
>  
>  obj-$(CONFIG_COUNTER) += counter.o
> +counter-y := counter-core.o counter-sysfs.o
>  
>  obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
>  obj-$(CONFIG_INTERRUPT_CNT)		+= interrupt-cnt.o
> diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
> new file mode 100644
> index 000000000000..c533a6ff12cf
> --- /dev/null
> +++ b/drivers/counter/counter-core.c
> @@ -0,0 +1,142 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Generic Counter interface
> + * Copyright (C) 2020 William Breathitt Gray
> + */
> +#include <linux/counter.h>
> +#include <linux/device.h>
> +#include <linux/export.h>
> +#include <linux/gfp.h>
> +#include <linux/idr.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +
> +#include "counter-sysfs.h"
> +
> +/* Provides a unique ID for each counter device */
> +static DEFINE_IDA(counter_ida);
> +
> +static void counter_device_release(struct device *dev)
> +{
> +	ida_free(&counter_ida, dev->id);
> +}
> +
> +static struct device_type counter_device_type = {
> +	.name = "counter_device",
> +	.release = counter_device_release,
> +};
> +
> +static struct bus_type counter_bus_type = {
> +	.name = "counter",
> +	.dev_name = "counter",
> +};
> +
> +/**
> + * counter_register - register Counter to the system
> + * @counter:	pointer to Counter to register
> + *
> + * This function registers a Counter to the system. A sysfs "counter" directory
> + * will be created and populated with sysfs attributes correlating with the
> + * Counter Signals, Synapses, and Counts respectively.
> + */
> +int counter_register(struct counter_device *const counter)
> +{
> +	struct device *const dev = &counter->dev;
> +	int id;
> +	int err;
> +
> +	/* Acquire unique ID */
> +	id = ida_alloc(&counter_ida, GFP_KERNEL);
> +	if (id < 0)
> +		return id;
> +
> +	/* Configure device structure for Counter */
> +	dev->id = id;
> +	dev->type = &counter_device_type;
> +	dev->bus = &counter_bus_type;
> +	if (counter->parent) {
> +		dev->parent = counter->parent;
> +		dev->of_node = counter->parent->of_node;
> +	}
> +	device_initialize(dev);
> +	dev_set_drvdata(dev, counter);
> +
> +	/* Add Counter sysfs attributes */
> +	err = counter_sysfs_add(counter);
> +	if (err < 0)
> +		goto err_free_id;
> +
> +	/* Add device to system */
> +	err = device_add(dev);
> +	if (err < 0)
> +		goto err_free_id;
> +
> +	return 0;
> +
> +err_free_id:
> +	put_device(dev);
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(counter_register);
> +
> +/**
> + * counter_unregister - unregister Counter from the system
> + * @counter:	pointer to Counter to unregister
> + *
> + * The Counter is unregistered from the system.
> + */
> +void counter_unregister(struct counter_device *const counter)
> +{
> +	if (!counter)
> +		return;
> +
> +	device_unregister(&counter->dev);
> +}
> +EXPORT_SYMBOL_GPL(counter_unregister);
> +
> +static void devm_counter_release(void *counter)
> +{
> +	counter_unregister(counter);
> +}
> +
> +/**
> + * devm_counter_register - Resource-managed counter_register
> + * @dev:	device to allocate counter_device for
> + * @counter:	pointer to Counter to register
> + *
> + * Managed counter_register. The Counter registered with this function is
> + * automatically unregistered on driver detach. This function calls
> + * counter_register internally. Refer to that function for more information.
> + *
> + * RETURNS:
> + * 0 on success, negative error number on failure.
> + */
> +int devm_counter_register(struct device *dev,
> +			  struct counter_device *const counter)
> +{
> +	int err;
> +
> +	err = counter_register(counter);
> +	if (err < 0)
> +		return err;
> +
> +	return devm_add_action_or_reset(dev, devm_counter_release, counter);
> +}
> +EXPORT_SYMBOL_GPL(devm_counter_register);
> +
> +static int __init counter_init(void)
> +{
> +	return bus_register(&counter_bus_type);
> +}
> +
> +static void __exit counter_exit(void)
> +{
> +	bus_unregister(&counter_bus_type);
> +}
> +
> +subsys_initcall(counter_init);
> +module_exit(counter_exit);
> +
> +MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
> +MODULE_DESCRIPTION("Generic Counter interface");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
> new file mode 100644
> index 000000000000..108cbd838eb9
> --- /dev/null
> +++ b/drivers/counter/counter-sysfs.c
> @@ -0,0 +1,849 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Generic Counter sysfs interface
> + * Copyright (C) 2020 William Breathitt Gray
> + */
> +#include <linux/counter.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/gfp.h>
> +#include <linux/kernel.h>
> +#include <linux/kstrtox.h>
> +#include <linux/list.h>
> +#include <linux/string.h>
> +#include <linux/sysfs.h>
> +#include <linux/types.h>
> +
> +#include "counter-sysfs.h"
> +
> +/**
> + * struct counter_attribute - Counter sysfs attribute
> + * @dev_attr:	device attribute for sysfs
> + * @l:		node to add Counter attribute to attribute group list
> + * @comp:	Counter component callbacks and data
> + * @scope:	Counter scope of the attribute
> + * @parent:	pointer to the parent component
> + */
> +struct counter_attribute {
> +	struct device_attribute dev_attr;
> +	struct list_head l;
> +
> +	struct counter_comp comp;
> +	enum counter_scope scope;
> +	void *parent;
> +};
> +
> +#define to_counter_attribute(_dev_attr) \
> +	container_of(_dev_attr, struct counter_attribute, dev_attr)
> +
> +/**
> + * struct counter_attribute_group - container for attribute group
> + * @name:	name of the attribute group
> + * @attr_list:	list to keep track of created attributes
> + * @num_attr:	number of attributes
> + */
> +struct counter_attribute_group {
> +	const char *name;
> +	struct list_head attr_list;
> +	size_t num_attr;
> +};
> +
> +static const char *const counter_function_str[] = {
> +	[COUNTER_FUNCTION_INCREASE] = "increase",
> +	[COUNTER_FUNCTION_DECREASE] = "decrease",
> +	[COUNTER_FUNCTION_PULSE_DIRECTION] = "pulse-direction",
> +	[COUNTER_FUNCTION_QUADRATURE_X1_A] = "quadrature x1 a",
> +	[COUNTER_FUNCTION_QUADRATURE_X1_B] = "quadrature x1 b",
> +	[COUNTER_FUNCTION_QUADRATURE_X2_A] = "quadrature x2 a",
> +	[COUNTER_FUNCTION_QUADRATURE_X2_B] = "quadrature x2 b",
> +	[COUNTER_FUNCTION_QUADRATURE_X4] = "quadrature x4"
> +};
> +
> +static const char *const counter_signal_value_str[] = {
> +	[COUNTER_SIGNAL_LEVEL_LOW] = "low",
> +	[COUNTER_SIGNAL_LEVEL_HIGH] = "high"
> +};
> +
> +static const char *const counter_synapse_action_str[] = {
> +	[COUNTER_SYNAPSE_ACTION_NONE] = "none",
> +	[COUNTER_SYNAPSE_ACTION_RISING_EDGE] = "rising edge",
> +	[COUNTER_SYNAPSE_ACTION_FALLING_EDGE] = "falling edge",
> +	[COUNTER_SYNAPSE_ACTION_BOTH_EDGES] = "both edges"
> +};
> +
> +static const char *const counter_count_direction_str[] = {
> +	[COUNTER_COUNT_DIRECTION_FORWARD] = "forward",
> +	[COUNTER_COUNT_DIRECTION_BACKWARD] = "backward"
> +};
> +
> +static const char *const counter_count_mode_str[] = {
> +	[COUNTER_COUNT_MODE_NORMAL] = "normal",
> +	[COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit",
> +	[COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle",
> +	[COUNTER_COUNT_MODE_MODULO_N] = "modulo-n"
> +};
> +
> +static ssize_t counter_comp_u8_show(struct device *dev,
> +				    struct device_attribute *attr, char *buf)
> +{
> +	const struct counter_attribute *const a = to_counter_attribute(attr);
> +	struct counter_device *const counter = dev_get_drvdata(dev);
> +	int err;
> +	u8 data = 0;
> +
> +	switch (a->scope) {
> +	case COUNTER_SCOPE_DEVICE:
> +		err = a->comp.device_u8_read(counter, &data);
> +		break;
> +	case COUNTER_SCOPE_SIGNAL:
> +		err = a->comp.signal_u8_read(counter, a->parent, &data);
> +		break;
> +	case COUNTER_SCOPE_COUNT:
> +		err = a->comp.count_u8_read(counter, a->parent, &data);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	if (err < 0)
> +		return err;
> +
> +	if (a->comp.type == COUNTER_COMP_BOOL)
> +		/* data should already be boolean but ensure just to be safe */
> +		data = !!data;
> +
> +	return sprintf(buf, "%u\n", (unsigned int)data);
> +}
> +
> +static ssize_t counter_comp_u8_store(struct device *dev,
> +				     struct device_attribute *attr,
> +				     const char *buf, size_t len)
> +{
> +	const struct counter_attribute *const a = to_counter_attribute(attr);
> +	struct counter_device *const counter = dev_get_drvdata(dev);
> +	int err;
> +	bool bool_data = 0;
> +	u8 data = 0;
> +
> +	if (a->comp.type == COUNTER_COMP_BOOL) {
> +		err = kstrtobool(buf, &bool_data);
> +		data = bool_data;
> +	} else
> +		err = kstrtou8(buf, 0, &data);
> +	if (err < 0)
> +		return err;
> +
> +	switch (a->scope) {
> +	case COUNTER_SCOPE_DEVICE:
> +		err = a->comp.device_u8_write(counter, data);
> +		break;
> +	case COUNTER_SCOPE_SIGNAL:
> +		err = a->comp.signal_u8_write(counter, a->parent, data);
> +		break;
> +	case COUNTER_SCOPE_COUNT:
> +		err = a->comp.count_u8_write(counter, a->parent, data);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	if (err < 0)
> +		return err;
> +
> +	return len;
> +}
> +
> +static ssize_t counter_comp_u32_show(struct device *dev,
> +				     struct device_attribute *attr, char *buf)
> +{
> +	const struct counter_attribute *const a = to_counter_attribute(attr);
> +	struct counter_device *const counter = dev_get_drvdata(dev);
> +	const struct counter_available *const avail = a->comp.priv;
> +	int err;
> +	u32 data = 0;
> +
> +	switch (a->scope) {
> +	case COUNTER_SCOPE_DEVICE:
> +		err = a->comp.device_u32_read(counter, &data);
> +		break;
> +	case COUNTER_SCOPE_SIGNAL:
> +		err = a->comp.signal_u32_read(counter, a->parent, &data);
> +		break;
> +	case COUNTER_SCOPE_COUNT:
> +		if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)
> +			err = a->comp.action_read(counter, a->parent,
> +						  a->comp.priv, &data);
> +		else
> +			err = a->comp.count_u32_read(counter, a->parent, &data);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	if (err < 0)
> +		return err;
> +
> +	switch (a->comp.type) {
> +	case COUNTER_COMP_FUNCTION:
> +		return sysfs_emit(buf, "%s\n", counter_function_str[data]);
> +	case COUNTER_COMP_SIGNAL_LEVEL:
> +		return sysfs_emit(buf, "%s\n", counter_signal_value_str[data]);
> +	case COUNTER_COMP_SYNAPSE_ACTION:
> +		return sysfs_emit(buf, "%s\n", counter_synapse_action_str[data]);
> +	case COUNTER_COMP_ENUM:
> +		return sysfs_emit(buf, "%s\n", avail->strs[data]);
> +	case COUNTER_COMP_COUNT_DIRECTION:
> +		return sysfs_emit(buf, "%s\n", counter_count_direction_str[data]);
> +	case COUNTER_COMP_COUNT_MODE:
> +		return sysfs_emit(buf, "%s\n", counter_count_mode_str[data]);
> +	default:
> +		return sprintf(buf, "%u\n", (unsigned int)data);
> +	}
> +}
> +
> +static int counter_find_enum(u32 *const enum_item, const u32 *const enums,
> +			     const size_t num_enums, const char *const buf,
> +			     const char *const string_array[])
> +{
> +	size_t index;
> +
> +	for (index = 0; index < num_enums; index++) {
> +		*enum_item = enums[index];
> +		if (sysfs_streq(buf, string_array[*enum_item]))
> +			return 0;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static ssize_t counter_comp_u32_store(struct device *dev,
> +				      struct device_attribute *attr,
> +				      const char *buf, size_t len)
> +{
> +	const struct counter_attribute *const a = to_counter_attribute(attr);
> +	struct counter_device *const counter = dev_get_drvdata(dev);
> +	struct counter_count *const count = a->parent;
> +	struct counter_synapse *const synapse = a->comp.priv;
> +	const struct counter_available *const avail = a->comp.priv;
> +	int err;
> +	u32 data = 0;
> +
> +	switch (a->comp.type) {
> +	case COUNTER_COMP_FUNCTION:
> +		err = counter_find_enum(&data, count->functions_list,
> +					count->num_functions, buf,
> +					counter_function_str);
> +		break;
> +	case COUNTER_COMP_SYNAPSE_ACTION:
> +		err = counter_find_enum(&data, synapse->actions_list,
> +					synapse->num_actions, buf,
> +					counter_synapse_action_str);
> +		break;
> +	case COUNTER_COMP_ENUM:
> +		err = __sysfs_match_string(avail->strs, avail->num_items, buf);
> +		data = err;
> +		break;
> +	case COUNTER_COMP_COUNT_MODE:
> +		err = counter_find_enum(&data, avail->enums, avail->num_items,
> +					buf, counter_count_mode_str);
> +		break;
> +	default:
> +		err = kstrtou32(buf, 0, &data);
> +		break;
> +	}
> +	if (err < 0)
> +		return err;
> +
> +	switch (a->scope) {
> +	case COUNTER_SCOPE_DEVICE:
> +		err = a->comp.device_u32_write(counter, data);
> +		break;
> +	case COUNTER_SCOPE_SIGNAL:
> +		err = a->comp.signal_u32_write(counter, a->parent, data);
> +		break;
> +	case COUNTER_SCOPE_COUNT:
> +		if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)
> +			err = a->comp.action_write(counter, count, synapse,
> +						   data);
> +		else
> +			err = a->comp.count_u32_write(counter, count, data);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	if (err < 0)
> +		return err;
> +
> +	return len;
> +}
> +
> +static ssize_t counter_comp_u64_show(struct device *dev,
> +				     struct device_attribute *attr, char *buf)
> +{
> +	const struct counter_attribute *const a = to_counter_attribute(attr);
> +	struct counter_device *const counter = dev_get_drvdata(dev);
> +	int err;
> +	u64 data = 0;
> +
> +	switch (a->scope) {
> +	case COUNTER_SCOPE_DEVICE:
> +		err = a->comp.device_u64_read(counter, &data);
> +		break;
> +	case COUNTER_SCOPE_SIGNAL:
> +		err = a->comp.signal_u64_read(counter, a->parent, &data);
> +		break;
> +	case COUNTER_SCOPE_COUNT:
> +		err = a->comp.count_u64_read(counter, a->parent, &data);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	if (err < 0)
> +		return err;
> +
> +	return sprintf(buf, "%llu\n", (unsigned long long)data);
> +}
> +
> +static ssize_t counter_comp_u64_store(struct device *dev,
> +				      struct device_attribute *attr,
> +				      const char *buf, size_t len)
> +{
> +	const struct counter_attribute *const a = to_counter_attribute(attr);
> +	struct counter_device *const counter = dev_get_drvdata(dev);
> +	int err;
> +	u64 data = 0;
> +
> +	err = kstrtou64(buf, 0, &data);
> +	if (err < 0)
> +		return err;
> +
> +	switch (a->scope) {
> +	case COUNTER_SCOPE_DEVICE:
> +		err = a->comp.device_u64_write(counter, data);
> +		break;
> +	case COUNTER_SCOPE_SIGNAL:
> +		err = a->comp.signal_u64_write(counter, a->parent, data);
> +		break;
> +	case COUNTER_SCOPE_COUNT:
> +		err = a->comp.count_u64_write(counter, a->parent, data);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	if (err < 0)
> +		return err;
> +
> +	return len;
> +}
> +
> +static ssize_t enums_available_show(const u32 *const enums,
> +				    const size_t num_enums,
> +				    const char *const strs[], char *buf)
> +{
> +	size_t len = 0;
> +	size_t index;
> +
> +	for (index = 0; index < num_enums; index++)
> +		len += sysfs_emit_at(buf, len, "%s\n", strs[enums[index]]);
> +
> +	return len;
> +}
> +
> +static ssize_t strs_available_show(const struct counter_available *const avail,
> +				   char *buf)
> +{
> +	size_t len = 0;
> +	size_t index;
> +
> +	for (index = 0; index < avail->num_items; index++)
> +		len += sysfs_emit_at(buf, len, "%s\n", avail->strs[index]);
> +
> +	return len;
> +}
> +
> +static ssize_t counter_comp_available_show(struct device *dev,
> +					   struct device_attribute *attr,
> +					   char *buf)
> +{
> +	const struct counter_attribute *const a = to_counter_attribute(attr);
> +	const struct counter_count *const count = a->parent;
> +	const struct counter_synapse *const synapse = a->comp.priv;
> +	const struct counter_available *const avail = a->comp.priv;
> +
> +	switch (a->comp.type) {
> +	case COUNTER_COMP_FUNCTION:
> +		return enums_available_show(count->functions_list,
> +					    count->num_functions,
> +					    counter_function_str, buf);
> +	case COUNTER_COMP_SYNAPSE_ACTION:
> +		return enums_available_show(synapse->actions_list,
> +					    synapse->num_actions,
> +					    counter_synapse_action_str, buf);
> +	case COUNTER_COMP_ENUM:
> +		return strs_available_show(avail, buf);
> +	case COUNTER_COMP_COUNT_MODE:
> +		return enums_available_show(avail->enums, avail->num_items,
> +					    counter_count_mode_str, buf);
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int counter_avail_attr_create(struct device *const dev,
> +	struct counter_attribute_group *const group,
> +	const struct counter_comp *const comp, void *const parent)
> +{
> +	struct counter_attribute *counter_attr;
> +	struct device_attribute *dev_attr;
> +
> +	counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
> +	if (!counter_attr)
> +		return -ENOMEM;
> +
> +	/* Configure Counter attribute */
> +	counter_attr->comp.type = comp->type;
> +	counter_attr->comp.priv = comp->priv;
> +	counter_attr->parent = parent;
> +
> +	/* Initialize sysfs attribute */
> +	dev_attr = &counter_attr->dev_attr;
> +	sysfs_attr_init(&dev_attr->attr);
> +
> +	/* Configure device attribute */
> +	dev_attr->attr.name = devm_kasprintf(dev, GFP_KERNEL, "%s_available",
> +					     comp->name);
> +	if (!dev_attr->attr.name)
> +		return -ENOMEM;
> +	dev_attr->attr.mode = 0444;
> +	dev_attr->show = counter_comp_available_show;
> +
> +	/* Store list node */
> +	list_add(&counter_attr->l, &group->attr_list);
> +	group->num_attr++;
> +
> +	return 0;
> +}
> +
> +static int counter_attr_create(struct device *const dev,
> +			       struct counter_attribute_group *const group,
> +			       const struct counter_comp *const comp,
> +			       const enum counter_scope scope,
> +			       void *const parent)
> +{
> +	struct counter_attribute *counter_attr;
> +	struct device_attribute *dev_attr;
> +
> +	counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
> +	if (!counter_attr)
> +		return -ENOMEM;
> +
> +	/* Configure Counter attribute */
> +	counter_attr->comp = *comp;
> +	counter_attr->scope = scope;
> +	counter_attr->parent = parent;
> +
> +	/* Configure device attribute */
> +	dev_attr = &counter_attr->dev_attr;
> +	sysfs_attr_init(&dev_attr->attr);
> +	dev_attr->attr.name = comp->name;
> +	switch (comp->type) {
> +	case COUNTER_COMP_U8:
> +	case COUNTER_COMP_BOOL:
> +		if (comp->device_u8_read) {
> +			dev_attr->attr.mode |= 0444;
> +			dev_attr->show = counter_comp_u8_show;
> +		}
> +		if (comp->device_u8_write) {
> +			dev_attr->attr.mode |= 0200;
> +			dev_attr->store = counter_comp_u8_store;
> +		}
> +		break;
> +	case COUNTER_COMP_SIGNAL_LEVEL:
> +	case COUNTER_COMP_FUNCTION:
> +	case COUNTER_COMP_SYNAPSE_ACTION:
> +	case COUNTER_COMP_ENUM:
> +	case COUNTER_COMP_COUNT_DIRECTION:
> +	case COUNTER_COMP_COUNT_MODE:
> +		if (comp->device_u32_read) {
> +			dev_attr->attr.mode |= 0444;
> +			dev_attr->show = counter_comp_u32_show;
> +		}
> +		if (comp->device_u32_write) {
> +			dev_attr->attr.mode |= 0200;
> +			dev_attr->store = counter_comp_u32_store;
> +		}
> +		break;
> +	case COUNTER_COMP_U64:
> +		if (comp->device_u64_read) {
> +			dev_attr->attr.mode |= 0444;
> +			dev_attr->show = counter_comp_u64_show;
> +		}
> +		if (comp->device_u64_write) {
> +			dev_attr->attr.mode |= 0200;
> +			dev_attr->store = counter_comp_u64_store;
> +		}
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	/* Store list node */
> +	list_add(&counter_attr->l, &group->attr_list);
> +	group->num_attr++;
> +
> +	/* Create "*_available" attribute if needed */
> +	switch (comp->type) {
> +	case COUNTER_COMP_FUNCTION:
> +	case COUNTER_COMP_SYNAPSE_ACTION:
> +	case COUNTER_COMP_ENUM:
> +	case COUNTER_COMP_COUNT_MODE:
> +		return counter_avail_attr_create(dev, group, comp, parent);
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static ssize_t counter_comp_name_show(struct device *dev,
> +				      struct device_attribute *attr, char *buf)
> +{
> +	return sysfs_emit(buf, "%s\n", to_counter_attribute(attr)->comp.name);
> +}
> +
> +static int counter_name_attr_create(struct device *const dev,
> +				    struct counter_attribute_group *const group,
> +				    const char *const name)
> +{
> +	struct counter_attribute *counter_attr;
> +
> +	counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
> +	if (!counter_attr)
> +		return -ENOMEM;
> +
> +	/* Configure Counter attribute */
> +	counter_attr->comp.name = name;
> +
> +	/* Configure device attribute */
> +	sysfs_attr_init(&counter_attr->dev_attr.attr);
> +	counter_attr->dev_attr.attr.name = "name";
> +	counter_attr->dev_attr.attr.mode = 0444;
> +	counter_attr->dev_attr.show = counter_comp_name_show;
> +
> +	/* Store list node */
> +	list_add(&counter_attr->l, &group->attr_list);
> +	group->num_attr++;
> +
> +	return 0;
> +}
> +
> +static struct counter_comp counter_signal_comp = {
> +	.type = COUNTER_COMP_SIGNAL_LEVEL,
> +	.name = "signal",
> +};
> +
> +static int counter_signal_attrs_create(struct counter_device *const counter,
> +	struct counter_attribute_group *const cattr_group,
> +	struct counter_signal *const signal)
> +{
> +	const enum counter_scope scope = COUNTER_SCOPE_SIGNAL;
> +	struct device *const dev = &counter->dev;
> +	int err;
> +	struct counter_comp comp;
> +	size_t i;
> +
> +	/* Create main Signal attribute */
> +	comp = counter_signal_comp;
> +	comp.signal_u32_read = counter->ops->signal_read;
> +	err = counter_attr_create(dev, cattr_group, &comp, scope, signal);
> +	if (err < 0)
> +		return err;
> +
> +	/* Create Signal name attribute */
> +	err = counter_name_attr_create(dev, cattr_group, signal->name);
> +	if (err < 0)
> +		return err;
> +
> +	/* Create an attribute for each extension */
> +	for (i = 0; i < signal->num_ext; i++) {
> +		err = counter_attr_create(dev, cattr_group, signal->ext + i,
> +					  scope, signal);
> +		if (err < 0)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int counter_sysfs_signals_add(struct counter_device *const counter,
> +	struct counter_attribute_group *const groups)
> +{
> +	size_t i;
> +	int err;
> +
> +	/* Add each Signal */
> +	for (i = 0; i < counter->num_signals; i++) {
> +		/* Generate Signal attribute directory name */
> +		groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL,
> +						"signal%zu", i);
> +		if (!groups[i].name)
> +			return -ENOMEM;
> +
> +		/* Create all attributes associated with Signal */
> +		err = counter_signal_attrs_create(counter, groups + i,
> +						  counter->signals + i);
> +		if (err < 0)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int counter_sysfs_synapses_add(struct counter_device *const counter,
> +	struct counter_attribute_group *const group,
> +	struct counter_count *const count)
> +{
> +	size_t i;
> +
> +	/* Add each Synapse */
> +	for (i = 0; i < count->num_synapses; i++) {
> +		struct device *const dev = &counter->dev;
> +		struct counter_synapse *synapse;
> +		size_t id;
> +		struct counter_comp comp;
> +		int err;
> +
> +		synapse = count->synapses + i;
> +
> +		/* Generate Synapse action name */
> +		id = synapse->signal - counter->signals;
> +		comp.name = devm_kasprintf(dev, GFP_KERNEL, "signal%zu_action",
> +					   id);
> +		if (!comp.name)
> +			return -ENOMEM;
> +
> +		/* Create action attribute */
> +		comp.type = COUNTER_COMP_SYNAPSE_ACTION;
> +		comp.action_read = counter->ops->action_read;
> +		comp.action_write = counter->ops->action_write;
> +		comp.priv = synapse;
> +		err = counter_attr_create(dev, group, &comp,
> +					  COUNTER_SCOPE_COUNT, count);
> +		if (err < 0)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct counter_comp counter_count_comp =
> +	COUNTER_COMP_COUNT_U64("count", NULL, NULL);
> +
> +static struct counter_comp counter_function_comp = {
> +	.type = COUNTER_COMP_FUNCTION,
> +	.name = "function",
> +};
> +
> +static int counter_count_attrs_create(struct counter_device *const counter,
> +	struct counter_attribute_group *const cattr_group,
> +	struct counter_count *const count)
> +{
> +	const enum counter_scope scope = COUNTER_SCOPE_COUNT;
> +	struct device *const dev = &counter->dev;
> +	int err;
> +	struct counter_comp comp;
> +	size_t i;
> +
> +	/* Create main Count attribute */
> +	comp = counter_count_comp;
> +	comp.count_u64_read = counter->ops->count_read;
> +	comp.count_u64_write = counter->ops->count_write;
> +	err = counter_attr_create(dev, cattr_group, &comp, scope, count);
> +	if (err < 0)
> +		return err;
> +
> +	/* Create Count name attribute */
> +	err = counter_name_attr_create(dev, cattr_group, count->name);
> +	if (err < 0)
> +		return err;
> +
> +	/* Create Count function attribute */
> +	comp = counter_function_comp;
> +	comp.count_u32_read = counter->ops->function_read;
> +	comp.count_u32_write = counter->ops->function_write;
> +	err = counter_attr_create(dev, cattr_group, &comp, scope, count);
> +	if (err < 0)
> +		return err;
> +
> +	/* Create an attribute for each extension */
> +	for (i = 0; i < count->num_ext; i++) {
> +		err = counter_attr_create(dev, cattr_group, count->ext + i,
> +					  scope, count);
> +		if (err < 0)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int counter_sysfs_counts_add(struct counter_device *const counter,
> +	struct counter_attribute_group *const groups)
> +{
> +	size_t i;
> +	struct counter_count *count;
> +	int err;
> +
> +	/* Add each Count */
> +	for (i = 0; i < counter->num_counts; i++) {
> +		count = counter->counts + i;
> +
> +		/* Generate Count attribute directory name */
> +		groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL,
> +						"count%zu", i);
> +		if (!groups[i].name)
> +			return -ENOMEM;
> +
> +		/* Add sysfs attributes of the Synapses */
> +		err = counter_sysfs_synapses_add(counter, groups + i, count);
> +		if (err < 0)
> +			return err;
> +
> +		/* Create all attributes associated with Count */
> +		err = counter_count_attrs_create(counter, groups + i, count);
> +		if (err < 0)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int counter_num_signals_read(struct counter_device *counter, u8 *val)
> +{
> +	*val = counter->num_signals;
> +	return 0;
> +}
> +
> +static int counter_num_counts_read(struct counter_device *counter, u8 *val)
> +{
> +	*val = counter->num_counts;
> +	return 0;
> +}
> +
> +static struct counter_comp counter_num_signals_comp =
> +	COUNTER_COMP_DEVICE_U8("num_signals", counter_num_signals_read, NULL);
> +
> +static struct counter_comp counter_num_counts_comp =
> +	COUNTER_COMP_DEVICE_U8("num_counts", counter_num_counts_read, NULL);
> +
> +static int counter_sysfs_attr_add(struct counter_device *const counter,
> +				  struct counter_attribute_group *cattr_group)
> +{
> +	const enum counter_scope scope = COUNTER_SCOPE_DEVICE;
> +	struct device *const dev = &counter->dev;
> +	int err;
> +	size_t i;
> +
> +	/* Add Signals sysfs attributes */
> +	err = counter_sysfs_signals_add(counter, cattr_group);
> +	if (err < 0)
> +		return err;
> +	cattr_group += counter->num_signals;
> +
> +	/* Add Counts sysfs attributes */
> +	err = counter_sysfs_counts_add(counter, cattr_group);
> +	if (err < 0)
> +		return err;
> +	cattr_group += counter->num_counts;
> +
> +	/* Create name attribute */
> +	err = counter_name_attr_create(dev, cattr_group, counter->name);
> +	if (err < 0)
> +		return err;
> +
> +	/* Create num_signals attribute */
> +	err = counter_attr_create(dev, cattr_group, &counter_num_signals_comp,
> +				  scope, NULL);
> +	if (err < 0)
> +		return err;
> +
> +	/* Create num_counts attribute */
> +	err = counter_attr_create(dev, cattr_group, &counter_num_counts_comp,
> +				  scope, NULL);
> +	if (err < 0)
> +		return err;
> +
> +	/* Create an attribute for each extension */
> +	for (i = 0; i < counter->num_ext; i++) {
> +		err = counter_attr_create(dev, cattr_group, counter->ext + i,
> +					  scope, NULL);
> +		if (err < 0)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * counter_sysfs_add - Adds Counter sysfs attributes to the device structure
> + * @counter:	Pointer to the Counter device structure
> + *
> + * Counter sysfs attributes are created and added to the respective device
> + * structure for later registration to the system. Resource-managed memory
> + * allocation is performed by this function, and this memory should be freed
> + * when no longer needed (automatically by a device_unregister call, or
> + * manually by a devres_release_all call).
> + */
> +int counter_sysfs_add(struct counter_device *const counter)
> +{
> +	struct device *const dev = &counter->dev;
> +	const size_t num_groups = counter->num_signals + counter->num_counts + 1;
> +	struct counter_attribute_group *cattr_groups;
> +	size_t i, j;
> +	int err;
> +	struct attribute_group *groups;
> +	struct counter_attribute *p;
> +
> +	/* Allocate space for attribute groups (signals, counts, and ext) */
> +	cattr_groups = devm_kcalloc(dev, num_groups, sizeof(*cattr_groups),
> +				    GFP_KERNEL);
> +	if (!cattr_groups)
> +		return -ENOMEM;
> +
> +	/* Initialize attribute lists */
> +	for (i = 0; i < num_groups; i++)
> +		INIT_LIST_HEAD(&cattr_groups[i].attr_list);
> +
> +	/* Add Counter device sysfs attributes */
> +	err = counter_sysfs_attr_add(counter, cattr_groups);
> +	if (err < 0)
> +		return err;
> +
> +	/* Allocate attribute group pointers for association with device */
> +	dev->groups = devm_kcalloc(dev, num_groups + 1, sizeof(*dev->groups),
> +				   GFP_KERNEL);
> +	if (!dev->groups)
> +		return -ENOMEM;
> +
> +	/* Allocate space for attribute groups */
> +	groups = devm_kcalloc(dev, num_groups, sizeof(*groups), GFP_KERNEL);
> +	if (!groups)
> +		return -ENOMEM;
> +
> +	/* Prepare each group of attributes for association */
> +	for (i = 0; i < num_groups; i++) {
> +		groups[i].name = cattr_groups[i].name;
> +
> +		/* Allocate space for attribute pointers */
> +		groups[i].attrs = devm_kcalloc(dev,
> +					       cattr_groups[i].num_attr + 1,
> +					       sizeof(*groups[i].attrs),
> +					       GFP_KERNEL);
> +		if (!groups[i].attrs)
> +			return -ENOMEM;
> +
> +		/* Add attribute pointers to attribute group */
> +		j = 0;
> +		list_for_each_entry(p, &cattr_groups[i].attr_list, l)
> +			groups[i].attrs[j++] = &p->dev_attr.attr;
> +
> +		/* Associate attribute group */
> +		dev->groups[i] = &groups[i];
> +	}
> +
> +	return 0;
> +}
> diff --git a/drivers/counter/counter-sysfs.h b/drivers/counter/counter-sysfs.h
> new file mode 100644
> index 000000000000..14fe566aca0e
> --- /dev/null
> +++ b/drivers/counter/counter-sysfs.h
> @@ -0,0 +1,13 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Counter sysfs interface
> + * Copyright (C) 2020 William Breathitt Gray
> + */
> +#ifndef _COUNTER_SYSFS_H_
> +#define _COUNTER_SYSFS_H_
> +
> +#include <linux/counter.h>
> +
> +int counter_sysfs_add(struct counter_device *const counter);
> +
> +#endif /* _COUNTER_SYSFS_H_ */
> diff --git a/drivers/counter/counter.c b/drivers/counter/counter.c
> deleted file mode 100644
> index de921e8a3f72..000000000000
> --- a/drivers/counter/counter.c
> +++ /dev/null
> @@ -1,1496 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -/*
> - * Generic Counter interface
> - * Copyright (C) 2018 William Breathitt Gray
> - */
> -#include <linux/counter.h>
> -#include <linux/device.h>
> -#include <linux/err.h>
> -#include <linux/export.h>
> -#include <linux/fs.h>
> -#include <linux/gfp.h>
> -#include <linux/idr.h>
> -#include <linux/init.h>
> -#include <linux/kernel.h>
> -#include <linux/list.h>
> -#include <linux/module.h>
> -#include <linux/printk.h>
> -#include <linux/slab.h>
> -#include <linux/string.h>
> -#include <linux/sysfs.h>
> -#include <linux/types.h>
> -
> -const char *const counter_count_direction_str[2] = {
> -	[COUNTER_COUNT_DIRECTION_FORWARD] = "forward",
> -	[COUNTER_COUNT_DIRECTION_BACKWARD] = "backward"
> -};
> -EXPORT_SYMBOL_GPL(counter_count_direction_str);
> -
> -const char *const counter_count_mode_str[4] = {
> -	[COUNTER_COUNT_MODE_NORMAL] = "normal",
> -	[COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit",
> -	[COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle",
> -	[COUNTER_COUNT_MODE_MODULO_N] = "modulo-n"
> -};
> -EXPORT_SYMBOL_GPL(counter_count_mode_str);
> -
> -ssize_t counter_signal_enum_read(struct counter_device *counter,
> -				 struct counter_signal *signal, void *priv,
> -				 char *buf)
> -{
> -	const struct counter_signal_enum_ext *const e = priv;
> -	int err;
> -	size_t index;
> -
> -	if (!e->get)
> -		return -EINVAL;
> -
> -	err = e->get(counter, signal, &index);
> -	if (err)
> -		return err;
> -
> -	if (index >= e->num_items)
> -		return -EINVAL;
> -
> -	return sprintf(buf, "%s\n", e->items[index]);
> -}
> -EXPORT_SYMBOL_GPL(counter_signal_enum_read);
> -
> -ssize_t counter_signal_enum_write(struct counter_device *counter,
> -				  struct counter_signal *signal, void *priv,
> -				  const char *buf, size_t len)
> -{
> -	const struct counter_signal_enum_ext *const e = priv;
> -	ssize_t index;
> -	int err;
> -
> -	if (!e->set)
> -		return -EINVAL;
> -
> -	index = __sysfs_match_string(e->items, e->num_items, buf);
> -	if (index < 0)
> -		return index;
> -
> -	err = e->set(counter, signal, index);
> -	if (err)
> -		return err;
> -
> -	return len;
> -}
> -EXPORT_SYMBOL_GPL(counter_signal_enum_write);
> -
> -ssize_t counter_signal_enum_available_read(struct counter_device *counter,
> -					   struct counter_signal *signal,
> -					   void *priv, char *buf)
> -{
> -	const struct counter_signal_enum_ext *const e = priv;
> -	size_t i;
> -	size_t len = 0;
> -
> -	if (!e->num_items)
> -		return 0;
> -
> -	for (i = 0; i < e->num_items; i++)
> -		len += sprintf(buf + len, "%s\n", e->items[i]);
> -
> -	return len;
> -}
> -EXPORT_SYMBOL_GPL(counter_signal_enum_available_read);
> -
> -ssize_t counter_count_enum_read(struct counter_device *counter,
> -				struct counter_count *count, void *priv,
> -				char *buf)
> -{
> -	const struct counter_count_enum_ext *const e = priv;
> -	int err;
> -	size_t index;
> -
> -	if (!e->get)
> -		return -EINVAL;
> -
> -	err = e->get(counter, count, &index);
> -	if (err)
> -		return err;
> -
> -	if (index >= e->num_items)
> -		return -EINVAL;
> -
> -	return sprintf(buf, "%s\n", e->items[index]);
> -}
> -EXPORT_SYMBOL_GPL(counter_count_enum_read);
> -
> -ssize_t counter_count_enum_write(struct counter_device *counter,
> -				 struct counter_count *count, void *priv,
> -				 const char *buf, size_t len)
> -{
> -	const struct counter_count_enum_ext *const e = priv;
> -	ssize_t index;
> -	int err;
> -
> -	if (!e->set)
> -		return -EINVAL;
> -
> -	index = __sysfs_match_string(e->items, e->num_items, buf);
> -	if (index < 0)
> -		return index;
> -
> -	err = e->set(counter, count, index);
> -	if (err)
> -		return err;
> -
> -	return len;
> -}
> -EXPORT_SYMBOL_GPL(counter_count_enum_write);
> -
> -ssize_t counter_count_enum_available_read(struct counter_device *counter,
> -					  struct counter_count *count,
> -					  void *priv, char *buf)
> -{
> -	const struct counter_count_enum_ext *const e = priv;
> -	size_t i;
> -	size_t len = 0;
> -
> -	if (!e->num_items)
> -		return 0;
> -
> -	for (i = 0; i < e->num_items; i++)
> -		len += sprintf(buf + len, "%s\n", e->items[i]);
> -
> -	return len;
> -}
> -EXPORT_SYMBOL_GPL(counter_count_enum_available_read);
> -
> -ssize_t counter_device_enum_read(struct counter_device *counter, void *priv,
> -				 char *buf)
> -{
> -	const struct counter_device_enum_ext *const e = priv;
> -	int err;
> -	size_t index;
> -
> -	if (!e->get)
> -		return -EINVAL;
> -
> -	err = e->get(counter, &index);
> -	if (err)
> -		return err;
> -
> -	if (index >= e->num_items)
> -		return -EINVAL;
> -
> -	return sprintf(buf, "%s\n", e->items[index]);
> -}
> -EXPORT_SYMBOL_GPL(counter_device_enum_read);
> -
> -ssize_t counter_device_enum_write(struct counter_device *counter, void *priv,
> -				  const char *buf, size_t len)
> -{
> -	const struct counter_device_enum_ext *const e = priv;
> -	ssize_t index;
> -	int err;
> -
> -	if (!e->set)
> -		return -EINVAL;
> -
> -	index = __sysfs_match_string(e->items, e->num_items, buf);
> -	if (index < 0)
> -		return index;
> -
> -	err = e->set(counter, index);
> -	if (err)
> -		return err;
> -
> -	return len;
> -}
> -EXPORT_SYMBOL_GPL(counter_device_enum_write);
> -
> -ssize_t counter_device_enum_available_read(struct counter_device *counter,
> -					   void *priv, char *buf)
> -{
> -	const struct counter_device_enum_ext *const e = priv;
> -	size_t i;
> -	size_t len = 0;
> -
> -	if (!e->num_items)
> -		return 0;
> -
> -	for (i = 0; i < e->num_items; i++)
> -		len += sprintf(buf + len, "%s\n", e->items[i]);
> -
> -	return len;
> -}
> -EXPORT_SYMBOL_GPL(counter_device_enum_available_read);
> -
> -struct counter_attr_parm {
> -	struct counter_device_attr_group *group;
> -	const char *prefix;
> -	const char *name;
> -	ssize_t (*show)(struct device *dev, struct device_attribute *attr,
> -			char *buf);
> -	ssize_t (*store)(struct device *dev, struct device_attribute *attr,
> -			 const char *buf, size_t len);
> -	void *component;
> -};
> -
> -struct counter_device_attr {
> -	struct device_attribute dev_attr;
> -	struct list_head l;
> -	void *component;
> -};
> -
> -static int counter_attribute_create(const struct counter_attr_parm *const parm)
> -{
> -	struct counter_device_attr *counter_attr;
> -	struct device_attribute *dev_attr;
> -	int err;
> -	struct list_head *const attr_list = &parm->group->attr_list;
> -
> -	/* Allocate a Counter device attribute */
> -	counter_attr = kzalloc(sizeof(*counter_attr), GFP_KERNEL);
> -	if (!counter_attr)
> -		return -ENOMEM;
> -	dev_attr = &counter_attr->dev_attr;
> -
> -	sysfs_attr_init(&dev_attr->attr);
> -
> -	/* Configure device attribute */
> -	dev_attr->attr.name = kasprintf(GFP_KERNEL, "%s%s", parm->prefix,
> -					parm->name);
> -	if (!dev_attr->attr.name) {
> -		err = -ENOMEM;
> -		goto err_free_counter_attr;
> -	}
> -	if (parm->show) {
> -		dev_attr->attr.mode |= 0444;
> -		dev_attr->show = parm->show;
> -	}
> -	if (parm->store) {
> -		dev_attr->attr.mode |= 0200;
> -		dev_attr->store = parm->store;
> -	}
> -
> -	/* Store associated Counter component with attribute */
> -	counter_attr->component = parm->component;
> -
> -	/* Keep track of the attribute for later cleanup */
> -	list_add(&counter_attr->l, attr_list);
> -	parm->group->num_attr++;
> -
> -	return 0;
> -
> -err_free_counter_attr:
> -	kfree(counter_attr);
> -	return err;
> -}
> -
> -#define to_counter_attr(_dev_attr) \
> -	container_of(_dev_attr, struct counter_device_attr, dev_attr)
> -
> -struct counter_signal_unit {
> -	struct counter_signal *signal;
> -};
> -
> -static const char *const counter_signal_level_str[] = {
> -	[COUNTER_SIGNAL_LEVEL_LOW] = "low",
> -	[COUNTER_SIGNAL_LEVEL_HIGH] = "high"
> -};
> -
> -static ssize_t counter_signal_show(struct device *dev,
> -				   struct device_attribute *attr, char *buf)
> -{
> -	struct counter_device *const counter = dev_get_drvdata(dev);
> -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> -	const struct counter_signal_unit *const component = devattr->component;
> -	struct counter_signal *const signal = component->signal;
> -	int err;
> -	enum counter_signal_level level;
> -
> -	err = counter->ops->signal_read(counter, signal, &level);
> -	if (err)
> -		return err;
> -
> -	return sprintf(buf, "%s\n", counter_signal_level_str[level]);
> -}
> -
> -struct counter_name_unit {
> -	const char *name;
> -};
> -
> -static ssize_t counter_device_attr_name_show(struct device *dev,
> -					     struct device_attribute *attr,
> -					     char *buf)
> -{
> -	const struct counter_name_unit *const comp = to_counter_attr(attr)->component;
> -
> -	return sprintf(buf, "%s\n", comp->name);
> -}
> -
> -static int counter_name_attribute_create(
> -	struct counter_device_attr_group *const group,
> -	const char *const name)
> -{
> -	struct counter_name_unit *name_comp;
> -	struct counter_attr_parm parm;
> -	int err;
> -
> -	/* Skip if no name */
> -	if (!name)
> -		return 0;
> -
> -	/* Allocate name attribute component */
> -	name_comp = kmalloc(sizeof(*name_comp), GFP_KERNEL);
> -	if (!name_comp)
> -		return -ENOMEM;
> -	name_comp->name = name;
> -
> -	/* Allocate Signal name attribute */
> -	parm.group = group;
> -	parm.prefix = "";
> -	parm.name = "name";
> -	parm.show = counter_device_attr_name_show;
> -	parm.store = NULL;
> -	parm.component = name_comp;
> -	err = counter_attribute_create(&parm);
> -	if (err)
> -		goto err_free_name_comp;
> -
> -	return 0;
> -
> -err_free_name_comp:
> -	kfree(name_comp);
> -	return err;
> -}
> -
> -struct counter_signal_ext_unit {
> -	struct counter_signal *signal;
> -	const struct counter_signal_ext *ext;
> -};
> -
> -static ssize_t counter_signal_ext_show(struct device *dev,
> -				       struct device_attribute *attr, char *buf)
> -{
> -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> -	const struct counter_signal_ext_unit *const comp = devattr->component;
> -	const struct counter_signal_ext *const ext = comp->ext;
> -
> -	return ext->read(dev_get_drvdata(dev), comp->signal, ext->priv, buf);
> -}
> -
> -static ssize_t counter_signal_ext_store(struct device *dev,
> -					struct device_attribute *attr,
> -					const char *buf, size_t len)
> -{
> -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> -	const struct counter_signal_ext_unit *const comp = devattr->component;
> -	const struct counter_signal_ext *const ext = comp->ext;
> -
> -	return ext->write(dev_get_drvdata(dev), comp->signal, ext->priv, buf,
> -		len);
> -}
> -
> -static void counter_device_attr_list_free(struct list_head *attr_list)
> -{
> -	struct counter_device_attr *p, *n;
> -
> -	list_for_each_entry_safe(p, n, attr_list, l) {
> -		/* free attribute name and associated component memory */
> -		kfree(p->dev_attr.attr.name);
> -		kfree(p->component);
> -		list_del(&p->l);
> -		kfree(p);
> -	}
> -}
> -
> -static int counter_signal_ext_register(
> -	struct counter_device_attr_group *const group,
> -	struct counter_signal *const signal)
> -{
> -	const size_t num_ext = signal->num_ext;
> -	size_t i;
> -	const struct counter_signal_ext *ext;
> -	struct counter_signal_ext_unit *signal_ext_comp;
> -	struct counter_attr_parm parm;
> -	int err;
> -
> -	/* Create an attribute for each extension */
> -	for (i = 0 ; i < num_ext; i++) {
> -		ext = signal->ext + i;
> -
> -		/* Allocate signal_ext attribute component */
> -		signal_ext_comp = kmalloc(sizeof(*signal_ext_comp), GFP_KERNEL);
> -		if (!signal_ext_comp) {
> -			err = -ENOMEM;
> -			goto err_free_attr_list;
> -		}
> -		signal_ext_comp->signal = signal;
> -		signal_ext_comp->ext = ext;
> -
> -		/* Allocate a Counter device attribute */
> -		parm.group = group;
> -		parm.prefix = "";
> -		parm.name = ext->name;
> -		parm.show = (ext->read) ? counter_signal_ext_show : NULL;
> -		parm.store = (ext->write) ? counter_signal_ext_store : NULL;
> -		parm.component = signal_ext_comp;
> -		err = counter_attribute_create(&parm);
> -		if (err) {
> -			kfree(signal_ext_comp);
> -			goto err_free_attr_list;
> -		}
> -	}
> -
> -	return 0;
> -
> -err_free_attr_list:
> -	counter_device_attr_list_free(&group->attr_list);
> -	return err;
> -}
> -
> -static int counter_signal_attributes_create(
> -	struct counter_device_attr_group *const group,
> -	const struct counter_device *const counter,
> -	struct counter_signal *const signal)
> -{
> -	struct counter_signal_unit *signal_comp;
> -	struct counter_attr_parm parm;
> -	int err;
> -
> -	/* Allocate Signal attribute component */
> -	signal_comp = kmalloc(sizeof(*signal_comp), GFP_KERNEL);
> -	if (!signal_comp)
> -		return -ENOMEM;
> -	signal_comp->signal = signal;
> -
> -	/* Create main Signal attribute */
> -	parm.group = group;
> -	parm.prefix = "";
> -	parm.name = "signal";
> -	parm.show = (counter->ops->signal_read) ? counter_signal_show : NULL;
> -	parm.store = NULL;
> -	parm.component = signal_comp;
> -	err = counter_attribute_create(&parm);
> -	if (err) {
> -		kfree(signal_comp);
> -		return err;
> -	}
> -
> -	/* Create Signal name attribute */
> -	err = counter_name_attribute_create(group, signal->name);
> -	if (err)
> -		goto err_free_attr_list;
> -
> -	/* Register Signal extension attributes */
> -	err = counter_signal_ext_register(group, signal);
> -	if (err)
> -		goto err_free_attr_list;
> -
> -	return 0;
> -
> -err_free_attr_list:
> -	counter_device_attr_list_free(&group->attr_list);
> -	return err;
> -}
> -
> -static int counter_signals_register(
> -	struct counter_device_attr_group *const groups_list,
> -	const struct counter_device *const counter)
> -{
> -	const size_t num_signals = counter->num_signals;
> -	size_t i;
> -	struct counter_signal *signal;
> -	const char *name;
> -	int err;
> -
> -	/* Register each Signal */
> -	for (i = 0; i < num_signals; i++) {
> -		signal = counter->signals + i;
> -
> -		/* Generate Signal attribute directory name */
> -		name = kasprintf(GFP_KERNEL, "signal%d", signal->id);
> -		if (!name) {
> -			err = -ENOMEM;
> -			goto err_free_attr_groups;
> -		}
> -		groups_list[i].attr_group.name = name;
> -
> -		/* Create all attributes associated with Signal */
> -		err = counter_signal_attributes_create(groups_list + i, counter,
> -						       signal);
> -		if (err)
> -			goto err_free_attr_groups;
> -	}
> -
> -	return 0;
> -
> -err_free_attr_groups:
> -	do {
> -		kfree(groups_list[i].attr_group.name);
> -		counter_device_attr_list_free(&groups_list[i].attr_list);
> -	} while (i--);
> -	return err;
> -}
> -
> -static const char *const counter_synapse_action_str[] = {
> -	[COUNTER_SYNAPSE_ACTION_NONE] = "none",
> -	[COUNTER_SYNAPSE_ACTION_RISING_EDGE] = "rising edge",
> -	[COUNTER_SYNAPSE_ACTION_FALLING_EDGE] = "falling edge",
> -	[COUNTER_SYNAPSE_ACTION_BOTH_EDGES] = "both edges"
> -};
> -
> -struct counter_action_unit {
> -	struct counter_synapse *synapse;
> -	struct counter_count *count;
> -};
> -
> -static ssize_t counter_action_show(struct device *dev,
> -				   struct device_attribute *attr, char *buf)
> -{
> -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> -	int err;
> -	struct counter_device *const counter = dev_get_drvdata(dev);
> -	const struct counter_action_unit *const component = devattr->component;
> -	struct counter_count *const count = component->count;
> -	struct counter_synapse *const synapse = component->synapse;
> -	size_t action_index;
> -	enum counter_synapse_action action;
> -
> -	err = counter->ops->action_get(counter, count, synapse, &action_index);
> -	if (err)
> -		return err;
> -
> -	synapse->action = action_index;
> -
> -	action = synapse->actions_list[action_index];
> -	return sprintf(buf, "%s\n", counter_synapse_action_str[action]);
> -}
> -
> -static ssize_t counter_action_store(struct device *dev,
> -				    struct device_attribute *attr,
> -				    const char *buf, size_t len)
> -{
> -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> -	const struct counter_action_unit *const component = devattr->component;
> -	struct counter_synapse *const synapse = component->synapse;
> -	size_t action_index;
> -	const size_t num_actions = synapse->num_actions;
> -	enum counter_synapse_action action;
> -	int err;
> -	struct counter_device *const counter = dev_get_drvdata(dev);
> -	struct counter_count *const count = component->count;
> -
> -	/* Find requested action mode */
> -	for (action_index = 0; action_index < num_actions; action_index++) {
> -		action = synapse->actions_list[action_index];
> -		if (sysfs_streq(buf, counter_synapse_action_str[action]))
> -			break;
> -	}
> -	/* If requested action mode not found */
> -	if (action_index >= num_actions)
> -		return -EINVAL;
> -
> -	err = counter->ops->action_set(counter, count, synapse, action_index);
> -	if (err)
> -		return err;
> -
> -	synapse->action = action_index;
> -
> -	return len;
> -}
> -
> -struct counter_action_avail_unit {
> -	const enum counter_synapse_action *actions_list;
> -	size_t num_actions;
> -};
> -
> -static ssize_t counter_synapse_action_available_show(struct device *dev,
> -	struct device_attribute *attr, char *buf)
> -{
> -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> -	const struct counter_action_avail_unit *const component = devattr->component;
> -	size_t i;
> -	enum counter_synapse_action action;
> -	ssize_t len = 0;
> -
> -	for (i = 0; i < component->num_actions; i++) {
> -		action = component->actions_list[i];
> -		len += sprintf(buf + len, "%s\n",
> -			       counter_synapse_action_str[action]);
> -	}
> -
> -	return len;
> -}
> -
> -static int counter_synapses_register(
> -	struct counter_device_attr_group *const group,
> -	const struct counter_device *const counter,
> -	struct counter_count *const count, const char *const count_attr_name)
> -{
> -	size_t i;
> -	struct counter_synapse *synapse;
> -	const char *prefix;
> -	struct counter_action_unit *action_comp;
> -	struct counter_attr_parm parm;
> -	int err;
> -	struct counter_action_avail_unit *avail_comp;
> -
> -	/* Register each Synapse */
> -	for (i = 0; i < count->num_synapses; i++) {
> -		synapse = count->synapses + i;
> -
> -		/* Generate attribute prefix */
> -		prefix = kasprintf(GFP_KERNEL, "signal%d_",
> -				   synapse->signal->id);
> -		if (!prefix) {
> -			err = -ENOMEM;
> -			goto err_free_attr_list;
> -		}
> -
> -		/* Allocate action attribute component */
> -		action_comp = kmalloc(sizeof(*action_comp), GFP_KERNEL);
> -		if (!action_comp) {
> -			err = -ENOMEM;
> -			goto err_free_prefix;
> -		}
> -		action_comp->synapse = synapse;
> -		action_comp->count = count;
> -
> -		/* Create action attribute */
> -		parm.group = group;
> -		parm.prefix = prefix;
> -		parm.name = "action";
> -		parm.show = (counter->ops->action_get) ? counter_action_show : NULL;
> -		parm.store = (counter->ops->action_set) ? counter_action_store : NULL;
> -		parm.component = action_comp;
> -		err = counter_attribute_create(&parm);
> -		if (err) {
> -			kfree(action_comp);
> -			goto err_free_prefix;
> -		}
> -
> -		/* Allocate action available attribute component */
> -		avail_comp = kmalloc(sizeof(*avail_comp), GFP_KERNEL);
> -		if (!avail_comp) {
> -			err = -ENOMEM;
> -			goto err_free_prefix;
> -		}
> -		avail_comp->actions_list = synapse->actions_list;
> -		avail_comp->num_actions = synapse->num_actions;
> -
> -		/* Create action_available attribute */
> -		parm.group = group;
> -		parm.prefix = prefix;
> -		parm.name = "action_available";
> -		parm.show = counter_synapse_action_available_show;
> -		parm.store = NULL;
> -		parm.component = avail_comp;
> -		err = counter_attribute_create(&parm);
> -		if (err) {
> -			kfree(avail_comp);
> -			goto err_free_prefix;
> -		}
> -
> -		kfree(prefix);
> -	}
> -
> -	return 0;
> -
> -err_free_prefix:
> -	kfree(prefix);
> -err_free_attr_list:
> -	counter_device_attr_list_free(&group->attr_list);
> -	return err;
> -}
> -
> -struct counter_count_unit {
> -	struct counter_count *count;
> -};
> -
> -static ssize_t counter_count_show(struct device *dev,
> -				  struct device_attribute *attr,
> -				  char *buf)
> -{
> -	struct counter_device *const counter = dev_get_drvdata(dev);
> -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> -	const struct counter_count_unit *const component = devattr->component;
> -	struct counter_count *const count = component->count;
> -	int err;
> -	unsigned long val;
> -
> -	err = counter->ops->count_read(counter, count, &val);
> -	if (err)
> -		return err;
> -
> -	return sprintf(buf, "%lu\n", val);
> -}
> -
> -static ssize_t counter_count_store(struct device *dev,
> -				   struct device_attribute *attr,
> -				   const char *buf, size_t len)
> -{
> -	struct counter_device *const counter = dev_get_drvdata(dev);
> -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> -	const struct counter_count_unit *const component = devattr->component;
> -	struct counter_count *const count = component->count;
> -	int err;
> -	unsigned long val;
> -
> -	err = kstrtoul(buf, 0, &val);
> -	if (err)
> -		return err;
> -
> -	err = counter->ops->count_write(counter, count, val);
> -	if (err)
> -		return err;
> -
> -	return len;
> -}
> -
> -static const char *const counter_function_str[] = {
> -	[COUNTER_FUNCTION_INCREASE] = "increase",
> -	[COUNTER_FUNCTION_DECREASE] = "decrease",
> -	[COUNTER_FUNCTION_PULSE_DIRECTION] = "pulse-direction",
> -	[COUNTER_FUNCTION_QUADRATURE_X1_A] = "quadrature x1 a",
> -	[COUNTER_FUNCTION_QUADRATURE_X1_B] = "quadrature x1 b",
> -	[COUNTER_FUNCTION_QUADRATURE_X2_A] = "quadrature x2 a",
> -	[COUNTER_FUNCTION_QUADRATURE_X2_B] = "quadrature x2 b",
> -	[COUNTER_FUNCTION_QUADRATURE_X4] = "quadrature x4"
> -};
> -
> -static ssize_t counter_function_show(struct device *dev,
> -				     struct device_attribute *attr, char *buf)
> -{
> -	int err;
> -	struct counter_device *const counter = dev_get_drvdata(dev);
> -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> -	const struct counter_count_unit *const component = devattr->component;
> -	struct counter_count *const count = component->count;
> -	size_t func_index;
> -	enum counter_function function;
> -
> -	err = counter->ops->function_get(counter, count, &func_index);
> -	if (err)
> -		return err;
> -
> -	count->function = func_index;
> -
> -	function = count->functions_list[func_index];
> -	return sprintf(buf, "%s\n", counter_function_str[function]);
> -}
> -
> -static ssize_t counter_function_store(struct device *dev,
> -				      struct device_attribute *attr,
> -				      const char *buf, size_t len)
> -{
> -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> -	const struct counter_count_unit *const component = devattr->component;
> -	struct counter_count *const count = component->count;
> -	const size_t num_functions = count->num_functions;
> -	size_t func_index;
> -	enum counter_function function;
> -	int err;
> -	struct counter_device *const counter = dev_get_drvdata(dev);
> -
> -	/* Find requested Count function mode */
> -	for (func_index = 0; func_index < num_functions; func_index++) {
> -		function = count->functions_list[func_index];
> -		if (sysfs_streq(buf, counter_function_str[function]))
> -			break;
> -	}
> -	/* Return error if requested Count function mode not found */
> -	if (func_index >= num_functions)
> -		return -EINVAL;
> -
> -	err = counter->ops->function_set(counter, count, func_index);
> -	if (err)
> -		return err;
> -
> -	count->function = func_index;
> -
> -	return len;
> -}
> -
> -struct counter_count_ext_unit {
> -	struct counter_count *count;
> -	const struct counter_count_ext *ext;
> -};
> -
> -static ssize_t counter_count_ext_show(struct device *dev,
> -				      struct device_attribute *attr, char *buf)
> -{
> -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> -	const struct counter_count_ext_unit *const comp = devattr->component;
> -	const struct counter_count_ext *const ext = comp->ext;
> -
> -	return ext->read(dev_get_drvdata(dev), comp->count, ext->priv, buf);
> -}
> -
> -static ssize_t counter_count_ext_store(struct device *dev,
> -				       struct device_attribute *attr,
> -				       const char *buf, size_t len)
> -{
> -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> -	const struct counter_count_ext_unit *const comp = devattr->component;
> -	const struct counter_count_ext *const ext = comp->ext;
> -
> -	return ext->write(dev_get_drvdata(dev), comp->count, ext->priv, buf,
> -		len);
> -}
> -
> -static int counter_count_ext_register(
> -	struct counter_device_attr_group *const group,
> -	struct counter_count *const count)
> -{
> -	size_t i;
> -	const struct counter_count_ext *ext;
> -	struct counter_count_ext_unit *count_ext_comp;
> -	struct counter_attr_parm parm;
> -	int err;
> -
> -	/* Create an attribute for each extension */
> -	for (i = 0 ; i < count->num_ext; i++) {
> -		ext = count->ext + i;
> -
> -		/* Allocate count_ext attribute component */
> -		count_ext_comp = kmalloc(sizeof(*count_ext_comp), GFP_KERNEL);
> -		if (!count_ext_comp) {
> -			err = -ENOMEM;
> -			goto err_free_attr_list;
> -		}
> -		count_ext_comp->count = count;
> -		count_ext_comp->ext = ext;
> -
> -		/* Allocate count_ext attribute */
> -		parm.group = group;
> -		parm.prefix = "";
> -		parm.name = ext->name;
> -		parm.show = (ext->read) ? counter_count_ext_show : NULL;
> -		parm.store = (ext->write) ? counter_count_ext_store : NULL;
> -		parm.component = count_ext_comp;
> -		err = counter_attribute_create(&parm);
> -		if (err) {
> -			kfree(count_ext_comp);
> -			goto err_free_attr_list;
> -		}
> -	}
> -
> -	return 0;
> -
> -err_free_attr_list:
> -	counter_device_attr_list_free(&group->attr_list);
> -	return err;
> -}
> -
> -struct counter_func_avail_unit {
> -	const enum counter_function *functions_list;
> -	size_t num_functions;
> -};
> -
> -static ssize_t counter_function_available_show(struct device *dev,
> -	struct device_attribute *attr, char *buf)
> -{
> -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> -	const struct counter_func_avail_unit *const component = devattr->component;
> -	const enum counter_function *const func_list = component->functions_list;
> -	const size_t num_functions = component->num_functions;
> -	size_t i;
> -	enum counter_function function;
> -	ssize_t len = 0;
> -
> -	for (i = 0; i < num_functions; i++) {
> -		function = func_list[i];
> -		len += sprintf(buf + len, "%s\n",
> -			       counter_function_str[function]);
> -	}
> -
> -	return len;
> -}
> -
> -static int counter_count_attributes_create(
> -	struct counter_device_attr_group *const group,
> -	const struct counter_device *const counter,
> -	struct counter_count *const count)
> -{
> -	struct counter_count_unit *count_comp;
> -	struct counter_attr_parm parm;
> -	int err;
> -	struct counter_count_unit *func_comp;
> -	struct counter_func_avail_unit *avail_comp;
> -
> -	/* Allocate count attribute component */
> -	count_comp = kmalloc(sizeof(*count_comp), GFP_KERNEL);
> -	if (!count_comp)
> -		return -ENOMEM;
> -	count_comp->count = count;
> -
> -	/* Create main Count attribute */
> -	parm.group = group;
> -	parm.prefix = "";
> -	parm.name = "count";
> -	parm.show = (counter->ops->count_read) ? counter_count_show : NULL;
> -	parm.store = (counter->ops->count_write) ? counter_count_store : NULL;
> -	parm.component = count_comp;
> -	err = counter_attribute_create(&parm);
> -	if (err) {
> -		kfree(count_comp);
> -		return err;
> -	}
> -
> -	/* Allocate function attribute component */
> -	func_comp = kmalloc(sizeof(*func_comp), GFP_KERNEL);
> -	if (!func_comp) {
> -		err = -ENOMEM;
> -		goto err_free_attr_list;
> -	}
> -	func_comp->count = count;
> -
> -	/* Create Count function attribute */
> -	parm.group = group;
> -	parm.prefix = "";
> -	parm.name = "function";
> -	parm.show = (counter->ops->function_get) ? counter_function_show : NULL;
> -	parm.store = (counter->ops->function_set) ? counter_function_store : NULL;
> -	parm.component = func_comp;
> -	err = counter_attribute_create(&parm);
> -	if (err) {
> -		kfree(func_comp);
> -		goto err_free_attr_list;
> -	}
> -
> -	/* Allocate function available attribute component */
> -	avail_comp = kmalloc(sizeof(*avail_comp), GFP_KERNEL);
> -	if (!avail_comp) {
> -		err = -ENOMEM;
> -		goto err_free_attr_list;
> -	}
> -	avail_comp->functions_list = count->functions_list;
> -	avail_comp->num_functions = count->num_functions;
> -
> -	/* Create Count function_available attribute */
> -	parm.group = group;
> -	parm.prefix = "";
> -	parm.name = "function_available";
> -	parm.show = counter_function_available_show;
> -	parm.store = NULL;
> -	parm.component = avail_comp;
> -	err = counter_attribute_create(&parm);
> -	if (err) {
> -		kfree(avail_comp);
> -		goto err_free_attr_list;
> -	}
> -
> -	/* Create Count name attribute */
> -	err = counter_name_attribute_create(group, count->name);
> -	if (err)
> -		goto err_free_attr_list;
> -
> -	/* Register Count extension attributes */
> -	err = counter_count_ext_register(group, count);
> -	if (err)
> -		goto err_free_attr_list;
> -
> -	return 0;
> -
> -err_free_attr_list:
> -	counter_device_attr_list_free(&group->attr_list);
> -	return err;
> -}
> -
> -static int counter_counts_register(
> -	struct counter_device_attr_group *const groups_list,
> -	const struct counter_device *const counter)
> -{
> -	size_t i;
> -	struct counter_count *count;
> -	const char *name;
> -	int err;
> -
> -	/* Register each Count */
> -	for (i = 0; i < counter->num_counts; i++) {
> -		count = counter->counts + i;
> -
> -		/* Generate Count attribute directory name */
> -		name = kasprintf(GFP_KERNEL, "count%d", count->id);
> -		if (!name) {
> -			err = -ENOMEM;
> -			goto err_free_attr_groups;
> -		}
> -		groups_list[i].attr_group.name = name;
> -
> -		/* Register the Synapses associated with each Count */
> -		err = counter_synapses_register(groups_list + i, counter, count,
> -						name);
> -		if (err)
> -			goto err_free_attr_groups;
> -
> -		/* Create all attributes associated with Count */
> -		err = counter_count_attributes_create(groups_list + i, counter,
> -						      count);
> -		if (err)
> -			goto err_free_attr_groups;
> -	}
> -
> -	return 0;
> -
> -err_free_attr_groups:
> -	do {
> -		kfree(groups_list[i].attr_group.name);
> -		counter_device_attr_list_free(&groups_list[i].attr_list);
> -	} while (i--);
> -	return err;
> -}
> -
> -struct counter_size_unit {
> -	size_t size;
> -};
> -
> -static ssize_t counter_device_attr_size_show(struct device *dev,
> -					     struct device_attribute *attr,
> -					     char *buf)
> -{
> -	const struct counter_size_unit *const comp = to_counter_attr(attr)->component;
> -
> -	return sprintf(buf, "%zu\n", comp->size);
> -}
> -
> -static int counter_size_attribute_create(
> -	struct counter_device_attr_group *const group,
> -	const size_t size, const char *const name)
> -{
> -	struct counter_size_unit *size_comp;
> -	struct counter_attr_parm parm;
> -	int err;
> -
> -	/* Allocate size attribute component */
> -	size_comp = kmalloc(sizeof(*size_comp), GFP_KERNEL);
> -	if (!size_comp)
> -		return -ENOMEM;
> -	size_comp->size = size;
> -
> -	parm.group = group;
> -	parm.prefix = "";
> -	parm.name = name;
> -	parm.show = counter_device_attr_size_show;
> -	parm.store = NULL;
> -	parm.component = size_comp;
> -	err = counter_attribute_create(&parm);
> -	if (err)
> -		goto err_free_size_comp;
> -
> -	return 0;
> -
> -err_free_size_comp:
> -	kfree(size_comp);
> -	return err;
> -}
> -
> -struct counter_ext_unit {
> -	const struct counter_device_ext *ext;
> -};
> -
> -static ssize_t counter_device_ext_show(struct device *dev,
> -				       struct device_attribute *attr, char *buf)
> -{
> -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> -	const struct counter_ext_unit *const component = devattr->component;
> -	const struct counter_device_ext *const ext = component->ext;
> -
> -	return ext->read(dev_get_drvdata(dev), ext->priv, buf);
> -}
> -
> -static ssize_t counter_device_ext_store(struct device *dev,
> -					struct device_attribute *attr,
> -					const char *buf, size_t len)
> -{
> -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> -	const struct counter_ext_unit *const component = devattr->component;
> -	const struct counter_device_ext *const ext = component->ext;
> -
> -	return ext->write(dev_get_drvdata(dev), ext->priv, buf, len);
> -}
> -
> -static int counter_device_ext_register(
> -	struct counter_device_attr_group *const group,
> -	struct counter_device *const counter)
> -{
> -	size_t i;
> -	struct counter_ext_unit *ext_comp;
> -	struct counter_attr_parm parm;
> -	int err;
> -
> -	/* Create an attribute for each extension */
> -	for (i = 0 ; i < counter->num_ext; i++) {
> -		/* Allocate extension attribute component */
> -		ext_comp = kmalloc(sizeof(*ext_comp), GFP_KERNEL);
> -		if (!ext_comp) {
> -			err = -ENOMEM;
> -			goto err_free_attr_list;
> -		}
> -
> -		ext_comp->ext = counter->ext + i;
> -
> -		/* Allocate extension attribute */
> -		parm.group = group;
> -		parm.prefix = "";
> -		parm.name = counter->ext[i].name;
> -		parm.show = (counter->ext[i].read) ? counter_device_ext_show : NULL;
> -		parm.store = (counter->ext[i].write) ? counter_device_ext_store : NULL;
> -		parm.component = ext_comp;
> -		err = counter_attribute_create(&parm);
> -		if (err) {
> -			kfree(ext_comp);
> -			goto err_free_attr_list;
> -		}
> -	}
> -
> -	return 0;
> -
> -err_free_attr_list:
> -	counter_device_attr_list_free(&group->attr_list);
> -	return err;
> -}
> -
> -static int counter_global_attr_register(
> -	struct counter_device_attr_group *const group,
> -	struct counter_device *const counter)
> -{
> -	int err;
> -
> -	/* Create name attribute */
> -	err = counter_name_attribute_create(group, counter->name);
> -	if (err)
> -		return err;
> -
> -	/* Create num_counts attribute */
> -	err = counter_size_attribute_create(group, counter->num_counts,
> -					    "num_counts");
> -	if (err)
> -		goto err_free_attr_list;
> -
> -	/* Create num_signals attribute */
> -	err = counter_size_attribute_create(group, counter->num_signals,
> -					    "num_signals");
> -	if (err)
> -		goto err_free_attr_list;
> -
> -	/* Register Counter device extension attributes */
> -	err = counter_device_ext_register(group, counter);
> -	if (err)
> -		goto err_free_attr_list;
> -
> -	return 0;
> -
> -err_free_attr_list:
> -	counter_device_attr_list_free(&group->attr_list);
> -	return err;
> -}
> -
> -static void counter_device_groups_list_free(
> -	struct counter_device_attr_group *const groups_list,
> -	const size_t num_groups)
> -{
> -	struct counter_device_attr_group *group;
> -	size_t i;
> -
> -	/* loop through all attribute groups (signals, counts, global, etc.) */
> -	for (i = 0; i < num_groups; i++) {
> -		group = groups_list + i;
> -
> -		/* free all attribute group and associated attributes memory */
> -		kfree(group->attr_group.name);
> -		kfree(group->attr_group.attrs);
> -		counter_device_attr_list_free(&group->attr_list);
> -	}
> -
> -	kfree(groups_list);
> -}
> -
> -static int counter_device_groups_list_prepare(
> -	struct counter_device *const counter)
> -{
> -	const size_t total_num_groups =
> -		counter->num_signals + counter->num_counts + 1;
> -	struct counter_device_attr_group *groups_list;
> -	size_t i;
> -	int err;
> -	size_t num_groups = 0;
> -
> -	/* Allocate space for attribute groups (signals, counts, and ext) */
> -	groups_list = kcalloc(total_num_groups, sizeof(*groups_list),
> -			      GFP_KERNEL);
> -	if (!groups_list)
> -		return -ENOMEM;
> -
> -	/* Initialize attribute lists */
> -	for (i = 0; i < total_num_groups; i++)
> -		INIT_LIST_HEAD(&groups_list[i].attr_list);
> -
> -	/* Register Signals */
> -	err = counter_signals_register(groups_list, counter);
> -	if (err)
> -		goto err_free_groups_list;
> -	num_groups += counter->num_signals;
> -
> -	/* Register Counts and respective Synapses */
> -	err = counter_counts_register(groups_list + num_groups, counter);
> -	if (err)
> -		goto err_free_groups_list;
> -	num_groups += counter->num_counts;
> -
> -	/* Register Counter global attributes */
> -	err = counter_global_attr_register(groups_list + num_groups, counter);
> -	if (err)
> -		goto err_free_groups_list;
> -	num_groups++;
> -
> -	/* Store groups_list in device_state */
> -	counter->device_state->groups_list = groups_list;
> -	counter->device_state->num_groups = num_groups;
> -
> -	return 0;
> -
> -err_free_groups_list:
> -	counter_device_groups_list_free(groups_list, num_groups);
> -	return err;
> -}
> -
> -static int counter_device_groups_prepare(
> -	struct counter_device_state *const device_state)
> -{
> -	size_t i, j;
> -	struct counter_device_attr_group *group;
> -	int err;
> -	struct counter_device_attr *p;
> -
> -	/* Allocate attribute groups for association with device */
> -	device_state->groups = kcalloc(device_state->num_groups + 1,
> -				       sizeof(*device_state->groups),
> -				       GFP_KERNEL);
> -	if (!device_state->groups)
> -		return -ENOMEM;
> -
> -	/* Prepare each group of attributes for association */
> -	for (i = 0; i < device_state->num_groups; i++) {
> -		group = device_state->groups_list + i;
> -
> -		/* Allocate space for attribute pointers in attribute group */
> -		group->attr_group.attrs = kcalloc(group->num_attr + 1,
> -			sizeof(*group->attr_group.attrs), GFP_KERNEL);
> -		if (!group->attr_group.attrs) {
> -			err = -ENOMEM;
> -			goto err_free_groups;
> -		}
> -
> -		/* Add attribute pointers to attribute group */
> -		j = 0;
> -		list_for_each_entry(p, &group->attr_list, l)
> -			group->attr_group.attrs[j++] = &p->dev_attr.attr;
> -
> -		/* Group attributes in attribute group */
> -		device_state->groups[i] = &group->attr_group;
> -	}
> -	/* Associate attributes with device */
> -	device_state->dev.groups = device_state->groups;
> -
> -	return 0;
> -
> -err_free_groups:
> -	do {
> -		group = device_state->groups_list + i;
> -		kfree(group->attr_group.attrs);
> -		group->attr_group.attrs = NULL;
> -	} while (i--);
> -	kfree(device_state->groups);
> -	return err;
> -}
> -
> -/* Provides a unique ID for each counter device */
> -static DEFINE_IDA(counter_ida);
> -
> -static void counter_device_release(struct device *dev)
> -{
> -	struct counter_device *const counter = dev_get_drvdata(dev);
> -	struct counter_device_state *const device_state = counter->device_state;
> -
> -	kfree(device_state->groups);
> -	counter_device_groups_list_free(device_state->groups_list,
> -					device_state->num_groups);
> -	ida_simple_remove(&counter_ida, device_state->id);
> -	kfree(device_state);
> -}
> -
> -static struct device_type counter_device_type = {
> -	.name = "counter_device",
> -	.release = counter_device_release
> -};
> -
> -static struct bus_type counter_bus_type = {
> -	.name = "counter"
> -};
> -
> -/**
> - * counter_register - register Counter to the system
> - * @counter:	pointer to Counter to register
> - *
> - * This function registers a Counter to the system. A sysfs "counter" directory
> - * will be created and populated with sysfs attributes correlating with the
> - * Counter Signals, Synapses, and Counts respectively.
> - */
> -int counter_register(struct counter_device *const counter)
> -{
> -	struct counter_device_state *device_state;
> -	int err;
> -
> -	/* Allocate internal state container for Counter device */
> -	device_state = kzalloc(sizeof(*device_state), GFP_KERNEL);
> -	if (!device_state)
> -		return -ENOMEM;
> -	counter->device_state = device_state;
> -
> -	/* Acquire unique ID */
> -	device_state->id = ida_simple_get(&counter_ida, 0, 0, GFP_KERNEL);
> -	if (device_state->id < 0) {
> -		err = device_state->id;
> -		goto err_free_device_state;
> -	}
> -
> -	/* Configure device structure for Counter */
> -	device_state->dev.type = &counter_device_type;
> -	device_state->dev.bus = &counter_bus_type;
> -	if (counter->parent) {
> -		device_state->dev.parent = counter->parent;
> -		device_state->dev.of_node = counter->parent->of_node;
> -	}
> -	dev_set_name(&device_state->dev, "counter%d", device_state->id);
> -	device_initialize(&device_state->dev);
> -	dev_set_drvdata(&device_state->dev, counter);
> -
> -	/* Prepare device attributes */
> -	err = counter_device_groups_list_prepare(counter);
> -	if (err)
> -		goto err_free_id;
> -
> -	/* Organize device attributes to groups and match to device */
> -	err = counter_device_groups_prepare(device_state);
> -	if (err)
> -		goto err_free_groups_list;
> -
> -	/* Add device to system */
> -	err = device_add(&device_state->dev);
> -	if (err)
> -		goto err_free_groups;
> -
> -	return 0;
> -
> -err_free_groups:
> -	kfree(device_state->groups);
> -err_free_groups_list:
> -	counter_device_groups_list_free(device_state->groups_list,
> -					device_state->num_groups);
> -err_free_id:
> -	ida_simple_remove(&counter_ida, device_state->id);
> -err_free_device_state:
> -	kfree(device_state);
> -	return err;
> -}
> -EXPORT_SYMBOL_GPL(counter_register);
> -
> -/**
> - * counter_unregister - unregister Counter from the system
> - * @counter:	pointer to Counter to unregister
> - *
> - * The Counter is unregistered from the system; all allocated memory is freed.
> - */
> -void counter_unregister(struct counter_device *const counter)
> -{
> -	if (counter)
> -		device_del(&counter->device_state->dev);
> -}
> -EXPORT_SYMBOL_GPL(counter_unregister);
> -
> -static void devm_counter_unreg(struct device *dev, void *res)
> -{
> -	counter_unregister(*(struct counter_device **)res);
> -}
> -
> -/**
> - * devm_counter_register - Resource-managed counter_register
> - * @dev:	device to allocate counter_device for
> - * @counter:	pointer to Counter to register
> - *
> - * Managed counter_register. The Counter registered with this function is
> - * automatically unregistered on driver detach. This function calls
> - * counter_register internally. Refer to that function for more information.
> - *
> - * If an Counter registered with this function needs to be unregistered
> - * separately, devm_counter_unregister must be used.
> - *
> - * RETURNS:
> - * 0 on success, negative error number on failure.
> - */
> -int devm_counter_register(struct device *dev,
> -			  struct counter_device *const counter)
> -{
> -	struct counter_device **ptr;
> -	int ret;
> -
> -	ptr = devres_alloc(devm_counter_unreg, sizeof(*ptr), GFP_KERNEL);
> -	if (!ptr)
> -		return -ENOMEM;
> -
> -	ret = counter_register(counter);
> -	if (!ret) {
> -		*ptr = counter;
> -		devres_add(dev, ptr);
> -	} else {
> -		devres_free(ptr);
> -	}
> -
> -	return ret;
> -}
> -EXPORT_SYMBOL_GPL(devm_counter_register);
> -
> -static int devm_counter_match(struct device *dev, void *res, void *data)
> -{
> -	struct counter_device **r = res;
> -
> -	if (!r || !*r) {
> -		WARN_ON(!r || !*r);
> -		return 0;
> -	}
> -
> -	return *r == data;
> -}
> -
> -/**
> - * devm_counter_unregister - Resource-managed counter_unregister
> - * @dev:	device this counter_device belongs to
> - * @counter:	pointer to Counter associated with the device
> - *
> - * Unregister Counter registered with devm_counter_register.
> - */
> -void devm_counter_unregister(struct device *dev,
> -			     struct counter_device *const counter)
> -{
> -	int rc;
> -
> -	rc = devres_release(dev, devm_counter_unreg, devm_counter_match,
> -			    counter);
> -	WARN_ON(rc);
> -}
> -EXPORT_SYMBOL_GPL(devm_counter_unregister);
> -
> -static int __init counter_init(void)
> -{
> -	return bus_register(&counter_bus_type);
> -}
> -
> -static void __exit counter_exit(void)
> -{
> -	bus_unregister(&counter_bus_type);
> -}
> -
> -subsys_initcall(counter_init);
> -module_exit(counter_exit);
> -
> -MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
> -MODULE_DESCRIPTION("Generic Counter interface");
> -MODULE_LICENSE("GPL v2");
> diff --git a/drivers/counter/ftm-quaddec.c b/drivers/counter/ftm-quaddec.c
> index 53c15f84909b..5ef0478709cd 100644
> --- a/drivers/counter/ftm-quaddec.c
> +++ b/drivers/counter/ftm-quaddec.c
> @@ -14,6 +14,7 @@
>  #include <linux/mutex.h>
>  #include <linux/counter.h>
>  #include <linux/bitfield.h>
> +#include <linux/types.h>
>  
>  #define FTM_FIELD_UPDATE(ftm, offset, mask, val)			\
>  	({								\
> @@ -115,8 +116,7 @@ static void ftm_quaddec_disable(void *ftm)
>  }
>  
>  static int ftm_quaddec_get_prescaler(struct counter_device *counter,
> -				     struct counter_count *count,
> -				     size_t *cnt_mode)
> +				     struct counter_count *count, u32 *cnt_mode)
>  {
>  	struct ftm_quaddec *ftm = counter->priv;
>  	uint32_t scflags;
> @@ -129,8 +129,7 @@ static int ftm_quaddec_get_prescaler(struct counter_device *counter,
>  }
>  
>  static int ftm_quaddec_set_prescaler(struct counter_device *counter,
> -				     struct counter_count *count,
> -				     size_t cnt_mode)
> +				     struct counter_count *count, u32 cnt_mode)
>  {
>  	struct ftm_quaddec *ftm = counter->priv;
>  
> @@ -151,33 +150,17 @@ static const char * const ftm_quaddec_prescaler[] = {
>  	"1", "2", "4", "8", "16", "32", "64", "128"
>  };
>  
> -static struct counter_count_enum_ext ftm_quaddec_prescaler_enum = {
> -	.items = ftm_quaddec_prescaler,
> -	.num_items = ARRAY_SIZE(ftm_quaddec_prescaler),
> -	.get = ftm_quaddec_get_prescaler,
> -	.set = ftm_quaddec_set_prescaler
> -};
> -
> -enum ftm_quaddec_synapse_action {
> -	FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES,
> -};
> -
>  static const enum counter_synapse_action ftm_quaddec_synapse_actions[] = {
> -	[FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES] =
>  	COUNTER_SYNAPSE_ACTION_BOTH_EDGES
>  };
>  
> -enum ftm_quaddec_count_function {
> -	FTM_QUADDEC_COUNT_ENCODER_MODE_1,
> -};
> -
>  static const enum counter_function ftm_quaddec_count_functions[] = {
> -	[FTM_QUADDEC_COUNT_ENCODER_MODE_1] = COUNTER_FUNCTION_QUADRATURE_X4
> +	COUNTER_FUNCTION_QUADRATURE_X4
>  };
>  
>  static int ftm_quaddec_count_read(struct counter_device *counter,
>  				  struct counter_count *count,
> -				  unsigned long *val)
> +				  u64 *val)
>  {
>  	struct ftm_quaddec *const ftm = counter->priv;
>  	uint32_t cntval;
> @@ -191,7 +174,7 @@ static int ftm_quaddec_count_read(struct counter_device *counter,
>  
>  static int ftm_quaddec_count_write(struct counter_device *counter,
>  				   struct counter_count *count,
> -				   const unsigned long val)
> +				   const u64 val)
>  {
>  	struct ftm_quaddec *const ftm = counter->priv;
>  
> @@ -205,21 +188,21 @@ static int ftm_quaddec_count_write(struct counter_device *counter,
>  	return 0;
>  }
>  
> -static int ftm_quaddec_count_function_get(struct counter_device *counter,
> -					  struct counter_count *count,
> -					  size_t *function)
> +static int ftm_quaddec_count_function_read(struct counter_device *counter,
> +					   struct counter_count *count,
> +					   enum counter_function *function)
>  {
> -	*function = FTM_QUADDEC_COUNT_ENCODER_MODE_1;
> +	*function = COUNTER_FUNCTION_QUADRATURE_X4;
>  
>  	return 0;
>  }
>  
> -static int ftm_quaddec_action_get(struct counter_device *counter,
> -				  struct counter_count *count,
> -				  struct counter_synapse *synapse,
> -				  size_t *action)
> +static int ftm_quaddec_action_read(struct counter_device *counter,
> +				   struct counter_count *count,
> +				   struct counter_synapse *synapse,
> +				   enum counter_synapse_action *action)
>  {
> -	*action = FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES;
> +	*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
>  
>  	return 0;
>  }
> @@ -227,8 +210,8 @@ static int ftm_quaddec_action_get(struct counter_device *counter,
>  static const struct counter_ops ftm_quaddec_cnt_ops = {
>  	.count_read = ftm_quaddec_count_read,
>  	.count_write = ftm_quaddec_count_write,
> -	.function_get = ftm_quaddec_count_function_get,
> -	.action_get = ftm_quaddec_action_get,
> +	.function_read = ftm_quaddec_count_function_read,
> +	.action_read = ftm_quaddec_action_read,
>  };
>  
>  static struct counter_signal ftm_quaddec_signals[] = {
> @@ -255,9 +238,12 @@ static struct counter_synapse ftm_quaddec_count_synapses[] = {
>  	}
>  };
>  
> -static const struct counter_count_ext ftm_quaddec_count_ext[] = {
> -	COUNTER_COUNT_ENUM("prescaler", &ftm_quaddec_prescaler_enum),
> -	COUNTER_COUNT_ENUM_AVAILABLE("prescaler", &ftm_quaddec_prescaler_enum),
> +static DEFINE_COUNTER_ENUM(ftm_quaddec_prescaler_enum, ftm_quaddec_prescaler);
> +
> +static struct counter_comp ftm_quaddec_count_ext[] = {
> +	COUNTER_COMP_COUNT_ENUM("prescaler", ftm_quaddec_get_prescaler,
> +				ftm_quaddec_set_prescaler,
> +				ftm_quaddec_prescaler_enum),
>  };
>  
>  static struct counter_count ftm_quaddec_counts = {
> diff --git a/drivers/counter/intel-qep.c b/drivers/counter/intel-qep.c
> index 8a6847d5fb2b..696f68bc93bc 100644
> --- a/drivers/counter/intel-qep.c
> +++ b/drivers/counter/intel-qep.c
> @@ -62,13 +62,6 @@
>  
>  #define INTEL_QEP_CLK_PERIOD_NS		10
>  
> -#define INTEL_QEP_COUNTER_EXT_RW(_name)				\
> -{								\
> -	.name = #_name,						\
> -	.read = _name##_read,					\
> -	.write = _name##_write,					\
> -}
> -
>  struct intel_qep {
>  	struct counter_device counter;
>  	struct mutex lock;
> @@ -114,8 +107,7 @@ static void intel_qep_init(struct intel_qep *qep)
>  }
>  
>  static int intel_qep_count_read(struct counter_device *counter,
> -				struct counter_count *count,
> -				unsigned long *val)
> +				struct counter_count *count, u64 *val)
>  {
>  	struct intel_qep *const qep = counter->priv;
>  
> @@ -130,11 +122,11 @@ static const enum counter_function intel_qep_count_functions[] = {
>  	COUNTER_FUNCTION_QUADRATURE_X4,
>  };
>  
> -static int intel_qep_function_get(struct counter_device *counter,
> -				  struct counter_count *count,
> -				  size_t *function)
> +static int intel_qep_function_read(struct counter_device *counter,
> +				   struct counter_count *count,
> +				   enum counter_function *function)
>  {
> -	*function = 0;
> +	*function = COUNTER_FUNCTION_QUADRATURE_X4;
>  
>  	return 0;
>  }
> @@ -143,19 +135,19 @@ static const enum counter_synapse_action intel_qep_synapse_actions[] = {
>  	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
>  };
>  
> -static int intel_qep_action_get(struct counter_device *counter,
> -				struct counter_count *count,
> -				struct counter_synapse *synapse,
> -				size_t *action)
> +static int intel_qep_action_read(struct counter_device *counter,
> +				 struct counter_count *count,
> +				 struct counter_synapse *synapse,
> +				 enum counter_synapse_action *action)
>  {
> -	*action = 0;
> +	*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
>  	return 0;
>  }
>  
>  static const struct counter_ops intel_qep_counter_ops = {
>  	.count_read = intel_qep_count_read,
> -	.function_get = intel_qep_function_get,
> -	.action_get = intel_qep_action_get,
> +	.function_read = intel_qep_function_read,
> +	.action_read = intel_qep_action_read,
>  };
>  
>  #define INTEL_QEP_SIGNAL(_id, _name) {				\
> @@ -181,31 +173,27 @@ static struct counter_synapse intel_qep_count_synapses[] = {
>  	INTEL_QEP_SYNAPSE(2),
>  };
>  
> -static ssize_t ceiling_read(struct counter_device *counter,
> -			    struct counter_count *count,
> -			    void *priv, char *buf)
> +static int intel_qep_ceiling_read(struct counter_device *counter,
> +				  struct counter_count *count, u64 *ceiling)
>  {
>  	struct intel_qep *qep = counter->priv;
> -	u32 reg;
>  
>  	pm_runtime_get_sync(qep->dev);
> -	reg = intel_qep_readl(qep, INTEL_QEPMAX);
> +	*ceiling = intel_qep_readl(qep, INTEL_QEPMAX);
>  	pm_runtime_put(qep->dev);
>  
> -	return sysfs_emit(buf, "%u\n", reg);
> +	return 0;
>  }
>  
> -static ssize_t ceiling_write(struct counter_device *counter,
> -			     struct counter_count *count,
> -			     void *priv, const char *buf, size_t len)
> +static int intel_qep_ceiling_write(struct counter_device *counter,
> +				   struct counter_count *count, u64 max)
>  {
>  	struct intel_qep *qep = counter->priv;
> -	u32 max;
> -	int ret;
> +	int ret = 0;
>  
> -	ret = kstrtou32(buf, 0, &max);
> -	if (ret < 0)
> -		return ret;
> +	/* Intel QEP ceiling configuration only supports 32-bit values */
> +	if (max != (u32)max)
> +		return -ERANGE;
>  
>  	mutex_lock(&qep->lock);
>  	if (qep->enabled) {
> @@ -216,34 +204,28 @@ static ssize_t ceiling_write(struct counter_device *counter,
>  	pm_runtime_get_sync(qep->dev);
>  	intel_qep_writel(qep, INTEL_QEPMAX, max);
>  	pm_runtime_put(qep->dev);
> -	ret = len;
>  
>  out:
>  	mutex_unlock(&qep->lock);
>  	return ret;
>  }
>  
> -static ssize_t enable_read(struct counter_device *counter,
> -			   struct counter_count *count,
> -			   void *priv, char *buf)
> +static int intel_qep_enable_read(struct counter_device *counter,
> +				 struct counter_count *count, u8 *enable)
>  {
>  	struct intel_qep *qep = counter->priv;
>  
> -	return sysfs_emit(buf, "%u\n", qep->enabled);
> +	*enable = qep->enabled;
> +
> +	return 0;
>  }
>  
> -static ssize_t enable_write(struct counter_device *counter,
> -			    struct counter_count *count,
> -			    void *priv, const char *buf, size_t len)
> +static int intel_qep_enable_write(struct counter_device *counter,
> +				  struct counter_count *count, u8 val)
>  {
>  	struct intel_qep *qep = counter->priv;
>  	u32 reg;
> -	bool val, changed;
> -	int ret;
> -
> -	ret = kstrtobool(buf, &val);
> -	if (ret)
> -		return ret;
> +	bool changed;
>  
>  	mutex_lock(&qep->lock);
>  	changed = val ^ qep->enabled;
> @@ -267,12 +249,12 @@ static ssize_t enable_write(struct counter_device *counter,
>  
>  out:
>  	mutex_unlock(&qep->lock);
> -	return len;
> +	return 0;
>  }
>  
> -static ssize_t spike_filter_ns_read(struct counter_device *counter,
> -				    struct counter_count *count,
> -				    void *priv, char *buf)
> +static int intel_qep_spike_filter_ns_read(struct counter_device *counter,
> +					  struct counter_count *count,
> +					  u64 *length)
>  {
>  	struct intel_qep *qep = counter->priv;
>  	u32 reg;
> @@ -281,26 +263,24 @@ static ssize_t spike_filter_ns_read(struct counter_device *counter,
>  	reg = intel_qep_readl(qep, INTEL_QEPCON);
>  	if (!(reg & INTEL_QEPCON_FLT_EN)) {
>  		pm_runtime_put(qep->dev);
> -		return sysfs_emit(buf, "0\n");
> +		return 0;
>  	}
>  	reg = INTEL_QEPFLT_MAX_COUNT(intel_qep_readl(qep, INTEL_QEPFLT));
>  	pm_runtime_put(qep->dev);
>  
> -	return sysfs_emit(buf, "%u\n", (reg + 2) * INTEL_QEP_CLK_PERIOD_NS);
> +	*length = (reg + 2) * INTEL_QEP_CLK_PERIOD_NS;
> +
> +	return 0;
>  }
>  
> -static ssize_t spike_filter_ns_write(struct counter_device *counter,
> -				     struct counter_count *count,
> -				     void *priv, const char *buf, size_t len)
> +static int intel_qep_spike_filter_ns_write(struct counter_device *counter,
> +					   struct counter_count *count,
> +					   u64 length)
>  {
>  	struct intel_qep *qep = counter->priv;
> -	u32 reg, length;
> +	u32 reg;
>  	bool enable;
> -	int ret;
> -
> -	ret = kstrtou32(buf, 0, &length);
> -	if (ret < 0)
> -		return ret;
> +	int ret = 0;
>  
>  	/*
>  	 * Spike filter length is (MAX_COUNT + 2) clock periods.
> @@ -336,16 +316,15 @@ static ssize_t spike_filter_ns_write(struct counter_device *counter,
>  	intel_qep_writel(qep, INTEL_QEPFLT, length);
>  	intel_qep_writel(qep, INTEL_QEPCON, reg);
>  	pm_runtime_put(qep->dev);
> -	ret = len;
>  
>  out:
>  	mutex_unlock(&qep->lock);
>  	return ret;
>  }
>  
> -static ssize_t preset_enable_read(struct counter_device *counter,
> -				  struct counter_count *count,
> -				  void *priv, char *buf)
> +static int intel_qep_preset_enable_read(struct counter_device *counter,
> +					struct counter_count *count,
> +					u8 *preset_enable)
>  {
>  	struct intel_qep *qep = counter->priv;
>  	u32 reg;
> @@ -353,21 +332,18 @@ static ssize_t preset_enable_read(struct counter_device *counter,
>  	pm_runtime_get_sync(qep->dev);
>  	reg = intel_qep_readl(qep, INTEL_QEPCON);
>  	pm_runtime_put(qep->dev);
> -	return sysfs_emit(buf, "%u\n", !(reg & INTEL_QEPCON_COUNT_RST_MODE));
> +
> +	*preset_enable = !(reg & INTEL_QEPCON_COUNT_RST_MODE);
> +
> +	return 0;
>  }
>  
> -static ssize_t preset_enable_write(struct counter_device *counter,
> -				   struct counter_count *count,
> -				   void *priv, const char *buf, size_t len)
> +static int intel_qep_preset_enable_write(struct counter_device *counter,
> +					 struct counter_count *count, u8 val)
>  {
>  	struct intel_qep *qep = counter->priv;
>  	u32 reg;
> -	bool val;
> -	int ret;
> -
> -	ret = kstrtobool(buf, &val);
> -	if (ret)
> -		return ret;
> +	int ret = 0;
>  
>  	mutex_lock(&qep->lock);
>  	if (qep->enabled) {
> @@ -384,7 +360,6 @@ static ssize_t preset_enable_write(struct counter_device *counter,
>  
>  	intel_qep_writel(qep, INTEL_QEPCON, reg);
>  	pm_runtime_put(qep->dev);
> -	ret = len;
>  
>  out:
>  	mutex_unlock(&qep->lock);
> @@ -392,11 +367,14 @@ static ssize_t preset_enable_write(struct counter_device *counter,
>  	return ret;
>  }
>  
> -static const struct counter_count_ext intel_qep_count_ext[] = {
> -	INTEL_QEP_COUNTER_EXT_RW(ceiling),
> -	INTEL_QEP_COUNTER_EXT_RW(enable),
> -	INTEL_QEP_COUNTER_EXT_RW(spike_filter_ns),
> -	INTEL_QEP_COUNTER_EXT_RW(preset_enable)
> +static struct counter_comp intel_qep_count_ext[] = {
> +	COUNTER_COMP_ENABLE(intel_qep_enable_read, intel_qep_enable_write),
> +	COUNTER_COMP_CEILING(intel_qep_ceiling_read, intel_qep_ceiling_write),
> +	COUNTER_COMP_PRESET_ENABLE(intel_qep_preset_enable_read,
> +				   intel_qep_preset_enable_write),
> +	COUNTER_COMP_COUNT_U64("spike_filter_ns",
> +			       intel_qep_spike_filter_ns_read,
> +			       intel_qep_spike_filter_ns_write),
>  };
>  
>  static struct counter_count intel_qep_counter_count[] = {
> diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c
> index 1de4243db488..8514a87fcbee 100644
> --- a/drivers/counter/interrupt-cnt.c
> +++ b/drivers/counter/interrupt-cnt.c
> @@ -10,6 +10,7 @@
>  #include <linux/mod_devicetable.h>
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
> +#include <linux/types.h>
>  
>  #define INTERRUPT_CNT_NAME "interrupt-cnt"
>  
> @@ -33,30 +34,23 @@ static irqreturn_t interrupt_cnt_isr(int irq, void *dev_id)
>  	return IRQ_HANDLED;
>  }
>  
> -static ssize_t interrupt_cnt_enable_read(struct counter_device *counter,
> -					 struct counter_count *count,
> -					 void *private, char *buf)
> +static int interrupt_cnt_enable_read(struct counter_device *counter,
> +				     struct counter_count *count, u8 *enable)
>  {
>  	struct interrupt_cnt_priv *priv = counter->priv;
>  
> -	return sysfs_emit(buf, "%d\n", priv->enabled);
> +	*enable = priv->enabled;
> +
> +	return 0;
>  }
>  
> -static ssize_t interrupt_cnt_enable_write(struct counter_device *counter,
> -					  struct counter_count *count,
> -					  void *private, const char *buf,
> -					  size_t len)
> +static int interrupt_cnt_enable_write(struct counter_device *counter,
> +				      struct counter_count *count, u8 enable)
>  {
>  	struct interrupt_cnt_priv *priv = counter->priv;
> -	bool enable;
> -	ssize_t ret;
> -
> -	ret = kstrtobool(buf, &enable);
> -	if (ret)
> -		return ret;
>  
>  	if (priv->enabled == enable)
> -		return len;
> +		return 0;
>  
>  	if (enable) {
>  		priv->enabled = true;
> @@ -66,33 +60,30 @@ static ssize_t interrupt_cnt_enable_write(struct counter_device *counter,
>  		priv->enabled = false;
>  	}
>  
> -	return len;
> +	return 0;
>  }
>  
> -static const struct counter_count_ext interrupt_cnt_ext[] = {
> -	{
> -		.name = "enable",
> -		.read = interrupt_cnt_enable_read,
> -		.write = interrupt_cnt_enable_write,
> -	},
> +static struct counter_comp interrupt_cnt_ext[] = {
> +	COUNTER_COMP_ENABLE(interrupt_cnt_enable_read,
> +			    interrupt_cnt_enable_write),
>  };
>  
>  static const enum counter_synapse_action interrupt_cnt_synapse_actions[] = {
>  	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
>  };
>  
> -static int interrupt_cnt_action_get(struct counter_device *counter,
> -				    struct counter_count *count,
> -				    struct counter_synapse *synapse,
> -				    size_t *action)
> +static int interrupt_cnt_action_read(struct counter_device *counter,
> +				     struct counter_count *count,
> +				     struct counter_synapse *synapse,
> +				     enum counter_synapse_action *action)
>  {
> -	*action = 0;
> +	*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
>  
>  	return 0;
>  }
>  
>  static int interrupt_cnt_read(struct counter_device *counter,
> -			      struct counter_count *count, unsigned long *val)
> +			      struct counter_count *count, u64 *val)
>  {
>  	struct interrupt_cnt_priv *priv = counter->priv;
>  
> @@ -102,8 +93,7 @@ static int interrupt_cnt_read(struct counter_device *counter,
>  }
>  
>  static int interrupt_cnt_write(struct counter_device *counter,
> -			       struct counter_count *count,
> -			       const unsigned long val)
> +			       struct counter_count *count, const u64 val)
>  {
>  	struct interrupt_cnt_priv *priv = counter->priv;
>  
> @@ -119,11 +109,11 @@ static const enum counter_function interrupt_cnt_functions[] = {
>  	COUNTER_FUNCTION_INCREASE,
>  };
>  
> -static int interrupt_cnt_function_get(struct counter_device *counter,
> -				      struct counter_count *count,
> -				      size_t *function)
> +static int interrupt_cnt_function_read(struct counter_device *counter,
> +				       struct counter_count *count,
> +				       enum counter_function *function)
>  {
> -	*function = 0;
> +	*function = COUNTER_FUNCTION_INCREASE;
>  
>  	return 0;
>  }
> @@ -148,10 +138,10 @@ static int interrupt_cnt_signal_read(struct counter_device *counter,
>  }
>  
>  static const struct counter_ops interrupt_cnt_ops = {
> -	.action_get = interrupt_cnt_action_get,
> +	.action_read = interrupt_cnt_action_read,
>  	.count_read = interrupt_cnt_read,
>  	.count_write = interrupt_cnt_write,
> -	.function_get = interrupt_cnt_function_get,
> +	.function_read = interrupt_cnt_function_read,
>  	.signal_read  = interrupt_cnt_signal_read,
>  };
>  
> diff --git a/drivers/counter/microchip-tcb-capture.c b/drivers/counter/microchip-tcb-capture.c
> index 1aa70b9c4833..79e0c84a3b81 100644
> --- a/drivers/counter/microchip-tcb-capture.c
> +++ b/drivers/counter/microchip-tcb-capture.c
> @@ -32,28 +32,16 @@ struct mchp_tc_data {
>  	bool trig_inverted;
>  };
>  
> -enum mchp_tc_count_function {
> -	MCHP_TC_FUNCTION_INCREASE,
> -	MCHP_TC_FUNCTION_QUADRATURE,
> -};
> -
>  static const enum counter_function mchp_tc_count_functions[] = {
> -	[MCHP_TC_FUNCTION_INCREASE] = COUNTER_FUNCTION_INCREASE,
> -	[MCHP_TC_FUNCTION_QUADRATURE] = COUNTER_FUNCTION_QUADRATURE_X4,
> -};
> -
> -enum mchp_tc_synapse_action {
> -	MCHP_TC_SYNAPSE_ACTION_NONE = 0,
> -	MCHP_TC_SYNAPSE_ACTION_RISING_EDGE,
> -	MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE,
> -	MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE
> +	COUNTER_FUNCTION_INCREASE,
> +	COUNTER_FUNCTION_QUADRATURE_X4,
>  };
>  
>  static const enum counter_synapse_action mchp_tc_synapse_actions[] = {
> -	[MCHP_TC_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
> -	[MCHP_TC_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> -	[MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
> -	[MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
> +	COUNTER_SYNAPSE_ACTION_NONE,
> +	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> +	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
> +	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
>  };
>  
>  static struct counter_signal mchp_tc_count_signals[] = {
> @@ -80,23 +68,23 @@ static struct counter_synapse mchp_tc_count_synapses[] = {
>  	}
>  };
>  
> -static int mchp_tc_count_function_get(struct counter_device *counter,
> -				      struct counter_count *count,
> -				      size_t *function)
> +static int mchp_tc_count_function_read(struct counter_device *counter,
> +				       struct counter_count *count,
> +				       enum counter_function *function)
>  {
>  	struct mchp_tc_data *const priv = counter->priv;
>  
>  	if (priv->qdec_mode)
> -		*function = MCHP_TC_FUNCTION_QUADRATURE;
> +		*function = COUNTER_FUNCTION_QUADRATURE_X4;
>  	else
> -		*function = MCHP_TC_FUNCTION_INCREASE;
> +		*function = COUNTER_FUNCTION_INCREASE;
>  
>  	return 0;
>  }
>  
> -static int mchp_tc_count_function_set(struct counter_device *counter,
> -				      struct counter_count *count,
> -				      size_t function)
> +static int mchp_tc_count_function_write(struct counter_device *counter,
> +					struct counter_count *count,
> +					enum counter_function function)
>  {
>  	struct mchp_tc_data *const priv = counter->priv;
>  	u32 bmr, cmr;
> @@ -108,7 +96,7 @@ static int mchp_tc_count_function_set(struct counter_device *counter,
>  	cmr &= ~ATMEL_TC_WAVE;
>  
>  	switch (function) {
> -	case MCHP_TC_FUNCTION_INCREASE:
> +	case COUNTER_FUNCTION_INCREASE:
>  		priv->qdec_mode = 0;
>  		/* Set highest rate based on whether soc has gclk or not */
>  		bmr &= ~(ATMEL_TC_QDEN | ATMEL_TC_POSEN);
> @@ -120,7 +108,7 @@ static int mchp_tc_count_function_set(struct counter_device *counter,
>  		cmr |=  ATMEL_TC_CMR_MASK;
>  		cmr &= ~(ATMEL_TC_ABETRG | ATMEL_TC_XC0);
>  		break;
> -	case MCHP_TC_FUNCTION_QUADRATURE:
> +	case COUNTER_FUNCTION_QUADRATURE_X4:
>  		if (!priv->tc_cfg->has_qdec)
>  			return -EINVAL;
>  		/* In QDEC mode settings both channels 0 and 1 are required */
> @@ -176,10 +164,10 @@ static int mchp_tc_count_signal_read(struct counter_device *counter,
>  	return 0;
>  }
>  
> -static int mchp_tc_count_action_get(struct counter_device *counter,
> -				    struct counter_count *count,
> -				    struct counter_synapse *synapse,
> -				    size_t *action)
> +static int mchp_tc_count_action_read(struct counter_device *counter,
> +				     struct counter_count *count,
> +				     struct counter_synapse *synapse,
> +				     enum counter_synapse_action *action)
>  {
>  	struct mchp_tc_data *const priv = counter->priv;
>  	u32 cmr;
> @@ -188,26 +176,26 @@ static int mchp_tc_count_action_get(struct counter_device *counter,
>  
>  	switch (cmr & ATMEL_TC_ETRGEDG) {
>  	default:
> -		*action = MCHP_TC_SYNAPSE_ACTION_NONE;
> +		*action = COUNTER_SYNAPSE_ACTION_NONE;
>  		break;
>  	case ATMEL_TC_ETRGEDG_RISING:
> -		*action = MCHP_TC_SYNAPSE_ACTION_RISING_EDGE;
> +		*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
>  		break;
>  	case ATMEL_TC_ETRGEDG_FALLING:
> -		*action = MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE;
> +		*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
>  		break;
>  	case ATMEL_TC_ETRGEDG_BOTH:
> -		*action = MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE;
> +		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
>  		break;
>  	}
>  
>  	return 0;
>  }
>  
> -static int mchp_tc_count_action_set(struct counter_device *counter,
> -				    struct counter_count *count,
> -				    struct counter_synapse *synapse,
> -				    size_t action)
> +static int mchp_tc_count_action_write(struct counter_device *counter,
> +				      struct counter_count *count,
> +				      struct counter_synapse *synapse,
> +				      enum counter_synapse_action action)
>  {
>  	struct mchp_tc_data *const priv = counter->priv;
>  	u32 edge = ATMEL_TC_ETRGEDG_NONE;
> @@ -217,16 +205,16 @@ static int mchp_tc_count_action_set(struct counter_device *counter,
>  		return -EINVAL;
>  
>  	switch (action) {
> -	case MCHP_TC_SYNAPSE_ACTION_NONE:
> +	case COUNTER_SYNAPSE_ACTION_NONE:
>  		edge = ATMEL_TC_ETRGEDG_NONE;
>  		break;
> -	case MCHP_TC_SYNAPSE_ACTION_RISING_EDGE:
> +	case COUNTER_SYNAPSE_ACTION_RISING_EDGE:
>  		edge = ATMEL_TC_ETRGEDG_RISING;
>  		break;
> -	case MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE:
> +	case COUNTER_SYNAPSE_ACTION_FALLING_EDGE:
>  		edge = ATMEL_TC_ETRGEDG_FALLING;
>  		break;
> -	case MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE:
> +	case COUNTER_SYNAPSE_ACTION_BOTH_EDGES:
>  		edge = ATMEL_TC_ETRGEDG_BOTH;
>  		break;
>  	default:
> @@ -240,8 +228,7 @@ static int mchp_tc_count_action_set(struct counter_device *counter,
>  }
>  
>  static int mchp_tc_count_read(struct counter_device *counter,
> -			      struct counter_count *count,
> -			      unsigned long *val)
> +			      struct counter_count *count, u64 *val)
>  {
>  	struct mchp_tc_data *const priv = counter->priv;
>  	u32 cnt;
> @@ -264,12 +251,12 @@ static struct counter_count mchp_tc_counts[] = {
>  };
>  
>  static const struct counter_ops mchp_tc_ops = {
> -	.signal_read  = mchp_tc_count_signal_read,
> -	.count_read   = mchp_tc_count_read,
> -	.function_get = mchp_tc_count_function_get,
> -	.function_set = mchp_tc_count_function_set,
> -	.action_get   = mchp_tc_count_action_get,
> -	.action_set   = mchp_tc_count_action_set
> +	.signal_read    = mchp_tc_count_signal_read,
> +	.count_read     = mchp_tc_count_read,
> +	.function_read  = mchp_tc_count_function_read,
> +	.function_write = mchp_tc_count_function_write,
> +	.action_read    = mchp_tc_count_action_read,
> +	.action_write   = mchp_tc_count_action_write
>  };
>  
>  static const struct atmel_tcb_config tcb_rm9200_config = {
> diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c
> index 7367f46c6f91..5168833b1fdf 100644
> --- a/drivers/counter/stm32-lptimer-cnt.c
> +++ b/drivers/counter/stm32-lptimer-cnt.c
> @@ -17,6 +17,7 @@
>  #include <linux/module.h>
>  #include <linux/pinctrl/consumer.h>
>  #include <linux/platform_device.h>
> +#include <linux/types.h>
>  
>  struct stm32_lptim_cnt {
>  	struct counter_device counter;
> @@ -107,11 +108,7 @@ static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
>  	return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
>  }
>  
> -/**
> - * enum stm32_lptim_cnt_function - enumerates LPTimer counter & encoder modes
> - * @STM32_LPTIM_COUNTER_INCREASE: up count on IN1 rising, falling or both edges
> - * @STM32_LPTIM_ENCODER_BOTH_EDGE: count on both edges (IN1 & IN2 quadrature)
> - *
> +/*
>   * In non-quadrature mode, device counts up on active edge.
>   * In quadrature mode, encoder counting scenarios are as follows:
>   * +---------+----------+--------------------+--------------------+
> @@ -129,33 +126,20 @@ static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
>   * | edges   | Low  ->  |   Up     |   Down  |   Down   |   Up    |
>   * +---------+----------+----------+---------+----------+---------+
>   */
> -enum stm32_lptim_cnt_function {
> -	STM32_LPTIM_COUNTER_INCREASE,
> -	STM32_LPTIM_ENCODER_BOTH_EDGE,
> -};
> -
>  static const enum counter_function stm32_lptim_cnt_functions[] = {
> -	[STM32_LPTIM_COUNTER_INCREASE] = COUNTER_FUNCTION_INCREASE,
> -	[STM32_LPTIM_ENCODER_BOTH_EDGE] = COUNTER_FUNCTION_QUADRATURE_X4,
> -};
> -
> -enum stm32_lptim_synapse_action {
> -	STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE = STM32_LPTIM_CKPOL_RISING_EDGE,
> -	STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE = STM32_LPTIM_CKPOL_FALLING_EDGE,
> -	STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES = STM32_LPTIM_CKPOL_BOTH_EDGES,
> -	STM32_LPTIM_SYNAPSE_ACTION_NONE,
> +	COUNTER_FUNCTION_INCREASE,
> +	COUNTER_FUNCTION_QUADRATURE_X4,
>  };
>  
>  static const enum counter_synapse_action stm32_lptim_cnt_synapse_actions[] = {
> -	/* Index must match with stm32_lptim_cnt_polarity[] (priv->polarity) */
> -	[STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> -	[STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
> -	[STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
> -	[STM32_LPTIM_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
> +	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> +	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
> +	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
> +	COUNTER_SYNAPSE_ACTION_NONE,
>  };
>  
>  static int stm32_lptim_cnt_read(struct counter_device *counter,
> -				struct counter_count *count, unsigned long *val)
> +				struct counter_count *count, u64 *val)
>  {
>  	struct stm32_lptim_cnt *const priv = counter->priv;
>  	u32 cnt;
> @@ -170,28 +154,28 @@ static int stm32_lptim_cnt_read(struct counter_device *counter,
>  	return 0;
>  }
>  
> -static int stm32_lptim_cnt_function_get(struct counter_device *counter,
> -					struct counter_count *count,
> -					size_t *function)
> +static int stm32_lptim_cnt_function_read(struct counter_device *counter,
> +					 struct counter_count *count,
> +					 enum counter_function *function)
>  {
>  	struct stm32_lptim_cnt *const priv = counter->priv;
>  
>  	if (!priv->quadrature_mode) {
> -		*function = STM32_LPTIM_COUNTER_INCREASE;
> +		*function = COUNTER_FUNCTION_INCREASE;
>  		return 0;
>  	}
>  
> -	if (priv->polarity == STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES) {
> -		*function = STM32_LPTIM_ENCODER_BOTH_EDGE;
> +	if (priv->polarity == STM32_LPTIM_CKPOL_BOTH_EDGES) {
> +		*function = COUNTER_FUNCTION_QUADRATURE_X4;
>  		return 0;
>  	}
>  
>  	return -EINVAL;
>  }
>  
> -static int stm32_lptim_cnt_function_set(struct counter_device *counter,
> -					struct counter_count *count,
> -					size_t function)
> +static int stm32_lptim_cnt_function_write(struct counter_device *counter,
> +					  struct counter_count *count,
> +					  enum counter_function function)
>  {
>  	struct stm32_lptim_cnt *const priv = counter->priv;
>  
> @@ -199,12 +183,12 @@ static int stm32_lptim_cnt_function_set(struct counter_device *counter,
>  		return -EBUSY;
>  
>  	switch (function) {
> -	case STM32_LPTIM_COUNTER_INCREASE:
> +	case COUNTER_FUNCTION_INCREASE:
>  		priv->quadrature_mode = 0;
>  		return 0;
> -	case STM32_LPTIM_ENCODER_BOTH_EDGE:
> +	case COUNTER_FUNCTION_QUADRATURE_X4:
>  		priv->quadrature_mode = 1;
> -		priv->polarity = STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES;
> +		priv->polarity = STM32_LPTIM_CKPOL_BOTH_EDGES;
>  		return 0;
>  	default:
>  		/* should never reach this path */
> @@ -212,9 +196,9 @@ static int stm32_lptim_cnt_function_set(struct counter_device *counter,
>  	}
>  }
>  
> -static ssize_t stm32_lptim_cnt_enable_read(struct counter_device *counter,
> -					   struct counter_count *count,
> -					   void *private, char *buf)
> +static int stm32_lptim_cnt_enable_read(struct counter_device *counter,
> +				       struct counter_count *count,
> +				       u8 *enable)
>  {
>  	struct stm32_lptim_cnt *const priv = counter->priv;
>  	int ret;
> @@ -223,22 +207,18 @@ static ssize_t stm32_lptim_cnt_enable_read(struct counter_device *counter,
>  	if (ret < 0)
>  		return ret;
>  
> -	return scnprintf(buf, PAGE_SIZE, "%u\n", ret);
> +	*enable = ret;
> +
> +	return 0;
>  }
>  
> -static ssize_t stm32_lptim_cnt_enable_write(struct counter_device *counter,
> -					    struct counter_count *count,
> -					    void *private,
> -					    const char *buf, size_t len)
> +static int stm32_lptim_cnt_enable_write(struct counter_device *counter,
> +					struct counter_count *count,
> +					u8 enable)
>  {
>  	struct stm32_lptim_cnt *const priv = counter->priv;
> -	bool enable;
>  	int ret;
>  
> -	ret = kstrtobool(buf, &enable);
> -	if (ret)
> -		return ret;
> -
>  	/* Check nobody uses the timer, or already disabled/enabled */
>  	ret = stm32_lptim_is_enabled(priv);
>  	if ((ret < 0) || (!ret && !enable))
> @@ -254,78 +234,81 @@ static ssize_t stm32_lptim_cnt_enable_write(struct counter_device *counter,
>  	if (ret)
>  		return ret;
>  
> -	return len;
> +	return 0;
>  }
>  
> -static ssize_t stm32_lptim_cnt_ceiling_read(struct counter_device *counter,
> -					    struct counter_count *count,
> -					    void *private, char *buf)
> +static int stm32_lptim_cnt_ceiling_read(struct counter_device *counter,
> +					struct counter_count *count,
> +					u64 *ceiling)
>  {
>  	struct stm32_lptim_cnt *const priv = counter->priv;
>  
> -	return snprintf(buf, PAGE_SIZE, "%u\n", priv->ceiling);
> +	*ceiling = priv->ceiling;
> +
> +	return 0;
>  }
>  
> -static ssize_t stm32_lptim_cnt_ceiling_write(struct counter_device *counter,
> -					     struct counter_count *count,
> -					     void *private,
> -					     const char *buf, size_t len)
> +static int stm32_lptim_cnt_ceiling_write(struct counter_device *counter,
> +					 struct counter_count *count,
> +					 u64 ceiling)
>  {
>  	struct stm32_lptim_cnt *const priv = counter->priv;
> -	unsigned int ceiling;
> -	int ret;
>  
>  	if (stm32_lptim_is_enabled(priv))
>  		return -EBUSY;
>  
> -	ret = kstrtouint(buf, 0, &ceiling);
> -	if (ret)
> -		return ret;
> -
>  	if (ceiling > STM32_LPTIM_MAX_ARR)
>  		return -ERANGE;
>  
>  	priv->ceiling = ceiling;
>  
> -	return len;
> +	return 0;
>  }
>  
> -static const struct counter_count_ext stm32_lptim_cnt_ext[] = {
> -	{
> -		.name = "enable",
> -		.read = stm32_lptim_cnt_enable_read,
> -		.write = stm32_lptim_cnt_enable_write
> -	},
> -	{
> -		.name = "ceiling",
> -		.read = stm32_lptim_cnt_ceiling_read,
> -		.write = stm32_lptim_cnt_ceiling_write
> -	},
> +static struct counter_comp stm32_lptim_cnt_ext[] = {
> +	COUNTER_COMP_ENABLE(stm32_lptim_cnt_enable_read,
> +			    stm32_lptim_cnt_enable_write),
> +	COUNTER_COMP_CEILING(stm32_lptim_cnt_ceiling_read,
> +			     stm32_lptim_cnt_ceiling_write),
>  };
>  
> -static int stm32_lptim_cnt_action_get(struct counter_device *counter,
> -				      struct counter_count *count,
> -				      struct counter_synapse *synapse,
> -				      size_t *action)
> +static int stm32_lptim_cnt_action_read(struct counter_device *counter,
> +				       struct counter_count *count,
> +				       struct counter_synapse *synapse,
> +				       enum counter_synapse_action *action)
>  {
>  	struct stm32_lptim_cnt *const priv = counter->priv;
> -	size_t function;
> +	enum counter_function function;
>  	int err;
>  
> -	err = stm32_lptim_cnt_function_get(counter, count, &function);
> +	err = stm32_lptim_cnt_function_read(counter, count, &function);
>  	if (err)
>  		return err;
>  
>  	switch (function) {
> -	case STM32_LPTIM_COUNTER_INCREASE:
> +	case COUNTER_FUNCTION_INCREASE:
>  		/* LP Timer acts as up-counter on input 1 */
> -		if (synapse->signal->id == count->synapses[0].signal->id)
> -			*action = priv->polarity;
> -		else
> -			*action = STM32_LPTIM_SYNAPSE_ACTION_NONE;
> -		return 0;
> -	case STM32_LPTIM_ENCODER_BOTH_EDGE:
> -		*action = priv->polarity;
> +		if (synapse->signal->id != count->synapses[0].signal->id) {
> +			*action = COUNTER_SYNAPSE_ACTION_NONE;
> +			return 0;
> +		}
> +
> +		switch (priv->polarity) {
> +		case STM32_LPTIM_CKPOL_RISING_EDGE:
> +			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
> +			return 0;
> +		case STM32_LPTIM_CKPOL_FALLING_EDGE:
> +			*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
> +			return 0;
> +		case STM32_LPTIM_CKPOL_BOTH_EDGES:
> +			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
> +			return 0;
> +		default:
> +			/* should never reach this path */
> +			return -EINVAL;
> +		}
> +	case COUNTER_FUNCTION_QUADRATURE_X4:
> +		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
>  		return 0;
>  	default:
>  		/* should never reach this path */
> @@ -333,43 +316,48 @@ static int stm32_lptim_cnt_action_get(struct counter_device *counter,
>  	}
>  }
>  
> -static int stm32_lptim_cnt_action_set(struct counter_device *counter,
> -				      struct counter_count *count,
> -				      struct counter_synapse *synapse,
> -				      size_t action)
> +static int stm32_lptim_cnt_action_write(struct counter_device *counter,
> +					struct counter_count *count,
> +					struct counter_synapse *synapse,
> +					enum counter_synapse_action action)
>  {
>  	struct stm32_lptim_cnt *const priv = counter->priv;
> -	size_t function;
> +	enum counter_function function;
>  	int err;
>  
>  	if (stm32_lptim_is_enabled(priv))
>  		return -EBUSY;
>  
> -	err = stm32_lptim_cnt_function_get(counter, count, &function);
> +	err = stm32_lptim_cnt_function_read(counter, count, &function);
>  	if (err)
>  		return err;
>  
>  	/* only set polarity when in counter mode (on input 1) */
> -	if (function == STM32_LPTIM_COUNTER_INCREASE
> -	    && synapse->signal->id == count->synapses[0].signal->id) {
> -		switch (action) {
> -		case STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE:
> -		case STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE:
> -		case STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES:
> -			priv->polarity = action;
> -			return 0;
> -		}
> -	}
> +	if (function != COUNTER_FUNCTION_INCREASE
> +	    || synapse->signal->id != count->synapses[0].signal->id)
> +		return -EINVAL;
>  
> -	return -EINVAL;
> +	switch (action) {
> +	case COUNTER_SYNAPSE_ACTION_RISING_EDGE:
> +		priv->polarity = STM32_LPTIM_CKPOL_RISING_EDGE;
> +		return 0;
> +	case COUNTER_SYNAPSE_ACTION_FALLING_EDGE:
> +		priv->polarity = STM32_LPTIM_CKPOL_FALLING_EDGE;
> +		return 0;
> +	case COUNTER_SYNAPSE_ACTION_BOTH_EDGES:
> +		priv->polarity = STM32_LPTIM_CKPOL_BOTH_EDGES;
> +		return 0;
> +	default:
> +		return -EINVAL;
> +	}
>  }
>  
>  static const struct counter_ops stm32_lptim_cnt_ops = {
>  	.count_read = stm32_lptim_cnt_read,
> -	.function_get = stm32_lptim_cnt_function_get,
> -	.function_set = stm32_lptim_cnt_function_set,
> -	.action_get = stm32_lptim_cnt_action_get,
> -	.action_set = stm32_lptim_cnt_action_set,
> +	.function_read = stm32_lptim_cnt_function_read,
> +	.function_write = stm32_lptim_cnt_function_write,
> +	.action_read = stm32_lptim_cnt_action_read,
> +	.action_write = stm32_lptim_cnt_action_write,
>  };
>  
>  static struct counter_signal stm32_lptim_cnt_signals[] = {
> diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c
> index 1fbc46f4ee66..0546e932db0c 100644
> --- a/drivers/counter/stm32-timer-cnt.c
> +++ b/drivers/counter/stm32-timer-cnt.c
> @@ -13,6 +13,7 @@
>  #include <linux/module.h>
>  #include <linux/pinctrl/consumer.h>
>  #include <linux/platform_device.h>
> +#include <linux/types.h>
>  
>  #define TIM_CCMR_CCXS	(BIT(8) | BIT(0))
>  #define TIM_CCMR_MASK	(TIM_CCMR_CC1S | TIM_CCMR_CC2S | \
> @@ -36,29 +37,15 @@ struct stm32_timer_cnt {
>  	struct stm32_timer_regs bak;
>  };
>  
> -/**
> - * enum stm32_count_function - enumerates stm32 timer counter encoder modes
> - * @STM32_COUNT_SLAVE_MODE_DISABLED: counts on internal clock when CEN=1
> - * @STM32_COUNT_ENCODER_MODE_1: counts TI1FP1 edges, depending on TI2FP2 level
> - * @STM32_COUNT_ENCODER_MODE_2: counts TI2FP2 edges, depending on TI1FP1 level
> - * @STM32_COUNT_ENCODER_MODE_3: counts on both TI1FP1 and TI2FP2 edges
> - */
> -enum stm32_count_function {
> -	STM32_COUNT_SLAVE_MODE_DISABLED,
> -	STM32_COUNT_ENCODER_MODE_1,
> -	STM32_COUNT_ENCODER_MODE_2,
> -	STM32_COUNT_ENCODER_MODE_3,
> -};
> -
>  static const enum counter_function stm32_count_functions[] = {
> -	[STM32_COUNT_SLAVE_MODE_DISABLED] = COUNTER_FUNCTION_INCREASE,
> -	[STM32_COUNT_ENCODER_MODE_1] = COUNTER_FUNCTION_QUADRATURE_X2_A,
> -	[STM32_COUNT_ENCODER_MODE_2] = COUNTER_FUNCTION_QUADRATURE_X2_B,
> -	[STM32_COUNT_ENCODER_MODE_3] = COUNTER_FUNCTION_QUADRATURE_X4,
> +	COUNTER_FUNCTION_INCREASE,
> +	COUNTER_FUNCTION_QUADRATURE_X2_A,
> +	COUNTER_FUNCTION_QUADRATURE_X2_B,
> +	COUNTER_FUNCTION_QUADRATURE_X4,
>  };
>  
>  static int stm32_count_read(struct counter_device *counter,
> -			    struct counter_count *count, unsigned long *val)
> +			    struct counter_count *count, u64 *val)
>  {
>  	struct stm32_timer_cnt *const priv = counter->priv;
>  	u32 cnt;
> @@ -70,8 +57,7 @@ static int stm32_count_read(struct counter_device *counter,
>  }
>  
>  static int stm32_count_write(struct counter_device *counter,
> -			     struct counter_count *count,
> -			     const unsigned long val)
> +			     struct counter_count *count, const u64 val)
>  {
>  	struct stm32_timer_cnt *const priv = counter->priv;
>  	u32 ceiling;
> @@ -83,9 +69,9 @@ static int stm32_count_write(struct counter_device *counter,
>  	return regmap_write(priv->regmap, TIM_CNT, val);
>  }
>  
> -static int stm32_count_function_get(struct counter_device *counter,
> -				    struct counter_count *count,
> -				    size_t *function)
> +static int stm32_count_function_read(struct counter_device *counter,
> +				     struct counter_count *count,
> +				     enum counter_function *function)
>  {
>  	struct stm32_timer_cnt *const priv = counter->priv;
>  	u32 smcr;
> @@ -94,40 +80,40 @@ static int stm32_count_function_get(struct counter_device *counter,
>  
>  	switch (smcr & TIM_SMCR_SMS) {
>  	case TIM_SMCR_SMS_SLAVE_MODE_DISABLED:
> -		*function = STM32_COUNT_SLAVE_MODE_DISABLED;
> +		*function = COUNTER_FUNCTION_INCREASE;
>  		return 0;
>  	case TIM_SMCR_SMS_ENCODER_MODE_1:
> -		*function = STM32_COUNT_ENCODER_MODE_1;
> +		*function = COUNTER_FUNCTION_QUADRATURE_X2_A;
>  		return 0;
>  	case TIM_SMCR_SMS_ENCODER_MODE_2:
> -		*function = STM32_COUNT_ENCODER_MODE_2;
> +		*function = COUNTER_FUNCTION_QUADRATURE_X2_B;
>  		return 0;
>  	case TIM_SMCR_SMS_ENCODER_MODE_3:
> -		*function = STM32_COUNT_ENCODER_MODE_3;
> +		*function = COUNTER_FUNCTION_QUADRATURE_X4;
>  		return 0;
>  	default:
>  		return -EINVAL;
>  	}
>  }
>  
> -static int stm32_count_function_set(struct counter_device *counter,
> -				    struct counter_count *count,
> -				    size_t function)
> +static int stm32_count_function_write(struct counter_device *counter,
> +				      struct counter_count *count,
> +				      enum counter_function function)
>  {
>  	struct stm32_timer_cnt *const priv = counter->priv;
>  	u32 cr1, sms;
>  
>  	switch (function) {
> -	case STM32_COUNT_SLAVE_MODE_DISABLED:
> +	case COUNTER_FUNCTION_INCREASE:
>  		sms = TIM_SMCR_SMS_SLAVE_MODE_DISABLED;
>  		break;
> -	case STM32_COUNT_ENCODER_MODE_1:
> +	case COUNTER_FUNCTION_QUADRATURE_X2_A:
>  		sms = TIM_SMCR_SMS_ENCODER_MODE_1;
>  		break;
> -	case STM32_COUNT_ENCODER_MODE_2:
> +	case COUNTER_FUNCTION_QUADRATURE_X2_B:
>  		sms = TIM_SMCR_SMS_ENCODER_MODE_2;
>  		break;
> -	case STM32_COUNT_ENCODER_MODE_3:
> +	case COUNTER_FUNCTION_QUADRATURE_X4:
>  		sms = TIM_SMCR_SMS_ENCODER_MODE_3;
>  		break;
>  	default:
> @@ -150,44 +136,37 @@ static int stm32_count_function_set(struct counter_device *counter,
>  	return 0;
>  }
>  
> -static ssize_t stm32_count_direction_read(struct counter_device *counter,
> +static int stm32_count_direction_read(struct counter_device *counter,
>  				      struct counter_count *count,
> -				      void *private, char *buf)
> +				      enum counter_count_direction *direction)
>  {
>  	struct stm32_timer_cnt *const priv = counter->priv;
> -	const char *direction;
>  	u32 cr1;
>  
>  	regmap_read(priv->regmap, TIM_CR1, &cr1);
> -	direction = (cr1 & TIM_CR1_DIR) ? "backward" : "forward";
> +	*direction = (cr1 & TIM_CR1_DIR) ? COUNTER_COUNT_DIRECTION_BACKWARD :
> +		COUNTER_COUNT_DIRECTION_FORWARD;
>  
> -	return scnprintf(buf, PAGE_SIZE, "%s\n", direction);
> +	return 0;
>  }
>  
> -static ssize_t stm32_count_ceiling_read(struct counter_device *counter,
> -					struct counter_count *count,
> -					void *private, char *buf)
> +static int stm32_count_ceiling_read(struct counter_device *counter,
> +				    struct counter_count *count, u64 *ceiling)
>  {
>  	struct stm32_timer_cnt *const priv = counter->priv;
>  	u32 arr;
>  
>  	regmap_read(priv->regmap, TIM_ARR, &arr);
>  
> -	return snprintf(buf, PAGE_SIZE, "%u\n", arr);
> +	*ceiling = arr;
> +
> +	return 0;
>  }
>  
> -static ssize_t stm32_count_ceiling_write(struct counter_device *counter,
> -					 struct counter_count *count,
> -					 void *private,
> -					 const char *buf, size_t len)
> +static int stm32_count_ceiling_write(struct counter_device *counter,
> +				     struct counter_count *count, u64 ceiling)
>  {
>  	struct stm32_timer_cnt *const priv = counter->priv;
> -	unsigned int ceiling;
> -	int ret;
> -
> -	ret = kstrtouint(buf, 0, &ceiling);
> -	if (ret)
> -		return ret;
>  
>  	if (ceiling > priv->max_arr)
>  		return -ERANGE;
> @@ -196,34 +175,27 @@ static ssize_t stm32_count_ceiling_write(struct counter_device *counter,
>  	regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, 0);
>  	regmap_write(priv->regmap, TIM_ARR, ceiling);
>  
> -	return len;
> +	return 0;
>  }
>  
> -static ssize_t stm32_count_enable_read(struct counter_device *counter,
> -				       struct counter_count *count,
> -				       void *private, char *buf)
> +static int stm32_count_enable_read(struct counter_device *counter,
> +				   struct counter_count *count, u8 *enable)
>  {
>  	struct stm32_timer_cnt *const priv = counter->priv;
>  	u32 cr1;
>  
>  	regmap_read(priv->regmap, TIM_CR1, &cr1);
>  
> -	return scnprintf(buf, PAGE_SIZE, "%d\n", (bool)(cr1 & TIM_CR1_CEN));
> +	*enable = cr1 & TIM_CR1_CEN;
> +
> +	return 0;
>  }
>  
> -static ssize_t stm32_count_enable_write(struct counter_device *counter,
> -					struct counter_count *count,
> -					void *private,
> -					const char *buf, size_t len)
> +static int stm32_count_enable_write(struct counter_device *counter,
> +				    struct counter_count *count, u8 enable)
>  {
>  	struct stm32_timer_cnt *const priv = counter->priv;
> -	int err;
>  	u32 cr1;
> -	bool enable;
> -
> -	err = kstrtobool(buf, &enable);
> -	if (err)
> -		return err;
>  
>  	if (enable) {
>  		regmap_read(priv->regmap, TIM_CR1, &cr1);
> @@ -242,70 +214,55 @@ static ssize_t stm32_count_enable_write(struct counter_device *counter,
>  	/* Keep enabled state to properly handle low power states */
>  	priv->enabled = enable;
>  
> -	return len;
> +	return 0;
>  }
>  
> -static const struct counter_count_ext stm32_count_ext[] = {
> -	{
> -		.name = "direction",
> -		.read = stm32_count_direction_read,
> -	},
> -	{
> -		.name = "enable",
> -		.read = stm32_count_enable_read,
> -		.write = stm32_count_enable_write
> -	},
> -	{
> -		.name = "ceiling",
> -		.read = stm32_count_ceiling_read,
> -		.write = stm32_count_ceiling_write
> -	},
> -};
> -
> -enum stm32_synapse_action {
> -	STM32_SYNAPSE_ACTION_NONE,
> -	STM32_SYNAPSE_ACTION_BOTH_EDGES
> +static struct counter_comp stm32_count_ext[] = {
> +	COUNTER_COMP_DIRECTION(stm32_count_direction_read),
> +	COUNTER_COMP_ENABLE(stm32_count_enable_read, stm32_count_enable_write),
> +	COUNTER_COMP_CEILING(stm32_count_ceiling_read,
> +			     stm32_count_ceiling_write),
>  };
>  
>  static const enum counter_synapse_action stm32_synapse_actions[] = {
> -	[STM32_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
> -	[STM32_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES
> +	COUNTER_SYNAPSE_ACTION_NONE,
> +	COUNTER_SYNAPSE_ACTION_BOTH_EDGES
>  };
>  
> -static int stm32_action_get(struct counter_device *counter,
> -			    struct counter_count *count,
> -			    struct counter_synapse *synapse,
> -			    size_t *action)
> +static int stm32_action_read(struct counter_device *counter,
> +			     struct counter_count *count,
> +			     struct counter_synapse *synapse,
> +			     enum counter_synapse_action *action)
>  {
> -	size_t function;
> +	enum counter_function function;
>  	int err;
>  
> -	err = stm32_count_function_get(counter, count, &function);
> +	err = stm32_count_function_read(counter, count, &function);
>  	if (err)
>  		return err;
>  
>  	switch (function) {
> -	case STM32_COUNT_SLAVE_MODE_DISABLED:
> +	case COUNTER_FUNCTION_INCREASE:
>  		/* counts on internal clock when CEN=1 */
> -		*action = STM32_SYNAPSE_ACTION_NONE;
> +		*action = COUNTER_SYNAPSE_ACTION_NONE;
>  		return 0;
> -	case STM32_COUNT_ENCODER_MODE_1:
> +	case COUNTER_FUNCTION_QUADRATURE_X2_A:
>  		/* counts up/down on TI1FP1 edge depending on TI2FP2 level */
>  		if (synapse->signal->id == count->synapses[0].signal->id)
> -			*action = STM32_SYNAPSE_ACTION_BOTH_EDGES;
> +			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
>  		else
> -			*action = STM32_SYNAPSE_ACTION_NONE;
> +			*action = COUNTER_SYNAPSE_ACTION_NONE;
>  		return 0;
> -	case STM32_COUNT_ENCODER_MODE_2:
> +	case COUNTER_FUNCTION_QUADRATURE_X2_B:
>  		/* counts up/down on TI2FP2 edge depending on TI1FP1 level */
>  		if (synapse->signal->id == count->synapses[1].signal->id)
> -			*action = STM32_SYNAPSE_ACTION_BOTH_EDGES;
> +			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
>  		else
> -			*action = STM32_SYNAPSE_ACTION_NONE;
> +			*action = COUNTER_SYNAPSE_ACTION_NONE;
>  		return 0;
> -	case STM32_COUNT_ENCODER_MODE_3:
> +	case COUNTER_FUNCTION_QUADRATURE_X4:
>  		/* counts up/down on both TI1FP1 and TI2FP2 edges */
> -		*action = STM32_SYNAPSE_ACTION_BOTH_EDGES;
> +		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
>  		return 0;
>  	default:
>  		return -EINVAL;
> @@ -315,9 +272,9 @@ static int stm32_action_get(struct counter_device *counter,
>  static const struct counter_ops stm32_timer_cnt_ops = {
>  	.count_read = stm32_count_read,
>  	.count_write = stm32_count_write,
> -	.function_get = stm32_count_function_get,
> -	.function_set = stm32_count_function_set,
> -	.action_get = stm32_action_get,
> +	.function_read = stm32_count_function_read,
> +	.function_write = stm32_count_function_write,
> +	.action_read = stm32_action_read,
>  };
>  
>  static struct counter_signal stm32_signals[] = {
> diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c
> index 94fe58bb3eab..09817c953f9a 100644
> --- a/drivers/counter/ti-eqep.c
> +++ b/drivers/counter/ti-eqep.c
> @@ -13,6 +13,7 @@
>  #include <linux/platform_device.h>
>  #include <linux/pm_runtime.h>
>  #include <linux/regmap.h>
> +#include <linux/types.h>
>  
>  /* 32-bit registers */
>  #define QPOSCNT		0x0
> @@ -73,19 +74,13 @@ enum {
>  };
>  
>  /* Position Counter Input Modes */
> -enum {
> +enum ti_eqep_count_func {
>  	TI_EQEP_COUNT_FUNC_QUAD_COUNT,
>  	TI_EQEP_COUNT_FUNC_DIR_COUNT,
>  	TI_EQEP_COUNT_FUNC_UP_COUNT,
>  	TI_EQEP_COUNT_FUNC_DOWN_COUNT,
>  };
>  
> -enum {
> -	TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES,
> -	TI_EQEP_SYNAPSE_ACTION_RISING_EDGE,
> -	TI_EQEP_SYNAPSE_ACTION_NONE,
> -};
> -
>  struct ti_eqep_cnt {
>  	struct counter_device counter;
>  	struct regmap *regmap32;
> @@ -93,7 +88,7 @@ struct ti_eqep_cnt {
>  };
>  
>  static int ti_eqep_count_read(struct counter_device *counter,
> -			      struct counter_count *count, unsigned long *val)
> +			      struct counter_count *count, u64 *val)
>  {
>  	struct ti_eqep_cnt *priv = counter->priv;
>  	u32 cnt;
> @@ -105,7 +100,7 @@ static int ti_eqep_count_read(struct counter_device *counter,
>  }
>  
>  static int ti_eqep_count_write(struct counter_device *counter,
> -			       struct counter_count *count, unsigned long val)
> +			       struct counter_count *count, u64 val)
>  {
>  	struct ti_eqep_cnt *priv = counter->priv;
>  	u32 max;
> @@ -117,64 +112,100 @@ static int ti_eqep_count_write(struct counter_device *counter,
>  	return regmap_write(priv->regmap32, QPOSCNT, val);
>  }
>  
> -static int ti_eqep_function_get(struct counter_device *counter,
> -				struct counter_count *count, size_t *function)
> +static int ti_eqep_function_read(struct counter_device *counter,
> +				 struct counter_count *count,
> +				 enum counter_function *function)
>  {
>  	struct ti_eqep_cnt *priv = counter->priv;
>  	u32 qdecctl;
>  
>  	regmap_read(priv->regmap16, QDECCTL, &qdecctl);
> -	*function = (qdecctl & QDECCTL_QSRC) >> QDECCTL_QSRC_SHIFT;
> +
> +	switch ((qdecctl & QDECCTL_QSRC) >> QDECCTL_QSRC_SHIFT) {
> +	case TI_EQEP_COUNT_FUNC_QUAD_COUNT:
> +		*function = COUNTER_FUNCTION_QUADRATURE_X4;
> +		break;
> +	case TI_EQEP_COUNT_FUNC_DIR_COUNT:
> +		*function = COUNTER_FUNCTION_PULSE_DIRECTION;
> +		break;
> +	case TI_EQEP_COUNT_FUNC_UP_COUNT:
> +		*function = COUNTER_FUNCTION_INCREASE;
> +		break;
> +	case TI_EQEP_COUNT_FUNC_DOWN_COUNT:
> +		*function = COUNTER_FUNCTION_DECREASE;
> +		break;
> +	}
>  
>  	return 0;
>  }
>  
> -static int ti_eqep_function_set(struct counter_device *counter,
> -				struct counter_count *count, size_t function)
> +static int ti_eqep_function_write(struct counter_device *counter,
> +				  struct counter_count *count,
> +				  enum counter_function function)
>  {
>  	struct ti_eqep_cnt *priv = counter->priv;
> +	enum ti_eqep_count_func qsrc;
> +
> +	switch (function) {
> +	case COUNTER_FUNCTION_QUADRATURE_X4:
> +		qsrc = TI_EQEP_COUNT_FUNC_QUAD_COUNT;
> +		break;
> +	case COUNTER_FUNCTION_PULSE_DIRECTION:
> +		qsrc = TI_EQEP_COUNT_FUNC_DIR_COUNT;
> +		break;
> +	case COUNTER_FUNCTION_INCREASE:
> +		qsrc = TI_EQEP_COUNT_FUNC_UP_COUNT;
> +		break;
> +	case COUNTER_FUNCTION_DECREASE:
> +		qsrc = TI_EQEP_COUNT_FUNC_DOWN_COUNT;
> +		break;
> +	default:
> +		/* should never reach this path */
> +		return -EINVAL;
> +	}
>  
>  	return regmap_write_bits(priv->regmap16, QDECCTL, QDECCTL_QSRC,
> -				 function << QDECCTL_QSRC_SHIFT);
> +				 qsrc << QDECCTL_QSRC_SHIFT);
>  }
>  
> -static int ti_eqep_action_get(struct counter_device *counter,
> -			      struct counter_count *count,
> -			      struct counter_synapse *synapse, size_t *action)
> +static int ti_eqep_action_read(struct counter_device *counter,
> +			       struct counter_count *count,
> +			       struct counter_synapse *synapse,
> +			       enum counter_synapse_action *action)
>  {
>  	struct ti_eqep_cnt *priv = counter->priv;
> -	size_t function;
> +	enum counter_function function;
>  	u32 qdecctl;
>  	int err;
>  
> -	err = ti_eqep_function_get(counter, count, &function);
> +	err = ti_eqep_function_read(counter, count, &function);
>  	if (err)
>  		return err;
>  
>  	switch (function) {
> -	case TI_EQEP_COUNT_FUNC_QUAD_COUNT:
> +	case COUNTER_FUNCTION_QUADRATURE_X4:
>  		/* In quadrature mode, the rising and falling edge of both
>  		 * QEPA and QEPB trigger QCLK.
>  		 */
> -		*action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES;
> +		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
>  		return 0;
> -	case TI_EQEP_COUNT_FUNC_DIR_COUNT:
> +	case COUNTER_FUNCTION_PULSE_DIRECTION:
>  		/* In direction-count mode only rising edge of QEPA is counted
>  		 * and QEPB gives direction.
>  		 */
>  		switch (synapse->signal->id) {
>  		case TI_EQEP_SIGNAL_QEPA:
> -			*action = TI_EQEP_SYNAPSE_ACTION_RISING_EDGE;
> +			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
>  			return 0;
>  		case TI_EQEP_SIGNAL_QEPB:
> -			*action = TI_EQEP_SYNAPSE_ACTION_NONE;
> +			*action = COUNTER_SYNAPSE_ACTION_NONE;
>  			return 0;
>  		default:
>  			/* should never reach this path */
>  			return -EINVAL;
>  		}
> -	case TI_EQEP_COUNT_FUNC_UP_COUNT:
> -	case TI_EQEP_COUNT_FUNC_DOWN_COUNT:
> +	case COUNTER_FUNCTION_INCREASE:
> +	case COUNTER_FUNCTION_DECREASE:
>  		/* In up/down-count modes only QEPA is counted and QEPB is not
>  		 * used.
>  		 */
> @@ -185,12 +216,12 @@ static int ti_eqep_action_get(struct counter_device *counter,
>  				return err;
>  
>  			if (qdecctl & QDECCTL_XCR)
> -				*action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES;
> +				*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
>  			else
> -				*action = TI_EQEP_SYNAPSE_ACTION_RISING_EDGE;
> +				*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
>  			return 0;
>  		case TI_EQEP_SIGNAL_QEPB:
> -			*action = TI_EQEP_SYNAPSE_ACTION_NONE;
> +			*action = COUNTER_SYNAPSE_ACTION_NONE;
>  			return 0;
>  		default:
>  			/* should never reach this path */
> @@ -205,82 +236,67 @@ static int ti_eqep_action_get(struct counter_device *counter,
>  static const struct counter_ops ti_eqep_counter_ops = {
>  	.count_read	= ti_eqep_count_read,
>  	.count_write	= ti_eqep_count_write,
> -	.function_get	= ti_eqep_function_get,
> -	.function_set	= ti_eqep_function_set,
> -	.action_get	= ti_eqep_action_get,
> +	.function_read	= ti_eqep_function_read,
> +	.function_write	= ti_eqep_function_write,
> +	.action_read	= ti_eqep_action_read,
>  };
>  
> -static ssize_t ti_eqep_position_ceiling_read(struct counter_device *counter,
> -					     struct counter_count *count,
> -					     void *ext_priv, char *buf)
> +static int ti_eqep_position_ceiling_read(struct counter_device *counter,
> +					 struct counter_count *count,
> +					 u64 *ceiling)
>  {
>  	struct ti_eqep_cnt *priv = counter->priv;
>  	u32 qposmax;
>  
>  	regmap_read(priv->regmap32, QPOSMAX, &qposmax);
>  
> -	return sprintf(buf, "%u\n", qposmax);
> +	*ceiling = qposmax;
> +
> +	return 0;
>  }
>  
> -static ssize_t ti_eqep_position_ceiling_write(struct counter_device *counter,
> -					      struct counter_count *count,
> -					      void *ext_priv, const char *buf,
> -					      size_t len)
> +static int ti_eqep_position_ceiling_write(struct counter_device *counter,
> +					  struct counter_count *count,
> +					  u64 ceiling)
>  {
>  	struct ti_eqep_cnt *priv = counter->priv;
> -	int err;
> -	u32 res;
>  
> -	err = kstrtouint(buf, 0, &res);
> -	if (err < 0)
> -		return err;
> +	if (ceiling != (u32)ceiling)
> +		return -ERANGE;
>  
> -	regmap_write(priv->regmap32, QPOSMAX, res);
> +	regmap_write(priv->regmap32, QPOSMAX, ceiling);
>  
> -	return len;
> +	return 0;
>  }
>  
> -static ssize_t ti_eqep_position_enable_read(struct counter_device *counter,
> -					    struct counter_count *count,
> -					    void *ext_priv, char *buf)
> +static int ti_eqep_position_enable_read(struct counter_device *counter,
> +					struct counter_count *count, u8 *enable)
>  {
>  	struct ti_eqep_cnt *priv = counter->priv;
>  	u32 qepctl;
>  
>  	regmap_read(priv->regmap16, QEPCTL, &qepctl);
>  
> -	return sprintf(buf, "%u\n", !!(qepctl & QEPCTL_PHEN));
> +	*enable = !!(qepctl & QEPCTL_PHEN);
> +
> +	return 0;
>  }
>  
> -static ssize_t ti_eqep_position_enable_write(struct counter_device *counter,
> -					     struct counter_count *count,
> -					     void *ext_priv, const char *buf,
> -					     size_t len)
> +static int ti_eqep_position_enable_write(struct counter_device *counter,
> +					 struct counter_count *count, u8 enable)
>  {
>  	struct ti_eqep_cnt *priv = counter->priv;
> -	int err;
> -	bool res;
> -
> -	err = kstrtobool(buf, &res);
> -	if (err < 0)
> -		return err;
>  
> -	regmap_write_bits(priv->regmap16, QEPCTL, QEPCTL_PHEN, res ? -1 : 0);
> +	regmap_write_bits(priv->regmap16, QEPCTL, QEPCTL_PHEN, enable ? -1 : 0);
>  
> -	return len;
> +	return 0;
>  }
>  
> -static struct counter_count_ext ti_eqep_position_ext[] = {
> -	{
> -		.name	= "ceiling",
> -		.read	= ti_eqep_position_ceiling_read,
> -		.write	= ti_eqep_position_ceiling_write,
> -	},
> -	{
> -		.name	= "enable",
> -		.read	= ti_eqep_position_enable_read,
> -		.write	= ti_eqep_position_enable_write,
> -	},
> +static struct counter_comp ti_eqep_position_ext[] = {
> +	COUNTER_COMP_CEILING(ti_eqep_position_ceiling_read,
> +			     ti_eqep_position_ceiling_write),
> +	COUNTER_COMP_ENABLE(ti_eqep_position_enable_read,
> +			    ti_eqep_position_enable_write),
>  };
>  
>  static struct counter_signal ti_eqep_signals[] = {
> @@ -295,16 +311,16 @@ static struct counter_signal ti_eqep_signals[] = {
>  };
>  
>  static const enum counter_function ti_eqep_position_functions[] = {
> -	[TI_EQEP_COUNT_FUNC_QUAD_COUNT]	= COUNTER_FUNCTION_QUADRATURE_X4,
> -	[TI_EQEP_COUNT_FUNC_DIR_COUNT]	= COUNTER_FUNCTION_PULSE_DIRECTION,
> -	[TI_EQEP_COUNT_FUNC_UP_COUNT]	= COUNTER_FUNCTION_INCREASE,
> -	[TI_EQEP_COUNT_FUNC_DOWN_COUNT]	= COUNTER_FUNCTION_DECREASE,
> +	COUNTER_FUNCTION_QUADRATURE_X4,
> +	COUNTER_FUNCTION_PULSE_DIRECTION,
> +	COUNTER_FUNCTION_INCREASE,
> +	COUNTER_FUNCTION_DECREASE,
>  };
>  
>  static const enum counter_synapse_action ti_eqep_position_synapse_actions[] = {
> -	[TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES]	= COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
> -	[TI_EQEP_SYNAPSE_ACTION_RISING_EDGE]	= COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> -	[TI_EQEP_SYNAPSE_ACTION_NONE]		= COUNTER_SYNAPSE_ACTION_NONE,
> +	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
> +	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> +	COUNTER_SYNAPSE_ACTION_NONE,
>  };
>  
>  static struct counter_synapse ti_eqep_position_synapses[] = {
> diff --git a/include/linux/counter.h b/include/linux/counter.h
> index d16ce2819b48..b69277f5c4c5 100644
> --- a/include/linux/counter.h
> +++ b/include/linux/counter.h
> @@ -6,42 +6,184 @@
>  #ifndef _COUNTER_H_
>  #define _COUNTER_H_
>  
> -#include <linux/counter_enum.h>
>  #include <linux/device.h>
> +#include <linux/kernel.h>
>  #include <linux/types.h>
>  
> +struct counter_device;
> +struct counter_count;
> +struct counter_synapse;
> +struct counter_signal;
> +
> +enum counter_comp_type {
> +	COUNTER_COMP_U8,
> +	COUNTER_COMP_U64,
> +	COUNTER_COMP_BOOL,
> +	COUNTER_COMP_SIGNAL_LEVEL,
> +	COUNTER_COMP_FUNCTION,
> +	COUNTER_COMP_SYNAPSE_ACTION,
> +	COUNTER_COMP_ENUM,
> +	COUNTER_COMP_COUNT_DIRECTION,
> +	COUNTER_COMP_COUNT_MODE,
> +};
> +
> +enum counter_scope {
> +	COUNTER_SCOPE_DEVICE,
> +	COUNTER_SCOPE_SIGNAL,
> +	COUNTER_SCOPE_COUNT,
> +};
> +
>  enum counter_count_direction {
> -	COUNTER_COUNT_DIRECTION_FORWARD = 0,
> -	COUNTER_COUNT_DIRECTION_BACKWARD
> +	COUNTER_COUNT_DIRECTION_FORWARD,
> +	COUNTER_COUNT_DIRECTION_BACKWARD,
>  };
> -extern const char *const counter_count_direction_str[2];
>  
>  enum counter_count_mode {
> -	COUNTER_COUNT_MODE_NORMAL = 0,
> +	COUNTER_COUNT_MODE_NORMAL,
>  	COUNTER_COUNT_MODE_RANGE_LIMIT,
>  	COUNTER_COUNT_MODE_NON_RECYCLE,
> -	COUNTER_COUNT_MODE_MODULO_N
> +	COUNTER_COUNT_MODE_MODULO_N,
>  };
> -extern const char *const counter_count_mode_str[4];
>  
> -struct counter_device;
> -struct counter_signal;
> +enum counter_function {
> +	COUNTER_FUNCTION_INCREASE,
> +	COUNTER_FUNCTION_DECREASE,
> +	COUNTER_FUNCTION_PULSE_DIRECTION,
> +	COUNTER_FUNCTION_QUADRATURE_X1_A,
> +	COUNTER_FUNCTION_QUADRATURE_X1_B,
> +	COUNTER_FUNCTION_QUADRATURE_X2_A,
> +	COUNTER_FUNCTION_QUADRATURE_X2_B,
> +	COUNTER_FUNCTION_QUADRATURE_X4,
> +};
> +
> +enum counter_signal_level {
> +	COUNTER_SIGNAL_LEVEL_LOW,
> +	COUNTER_SIGNAL_LEVEL_HIGH,
> +};
> +
> +enum counter_synapse_action {
> +	COUNTER_SYNAPSE_ACTION_NONE,
> +	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> +	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
> +	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
> +};
>  
>  /**
> - * struct counter_signal_ext - Counter Signal extensions
> - * @name:	attribute name
> - * @read:	read callback for this attribute; may be NULL
> - * @write:	write callback for this attribute; may be NULL
> - * @priv:	data private to the driver
> + * struct counter_comp - Counter component node
> + * @type:		Counter component data type
> + * @name:		device-specific component name
> + * @priv:		component-relevant data
> + * @action_read		Synapse action mode read callback. The read value of the
> + *			respective Synapse action mode should be passed back via
> + *			the action parameter.
> + * @device_u8_read	Device u8 component read callback. The read value of the
> + *			respective Device u8 component should be passed back via
> + *			the val parameter.
> + * @count_u8_read	Count u8 component read callback. The read value of the
> + *			respective Count u8 component should be passed back via
> + *			the val parameter.
> + * @signal_u8_read	Signal u8 component read callback. The read value of the
> + *			respective Signal u8 component should be passed back via
> + *			the val parameter.
> + * @device_u32_read	Device u32 component read callback. The read value of
> + *			the respective Device u32 component should be passed
> + *			back via the val parameter.
> + * @count_u32_read	Count u32 component read callback. The read value of the
> + *			respective Count u32 component should be passed back via
> + *			the val parameter.
> + * @signal_u32_read	Signal u32 component read callback. The read value of
> + *			the respective Signal u32 component should be passed
> + *			back via the val parameter.
> + * @device_u64_read	Device u64 component read callback. The read value of
> + *			the respective Device u64 component should be passed
> + *			back via the val parameter.
> + * @count_u64_read	Count u64 component read callback. The read value of the
> + *			respective Count u64 component should be passed back via
> + *			the val parameter.
> + * @signal_u64_read	Signal u64 component read callback. The read value of
> + *			the respective Signal u64 component should be passed
> + *			back via the val parameter.
> + * @action_write	Synapse action mode write callback. The write value of
> + *			the respective Synapse action mode is passed via the
> + *			action parameter.
> + * @device_u8_write	Device u8 component write callback. The write value of
> + *			the respective Device u8 component is passed via the val
> + *			parameter.
> + * @count_u8_write	Count u8 component write callback. The write value of
> + *			the respective Count u8 component is passed via the val
> + *			parameter.
> + * @signal_u8_write	Signal u8 component write callback. The write value of
> + *			the respective Signal u8 component is passed via the val
> + *			parameter.
> + * @device_u32_write	Device u32 component write callback. The write value of
> + *			the respective Device u32 component is passed via the
> + *			val parameter.
> + * @count_u32_write	Count u32 component write callback. The write value of
> + *			the respective Count u32 component is passed via the val
> + *			parameter.
> + * @signal_u32_write	Signal u32 component write callback. The write value of
> + *			the respective Signal u32 component is passed via the
> + *			val parameter.
> + * @device_u64_write	Device u64 component write callback. The write value of
> + *			the respective Device u64 component is passed via the
> + *			val parameter.
> + * @count_u64_write	Count u64 component write callback. The write value of
> + *			the respective Count u64 component is passed via the val
> + *			parameter.
> + * @signal_u64_write	Signal u64 component write callback. The write value of
> + *			the respective Signal u64 component is passed via the
> + *			val parameter.
>   */
> -struct counter_signal_ext {
> +struct counter_comp {
> +	enum counter_comp_type type;
>  	const char *name;
> -	ssize_t (*read)(struct counter_device *counter,
> -			struct counter_signal *signal, void *priv, char *buf);
> -	ssize_t (*write)(struct counter_device *counter,
> -			 struct counter_signal *signal, void *priv,
> -			 const char *buf, size_t len);
>  	void *priv;
> +	union {
> +		int (*action_read)(struct counter_device *counter,
> +				   struct counter_count *count,
> +				   struct counter_synapse *synapse,
> +				   enum counter_synapse_action *action);
> +		int (*device_u8_read)(struct counter_device *counter, u8 *val);
> +		int (*count_u8_read)(struct counter_device *counter,
> +				     struct counter_count *count, u8 *val);
> +		int (*signal_u8_read)(struct counter_device *counter,
> +				      struct counter_signal *signal, u8 *val);
> +		int (*device_u32_read)(struct counter_device *counter,
> +				       u32 *val);
> +		int (*count_u32_read)(struct counter_device *counter,
> +				      struct counter_count *count, u32 *val);
> +		int (*signal_u32_read)(struct counter_device *counter,
> +				       struct counter_signal *signal, u32 *val);
> +		int (*device_u64_read)(struct counter_device *counter,
> +				       u64 *val);
> +		int (*count_u64_read)(struct counter_device *counter,
> +				      struct counter_count *count, u64 *val);
> +		int (*signal_u64_read)(struct counter_device *counter,
> +				       struct counter_signal *signal, u64 *val);
> +	};
> +	union {
> +		int (*action_write)(struct counter_device *counter,
> +				    struct counter_count *count,
> +				    struct counter_synapse *synapse,
> +				    enum counter_synapse_action action);
> +		int (*device_u8_write)(struct counter_device *counter, u8 val);
> +		int (*count_u8_write)(struct counter_device *counter,
> +				      struct counter_count *count, u8 val);
> +		int (*signal_u8_write)(struct counter_device *counter,
> +				       struct counter_signal *signal, u8 val);
> +		int (*device_u32_write)(struct counter_device *counter,
> +					u32 val);
> +		int (*count_u32_write)(struct counter_device *counter,
> +				       struct counter_count *count, u32 val);
> +		int (*signal_u32_write)(struct counter_device *counter,
> +					struct counter_signal *signal, u32 val);
> +		int (*device_u64_write)(struct counter_device *counter,
> +					u64 val);
> +		int (*count_u64_write)(struct counter_device *counter,
> +				       struct counter_count *count, u64 val);
> +		int (*signal_u64_write)(struct counter_device *counter,
> +					struct counter_signal *signal, u64 val);
> +	};
>  };
>  
>  /**
> @@ -51,248 +193,52 @@ struct counter_signal_ext {
>   *		as it appears in the datasheet documentation
>   * @ext:	optional array of Counter Signal extensions
>   * @num_ext:	number of Counter Signal extensions specified in @ext
> - * @priv:	optional private data supplied by driver
>   */
>  struct counter_signal {
>  	int id;
>  	const char *name;
>  
> -	const struct counter_signal_ext *ext;
> +	struct counter_comp *ext;
>  	size_t num_ext;
> -
> -	void *priv;
> -};
> -
> -/**
> - * struct counter_signal_enum_ext - Signal enum extension attribute
> - * @items:	Array of strings
> - * @num_items:	Number of items specified in @items
> - * @set:	Set callback function; may be NULL
> - * @get:	Get callback function; may be NULL
> - *
> - * The counter_signal_enum_ext structure can be used to implement enum style
> - * Signal extension attributes. Enum style attributes are those which have a set
> - * of strings that map to unsigned integer values. The Generic Counter Signal
> - * enum extension helper code takes care of mapping between value and string, as
> - * well as generating a "_available" file which contains a list of all available
> - * items. The get callback is used to query the currently active item; the index
> - * of the item within the respective items array is returned via the 'item'
> - * parameter. The set callback is called when the attribute is updated; the
> - * 'item' parameter contains the index of the newly activated item within the
> - * respective items array.
> - */
> -struct counter_signal_enum_ext {
> -	const char * const *items;
> -	size_t num_items;
> -	int (*get)(struct counter_device *counter,
> -		   struct counter_signal *signal, size_t *item);
> -	int (*set)(struct counter_device *counter,
> -		   struct counter_signal *signal, size_t item);
> -};
> -
> -/**
> - * COUNTER_SIGNAL_ENUM() - Initialize Signal enum extension
> - * @_name:	Attribute name
> - * @_e:		Pointer to a counter_signal_enum_ext structure
> - *
> - * This should usually be used together with COUNTER_SIGNAL_ENUM_AVAILABLE()
> - */
> -#define COUNTER_SIGNAL_ENUM(_name, _e) \
> -{ \
> -	.name = (_name), \
> -	.read = counter_signal_enum_read, \
> -	.write = counter_signal_enum_write, \
> -	.priv = (_e) \
> -}
> -
> -/**
> - * COUNTER_SIGNAL_ENUM_AVAILABLE() - Initialize Signal enum available extension
> - * @_name:	Attribute name ("_available" will be appended to the name)
> - * @_e:		Pointer to a counter_signal_enum_ext structure
> - *
> - * Creates a read only attribute that lists all the available enum items in a
> - * newline separated list. This should usually be used together with
> - * COUNTER_SIGNAL_ENUM()
> - */
> -#define COUNTER_SIGNAL_ENUM_AVAILABLE(_name, _e) \
> -{ \
> -	.name = (_name "_available"), \
> -	.read = counter_signal_enum_available_read, \
> -	.priv = (_e) \
> -}
> -
> -enum counter_synapse_action {
> -	COUNTER_SYNAPSE_ACTION_NONE = 0,
> -	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> -	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
> -	COUNTER_SYNAPSE_ACTION_BOTH_EDGES
>  };
>  
>  /**
>   * struct counter_synapse - Counter Synapse node
> - * @action:		index of current action mode
>   * @actions_list:	array of available action modes
>   * @num_actions:	number of action modes specified in @actions_list
>   * @signal:		pointer to associated signal
>   */
>  struct counter_synapse {
> -	size_t action;
>  	const enum counter_synapse_action *actions_list;
>  	size_t num_actions;
>  
>  	struct counter_signal *signal;
>  };
>  
> -struct counter_count;
> -
> -/**
> - * struct counter_count_ext - Counter Count extension
> - * @name:	attribute name
> - * @read:	read callback for this attribute; may be NULL
> - * @write:	write callback for this attribute; may be NULL
> - * @priv:	data private to the driver
> - */
> -struct counter_count_ext {
> -	const char *name;
> -	ssize_t (*read)(struct counter_device *counter,
> -			struct counter_count *count, void *priv, char *buf);
> -	ssize_t (*write)(struct counter_device *counter,
> -			 struct counter_count *count, void *priv,
> -			 const char *buf, size_t len);
> -	void *priv;
> -};
> -
> -enum counter_function {
> -	COUNTER_FUNCTION_INCREASE = 0,
> -	COUNTER_FUNCTION_DECREASE,
> -	COUNTER_FUNCTION_PULSE_DIRECTION,
> -	COUNTER_FUNCTION_QUADRATURE_X1_A,
> -	COUNTER_FUNCTION_QUADRATURE_X1_B,
> -	COUNTER_FUNCTION_QUADRATURE_X2_A,
> -	COUNTER_FUNCTION_QUADRATURE_X2_B,
> -	COUNTER_FUNCTION_QUADRATURE_X4
> -};
> -
>  /**
>   * struct counter_count - Counter Count node
>   * @id:			unique ID used to identify Count
>   * @name:		device-specific Count name; ideally, this should match
>   *			the name as it appears in the datasheet documentation
> - * @function:		index of current function mode
>   * @functions_list:	array available function modes
>   * @num_functions:	number of function modes specified in @functions_list
>   * @synapses:		array of synapses for initialization
>   * @num_synapses:	number of synapses specified in @synapses
>   * @ext:		optional array of Counter Count extensions
>   * @num_ext:		number of Counter Count extensions specified in @ext
> - * @priv:		optional private data supplied by driver
>   */
>  struct counter_count {
>  	int id;
>  	const char *name;
>  
> -	size_t function;
>  	const enum counter_function *functions_list;
>  	size_t num_functions;
>  
>  	struct counter_synapse *synapses;
>  	size_t num_synapses;
>  
> -	const struct counter_count_ext *ext;
> +	struct counter_comp *ext;
>  	size_t num_ext;
> -
> -	void *priv;
> -};
> -
> -/**
> - * struct counter_count_enum_ext - Count enum extension attribute
> - * @items:	Array of strings
> - * @num_items:	Number of items specified in @items
> - * @set:	Set callback function; may be NULL
> - * @get:	Get callback function; may be NULL
> - *
> - * The counter_count_enum_ext structure can be used to implement enum style
> - * Count extension attributes. Enum style attributes are those which have a set
> - * of strings that map to unsigned integer values. The Generic Counter Count
> - * enum extension helper code takes care of mapping between value and string, as
> - * well as generating a "_available" file which contains a list of all available
> - * items. The get callback is used to query the currently active item; the index
> - * of the item within the respective items array is returned via the 'item'
> - * parameter. The set callback is called when the attribute is updated; the
> - * 'item' parameter contains the index of the newly activated item within the
> - * respective items array.
> - */
> -struct counter_count_enum_ext {
> -	const char * const *items;
> -	size_t num_items;
> -	int (*get)(struct counter_device *counter, struct counter_count *count,
> -		   size_t *item);
> -	int (*set)(struct counter_device *counter, struct counter_count *count,
> -		   size_t item);
> -};
> -
> -/**
> - * COUNTER_COUNT_ENUM() - Initialize Count enum extension
> - * @_name:	Attribute name
> - * @_e:		Pointer to a counter_count_enum_ext structure
> - *
> - * This should usually be used together with COUNTER_COUNT_ENUM_AVAILABLE()
> - */
> -#define COUNTER_COUNT_ENUM(_name, _e) \
> -{ \
> -	.name = (_name), \
> -	.read = counter_count_enum_read, \
> -	.write = counter_count_enum_write, \
> -	.priv = (_e) \
> -}
> -
> -/**
> - * COUNTER_COUNT_ENUM_AVAILABLE() - Initialize Count enum available extension
> - * @_name:	Attribute name ("_available" will be appended to the name)
> - * @_e:		Pointer to a counter_count_enum_ext structure
> - *
> - * Creates a read only attribute that lists all the available enum items in a
> - * newline separated list. This should usually be used together with
> - * COUNTER_COUNT_ENUM()
> - */
> -#define COUNTER_COUNT_ENUM_AVAILABLE(_name, _e) \
> -{ \
> -	.name = (_name "_available"), \
> -	.read = counter_count_enum_available_read, \
> -	.priv = (_e) \
> -}
> -
> -/**
> - * struct counter_device_attr_group - internal container for attribute group
> - * @attr_group:	Counter sysfs attributes group
> - * @attr_list:	list to keep track of created Counter sysfs attributes
> - * @num_attr:	number of Counter sysfs attributes
> - */
> -struct counter_device_attr_group {
> -	struct attribute_group attr_group;
> -	struct list_head attr_list;
> -	size_t num_attr;
> -};
> -
> -/**
> - * struct counter_device_state - internal state container for a Counter device
> - * @id:			unique ID used to identify the Counter
> - * @dev:		internal device structure
> - * @groups_list:	attribute groups list (for Signals, Counts, and ext)
> - * @num_groups:		number of attribute groups containers
> - * @groups:		Counter sysfs attribute groups (to populate @dev.groups)
> - */
> -struct counter_device_state {
> -	int id;
> -	struct device dev;
> -	struct counter_device_attr_group *groups_list;
> -	size_t num_groups;
> -	const struct attribute_group **groups;
> -};
> -
> -enum counter_signal_level {
> -	COUNTER_SIGNAL_LEVEL_LOW,
> -	COUNTER_SIGNAL_LEVEL_HIGH,
>  };
>  
>  /**
> @@ -306,117 +252,47 @@ enum counter_signal_level {
>   * @count_write:	optional write callback for Count attribute. The write
>   *			value for the respective Count is passed in via the val
>   *			parameter.
> - * @function_get:	function to get the current count function mode. Returns
> - *			0 on success and negative error code on error. The index
> - *			of the respective Count's returned function mode should
> - *			be passed back via the function parameter.
> - * @function_set:	function to set the count function mode. function is the
> - *			index of the requested function mode from the respective
> - *			Count's functions_list array.
> - * @action_get:		function to get the current action mode. Returns 0 on
> - *			success and negative error code on error. The index of
> - *			the respective Synapse's returned action mode should be
> - *			passed back via the action parameter.
> - * @action_set:		function to set the action mode. action is the index of
> - *			the requested action mode from the respective Synapse's
> - *			actions_list array.
> + * @function_read:	read callback the Count function modes. The read
> + *			function mode of the respective Count should be passed
> + *			back via the function parameter.
> + * @function_write:	write callback for Count function modes. The function
> + *			mode to write for the respective Count is passed in via
> + *			the function parameter.
> + * @action_read:	read callback the Synapse action modes. The read action
> + *			mode of the respective Synapse should be passed back via
> + *			the action parameter.
> + * @action_write:	write callback for Synapse action modes. The action mode
> + *			to write for the respective Synapse is passed in via the
> + *			action parameter.
>   */
>  struct counter_ops {
>  	int (*signal_read)(struct counter_device *counter,
>  			   struct counter_signal *signal,
>  			   enum counter_signal_level *level);
>  	int (*count_read)(struct counter_device *counter,
> -			  struct counter_count *count, unsigned long *val);
> +			  struct counter_count *count, u64 *value);
>  	int (*count_write)(struct counter_device *counter,
> -			   struct counter_count *count, unsigned long val);
> -	int (*function_get)(struct counter_device *counter,
> -			    struct counter_count *count, size_t *function);
> -	int (*function_set)(struct counter_device *counter,
> -			    struct counter_count *count, size_t function);
> -	int (*action_get)(struct counter_device *counter,
> -			  struct counter_count *count,
> -			  struct counter_synapse *synapse, size_t *action);
> -	int (*action_set)(struct counter_device *counter,
> -			  struct counter_count *count,
> -			  struct counter_synapse *synapse, size_t action);
> -};
> -
> -/**
> - * struct counter_device_ext - Counter device extension
> - * @name:	attribute name
> - * @read:	read callback for this attribute; may be NULL
> - * @write:	write callback for this attribute; may be NULL
> - * @priv:	data private to the driver
> - */
> -struct counter_device_ext {
> -	const char *name;
> -	ssize_t (*read)(struct counter_device *counter, void *priv, char *buf);
> -	ssize_t (*write)(struct counter_device *counter, void *priv,
> -			 const char *buf, size_t len);
> -	void *priv;
> +			   struct counter_count *count, u64 value);
> +	int (*function_read)(struct counter_device *counter,
> +			     struct counter_count *count,
> +			     enum counter_function *function);
> +	int (*function_write)(struct counter_device *counter,
> +			      struct counter_count *count,
> +			      enum counter_function function);
> +	int (*action_read)(struct counter_device *counter,
> +			   struct counter_count *count,
> +			   struct counter_synapse *synapse,
> +			   enum counter_synapse_action *action);
> +	int (*action_write)(struct counter_device *counter,
> +			    struct counter_count *count,
> +			    struct counter_synapse *synapse,
> +			    enum counter_synapse_action action);
>  };
>  
> -/**
> - * struct counter_device_enum_ext - Counter enum extension attribute
> - * @items:	Array of strings
> - * @num_items:	Number of items specified in @items
> - * @set:	Set callback function; may be NULL
> - * @get:	Get callback function; may be NULL
> - *
> - * The counter_device_enum_ext structure can be used to implement enum style
> - * Counter extension attributes. Enum style attributes are those which have a
> - * set of strings that map to unsigned integer values. The Generic Counter enum
> - * extension helper code takes care of mapping between value and string, as well
> - * as generating a "_available" file which contains a list of all available
> - * items. The get callback is used to query the currently active item; the index
> - * of the item within the respective items array is returned via the 'item'
> - * parameter. The set callback is called when the attribute is updated; the
> - * 'item' parameter contains the index of the newly activated item within the
> - * respective items array.
> - */
> -struct counter_device_enum_ext {
> -	const char * const *items;
> -	size_t num_items;
> -	int (*get)(struct counter_device *counter, size_t *item);
> -	int (*set)(struct counter_device *counter, size_t item);
> -};
> -
> -/**
> - * COUNTER_DEVICE_ENUM() - Initialize Counter enum extension
> - * @_name:	Attribute name
> - * @_e:		Pointer to a counter_device_enum_ext structure
> - *
> - * This should usually be used together with COUNTER_DEVICE_ENUM_AVAILABLE()
> - */
> -#define COUNTER_DEVICE_ENUM(_name, _e) \
> -{ \
> -	.name = (_name), \
> -	.read = counter_device_enum_read, \
> -	.write = counter_device_enum_write, \
> -	.priv = (_e) \
> -}
> -
> -/**
> - * COUNTER_DEVICE_ENUM_AVAILABLE() - Initialize Counter enum available extension
> - * @_name:	Attribute name ("_available" will be appended to the name)
> - * @_e:		Pointer to a counter_device_enum_ext structure
> - *
> - * Creates a read only attribute that lists all the available enum items in a
> - * newline separated list. This should usually be used together with
> - * COUNTER_DEVICE_ENUM()
> - */
> -#define COUNTER_DEVICE_ENUM_AVAILABLE(_name, _e) \
> -{ \
> -	.name = (_name "_available"), \
> -	.read = counter_device_enum_available_read, \
> -	.priv = (_e) \
> -}
> -
>  /**
>   * struct counter_device - Counter data structure
>   * @name:		name of the device as it appears in the datasheet
>   * @parent:		optional parent device providing the counters
> - * @device_state:	internal device state container
>   * @ops:		callbacks from driver
>   * @signals:		array of Signals
>   * @num_signals:	number of Signals specified in @signals
> @@ -425,11 +301,11 @@ struct counter_device_enum_ext {
>   * @ext:		optional array of Counter device extensions
>   * @num_ext:		number of Counter device extensions specified in @ext
>   * @priv:		optional private data supplied by driver
> + * @dev:		internal device structure
>   */
>  struct counter_device {
>  	const char *name;
>  	struct device *parent;
> -	struct counter_device_state *device_state;
>  
>  	const struct counter_ops *ops;
>  
> @@ -438,17 +314,159 @@ struct counter_device {
>  	struct counter_count *counts;
>  	size_t num_counts;
>  
> -	const struct counter_device_ext *ext;
> +	struct counter_comp *ext;
>  	size_t num_ext;
>  
>  	void *priv;
> +
> +	struct device dev;
>  };
>  
>  int counter_register(struct counter_device *const counter);
>  void counter_unregister(struct counter_device *const counter);
>  int devm_counter_register(struct device *dev,
>  			  struct counter_device *const counter);
> -void devm_counter_unregister(struct device *dev,
> -			     struct counter_device *const counter);
> +
> +#define COUNTER_COMP_DEVICE_U8(_name, _read, _write) \
> +{ \
> +	.type = COUNTER_COMP_U8, \
> +	.name = (_name), \
> +	.device_u8_read = (_read), \
> +	.device_u8_write = (_write), \
> +}
> +#define COUNTER_COMP_COUNT_U8(_name, _read, _write) \
> +{ \
> +	.type = COUNTER_COMP_U8, \
> +	.name = (_name), \
> +	.count_u8_read = (_read), \
> +	.count_u8_write = (_write), \
> +}
> +#define COUNTER_COMP_SIGNAL_U8(_name, _read, _write) \
> +{ \
> +	.type = COUNTER_COMP_U8, \
> +	.name = (_name), \
> +	.signal_u8_read = (_read), \
> +	.signal_u8_write = (_write), \
> +}
> +
> +#define COUNTER_COMP_DEVICE_U64(_name, _read, _write) \
> +{ \
> +	.type = COUNTER_COMP_U64, \
> +	.name = (_name), \
> +	.device_u64_read = (_read), \
> +	.device_u64_write = (_write), \
> +}
> +#define COUNTER_COMP_COUNT_U64(_name, _read, _write) \
> +{ \
> +	.type = COUNTER_COMP_U64, \
> +	.name = (_name), \
> +	.count_u64_read = (_read), \
> +	.count_u64_write = (_write), \
> +}
> +#define COUNTER_COMP_SIGNAL_U64(_name, _read, _write) \
> +{ \
> +	.type = COUNTER_COMP_U64, \
> +	.name = (_name), \
> +	.signal_u64_read = (_read), \
> +	.signal_u64_write = (_write), \
> +}
> +
> +#define COUNTER_COMP_DEVICE_BOOL(_name, _read, _write) \
> +{ \
> +	.type = COUNTER_COMP_BOOL, \
> +	.name = (_name), \
> +	.device_u8_read = (_read), \
> +	.device_u8_write = (_write), \
> +}
> +#define COUNTER_COMP_COUNT_BOOL(_name, _read, _write) \
> +{ \
> +	.type = COUNTER_COMP_BOOL, \
> +	.name = (_name), \
> +	.count_u8_read = (_read), \
> +	.count_u8_write = (_write), \
> +}
> +#define COUNTER_COMP_SIGNAL_BOOL(_name, _read, _write) \
> +{ \
> +	.type = COUNTER_COMP_BOOL, \
> +	.name = (_name), \
> +	.signal_u8_read = (_read), \
> +	.signal_u8_write = (_write), \
> +}
> +
> +struct counter_available {
> +	union {
> +		const u32 *enums;
> +		const char *const *strs;
> +	};
> +	size_t num_items;
> +};
> +
> +#define DEFINE_COUNTER_AVAILABLE(_name, _enums) \
> +	struct counter_available _name = { \
> +		.enums = (_enums), \
> +		.num_items = ARRAY_SIZE(_enums), \
> +	}
> +
> +#define DEFINE_COUNTER_ENUM(_name, _strs) \
> +	struct counter_available _name = { \
> +		.strs = (_strs), \
> +		.num_items = ARRAY_SIZE(_strs), \
> +	}
> +
> +#define COUNTER_COMP_DEVICE_ENUM(_name, _get, _set, _available) \
> +{ \
> +	.type = COUNTER_COMP_ENUM, \
> +	.name = (_name), \
> +	.device_u32_read = (_get), \
> +	.device_u32_write = (_set), \
> +	.priv = &(_available), \
> +}
> +#define COUNTER_COMP_COUNT_ENUM(_name, _get, _set, _available) \
> +{ \
> +	.type = COUNTER_COMP_ENUM, \
> +	.name = (_name), \
> +	.count_u32_read = (_get), \
> +	.count_u32_write = (_set), \
> +	.priv = &(_available), \
> +}
> +#define COUNTER_COMP_SIGNAL_ENUM(_name, _get, _set, _available) \
> +{ \
> +	.type = COUNTER_COMP_ENUM, \
> +	.name = (_name), \
> +	.signal_u32_read = (_get), \
> +	.signal_u32_write = (_set), \
> +	.priv = &(_available), \
> +}
> +
> +#define COUNTER_COMP_CEILING(_read, _write) \
> +	COUNTER_COMP_COUNT_U64("ceiling", _read, _write)
> +
> +#define COUNTER_COMP_COUNT_MODE(_read, _write, _available) \
> +{ \
> +	.type = COUNTER_COMP_COUNT_MODE, \
> +	.name = "count_mode", \
> +	.count_u32_read = (_read), \
> +	.count_u32_write = (_write), \
> +	.priv = &(_available), \
> +}
> +
> +#define COUNTER_COMP_DIRECTION(_read) \
> +{ \
> +	.type = COUNTER_COMP_COUNT_DIRECTION, \
> +	.name = "direction", \
> +	.count_u32_read = (_read), \
> +}
> +
> +#define COUNTER_COMP_ENABLE(_read, _write) \
> +	COUNTER_COMP_COUNT_BOOL("enable", _read, _write)
> +
> +#define COUNTER_COMP_FLOOR(_read, _write) \
> +	COUNTER_COMP_COUNT_U64("floor", _read, _write)
> +
> +#define COUNTER_COMP_PRESET(_read, _write) \
> +	COUNTER_COMP_COUNT_U64("preset", _read, _write)
> +
> +#define COUNTER_COMP_PRESET_ENABLE(_read, _write) \
> +	COUNTER_COMP_COUNT_BOOL("preset_enable", _read, _write)
>  
>  #endif /* _COUNTER_H_ */
> diff --git a/include/linux/counter_enum.h b/include/linux/counter_enum.h
> deleted file mode 100644
> index 9f917298a88f..000000000000
> --- a/include/linux/counter_enum.h
> +++ /dev/null
> @@ -1,45 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0 */
> -/*
> - * Counter interface enum functions
> - * Copyright (C) 2018 William Breathitt Gray
> - */
> -#ifndef _COUNTER_ENUM_H_
> -#define _COUNTER_ENUM_H_
> -
> -#include <linux/types.h>
> -
> -struct counter_device;
> -struct counter_signal;
> -struct counter_count;
> -
> -ssize_t counter_signal_enum_read(struct counter_device *counter,
> -				 struct counter_signal *signal, void *priv,
> -				 char *buf);
> -ssize_t counter_signal_enum_write(struct counter_device *counter,
> -				  struct counter_signal *signal, void *priv,
> -				  const char *buf, size_t len);
> -
> -ssize_t counter_signal_enum_available_read(struct counter_device *counter,
> -					   struct counter_signal *signal,
> -					   void *priv, char *buf);
> -
> -ssize_t counter_count_enum_read(struct counter_device *counter,
> -				struct counter_count *count, void *priv,
> -				char *buf);
> -ssize_t counter_count_enum_write(struct counter_device *counter,
> -				 struct counter_count *count, void *priv,
> -				 const char *buf, size_t len);
> -
> -ssize_t counter_count_enum_available_read(struct counter_device *counter,
> -					  struct counter_count *count,
> -					  void *priv, char *buf);
> -
> -ssize_t counter_device_enum_read(struct counter_device *counter, void *priv,
> -				 char *buf);
> -ssize_t counter_device_enum_write(struct counter_device *counter, void *priv,
> -				  const char *buf, size_t len);
> -
> -ssize_t counter_device_enum_available_read(struct counter_device *counter,
> -					   void *priv, char *buf);
> -
> -#endif /* _COUNTER_ENUM_H_ */
> 

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Linux-stm32] [PATCH v16 03/14] counter: Internalize sysfs interface code
  2021-08-31 13:44   ` [Linux-stm32] " Fabrice Gasnier
@ 2021-08-31 14:16       ` William Breathitt Gray
  2021-09-08 17:44     ` Jonathan Cameron
  1 sibling, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-31 14:16 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: jic23, kamel.bouhara, gwendal, david, linux-iio,
	patrick.havelange, alexandre.belloni, mcoquelin.stm32,
	linux-kernel, o.rempel, jarkko.nikula, Dan Carpenter, kernel,
	fabrice.gasnier, syednwaris, linux-stm32, linux-arm-kernel,
	alexandre.torgue

[-- Attachment #1: Type: text/plain, Size: 8813 bytes --]

On Tue, Aug 31, 2021 at 03:44:04PM +0200, Fabrice Gasnier wrote:
> On 8/27/21 5:47 AM, William Breathitt Gray wrote:
> > This is a reimplementation of the Generic Counter driver interface.
> > There are no modifications to the Counter subsystem userspace interface,
> > so existing userspace applications should continue to run seamlessly.
> > 
> > The purpose of this patch is to internalize the sysfs interface code
> > among the various counter drivers into a shared module. Counter drivers
> > pass and take data natively (i.e. u8, u64, etc.) and the shared counter
> > module handles the translation between the sysfs interface and the
> > device drivers. This guarantees a standard userspace interface for all
> > counter drivers, and helps generalize the Generic Counter driver ABI in
> > order to support the Generic Counter chrdev interface (introduced in a
> > subsequent patch) without significant changes to the existing counter
> > drivers.
> > 
> > Note, Counter device registration is the same as before: drivers
> > populate a struct counter_device with components and callbacks, then
> > pass the structure to the devm_counter_register function. However,
> > what's different now is how the Counter subsystem code handles this
> > registration internally.
> > 
> > Whereas before callbacks would interact directly with sysfs data, this
> > interaction is now abstracted and instead callbacks interact with native
> > C data types. The counter_comp structure forms the basis for Counter
> > extensions.
> > 
> > The counter-sysfs.c file contains the code to parse through the
> > counter_device structure and register the requested components and
> > extensions. Attributes are created and populated based on type, with
> > respective translation functions to handle the mapping between sysfs and
> > the counter driver callbacks.
> > 
> > The translation performed for each attribute is straightforward: the
> > attribute type and data is parsed from the counter_attribute structure,
> > the respective counter driver read/write callback is called, and sysfs
> > I/O is handled before or after the driver read/write function is called.
> > 
> > Cc: Jarkko Nikula <jarkko.nikula@linux.intel.com>
> > Cc: Patrick Havelange <patrick.havelange@essensium.com>
> > Cc: Kamel Bouhara <kamel.bouhara@bootlin.com>
> > Cc: Fabrice Gasnier <fabrice.gasnier@st.com>
> > Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com>
> > Cc: Alexandre Torgue <alexandre.torgue@st.com>
> > Cc: Dan Carpenter <dan.carpenter@oracle.com>
> > Acked-by: Syed Nayyar Waris <syednwaris@gmail.com>
> > Reviewed-by: David Lechner <david@lechnology.com>
> > Tested-by: David Lechner <david@lechnology.com>
> > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> > ---
> >  MAINTAINERS                             |    1 -
> >  drivers/counter/104-quad-8.c            |  449 +++----
> >  drivers/counter/Makefile                |    1 +
> >  drivers/counter/counter-core.c          |  142 +++
> >  drivers/counter/counter-sysfs.c         |  849 +++++++++++++
> >  drivers/counter/counter-sysfs.h         |   13 +
> >  drivers/counter/counter.c               | 1496 -----------------------
> >  drivers/counter/ftm-quaddec.c           |   60 +-
> >  drivers/counter/intel-qep.c             |  144 +--
> >  drivers/counter/interrupt-cnt.c         |   62 +-
> >  drivers/counter/microchip-tcb-capture.c |   91 +-
> >  drivers/counter/stm32-lptimer-cnt.c     |  212 ++--
> >  drivers/counter/stm32-timer-cnt.c       |  179 ++-
> >  drivers/counter/ti-eqep.c               |  180 +--
> >  include/linux/counter.h                 |  658 +++++-----
> >  include/linux/counter_enum.h            |   45 -
> >  16 files changed, 1949 insertions(+), 2633 deletions(-)
> >  create mode 100644 drivers/counter/counter-core.c
> >  create mode 100644 drivers/counter/counter-sysfs.c
> >  create mode 100644 drivers/counter/counter-sysfs.h
> >  delete mode 100644 drivers/counter/counter.c
> >  delete mode 100644 include/linux/counter_enum.h
> > 
> 
> Hi William,
> 
> For the STM32 drivers, you can add my:
> Reviewed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
> 
> Thanks,
> Fabrice

Hello Fabrice,

I noticed the stm32-lptimer-cnt.c function_write() and action_write()
callbacks do not appear to write the desired function/action
configuration to the device. Would you check whether the device actually
supports this functionality or if these callbacks should be removed from
this driver.

Thanks,

William Breathitt Gray

> > diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c
> > index 7367f46c6f91..5168833b1fdf 100644
> > --- a/drivers/counter/stm32-lptimer-cnt.c
> > +++ b/drivers/counter/stm32-lptimer-cnt.c
> > @@ -170,28 +154,28 @@ static int stm32_lptim_cnt_read(struct counter_device *counter,
> >  	return 0;
> >  }
> >  
> > -static int stm32_lptim_cnt_function_get(struct counter_device *counter,
> > -					struct counter_count *count,
> > -					size_t *function)
> > +static int stm32_lptim_cnt_function_read(struct counter_device *counter,
> > +					 struct counter_count *count,
> > +					 enum counter_function *function)
> >  {
> >  	struct stm32_lptim_cnt *const priv = counter->priv;
> >  
> >  	if (!priv->quadrature_mode) {
> > -		*function = STM32_LPTIM_COUNTER_INCREASE;
> > +		*function = COUNTER_FUNCTION_INCREASE;
> >  		return 0;
> >  	}
> >  
> > -	if (priv->polarity == STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES) {
> > -		*function = STM32_LPTIM_ENCODER_BOTH_EDGE;
> > +	if (priv->polarity == STM32_LPTIM_CKPOL_BOTH_EDGES) {
> > +		*function = COUNTER_FUNCTION_QUADRATURE_X4;
> >  		return 0;
> >  	}
> >  
> >  	return -EINVAL;
> >  }
> >  
> > -static int stm32_lptim_cnt_function_set(struct counter_device *counter,
> > -					struct counter_count *count,
> > -					size_t function)
> > +static int stm32_lptim_cnt_function_write(struct counter_device *counter,
> > +					  struct counter_count *count,
> > +					  enum counter_function function)
> >  {
> >  	struct stm32_lptim_cnt *const priv = counter->priv;
> >  
> > @@ -199,12 +183,12 @@ static int stm32_lptim_cnt_function_set(struct counter_device *counter,
> >  		return -EBUSY;
> >  
> >  	switch (function) {
> > -	case STM32_LPTIM_COUNTER_INCREASE:
> > +	case COUNTER_FUNCTION_INCREASE:
> >  		priv->quadrature_mode = 0;
> >  		return 0;
> > -	case STM32_LPTIM_ENCODER_BOTH_EDGE:
> > +	case COUNTER_FUNCTION_QUADRATURE_X4:
> >  		priv->quadrature_mode = 1;
> > -		priv->polarity = STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES;
> > +		priv->polarity = STM32_LPTIM_CKPOL_BOTH_EDGES;
> >  		return 0;
> >  	default:
> >  		/* should never reach this path */
> > @@ -333,43 +316,48 @@ static int stm32_lptim_cnt_action_get(struct counter_device *counter,
> >  	}
> >  }
> >  
> > -static int stm32_lptim_cnt_action_set(struct counter_device *counter,
> > -				      struct counter_count *count,
> > -				      struct counter_synapse *synapse,
> > -				      size_t action)
> > +static int stm32_lptim_cnt_action_write(struct counter_device *counter,
> > +					struct counter_count *count,
> > +					struct counter_synapse *synapse,
> > +					enum counter_synapse_action action)
> >  {
> >  	struct stm32_lptim_cnt *const priv = counter->priv;
> > -	size_t function;
> > +	enum counter_function function;
> >  	int err;
> >  
> >  	if (stm32_lptim_is_enabled(priv))
> >  		return -EBUSY;
> >  
> > -	err = stm32_lptim_cnt_function_get(counter, count, &function);
> > +	err = stm32_lptim_cnt_function_read(counter, count, &function);
> >  	if (err)
> >  		return err;
> >  
> >  	/* only set polarity when in counter mode (on input 1) */
> > -	if (function == STM32_LPTIM_COUNTER_INCREASE
> > -	    && synapse->signal->id == count->synapses[0].signal->id) {
> > -		switch (action) {
> > -		case STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE:
> > -		case STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE:
> > -		case STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES:
> > -			priv->polarity = action;
> > -			return 0;
> > -		}
> > -	}
> > +	if (function != COUNTER_FUNCTION_INCREASE
> > +	    || synapse->signal->id != count->synapses[0].signal->id)
> > +		return -EINVAL;
> >  
> > -	return -EINVAL;
> > +	switch (action) {
> > +	case COUNTER_SYNAPSE_ACTION_RISING_EDGE:
> > +		priv->polarity = STM32_LPTIM_CKPOL_RISING_EDGE;
> > +		return 0;
> > +	case COUNTER_SYNAPSE_ACTION_FALLING_EDGE:
> > +		priv->polarity = STM32_LPTIM_CKPOL_FALLING_EDGE;
> > +		return 0;
> > +	case COUNTER_SYNAPSE_ACTION_BOTH_EDGES:
> > +		priv->polarity = STM32_LPTIM_CKPOL_BOTH_EDGES;
> > +		return 0;
> > +	default:
> > +		return -EINVAL;
> > +	}
> >  }

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Linux-stm32] [PATCH v16 03/14] counter: Internalize sysfs interface code
@ 2021-08-31 14:16       ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-31 14:16 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: jic23, kamel.bouhara, gwendal, david, linux-iio,
	patrick.havelange, alexandre.belloni, mcoquelin.stm32,
	linux-kernel, o.rempel, jarkko.nikula, Dan Carpenter, kernel,
	fabrice.gasnier, syednwaris, linux-stm32, linux-arm-kernel,
	alexandre.torgue


[-- Attachment #1.1: Type: text/plain, Size: 8813 bytes --]

On Tue, Aug 31, 2021 at 03:44:04PM +0200, Fabrice Gasnier wrote:
> On 8/27/21 5:47 AM, William Breathitt Gray wrote:
> > This is a reimplementation of the Generic Counter driver interface.
> > There are no modifications to the Counter subsystem userspace interface,
> > so existing userspace applications should continue to run seamlessly.
> > 
> > The purpose of this patch is to internalize the sysfs interface code
> > among the various counter drivers into a shared module. Counter drivers
> > pass and take data natively (i.e. u8, u64, etc.) and the shared counter
> > module handles the translation between the sysfs interface and the
> > device drivers. This guarantees a standard userspace interface for all
> > counter drivers, and helps generalize the Generic Counter driver ABI in
> > order to support the Generic Counter chrdev interface (introduced in a
> > subsequent patch) without significant changes to the existing counter
> > drivers.
> > 
> > Note, Counter device registration is the same as before: drivers
> > populate a struct counter_device with components and callbacks, then
> > pass the structure to the devm_counter_register function. However,
> > what's different now is how the Counter subsystem code handles this
> > registration internally.
> > 
> > Whereas before callbacks would interact directly with sysfs data, this
> > interaction is now abstracted and instead callbacks interact with native
> > C data types. The counter_comp structure forms the basis for Counter
> > extensions.
> > 
> > The counter-sysfs.c file contains the code to parse through the
> > counter_device structure and register the requested components and
> > extensions. Attributes are created and populated based on type, with
> > respective translation functions to handle the mapping between sysfs and
> > the counter driver callbacks.
> > 
> > The translation performed for each attribute is straightforward: the
> > attribute type and data is parsed from the counter_attribute structure,
> > the respective counter driver read/write callback is called, and sysfs
> > I/O is handled before or after the driver read/write function is called.
> > 
> > Cc: Jarkko Nikula <jarkko.nikula@linux.intel.com>
> > Cc: Patrick Havelange <patrick.havelange@essensium.com>
> > Cc: Kamel Bouhara <kamel.bouhara@bootlin.com>
> > Cc: Fabrice Gasnier <fabrice.gasnier@st.com>
> > Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com>
> > Cc: Alexandre Torgue <alexandre.torgue@st.com>
> > Cc: Dan Carpenter <dan.carpenter@oracle.com>
> > Acked-by: Syed Nayyar Waris <syednwaris@gmail.com>
> > Reviewed-by: David Lechner <david@lechnology.com>
> > Tested-by: David Lechner <david@lechnology.com>
> > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> > ---
> >  MAINTAINERS                             |    1 -
> >  drivers/counter/104-quad-8.c            |  449 +++----
> >  drivers/counter/Makefile                |    1 +
> >  drivers/counter/counter-core.c          |  142 +++
> >  drivers/counter/counter-sysfs.c         |  849 +++++++++++++
> >  drivers/counter/counter-sysfs.h         |   13 +
> >  drivers/counter/counter.c               | 1496 -----------------------
> >  drivers/counter/ftm-quaddec.c           |   60 +-
> >  drivers/counter/intel-qep.c             |  144 +--
> >  drivers/counter/interrupt-cnt.c         |   62 +-
> >  drivers/counter/microchip-tcb-capture.c |   91 +-
> >  drivers/counter/stm32-lptimer-cnt.c     |  212 ++--
> >  drivers/counter/stm32-timer-cnt.c       |  179 ++-
> >  drivers/counter/ti-eqep.c               |  180 +--
> >  include/linux/counter.h                 |  658 +++++-----
> >  include/linux/counter_enum.h            |   45 -
> >  16 files changed, 1949 insertions(+), 2633 deletions(-)
> >  create mode 100644 drivers/counter/counter-core.c
> >  create mode 100644 drivers/counter/counter-sysfs.c
> >  create mode 100644 drivers/counter/counter-sysfs.h
> >  delete mode 100644 drivers/counter/counter.c
> >  delete mode 100644 include/linux/counter_enum.h
> > 
> 
> Hi William,
> 
> For the STM32 drivers, you can add my:
> Reviewed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
> 
> Thanks,
> Fabrice

Hello Fabrice,

I noticed the stm32-lptimer-cnt.c function_write() and action_write()
callbacks do not appear to write the desired function/action
configuration to the device. Would you check whether the device actually
supports this functionality or if these callbacks should be removed from
this driver.

Thanks,

William Breathitt Gray

> > diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c
> > index 7367f46c6f91..5168833b1fdf 100644
> > --- a/drivers/counter/stm32-lptimer-cnt.c
> > +++ b/drivers/counter/stm32-lptimer-cnt.c
> > @@ -170,28 +154,28 @@ static int stm32_lptim_cnt_read(struct counter_device *counter,
> >  	return 0;
> >  }
> >  
> > -static int stm32_lptim_cnt_function_get(struct counter_device *counter,
> > -					struct counter_count *count,
> > -					size_t *function)
> > +static int stm32_lptim_cnt_function_read(struct counter_device *counter,
> > +					 struct counter_count *count,
> > +					 enum counter_function *function)
> >  {
> >  	struct stm32_lptim_cnt *const priv = counter->priv;
> >  
> >  	if (!priv->quadrature_mode) {
> > -		*function = STM32_LPTIM_COUNTER_INCREASE;
> > +		*function = COUNTER_FUNCTION_INCREASE;
> >  		return 0;
> >  	}
> >  
> > -	if (priv->polarity == STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES) {
> > -		*function = STM32_LPTIM_ENCODER_BOTH_EDGE;
> > +	if (priv->polarity == STM32_LPTIM_CKPOL_BOTH_EDGES) {
> > +		*function = COUNTER_FUNCTION_QUADRATURE_X4;
> >  		return 0;
> >  	}
> >  
> >  	return -EINVAL;
> >  }
> >  
> > -static int stm32_lptim_cnt_function_set(struct counter_device *counter,
> > -					struct counter_count *count,
> > -					size_t function)
> > +static int stm32_lptim_cnt_function_write(struct counter_device *counter,
> > +					  struct counter_count *count,
> > +					  enum counter_function function)
> >  {
> >  	struct stm32_lptim_cnt *const priv = counter->priv;
> >  
> > @@ -199,12 +183,12 @@ static int stm32_lptim_cnt_function_set(struct counter_device *counter,
> >  		return -EBUSY;
> >  
> >  	switch (function) {
> > -	case STM32_LPTIM_COUNTER_INCREASE:
> > +	case COUNTER_FUNCTION_INCREASE:
> >  		priv->quadrature_mode = 0;
> >  		return 0;
> > -	case STM32_LPTIM_ENCODER_BOTH_EDGE:
> > +	case COUNTER_FUNCTION_QUADRATURE_X4:
> >  		priv->quadrature_mode = 1;
> > -		priv->polarity = STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES;
> > +		priv->polarity = STM32_LPTIM_CKPOL_BOTH_EDGES;
> >  		return 0;
> >  	default:
> >  		/* should never reach this path */
> > @@ -333,43 +316,48 @@ static int stm32_lptim_cnt_action_get(struct counter_device *counter,
> >  	}
> >  }
> >  
> > -static int stm32_lptim_cnt_action_set(struct counter_device *counter,
> > -				      struct counter_count *count,
> > -				      struct counter_synapse *synapse,
> > -				      size_t action)
> > +static int stm32_lptim_cnt_action_write(struct counter_device *counter,
> > +					struct counter_count *count,
> > +					struct counter_synapse *synapse,
> > +					enum counter_synapse_action action)
> >  {
> >  	struct stm32_lptim_cnt *const priv = counter->priv;
> > -	size_t function;
> > +	enum counter_function function;
> >  	int err;
> >  
> >  	if (stm32_lptim_is_enabled(priv))
> >  		return -EBUSY;
> >  
> > -	err = stm32_lptim_cnt_function_get(counter, count, &function);
> > +	err = stm32_lptim_cnt_function_read(counter, count, &function);
> >  	if (err)
> >  		return err;
> >  
> >  	/* only set polarity when in counter mode (on input 1) */
> > -	if (function == STM32_LPTIM_COUNTER_INCREASE
> > -	    && synapse->signal->id == count->synapses[0].signal->id) {
> > -		switch (action) {
> > -		case STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE:
> > -		case STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE:
> > -		case STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES:
> > -			priv->polarity = action;
> > -			return 0;
> > -		}
> > -	}
> > +	if (function != COUNTER_FUNCTION_INCREASE
> > +	    || synapse->signal->id != count->synapses[0].signal->id)
> > +		return -EINVAL;
> >  
> > -	return -EINVAL;
> > +	switch (action) {
> > +	case COUNTER_SYNAPSE_ACTION_RISING_EDGE:
> > +		priv->polarity = STM32_LPTIM_CKPOL_RISING_EDGE;
> > +		return 0;
> > +	case COUNTER_SYNAPSE_ACTION_FALLING_EDGE:
> > +		priv->polarity = STM32_LPTIM_CKPOL_FALLING_EDGE;
> > +		return 0;
> > +	case COUNTER_SYNAPSE_ACTION_BOTH_EDGES:
> > +		priv->polarity = STM32_LPTIM_CKPOL_BOTH_EDGES;
> > +		return 0;
> > +	default:
> > +		return -EINVAL;
> > +	}
> >  }

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Linux-stm32] [PATCH v16 03/14] counter: Internalize sysfs interface code
  2021-08-31 14:16       ` William Breathitt Gray
@ 2021-08-31 14:55         ` William Breathitt Gray
  -1 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-31 14:55 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: jic23, kamel.bouhara, gwendal, david, linux-iio,
	patrick.havelange, alexandre.belloni, mcoquelin.stm32,
	linux-kernel, o.rempel, jarkko.nikula, Dan Carpenter, kernel,
	fabrice.gasnier, syednwaris, linux-stm32, linux-arm-kernel,
	alexandre.torgue

[-- Attachment #1: Type: text/plain, Size: 5060 bytes --]

On Tue, Aug 31, 2021 at 11:16:59PM +0900, William Breathitt Gray wrote:
> On Tue, Aug 31, 2021 at 03:44:04PM +0200, Fabrice Gasnier wrote:
> > On 8/27/21 5:47 AM, William Breathitt Gray wrote:
> > > This is a reimplementation of the Generic Counter driver interface.
> > > There are no modifications to the Counter subsystem userspace interface,
> > > so existing userspace applications should continue to run seamlessly.
> > > 
> > > The purpose of this patch is to internalize the sysfs interface code
> > > among the various counter drivers into a shared module. Counter drivers
> > > pass and take data natively (i.e. u8, u64, etc.) and the shared counter
> > > module handles the translation between the sysfs interface and the
> > > device drivers. This guarantees a standard userspace interface for all
> > > counter drivers, and helps generalize the Generic Counter driver ABI in
> > > order to support the Generic Counter chrdev interface (introduced in a
> > > subsequent patch) without significant changes to the existing counter
> > > drivers.
> > > 
> > > Note, Counter device registration is the same as before: drivers
> > > populate a struct counter_device with components and callbacks, then
> > > pass the structure to the devm_counter_register function. However,
> > > what's different now is how the Counter subsystem code handles this
> > > registration internally.
> > > 
> > > Whereas before callbacks would interact directly with sysfs data, this
> > > interaction is now abstracted and instead callbacks interact with native
> > > C data types. The counter_comp structure forms the basis for Counter
> > > extensions.
> > > 
> > > The counter-sysfs.c file contains the code to parse through the
> > > counter_device structure and register the requested components and
> > > extensions. Attributes are created and populated based on type, with
> > > respective translation functions to handle the mapping between sysfs and
> > > the counter driver callbacks.
> > > 
> > > The translation performed for each attribute is straightforward: the
> > > attribute type and data is parsed from the counter_attribute structure,
> > > the respective counter driver read/write callback is called, and sysfs
> > > I/O is handled before or after the driver read/write function is called.
> > > 
> > > Cc: Jarkko Nikula <jarkko.nikula@linux.intel.com>
> > > Cc: Patrick Havelange <patrick.havelange@essensium.com>
> > > Cc: Kamel Bouhara <kamel.bouhara@bootlin.com>
> > > Cc: Fabrice Gasnier <fabrice.gasnier@st.com>
> > > Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com>
> > > Cc: Alexandre Torgue <alexandre.torgue@st.com>
> > > Cc: Dan Carpenter <dan.carpenter@oracle.com>
> > > Acked-by: Syed Nayyar Waris <syednwaris@gmail.com>
> > > Reviewed-by: David Lechner <david@lechnology.com>
> > > Tested-by: David Lechner <david@lechnology.com>
> > > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> > > ---
> > >  MAINTAINERS                             |    1 -
> > >  drivers/counter/104-quad-8.c            |  449 +++----
> > >  drivers/counter/Makefile                |    1 +
> > >  drivers/counter/counter-core.c          |  142 +++
> > >  drivers/counter/counter-sysfs.c         |  849 +++++++++++++
> > >  drivers/counter/counter-sysfs.h         |   13 +
> > >  drivers/counter/counter.c               | 1496 -----------------------
> > >  drivers/counter/ftm-quaddec.c           |   60 +-
> > >  drivers/counter/intel-qep.c             |  144 +--
> > >  drivers/counter/interrupt-cnt.c         |   62 +-
> > >  drivers/counter/microchip-tcb-capture.c |   91 +-
> > >  drivers/counter/stm32-lptimer-cnt.c     |  212 ++--
> > >  drivers/counter/stm32-timer-cnt.c       |  179 ++-
> > >  drivers/counter/ti-eqep.c               |  180 +--
> > >  include/linux/counter.h                 |  658 +++++-----
> > >  include/linux/counter_enum.h            |   45 -
> > >  16 files changed, 1949 insertions(+), 2633 deletions(-)
> > >  create mode 100644 drivers/counter/counter-core.c
> > >  create mode 100644 drivers/counter/counter-sysfs.c
> > >  create mode 100644 drivers/counter/counter-sysfs.h
> > >  delete mode 100644 drivers/counter/counter.c
> > >  delete mode 100644 include/linux/counter_enum.h
> > > 
> > 
> > Hi William,
> > 
> > For the STM32 drivers, you can add my:
> > Reviewed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
> > 
> > Thanks,
> > Fabrice
> 
> Hello Fabrice,
> 
> I noticed the stm32-lptimer-cnt.c function_write() and action_write()
> callbacks do not appear to write the desired function/action
> configuration to the device. Would you check whether the device actually
> supports this functionality or if these callbacks should be removed from
> this driver.
> 
> Thanks,
> 
> William Breathitt Gray

Nevermind, I see that these configurations are sent to the device via
stm32_lptim_setup() when the counter is enabled. So that setup should be
fine after all.

Thanks,

William Breathitt Gray

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Linux-stm32] [PATCH v16 03/14] counter: Internalize sysfs interface code
@ 2021-08-31 14:55         ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-08-31 14:55 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: jic23, kamel.bouhara, gwendal, david, linux-iio,
	patrick.havelange, alexandre.belloni, mcoquelin.stm32,
	linux-kernel, o.rempel, jarkko.nikula, Dan Carpenter, kernel,
	fabrice.gasnier, syednwaris, linux-stm32, linux-arm-kernel,
	alexandre.torgue


[-- Attachment #1.1: Type: text/plain, Size: 5060 bytes --]

On Tue, Aug 31, 2021 at 11:16:59PM +0900, William Breathitt Gray wrote:
> On Tue, Aug 31, 2021 at 03:44:04PM +0200, Fabrice Gasnier wrote:
> > On 8/27/21 5:47 AM, William Breathitt Gray wrote:
> > > This is a reimplementation of the Generic Counter driver interface.
> > > There are no modifications to the Counter subsystem userspace interface,
> > > so existing userspace applications should continue to run seamlessly.
> > > 
> > > The purpose of this patch is to internalize the sysfs interface code
> > > among the various counter drivers into a shared module. Counter drivers
> > > pass and take data natively (i.e. u8, u64, etc.) and the shared counter
> > > module handles the translation between the sysfs interface and the
> > > device drivers. This guarantees a standard userspace interface for all
> > > counter drivers, and helps generalize the Generic Counter driver ABI in
> > > order to support the Generic Counter chrdev interface (introduced in a
> > > subsequent patch) without significant changes to the existing counter
> > > drivers.
> > > 
> > > Note, Counter device registration is the same as before: drivers
> > > populate a struct counter_device with components and callbacks, then
> > > pass the structure to the devm_counter_register function. However,
> > > what's different now is how the Counter subsystem code handles this
> > > registration internally.
> > > 
> > > Whereas before callbacks would interact directly with sysfs data, this
> > > interaction is now abstracted and instead callbacks interact with native
> > > C data types. The counter_comp structure forms the basis for Counter
> > > extensions.
> > > 
> > > The counter-sysfs.c file contains the code to parse through the
> > > counter_device structure and register the requested components and
> > > extensions. Attributes are created and populated based on type, with
> > > respective translation functions to handle the mapping between sysfs and
> > > the counter driver callbacks.
> > > 
> > > The translation performed for each attribute is straightforward: the
> > > attribute type and data is parsed from the counter_attribute structure,
> > > the respective counter driver read/write callback is called, and sysfs
> > > I/O is handled before or after the driver read/write function is called.
> > > 
> > > Cc: Jarkko Nikula <jarkko.nikula@linux.intel.com>
> > > Cc: Patrick Havelange <patrick.havelange@essensium.com>
> > > Cc: Kamel Bouhara <kamel.bouhara@bootlin.com>
> > > Cc: Fabrice Gasnier <fabrice.gasnier@st.com>
> > > Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com>
> > > Cc: Alexandre Torgue <alexandre.torgue@st.com>
> > > Cc: Dan Carpenter <dan.carpenter@oracle.com>
> > > Acked-by: Syed Nayyar Waris <syednwaris@gmail.com>
> > > Reviewed-by: David Lechner <david@lechnology.com>
> > > Tested-by: David Lechner <david@lechnology.com>
> > > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> > > ---
> > >  MAINTAINERS                             |    1 -
> > >  drivers/counter/104-quad-8.c            |  449 +++----
> > >  drivers/counter/Makefile                |    1 +
> > >  drivers/counter/counter-core.c          |  142 +++
> > >  drivers/counter/counter-sysfs.c         |  849 +++++++++++++
> > >  drivers/counter/counter-sysfs.h         |   13 +
> > >  drivers/counter/counter.c               | 1496 -----------------------
> > >  drivers/counter/ftm-quaddec.c           |   60 +-
> > >  drivers/counter/intel-qep.c             |  144 +--
> > >  drivers/counter/interrupt-cnt.c         |   62 +-
> > >  drivers/counter/microchip-tcb-capture.c |   91 +-
> > >  drivers/counter/stm32-lptimer-cnt.c     |  212 ++--
> > >  drivers/counter/stm32-timer-cnt.c       |  179 ++-
> > >  drivers/counter/ti-eqep.c               |  180 +--
> > >  include/linux/counter.h                 |  658 +++++-----
> > >  include/linux/counter_enum.h            |   45 -
> > >  16 files changed, 1949 insertions(+), 2633 deletions(-)
> > >  create mode 100644 drivers/counter/counter-core.c
> > >  create mode 100644 drivers/counter/counter-sysfs.c
> > >  create mode 100644 drivers/counter/counter-sysfs.h
> > >  delete mode 100644 drivers/counter/counter.c
> > >  delete mode 100644 include/linux/counter_enum.h
> > > 
> > 
> > Hi William,
> > 
> > For the STM32 drivers, you can add my:
> > Reviewed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
> > 
> > Thanks,
> > Fabrice
> 
> Hello Fabrice,
> 
> I noticed the stm32-lptimer-cnt.c function_write() and action_write()
> callbacks do not appear to write the desired function/action
> configuration to the device. Would you check whether the device actually
> supports this functionality or if these callbacks should be removed from
> this driver.
> 
> Thanks,
> 
> William Breathitt Gray

Nevermind, I see that these configurations are sent to the device via
stm32_lptim_setup() when the counter is enabled. So that setup should be
fine after all.

Thanks,

William Breathitt Gray

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 01/14] counter: stm32-lptimer-cnt: Provide defines for clock polarities
  2021-08-31 13:38     ` Fabrice Gasnier
@ 2021-09-08 17:31       ` Jonathan Cameron
  -1 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-08 17:31 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: William Breathitt Gray, linux-stm32, kernel, a.fatoum,
	kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
	linux-kernel, linux-arm-kernel, syednwaris, patrick.havelange,
	fabrice.gasnier, mcoquelin.stm32, alexandre.torgue, o.rempel,
	jarkko.nikula

On Tue, 31 Aug 2021 15:38:50 +0200
Fabrice Gasnier <fabrice.gasnier@foss.st.com> wrote:

> On 8/27/21 5:47 AM, William Breathitt Gray wrote:
> > The STM32 low-power timer permits configuration of the clock polarity
> > via the LPTIMX_CFGR register CKPOL bits. This patch provides
> > preprocessor defines for the supported clock polarities.
> > 
> > Cc: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
> > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> > ---
> >  drivers/counter/stm32-lptimer-cnt.c | 6 +++---
> >  include/linux/mfd/stm32-lptimer.h   | 5 +++++
> >  2 files changed, 8 insertions(+), 3 deletions(-)  
> 
> Hi William,
> 
> You can add my:
> Reviewed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Applied to the togreg branch of iio.git and push out as testing for all the normal reasons

> 
> Thanks,
> Fabrice
> 
> > 
> > diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c
> > index 13656957c45f..7367f46c6f91 100644
> > --- a/drivers/counter/stm32-lptimer-cnt.c
> > +++ b/drivers/counter/stm32-lptimer-cnt.c
> > @@ -140,9 +140,9 @@ static const enum counter_function stm32_lptim_cnt_functions[] = {
> >  };
> >  
> >  enum stm32_lptim_synapse_action {
> > -	STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE,
> > -	STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE,
> > -	STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES,
> > +	STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE = STM32_LPTIM_CKPOL_RISING_EDGE,
> > +	STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE = STM32_LPTIM_CKPOL_FALLING_EDGE,
> > +	STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES = STM32_LPTIM_CKPOL_BOTH_EDGES,
> >  	STM32_LPTIM_SYNAPSE_ACTION_NONE,
> >  };
> >  
> > diff --git a/include/linux/mfd/stm32-lptimer.h b/include/linux/mfd/stm32-lptimer.h
> > index 90b20550c1c8..06d3f11dc3c9 100644
> > --- a/include/linux/mfd/stm32-lptimer.h
> > +++ b/include/linux/mfd/stm32-lptimer.h
> > @@ -45,6 +45,11 @@
> >  #define STM32_LPTIM_PRESC	GENMASK(11, 9)
> >  #define STM32_LPTIM_CKPOL	GENMASK(2, 1)
> >  
> > +/* STM32_LPTIM_CKPOL */
> > +#define STM32_LPTIM_CKPOL_RISING_EDGE	0
> > +#define STM32_LPTIM_CKPOL_FALLING_EDGE	1
> > +#define STM32_LPTIM_CKPOL_BOTH_EDGES	2
> > +
> >  /* STM32_LPTIM_ARR */
> >  #define STM32_LPTIM_MAX_ARR	0xFFFF
> >  
> >   


^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 01/14] counter: stm32-lptimer-cnt: Provide defines for clock polarities
@ 2021-09-08 17:31       ` Jonathan Cameron
  0 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-08 17:31 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: William Breathitt Gray, linux-stm32, kernel, a.fatoum,
	kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
	linux-kernel, linux-arm-kernel, syednwaris, patrick.havelange,
	fabrice.gasnier, mcoquelin.stm32, alexandre.torgue, o.rempel,
	jarkko.nikula

On Tue, 31 Aug 2021 15:38:50 +0200
Fabrice Gasnier <fabrice.gasnier@foss.st.com> wrote:

> On 8/27/21 5:47 AM, William Breathitt Gray wrote:
> > The STM32 low-power timer permits configuration of the clock polarity
> > via the LPTIMX_CFGR register CKPOL bits. This patch provides
> > preprocessor defines for the supported clock polarities.
> > 
> > Cc: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
> > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> > ---
> >  drivers/counter/stm32-lptimer-cnt.c | 6 +++---
> >  include/linux/mfd/stm32-lptimer.h   | 5 +++++
> >  2 files changed, 8 insertions(+), 3 deletions(-)  
> 
> Hi William,
> 
> You can add my:
> Reviewed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Applied to the togreg branch of iio.git and push out as testing for all the normal reasons

> 
> Thanks,
> Fabrice
> 
> > 
> > diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c
> > index 13656957c45f..7367f46c6f91 100644
> > --- a/drivers/counter/stm32-lptimer-cnt.c
> > +++ b/drivers/counter/stm32-lptimer-cnt.c
> > @@ -140,9 +140,9 @@ static const enum counter_function stm32_lptim_cnt_functions[] = {
> >  };
> >  
> >  enum stm32_lptim_synapse_action {
> > -	STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE,
> > -	STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE,
> > -	STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES,
> > +	STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE = STM32_LPTIM_CKPOL_RISING_EDGE,
> > +	STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE = STM32_LPTIM_CKPOL_FALLING_EDGE,
> > +	STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES = STM32_LPTIM_CKPOL_BOTH_EDGES,
> >  	STM32_LPTIM_SYNAPSE_ACTION_NONE,
> >  };
> >  
> > diff --git a/include/linux/mfd/stm32-lptimer.h b/include/linux/mfd/stm32-lptimer.h
> > index 90b20550c1c8..06d3f11dc3c9 100644
> > --- a/include/linux/mfd/stm32-lptimer.h
> > +++ b/include/linux/mfd/stm32-lptimer.h
> > @@ -45,6 +45,11 @@
> >  #define STM32_LPTIM_PRESC	GENMASK(11, 9)
> >  #define STM32_LPTIM_CKPOL	GENMASK(2, 1)
> >  
> > +/* STM32_LPTIM_CKPOL */
> > +#define STM32_LPTIM_CKPOL_RISING_EDGE	0
> > +#define STM32_LPTIM_CKPOL_FALLING_EDGE	1
> > +#define STM32_LPTIM_CKPOL_BOTH_EDGES	2
> > +
> >  /* STM32_LPTIM_ARR */
> >  #define STM32_LPTIM_MAX_ARR	0xFFFF
> >  
> >   


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 02/14] counter: stm32-timer-cnt: Provide defines for slave mode selection
  2021-08-31 13:40     ` Fabrice Gasnier
@ 2021-09-08 17:31       ` Jonathan Cameron
  -1 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-08 17:31 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: William Breathitt Gray, linux-stm32, kernel, a.fatoum,
	kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
	linux-kernel, linux-arm-kernel, syednwaris, patrick.havelange,
	fabrice.gasnier, mcoquelin.stm32, alexandre.torgue, o.rempel,
	jarkko.nikula

On Tue, 31 Aug 2021 15:40:37 +0200
Fabrice Gasnier <fabrice.gasnier@foss.st.com> wrote:

> On 8/27/21 5:47 AM, William Breathitt Gray wrote:
> > The STM32 timer permits configuration of the counter encoder mode via
> > the slave mode control register (SMCR) slave mode selection (SMS) bits.
> > This patch provides preprocessor defines for the supported encoder
> > modes.
> > 
> > Cc: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
> > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> > ---
> >  drivers/counter/stm32-timer-cnt.c | 16 ++++++++--------
> >  include/linux/mfd/stm32-timers.h  |  4 ++++
> >  2 files changed, 12 insertions(+), 8 deletions(-)  
> 
> Hi William,
> 
> You can add my:
> Reviewed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Applied.

Thanks,

J
> 
> Thanks,
> Fabrice
> 
> > 
> > diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c
> > index 3fb0debd7425..1fbc46f4ee66 100644
> > --- a/drivers/counter/stm32-timer-cnt.c
> > +++ b/drivers/counter/stm32-timer-cnt.c
> > @@ -93,16 +93,16 @@ static int stm32_count_function_get(struct counter_device *counter,
> >  	regmap_read(priv->regmap, TIM_SMCR, &smcr);
> >  
> >  	switch (smcr & TIM_SMCR_SMS) {
> > -	case 0:
> > +	case TIM_SMCR_SMS_SLAVE_MODE_DISABLED:
> >  		*function = STM32_COUNT_SLAVE_MODE_DISABLED;
> >  		return 0;
> > -	case 1:
> > +	case TIM_SMCR_SMS_ENCODER_MODE_1:
> >  		*function = STM32_COUNT_ENCODER_MODE_1;
> >  		return 0;
> > -	case 2:
> > +	case TIM_SMCR_SMS_ENCODER_MODE_2:
> >  		*function = STM32_COUNT_ENCODER_MODE_2;
> >  		return 0;
> > -	case 3:
> > +	case TIM_SMCR_SMS_ENCODER_MODE_3:
> >  		*function = STM32_COUNT_ENCODER_MODE_3;
> >  		return 0;
> >  	default:
> > @@ -119,16 +119,16 @@ static int stm32_count_function_set(struct counter_device *counter,
> >  
> >  	switch (function) {
> >  	case STM32_COUNT_SLAVE_MODE_DISABLED:
> > -		sms = 0;
> > +		sms = TIM_SMCR_SMS_SLAVE_MODE_DISABLED;
> >  		break;
> >  	case STM32_COUNT_ENCODER_MODE_1:
> > -		sms = 1;
> > +		sms = TIM_SMCR_SMS_ENCODER_MODE_1;
> >  		break;
> >  	case STM32_COUNT_ENCODER_MODE_2:
> > -		sms = 2;
> > +		sms = TIM_SMCR_SMS_ENCODER_MODE_2;
> >  		break;
> >  	case STM32_COUNT_ENCODER_MODE_3:
> > -		sms = 3;
> > +		sms = TIM_SMCR_SMS_ENCODER_MODE_3;
> >  		break;
> >  	default:
> >  		return -EINVAL;
> > diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h
> > index f8db83aedb2b..5f5c43fd69dd 100644
> > --- a/include/linux/mfd/stm32-timers.h
> > +++ b/include/linux/mfd/stm32-timers.h
> > @@ -82,6 +82,10 @@
> >  #define MAX_TIM_ICPSC		0x3
> >  #define TIM_CR2_MMS_SHIFT	4
> >  #define TIM_CR2_MMS2_SHIFT	20
> > +#define TIM_SMCR_SMS_SLAVE_MODE_DISABLED	0 /* counts on internal clock when CEN=1 */
> > +#define TIM_SMCR_SMS_ENCODER_MODE_1		1 /* counts TI1FP1 edges, depending on TI2FP2 level */
> > +#define TIM_SMCR_SMS_ENCODER_MODE_2		2 /* counts TI2FP2 edges, depending on TI1FP1 level */
> > +#define TIM_SMCR_SMS_ENCODER_MODE_3		3 /* counts on both TI1FP1 and TI2FP2 edges */
> >  #define TIM_SMCR_TS_SHIFT	4
> >  #define TIM_BDTR_BKF_MASK	0xF
> >  #define TIM_BDTR_BKF_SHIFT(x)	(16 + (x) * 4)
> >   


^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 02/14] counter: stm32-timer-cnt: Provide defines for slave mode selection
@ 2021-09-08 17:31       ` Jonathan Cameron
  0 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-08 17:31 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: William Breathitt Gray, linux-stm32, kernel, a.fatoum,
	kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
	linux-kernel, linux-arm-kernel, syednwaris, patrick.havelange,
	fabrice.gasnier, mcoquelin.stm32, alexandre.torgue, o.rempel,
	jarkko.nikula

On Tue, 31 Aug 2021 15:40:37 +0200
Fabrice Gasnier <fabrice.gasnier@foss.st.com> wrote:

> On 8/27/21 5:47 AM, William Breathitt Gray wrote:
> > The STM32 timer permits configuration of the counter encoder mode via
> > the slave mode control register (SMCR) slave mode selection (SMS) bits.
> > This patch provides preprocessor defines for the supported encoder
> > modes.
> > 
> > Cc: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
> > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> > ---
> >  drivers/counter/stm32-timer-cnt.c | 16 ++++++++--------
> >  include/linux/mfd/stm32-timers.h  |  4 ++++
> >  2 files changed, 12 insertions(+), 8 deletions(-)  
> 
> Hi William,
> 
> You can add my:
> Reviewed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Applied.

Thanks,

J
> 
> Thanks,
> Fabrice
> 
> > 
> > diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c
> > index 3fb0debd7425..1fbc46f4ee66 100644
> > --- a/drivers/counter/stm32-timer-cnt.c
> > +++ b/drivers/counter/stm32-timer-cnt.c
> > @@ -93,16 +93,16 @@ static int stm32_count_function_get(struct counter_device *counter,
> >  	regmap_read(priv->regmap, TIM_SMCR, &smcr);
> >  
> >  	switch (smcr & TIM_SMCR_SMS) {
> > -	case 0:
> > +	case TIM_SMCR_SMS_SLAVE_MODE_DISABLED:
> >  		*function = STM32_COUNT_SLAVE_MODE_DISABLED;
> >  		return 0;
> > -	case 1:
> > +	case TIM_SMCR_SMS_ENCODER_MODE_1:
> >  		*function = STM32_COUNT_ENCODER_MODE_1;
> >  		return 0;
> > -	case 2:
> > +	case TIM_SMCR_SMS_ENCODER_MODE_2:
> >  		*function = STM32_COUNT_ENCODER_MODE_2;
> >  		return 0;
> > -	case 3:
> > +	case TIM_SMCR_SMS_ENCODER_MODE_3:
> >  		*function = STM32_COUNT_ENCODER_MODE_3;
> >  		return 0;
> >  	default:
> > @@ -119,16 +119,16 @@ static int stm32_count_function_set(struct counter_device *counter,
> >  
> >  	switch (function) {
> >  	case STM32_COUNT_SLAVE_MODE_DISABLED:
> > -		sms = 0;
> > +		sms = TIM_SMCR_SMS_SLAVE_MODE_DISABLED;
> >  		break;
> >  	case STM32_COUNT_ENCODER_MODE_1:
> > -		sms = 1;
> > +		sms = TIM_SMCR_SMS_ENCODER_MODE_1;
> >  		break;
> >  	case STM32_COUNT_ENCODER_MODE_2:
> > -		sms = 2;
> > +		sms = TIM_SMCR_SMS_ENCODER_MODE_2;
> >  		break;
> >  	case STM32_COUNT_ENCODER_MODE_3:
> > -		sms = 3;
> > +		sms = TIM_SMCR_SMS_ENCODER_MODE_3;
> >  		break;
> >  	default:
> >  		return -EINVAL;
> > diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h
> > index f8db83aedb2b..5f5c43fd69dd 100644
> > --- a/include/linux/mfd/stm32-timers.h
> > +++ b/include/linux/mfd/stm32-timers.h
> > @@ -82,6 +82,10 @@
> >  #define MAX_TIM_ICPSC		0x3
> >  #define TIM_CR2_MMS_SHIFT	4
> >  #define TIM_CR2_MMS2_SHIFT	20
> > +#define TIM_SMCR_SMS_SLAVE_MODE_DISABLED	0 /* counts on internal clock when CEN=1 */
> > +#define TIM_SMCR_SMS_ENCODER_MODE_1		1 /* counts TI1FP1 edges, depending on TI2FP2 level */
> > +#define TIM_SMCR_SMS_ENCODER_MODE_2		2 /* counts TI2FP2 edges, depending on TI1FP1 level */
> > +#define TIM_SMCR_SMS_ENCODER_MODE_3		3 /* counts on both TI1FP1 and TI2FP2 edges */
> >  #define TIM_SMCR_TS_SHIFT	4
> >  #define TIM_BDTR_BKF_MASK	0xF
> >  #define TIM_BDTR_BKF_SHIFT(x)	(16 + (x) * 4)
> >   


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Linux-stm32] [PATCH v16 03/14] counter: Internalize sysfs interface code
  2021-08-31 13:44   ` [Linux-stm32] " Fabrice Gasnier
  2021-08-31 14:16       ` William Breathitt Gray
@ 2021-09-08 17:44     ` Jonathan Cameron
  1 sibling, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-08 17:44 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: William Breathitt Gray, kamel.bouhara, gwendal, david, linux-iio,
	patrick.havelange, alexandre.belloni, mcoquelin.stm32,
	linux-kernel, o.rempel, jarkko.nikula, Dan Carpenter, kernel,
	fabrice.gasnier, syednwaris, linux-stm32, linux-arm-kernel,
	alexandre.torgue

On Tue, 31 Aug 2021 15:44:04 +0200
Fabrice Gasnier <fabrice.gasnier@foss.st.com> wrote:

> On 8/27/21 5:47 AM, William Breathitt Gray wrote:
> > This is a reimplementation of the Generic Counter driver interface.
> > There are no modifications to the Counter subsystem userspace interface,
> > so existing userspace applications should continue to run seamlessly.
> > 
> > The purpose of this patch is to internalize the sysfs interface code
> > among the various counter drivers into a shared module. Counter drivers
> > pass and take data natively (i.e. u8, u64, etc.) and the shared counter
> > module handles the translation between the sysfs interface and the
> > device drivers. This guarantees a standard userspace interface for all
> > counter drivers, and helps generalize the Generic Counter driver ABI in
> > order to support the Generic Counter chrdev interface (introduced in a
> > subsequent patch) without significant changes to the existing counter
> > drivers.
> > 
> > Note, Counter device registration is the same as before: drivers
> > populate a struct counter_device with components and callbacks, then
> > pass the structure to the devm_counter_register function. However,
> > what's different now is how the Counter subsystem code handles this
> > registration internally.
> > 
> > Whereas before callbacks would interact directly with sysfs data, this
> > interaction is now abstracted and instead callbacks interact with native
> > C data types. The counter_comp structure forms the basis for Counter
> > extensions.
> > 
> > The counter-sysfs.c file contains the code to parse through the
> > counter_device structure and register the requested components and
> > extensions. Attributes are created and populated based on type, with
> > respective translation functions to handle the mapping between sysfs and
> > the counter driver callbacks.
> > 
> > The translation performed for each attribute is straightforward: the
> > attribute type and data is parsed from the counter_attribute structure,
> > the respective counter driver read/write callback is called, and sysfs
> > I/O is handled before or after the driver read/write function is called.
> > 
> > Cc: Jarkko Nikula <jarkko.nikula@linux.intel.com>
> > Cc: Patrick Havelange <patrick.havelange@essensium.com>
> > Cc: Kamel Bouhara <kamel.bouhara@bootlin.com>
> > Cc: Fabrice Gasnier <fabrice.gasnier@st.com>
> > Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com>
> > Cc: Alexandre Torgue <alexandre.torgue@st.com>
> > Cc: Dan Carpenter <dan.carpenter@oracle.com>
> > Acked-by: Syed Nayyar Waris <syednwaris@gmail.com>
> > Reviewed-by: David Lechner <david@lechnology.com>
> > Tested-by: David Lechner <david@lechnology.com>
> > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> > ---
> >  MAINTAINERS                             |    1 -
> >  drivers/counter/104-quad-8.c            |  449 +++----
> >  drivers/counter/Makefile                |    1 +
> >  drivers/counter/counter-core.c          |  142 +++
> >  drivers/counter/counter-sysfs.c         |  849 +++++++++++++
> >  drivers/counter/counter-sysfs.h         |   13 +
> >  drivers/counter/counter.c               | 1496 -----------------------
> >  drivers/counter/ftm-quaddec.c           |   60 +-
> >  drivers/counter/intel-qep.c             |  144 +--
> >  drivers/counter/interrupt-cnt.c         |   62 +-
> >  drivers/counter/microchip-tcb-capture.c |   91 +-
> >  drivers/counter/stm32-lptimer-cnt.c     |  212 ++--
> >  drivers/counter/stm32-timer-cnt.c       |  179 ++-
> >  drivers/counter/ti-eqep.c               |  180 +--
> >  include/linux/counter.h                 |  658 +++++-----
> >  include/linux/counter_enum.h            |   45 -
> >  16 files changed, 1949 insertions(+), 2633 deletions(-)
> >  create mode 100644 drivers/counter/counter-core.c
> >  create mode 100644 drivers/counter/counter-sysfs.c
> >  create mode 100644 drivers/counter/counter-sysfs.h
> >  delete mode 100644 drivers/counter/counter.c
> >  delete mode 100644 include/linux/counter_enum.h
> >   
> 
> Hi William,
> 
> For the STM32 drivers, you can add my:
> Reviewed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>

Thanks Fabrice!

Looks like the only way we are going to move forward with this
is to just get on and apply it then see if we get any breakage
reports.

So, applied to the togreg branch of iio.git and pushed out as testing
for 0-day to poke at it.

Thank,

Jonathan

> 
> Thanks,
> Fabrice
> 
> 
> 
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 212bad85e07a..b0cdb9aaca85 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -4781,7 +4781,6 @@ F:	Documentation/ABI/testing/sysfs-bus-counter
> >  F:	Documentation/driver-api/generic-counter.rst
> >  F:	drivers/counter/
> >  F:	include/linux/counter.h
> > -F:	include/linux/counter_enum.h
> >  
> >  CP2615 I2C DRIVER
> >  M:	Bence Csókás <bence98@sch.bme.hu>
> > diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c
> > index 0caa60537b14..c587f295d720 100644
> > --- a/drivers/counter/104-quad-8.c
> > +++ b/drivers/counter/104-quad-8.c
> > @@ -117,7 +117,7 @@ static int quad8_signal_read(struct counter_device *counter,
> >  }
> >  
> >  static int quad8_count_read(struct counter_device *counter,
> > -	struct counter_count *count, unsigned long *val)
> > +			    struct counter_count *count, u64 *val)
> >  {
> >  	struct quad8 *const priv = counter->priv;
> >  	const int base_offset = priv->base + 2 * count->id;
> > @@ -148,7 +148,7 @@ static int quad8_count_read(struct counter_device *counter,
> >  }
> >  
> >  static int quad8_count_write(struct counter_device *counter,
> > -	struct counter_count *count, unsigned long val)
> > +			     struct counter_count *count, u64 val)
> >  {
> >  	struct quad8 *const priv = counter->priv;
> >  	const int base_offset = priv->base + 2 * count->id;
> > @@ -188,22 +188,16 @@ static int quad8_count_write(struct counter_device *counter,
> >  	return 0;
> >  }
> >  
> > -enum quad8_count_function {
> > -	QUAD8_COUNT_FUNCTION_PULSE_DIRECTION = 0,
> > -	QUAD8_COUNT_FUNCTION_QUADRATURE_X1,
> > -	QUAD8_COUNT_FUNCTION_QUADRATURE_X2,
> > -	QUAD8_COUNT_FUNCTION_QUADRATURE_X4
> > -};
> > -
> >  static const enum counter_function quad8_count_functions_list[] = {
> > -	[QUAD8_COUNT_FUNCTION_PULSE_DIRECTION] = COUNTER_FUNCTION_PULSE_DIRECTION,
> > -	[QUAD8_COUNT_FUNCTION_QUADRATURE_X1] = COUNTER_FUNCTION_QUADRATURE_X1_A,
> > -	[QUAD8_COUNT_FUNCTION_QUADRATURE_X2] = COUNTER_FUNCTION_QUADRATURE_X2_A,
> > -	[QUAD8_COUNT_FUNCTION_QUADRATURE_X4] = COUNTER_FUNCTION_QUADRATURE_X4
> > +	COUNTER_FUNCTION_PULSE_DIRECTION,
> > +	COUNTER_FUNCTION_QUADRATURE_X1_A,
> > +	COUNTER_FUNCTION_QUADRATURE_X2_A,
> > +	COUNTER_FUNCTION_QUADRATURE_X4,
> >  };
> >  
> > -static int quad8_function_get(struct counter_device *counter,
> > -	struct counter_count *count, size_t *function)
> > +static int quad8_function_read(struct counter_device *counter,
> > +			       struct counter_count *count,
> > +			       enum counter_function *function)
> >  {
> >  	struct quad8 *const priv = counter->priv;
> >  	const int id = count->id;
> > @@ -213,25 +207,26 @@ static int quad8_function_get(struct counter_device *counter,
> >  	if (priv->quadrature_mode[id])
> >  		switch (priv->quadrature_scale[id]) {
> >  		case 0:
> > -			*function = QUAD8_COUNT_FUNCTION_QUADRATURE_X1;
> > +			*function = COUNTER_FUNCTION_QUADRATURE_X1_A;
> >  			break;
> >  		case 1:
> > -			*function = QUAD8_COUNT_FUNCTION_QUADRATURE_X2;
> > +			*function = COUNTER_FUNCTION_QUADRATURE_X2_A;
> >  			break;
> >  		case 2:
> > -			*function = QUAD8_COUNT_FUNCTION_QUADRATURE_X4;
> > +			*function = COUNTER_FUNCTION_QUADRATURE_X4;
> >  			break;
> >  		}
> >  	else
> > -		*function = QUAD8_COUNT_FUNCTION_PULSE_DIRECTION;
> > +		*function = COUNTER_FUNCTION_PULSE_DIRECTION;
> >  
> >  	mutex_unlock(&priv->lock);
> >  
> >  	return 0;
> >  }
> >  
> > -static int quad8_function_set(struct counter_device *counter,
> > -	struct counter_count *count, size_t function)
> > +static int quad8_function_write(struct counter_device *counter,
> > +				struct counter_count *count,
> > +				enum counter_function function)
> >  {
> >  	struct quad8 *const priv = counter->priv;
> >  	const int id = count->id;
> > @@ -247,7 +242,7 @@ static int quad8_function_set(struct counter_device *counter,
> >  	mode_cfg = priv->count_mode[id] << 1;
> >  	idr_cfg = priv->index_polarity[id] << 1;
> >  
> > -	if (function == QUAD8_COUNT_FUNCTION_PULSE_DIRECTION) {
> > +	if (function == COUNTER_FUNCTION_PULSE_DIRECTION) {
> >  		*quadrature_mode = 0;
> >  
> >  		/* Quadrature scaling only available in quadrature mode */
> > @@ -263,15 +258,15 @@ static int quad8_function_set(struct counter_device *counter,
> >  		*quadrature_mode = 1;
> >  
> >  		switch (function) {
> > -		case QUAD8_COUNT_FUNCTION_QUADRATURE_X1:
> > +		case COUNTER_FUNCTION_QUADRATURE_X1_A:
> >  			*scale = 0;
> >  			mode_cfg |= QUAD8_CMR_QUADRATURE_X1;
> >  			break;
> > -		case QUAD8_COUNT_FUNCTION_QUADRATURE_X2:
> > +		case COUNTER_FUNCTION_QUADRATURE_X2_A:
> >  			*scale = 1;
> >  			mode_cfg |= QUAD8_CMR_QUADRATURE_X2;
> >  			break;
> > -		case QUAD8_COUNT_FUNCTION_QUADRATURE_X4:
> > +		case COUNTER_FUNCTION_QUADRATURE_X4:
> >  			*scale = 2;
> >  			mode_cfg |= QUAD8_CMR_QUADRATURE_X4;
> >  			break;
> > @@ -290,8 +285,9 @@ static int quad8_function_set(struct counter_device *counter,
> >  	return 0;
> >  }
> >  
> > -static void quad8_direction_get(struct counter_device *counter,
> > -	struct counter_count *count, enum counter_count_direction *direction)
> > +static int quad8_direction_read(struct counter_device *counter,
> > +				struct counter_count *count,
> > +				enum counter_count_direction *direction)
> >  {
> >  	const struct quad8 *const priv = counter->priv;
> >  	unsigned int ud_flag;
> > @@ -302,76 +298,74 @@ static void quad8_direction_get(struct counter_device *counter,
> >  
> >  	*direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD :
> >  		COUNTER_COUNT_DIRECTION_BACKWARD;
> > -}
> >  
> > -enum quad8_synapse_action {
> > -	QUAD8_SYNAPSE_ACTION_NONE = 0,
> > -	QUAD8_SYNAPSE_ACTION_RISING_EDGE,
> > -	QUAD8_SYNAPSE_ACTION_FALLING_EDGE,
> > -	QUAD8_SYNAPSE_ACTION_BOTH_EDGES
> > -};
> > +	return 0;
> > +}
> >  
> >  static const enum counter_synapse_action quad8_index_actions_list[] = {
> > -	[QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
> > -	[QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE
> > +	COUNTER_SYNAPSE_ACTION_NONE,
> > +	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> >  };
> >  
> >  static const enum counter_synapse_action quad8_synapse_actions_list[] = {
> > -	[QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
> > -	[QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> > -	[QUAD8_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
> > -	[QUAD8_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES
> > +	COUNTER_SYNAPSE_ACTION_NONE,
> > +	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> > +	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
> > +	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
> >  };
> >  
> > -static int quad8_action_get(struct counter_device *counter,
> > -	struct counter_count *count, struct counter_synapse *synapse,
> > -	size_t *action)
> > +static int quad8_action_read(struct counter_device *counter,
> > +			     struct counter_count *count,
> > +			     struct counter_synapse *synapse,
> > +			     enum counter_synapse_action *action)
> >  {
> >  	struct quad8 *const priv = counter->priv;
> >  	int err;
> > -	size_t function = 0;
> > +	enum counter_function function;
> >  	const size_t signal_a_id = count->synapses[0].signal->id;
> >  	enum counter_count_direction direction;
> >  
> >  	/* Handle Index signals */
> >  	if (synapse->signal->id >= 16) {
> >  		if (priv->preset_enable[count->id])
> > -			*action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
> > +			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
> >  		else
> > -			*action = QUAD8_SYNAPSE_ACTION_NONE;
> > +			*action = COUNTER_SYNAPSE_ACTION_NONE;
> >  
> >  		return 0;
> >  	}
> >  
> > -	err = quad8_function_get(counter, count, &function);
> > +	err = quad8_function_read(counter, count, &function);
> >  	if (err)
> >  		return err;
> >  
> >  	/* Default action mode */
> > -	*action = QUAD8_SYNAPSE_ACTION_NONE;
> > +	*action = COUNTER_SYNAPSE_ACTION_NONE;
> >  
> >  	/* Determine action mode based on current count function mode */
> >  	switch (function) {
> > -	case QUAD8_COUNT_FUNCTION_PULSE_DIRECTION:
> > +	case COUNTER_FUNCTION_PULSE_DIRECTION:
> >  		if (synapse->signal->id == signal_a_id)
> > -			*action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
> > +			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
> >  		return 0;
> > -	case QUAD8_COUNT_FUNCTION_QUADRATURE_X1:
> > +	case COUNTER_FUNCTION_QUADRATURE_X1_A:
> >  		if (synapse->signal->id == signal_a_id) {
> > -			quad8_direction_get(counter, count, &direction);
> > +			err = quad8_direction_read(counter, count, &direction);
> > +			if (err)
> > +				return err;
> >  
> >  			if (direction == COUNTER_COUNT_DIRECTION_FORWARD)
> > -				*action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
> > +				*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
> >  			else
> > -				*action = QUAD8_SYNAPSE_ACTION_FALLING_EDGE;
> > +				*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
> >  		}
> >  		return 0;
> > -	case QUAD8_COUNT_FUNCTION_QUADRATURE_X2:
> > +	case COUNTER_FUNCTION_QUADRATURE_X2_A:
> >  		if (synapse->signal->id == signal_a_id)
> > -			*action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES;
> > +			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
> >  		return 0;
> > -	case QUAD8_COUNT_FUNCTION_QUADRATURE_X4:
> > -		*action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES;
> > +	case COUNTER_FUNCTION_QUADRATURE_X4:
> > +		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
> >  		return 0;
> >  	default:
> >  		/* should never reach this path */
> > @@ -383,9 +377,9 @@ static const struct counter_ops quad8_ops = {
> >  	.signal_read = quad8_signal_read,
> >  	.count_read = quad8_count_read,
> >  	.count_write = quad8_count_write,
> > -	.function_get = quad8_function_get,
> > -	.function_set = quad8_function_set,
> > -	.action_get = quad8_action_get
> > +	.function_read = quad8_function_read,
> > +	.function_write = quad8_function_write,
> > +	.action_read = quad8_action_read
> >  };
> >  
> >  static const char *const quad8_index_polarity_modes[] = {
> > @@ -394,7 +388,8 @@ static const char *const quad8_index_polarity_modes[] = {
> >  };
> >  
> >  static int quad8_index_polarity_get(struct counter_device *counter,
> > -	struct counter_signal *signal, size_t *index_polarity)
> > +				    struct counter_signal *signal,
> > +				    u32 *index_polarity)
> >  {
> >  	const struct quad8 *const priv = counter->priv;
> >  	const size_t channel_id = signal->id - 16;
> > @@ -405,7 +400,8 @@ static int quad8_index_polarity_get(struct counter_device *counter,
> >  }
> >  
> >  static int quad8_index_polarity_set(struct counter_device *counter,
> > -	struct counter_signal *signal, size_t index_polarity)
> > +				    struct counter_signal *signal,
> > +				    u32 index_polarity)
> >  {
> >  	struct quad8 *const priv = counter->priv;
> >  	const size_t channel_id = signal->id - 16;
> > @@ -426,20 +422,14 @@ static int quad8_index_polarity_set(struct counter_device *counter,
> >  	return 0;
> >  }
> >  
> > -static struct counter_signal_enum_ext quad8_index_pol_enum = {
> > -	.items = quad8_index_polarity_modes,
> > -	.num_items = ARRAY_SIZE(quad8_index_polarity_modes),
> > -	.get = quad8_index_polarity_get,
> > -	.set = quad8_index_polarity_set
> > -};
> > -
> >  static const char *const quad8_synchronous_modes[] = {
> >  	"non-synchronous",
> >  	"synchronous"
> >  };
> >  
> >  static int quad8_synchronous_mode_get(struct counter_device *counter,
> > -	struct counter_signal *signal, size_t *synchronous_mode)
> > +				      struct counter_signal *signal,
> > +				      u32 *synchronous_mode)
> >  {
> >  	const struct quad8 *const priv = counter->priv;
> >  	const size_t channel_id = signal->id - 16;
> > @@ -450,7 +440,8 @@ static int quad8_synchronous_mode_get(struct counter_device *counter,
> >  }
> >  
> >  static int quad8_synchronous_mode_set(struct counter_device *counter,
> > -	struct counter_signal *signal, size_t synchronous_mode)
> > +				      struct counter_signal *signal,
> > +				      u32 synchronous_mode)
> >  {
> >  	struct quad8 *const priv = counter->priv;
> >  	const size_t channel_id = signal->id - 16;
> > @@ -477,22 +468,18 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,
> >  	return 0;
> >  }
> >  
> > -static struct counter_signal_enum_ext quad8_syn_mode_enum = {
> > -	.items = quad8_synchronous_modes,
> > -	.num_items = ARRAY_SIZE(quad8_synchronous_modes),
> > -	.get = quad8_synchronous_mode_get,
> > -	.set = quad8_synchronous_mode_set
> > -};
> > -
> > -static ssize_t quad8_count_floor_read(struct counter_device *counter,
> > -	struct counter_count *count, void *private, char *buf)
> > +static int quad8_count_floor_read(struct counter_device *counter,
> > +				  struct counter_count *count, u64 *floor)
> >  {
> >  	/* Only a floor of 0 is supported */
> > -	return sprintf(buf, "0\n");
> > +	*floor = 0;
> > +
> > +	return 0;
> >  }
> >  
> > -static int quad8_count_mode_get(struct counter_device *counter,
> > -	struct counter_count *count, size_t *cnt_mode)
> > +static int quad8_count_mode_read(struct counter_device *counter,
> > +				 struct counter_count *count,
> > +				 enum counter_count_mode *cnt_mode)
> >  {
> >  	const struct quad8 *const priv = counter->priv;
> >  
> > @@ -515,26 +502,28 @@ static int quad8_count_mode_get(struct counter_device *counter,
> >  	return 0;
> >  }
> >  
> > -static int quad8_count_mode_set(struct counter_device *counter,
> > -	struct counter_count *count, size_t cnt_mode)
> > +static int quad8_count_mode_write(struct counter_device *counter,
> > +				  struct counter_count *count,
> > +				  enum counter_count_mode cnt_mode)
> >  {
> >  	struct quad8 *const priv = counter->priv;
> > +	unsigned int count_mode;
> >  	unsigned int mode_cfg;
> >  	const int base_offset = priv->base + 2 * count->id + 1;
> >  
> >  	/* Map Generic Counter count mode to 104-QUAD-8 count mode */
> >  	switch (cnt_mode) {
> >  	case COUNTER_COUNT_MODE_NORMAL:
> > -		cnt_mode = 0;
> > +		count_mode = 0;
> >  		break;
> >  	case COUNTER_COUNT_MODE_RANGE_LIMIT:
> > -		cnt_mode = 1;
> > +		count_mode = 1;
> >  		break;
> >  	case COUNTER_COUNT_MODE_NON_RECYCLE:
> > -		cnt_mode = 2;
> > +		count_mode = 2;
> >  		break;
> >  	case COUNTER_COUNT_MODE_MODULO_N:
> > -		cnt_mode = 3;
> > +		count_mode = 3;
> >  		break;
> >  	default:
> >  		/* should never reach this path */
> > @@ -543,10 +532,10 @@ static int quad8_count_mode_set(struct counter_device *counter,
> >  
> >  	mutex_lock(&priv->lock);
> >  
> > -	priv->count_mode[count->id] = cnt_mode;
> > +	priv->count_mode[count->id] = count_mode;
> >  
> >  	/* Set count mode configuration value */
> > -	mode_cfg = cnt_mode << 1;
> > +	mode_cfg = count_mode << 1;
> >  
> >  	/* Add quadrature mode configuration */
> >  	if (priv->quadrature_mode[count->id])
> > @@ -560,56 +549,35 @@ static int quad8_count_mode_set(struct counter_device *counter,
> >  	return 0;
> >  }
> >  
> > -static struct counter_count_enum_ext quad8_cnt_mode_enum = {
> > -	.items = counter_count_mode_str,
> > -	.num_items = ARRAY_SIZE(counter_count_mode_str),
> > -	.get = quad8_count_mode_get,
> > -	.set = quad8_count_mode_set
> > -};
> > -
> > -static ssize_t quad8_count_direction_read(struct counter_device *counter,
> > -	struct counter_count *count, void *priv, char *buf)
> > -{
> > -	enum counter_count_direction dir;
> > -
> > -	quad8_direction_get(counter, count, &dir);
> > -
> > -	return sprintf(buf, "%s\n", counter_count_direction_str[dir]);
> > -}
> > -
> > -static ssize_t quad8_count_enable_read(struct counter_device *counter,
> > -	struct counter_count *count, void *private, char *buf)
> > +static int quad8_count_enable_read(struct counter_device *counter,
> > +				   struct counter_count *count, u8 *enable)
> >  {
> >  	const struct quad8 *const priv = counter->priv;
> >  
> > -	return sprintf(buf, "%u\n", priv->ab_enable[count->id]);
> > +	*enable = priv->ab_enable[count->id];
> > +
> > +	return 0;
> >  }
> >  
> > -static ssize_t quad8_count_enable_write(struct counter_device *counter,
> > -	struct counter_count *count, void *private, const char *buf, size_t len)
> > +static int quad8_count_enable_write(struct counter_device *counter,
> > +				    struct counter_count *count, u8 enable)
> >  {
> >  	struct quad8 *const priv = counter->priv;
> >  	const int base_offset = priv->base + 2 * count->id;
> > -	int err;
> > -	bool ab_enable;
> >  	unsigned int ior_cfg;
> >  
> > -	err = kstrtobool(buf, &ab_enable);
> > -	if (err)
> > -		return err;
> > -
> >  	mutex_lock(&priv->lock);
> >  
> > -	priv->ab_enable[count->id] = ab_enable;
> > +	priv->ab_enable[count->id] = enable;
> >  
> > -	ior_cfg = ab_enable | priv->preset_enable[count->id] << 1;
> > +	ior_cfg = enable | priv->preset_enable[count->id] << 1;
> >  
> >  	/* Load I/O control configuration */
> >  	outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
> >  
> >  	mutex_unlock(&priv->lock);
> >  
> > -	return len;
> > +	return 0;
> >  }
> >  
> >  static const char *const quad8_noise_error_states[] = {
> > @@ -618,7 +586,7 @@ static const char *const quad8_noise_error_states[] = {
> >  };
> >  
> >  static int quad8_error_noise_get(struct counter_device *counter,
> > -	struct counter_count *count, size_t *noise_error)
> > +				 struct counter_count *count, u32 *noise_error)
> >  {
> >  	const struct quad8 *const priv = counter->priv;
> >  	const int base_offset = priv->base + 2 * count->id + 1;
> > @@ -628,18 +596,14 @@ static int quad8_error_noise_get(struct counter_device *counter,
> >  	return 0;
> >  }
> >  
> > -static struct counter_count_enum_ext quad8_error_noise_enum = {
> > -	.items = quad8_noise_error_states,
> > -	.num_items = ARRAY_SIZE(quad8_noise_error_states),
> > -	.get = quad8_error_noise_get
> > -};
> > -
> > -static ssize_t quad8_count_preset_read(struct counter_device *counter,
> > -	struct counter_count *count, void *private, char *buf)
> > +static int quad8_count_preset_read(struct counter_device *counter,
> > +				   struct counter_count *count, u64 *preset)
> >  {
> >  	const struct quad8 *const priv = counter->priv;
> >  
> > -	return sprintf(buf, "%u\n", priv->preset[count->id]);
> > +	*preset = priv->preset[count->id];
> > +
> > +	return 0;
> >  }
> >  
> >  static void quad8_preset_register_set(struct quad8 *const priv, const int id,
> > @@ -658,16 +622,10 @@ static void quad8_preset_register_set(struct quad8 *const priv, const int id,
> >  		outb(preset >> (8 * i), base_offset);
> >  }
> >  
> > -static ssize_t quad8_count_preset_write(struct counter_device *counter,
> > -	struct counter_count *count, void *private, const char *buf, size_t len)
> > +static int quad8_count_preset_write(struct counter_device *counter,
> > +				    struct counter_count *count, u64 preset)
> >  {
> >  	struct quad8 *const priv = counter->priv;
> > -	unsigned int preset;
> > -	int ret;
> > -
> > -	ret = kstrtouint(buf, 0, &preset);
> > -	if (ret)
> > -		return ret;
> >  
> >  	/* Only 24-bit values are supported */
> >  	if (preset > 0xFFFFFF)
> > @@ -679,11 +637,11 @@ static ssize_t quad8_count_preset_write(struct counter_device *counter,
> >  
> >  	mutex_unlock(&priv->lock);
> >  
> > -	return len;
> > +	return 0;
> >  }
> >  
> > -static ssize_t quad8_count_ceiling_read(struct counter_device *counter,
> > -	struct counter_count *count, void *private, char *buf)
> > +static int quad8_count_ceiling_read(struct counter_device *counter,
> > +				    struct counter_count *count, u64 *ceiling)
> >  {
> >  	struct quad8 *const priv = counter->priv;
> >  
> > @@ -693,26 +651,23 @@ static ssize_t quad8_count_ceiling_read(struct counter_device *counter,
> >  	switch (priv->count_mode[count->id]) {
> >  	case 1:
> >  	case 3:
> > -		mutex_unlock(&priv->lock);
> > -		return sprintf(buf, "%u\n", priv->preset[count->id]);
> > +		*ceiling = priv->preset[count->id];
> > +		break;
> > +	default:
> > +		/* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
> > +		*ceiling = 0x1FFFFFF;
> > +		break;
> >  	}
> >  
> >  	mutex_unlock(&priv->lock);
> >  
> > -	/* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
> > -	return sprintf(buf, "33554431\n");
> > +	return 0;
> >  }
> >  
> > -static ssize_t quad8_count_ceiling_write(struct counter_device *counter,
> > -	struct counter_count *count, void *private, const char *buf, size_t len)
> > +static int quad8_count_ceiling_write(struct counter_device *counter,
> > +				     struct counter_count *count, u64 ceiling)
> >  {
> >  	struct quad8 *const priv = counter->priv;
> > -	unsigned int ceiling;
> > -	int ret;
> > -
> > -	ret = kstrtouint(buf, 0, &ceiling);
> > -	if (ret)
> > -		return ret;
> >  
> >  	/* Only 24-bit values are supported */
> >  	if (ceiling > 0xFFFFFF)
> > @@ -726,7 +681,7 @@ static ssize_t quad8_count_ceiling_write(struct counter_device *counter,
> >  	case 3:
> >  		quad8_preset_register_set(priv, count->id, ceiling);
> >  		mutex_unlock(&priv->lock);
> > -		return len;
> > +		return 0;
> >  	}
> >  
> >  	mutex_unlock(&priv->lock);
> > @@ -734,27 +689,25 @@ static ssize_t quad8_count_ceiling_write(struct counter_device *counter,
> >  	return -EINVAL;
> >  }
> >  
> > -static ssize_t quad8_count_preset_enable_read(struct counter_device *counter,
> > -	struct counter_count *count, void *private, char *buf)
> > +static int quad8_count_preset_enable_read(struct counter_device *counter,
> > +					  struct counter_count *count,
> > +					  u8 *preset_enable)
> >  {
> >  	const struct quad8 *const priv = counter->priv;
> >  
> > -	return sprintf(buf, "%u\n", !priv->preset_enable[count->id]);
> > +	*preset_enable = !priv->preset_enable[count->id];
> > +
> > +	return 0;
> >  }
> >  
> > -static ssize_t quad8_count_preset_enable_write(struct counter_device *counter,
> > -	struct counter_count *count, void *private, const char *buf, size_t len)
> > +static int quad8_count_preset_enable_write(struct counter_device *counter,
> > +					   struct counter_count *count,
> > +					   u8 preset_enable)
> >  {
> >  	struct quad8 *const priv = counter->priv;
> >  	const int base_offset = priv->base + 2 * count->id + 1;
> > -	bool preset_enable;
> > -	int ret;
> >  	unsigned int ior_cfg;
> >  
> > -	ret = kstrtobool(buf, &preset_enable);
> > -	if (ret)
> > -		return ret;
> > -
> >  	/* Preset enable is active low in Input/Output Control register */
> >  	preset_enable = !preset_enable;
> >  
> > @@ -762,25 +715,24 @@ static ssize_t quad8_count_preset_enable_write(struct counter_device *counter,
> >  
> >  	priv->preset_enable[count->id] = preset_enable;
> >  
> > -	ior_cfg = priv->ab_enable[count->id] | (unsigned int)preset_enable << 1;
> > +	ior_cfg = priv->ab_enable[count->id] | preset_enable << 1;
> >  
> >  	/* Load I/O control configuration to Input / Output Control Register */
> >  	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
> >  
> >  	mutex_unlock(&priv->lock);
> >  
> > -	return len;
> > +	return 0;
> >  }
> >  
> > -static ssize_t quad8_signal_cable_fault_read(struct counter_device *counter,
> > -					     struct counter_signal *signal,
> > -					     void *private, char *buf)
> > +static int quad8_signal_cable_fault_read(struct counter_device *counter,
> > +					 struct counter_signal *signal,
> > +					 u8 *cable_fault)
> >  {
> >  	struct quad8 *const priv = counter->priv;
> >  	const size_t channel_id = signal->id / 2;
> >  	bool disabled;
> >  	unsigned int status;
> > -	unsigned int fault;
> >  
> >  	mutex_lock(&priv->lock);
> >  
> > @@ -797,36 +749,31 @@ static ssize_t quad8_signal_cable_fault_read(struct counter_device *counter,
> >  	mutex_unlock(&priv->lock);
> >  
> >  	/* Mask respective channel and invert logic */
> > -	fault = !(status & BIT(channel_id));
> > +	*cable_fault = !(status & BIT(channel_id));
> >  
> > -	return sprintf(buf, "%u\n", fault);
> > +	return 0;
> >  }
> >  
> > -static ssize_t quad8_signal_cable_fault_enable_read(
> > -	struct counter_device *counter, struct counter_signal *signal,
> > -	void *private, char *buf)
> > +static int quad8_signal_cable_fault_enable_read(struct counter_device *counter,
> > +						struct counter_signal *signal,
> > +						u8 *enable)
> >  {
> >  	const struct quad8 *const priv = counter->priv;
> >  	const size_t channel_id = signal->id / 2;
> > -	const unsigned int enb = !!(priv->cable_fault_enable & BIT(channel_id));
> >  
> > -	return sprintf(buf, "%u\n", enb);
> > +	*enable = !!(priv->cable_fault_enable & BIT(channel_id));
> > +
> > +	return 0;
> >  }
> >  
> > -static ssize_t quad8_signal_cable_fault_enable_write(
> > -	struct counter_device *counter, struct counter_signal *signal,
> > -	void *private, const char *buf, size_t len)
> > +static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
> > +						 struct counter_signal *signal,
> > +						 u8 enable)
> >  {
> >  	struct quad8 *const priv = counter->priv;
> >  	const size_t channel_id = signal->id / 2;
> > -	bool enable;
> > -	int ret;
> >  	unsigned int cable_fault_enable;
> >  
> > -	ret = kstrtobool(buf, &enable);
> > -	if (ret)
> > -		return ret;
> > -
> >  	mutex_lock(&priv->lock);
> >  
> >  	if (enable)
> > @@ -841,31 +788,27 @@ static ssize_t quad8_signal_cable_fault_enable_write(
> >  
> >  	mutex_unlock(&priv->lock);
> >  
> > -	return len;
> > +	return 0;
> >  }
> >  
> > -static ssize_t quad8_signal_fck_prescaler_read(struct counter_device *counter,
> > -	struct counter_signal *signal, void *private, char *buf)
> > +static int quad8_signal_fck_prescaler_read(struct counter_device *counter,
> > +					   struct counter_signal *signal,
> > +					   u8 *prescaler)
> >  {
> >  	const struct quad8 *const priv = counter->priv;
> > -	const size_t channel_id = signal->id / 2;
> >  
> > -	return sprintf(buf, "%u\n", priv->fck_prescaler[channel_id]);
> > +	*prescaler = priv->fck_prescaler[signal->id / 2];
> > +
> > +	return 0;
> >  }
> >  
> > -static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter,
> > -	struct counter_signal *signal, void *private, const char *buf,
> > -	size_t len)
> > +static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
> > +					    struct counter_signal *signal,
> > +					    u8 prescaler)
> >  {
> >  	struct quad8 *const priv = counter->priv;
> >  	const size_t channel_id = signal->id / 2;
> >  	const int base_offset = priv->base + 2 * channel_id;
> > -	u8 prescaler;
> > -	int ret;
> > -
> > -	ret = kstrtou8(buf, 0, &prescaler);
> > -	if (ret)
> > -		return ret;
> >  
> >  	mutex_lock(&priv->lock);
> >  
> > @@ -881,31 +824,30 @@ static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter,
> >  
> >  	mutex_unlock(&priv->lock);
> >  
> > -	return len;
> > +	return 0;
> >  }
> >  
> > -static const struct counter_signal_ext quad8_signal_ext[] = {
> > -	{
> > -		.name = "cable_fault",
> > -		.read = quad8_signal_cable_fault_read
> > -	},
> > -	{
> > -		.name = "cable_fault_enable",
> > -		.read = quad8_signal_cable_fault_enable_read,
> > -		.write = quad8_signal_cable_fault_enable_write
> > -	},
> > -	{
> > -		.name = "filter_clock_prescaler",
> > -		.read = quad8_signal_fck_prescaler_read,
> > -		.write = quad8_signal_fck_prescaler_write
> > -	}
> > +static struct counter_comp quad8_signal_ext[] = {
> > +	COUNTER_COMP_SIGNAL_BOOL("cable_fault", quad8_signal_cable_fault_read,
> > +				 NULL),
> > +	COUNTER_COMP_SIGNAL_BOOL("cable_fault_enable",
> > +				 quad8_signal_cable_fault_enable_read,
> > +				 quad8_signal_cable_fault_enable_write),
> > +	COUNTER_COMP_SIGNAL_U8("filter_clock_prescaler",
> > +			       quad8_signal_fck_prescaler_read,
> > +			       quad8_signal_fck_prescaler_write)
> >  };
> >  
> > -static const struct counter_signal_ext quad8_index_ext[] = {
> > -	COUNTER_SIGNAL_ENUM("index_polarity", &quad8_index_pol_enum),
> > -	COUNTER_SIGNAL_ENUM_AVAILABLE("index_polarity",	&quad8_index_pol_enum),
> > -	COUNTER_SIGNAL_ENUM("synchronous_mode", &quad8_syn_mode_enum),
> > -	COUNTER_SIGNAL_ENUM_AVAILABLE("synchronous_mode", &quad8_syn_mode_enum)
> > +static DEFINE_COUNTER_ENUM(quad8_index_pol_enum, quad8_index_polarity_modes);
> > +static DEFINE_COUNTER_ENUM(quad8_synch_mode_enum, quad8_synchronous_modes);
> > +
> > +static struct counter_comp quad8_index_ext[] = {
> > +	COUNTER_COMP_SIGNAL_ENUM("index_polarity", quad8_index_polarity_get,
> > +				 quad8_index_polarity_set,
> > +				 quad8_index_pol_enum),
> > +	COUNTER_COMP_SIGNAL_ENUM("synchronous_mode", quad8_synchronous_mode_get,
> > +				 quad8_synchronous_mode_set,
> > +				 quad8_synch_mode_enum),
> >  };
> >  
> >  #define QUAD8_QUAD_SIGNAL(_id, _name) {		\
> > @@ -974,39 +916,30 @@ static struct counter_synapse quad8_count_synapses[][3] = {
> >  	QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7)
> >  };
> >  
> > -static const struct counter_count_ext quad8_count_ext[] = {
> > -	{
> > -		.name = "ceiling",
> > -		.read = quad8_count_ceiling_read,
> > -		.write = quad8_count_ceiling_write
> > -	},
> > -	{
> > -		.name = "floor",
> > -		.read = quad8_count_floor_read
> > -	},
> > -	COUNTER_COUNT_ENUM("count_mode", &quad8_cnt_mode_enum),
> > -	COUNTER_COUNT_ENUM_AVAILABLE("count_mode", &quad8_cnt_mode_enum),
> > -	{
> > -		.name = "direction",
> > -		.read = quad8_count_direction_read
> > -	},
> > -	{
> > -		.name = "enable",
> > -		.read = quad8_count_enable_read,
> > -		.write = quad8_count_enable_write
> > -	},
> > -	COUNTER_COUNT_ENUM("error_noise", &quad8_error_noise_enum),
> > -	COUNTER_COUNT_ENUM_AVAILABLE("error_noise", &quad8_error_noise_enum),
> > -	{
> > -		.name = "preset",
> > -		.read = quad8_count_preset_read,
> > -		.write = quad8_count_preset_write
> > -	},
> > -	{
> > -		.name = "preset_enable",
> > -		.read = quad8_count_preset_enable_read,
> > -		.write = quad8_count_preset_enable_write
> > -	}
> > +static const enum counter_count_mode quad8_cnt_modes[] = {
> > +	COUNTER_COUNT_MODE_NORMAL,
> > +	COUNTER_COUNT_MODE_RANGE_LIMIT,
> > +	COUNTER_COUNT_MODE_NON_RECYCLE,
> > +	COUNTER_COUNT_MODE_MODULO_N,
> > +};
> > +
> > +static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes);
> > +
> > +static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states);
> > +
> > +static struct counter_comp quad8_count_ext[] = {
> > +	COUNTER_COMP_CEILING(quad8_count_ceiling_read,
> > +			     quad8_count_ceiling_write),
> > +	COUNTER_COMP_FLOOR(quad8_count_floor_read, NULL),
> > +	COUNTER_COMP_COUNT_MODE(quad8_count_mode_read, quad8_count_mode_write,
> > +				quad8_count_mode_available),
> > +	COUNTER_COMP_DIRECTION(quad8_direction_read),
> > +	COUNTER_COMP_ENABLE(quad8_count_enable_read, quad8_count_enable_write),
> > +	COUNTER_COMP_COUNT_ENUM("error_noise", quad8_error_noise_get, NULL,
> > +				quad8_error_noise_enum),
> > +	COUNTER_COMP_PRESET(quad8_count_preset_read, quad8_count_preset_write),
> > +	COUNTER_COMP_PRESET_ENABLE(quad8_count_preset_enable_read,
> > +				   quad8_count_preset_enable_write),
> >  };
> >  
> >  #define QUAD8_COUNT(_id, _cntname) {					\
> > diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
> > index 19742e6f5e3e..1ab7e087fdc2 100644
> > --- a/drivers/counter/Makefile
> > +++ b/drivers/counter/Makefile
> > @@ -4,6 +4,7 @@
> >  #
> >  
> >  obj-$(CONFIG_COUNTER) += counter.o
> > +counter-y := counter-core.o counter-sysfs.o
> >  
> >  obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
> >  obj-$(CONFIG_INTERRUPT_CNT)		+= interrupt-cnt.o
> > diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
> > new file mode 100644
> > index 000000000000..c533a6ff12cf
> > --- /dev/null
> > +++ b/drivers/counter/counter-core.c
> > @@ -0,0 +1,142 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Generic Counter interface
> > + * Copyright (C) 2020 William Breathitt Gray
> > + */
> > +#include <linux/counter.h>
> > +#include <linux/device.h>
> > +#include <linux/export.h>
> > +#include <linux/gfp.h>
> > +#include <linux/idr.h>
> > +#include <linux/init.h>
> > +#include <linux/module.h>
> > +
> > +#include "counter-sysfs.h"
> > +
> > +/* Provides a unique ID for each counter device */
> > +static DEFINE_IDA(counter_ida);
> > +
> > +static void counter_device_release(struct device *dev)
> > +{
> > +	ida_free(&counter_ida, dev->id);
> > +}
> > +
> > +static struct device_type counter_device_type = {
> > +	.name = "counter_device",
> > +	.release = counter_device_release,
> > +};
> > +
> > +static struct bus_type counter_bus_type = {
> > +	.name = "counter",
> > +	.dev_name = "counter",
> > +};
> > +
> > +/**
> > + * counter_register - register Counter to the system
> > + * @counter:	pointer to Counter to register
> > + *
> > + * This function registers a Counter to the system. A sysfs "counter" directory
> > + * will be created and populated with sysfs attributes correlating with the
> > + * Counter Signals, Synapses, and Counts respectively.
> > + */
> > +int counter_register(struct counter_device *const counter)
> > +{
> > +	struct device *const dev = &counter->dev;
> > +	int id;
> > +	int err;
> > +
> > +	/* Acquire unique ID */
> > +	id = ida_alloc(&counter_ida, GFP_KERNEL);
> > +	if (id < 0)
> > +		return id;
> > +
> > +	/* Configure device structure for Counter */
> > +	dev->id = id;
> > +	dev->type = &counter_device_type;
> > +	dev->bus = &counter_bus_type;
> > +	if (counter->parent) {
> > +		dev->parent = counter->parent;
> > +		dev->of_node = counter->parent->of_node;
> > +	}
> > +	device_initialize(dev);
> > +	dev_set_drvdata(dev, counter);
> > +
> > +	/* Add Counter sysfs attributes */
> > +	err = counter_sysfs_add(counter);
> > +	if (err < 0)
> > +		goto err_free_id;
> > +
> > +	/* Add device to system */
> > +	err = device_add(dev);
> > +	if (err < 0)
> > +		goto err_free_id;
> > +
> > +	return 0;
> > +
> > +err_free_id:
> > +	put_device(dev);
> > +	return err;
> > +}
> > +EXPORT_SYMBOL_GPL(counter_register);
> > +
> > +/**
> > + * counter_unregister - unregister Counter from the system
> > + * @counter:	pointer to Counter to unregister
> > + *
> > + * The Counter is unregistered from the system.
> > + */
> > +void counter_unregister(struct counter_device *const counter)
> > +{
> > +	if (!counter)
> > +		return;
> > +
> > +	device_unregister(&counter->dev);
> > +}
> > +EXPORT_SYMBOL_GPL(counter_unregister);
> > +
> > +static void devm_counter_release(void *counter)
> > +{
> > +	counter_unregister(counter);
> > +}
> > +
> > +/**
> > + * devm_counter_register - Resource-managed counter_register
> > + * @dev:	device to allocate counter_device for
> > + * @counter:	pointer to Counter to register
> > + *
> > + * Managed counter_register. The Counter registered with this function is
> > + * automatically unregistered on driver detach. This function calls
> > + * counter_register internally. Refer to that function for more information.
> > + *
> > + * RETURNS:
> > + * 0 on success, negative error number on failure.
> > + */
> > +int devm_counter_register(struct device *dev,
> > +			  struct counter_device *const counter)
> > +{
> > +	int err;
> > +
> > +	err = counter_register(counter);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	return devm_add_action_or_reset(dev, devm_counter_release, counter);
> > +}
> > +EXPORT_SYMBOL_GPL(devm_counter_register);
> > +
> > +static int __init counter_init(void)
> > +{
> > +	return bus_register(&counter_bus_type);
> > +}
> > +
> > +static void __exit counter_exit(void)
> > +{
> > +	bus_unregister(&counter_bus_type);
> > +}
> > +
> > +subsys_initcall(counter_init);
> > +module_exit(counter_exit);
> > +
> > +MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
> > +MODULE_DESCRIPTION("Generic Counter interface");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
> > new file mode 100644
> > index 000000000000..108cbd838eb9
> > --- /dev/null
> > +++ b/drivers/counter/counter-sysfs.c
> > @@ -0,0 +1,849 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Generic Counter sysfs interface
> > + * Copyright (C) 2020 William Breathitt Gray
> > + */
> > +#include <linux/counter.h>
> > +#include <linux/device.h>
> > +#include <linux/err.h>
> > +#include <linux/gfp.h>
> > +#include <linux/kernel.h>
> > +#include <linux/kstrtox.h>
> > +#include <linux/list.h>
> > +#include <linux/string.h>
> > +#include <linux/sysfs.h>
> > +#include <linux/types.h>
> > +
> > +#include "counter-sysfs.h"
> > +
> > +/**
> > + * struct counter_attribute - Counter sysfs attribute
> > + * @dev_attr:	device attribute for sysfs
> > + * @l:		node to add Counter attribute to attribute group list
> > + * @comp:	Counter component callbacks and data
> > + * @scope:	Counter scope of the attribute
> > + * @parent:	pointer to the parent component
> > + */
> > +struct counter_attribute {
> > +	struct device_attribute dev_attr;
> > +	struct list_head l;
> > +
> > +	struct counter_comp comp;
> > +	enum counter_scope scope;
> > +	void *parent;
> > +};
> > +
> > +#define to_counter_attribute(_dev_attr) \
> > +	container_of(_dev_attr, struct counter_attribute, dev_attr)
> > +
> > +/**
> > + * struct counter_attribute_group - container for attribute group
> > + * @name:	name of the attribute group
> > + * @attr_list:	list to keep track of created attributes
> > + * @num_attr:	number of attributes
> > + */
> > +struct counter_attribute_group {
> > +	const char *name;
> > +	struct list_head attr_list;
> > +	size_t num_attr;
> > +};
> > +
> > +static const char *const counter_function_str[] = {
> > +	[COUNTER_FUNCTION_INCREASE] = "increase",
> > +	[COUNTER_FUNCTION_DECREASE] = "decrease",
> > +	[COUNTER_FUNCTION_PULSE_DIRECTION] = "pulse-direction",
> > +	[COUNTER_FUNCTION_QUADRATURE_X1_A] = "quadrature x1 a",
> > +	[COUNTER_FUNCTION_QUADRATURE_X1_B] = "quadrature x1 b",
> > +	[COUNTER_FUNCTION_QUADRATURE_X2_A] = "quadrature x2 a",
> > +	[COUNTER_FUNCTION_QUADRATURE_X2_B] = "quadrature x2 b",
> > +	[COUNTER_FUNCTION_QUADRATURE_X4] = "quadrature x4"
> > +};
> > +
> > +static const char *const counter_signal_value_str[] = {
> > +	[COUNTER_SIGNAL_LEVEL_LOW] = "low",
> > +	[COUNTER_SIGNAL_LEVEL_HIGH] = "high"
> > +};
> > +
> > +static const char *const counter_synapse_action_str[] = {
> > +	[COUNTER_SYNAPSE_ACTION_NONE] = "none",
> > +	[COUNTER_SYNAPSE_ACTION_RISING_EDGE] = "rising edge",
> > +	[COUNTER_SYNAPSE_ACTION_FALLING_EDGE] = "falling edge",
> > +	[COUNTER_SYNAPSE_ACTION_BOTH_EDGES] = "both edges"
> > +};
> > +
> > +static const char *const counter_count_direction_str[] = {
> > +	[COUNTER_COUNT_DIRECTION_FORWARD] = "forward",
> > +	[COUNTER_COUNT_DIRECTION_BACKWARD] = "backward"
> > +};
> > +
> > +static const char *const counter_count_mode_str[] = {
> > +	[COUNTER_COUNT_MODE_NORMAL] = "normal",
> > +	[COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit",
> > +	[COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle",
> > +	[COUNTER_COUNT_MODE_MODULO_N] = "modulo-n"
> > +};
> > +
> > +static ssize_t counter_comp_u8_show(struct device *dev,
> > +				    struct device_attribute *attr, char *buf)
> > +{
> > +	const struct counter_attribute *const a = to_counter_attribute(attr);
> > +	struct counter_device *const counter = dev_get_drvdata(dev);
> > +	int err;
> > +	u8 data = 0;
> > +
> > +	switch (a->scope) {
> > +	case COUNTER_SCOPE_DEVICE:
> > +		err = a->comp.device_u8_read(counter, &data);
> > +		break;
> > +	case COUNTER_SCOPE_SIGNAL:
> > +		err = a->comp.signal_u8_read(counter, a->parent, &data);
> > +		break;
> > +	case COUNTER_SCOPE_COUNT:
> > +		err = a->comp.count_u8_read(counter, a->parent, &data);
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +	if (err < 0)
> > +		return err;
> > +
> > +	if (a->comp.type == COUNTER_COMP_BOOL)
> > +		/* data should already be boolean but ensure just to be safe */
> > +		data = !!data;
> > +
> > +	return sprintf(buf, "%u\n", (unsigned int)data);
> > +}
> > +
> > +static ssize_t counter_comp_u8_store(struct device *dev,
> > +				     struct device_attribute *attr,
> > +				     const char *buf, size_t len)
> > +{
> > +	const struct counter_attribute *const a = to_counter_attribute(attr);
> > +	struct counter_device *const counter = dev_get_drvdata(dev);
> > +	int err;
> > +	bool bool_data = 0;
> > +	u8 data = 0;
> > +
> > +	if (a->comp.type == COUNTER_COMP_BOOL) {
> > +		err = kstrtobool(buf, &bool_data);
> > +		data = bool_data;
> > +	} else
> > +		err = kstrtou8(buf, 0, &data);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	switch (a->scope) {
> > +	case COUNTER_SCOPE_DEVICE:
> > +		err = a->comp.device_u8_write(counter, data);
> > +		break;
> > +	case COUNTER_SCOPE_SIGNAL:
> > +		err = a->comp.signal_u8_write(counter, a->parent, data);
> > +		break;
> > +	case COUNTER_SCOPE_COUNT:
> > +		err = a->comp.count_u8_write(counter, a->parent, data);
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +	if (err < 0)
> > +		return err;
> > +
> > +	return len;
> > +}
> > +
> > +static ssize_t counter_comp_u32_show(struct device *dev,
> > +				     struct device_attribute *attr, char *buf)
> > +{
> > +	const struct counter_attribute *const a = to_counter_attribute(attr);
> > +	struct counter_device *const counter = dev_get_drvdata(dev);
> > +	const struct counter_available *const avail = a->comp.priv;
> > +	int err;
> > +	u32 data = 0;
> > +
> > +	switch (a->scope) {
> > +	case COUNTER_SCOPE_DEVICE:
> > +		err = a->comp.device_u32_read(counter, &data);
> > +		break;
> > +	case COUNTER_SCOPE_SIGNAL:
> > +		err = a->comp.signal_u32_read(counter, a->parent, &data);
> > +		break;
> > +	case COUNTER_SCOPE_COUNT:
> > +		if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)
> > +			err = a->comp.action_read(counter, a->parent,
> > +						  a->comp.priv, &data);
> > +		else
> > +			err = a->comp.count_u32_read(counter, a->parent, &data);
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +	if (err < 0)
> > +		return err;
> > +
> > +	switch (a->comp.type) {
> > +	case COUNTER_COMP_FUNCTION:
> > +		return sysfs_emit(buf, "%s\n", counter_function_str[data]);
> > +	case COUNTER_COMP_SIGNAL_LEVEL:
> > +		return sysfs_emit(buf, "%s\n", counter_signal_value_str[data]);
> > +	case COUNTER_COMP_SYNAPSE_ACTION:
> > +		return sysfs_emit(buf, "%s\n", counter_synapse_action_str[data]);
> > +	case COUNTER_COMP_ENUM:
> > +		return sysfs_emit(buf, "%s\n", avail->strs[data]);
> > +	case COUNTER_COMP_COUNT_DIRECTION:
> > +		return sysfs_emit(buf, "%s\n", counter_count_direction_str[data]);
> > +	case COUNTER_COMP_COUNT_MODE:
> > +		return sysfs_emit(buf, "%s\n", counter_count_mode_str[data]);
> > +	default:
> > +		return sprintf(buf, "%u\n", (unsigned int)data);
> > +	}
> > +}
> > +
> > +static int counter_find_enum(u32 *const enum_item, const u32 *const enums,
> > +			     const size_t num_enums, const char *const buf,
> > +			     const char *const string_array[])
> > +{
> > +	size_t index;
> > +
> > +	for (index = 0; index < num_enums; index++) {
> > +		*enum_item = enums[index];
> > +		if (sysfs_streq(buf, string_array[*enum_item]))
> > +			return 0;
> > +	}
> > +
> > +	return -EINVAL;
> > +}
> > +
> > +static ssize_t counter_comp_u32_store(struct device *dev,
> > +				      struct device_attribute *attr,
> > +				      const char *buf, size_t len)
> > +{
> > +	const struct counter_attribute *const a = to_counter_attribute(attr);
> > +	struct counter_device *const counter = dev_get_drvdata(dev);
> > +	struct counter_count *const count = a->parent;
> > +	struct counter_synapse *const synapse = a->comp.priv;
> > +	const struct counter_available *const avail = a->comp.priv;
> > +	int err;
> > +	u32 data = 0;
> > +
> > +	switch (a->comp.type) {
> > +	case COUNTER_COMP_FUNCTION:
> > +		err = counter_find_enum(&data, count->functions_list,
> > +					count->num_functions, buf,
> > +					counter_function_str);
> > +		break;
> > +	case COUNTER_COMP_SYNAPSE_ACTION:
> > +		err = counter_find_enum(&data, synapse->actions_list,
> > +					synapse->num_actions, buf,
> > +					counter_synapse_action_str);
> > +		break;
> > +	case COUNTER_COMP_ENUM:
> > +		err = __sysfs_match_string(avail->strs, avail->num_items, buf);
> > +		data = err;
> > +		break;
> > +	case COUNTER_COMP_COUNT_MODE:
> > +		err = counter_find_enum(&data, avail->enums, avail->num_items,
> > +					buf, counter_count_mode_str);
> > +		break;
> > +	default:
> > +		err = kstrtou32(buf, 0, &data);
> > +		break;
> > +	}
> > +	if (err < 0)
> > +		return err;
> > +
> > +	switch (a->scope) {
> > +	case COUNTER_SCOPE_DEVICE:
> > +		err = a->comp.device_u32_write(counter, data);
> > +		break;
> > +	case COUNTER_SCOPE_SIGNAL:
> > +		err = a->comp.signal_u32_write(counter, a->parent, data);
> > +		break;
> > +	case COUNTER_SCOPE_COUNT:
> > +		if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)
> > +			err = a->comp.action_write(counter, count, synapse,
> > +						   data);
> > +		else
> > +			err = a->comp.count_u32_write(counter, count, data);
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +	if (err < 0)
> > +		return err;
> > +
> > +	return len;
> > +}
> > +
> > +static ssize_t counter_comp_u64_show(struct device *dev,
> > +				     struct device_attribute *attr, char *buf)
> > +{
> > +	const struct counter_attribute *const a = to_counter_attribute(attr);
> > +	struct counter_device *const counter = dev_get_drvdata(dev);
> > +	int err;
> > +	u64 data = 0;
> > +
> > +	switch (a->scope) {
> > +	case COUNTER_SCOPE_DEVICE:
> > +		err = a->comp.device_u64_read(counter, &data);
> > +		break;
> > +	case COUNTER_SCOPE_SIGNAL:
> > +		err = a->comp.signal_u64_read(counter, a->parent, &data);
> > +		break;
> > +	case COUNTER_SCOPE_COUNT:
> > +		err = a->comp.count_u64_read(counter, a->parent, &data);
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +	if (err < 0)
> > +		return err;
> > +
> > +	return sprintf(buf, "%llu\n", (unsigned long long)data);
> > +}
> > +
> > +static ssize_t counter_comp_u64_store(struct device *dev,
> > +				      struct device_attribute *attr,
> > +				      const char *buf, size_t len)
> > +{
> > +	const struct counter_attribute *const a = to_counter_attribute(attr);
> > +	struct counter_device *const counter = dev_get_drvdata(dev);
> > +	int err;
> > +	u64 data = 0;
> > +
> > +	err = kstrtou64(buf, 0, &data);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	switch (a->scope) {
> > +	case COUNTER_SCOPE_DEVICE:
> > +		err = a->comp.device_u64_write(counter, data);
> > +		break;
> > +	case COUNTER_SCOPE_SIGNAL:
> > +		err = a->comp.signal_u64_write(counter, a->parent, data);
> > +		break;
> > +	case COUNTER_SCOPE_COUNT:
> > +		err = a->comp.count_u64_write(counter, a->parent, data);
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +	if (err < 0)
> > +		return err;
> > +
> > +	return len;
> > +}
> > +
> > +static ssize_t enums_available_show(const u32 *const enums,
> > +				    const size_t num_enums,
> > +				    const char *const strs[], char *buf)
> > +{
> > +	size_t len = 0;
> > +	size_t index;
> > +
> > +	for (index = 0; index < num_enums; index++)
> > +		len += sysfs_emit_at(buf, len, "%s\n", strs[enums[index]]);
> > +
> > +	return len;
> > +}
> > +
> > +static ssize_t strs_available_show(const struct counter_available *const avail,
> > +				   char *buf)
> > +{
> > +	size_t len = 0;
> > +	size_t index;
> > +
> > +	for (index = 0; index < avail->num_items; index++)
> > +		len += sysfs_emit_at(buf, len, "%s\n", avail->strs[index]);
> > +
> > +	return len;
> > +}
> > +
> > +static ssize_t counter_comp_available_show(struct device *dev,
> > +					   struct device_attribute *attr,
> > +					   char *buf)
> > +{
> > +	const struct counter_attribute *const a = to_counter_attribute(attr);
> > +	const struct counter_count *const count = a->parent;
> > +	const struct counter_synapse *const synapse = a->comp.priv;
> > +	const struct counter_available *const avail = a->comp.priv;
> > +
> > +	switch (a->comp.type) {
> > +	case COUNTER_COMP_FUNCTION:
> > +		return enums_available_show(count->functions_list,
> > +					    count->num_functions,
> > +					    counter_function_str, buf);
> > +	case COUNTER_COMP_SYNAPSE_ACTION:
> > +		return enums_available_show(synapse->actions_list,
> > +					    synapse->num_actions,
> > +					    counter_synapse_action_str, buf);
> > +	case COUNTER_COMP_ENUM:
> > +		return strs_available_show(avail, buf);
> > +	case COUNTER_COMP_COUNT_MODE:
> > +		return enums_available_show(avail->enums, avail->num_items,
> > +					    counter_count_mode_str, buf);
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static int counter_avail_attr_create(struct device *const dev,
> > +	struct counter_attribute_group *const group,
> > +	const struct counter_comp *const comp, void *const parent)
> > +{
> > +	struct counter_attribute *counter_attr;
> > +	struct device_attribute *dev_attr;
> > +
> > +	counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
> > +	if (!counter_attr)
> > +		return -ENOMEM;
> > +
> > +	/* Configure Counter attribute */
> > +	counter_attr->comp.type = comp->type;
> > +	counter_attr->comp.priv = comp->priv;
> > +	counter_attr->parent = parent;
> > +
> > +	/* Initialize sysfs attribute */
> > +	dev_attr = &counter_attr->dev_attr;
> > +	sysfs_attr_init(&dev_attr->attr);
> > +
> > +	/* Configure device attribute */
> > +	dev_attr->attr.name = devm_kasprintf(dev, GFP_KERNEL, "%s_available",
> > +					     comp->name);
> > +	if (!dev_attr->attr.name)
> > +		return -ENOMEM;
> > +	dev_attr->attr.mode = 0444;
> > +	dev_attr->show = counter_comp_available_show;
> > +
> > +	/* Store list node */
> > +	list_add(&counter_attr->l, &group->attr_list);
> > +	group->num_attr++;
> > +
> > +	return 0;
> > +}
> > +
> > +static int counter_attr_create(struct device *const dev,
> > +			       struct counter_attribute_group *const group,
> > +			       const struct counter_comp *const comp,
> > +			       const enum counter_scope scope,
> > +			       void *const parent)
> > +{
> > +	struct counter_attribute *counter_attr;
> > +	struct device_attribute *dev_attr;
> > +
> > +	counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
> > +	if (!counter_attr)
> > +		return -ENOMEM;
> > +
> > +	/* Configure Counter attribute */
> > +	counter_attr->comp = *comp;
> > +	counter_attr->scope = scope;
> > +	counter_attr->parent = parent;
> > +
> > +	/* Configure device attribute */
> > +	dev_attr = &counter_attr->dev_attr;
> > +	sysfs_attr_init(&dev_attr->attr);
> > +	dev_attr->attr.name = comp->name;
> > +	switch (comp->type) {
> > +	case COUNTER_COMP_U8:
> > +	case COUNTER_COMP_BOOL:
> > +		if (comp->device_u8_read) {
> > +			dev_attr->attr.mode |= 0444;
> > +			dev_attr->show = counter_comp_u8_show;
> > +		}
> > +		if (comp->device_u8_write) {
> > +			dev_attr->attr.mode |= 0200;
> > +			dev_attr->store = counter_comp_u8_store;
> > +		}
> > +		break;
> > +	case COUNTER_COMP_SIGNAL_LEVEL:
> > +	case COUNTER_COMP_FUNCTION:
> > +	case COUNTER_COMP_SYNAPSE_ACTION:
> > +	case COUNTER_COMP_ENUM:
> > +	case COUNTER_COMP_COUNT_DIRECTION:
> > +	case COUNTER_COMP_COUNT_MODE:
> > +		if (comp->device_u32_read) {
> > +			dev_attr->attr.mode |= 0444;
> > +			dev_attr->show = counter_comp_u32_show;
> > +		}
> > +		if (comp->device_u32_write) {
> > +			dev_attr->attr.mode |= 0200;
> > +			dev_attr->store = counter_comp_u32_store;
> > +		}
> > +		break;
> > +	case COUNTER_COMP_U64:
> > +		if (comp->device_u64_read) {
> > +			dev_attr->attr.mode |= 0444;
> > +			dev_attr->show = counter_comp_u64_show;
> > +		}
> > +		if (comp->device_u64_write) {
> > +			dev_attr->attr.mode |= 0200;
> > +			dev_attr->store = counter_comp_u64_store;
> > +		}
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* Store list node */
> > +	list_add(&counter_attr->l, &group->attr_list);
> > +	group->num_attr++;
> > +
> > +	/* Create "*_available" attribute if needed */
> > +	switch (comp->type) {
> > +	case COUNTER_COMP_FUNCTION:
> > +	case COUNTER_COMP_SYNAPSE_ACTION:
> > +	case COUNTER_COMP_ENUM:
> > +	case COUNTER_COMP_COUNT_MODE:
> > +		return counter_avail_attr_create(dev, group, comp, parent);
> > +	default:
> > +		return 0;
> > +	}
> > +}
> > +
> > +static ssize_t counter_comp_name_show(struct device *dev,
> > +				      struct device_attribute *attr, char *buf)
> > +{
> > +	return sysfs_emit(buf, "%s\n", to_counter_attribute(attr)->comp.name);
> > +}
> > +
> > +static int counter_name_attr_create(struct device *const dev,
> > +				    struct counter_attribute_group *const group,
> > +				    const char *const name)
> > +{
> > +	struct counter_attribute *counter_attr;
> > +
> > +	counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
> > +	if (!counter_attr)
> > +		return -ENOMEM;
> > +
> > +	/* Configure Counter attribute */
> > +	counter_attr->comp.name = name;
> > +
> > +	/* Configure device attribute */
> > +	sysfs_attr_init(&counter_attr->dev_attr.attr);
> > +	counter_attr->dev_attr.attr.name = "name";
> > +	counter_attr->dev_attr.attr.mode = 0444;
> > +	counter_attr->dev_attr.show = counter_comp_name_show;
> > +
> > +	/* Store list node */
> > +	list_add(&counter_attr->l, &group->attr_list);
> > +	group->num_attr++;
> > +
> > +	return 0;
> > +}
> > +
> > +static struct counter_comp counter_signal_comp = {
> > +	.type = COUNTER_COMP_SIGNAL_LEVEL,
> > +	.name = "signal",
> > +};
> > +
> > +static int counter_signal_attrs_create(struct counter_device *const counter,
> > +	struct counter_attribute_group *const cattr_group,
> > +	struct counter_signal *const signal)
> > +{
> > +	const enum counter_scope scope = COUNTER_SCOPE_SIGNAL;
> > +	struct device *const dev = &counter->dev;
> > +	int err;
> > +	struct counter_comp comp;
> > +	size_t i;
> > +
> > +	/* Create main Signal attribute */
> > +	comp = counter_signal_comp;
> > +	comp.signal_u32_read = counter->ops->signal_read;
> > +	err = counter_attr_create(dev, cattr_group, &comp, scope, signal);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	/* Create Signal name attribute */
> > +	err = counter_name_attr_create(dev, cattr_group, signal->name);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	/* Create an attribute for each extension */
> > +	for (i = 0; i < signal->num_ext; i++) {
> > +		err = counter_attr_create(dev, cattr_group, signal->ext + i,
> > +					  scope, signal);
> > +		if (err < 0)
> > +			return err;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int counter_sysfs_signals_add(struct counter_device *const counter,
> > +	struct counter_attribute_group *const groups)
> > +{
> > +	size_t i;
> > +	int err;
> > +
> > +	/* Add each Signal */
> > +	for (i = 0; i < counter->num_signals; i++) {
> > +		/* Generate Signal attribute directory name */
> > +		groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL,
> > +						"signal%zu", i);
> > +		if (!groups[i].name)
> > +			return -ENOMEM;
> > +
> > +		/* Create all attributes associated with Signal */
> > +		err = counter_signal_attrs_create(counter, groups + i,
> > +						  counter->signals + i);
> > +		if (err < 0)
> > +			return err;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int counter_sysfs_synapses_add(struct counter_device *const counter,
> > +	struct counter_attribute_group *const group,
> > +	struct counter_count *const count)
> > +{
> > +	size_t i;
> > +
> > +	/* Add each Synapse */
> > +	for (i = 0; i < count->num_synapses; i++) {
> > +		struct device *const dev = &counter->dev;
> > +		struct counter_synapse *synapse;
> > +		size_t id;
> > +		struct counter_comp comp;
> > +		int err;
> > +
> > +		synapse = count->synapses + i;
> > +
> > +		/* Generate Synapse action name */
> > +		id = synapse->signal - counter->signals;
> > +		comp.name = devm_kasprintf(dev, GFP_KERNEL, "signal%zu_action",
> > +					   id);
> > +		if (!comp.name)
> > +			return -ENOMEM;
> > +
> > +		/* Create action attribute */
> > +		comp.type = COUNTER_COMP_SYNAPSE_ACTION;
> > +		comp.action_read = counter->ops->action_read;
> > +		comp.action_write = counter->ops->action_write;
> > +		comp.priv = synapse;
> > +		err = counter_attr_create(dev, group, &comp,
> > +					  COUNTER_SCOPE_COUNT, count);
> > +		if (err < 0)
> > +			return err;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static struct counter_comp counter_count_comp =
> > +	COUNTER_COMP_COUNT_U64("count", NULL, NULL);
> > +
> > +static struct counter_comp counter_function_comp = {
> > +	.type = COUNTER_COMP_FUNCTION,
> > +	.name = "function",
> > +};
> > +
> > +static int counter_count_attrs_create(struct counter_device *const counter,
> > +	struct counter_attribute_group *const cattr_group,
> > +	struct counter_count *const count)
> > +{
> > +	const enum counter_scope scope = COUNTER_SCOPE_COUNT;
> > +	struct device *const dev = &counter->dev;
> > +	int err;
> > +	struct counter_comp comp;
> > +	size_t i;
> > +
> > +	/* Create main Count attribute */
> > +	comp = counter_count_comp;
> > +	comp.count_u64_read = counter->ops->count_read;
> > +	comp.count_u64_write = counter->ops->count_write;
> > +	err = counter_attr_create(dev, cattr_group, &comp, scope, count);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	/* Create Count name attribute */
> > +	err = counter_name_attr_create(dev, cattr_group, count->name);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	/* Create Count function attribute */
> > +	comp = counter_function_comp;
> > +	comp.count_u32_read = counter->ops->function_read;
> > +	comp.count_u32_write = counter->ops->function_write;
> > +	err = counter_attr_create(dev, cattr_group, &comp, scope, count);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	/* Create an attribute for each extension */
> > +	for (i = 0; i < count->num_ext; i++) {
> > +		err = counter_attr_create(dev, cattr_group, count->ext + i,
> > +					  scope, count);
> > +		if (err < 0)
> > +			return err;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int counter_sysfs_counts_add(struct counter_device *const counter,
> > +	struct counter_attribute_group *const groups)
> > +{
> > +	size_t i;
> > +	struct counter_count *count;
> > +	int err;
> > +
> > +	/* Add each Count */
> > +	for (i = 0; i < counter->num_counts; i++) {
> > +		count = counter->counts + i;
> > +
> > +		/* Generate Count attribute directory name */
> > +		groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL,
> > +						"count%zu", i);
> > +		if (!groups[i].name)
> > +			return -ENOMEM;
> > +
> > +		/* Add sysfs attributes of the Synapses */
> > +		err = counter_sysfs_synapses_add(counter, groups + i, count);
> > +		if (err < 0)
> > +			return err;
> > +
> > +		/* Create all attributes associated with Count */
> > +		err = counter_count_attrs_create(counter, groups + i, count);
> > +		if (err < 0)
> > +			return err;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int counter_num_signals_read(struct counter_device *counter, u8 *val)
> > +{
> > +	*val = counter->num_signals;
> > +	return 0;
> > +}
> > +
> > +static int counter_num_counts_read(struct counter_device *counter, u8 *val)
> > +{
> > +	*val = counter->num_counts;
> > +	return 0;
> > +}
> > +
> > +static struct counter_comp counter_num_signals_comp =
> > +	COUNTER_COMP_DEVICE_U8("num_signals", counter_num_signals_read, NULL);
> > +
> > +static struct counter_comp counter_num_counts_comp =
> > +	COUNTER_COMP_DEVICE_U8("num_counts", counter_num_counts_read, NULL);
> > +
> > +static int counter_sysfs_attr_add(struct counter_device *const counter,
> > +				  struct counter_attribute_group *cattr_group)
> > +{
> > +	const enum counter_scope scope = COUNTER_SCOPE_DEVICE;
> > +	struct device *const dev = &counter->dev;
> > +	int err;
> > +	size_t i;
> > +
> > +	/* Add Signals sysfs attributes */
> > +	err = counter_sysfs_signals_add(counter, cattr_group);
> > +	if (err < 0)
> > +		return err;
> > +	cattr_group += counter->num_signals;
> > +
> > +	/* Add Counts sysfs attributes */
> > +	err = counter_sysfs_counts_add(counter, cattr_group);
> > +	if (err < 0)
> > +		return err;
> > +	cattr_group += counter->num_counts;
> > +
> > +	/* Create name attribute */
> > +	err = counter_name_attr_create(dev, cattr_group, counter->name);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	/* Create num_signals attribute */
> > +	err = counter_attr_create(dev, cattr_group, &counter_num_signals_comp,
> > +				  scope, NULL);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	/* Create num_counts attribute */
> > +	err = counter_attr_create(dev, cattr_group, &counter_num_counts_comp,
> > +				  scope, NULL);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	/* Create an attribute for each extension */
> > +	for (i = 0; i < counter->num_ext; i++) {
> > +		err = counter_attr_create(dev, cattr_group, counter->ext + i,
> > +					  scope, NULL);
> > +		if (err < 0)
> > +			return err;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * counter_sysfs_add - Adds Counter sysfs attributes to the device structure
> > + * @counter:	Pointer to the Counter device structure
> > + *
> > + * Counter sysfs attributes are created and added to the respective device
> > + * structure for later registration to the system. Resource-managed memory
> > + * allocation is performed by this function, and this memory should be freed
> > + * when no longer needed (automatically by a device_unregister call, or
> > + * manually by a devres_release_all call).
> > + */
> > +int counter_sysfs_add(struct counter_device *const counter)
> > +{
> > +	struct device *const dev = &counter->dev;
> > +	const size_t num_groups = counter->num_signals + counter->num_counts + 1;
> > +	struct counter_attribute_group *cattr_groups;
> > +	size_t i, j;
> > +	int err;
> > +	struct attribute_group *groups;
> > +	struct counter_attribute *p;
> > +
> > +	/* Allocate space for attribute groups (signals, counts, and ext) */
> > +	cattr_groups = devm_kcalloc(dev, num_groups, sizeof(*cattr_groups),
> > +				    GFP_KERNEL);
> > +	if (!cattr_groups)
> > +		return -ENOMEM;
> > +
> > +	/* Initialize attribute lists */
> > +	for (i = 0; i < num_groups; i++)
> > +		INIT_LIST_HEAD(&cattr_groups[i].attr_list);
> > +
> > +	/* Add Counter device sysfs attributes */
> > +	err = counter_sysfs_attr_add(counter, cattr_groups);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	/* Allocate attribute group pointers for association with device */
> > +	dev->groups = devm_kcalloc(dev, num_groups + 1, sizeof(*dev->groups),
> > +				   GFP_KERNEL);
> > +	if (!dev->groups)
> > +		return -ENOMEM;
> > +
> > +	/* Allocate space for attribute groups */
> > +	groups = devm_kcalloc(dev, num_groups, sizeof(*groups), GFP_KERNEL);
> > +	if (!groups)
> > +		return -ENOMEM;
> > +
> > +	/* Prepare each group of attributes for association */
> > +	for (i = 0; i < num_groups; i++) {
> > +		groups[i].name = cattr_groups[i].name;
> > +
> > +		/* Allocate space for attribute pointers */
> > +		groups[i].attrs = devm_kcalloc(dev,
> > +					       cattr_groups[i].num_attr + 1,
> > +					       sizeof(*groups[i].attrs),
> > +					       GFP_KERNEL);
> > +		if (!groups[i].attrs)
> > +			return -ENOMEM;
> > +
> > +		/* Add attribute pointers to attribute group */
> > +		j = 0;
> > +		list_for_each_entry(p, &cattr_groups[i].attr_list, l)
> > +			groups[i].attrs[j++] = &p->dev_attr.attr;
> > +
> > +		/* Associate attribute group */
> > +		dev->groups[i] = &groups[i];
> > +	}
> > +
> > +	return 0;
> > +}
> > diff --git a/drivers/counter/counter-sysfs.h b/drivers/counter/counter-sysfs.h
> > new file mode 100644
> > index 000000000000..14fe566aca0e
> > --- /dev/null
> > +++ b/drivers/counter/counter-sysfs.h
> > @@ -0,0 +1,13 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Counter sysfs interface
> > + * Copyright (C) 2020 William Breathitt Gray
> > + */
> > +#ifndef _COUNTER_SYSFS_H_
> > +#define _COUNTER_SYSFS_H_
> > +
> > +#include <linux/counter.h>
> > +
> > +int counter_sysfs_add(struct counter_device *const counter);
> > +
> > +#endif /* _COUNTER_SYSFS_H_ */
> > diff --git a/drivers/counter/counter.c b/drivers/counter/counter.c
> > deleted file mode 100644
> > index de921e8a3f72..000000000000
> > --- a/drivers/counter/counter.c
> > +++ /dev/null
> > @@ -1,1496 +0,0 @@
> > -// SPDX-License-Identifier: GPL-2.0
> > -/*
> > - * Generic Counter interface
> > - * Copyright (C) 2018 William Breathitt Gray
> > - */
> > -#include <linux/counter.h>
> > -#include <linux/device.h>
> > -#include <linux/err.h>
> > -#include <linux/export.h>
> > -#include <linux/fs.h>
> > -#include <linux/gfp.h>
> > -#include <linux/idr.h>
> > -#include <linux/init.h>
> > -#include <linux/kernel.h>
> > -#include <linux/list.h>
> > -#include <linux/module.h>
> > -#include <linux/printk.h>
> > -#include <linux/slab.h>
> > -#include <linux/string.h>
> > -#include <linux/sysfs.h>
> > -#include <linux/types.h>
> > -
> > -const char *const counter_count_direction_str[2] = {
> > -	[COUNTER_COUNT_DIRECTION_FORWARD] = "forward",
> > -	[COUNTER_COUNT_DIRECTION_BACKWARD] = "backward"
> > -};
> > -EXPORT_SYMBOL_GPL(counter_count_direction_str);
> > -
> > -const char *const counter_count_mode_str[4] = {
> > -	[COUNTER_COUNT_MODE_NORMAL] = "normal",
> > -	[COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit",
> > -	[COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle",
> > -	[COUNTER_COUNT_MODE_MODULO_N] = "modulo-n"
> > -};
> > -EXPORT_SYMBOL_GPL(counter_count_mode_str);
> > -
> > -ssize_t counter_signal_enum_read(struct counter_device *counter,
> > -				 struct counter_signal *signal, void *priv,
> > -				 char *buf)
> > -{
> > -	const struct counter_signal_enum_ext *const e = priv;
> > -	int err;
> > -	size_t index;
> > -
> > -	if (!e->get)
> > -		return -EINVAL;
> > -
> > -	err = e->get(counter, signal, &index);
> > -	if (err)
> > -		return err;
> > -
> > -	if (index >= e->num_items)
> > -		return -EINVAL;
> > -
> > -	return sprintf(buf, "%s\n", e->items[index]);
> > -}
> > -EXPORT_SYMBOL_GPL(counter_signal_enum_read);
> > -
> > -ssize_t counter_signal_enum_write(struct counter_device *counter,
> > -				  struct counter_signal *signal, void *priv,
> > -				  const char *buf, size_t len)
> > -{
> > -	const struct counter_signal_enum_ext *const e = priv;
> > -	ssize_t index;
> > -	int err;
> > -
> > -	if (!e->set)
> > -		return -EINVAL;
> > -
> > -	index = __sysfs_match_string(e->items, e->num_items, buf);
> > -	if (index < 0)
> > -		return index;
> > -
> > -	err = e->set(counter, signal, index);
> > -	if (err)
> > -		return err;
> > -
> > -	return len;
> > -}
> > -EXPORT_SYMBOL_GPL(counter_signal_enum_write);
> > -
> > -ssize_t counter_signal_enum_available_read(struct counter_device *counter,
> > -					   struct counter_signal *signal,
> > -					   void *priv, char *buf)
> > -{
> > -	const struct counter_signal_enum_ext *const e = priv;
> > -	size_t i;
> > -	size_t len = 0;
> > -
> > -	if (!e->num_items)
> > -		return 0;
> > -
> > -	for (i = 0; i < e->num_items; i++)
> > -		len += sprintf(buf + len, "%s\n", e->items[i]);
> > -
> > -	return len;
> > -}
> > -EXPORT_SYMBOL_GPL(counter_signal_enum_available_read);
> > -
> > -ssize_t counter_count_enum_read(struct counter_device *counter,
> > -				struct counter_count *count, void *priv,
> > -				char *buf)
> > -{
> > -	const struct counter_count_enum_ext *const e = priv;
> > -	int err;
> > -	size_t index;
> > -
> > -	if (!e->get)
> > -		return -EINVAL;
> > -
> > -	err = e->get(counter, count, &index);
> > -	if (err)
> > -		return err;
> > -
> > -	if (index >= e->num_items)
> > -		return -EINVAL;
> > -
> > -	return sprintf(buf, "%s\n", e->items[index]);
> > -}
> > -EXPORT_SYMBOL_GPL(counter_count_enum_read);
> > -
> > -ssize_t counter_count_enum_write(struct counter_device *counter,
> > -				 struct counter_count *count, void *priv,
> > -				 const char *buf, size_t len)
> > -{
> > -	const struct counter_count_enum_ext *const e = priv;
> > -	ssize_t index;
> > -	int err;
> > -
> > -	if (!e->set)
> > -		return -EINVAL;
> > -
> > -	index = __sysfs_match_string(e->items, e->num_items, buf);
> > -	if (index < 0)
> > -		return index;
> > -
> > -	err = e->set(counter, count, index);
> > -	if (err)
> > -		return err;
> > -
> > -	return len;
> > -}
> > -EXPORT_SYMBOL_GPL(counter_count_enum_write);
> > -
> > -ssize_t counter_count_enum_available_read(struct counter_device *counter,
> > -					  struct counter_count *count,
> > -					  void *priv, char *buf)
> > -{
> > -	const struct counter_count_enum_ext *const e = priv;
> > -	size_t i;
> > -	size_t len = 0;
> > -
> > -	if (!e->num_items)
> > -		return 0;
> > -
> > -	for (i = 0; i < e->num_items; i++)
> > -		len += sprintf(buf + len, "%s\n", e->items[i]);
> > -
> > -	return len;
> > -}
> > -EXPORT_SYMBOL_GPL(counter_count_enum_available_read);
> > -
> > -ssize_t counter_device_enum_read(struct counter_device *counter, void *priv,
> > -				 char *buf)
> > -{
> > -	const struct counter_device_enum_ext *const e = priv;
> > -	int err;
> > -	size_t index;
> > -
> > -	if (!e->get)
> > -		return -EINVAL;
> > -
> > -	err = e->get(counter, &index);
> > -	if (err)
> > -		return err;
> > -
> > -	if (index >= e->num_items)
> > -		return -EINVAL;
> > -
> > -	return sprintf(buf, "%s\n", e->items[index]);
> > -}
> > -EXPORT_SYMBOL_GPL(counter_device_enum_read);
> > -
> > -ssize_t counter_device_enum_write(struct counter_device *counter, void *priv,
> > -				  const char *buf, size_t len)
> > -{
> > -	const struct counter_device_enum_ext *const e = priv;
> > -	ssize_t index;
> > -	int err;
> > -
> > -	if (!e->set)
> > -		return -EINVAL;
> > -
> > -	index = __sysfs_match_string(e->items, e->num_items, buf);
> > -	if (index < 0)
> > -		return index;
> > -
> > -	err = e->set(counter, index);
> > -	if (err)
> > -		return err;
> > -
> > -	return len;
> > -}
> > -EXPORT_SYMBOL_GPL(counter_device_enum_write);
> > -
> > -ssize_t counter_device_enum_available_read(struct counter_device *counter,
> > -					   void *priv, char *buf)
> > -{
> > -	const struct counter_device_enum_ext *const e = priv;
> > -	size_t i;
> > -	size_t len = 0;
> > -
> > -	if (!e->num_items)
> > -		return 0;
> > -
> > -	for (i = 0; i < e->num_items; i++)
> > -		len += sprintf(buf + len, "%s\n", e->items[i]);
> > -
> > -	return len;
> > -}
> > -EXPORT_SYMBOL_GPL(counter_device_enum_available_read);
> > -
> > -struct counter_attr_parm {
> > -	struct counter_device_attr_group *group;
> > -	const char *prefix;
> > -	const char *name;
> > -	ssize_t (*show)(struct device *dev, struct device_attribute *attr,
> > -			char *buf);
> > -	ssize_t (*store)(struct device *dev, struct device_attribute *attr,
> > -			 const char *buf, size_t len);
> > -	void *component;
> > -};
> > -
> > -struct counter_device_attr {
> > -	struct device_attribute dev_attr;
> > -	struct list_head l;
> > -	void *component;
> > -};
> > -
> > -static int counter_attribute_create(const struct counter_attr_parm *const parm)
> > -{
> > -	struct counter_device_attr *counter_attr;
> > -	struct device_attribute *dev_attr;
> > -	int err;
> > -	struct list_head *const attr_list = &parm->group->attr_list;
> > -
> > -	/* Allocate a Counter device attribute */
> > -	counter_attr = kzalloc(sizeof(*counter_attr), GFP_KERNEL);
> > -	if (!counter_attr)
> > -		return -ENOMEM;
> > -	dev_attr = &counter_attr->dev_attr;
> > -
> > -	sysfs_attr_init(&dev_attr->attr);
> > -
> > -	/* Configure device attribute */
> > -	dev_attr->attr.name = kasprintf(GFP_KERNEL, "%s%s", parm->prefix,
> > -					parm->name);
> > -	if (!dev_attr->attr.name) {
> > -		err = -ENOMEM;
> > -		goto err_free_counter_attr;
> > -	}
> > -	if (parm->show) {
> > -		dev_attr->attr.mode |= 0444;
> > -		dev_attr->show = parm->show;
> > -	}
> > -	if (parm->store) {
> > -		dev_attr->attr.mode |= 0200;
> > -		dev_attr->store = parm->store;
> > -	}
> > -
> > -	/* Store associated Counter component with attribute */
> > -	counter_attr->component = parm->component;
> > -
> > -	/* Keep track of the attribute for later cleanup */
> > -	list_add(&counter_attr->l, attr_list);
> > -	parm->group->num_attr++;
> > -
> > -	return 0;
> > -
> > -err_free_counter_attr:
> > -	kfree(counter_attr);
> > -	return err;
> > -}
> > -
> > -#define to_counter_attr(_dev_attr) \
> > -	container_of(_dev_attr, struct counter_device_attr, dev_attr)
> > -
> > -struct counter_signal_unit {
> > -	struct counter_signal *signal;
> > -};
> > -
> > -static const char *const counter_signal_level_str[] = {
> > -	[COUNTER_SIGNAL_LEVEL_LOW] = "low",
> > -	[COUNTER_SIGNAL_LEVEL_HIGH] = "high"
> > -};
> > -
> > -static ssize_t counter_signal_show(struct device *dev,
> > -				   struct device_attribute *attr, char *buf)
> > -{
> > -	struct counter_device *const counter = dev_get_drvdata(dev);
> > -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> > -	const struct counter_signal_unit *const component = devattr->component;
> > -	struct counter_signal *const signal = component->signal;
> > -	int err;
> > -	enum counter_signal_level level;
> > -
> > -	err = counter->ops->signal_read(counter, signal, &level);
> > -	if (err)
> > -		return err;
> > -
> > -	return sprintf(buf, "%s\n", counter_signal_level_str[level]);
> > -}
> > -
> > -struct counter_name_unit {
> > -	const char *name;
> > -};
> > -
> > -static ssize_t counter_device_attr_name_show(struct device *dev,
> > -					     struct device_attribute *attr,
> > -					     char *buf)
> > -{
> > -	const struct counter_name_unit *const comp = to_counter_attr(attr)->component;
> > -
> > -	return sprintf(buf, "%s\n", comp->name);
> > -}
> > -
> > -static int counter_name_attribute_create(
> > -	struct counter_device_attr_group *const group,
> > -	const char *const name)
> > -{
> > -	struct counter_name_unit *name_comp;
> > -	struct counter_attr_parm parm;
> > -	int err;
> > -
> > -	/* Skip if no name */
> > -	if (!name)
> > -		return 0;
> > -
> > -	/* Allocate name attribute component */
> > -	name_comp = kmalloc(sizeof(*name_comp), GFP_KERNEL);
> > -	if (!name_comp)
> > -		return -ENOMEM;
> > -	name_comp->name = name;
> > -
> > -	/* Allocate Signal name attribute */
> > -	parm.group = group;
> > -	parm.prefix = "";
> > -	parm.name = "name";
> > -	parm.show = counter_device_attr_name_show;
> > -	parm.store = NULL;
> > -	parm.component = name_comp;
> > -	err = counter_attribute_create(&parm);
> > -	if (err)
> > -		goto err_free_name_comp;
> > -
> > -	return 0;
> > -
> > -err_free_name_comp:
> > -	kfree(name_comp);
> > -	return err;
> > -}
> > -
> > -struct counter_signal_ext_unit {
> > -	struct counter_signal *signal;
> > -	const struct counter_signal_ext *ext;
> > -};
> > -
> > -static ssize_t counter_signal_ext_show(struct device *dev,
> > -				       struct device_attribute *attr, char *buf)
> > -{
> > -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> > -	const struct counter_signal_ext_unit *const comp = devattr->component;
> > -	const struct counter_signal_ext *const ext = comp->ext;
> > -
> > -	return ext->read(dev_get_drvdata(dev), comp->signal, ext->priv, buf);
> > -}
> > -
> > -static ssize_t counter_signal_ext_store(struct device *dev,
> > -					struct device_attribute *attr,
> > -					const char *buf, size_t len)
> > -{
> > -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> > -	const struct counter_signal_ext_unit *const comp = devattr->component;
> > -	const struct counter_signal_ext *const ext = comp->ext;
> > -
> > -	return ext->write(dev_get_drvdata(dev), comp->signal, ext->priv, buf,
> > -		len);
> > -}
> > -
> > -static void counter_device_attr_list_free(struct list_head *attr_list)
> > -{
> > -	struct counter_device_attr *p, *n;
> > -
> > -	list_for_each_entry_safe(p, n, attr_list, l) {
> > -		/* free attribute name and associated component memory */
> > -		kfree(p->dev_attr.attr.name);
> > -		kfree(p->component);
> > -		list_del(&p->l);
> > -		kfree(p);
> > -	}
> > -}
> > -
> > -static int counter_signal_ext_register(
> > -	struct counter_device_attr_group *const group,
> > -	struct counter_signal *const signal)
> > -{
> > -	const size_t num_ext = signal->num_ext;
> > -	size_t i;
> > -	const struct counter_signal_ext *ext;
> > -	struct counter_signal_ext_unit *signal_ext_comp;
> > -	struct counter_attr_parm parm;
> > -	int err;
> > -
> > -	/* Create an attribute for each extension */
> > -	for (i = 0 ; i < num_ext; i++) {
> > -		ext = signal->ext + i;
> > -
> > -		/* Allocate signal_ext attribute component */
> > -		signal_ext_comp = kmalloc(sizeof(*signal_ext_comp), GFP_KERNEL);
> > -		if (!signal_ext_comp) {
> > -			err = -ENOMEM;
> > -			goto err_free_attr_list;
> > -		}
> > -		signal_ext_comp->signal = signal;
> > -		signal_ext_comp->ext = ext;
> > -
> > -		/* Allocate a Counter device attribute */
> > -		parm.group = group;
> > -		parm.prefix = "";
> > -		parm.name = ext->name;
> > -		parm.show = (ext->read) ? counter_signal_ext_show : NULL;
> > -		parm.store = (ext->write) ? counter_signal_ext_store : NULL;
> > -		parm.component = signal_ext_comp;
> > -		err = counter_attribute_create(&parm);
> > -		if (err) {
> > -			kfree(signal_ext_comp);
> > -			goto err_free_attr_list;
> > -		}
> > -	}
> > -
> > -	return 0;
> > -
> > -err_free_attr_list:
> > -	counter_device_attr_list_free(&group->attr_list);
> > -	return err;
> > -}
> > -
> > -static int counter_signal_attributes_create(
> > -	struct counter_device_attr_group *const group,
> > -	const struct counter_device *const counter,
> > -	struct counter_signal *const signal)
> > -{
> > -	struct counter_signal_unit *signal_comp;
> > -	struct counter_attr_parm parm;
> > -	int err;
> > -
> > -	/* Allocate Signal attribute component */
> > -	signal_comp = kmalloc(sizeof(*signal_comp), GFP_KERNEL);
> > -	if (!signal_comp)
> > -		return -ENOMEM;
> > -	signal_comp->signal = signal;
> > -
> > -	/* Create main Signal attribute */
> > -	parm.group = group;
> > -	parm.prefix = "";
> > -	parm.name = "signal";
> > -	parm.show = (counter->ops->signal_read) ? counter_signal_show : NULL;
> > -	parm.store = NULL;
> > -	parm.component = signal_comp;
> > -	err = counter_attribute_create(&parm);
> > -	if (err) {
> > -		kfree(signal_comp);
> > -		return err;
> > -	}
> > -
> > -	/* Create Signal name attribute */
> > -	err = counter_name_attribute_create(group, signal->name);
> > -	if (err)
> > -		goto err_free_attr_list;
> > -
> > -	/* Register Signal extension attributes */
> > -	err = counter_signal_ext_register(group, signal);
> > -	if (err)
> > -		goto err_free_attr_list;
> > -
> > -	return 0;
> > -
> > -err_free_attr_list:
> > -	counter_device_attr_list_free(&group->attr_list);
> > -	return err;
> > -}
> > -
> > -static int counter_signals_register(
> > -	struct counter_device_attr_group *const groups_list,
> > -	const struct counter_device *const counter)
> > -{
> > -	const size_t num_signals = counter->num_signals;
> > -	size_t i;
> > -	struct counter_signal *signal;
> > -	const char *name;
> > -	int err;
> > -
> > -	/* Register each Signal */
> > -	for (i = 0; i < num_signals; i++) {
> > -		signal = counter->signals + i;
> > -
> > -		/* Generate Signal attribute directory name */
> > -		name = kasprintf(GFP_KERNEL, "signal%d", signal->id);
> > -		if (!name) {
> > -			err = -ENOMEM;
> > -			goto err_free_attr_groups;
> > -		}
> > -		groups_list[i].attr_group.name = name;
> > -
> > -		/* Create all attributes associated with Signal */
> > -		err = counter_signal_attributes_create(groups_list + i, counter,
> > -						       signal);
> > -		if (err)
> > -			goto err_free_attr_groups;
> > -	}
> > -
> > -	return 0;
> > -
> > -err_free_attr_groups:
> > -	do {
> > -		kfree(groups_list[i].attr_group.name);
> > -		counter_device_attr_list_free(&groups_list[i].attr_list);
> > -	} while (i--);
> > -	return err;
> > -}
> > -
> > -static const char *const counter_synapse_action_str[] = {
> > -	[COUNTER_SYNAPSE_ACTION_NONE] = "none",
> > -	[COUNTER_SYNAPSE_ACTION_RISING_EDGE] = "rising edge",
> > -	[COUNTER_SYNAPSE_ACTION_FALLING_EDGE] = "falling edge",
> > -	[COUNTER_SYNAPSE_ACTION_BOTH_EDGES] = "both edges"
> > -};
> > -
> > -struct counter_action_unit {
> > -	struct counter_synapse *synapse;
> > -	struct counter_count *count;
> > -};
> > -
> > -static ssize_t counter_action_show(struct device *dev,
> > -				   struct device_attribute *attr, char *buf)
> > -{
> > -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> > -	int err;
> > -	struct counter_device *const counter = dev_get_drvdata(dev);
> > -	const struct counter_action_unit *const component = devattr->component;
> > -	struct counter_count *const count = component->count;
> > -	struct counter_synapse *const synapse = component->synapse;
> > -	size_t action_index;
> > -	enum counter_synapse_action action;
> > -
> > -	err = counter->ops->action_get(counter, count, synapse, &action_index);
> > -	if (err)
> > -		return err;
> > -
> > -	synapse->action = action_index;
> > -
> > -	action = synapse->actions_list[action_index];
> > -	return sprintf(buf, "%s\n", counter_synapse_action_str[action]);
> > -}
> > -
> > -static ssize_t counter_action_store(struct device *dev,
> > -				    struct device_attribute *attr,
> > -				    const char *buf, size_t len)
> > -{
> > -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> > -	const struct counter_action_unit *const component = devattr->component;
> > -	struct counter_synapse *const synapse = component->synapse;
> > -	size_t action_index;
> > -	const size_t num_actions = synapse->num_actions;
> > -	enum counter_synapse_action action;
> > -	int err;
> > -	struct counter_device *const counter = dev_get_drvdata(dev);
> > -	struct counter_count *const count = component->count;
> > -
> > -	/* Find requested action mode */
> > -	for (action_index = 0; action_index < num_actions; action_index++) {
> > -		action = synapse->actions_list[action_index];
> > -		if (sysfs_streq(buf, counter_synapse_action_str[action]))
> > -			break;
> > -	}
> > -	/* If requested action mode not found */
> > -	if (action_index >= num_actions)
> > -		return -EINVAL;
> > -
> > -	err = counter->ops->action_set(counter, count, synapse, action_index);
> > -	if (err)
> > -		return err;
> > -
> > -	synapse->action = action_index;
> > -
> > -	return len;
> > -}
> > -
> > -struct counter_action_avail_unit {
> > -	const enum counter_synapse_action *actions_list;
> > -	size_t num_actions;
> > -};
> > -
> > -static ssize_t counter_synapse_action_available_show(struct device *dev,
> > -	struct device_attribute *attr, char *buf)
> > -{
> > -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> > -	const struct counter_action_avail_unit *const component = devattr->component;
> > -	size_t i;
> > -	enum counter_synapse_action action;
> > -	ssize_t len = 0;
> > -
> > -	for (i = 0; i < component->num_actions; i++) {
> > -		action = component->actions_list[i];
> > -		len += sprintf(buf + len, "%s\n",
> > -			       counter_synapse_action_str[action]);
> > -	}
> > -
> > -	return len;
> > -}
> > -
> > -static int counter_synapses_register(
> > -	struct counter_device_attr_group *const group,
> > -	const struct counter_device *const counter,
> > -	struct counter_count *const count, const char *const count_attr_name)
> > -{
> > -	size_t i;
> > -	struct counter_synapse *synapse;
> > -	const char *prefix;
> > -	struct counter_action_unit *action_comp;
> > -	struct counter_attr_parm parm;
> > -	int err;
> > -	struct counter_action_avail_unit *avail_comp;
> > -
> > -	/* Register each Synapse */
> > -	for (i = 0; i < count->num_synapses; i++) {
> > -		synapse = count->synapses + i;
> > -
> > -		/* Generate attribute prefix */
> > -		prefix = kasprintf(GFP_KERNEL, "signal%d_",
> > -				   synapse->signal->id);
> > -		if (!prefix) {
> > -			err = -ENOMEM;
> > -			goto err_free_attr_list;
> > -		}
> > -
> > -		/* Allocate action attribute component */
> > -		action_comp = kmalloc(sizeof(*action_comp), GFP_KERNEL);
> > -		if (!action_comp) {
> > -			err = -ENOMEM;
> > -			goto err_free_prefix;
> > -		}
> > -		action_comp->synapse = synapse;
> > -		action_comp->count = count;
> > -
> > -		/* Create action attribute */
> > -		parm.group = group;
> > -		parm.prefix = prefix;
> > -		parm.name = "action";
> > -		parm.show = (counter->ops->action_get) ? counter_action_show : NULL;
> > -		parm.store = (counter->ops->action_set) ? counter_action_store : NULL;
> > -		parm.component = action_comp;
> > -		err = counter_attribute_create(&parm);
> > -		if (err) {
> > -			kfree(action_comp);
> > -			goto err_free_prefix;
> > -		}
> > -
> > -		/* Allocate action available attribute component */
> > -		avail_comp = kmalloc(sizeof(*avail_comp), GFP_KERNEL);
> > -		if (!avail_comp) {
> > -			err = -ENOMEM;
> > -			goto err_free_prefix;
> > -		}
> > -		avail_comp->actions_list = synapse->actions_list;
> > -		avail_comp->num_actions = synapse->num_actions;
> > -
> > -		/* Create action_available attribute */
> > -		parm.group = group;
> > -		parm.prefix = prefix;
> > -		parm.name = "action_available";
> > -		parm.show = counter_synapse_action_available_show;
> > -		parm.store = NULL;
> > -		parm.component = avail_comp;
> > -		err = counter_attribute_create(&parm);
> > -		if (err) {
> > -			kfree(avail_comp);
> > -			goto err_free_prefix;
> > -		}
> > -
> > -		kfree(prefix);
> > -	}
> > -
> > -	return 0;
> > -
> > -err_free_prefix:
> > -	kfree(prefix);
> > -err_free_attr_list:
> > -	counter_device_attr_list_free(&group->attr_list);
> > -	return err;
> > -}
> > -
> > -struct counter_count_unit {
> > -	struct counter_count *count;
> > -};
> > -
> > -static ssize_t counter_count_show(struct device *dev,
> > -				  struct device_attribute *attr,
> > -				  char *buf)
> > -{
> > -	struct counter_device *const counter = dev_get_drvdata(dev);
> > -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> > -	const struct counter_count_unit *const component = devattr->component;
> > -	struct counter_count *const count = component->count;
> > -	int err;
> > -	unsigned long val;
> > -
> > -	err = counter->ops->count_read(counter, count, &val);
> > -	if (err)
> > -		return err;
> > -
> > -	return sprintf(buf, "%lu\n", val);
> > -}
> > -
> > -static ssize_t counter_count_store(struct device *dev,
> > -				   struct device_attribute *attr,
> > -				   const char *buf, size_t len)
> > -{
> > -	struct counter_device *const counter = dev_get_drvdata(dev);
> > -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> > -	const struct counter_count_unit *const component = devattr->component;
> > -	struct counter_count *const count = component->count;
> > -	int err;
> > -	unsigned long val;
> > -
> > -	err = kstrtoul(buf, 0, &val);
> > -	if (err)
> > -		return err;
> > -
> > -	err = counter->ops->count_write(counter, count, val);
> > -	if (err)
> > -		return err;
> > -
> > -	return len;
> > -}
> > -
> > -static const char *const counter_function_str[] = {
> > -	[COUNTER_FUNCTION_INCREASE] = "increase",
> > -	[COUNTER_FUNCTION_DECREASE] = "decrease",
> > -	[COUNTER_FUNCTION_PULSE_DIRECTION] = "pulse-direction",
> > -	[COUNTER_FUNCTION_QUADRATURE_X1_A] = "quadrature x1 a",
> > -	[COUNTER_FUNCTION_QUADRATURE_X1_B] = "quadrature x1 b",
> > -	[COUNTER_FUNCTION_QUADRATURE_X2_A] = "quadrature x2 a",
> > -	[COUNTER_FUNCTION_QUADRATURE_X2_B] = "quadrature x2 b",
> > -	[COUNTER_FUNCTION_QUADRATURE_X4] = "quadrature x4"
> > -};
> > -
> > -static ssize_t counter_function_show(struct device *dev,
> > -				     struct device_attribute *attr, char *buf)
> > -{
> > -	int err;
> > -	struct counter_device *const counter = dev_get_drvdata(dev);
> > -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> > -	const struct counter_count_unit *const component = devattr->component;
> > -	struct counter_count *const count = component->count;
> > -	size_t func_index;
> > -	enum counter_function function;
> > -
> > -	err = counter->ops->function_get(counter, count, &func_index);
> > -	if (err)
> > -		return err;
> > -
> > -	count->function = func_index;
> > -
> > -	function = count->functions_list[func_index];
> > -	return sprintf(buf, "%s\n", counter_function_str[function]);
> > -}
> > -
> > -static ssize_t counter_function_store(struct device *dev,
> > -				      struct device_attribute *attr,
> > -				      const char *buf, size_t len)
> > -{
> > -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> > -	const struct counter_count_unit *const component = devattr->component;
> > -	struct counter_count *const count = component->count;
> > -	const size_t num_functions = count->num_functions;
> > -	size_t func_index;
> > -	enum counter_function function;
> > -	int err;
> > -	struct counter_device *const counter = dev_get_drvdata(dev);
> > -
> > -	/* Find requested Count function mode */
> > -	for (func_index = 0; func_index < num_functions; func_index++) {
> > -		function = count->functions_list[func_index];
> > -		if (sysfs_streq(buf, counter_function_str[function]))
> > -			break;
> > -	}
> > -	/* Return error if requested Count function mode not found */
> > -	if (func_index >= num_functions)
> > -		return -EINVAL;
> > -
> > -	err = counter->ops->function_set(counter, count, func_index);
> > -	if (err)
> > -		return err;
> > -
> > -	count->function = func_index;
> > -
> > -	return len;
> > -}
> > -
> > -struct counter_count_ext_unit {
> > -	struct counter_count *count;
> > -	const struct counter_count_ext *ext;
> > -};
> > -
> > -static ssize_t counter_count_ext_show(struct device *dev,
> > -				      struct device_attribute *attr, char *buf)
> > -{
> > -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> > -	const struct counter_count_ext_unit *const comp = devattr->component;
> > -	const struct counter_count_ext *const ext = comp->ext;
> > -
> > -	return ext->read(dev_get_drvdata(dev), comp->count, ext->priv, buf);
> > -}
> > -
> > -static ssize_t counter_count_ext_store(struct device *dev,
> > -				       struct device_attribute *attr,
> > -				       const char *buf, size_t len)
> > -{
> > -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> > -	const struct counter_count_ext_unit *const comp = devattr->component;
> > -	const struct counter_count_ext *const ext = comp->ext;
> > -
> > -	return ext->write(dev_get_drvdata(dev), comp->count, ext->priv, buf,
> > -		len);
> > -}
> > -
> > -static int counter_count_ext_register(
> > -	struct counter_device_attr_group *const group,
> > -	struct counter_count *const count)
> > -{
> > -	size_t i;
> > -	const struct counter_count_ext *ext;
> > -	struct counter_count_ext_unit *count_ext_comp;
> > -	struct counter_attr_parm parm;
> > -	int err;
> > -
> > -	/* Create an attribute for each extension */
> > -	for (i = 0 ; i < count->num_ext; i++) {
> > -		ext = count->ext + i;
> > -
> > -		/* Allocate count_ext attribute component */
> > -		count_ext_comp = kmalloc(sizeof(*count_ext_comp), GFP_KERNEL);
> > -		if (!count_ext_comp) {
> > -			err = -ENOMEM;
> > -			goto err_free_attr_list;
> > -		}
> > -		count_ext_comp->count = count;
> > -		count_ext_comp->ext = ext;
> > -
> > -		/* Allocate count_ext attribute */
> > -		parm.group = group;
> > -		parm.prefix = "";
> > -		parm.name = ext->name;
> > -		parm.show = (ext->read) ? counter_count_ext_show : NULL;
> > -		parm.store = (ext->write) ? counter_count_ext_store : NULL;
> > -		parm.component = count_ext_comp;
> > -		err = counter_attribute_create(&parm);
> > -		if (err) {
> > -			kfree(count_ext_comp);
> > -			goto err_free_attr_list;
> > -		}
> > -	}
> > -
> > -	return 0;
> > -
> > -err_free_attr_list:
> > -	counter_device_attr_list_free(&group->attr_list);
> > -	return err;
> > -}
> > -
> > -struct counter_func_avail_unit {
> > -	const enum counter_function *functions_list;
> > -	size_t num_functions;
> > -};
> > -
> > -static ssize_t counter_function_available_show(struct device *dev,
> > -	struct device_attribute *attr, char *buf)
> > -{
> > -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> > -	const struct counter_func_avail_unit *const component = devattr->component;
> > -	const enum counter_function *const func_list = component->functions_list;
> > -	const size_t num_functions = component->num_functions;
> > -	size_t i;
> > -	enum counter_function function;
> > -	ssize_t len = 0;
> > -
> > -	for (i = 0; i < num_functions; i++) {
> > -		function = func_list[i];
> > -		len += sprintf(buf + len, "%s\n",
> > -			       counter_function_str[function]);
> > -	}
> > -
> > -	return len;
> > -}
> > -
> > -static int counter_count_attributes_create(
> > -	struct counter_device_attr_group *const group,
> > -	const struct counter_device *const counter,
> > -	struct counter_count *const count)
> > -{
> > -	struct counter_count_unit *count_comp;
> > -	struct counter_attr_parm parm;
> > -	int err;
> > -	struct counter_count_unit *func_comp;
> > -	struct counter_func_avail_unit *avail_comp;
> > -
> > -	/* Allocate count attribute component */
> > -	count_comp = kmalloc(sizeof(*count_comp), GFP_KERNEL);
> > -	if (!count_comp)
> > -		return -ENOMEM;
> > -	count_comp->count = count;
> > -
> > -	/* Create main Count attribute */
> > -	parm.group = group;
> > -	parm.prefix = "";
> > -	parm.name = "count";
> > -	parm.show = (counter->ops->count_read) ? counter_count_show : NULL;
> > -	parm.store = (counter->ops->count_write) ? counter_count_store : NULL;
> > -	parm.component = count_comp;
> > -	err = counter_attribute_create(&parm);
> > -	if (err) {
> > -		kfree(count_comp);
> > -		return err;
> > -	}
> > -
> > -	/* Allocate function attribute component */
> > -	func_comp = kmalloc(sizeof(*func_comp), GFP_KERNEL);
> > -	if (!func_comp) {
> > -		err = -ENOMEM;
> > -		goto err_free_attr_list;
> > -	}
> > -	func_comp->count = count;
> > -
> > -	/* Create Count function attribute */
> > -	parm.group = group;
> > -	parm.prefix = "";
> > -	parm.name = "function";
> > -	parm.show = (counter->ops->function_get) ? counter_function_show : NULL;
> > -	parm.store = (counter->ops->function_set) ? counter_function_store : NULL;
> > -	parm.component = func_comp;
> > -	err = counter_attribute_create(&parm);
> > -	if (err) {
> > -		kfree(func_comp);
> > -		goto err_free_attr_list;
> > -	}
> > -
> > -	/* Allocate function available attribute component */
> > -	avail_comp = kmalloc(sizeof(*avail_comp), GFP_KERNEL);
> > -	if (!avail_comp) {
> > -		err = -ENOMEM;
> > -		goto err_free_attr_list;
> > -	}
> > -	avail_comp->functions_list = count->functions_list;
> > -	avail_comp->num_functions = count->num_functions;
> > -
> > -	/* Create Count function_available attribute */
> > -	parm.group = group;
> > -	parm.prefix = "";
> > -	parm.name = "function_available";
> > -	parm.show = counter_function_available_show;
> > -	parm.store = NULL;
> > -	parm.component = avail_comp;
> > -	err = counter_attribute_create(&parm);
> > -	if (err) {
> > -		kfree(avail_comp);
> > -		goto err_free_attr_list;
> > -	}
> > -
> > -	/* Create Count name attribute */
> > -	err = counter_name_attribute_create(group, count->name);
> > -	if (err)
> > -		goto err_free_attr_list;
> > -
> > -	/* Register Count extension attributes */
> > -	err = counter_count_ext_register(group, count);
> > -	if (err)
> > -		goto err_free_attr_list;
> > -
> > -	return 0;
> > -
> > -err_free_attr_list:
> > -	counter_device_attr_list_free(&group->attr_list);
> > -	return err;
> > -}
> > -
> > -static int counter_counts_register(
> > -	struct counter_device_attr_group *const groups_list,
> > -	const struct counter_device *const counter)
> > -{
> > -	size_t i;
> > -	struct counter_count *count;
> > -	const char *name;
> > -	int err;
> > -
> > -	/* Register each Count */
> > -	for (i = 0; i < counter->num_counts; i++) {
> > -		count = counter->counts + i;
> > -
> > -		/* Generate Count attribute directory name */
> > -		name = kasprintf(GFP_KERNEL, "count%d", count->id);
> > -		if (!name) {
> > -			err = -ENOMEM;
> > -			goto err_free_attr_groups;
> > -		}
> > -		groups_list[i].attr_group.name = name;
> > -
> > -		/* Register the Synapses associated with each Count */
> > -		err = counter_synapses_register(groups_list + i, counter, count,
> > -						name);
> > -		if (err)
> > -			goto err_free_attr_groups;
> > -
> > -		/* Create all attributes associated with Count */
> > -		err = counter_count_attributes_create(groups_list + i, counter,
> > -						      count);
> > -		if (err)
> > -			goto err_free_attr_groups;
> > -	}
> > -
> > -	return 0;
> > -
> > -err_free_attr_groups:
> > -	do {
> > -		kfree(groups_list[i].attr_group.name);
> > -		counter_device_attr_list_free(&groups_list[i].attr_list);
> > -	} while (i--);
> > -	return err;
> > -}
> > -
> > -struct counter_size_unit {
> > -	size_t size;
> > -};
> > -
> > -static ssize_t counter_device_attr_size_show(struct device *dev,
> > -					     struct device_attribute *attr,
> > -					     char *buf)
> > -{
> > -	const struct counter_size_unit *const comp = to_counter_attr(attr)->component;
> > -
> > -	return sprintf(buf, "%zu\n", comp->size);
> > -}
> > -
> > -static int counter_size_attribute_create(
> > -	struct counter_device_attr_group *const group,
> > -	const size_t size, const char *const name)
> > -{
> > -	struct counter_size_unit *size_comp;
> > -	struct counter_attr_parm parm;
> > -	int err;
> > -
> > -	/* Allocate size attribute component */
> > -	size_comp = kmalloc(sizeof(*size_comp), GFP_KERNEL);
> > -	if (!size_comp)
> > -		return -ENOMEM;
> > -	size_comp->size = size;
> > -
> > -	parm.group = group;
> > -	parm.prefix = "";
> > -	parm.name = name;
> > -	parm.show = counter_device_attr_size_show;
> > -	parm.store = NULL;
> > -	parm.component = size_comp;
> > -	err = counter_attribute_create(&parm);
> > -	if (err)
> > -		goto err_free_size_comp;
> > -
> > -	return 0;
> > -
> > -err_free_size_comp:
> > -	kfree(size_comp);
> > -	return err;
> > -}
> > -
> > -struct counter_ext_unit {
> > -	const struct counter_device_ext *ext;
> > -};
> > -
> > -static ssize_t counter_device_ext_show(struct device *dev,
> > -				       struct device_attribute *attr, char *buf)
> > -{
> > -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> > -	const struct counter_ext_unit *const component = devattr->component;
> > -	const struct counter_device_ext *const ext = component->ext;
> > -
> > -	return ext->read(dev_get_drvdata(dev), ext->priv, buf);
> > -}
> > -
> > -static ssize_t counter_device_ext_store(struct device *dev,
> > -					struct device_attribute *attr,
> > -					const char *buf, size_t len)
> > -{
> > -	const struct counter_device_attr *const devattr = to_counter_attr(attr);
> > -	const struct counter_ext_unit *const component = devattr->component;
> > -	const struct counter_device_ext *const ext = component->ext;
> > -
> > -	return ext->write(dev_get_drvdata(dev), ext->priv, buf, len);
> > -}
> > -
> > -static int counter_device_ext_register(
> > -	struct counter_device_attr_group *const group,
> > -	struct counter_device *const counter)
> > -{
> > -	size_t i;
> > -	struct counter_ext_unit *ext_comp;
> > -	struct counter_attr_parm parm;
> > -	int err;
> > -
> > -	/* Create an attribute for each extension */
> > -	for (i = 0 ; i < counter->num_ext; i++) {
> > -		/* Allocate extension attribute component */
> > -		ext_comp = kmalloc(sizeof(*ext_comp), GFP_KERNEL);
> > -		if (!ext_comp) {
> > -			err = -ENOMEM;
> > -			goto err_free_attr_list;
> > -		}
> > -
> > -		ext_comp->ext = counter->ext + i;
> > -
> > -		/* Allocate extension attribute */
> > -		parm.group = group;
> > -		parm.prefix = "";
> > -		parm.name = counter->ext[i].name;
> > -		parm.show = (counter->ext[i].read) ? counter_device_ext_show : NULL;
> > -		parm.store = (counter->ext[i].write) ? counter_device_ext_store : NULL;
> > -		parm.component = ext_comp;
> > -		err = counter_attribute_create(&parm);
> > -		if (err) {
> > -			kfree(ext_comp);
> > -			goto err_free_attr_list;
> > -		}
> > -	}
> > -
> > -	return 0;
> > -
> > -err_free_attr_list:
> > -	counter_device_attr_list_free(&group->attr_list);
> > -	return err;
> > -}
> > -
> > -static int counter_global_attr_register(
> > -	struct counter_device_attr_group *const group,
> > -	struct counter_device *const counter)
> > -{
> > -	int err;
> > -
> > -	/* Create name attribute */
> > -	err = counter_name_attribute_create(group, counter->name);
> > -	if (err)
> > -		return err;
> > -
> > -	/* Create num_counts attribute */
> > -	err = counter_size_attribute_create(group, counter->num_counts,
> > -					    "num_counts");
> > -	if (err)
> > -		goto err_free_attr_list;
> > -
> > -	/* Create num_signals attribute */
> > -	err = counter_size_attribute_create(group, counter->num_signals,
> > -					    "num_signals");
> > -	if (err)
> > -		goto err_free_attr_list;
> > -
> > -	/* Register Counter device extension attributes */
> > -	err = counter_device_ext_register(group, counter);
> > -	if (err)
> > -		goto err_free_attr_list;
> > -
> > -	return 0;
> > -
> > -err_free_attr_list:
> > -	counter_device_attr_list_free(&group->attr_list);
> > -	return err;
> > -}
> > -
> > -static void counter_device_groups_list_free(
> > -	struct counter_device_attr_group *const groups_list,
> > -	const size_t num_groups)
> > -{
> > -	struct counter_device_attr_group *group;
> > -	size_t i;
> > -
> > -	/* loop through all attribute groups (signals, counts, global, etc.) */
> > -	for (i = 0; i < num_groups; i++) {
> > -		group = groups_list + i;
> > -
> > -		/* free all attribute group and associated attributes memory */
> > -		kfree(group->attr_group.name);
> > -		kfree(group->attr_group.attrs);
> > -		counter_device_attr_list_free(&group->attr_list);
> > -	}
> > -
> > -	kfree(groups_list);
> > -}
> > -
> > -static int counter_device_groups_list_prepare(
> > -	struct counter_device *const counter)
> > -{
> > -	const size_t total_num_groups =
> > -		counter->num_signals + counter->num_counts + 1;
> > -	struct counter_device_attr_group *groups_list;
> > -	size_t i;
> > -	int err;
> > -	size_t num_groups = 0;
> > -
> > -	/* Allocate space for attribute groups (signals, counts, and ext) */
> > -	groups_list = kcalloc(total_num_groups, sizeof(*groups_list),
> > -			      GFP_KERNEL);
> > -	if (!groups_list)
> > -		return -ENOMEM;
> > -
> > -	/* Initialize attribute lists */
> > -	for (i = 0; i < total_num_groups; i++)
> > -		INIT_LIST_HEAD(&groups_list[i].attr_list);
> > -
> > -	/* Register Signals */
> > -	err = counter_signals_register(groups_list, counter);
> > -	if (err)
> > -		goto err_free_groups_list;
> > -	num_groups += counter->num_signals;
> > -
> > -	/* Register Counts and respective Synapses */
> > -	err = counter_counts_register(groups_list + num_groups, counter);
> > -	if (err)
> > -		goto err_free_groups_list;
> > -	num_groups += counter->num_counts;
> > -
> > -	/* Register Counter global attributes */
> > -	err = counter_global_attr_register(groups_list + num_groups, counter);
> > -	if (err)
> > -		goto err_free_groups_list;
> > -	num_groups++;
> > -
> > -	/* Store groups_list in device_state */
> > -	counter->device_state->groups_list = groups_list;
> > -	counter->device_state->num_groups = num_groups;
> > -
> > -	return 0;
> > -
> > -err_free_groups_list:
> > -	counter_device_groups_list_free(groups_list, num_groups);
> > -	return err;
> > -}
> > -
> > -static int counter_device_groups_prepare(
> > -	struct counter_device_state *const device_state)
> > -{
> > -	size_t i, j;
> > -	struct counter_device_attr_group *group;
> > -	int err;
> > -	struct counter_device_attr *p;
> > -
> > -	/* Allocate attribute groups for association with device */
> > -	device_state->groups = kcalloc(device_state->num_groups + 1,
> > -				       sizeof(*device_state->groups),
> > -				       GFP_KERNEL);
> > -	if (!device_state->groups)
> > -		return -ENOMEM;
> > -
> > -	/* Prepare each group of attributes for association */
> > -	for (i = 0; i < device_state->num_groups; i++) {
> > -		group = device_state->groups_list + i;
> > -
> > -		/* Allocate space for attribute pointers in attribute group */
> > -		group->attr_group.attrs = kcalloc(group->num_attr + 1,
> > -			sizeof(*group->attr_group.attrs), GFP_KERNEL);
> > -		if (!group->attr_group.attrs) {
> > -			err = -ENOMEM;
> > -			goto err_free_groups;
> > -		}
> > -
> > -		/* Add attribute pointers to attribute group */
> > -		j = 0;
> > -		list_for_each_entry(p, &group->attr_list, l)
> > -			group->attr_group.attrs[j++] = &p->dev_attr.attr;
> > -
> > -		/* Group attributes in attribute group */
> > -		device_state->groups[i] = &group->attr_group;
> > -	}
> > -	/* Associate attributes with device */
> > -	device_state->dev.groups = device_state->groups;
> > -
> > -	return 0;
> > -
> > -err_free_groups:
> > -	do {
> > -		group = device_state->groups_list + i;
> > -		kfree(group->attr_group.attrs);
> > -		group->attr_group.attrs = NULL;
> > -	} while (i--);
> > -	kfree(device_state->groups);
> > -	return err;
> > -}
> > -
> > -/* Provides a unique ID for each counter device */
> > -static DEFINE_IDA(counter_ida);
> > -
> > -static void counter_device_release(struct device *dev)
> > -{
> > -	struct counter_device *const counter = dev_get_drvdata(dev);
> > -	struct counter_device_state *const device_state = counter->device_state;
> > -
> > -	kfree(device_state->groups);
> > -	counter_device_groups_list_free(device_state->groups_list,
> > -					device_state->num_groups);
> > -	ida_simple_remove(&counter_ida, device_state->id);
> > -	kfree(device_state);
> > -}
> > -
> > -static struct device_type counter_device_type = {
> > -	.name = "counter_device",
> > -	.release = counter_device_release
> > -};
> > -
> > -static struct bus_type counter_bus_type = {
> > -	.name = "counter"
> > -};
> > -
> > -/**
> > - * counter_register - register Counter to the system
> > - * @counter:	pointer to Counter to register
> > - *
> > - * This function registers a Counter to the system. A sysfs "counter" directory
> > - * will be created and populated with sysfs attributes correlating with the
> > - * Counter Signals, Synapses, and Counts respectively.
> > - */
> > -int counter_register(struct counter_device *const counter)
> > -{
> > -	struct counter_device_state *device_state;
> > -	int err;
> > -
> > -	/* Allocate internal state container for Counter device */
> > -	device_state = kzalloc(sizeof(*device_state), GFP_KERNEL);
> > -	if (!device_state)
> > -		return -ENOMEM;
> > -	counter->device_state = device_state;
> > -
> > -	/* Acquire unique ID */
> > -	device_state->id = ida_simple_get(&counter_ida, 0, 0, GFP_KERNEL);
> > -	if (device_state->id < 0) {
> > -		err = device_state->id;
> > -		goto err_free_device_state;
> > -	}
> > -
> > -	/* Configure device structure for Counter */
> > -	device_state->dev.type = &counter_device_type;
> > -	device_state->dev.bus = &counter_bus_type;
> > -	if (counter->parent) {
> > -		device_state->dev.parent = counter->parent;
> > -		device_state->dev.of_node = counter->parent->of_node;
> > -	}
> > -	dev_set_name(&device_state->dev, "counter%d", device_state->id);
> > -	device_initialize(&device_state->dev);
> > -	dev_set_drvdata(&device_state->dev, counter);
> > -
> > -	/* Prepare device attributes */
> > -	err = counter_device_groups_list_prepare(counter);
> > -	if (err)
> > -		goto err_free_id;
> > -
> > -	/* Organize device attributes to groups and match to device */
> > -	err = counter_device_groups_prepare(device_state);
> > -	if (err)
> > -		goto err_free_groups_list;
> > -
> > -	/* Add device to system */
> > -	err = device_add(&device_state->dev);
> > -	if (err)
> > -		goto err_free_groups;
> > -
> > -	return 0;
> > -
> > -err_free_groups:
> > -	kfree(device_state->groups);
> > -err_free_groups_list:
> > -	counter_device_groups_list_free(device_state->groups_list,
> > -					device_state->num_groups);
> > -err_free_id:
> > -	ida_simple_remove(&counter_ida, device_state->id);
> > -err_free_device_state:
> > -	kfree(device_state);
> > -	return err;
> > -}
> > -EXPORT_SYMBOL_GPL(counter_register);
> > -
> > -/**
> > - * counter_unregister - unregister Counter from the system
> > - * @counter:	pointer to Counter to unregister
> > - *
> > - * The Counter is unregistered from the system; all allocated memory is freed.
> > - */
> > -void counter_unregister(struct counter_device *const counter)
> > -{
> > -	if (counter)
> > -		device_del(&counter->device_state->dev);
> > -}
> > -EXPORT_SYMBOL_GPL(counter_unregister);
> > -
> > -static void devm_counter_unreg(struct device *dev, void *res)
> > -{
> > -	counter_unregister(*(struct counter_device **)res);
> > -}
> > -
> > -/**
> > - * devm_counter_register - Resource-managed counter_register
> > - * @dev:	device to allocate counter_device for
> > - * @counter:	pointer to Counter to register
> > - *
> > - * Managed counter_register. The Counter registered with this function is
> > - * automatically unregistered on driver detach. This function calls
> > - * counter_register internally. Refer to that function for more information.
> > - *
> > - * If an Counter registered with this function needs to be unregistered
> > - * separately, devm_counter_unregister must be used.
> > - *
> > - * RETURNS:
> > - * 0 on success, negative error number on failure.
> > - */
> > -int devm_counter_register(struct device *dev,
> > -			  struct counter_device *const counter)
> > -{
> > -	struct counter_device **ptr;
> > -	int ret;
> > -
> > -	ptr = devres_alloc(devm_counter_unreg, sizeof(*ptr), GFP_KERNEL);
> > -	if (!ptr)
> > -		return -ENOMEM;
> > -
> > -	ret = counter_register(counter);
> > -	if (!ret) {
> > -		*ptr = counter;
> > -		devres_add(dev, ptr);
> > -	} else {
> > -		devres_free(ptr);
> > -	}
> > -
> > -	return ret;
> > -}
> > -EXPORT_SYMBOL_GPL(devm_counter_register);
> > -
> > -static int devm_counter_match(struct device *dev, void *res, void *data)
> > -{
> > -	struct counter_device **r = res;
> > -
> > -	if (!r || !*r) {
> > -		WARN_ON(!r || !*r);
> > -		return 0;
> > -	}
> > -
> > -	return *r == data;
> > -}
> > -
> > -/**
> > - * devm_counter_unregister - Resource-managed counter_unregister
> > - * @dev:	device this counter_device belongs to
> > - * @counter:	pointer to Counter associated with the device
> > - *
> > - * Unregister Counter registered with devm_counter_register.
> > - */
> > -void devm_counter_unregister(struct device *dev,
> > -			     struct counter_device *const counter)
> > -{
> > -	int rc;
> > -
> > -	rc = devres_release(dev, devm_counter_unreg, devm_counter_match,
> > -			    counter);
> > -	WARN_ON(rc);
> > -}
> > -EXPORT_SYMBOL_GPL(devm_counter_unregister);
> > -
> > -static int __init counter_init(void)
> > -{
> > -	return bus_register(&counter_bus_type);
> > -}
> > -
> > -static void __exit counter_exit(void)
> > -{
> > -	bus_unregister(&counter_bus_type);
> > -}
> > -
> > -subsys_initcall(counter_init);
> > -module_exit(counter_exit);
> > -
> > -MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
> > -MODULE_DESCRIPTION("Generic Counter interface");
> > -MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/counter/ftm-quaddec.c b/drivers/counter/ftm-quaddec.c
> > index 53c15f84909b..5ef0478709cd 100644
> > --- a/drivers/counter/ftm-quaddec.c
> > +++ b/drivers/counter/ftm-quaddec.c
> > @@ -14,6 +14,7 @@
> >  #include <linux/mutex.h>
> >  #include <linux/counter.h>
> >  #include <linux/bitfield.h>
> > +#include <linux/types.h>
> >  
> >  #define FTM_FIELD_UPDATE(ftm, offset, mask, val)			\
> >  	({								\
> > @@ -115,8 +116,7 @@ static void ftm_quaddec_disable(void *ftm)
> >  }
> >  
> >  static int ftm_quaddec_get_prescaler(struct counter_device *counter,
> > -				     struct counter_count *count,
> > -				     size_t *cnt_mode)
> > +				     struct counter_count *count, u32 *cnt_mode)
> >  {
> >  	struct ftm_quaddec *ftm = counter->priv;
> >  	uint32_t scflags;
> > @@ -129,8 +129,7 @@ static int ftm_quaddec_get_prescaler(struct counter_device *counter,
> >  }
> >  
> >  static int ftm_quaddec_set_prescaler(struct counter_device *counter,
> > -				     struct counter_count *count,
> > -				     size_t cnt_mode)
> > +				     struct counter_count *count, u32 cnt_mode)
> >  {
> >  	struct ftm_quaddec *ftm = counter->priv;
> >  
> > @@ -151,33 +150,17 @@ static const char * const ftm_quaddec_prescaler[] = {
> >  	"1", "2", "4", "8", "16", "32", "64", "128"
> >  };
> >  
> > -static struct counter_count_enum_ext ftm_quaddec_prescaler_enum = {
> > -	.items = ftm_quaddec_prescaler,
> > -	.num_items = ARRAY_SIZE(ftm_quaddec_prescaler),
> > -	.get = ftm_quaddec_get_prescaler,
> > -	.set = ftm_quaddec_set_prescaler
> > -};
> > -
> > -enum ftm_quaddec_synapse_action {
> > -	FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES,
> > -};
> > -
> >  static const enum counter_synapse_action ftm_quaddec_synapse_actions[] = {
> > -	[FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES] =
> >  	COUNTER_SYNAPSE_ACTION_BOTH_EDGES
> >  };
> >  
> > -enum ftm_quaddec_count_function {
> > -	FTM_QUADDEC_COUNT_ENCODER_MODE_1,
> > -};
> > -
> >  static const enum counter_function ftm_quaddec_count_functions[] = {
> > -	[FTM_QUADDEC_COUNT_ENCODER_MODE_1] = COUNTER_FUNCTION_QUADRATURE_X4
> > +	COUNTER_FUNCTION_QUADRATURE_X4
> >  };
> >  
> >  static int ftm_quaddec_count_read(struct counter_device *counter,
> >  				  struct counter_count *count,
> > -				  unsigned long *val)
> > +				  u64 *val)
> >  {
> >  	struct ftm_quaddec *const ftm = counter->priv;
> >  	uint32_t cntval;
> > @@ -191,7 +174,7 @@ static int ftm_quaddec_count_read(struct counter_device *counter,
> >  
> >  static int ftm_quaddec_count_write(struct counter_device *counter,
> >  				   struct counter_count *count,
> > -				   const unsigned long val)
> > +				   const u64 val)
> >  {
> >  	struct ftm_quaddec *const ftm = counter->priv;
> >  
> > @@ -205,21 +188,21 @@ static int ftm_quaddec_count_write(struct counter_device *counter,
> >  	return 0;
> >  }
> >  
> > -static int ftm_quaddec_count_function_get(struct counter_device *counter,
> > -					  struct counter_count *count,
> > -					  size_t *function)
> > +static int ftm_quaddec_count_function_read(struct counter_device *counter,
> > +					   struct counter_count *count,
> > +					   enum counter_function *function)
> >  {
> > -	*function = FTM_QUADDEC_COUNT_ENCODER_MODE_1;
> > +	*function = COUNTER_FUNCTION_QUADRATURE_X4;
> >  
> >  	return 0;
> >  }
> >  
> > -static int ftm_quaddec_action_get(struct counter_device *counter,
> > -				  struct counter_count *count,
> > -				  struct counter_synapse *synapse,
> > -				  size_t *action)
> > +static int ftm_quaddec_action_read(struct counter_device *counter,
> > +				   struct counter_count *count,
> > +				   struct counter_synapse *synapse,
> > +				   enum counter_synapse_action *action)
> >  {
> > -	*action = FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES;
> > +	*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
> >  
> >  	return 0;
> >  }
> > @@ -227,8 +210,8 @@ static int ftm_quaddec_action_get(struct counter_device *counter,
> >  static const struct counter_ops ftm_quaddec_cnt_ops = {
> >  	.count_read = ftm_quaddec_count_read,
> >  	.count_write = ftm_quaddec_count_write,
> > -	.function_get = ftm_quaddec_count_function_get,
> > -	.action_get = ftm_quaddec_action_get,
> > +	.function_read = ftm_quaddec_count_function_read,
> > +	.action_read = ftm_quaddec_action_read,
> >  };
> >  
> >  static struct counter_signal ftm_quaddec_signals[] = {
> > @@ -255,9 +238,12 @@ static struct counter_synapse ftm_quaddec_count_synapses[] = {
> >  	}
> >  };
> >  
> > -static const struct counter_count_ext ftm_quaddec_count_ext[] = {
> > -	COUNTER_COUNT_ENUM("prescaler", &ftm_quaddec_prescaler_enum),
> > -	COUNTER_COUNT_ENUM_AVAILABLE("prescaler", &ftm_quaddec_prescaler_enum),
> > +static DEFINE_COUNTER_ENUM(ftm_quaddec_prescaler_enum, ftm_quaddec_prescaler);
> > +
> > +static struct counter_comp ftm_quaddec_count_ext[] = {
> > +	COUNTER_COMP_COUNT_ENUM("prescaler", ftm_quaddec_get_prescaler,
> > +				ftm_quaddec_set_prescaler,
> > +				ftm_quaddec_prescaler_enum),
> >  };
> >  
> >  static struct counter_count ftm_quaddec_counts = {
> > diff --git a/drivers/counter/intel-qep.c b/drivers/counter/intel-qep.c
> > index 8a6847d5fb2b..696f68bc93bc 100644
> > --- a/drivers/counter/intel-qep.c
> > +++ b/drivers/counter/intel-qep.c
> > @@ -62,13 +62,6 @@
> >  
> >  #define INTEL_QEP_CLK_PERIOD_NS		10
> >  
> > -#define INTEL_QEP_COUNTER_EXT_RW(_name)				\
> > -{								\
> > -	.name = #_name,						\
> > -	.read = _name##_read,					\
> > -	.write = _name##_write,					\
> > -}
> > -
> >  struct intel_qep {
> >  	struct counter_device counter;
> >  	struct mutex lock;
> > @@ -114,8 +107,7 @@ static void intel_qep_init(struct intel_qep *qep)
> >  }
> >  
> >  static int intel_qep_count_read(struct counter_device *counter,
> > -				struct counter_count *count,
> > -				unsigned long *val)
> > +				struct counter_count *count, u64 *val)
> >  {
> >  	struct intel_qep *const qep = counter->priv;
> >  
> > @@ -130,11 +122,11 @@ static const enum counter_function intel_qep_count_functions[] = {
> >  	COUNTER_FUNCTION_QUADRATURE_X4,
> >  };
> >  
> > -static int intel_qep_function_get(struct counter_device *counter,
> > -				  struct counter_count *count,
> > -				  size_t *function)
> > +static int intel_qep_function_read(struct counter_device *counter,
> > +				   struct counter_count *count,
> > +				   enum counter_function *function)
> >  {
> > -	*function = 0;
> > +	*function = COUNTER_FUNCTION_QUADRATURE_X4;
> >  
> >  	return 0;
> >  }
> > @@ -143,19 +135,19 @@ static const enum counter_synapse_action intel_qep_synapse_actions[] = {
> >  	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
> >  };
> >  
> > -static int intel_qep_action_get(struct counter_device *counter,
> > -				struct counter_count *count,
> > -				struct counter_synapse *synapse,
> > -				size_t *action)
> > +static int intel_qep_action_read(struct counter_device *counter,
> > +				 struct counter_count *count,
> > +				 struct counter_synapse *synapse,
> > +				 enum counter_synapse_action *action)
> >  {
> > -	*action = 0;
> > +	*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
> >  	return 0;
> >  }
> >  
> >  static const struct counter_ops intel_qep_counter_ops = {
> >  	.count_read = intel_qep_count_read,
> > -	.function_get = intel_qep_function_get,
> > -	.action_get = intel_qep_action_get,
> > +	.function_read = intel_qep_function_read,
> > +	.action_read = intel_qep_action_read,
> >  };
> >  
> >  #define INTEL_QEP_SIGNAL(_id, _name) {				\
> > @@ -181,31 +173,27 @@ static struct counter_synapse intel_qep_count_synapses[] = {
> >  	INTEL_QEP_SYNAPSE(2),
> >  };
> >  
> > -static ssize_t ceiling_read(struct counter_device *counter,
> > -			    struct counter_count *count,
> > -			    void *priv, char *buf)
> > +static int intel_qep_ceiling_read(struct counter_device *counter,
> > +				  struct counter_count *count, u64 *ceiling)
> >  {
> >  	struct intel_qep *qep = counter->priv;
> > -	u32 reg;
> >  
> >  	pm_runtime_get_sync(qep->dev);
> > -	reg = intel_qep_readl(qep, INTEL_QEPMAX);
> > +	*ceiling = intel_qep_readl(qep, INTEL_QEPMAX);
> >  	pm_runtime_put(qep->dev);
> >  
> > -	return sysfs_emit(buf, "%u\n", reg);
> > +	return 0;
> >  }
> >  
> > -static ssize_t ceiling_write(struct counter_device *counter,
> > -			     struct counter_count *count,
> > -			     void *priv, const char *buf, size_t len)
> > +static int intel_qep_ceiling_write(struct counter_device *counter,
> > +				   struct counter_count *count, u64 max)
> >  {
> >  	struct intel_qep *qep = counter->priv;
> > -	u32 max;
> > -	int ret;
> > +	int ret = 0;
> >  
> > -	ret = kstrtou32(buf, 0, &max);
> > -	if (ret < 0)
> > -		return ret;
> > +	/* Intel QEP ceiling configuration only supports 32-bit values */
> > +	if (max != (u32)max)
> > +		return -ERANGE;
> >  
> >  	mutex_lock(&qep->lock);
> >  	if (qep->enabled) {
> > @@ -216,34 +204,28 @@ static ssize_t ceiling_write(struct counter_device *counter,
> >  	pm_runtime_get_sync(qep->dev);
> >  	intel_qep_writel(qep, INTEL_QEPMAX, max);
> >  	pm_runtime_put(qep->dev);
> > -	ret = len;
> >  
> >  out:
> >  	mutex_unlock(&qep->lock);
> >  	return ret;
> >  }
> >  
> > -static ssize_t enable_read(struct counter_device *counter,
> > -			   struct counter_count *count,
> > -			   void *priv, char *buf)
> > +static int intel_qep_enable_read(struct counter_device *counter,
> > +				 struct counter_count *count, u8 *enable)
> >  {
> >  	struct intel_qep *qep = counter->priv;
> >  
> > -	return sysfs_emit(buf, "%u\n", qep->enabled);
> > +	*enable = qep->enabled;
> > +
> > +	return 0;
> >  }
> >  
> > -static ssize_t enable_write(struct counter_device *counter,
> > -			    struct counter_count *count,
> > -			    void *priv, const char *buf, size_t len)
> > +static int intel_qep_enable_write(struct counter_device *counter,
> > +				  struct counter_count *count, u8 val)
> >  {
> >  	struct intel_qep *qep = counter->priv;
> >  	u32 reg;
> > -	bool val, changed;
> > -	int ret;
> > -
> > -	ret = kstrtobool(buf, &val);
> > -	if (ret)
> > -		return ret;
> > +	bool changed;
> >  
> >  	mutex_lock(&qep->lock);
> >  	changed = val ^ qep->enabled;
> > @@ -267,12 +249,12 @@ static ssize_t enable_write(struct counter_device *counter,
> >  
> >  out:
> >  	mutex_unlock(&qep->lock);
> > -	return len;
> > +	return 0;
> >  }
> >  
> > -static ssize_t spike_filter_ns_read(struct counter_device *counter,
> > -				    struct counter_count *count,
> > -				    void *priv, char *buf)
> > +static int intel_qep_spike_filter_ns_read(struct counter_device *counter,
> > +					  struct counter_count *count,
> > +					  u64 *length)
> >  {
> >  	struct intel_qep *qep = counter->priv;
> >  	u32 reg;
> > @@ -281,26 +263,24 @@ static ssize_t spike_filter_ns_read(struct counter_device *counter,
> >  	reg = intel_qep_readl(qep, INTEL_QEPCON);
> >  	if (!(reg & INTEL_QEPCON_FLT_EN)) {
> >  		pm_runtime_put(qep->dev);
> > -		return sysfs_emit(buf, "0\n");
> > +		return 0;
> >  	}
> >  	reg = INTEL_QEPFLT_MAX_COUNT(intel_qep_readl(qep, INTEL_QEPFLT));
> >  	pm_runtime_put(qep->dev);
> >  
> > -	return sysfs_emit(buf, "%u\n", (reg + 2) * INTEL_QEP_CLK_PERIOD_NS);
> > +	*length = (reg + 2) * INTEL_QEP_CLK_PERIOD_NS;
> > +
> > +	return 0;
> >  }
> >  
> > -static ssize_t spike_filter_ns_write(struct counter_device *counter,
> > -				     struct counter_count *count,
> > -				     void *priv, const char *buf, size_t len)
> > +static int intel_qep_spike_filter_ns_write(struct counter_device *counter,
> > +					   struct counter_count *count,
> > +					   u64 length)
> >  {
> >  	struct intel_qep *qep = counter->priv;
> > -	u32 reg, length;
> > +	u32 reg;
> >  	bool enable;
> > -	int ret;
> > -
> > -	ret = kstrtou32(buf, 0, &length);
> > -	if (ret < 0)
> > -		return ret;
> > +	int ret = 0;
> >  
> >  	/*
> >  	 * Spike filter length is (MAX_COUNT + 2) clock periods.
> > @@ -336,16 +316,15 @@ static ssize_t spike_filter_ns_write(struct counter_device *counter,
> >  	intel_qep_writel(qep, INTEL_QEPFLT, length);
> >  	intel_qep_writel(qep, INTEL_QEPCON, reg);
> >  	pm_runtime_put(qep->dev);
> > -	ret = len;
> >  
> >  out:
> >  	mutex_unlock(&qep->lock);
> >  	return ret;
> >  }
> >  
> > -static ssize_t preset_enable_read(struct counter_device *counter,
> > -				  struct counter_count *count,
> > -				  void *priv, char *buf)
> > +static int intel_qep_preset_enable_read(struct counter_device *counter,
> > +					struct counter_count *count,
> > +					u8 *preset_enable)
> >  {
> >  	struct intel_qep *qep = counter->priv;
> >  	u32 reg;
> > @@ -353,21 +332,18 @@ static ssize_t preset_enable_read(struct counter_device *counter,
> >  	pm_runtime_get_sync(qep->dev);
> >  	reg = intel_qep_readl(qep, INTEL_QEPCON);
> >  	pm_runtime_put(qep->dev);
> > -	return sysfs_emit(buf, "%u\n", !(reg & INTEL_QEPCON_COUNT_RST_MODE));
> > +
> > +	*preset_enable = !(reg & INTEL_QEPCON_COUNT_RST_MODE);
> > +
> > +	return 0;
> >  }
> >  
> > -static ssize_t preset_enable_write(struct counter_device *counter,
> > -				   struct counter_count *count,
> > -				   void *priv, const char *buf, size_t len)
> > +static int intel_qep_preset_enable_write(struct counter_device *counter,
> > +					 struct counter_count *count, u8 val)
> >  {
> >  	struct intel_qep *qep = counter->priv;
> >  	u32 reg;
> > -	bool val;
> > -	int ret;
> > -
> > -	ret = kstrtobool(buf, &val);
> > -	if (ret)
> > -		return ret;
> > +	int ret = 0;
> >  
> >  	mutex_lock(&qep->lock);
> >  	if (qep->enabled) {
> > @@ -384,7 +360,6 @@ static ssize_t preset_enable_write(struct counter_device *counter,
> >  
> >  	intel_qep_writel(qep, INTEL_QEPCON, reg);
> >  	pm_runtime_put(qep->dev);
> > -	ret = len;
> >  
> >  out:
> >  	mutex_unlock(&qep->lock);
> > @@ -392,11 +367,14 @@ static ssize_t preset_enable_write(struct counter_device *counter,
> >  	return ret;
> >  }
> >  
> > -static const struct counter_count_ext intel_qep_count_ext[] = {
> > -	INTEL_QEP_COUNTER_EXT_RW(ceiling),
> > -	INTEL_QEP_COUNTER_EXT_RW(enable),
> > -	INTEL_QEP_COUNTER_EXT_RW(spike_filter_ns),
> > -	INTEL_QEP_COUNTER_EXT_RW(preset_enable)
> > +static struct counter_comp intel_qep_count_ext[] = {
> > +	COUNTER_COMP_ENABLE(intel_qep_enable_read, intel_qep_enable_write),
> > +	COUNTER_COMP_CEILING(intel_qep_ceiling_read, intel_qep_ceiling_write),
> > +	COUNTER_COMP_PRESET_ENABLE(intel_qep_preset_enable_read,
> > +				   intel_qep_preset_enable_write),
> > +	COUNTER_COMP_COUNT_U64("spike_filter_ns",
> > +			       intel_qep_spike_filter_ns_read,
> > +			       intel_qep_spike_filter_ns_write),
> >  };
> >  
> >  static struct counter_count intel_qep_counter_count[] = {
> > diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c
> > index 1de4243db488..8514a87fcbee 100644
> > --- a/drivers/counter/interrupt-cnt.c
> > +++ b/drivers/counter/interrupt-cnt.c
> > @@ -10,6 +10,7 @@
> >  #include <linux/mod_devicetable.h>
> >  #include <linux/module.h>
> >  #include <linux/platform_device.h>
> > +#include <linux/types.h>
> >  
> >  #define INTERRUPT_CNT_NAME "interrupt-cnt"
> >  
> > @@ -33,30 +34,23 @@ static irqreturn_t interrupt_cnt_isr(int irq, void *dev_id)
> >  	return IRQ_HANDLED;
> >  }
> >  
> > -static ssize_t interrupt_cnt_enable_read(struct counter_device *counter,
> > -					 struct counter_count *count,
> > -					 void *private, char *buf)
> > +static int interrupt_cnt_enable_read(struct counter_device *counter,
> > +				     struct counter_count *count, u8 *enable)
> >  {
> >  	struct interrupt_cnt_priv *priv = counter->priv;
> >  
> > -	return sysfs_emit(buf, "%d\n", priv->enabled);
> > +	*enable = priv->enabled;
> > +
> > +	return 0;
> >  }
> >  
> > -static ssize_t interrupt_cnt_enable_write(struct counter_device *counter,
> > -					  struct counter_count *count,
> > -					  void *private, const char *buf,
> > -					  size_t len)
> > +static int interrupt_cnt_enable_write(struct counter_device *counter,
> > +				      struct counter_count *count, u8 enable)
> >  {
> >  	struct interrupt_cnt_priv *priv = counter->priv;
> > -	bool enable;
> > -	ssize_t ret;
> > -
> > -	ret = kstrtobool(buf, &enable);
> > -	if (ret)
> > -		return ret;
> >  
> >  	if (priv->enabled == enable)
> > -		return len;
> > +		return 0;
> >  
> >  	if (enable) {
> >  		priv->enabled = true;
> > @@ -66,33 +60,30 @@ static ssize_t interrupt_cnt_enable_write(struct counter_device *counter,
> >  		priv->enabled = false;
> >  	}
> >  
> > -	return len;
> > +	return 0;
> >  }
> >  
> > -static const struct counter_count_ext interrupt_cnt_ext[] = {
> > -	{
> > -		.name = "enable",
> > -		.read = interrupt_cnt_enable_read,
> > -		.write = interrupt_cnt_enable_write,
> > -	},
> > +static struct counter_comp interrupt_cnt_ext[] = {
> > +	COUNTER_COMP_ENABLE(interrupt_cnt_enable_read,
> > +			    interrupt_cnt_enable_write),
> >  };
> >  
> >  static const enum counter_synapse_action interrupt_cnt_synapse_actions[] = {
> >  	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> >  };
> >  
> > -static int interrupt_cnt_action_get(struct counter_device *counter,
> > -				    struct counter_count *count,
> > -				    struct counter_synapse *synapse,
> > -				    size_t *action)
> > +static int interrupt_cnt_action_read(struct counter_device *counter,
> > +				     struct counter_count *count,
> > +				     struct counter_synapse *synapse,
> > +				     enum counter_synapse_action *action)
> >  {
> > -	*action = 0;
> > +	*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
> >  
> >  	return 0;
> >  }
> >  
> >  static int interrupt_cnt_read(struct counter_device *counter,
> > -			      struct counter_count *count, unsigned long *val)
> > +			      struct counter_count *count, u64 *val)
> >  {
> >  	struct interrupt_cnt_priv *priv = counter->priv;
> >  
> > @@ -102,8 +93,7 @@ static int interrupt_cnt_read(struct counter_device *counter,
> >  }
> >  
> >  static int interrupt_cnt_write(struct counter_device *counter,
> > -			       struct counter_count *count,
> > -			       const unsigned long val)
> > +			       struct counter_count *count, const u64 val)
> >  {
> >  	struct interrupt_cnt_priv *priv = counter->priv;
> >  
> > @@ -119,11 +109,11 @@ static const enum counter_function interrupt_cnt_functions[] = {
> >  	COUNTER_FUNCTION_INCREASE,
> >  };
> >  
> > -static int interrupt_cnt_function_get(struct counter_device *counter,
> > -				      struct counter_count *count,
> > -				      size_t *function)
> > +static int interrupt_cnt_function_read(struct counter_device *counter,
> > +				       struct counter_count *count,
> > +				       enum counter_function *function)
> >  {
> > -	*function = 0;
> > +	*function = COUNTER_FUNCTION_INCREASE;
> >  
> >  	return 0;
> >  }
> > @@ -148,10 +138,10 @@ static int interrupt_cnt_signal_read(struct counter_device *counter,
> >  }
> >  
> >  static const struct counter_ops interrupt_cnt_ops = {
> > -	.action_get = interrupt_cnt_action_get,
> > +	.action_read = interrupt_cnt_action_read,
> >  	.count_read = interrupt_cnt_read,
> >  	.count_write = interrupt_cnt_write,
> > -	.function_get = interrupt_cnt_function_get,
> > +	.function_read = interrupt_cnt_function_read,
> >  	.signal_read  = interrupt_cnt_signal_read,
> >  };
> >  
> > diff --git a/drivers/counter/microchip-tcb-capture.c b/drivers/counter/microchip-tcb-capture.c
> > index 1aa70b9c4833..79e0c84a3b81 100644
> > --- a/drivers/counter/microchip-tcb-capture.c
> > +++ b/drivers/counter/microchip-tcb-capture.c
> > @@ -32,28 +32,16 @@ struct mchp_tc_data {
> >  	bool trig_inverted;
> >  };
> >  
> > -enum mchp_tc_count_function {
> > -	MCHP_TC_FUNCTION_INCREASE,
> > -	MCHP_TC_FUNCTION_QUADRATURE,
> > -};
> > -
> >  static const enum counter_function mchp_tc_count_functions[] = {
> > -	[MCHP_TC_FUNCTION_INCREASE] = COUNTER_FUNCTION_INCREASE,
> > -	[MCHP_TC_FUNCTION_QUADRATURE] = COUNTER_FUNCTION_QUADRATURE_X4,
> > -};
> > -
> > -enum mchp_tc_synapse_action {
> > -	MCHP_TC_SYNAPSE_ACTION_NONE = 0,
> > -	MCHP_TC_SYNAPSE_ACTION_RISING_EDGE,
> > -	MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE,
> > -	MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE
> > +	COUNTER_FUNCTION_INCREASE,
> > +	COUNTER_FUNCTION_QUADRATURE_X4,
> >  };
> >  
> >  static const enum counter_synapse_action mchp_tc_synapse_actions[] = {
> > -	[MCHP_TC_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
> > -	[MCHP_TC_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> > -	[MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
> > -	[MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
> > +	COUNTER_SYNAPSE_ACTION_NONE,
> > +	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> > +	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
> > +	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
> >  };
> >  
> >  static struct counter_signal mchp_tc_count_signals[] = {
> > @@ -80,23 +68,23 @@ static struct counter_synapse mchp_tc_count_synapses[] = {
> >  	}
> >  };
> >  
> > -static int mchp_tc_count_function_get(struct counter_device *counter,
> > -				      struct counter_count *count,
> > -				      size_t *function)
> > +static int mchp_tc_count_function_read(struct counter_device *counter,
> > +				       struct counter_count *count,
> > +				       enum counter_function *function)
> >  {
> >  	struct mchp_tc_data *const priv = counter->priv;
> >  
> >  	if (priv->qdec_mode)
> > -		*function = MCHP_TC_FUNCTION_QUADRATURE;
> > +		*function = COUNTER_FUNCTION_QUADRATURE_X4;
> >  	else
> > -		*function = MCHP_TC_FUNCTION_INCREASE;
> > +		*function = COUNTER_FUNCTION_INCREASE;
> >  
> >  	return 0;
> >  }
> >  
> > -static int mchp_tc_count_function_set(struct counter_device *counter,
> > -				      struct counter_count *count,
> > -				      size_t function)
> > +static int mchp_tc_count_function_write(struct counter_device *counter,
> > +					struct counter_count *count,
> > +					enum counter_function function)
> >  {
> >  	struct mchp_tc_data *const priv = counter->priv;
> >  	u32 bmr, cmr;
> > @@ -108,7 +96,7 @@ static int mchp_tc_count_function_set(struct counter_device *counter,
> >  	cmr &= ~ATMEL_TC_WAVE;
> >  
> >  	switch (function) {
> > -	case MCHP_TC_FUNCTION_INCREASE:
> > +	case COUNTER_FUNCTION_INCREASE:
> >  		priv->qdec_mode = 0;
> >  		/* Set highest rate based on whether soc has gclk or not */
> >  		bmr &= ~(ATMEL_TC_QDEN | ATMEL_TC_POSEN);
> > @@ -120,7 +108,7 @@ static int mchp_tc_count_function_set(struct counter_device *counter,
> >  		cmr |=  ATMEL_TC_CMR_MASK;
> >  		cmr &= ~(ATMEL_TC_ABETRG | ATMEL_TC_XC0);
> >  		break;
> > -	case MCHP_TC_FUNCTION_QUADRATURE:
> > +	case COUNTER_FUNCTION_QUADRATURE_X4:
> >  		if (!priv->tc_cfg->has_qdec)
> >  			return -EINVAL;
> >  		/* In QDEC mode settings both channels 0 and 1 are required */
> > @@ -176,10 +164,10 @@ static int mchp_tc_count_signal_read(struct counter_device *counter,
> >  	return 0;
> >  }
> >  
> > -static int mchp_tc_count_action_get(struct counter_device *counter,
> > -				    struct counter_count *count,
> > -				    struct counter_synapse *synapse,
> > -				    size_t *action)
> > +static int mchp_tc_count_action_read(struct counter_device *counter,
> > +				     struct counter_count *count,
> > +				     struct counter_synapse *synapse,
> > +				     enum counter_synapse_action *action)
> >  {
> >  	struct mchp_tc_data *const priv = counter->priv;
> >  	u32 cmr;
> > @@ -188,26 +176,26 @@ static int mchp_tc_count_action_get(struct counter_device *counter,
> >  
> >  	switch (cmr & ATMEL_TC_ETRGEDG) {
> >  	default:
> > -		*action = MCHP_TC_SYNAPSE_ACTION_NONE;
> > +		*action = COUNTER_SYNAPSE_ACTION_NONE;
> >  		break;
> >  	case ATMEL_TC_ETRGEDG_RISING:
> > -		*action = MCHP_TC_SYNAPSE_ACTION_RISING_EDGE;
> > +		*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
> >  		break;
> >  	case ATMEL_TC_ETRGEDG_FALLING:
> > -		*action = MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE;
> > +		*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
> >  		break;
> >  	case ATMEL_TC_ETRGEDG_BOTH:
> > -		*action = MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE;
> > +		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
> >  		break;
> >  	}
> >  
> >  	return 0;
> >  }
> >  
> > -static int mchp_tc_count_action_set(struct counter_device *counter,
> > -				    struct counter_count *count,
> > -				    struct counter_synapse *synapse,
> > -				    size_t action)
> > +static int mchp_tc_count_action_write(struct counter_device *counter,
> > +				      struct counter_count *count,
> > +				      struct counter_synapse *synapse,
> > +				      enum counter_synapse_action action)
> >  {
> >  	struct mchp_tc_data *const priv = counter->priv;
> >  	u32 edge = ATMEL_TC_ETRGEDG_NONE;
> > @@ -217,16 +205,16 @@ static int mchp_tc_count_action_set(struct counter_device *counter,
> >  		return -EINVAL;
> >  
> >  	switch (action) {
> > -	case MCHP_TC_SYNAPSE_ACTION_NONE:
> > +	case COUNTER_SYNAPSE_ACTION_NONE:
> >  		edge = ATMEL_TC_ETRGEDG_NONE;
> >  		break;
> > -	case MCHP_TC_SYNAPSE_ACTION_RISING_EDGE:
> > +	case COUNTER_SYNAPSE_ACTION_RISING_EDGE:
> >  		edge = ATMEL_TC_ETRGEDG_RISING;
> >  		break;
> > -	case MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE:
> > +	case COUNTER_SYNAPSE_ACTION_FALLING_EDGE:
> >  		edge = ATMEL_TC_ETRGEDG_FALLING;
> >  		break;
> > -	case MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE:
> > +	case COUNTER_SYNAPSE_ACTION_BOTH_EDGES:
> >  		edge = ATMEL_TC_ETRGEDG_BOTH;
> >  		break;
> >  	default:
> > @@ -240,8 +228,7 @@ static int mchp_tc_count_action_set(struct counter_device *counter,
> >  }
> >  
> >  static int mchp_tc_count_read(struct counter_device *counter,
> > -			      struct counter_count *count,
> > -			      unsigned long *val)
> > +			      struct counter_count *count, u64 *val)
> >  {
> >  	struct mchp_tc_data *const priv = counter->priv;
> >  	u32 cnt;
> > @@ -264,12 +251,12 @@ static struct counter_count mchp_tc_counts[] = {
> >  };
> >  
> >  static const struct counter_ops mchp_tc_ops = {
> > -	.signal_read  = mchp_tc_count_signal_read,
> > -	.count_read   = mchp_tc_count_read,
> > -	.function_get = mchp_tc_count_function_get,
> > -	.function_set = mchp_tc_count_function_set,
> > -	.action_get   = mchp_tc_count_action_get,
> > -	.action_set   = mchp_tc_count_action_set
> > +	.signal_read    = mchp_tc_count_signal_read,
> > +	.count_read     = mchp_tc_count_read,
> > +	.function_read  = mchp_tc_count_function_read,
> > +	.function_write = mchp_tc_count_function_write,
> > +	.action_read    = mchp_tc_count_action_read,
> > +	.action_write   = mchp_tc_count_action_write
> >  };
> >  
> >  static const struct atmel_tcb_config tcb_rm9200_config = {
> > diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c
> > index 7367f46c6f91..5168833b1fdf 100644
> > --- a/drivers/counter/stm32-lptimer-cnt.c
> > +++ b/drivers/counter/stm32-lptimer-cnt.c
> > @@ -17,6 +17,7 @@
> >  #include <linux/module.h>
> >  #include <linux/pinctrl/consumer.h>
> >  #include <linux/platform_device.h>
> > +#include <linux/types.h>
> >  
> >  struct stm32_lptim_cnt {
> >  	struct counter_device counter;
> > @@ -107,11 +108,7 @@ static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
> >  	return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
> >  }
> >  
> > -/**
> > - * enum stm32_lptim_cnt_function - enumerates LPTimer counter & encoder modes
> > - * @STM32_LPTIM_COUNTER_INCREASE: up count on IN1 rising, falling or both edges
> > - * @STM32_LPTIM_ENCODER_BOTH_EDGE: count on both edges (IN1 & IN2 quadrature)
> > - *
> > +/*
> >   * In non-quadrature mode, device counts up on active edge.
> >   * In quadrature mode, encoder counting scenarios are as follows:
> >   * +---------+----------+--------------------+--------------------+
> > @@ -129,33 +126,20 @@ static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
> >   * | edges   | Low  ->  |   Up     |   Down  |   Down   |   Up    |
> >   * +---------+----------+----------+---------+----------+---------+
> >   */
> > -enum stm32_lptim_cnt_function {
> > -	STM32_LPTIM_COUNTER_INCREASE,
> > -	STM32_LPTIM_ENCODER_BOTH_EDGE,
> > -};
> > -
> >  static const enum counter_function stm32_lptim_cnt_functions[] = {
> > -	[STM32_LPTIM_COUNTER_INCREASE] = COUNTER_FUNCTION_INCREASE,
> > -	[STM32_LPTIM_ENCODER_BOTH_EDGE] = COUNTER_FUNCTION_QUADRATURE_X4,
> > -};
> > -
> > -enum stm32_lptim_synapse_action {
> > -	STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE = STM32_LPTIM_CKPOL_RISING_EDGE,
> > -	STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE = STM32_LPTIM_CKPOL_FALLING_EDGE,
> > -	STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES = STM32_LPTIM_CKPOL_BOTH_EDGES,
> > -	STM32_LPTIM_SYNAPSE_ACTION_NONE,
> > +	COUNTER_FUNCTION_INCREASE,
> > +	COUNTER_FUNCTION_QUADRATURE_X4,
> >  };
> >  
> >  static const enum counter_synapse_action stm32_lptim_cnt_synapse_actions[] = {
> > -	/* Index must match with stm32_lptim_cnt_polarity[] (priv->polarity) */
> > -	[STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> > -	[STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
> > -	[STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
> > -	[STM32_LPTIM_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
> > +	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> > +	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
> > +	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
> > +	COUNTER_SYNAPSE_ACTION_NONE,
> >  };
> >  
> >  static int stm32_lptim_cnt_read(struct counter_device *counter,
> > -				struct counter_count *count, unsigned long *val)
> > +				struct counter_count *count, u64 *val)
> >  {
> >  	struct stm32_lptim_cnt *const priv = counter->priv;
> >  	u32 cnt;
> > @@ -170,28 +154,28 @@ static int stm32_lptim_cnt_read(struct counter_device *counter,
> >  	return 0;
> >  }
> >  
> > -static int stm32_lptim_cnt_function_get(struct counter_device *counter,
> > -					struct counter_count *count,
> > -					size_t *function)
> > +static int stm32_lptim_cnt_function_read(struct counter_device *counter,
> > +					 struct counter_count *count,
> > +					 enum counter_function *function)
> >  {
> >  	struct stm32_lptim_cnt *const priv = counter->priv;
> >  
> >  	if (!priv->quadrature_mode) {
> > -		*function = STM32_LPTIM_COUNTER_INCREASE;
> > +		*function = COUNTER_FUNCTION_INCREASE;
> >  		return 0;
> >  	}
> >  
> > -	if (priv->polarity == STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES) {
> > -		*function = STM32_LPTIM_ENCODER_BOTH_EDGE;
> > +	if (priv->polarity == STM32_LPTIM_CKPOL_BOTH_EDGES) {
> > +		*function = COUNTER_FUNCTION_QUADRATURE_X4;
> >  		return 0;
> >  	}
> >  
> >  	return -EINVAL;
> >  }
> >  
> > -static int stm32_lptim_cnt_function_set(struct counter_device *counter,
> > -					struct counter_count *count,
> > -					size_t function)
> > +static int stm32_lptim_cnt_function_write(struct counter_device *counter,
> > +					  struct counter_count *count,
> > +					  enum counter_function function)
> >  {
> >  	struct stm32_lptim_cnt *const priv = counter->priv;
> >  
> > @@ -199,12 +183,12 @@ static int stm32_lptim_cnt_function_set(struct counter_device *counter,
> >  		return -EBUSY;
> >  
> >  	switch (function) {
> > -	case STM32_LPTIM_COUNTER_INCREASE:
> > +	case COUNTER_FUNCTION_INCREASE:
> >  		priv->quadrature_mode = 0;
> >  		return 0;
> > -	case STM32_LPTIM_ENCODER_BOTH_EDGE:
> > +	case COUNTER_FUNCTION_QUADRATURE_X4:
> >  		priv->quadrature_mode = 1;
> > -		priv->polarity = STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES;
> > +		priv->polarity = STM32_LPTIM_CKPOL_BOTH_EDGES;
> >  		return 0;
> >  	default:
> >  		/* should never reach this path */
> > @@ -212,9 +196,9 @@ static int stm32_lptim_cnt_function_set(struct counter_device *counter,
> >  	}
> >  }
> >  
> > -static ssize_t stm32_lptim_cnt_enable_read(struct counter_device *counter,
> > -					   struct counter_count *count,
> > -					   void *private, char *buf)
> > +static int stm32_lptim_cnt_enable_read(struct counter_device *counter,
> > +				       struct counter_count *count,
> > +				       u8 *enable)
> >  {
> >  	struct stm32_lptim_cnt *const priv = counter->priv;
> >  	int ret;
> > @@ -223,22 +207,18 @@ static ssize_t stm32_lptim_cnt_enable_read(struct counter_device *counter,
> >  	if (ret < 0)
> >  		return ret;
> >  
> > -	return scnprintf(buf, PAGE_SIZE, "%u\n", ret);
> > +	*enable = ret;
> > +
> > +	return 0;
> >  }
> >  
> > -static ssize_t stm32_lptim_cnt_enable_write(struct counter_device *counter,
> > -					    struct counter_count *count,
> > -					    void *private,
> > -					    const char *buf, size_t len)
> > +static int stm32_lptim_cnt_enable_write(struct counter_device *counter,
> > +					struct counter_count *count,
> > +					u8 enable)
> >  {
> >  	struct stm32_lptim_cnt *const priv = counter->priv;
> > -	bool enable;
> >  	int ret;
> >  
> > -	ret = kstrtobool(buf, &enable);
> > -	if (ret)
> > -		return ret;
> > -
> >  	/* Check nobody uses the timer, or already disabled/enabled */
> >  	ret = stm32_lptim_is_enabled(priv);
> >  	if ((ret < 0) || (!ret && !enable))
> > @@ -254,78 +234,81 @@ static ssize_t stm32_lptim_cnt_enable_write(struct counter_device *counter,
> >  	if (ret)
> >  		return ret;
> >  
> > -	return len;
> > +	return 0;
> >  }
> >  
> > -static ssize_t stm32_lptim_cnt_ceiling_read(struct counter_device *counter,
> > -					    struct counter_count *count,
> > -					    void *private, char *buf)
> > +static int stm32_lptim_cnt_ceiling_read(struct counter_device *counter,
> > +					struct counter_count *count,
> > +					u64 *ceiling)
> >  {
> >  	struct stm32_lptim_cnt *const priv = counter->priv;
> >  
> > -	return snprintf(buf, PAGE_SIZE, "%u\n", priv->ceiling);
> > +	*ceiling = priv->ceiling;
> > +
> > +	return 0;
> >  }
> >  
> > -static ssize_t stm32_lptim_cnt_ceiling_write(struct counter_device *counter,
> > -					     struct counter_count *count,
> > -					     void *private,
> > -					     const char *buf, size_t len)
> > +static int stm32_lptim_cnt_ceiling_write(struct counter_device *counter,
> > +					 struct counter_count *count,
> > +					 u64 ceiling)
> >  {
> >  	struct stm32_lptim_cnt *const priv = counter->priv;
> > -	unsigned int ceiling;
> > -	int ret;
> >  
> >  	if (stm32_lptim_is_enabled(priv))
> >  		return -EBUSY;
> >  
> > -	ret = kstrtouint(buf, 0, &ceiling);
> > -	if (ret)
> > -		return ret;
> > -
> >  	if (ceiling > STM32_LPTIM_MAX_ARR)
> >  		return -ERANGE;
> >  
> >  	priv->ceiling = ceiling;
> >  
> > -	return len;
> > +	return 0;
> >  }
> >  
> > -static const struct counter_count_ext stm32_lptim_cnt_ext[] = {
> > -	{
> > -		.name = "enable",
> > -		.read = stm32_lptim_cnt_enable_read,
> > -		.write = stm32_lptim_cnt_enable_write
> > -	},
> > -	{
> > -		.name = "ceiling",
> > -		.read = stm32_lptim_cnt_ceiling_read,
> > -		.write = stm32_lptim_cnt_ceiling_write
> > -	},
> > +static struct counter_comp stm32_lptim_cnt_ext[] = {
> > +	COUNTER_COMP_ENABLE(stm32_lptim_cnt_enable_read,
> > +			    stm32_lptim_cnt_enable_write),
> > +	COUNTER_COMP_CEILING(stm32_lptim_cnt_ceiling_read,
> > +			     stm32_lptim_cnt_ceiling_write),
> >  };
> >  
> > -static int stm32_lptim_cnt_action_get(struct counter_device *counter,
> > -				      struct counter_count *count,
> > -				      struct counter_synapse *synapse,
> > -				      size_t *action)
> > +static int stm32_lptim_cnt_action_read(struct counter_device *counter,
> > +				       struct counter_count *count,
> > +				       struct counter_synapse *synapse,
> > +				       enum counter_synapse_action *action)
> >  {
> >  	struct stm32_lptim_cnt *const priv = counter->priv;
> > -	size_t function;
> > +	enum counter_function function;
> >  	int err;
> >  
> > -	err = stm32_lptim_cnt_function_get(counter, count, &function);
> > +	err = stm32_lptim_cnt_function_read(counter, count, &function);
> >  	if (err)
> >  		return err;
> >  
> >  	switch (function) {
> > -	case STM32_LPTIM_COUNTER_INCREASE:
> > +	case COUNTER_FUNCTION_INCREASE:
> >  		/* LP Timer acts as up-counter on input 1 */
> > -		if (synapse->signal->id == count->synapses[0].signal->id)
> > -			*action = priv->polarity;
> > -		else
> > -			*action = STM32_LPTIM_SYNAPSE_ACTION_NONE;
> > -		return 0;
> > -	case STM32_LPTIM_ENCODER_BOTH_EDGE:
> > -		*action = priv->polarity;
> > +		if (synapse->signal->id != count->synapses[0].signal->id) {
> > +			*action = COUNTER_SYNAPSE_ACTION_NONE;
> > +			return 0;
> > +		}
> > +
> > +		switch (priv->polarity) {
> > +		case STM32_LPTIM_CKPOL_RISING_EDGE:
> > +			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
> > +			return 0;
> > +		case STM32_LPTIM_CKPOL_FALLING_EDGE:
> > +			*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
> > +			return 0;
> > +		case STM32_LPTIM_CKPOL_BOTH_EDGES:
> > +			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
> > +			return 0;
> > +		default:
> > +			/* should never reach this path */
> > +			return -EINVAL;
> > +		}
> > +	case COUNTER_FUNCTION_QUADRATURE_X4:
> > +		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
> >  		return 0;
> >  	default:
> >  		/* should never reach this path */
> > @@ -333,43 +316,48 @@ static int stm32_lptim_cnt_action_get(struct counter_device *counter,
> >  	}
> >  }
> >  
> > -static int stm32_lptim_cnt_action_set(struct counter_device *counter,
> > -				      struct counter_count *count,
> > -				      struct counter_synapse *synapse,
> > -				      size_t action)
> > +static int stm32_lptim_cnt_action_write(struct counter_device *counter,
> > +					struct counter_count *count,
> > +					struct counter_synapse *synapse,
> > +					enum counter_synapse_action action)
> >  {
> >  	struct stm32_lptim_cnt *const priv = counter->priv;
> > -	size_t function;
> > +	enum counter_function function;
> >  	int err;
> >  
> >  	if (stm32_lptim_is_enabled(priv))
> >  		return -EBUSY;
> >  
> > -	err = stm32_lptim_cnt_function_get(counter, count, &function);
> > +	err = stm32_lptim_cnt_function_read(counter, count, &function);
> >  	if (err)
> >  		return err;
> >  
> >  	/* only set polarity when in counter mode (on input 1) */
> > -	if (function == STM32_LPTIM_COUNTER_INCREASE
> > -	    && synapse->signal->id == count->synapses[0].signal->id) {
> > -		switch (action) {
> > -		case STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE:
> > -		case STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE:
> > -		case STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES:
> > -			priv->polarity = action;
> > -			return 0;
> > -		}
> > -	}
> > +	if (function != COUNTER_FUNCTION_INCREASE
> > +	    || synapse->signal->id != count->synapses[0].signal->id)
> > +		return -EINVAL;
> >  
> > -	return -EINVAL;
> > +	switch (action) {
> > +	case COUNTER_SYNAPSE_ACTION_RISING_EDGE:
> > +		priv->polarity = STM32_LPTIM_CKPOL_RISING_EDGE;
> > +		return 0;
> > +	case COUNTER_SYNAPSE_ACTION_FALLING_EDGE:
> > +		priv->polarity = STM32_LPTIM_CKPOL_FALLING_EDGE;
> > +		return 0;
> > +	case COUNTER_SYNAPSE_ACTION_BOTH_EDGES:
> > +		priv->polarity = STM32_LPTIM_CKPOL_BOTH_EDGES;
> > +		return 0;
> > +	default:
> > +		return -EINVAL;
> > +	}
> >  }
> >  
> >  static const struct counter_ops stm32_lptim_cnt_ops = {
> >  	.count_read = stm32_lptim_cnt_read,
> > -	.function_get = stm32_lptim_cnt_function_get,
> > -	.function_set = stm32_lptim_cnt_function_set,
> > -	.action_get = stm32_lptim_cnt_action_get,
> > -	.action_set = stm32_lptim_cnt_action_set,
> > +	.function_read = stm32_lptim_cnt_function_read,
> > +	.function_write = stm32_lptim_cnt_function_write,
> > +	.action_read = stm32_lptim_cnt_action_read,
> > +	.action_write = stm32_lptim_cnt_action_write,
> >  };
> >  
> >  static struct counter_signal stm32_lptim_cnt_signals[] = {
> > diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c
> > index 1fbc46f4ee66..0546e932db0c 100644
> > --- a/drivers/counter/stm32-timer-cnt.c
> > +++ b/drivers/counter/stm32-timer-cnt.c
> > @@ -13,6 +13,7 @@
> >  #include <linux/module.h>
> >  #include <linux/pinctrl/consumer.h>
> >  #include <linux/platform_device.h>
> > +#include <linux/types.h>
> >  
> >  #define TIM_CCMR_CCXS	(BIT(8) | BIT(0))
> >  #define TIM_CCMR_MASK	(TIM_CCMR_CC1S | TIM_CCMR_CC2S | \
> > @@ -36,29 +37,15 @@ struct stm32_timer_cnt {
> >  	struct stm32_timer_regs bak;
> >  };
> >  
> > -/**
> > - * enum stm32_count_function - enumerates stm32 timer counter encoder modes
> > - * @STM32_COUNT_SLAVE_MODE_DISABLED: counts on internal clock when CEN=1
> > - * @STM32_COUNT_ENCODER_MODE_1: counts TI1FP1 edges, depending on TI2FP2 level
> > - * @STM32_COUNT_ENCODER_MODE_2: counts TI2FP2 edges, depending on TI1FP1 level
> > - * @STM32_COUNT_ENCODER_MODE_3: counts on both TI1FP1 and TI2FP2 edges
> > - */
> > -enum stm32_count_function {
> > -	STM32_COUNT_SLAVE_MODE_DISABLED,
> > -	STM32_COUNT_ENCODER_MODE_1,
> > -	STM32_COUNT_ENCODER_MODE_2,
> > -	STM32_COUNT_ENCODER_MODE_3,
> > -};
> > -
> >  static const enum counter_function stm32_count_functions[] = {
> > -	[STM32_COUNT_SLAVE_MODE_DISABLED] = COUNTER_FUNCTION_INCREASE,
> > -	[STM32_COUNT_ENCODER_MODE_1] = COUNTER_FUNCTION_QUADRATURE_X2_A,
> > -	[STM32_COUNT_ENCODER_MODE_2] = COUNTER_FUNCTION_QUADRATURE_X2_B,
> > -	[STM32_COUNT_ENCODER_MODE_3] = COUNTER_FUNCTION_QUADRATURE_X4,
> > +	COUNTER_FUNCTION_INCREASE,
> > +	COUNTER_FUNCTION_QUADRATURE_X2_A,
> > +	COUNTER_FUNCTION_QUADRATURE_X2_B,
> > +	COUNTER_FUNCTION_QUADRATURE_X4,
> >  };
> >  
> >  static int stm32_count_read(struct counter_device *counter,
> > -			    struct counter_count *count, unsigned long *val)
> > +			    struct counter_count *count, u64 *val)
> >  {
> >  	struct stm32_timer_cnt *const priv = counter->priv;
> >  	u32 cnt;
> > @@ -70,8 +57,7 @@ static int stm32_count_read(struct counter_device *counter,
> >  }
> >  
> >  static int stm32_count_write(struct counter_device *counter,
> > -			     struct counter_count *count,
> > -			     const unsigned long val)
> > +			     struct counter_count *count, const u64 val)
> >  {
> >  	struct stm32_timer_cnt *const priv = counter->priv;
> >  	u32 ceiling;
> > @@ -83,9 +69,9 @@ static int stm32_count_write(struct counter_device *counter,
> >  	return regmap_write(priv->regmap, TIM_CNT, val);
> >  }
> >  
> > -static int stm32_count_function_get(struct counter_device *counter,
> > -				    struct counter_count *count,
> > -				    size_t *function)
> > +static int stm32_count_function_read(struct counter_device *counter,
> > +				     struct counter_count *count,
> > +				     enum counter_function *function)
> >  {
> >  	struct stm32_timer_cnt *const priv = counter->priv;
> >  	u32 smcr;
> > @@ -94,40 +80,40 @@ static int stm32_count_function_get(struct counter_device *counter,
> >  
> >  	switch (smcr & TIM_SMCR_SMS) {
> >  	case TIM_SMCR_SMS_SLAVE_MODE_DISABLED:
> > -		*function = STM32_COUNT_SLAVE_MODE_DISABLED;
> > +		*function = COUNTER_FUNCTION_INCREASE;
> >  		return 0;
> >  	case TIM_SMCR_SMS_ENCODER_MODE_1:
> > -		*function = STM32_COUNT_ENCODER_MODE_1;
> > +		*function = COUNTER_FUNCTION_QUADRATURE_X2_A;
> >  		return 0;
> >  	case TIM_SMCR_SMS_ENCODER_MODE_2:
> > -		*function = STM32_COUNT_ENCODER_MODE_2;
> > +		*function = COUNTER_FUNCTION_QUADRATURE_X2_B;
> >  		return 0;
> >  	case TIM_SMCR_SMS_ENCODER_MODE_3:
> > -		*function = STM32_COUNT_ENCODER_MODE_3;
> > +		*function = COUNTER_FUNCTION_QUADRATURE_X4;
> >  		return 0;
> >  	default:
> >  		return -EINVAL;
> >  	}
> >  }
> >  
> > -static int stm32_count_function_set(struct counter_device *counter,
> > -				    struct counter_count *count,
> > -				    size_t function)
> > +static int stm32_count_function_write(struct counter_device *counter,
> > +				      struct counter_count *count,
> > +				      enum counter_function function)
> >  {
> >  	struct stm32_timer_cnt *const priv = counter->priv;
> >  	u32 cr1, sms;
> >  
> >  	switch (function) {
> > -	case STM32_COUNT_SLAVE_MODE_DISABLED:
> > +	case COUNTER_FUNCTION_INCREASE:
> >  		sms = TIM_SMCR_SMS_SLAVE_MODE_DISABLED;
> >  		break;
> > -	case STM32_COUNT_ENCODER_MODE_1:
> > +	case COUNTER_FUNCTION_QUADRATURE_X2_A:
> >  		sms = TIM_SMCR_SMS_ENCODER_MODE_1;
> >  		break;
> > -	case STM32_COUNT_ENCODER_MODE_2:
> > +	case COUNTER_FUNCTION_QUADRATURE_X2_B:
> >  		sms = TIM_SMCR_SMS_ENCODER_MODE_2;
> >  		break;
> > -	case STM32_COUNT_ENCODER_MODE_3:
> > +	case COUNTER_FUNCTION_QUADRATURE_X4:
> >  		sms = TIM_SMCR_SMS_ENCODER_MODE_3;
> >  		break;
> >  	default:
> > @@ -150,44 +136,37 @@ static int stm32_count_function_set(struct counter_device *counter,
> >  	return 0;
> >  }
> >  
> > -static ssize_t stm32_count_direction_read(struct counter_device *counter,
> > +static int stm32_count_direction_read(struct counter_device *counter,
> >  				      struct counter_count *count,
> > -				      void *private, char *buf)
> > +				      enum counter_count_direction *direction)
> >  {
> >  	struct stm32_timer_cnt *const priv = counter->priv;
> > -	const char *direction;
> >  	u32 cr1;
> >  
> >  	regmap_read(priv->regmap, TIM_CR1, &cr1);
> > -	direction = (cr1 & TIM_CR1_DIR) ? "backward" : "forward";
> > +	*direction = (cr1 & TIM_CR1_DIR) ? COUNTER_COUNT_DIRECTION_BACKWARD :
> > +		COUNTER_COUNT_DIRECTION_FORWARD;
> >  
> > -	return scnprintf(buf, PAGE_SIZE, "%s\n", direction);
> > +	return 0;
> >  }
> >  
> > -static ssize_t stm32_count_ceiling_read(struct counter_device *counter,
> > -					struct counter_count *count,
> > -					void *private, char *buf)
> > +static int stm32_count_ceiling_read(struct counter_device *counter,
> > +				    struct counter_count *count, u64 *ceiling)
> >  {
> >  	struct stm32_timer_cnt *const priv = counter->priv;
> >  	u32 arr;
> >  
> >  	regmap_read(priv->regmap, TIM_ARR, &arr);
> >  
> > -	return snprintf(buf, PAGE_SIZE, "%u\n", arr);
> > +	*ceiling = arr;
> > +
> > +	return 0;
> >  }
> >  
> > -static ssize_t stm32_count_ceiling_write(struct counter_device *counter,
> > -					 struct counter_count *count,
> > -					 void *private,
> > -					 const char *buf, size_t len)
> > +static int stm32_count_ceiling_write(struct counter_device *counter,
> > +				     struct counter_count *count, u64 ceiling)
> >  {
> >  	struct stm32_timer_cnt *const priv = counter->priv;
> > -	unsigned int ceiling;
> > -	int ret;
> > -
> > -	ret = kstrtouint(buf, 0, &ceiling);
> > -	if (ret)
> > -		return ret;
> >  
> >  	if (ceiling > priv->max_arr)
> >  		return -ERANGE;
> > @@ -196,34 +175,27 @@ static ssize_t stm32_count_ceiling_write(struct counter_device *counter,
> >  	regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, 0);
> >  	regmap_write(priv->regmap, TIM_ARR, ceiling);
> >  
> > -	return len;
> > +	return 0;
> >  }
> >  
> > -static ssize_t stm32_count_enable_read(struct counter_device *counter,
> > -				       struct counter_count *count,
> > -				       void *private, char *buf)
> > +static int stm32_count_enable_read(struct counter_device *counter,
> > +				   struct counter_count *count, u8 *enable)
> >  {
> >  	struct stm32_timer_cnt *const priv = counter->priv;
> >  	u32 cr1;
> >  
> >  	regmap_read(priv->regmap, TIM_CR1, &cr1);
> >  
> > -	return scnprintf(buf, PAGE_SIZE, "%d\n", (bool)(cr1 & TIM_CR1_CEN));
> > +	*enable = cr1 & TIM_CR1_CEN;
> > +
> > +	return 0;
> >  }
> >  
> > -static ssize_t stm32_count_enable_write(struct counter_device *counter,
> > -					struct counter_count *count,
> > -					void *private,
> > -					const char *buf, size_t len)
> > +static int stm32_count_enable_write(struct counter_device *counter,
> > +				    struct counter_count *count, u8 enable)
> >  {
> >  	struct stm32_timer_cnt *const priv = counter->priv;
> > -	int err;
> >  	u32 cr1;
> > -	bool enable;
> > -
> > -	err = kstrtobool(buf, &enable);
> > -	if (err)
> > -		return err;
> >  
> >  	if (enable) {
> >  		regmap_read(priv->regmap, TIM_CR1, &cr1);
> > @@ -242,70 +214,55 @@ static ssize_t stm32_count_enable_write(struct counter_device *counter,
> >  	/* Keep enabled state to properly handle low power states */
> >  	priv->enabled = enable;
> >  
> > -	return len;
> > +	return 0;
> >  }
> >  
> > -static const struct counter_count_ext stm32_count_ext[] = {
> > -	{
> > -		.name = "direction",
> > -		.read = stm32_count_direction_read,
> > -	},
> > -	{
> > -		.name = "enable",
> > -		.read = stm32_count_enable_read,
> > -		.write = stm32_count_enable_write
> > -	},
> > -	{
> > -		.name = "ceiling",
> > -		.read = stm32_count_ceiling_read,
> > -		.write = stm32_count_ceiling_write
> > -	},
> > -};
> > -
> > -enum stm32_synapse_action {
> > -	STM32_SYNAPSE_ACTION_NONE,
> > -	STM32_SYNAPSE_ACTION_BOTH_EDGES
> > +static struct counter_comp stm32_count_ext[] = {
> > +	COUNTER_COMP_DIRECTION(stm32_count_direction_read),
> > +	COUNTER_COMP_ENABLE(stm32_count_enable_read, stm32_count_enable_write),
> > +	COUNTER_COMP_CEILING(stm32_count_ceiling_read,
> > +			     stm32_count_ceiling_write),
> >  };
> >  
> >  static const enum counter_synapse_action stm32_synapse_actions[] = {
> > -	[STM32_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
> > -	[STM32_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES
> > +	COUNTER_SYNAPSE_ACTION_NONE,
> > +	COUNTER_SYNAPSE_ACTION_BOTH_EDGES
> >  };
> >  
> > -static int stm32_action_get(struct counter_device *counter,
> > -			    struct counter_count *count,
> > -			    struct counter_synapse *synapse,
> > -			    size_t *action)
> > +static int stm32_action_read(struct counter_device *counter,
> > +			     struct counter_count *count,
> > +			     struct counter_synapse *synapse,
> > +			     enum counter_synapse_action *action)
> >  {
> > -	size_t function;
> > +	enum counter_function function;
> >  	int err;
> >  
> > -	err = stm32_count_function_get(counter, count, &function);
> > +	err = stm32_count_function_read(counter, count, &function);
> >  	if (err)
> >  		return err;
> >  
> >  	switch (function) {
> > -	case STM32_COUNT_SLAVE_MODE_DISABLED:
> > +	case COUNTER_FUNCTION_INCREASE:
> >  		/* counts on internal clock when CEN=1 */
> > -		*action = STM32_SYNAPSE_ACTION_NONE;
> > +		*action = COUNTER_SYNAPSE_ACTION_NONE;
> >  		return 0;
> > -	case STM32_COUNT_ENCODER_MODE_1:
> > +	case COUNTER_FUNCTION_QUADRATURE_X2_A:
> >  		/* counts up/down on TI1FP1 edge depending on TI2FP2 level */
> >  		if (synapse->signal->id == count->synapses[0].signal->id)
> > -			*action = STM32_SYNAPSE_ACTION_BOTH_EDGES;
> > +			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
> >  		else
> > -			*action = STM32_SYNAPSE_ACTION_NONE;
> > +			*action = COUNTER_SYNAPSE_ACTION_NONE;
> >  		return 0;
> > -	case STM32_COUNT_ENCODER_MODE_2:
> > +	case COUNTER_FUNCTION_QUADRATURE_X2_B:
> >  		/* counts up/down on TI2FP2 edge depending on TI1FP1 level */
> >  		if (synapse->signal->id == count->synapses[1].signal->id)
> > -			*action = STM32_SYNAPSE_ACTION_BOTH_EDGES;
> > +			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
> >  		else
> > -			*action = STM32_SYNAPSE_ACTION_NONE;
> > +			*action = COUNTER_SYNAPSE_ACTION_NONE;
> >  		return 0;
> > -	case STM32_COUNT_ENCODER_MODE_3:
> > +	case COUNTER_FUNCTION_QUADRATURE_X4:
> >  		/* counts up/down on both TI1FP1 and TI2FP2 edges */
> > -		*action = STM32_SYNAPSE_ACTION_BOTH_EDGES;
> > +		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
> >  		return 0;
> >  	default:
> >  		return -EINVAL;
> > @@ -315,9 +272,9 @@ static int stm32_action_get(struct counter_device *counter,
> >  static const struct counter_ops stm32_timer_cnt_ops = {
> >  	.count_read = stm32_count_read,
> >  	.count_write = stm32_count_write,
> > -	.function_get = stm32_count_function_get,
> > -	.function_set = stm32_count_function_set,
> > -	.action_get = stm32_action_get,
> > +	.function_read = stm32_count_function_read,
> > +	.function_write = stm32_count_function_write,
> > +	.action_read = stm32_action_read,
> >  };
> >  
> >  static struct counter_signal stm32_signals[] = {
> > diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c
> > index 94fe58bb3eab..09817c953f9a 100644
> > --- a/drivers/counter/ti-eqep.c
> > +++ b/drivers/counter/ti-eqep.c
> > @@ -13,6 +13,7 @@
> >  #include <linux/platform_device.h>
> >  #include <linux/pm_runtime.h>
> >  #include <linux/regmap.h>
> > +#include <linux/types.h>
> >  
> >  /* 32-bit registers */
> >  #define QPOSCNT		0x0
> > @@ -73,19 +74,13 @@ enum {
> >  };
> >  
> >  /* Position Counter Input Modes */
> > -enum {
> > +enum ti_eqep_count_func {
> >  	TI_EQEP_COUNT_FUNC_QUAD_COUNT,
> >  	TI_EQEP_COUNT_FUNC_DIR_COUNT,
> >  	TI_EQEP_COUNT_FUNC_UP_COUNT,
> >  	TI_EQEP_COUNT_FUNC_DOWN_COUNT,
> >  };
> >  
> > -enum {
> > -	TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES,
> > -	TI_EQEP_SYNAPSE_ACTION_RISING_EDGE,
> > -	TI_EQEP_SYNAPSE_ACTION_NONE,
> > -};
> > -
> >  struct ti_eqep_cnt {
> >  	struct counter_device counter;
> >  	struct regmap *regmap32;
> > @@ -93,7 +88,7 @@ struct ti_eqep_cnt {
> >  };
> >  
> >  static int ti_eqep_count_read(struct counter_device *counter,
> > -			      struct counter_count *count, unsigned long *val)
> > +			      struct counter_count *count, u64 *val)
> >  {
> >  	struct ti_eqep_cnt *priv = counter->priv;
> >  	u32 cnt;
> > @@ -105,7 +100,7 @@ static int ti_eqep_count_read(struct counter_device *counter,
> >  }
> >  
> >  static int ti_eqep_count_write(struct counter_device *counter,
> > -			       struct counter_count *count, unsigned long val)
> > +			       struct counter_count *count, u64 val)
> >  {
> >  	struct ti_eqep_cnt *priv = counter->priv;
> >  	u32 max;
> > @@ -117,64 +112,100 @@ static int ti_eqep_count_write(struct counter_device *counter,
> >  	return regmap_write(priv->regmap32, QPOSCNT, val);
> >  }
> >  
> > -static int ti_eqep_function_get(struct counter_device *counter,
> > -				struct counter_count *count, size_t *function)
> > +static int ti_eqep_function_read(struct counter_device *counter,
> > +				 struct counter_count *count,
> > +				 enum counter_function *function)
> >  {
> >  	struct ti_eqep_cnt *priv = counter->priv;
> >  	u32 qdecctl;
> >  
> >  	regmap_read(priv->regmap16, QDECCTL, &qdecctl);
> > -	*function = (qdecctl & QDECCTL_QSRC) >> QDECCTL_QSRC_SHIFT;
> > +
> > +	switch ((qdecctl & QDECCTL_QSRC) >> QDECCTL_QSRC_SHIFT) {
> > +	case TI_EQEP_COUNT_FUNC_QUAD_COUNT:
> > +		*function = COUNTER_FUNCTION_QUADRATURE_X4;
> > +		break;
> > +	case TI_EQEP_COUNT_FUNC_DIR_COUNT:
> > +		*function = COUNTER_FUNCTION_PULSE_DIRECTION;
> > +		break;
> > +	case TI_EQEP_COUNT_FUNC_UP_COUNT:
> > +		*function = COUNTER_FUNCTION_INCREASE;
> > +		break;
> > +	case TI_EQEP_COUNT_FUNC_DOWN_COUNT:
> > +		*function = COUNTER_FUNCTION_DECREASE;
> > +		break;
> > +	}
> >  
> >  	return 0;
> >  }
> >  
> > -static int ti_eqep_function_set(struct counter_device *counter,
> > -				struct counter_count *count, size_t function)
> > +static int ti_eqep_function_write(struct counter_device *counter,
> > +				  struct counter_count *count,
> > +				  enum counter_function function)
> >  {
> >  	struct ti_eqep_cnt *priv = counter->priv;
> > +	enum ti_eqep_count_func qsrc;
> > +
> > +	switch (function) {
> > +	case COUNTER_FUNCTION_QUADRATURE_X4:
> > +		qsrc = TI_EQEP_COUNT_FUNC_QUAD_COUNT;
> > +		break;
> > +	case COUNTER_FUNCTION_PULSE_DIRECTION:
> > +		qsrc = TI_EQEP_COUNT_FUNC_DIR_COUNT;
> > +		break;
> > +	case COUNTER_FUNCTION_INCREASE:
> > +		qsrc = TI_EQEP_COUNT_FUNC_UP_COUNT;
> > +		break;
> > +	case COUNTER_FUNCTION_DECREASE:
> > +		qsrc = TI_EQEP_COUNT_FUNC_DOWN_COUNT;
> > +		break;
> > +	default:
> > +		/* should never reach this path */
> > +		return -EINVAL;
> > +	}
> >  
> >  	return regmap_write_bits(priv->regmap16, QDECCTL, QDECCTL_QSRC,
> > -				 function << QDECCTL_QSRC_SHIFT);
> > +				 qsrc << QDECCTL_QSRC_SHIFT);
> >  }
> >  
> > -static int ti_eqep_action_get(struct counter_device *counter,
> > -			      struct counter_count *count,
> > -			      struct counter_synapse *synapse, size_t *action)
> > +static int ti_eqep_action_read(struct counter_device *counter,
> > +			       struct counter_count *count,
> > +			       struct counter_synapse *synapse,
> > +			       enum counter_synapse_action *action)
> >  {
> >  	struct ti_eqep_cnt *priv = counter->priv;
> > -	size_t function;
> > +	enum counter_function function;
> >  	u32 qdecctl;
> >  	int err;
> >  
> > -	err = ti_eqep_function_get(counter, count, &function);
> > +	err = ti_eqep_function_read(counter, count, &function);
> >  	if (err)
> >  		return err;
> >  
> >  	switch (function) {
> > -	case TI_EQEP_COUNT_FUNC_QUAD_COUNT:
> > +	case COUNTER_FUNCTION_QUADRATURE_X4:
> >  		/* In quadrature mode, the rising and falling edge of both
> >  		 * QEPA and QEPB trigger QCLK.
> >  		 */
> > -		*action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES;
> > +		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
> >  		return 0;
> > -	case TI_EQEP_COUNT_FUNC_DIR_COUNT:
> > +	case COUNTER_FUNCTION_PULSE_DIRECTION:
> >  		/* In direction-count mode only rising edge of QEPA is counted
> >  		 * and QEPB gives direction.
> >  		 */
> >  		switch (synapse->signal->id) {
> >  		case TI_EQEP_SIGNAL_QEPA:
> > -			*action = TI_EQEP_SYNAPSE_ACTION_RISING_EDGE;
> > +			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
> >  			return 0;
> >  		case TI_EQEP_SIGNAL_QEPB:
> > -			*action = TI_EQEP_SYNAPSE_ACTION_NONE;
> > +			*action = COUNTER_SYNAPSE_ACTION_NONE;
> >  			return 0;
> >  		default:
> >  			/* should never reach this path */
> >  			return -EINVAL;
> >  		}
> > -	case TI_EQEP_COUNT_FUNC_UP_COUNT:
> > -	case TI_EQEP_COUNT_FUNC_DOWN_COUNT:
> > +	case COUNTER_FUNCTION_INCREASE:
> > +	case COUNTER_FUNCTION_DECREASE:
> >  		/* In up/down-count modes only QEPA is counted and QEPB is not
> >  		 * used.
> >  		 */
> > @@ -185,12 +216,12 @@ static int ti_eqep_action_get(struct counter_device *counter,
> >  				return err;
> >  
> >  			if (qdecctl & QDECCTL_XCR)
> > -				*action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES;
> > +				*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
> >  			else
> > -				*action = TI_EQEP_SYNAPSE_ACTION_RISING_EDGE;
> > +				*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
> >  			return 0;
> >  		case TI_EQEP_SIGNAL_QEPB:
> > -			*action = TI_EQEP_SYNAPSE_ACTION_NONE;
> > +			*action = COUNTER_SYNAPSE_ACTION_NONE;
> >  			return 0;
> >  		default:
> >  			/* should never reach this path */
> > @@ -205,82 +236,67 @@ static int ti_eqep_action_get(struct counter_device *counter,
> >  static const struct counter_ops ti_eqep_counter_ops = {
> >  	.count_read	= ti_eqep_count_read,
> >  	.count_write	= ti_eqep_count_write,
> > -	.function_get	= ti_eqep_function_get,
> > -	.function_set	= ti_eqep_function_set,
> > -	.action_get	= ti_eqep_action_get,
> > +	.function_read	= ti_eqep_function_read,
> > +	.function_write	= ti_eqep_function_write,
> > +	.action_read	= ti_eqep_action_read,
> >  };
> >  
> > -static ssize_t ti_eqep_position_ceiling_read(struct counter_device *counter,
> > -					     struct counter_count *count,
> > -					     void *ext_priv, char *buf)
> > +static int ti_eqep_position_ceiling_read(struct counter_device *counter,
> > +					 struct counter_count *count,
> > +					 u64 *ceiling)
> >  {
> >  	struct ti_eqep_cnt *priv = counter->priv;
> >  	u32 qposmax;
> >  
> >  	regmap_read(priv->regmap32, QPOSMAX, &qposmax);
> >  
> > -	return sprintf(buf, "%u\n", qposmax);
> > +	*ceiling = qposmax;
> > +
> > +	return 0;
> >  }
> >  
> > -static ssize_t ti_eqep_position_ceiling_write(struct counter_device *counter,
> > -					      struct counter_count *count,
> > -					      void *ext_priv, const char *buf,
> > -					      size_t len)
> > +static int ti_eqep_position_ceiling_write(struct counter_device *counter,
> > +					  struct counter_count *count,
> > +					  u64 ceiling)
> >  {
> >  	struct ti_eqep_cnt *priv = counter->priv;
> > -	int err;
> > -	u32 res;
> >  
> > -	err = kstrtouint(buf, 0, &res);
> > -	if (err < 0)
> > -		return err;
> > +	if (ceiling != (u32)ceiling)
> > +		return -ERANGE;
> >  
> > -	regmap_write(priv->regmap32, QPOSMAX, res);
> > +	regmap_write(priv->regmap32, QPOSMAX, ceiling);
> >  
> > -	return len;
> > +	return 0;
> >  }
> >  
> > -static ssize_t ti_eqep_position_enable_read(struct counter_device *counter,
> > -					    struct counter_count *count,
> > -					    void *ext_priv, char *buf)
> > +static int ti_eqep_position_enable_read(struct counter_device *counter,
> > +					struct counter_count *count, u8 *enable)
> >  {
> >  	struct ti_eqep_cnt *priv = counter->priv;
> >  	u32 qepctl;
> >  
> >  	regmap_read(priv->regmap16, QEPCTL, &qepctl);
> >  
> > -	return sprintf(buf, "%u\n", !!(qepctl & QEPCTL_PHEN));
> > +	*enable = !!(qepctl & QEPCTL_PHEN);
> > +
> > +	return 0;
> >  }
> >  
> > -static ssize_t ti_eqep_position_enable_write(struct counter_device *counter,
> > -					     struct counter_count *count,
> > -					     void *ext_priv, const char *buf,
> > -					     size_t len)
> > +static int ti_eqep_position_enable_write(struct counter_device *counter,
> > +					 struct counter_count *count, u8 enable)
> >  {
> >  	struct ti_eqep_cnt *priv = counter->priv;
> > -	int err;
> > -	bool res;
> > -
> > -	err = kstrtobool(buf, &res);
> > -	if (err < 0)
> > -		return err;
> >  
> > -	regmap_write_bits(priv->regmap16, QEPCTL, QEPCTL_PHEN, res ? -1 : 0);
> > +	regmap_write_bits(priv->regmap16, QEPCTL, QEPCTL_PHEN, enable ? -1 : 0);
> >  
> > -	return len;
> > +	return 0;
> >  }
> >  
> > -static struct counter_count_ext ti_eqep_position_ext[] = {
> > -	{
> > -		.name	= "ceiling",
> > -		.read	= ti_eqep_position_ceiling_read,
> > -		.write	= ti_eqep_position_ceiling_write,
> > -	},
> > -	{
> > -		.name	= "enable",
> > -		.read	= ti_eqep_position_enable_read,
> > -		.write	= ti_eqep_position_enable_write,
> > -	},
> > +static struct counter_comp ti_eqep_position_ext[] = {
> > +	COUNTER_COMP_CEILING(ti_eqep_position_ceiling_read,
> > +			     ti_eqep_position_ceiling_write),
> > +	COUNTER_COMP_ENABLE(ti_eqep_position_enable_read,
> > +			    ti_eqep_position_enable_write),
> >  };
> >  
> >  static struct counter_signal ti_eqep_signals[] = {
> > @@ -295,16 +311,16 @@ static struct counter_signal ti_eqep_signals[] = {
> >  };
> >  
> >  static const enum counter_function ti_eqep_position_functions[] = {
> > -	[TI_EQEP_COUNT_FUNC_QUAD_COUNT]	= COUNTER_FUNCTION_QUADRATURE_X4,
> > -	[TI_EQEP_COUNT_FUNC_DIR_COUNT]	= COUNTER_FUNCTION_PULSE_DIRECTION,
> > -	[TI_EQEP_COUNT_FUNC_UP_COUNT]	= COUNTER_FUNCTION_INCREASE,
> > -	[TI_EQEP_COUNT_FUNC_DOWN_COUNT]	= COUNTER_FUNCTION_DECREASE,
> > +	COUNTER_FUNCTION_QUADRATURE_X4,
> > +	COUNTER_FUNCTION_PULSE_DIRECTION,
> > +	COUNTER_FUNCTION_INCREASE,
> > +	COUNTER_FUNCTION_DECREASE,
> >  };
> >  
> >  static const enum counter_synapse_action ti_eqep_position_synapse_actions[] = {
> > -	[TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES]	= COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
> > -	[TI_EQEP_SYNAPSE_ACTION_RISING_EDGE]	= COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> > -	[TI_EQEP_SYNAPSE_ACTION_NONE]		= COUNTER_SYNAPSE_ACTION_NONE,
> > +	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
> > +	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> > +	COUNTER_SYNAPSE_ACTION_NONE,
> >  };
> >  
> >  static struct counter_synapse ti_eqep_position_synapses[] = {
> > diff --git a/include/linux/counter.h b/include/linux/counter.h
> > index d16ce2819b48..b69277f5c4c5 100644
> > --- a/include/linux/counter.h
> > +++ b/include/linux/counter.h
> > @@ -6,42 +6,184 @@
> >  #ifndef _COUNTER_H_
> >  #define _COUNTER_H_
> >  
> > -#include <linux/counter_enum.h>
> >  #include <linux/device.h>
> > +#include <linux/kernel.h>
> >  #include <linux/types.h>
> >  
> > +struct counter_device;
> > +struct counter_count;
> > +struct counter_synapse;
> > +struct counter_signal;
> > +
> > +enum counter_comp_type {
> > +	COUNTER_COMP_U8,
> > +	COUNTER_COMP_U64,
> > +	COUNTER_COMP_BOOL,
> > +	COUNTER_COMP_SIGNAL_LEVEL,
> > +	COUNTER_COMP_FUNCTION,
> > +	COUNTER_COMP_SYNAPSE_ACTION,
> > +	COUNTER_COMP_ENUM,
> > +	COUNTER_COMP_COUNT_DIRECTION,
> > +	COUNTER_COMP_COUNT_MODE,
> > +};
> > +
> > +enum counter_scope {
> > +	COUNTER_SCOPE_DEVICE,
> > +	COUNTER_SCOPE_SIGNAL,
> > +	COUNTER_SCOPE_COUNT,
> > +};
> > +
> >  enum counter_count_direction {
> > -	COUNTER_COUNT_DIRECTION_FORWARD = 0,
> > -	COUNTER_COUNT_DIRECTION_BACKWARD
> > +	COUNTER_COUNT_DIRECTION_FORWARD,
> > +	COUNTER_COUNT_DIRECTION_BACKWARD,
> >  };
> > -extern const char *const counter_count_direction_str[2];
> >  
> >  enum counter_count_mode {
> > -	COUNTER_COUNT_MODE_NORMAL = 0,
> > +	COUNTER_COUNT_MODE_NORMAL,
> >  	COUNTER_COUNT_MODE_RANGE_LIMIT,
> >  	COUNTER_COUNT_MODE_NON_RECYCLE,
> > -	COUNTER_COUNT_MODE_MODULO_N
> > +	COUNTER_COUNT_MODE_MODULO_N,
> >  };
> > -extern const char *const counter_count_mode_str[4];
> >  
> > -struct counter_device;
> > -struct counter_signal;
> > +enum counter_function {
> > +	COUNTER_FUNCTION_INCREASE,
> > +	COUNTER_FUNCTION_DECREASE,
> > +	COUNTER_FUNCTION_PULSE_DIRECTION,
> > +	COUNTER_FUNCTION_QUADRATURE_X1_A,
> > +	COUNTER_FUNCTION_QUADRATURE_X1_B,
> > +	COUNTER_FUNCTION_QUADRATURE_X2_A,
> > +	COUNTER_FUNCTION_QUADRATURE_X2_B,
> > +	COUNTER_FUNCTION_QUADRATURE_X4,
> > +};
> > +
> > +enum counter_signal_level {
> > +	COUNTER_SIGNAL_LEVEL_LOW,
> > +	COUNTER_SIGNAL_LEVEL_HIGH,
> > +};
> > +
> > +enum counter_synapse_action {
> > +	COUNTER_SYNAPSE_ACTION_NONE,
> > +	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> > +	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
> > +	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
> > +};
> >  
> >  /**
> > - * struct counter_signal_ext - Counter Signal extensions
> > - * @name:	attribute name
> > - * @read:	read callback for this attribute; may be NULL
> > - * @write:	write callback for this attribute; may be NULL
> > - * @priv:	data private to the driver
> > + * struct counter_comp - Counter component node
> > + * @type:		Counter component data type
> > + * @name:		device-specific component name
> > + * @priv:		component-relevant data
> > + * @action_read		Synapse action mode read callback. The read value of the
> > + *			respective Synapse action mode should be passed back via
> > + *			the action parameter.
> > + * @device_u8_read	Device u8 component read callback. The read value of the
> > + *			respective Device u8 component should be passed back via
> > + *			the val parameter.
> > + * @count_u8_read	Count u8 component read callback. The read value of the
> > + *			respective Count u8 component should be passed back via
> > + *			the val parameter.
> > + * @signal_u8_read	Signal u8 component read callback. The read value of the
> > + *			respective Signal u8 component should be passed back via
> > + *			the val parameter.
> > + * @device_u32_read	Device u32 component read callback. The read value of
> > + *			the respective Device u32 component should be passed
> > + *			back via the val parameter.
> > + * @count_u32_read	Count u32 component read callback. The read value of the
> > + *			respective Count u32 component should be passed back via
> > + *			the val parameter.
> > + * @signal_u32_read	Signal u32 component read callback. The read value of
> > + *			the respective Signal u32 component should be passed
> > + *			back via the val parameter.
> > + * @device_u64_read	Device u64 component read callback. The read value of
> > + *			the respective Device u64 component should be passed
> > + *			back via the val parameter.
> > + * @count_u64_read	Count u64 component read callback. The read value of the
> > + *			respective Count u64 component should be passed back via
> > + *			the val parameter.
> > + * @signal_u64_read	Signal u64 component read callback. The read value of
> > + *			the respective Signal u64 component should be passed
> > + *			back via the val parameter.
> > + * @action_write	Synapse action mode write callback. The write value of
> > + *			the respective Synapse action mode is passed via the
> > + *			action parameter.
> > + * @device_u8_write	Device u8 component write callback. The write value of
> > + *			the respective Device u8 component is passed via the val
> > + *			parameter.
> > + * @count_u8_write	Count u8 component write callback. The write value of
> > + *			the respective Count u8 component is passed via the val
> > + *			parameter.
> > + * @signal_u8_write	Signal u8 component write callback. The write value of
> > + *			the respective Signal u8 component is passed via the val
> > + *			parameter.
> > + * @device_u32_write	Device u32 component write callback. The write value of
> > + *			the respective Device u32 component is passed via the
> > + *			val parameter.
> > + * @count_u32_write	Count u32 component write callback. The write value of
> > + *			the respective Count u32 component is passed via the val
> > + *			parameter.
> > + * @signal_u32_write	Signal u32 component write callback. The write value of
> > + *			the respective Signal u32 component is passed via the
> > + *			val parameter.
> > + * @device_u64_write	Device u64 component write callback. The write value of
> > + *			the respective Device u64 component is passed via the
> > + *			val parameter.
> > + * @count_u64_write	Count u64 component write callback. The write value of
> > + *			the respective Count u64 component is passed via the val
> > + *			parameter.
> > + * @signal_u64_write	Signal u64 component write callback. The write value of
> > + *			the respective Signal u64 component is passed via the
> > + *			val parameter.
> >   */
> > -struct counter_signal_ext {
> > +struct counter_comp {
> > +	enum counter_comp_type type;
> >  	const char *name;
> > -	ssize_t (*read)(struct counter_device *counter,
> > -			struct counter_signal *signal, void *priv, char *buf);
> > -	ssize_t (*write)(struct counter_device *counter,
> > -			 struct counter_signal *signal, void *priv,
> > -			 const char *buf, size_t len);
> >  	void *priv;
> > +	union {
> > +		int (*action_read)(struct counter_device *counter,
> > +				   struct counter_count *count,
> > +				   struct counter_synapse *synapse,
> > +				   enum counter_synapse_action *action);
> > +		int (*device_u8_read)(struct counter_device *counter, u8 *val);
> > +		int (*count_u8_read)(struct counter_device *counter,
> > +				     struct counter_count *count, u8 *val);
> > +		int (*signal_u8_read)(struct counter_device *counter,
> > +				      struct counter_signal *signal, u8 *val);
> > +		int (*device_u32_read)(struct counter_device *counter,
> > +				       u32 *val);
> > +		int (*count_u32_read)(struct counter_device *counter,
> > +				      struct counter_count *count, u32 *val);
> > +		int (*signal_u32_read)(struct counter_device *counter,
> > +				       struct counter_signal *signal, u32 *val);
> > +		int (*device_u64_read)(struct counter_device *counter,
> > +				       u64 *val);
> > +		int (*count_u64_read)(struct counter_device *counter,
> > +				      struct counter_count *count, u64 *val);
> > +		int (*signal_u64_read)(struct counter_device *counter,
> > +				       struct counter_signal *signal, u64 *val);
> > +	};
> > +	union {
> > +		int (*action_write)(struct counter_device *counter,
> > +				    struct counter_count *count,
> > +				    struct counter_synapse *synapse,
> > +				    enum counter_synapse_action action);
> > +		int (*device_u8_write)(struct counter_device *counter, u8 val);
> > +		int (*count_u8_write)(struct counter_device *counter,
> > +				      struct counter_count *count, u8 val);
> > +		int (*signal_u8_write)(struct counter_device *counter,
> > +				       struct counter_signal *signal, u8 val);
> > +		int (*device_u32_write)(struct counter_device *counter,
> > +					u32 val);
> > +		int (*count_u32_write)(struct counter_device *counter,
> > +				       struct counter_count *count, u32 val);
> > +		int (*signal_u32_write)(struct counter_device *counter,
> > +					struct counter_signal *signal, u32 val);
> > +		int (*device_u64_write)(struct counter_device *counter,
> > +					u64 val);
> > +		int (*count_u64_write)(struct counter_device *counter,
> > +				       struct counter_count *count, u64 val);
> > +		int (*signal_u64_write)(struct counter_device *counter,
> > +					struct counter_signal *signal, u64 val);
> > +	};
> >  };
> >  
> >  /**
> > @@ -51,248 +193,52 @@ struct counter_signal_ext {
> >   *		as it appears in the datasheet documentation
> >   * @ext:	optional array of Counter Signal extensions
> >   * @num_ext:	number of Counter Signal extensions specified in @ext
> > - * @priv:	optional private data supplied by driver
> >   */
> >  struct counter_signal {
> >  	int id;
> >  	const char *name;
> >  
> > -	const struct counter_signal_ext *ext;
> > +	struct counter_comp *ext;
> >  	size_t num_ext;
> > -
> > -	void *priv;
> > -};
> > -
> > -/**
> > - * struct counter_signal_enum_ext - Signal enum extension attribute
> > - * @items:	Array of strings
> > - * @num_items:	Number of items specified in @items
> > - * @set:	Set callback function; may be NULL
> > - * @get:	Get callback function; may be NULL
> > - *
> > - * The counter_signal_enum_ext structure can be used to implement enum style
> > - * Signal extension attributes. Enum style attributes are those which have a set
> > - * of strings that map to unsigned integer values. The Generic Counter Signal
> > - * enum extension helper code takes care of mapping between value and string, as
> > - * well as generating a "_available" file which contains a list of all available
> > - * items. The get callback is used to query the currently active item; the index
> > - * of the item within the respective items array is returned via the 'item'
> > - * parameter. The set callback is called when the attribute is updated; the
> > - * 'item' parameter contains the index of the newly activated item within the
> > - * respective items array.
> > - */
> > -struct counter_signal_enum_ext {
> > -	const char * const *items;
> > -	size_t num_items;
> > -	int (*get)(struct counter_device *counter,
> > -		   struct counter_signal *signal, size_t *item);
> > -	int (*set)(struct counter_device *counter,
> > -		   struct counter_signal *signal, size_t item);
> > -};
> > -
> > -/**
> > - * COUNTER_SIGNAL_ENUM() - Initialize Signal enum extension
> > - * @_name:	Attribute name
> > - * @_e:		Pointer to a counter_signal_enum_ext structure
> > - *
> > - * This should usually be used together with COUNTER_SIGNAL_ENUM_AVAILABLE()
> > - */
> > -#define COUNTER_SIGNAL_ENUM(_name, _e) \
> > -{ \
> > -	.name = (_name), \
> > -	.read = counter_signal_enum_read, \
> > -	.write = counter_signal_enum_write, \
> > -	.priv = (_e) \
> > -}
> > -
> > -/**
> > - * COUNTER_SIGNAL_ENUM_AVAILABLE() - Initialize Signal enum available extension
> > - * @_name:	Attribute name ("_available" will be appended to the name)
> > - * @_e:		Pointer to a counter_signal_enum_ext structure
> > - *
> > - * Creates a read only attribute that lists all the available enum items in a
> > - * newline separated list. This should usually be used together with
> > - * COUNTER_SIGNAL_ENUM()
> > - */
> > -#define COUNTER_SIGNAL_ENUM_AVAILABLE(_name, _e) \
> > -{ \
> > -	.name = (_name "_available"), \
> > -	.read = counter_signal_enum_available_read, \
> > -	.priv = (_e) \
> > -}
> > -
> > -enum counter_synapse_action {
> > -	COUNTER_SYNAPSE_ACTION_NONE = 0,
> > -	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> > -	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
> > -	COUNTER_SYNAPSE_ACTION_BOTH_EDGES
> >  };
> >  
> >  /**
> >   * struct counter_synapse - Counter Synapse node
> > - * @action:		index of current action mode
> >   * @actions_list:	array of available action modes
> >   * @num_actions:	number of action modes specified in @actions_list
> >   * @signal:		pointer to associated signal
> >   */
> >  struct counter_synapse {
> > -	size_t action;
> >  	const enum counter_synapse_action *actions_list;
> >  	size_t num_actions;
> >  
> >  	struct counter_signal *signal;
> >  };
> >  
> > -struct counter_count;
> > -
> > -/**
> > - * struct counter_count_ext - Counter Count extension
> > - * @name:	attribute name
> > - * @read:	read callback for this attribute; may be NULL
> > - * @write:	write callback for this attribute; may be NULL
> > - * @priv:	data private to the driver
> > - */
> > -struct counter_count_ext {
> > -	const char *name;
> > -	ssize_t (*read)(struct counter_device *counter,
> > -			struct counter_count *count, void *priv, char *buf);
> > -	ssize_t (*write)(struct counter_device *counter,
> > -			 struct counter_count *count, void *priv,
> > -			 const char *buf, size_t len);
> > -	void *priv;
> > -};
> > -
> > -enum counter_function {
> > -	COUNTER_FUNCTION_INCREASE = 0,
> > -	COUNTER_FUNCTION_DECREASE,
> > -	COUNTER_FUNCTION_PULSE_DIRECTION,
> > -	COUNTER_FUNCTION_QUADRATURE_X1_A,
> > -	COUNTER_FUNCTION_QUADRATURE_X1_B,
> > -	COUNTER_FUNCTION_QUADRATURE_X2_A,
> > -	COUNTER_FUNCTION_QUADRATURE_X2_B,
> > -	COUNTER_FUNCTION_QUADRATURE_X4
> > -};
> > -
> >  /**
> >   * struct counter_count - Counter Count node
> >   * @id:			unique ID used to identify Count
> >   * @name:		device-specific Count name; ideally, this should match
> >   *			the name as it appears in the datasheet documentation
> > - * @function:		index of current function mode
> >   * @functions_list:	array available function modes
> >   * @num_functions:	number of function modes specified in @functions_list
> >   * @synapses:		array of synapses for initialization
> >   * @num_synapses:	number of synapses specified in @synapses
> >   * @ext:		optional array of Counter Count extensions
> >   * @num_ext:		number of Counter Count extensions specified in @ext
> > - * @priv:		optional private data supplied by driver
> >   */
> >  struct counter_count {
> >  	int id;
> >  	const char *name;
> >  
> > -	size_t function;
> >  	const enum counter_function *functions_list;
> >  	size_t num_functions;
> >  
> >  	struct counter_synapse *synapses;
> >  	size_t num_synapses;
> >  
> > -	const struct counter_count_ext *ext;
> > +	struct counter_comp *ext;
> >  	size_t num_ext;
> > -
> > -	void *priv;
> > -};
> > -
> > -/**
> > - * struct counter_count_enum_ext - Count enum extension attribute
> > - * @items:	Array of strings
> > - * @num_items:	Number of items specified in @items
> > - * @set:	Set callback function; may be NULL
> > - * @get:	Get callback function; may be NULL
> > - *
> > - * The counter_count_enum_ext structure can be used to implement enum style
> > - * Count extension attributes. Enum style attributes are those which have a set
> > - * of strings that map to unsigned integer values. The Generic Counter Count
> > - * enum extension helper code takes care of mapping between value and string, as
> > - * well as generating a "_available" file which contains a list of all available
> > - * items. The get callback is used to query the currently active item; the index
> > - * of the item within the respective items array is returned via the 'item'
> > - * parameter. The set callback is called when the attribute is updated; the
> > - * 'item' parameter contains the index of the newly activated item within the
> > - * respective items array.
> > - */
> > -struct counter_count_enum_ext {
> > -	const char * const *items;
> > -	size_t num_items;
> > -	int (*get)(struct counter_device *counter, struct counter_count *count,
> > -		   size_t *item);
> > -	int (*set)(struct counter_device *counter, struct counter_count *count,
> > -		   size_t item);
> > -};
> > -
> > -/**
> > - * COUNTER_COUNT_ENUM() - Initialize Count enum extension
> > - * @_name:	Attribute name
> > - * @_e:		Pointer to a counter_count_enum_ext structure
> > - *
> > - * This should usually be used together with COUNTER_COUNT_ENUM_AVAILABLE()
> > - */
> > -#define COUNTER_COUNT_ENUM(_name, _e) \
> > -{ \
> > -	.name = (_name), \
> > -	.read = counter_count_enum_read, \
> > -	.write = counter_count_enum_write, \
> > -	.priv = (_e) \
> > -}
> > -
> > -/**
> > - * COUNTER_COUNT_ENUM_AVAILABLE() - Initialize Count enum available extension
> > - * @_name:	Attribute name ("_available" will be appended to the name)
> > - * @_e:		Pointer to a counter_count_enum_ext structure
> > - *
> > - * Creates a read only attribute that lists all the available enum items in a
> > - * newline separated list. This should usually be used together with
> > - * COUNTER_COUNT_ENUM()
> > - */
> > -#define COUNTER_COUNT_ENUM_AVAILABLE(_name, _e) \
> > -{ \
> > -	.name = (_name "_available"), \
> > -	.read = counter_count_enum_available_read, \
> > -	.priv = (_e) \
> > -}
> > -
> > -/**
> > - * struct counter_device_attr_group - internal container for attribute group
> > - * @attr_group:	Counter sysfs attributes group
> > - * @attr_list:	list to keep track of created Counter sysfs attributes
> > - * @num_attr:	number of Counter sysfs attributes
> > - */
> > -struct counter_device_attr_group {
> > -	struct attribute_group attr_group;
> > -	struct list_head attr_list;
> > -	size_t num_attr;
> > -};
> > -
> > -/**
> > - * struct counter_device_state - internal state container for a Counter device
> > - * @id:			unique ID used to identify the Counter
> > - * @dev:		internal device structure
> > - * @groups_list:	attribute groups list (for Signals, Counts, and ext)
> > - * @num_groups:		number of attribute groups containers
> > - * @groups:		Counter sysfs attribute groups (to populate @dev.groups)
> > - */
> > -struct counter_device_state {
> > -	int id;
> > -	struct device dev;
> > -	struct counter_device_attr_group *groups_list;
> > -	size_t num_groups;
> > -	const struct attribute_group **groups;
> > -};
> > -
> > -enum counter_signal_level {
> > -	COUNTER_SIGNAL_LEVEL_LOW,
> > -	COUNTER_SIGNAL_LEVEL_HIGH,
> >  };
> >  
> >  /**
> > @@ -306,117 +252,47 @@ enum counter_signal_level {
> >   * @count_write:	optional write callback for Count attribute. The write
> >   *			value for the respective Count is passed in via the val
> >   *			parameter.
> > - * @function_get:	function to get the current count function mode. Returns
> > - *			0 on success and negative error code on error. The index
> > - *			of the respective Count's returned function mode should
> > - *			be passed back via the function parameter.
> > - * @function_set:	function to set the count function mode. function is the
> > - *			index of the requested function mode from the respective
> > - *			Count's functions_list array.
> > - * @action_get:		function to get the current action mode. Returns 0 on
> > - *			success and negative error code on error. The index of
> > - *			the respective Synapse's returned action mode should be
> > - *			passed back via the action parameter.
> > - * @action_set:		function to set the action mode. action is the index of
> > - *			the requested action mode from the respective Synapse's
> > - *			actions_list array.
> > + * @function_read:	read callback the Count function modes. The read
> > + *			function mode of the respective Count should be passed
> > + *			back via the function parameter.
> > + * @function_write:	write callback for Count function modes. The function
> > + *			mode to write for the respective Count is passed in via
> > + *			the function parameter.
> > + * @action_read:	read callback the Synapse action modes. The read action
> > + *			mode of the respective Synapse should be passed back via
> > + *			the action parameter.
> > + * @action_write:	write callback for Synapse action modes. The action mode
> > + *			to write for the respective Synapse is passed in via the
> > + *			action parameter.
> >   */
> >  struct counter_ops {
> >  	int (*signal_read)(struct counter_device *counter,
> >  			   struct counter_signal *signal,
> >  			   enum counter_signal_level *level);
> >  	int (*count_read)(struct counter_device *counter,
> > -			  struct counter_count *count, unsigned long *val);
> > +			  struct counter_count *count, u64 *value);
> >  	int (*count_write)(struct counter_device *counter,
> > -			   struct counter_count *count, unsigned long val);
> > -	int (*function_get)(struct counter_device *counter,
> > -			    struct counter_count *count, size_t *function);
> > -	int (*function_set)(struct counter_device *counter,
> > -			    struct counter_count *count, size_t function);
> > -	int (*action_get)(struct counter_device *counter,
> > -			  struct counter_count *count,
> > -			  struct counter_synapse *synapse, size_t *action);
> > -	int (*action_set)(struct counter_device *counter,
> > -			  struct counter_count *count,
> > -			  struct counter_synapse *synapse, size_t action);
> > -};
> > -
> > -/**
> > - * struct counter_device_ext - Counter device extension
> > - * @name:	attribute name
> > - * @read:	read callback for this attribute; may be NULL
> > - * @write:	write callback for this attribute; may be NULL
> > - * @priv:	data private to the driver
> > - */
> > -struct counter_device_ext {
> > -	const char *name;
> > -	ssize_t (*read)(struct counter_device *counter, void *priv, char *buf);
> > -	ssize_t (*write)(struct counter_device *counter, void *priv,
> > -			 const char *buf, size_t len);
> > -	void *priv;
> > +			   struct counter_count *count, u64 value);
> > +	int (*function_read)(struct counter_device *counter,
> > +			     struct counter_count *count,
> > +			     enum counter_function *function);
> > +	int (*function_write)(struct counter_device *counter,
> > +			      struct counter_count *count,
> > +			      enum counter_function function);
> > +	int (*action_read)(struct counter_device *counter,
> > +			   struct counter_count *count,
> > +			   struct counter_synapse *synapse,
> > +			   enum counter_synapse_action *action);
> > +	int (*action_write)(struct counter_device *counter,
> > +			    struct counter_count *count,
> > +			    struct counter_synapse *synapse,
> > +			    enum counter_synapse_action action);
> >  };
> >  
> > -/**
> > - * struct counter_device_enum_ext - Counter enum extension attribute
> > - * @items:	Array of strings
> > - * @num_items:	Number of items specified in @items
> > - * @set:	Set callback function; may be NULL
> > - * @get:	Get callback function; may be NULL
> > - *
> > - * The counter_device_enum_ext structure can be used to implement enum style
> > - * Counter extension attributes. Enum style attributes are those which have a
> > - * set of strings that map to unsigned integer values. The Generic Counter enum
> > - * extension helper code takes care of mapping between value and string, as well
> > - * as generating a "_available" file which contains a list of all available
> > - * items. The get callback is used to query the currently active item; the index
> > - * of the item within the respective items array is returned via the 'item'
> > - * parameter. The set callback is called when the attribute is updated; the
> > - * 'item' parameter contains the index of the newly activated item within the
> > - * respective items array.
> > - */
> > -struct counter_device_enum_ext {
> > -	const char * const *items;
> > -	size_t num_items;
> > -	int (*get)(struct counter_device *counter, size_t *item);
> > -	int (*set)(struct counter_device *counter, size_t item);
> > -};
> > -
> > -/**
> > - * COUNTER_DEVICE_ENUM() - Initialize Counter enum extension
> > - * @_name:	Attribute name
> > - * @_e:		Pointer to a counter_device_enum_ext structure
> > - *
> > - * This should usually be used together with COUNTER_DEVICE_ENUM_AVAILABLE()
> > - */
> > -#define COUNTER_DEVICE_ENUM(_name, _e) \
> > -{ \
> > -	.name = (_name), \
> > -	.read = counter_device_enum_read, \
> > -	.write = counter_device_enum_write, \
> > -	.priv = (_e) \
> > -}
> > -
> > -/**
> > - * COUNTER_DEVICE_ENUM_AVAILABLE() - Initialize Counter enum available extension
> > - * @_name:	Attribute name ("_available" will be appended to the name)
> > - * @_e:		Pointer to a counter_device_enum_ext structure
> > - *
> > - * Creates a read only attribute that lists all the available enum items in a
> > - * newline separated list. This should usually be used together with
> > - * COUNTER_DEVICE_ENUM()
> > - */
> > -#define COUNTER_DEVICE_ENUM_AVAILABLE(_name, _e) \
> > -{ \
> > -	.name = (_name "_available"), \
> > -	.read = counter_device_enum_available_read, \
> > -	.priv = (_e) \
> > -}
> > -
> >  /**
> >   * struct counter_device - Counter data structure
> >   * @name:		name of the device as it appears in the datasheet
> >   * @parent:		optional parent device providing the counters
> > - * @device_state:	internal device state container
> >   * @ops:		callbacks from driver
> >   * @signals:		array of Signals
> >   * @num_signals:	number of Signals specified in @signals
> > @@ -425,11 +301,11 @@ struct counter_device_enum_ext {
> >   * @ext:		optional array of Counter device extensions
> >   * @num_ext:		number of Counter device extensions specified in @ext
> >   * @priv:		optional private data supplied by driver
> > + * @dev:		internal device structure
> >   */
> >  struct counter_device {
> >  	const char *name;
> >  	struct device *parent;
> > -	struct counter_device_state *device_state;
> >  
> >  	const struct counter_ops *ops;
> >  
> > @@ -438,17 +314,159 @@ struct counter_device {
> >  	struct counter_count *counts;
> >  	size_t num_counts;
> >  
> > -	const struct counter_device_ext *ext;
> > +	struct counter_comp *ext;
> >  	size_t num_ext;
> >  
> >  	void *priv;
> > +
> > +	struct device dev;
> >  };
> >  
> >  int counter_register(struct counter_device *const counter);
> >  void counter_unregister(struct counter_device *const counter);
> >  int devm_counter_register(struct device *dev,
> >  			  struct counter_device *const counter);
> > -void devm_counter_unregister(struct device *dev,
> > -			     struct counter_device *const counter);
> > +
> > +#define COUNTER_COMP_DEVICE_U8(_name, _read, _write) \
> > +{ \
> > +	.type = COUNTER_COMP_U8, \
> > +	.name = (_name), \
> > +	.device_u8_read = (_read), \
> > +	.device_u8_write = (_write), \
> > +}
> > +#define COUNTER_COMP_COUNT_U8(_name, _read, _write) \
> > +{ \
> > +	.type = COUNTER_COMP_U8, \
> > +	.name = (_name), \
> > +	.count_u8_read = (_read), \
> > +	.count_u8_write = (_write), \
> > +}
> > +#define COUNTER_COMP_SIGNAL_U8(_name, _read, _write) \
> > +{ \
> > +	.type = COUNTER_COMP_U8, \
> > +	.name = (_name), \
> > +	.signal_u8_read = (_read), \
> > +	.signal_u8_write = (_write), \
> > +}
> > +
> > +#define COUNTER_COMP_DEVICE_U64(_name, _read, _write) \
> > +{ \
> > +	.type = COUNTER_COMP_U64, \
> > +	.name = (_name), \
> > +	.device_u64_read = (_read), \
> > +	.device_u64_write = (_write), \
> > +}
> > +#define COUNTER_COMP_COUNT_U64(_name, _read, _write) \
> > +{ \
> > +	.type = COUNTER_COMP_U64, \
> > +	.name = (_name), \
> > +	.count_u64_read = (_read), \
> > +	.count_u64_write = (_write), \
> > +}
> > +#define COUNTER_COMP_SIGNAL_U64(_name, _read, _write) \
> > +{ \
> > +	.type = COUNTER_COMP_U64, \
> > +	.name = (_name), \
> > +	.signal_u64_read = (_read), \
> > +	.signal_u64_write = (_write), \
> > +}
> > +
> > +#define COUNTER_COMP_DEVICE_BOOL(_name, _read, _write) \
> > +{ \
> > +	.type = COUNTER_COMP_BOOL, \
> > +	.name = (_name), \
> > +	.device_u8_read = (_read), \
> > +	.device_u8_write = (_write), \
> > +}
> > +#define COUNTER_COMP_COUNT_BOOL(_name, _read, _write) \
> > +{ \
> > +	.type = COUNTER_COMP_BOOL, \
> > +	.name = (_name), \
> > +	.count_u8_read = (_read), \
> > +	.count_u8_write = (_write), \
> > +}
> > +#define COUNTER_COMP_SIGNAL_BOOL(_name, _read, _write) \
> > +{ \
> > +	.type = COUNTER_COMP_BOOL, \
> > +	.name = (_name), \
> > +	.signal_u8_read = (_read), \
> > +	.signal_u8_write = (_write), \
> > +}
> > +
> > +struct counter_available {
> > +	union {
> > +		const u32 *enums;
> > +		const char *const *strs;
> > +	};
> > +	size_t num_items;
> > +};
> > +
> > +#define DEFINE_COUNTER_AVAILABLE(_name, _enums) \
> > +	struct counter_available _name = { \
> > +		.enums = (_enums), \
> > +		.num_items = ARRAY_SIZE(_enums), \
> > +	}
> > +
> > +#define DEFINE_COUNTER_ENUM(_name, _strs) \
> > +	struct counter_available _name = { \
> > +		.strs = (_strs), \
> > +		.num_items = ARRAY_SIZE(_strs), \
> > +	}
> > +
> > +#define COUNTER_COMP_DEVICE_ENUM(_name, _get, _set, _available) \
> > +{ \
> > +	.type = COUNTER_COMP_ENUM, \
> > +	.name = (_name), \
> > +	.device_u32_read = (_get), \
> > +	.device_u32_write = (_set), \
> > +	.priv = &(_available), \
> > +}
> > +#define COUNTER_COMP_COUNT_ENUM(_name, _get, _set, _available) \
> > +{ \
> > +	.type = COUNTER_COMP_ENUM, \
> > +	.name = (_name), \
> > +	.count_u32_read = (_get), \
> > +	.count_u32_write = (_set), \
> > +	.priv = &(_available), \
> > +}
> > +#define COUNTER_COMP_SIGNAL_ENUM(_name, _get, _set, _available) \
> > +{ \
> > +	.type = COUNTER_COMP_ENUM, \
> > +	.name = (_name), \
> > +	.signal_u32_read = (_get), \
> > +	.signal_u32_write = (_set), \
> > +	.priv = &(_available), \
> > +}
> > +
> > +#define COUNTER_COMP_CEILING(_read, _write) \
> > +	COUNTER_COMP_COUNT_U64("ceiling", _read, _write)
> > +
> > +#define COUNTER_COMP_COUNT_MODE(_read, _write, _available) \
> > +{ \
> > +	.type = COUNTER_COMP_COUNT_MODE, \
> > +	.name = "count_mode", \
> > +	.count_u32_read = (_read), \
> > +	.count_u32_write = (_write), \
> > +	.priv = &(_available), \
> > +}
> > +
> > +#define COUNTER_COMP_DIRECTION(_read) \
> > +{ \
> > +	.type = COUNTER_COMP_COUNT_DIRECTION, \
> > +	.name = "direction", \
> > +	.count_u32_read = (_read), \
> > +}
> > +
> > +#define COUNTER_COMP_ENABLE(_read, _write) \
> > +	COUNTER_COMP_COUNT_BOOL("enable", _read, _write)
> > +
> > +#define COUNTER_COMP_FLOOR(_read, _write) \
> > +	COUNTER_COMP_COUNT_U64("floor", _read, _write)
> > +
> > +#define COUNTER_COMP_PRESET(_read, _write) \
> > +	COUNTER_COMP_COUNT_U64("preset", _read, _write)
> > +
> > +#define COUNTER_COMP_PRESET_ENABLE(_read, _write) \
> > +	COUNTER_COMP_COUNT_BOOL("preset_enable", _read, _write)
> >  
> >  #endif /* _COUNTER_H_ */
> > diff --git a/include/linux/counter_enum.h b/include/linux/counter_enum.h
> > deleted file mode 100644
> > index 9f917298a88f..000000000000
> > --- a/include/linux/counter_enum.h
> > +++ /dev/null
> > @@ -1,45 +0,0 @@
> > -/* SPDX-License-Identifier: GPL-2.0 */
> > -/*
> > - * Counter interface enum functions
> > - * Copyright (C) 2018 William Breathitt Gray
> > - */
> > -#ifndef _COUNTER_ENUM_H_
> > -#define _COUNTER_ENUM_H_
> > -
> > -#include <linux/types.h>
> > -
> > -struct counter_device;
> > -struct counter_signal;
> > -struct counter_count;
> > -
> > -ssize_t counter_signal_enum_read(struct counter_device *counter,
> > -				 struct counter_signal *signal, void *priv,
> > -				 char *buf);
> > -ssize_t counter_signal_enum_write(struct counter_device *counter,
> > -				  struct counter_signal *signal, void *priv,
> > -				  const char *buf, size_t len);
> > -
> > -ssize_t counter_signal_enum_available_read(struct counter_device *counter,
> > -					   struct counter_signal *signal,
> > -					   void *priv, char *buf);
> > -
> > -ssize_t counter_count_enum_read(struct counter_device *counter,
> > -				struct counter_count *count, void *priv,
> > -				char *buf);
> > -ssize_t counter_count_enum_write(struct counter_device *counter,
> > -				 struct counter_count *count, void *priv,
> > -				 const char *buf, size_t len);
> > -
> > -ssize_t counter_count_enum_available_read(struct counter_device *counter,
> > -					  struct counter_count *count,
> > -					  void *priv, char *buf);
> > -
> > -ssize_t counter_device_enum_read(struct counter_device *counter, void *priv,
> > -				 char *buf);
> > -ssize_t counter_device_enum_write(struct counter_device *counter, void *priv,
> > -				  const char *buf, size_t len);
> > -
> > -ssize_t counter_device_enum_available_read(struct counter_device *counter,
> > -					   void *priv, char *buf);
> > -
> > -#endif /* _COUNTER_ENUM_H_ */
> >   


^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 04/14] counter: Update counter.h comments to reflect sysfs internalization
  2021-08-27  3:47   ` William Breathitt Gray
@ 2021-09-08 17:46     ` Jonathan Cameron
  -1 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-08 17:46 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula

On Fri, 27 Aug 2021 12:47:48 +0900
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> The Counter subsystem architecture and driver implementations have
> changed in order to handle Counter sysfs interactions in a more
> consistent way. This patch updates the Generic Counter interface
> header file comments to reflect the changes.
> 
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>f
Applied

> ---
>  drivers/counter/counter-core.c |  3 ++
>  include/linux/counter.h        | 62 ++++++++++++++++------------------
>  2 files changed, 33 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
> index c533a6ff12cf..3cda2c47bacb 100644
> --- a/drivers/counter/counter-core.c
> +++ b/drivers/counter/counter-core.c
> @@ -38,6 +38,9 @@ static struct bus_type counter_bus_type = {
>   * This function registers a Counter to the system. A sysfs "counter" directory
>   * will be created and populated with sysfs attributes correlating with the
>   * Counter Signals, Synapses, and Counts respectively.
> + *
> + * RETURNS:
> + * 0 on success, negative error number on failure.
>   */
>  int counter_register(struct counter_device *const counter)
>  {
> diff --git a/include/linux/counter.h b/include/linux/counter.h
> index b69277f5c4c5..445f22d8bfe2 100644
> --- a/include/linux/counter.h
> +++ b/include/linux/counter.h
> @@ -188,11 +188,10 @@ struct counter_comp {
>  
>  /**
>   * struct counter_signal - Counter Signal node
> - * @id:		unique ID used to identify signal
> - * @name:	device-specific Signal name; ideally, this should match the name
> - *		as it appears in the datasheet documentation
> - * @ext:	optional array of Counter Signal extensions
> - * @num_ext:	number of Counter Signal extensions specified in @ext
> + * @id:		unique ID used to identify the Signal
> + * @name:	device-specific Signal name
> + * @ext:	optional array of Signal extensions
> + * @num_ext:	number of Signal extensions specified in @ext
>   */
>  struct counter_signal {
>  	int id;
> @@ -206,7 +205,7 @@ struct counter_signal {
>   * struct counter_synapse - Counter Synapse node
>   * @actions_list:	array of available action modes
>   * @num_actions:	number of action modes specified in @actions_list
> - * @signal:		pointer to associated signal
> + * @signal:		pointer to the associated Signal
>   */
>  struct counter_synapse {
>  	const enum counter_synapse_action *actions_list;
> @@ -217,15 +216,14 @@ struct counter_synapse {
>  
>  /**
>   * struct counter_count - Counter Count node
> - * @id:			unique ID used to identify Count
> - * @name:		device-specific Count name; ideally, this should match
> - *			the name as it appears in the datasheet documentation
> - * @functions_list:	array available function modes
> + * @id:			unique ID used to identify the Count
> + * @name:		device-specific Count name
> + * @functions_list:	array of available function modes
>   * @num_functions:	number of function modes specified in @functions_list
> - * @synapses:		array of synapses for initialization
> - * @num_synapses:	number of synapses specified in @synapses
> - * @ext:		optional array of Counter Count extensions
> - * @num_ext:		number of Counter Count extensions specified in @ext
> + * @synapses:		array of Synapses for initialization
> + * @num_synapses:	number of Synapses specified in @synapses
> + * @ext:		optional array of Count extensions
> + * @num_ext:		number of Count extensions specified in @ext
>   */
>  struct counter_count {
>  	int id;
> @@ -243,27 +241,27 @@ struct counter_count {
>  
>  /**
>   * struct counter_ops - Callbacks from driver
> - * @signal_read:	optional read callback for Signal attribute. The read
> - *			level of the respective Signal should be passed back via
> - *			the level parameter.
> - * @count_read:		optional read callback for Count attribute. The read
> - *			value of the respective Count should be passed back via
> - *			the val parameter.
> - * @count_write:	optional write callback for Count attribute. The write
> - *			value for the respective Count is passed in via the val
> + * @signal_read:	optional read callback for Signals. The read level of
> + *			the respective Signal should be passed back via the
> + *			level parameter.
> + * @count_read:		read callback for Counts. The read value of the
> + *			respective Count should be passed back via the value
> + *			parameter.
> + * @count_write:	optional write callback for Counts. The write value for
> + *			the respective Count is passed in via the value
>   *			parameter.
>   * @function_read:	read callback the Count function modes. The read
>   *			function mode of the respective Count should be passed
>   *			back via the function parameter.
> - * @function_write:	write callback for Count function modes. The function
> - *			mode to write for the respective Count is passed in via
> - *			the function parameter.
> - * @action_read:	read callback the Synapse action modes. The read action
> - *			mode of the respective Synapse should be passed back via
> - *			the action parameter.
> - * @action_write:	write callback for Synapse action modes. The action mode
> - *			to write for the respective Synapse is passed in via the
> - *			action parameter.
> + * @function_write:	optional write callback for Count function modes. The
> + *			function mode to write for the respective Count is
> + *			passed in via the function parameter.
> + * @action_read:	optional read callback the Synapse action modes. The
> + *			read action mode of the respective Synapse should be
> + *			passed back via the action parameter.
> + * @action_write:	optional write callback for Synapse action modes. The
> + *			action mode to write for the respective Synapse is
> + *			passed in via the action parameter.
>   */
>  struct counter_ops {
>  	int (*signal_read)(struct counter_device *counter,
> @@ -291,7 +289,7 @@ struct counter_ops {
>  
>  /**
>   * struct counter_device - Counter data structure
> - * @name:		name of the device as it appears in the datasheet
> + * @name:		name of the device
>   * @parent:		optional parent device providing the counters
>   * @ops:		callbacks from driver
>   * @signals:		array of Signals


^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 04/14] counter: Update counter.h comments to reflect sysfs internalization
@ 2021-09-08 17:46     ` Jonathan Cameron
  0 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-08 17:46 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula

On Fri, 27 Aug 2021 12:47:48 +0900
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> The Counter subsystem architecture and driver implementations have
> changed in order to handle Counter sysfs interactions in a more
> consistent way. This patch updates the Generic Counter interface
> header file comments to reflect the changes.
> 
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>f
Applied

> ---
>  drivers/counter/counter-core.c |  3 ++
>  include/linux/counter.h        | 62 ++++++++++++++++------------------
>  2 files changed, 33 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
> index c533a6ff12cf..3cda2c47bacb 100644
> --- a/drivers/counter/counter-core.c
> +++ b/drivers/counter/counter-core.c
> @@ -38,6 +38,9 @@ static struct bus_type counter_bus_type = {
>   * This function registers a Counter to the system. A sysfs "counter" directory
>   * will be created and populated with sysfs attributes correlating with the
>   * Counter Signals, Synapses, and Counts respectively.
> + *
> + * RETURNS:
> + * 0 on success, negative error number on failure.
>   */
>  int counter_register(struct counter_device *const counter)
>  {
> diff --git a/include/linux/counter.h b/include/linux/counter.h
> index b69277f5c4c5..445f22d8bfe2 100644
> --- a/include/linux/counter.h
> +++ b/include/linux/counter.h
> @@ -188,11 +188,10 @@ struct counter_comp {
>  
>  /**
>   * struct counter_signal - Counter Signal node
> - * @id:		unique ID used to identify signal
> - * @name:	device-specific Signal name; ideally, this should match the name
> - *		as it appears in the datasheet documentation
> - * @ext:	optional array of Counter Signal extensions
> - * @num_ext:	number of Counter Signal extensions specified in @ext
> + * @id:		unique ID used to identify the Signal
> + * @name:	device-specific Signal name
> + * @ext:	optional array of Signal extensions
> + * @num_ext:	number of Signal extensions specified in @ext
>   */
>  struct counter_signal {
>  	int id;
> @@ -206,7 +205,7 @@ struct counter_signal {
>   * struct counter_synapse - Counter Synapse node
>   * @actions_list:	array of available action modes
>   * @num_actions:	number of action modes specified in @actions_list
> - * @signal:		pointer to associated signal
> + * @signal:		pointer to the associated Signal
>   */
>  struct counter_synapse {
>  	const enum counter_synapse_action *actions_list;
> @@ -217,15 +216,14 @@ struct counter_synapse {
>  
>  /**
>   * struct counter_count - Counter Count node
> - * @id:			unique ID used to identify Count
> - * @name:		device-specific Count name; ideally, this should match
> - *			the name as it appears in the datasheet documentation
> - * @functions_list:	array available function modes
> + * @id:			unique ID used to identify the Count
> + * @name:		device-specific Count name
> + * @functions_list:	array of available function modes
>   * @num_functions:	number of function modes specified in @functions_list
> - * @synapses:		array of synapses for initialization
> - * @num_synapses:	number of synapses specified in @synapses
> - * @ext:		optional array of Counter Count extensions
> - * @num_ext:		number of Counter Count extensions specified in @ext
> + * @synapses:		array of Synapses for initialization
> + * @num_synapses:	number of Synapses specified in @synapses
> + * @ext:		optional array of Count extensions
> + * @num_ext:		number of Count extensions specified in @ext
>   */
>  struct counter_count {
>  	int id;
> @@ -243,27 +241,27 @@ struct counter_count {
>  
>  /**
>   * struct counter_ops - Callbacks from driver
> - * @signal_read:	optional read callback for Signal attribute. The read
> - *			level of the respective Signal should be passed back via
> - *			the level parameter.
> - * @count_read:		optional read callback for Count attribute. The read
> - *			value of the respective Count should be passed back via
> - *			the val parameter.
> - * @count_write:	optional write callback for Count attribute. The write
> - *			value for the respective Count is passed in via the val
> + * @signal_read:	optional read callback for Signals. The read level of
> + *			the respective Signal should be passed back via the
> + *			level parameter.
> + * @count_read:		read callback for Counts. The read value of the
> + *			respective Count should be passed back via the value
> + *			parameter.
> + * @count_write:	optional write callback for Counts. The write value for
> + *			the respective Count is passed in via the value
>   *			parameter.
>   * @function_read:	read callback the Count function modes. The read
>   *			function mode of the respective Count should be passed
>   *			back via the function parameter.
> - * @function_write:	write callback for Count function modes. The function
> - *			mode to write for the respective Count is passed in via
> - *			the function parameter.
> - * @action_read:	read callback the Synapse action modes. The read action
> - *			mode of the respective Synapse should be passed back via
> - *			the action parameter.
> - * @action_write:	write callback for Synapse action modes. The action mode
> - *			to write for the respective Synapse is passed in via the
> - *			action parameter.
> + * @function_write:	optional write callback for Count function modes. The
> + *			function mode to write for the respective Count is
> + *			passed in via the function parameter.
> + * @action_read:	optional read callback the Synapse action modes. The
> + *			read action mode of the respective Synapse should be
> + *			passed back via the action parameter.
> + * @action_write:	optional write callback for Synapse action modes. The
> + *			action mode to write for the respective Synapse is
> + *			passed in via the action parameter.
>   */
>  struct counter_ops {
>  	int (*signal_read)(struct counter_device *counter,
> @@ -291,7 +289,7 @@ struct counter_ops {
>  
>  /**
>   * struct counter_device - Counter data structure
> - * @name:		name of the device as it appears in the datasheet
> + * @name:		name of the device
>   * @parent:		optional parent device providing the counters
>   * @ops:		callbacks from driver
>   * @signals:		array of Signals


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 05/14] docs: counter: Update to reflect sysfs internalization
  2021-08-27  3:47   ` William Breathitt Gray
@ 2021-09-08 17:48     ` Jonathan Cameron
  -1 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-08 17:48 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula

On Fri, 27 Aug 2021 12:47:49 +0900
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> The Counter subsystem architecture and driver implementations have
> changed in order to handle Counter sysfs interactions in a more
> consistent way. This patch updates the Generic Counter interface
> documentation to reflect the changes.
> 
> Reviewed-by: David Lechner <david@lechnology.com>
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
Applied.

I'm going to pause at this point.  We are early in the cycle anyway
so there is no rush and I'd like to take another look at the remainder
of the series + leave a little longer for others to take a look at it!

Thanks,

Jonathan

> ---
>  Documentation/ABI/testing/sysfs-bus-counter  |   9 +-
>  Documentation/driver-api/generic-counter.rst | 243 ++++++++++++++-----
>  2 files changed, 185 insertions(+), 67 deletions(-)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter
> index 20fe5afd4f9e..dee79b606847 100644
> --- a/Documentation/ABI/testing/sysfs-bus-counter
> +++ b/Documentation/ABI/testing/sysfs-bus-counter
> @@ -286,7 +286,14 @@ What:		/sys/bus/counter/devices/counterX/signalY/signal
>  KernelVersion:	5.2
>  Contact:	linux-iio@vger.kernel.org
>  Description:
> -		Signal data of Signal Y represented as a string.
> +		Signal level state of Signal Y. The following signal level
> +		states are available:
> +
> +		low:
> +			Low level state.
> +
> +		high:
> +			High level state.
>  
>  What:		/sys/bus/counter/devices/counterX/signalY/synchronous_mode
>  KernelVersion:	5.2
> diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst
> index 64fe7db080e5..f6397218aa4c 100644
> --- a/Documentation/driver-api/generic-counter.rst
> +++ b/Documentation/driver-api/generic-counter.rst
> @@ -250,8 +250,8 @@ for defining a counter device.
>  .. kernel-doc:: drivers/counter/counter.c
>     :export:
>  
> -Implementation
> -==============
> +Driver Implementation
> +=====================
>  
>  To support a counter device, a driver must first allocate the available
>  Counter Signals via counter_signal structures. These Signals should
> @@ -267,25 +267,61 @@ respective counter_count structure. These counter_count structures are
>  set to the counts array member of an allocated counter_device structure
>  before the Counter is registered to the system.
>  
> -Driver callbacks should be provided to the counter_device structure via
> -a constant counter_ops structure in order to communicate with the
> -device: to read and write various Signals and Counts, and to set and get
> -the "action mode" and "function mode" for various Synapses and Counts
> -respectively.
> +Driver callbacks must be provided to the counter_device structure in
> +order to communicate with the device: to read and write various Signals
> +and Counts, and to set and get the "action mode" and "function mode" for
> +various Synapses and Counts respectively.
>  
>  A defined counter_device structure may be registered to the system by
>  passing it to the counter_register function, and unregistered by passing
>  it to the counter_unregister function. Similarly, the
> -devm_counter_register and devm_counter_unregister functions may be used
> -if device memory-managed registration is desired.
> -
> -Extension sysfs attributes can be created for auxiliary functionality
> -and data by passing in defined counter_device_ext, counter_count_ext,
> -and counter_signal_ext structures. In these cases, the
> -counter_device_ext structure is used for global/miscellaneous exposure
> -and configuration of the respective Counter device, while the
> -counter_count_ext and counter_signal_ext structures allow for auxiliary
> -exposure and configuration of a specific Count or Signal respectively.
> +devm_counter_register function may be used if device memory-managed
> +registration is desired.
> +
> +The struct counter_comp structure is used to define counter extensions
> +for Signals, Synapses, and Counts.
> +
> +The "type" member specifies the type of high-level data (e.g. BOOL,
> +COUNT_DIRECTION, etc.) handled by this extension. The "``*_read``" and
> +"``*_write``" members can then be set by the counter device driver with
> +callbacks to handle that data using native C data types (i.e. u8, u64,
> +etc.).
> +
> +Convenience macros such as ``COUNTER_COMP_COUNT_U64`` are provided for
> +use by driver authors. In particular, driver authors are expected to use
> +the provided macros for standard Counter subsystem attributes in order
> +to maintain a consistent interface for userspace. For example, a counter
> +device driver may define several standard attributes like so::
> +
> +        struct counter_comp count_ext[] = {
> +                COUNTER_COMP_DIRECTION(count_direction_read),
> +                COUNTER_COMP_ENABLE(count_enable_read, count_enable_write),
> +                COUNTER_COMP_CEILING(count_ceiling_read, count_ceiling_write),
> +        };
> +
> +This makes it simple to see, add, and modify the attributes that are
> +supported by this driver ("direction", "enable", and "ceiling") and to
> +maintain this code without getting lost in a web of struct braces.
> +
> +Callbacks must match the function type expected for the respective
> +component or extension. These function types are defined in the struct
> +counter_comp structure as the "``*_read``" and "``*_write``" union
> +members.
> +
> +The corresponding callback prototypes for the extensions mentioned in
> +the previous example above would be::
> +
> +        int count_direction_read(struct counter_device *counter,
> +                                 struct counter_count *count,
> +                                 enum counter_count_direction *direction);
> +        int count_enable_read(struct counter_device *counter,
> +                              struct counter_count *count, u8 *enable);
> +        int count_enable_write(struct counter_device *counter,
> +                               struct counter_count *count, u8 enable);
> +        int count_ceiling_read(struct counter_device *counter,
> +                               struct counter_count *count, u64 *ceiling);
> +        int count_ceiling_write(struct counter_device *counter,
> +                                struct counter_count *count, u64 ceiling);
>  
>  Determining the type of extension to create is a matter of scope.
>  
> @@ -313,52 +349,127 @@ Determining the type of extension to create is a matter of scope.
>    chip overheated via a device extension called "error_overtemp":
>    /sys/bus/counter/devices/counterX/error_overtemp
>  
> -Architecture
> -============
> -
> -When the Generic Counter interface counter module is loaded, the
> -counter_init function is called which registers a bus_type named
> -"counter" to the system. Subsequently, when the module is unloaded, the
> -counter_exit function is called which unregisters the bus_type named
> -"counter" from the system.
> -
> -Counter devices are registered to the system via the counter_register
> -function, and later removed via the counter_unregister function. The
> -counter_register function establishes a unique ID for the Counter
> -device and creates a respective sysfs directory, where X is the
> -mentioned unique ID:
> -
> -    /sys/bus/counter/devices/counterX
> -
> -Sysfs attributes are created within the counterX directory to expose
> -functionality, configurations, and data relating to the Counts, Signals,
> -and Synapses of the Counter device, as well as options and information
> -for the Counter device itself.
> -
> -Each Signal has a directory created to house its relevant sysfs
> -attributes, where Y is the unique ID of the respective Signal:
> -
> -    /sys/bus/counter/devices/counterX/signalY
> -
> -Similarly, each Count has a directory created to house its relevant
> -sysfs attributes, where Y is the unique ID of the respective Count:
> -
> -    /sys/bus/counter/devices/counterX/countY
> -
> -For a more detailed breakdown of the available Generic Counter interface
> -sysfs attributes, please refer to the
> -Documentation/ABI/testing/sysfs-bus-counter file.
> -
> -The Signals and Counts associated with the Counter device are registered
> -to the system as well by the counter_register function. The
> -signal_read/signal_write driver callbacks are associated with their
> -respective Signal attributes, while the count_read/count_write and
> -function_get/function_set driver callbacks are associated with their
> -respective Count attributes; similarly, the same is true for the
> -action_get/action_set driver callbacks and their respective Synapse
> -attributes. If a driver callback is left undefined, then the respective
> -read/write permission is left disabled for the relevant attributes.
> -
> -Similarly, extension sysfs attributes are created for the defined
> -counter_device_ext, counter_count_ext, and counter_signal_ext
> -structures that are passed in.
> +Subsystem Architecture
> +======================
> +
> +Counter drivers pass and take data natively (i.e. ``u8``, ``u64``, etc.)
> +and the shared counter module handles the translation between the sysfs
> +interface. This guarantees a standard userspace interface for all
> +counter drivers, and enables a Generic Counter chrdev interface via a
> +generalized device driver ABI.
> +
> +A high-level view of how a count value is passed down from a counter
> +driver is exemplified by the following. The driver callbacks are first
> +registered to the Counter core component for use by the Counter
> +userspace interface components::
> +
> +        Driver callbacks registration:
> +        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +                        +----------------------------+
> +                        | Counter device driver      |
> +                        +----------------------------+
> +                        | Processes data from device |
> +                        +----------------------------+
> +                                |
> +                         -------------------
> +                        / driver callbacks /
> +                        -------------------
> +                                |
> +                                V
> +                        +----------------------+
> +                        | Counter core         |
> +                        +----------------------+
> +                        | Routes device driver |
> +                        | callbacks to the     |
> +                        | userspace interfaces |
> +                        +----------------------+
> +                                |
> +                         -------------------
> +                        / driver callbacks /
> +                        -------------------
> +                                |
> +                +---------------+
> +                |
> +                V
> +        +--------------------+
> +        | Counter sysfs      |
> +        +--------------------+
> +        | Translates to the  |
> +        | standard Counter   |
> +        | sysfs output       |
> +        +--------------------+
> +
> +Thereafter, data can be transferred directly between the Counter device
> +driver and Counter userspace interface::
> +
> +        Count data request:
> +        ~~~~~~~~~~~~~~~~~~~
> +                         ----------------------
> +                        / Counter device       \
> +                        +----------------------+
> +                        | Count register: 0x28 |
> +                        +----------------------+
> +                                |
> +                         -----------------
> +                        / raw count data /
> +                        -----------------
> +                                |
> +                                V
> +                        +----------------------------+
> +                        | Counter device driver      |
> +                        +----------------------------+
> +                        | Processes data from device |
> +                        |----------------------------|
> +                        | Type: u64                  |
> +                        | Value: 42                  |
> +                        +----------------------------+
> +                                |
> +                         ----------
> +                        / u64     /
> +                        ----------
> +                                |
> +                +---------------+
> +                |
> +                V
> +        +--------------------+
> +        | Counter sysfs      |
> +        +--------------------+
> +        | Translates to the  |
> +        | standard Counter   |
> +        | sysfs output       |
> +        |--------------------|
> +        | Type: const char * |
> +        | Value: "42"        |
> +        +--------------------+
> +                |
> +         ---------------
> +        / const char * /
> +        ---------------
> +                |
> +                V
> +        +--------------------------------------------------+
> +        | `/sys/bus/counter/devices/counterX/countY/count` |
> +        +--------------------------------------------------+
> +        \ Count: "42"                                      /
> +         --------------------------------------------------
> +
> +There are three primary components involved:
> +
> +Counter device driver
> +---------------------
> +Communicates with the hardware device to read/write data; e.g. counter
> +drivers for quadrature encoders, timers, etc.
> +
> +Counter core
> +------------
> +Registers the counter device driver to the system so that the respective
> +callbacks are called during userspace interaction.
> +
> +Counter sysfs
> +-------------
> +Translates counter data to the standard Counter sysfs interface format
> +and vice versa.
> +
> +Please refer to the ``Documentation/ABI/testing/sysfs-bus-counter`` file
> +for a detailed breakdown of the available Generic Counter interface
> +sysfs attributes.


^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 05/14] docs: counter: Update to reflect sysfs internalization
@ 2021-09-08 17:48     ` Jonathan Cameron
  0 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-08 17:48 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula

On Fri, 27 Aug 2021 12:47:49 +0900
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> The Counter subsystem architecture and driver implementations have
> changed in order to handle Counter sysfs interactions in a more
> consistent way. This patch updates the Generic Counter interface
> documentation to reflect the changes.
> 
> Reviewed-by: David Lechner <david@lechnology.com>
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
Applied.

I'm going to pause at this point.  We are early in the cycle anyway
so there is no rush and I'd like to take another look at the remainder
of the series + leave a little longer for others to take a look at it!

Thanks,

Jonathan

> ---
>  Documentation/ABI/testing/sysfs-bus-counter  |   9 +-
>  Documentation/driver-api/generic-counter.rst | 243 ++++++++++++++-----
>  2 files changed, 185 insertions(+), 67 deletions(-)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter
> index 20fe5afd4f9e..dee79b606847 100644
> --- a/Documentation/ABI/testing/sysfs-bus-counter
> +++ b/Documentation/ABI/testing/sysfs-bus-counter
> @@ -286,7 +286,14 @@ What:		/sys/bus/counter/devices/counterX/signalY/signal
>  KernelVersion:	5.2
>  Contact:	linux-iio@vger.kernel.org
>  Description:
> -		Signal data of Signal Y represented as a string.
> +		Signal level state of Signal Y. The following signal level
> +		states are available:
> +
> +		low:
> +			Low level state.
> +
> +		high:
> +			High level state.
>  
>  What:		/sys/bus/counter/devices/counterX/signalY/synchronous_mode
>  KernelVersion:	5.2
> diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst
> index 64fe7db080e5..f6397218aa4c 100644
> --- a/Documentation/driver-api/generic-counter.rst
> +++ b/Documentation/driver-api/generic-counter.rst
> @@ -250,8 +250,8 @@ for defining a counter device.
>  .. kernel-doc:: drivers/counter/counter.c
>     :export:
>  
> -Implementation
> -==============
> +Driver Implementation
> +=====================
>  
>  To support a counter device, a driver must first allocate the available
>  Counter Signals via counter_signal structures. These Signals should
> @@ -267,25 +267,61 @@ respective counter_count structure. These counter_count structures are
>  set to the counts array member of an allocated counter_device structure
>  before the Counter is registered to the system.
>  
> -Driver callbacks should be provided to the counter_device structure via
> -a constant counter_ops structure in order to communicate with the
> -device: to read and write various Signals and Counts, and to set and get
> -the "action mode" and "function mode" for various Synapses and Counts
> -respectively.
> +Driver callbacks must be provided to the counter_device structure in
> +order to communicate with the device: to read and write various Signals
> +and Counts, and to set and get the "action mode" and "function mode" for
> +various Synapses and Counts respectively.
>  
>  A defined counter_device structure may be registered to the system by
>  passing it to the counter_register function, and unregistered by passing
>  it to the counter_unregister function. Similarly, the
> -devm_counter_register and devm_counter_unregister functions may be used
> -if device memory-managed registration is desired.
> -
> -Extension sysfs attributes can be created for auxiliary functionality
> -and data by passing in defined counter_device_ext, counter_count_ext,
> -and counter_signal_ext structures. In these cases, the
> -counter_device_ext structure is used for global/miscellaneous exposure
> -and configuration of the respective Counter device, while the
> -counter_count_ext and counter_signal_ext structures allow for auxiliary
> -exposure and configuration of a specific Count or Signal respectively.
> +devm_counter_register function may be used if device memory-managed
> +registration is desired.
> +
> +The struct counter_comp structure is used to define counter extensions
> +for Signals, Synapses, and Counts.
> +
> +The "type" member specifies the type of high-level data (e.g. BOOL,
> +COUNT_DIRECTION, etc.) handled by this extension. The "``*_read``" and
> +"``*_write``" members can then be set by the counter device driver with
> +callbacks to handle that data using native C data types (i.e. u8, u64,
> +etc.).
> +
> +Convenience macros such as ``COUNTER_COMP_COUNT_U64`` are provided for
> +use by driver authors. In particular, driver authors are expected to use
> +the provided macros for standard Counter subsystem attributes in order
> +to maintain a consistent interface for userspace. For example, a counter
> +device driver may define several standard attributes like so::
> +
> +        struct counter_comp count_ext[] = {
> +                COUNTER_COMP_DIRECTION(count_direction_read),
> +                COUNTER_COMP_ENABLE(count_enable_read, count_enable_write),
> +                COUNTER_COMP_CEILING(count_ceiling_read, count_ceiling_write),
> +        };
> +
> +This makes it simple to see, add, and modify the attributes that are
> +supported by this driver ("direction", "enable", and "ceiling") and to
> +maintain this code without getting lost in a web of struct braces.
> +
> +Callbacks must match the function type expected for the respective
> +component or extension. These function types are defined in the struct
> +counter_comp structure as the "``*_read``" and "``*_write``" union
> +members.
> +
> +The corresponding callback prototypes for the extensions mentioned in
> +the previous example above would be::
> +
> +        int count_direction_read(struct counter_device *counter,
> +                                 struct counter_count *count,
> +                                 enum counter_count_direction *direction);
> +        int count_enable_read(struct counter_device *counter,
> +                              struct counter_count *count, u8 *enable);
> +        int count_enable_write(struct counter_device *counter,
> +                               struct counter_count *count, u8 enable);
> +        int count_ceiling_read(struct counter_device *counter,
> +                               struct counter_count *count, u64 *ceiling);
> +        int count_ceiling_write(struct counter_device *counter,
> +                                struct counter_count *count, u64 ceiling);
>  
>  Determining the type of extension to create is a matter of scope.
>  
> @@ -313,52 +349,127 @@ Determining the type of extension to create is a matter of scope.
>    chip overheated via a device extension called "error_overtemp":
>    /sys/bus/counter/devices/counterX/error_overtemp
>  
> -Architecture
> -============
> -
> -When the Generic Counter interface counter module is loaded, the
> -counter_init function is called which registers a bus_type named
> -"counter" to the system. Subsequently, when the module is unloaded, the
> -counter_exit function is called which unregisters the bus_type named
> -"counter" from the system.
> -
> -Counter devices are registered to the system via the counter_register
> -function, and later removed via the counter_unregister function. The
> -counter_register function establishes a unique ID for the Counter
> -device and creates a respective sysfs directory, where X is the
> -mentioned unique ID:
> -
> -    /sys/bus/counter/devices/counterX
> -
> -Sysfs attributes are created within the counterX directory to expose
> -functionality, configurations, and data relating to the Counts, Signals,
> -and Synapses of the Counter device, as well as options and information
> -for the Counter device itself.
> -
> -Each Signal has a directory created to house its relevant sysfs
> -attributes, where Y is the unique ID of the respective Signal:
> -
> -    /sys/bus/counter/devices/counterX/signalY
> -
> -Similarly, each Count has a directory created to house its relevant
> -sysfs attributes, where Y is the unique ID of the respective Count:
> -
> -    /sys/bus/counter/devices/counterX/countY
> -
> -For a more detailed breakdown of the available Generic Counter interface
> -sysfs attributes, please refer to the
> -Documentation/ABI/testing/sysfs-bus-counter file.
> -
> -The Signals and Counts associated with the Counter device are registered
> -to the system as well by the counter_register function. The
> -signal_read/signal_write driver callbacks are associated with their
> -respective Signal attributes, while the count_read/count_write and
> -function_get/function_set driver callbacks are associated with their
> -respective Count attributes; similarly, the same is true for the
> -action_get/action_set driver callbacks and their respective Synapse
> -attributes. If a driver callback is left undefined, then the respective
> -read/write permission is left disabled for the relevant attributes.
> -
> -Similarly, extension sysfs attributes are created for the defined
> -counter_device_ext, counter_count_ext, and counter_signal_ext
> -structures that are passed in.
> +Subsystem Architecture
> +======================
> +
> +Counter drivers pass and take data natively (i.e. ``u8``, ``u64``, etc.)
> +and the shared counter module handles the translation between the sysfs
> +interface. This guarantees a standard userspace interface for all
> +counter drivers, and enables a Generic Counter chrdev interface via a
> +generalized device driver ABI.
> +
> +A high-level view of how a count value is passed down from a counter
> +driver is exemplified by the following. The driver callbacks are first
> +registered to the Counter core component for use by the Counter
> +userspace interface components::
> +
> +        Driver callbacks registration:
> +        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +                        +----------------------------+
> +                        | Counter device driver      |
> +                        +----------------------------+
> +                        | Processes data from device |
> +                        +----------------------------+
> +                                |
> +                         -------------------
> +                        / driver callbacks /
> +                        -------------------
> +                                |
> +                                V
> +                        +----------------------+
> +                        | Counter core         |
> +                        +----------------------+
> +                        | Routes device driver |
> +                        | callbacks to the     |
> +                        | userspace interfaces |
> +                        +----------------------+
> +                                |
> +                         -------------------
> +                        / driver callbacks /
> +                        -------------------
> +                                |
> +                +---------------+
> +                |
> +                V
> +        +--------------------+
> +        | Counter sysfs      |
> +        +--------------------+
> +        | Translates to the  |
> +        | standard Counter   |
> +        | sysfs output       |
> +        +--------------------+
> +
> +Thereafter, data can be transferred directly between the Counter device
> +driver and Counter userspace interface::
> +
> +        Count data request:
> +        ~~~~~~~~~~~~~~~~~~~
> +                         ----------------------
> +                        / Counter device       \
> +                        +----------------------+
> +                        | Count register: 0x28 |
> +                        +----------------------+
> +                                |
> +                         -----------------
> +                        / raw count data /
> +                        -----------------
> +                                |
> +                                V
> +                        +----------------------------+
> +                        | Counter device driver      |
> +                        +----------------------------+
> +                        | Processes data from device |
> +                        |----------------------------|
> +                        | Type: u64                  |
> +                        | Value: 42                  |
> +                        +----------------------------+
> +                                |
> +                         ----------
> +                        / u64     /
> +                        ----------
> +                                |
> +                +---------------+
> +                |
> +                V
> +        +--------------------+
> +        | Counter sysfs      |
> +        +--------------------+
> +        | Translates to the  |
> +        | standard Counter   |
> +        | sysfs output       |
> +        |--------------------|
> +        | Type: const char * |
> +        | Value: "42"        |
> +        +--------------------+
> +                |
> +         ---------------
> +        / const char * /
> +        ---------------
> +                |
> +                V
> +        +--------------------------------------------------+
> +        | `/sys/bus/counter/devices/counterX/countY/count` |
> +        +--------------------------------------------------+
> +        \ Count: "42"                                      /
> +         --------------------------------------------------
> +
> +There are three primary components involved:
> +
> +Counter device driver
> +---------------------
> +Communicates with the hardware device to read/write data; e.g. counter
> +drivers for quadrature encoders, timers, etc.
> +
> +Counter core
> +------------
> +Registers the counter device driver to the system so that the respective
> +callbacks are called during userspace interaction.
> +
> +Counter sysfs
> +-------------
> +Translates counter data to the standard Counter sysfs interface format
> +and vice versa.
> +
> +Please refer to the ``Documentation/ABI/testing/sysfs-bus-counter`` file
> +for a detailed breakdown of the available Generic Counter interface
> +sysfs attributes.


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 08/14] docs: counter: Document character device interface
  2021-08-27  3:47   ` William Breathitt Gray
@ 2021-09-12 16:18     ` Jonathan Cameron
  -1 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-12 16:18 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula

On Fri, 27 Aug 2021 12:47:52 +0900
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> This patch adds high-level documentation about the Counter subsystem
> character device interface.
> 
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
One trivial suggestion inline.  This document is very good.

Jonathan

> ---
>  Documentation/driver-api/generic-counter.rst  | 177 ++++++++++++++----
>  .../userspace-api/ioctl/ioctl-number.rst      |   1 +
>  2 files changed, 137 insertions(+), 41 deletions(-)
> 
> diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst
> index f6397218aa4c..6ecb5d1942c7 100644
> --- a/Documentation/driver-api/generic-counter.rst
> +++ b/Documentation/driver-api/generic-counter.rst
> @@ -223,19 +223,6 @@ whether an input line is differential or single-ended) and instead focus
>  on the core idea of what the data and process represent (e.g. position
>  as interpreted from quadrature encoding data).
>  
> -Userspace Interface
> -===================
> -
> -Several sysfs attributes are generated by the Generic Counter interface,
> -and reside under the /sys/bus/counter/devices/counterX directory, where
> -counterX refers to the respective counter device. Please see
> -Documentation/ABI/testing/sysfs-bus-counter for detailed
> -information on each Generic Counter interface sysfs attribute.
> -
> -Through these sysfs attributes, programs and scripts may interact with
> -the Generic Counter paradigm Counts, Signals, and Synapses of respective
> -counter devices.
> -
>  Driver API
>  ==========
>  
> @@ -388,16 +375,16 @@ userspace interface components::
>                          / driver callbacks /
>                          -------------------
>                                  |
> -                +---------------+
> -                |
> -                V
> -        +--------------------+
> -        | Counter sysfs      |
> -        +--------------------+
> -        | Translates to the  |
> -        | standard Counter   |
> -        | sysfs output       |
> -        +--------------------+
> +                +---------------+---------------+
> +                |                               |
> +                V                               V
> +        +--------------------+          +---------------------+
> +        | Counter sysfs      |          | Counter chrdev      |
> +        +--------------------+          +---------------------+
> +        | Translates to the  |          | Translates to the   |
> +        | standard Counter   |          | standard Counter    |
> +        | sysfs output       |          | character device    |
> +        +--------------------+          +---------------------+
>  
>  Thereafter, data can be transferred directly between the Counter device
>  driver and Counter userspace interface::
> @@ -428,23 +415,30 @@ driver and Counter userspace interface::
>                          / u64     /
>                          ----------
>                                  |
> -                +---------------+
> -                |
> -                V
> -        +--------------------+
> -        | Counter sysfs      |
> -        +--------------------+
> -        | Translates to the  |
> -        | standard Counter   |
> -        | sysfs output       |
> -        |--------------------|
> -        | Type: const char * |
> -        | Value: "42"        |
> -        +--------------------+
> -                |
> -         ---------------
> -        / const char * /
> -        ---------------
> +                +---------------+---------------+
> +                |                               |
> +                V                               V
> +        +--------------------+          +---------------------+
> +        | Counter sysfs      |          | Counter chrdev      |
> +        +--------------------+          +---------------------+
> +        | Translates to the  |          | Translates to the   |
> +        | standard Counter   |          | standard Counter    |
> +        | sysfs output       |          | character device    |
> +        |--------------------|          |---------------------|
> +        | Type: const char * |          | Type: u64           |
> +        | Value: "42"        |          | Value: 42           |
> +        +--------------------+          +---------------------+
> +                |                               |
> +         ---------------                 -----------------------
> +        / const char * /                / struct counter_event /
> +        ---------------                 -----------------------
> +                |                               |
> +                |                               V
> +                |                       +-----------+
> +                |                       | read      |
> +                |                       +-----------+
> +                |                       \ Count: 42 /
> +                |                        -----------
>                  |
>                  V
>          +--------------------------------------------------+
> @@ -453,7 +447,7 @@ driver and Counter userspace interface::
>          \ Count: "42"                                      /
>           --------------------------------------------------
>  
> -There are three primary components involved:
> +There are four primary components involved:
>  
>  Counter device driver
>  ---------------------
> @@ -473,3 +467,104 @@ and vice versa.
>  Please refer to the ``Documentation/ABI/testing/sysfs-bus-counter`` file
>  for a detailed breakdown of the available Generic Counter interface
>  sysfs attributes.
> +
> +Counter chrdev
> +--------------
> +Translates counter data to the standard Counter character device; data

Whilst it is clear when you read on, perhaps change "data" to "Counter
events" here.

> +is transferred via standard character device read calls, while Counter
> +events are configured via ioctl calls.
> +
> +Sysfs Interface
> +===============
> +
> +Several sysfs attributes are generated by the Generic Counter interface,
> +and reside under the ``/sys/bus/counter/devices/counterX`` directory,
> +where ``X`` is to the respective counter device id. Please see
> +``Documentation/ABI/testing/sysfs-bus-counter`` for detailed information
> +on each Generic Counter interface sysfs attribute.
> +
> +Through these sysfs attributes, programs and scripts may interact with
> +the Generic Counter paradigm Counts, Signals, and Synapses of respective
> +counter devices.
> +
> +Counter Character Device
> +========================
> +
> +Counter character device nodes are created under the ``/dev`` directory
> +as ``counterX``, where ``X`` is the respective counter device id.
> +Defines for the standard Counter data types are exposed via the
> +userspace ``include/uapi/linux/counter.h`` file.
> +
> +Counter events
> +--------------
> +Counter device drivers can support Counter events by utilizing the
> +``counter_push_event`` function::
> +
> +        void counter_push_event(struct counter_device *const counter, const u8 event,
> +                                const u8 channel);
> +
> +The event id is specified by the ``event`` parameter; the event channel
> +id is specified by the ``channel`` parameter. When this function is
> +called, the Counter data associated with the respective event is
> +gathered, and a ``struct counter_event`` is generated for each datum and
> +pushed to userspace.
> +
> +Counter events can be configured by users to report various Counter
> +data of interest. This can be conceptualized as a list of Counter
> +component read calls to perform. For example:
> +
> +        +------------------------+------------------------+
> +        | COUNTER_EVENT_OVERFLOW | COUNTER_EVENT_INDEX    |
> +        +========================+========================+
> +        | Channel 0              | Channel 0              |
> +        +------------------------+------------------------+
> +        | * Count 0              | * Signal 0             |
> +        | * Count 1              | * Signal 0 Extension 0 |
> +        | * Signal 3             | * Extension 4          |
> +        | * Count 4 Extension 2  +------------------------+
> +        | * Signal 5 Extension 0 | Channel 1              |
> +        |                        +------------------------+
> +        |                        | * Signal 4             |
> +        |                        | * Signal 4 Extension 0 |
> +        |                        | * Count 7              |
> +        +------------------------+------------------------+
> +
> +When ``counter_push_event(counter, COUNTER_EVENT_INDEX, 1)`` is called
> +for example, it will go down the list for the ``COUNTER_EVENT_INDEX``
> +event channel 1 and execute the read callbacks for Signal 4, Signal 4
> +Extension 0, and Count 7 -- the data returned for each is pushed to a
> +kfifo as a ``struct counter_event``, which userspace can retrieve via a
> +standard read operation on the respective character device node.
> +
> +Userspace
> +---------
> +Userspace applications can configure Counter events via ioctl operations
> +on the Counter character device node. There following ioctl codes are
> +supported and provided by the ``linux/counter.h`` userspace header file:
> +
> +* :c:macro:`COUNTER_ADD_WATCH_IOCTL`
> +
> +* :c:macro:`COUNTER_ENABLE_EVENTS_IOCTL`
> +
> +* :c:macro:`COUNTER_DISABLE_EVENTS_IOCTL`
> +
> +To configure events to gather Counter data, users first populate a
> +``struct counter_watch`` with the relevant event id, event channel id,
> +and the information for the desired Counter component from which to
> +read, and then pass it via the ``COUNTER_ADD_WATCH_IOCTL`` ioctl
> +command.
> +
> +Note that an event can be watched without gathering Counter data by
> +setting the ``component.type`` member equal to
> +``COUNTER_COMPONENT_NONE``. With this configuration the Counter
> +character device will simply populate the event timestamps for those
> +respective ``struct counter_event`` elements and ignore the component
> +value.
> +
> +The ``COUNTER_ADD_WATCH_IOCTL`` command will buffer these Counter
> +watches. When ready, the ``COUNTER_ENABLE_EVENTS_IOCTL`` ioctl command
> +may be used to activate these Counter watches.
> +
> +Userspace applications can then execute a ``read`` operation (optionally
> +calling ``poll`` first) on the Counter character device node to retrieve
> +``struct counter_event`` elements with the desired data.
> diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
> index 1409e40e6345..fc4ccc79b1b8 100644
> --- a/Documentation/userspace-api/ioctl/ioctl-number.rst
> +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
> @@ -88,6 +88,7 @@ Code  Seq#    Include File                                           Comments
>                                                                       <http://infiniband.sourceforge.net/>
>  0x20  all    drivers/cdrom/cm206.h
>  0x22  all    scsi/sg.h
> +0x3E  00-0F  linux/counter.h                                         <mailto:linux-iio@vger.kernel.org>
>  '!'   00-1F  uapi/linux/seccomp.h
>  '#'   00-3F                                                          IEEE 1394 Subsystem
>                                                                       Block for the entire subsystem


^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 08/14] docs: counter: Document character device interface
@ 2021-09-12 16:18     ` Jonathan Cameron
  0 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-12 16:18 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula

On Fri, 27 Aug 2021 12:47:52 +0900
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> This patch adds high-level documentation about the Counter subsystem
> character device interface.
> 
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
One trivial suggestion inline.  This document is very good.

Jonathan

> ---
>  Documentation/driver-api/generic-counter.rst  | 177 ++++++++++++++----
>  .../userspace-api/ioctl/ioctl-number.rst      |   1 +
>  2 files changed, 137 insertions(+), 41 deletions(-)
> 
> diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst
> index f6397218aa4c..6ecb5d1942c7 100644
> --- a/Documentation/driver-api/generic-counter.rst
> +++ b/Documentation/driver-api/generic-counter.rst
> @@ -223,19 +223,6 @@ whether an input line is differential or single-ended) and instead focus
>  on the core idea of what the data and process represent (e.g. position
>  as interpreted from quadrature encoding data).
>  
> -Userspace Interface
> -===================
> -
> -Several sysfs attributes are generated by the Generic Counter interface,
> -and reside under the /sys/bus/counter/devices/counterX directory, where
> -counterX refers to the respective counter device. Please see
> -Documentation/ABI/testing/sysfs-bus-counter for detailed
> -information on each Generic Counter interface sysfs attribute.
> -
> -Through these sysfs attributes, programs and scripts may interact with
> -the Generic Counter paradigm Counts, Signals, and Synapses of respective
> -counter devices.
> -
>  Driver API
>  ==========
>  
> @@ -388,16 +375,16 @@ userspace interface components::
>                          / driver callbacks /
>                          -------------------
>                                  |
> -                +---------------+
> -                |
> -                V
> -        +--------------------+
> -        | Counter sysfs      |
> -        +--------------------+
> -        | Translates to the  |
> -        | standard Counter   |
> -        | sysfs output       |
> -        +--------------------+
> +                +---------------+---------------+
> +                |                               |
> +                V                               V
> +        +--------------------+          +---------------------+
> +        | Counter sysfs      |          | Counter chrdev      |
> +        +--------------------+          +---------------------+
> +        | Translates to the  |          | Translates to the   |
> +        | standard Counter   |          | standard Counter    |
> +        | sysfs output       |          | character device    |
> +        +--------------------+          +---------------------+
>  
>  Thereafter, data can be transferred directly between the Counter device
>  driver and Counter userspace interface::
> @@ -428,23 +415,30 @@ driver and Counter userspace interface::
>                          / u64     /
>                          ----------
>                                  |
> -                +---------------+
> -                |
> -                V
> -        +--------------------+
> -        | Counter sysfs      |
> -        +--------------------+
> -        | Translates to the  |
> -        | standard Counter   |
> -        | sysfs output       |
> -        |--------------------|
> -        | Type: const char * |
> -        | Value: "42"        |
> -        +--------------------+
> -                |
> -         ---------------
> -        / const char * /
> -        ---------------
> +                +---------------+---------------+
> +                |                               |
> +                V                               V
> +        +--------------------+          +---------------------+
> +        | Counter sysfs      |          | Counter chrdev      |
> +        +--------------------+          +---------------------+
> +        | Translates to the  |          | Translates to the   |
> +        | standard Counter   |          | standard Counter    |
> +        | sysfs output       |          | character device    |
> +        |--------------------|          |---------------------|
> +        | Type: const char * |          | Type: u64           |
> +        | Value: "42"        |          | Value: 42           |
> +        +--------------------+          +---------------------+
> +                |                               |
> +         ---------------                 -----------------------
> +        / const char * /                / struct counter_event /
> +        ---------------                 -----------------------
> +                |                               |
> +                |                               V
> +                |                       +-----------+
> +                |                       | read      |
> +                |                       +-----------+
> +                |                       \ Count: 42 /
> +                |                        -----------
>                  |
>                  V
>          +--------------------------------------------------+
> @@ -453,7 +447,7 @@ driver and Counter userspace interface::
>          \ Count: "42"                                      /
>           --------------------------------------------------
>  
> -There are three primary components involved:
> +There are four primary components involved:
>  
>  Counter device driver
>  ---------------------
> @@ -473,3 +467,104 @@ and vice versa.
>  Please refer to the ``Documentation/ABI/testing/sysfs-bus-counter`` file
>  for a detailed breakdown of the available Generic Counter interface
>  sysfs attributes.
> +
> +Counter chrdev
> +--------------
> +Translates counter data to the standard Counter character device; data

Whilst it is clear when you read on, perhaps change "data" to "Counter
events" here.

> +is transferred via standard character device read calls, while Counter
> +events are configured via ioctl calls.
> +
> +Sysfs Interface
> +===============
> +
> +Several sysfs attributes are generated by the Generic Counter interface,
> +and reside under the ``/sys/bus/counter/devices/counterX`` directory,
> +where ``X`` is to the respective counter device id. Please see
> +``Documentation/ABI/testing/sysfs-bus-counter`` for detailed information
> +on each Generic Counter interface sysfs attribute.
> +
> +Through these sysfs attributes, programs and scripts may interact with
> +the Generic Counter paradigm Counts, Signals, and Synapses of respective
> +counter devices.
> +
> +Counter Character Device
> +========================
> +
> +Counter character device nodes are created under the ``/dev`` directory
> +as ``counterX``, where ``X`` is the respective counter device id.
> +Defines for the standard Counter data types are exposed via the
> +userspace ``include/uapi/linux/counter.h`` file.
> +
> +Counter events
> +--------------
> +Counter device drivers can support Counter events by utilizing the
> +``counter_push_event`` function::
> +
> +        void counter_push_event(struct counter_device *const counter, const u8 event,
> +                                const u8 channel);
> +
> +The event id is specified by the ``event`` parameter; the event channel
> +id is specified by the ``channel`` parameter. When this function is
> +called, the Counter data associated with the respective event is
> +gathered, and a ``struct counter_event`` is generated for each datum and
> +pushed to userspace.
> +
> +Counter events can be configured by users to report various Counter
> +data of interest. This can be conceptualized as a list of Counter
> +component read calls to perform. For example:
> +
> +        +------------------------+------------------------+
> +        | COUNTER_EVENT_OVERFLOW | COUNTER_EVENT_INDEX    |
> +        +========================+========================+
> +        | Channel 0              | Channel 0              |
> +        +------------------------+------------------------+
> +        | * Count 0              | * Signal 0             |
> +        | * Count 1              | * Signal 0 Extension 0 |
> +        | * Signal 3             | * Extension 4          |
> +        | * Count 4 Extension 2  +------------------------+
> +        | * Signal 5 Extension 0 | Channel 1              |
> +        |                        +------------------------+
> +        |                        | * Signal 4             |
> +        |                        | * Signal 4 Extension 0 |
> +        |                        | * Count 7              |
> +        +------------------------+------------------------+
> +
> +When ``counter_push_event(counter, COUNTER_EVENT_INDEX, 1)`` is called
> +for example, it will go down the list for the ``COUNTER_EVENT_INDEX``
> +event channel 1 and execute the read callbacks for Signal 4, Signal 4
> +Extension 0, and Count 7 -- the data returned for each is pushed to a
> +kfifo as a ``struct counter_event``, which userspace can retrieve via a
> +standard read operation on the respective character device node.
> +
> +Userspace
> +---------
> +Userspace applications can configure Counter events via ioctl operations
> +on the Counter character device node. There following ioctl codes are
> +supported and provided by the ``linux/counter.h`` userspace header file:
> +
> +* :c:macro:`COUNTER_ADD_WATCH_IOCTL`
> +
> +* :c:macro:`COUNTER_ENABLE_EVENTS_IOCTL`
> +
> +* :c:macro:`COUNTER_DISABLE_EVENTS_IOCTL`
> +
> +To configure events to gather Counter data, users first populate a
> +``struct counter_watch`` with the relevant event id, event channel id,
> +and the information for the desired Counter component from which to
> +read, and then pass it via the ``COUNTER_ADD_WATCH_IOCTL`` ioctl
> +command.
> +
> +Note that an event can be watched without gathering Counter data by
> +setting the ``component.type`` member equal to
> +``COUNTER_COMPONENT_NONE``. With this configuration the Counter
> +character device will simply populate the event timestamps for those
> +respective ``struct counter_event`` elements and ignore the component
> +value.
> +
> +The ``COUNTER_ADD_WATCH_IOCTL`` command will buffer these Counter
> +watches. When ready, the ``COUNTER_ENABLE_EVENTS_IOCTL`` ioctl command
> +may be used to activate these Counter watches.
> +
> +Userspace applications can then execute a ``read`` operation (optionally
> +calling ``poll`` first) on the Counter character device node to retrieve
> +``struct counter_event`` elements with the desired data.
> diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
> index 1409e40e6345..fc4ccc79b1b8 100644
> --- a/Documentation/userspace-api/ioctl/ioctl-number.rst
> +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
> @@ -88,6 +88,7 @@ Code  Seq#    Include File                                           Comments
>                                                                       <http://infiniband.sourceforge.net/>
>  0x20  all    drivers/cdrom/cm206.h
>  0x22  all    scsi/sg.h
> +0x3E  00-0F  linux/counter.h                                         <mailto:linux-iio@vger.kernel.org>
>  '!'   00-1F  uapi/linux/seccomp.h
>  '#'   00-3F                                                          IEEE 1394 Subsystem
>                                                                       Block for the entire subsystem


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 07/14] counter: Add character device interface
  2021-08-27  3:47   ` William Breathitt Gray
@ 2021-09-12 16:18     ` Jonathan Cameron
  -1 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-12 16:18 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	Dan Carpenter

On Fri, 27 Aug 2021 12:47:51 +0900
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> This patch introduces a character device interface for the Counter
> subsystem. Device data is exposed through standard character device read
> operations. Device data is gathered when a Counter event is pushed by
> the respective Counter device driver. Configuration is handled via ioctl
> operations on the respective Counter character device node.
> 
> Cc: David Lechner <david@lechnology.com>
> Cc: Gwendal Grignou <gwendal@chromium.org>
> Cc: Dan Carpenter <dan.carpenter@oracle.com>
> Cc: Oleksij Rempel <o.rempel@pengutronix.de>
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>

Hi William,

Why the bit based lock?  It feels like a mutex_trylock() type approach or
spinlock_trylock() would be a more common solution to this problem.
There is precedence for doing what you have here though so I'm not that
worried about it.

There are a few more things inline.

I've now been through this patch with as fine toothed comb as I'm likely to
do so.  Hence I won't do another review unless there are substantial changes.
I nearly applied it as it stands, but given we aren't in a rush (merge window
open), it's worth just a little more time to tidy up loose ends.

Jonathan


> ---
>  drivers/counter/Makefile         |   2 +-
>  drivers/counter/counter-chrdev.c | 553 +++++++++++++++++++++++++++++++
>  drivers/counter/counter-chrdev.h |  14 +
>  drivers/counter/counter-core.c   |  60 +++-
>  include/linux/counter.h          |  51 +++
>  include/uapi/linux/counter.h     |  98 ++++++
>  6 files changed, 770 insertions(+), 8 deletions(-)
>  create mode 100644 drivers/counter/counter-chrdev.c
>  create mode 100644 drivers/counter/counter-chrdev.h
> 
> diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
> index 1ab7e087fdc2..8fde6c100ebc 100644
> --- a/drivers/counter/Makefile
> +++ b/drivers/counter/Makefile
> @@ -4,7 +4,7 @@
>  #
>  
>  obj-$(CONFIG_COUNTER) += counter.o
> -counter-y := counter-core.o counter-sysfs.o
> +counter-y := counter-core.o counter-sysfs.o counter-chrdev.o
>  
>  obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
>  obj-$(CONFIG_INTERRUPT_CNT)		+= interrupt-cnt.o
> diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c
> new file mode 100644
> index 000000000000..30659b67d8f3
> --- /dev/null
> +++ b/drivers/counter/counter-chrdev.c
> @@ -0,0 +1,553 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Generic Counter character device interface
> + * Copyright (C) 2020 William Breathitt Gray
> + */
> +#include <linux/bitops.h>
> +#include <linux/cdev.h>
> +#include <linux/counter.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/export.h>
> +#include <linux/fs.h>
> +#include <linux/kfifo.h>
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> +#include <linux/nospec.h>
> +#include <linux/poll.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/timekeeping.h>
> +#include <linux/types.h>
> +#include <linux/uaccess.h>
> +#include <linux/wait.h>
> +
> +#include "counter-chrdev.h"
> +
> +struct counter_comp_node {
> +	struct list_head l;
> +	struct counter_component component;
> +	struct counter_comp comp;
> +	void *parent;
> +};
> +
> +static ssize_t counter_chrdev_read(struct file *filp, char __user *buf,
> +				   size_t len, loff_t *f_ps)
> +{
> +	struct counter_device *const counter = filp->private_data;
> +	int err;
> +	unsigned int copied;
> +
> +	if (!counter->ops)
> +		return -ENODEV;
> +
> +	if (len < sizeof(struct counter_event))
> +		return -EINVAL;
> +
> +	do {
> +		if (kfifo_is_empty(&counter->events)) {
> +			if (filp->f_flags & O_NONBLOCK)
> +				return -EAGAIN;
> +
> +			err = wait_event_interruptible(counter->events_wait,
> +					!kfifo_is_empty(&counter->events) ||
> +					!counter->ops);
> +			if (err < 0)
> +				return err;
> +			if (!counter->ops)
> +				return -ENODEV;
> +		}
> +
> +		if (mutex_lock_interruptible(&counter->events_lock))
> +			return -ERESTARTSYS;
> +		err = kfifo_to_user(&counter->events, buf, len, &copied);
> +		mutex_unlock(&counter->events_lock);
> +		if (err < 0)
> +			return err;
> +	} while (!copied);
> +
> +	return copied;
> +}
> +
> +static __poll_t counter_chrdev_poll(struct file *filp,
> +				    struct poll_table_struct *pollt)
> +{
> +	struct counter_device *const counter = filp->private_data;
> +	__poll_t events = 0;
> +
> +	if (!counter->ops)
> +		return events;
> +
> +	poll_wait(filp, &counter->events_wait, pollt);
> +
> +	if (!kfifo_is_empty(&counter->events))
> +		events = EPOLLIN | EPOLLRDNORM;
> +
> +	return events;
> +}
> +
> +static void counter_events_list_free(struct list_head *const events_list)
> +{
> +	struct counter_event_node *p, *n;
> +	struct counter_comp_node *q, *o;
> +
> +	list_for_each_entry_safe(p, n, events_list, l) {
> +		/* Free associated component nodes */
> +		list_for_each_entry_safe(q, o, &p->comp_list, l) {
> +			list_del(&q->l);
> +			kfree(q);
> +		}
> +
> +		/* Free event node */
> +		list_del(&p->l);
> +		kfree(p);
> +	}
> +}
> +
> +static int counter_set_event_node(struct counter_device *const counter,
> +				  struct counter_watch *const watch,
> +				  const struct counter_comp_node *const cfg)
> +{
> +	struct counter_event_node *event_node;
> +	int err = 0;
> +	struct counter_comp_node *comp_node;
> +
> +	/* Search for event in the list */
> +	list_for_each_entry(event_node, &counter->next_events_list, l)
> +		if (event_node->event == watch->event &&
> +		    event_node->channel == watch->channel)
> +			break;
> +
> +	/* If event is not already in the list */
> +	if (&event_node->l == &counter->next_events_list) {
> +		/* Allocate new event node */
> +		event_node = kmalloc(sizeof(*event_node), GFP_KERNEL);
> +		if (!event_node)
> +			return -ENOMEM;
> +
> +		/* Configure event node and add to the list */
> +		event_node->event = watch->event;
> +		event_node->channel = watch->channel;
> +		INIT_LIST_HEAD(&event_node->comp_list);
> +		list_add(&event_node->l, &counter->next_events_list);
> +	}
> +
> +	/* Check if component watch has already been set before */
> +	list_for_each_entry(comp_node, &event_node->comp_list, l)
> +		if (comp_node->parent == cfg->parent &&
> +		    comp_node->comp.count_u8_read == cfg->comp.count_u8_read) {
> +			err = -EINVAL;
> +			goto exit_free_event_node;
> +		}
> +
> +	/* Allocate component node */
> +	comp_node = kmalloc(sizeof(*comp_node), GFP_KERNEL);
> +	if (!comp_node) {
> +		err = -ENOMEM;
> +		goto exit_free_event_node;
> +	}
> +	*comp_node = *cfg;
> +
> +	/* Add component node to event node */
> +	list_add_tail(&comp_node->l, &event_node->comp_list);
> +
> +exit_free_event_node:
> +	/* Free event node if no one else is watching */
> +	if (list_empty(&event_node->comp_list)) {
> +		list_del(&event_node->l);
> +		kfree(event_node);
> +	}
> +
> +	return err;
> +}
> +
> +static int counter_enable_events(struct counter_device *const counter)
> +{
> +	unsigned long flags;
> +	int err = 0;
> +
> +	mutex_lock(&counter->n_events_list_lock);
> +	spin_lock_irqsave(&counter->events_list_lock, flags);
> +
> +	counter_events_list_free(&counter->events_list);
> +	list_replace_init(&counter->next_events_list,
> +			  &counter->events_list);
> +
> +	if (counter->ops->events_configure)
> +		err = counter->ops->events_configure(counter);
> +
> +	spin_unlock_irqrestore(&counter->events_list_lock, flags);
> +	mutex_unlock(&counter->n_events_list_lock);
> +
> +	return err;
> +}
> +
> +static int counter_disable_events(struct counter_device *const counter)
> +{
> +	unsigned long flags;
> +	int err = 0;
> +
> +	spin_lock_irqsave(&counter->events_list_lock, flags);
> +
> +	counter_events_list_free(&counter->events_list);
> +
> +	if (counter->ops->events_configure)
> +		err = counter->ops->events_configure(counter);
> +
> +	spin_unlock_irqrestore(&counter->events_list_lock, flags);
> +
> +	mutex_lock(&counter->n_events_list_lock);
> +
> +	counter_events_list_free(&counter->next_events_list);
> +
> +	mutex_unlock(&counter->n_events_list_lock);
> +
> +	return err;
> +}
> +
> +static int counter_add_watch(struct counter_device *const counter,
> +			     const unsigned long arg)
> +{
> +	void __user *const uwatch = (void __user *)arg;
> +	struct counter_watch watch;
> +	struct counter_comp_node comp_node = {};
> +	size_t parent, id;
> +	struct counter_comp *ext;
> +	size_t num_ext;
> +	int err = 0;
> +
> +	if (copy_from_user(&watch, uwatch, sizeof(watch)))
> +		return -EFAULT;
> +
> +	if (watch.component.type == COUNTER_COMPONENT_NONE)
> +		goto no_component;
> +
> +	parent = watch.component.parent;
> +
> +	/* Configure parent component info for comp node */
> +	switch (watch.component.scope) {
> +	case COUNTER_SCOPE_DEVICE:
> +		ext = counter->ext;
> +		num_ext = counter->num_ext;
> +		break;
> +	case COUNTER_SCOPE_SIGNAL:
> +		if (parent >= counter->num_signals)
> +			return -EINVAL;
> +		parent = array_index_nospec(parent, counter->num_signals);
> +
> +		comp_node.parent = counter->signals + parent;
> +
> +		ext = counter->signals[parent].ext;
> +		num_ext = counter->signals[parent].num_ext;
> +		break;
> +	case COUNTER_SCOPE_COUNT:
> +		if (parent >= counter->num_counts)
> +			return -EINVAL;
> +		parent = array_index_nospec(parent, counter->num_counts);
> +
> +		comp_node.parent = counter->counts + parent;
> +
> +		ext = counter->counts[parent].ext;
> +		num_ext = counter->counts[parent].num_ext;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	id = watch.component.id;
> +
> +	/* Configure component info for comp node */
> +	switch (watch.component.type) {
> +	case COUNTER_COMPONENT_SIGNAL:
> +		if (watch.component.scope != COUNTER_SCOPE_SIGNAL)
> +			return -EINVAL;
> +
> +		comp_node.comp.type = COUNTER_COMP_SIGNAL_LEVEL;
> +		comp_node.comp.signal_u32_read = counter->ops->signal_read;
> +		break;
> +	case COUNTER_COMPONENT_COUNT:
> +		if (watch.component.scope != COUNTER_SCOPE_COUNT)
> +			return -EINVAL;
> +
> +		comp_node.comp.type = COUNTER_COMP_U64;
> +		comp_node.comp.count_u64_read = counter->ops->count_read;
> +		break;
> +	case COUNTER_COMPONENT_FUNCTION:
> +		if (watch.component.scope != COUNTER_SCOPE_COUNT)
> +			return -EINVAL;
> +
> +		comp_node.comp.type = COUNTER_COMP_FUNCTION;
> +		comp_node.comp.count_u32_read = counter->ops->function_read;
> +		break;
> +	case COUNTER_COMPONENT_SYNAPSE_ACTION:
> +		if (watch.component.scope != COUNTER_SCOPE_COUNT)
> +			return -EINVAL;
> +		if (id >= counter->counts[parent].num_synapses)
> +			return -EINVAL;
> +		id = array_index_nospec(id, counter->counts[parent].num_synapses);
> +
> +		comp_node.comp.type = COUNTER_COMP_SYNAPSE_ACTION;
> +		comp_node.comp.action_read = counter->ops->action_read;
> +		comp_node.comp.priv = counter->counts[parent].synapses + id;
> +		break;
> +	case COUNTER_COMPONENT_EXTENSION:
> +		if (id >= num_ext)
> +			return -EINVAL;
> +		id = array_index_nospec(id, num_ext);
> +
> +		comp_node.comp = ext[id];
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	/* Check if any read callback is set; this is part of a union */
> +	if (!comp_node.comp.count_u8_read)

This isn't valid for C.  The compiler is allowed to treat the elements
of a union as not being in the same memory etc unless they are of the same
type (which isn't true here - I think anyway)...

So whilst it seems silly you need to check all the callbacks in this if
statement. It will 'almost' always work as you have it, but it's possible
some future optimisation by the compiler will mean that u8_read would be
set by this point, but it delayed setting e.g. u32_read until after
this check.

> +		return -EOPNOTSUPP;
> +
> +no_component:
> +	mutex_lock(&counter->n_events_list_lock);
> +
> +	if (counter->ops->watch_validate) {
> +		err = counter->ops->watch_validate(counter, &watch);
> +		if (err < 0)
> +			goto err_exit;
> +	}
> +
> +	comp_node.component = watch.component;
> +
> +	err = counter_set_event_node(counter, &watch, &comp_node);
> +
> +err_exit:
> +	mutex_unlock(&counter->n_events_list_lock);
> +
> +	return err;
> +}
> +
> +static long counter_chrdev_ioctl(struct file *filp, unsigned int cmd,
> +				 unsigned long arg)
> +{
> +	struct counter_device *const counter = filp->private_data;
> +	int ret = -ENODEV;
> +
> +	mutex_lock(&counter->ops_exist_lock);
> +
> +	if (!counter->ops)
> +		goto out_unlock;
> +
> +	switch (cmd) {
> +	case COUNTER_ADD_WATCH_IOCTL:
> +		ret = counter_add_watch(counter, arg);
> +		break;
> +	case COUNTER_ENABLE_EVENTS_IOCTL:
> +		ret = counter_enable_events(counter);
> +		break;
> +	case COUNTER_DISABLE_EVENTS_IOCTL:
> +		ret = counter_disable_events(counter);
> +		break;
> +	default:
> +		ret = -ENOIOCTLCMD;
> +		break;
> +	}
> +
> +out_unlock:
> +	mutex_unlock(&counter->ops_exist_lock);
> +
> +	return ret;
> +}
> +
> +static int counter_chrdev_open(struct inode *inode, struct file *filp)
> +{
> +	struct counter_device *const counter = container_of(inode->i_cdev,
> +							    typeof(*counter),
> +							    chrdev);
> +
> +	if (test_and_set_bit_lock(0, counter->chrdev_lock))
> +		return -EBUSY;
> +
> +	get_device(&counter->dev);
> +	filp->private_data = counter;
> +
> +	return nonseekable_open(inode, filp);
> +}
> +
> +static int counter_chrdev_release(struct inode *inode, struct file *filp)
> +{
> +	struct counter_device *const counter = filp->private_data;
> +	int ret = 0;
> +
> +	mutex_lock(&counter->ops_exist_lock);
> +
> +	if (!counter->ops) {

This needs a comment to explain how you would get here and
why these two lists need cleaning up here if we do.

Superficially it feels to me like you could just add a counter->ops check in
counter_disable_events() and then call that directly.  I'm guessing
I am missing a deadlock or similar however.


> +		counter_events_list_free(&counter->events_list);
> +		counter_events_list_free(&counter->next_events_list);
> +		ret = -ENODEV;
> +		goto out_unlock;
> +	}
> +
> +	ret = counter_disable_events(counter);
> +	if (ret < 0) {
> +		mutex_unlock(&counter->ops_exist_lock);
> +		return ret;
> +	}
> +
> +out_unlock:
> +	mutex_unlock(&counter->ops_exist_lock);
> +
> +	put_device(&counter->dev);
> +	clear_bit_unlock(0, counter->chrdev_lock);
> +
> +	return ret;
> +}
> +
> +static const struct file_operations counter_fops = {
> +	.owner = THIS_MODULE,
> +	.llseek = no_llseek,
> +	.read = counter_chrdev_read,
> +	.poll = counter_chrdev_poll,
> +	.unlocked_ioctl = counter_chrdev_ioctl,
> +	.open = counter_chrdev_open,
> +	.release = counter_chrdev_release,
> +};
> +
> +int counter_chrdev_add(struct counter_device *const counter)

To think about: This isn't doing the add. So would counter_chrdev_init() be more
appropriate? The fact it can fail due to the kfifo_alloc makes that
naming less than ideal though.

> +{
> +	/* Initialize Counter events lists */
> +	INIT_LIST_HEAD(&counter->events_list);
> +	INIT_LIST_HEAD(&counter->next_events_list);
> +	spin_lock_init(&counter->events_list_lock);
> +	mutex_init(&counter->n_events_list_lock);
> +	init_waitqueue_head(&counter->events_wait);
> +	mutex_init(&counter->events_lock);
> +
> +	/* Initialize character device */
> +	clear_bit(0, counter->chrdev_lock);
> +	cdev_init(&counter->chrdev, &counter_fops);
> +
> +	/* Allocate Counter events queue */
> +	return kfifo_alloc(&counter->events, 64, GFP_KERNEL);
> +}
> +
> +void counter_chrdev_remove(struct counter_device *const counter)
> +{
> +	kfifo_free(&counter->events);
> +}
> +
> +static int counter_get_data(struct counter_device *const counter,
> +			    const struct counter_comp_node *const comp_node,
> +			    u64 *const value)
> +{
> +	const struct counter_comp *const comp = &comp_node->comp;
> +	void *const parent = comp_node->parent;
> +	u8 value_u8 = 0;
> +	u32 value_u32 = 0;
> +	int ret;
> +
> +	if (comp_node->component.type == COUNTER_COMPONENT_NONE)
> +		return 0;
> +
> +	switch (comp->type) {
> +	case COUNTER_COMP_U8:
> +	case COUNTER_COMP_BOOL:
> +		switch (comp_node->component.scope) {
> +		case COUNTER_SCOPE_DEVICE:
> +			ret = comp->device_u8_read(counter, &value_u8);
> +			break;
> +		case COUNTER_SCOPE_SIGNAL:
> +			ret = comp->signal_u8_read(counter, parent, &value_u8);
> +			break;
> +		case COUNTER_SCOPE_COUNT:
> +			ret = comp->count_u8_read(counter, parent, &value_u8);
> +			break;
> +		}
> +		*value = value_u8;
> +		return ret;
> +	case COUNTER_COMP_SIGNAL_LEVEL:
> +	case COUNTER_COMP_FUNCTION:
> +	case COUNTER_COMP_ENUM:
> +	case COUNTER_COMP_COUNT_DIRECTION:
> +	case COUNTER_COMP_COUNT_MODE:
> +		switch (comp_node->component.scope) {
> +		case COUNTER_SCOPE_DEVICE:
> +			ret = comp->device_u32_read(counter, &value_u32);
> +			break;
> +		case COUNTER_SCOPE_SIGNAL:
> +			ret = comp->signal_u32_read(counter, parent,
> +						    &value_u32);
> +			break;
> +		case COUNTER_SCOPE_COUNT:
> +			ret = comp->count_u32_read(counter, parent, &value_u32);
> +			break;
> +		}
> +		*value = value_u32;
> +		return ret;
> +	case COUNTER_COMP_U64:
> +		switch (comp_node->component.scope) {
> +		case COUNTER_SCOPE_DEVICE:
> +			return comp->device_u64_read(counter, value);
> +		case COUNTER_SCOPE_SIGNAL:
> +			return comp->signal_u64_read(counter, parent, value);
> +		case COUNTER_SCOPE_COUNT:
> +			return comp->count_u64_read(counter, parent, value);
> +		default:
> +			return -EINVAL;
> +		}
> +	case COUNTER_COMP_SYNAPSE_ACTION:
> +		ret = comp->action_read(counter, parent, comp->priv,
> +					&value_u32);
> +		*value = value_u32;
> +		return ret;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +/**
> + * counter_push_event - queue event for userspace reading
> + * @counter:	pointer to Counter structure
> + * @event:	triggered event
> + * @channel:	event channel
> + *
> + * Note: If no one is watching for the respective event, it is silently
> + * discarded.
> + */
> +void counter_push_event(struct counter_device *const counter, const u8 event,
> +			const u8 channel)
> +{
> +	struct counter_event ev;
> +	unsigned int copied = 0;
> +	unsigned long flags;
> +	struct counter_event_node *event_node;
> +	struct counter_comp_node *comp_node;
> +
> +	ev.timestamp = ktime_get_ns();
> +	ev.watch.event = event;
> +	ev.watch.channel = channel;
> +
> +	/* Could be in an interrupt context, so use a spin lock */
> +	spin_lock_irqsave(&counter->events_list_lock, flags);
> +
> +	/* Search for event in the list */

Worth keeping in mind that searching a list is not exactly a scalable
solution even if it's fine for now.

> +	list_for_each_entry(event_node, &counter->events_list, l)
> +		if (event_node->event == event &&
> +		    event_node->channel == channel)
> +			break;
> +
> +	/* If event is not in the list */
> +	if (&event_node->l == &counter->events_list)
> +		goto exit_early;
> +
> +	/* Read and queue relevant comp for userspace */
> +	list_for_each_entry(comp_node, &event_node->comp_list, l) {
> +		ev.watch.component = comp_node->component;
> +		ev.status = -counter_get_data(counter, comp_node, &ev.value);
> +
> +		copied += kfifo_in(&counter->events, &ev, 1);

I had a short debate with myself on whether this or kfifo_put() was more appropriate
(as fixed record length).  I think this particular case they end up the same anyway
so it doesn't matter (nothing to do here!)
 
> +	}
> +
> +exit_early:
> +	spin_unlock_irqrestore(&counter->events_list_lock, flags);
> +
> +	if (copied)
> +		wake_up_poll(&counter->events_wait, EPOLLIN);
> +}
> +EXPORT_SYMBOL_GPL(counter_push_event);
> diff --git a/drivers/counter/counter-chrdev.h b/drivers/counter/counter-chrdev.h
> new file mode 100644
> index 000000000000..5529d16703c4
> --- /dev/null
> +++ b/drivers/counter/counter-chrdev.h
> @@ -0,0 +1,14 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Counter character device interface
> + * Copyright (C) 2020 William Breathitt Gray
> + */
> +#ifndef _COUNTER_CHRDEV_H_
> +#define _COUNTER_CHRDEV_H_
> +
> +#include <linux/counter.h>
> +
> +int counter_chrdev_add(struct counter_device *const counter);
> +void counter_chrdev_remove(struct counter_device *const counter);
> +
> +#endif /* _COUNTER_CHRDEV_H_ */
> diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
> index 3cda2c47bacb..28dc1ff63c94 100644
> --- a/drivers/counter/counter-core.c
> +++ b/drivers/counter/counter-core.c
> @@ -3,14 +3,22 @@
>   * Generic Counter interface
>   * Copyright (C) 2020 William Breathitt Gray
>   */
> +#include <linux/cdev.h>
>  #include <linux/counter.h>
>  #include <linux/device.h>
> +#include <linux/device/bus.h>
>  #include <linux/export.h>
> +#include <linux/fs.h>
>  #include <linux/gfp.h>
>  #include <linux/idr.h>
>  #include <linux/init.h>
> +#include <linux/kdev_t.h>
>  #include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/types.h>
> +#include <linux/wait.h>
>  
> +#include "counter-chrdev.h"
>  #include "counter-sysfs.h"
>  
>  /* Provides a unique ID for each counter device */
> @@ -18,6 +26,9 @@ static DEFINE_IDA(counter_ida);
>  
>  static void counter_device_release(struct device *dev)
>  {
> +	struct counter_device *const counter = dev_get_drvdata(dev);
> +
> +	counter_chrdev_remove(counter);
>  	ida_free(&counter_ida, dev->id);
>  }
>  
> @@ -31,6 +42,8 @@ static struct bus_type counter_bus_type = {
>  	.dev_name = "counter",
>  };
>  
> +static dev_t counter_devt;
> +
>  /**
>   * counter_register - register Counter to the system
>   * @counter:	pointer to Counter to register
> @@ -53,10 +66,13 @@ int counter_register(struct counter_device *const counter)
>  	if (id < 0)
>  		return id;
>  
> +	mutex_init(&counter->ops_exist_lock);
> +
>  	/* Configure device structure for Counter */
>  	dev->id = id;
>  	dev->type = &counter_device_type;
>  	dev->bus = &counter_bus_type;
> +	dev->devt = MKDEV(MAJOR(counter_devt), id);
>  	if (counter->parent) {
>  		dev->parent = counter->parent;
>  		dev->of_node = counter->parent->of_node;
> @@ -64,18 +80,22 @@ int counter_register(struct counter_device *const counter)
>  	device_initialize(dev);
>  	dev_set_drvdata(dev, counter);
>  
> -	/* Add Counter sysfs attributes */
> -	err = counter_sysfs_add(counter);
> +	err = counter_chrdev_add(counter);
>  	if (err < 0)
>  		goto err_free_id;
>  
> -	/* Add device to system */
> -	err = device_add(dev);
> +	err = counter_sysfs_add(counter);
>  	if (err < 0)
> -		goto err_free_id;
> +		goto err_remove_chrdev;
> +
> +	err = cdev_device_add(&counter->chrdev, dev);
> +	if (err < 0)
> +		goto err_remove_chrdev;
>  
>  	return 0;
>  
> +err_remove_chrdev:
> +	counter_chrdev_remove(counter);

Totally trivial, but the sysfs cleanup is managed by devm_ whilst this counter_chrdev_remove()
is manual.  That leaves us in a case where the unwind order is subtly different from the
reverse of the setup order.  Is there any reason to not do the sysfs part of register first
and hence have the ordering match?

>  err_free_id:
>  	put_device(dev);
>  	return err;
> @@ -93,7 +113,16 @@ void counter_unregister(struct counter_device *const counter)
>  	if (!counter)
>  		return;
>  
> -	device_unregister(&counter->dev);
> +	cdev_device_del(&counter->chrdev, &counter->dev);
> +
> +	mutex_lock(&counter->ops_exist_lock);
> +
> +	counter->ops = NULL;
> +	wake_up(&counter->events_wait);
> +
> +	mutex_unlock(&counter->ops_exist_lock);
> +
> +	put_device(&counter->dev);
>  }
>  EXPORT_SYMBOL_GPL(counter_unregister);
>  
> @@ -127,13 +156,30 @@ int devm_counter_register(struct device *dev,
>  }
>  EXPORT_SYMBOL_GPL(devm_counter_register);
>  
> +#define COUNTER_DEV_MAX 256
> +
>  static int __init counter_init(void)
>  {
> -	return bus_register(&counter_bus_type);
> +	int err;
> +
> +	err = bus_register(&counter_bus_type);

Hmm. This isn't ideal in IIO either, but logically bus_register() is the part that
exposes the infrastructure for other drivers to attach to so should be last thing
in init.  However, I'm fairly sure the dependency handing will ensure this init()
is finished before it tries to register any counter drivers.  Hence we are fine
anyway.

So in conclusion - nothing needs changing here...

> +	if (err < 0)
> +		return err;
> +
> +	err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter");
> +	if (err < 0)
> +		goto err_unregister_bus;
> +
> +	return 0;
> +
> +err_unregister_bus:
> +	bus_unregister(&counter_bus_type);
> +	return err;
>  }
>  
>  static void __exit counter_exit(void)
>  {
> +	unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX);
>  	bus_unregister(&counter_bus_type);
>  }
>  
> diff --git a/include/linux/counter.h b/include/linux/counter.h
> index 7c9f7e23953a..9c9984655f8c 100644
> --- a/include/linux/counter.h
> +++ b/include/linux/counter.h
> @@ -6,9 +6,14 @@
>  #ifndef _COUNTER_H_
>  #define _COUNTER_H_
>  
> +#include <linux/cdev.h>
>  #include <linux/device.h>
>  #include <linux/kernel.h>
> +#include <linux/kfifo.h>
> +#include <linux/mutex.h>
> +#include <linux/spinlock_types.h>
>  #include <linux/types.h>
> +#include <linux/wait.h>
>  #include <uapi/linux/counter.h>
>  
>  struct counter_device;
> @@ -199,6 +204,20 @@ struct counter_count {
>  	size_t num_ext;
>  };
>  
> +/**
> + * struct counter_event_node - Counter Event node
> + * @l:		list of current watching Counter events
> + * @event:	event that triggers
> + * @channel:	event channel
> + * @comp_list:	list of components to watch when event triggers
> + */
> +struct counter_event_node {
> +	struct list_head l;
> +	u8 event;
> +	u8 channel;
> +	struct list_head comp_list;
> +};
> +
>  /**
>   * struct counter_ops - Callbacks from driver
>   * @signal_read:	optional read callback for Signals. The read level of
> @@ -222,6 +241,13 @@ struct counter_count {
>   * @action_write:	optional write callback for Synapse action modes. The
>   *			action mode to write for the respective Synapse is
>   *			passed in via the action parameter.
> + * @events_configure:	optional write callback to configure events. The list of
> + *			struct counter_event_node may be accessed via the
> + *			events_list member of the counter parameter.
> + * @watch_validate:	optional callback to validate a watch. The Counter
> + *			component watch configuration is passed in via the watch
> + *			parameter. A return value of 0 indicates a valid Counter
> + *			component watch configuration.
>   */
>  struct counter_ops {
>  	int (*signal_read)(struct counter_device *counter,
> @@ -245,6 +271,9 @@ struct counter_ops {
>  			    struct counter_count *count,
>  			    struct counter_synapse *synapse,
>  			    enum counter_synapse_action action);
> +	int (*events_configure)(struct counter_device *counter);
> +	int (*watch_validate)(struct counter_device *counter,
> +			      const struct counter_watch *watch);
>  };
>  
>  /**
> @@ -260,6 +289,16 @@ struct counter_ops {
>   * @num_ext:		number of Counter device extensions specified in @ext
>   * @priv:		optional private data supplied by driver
>   * @dev:		internal device structure
> + * @chrdev:		internal character device structure
> + * @events_list:	list of current watching Counter events
> + * @events_list_lock:	lock to protect Counter events list operations
> + * @next_events_list:	list of next watching Counter events
> + * @n_events_list_lock:	lock to protect Counter next events list operations
> + * @events:		queue of detected Counter events
> + * @events_wait:	wait queue to allow blocking reads of Counter events
> + * @events_lock:	lock to protect Counter events queue read operations
> + * @chrdev_lock:	lock to limit chrdev to a single open at a time
> + * @ops_exist_lock:	lock to prevent use during removal
>   */
>  struct counter_device {
>  	const char *name;
> @@ -278,12 +317,24 @@ struct counter_device {
>  	void *priv;
>  
>  	struct device dev;
> +	struct cdev chrdev;
> +	struct list_head events_list;
> +	spinlock_t events_list_lock;
> +	struct list_head next_events_list;
> +	struct mutex n_events_list_lock;
> +	DECLARE_KFIFO_PTR(events, struct counter_event);
> +	wait_queue_head_t events_wait;
> +	struct mutex events_lock;
> +	DECLARE_BITMAP(chrdev_lock, 1);

Why use a bitmap for this rather than any of the other lock types?

> +	struct mutex ops_exist_lock;
>  };
>  
>  int counter_register(struct counter_device *const counter);
>  void counter_unregister(struct counter_device *const counter);
>  int devm_counter_register(struct device *dev,
>  			  struct counter_device *const counter);
> +void counter_push_event(struct counter_device *const counter, const u8 event,
> +			const u8 channel);
>  
>  #define COUNTER_COMP_DEVICE_U8(_name, _read, _write) \
>  { \

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 07/14] counter: Add character device interface
@ 2021-09-12 16:18     ` Jonathan Cameron
  0 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-12 16:18 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	Dan Carpenter

On Fri, 27 Aug 2021 12:47:51 +0900
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> This patch introduces a character device interface for the Counter
> subsystem. Device data is exposed through standard character device read
> operations. Device data is gathered when a Counter event is pushed by
> the respective Counter device driver. Configuration is handled via ioctl
> operations on the respective Counter character device node.
> 
> Cc: David Lechner <david@lechnology.com>
> Cc: Gwendal Grignou <gwendal@chromium.org>
> Cc: Dan Carpenter <dan.carpenter@oracle.com>
> Cc: Oleksij Rempel <o.rempel@pengutronix.de>
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>

Hi William,

Why the bit based lock?  It feels like a mutex_trylock() type approach or
spinlock_trylock() would be a more common solution to this problem.
There is precedence for doing what you have here though so I'm not that
worried about it.

There are a few more things inline.

I've now been through this patch with as fine toothed comb as I'm likely to
do so.  Hence I won't do another review unless there are substantial changes.
I nearly applied it as it stands, but given we aren't in a rush (merge window
open), it's worth just a little more time to tidy up loose ends.

Jonathan


> ---
>  drivers/counter/Makefile         |   2 +-
>  drivers/counter/counter-chrdev.c | 553 +++++++++++++++++++++++++++++++
>  drivers/counter/counter-chrdev.h |  14 +
>  drivers/counter/counter-core.c   |  60 +++-
>  include/linux/counter.h          |  51 +++
>  include/uapi/linux/counter.h     |  98 ++++++
>  6 files changed, 770 insertions(+), 8 deletions(-)
>  create mode 100644 drivers/counter/counter-chrdev.c
>  create mode 100644 drivers/counter/counter-chrdev.h
> 
> diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
> index 1ab7e087fdc2..8fde6c100ebc 100644
> --- a/drivers/counter/Makefile
> +++ b/drivers/counter/Makefile
> @@ -4,7 +4,7 @@
>  #
>  
>  obj-$(CONFIG_COUNTER) += counter.o
> -counter-y := counter-core.o counter-sysfs.o
> +counter-y := counter-core.o counter-sysfs.o counter-chrdev.o
>  
>  obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
>  obj-$(CONFIG_INTERRUPT_CNT)		+= interrupt-cnt.o
> diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c
> new file mode 100644
> index 000000000000..30659b67d8f3
> --- /dev/null
> +++ b/drivers/counter/counter-chrdev.c
> @@ -0,0 +1,553 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Generic Counter character device interface
> + * Copyright (C) 2020 William Breathitt Gray
> + */
> +#include <linux/bitops.h>
> +#include <linux/cdev.h>
> +#include <linux/counter.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/export.h>
> +#include <linux/fs.h>
> +#include <linux/kfifo.h>
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> +#include <linux/nospec.h>
> +#include <linux/poll.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/timekeeping.h>
> +#include <linux/types.h>
> +#include <linux/uaccess.h>
> +#include <linux/wait.h>
> +
> +#include "counter-chrdev.h"
> +
> +struct counter_comp_node {
> +	struct list_head l;
> +	struct counter_component component;
> +	struct counter_comp comp;
> +	void *parent;
> +};
> +
> +static ssize_t counter_chrdev_read(struct file *filp, char __user *buf,
> +				   size_t len, loff_t *f_ps)
> +{
> +	struct counter_device *const counter = filp->private_data;
> +	int err;
> +	unsigned int copied;
> +
> +	if (!counter->ops)
> +		return -ENODEV;
> +
> +	if (len < sizeof(struct counter_event))
> +		return -EINVAL;
> +
> +	do {
> +		if (kfifo_is_empty(&counter->events)) {
> +			if (filp->f_flags & O_NONBLOCK)
> +				return -EAGAIN;
> +
> +			err = wait_event_interruptible(counter->events_wait,
> +					!kfifo_is_empty(&counter->events) ||
> +					!counter->ops);
> +			if (err < 0)
> +				return err;
> +			if (!counter->ops)
> +				return -ENODEV;
> +		}
> +
> +		if (mutex_lock_interruptible(&counter->events_lock))
> +			return -ERESTARTSYS;
> +		err = kfifo_to_user(&counter->events, buf, len, &copied);
> +		mutex_unlock(&counter->events_lock);
> +		if (err < 0)
> +			return err;
> +	} while (!copied);
> +
> +	return copied;
> +}
> +
> +static __poll_t counter_chrdev_poll(struct file *filp,
> +				    struct poll_table_struct *pollt)
> +{
> +	struct counter_device *const counter = filp->private_data;
> +	__poll_t events = 0;
> +
> +	if (!counter->ops)
> +		return events;
> +
> +	poll_wait(filp, &counter->events_wait, pollt);
> +
> +	if (!kfifo_is_empty(&counter->events))
> +		events = EPOLLIN | EPOLLRDNORM;
> +
> +	return events;
> +}
> +
> +static void counter_events_list_free(struct list_head *const events_list)
> +{
> +	struct counter_event_node *p, *n;
> +	struct counter_comp_node *q, *o;
> +
> +	list_for_each_entry_safe(p, n, events_list, l) {
> +		/* Free associated component nodes */
> +		list_for_each_entry_safe(q, o, &p->comp_list, l) {
> +			list_del(&q->l);
> +			kfree(q);
> +		}
> +
> +		/* Free event node */
> +		list_del(&p->l);
> +		kfree(p);
> +	}
> +}
> +
> +static int counter_set_event_node(struct counter_device *const counter,
> +				  struct counter_watch *const watch,
> +				  const struct counter_comp_node *const cfg)
> +{
> +	struct counter_event_node *event_node;
> +	int err = 0;
> +	struct counter_comp_node *comp_node;
> +
> +	/* Search for event in the list */
> +	list_for_each_entry(event_node, &counter->next_events_list, l)
> +		if (event_node->event == watch->event &&
> +		    event_node->channel == watch->channel)
> +			break;
> +
> +	/* If event is not already in the list */
> +	if (&event_node->l == &counter->next_events_list) {
> +		/* Allocate new event node */
> +		event_node = kmalloc(sizeof(*event_node), GFP_KERNEL);
> +		if (!event_node)
> +			return -ENOMEM;
> +
> +		/* Configure event node and add to the list */
> +		event_node->event = watch->event;
> +		event_node->channel = watch->channel;
> +		INIT_LIST_HEAD(&event_node->comp_list);
> +		list_add(&event_node->l, &counter->next_events_list);
> +	}
> +
> +	/* Check if component watch has already been set before */
> +	list_for_each_entry(comp_node, &event_node->comp_list, l)
> +		if (comp_node->parent == cfg->parent &&
> +		    comp_node->comp.count_u8_read == cfg->comp.count_u8_read) {
> +			err = -EINVAL;
> +			goto exit_free_event_node;
> +		}
> +
> +	/* Allocate component node */
> +	comp_node = kmalloc(sizeof(*comp_node), GFP_KERNEL);
> +	if (!comp_node) {
> +		err = -ENOMEM;
> +		goto exit_free_event_node;
> +	}
> +	*comp_node = *cfg;
> +
> +	/* Add component node to event node */
> +	list_add_tail(&comp_node->l, &event_node->comp_list);
> +
> +exit_free_event_node:
> +	/* Free event node if no one else is watching */
> +	if (list_empty(&event_node->comp_list)) {
> +		list_del(&event_node->l);
> +		kfree(event_node);
> +	}
> +
> +	return err;
> +}
> +
> +static int counter_enable_events(struct counter_device *const counter)
> +{
> +	unsigned long flags;
> +	int err = 0;
> +
> +	mutex_lock(&counter->n_events_list_lock);
> +	spin_lock_irqsave(&counter->events_list_lock, flags);
> +
> +	counter_events_list_free(&counter->events_list);
> +	list_replace_init(&counter->next_events_list,
> +			  &counter->events_list);
> +
> +	if (counter->ops->events_configure)
> +		err = counter->ops->events_configure(counter);
> +
> +	spin_unlock_irqrestore(&counter->events_list_lock, flags);
> +	mutex_unlock(&counter->n_events_list_lock);
> +
> +	return err;
> +}
> +
> +static int counter_disable_events(struct counter_device *const counter)
> +{
> +	unsigned long flags;
> +	int err = 0;
> +
> +	spin_lock_irqsave(&counter->events_list_lock, flags);
> +
> +	counter_events_list_free(&counter->events_list);
> +
> +	if (counter->ops->events_configure)
> +		err = counter->ops->events_configure(counter);
> +
> +	spin_unlock_irqrestore(&counter->events_list_lock, flags);
> +
> +	mutex_lock(&counter->n_events_list_lock);
> +
> +	counter_events_list_free(&counter->next_events_list);
> +
> +	mutex_unlock(&counter->n_events_list_lock);
> +
> +	return err;
> +}
> +
> +static int counter_add_watch(struct counter_device *const counter,
> +			     const unsigned long arg)
> +{
> +	void __user *const uwatch = (void __user *)arg;
> +	struct counter_watch watch;
> +	struct counter_comp_node comp_node = {};
> +	size_t parent, id;
> +	struct counter_comp *ext;
> +	size_t num_ext;
> +	int err = 0;
> +
> +	if (copy_from_user(&watch, uwatch, sizeof(watch)))
> +		return -EFAULT;
> +
> +	if (watch.component.type == COUNTER_COMPONENT_NONE)
> +		goto no_component;
> +
> +	parent = watch.component.parent;
> +
> +	/* Configure parent component info for comp node */
> +	switch (watch.component.scope) {
> +	case COUNTER_SCOPE_DEVICE:
> +		ext = counter->ext;
> +		num_ext = counter->num_ext;
> +		break;
> +	case COUNTER_SCOPE_SIGNAL:
> +		if (parent >= counter->num_signals)
> +			return -EINVAL;
> +		parent = array_index_nospec(parent, counter->num_signals);
> +
> +		comp_node.parent = counter->signals + parent;
> +
> +		ext = counter->signals[parent].ext;
> +		num_ext = counter->signals[parent].num_ext;
> +		break;
> +	case COUNTER_SCOPE_COUNT:
> +		if (parent >= counter->num_counts)
> +			return -EINVAL;
> +		parent = array_index_nospec(parent, counter->num_counts);
> +
> +		comp_node.parent = counter->counts + parent;
> +
> +		ext = counter->counts[parent].ext;
> +		num_ext = counter->counts[parent].num_ext;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	id = watch.component.id;
> +
> +	/* Configure component info for comp node */
> +	switch (watch.component.type) {
> +	case COUNTER_COMPONENT_SIGNAL:
> +		if (watch.component.scope != COUNTER_SCOPE_SIGNAL)
> +			return -EINVAL;
> +
> +		comp_node.comp.type = COUNTER_COMP_SIGNAL_LEVEL;
> +		comp_node.comp.signal_u32_read = counter->ops->signal_read;
> +		break;
> +	case COUNTER_COMPONENT_COUNT:
> +		if (watch.component.scope != COUNTER_SCOPE_COUNT)
> +			return -EINVAL;
> +
> +		comp_node.comp.type = COUNTER_COMP_U64;
> +		comp_node.comp.count_u64_read = counter->ops->count_read;
> +		break;
> +	case COUNTER_COMPONENT_FUNCTION:
> +		if (watch.component.scope != COUNTER_SCOPE_COUNT)
> +			return -EINVAL;
> +
> +		comp_node.comp.type = COUNTER_COMP_FUNCTION;
> +		comp_node.comp.count_u32_read = counter->ops->function_read;
> +		break;
> +	case COUNTER_COMPONENT_SYNAPSE_ACTION:
> +		if (watch.component.scope != COUNTER_SCOPE_COUNT)
> +			return -EINVAL;
> +		if (id >= counter->counts[parent].num_synapses)
> +			return -EINVAL;
> +		id = array_index_nospec(id, counter->counts[parent].num_synapses);
> +
> +		comp_node.comp.type = COUNTER_COMP_SYNAPSE_ACTION;
> +		comp_node.comp.action_read = counter->ops->action_read;
> +		comp_node.comp.priv = counter->counts[parent].synapses + id;
> +		break;
> +	case COUNTER_COMPONENT_EXTENSION:
> +		if (id >= num_ext)
> +			return -EINVAL;
> +		id = array_index_nospec(id, num_ext);
> +
> +		comp_node.comp = ext[id];
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	/* Check if any read callback is set; this is part of a union */
> +	if (!comp_node.comp.count_u8_read)

This isn't valid for C.  The compiler is allowed to treat the elements
of a union as not being in the same memory etc unless they are of the same
type (which isn't true here - I think anyway)...

So whilst it seems silly you need to check all the callbacks in this if
statement. It will 'almost' always work as you have it, but it's possible
some future optimisation by the compiler will mean that u8_read would be
set by this point, but it delayed setting e.g. u32_read until after
this check.

> +		return -EOPNOTSUPP;
> +
> +no_component:
> +	mutex_lock(&counter->n_events_list_lock);
> +
> +	if (counter->ops->watch_validate) {
> +		err = counter->ops->watch_validate(counter, &watch);
> +		if (err < 0)
> +			goto err_exit;
> +	}
> +
> +	comp_node.component = watch.component;
> +
> +	err = counter_set_event_node(counter, &watch, &comp_node);
> +
> +err_exit:
> +	mutex_unlock(&counter->n_events_list_lock);
> +
> +	return err;
> +}
> +
> +static long counter_chrdev_ioctl(struct file *filp, unsigned int cmd,
> +				 unsigned long arg)
> +{
> +	struct counter_device *const counter = filp->private_data;
> +	int ret = -ENODEV;
> +
> +	mutex_lock(&counter->ops_exist_lock);
> +
> +	if (!counter->ops)
> +		goto out_unlock;
> +
> +	switch (cmd) {
> +	case COUNTER_ADD_WATCH_IOCTL:
> +		ret = counter_add_watch(counter, arg);
> +		break;
> +	case COUNTER_ENABLE_EVENTS_IOCTL:
> +		ret = counter_enable_events(counter);
> +		break;
> +	case COUNTER_DISABLE_EVENTS_IOCTL:
> +		ret = counter_disable_events(counter);
> +		break;
> +	default:
> +		ret = -ENOIOCTLCMD;
> +		break;
> +	}
> +
> +out_unlock:
> +	mutex_unlock(&counter->ops_exist_lock);
> +
> +	return ret;
> +}
> +
> +static int counter_chrdev_open(struct inode *inode, struct file *filp)
> +{
> +	struct counter_device *const counter = container_of(inode->i_cdev,
> +							    typeof(*counter),
> +							    chrdev);
> +
> +	if (test_and_set_bit_lock(0, counter->chrdev_lock))
> +		return -EBUSY;
> +
> +	get_device(&counter->dev);
> +	filp->private_data = counter;
> +
> +	return nonseekable_open(inode, filp);
> +}
> +
> +static int counter_chrdev_release(struct inode *inode, struct file *filp)
> +{
> +	struct counter_device *const counter = filp->private_data;
> +	int ret = 0;
> +
> +	mutex_lock(&counter->ops_exist_lock);
> +
> +	if (!counter->ops) {

This needs a comment to explain how you would get here and
why these two lists need cleaning up here if we do.

Superficially it feels to me like you could just add a counter->ops check in
counter_disable_events() and then call that directly.  I'm guessing
I am missing a deadlock or similar however.


> +		counter_events_list_free(&counter->events_list);
> +		counter_events_list_free(&counter->next_events_list);
> +		ret = -ENODEV;
> +		goto out_unlock;
> +	}
> +
> +	ret = counter_disable_events(counter);
> +	if (ret < 0) {
> +		mutex_unlock(&counter->ops_exist_lock);
> +		return ret;
> +	}
> +
> +out_unlock:
> +	mutex_unlock(&counter->ops_exist_lock);
> +
> +	put_device(&counter->dev);
> +	clear_bit_unlock(0, counter->chrdev_lock);
> +
> +	return ret;
> +}
> +
> +static const struct file_operations counter_fops = {
> +	.owner = THIS_MODULE,
> +	.llseek = no_llseek,
> +	.read = counter_chrdev_read,
> +	.poll = counter_chrdev_poll,
> +	.unlocked_ioctl = counter_chrdev_ioctl,
> +	.open = counter_chrdev_open,
> +	.release = counter_chrdev_release,
> +};
> +
> +int counter_chrdev_add(struct counter_device *const counter)

To think about: This isn't doing the add. So would counter_chrdev_init() be more
appropriate? The fact it can fail due to the kfifo_alloc makes that
naming less than ideal though.

> +{
> +	/* Initialize Counter events lists */
> +	INIT_LIST_HEAD(&counter->events_list);
> +	INIT_LIST_HEAD(&counter->next_events_list);
> +	spin_lock_init(&counter->events_list_lock);
> +	mutex_init(&counter->n_events_list_lock);
> +	init_waitqueue_head(&counter->events_wait);
> +	mutex_init(&counter->events_lock);
> +
> +	/* Initialize character device */
> +	clear_bit(0, counter->chrdev_lock);
> +	cdev_init(&counter->chrdev, &counter_fops);
> +
> +	/* Allocate Counter events queue */
> +	return kfifo_alloc(&counter->events, 64, GFP_KERNEL);
> +}
> +
> +void counter_chrdev_remove(struct counter_device *const counter)
> +{
> +	kfifo_free(&counter->events);
> +}
> +
> +static int counter_get_data(struct counter_device *const counter,
> +			    const struct counter_comp_node *const comp_node,
> +			    u64 *const value)
> +{
> +	const struct counter_comp *const comp = &comp_node->comp;
> +	void *const parent = comp_node->parent;
> +	u8 value_u8 = 0;
> +	u32 value_u32 = 0;
> +	int ret;
> +
> +	if (comp_node->component.type == COUNTER_COMPONENT_NONE)
> +		return 0;
> +
> +	switch (comp->type) {
> +	case COUNTER_COMP_U8:
> +	case COUNTER_COMP_BOOL:
> +		switch (comp_node->component.scope) {
> +		case COUNTER_SCOPE_DEVICE:
> +			ret = comp->device_u8_read(counter, &value_u8);
> +			break;
> +		case COUNTER_SCOPE_SIGNAL:
> +			ret = comp->signal_u8_read(counter, parent, &value_u8);
> +			break;
> +		case COUNTER_SCOPE_COUNT:
> +			ret = comp->count_u8_read(counter, parent, &value_u8);
> +			break;
> +		}
> +		*value = value_u8;
> +		return ret;
> +	case COUNTER_COMP_SIGNAL_LEVEL:
> +	case COUNTER_COMP_FUNCTION:
> +	case COUNTER_COMP_ENUM:
> +	case COUNTER_COMP_COUNT_DIRECTION:
> +	case COUNTER_COMP_COUNT_MODE:
> +		switch (comp_node->component.scope) {
> +		case COUNTER_SCOPE_DEVICE:
> +			ret = comp->device_u32_read(counter, &value_u32);
> +			break;
> +		case COUNTER_SCOPE_SIGNAL:
> +			ret = comp->signal_u32_read(counter, parent,
> +						    &value_u32);
> +			break;
> +		case COUNTER_SCOPE_COUNT:
> +			ret = comp->count_u32_read(counter, parent, &value_u32);
> +			break;
> +		}
> +		*value = value_u32;
> +		return ret;
> +	case COUNTER_COMP_U64:
> +		switch (comp_node->component.scope) {
> +		case COUNTER_SCOPE_DEVICE:
> +			return comp->device_u64_read(counter, value);
> +		case COUNTER_SCOPE_SIGNAL:
> +			return comp->signal_u64_read(counter, parent, value);
> +		case COUNTER_SCOPE_COUNT:
> +			return comp->count_u64_read(counter, parent, value);
> +		default:
> +			return -EINVAL;
> +		}
> +	case COUNTER_COMP_SYNAPSE_ACTION:
> +		ret = comp->action_read(counter, parent, comp->priv,
> +					&value_u32);
> +		*value = value_u32;
> +		return ret;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +/**
> + * counter_push_event - queue event for userspace reading
> + * @counter:	pointer to Counter structure
> + * @event:	triggered event
> + * @channel:	event channel
> + *
> + * Note: If no one is watching for the respective event, it is silently
> + * discarded.
> + */
> +void counter_push_event(struct counter_device *const counter, const u8 event,
> +			const u8 channel)
> +{
> +	struct counter_event ev;
> +	unsigned int copied = 0;
> +	unsigned long flags;
> +	struct counter_event_node *event_node;
> +	struct counter_comp_node *comp_node;
> +
> +	ev.timestamp = ktime_get_ns();
> +	ev.watch.event = event;
> +	ev.watch.channel = channel;
> +
> +	/* Could be in an interrupt context, so use a spin lock */
> +	spin_lock_irqsave(&counter->events_list_lock, flags);
> +
> +	/* Search for event in the list */

Worth keeping in mind that searching a list is not exactly a scalable
solution even if it's fine for now.

> +	list_for_each_entry(event_node, &counter->events_list, l)
> +		if (event_node->event == event &&
> +		    event_node->channel == channel)
> +			break;
> +
> +	/* If event is not in the list */
> +	if (&event_node->l == &counter->events_list)
> +		goto exit_early;
> +
> +	/* Read and queue relevant comp for userspace */
> +	list_for_each_entry(comp_node, &event_node->comp_list, l) {
> +		ev.watch.component = comp_node->component;
> +		ev.status = -counter_get_data(counter, comp_node, &ev.value);
> +
> +		copied += kfifo_in(&counter->events, &ev, 1);

I had a short debate with myself on whether this or kfifo_put() was more appropriate
(as fixed record length).  I think this particular case they end up the same anyway
so it doesn't matter (nothing to do here!)
 
> +	}
> +
> +exit_early:
> +	spin_unlock_irqrestore(&counter->events_list_lock, flags);
> +
> +	if (copied)
> +		wake_up_poll(&counter->events_wait, EPOLLIN);
> +}
> +EXPORT_SYMBOL_GPL(counter_push_event);
> diff --git a/drivers/counter/counter-chrdev.h b/drivers/counter/counter-chrdev.h
> new file mode 100644
> index 000000000000..5529d16703c4
> --- /dev/null
> +++ b/drivers/counter/counter-chrdev.h
> @@ -0,0 +1,14 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Counter character device interface
> + * Copyright (C) 2020 William Breathitt Gray
> + */
> +#ifndef _COUNTER_CHRDEV_H_
> +#define _COUNTER_CHRDEV_H_
> +
> +#include <linux/counter.h>
> +
> +int counter_chrdev_add(struct counter_device *const counter);
> +void counter_chrdev_remove(struct counter_device *const counter);
> +
> +#endif /* _COUNTER_CHRDEV_H_ */
> diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
> index 3cda2c47bacb..28dc1ff63c94 100644
> --- a/drivers/counter/counter-core.c
> +++ b/drivers/counter/counter-core.c
> @@ -3,14 +3,22 @@
>   * Generic Counter interface
>   * Copyright (C) 2020 William Breathitt Gray
>   */
> +#include <linux/cdev.h>
>  #include <linux/counter.h>
>  #include <linux/device.h>
> +#include <linux/device/bus.h>
>  #include <linux/export.h>
> +#include <linux/fs.h>
>  #include <linux/gfp.h>
>  #include <linux/idr.h>
>  #include <linux/init.h>
> +#include <linux/kdev_t.h>
>  #include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/types.h>
> +#include <linux/wait.h>
>  
> +#include "counter-chrdev.h"
>  #include "counter-sysfs.h"
>  
>  /* Provides a unique ID for each counter device */
> @@ -18,6 +26,9 @@ static DEFINE_IDA(counter_ida);
>  
>  static void counter_device_release(struct device *dev)
>  {
> +	struct counter_device *const counter = dev_get_drvdata(dev);
> +
> +	counter_chrdev_remove(counter);
>  	ida_free(&counter_ida, dev->id);
>  }
>  
> @@ -31,6 +42,8 @@ static struct bus_type counter_bus_type = {
>  	.dev_name = "counter",
>  };
>  
> +static dev_t counter_devt;
> +
>  /**
>   * counter_register - register Counter to the system
>   * @counter:	pointer to Counter to register
> @@ -53,10 +66,13 @@ int counter_register(struct counter_device *const counter)
>  	if (id < 0)
>  		return id;
>  
> +	mutex_init(&counter->ops_exist_lock);
> +
>  	/* Configure device structure for Counter */
>  	dev->id = id;
>  	dev->type = &counter_device_type;
>  	dev->bus = &counter_bus_type;
> +	dev->devt = MKDEV(MAJOR(counter_devt), id);
>  	if (counter->parent) {
>  		dev->parent = counter->parent;
>  		dev->of_node = counter->parent->of_node;
> @@ -64,18 +80,22 @@ int counter_register(struct counter_device *const counter)
>  	device_initialize(dev);
>  	dev_set_drvdata(dev, counter);
>  
> -	/* Add Counter sysfs attributes */
> -	err = counter_sysfs_add(counter);
> +	err = counter_chrdev_add(counter);
>  	if (err < 0)
>  		goto err_free_id;
>  
> -	/* Add device to system */
> -	err = device_add(dev);
> +	err = counter_sysfs_add(counter);
>  	if (err < 0)
> -		goto err_free_id;
> +		goto err_remove_chrdev;
> +
> +	err = cdev_device_add(&counter->chrdev, dev);
> +	if (err < 0)
> +		goto err_remove_chrdev;
>  
>  	return 0;
>  
> +err_remove_chrdev:
> +	counter_chrdev_remove(counter);

Totally trivial, but the sysfs cleanup is managed by devm_ whilst this counter_chrdev_remove()
is manual.  That leaves us in a case where the unwind order is subtly different from the
reverse of the setup order.  Is there any reason to not do the sysfs part of register first
and hence have the ordering match?

>  err_free_id:
>  	put_device(dev);
>  	return err;
> @@ -93,7 +113,16 @@ void counter_unregister(struct counter_device *const counter)
>  	if (!counter)
>  		return;
>  
> -	device_unregister(&counter->dev);
> +	cdev_device_del(&counter->chrdev, &counter->dev);
> +
> +	mutex_lock(&counter->ops_exist_lock);
> +
> +	counter->ops = NULL;
> +	wake_up(&counter->events_wait);
> +
> +	mutex_unlock(&counter->ops_exist_lock);
> +
> +	put_device(&counter->dev);
>  }
>  EXPORT_SYMBOL_GPL(counter_unregister);
>  
> @@ -127,13 +156,30 @@ int devm_counter_register(struct device *dev,
>  }
>  EXPORT_SYMBOL_GPL(devm_counter_register);
>  
> +#define COUNTER_DEV_MAX 256
> +
>  static int __init counter_init(void)
>  {
> -	return bus_register(&counter_bus_type);
> +	int err;
> +
> +	err = bus_register(&counter_bus_type);

Hmm. This isn't ideal in IIO either, but logically bus_register() is the part that
exposes the infrastructure for other drivers to attach to so should be last thing
in init.  However, I'm fairly sure the dependency handing will ensure this init()
is finished before it tries to register any counter drivers.  Hence we are fine
anyway.

So in conclusion - nothing needs changing here...

> +	if (err < 0)
> +		return err;
> +
> +	err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter");
> +	if (err < 0)
> +		goto err_unregister_bus;
> +
> +	return 0;
> +
> +err_unregister_bus:
> +	bus_unregister(&counter_bus_type);
> +	return err;
>  }
>  
>  static void __exit counter_exit(void)
>  {
> +	unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX);
>  	bus_unregister(&counter_bus_type);
>  }
>  
> diff --git a/include/linux/counter.h b/include/linux/counter.h
> index 7c9f7e23953a..9c9984655f8c 100644
> --- a/include/linux/counter.h
> +++ b/include/linux/counter.h
> @@ -6,9 +6,14 @@
>  #ifndef _COUNTER_H_
>  #define _COUNTER_H_
>  
> +#include <linux/cdev.h>
>  #include <linux/device.h>
>  #include <linux/kernel.h>
> +#include <linux/kfifo.h>
> +#include <linux/mutex.h>
> +#include <linux/spinlock_types.h>
>  #include <linux/types.h>
> +#include <linux/wait.h>
>  #include <uapi/linux/counter.h>
>  
>  struct counter_device;
> @@ -199,6 +204,20 @@ struct counter_count {
>  	size_t num_ext;
>  };
>  
> +/**
> + * struct counter_event_node - Counter Event node
> + * @l:		list of current watching Counter events
> + * @event:	event that triggers
> + * @channel:	event channel
> + * @comp_list:	list of components to watch when event triggers
> + */
> +struct counter_event_node {
> +	struct list_head l;
> +	u8 event;
> +	u8 channel;
> +	struct list_head comp_list;
> +};
> +
>  /**
>   * struct counter_ops - Callbacks from driver
>   * @signal_read:	optional read callback for Signals. The read level of
> @@ -222,6 +241,13 @@ struct counter_count {
>   * @action_write:	optional write callback for Synapse action modes. The
>   *			action mode to write for the respective Synapse is
>   *			passed in via the action parameter.
> + * @events_configure:	optional write callback to configure events. The list of
> + *			struct counter_event_node may be accessed via the
> + *			events_list member of the counter parameter.
> + * @watch_validate:	optional callback to validate a watch. The Counter
> + *			component watch configuration is passed in via the watch
> + *			parameter. A return value of 0 indicates a valid Counter
> + *			component watch configuration.
>   */
>  struct counter_ops {
>  	int (*signal_read)(struct counter_device *counter,
> @@ -245,6 +271,9 @@ struct counter_ops {
>  			    struct counter_count *count,
>  			    struct counter_synapse *synapse,
>  			    enum counter_synapse_action action);
> +	int (*events_configure)(struct counter_device *counter);
> +	int (*watch_validate)(struct counter_device *counter,
> +			      const struct counter_watch *watch);
>  };
>  
>  /**
> @@ -260,6 +289,16 @@ struct counter_ops {
>   * @num_ext:		number of Counter device extensions specified in @ext
>   * @priv:		optional private data supplied by driver
>   * @dev:		internal device structure
> + * @chrdev:		internal character device structure
> + * @events_list:	list of current watching Counter events
> + * @events_list_lock:	lock to protect Counter events list operations
> + * @next_events_list:	list of next watching Counter events
> + * @n_events_list_lock:	lock to protect Counter next events list operations
> + * @events:		queue of detected Counter events
> + * @events_wait:	wait queue to allow blocking reads of Counter events
> + * @events_lock:	lock to protect Counter events queue read operations
> + * @chrdev_lock:	lock to limit chrdev to a single open at a time
> + * @ops_exist_lock:	lock to prevent use during removal
>   */
>  struct counter_device {
>  	const char *name;
> @@ -278,12 +317,24 @@ struct counter_device {
>  	void *priv;
>  
>  	struct device dev;
> +	struct cdev chrdev;
> +	struct list_head events_list;
> +	spinlock_t events_list_lock;
> +	struct list_head next_events_list;
> +	struct mutex n_events_list_lock;
> +	DECLARE_KFIFO_PTR(events, struct counter_event);
> +	wait_queue_head_t events_wait;
> +	struct mutex events_lock;
> +	DECLARE_BITMAP(chrdev_lock, 1);

Why use a bitmap for this rather than any of the other lock types?

> +	struct mutex ops_exist_lock;
>  };
>  
>  int counter_register(struct counter_device *const counter);
>  void counter_unregister(struct counter_device *const counter);
>  int devm_counter_register(struct device *dev,
>  			  struct counter_device *const counter);
> +void counter_push_event(struct counter_device *const counter, const u8 event,
> +			const u8 channel);
>  
>  #define COUNTER_COMP_DEVICE_U8(_name, _read, _write) \
>  { \

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 09/14] tools/counter: Create Counter tools
  2021-08-27  3:47   ` William Breathitt Gray
@ 2021-09-12 16:26     ` Jonathan Cameron
  -1 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-12 16:26 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	Pavel Machek

On Fri, 27 Aug 2021 12:47:53 +0900
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> This creates an example Counter program under tools/counter/*
> to exemplify the Counter character device interface.
> 
> Cc: Pavel Machek <pavel@ucw.cz>
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>

It's great to have this example, but it very much an example rather than
a generic tool. You may want to revisit and provide a more generic
tool in the future.

A trivial comment about using a loop inline.

Jonathan

> ---
>  MAINTAINERS                     |  1 +
>  tools/Makefile                  | 13 ++---
>  tools/counter/Build             |  1 +
>  tools/counter/Makefile          | 53 +++++++++++++++++++
>  tools/counter/counter_example.c | 93 +++++++++++++++++++++++++++++++++
>  5 files changed, 155 insertions(+), 6 deletions(-)
>  create mode 100644 tools/counter/Build
>  create mode 100644 tools/counter/Makefile
>  create mode 100644 tools/counter/counter_example.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index f2fdd2202605..57dc9b6ff82a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4782,6 +4782,7 @@ F:	Documentation/driver-api/generic-counter.rst
>  F:	drivers/counter/
>  F:	include/linux/counter.h
>  F:	include/uapi/linux/counter.h
> +F:	tools/counter/
>  
>  CP2615 I2C DRIVER
>  M:	Bence Csókás <bence98@sch.bme.hu>
> diff --git a/tools/Makefile b/tools/Makefile
> index 7e9d34ddd74c..5da1fde03a9a 100644
> --- a/tools/Makefile
> +++ b/tools/Makefile
> @@ -12,6 +12,7 @@ help:
>  	@echo '  acpi                   - ACPI tools'
>  	@echo '  bpf                    - misc BPF tools'
>  	@echo '  cgroup                 - cgroup tools'
> +	@echo '  counter                - counter tools'
>  	@echo '  cpupower               - a tool for all things x86 CPU power'
>  	@echo '  debugging              - tools for debugging'
>  	@echo '  firewire               - the userspace part of nosy, an IEEE-1394 traffic sniffer'
> @@ -65,7 +66,7 @@ acpi: FORCE
>  cpupower: FORCE
>  	$(call descend,power/$@)
>  
> -cgroup firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE
> +cgroup counter firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE
>  	$(call descend,$@)
>  
>  bpf/%: FORCE
> @@ -100,7 +101,7 @@ freefall: FORCE
>  kvm_stat: FORCE
>  	$(call descend,kvm/$@)
>  
> -all: acpi cgroup cpupower gpio hv firewire liblockdep \
> +all: acpi cgroup counter cpupower gpio hv firewire liblockdep \
>  		perf selftests bootconfig spi turbostat usb \
>  		virtio vm bpf x86_energy_perf_policy \
>  		tmon freefall iio objtool kvm_stat wmi \
> @@ -112,7 +113,7 @@ acpi_install:
>  cpupower_install:
>  	$(call descend,power/$(@:_install=),install)
>  
> -cgroup_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install:
> +cgroup_install counter_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install:
>  	$(call descend,$(@:_install=),install)
>  
>  liblockdep_install:
> @@ -133,7 +134,7 @@ freefall_install:
>  kvm_stat_install:
>  	$(call descend,kvm/$(@:_install=),install)
>  
> -install: acpi_install cgroup_install cpupower_install gpio_install \
> +install: acpi_install cgroup_install counter_install cpupower_install gpio_install \
>  		hv_install firewire_install iio_install liblockdep_install \
>  		perf_install selftests_install turbostat_install usb_install \
>  		virtio_install vm_install bpf_install x86_energy_perf_policy_install \
> @@ -147,7 +148,7 @@ acpi_clean:
>  cpupower_clean:
>  	$(call descend,power/cpupower,clean)
>  
> -cgroup_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean:
> +cgroup_clean counter_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean:
>  	$(call descend,$(@:_clean=),clean)
>  
>  liblockdep_clean:
> @@ -181,7 +182,7 @@ freefall_clean:
>  build_clean:
>  	$(call descend,build,clean)
>  
> -clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
> +clean: acpi_clean cgroup_clean counter_clean cpupower_clean hv_clean firewire_clean \

This is far from alphabetical, though I have no idea if there is any strong convention on where
to add new entries.

>  		perf_clean selftests_clean turbostat_clean bootconfig_clean spi_clean usb_clean virtio_clean \
>  		vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
>  		freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
> diff --git a/tools/counter/Build b/tools/counter/Build
> new file mode 100644
> index 000000000000..33f4a51d715e
> --- /dev/null
> +++ b/tools/counter/Build
> @@ -0,0 +1 @@
> +counter_example-y += counter_example.o
> diff --git a/tools/counter/Makefile b/tools/counter/Makefile
> new file mode 100644
> index 000000000000..5ebc195fd9c0
> --- /dev/null
> +++ b/tools/counter/Makefile
> @@ -0,0 +1,53 @@
> +# SPDX-License-Identifier: GPL-2.0
> +include ../scripts/Makefile.include
> +
> +bindir ?= /usr/bin
> +
> +ifeq ($(srctree),)
> +srctree := $(patsubst %/,%,$(dir $(CURDIR)))
> +srctree := $(patsubst %/,%,$(dir $(srctree)))
> +endif
> +
> +# Do not use make's built-in rules
> +# (this improves performance and avoids hard-to-debug behaviour);
> +MAKEFLAGS += -r
> +
> +override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
> +
> +ALL_TARGETS := counter_example
> +ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
> +
> +all: $(ALL_PROGRAMS)
> +
> +export srctree OUTPUT CC LD CFLAGS
> +include $(srctree)/tools/build/Makefile.include
> +
> +#
> +# We need the following to be outside of kernel tree
> +#
> +$(OUTPUT)include/linux/counter.h: ../../include/uapi/linux/counter.h
> +	mkdir -p $(OUTPUT)include/linux 2>&1 || true
> +	ln -sf $(CURDIR)/../../include/uapi/linux/counter.h $@
> +
> +prepare: $(OUTPUT)include/linux/counter.h
> +
> +COUNTER_EXAMPLE := $(OUTPUT)counter_example.o
> +$(COUNTER_EXAMPLE): prepare FORCE
> +	$(Q)$(MAKE) $(build)=counter_example
> +$(OUTPUT)counter_example: $(COUNTER_EXAMPLE)
> +	$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
> +
> +clean:
> +	rm -f $(ALL_PROGRAMS)
> +	rm -rf $(OUTPUT)include/linux/counter.h
> +	find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete
> +
> +install: $(ALL_PROGRAMS)
> +	install -d -m 755 $(DESTDIR)$(bindir);		\
> +	for program in $(ALL_PROGRAMS); do		\
> +		install $$program $(DESTDIR)$(bindir);	\
> +	done
> +
> +FORCE:
> +
> +.PHONY: all install clean FORCE prepare
> diff --git a/tools/counter/counter_example.c b/tools/counter/counter_example.c
> new file mode 100644
> index 000000000000..90d69fb9463b
> --- /dev/null
> +++ b/tools/counter/counter_example.c
> @@ -0,0 +1,93 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Counter - example userspace application
> + *
> + * The userspace application opens /dev/counter0, configures the
> + * COUNTER_EVENT_INDEX event channel 0 to gather Count 0 count and Count
> + * 1 count, and prints out the data as it becomes available on the
> + * character device node.
> + *
> + * Copyright (C) 2021 William Breathitt Gray
> + */
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <linux/counter.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <sys/ioctl.h>
> +#include <unistd.h>
> +
> +static struct counter_watch watches[2] = {
> +	{
> +		/* Component data: Count 0 count */
> +		.component.type = COUNTER_COMPONENT_COUNT,
> +		.component.scope = COUNTER_SCOPE_COUNT,
> +		.component.parent = 0,
> +		/* Event type: Index */
> +		.event = COUNTER_EVENT_INDEX,
> +		/* Device event channel 0 */
> +		.channel = 0,
> +	},
> +	{
> +		/* Component data: Count 1 count */
> +		.component.type = COUNTER_COMPONENT_COUNT,
> +		.component.scope = COUNTER_SCOPE_COUNT,
> +		.component.parent = 1,
> +		/* Event type: Index */
> +		.event = COUNTER_EVENT_INDEX,
> +		/* Device event channel 0 */
> +		.channel = 0,
> +	},
> +};
> +
> +int main(void)
> +{
> +	int fd;
> +	int ret;
> +	struct counter_event event_data[2];
> +
> +	fd = open("/dev/counter0", O_RDWR);

You may want to make this tool more flexible at somepoint. It is
in tools rather than Documentation after all.

> +	if (fd == -1) {
> +		perror("Unable to open /dev/counter0");
> +		return -errno;
> +	}
> +
> +	ret = ioctl(fd, COUNTER_ADD_WATCH_IOCTL, watches);

Loop?

> +	if (ret == -1) {
> +		perror("Error adding watches[0]");
> +		return -errno;
> +	}
> +	ret = ioctl(fd, COUNTER_ADD_WATCH_IOCTL, watches + 1);
> +	if (ret == -1) {
> +		perror("Error adding watches[1]");
> +		return -errno;
> +	}
> +	ret = ioctl(fd, COUNTER_ENABLE_EVENTS_IOCTL);
> +	if (ret == -1) {
> +		perror("Error enabling events");
> +		return -errno;
> +	}
> +
> +	for (;;) {
> +		ret = read(fd, event_data, sizeof(event_data));
> +		if (ret == -1) {
> +			perror("Failed to read event data");
> +			return 1;
> +		}
> +
> +		if (ret != sizeof(event_data)) {
> +			fprintf(stderr, "Failed to read event data\n");
> +			return -EIO;
> +		}
> +
> +		printf("Timestamp 0: %llu\tCount 0: %llu\n"
> +		       "Error Message 0: %s\n"
> +		       "Timestamp 1: %llu\tCount 1: %llu\n"
> +		       "Error Message 1: %s\n",
> +		       event_data[0].timestamp, event_data[0].value,
> +		       strerror(event_data[0].status),
> +		       event_data[1].timestamp, event_data[1].value,
> +		       strerror(event_data[1].status));
> +	}
> +
> +	return 0;
> +}


^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 09/14] tools/counter: Create Counter tools
@ 2021-09-12 16:26     ` Jonathan Cameron
  0 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-12 16:26 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	Pavel Machek

On Fri, 27 Aug 2021 12:47:53 +0900
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> This creates an example Counter program under tools/counter/*
> to exemplify the Counter character device interface.
> 
> Cc: Pavel Machek <pavel@ucw.cz>
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>

It's great to have this example, but it very much an example rather than
a generic tool. You may want to revisit and provide a more generic
tool in the future.

A trivial comment about using a loop inline.

Jonathan

> ---
>  MAINTAINERS                     |  1 +
>  tools/Makefile                  | 13 ++---
>  tools/counter/Build             |  1 +
>  tools/counter/Makefile          | 53 +++++++++++++++++++
>  tools/counter/counter_example.c | 93 +++++++++++++++++++++++++++++++++
>  5 files changed, 155 insertions(+), 6 deletions(-)
>  create mode 100644 tools/counter/Build
>  create mode 100644 tools/counter/Makefile
>  create mode 100644 tools/counter/counter_example.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index f2fdd2202605..57dc9b6ff82a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4782,6 +4782,7 @@ F:	Documentation/driver-api/generic-counter.rst
>  F:	drivers/counter/
>  F:	include/linux/counter.h
>  F:	include/uapi/linux/counter.h
> +F:	tools/counter/
>  
>  CP2615 I2C DRIVER
>  M:	Bence Csókás <bence98@sch.bme.hu>
> diff --git a/tools/Makefile b/tools/Makefile
> index 7e9d34ddd74c..5da1fde03a9a 100644
> --- a/tools/Makefile
> +++ b/tools/Makefile
> @@ -12,6 +12,7 @@ help:
>  	@echo '  acpi                   - ACPI tools'
>  	@echo '  bpf                    - misc BPF tools'
>  	@echo '  cgroup                 - cgroup tools'
> +	@echo '  counter                - counter tools'
>  	@echo '  cpupower               - a tool for all things x86 CPU power'
>  	@echo '  debugging              - tools for debugging'
>  	@echo '  firewire               - the userspace part of nosy, an IEEE-1394 traffic sniffer'
> @@ -65,7 +66,7 @@ acpi: FORCE
>  cpupower: FORCE
>  	$(call descend,power/$@)
>  
> -cgroup firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE
> +cgroup counter firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE
>  	$(call descend,$@)
>  
>  bpf/%: FORCE
> @@ -100,7 +101,7 @@ freefall: FORCE
>  kvm_stat: FORCE
>  	$(call descend,kvm/$@)
>  
> -all: acpi cgroup cpupower gpio hv firewire liblockdep \
> +all: acpi cgroup counter cpupower gpio hv firewire liblockdep \
>  		perf selftests bootconfig spi turbostat usb \
>  		virtio vm bpf x86_energy_perf_policy \
>  		tmon freefall iio objtool kvm_stat wmi \
> @@ -112,7 +113,7 @@ acpi_install:
>  cpupower_install:
>  	$(call descend,power/$(@:_install=),install)
>  
> -cgroup_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install:
> +cgroup_install counter_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install:
>  	$(call descend,$(@:_install=),install)
>  
>  liblockdep_install:
> @@ -133,7 +134,7 @@ freefall_install:
>  kvm_stat_install:
>  	$(call descend,kvm/$(@:_install=),install)
>  
> -install: acpi_install cgroup_install cpupower_install gpio_install \
> +install: acpi_install cgroup_install counter_install cpupower_install gpio_install \
>  		hv_install firewire_install iio_install liblockdep_install \
>  		perf_install selftests_install turbostat_install usb_install \
>  		virtio_install vm_install bpf_install x86_energy_perf_policy_install \
> @@ -147,7 +148,7 @@ acpi_clean:
>  cpupower_clean:
>  	$(call descend,power/cpupower,clean)
>  
> -cgroup_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean:
> +cgroup_clean counter_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean:
>  	$(call descend,$(@:_clean=),clean)
>  
>  liblockdep_clean:
> @@ -181,7 +182,7 @@ freefall_clean:
>  build_clean:
>  	$(call descend,build,clean)
>  
> -clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
> +clean: acpi_clean cgroup_clean counter_clean cpupower_clean hv_clean firewire_clean \

This is far from alphabetical, though I have no idea if there is any strong convention on where
to add new entries.

>  		perf_clean selftests_clean turbostat_clean bootconfig_clean spi_clean usb_clean virtio_clean \
>  		vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
>  		freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
> diff --git a/tools/counter/Build b/tools/counter/Build
> new file mode 100644
> index 000000000000..33f4a51d715e
> --- /dev/null
> +++ b/tools/counter/Build
> @@ -0,0 +1 @@
> +counter_example-y += counter_example.o
> diff --git a/tools/counter/Makefile b/tools/counter/Makefile
> new file mode 100644
> index 000000000000..5ebc195fd9c0
> --- /dev/null
> +++ b/tools/counter/Makefile
> @@ -0,0 +1,53 @@
> +# SPDX-License-Identifier: GPL-2.0
> +include ../scripts/Makefile.include
> +
> +bindir ?= /usr/bin
> +
> +ifeq ($(srctree),)
> +srctree := $(patsubst %/,%,$(dir $(CURDIR)))
> +srctree := $(patsubst %/,%,$(dir $(srctree)))
> +endif
> +
> +# Do not use make's built-in rules
> +# (this improves performance and avoids hard-to-debug behaviour);
> +MAKEFLAGS += -r
> +
> +override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
> +
> +ALL_TARGETS := counter_example
> +ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
> +
> +all: $(ALL_PROGRAMS)
> +
> +export srctree OUTPUT CC LD CFLAGS
> +include $(srctree)/tools/build/Makefile.include
> +
> +#
> +# We need the following to be outside of kernel tree
> +#
> +$(OUTPUT)include/linux/counter.h: ../../include/uapi/linux/counter.h
> +	mkdir -p $(OUTPUT)include/linux 2>&1 || true
> +	ln -sf $(CURDIR)/../../include/uapi/linux/counter.h $@
> +
> +prepare: $(OUTPUT)include/linux/counter.h
> +
> +COUNTER_EXAMPLE := $(OUTPUT)counter_example.o
> +$(COUNTER_EXAMPLE): prepare FORCE
> +	$(Q)$(MAKE) $(build)=counter_example
> +$(OUTPUT)counter_example: $(COUNTER_EXAMPLE)
> +	$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
> +
> +clean:
> +	rm -f $(ALL_PROGRAMS)
> +	rm -rf $(OUTPUT)include/linux/counter.h
> +	find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete
> +
> +install: $(ALL_PROGRAMS)
> +	install -d -m 755 $(DESTDIR)$(bindir);		\
> +	for program in $(ALL_PROGRAMS); do		\
> +		install $$program $(DESTDIR)$(bindir);	\
> +	done
> +
> +FORCE:
> +
> +.PHONY: all install clean FORCE prepare
> diff --git a/tools/counter/counter_example.c b/tools/counter/counter_example.c
> new file mode 100644
> index 000000000000..90d69fb9463b
> --- /dev/null
> +++ b/tools/counter/counter_example.c
> @@ -0,0 +1,93 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Counter - example userspace application
> + *
> + * The userspace application opens /dev/counter0, configures the
> + * COUNTER_EVENT_INDEX event channel 0 to gather Count 0 count and Count
> + * 1 count, and prints out the data as it becomes available on the
> + * character device node.
> + *
> + * Copyright (C) 2021 William Breathitt Gray
> + */
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <linux/counter.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <sys/ioctl.h>
> +#include <unistd.h>
> +
> +static struct counter_watch watches[2] = {
> +	{
> +		/* Component data: Count 0 count */
> +		.component.type = COUNTER_COMPONENT_COUNT,
> +		.component.scope = COUNTER_SCOPE_COUNT,
> +		.component.parent = 0,
> +		/* Event type: Index */
> +		.event = COUNTER_EVENT_INDEX,
> +		/* Device event channel 0 */
> +		.channel = 0,
> +	},
> +	{
> +		/* Component data: Count 1 count */
> +		.component.type = COUNTER_COMPONENT_COUNT,
> +		.component.scope = COUNTER_SCOPE_COUNT,
> +		.component.parent = 1,
> +		/* Event type: Index */
> +		.event = COUNTER_EVENT_INDEX,
> +		/* Device event channel 0 */
> +		.channel = 0,
> +	},
> +};
> +
> +int main(void)
> +{
> +	int fd;
> +	int ret;
> +	struct counter_event event_data[2];
> +
> +	fd = open("/dev/counter0", O_RDWR);

You may want to make this tool more flexible at somepoint. It is
in tools rather than Documentation after all.

> +	if (fd == -1) {
> +		perror("Unable to open /dev/counter0");
> +		return -errno;
> +	}
> +
> +	ret = ioctl(fd, COUNTER_ADD_WATCH_IOCTL, watches);

Loop?

> +	if (ret == -1) {
> +		perror("Error adding watches[0]");
> +		return -errno;
> +	}
> +	ret = ioctl(fd, COUNTER_ADD_WATCH_IOCTL, watches + 1);
> +	if (ret == -1) {
> +		perror("Error adding watches[1]");
> +		return -errno;
> +	}
> +	ret = ioctl(fd, COUNTER_ENABLE_EVENTS_IOCTL);
> +	if (ret == -1) {
> +		perror("Error enabling events");
> +		return -errno;
> +	}
> +
> +	for (;;) {
> +		ret = read(fd, event_data, sizeof(event_data));
> +		if (ret == -1) {
> +			perror("Failed to read event data");
> +			return 1;
> +		}
> +
> +		if (ret != sizeof(event_data)) {
> +			fprintf(stderr, "Failed to read event data\n");
> +			return -EIO;
> +		}
> +
> +		printf("Timestamp 0: %llu\tCount 0: %llu\n"
> +		       "Error Message 0: %s\n"
> +		       "Timestamp 1: %llu\tCount 1: %llu\n"
> +		       "Error Message 1: %s\n",
> +		       event_data[0].timestamp, event_data[0].value,
> +		       strerror(event_data[0].status),
> +		       event_data[1].timestamp, event_data[1].value,
> +		       strerror(event_data[1].status));
> +	}
> +
> +	return 0;
> +}


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 00/14] Introduce the Counter character device interface
  2021-08-30 17:17   ` Jonathan Cameron
@ 2021-09-12 16:36     ` Jonathan Cameron
  -1 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-12 16:36 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula

On Mon, 30 Aug 2021 18:17:06 +0100
Jonathan Cameron <jic23@kernel.org> wrote:

> On Fri, 27 Aug 2021 12:47:44 +0900
> William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> 
> > Changes in v16:
> >  - Define magic numbers for stm32-lptimer-cnt clock polarities
> >  - Define magic numbers for stm32-timer-cnt encoder modes
> >  - Bump KernelVersion to 5.16 in sysfs-bus-counter ABI documentation
> >  - Fix typos in driver API generic-counter.rst documentation file
> > 
> > For convenience, this patchset is also available on my personal git
> > repo: https://gitlab.com/vilhelmgray/iio/-/tree/counter_chrdev_v16
> > 
> > The patches preceding "counter: Internalize sysfs interface code" are
> > primarily cleanup and fixes that can be picked up and applied now to the
> > IIO tree if so desired. The "counter: Internalize sysfs interface code"
> > patch as well may be considered for pickup because it is relatively safe
> > and makes no changes to the userspace interface.
> > 
> > To summarize the main points of this patchset: there are no changes to
> > the existing Counter sysfs userspace interface; a Counter character
> > device interface is introduced that allows Counter events and associated
> > data to be read() by userspace; the events_configure() and
> > watch_validate() driver callbacks are introduced to support Counter
> > events; and IRQ support is added to the 104-QUAD-8 driver, serving as an
> > example of how to support the new Counter events functionality.  
> 
> Hi William,
> 
> I'll aim to pick up the first part in a week (too tired today after a lot
> of reviewing to even manage the basic sanity check on the changes).
> 
> For the rest...
> 
> What I'd really like to know is if anyone other than William and I is planning
> to review them in depth? (particularly 7 and 8 which are the new interface
> patch and docs)
> 
> So if anyone reading this is in that category please let me know.  We can wait,
> but conversely if no one is going to get time / inclination to do it then I
> don't want to hold these up any longer and maximum time in linux-next may
> be more useful than sitting unloved on the mailing list.

Ah well, looks like it's just the two of us for the chrdev core patches :)

Anyhow, I found time for a more thorough review.  I'm not 100% convinced on
the model for the chrdev but you know a lot more about this sort of hardware than
I do and it definitely seems reasonable - if anything it might be more flexible
than it needs to be.

I've highlighted a few small things in the patches.  With those fixed I'm happy
to apply the remainder of this series unless someone shouts in the meantime.

There has been plenty of time for review, so fingers crossed that anyone
who hasn't commented, but cares, is happy with how you have done it.

So, lucky v17!  Persistence pays off in the end.

Thanks,

Jonathan

> 
> Jonathan
> 
> > 
> > William Breathitt Gray (14):
> >   counter: stm32-lptimer-cnt: Provide defines for clock polarities
> >   counter: stm32-timer-cnt: Provide defines for slave mode selection
> >   counter: Internalize sysfs interface code
> >   counter: Update counter.h comments to reflect sysfs internalization
> >   docs: counter: Update to reflect sysfs internalization
> >   counter: Move counter enums to uapi header
> >   counter: Add character device interface
> >   docs: counter: Document character device interface
> >   tools/counter: Create Counter tools
> >   counter: Implement signalZ_action_component_id sysfs attribute
> >   counter: Implement *_component_id sysfs attributes
> >   counter: Implement events_queue_size sysfs attribute
> >   counter: 104-quad-8: Replace mutex with spinlock
> >   counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
> > 
> >  Documentation/ABI/testing/sysfs-bus-counter   |   38 +-
> >  Documentation/driver-api/generic-counter.rst  |  358 +++-
> >  .../userspace-api/ioctl/ioctl-number.rst      |    1 +
> >  MAINTAINERS                                   |    3 +-
> >  drivers/counter/104-quad-8.c                  |  699 ++++----
> >  drivers/counter/Kconfig                       |    6 +-
> >  drivers/counter/Makefile                      |    1 +
> >  drivers/counter/counter-chrdev.c              |  553 ++++++
> >  drivers/counter/counter-chrdev.h              |   14 +
> >  drivers/counter/counter-core.c                |  191 +++
> >  drivers/counter/counter-sysfs.c               |  960 +++++++++++
> >  drivers/counter/counter-sysfs.h               |   13 +
> >  drivers/counter/counter.c                     | 1496 -----------------
> >  drivers/counter/ftm-quaddec.c                 |   60 +-
> >  drivers/counter/intel-qep.c                   |  144 +-
> >  drivers/counter/interrupt-cnt.c               |   62 +-
> >  drivers/counter/microchip-tcb-capture.c       |   91 +-
> >  drivers/counter/stm32-lptimer-cnt.c           |  212 ++-
> >  drivers/counter/stm32-timer-cnt.c             |  195 +--
> >  drivers/counter/ti-eqep.c                     |  180 +-
> >  include/linux/counter.h                       |  715 ++++----
> >  include/linux/counter_enum.h                  |   45 -
> >  include/linux/mfd/stm32-lptimer.h             |    5 +
> >  include/linux/mfd/stm32-timers.h              |    4 +
> >  include/uapi/linux/counter.h                  |  154 ++
> >  tools/Makefile                                |   13 +-
> >  tools/counter/Build                           |    1 +
> >  tools/counter/Makefile                        |   53 +
> >  tools/counter/counter_example.c               |   93 +
> >  29 files changed, 3569 insertions(+), 2791 deletions(-)
> >  create mode 100644 drivers/counter/counter-chrdev.c
> >  create mode 100644 drivers/counter/counter-chrdev.h
> >  create mode 100644 drivers/counter/counter-core.c
> >  create mode 100644 drivers/counter/counter-sysfs.c
> >  create mode 100644 drivers/counter/counter-sysfs.h
> >  delete mode 100644 drivers/counter/counter.c
> >  delete mode 100644 include/linux/counter_enum.h
> >  create mode 100644 include/uapi/linux/counter.h
> >  create mode 100644 tools/counter/Build
> >  create mode 100644 tools/counter/Makefile
> >  create mode 100644 tools/counter/counter_example.c
> > 
> > 
> > base-commit: 5ffeb17c0d3dd44704b4aee83e297ec07666e4d6  
> 


^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 00/14] Introduce the Counter character device interface
@ 2021-09-12 16:36     ` Jonathan Cameron
  0 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-12 16:36 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula

On Mon, 30 Aug 2021 18:17:06 +0100
Jonathan Cameron <jic23@kernel.org> wrote:

> On Fri, 27 Aug 2021 12:47:44 +0900
> William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> 
> > Changes in v16:
> >  - Define magic numbers for stm32-lptimer-cnt clock polarities
> >  - Define magic numbers for stm32-timer-cnt encoder modes
> >  - Bump KernelVersion to 5.16 in sysfs-bus-counter ABI documentation
> >  - Fix typos in driver API generic-counter.rst documentation file
> > 
> > For convenience, this patchset is also available on my personal git
> > repo: https://gitlab.com/vilhelmgray/iio/-/tree/counter_chrdev_v16
> > 
> > The patches preceding "counter: Internalize sysfs interface code" are
> > primarily cleanup and fixes that can be picked up and applied now to the
> > IIO tree if so desired. The "counter: Internalize sysfs interface code"
> > patch as well may be considered for pickup because it is relatively safe
> > and makes no changes to the userspace interface.
> > 
> > To summarize the main points of this patchset: there are no changes to
> > the existing Counter sysfs userspace interface; a Counter character
> > device interface is introduced that allows Counter events and associated
> > data to be read() by userspace; the events_configure() and
> > watch_validate() driver callbacks are introduced to support Counter
> > events; and IRQ support is added to the 104-QUAD-8 driver, serving as an
> > example of how to support the new Counter events functionality.  
> 
> Hi William,
> 
> I'll aim to pick up the first part in a week (too tired today after a lot
> of reviewing to even manage the basic sanity check on the changes).
> 
> For the rest...
> 
> What I'd really like to know is if anyone other than William and I is planning
> to review them in depth? (particularly 7 and 8 which are the new interface
> patch and docs)
> 
> So if anyone reading this is in that category please let me know.  We can wait,
> but conversely if no one is going to get time / inclination to do it then I
> don't want to hold these up any longer and maximum time in linux-next may
> be more useful than sitting unloved on the mailing list.

Ah well, looks like it's just the two of us for the chrdev core patches :)

Anyhow, I found time for a more thorough review.  I'm not 100% convinced on
the model for the chrdev but you know a lot more about this sort of hardware than
I do and it definitely seems reasonable - if anything it might be more flexible
than it needs to be.

I've highlighted a few small things in the patches.  With those fixed I'm happy
to apply the remainder of this series unless someone shouts in the meantime.

There has been plenty of time for review, so fingers crossed that anyone
who hasn't commented, but cares, is happy with how you have done it.

So, lucky v17!  Persistence pays off in the end.

Thanks,

Jonathan

> 
> Jonathan
> 
> > 
> > William Breathitt Gray (14):
> >   counter: stm32-lptimer-cnt: Provide defines for clock polarities
> >   counter: stm32-timer-cnt: Provide defines for slave mode selection
> >   counter: Internalize sysfs interface code
> >   counter: Update counter.h comments to reflect sysfs internalization
> >   docs: counter: Update to reflect sysfs internalization
> >   counter: Move counter enums to uapi header
> >   counter: Add character device interface
> >   docs: counter: Document character device interface
> >   tools/counter: Create Counter tools
> >   counter: Implement signalZ_action_component_id sysfs attribute
> >   counter: Implement *_component_id sysfs attributes
> >   counter: Implement events_queue_size sysfs attribute
> >   counter: 104-quad-8: Replace mutex with spinlock
> >   counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
> > 
> >  Documentation/ABI/testing/sysfs-bus-counter   |   38 +-
> >  Documentation/driver-api/generic-counter.rst  |  358 +++-
> >  .../userspace-api/ioctl/ioctl-number.rst      |    1 +
> >  MAINTAINERS                                   |    3 +-
> >  drivers/counter/104-quad-8.c                  |  699 ++++----
> >  drivers/counter/Kconfig                       |    6 +-
> >  drivers/counter/Makefile                      |    1 +
> >  drivers/counter/counter-chrdev.c              |  553 ++++++
> >  drivers/counter/counter-chrdev.h              |   14 +
> >  drivers/counter/counter-core.c                |  191 +++
> >  drivers/counter/counter-sysfs.c               |  960 +++++++++++
> >  drivers/counter/counter-sysfs.h               |   13 +
> >  drivers/counter/counter.c                     | 1496 -----------------
> >  drivers/counter/ftm-quaddec.c                 |   60 +-
> >  drivers/counter/intel-qep.c                   |  144 +-
> >  drivers/counter/interrupt-cnt.c               |   62 +-
> >  drivers/counter/microchip-tcb-capture.c       |   91 +-
> >  drivers/counter/stm32-lptimer-cnt.c           |  212 ++-
> >  drivers/counter/stm32-timer-cnt.c             |  195 +--
> >  drivers/counter/ti-eqep.c                     |  180 +-
> >  include/linux/counter.h                       |  715 ++++----
> >  include/linux/counter_enum.h                  |   45 -
> >  include/linux/mfd/stm32-lptimer.h             |    5 +
> >  include/linux/mfd/stm32-timers.h              |    4 +
> >  include/uapi/linux/counter.h                  |  154 ++
> >  tools/Makefile                                |   13 +-
> >  tools/counter/Build                           |    1 +
> >  tools/counter/Makefile                        |   53 +
> >  tools/counter/counter_example.c               |   93 +
> >  29 files changed, 3569 insertions(+), 2791 deletions(-)
> >  create mode 100644 drivers/counter/counter-chrdev.c
> >  create mode 100644 drivers/counter/counter-chrdev.h
> >  create mode 100644 drivers/counter/counter-core.c
> >  create mode 100644 drivers/counter/counter-sysfs.c
> >  create mode 100644 drivers/counter/counter-sysfs.h
> >  delete mode 100644 drivers/counter/counter.c
> >  delete mode 100644 include/linux/counter_enum.h
> >  create mode 100644 include/uapi/linux/counter.h
> >  create mode 100644 tools/counter/Build
> >  create mode 100644 tools/counter/Makefile
> >  create mode 100644 tools/counter/counter_example.c
> > 
> > 
> > base-commit: 5ffeb17c0d3dd44704b4aee83e297ec07666e4d6  
> 


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 07/14] counter: Add character device interface
  2021-09-12 16:18     ` Jonathan Cameron
@ 2021-09-20 10:09       ` William Breathitt Gray
  -1 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-09-20 10:09 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	Dan Carpenter

[-- Attachment #1: Type: text/plain, Size: 34337 bytes --]

On Sun, Sep 12, 2021 at 05:18:42PM +0100, Jonathan Cameron wrote:
> On Fri, 27 Aug 2021 12:47:51 +0900
> William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> 
> > This patch introduces a character device interface for the Counter
> > subsystem. Device data is exposed through standard character device read
> > operations. Device data is gathered when a Counter event is pushed by
> > the respective Counter device driver. Configuration is handled via ioctl
> > operations on the respective Counter character device node.
> > 
> > Cc: David Lechner <david@lechnology.com>
> > Cc: Gwendal Grignou <gwendal@chromium.org>
> > Cc: Dan Carpenter <dan.carpenter@oracle.com>
> > Cc: Oleksij Rempel <o.rempel@pengutronix.de>
> > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> 
> Hi William,
> 
> Why the bit based lock?  It feels like a mutex_trylock() type approach or
> spinlock_trylock() would be a more common solution to this problem.
> There is precedence for doing what you have here though so I'm not that
> worried about it.

Hi Jonathan,

We originally used a mutex for this, but Jarkko discovered that this
produced a warning because chrdev_lock would be held when returning to
user space:
https://lore.kernel.org/linux-arm-kernel/YOq19zTsOzKA8v7c@shinobu/T/#m6072133d418d598a5f368bb942c945e46cfab9a5

Following David Lechner's suggestion, I decided to reimplement
chrdev_lock as a bitmap using an atomic flag.

> There are a few more things inline.
> 
> I've now been through this patch with as fine toothed comb as I'm likely to
> do so.  Hence I won't do another review unless there are substantial changes.
> I nearly applied it as it stands, but given we aren't in a rush (merge window
> open), it's worth just a little more time to tidy up loose ends.
> 
> Jonathan

Responses follow below.

> > ---
> >  drivers/counter/Makefile         |   2 +-
> >  drivers/counter/counter-chrdev.c | 553 +++++++++++++++++++++++++++++++
> >  drivers/counter/counter-chrdev.h |  14 +
> >  drivers/counter/counter-core.c   |  60 +++-
> >  include/linux/counter.h          |  51 +++
> >  include/uapi/linux/counter.h     |  98 ++++++
> >  6 files changed, 770 insertions(+), 8 deletions(-)
> >  create mode 100644 drivers/counter/counter-chrdev.c
> >  create mode 100644 drivers/counter/counter-chrdev.h
> > 
> > diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
> > index 1ab7e087fdc2..8fde6c100ebc 100644
> > --- a/drivers/counter/Makefile
> > +++ b/drivers/counter/Makefile
> > @@ -4,7 +4,7 @@
> >  #
> >  
> >  obj-$(CONFIG_COUNTER) += counter.o
> > -counter-y := counter-core.o counter-sysfs.o
> > +counter-y := counter-core.o counter-sysfs.o counter-chrdev.o
> >  
> >  obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
> >  obj-$(CONFIG_INTERRUPT_CNT)		+= interrupt-cnt.o
> > diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c
> > new file mode 100644
> > index 000000000000..30659b67d8f3
> > --- /dev/null
> > +++ b/drivers/counter/counter-chrdev.c
> > @@ -0,0 +1,553 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Generic Counter character device interface
> > + * Copyright (C) 2020 William Breathitt Gray
> > + */
> > +#include <linux/bitops.h>
> > +#include <linux/cdev.h>
> > +#include <linux/counter.h>
> > +#include <linux/err.h>
> > +#include <linux/errno.h>
> > +#include <linux/export.h>
> > +#include <linux/fs.h>
> > +#include <linux/kfifo.h>
> > +#include <linux/list.h>
> > +#include <linux/mutex.h>
> > +#include <linux/nospec.h>
> > +#include <linux/poll.h>
> > +#include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/timekeeping.h>
> > +#include <linux/types.h>
> > +#include <linux/uaccess.h>
> > +#include <linux/wait.h>
> > +
> > +#include "counter-chrdev.h"
> > +
> > +struct counter_comp_node {
> > +	struct list_head l;
> > +	struct counter_component component;
> > +	struct counter_comp comp;
> > +	void *parent;
> > +};
> > +
> > +static ssize_t counter_chrdev_read(struct file *filp, char __user *buf,
> > +				   size_t len, loff_t *f_ps)
> > +{
> > +	struct counter_device *const counter = filp->private_data;
> > +	int err;
> > +	unsigned int copied;
> > +
> > +	if (!counter->ops)
> > +		return -ENODEV;
> > +
> > +	if (len < sizeof(struct counter_event))
> > +		return -EINVAL;
> > +
> > +	do {
> > +		if (kfifo_is_empty(&counter->events)) {
> > +			if (filp->f_flags & O_NONBLOCK)
> > +				return -EAGAIN;
> > +
> > +			err = wait_event_interruptible(counter->events_wait,
> > +					!kfifo_is_empty(&counter->events) ||
> > +					!counter->ops);
> > +			if (err < 0)
> > +				return err;
> > +			if (!counter->ops)
> > +				return -ENODEV;
> > +		}
> > +
> > +		if (mutex_lock_interruptible(&counter->events_lock))
> > +			return -ERESTARTSYS;
> > +		err = kfifo_to_user(&counter->events, buf, len, &copied);
> > +		mutex_unlock(&counter->events_lock);
> > +		if (err < 0)
> > +			return err;
> > +	} while (!copied);
> > +
> > +	return copied;
> > +}
> > +
> > +static __poll_t counter_chrdev_poll(struct file *filp,
> > +				    struct poll_table_struct *pollt)
> > +{
> > +	struct counter_device *const counter = filp->private_data;
> > +	__poll_t events = 0;
> > +
> > +	if (!counter->ops)
> > +		return events;
> > +
> > +	poll_wait(filp, &counter->events_wait, pollt);
> > +
> > +	if (!kfifo_is_empty(&counter->events))
> > +		events = EPOLLIN | EPOLLRDNORM;
> > +
> > +	return events;
> > +}
> > +
> > +static void counter_events_list_free(struct list_head *const events_list)
> > +{
> > +	struct counter_event_node *p, *n;
> > +	struct counter_comp_node *q, *o;
> > +
> > +	list_for_each_entry_safe(p, n, events_list, l) {
> > +		/* Free associated component nodes */
> > +		list_for_each_entry_safe(q, o, &p->comp_list, l) {
> > +			list_del(&q->l);
> > +			kfree(q);
> > +		}
> > +
> > +		/* Free event node */
> > +		list_del(&p->l);
> > +		kfree(p);
> > +	}
> > +}
> > +
> > +static int counter_set_event_node(struct counter_device *const counter,
> > +				  struct counter_watch *const watch,
> > +				  const struct counter_comp_node *const cfg)
> > +{
> > +	struct counter_event_node *event_node;
> > +	int err = 0;
> > +	struct counter_comp_node *comp_node;
> > +
> > +	/* Search for event in the list */
> > +	list_for_each_entry(event_node, &counter->next_events_list, l)
> > +		if (event_node->event == watch->event &&
> > +		    event_node->channel == watch->channel)
> > +			break;
> > +
> > +	/* If event is not already in the list */
> > +	if (&event_node->l == &counter->next_events_list) {
> > +		/* Allocate new event node */
> > +		event_node = kmalloc(sizeof(*event_node), GFP_KERNEL);
> > +		if (!event_node)
> > +			return -ENOMEM;
> > +
> > +		/* Configure event node and add to the list */
> > +		event_node->event = watch->event;
> > +		event_node->channel = watch->channel;
> > +		INIT_LIST_HEAD(&event_node->comp_list);
> > +		list_add(&event_node->l, &counter->next_events_list);
> > +	}
> > +
> > +	/* Check if component watch has already been set before */
> > +	list_for_each_entry(comp_node, &event_node->comp_list, l)
> > +		if (comp_node->parent == cfg->parent &&
> > +		    comp_node->comp.count_u8_read == cfg->comp.count_u8_read) {
> > +			err = -EINVAL;
> > +			goto exit_free_event_node;
> > +		}
> > +
> > +	/* Allocate component node */
> > +	comp_node = kmalloc(sizeof(*comp_node), GFP_KERNEL);
> > +	if (!comp_node) {
> > +		err = -ENOMEM;
> > +		goto exit_free_event_node;
> > +	}
> > +	*comp_node = *cfg;
> > +
> > +	/* Add component node to event node */
> > +	list_add_tail(&comp_node->l, &event_node->comp_list);
> > +
> > +exit_free_event_node:
> > +	/* Free event node if no one else is watching */
> > +	if (list_empty(&event_node->comp_list)) {
> > +		list_del(&event_node->l);
> > +		kfree(event_node);
> > +	}
> > +
> > +	return err;
> > +}
> > +
> > +static int counter_enable_events(struct counter_device *const counter)
> > +{
> > +	unsigned long flags;
> > +	int err = 0;
> > +
> > +	mutex_lock(&counter->n_events_list_lock);
> > +	spin_lock_irqsave(&counter->events_list_lock, flags);
> > +
> > +	counter_events_list_free(&counter->events_list);
> > +	list_replace_init(&counter->next_events_list,
> > +			  &counter->events_list);
> > +
> > +	if (counter->ops->events_configure)
> > +		err = counter->ops->events_configure(counter);
> > +
> > +	spin_unlock_irqrestore(&counter->events_list_lock, flags);
> > +	mutex_unlock(&counter->n_events_list_lock);
> > +
> > +	return err;
> > +}
> > +
> > +static int counter_disable_events(struct counter_device *const counter)
> > +{
> > +	unsigned long flags;
> > +	int err = 0;
> > +
> > +	spin_lock_irqsave(&counter->events_list_lock, flags);
> > +
> > +	counter_events_list_free(&counter->events_list);
> > +
> > +	if (counter->ops->events_configure)
> > +		err = counter->ops->events_configure(counter);
> > +
> > +	spin_unlock_irqrestore(&counter->events_list_lock, flags);
> > +
> > +	mutex_lock(&counter->n_events_list_lock);
> > +
> > +	counter_events_list_free(&counter->next_events_list);
> > +
> > +	mutex_unlock(&counter->n_events_list_lock);
> > +
> > +	return err;
> > +}
> > +
> > +static int counter_add_watch(struct counter_device *const counter,
> > +			     const unsigned long arg)
> > +{
> > +	void __user *const uwatch = (void __user *)arg;
> > +	struct counter_watch watch;
> > +	struct counter_comp_node comp_node = {};
> > +	size_t parent, id;
> > +	struct counter_comp *ext;
> > +	size_t num_ext;
> > +	int err = 0;
> > +
> > +	if (copy_from_user(&watch, uwatch, sizeof(watch)))
> > +		return -EFAULT;
> > +
> > +	if (watch.component.type == COUNTER_COMPONENT_NONE)
> > +		goto no_component;
> > +
> > +	parent = watch.component.parent;
> > +
> > +	/* Configure parent component info for comp node */
> > +	switch (watch.component.scope) {
> > +	case COUNTER_SCOPE_DEVICE:
> > +		ext = counter->ext;
> > +		num_ext = counter->num_ext;
> > +		break;
> > +	case COUNTER_SCOPE_SIGNAL:
> > +		if (parent >= counter->num_signals)
> > +			return -EINVAL;
> > +		parent = array_index_nospec(parent, counter->num_signals);
> > +
> > +		comp_node.parent = counter->signals + parent;
> > +
> > +		ext = counter->signals[parent].ext;
> > +		num_ext = counter->signals[parent].num_ext;
> > +		break;
> > +	case COUNTER_SCOPE_COUNT:
> > +		if (parent >= counter->num_counts)
> > +			return -EINVAL;
> > +		parent = array_index_nospec(parent, counter->num_counts);
> > +
> > +		comp_node.parent = counter->counts + parent;
> > +
> > +		ext = counter->counts[parent].ext;
> > +		num_ext = counter->counts[parent].num_ext;
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	id = watch.component.id;
> > +
> > +	/* Configure component info for comp node */
> > +	switch (watch.component.type) {
> > +	case COUNTER_COMPONENT_SIGNAL:
> > +		if (watch.component.scope != COUNTER_SCOPE_SIGNAL)
> > +			return -EINVAL;
> > +
> > +		comp_node.comp.type = COUNTER_COMP_SIGNAL_LEVEL;
> > +		comp_node.comp.signal_u32_read = counter->ops->signal_read;
> > +		break;
> > +	case COUNTER_COMPONENT_COUNT:
> > +		if (watch.component.scope != COUNTER_SCOPE_COUNT)
> > +			return -EINVAL;
> > +
> > +		comp_node.comp.type = COUNTER_COMP_U64;
> > +		comp_node.comp.count_u64_read = counter->ops->count_read;
> > +		break;
> > +	case COUNTER_COMPONENT_FUNCTION:
> > +		if (watch.component.scope != COUNTER_SCOPE_COUNT)
> > +			return -EINVAL;
> > +
> > +		comp_node.comp.type = COUNTER_COMP_FUNCTION;
> > +		comp_node.comp.count_u32_read = counter->ops->function_read;
> > +		break;
> > +	case COUNTER_COMPONENT_SYNAPSE_ACTION:
> > +		if (watch.component.scope != COUNTER_SCOPE_COUNT)
> > +			return -EINVAL;
> > +		if (id >= counter->counts[parent].num_synapses)
> > +			return -EINVAL;
> > +		id = array_index_nospec(id, counter->counts[parent].num_synapses);
> > +
> > +		comp_node.comp.type = COUNTER_COMP_SYNAPSE_ACTION;
> > +		comp_node.comp.action_read = counter->ops->action_read;
> > +		comp_node.comp.priv = counter->counts[parent].synapses + id;
> > +		break;
> > +	case COUNTER_COMPONENT_EXTENSION:
> > +		if (id >= num_ext)
> > +			return -EINVAL;
> > +		id = array_index_nospec(id, num_ext);
> > +
> > +		comp_node.comp = ext[id];
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +	/* Check if any read callback is set; this is part of a union */
> > +	if (!comp_node.comp.count_u8_read)
> 
> This isn't valid for C.  The compiler is allowed to treat the elements
> of a union as not being in the same memory etc unless they are of the same
> type (which isn't true here - I think anyway)...
> 
> So whilst it seems silly you need to check all the callbacks in this if
> statement. It will 'almost' always work as you have it, but it's possible
> some future optimisation by the compiler will mean that u8_read would be
> set by this point, but it delayed setting e.g. u32_read until after
> this check.

Ack, I'll recreate a couple macros to handle these checks.

> > +		return -EOPNOTSUPP;
> > +
> > +no_component:
> > +	mutex_lock(&counter->n_events_list_lock);
> > +
> > +	if (counter->ops->watch_validate) {
> > +		err = counter->ops->watch_validate(counter, &watch);
> > +		if (err < 0)
> > +			goto err_exit;
> > +	}
> > +
> > +	comp_node.component = watch.component;
> > +
> > +	err = counter_set_event_node(counter, &watch, &comp_node);
> > +
> > +err_exit:
> > +	mutex_unlock(&counter->n_events_list_lock);
> > +
> > +	return err;
> > +}
> > +
> > +static long counter_chrdev_ioctl(struct file *filp, unsigned int cmd,
> > +				 unsigned long arg)
> > +{
> > +	struct counter_device *const counter = filp->private_data;
> > +	int ret = -ENODEV;
> > +
> > +	mutex_lock(&counter->ops_exist_lock);
> > +
> > +	if (!counter->ops)
> > +		goto out_unlock;
> > +
> > +	switch (cmd) {
> > +	case COUNTER_ADD_WATCH_IOCTL:
> > +		ret = counter_add_watch(counter, arg);
> > +		break;
> > +	case COUNTER_ENABLE_EVENTS_IOCTL:
> > +		ret = counter_enable_events(counter);
> > +		break;
> > +	case COUNTER_DISABLE_EVENTS_IOCTL:
> > +		ret = counter_disable_events(counter);
> > +		break;
> > +	default:
> > +		ret = -ENOIOCTLCMD;
> > +		break;
> > +	}
> > +
> > +out_unlock:
> > +	mutex_unlock(&counter->ops_exist_lock);
> > +
> > +	return ret;
> > +}
> > +
> > +static int counter_chrdev_open(struct inode *inode, struct file *filp)
> > +{
> > +	struct counter_device *const counter = container_of(inode->i_cdev,
> > +							    typeof(*counter),
> > +							    chrdev);
> > +
> > +	if (test_and_set_bit_lock(0, counter->chrdev_lock))
> > +		return -EBUSY;
> > +
> > +	get_device(&counter->dev);
> > +	filp->private_data = counter;
> > +
> > +	return nonseekable_open(inode, filp);
> > +}
> > +
> > +static int counter_chrdev_release(struct inode *inode, struct file *filp)
> > +{
> > +	struct counter_device *const counter = filp->private_data;
> > +	int ret = 0;
> > +
> > +	mutex_lock(&counter->ops_exist_lock);
> > +
> > +	if (!counter->ops) {
> 
> This needs a comment to explain how you would get here and
> why these two lists need cleaning up here if we do.
> 
> Superficially it feels to me like you could just add a counter->ops check in
> counter_disable_events() and then call that directly.  I'm guessing
> I am missing a deadlock or similar however.

I don't believe there is a risk of a deadlock, I just felt this
conditional check isn't really related to the counter_disable_events()
path so I kept it seperate; the events lists are freed in
counter_disable_events() but that's incidental rather than the purpose
of the function. My reasoning for the separation is that there are two
scenarios where counter_chrdev_release is called: the first is when a
user closes the chrdev, while the second is when the Counter driver is
removed.

For the first scenario, the counter_disable_events() function is called
to stop events from firing off when noone has the chrdev open. In the
second scenario, we are not interested in disabling events, we know
we're no longer going to be interacting with this device again so we
want to free any held memory.

I want to keep the intention of these code paths clear because the
distinction is important if we start managing additional memory in the
future (i.e. memory unrelated to the events list that would not be freed
in counter_disable_events()), so I'll add a comment explaining what's
happening in this path. Alternatively, I could define a
counter_chrdev_free() function and toss those list free calls into
there, but perhaps that would be overkill right now for just two calls.

> > +		counter_events_list_free(&counter->events_list);
> > +		counter_events_list_free(&counter->next_events_list);
> > +		ret = -ENODEV;
> > +		goto out_unlock;
> > +	}
> > +
> > +	ret = counter_disable_events(counter);
> > +	if (ret < 0) {
> > +		mutex_unlock(&counter->ops_exist_lock);
> > +		return ret;
> > +	}
> > +
> > +out_unlock:
> > +	mutex_unlock(&counter->ops_exist_lock);
> > +
> > +	put_device(&counter->dev);
> > +	clear_bit_unlock(0, counter->chrdev_lock);
> > +
> > +	return ret;
> > +}
> > +
> > +static const struct file_operations counter_fops = {
> > +	.owner = THIS_MODULE,
> > +	.llseek = no_llseek,
> > +	.read = counter_chrdev_read,
> > +	.poll = counter_chrdev_poll,
> > +	.unlocked_ioctl = counter_chrdev_ioctl,
> > +	.open = counter_chrdev_open,
> > +	.release = counter_chrdev_release,
> > +};
> > +
> > +int counter_chrdev_add(struct counter_device *const counter)
> 
> To think about: This isn't doing the add. So would counter_chrdev_init() be more
> appropriate? The fact it can fail due to the kfifo_alloc makes that
> naming less than ideal though.

I've been using the "add" here to refer to adding the chrdev to the
counter_device structure rather than to the rest of the system. I used a
similar naming convention for the counter-sysfs.c file so I think
"counter_chrdev_add" here should be all right and is consistent with the
existing "counter_sysfs_add" function.

> > +{
> > +	/* Initialize Counter events lists */
> > +	INIT_LIST_HEAD(&counter->events_list);
> > +	INIT_LIST_HEAD(&counter->next_events_list);
> > +	spin_lock_init(&counter->events_list_lock);
> > +	mutex_init(&counter->n_events_list_lock);
> > +	init_waitqueue_head(&counter->events_wait);
> > +	mutex_init(&counter->events_lock);
> > +
> > +	/* Initialize character device */
> > +	clear_bit(0, counter->chrdev_lock);
> > +	cdev_init(&counter->chrdev, &counter_fops);
> > +
> > +	/* Allocate Counter events queue */
> > +	return kfifo_alloc(&counter->events, 64, GFP_KERNEL);
> > +}
> > +
> > +void counter_chrdev_remove(struct counter_device *const counter)
> > +{
> > +	kfifo_free(&counter->events);
> > +}
> > +
> > +static int counter_get_data(struct counter_device *const counter,
> > +			    const struct counter_comp_node *const comp_node,
> > +			    u64 *const value)
> > +{
> > +	const struct counter_comp *const comp = &comp_node->comp;
> > +	void *const parent = comp_node->parent;
> > +	u8 value_u8 = 0;
> > +	u32 value_u32 = 0;
> > +	int ret;
> > +
> > +	if (comp_node->component.type == COUNTER_COMPONENT_NONE)
> > +		return 0;
> > +
> > +	switch (comp->type) {
> > +	case COUNTER_COMP_U8:
> > +	case COUNTER_COMP_BOOL:
> > +		switch (comp_node->component.scope) {
> > +		case COUNTER_SCOPE_DEVICE:
> > +			ret = comp->device_u8_read(counter, &value_u8);
> > +			break;
> > +		case COUNTER_SCOPE_SIGNAL:
> > +			ret = comp->signal_u8_read(counter, parent, &value_u8);
> > +			break;
> > +		case COUNTER_SCOPE_COUNT:
> > +			ret = comp->count_u8_read(counter, parent, &value_u8);
> > +			break;
> > +		}
> > +		*value = value_u8;
> > +		return ret;
> > +	case COUNTER_COMP_SIGNAL_LEVEL:
> > +	case COUNTER_COMP_FUNCTION:
> > +	case COUNTER_COMP_ENUM:
> > +	case COUNTER_COMP_COUNT_DIRECTION:
> > +	case COUNTER_COMP_COUNT_MODE:
> > +		switch (comp_node->component.scope) {
> > +		case COUNTER_SCOPE_DEVICE:
> > +			ret = comp->device_u32_read(counter, &value_u32);
> > +			break;
> > +		case COUNTER_SCOPE_SIGNAL:
> > +			ret = comp->signal_u32_read(counter, parent,
> > +						    &value_u32);
> > +			break;
> > +		case COUNTER_SCOPE_COUNT:
> > +			ret = comp->count_u32_read(counter, parent, &value_u32);
> > +			break;
> > +		}
> > +		*value = value_u32;
> > +		return ret;
> > +	case COUNTER_COMP_U64:
> > +		switch (comp_node->component.scope) {
> > +		case COUNTER_SCOPE_DEVICE:
> > +			return comp->device_u64_read(counter, value);
> > +		case COUNTER_SCOPE_SIGNAL:
> > +			return comp->signal_u64_read(counter, parent, value);
> > +		case COUNTER_SCOPE_COUNT:
> > +			return comp->count_u64_read(counter, parent, value);
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case COUNTER_COMP_SYNAPSE_ACTION:
> > +		ret = comp->action_read(counter, parent, comp->priv,
> > +					&value_u32);
> > +		*value = value_u32;
> > +		return ret;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +/**
> > + * counter_push_event - queue event for userspace reading
> > + * @counter:	pointer to Counter structure
> > + * @event:	triggered event
> > + * @channel:	event channel
> > + *
> > + * Note: If no one is watching for the respective event, it is silently
> > + * discarded.
> > + */
> > +void counter_push_event(struct counter_device *const counter, const u8 event,
> > +			const u8 channel)
> > +{
> > +	struct counter_event ev;
> > +	unsigned int copied = 0;
> > +	unsigned long flags;
> > +	struct counter_event_node *event_node;
> > +	struct counter_comp_node *comp_node;
> > +
> > +	ev.timestamp = ktime_get_ns();
> > +	ev.watch.event = event;
> > +	ev.watch.channel = channel;
> > +
> > +	/* Could be in an interrupt context, so use a spin lock */
> > +	spin_lock_irqsave(&counter->events_list_lock, flags);
> > +
> > +	/* Search for event in the list */
> 
> Worth keeping in mind that searching a list is not exactly a scalable
> solution even if it's fine for now.
> 
> > +	list_for_each_entry(event_node, &counter->events_list, l)
> > +		if (event_node->event == event &&
> > +		    event_node->channel == channel)
> > +			break;
> > +
> > +	/* If event is not in the list */
> > +	if (&event_node->l == &counter->events_list)
> > +		goto exit_early;
> > +
> > +	/* Read and queue relevant comp for userspace */
> > +	list_for_each_entry(comp_node, &event_node->comp_list, l) {
> > +		ev.watch.component = comp_node->component;
> > +		ev.status = -counter_get_data(counter, comp_node, &ev.value);
> > +
> > +		copied += kfifo_in(&counter->events, &ev, 1);
> 
> I had a short debate with myself on whether this or kfifo_put() was more appropriate
> (as fixed record length).  I think this particular case they end up the same anyway
> so it doesn't matter (nothing to do here!)
>  
> > +	}
> > +
> > +exit_early:
> > +	spin_unlock_irqrestore(&counter->events_list_lock, flags);
> > +
> > +	if (copied)
> > +		wake_up_poll(&counter->events_wait, EPOLLIN);
> > +}
> > +EXPORT_SYMBOL_GPL(counter_push_event);
> > diff --git a/drivers/counter/counter-chrdev.h b/drivers/counter/counter-chrdev.h
> > new file mode 100644
> > index 000000000000..5529d16703c4
> > --- /dev/null
> > +++ b/drivers/counter/counter-chrdev.h
> > @@ -0,0 +1,14 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Counter character device interface
> > + * Copyright (C) 2020 William Breathitt Gray
> > + */
> > +#ifndef _COUNTER_CHRDEV_H_
> > +#define _COUNTER_CHRDEV_H_
> > +
> > +#include <linux/counter.h>
> > +
> > +int counter_chrdev_add(struct counter_device *const counter);
> > +void counter_chrdev_remove(struct counter_device *const counter);
> > +
> > +#endif /* _COUNTER_CHRDEV_H_ */
> > diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
> > index 3cda2c47bacb..28dc1ff63c94 100644
> > --- a/drivers/counter/counter-core.c
> > +++ b/drivers/counter/counter-core.c
> > @@ -3,14 +3,22 @@
> >   * Generic Counter interface
> >   * Copyright (C) 2020 William Breathitt Gray
> >   */
> > +#include <linux/cdev.h>
> >  #include <linux/counter.h>
> >  #include <linux/device.h>
> > +#include <linux/device/bus.h>
> >  #include <linux/export.h>
> > +#include <linux/fs.h>
> >  #include <linux/gfp.h>
> >  #include <linux/idr.h>
> >  #include <linux/init.h>
> > +#include <linux/kdev_t.h>
> >  #include <linux/module.h>
> > +#include <linux/mutex.h>
> > +#include <linux/types.h>
> > +#include <linux/wait.h>
> >  
> > +#include "counter-chrdev.h"
> >  #include "counter-sysfs.h"
> >  
> >  /* Provides a unique ID for each counter device */
> > @@ -18,6 +26,9 @@ static DEFINE_IDA(counter_ida);
> >  
> >  static void counter_device_release(struct device *dev)
> >  {
> > +	struct counter_device *const counter = dev_get_drvdata(dev);
> > +
> > +	counter_chrdev_remove(counter);
> >  	ida_free(&counter_ida, dev->id);
> >  }
> >  
> > @@ -31,6 +42,8 @@ static struct bus_type counter_bus_type = {
> >  	.dev_name = "counter",
> >  };
> >  
> > +static dev_t counter_devt;
> > +
> >  /**
> >   * counter_register - register Counter to the system
> >   * @counter:	pointer to Counter to register
> > @@ -53,10 +66,13 @@ int counter_register(struct counter_device *const counter)
> >  	if (id < 0)
> >  		return id;
> >  
> > +	mutex_init(&counter->ops_exist_lock);
> > +
> >  	/* Configure device structure for Counter */
> >  	dev->id = id;
> >  	dev->type = &counter_device_type;
> >  	dev->bus = &counter_bus_type;
> > +	dev->devt = MKDEV(MAJOR(counter_devt), id);
> >  	if (counter->parent) {
> >  		dev->parent = counter->parent;
> >  		dev->of_node = counter->parent->of_node;
> > @@ -64,18 +80,22 @@ int counter_register(struct counter_device *const counter)
> >  	device_initialize(dev);
> >  	dev_set_drvdata(dev, counter);
> >  
> > -	/* Add Counter sysfs attributes */
> > -	err = counter_sysfs_add(counter);
> > +	err = counter_chrdev_add(counter);
> >  	if (err < 0)
> >  		goto err_free_id;
> >  
> > -	/* Add device to system */
> > -	err = device_add(dev);
> > +	err = counter_sysfs_add(counter);
> >  	if (err < 0)
> > -		goto err_free_id;
> > +		goto err_remove_chrdev;
> > +
> > +	err = cdev_device_add(&counter->chrdev, dev);
> > +	if (err < 0)
> > +		goto err_remove_chrdev;
> >  
> >  	return 0;
> >  
> > +err_remove_chrdev:
> > +	counter_chrdev_remove(counter);
> 
> Totally trivial, but the sysfs cleanup is managed by devm_ whilst this counter_chrdev_remove()
> is manual.  That leaves us in a case where the unwind order is subtly different from the
> reverse of the setup order.  Is there any reason to not do the sysfs part of register first
> and hence have the ordering match?

I think in an earlier revision of this patchset counter-chrdev had a
dependency on counter-sysfs, but that's no longer the case so we should
be able to reorder these calls so that the unwind order matches.

> >  err_free_id:
> >  	put_device(dev);
> >  	return err;
> > @@ -93,7 +113,16 @@ void counter_unregister(struct counter_device *const counter)
> >  	if (!counter)
> >  		return;
> >  
> > -	device_unregister(&counter->dev);
> > +	cdev_device_del(&counter->chrdev, &counter->dev);
> > +
> > +	mutex_lock(&counter->ops_exist_lock);
> > +
> > +	counter->ops = NULL;
> > +	wake_up(&counter->events_wait);
> > +
> > +	mutex_unlock(&counter->ops_exist_lock);
> > +
> > +	put_device(&counter->dev);
> >  }
> >  EXPORT_SYMBOL_GPL(counter_unregister);
> >  
> > @@ -127,13 +156,30 @@ int devm_counter_register(struct device *dev,
> >  }
> >  EXPORT_SYMBOL_GPL(devm_counter_register);
> >  
> > +#define COUNTER_DEV_MAX 256
> > +
> >  static int __init counter_init(void)
> >  {
> > -	return bus_register(&counter_bus_type);
> > +	int err;
> > +
> > +	err = bus_register(&counter_bus_type);
> 
> Hmm. This isn't ideal in IIO either, but logically bus_register() is the part that
> exposes the infrastructure for other drivers to attach to so should be last thing
> in init.  However, I'm fairly sure the dependency handing will ensure this init()
> is finished before it tries to register any counter drivers.  Hence we are fine
> anyway.
> 
> So in conclusion - nothing needs changing here...

Ack. Given that things work all right rigth now, I'll revisit this again
in the future as a general cleanup improvment after this patchset is
merged so that we don't risk complicating the Counter character device
functionality introduction any further.

> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter");
> > +	if (err < 0)
> > +		goto err_unregister_bus;
> > +
> > +	return 0;
> > +
> > +err_unregister_bus:
> > +	bus_unregister(&counter_bus_type);
> > +	return err;
> >  }
> >  
> >  static void __exit counter_exit(void)
> >  {
> > +	unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX);
> >  	bus_unregister(&counter_bus_type);
> >  }
> >  
> > diff --git a/include/linux/counter.h b/include/linux/counter.h
> > index 7c9f7e23953a..9c9984655f8c 100644
> > --- a/include/linux/counter.h
> > +++ b/include/linux/counter.h
> > @@ -6,9 +6,14 @@
> >  #ifndef _COUNTER_H_
> >  #define _COUNTER_H_
> >  
> > +#include <linux/cdev.h>
> >  #include <linux/device.h>
> >  #include <linux/kernel.h>
> > +#include <linux/kfifo.h>
> > +#include <linux/mutex.h>
> > +#include <linux/spinlock_types.h>
> >  #include <linux/types.h>
> > +#include <linux/wait.h>
> >  #include <uapi/linux/counter.h>
> >  
> >  struct counter_device;
> > @@ -199,6 +204,20 @@ struct counter_count {
> >  	size_t num_ext;
> >  };
> >  
> > +/**
> > + * struct counter_event_node - Counter Event node
> > + * @l:		list of current watching Counter events
> > + * @event:	event that triggers
> > + * @channel:	event channel
> > + * @comp_list:	list of components to watch when event triggers
> > + */
> > +struct counter_event_node {
> > +	struct list_head l;
> > +	u8 event;
> > +	u8 channel;
> > +	struct list_head comp_list;
> > +};
> > +
> >  /**
> >   * struct counter_ops - Callbacks from driver
> >   * @signal_read:	optional read callback for Signals. The read level of
> > @@ -222,6 +241,13 @@ struct counter_count {
> >   * @action_write:	optional write callback for Synapse action modes. The
> >   *			action mode to write for the respective Synapse is
> >   *			passed in via the action parameter.
> > + * @events_configure:	optional write callback to configure events. The list of
> > + *			struct counter_event_node may be accessed via the
> > + *			events_list member of the counter parameter.
> > + * @watch_validate:	optional callback to validate a watch. The Counter
> > + *			component watch configuration is passed in via the watch
> > + *			parameter. A return value of 0 indicates a valid Counter
> > + *			component watch configuration.
> >   */
> >  struct counter_ops {
> >  	int (*signal_read)(struct counter_device *counter,
> > @@ -245,6 +271,9 @@ struct counter_ops {
> >  			    struct counter_count *count,
> >  			    struct counter_synapse *synapse,
> >  			    enum counter_synapse_action action);
> > +	int (*events_configure)(struct counter_device *counter);
> > +	int (*watch_validate)(struct counter_device *counter,
> > +			      const struct counter_watch *watch);
> >  };
> >  
> >  /**
> > @@ -260,6 +289,16 @@ struct counter_ops {
> >   * @num_ext:		number of Counter device extensions specified in @ext
> >   * @priv:		optional private data supplied by driver
> >   * @dev:		internal device structure
> > + * @chrdev:		internal character device structure
> > + * @events_list:	list of current watching Counter events
> > + * @events_list_lock:	lock to protect Counter events list operations
> > + * @next_events_list:	list of next watching Counter events
> > + * @n_events_list_lock:	lock to protect Counter next events list operations
> > + * @events:		queue of detected Counter events
> > + * @events_wait:	wait queue to allow blocking reads of Counter events
> > + * @events_lock:	lock to protect Counter events queue read operations
> > + * @chrdev_lock:	lock to limit chrdev to a single open at a time
> > + * @ops_exist_lock:	lock to prevent use during removal
> >   */
> >  struct counter_device {
> >  	const char *name;
> > @@ -278,12 +317,24 @@ struct counter_device {
> >  	void *priv;
> >  
> >  	struct device dev;
> > +	struct cdev chrdev;
> > +	struct list_head events_list;
> > +	spinlock_t events_list_lock;
> > +	struct list_head next_events_list;
> > +	struct mutex n_events_list_lock;
> > +	DECLARE_KFIFO_PTR(events, struct counter_event);
> > +	wait_queue_head_t events_wait;
> > +	struct mutex events_lock;
> > +	DECLARE_BITMAP(chrdev_lock, 1);
> 
> Why use a bitmap for this rather than any of the other lock types?

We can't use a mutex due to the problems mentioned in
https://lore.kernel.org/linux-arm-kernel/YOq19zTsOzKA8v7c@shinobu/T/#m6072133d418d598a5f368bb942c945e46cfab9a5
so I think a bitmap might be the most straightforward solution here. It
goes without saying that I'm open to any other suggestions as well if
there is a better solution for this case.

William Breathitt Gray

> > +	struct mutex ops_exist_lock;
> >  };
> >  
> >  int counter_register(struct counter_device *const counter);
> >  void counter_unregister(struct counter_device *const counter);
> >  int devm_counter_register(struct device *dev,
> >  			  struct counter_device *const counter);
> > +void counter_push_event(struct counter_device *const counter, const u8 event,
> > +			const u8 channel);
> >  
> >  #define COUNTER_COMP_DEVICE_U8(_name, _read, _write) \
> >  { \

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 07/14] counter: Add character device interface
@ 2021-09-20 10:09       ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-09-20 10:09 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	Dan Carpenter


[-- Attachment #1.1: Type: text/plain, Size: 34337 bytes --]

On Sun, Sep 12, 2021 at 05:18:42PM +0100, Jonathan Cameron wrote:
> On Fri, 27 Aug 2021 12:47:51 +0900
> William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> 
> > This patch introduces a character device interface for the Counter
> > subsystem. Device data is exposed through standard character device read
> > operations. Device data is gathered when a Counter event is pushed by
> > the respective Counter device driver. Configuration is handled via ioctl
> > operations on the respective Counter character device node.
> > 
> > Cc: David Lechner <david@lechnology.com>
> > Cc: Gwendal Grignou <gwendal@chromium.org>
> > Cc: Dan Carpenter <dan.carpenter@oracle.com>
> > Cc: Oleksij Rempel <o.rempel@pengutronix.de>
> > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> 
> Hi William,
> 
> Why the bit based lock?  It feels like a mutex_trylock() type approach or
> spinlock_trylock() would be a more common solution to this problem.
> There is precedence for doing what you have here though so I'm not that
> worried about it.

Hi Jonathan,

We originally used a mutex for this, but Jarkko discovered that this
produced a warning because chrdev_lock would be held when returning to
user space:
https://lore.kernel.org/linux-arm-kernel/YOq19zTsOzKA8v7c@shinobu/T/#m6072133d418d598a5f368bb942c945e46cfab9a5

Following David Lechner's suggestion, I decided to reimplement
chrdev_lock as a bitmap using an atomic flag.

> There are a few more things inline.
> 
> I've now been through this patch with as fine toothed comb as I'm likely to
> do so.  Hence I won't do another review unless there are substantial changes.
> I nearly applied it as it stands, but given we aren't in a rush (merge window
> open), it's worth just a little more time to tidy up loose ends.
> 
> Jonathan

Responses follow below.

> > ---
> >  drivers/counter/Makefile         |   2 +-
> >  drivers/counter/counter-chrdev.c | 553 +++++++++++++++++++++++++++++++
> >  drivers/counter/counter-chrdev.h |  14 +
> >  drivers/counter/counter-core.c   |  60 +++-
> >  include/linux/counter.h          |  51 +++
> >  include/uapi/linux/counter.h     |  98 ++++++
> >  6 files changed, 770 insertions(+), 8 deletions(-)
> >  create mode 100644 drivers/counter/counter-chrdev.c
> >  create mode 100644 drivers/counter/counter-chrdev.h
> > 
> > diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
> > index 1ab7e087fdc2..8fde6c100ebc 100644
> > --- a/drivers/counter/Makefile
> > +++ b/drivers/counter/Makefile
> > @@ -4,7 +4,7 @@
> >  #
> >  
> >  obj-$(CONFIG_COUNTER) += counter.o
> > -counter-y := counter-core.o counter-sysfs.o
> > +counter-y := counter-core.o counter-sysfs.o counter-chrdev.o
> >  
> >  obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
> >  obj-$(CONFIG_INTERRUPT_CNT)		+= interrupt-cnt.o
> > diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c
> > new file mode 100644
> > index 000000000000..30659b67d8f3
> > --- /dev/null
> > +++ b/drivers/counter/counter-chrdev.c
> > @@ -0,0 +1,553 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Generic Counter character device interface
> > + * Copyright (C) 2020 William Breathitt Gray
> > + */
> > +#include <linux/bitops.h>
> > +#include <linux/cdev.h>
> > +#include <linux/counter.h>
> > +#include <linux/err.h>
> > +#include <linux/errno.h>
> > +#include <linux/export.h>
> > +#include <linux/fs.h>
> > +#include <linux/kfifo.h>
> > +#include <linux/list.h>
> > +#include <linux/mutex.h>
> > +#include <linux/nospec.h>
> > +#include <linux/poll.h>
> > +#include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/timekeeping.h>
> > +#include <linux/types.h>
> > +#include <linux/uaccess.h>
> > +#include <linux/wait.h>
> > +
> > +#include "counter-chrdev.h"
> > +
> > +struct counter_comp_node {
> > +	struct list_head l;
> > +	struct counter_component component;
> > +	struct counter_comp comp;
> > +	void *parent;
> > +};
> > +
> > +static ssize_t counter_chrdev_read(struct file *filp, char __user *buf,
> > +				   size_t len, loff_t *f_ps)
> > +{
> > +	struct counter_device *const counter = filp->private_data;
> > +	int err;
> > +	unsigned int copied;
> > +
> > +	if (!counter->ops)
> > +		return -ENODEV;
> > +
> > +	if (len < sizeof(struct counter_event))
> > +		return -EINVAL;
> > +
> > +	do {
> > +		if (kfifo_is_empty(&counter->events)) {
> > +			if (filp->f_flags & O_NONBLOCK)
> > +				return -EAGAIN;
> > +
> > +			err = wait_event_interruptible(counter->events_wait,
> > +					!kfifo_is_empty(&counter->events) ||
> > +					!counter->ops);
> > +			if (err < 0)
> > +				return err;
> > +			if (!counter->ops)
> > +				return -ENODEV;
> > +		}
> > +
> > +		if (mutex_lock_interruptible(&counter->events_lock))
> > +			return -ERESTARTSYS;
> > +		err = kfifo_to_user(&counter->events, buf, len, &copied);
> > +		mutex_unlock(&counter->events_lock);
> > +		if (err < 0)
> > +			return err;
> > +	} while (!copied);
> > +
> > +	return copied;
> > +}
> > +
> > +static __poll_t counter_chrdev_poll(struct file *filp,
> > +				    struct poll_table_struct *pollt)
> > +{
> > +	struct counter_device *const counter = filp->private_data;
> > +	__poll_t events = 0;
> > +
> > +	if (!counter->ops)
> > +		return events;
> > +
> > +	poll_wait(filp, &counter->events_wait, pollt);
> > +
> > +	if (!kfifo_is_empty(&counter->events))
> > +		events = EPOLLIN | EPOLLRDNORM;
> > +
> > +	return events;
> > +}
> > +
> > +static void counter_events_list_free(struct list_head *const events_list)
> > +{
> > +	struct counter_event_node *p, *n;
> > +	struct counter_comp_node *q, *o;
> > +
> > +	list_for_each_entry_safe(p, n, events_list, l) {
> > +		/* Free associated component nodes */
> > +		list_for_each_entry_safe(q, o, &p->comp_list, l) {
> > +			list_del(&q->l);
> > +			kfree(q);
> > +		}
> > +
> > +		/* Free event node */
> > +		list_del(&p->l);
> > +		kfree(p);
> > +	}
> > +}
> > +
> > +static int counter_set_event_node(struct counter_device *const counter,
> > +				  struct counter_watch *const watch,
> > +				  const struct counter_comp_node *const cfg)
> > +{
> > +	struct counter_event_node *event_node;
> > +	int err = 0;
> > +	struct counter_comp_node *comp_node;
> > +
> > +	/* Search for event in the list */
> > +	list_for_each_entry(event_node, &counter->next_events_list, l)
> > +		if (event_node->event == watch->event &&
> > +		    event_node->channel == watch->channel)
> > +			break;
> > +
> > +	/* If event is not already in the list */
> > +	if (&event_node->l == &counter->next_events_list) {
> > +		/* Allocate new event node */
> > +		event_node = kmalloc(sizeof(*event_node), GFP_KERNEL);
> > +		if (!event_node)
> > +			return -ENOMEM;
> > +
> > +		/* Configure event node and add to the list */
> > +		event_node->event = watch->event;
> > +		event_node->channel = watch->channel;
> > +		INIT_LIST_HEAD(&event_node->comp_list);
> > +		list_add(&event_node->l, &counter->next_events_list);
> > +	}
> > +
> > +	/* Check if component watch has already been set before */
> > +	list_for_each_entry(comp_node, &event_node->comp_list, l)
> > +		if (comp_node->parent == cfg->parent &&
> > +		    comp_node->comp.count_u8_read == cfg->comp.count_u8_read) {
> > +			err = -EINVAL;
> > +			goto exit_free_event_node;
> > +		}
> > +
> > +	/* Allocate component node */
> > +	comp_node = kmalloc(sizeof(*comp_node), GFP_KERNEL);
> > +	if (!comp_node) {
> > +		err = -ENOMEM;
> > +		goto exit_free_event_node;
> > +	}
> > +	*comp_node = *cfg;
> > +
> > +	/* Add component node to event node */
> > +	list_add_tail(&comp_node->l, &event_node->comp_list);
> > +
> > +exit_free_event_node:
> > +	/* Free event node if no one else is watching */
> > +	if (list_empty(&event_node->comp_list)) {
> > +		list_del(&event_node->l);
> > +		kfree(event_node);
> > +	}
> > +
> > +	return err;
> > +}
> > +
> > +static int counter_enable_events(struct counter_device *const counter)
> > +{
> > +	unsigned long flags;
> > +	int err = 0;
> > +
> > +	mutex_lock(&counter->n_events_list_lock);
> > +	spin_lock_irqsave(&counter->events_list_lock, flags);
> > +
> > +	counter_events_list_free(&counter->events_list);
> > +	list_replace_init(&counter->next_events_list,
> > +			  &counter->events_list);
> > +
> > +	if (counter->ops->events_configure)
> > +		err = counter->ops->events_configure(counter);
> > +
> > +	spin_unlock_irqrestore(&counter->events_list_lock, flags);
> > +	mutex_unlock(&counter->n_events_list_lock);
> > +
> > +	return err;
> > +}
> > +
> > +static int counter_disable_events(struct counter_device *const counter)
> > +{
> > +	unsigned long flags;
> > +	int err = 0;
> > +
> > +	spin_lock_irqsave(&counter->events_list_lock, flags);
> > +
> > +	counter_events_list_free(&counter->events_list);
> > +
> > +	if (counter->ops->events_configure)
> > +		err = counter->ops->events_configure(counter);
> > +
> > +	spin_unlock_irqrestore(&counter->events_list_lock, flags);
> > +
> > +	mutex_lock(&counter->n_events_list_lock);
> > +
> > +	counter_events_list_free(&counter->next_events_list);
> > +
> > +	mutex_unlock(&counter->n_events_list_lock);
> > +
> > +	return err;
> > +}
> > +
> > +static int counter_add_watch(struct counter_device *const counter,
> > +			     const unsigned long arg)
> > +{
> > +	void __user *const uwatch = (void __user *)arg;
> > +	struct counter_watch watch;
> > +	struct counter_comp_node comp_node = {};
> > +	size_t parent, id;
> > +	struct counter_comp *ext;
> > +	size_t num_ext;
> > +	int err = 0;
> > +
> > +	if (copy_from_user(&watch, uwatch, sizeof(watch)))
> > +		return -EFAULT;
> > +
> > +	if (watch.component.type == COUNTER_COMPONENT_NONE)
> > +		goto no_component;
> > +
> > +	parent = watch.component.parent;
> > +
> > +	/* Configure parent component info for comp node */
> > +	switch (watch.component.scope) {
> > +	case COUNTER_SCOPE_DEVICE:
> > +		ext = counter->ext;
> > +		num_ext = counter->num_ext;
> > +		break;
> > +	case COUNTER_SCOPE_SIGNAL:
> > +		if (parent >= counter->num_signals)
> > +			return -EINVAL;
> > +		parent = array_index_nospec(parent, counter->num_signals);
> > +
> > +		comp_node.parent = counter->signals + parent;
> > +
> > +		ext = counter->signals[parent].ext;
> > +		num_ext = counter->signals[parent].num_ext;
> > +		break;
> > +	case COUNTER_SCOPE_COUNT:
> > +		if (parent >= counter->num_counts)
> > +			return -EINVAL;
> > +		parent = array_index_nospec(parent, counter->num_counts);
> > +
> > +		comp_node.parent = counter->counts + parent;
> > +
> > +		ext = counter->counts[parent].ext;
> > +		num_ext = counter->counts[parent].num_ext;
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	id = watch.component.id;
> > +
> > +	/* Configure component info for comp node */
> > +	switch (watch.component.type) {
> > +	case COUNTER_COMPONENT_SIGNAL:
> > +		if (watch.component.scope != COUNTER_SCOPE_SIGNAL)
> > +			return -EINVAL;
> > +
> > +		comp_node.comp.type = COUNTER_COMP_SIGNAL_LEVEL;
> > +		comp_node.comp.signal_u32_read = counter->ops->signal_read;
> > +		break;
> > +	case COUNTER_COMPONENT_COUNT:
> > +		if (watch.component.scope != COUNTER_SCOPE_COUNT)
> > +			return -EINVAL;
> > +
> > +		comp_node.comp.type = COUNTER_COMP_U64;
> > +		comp_node.comp.count_u64_read = counter->ops->count_read;
> > +		break;
> > +	case COUNTER_COMPONENT_FUNCTION:
> > +		if (watch.component.scope != COUNTER_SCOPE_COUNT)
> > +			return -EINVAL;
> > +
> > +		comp_node.comp.type = COUNTER_COMP_FUNCTION;
> > +		comp_node.comp.count_u32_read = counter->ops->function_read;
> > +		break;
> > +	case COUNTER_COMPONENT_SYNAPSE_ACTION:
> > +		if (watch.component.scope != COUNTER_SCOPE_COUNT)
> > +			return -EINVAL;
> > +		if (id >= counter->counts[parent].num_synapses)
> > +			return -EINVAL;
> > +		id = array_index_nospec(id, counter->counts[parent].num_synapses);
> > +
> > +		comp_node.comp.type = COUNTER_COMP_SYNAPSE_ACTION;
> > +		comp_node.comp.action_read = counter->ops->action_read;
> > +		comp_node.comp.priv = counter->counts[parent].synapses + id;
> > +		break;
> > +	case COUNTER_COMPONENT_EXTENSION:
> > +		if (id >= num_ext)
> > +			return -EINVAL;
> > +		id = array_index_nospec(id, num_ext);
> > +
> > +		comp_node.comp = ext[id];
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +	/* Check if any read callback is set; this is part of a union */
> > +	if (!comp_node.comp.count_u8_read)
> 
> This isn't valid for C.  The compiler is allowed to treat the elements
> of a union as not being in the same memory etc unless they are of the same
> type (which isn't true here - I think anyway)...
> 
> So whilst it seems silly you need to check all the callbacks in this if
> statement. It will 'almost' always work as you have it, but it's possible
> some future optimisation by the compiler will mean that u8_read would be
> set by this point, but it delayed setting e.g. u32_read until after
> this check.

Ack, I'll recreate a couple macros to handle these checks.

> > +		return -EOPNOTSUPP;
> > +
> > +no_component:
> > +	mutex_lock(&counter->n_events_list_lock);
> > +
> > +	if (counter->ops->watch_validate) {
> > +		err = counter->ops->watch_validate(counter, &watch);
> > +		if (err < 0)
> > +			goto err_exit;
> > +	}
> > +
> > +	comp_node.component = watch.component;
> > +
> > +	err = counter_set_event_node(counter, &watch, &comp_node);
> > +
> > +err_exit:
> > +	mutex_unlock(&counter->n_events_list_lock);
> > +
> > +	return err;
> > +}
> > +
> > +static long counter_chrdev_ioctl(struct file *filp, unsigned int cmd,
> > +				 unsigned long arg)
> > +{
> > +	struct counter_device *const counter = filp->private_data;
> > +	int ret = -ENODEV;
> > +
> > +	mutex_lock(&counter->ops_exist_lock);
> > +
> > +	if (!counter->ops)
> > +		goto out_unlock;
> > +
> > +	switch (cmd) {
> > +	case COUNTER_ADD_WATCH_IOCTL:
> > +		ret = counter_add_watch(counter, arg);
> > +		break;
> > +	case COUNTER_ENABLE_EVENTS_IOCTL:
> > +		ret = counter_enable_events(counter);
> > +		break;
> > +	case COUNTER_DISABLE_EVENTS_IOCTL:
> > +		ret = counter_disable_events(counter);
> > +		break;
> > +	default:
> > +		ret = -ENOIOCTLCMD;
> > +		break;
> > +	}
> > +
> > +out_unlock:
> > +	mutex_unlock(&counter->ops_exist_lock);
> > +
> > +	return ret;
> > +}
> > +
> > +static int counter_chrdev_open(struct inode *inode, struct file *filp)
> > +{
> > +	struct counter_device *const counter = container_of(inode->i_cdev,
> > +							    typeof(*counter),
> > +							    chrdev);
> > +
> > +	if (test_and_set_bit_lock(0, counter->chrdev_lock))
> > +		return -EBUSY;
> > +
> > +	get_device(&counter->dev);
> > +	filp->private_data = counter;
> > +
> > +	return nonseekable_open(inode, filp);
> > +}
> > +
> > +static int counter_chrdev_release(struct inode *inode, struct file *filp)
> > +{
> > +	struct counter_device *const counter = filp->private_data;
> > +	int ret = 0;
> > +
> > +	mutex_lock(&counter->ops_exist_lock);
> > +
> > +	if (!counter->ops) {
> 
> This needs a comment to explain how you would get here and
> why these two lists need cleaning up here if we do.
> 
> Superficially it feels to me like you could just add a counter->ops check in
> counter_disable_events() and then call that directly.  I'm guessing
> I am missing a deadlock or similar however.

I don't believe there is a risk of a deadlock, I just felt this
conditional check isn't really related to the counter_disable_events()
path so I kept it seperate; the events lists are freed in
counter_disable_events() but that's incidental rather than the purpose
of the function. My reasoning for the separation is that there are two
scenarios where counter_chrdev_release is called: the first is when a
user closes the chrdev, while the second is when the Counter driver is
removed.

For the first scenario, the counter_disable_events() function is called
to stop events from firing off when noone has the chrdev open. In the
second scenario, we are not interested in disabling events, we know
we're no longer going to be interacting with this device again so we
want to free any held memory.

I want to keep the intention of these code paths clear because the
distinction is important if we start managing additional memory in the
future (i.e. memory unrelated to the events list that would not be freed
in counter_disable_events()), so I'll add a comment explaining what's
happening in this path. Alternatively, I could define a
counter_chrdev_free() function and toss those list free calls into
there, but perhaps that would be overkill right now for just two calls.

> > +		counter_events_list_free(&counter->events_list);
> > +		counter_events_list_free(&counter->next_events_list);
> > +		ret = -ENODEV;
> > +		goto out_unlock;
> > +	}
> > +
> > +	ret = counter_disable_events(counter);
> > +	if (ret < 0) {
> > +		mutex_unlock(&counter->ops_exist_lock);
> > +		return ret;
> > +	}
> > +
> > +out_unlock:
> > +	mutex_unlock(&counter->ops_exist_lock);
> > +
> > +	put_device(&counter->dev);
> > +	clear_bit_unlock(0, counter->chrdev_lock);
> > +
> > +	return ret;
> > +}
> > +
> > +static const struct file_operations counter_fops = {
> > +	.owner = THIS_MODULE,
> > +	.llseek = no_llseek,
> > +	.read = counter_chrdev_read,
> > +	.poll = counter_chrdev_poll,
> > +	.unlocked_ioctl = counter_chrdev_ioctl,
> > +	.open = counter_chrdev_open,
> > +	.release = counter_chrdev_release,
> > +};
> > +
> > +int counter_chrdev_add(struct counter_device *const counter)
> 
> To think about: This isn't doing the add. So would counter_chrdev_init() be more
> appropriate? The fact it can fail due to the kfifo_alloc makes that
> naming less than ideal though.

I've been using the "add" here to refer to adding the chrdev to the
counter_device structure rather than to the rest of the system. I used a
similar naming convention for the counter-sysfs.c file so I think
"counter_chrdev_add" here should be all right and is consistent with the
existing "counter_sysfs_add" function.

> > +{
> > +	/* Initialize Counter events lists */
> > +	INIT_LIST_HEAD(&counter->events_list);
> > +	INIT_LIST_HEAD(&counter->next_events_list);
> > +	spin_lock_init(&counter->events_list_lock);
> > +	mutex_init(&counter->n_events_list_lock);
> > +	init_waitqueue_head(&counter->events_wait);
> > +	mutex_init(&counter->events_lock);
> > +
> > +	/* Initialize character device */
> > +	clear_bit(0, counter->chrdev_lock);
> > +	cdev_init(&counter->chrdev, &counter_fops);
> > +
> > +	/* Allocate Counter events queue */
> > +	return kfifo_alloc(&counter->events, 64, GFP_KERNEL);
> > +}
> > +
> > +void counter_chrdev_remove(struct counter_device *const counter)
> > +{
> > +	kfifo_free(&counter->events);
> > +}
> > +
> > +static int counter_get_data(struct counter_device *const counter,
> > +			    const struct counter_comp_node *const comp_node,
> > +			    u64 *const value)
> > +{
> > +	const struct counter_comp *const comp = &comp_node->comp;
> > +	void *const parent = comp_node->parent;
> > +	u8 value_u8 = 0;
> > +	u32 value_u32 = 0;
> > +	int ret;
> > +
> > +	if (comp_node->component.type == COUNTER_COMPONENT_NONE)
> > +		return 0;
> > +
> > +	switch (comp->type) {
> > +	case COUNTER_COMP_U8:
> > +	case COUNTER_COMP_BOOL:
> > +		switch (comp_node->component.scope) {
> > +		case COUNTER_SCOPE_DEVICE:
> > +			ret = comp->device_u8_read(counter, &value_u8);
> > +			break;
> > +		case COUNTER_SCOPE_SIGNAL:
> > +			ret = comp->signal_u8_read(counter, parent, &value_u8);
> > +			break;
> > +		case COUNTER_SCOPE_COUNT:
> > +			ret = comp->count_u8_read(counter, parent, &value_u8);
> > +			break;
> > +		}
> > +		*value = value_u8;
> > +		return ret;
> > +	case COUNTER_COMP_SIGNAL_LEVEL:
> > +	case COUNTER_COMP_FUNCTION:
> > +	case COUNTER_COMP_ENUM:
> > +	case COUNTER_COMP_COUNT_DIRECTION:
> > +	case COUNTER_COMP_COUNT_MODE:
> > +		switch (comp_node->component.scope) {
> > +		case COUNTER_SCOPE_DEVICE:
> > +			ret = comp->device_u32_read(counter, &value_u32);
> > +			break;
> > +		case COUNTER_SCOPE_SIGNAL:
> > +			ret = comp->signal_u32_read(counter, parent,
> > +						    &value_u32);
> > +			break;
> > +		case COUNTER_SCOPE_COUNT:
> > +			ret = comp->count_u32_read(counter, parent, &value_u32);
> > +			break;
> > +		}
> > +		*value = value_u32;
> > +		return ret;
> > +	case COUNTER_COMP_U64:
> > +		switch (comp_node->component.scope) {
> > +		case COUNTER_SCOPE_DEVICE:
> > +			return comp->device_u64_read(counter, value);
> > +		case COUNTER_SCOPE_SIGNAL:
> > +			return comp->signal_u64_read(counter, parent, value);
> > +		case COUNTER_SCOPE_COUNT:
> > +			return comp->count_u64_read(counter, parent, value);
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case COUNTER_COMP_SYNAPSE_ACTION:
> > +		ret = comp->action_read(counter, parent, comp->priv,
> > +					&value_u32);
> > +		*value = value_u32;
> > +		return ret;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +/**
> > + * counter_push_event - queue event for userspace reading
> > + * @counter:	pointer to Counter structure
> > + * @event:	triggered event
> > + * @channel:	event channel
> > + *
> > + * Note: If no one is watching for the respective event, it is silently
> > + * discarded.
> > + */
> > +void counter_push_event(struct counter_device *const counter, const u8 event,
> > +			const u8 channel)
> > +{
> > +	struct counter_event ev;
> > +	unsigned int copied = 0;
> > +	unsigned long flags;
> > +	struct counter_event_node *event_node;
> > +	struct counter_comp_node *comp_node;
> > +
> > +	ev.timestamp = ktime_get_ns();
> > +	ev.watch.event = event;
> > +	ev.watch.channel = channel;
> > +
> > +	/* Could be in an interrupt context, so use a spin lock */
> > +	spin_lock_irqsave(&counter->events_list_lock, flags);
> > +
> > +	/* Search for event in the list */
> 
> Worth keeping in mind that searching a list is not exactly a scalable
> solution even if it's fine for now.
> 
> > +	list_for_each_entry(event_node, &counter->events_list, l)
> > +		if (event_node->event == event &&
> > +		    event_node->channel == channel)
> > +			break;
> > +
> > +	/* If event is not in the list */
> > +	if (&event_node->l == &counter->events_list)
> > +		goto exit_early;
> > +
> > +	/* Read and queue relevant comp for userspace */
> > +	list_for_each_entry(comp_node, &event_node->comp_list, l) {
> > +		ev.watch.component = comp_node->component;
> > +		ev.status = -counter_get_data(counter, comp_node, &ev.value);
> > +
> > +		copied += kfifo_in(&counter->events, &ev, 1);
> 
> I had a short debate with myself on whether this or kfifo_put() was more appropriate
> (as fixed record length).  I think this particular case they end up the same anyway
> so it doesn't matter (nothing to do here!)
>  
> > +	}
> > +
> > +exit_early:
> > +	spin_unlock_irqrestore(&counter->events_list_lock, flags);
> > +
> > +	if (copied)
> > +		wake_up_poll(&counter->events_wait, EPOLLIN);
> > +}
> > +EXPORT_SYMBOL_GPL(counter_push_event);
> > diff --git a/drivers/counter/counter-chrdev.h b/drivers/counter/counter-chrdev.h
> > new file mode 100644
> > index 000000000000..5529d16703c4
> > --- /dev/null
> > +++ b/drivers/counter/counter-chrdev.h
> > @@ -0,0 +1,14 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Counter character device interface
> > + * Copyright (C) 2020 William Breathitt Gray
> > + */
> > +#ifndef _COUNTER_CHRDEV_H_
> > +#define _COUNTER_CHRDEV_H_
> > +
> > +#include <linux/counter.h>
> > +
> > +int counter_chrdev_add(struct counter_device *const counter);
> > +void counter_chrdev_remove(struct counter_device *const counter);
> > +
> > +#endif /* _COUNTER_CHRDEV_H_ */
> > diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
> > index 3cda2c47bacb..28dc1ff63c94 100644
> > --- a/drivers/counter/counter-core.c
> > +++ b/drivers/counter/counter-core.c
> > @@ -3,14 +3,22 @@
> >   * Generic Counter interface
> >   * Copyright (C) 2020 William Breathitt Gray
> >   */
> > +#include <linux/cdev.h>
> >  #include <linux/counter.h>
> >  #include <linux/device.h>
> > +#include <linux/device/bus.h>
> >  #include <linux/export.h>
> > +#include <linux/fs.h>
> >  #include <linux/gfp.h>
> >  #include <linux/idr.h>
> >  #include <linux/init.h>
> > +#include <linux/kdev_t.h>
> >  #include <linux/module.h>
> > +#include <linux/mutex.h>
> > +#include <linux/types.h>
> > +#include <linux/wait.h>
> >  
> > +#include "counter-chrdev.h"
> >  #include "counter-sysfs.h"
> >  
> >  /* Provides a unique ID for each counter device */
> > @@ -18,6 +26,9 @@ static DEFINE_IDA(counter_ida);
> >  
> >  static void counter_device_release(struct device *dev)
> >  {
> > +	struct counter_device *const counter = dev_get_drvdata(dev);
> > +
> > +	counter_chrdev_remove(counter);
> >  	ida_free(&counter_ida, dev->id);
> >  }
> >  
> > @@ -31,6 +42,8 @@ static struct bus_type counter_bus_type = {
> >  	.dev_name = "counter",
> >  };
> >  
> > +static dev_t counter_devt;
> > +
> >  /**
> >   * counter_register - register Counter to the system
> >   * @counter:	pointer to Counter to register
> > @@ -53,10 +66,13 @@ int counter_register(struct counter_device *const counter)
> >  	if (id < 0)
> >  		return id;
> >  
> > +	mutex_init(&counter->ops_exist_lock);
> > +
> >  	/* Configure device structure for Counter */
> >  	dev->id = id;
> >  	dev->type = &counter_device_type;
> >  	dev->bus = &counter_bus_type;
> > +	dev->devt = MKDEV(MAJOR(counter_devt), id);
> >  	if (counter->parent) {
> >  		dev->parent = counter->parent;
> >  		dev->of_node = counter->parent->of_node;
> > @@ -64,18 +80,22 @@ int counter_register(struct counter_device *const counter)
> >  	device_initialize(dev);
> >  	dev_set_drvdata(dev, counter);
> >  
> > -	/* Add Counter sysfs attributes */
> > -	err = counter_sysfs_add(counter);
> > +	err = counter_chrdev_add(counter);
> >  	if (err < 0)
> >  		goto err_free_id;
> >  
> > -	/* Add device to system */
> > -	err = device_add(dev);
> > +	err = counter_sysfs_add(counter);
> >  	if (err < 0)
> > -		goto err_free_id;
> > +		goto err_remove_chrdev;
> > +
> > +	err = cdev_device_add(&counter->chrdev, dev);
> > +	if (err < 0)
> > +		goto err_remove_chrdev;
> >  
> >  	return 0;
> >  
> > +err_remove_chrdev:
> > +	counter_chrdev_remove(counter);
> 
> Totally trivial, but the sysfs cleanup is managed by devm_ whilst this counter_chrdev_remove()
> is manual.  That leaves us in a case where the unwind order is subtly different from the
> reverse of the setup order.  Is there any reason to not do the sysfs part of register first
> and hence have the ordering match?

I think in an earlier revision of this patchset counter-chrdev had a
dependency on counter-sysfs, but that's no longer the case so we should
be able to reorder these calls so that the unwind order matches.

> >  err_free_id:
> >  	put_device(dev);
> >  	return err;
> > @@ -93,7 +113,16 @@ void counter_unregister(struct counter_device *const counter)
> >  	if (!counter)
> >  		return;
> >  
> > -	device_unregister(&counter->dev);
> > +	cdev_device_del(&counter->chrdev, &counter->dev);
> > +
> > +	mutex_lock(&counter->ops_exist_lock);
> > +
> > +	counter->ops = NULL;
> > +	wake_up(&counter->events_wait);
> > +
> > +	mutex_unlock(&counter->ops_exist_lock);
> > +
> > +	put_device(&counter->dev);
> >  }
> >  EXPORT_SYMBOL_GPL(counter_unregister);
> >  
> > @@ -127,13 +156,30 @@ int devm_counter_register(struct device *dev,
> >  }
> >  EXPORT_SYMBOL_GPL(devm_counter_register);
> >  
> > +#define COUNTER_DEV_MAX 256
> > +
> >  static int __init counter_init(void)
> >  {
> > -	return bus_register(&counter_bus_type);
> > +	int err;
> > +
> > +	err = bus_register(&counter_bus_type);
> 
> Hmm. This isn't ideal in IIO either, but logically bus_register() is the part that
> exposes the infrastructure for other drivers to attach to so should be last thing
> in init.  However, I'm fairly sure the dependency handing will ensure this init()
> is finished before it tries to register any counter drivers.  Hence we are fine
> anyway.
> 
> So in conclusion - nothing needs changing here...

Ack. Given that things work all right rigth now, I'll revisit this again
in the future as a general cleanup improvment after this patchset is
merged so that we don't risk complicating the Counter character device
functionality introduction any further.

> > +	if (err < 0)
> > +		return err;
> > +
> > +	err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter");
> > +	if (err < 0)
> > +		goto err_unregister_bus;
> > +
> > +	return 0;
> > +
> > +err_unregister_bus:
> > +	bus_unregister(&counter_bus_type);
> > +	return err;
> >  }
> >  
> >  static void __exit counter_exit(void)
> >  {
> > +	unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX);
> >  	bus_unregister(&counter_bus_type);
> >  }
> >  
> > diff --git a/include/linux/counter.h b/include/linux/counter.h
> > index 7c9f7e23953a..9c9984655f8c 100644
> > --- a/include/linux/counter.h
> > +++ b/include/linux/counter.h
> > @@ -6,9 +6,14 @@
> >  #ifndef _COUNTER_H_
> >  #define _COUNTER_H_
> >  
> > +#include <linux/cdev.h>
> >  #include <linux/device.h>
> >  #include <linux/kernel.h>
> > +#include <linux/kfifo.h>
> > +#include <linux/mutex.h>
> > +#include <linux/spinlock_types.h>
> >  #include <linux/types.h>
> > +#include <linux/wait.h>
> >  #include <uapi/linux/counter.h>
> >  
> >  struct counter_device;
> > @@ -199,6 +204,20 @@ struct counter_count {
> >  	size_t num_ext;
> >  };
> >  
> > +/**
> > + * struct counter_event_node - Counter Event node
> > + * @l:		list of current watching Counter events
> > + * @event:	event that triggers
> > + * @channel:	event channel
> > + * @comp_list:	list of components to watch when event triggers
> > + */
> > +struct counter_event_node {
> > +	struct list_head l;
> > +	u8 event;
> > +	u8 channel;
> > +	struct list_head comp_list;
> > +};
> > +
> >  /**
> >   * struct counter_ops - Callbacks from driver
> >   * @signal_read:	optional read callback for Signals. The read level of
> > @@ -222,6 +241,13 @@ struct counter_count {
> >   * @action_write:	optional write callback for Synapse action modes. The
> >   *			action mode to write for the respective Synapse is
> >   *			passed in via the action parameter.
> > + * @events_configure:	optional write callback to configure events. The list of
> > + *			struct counter_event_node may be accessed via the
> > + *			events_list member of the counter parameter.
> > + * @watch_validate:	optional callback to validate a watch. The Counter
> > + *			component watch configuration is passed in via the watch
> > + *			parameter. A return value of 0 indicates a valid Counter
> > + *			component watch configuration.
> >   */
> >  struct counter_ops {
> >  	int (*signal_read)(struct counter_device *counter,
> > @@ -245,6 +271,9 @@ struct counter_ops {
> >  			    struct counter_count *count,
> >  			    struct counter_synapse *synapse,
> >  			    enum counter_synapse_action action);
> > +	int (*events_configure)(struct counter_device *counter);
> > +	int (*watch_validate)(struct counter_device *counter,
> > +			      const struct counter_watch *watch);
> >  };
> >  
> >  /**
> > @@ -260,6 +289,16 @@ struct counter_ops {
> >   * @num_ext:		number of Counter device extensions specified in @ext
> >   * @priv:		optional private data supplied by driver
> >   * @dev:		internal device structure
> > + * @chrdev:		internal character device structure
> > + * @events_list:	list of current watching Counter events
> > + * @events_list_lock:	lock to protect Counter events list operations
> > + * @next_events_list:	list of next watching Counter events
> > + * @n_events_list_lock:	lock to protect Counter next events list operations
> > + * @events:		queue of detected Counter events
> > + * @events_wait:	wait queue to allow blocking reads of Counter events
> > + * @events_lock:	lock to protect Counter events queue read operations
> > + * @chrdev_lock:	lock to limit chrdev to a single open at a time
> > + * @ops_exist_lock:	lock to prevent use during removal
> >   */
> >  struct counter_device {
> >  	const char *name;
> > @@ -278,12 +317,24 @@ struct counter_device {
> >  	void *priv;
> >  
> >  	struct device dev;
> > +	struct cdev chrdev;
> > +	struct list_head events_list;
> > +	spinlock_t events_list_lock;
> > +	struct list_head next_events_list;
> > +	struct mutex n_events_list_lock;
> > +	DECLARE_KFIFO_PTR(events, struct counter_event);
> > +	wait_queue_head_t events_wait;
> > +	struct mutex events_lock;
> > +	DECLARE_BITMAP(chrdev_lock, 1);
> 
> Why use a bitmap for this rather than any of the other lock types?

We can't use a mutex due to the problems mentioned in
https://lore.kernel.org/linux-arm-kernel/YOq19zTsOzKA8v7c@shinobu/T/#m6072133d418d598a5f368bb942c945e46cfab9a5
so I think a bitmap might be the most straightforward solution here. It
goes without saying that I'm open to any other suggestions as well if
there is a better solution for this case.

William Breathitt Gray

> > +	struct mutex ops_exist_lock;
> >  };
> >  
> >  int counter_register(struct counter_device *const counter);
> >  void counter_unregister(struct counter_device *const counter);
> >  int devm_counter_register(struct device *dev,
> >  			  struct counter_device *const counter);
> > +void counter_push_event(struct counter_device *const counter, const u8 event,
> > +			const u8 channel);
> >  
> >  #define COUNTER_COMP_DEVICE_U8(_name, _read, _write) \
> >  { \

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 07/14] counter: Add character device interface
  2021-09-20 10:09       ` William Breathitt Gray
@ 2021-09-26 15:15         ` Jonathan Cameron
  -1 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-26 15:15 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	Dan Carpenter

On Mon, 20 Sep 2021 19:09:13 +0900
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> On Sun, Sep 12, 2021 at 05:18:42PM +0100, Jonathan Cameron wrote:
> > On Fri, 27 Aug 2021 12:47:51 +0900
> > William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> >   
> > > This patch introduces a character device interface for the Counter
> > > subsystem. Device data is exposed through standard character device read
> > > operations. Device data is gathered when a Counter event is pushed by
> > > the respective Counter device driver. Configuration is handled via ioctl
> > > operations on the respective Counter character device node.
> > > 
> > > Cc: David Lechner <david@lechnology.com>
> > > Cc: Gwendal Grignou <gwendal@chromium.org>
> > > Cc: Dan Carpenter <dan.carpenter@oracle.com>
> > > Cc: Oleksij Rempel <o.rempel@pengutronix.de>
> > > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>  
> > 
> > Hi William,
> > 
> > Why the bit based lock?  It feels like a mutex_trylock() type approach or
> > spinlock_trylock() would be a more common solution to this problem.
> > There is precedence for doing what you have here though so I'm not that
> > worried about it.  
> 
> Hi Jonathan,
> 
> We originally used a mutex for this, but Jarkko discovered that this
> produced a warning because chrdev_lock would be held when returning to
> user space:
> https://lore.kernel.org/linux-arm-kernel/YOq19zTsOzKA8v7c@shinobu/T/#m6072133d418d598a5f368bb942c945e46cfab9a5
> 
> Following David Lechner's suggestion, I decided to reimplement
> chrdev_lock as a bitmap using an atomic flag.

Ok.  I'm not sure bit lock was quite what was intended (as there is only one of them)
but I suppose it doesn't greatly matter.

> 
> > There are a few more things inline.
> > 
> > I've now been through this patch with as fine toothed comb as I'm likely to
> > do so.  Hence I won't do another review unless there are substantial changes.
> > I nearly applied it as it stands, but given we aren't in a rush (merge window
> > open), it's worth just a little more time to tidy up loose ends.
> > 
> > Jonathan  
> 
> Responses follow below.

...
...

> > > +static int counter_chrdev_release(struct inode *inode, struct file *filp)
> > > +{
> > > +	struct counter_device *const counter = filp->private_data;
> > > +	int ret = 0;
> > > +
> > > +	mutex_lock(&counter->ops_exist_lock);
> > > +
> > > +	if (!counter->ops) {  
> > 
> > This needs a comment to explain how you would get here and
> > why these two lists need cleaning up here if we do.
> > 
> > Superficially it feels to me like you could just add a counter->ops check in
> > counter_disable_events() and then call that directly.  I'm guessing
> > I am missing a deadlock or similar however.  
> 
> I don't believe there is a risk of a deadlock, I just felt this
> conditional check isn't really related to the counter_disable_events()
> path so I kept it seperate; the events lists are freed in
> counter_disable_events() but that's incidental rather than the purpose
> of the function. My reasoning for the separation is that there are two
> scenarios where counter_chrdev_release is called: the first is when a
> user closes the chrdev, while the second is when the Counter driver is
> removed.
> 
> For the first scenario, the counter_disable_events() function is called
> to stop events from firing off when noone has the chrdev open. In the
> second scenario, we are not interested in disabling events, we know
> we're no longer going to be interacting with this device again so we
> want to free any held memory.
> 
> I want to keep the intention of these code paths clear because the
> distinction is important if we start managing additional memory in the
> future (i.e. memory unrelated to the events list that would not be freed
> in counter_disable_events()), so I'll add a comment explaining what's
> happening in this path. Alternatively, I could define a
> counter_chrdev_free() function and toss those list free calls into
> there, but perhaps that would be overkill right now for just two calls.

A comment would be enough for now I think.

> 
> > > +		counter_events_list_free(&counter->events_list);
> > > +		counter_events_list_free(&counter->next_events_list);
> > > +		ret = -ENODEV;
> > > +		goto out_unlock;
> > > +	}
> > > +
> > > +	ret = counter_disable_events(counter);
> > > +	if (ret < 0) {
> > > +		mutex_unlock(&counter->ops_exist_lock);
> > > +		return ret;
> > > +	}
> > > +
> > > +out_unlock:
> > > +	mutex_unlock(&counter->ops_exist_lock);
> > > +
> > > +	put_device(&counter->dev);
> > > +	clear_bit_unlock(0, counter->chrdev_lock);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static const struct file_operations counter_fops = {
> > > +	.owner = THIS_MODULE,
> > > +	.llseek = no_llseek,
> > > +	.read = counter_chrdev_read,
> > > +	.poll = counter_chrdev_poll,
> > > +	.unlocked_ioctl = counter_chrdev_ioctl,
> > > +	.open = counter_chrdev_open,
> > > +	.release = counter_chrdev_release,
> > > +};
> > > +
> > > +int counter_chrdev_add(struct counter_device *const counter)  
> > 
> > To think about: This isn't doing the add. So would counter_chrdev_init() be more
> > appropriate? The fact it can fail due to the kfifo_alloc makes that
> > naming less than ideal though.  
> 
> I've been using the "add" here to refer to adding the chrdev to the
> counter_device structure rather than to the rest of the system. I used a
> similar naming convention for the counter-sysfs.c file so I think
> "counter_chrdev_add" here should be all right and is consistent with the
> existing "counter_sysfs_add" function.

Hmm. I'll go with maybe and reserve the right to say I told you so if
it ever causes problem (and I can remember this discussion which is
fairly unlikely!).

...


...

> > >  
> > >  /**
> > > @@ -260,6 +289,16 @@ struct counter_ops {
> > >   * @num_ext:		number of Counter device extensions specified in @ext
> > >   * @priv:		optional private data supplied by driver
> > >   * @dev:		internal device structure
> > > + * @chrdev:		internal character device structure
> > > + * @events_list:	list of current watching Counter events
> > > + * @events_list_lock:	lock to protect Counter events list operations
> > > + * @next_events_list:	list of next watching Counter events
> > > + * @n_events_list_lock:	lock to protect Counter next events list operations
> > > + * @events:		queue of detected Counter events
> > > + * @events_wait:	wait queue to allow blocking reads of Counter events
> > > + * @events_lock:	lock to protect Counter events queue read operations
> > > + * @chrdev_lock:	lock to limit chrdev to a single open at a time
> > > + * @ops_exist_lock:	lock to prevent use during removal
> > >   */
> > >  struct counter_device {
> > >  	const char *name;
> > > @@ -278,12 +317,24 @@ struct counter_device {
> > >  	void *priv;
> > >  
> > >  	struct device dev;
> > > +	struct cdev chrdev;
> > > +	struct list_head events_list;
> > > +	spinlock_t events_list_lock;
> > > +	struct list_head next_events_list;
> > > +	struct mutex n_events_list_lock;
> > > +	DECLARE_KFIFO_PTR(events, struct counter_event);
> > > +	wait_queue_head_t events_wait;
> > > +	struct mutex events_lock;
> > > +	DECLARE_BITMAP(chrdev_lock, 1);  
> > 
> > Why use a bitmap for this rather than any of the other lock types?  
> 
> We can't use a mutex due to the problems mentioned in
> https://lore.kernel.org/linux-arm-kernel/YOq19zTsOzKA8v7c@shinobu/T/#m6072133d418d598a5f368bb942c945e46cfab9a5
> so I think a bitmap might be the most straightforward solution here. It
> goes without saying that I'm open to any other suggestions as well if
> there is a better solution for this case.

Add a comment here so no one tries to refactor it in the future without
understanding  your reasoning.

> 
> William Breathitt Gray


Thanks,

Jonathan

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 07/14] counter: Add character device interface
@ 2021-09-26 15:15         ` Jonathan Cameron
  0 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-26 15:15 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	Dan Carpenter

On Mon, 20 Sep 2021 19:09:13 +0900
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> On Sun, Sep 12, 2021 at 05:18:42PM +0100, Jonathan Cameron wrote:
> > On Fri, 27 Aug 2021 12:47:51 +0900
> > William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> >   
> > > This patch introduces a character device interface for the Counter
> > > subsystem. Device data is exposed through standard character device read
> > > operations. Device data is gathered when a Counter event is pushed by
> > > the respective Counter device driver. Configuration is handled via ioctl
> > > operations on the respective Counter character device node.
> > > 
> > > Cc: David Lechner <david@lechnology.com>
> > > Cc: Gwendal Grignou <gwendal@chromium.org>
> > > Cc: Dan Carpenter <dan.carpenter@oracle.com>
> > > Cc: Oleksij Rempel <o.rempel@pengutronix.de>
> > > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>  
> > 
> > Hi William,
> > 
> > Why the bit based lock?  It feels like a mutex_trylock() type approach or
> > spinlock_trylock() would be a more common solution to this problem.
> > There is precedence for doing what you have here though so I'm not that
> > worried about it.  
> 
> Hi Jonathan,
> 
> We originally used a mutex for this, but Jarkko discovered that this
> produced a warning because chrdev_lock would be held when returning to
> user space:
> https://lore.kernel.org/linux-arm-kernel/YOq19zTsOzKA8v7c@shinobu/T/#m6072133d418d598a5f368bb942c945e46cfab9a5
> 
> Following David Lechner's suggestion, I decided to reimplement
> chrdev_lock as a bitmap using an atomic flag.

Ok.  I'm not sure bit lock was quite what was intended (as there is only one of them)
but I suppose it doesn't greatly matter.

> 
> > There are a few more things inline.
> > 
> > I've now been through this patch with as fine toothed comb as I'm likely to
> > do so.  Hence I won't do another review unless there are substantial changes.
> > I nearly applied it as it stands, but given we aren't in a rush (merge window
> > open), it's worth just a little more time to tidy up loose ends.
> > 
> > Jonathan  
> 
> Responses follow below.

...
...

> > > +static int counter_chrdev_release(struct inode *inode, struct file *filp)
> > > +{
> > > +	struct counter_device *const counter = filp->private_data;
> > > +	int ret = 0;
> > > +
> > > +	mutex_lock(&counter->ops_exist_lock);
> > > +
> > > +	if (!counter->ops) {  
> > 
> > This needs a comment to explain how you would get here and
> > why these two lists need cleaning up here if we do.
> > 
> > Superficially it feels to me like you could just add a counter->ops check in
> > counter_disable_events() and then call that directly.  I'm guessing
> > I am missing a deadlock or similar however.  
> 
> I don't believe there is a risk of a deadlock, I just felt this
> conditional check isn't really related to the counter_disable_events()
> path so I kept it seperate; the events lists are freed in
> counter_disable_events() but that's incidental rather than the purpose
> of the function. My reasoning for the separation is that there are two
> scenarios where counter_chrdev_release is called: the first is when a
> user closes the chrdev, while the second is when the Counter driver is
> removed.
> 
> For the first scenario, the counter_disable_events() function is called
> to stop events from firing off when noone has the chrdev open. In the
> second scenario, we are not interested in disabling events, we know
> we're no longer going to be interacting with this device again so we
> want to free any held memory.
> 
> I want to keep the intention of these code paths clear because the
> distinction is important if we start managing additional memory in the
> future (i.e. memory unrelated to the events list that would not be freed
> in counter_disable_events()), so I'll add a comment explaining what's
> happening in this path. Alternatively, I could define a
> counter_chrdev_free() function and toss those list free calls into
> there, but perhaps that would be overkill right now for just two calls.

A comment would be enough for now I think.

> 
> > > +		counter_events_list_free(&counter->events_list);
> > > +		counter_events_list_free(&counter->next_events_list);
> > > +		ret = -ENODEV;
> > > +		goto out_unlock;
> > > +	}
> > > +
> > > +	ret = counter_disable_events(counter);
> > > +	if (ret < 0) {
> > > +		mutex_unlock(&counter->ops_exist_lock);
> > > +		return ret;
> > > +	}
> > > +
> > > +out_unlock:
> > > +	mutex_unlock(&counter->ops_exist_lock);
> > > +
> > > +	put_device(&counter->dev);
> > > +	clear_bit_unlock(0, counter->chrdev_lock);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static const struct file_operations counter_fops = {
> > > +	.owner = THIS_MODULE,
> > > +	.llseek = no_llseek,
> > > +	.read = counter_chrdev_read,
> > > +	.poll = counter_chrdev_poll,
> > > +	.unlocked_ioctl = counter_chrdev_ioctl,
> > > +	.open = counter_chrdev_open,
> > > +	.release = counter_chrdev_release,
> > > +};
> > > +
> > > +int counter_chrdev_add(struct counter_device *const counter)  
> > 
> > To think about: This isn't doing the add. So would counter_chrdev_init() be more
> > appropriate? The fact it can fail due to the kfifo_alloc makes that
> > naming less than ideal though.  
> 
> I've been using the "add" here to refer to adding the chrdev to the
> counter_device structure rather than to the rest of the system. I used a
> similar naming convention for the counter-sysfs.c file so I think
> "counter_chrdev_add" here should be all right and is consistent with the
> existing "counter_sysfs_add" function.

Hmm. I'll go with maybe and reserve the right to say I told you so if
it ever causes problem (and I can remember this discussion which is
fairly unlikely!).

...


...

> > >  
> > >  /**
> > > @@ -260,6 +289,16 @@ struct counter_ops {
> > >   * @num_ext:		number of Counter device extensions specified in @ext
> > >   * @priv:		optional private data supplied by driver
> > >   * @dev:		internal device structure
> > > + * @chrdev:		internal character device structure
> > > + * @events_list:	list of current watching Counter events
> > > + * @events_list_lock:	lock to protect Counter events list operations
> > > + * @next_events_list:	list of next watching Counter events
> > > + * @n_events_list_lock:	lock to protect Counter next events list operations
> > > + * @events:		queue of detected Counter events
> > > + * @events_wait:	wait queue to allow blocking reads of Counter events
> > > + * @events_lock:	lock to protect Counter events queue read operations
> > > + * @chrdev_lock:	lock to limit chrdev to a single open at a time
> > > + * @ops_exist_lock:	lock to prevent use during removal
> > >   */
> > >  struct counter_device {
> > >  	const char *name;
> > > @@ -278,12 +317,24 @@ struct counter_device {
> > >  	void *priv;
> > >  
> > >  	struct device dev;
> > > +	struct cdev chrdev;
> > > +	struct list_head events_list;
> > > +	spinlock_t events_list_lock;
> > > +	struct list_head next_events_list;
> > > +	struct mutex n_events_list_lock;
> > > +	DECLARE_KFIFO_PTR(events, struct counter_event);
> > > +	wait_queue_head_t events_wait;
> > > +	struct mutex events_lock;
> > > +	DECLARE_BITMAP(chrdev_lock, 1);  
> > 
> > Why use a bitmap for this rather than any of the other lock types?  
> 
> We can't use a mutex due to the problems mentioned in
> https://lore.kernel.org/linux-arm-kernel/YOq19zTsOzKA8v7c@shinobu/T/#m6072133d418d598a5f368bb942c945e46cfab9a5
> so I think a bitmap might be the most straightforward solution here. It
> goes without saying that I'm open to any other suggestions as well if
> there is a better solution for this case.

Add a comment here so no one tries to refactor it in the future without
understanding  your reasoning.

> 
> William Breathitt Gray


Thanks,

Jonathan

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 07/14] counter: Add character device interface
  2021-09-26 15:15         ` Jonathan Cameron
@ 2021-09-27 10:21           ` William Breathitt Gray
  -1 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-09-27 10:21 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	Dan Carpenter

[-- Attachment #1: Type: text/plain, Size: 2083 bytes --]

On Sun, Sep 26, 2021 at 04:15:42PM +0100, Jonathan Cameron wrote:
> On Mon, 20 Sep 2021 19:09:13 +0900
> William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> 
> > On Sun, Sep 12, 2021 at 05:18:42PM +0100, Jonathan Cameron wrote:
> > > On Fri, 27 Aug 2021 12:47:51 +0900
> > > William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> > >   
> > > > This patch introduces a character device interface for the Counter
> > > > subsystem. Device data is exposed through standard character device read
> > > > operations. Device data is gathered when a Counter event is pushed by
> > > > the respective Counter device driver. Configuration is handled via ioctl
> > > > operations on the respective Counter character device node.
> > > > 
> > > > Cc: David Lechner <david@lechnology.com>
> > > > Cc: Gwendal Grignou <gwendal@chromium.org>
> > > > Cc: Dan Carpenter <dan.carpenter@oracle.com>
> > > > Cc: Oleksij Rempel <o.rempel@pengutronix.de>
> > > > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>  
> > > 
> > > Hi William,
> > > 
> > > Why the bit based lock?  It feels like a mutex_trylock() type approach or
> > > spinlock_trylock() would be a more common solution to this problem.
> > > There is precedence for doing what you have here though so I'm not that
> > > worried about it.  
> > 
> > Hi Jonathan,
> > 
> > We originally used a mutex for this, but Jarkko discovered that this
> > produced a warning because chrdev_lock would be held when returning to
> > user space:
> > https://lore.kernel.org/linux-arm-kernel/YOq19zTsOzKA8v7c@shinobu/T/#m6072133d418d598a5f368bb942c945e46cfab9a5
> > 
> > Following David Lechner's suggestion, I decided to reimplement
> > chrdev_lock as a bitmap using an atomic flag.
> 
> Ok.  I'm not sure bit lock was quite what was intended (as there is only one of them)
> but I suppose it doesn't greatly matter.

It didn't cross my mind before, but would declaring chrdev_lock as an
atomic_t be a more appropriate solution here because we have only one
flag?

William Breathitt Gray

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 07/14] counter: Add character device interface
@ 2021-09-27 10:21           ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-09-27 10:21 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-stm32, kernel, a.fatoum, kamel.bouhara, gwendal,
	alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	Dan Carpenter


[-- Attachment #1.1: Type: text/plain, Size: 2083 bytes --]

On Sun, Sep 26, 2021 at 04:15:42PM +0100, Jonathan Cameron wrote:
> On Mon, 20 Sep 2021 19:09:13 +0900
> William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> 
> > On Sun, Sep 12, 2021 at 05:18:42PM +0100, Jonathan Cameron wrote:
> > > On Fri, 27 Aug 2021 12:47:51 +0900
> > > William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> > >   
> > > > This patch introduces a character device interface for the Counter
> > > > subsystem. Device data is exposed through standard character device read
> > > > operations. Device data is gathered when a Counter event is pushed by
> > > > the respective Counter device driver. Configuration is handled via ioctl
> > > > operations on the respective Counter character device node.
> > > > 
> > > > Cc: David Lechner <david@lechnology.com>
> > > > Cc: Gwendal Grignou <gwendal@chromium.org>
> > > > Cc: Dan Carpenter <dan.carpenter@oracle.com>
> > > > Cc: Oleksij Rempel <o.rempel@pengutronix.de>
> > > > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>  
> > > 
> > > Hi William,
> > > 
> > > Why the bit based lock?  It feels like a mutex_trylock() type approach or
> > > spinlock_trylock() would be a more common solution to this problem.
> > > There is precedence for doing what you have here though so I'm not that
> > > worried about it.  
> > 
> > Hi Jonathan,
> > 
> > We originally used a mutex for this, but Jarkko discovered that this
> > produced a warning because chrdev_lock would be held when returning to
> > user space:
> > https://lore.kernel.org/linux-arm-kernel/YOq19zTsOzKA8v7c@shinobu/T/#m6072133d418d598a5f368bb942c945e46cfab9a5
> > 
> > Following David Lechner's suggestion, I decided to reimplement
> > chrdev_lock as a bitmap using an atomic flag.
> 
> Ok.  I'm not sure bit lock was quite what was intended (as there is only one of them)
> but I suppose it doesn't greatly matter.

It didn't cross my mind before, but would declaring chrdev_lock as an
atomic_t be a more appropriate solution here because we have only one
flag?

William Breathitt Gray

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 07/14] counter: Add character device interface
  2021-09-27 10:21           ` William Breathitt Gray
@ 2021-09-27 11:20             ` Jonathan Cameron
  -1 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-27 11:20 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: Jonathan Cameron, linux-stm32, kernel, a.fatoum, kamel.bouhara,
	gwendal, alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	Dan Carpenter

On Mon, 27 Sep 2021 19:21:17 +0900
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> On Sun, Sep 26, 2021 at 04:15:42PM +0100, Jonathan Cameron wrote:
> > On Mon, 20 Sep 2021 19:09:13 +0900
> > William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> >   
> > > On Sun, Sep 12, 2021 at 05:18:42PM +0100, Jonathan Cameron wrote:  
> > > > On Fri, 27 Aug 2021 12:47:51 +0900
> > > > William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> > > >     
> > > > > This patch introduces a character device interface for the Counter
> > > > > subsystem. Device data is exposed through standard character device read
> > > > > operations. Device data is gathered when a Counter event is pushed by
> > > > > the respective Counter device driver. Configuration is handled via ioctl
> > > > > operations on the respective Counter character device node.
> > > > > 
> > > > > Cc: David Lechner <david@lechnology.com>
> > > > > Cc: Gwendal Grignou <gwendal@chromium.org>
> > > > > Cc: Dan Carpenter <dan.carpenter@oracle.com>
> > > > > Cc: Oleksij Rempel <o.rempel@pengutronix.de>
> > > > > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>    
> > > > 
> > > > Hi William,
> > > > 
> > > > Why the bit based lock?  It feels like a mutex_trylock() type approach or
> > > > spinlock_trylock() would be a more common solution to this problem.
> > > > There is precedence for doing what you have here though so I'm not that
> > > > worried about it.    
> > > 
> > > Hi Jonathan,
> > > 
> > > We originally used a mutex for this, but Jarkko discovered that this
> > > produced a warning because chrdev_lock would be held when returning to
> > > user space:
> > > https://lore.kernel.org/linux-arm-kernel/YOq19zTsOzKA8v7c@shinobu/T/#m6072133d418d598a5f368bb942c945e46cfab9a5
> > > 
> > > Following David Lechner's suggestion, I decided to reimplement
> > > chrdev_lock as a bitmap using an atomic flag.  
> > 
> > Ok.  I'm not sure bit lock was quite what was intended (as there is only one of them)
> > but I suppose it doesn't greatly matter.  
> 
> It didn't cross my mind before, but would declaring chrdev_lock as an
> atomic_t be a more appropriate solution here because we have only one
> flag?
> 
> William Breathitt Gray
> 

It would be less esoteric.  This was the first time I've ever come across the bitlock stuff
whereas atomics are an every day thing.

Thanks,

Jonathan

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 07/14] counter: Add character device interface
@ 2021-09-27 11:20             ` Jonathan Cameron
  0 siblings, 0 replies; 69+ messages in thread
From: Jonathan Cameron @ 2021-09-27 11:20 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: Jonathan Cameron, linux-stm32, kernel, a.fatoum, kamel.bouhara,
	gwendal, alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	Dan Carpenter

On Mon, 27 Sep 2021 19:21:17 +0900
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> On Sun, Sep 26, 2021 at 04:15:42PM +0100, Jonathan Cameron wrote:
> > On Mon, 20 Sep 2021 19:09:13 +0900
> > William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> >   
> > > On Sun, Sep 12, 2021 at 05:18:42PM +0100, Jonathan Cameron wrote:  
> > > > On Fri, 27 Aug 2021 12:47:51 +0900
> > > > William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> > > >     
> > > > > This patch introduces a character device interface for the Counter
> > > > > subsystem. Device data is exposed through standard character device read
> > > > > operations. Device data is gathered when a Counter event is pushed by
> > > > > the respective Counter device driver. Configuration is handled via ioctl
> > > > > operations on the respective Counter character device node.
> > > > > 
> > > > > Cc: David Lechner <david@lechnology.com>
> > > > > Cc: Gwendal Grignou <gwendal@chromium.org>
> > > > > Cc: Dan Carpenter <dan.carpenter@oracle.com>
> > > > > Cc: Oleksij Rempel <o.rempel@pengutronix.de>
> > > > > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>    
> > > > 
> > > > Hi William,
> > > > 
> > > > Why the bit based lock?  It feels like a mutex_trylock() type approach or
> > > > spinlock_trylock() would be a more common solution to this problem.
> > > > There is precedence for doing what you have here though so I'm not that
> > > > worried about it.    
> > > 
> > > Hi Jonathan,
> > > 
> > > We originally used a mutex for this, but Jarkko discovered that this
> > > produced a warning because chrdev_lock would be held when returning to
> > > user space:
> > > https://lore.kernel.org/linux-arm-kernel/YOq19zTsOzKA8v7c@shinobu/T/#m6072133d418d598a5f368bb942c945e46cfab9a5
> > > 
> > > Following David Lechner's suggestion, I decided to reimplement
> > > chrdev_lock as a bitmap using an atomic flag.  
> > 
> > Ok.  I'm not sure bit lock was quite what was intended (as there is only one of them)
> > but I suppose it doesn't greatly matter.  
> 
> It didn't cross my mind before, but would declaring chrdev_lock as an
> atomic_t be a more appropriate solution here because we have only one
> flag?
> 
> William Breathitt Gray
> 

It would be less esoteric.  This was the first time I've ever come across the bitlock stuff
whereas atomics are an every day thing.

Thanks,

Jonathan

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 07/14] counter: Add character device interface
  2021-09-27 11:20             ` Jonathan Cameron
@ 2021-09-27 11:33               ` William Breathitt Gray
  -1 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-09-27 11:33 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Jonathan Cameron, linux-stm32, kernel, a.fatoum, kamel.bouhara,
	gwendal, alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	Dan Carpenter

[-- Attachment #1: Type: text/plain, Size: 2770 bytes --]

On Mon, Sep 27, 2021 at 12:20:00PM +0100, Jonathan Cameron wrote:
> On Mon, 27 Sep 2021 19:21:17 +0900
> William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> 
> > On Sun, Sep 26, 2021 at 04:15:42PM +0100, Jonathan Cameron wrote:
> > > On Mon, 20 Sep 2021 19:09:13 +0900
> > > William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> > >   
> > > > On Sun, Sep 12, 2021 at 05:18:42PM +0100, Jonathan Cameron wrote:  
> > > > > On Fri, 27 Aug 2021 12:47:51 +0900
> > > > > William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> > > > >     
> > > > > > This patch introduces a character device interface for the Counter
> > > > > > subsystem. Device data is exposed through standard character device read
> > > > > > operations. Device data is gathered when a Counter event is pushed by
> > > > > > the respective Counter device driver. Configuration is handled via ioctl
> > > > > > operations on the respective Counter character device node.
> > > > > > 
> > > > > > Cc: David Lechner <david@lechnology.com>
> > > > > > Cc: Gwendal Grignou <gwendal@chromium.org>
> > > > > > Cc: Dan Carpenter <dan.carpenter@oracle.com>
> > > > > > Cc: Oleksij Rempel <o.rempel@pengutronix.de>
> > > > > > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>    
> > > > > 
> > > > > Hi William,
> > > > > 
> > > > > Why the bit based lock?  It feels like a mutex_trylock() type approach or
> > > > > spinlock_trylock() would be a more common solution to this problem.
> > > > > There is precedence for doing what you have here though so I'm not that
> > > > > worried about it.    
> > > > 
> > > > Hi Jonathan,
> > > > 
> > > > We originally used a mutex for this, but Jarkko discovered that this
> > > > produced a warning because chrdev_lock would be held when returning to
> > > > user space:
> > > > https://lore.kernel.org/linux-arm-kernel/YOq19zTsOzKA8v7c@shinobu/T/#m6072133d418d598a5f368bb942c945e46cfab9a5
> > > > 
> > > > Following David Lechner's suggestion, I decided to reimplement
> > > > chrdev_lock as a bitmap using an atomic flag.  
> > > 
> > > Ok.  I'm not sure bit lock was quite what was intended (as there is only one of them)
> > > but I suppose it doesn't greatly matter.  
> > 
> > It didn't cross my mind before, but would declaring chrdev_lock as an
> > atomic_t be a more appropriate solution here because we have only one
> > flag?
> > 
> > William Breathitt Gray
> > 
> 
> It would be less esoteric.  This was the first time I've ever come across the bitlock stuff
> whereas atomics are an every day thing.
> 
> Thanks,
> 
> Jonathan

I agree. I'll try that out then and reimplement this using
atomic_inc_and_test() instead of test_and_set_bit_lock().

William Breathitt Gray

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH v16 07/14] counter: Add character device interface
@ 2021-09-27 11:33               ` William Breathitt Gray
  0 siblings, 0 replies; 69+ messages in thread
From: William Breathitt Gray @ 2021-09-27 11:33 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Jonathan Cameron, linux-stm32, kernel, a.fatoum, kamel.bouhara,
	gwendal, alexandre.belloni, david, linux-iio, linux-kernel,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, o.rempel, jarkko.nikula,
	Dan Carpenter


[-- Attachment #1.1: Type: text/plain, Size: 2770 bytes --]

On Mon, Sep 27, 2021 at 12:20:00PM +0100, Jonathan Cameron wrote:
> On Mon, 27 Sep 2021 19:21:17 +0900
> William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> 
> > On Sun, Sep 26, 2021 at 04:15:42PM +0100, Jonathan Cameron wrote:
> > > On Mon, 20 Sep 2021 19:09:13 +0900
> > > William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> > >   
> > > > On Sun, Sep 12, 2021 at 05:18:42PM +0100, Jonathan Cameron wrote:  
> > > > > On Fri, 27 Aug 2021 12:47:51 +0900
> > > > > William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> > > > >     
> > > > > > This patch introduces a character device interface for the Counter
> > > > > > subsystem. Device data is exposed through standard character device read
> > > > > > operations. Device data is gathered when a Counter event is pushed by
> > > > > > the respective Counter device driver. Configuration is handled via ioctl
> > > > > > operations on the respective Counter character device node.
> > > > > > 
> > > > > > Cc: David Lechner <david@lechnology.com>
> > > > > > Cc: Gwendal Grignou <gwendal@chromium.org>
> > > > > > Cc: Dan Carpenter <dan.carpenter@oracle.com>
> > > > > > Cc: Oleksij Rempel <o.rempel@pengutronix.de>
> > > > > > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>    
> > > > > 
> > > > > Hi William,
> > > > > 
> > > > > Why the bit based lock?  It feels like a mutex_trylock() type approach or
> > > > > spinlock_trylock() would be a more common solution to this problem.
> > > > > There is precedence for doing what you have here though so I'm not that
> > > > > worried about it.    
> > > > 
> > > > Hi Jonathan,
> > > > 
> > > > We originally used a mutex for this, but Jarkko discovered that this
> > > > produced a warning because chrdev_lock would be held when returning to
> > > > user space:
> > > > https://lore.kernel.org/linux-arm-kernel/YOq19zTsOzKA8v7c@shinobu/T/#m6072133d418d598a5f368bb942c945e46cfab9a5
> > > > 
> > > > Following David Lechner's suggestion, I decided to reimplement
> > > > chrdev_lock as a bitmap using an atomic flag.  
> > > 
> > > Ok.  I'm not sure bit lock was quite what was intended (as there is only one of them)
> > > but I suppose it doesn't greatly matter.  
> > 
> > It didn't cross my mind before, but would declaring chrdev_lock as an
> > atomic_t be a more appropriate solution here because we have only one
> > flag?
> > 
> > William Breathitt Gray
> > 
> 
> It would be less esoteric.  This was the first time I've ever come across the bitlock stuff
> whereas atomics are an every day thing.
> 
> Thanks,
> 
> Jonathan

I agree. I'll try that out then and reimplement this using
atomic_inc_and_test() instead of test_and_set_bit_lock().

William Breathitt Gray

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 69+ messages in thread

end of thread, other threads:[~2021-09-27 11:35 UTC | newest]

Thread overview: 69+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-27  3:47 [PATCH v16 00/14] Introduce the Counter character device interface William Breathitt Gray
2021-08-27  3:47 ` William Breathitt Gray
2021-08-27  3:47 ` [PATCH v16 01/14] counter: stm32-lptimer-cnt: Provide defines for clock polarities William Breathitt Gray
2021-08-27  3:47   ` William Breathitt Gray
2021-08-31 13:38   ` Fabrice Gasnier
2021-08-31 13:38     ` Fabrice Gasnier
2021-09-08 17:31     ` Jonathan Cameron
2021-09-08 17:31       ` Jonathan Cameron
2021-08-27  3:47 ` [PATCH v16 02/14] counter: stm32-timer-cnt: Provide defines for slave mode selection William Breathitt Gray
2021-08-27  3:47   ` William Breathitt Gray
2021-08-31 13:40   ` Fabrice Gasnier
2021-08-31 13:40     ` Fabrice Gasnier
2021-09-08 17:31     ` Jonathan Cameron
2021-09-08 17:31       ` Jonathan Cameron
2021-08-27  3:47 ` [PATCH v16 03/14] counter: Internalize sysfs interface code William Breathitt Gray
2021-08-27 14:09   ` kernel test robot
2021-08-28  4:56   ` kernel test robot
2021-08-31 13:44   ` [Linux-stm32] " Fabrice Gasnier
2021-08-31 14:16     ` William Breathitt Gray
2021-08-31 14:16       ` William Breathitt Gray
2021-08-31 14:55       ` William Breathitt Gray
2021-08-31 14:55         ` William Breathitt Gray
2021-09-08 17:44     ` Jonathan Cameron
2021-08-27  3:47 ` [PATCH v16 04/14] counter: Update counter.h comments to reflect sysfs internalization William Breathitt Gray
2021-08-27  3:47   ` William Breathitt Gray
2021-09-08 17:46   ` Jonathan Cameron
2021-09-08 17:46     ` Jonathan Cameron
2021-08-27  3:47 ` [PATCH v16 05/14] docs: counter: Update " William Breathitt Gray
2021-08-27  3:47   ` William Breathitt Gray
2021-09-08 17:48   ` Jonathan Cameron
2021-09-08 17:48     ` Jonathan Cameron
2021-08-27  3:47 ` [PATCH v16 06/14] counter: Move counter enums to uapi header William Breathitt Gray
2021-08-27  3:47   ` William Breathitt Gray
2021-08-27  3:47 ` [PATCH v16 07/14] counter: Add character device interface William Breathitt Gray
2021-08-27  3:47   ` William Breathitt Gray
2021-09-12 16:18   ` Jonathan Cameron
2021-09-12 16:18     ` Jonathan Cameron
2021-09-20 10:09     ` William Breathitt Gray
2021-09-20 10:09       ` William Breathitt Gray
2021-09-26 15:15       ` Jonathan Cameron
2021-09-26 15:15         ` Jonathan Cameron
2021-09-27 10:21         ` William Breathitt Gray
2021-09-27 10:21           ` William Breathitt Gray
2021-09-27 11:20           ` Jonathan Cameron
2021-09-27 11:20             ` Jonathan Cameron
2021-09-27 11:33             ` William Breathitt Gray
2021-09-27 11:33               ` William Breathitt Gray
2021-08-27  3:47 ` [PATCH v16 08/14] docs: counter: Document " William Breathitt Gray
2021-08-27  3:47   ` William Breathitt Gray
2021-09-12 16:18   ` Jonathan Cameron
2021-09-12 16:18     ` Jonathan Cameron
2021-08-27  3:47 ` [PATCH v16 09/14] tools/counter: Create Counter tools William Breathitt Gray
2021-08-27  3:47   ` William Breathitt Gray
2021-09-12 16:26   ` Jonathan Cameron
2021-09-12 16:26     ` Jonathan Cameron
2021-08-27  3:47 ` [PATCH v16 10/14] counter: Implement signalZ_action_component_id sysfs attribute William Breathitt Gray
2021-08-27  3:47   ` William Breathitt Gray
2021-08-27  3:47 ` [PATCH v16 11/14] counter: Implement *_component_id sysfs attributes William Breathitt Gray
2021-08-27  3:47   ` William Breathitt Gray
2021-08-27  3:47 ` [PATCH v16 12/14] counter: Implement events_queue_size sysfs attribute William Breathitt Gray
2021-08-27  3:47   ` William Breathitt Gray
2021-08-27  3:47 ` [PATCH v16 13/14] counter: 104-quad-8: Replace mutex with spinlock William Breathitt Gray
2021-08-27  3:47   ` William Breathitt Gray
2021-08-27  3:47 ` [PATCH v16 14/14] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8 William Breathitt Gray
2021-08-27  3:47   ` William Breathitt Gray
2021-08-30 17:17 ` [PATCH v16 00/14] Introduce the Counter character device interface Jonathan Cameron
2021-08-30 17:17   ` Jonathan Cameron
2021-09-12 16:36   ` Jonathan Cameron
2021-09-12 16:36     ` Jonathan Cameron

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.