counter: new TI eQEP driver
Subsystems (5)
David Lechner
reviewerv2 cover counter: new TI eQEP driver
This series adds device tree bindings and a new counter driver for the Texas Instruments Enhanced Quadrature Encoder Pulse (eQEP).
As mentioned in one of the commit messages, to start with, the driver only supports reading the current counter value and setting the min/max values. Other features can be added as the counter subsystem gains support for them.
v2 changes:
- New patch to move TI PWMSS driver from drivers/pwm/ to drivers/bus/
- Device tree bindings converted to .yaml format
- Device tree clock renamed from "fck" to "sysclkout"
- Dropped unused index and strobe signals from counter driver
- Added synapses and actions to counter driver
- Fixed base in of kstrtouint()
- Clarifications in commit messages
This series has been tested on a BeagleBone Blue with the following script:
#!/usr/bin/env python3
from os import path
from time import sleep
COUNTER_PATH = '/sys/bus/counter/devices'
COUNTERS = ['counter0', 'counter1', 'counter2']
COUNT0 = 'count0'
COUNT = 'count'
FUNCTION = 'function'
CEILING = 'ceiling'
FLOOR = 'floor'
ENABLE = 'enable'
cnts = []
for c in COUNTERS:
function_path = path.join(COUNTER_PATH, c, COUNT0, FUNCTION) with open(function_path, 'w') as f:
f.write('quadrature x4')
floor_path = path.join(COUNTER_PATH, c, COUNT0, FLOOR) with open(floor_path, 'w') as f:
f.write(str(0))
ceiling_path = path.join(COUNTER_PATH, c, COUNT0, CEILING) with open(ceiling_path, 'w') as f:
f.write(str(0xffffffff))
enable_path = path.join(COUNTER_PATH, c, COUNT0, ENABLE) with open(enable_path, 'w') as f:
f.write('1')
cnt_path = path.join(COUNTER_PATH, c, COUNT0, COUNT) cnts.append(open(cnt_path, 'r'))
while True:
for c in cnts:
c.seek(0)
val = int(c.read())
if val >= 0x80000000:
val -= 0x100000000
print(val, end=' ')
print()
sleep(1)David Lechner (5):
bus/ti-pwmss: move TI PWMSS driver from PWM to bus subsystem
dt-bindings: counter: new bindings for TI eQEP
counter: new TI eQEP driver
ARM: dts: am33xx: Add nodes for eQEP
ARM: dts: am335x-boneblue: Enable eQEP
.../devicetree/bindings/counter/ti-eqep.yaml | 50 ++
MAINTAINERS | 6 +
arch/arm/boot/dts/am335x-boneblue.dts | 54 ++
arch/arm/boot/dts/am33xx-l4.dtsi | 27 +
drivers/bus/Kconfig | 9 +
drivers/bus/Makefile | 1 +
drivers/{pwm/pwm-tipwmss.c => bus/ti-pwmss.c} | 0
drivers/counter/Kconfig | 12 +
drivers/counter/Makefile | 1 +
drivers/counter/ti-eqep.c | 460 ++++++++++++++++++
drivers/pwm/Kconfig | 9 -
drivers/pwm/Makefile | 1 -
12 files changed, 620 insertions(+), 10 deletions(-)
create mode 100644 Documentation/devicetree/bindings/counter/ti-eqep.yaml
rename drivers/{pwm/pwm-tipwmss.c => bus/ti-pwmss.c} (100%)
create mode 100644 drivers/counter/ti-eqep.cSignature · 1 line
2.17.1
David Lechner
reviewerPATCH v2 1/5 bus/ti-pwmss: move TI PWMSS driver from PWM to bus subsystem
The TI PWMSS driver is a simple bus driver for providing power power management for the PWM peripherals on TI AM33xx SoCs, namely eCAP, eHRPWM and eQEP. The eQEP is a counter rather than a PWM, so it does not make sense to have the bus driver in the PWM subsystem since the PWMSS is not exclusive to PWM devices.
---
v2 changes:
- new patch
drivers/bus/Kconfig | 9 +++++++++
drivers/bus/Makefile | 1 +
drivers/{pwm/pwm-tipwmss.c => bus/ti-pwmss.c} | 0
drivers/pwm/Kconfig | 9 ---------
drivers/pwm/Makefile | 1 -
5 files changed, 10 insertions(+), 10 deletions(-)
rename drivers/{pwm/pwm-tipwmss.c => bus/ti-pwmss.c} (100%)drivers/bus/Kconfig +9 -0
@@ -140,6 +140,15 @@ config TEGRA_GMI Driver for the Tegra Generic Memory Interface bus which can be used to attach devices such as NOR, UART, FPGA and more. +config TI_PWMSS + bool + default y if (ARCH_OMAP2PLUS) && (PWM_TIECAP || PWM_TIEHRPWM) + help + PWM Subsystem driver support for AM33xx SOC. + + PWM submodules require PWM config space access from submodule + drivers and require common parent driver support. + config TI_SYSC bool "TI sysc interconnect target module driver" depends on ARCH_OMAP2PLUS
drivers/bus/Makefile +1 -0
@@ -26,6 +26,7 @@ obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o obj-$(CONFIG_TEGRA_ACONNECT) += tegra-aconnect.o obj-$(CONFIG_TEGRA_GMI) += tegra-gmi.o +obj-$(CONFIG_TI_PWMSS) += ti-pwmss.o obj-$(CONFIG_TI_SYSC) += ti-sysc.o obj-$(CONFIG_TS_NBUS) += ts-nbus.o obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o
drivers/bus/ti-pwmss.c renamed from drivers/pwm/pwm-tipwmss.c +0 -0
drivers/pwm/Kconfig +0 -9
@@ -497,15 +497,6 @@ config PWM_TIEHRPWM To compile this driver as a module, choose M here: the module will be called pwm-tiehrpwm. -config PWM_TIPWMSS - bool - default y if (ARCH_OMAP2PLUS) && (PWM_TIECAP || PWM_TIEHRPWM) - help - PWM Subsystem driver support for AM33xx SOC. - - PWM submodules require PWM config space access from submodule - drivers and require common parent driver support. - config PWM_TWL tristate "TWL4030/6030 PWM support" depends on TWL4030_CORE
drivers/pwm/Makefile +0 -1
@@ -49,7 +49,6 @@ obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o -obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o obj-$(CONFIG_PWM_TWL) += pwm-twl.o obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
Signature · 1 line
2.17.1
David Lechner
reviewerPATCH v2 2/5 dt-bindings: counter: new bindings for TI eQEP
In reply to David LechnerThis documents device tree binding for the Texas Instruments Enhanced Quadrature Encoder Pulse (eQEP) Module found in various TI SoCs.
---
v2 changes:
- convert to .yaml format
- rename clock to "sysclkout"
.../devicetree/bindings/counter/ti-eqep.yaml | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 Documentation/devicetree/bindings/counter/ti-eqep.yaml
Documentation/devicetree/bindings/counter/ti-eqep.yaml new file +50 -0
@@ -0,0 +1,50 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/counter/ti-eqep.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments Enhanced Quadrature Encoder Pulse (eQEP) Module + +maintainers: + - David Lechner <[email protected]> + +properties: + compatible: + const: "ti,am3352-eqep" + + reg: + maxItems: 1 + + interrupts: + description: The eQEP event interrupt + maxItems: 1 + + clocks: + description: The clock that determines the SYSCLKOUT rate for the eQEP + peripheral. + maxItems: 1 + + clock-names: + const: sysclkout + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + eqep0: eqep@180 { + compatible = "ti,am3352-eqep"; + reg = <0x180 0x80>; + clocks = <&l4ls_gclk>; + clock-names = "sysclkout"; + interrupts = <79>; + }; + +...
Signature · 1 line
2.17.1
Rob Herring
maintainerRe: [PATCH v2 2/5] dt-bindings: counter: new bindings for TI eQEP
Quoted 24 lines · David Lechner
This documents device tree binding for the Texas Instruments Enhanced Quadrature Encoder Pulse (eQEP) Module found in various TI SoCs.
Signed-off-by David Lechner <[email protected]>---
v2 changes:
- convert to .yaml format
- rename clock to "sysclkout".../devicetree/bindings/counter/ti-eqep.yaml | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 Documentation/devicetree/bindings/counter/ti-eqep.yamlDocumentation/devicetree/bindings/counter/ti-eqep.yaml new file +4
-0@@ -0,0 +1,50 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/counter/ti-eqep.yaml#
Documentation/devicetree/bindings/counter/ti-eqep.yaml line 14
Quoted 10 lines
Documentation/devicetree/bindings/counter/ti-eqep.yaml new file +10
-0+$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments Enhanced Quadrature Encoder Pulse (eQEP) Module + +maintainers: + - David Lechner <[email protected]> + +properties: + compatible: + const: "ti,am3352-eqep"
No need for quotes
Documentation/devicetree/bindings/counter/ti-eqep.yaml line 20
Quoted 6 lines
Documentation/devicetree/bindings/counter/ti-eqep.yaml new file +6
-0+ + reg: + maxItems: 1 + + interrupts: + description: The eQEP event interrupt
Don't really have to have a description when there is only 1 entry.
Documentation/devicetree/bindings/counter/ti-eqep.yaml line 25
Quoted 5 lines
Documentation/devicetree/bindings/counter/ti-eqep.yaml new file +5
-0+ maxItems: 1 + + clocks: + description: The clock that determines the SYSCLKOUT rate for the eQEP + peripheral.
Same here. 2 spaces in from description is the normal indentation.
Quoted 7 lines
Documentation/devicetree/bindings/counter/ti-eqep.yaml new file +7
-0+ maxItems: 1 + + clock-names: + const: sysclkout + +required: + - compatible
Documentation/devicetree/bindings/counter/ti-eqep.yaml line 42
Quoted 10 lines
Documentation/devicetree/bindings/counter/ti-eqep.yaml new file +10
-0+ - reg + - interrupts + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + eqep0: eqep@180 {
counter@...
Quoted 11 lines
Documentation/devicetree/bindings/counter/ti-eqep.yaml new file +8
-0+ compatible = "ti,am3352-eqep"; + reg = <0x180 0x80>; + clocks = <&l4ls_gclk>; + clock-names = "sysclkout"; + interrupts = <79>; + }; + +...Signature · 1 line
2.17.1
David Lechner
reviewerPATCH v2 3/5 counter: new TI eQEP driver
In reply to David LechnerThis adds a new counter driver for the Texas Instruments Enhanced Quadrature Encoder Pulse (eQEP) module.
Only very basic functionality is currently implemented - only enough to be able to read the position. The actual device has many more features which can be added to the driver on an as-needed basis.
It is not possible to read the QEPA/B signal values in hardware, so that feature is omitted.
The TI_PWMSS kernel option is selected in Kconfig to enable the parent bus, which is needed for power management.
---
v2 changes:
- Dropped unused index and strobe signals
- Added synapses and actions
- Fixed base in of kstrtouint()
- Clarifications in commit message
MAINTAINERS | 6 + drivers/bus/Kconfig | 2 +- drivers/counter/Kconfig | 12 + drivers/counter/Makefile | 1 + drivers/counter/ti-eqep.c | 460 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 480 insertions(+), 1 deletion(-) create mode 100644 drivers/counter/ti-eqep.c
MAINTAINERS +6 -0
@@ -16014,6 +16014,12 @@ S: Maintained F: drivers/media/platform/davinci/ F: include/media/davinci/ +TI ENHANCED QUADRATURE ENCODER PULSE (eQEP) DRIVER +R: David Lechner <[email protected]> +L: [email protected] +F: Documentation/devicetree/bindings/counter/ti-eqep.yaml +F: drivers/counter/ti-eqep.c + TI ETHERNET SWITCH DRIVER (CPSW) R: Grygorii Strashko <[email protected]> L: [email protected]
drivers/bus/Kconfig +1 -1
@@ -142,7 +142,7 @@ config TEGRA_GMI config TI_PWMSS bool - default y if (ARCH_OMAP2PLUS) && (PWM_TIECAP || PWM_TIEHRPWM) + default y if (ARCH_OMAP2PLUS) && (PWM_TIECAP || PWM_TIEHRPWM || TI_EQEP) help PWM Subsystem driver support for AM33xx SOC.
drivers/counter/Kconfig +12 -0
@@ -49,6 +49,18 @@ config STM32_LPTIMER_CNT To compile this driver as a module, choose M here: the module will be called stm32-lptimer-cnt. +config TI_EQEP + tristate "TI eQEP counter driver" + depends on (SOC_AM33XX || COMPILE_TEST) + select PWM + select REGMAP_MMIO + help + Select this option to enable the Texas Instruments Enhanced Quadrature + Encoder Pulse (eQEP) counter driver. + + To compile this driver as a module, choose M here: the module will be + called ti-eqep. + config FTM_QUADDEC tristate "Flex Timer Module Quadrature decoder driver" depends on HAS_IOMEM && OF
drivers/counter/Makefile +1 -0
@@ -8,4 +8,5 @@ obj-$(CONFIG_COUNTER) += counter.o obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o obj-$(CONFIG_STM32_TIMER_CNT) += stm32-timer-cnt.o obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o +obj-$(CONFIG_TI_EQEP) += ti-eqep.o obj-$(CONFIG_FTM_QUADDEC) += ftm-quaddec.o
drivers/counter/ti-eqep.c new file +460 -0
460 lines not on this page · raw message
Signature · 1 line
2.17.1
David Lechner
reviewerdrivers/counter/Kconfig config STM32_LPTIMER_CNT · line 55
Quoted 12 lines · David Lechner
drivers/counter/Kconfig +4
-0@@ -49,6 +49,18 @@ config STM32_LPTIMER_CNT To compile this driver as a module, choose M here: the module will be called stm32-lptimer-cnt. +config TI_EQEP + tristate "TI eQEP counter driver" + depends on (SOC_AM33XX || COMPILE_TEST) + select PWM
oops, missed removing select PWM. This line is no longer needed with patch 1/5.
Quoted 11 lines
drivers/counter/Kconfig +8
-0+ select REGMAP_MMIO + help + Select this option to enable the Texas Instruments Enhanced Quadrature + Encoder Pulse (eQEP) counter driver. + + To compile this driver as a module, choose M here: the module will be + called ti-eqep. + config FTM_QUADDEC tristate "Flex Timer Module Quadrature decoder driver" depends on HAS_IOMEM && OF
Jonathan Cameron
maintainerQuoted 7 lines · On Wed, 7 Aug 2019 14:40:21 -0500 David Lechner <[email protected]> wrote:
This adds a new counter driver for the Texas Instruments Enhanced Quadrature Encoder Pulse (eQEP) module.
Only very basic functionality is currently implemented - only enough to be able to read the position. The actual device has many more features which can be added to the driver on an as-needed basis.
Quoted 7 lines
It is not possible to read the QEPA/B signal values in hardware, so that feature is omitted.
The
TI_PWMSSkernel option is selected inKconfigto enable the parent bus, which is needed for power management.Signed-off-by David Lechner <[email protected]>
Hi David,
A few questions from me around runtime pm and interaction with the userspace interface.
Quoted 502 lines
---
v2 changes:
- Dropped unused index and strobe signals
- Added synapses and actions
- Fixed base in ofkstrtouint()
- Clarifications in commit messageMAINTAINERS | 6 + drivers/bus/Kconfig | 2 +- drivers/counter/Kconfig | 12 + drivers/counter/Makefile | 1 + drivers/counter/ti-eqep.c | 460 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 480 insertions(+), 1 deletion(-) create mode 100644 drivers/counter/ti-eqep.cMAINTAINERS +6
-0@@ -16014,6 +16014,12 @@ S: Maintained F: drivers/media/platform/davinci/ F: include/media/davinci/ +TI ENHANCED QUADRATURE ENCODER PULSE (eQEP) DRIVER +R: David Lechner <[email protected]> +L: [email protected] +F: Documentation/devicetree/bindings/counter/ti-eqep.yaml +F: drivers/counter/ti-eqep.c + TI ETHERNET SWITCH DRIVER (CPSW) R: Grygorii Strashko <[email protected]> L: [email protected]drivers/bus/Kconfig +1
-1@@ -142,7 +142,7 @@ config TEGRA_GMI config TI_PWMSS bool - default y if (ARCH_OMAP2PLUS) && (PWM_TIECAP || PWM_TIEHRPWM) + default y if (ARCH_OMAP2PLUS) && (PWM_TIECAP || PWM_TIEHRPWM || TI_EQEP) help PWM Subsystem driver support for AM33xx SOC.drivers/counter/Kconfig +12
-0@@ -49,6 +49,18 @@ config STM32_LPTIMER_CNT To compile this driver as a module, choose M here: the module will be called stm32-lptimer-cnt. +config TI_EQEP + tristate "TI eQEP counter driver" + depends on (SOC_AM33XX || COMPILE_TEST) + select PWM + select REGMAP_MMIO + help + Select this option to enable the Texas Instruments Enhanced Quadrature + Encoder Pulse (eQEP) counter driver. + + To compile this driver as a module, choose M here: the module will be + called ti-eqep. + config FTM_QUADDEC tristate "Flex Timer Module Quadrature decoder driver" depends on HAS_IOMEM && OFdrivers/counter/Makefile +1
-0@@ -8,4 +8,5 @@ obj-$(CONFIG_COUNTER) += counter.o obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o obj-$(CONFIG_STM32_TIMER_CNT) += stm32-timer-cnt.o obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o +obj-$(CONFIG_TI_EQEP) += ti-eqep.o obj-$(CONFIG_FTM_QUADDEC) += ftm-quaddec.odrivers/counter/ti-eqep.c new file +417
-0@@ -0,0 +1,460 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* +* Copyright (C) 2019 David Lechner <[email protected]> +* +* Counter driver for Texas Instruments Enhanced Quadrature Encoder Pulse (eQEP) +*/ + +#include <linux/bitops.h> +#include <linux/counter.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> + +/* 32-bit registers */ +#define QPOSCNT 0x0 +#define QPOSINIT 0x4 +#define QPOSMAX 0x8 +#define QPOSCMP 0xc +#define QPOSILAT 0x10 +#define QPOSSLAT 0x14 +#define QPOSLAT 0x18 +#define QUTMR 0x1c +#define QUPRD 0x20 + +/* 16-bit registers */ +#define QWDTMR 0x0/* 0x24 */ +#define QWDPRD 0x2/* 0x26 */ +#define QDECCTL 0x4/* 0x28 */ +#define QEPCTL 0x6/* 0x2a */ +#define QCAPCTL 0x8/* 0x2c */ +#define QPOSCTL 0xa/* 0x2e */ +#define QEINT 0xc/* 0x30 */ +#define QFLG 0xe/* 0x32 */ +#define QCLR 0x10/* 0x34 */ +#define QFRC 0x12/* 0x36 */ +#define QEPSTS 0x14/* 0x38 */ +#define QCTMR 0x16/* 0x3a */ +#define QCPRD 0x18/* 0x3c */ +#define QCTMRLAT 0x1a/* 0x3e */ +#define QCPRDLAT 0x1c/* 0x40 */ + +#define QDECCTL_QSRC_SHIFT 14 +#define QDECCTL_QSRC GENMASK(15, 14) +#define QDECCTL_SOEN BIT(13) +#define QDECCTL_SPSEL BIT(12) +#define QDECCTL_XCR BIT(11) +#define QDECCTL_SWAP BIT(10) +#define QDECCTL_IGATE BIT(9) +#define QDECCTL_QAP BIT(8) +#define QDECCTL_QBP BIT(7) +#define QDECCTL_QIP BIT(6) +#define QDECCTL_QSP BIT(5) + +#define QEPCTL_FREE_SOFT GENMASK(15, 14) +#define QEPCTL_PCRM GENMASK(13, 12) +#define QEPCTL_SEI GENMASK(11, 10) +#define QEPCTL_IEI GENMASK(9, 8) +#define QEPCTL_SWI BIT(7) +#define QEPCTL_SEL BIT(6) +#define QEPCTL_IEL GENMASK(5, 4) +#define QEPCTL_PHEN BIT(3) +#define QEPCTL_QCLM BIT(2) +#define QEPCTL_UTE BIT(1) +#define QEPCTL_WDE BIT(0) + +/* EQEP Inputs */ +enum { +TI_EQEP_SIGNAL_QEPA ,/* QEPA/XCLK */ +TI_EQEP_SIGNAL_QEPB ,/* QEPB/XDIR */ +}; + +/* Position Counter Input Modes */ +enum { +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; +struct regmap *regmap16; +}; + +static int ti_eqep_count_read (struct counter_device *counter, +struct counter_count *count, +struct counter_count_read_value *val) +{ +struct ti_eqep_cnt *priv = counter->priv; +u32 cnt; + +regmap_read (priv->regmap32,QPOSCNT , &cnt); +counter_count_read_value_set (val,COUNTER_COUNT_POSITION , &cnt); + +return 0 ; +} + +static int ti_eqep_count_write (struct counter_device *counter, +struct counter_count *count, +struct counter_count_write_value *val) +{ +struct ti_eqep_cnt *priv = counter->priv; +u32 cnt, max; +int err; + + err =counter_count_write_value_get (&cnt,COUNTER_COUNT_POSITION , val); +if (err) +return err; + +regmap_read (priv->regmap32,QPOSMAX , &max); +if (cnt > max) +return -EINVAL ; + +return regmap_write (priv->regmap32,QPOSCNT , cnt); +} + +static int ti_eqep_function_get (struct counter_device *counter, +struct counter_count *count,size_t *function) +{ +struct ti_eqep_cnt *priv = counter->priv; +u32 qdecctl; + +regmap_read (priv->regmap16,QDECCTL , &qdecctl); + *function = (qdecctl &QDECCTL_QSRC ) >>QDECCTL_QSRC_SHIFT ; + +return 0 ; +} + +static int ti_eqep_function_set (struct counter_device *counter, +struct counter_count *count,size_t function) +{ +struct ti_eqep_cnt *priv = counter->priv; + +return regmap_write_bits (priv->regmap16,QDECCTL ,QDECCTL_QSRC , + function <<QDECCTL_QSRC_SHIFT ); +} + +static int ti_eqep_action_get (struct counter_device *counter, +struct counter_count *count, +struct counter_synapse *synapse,size_t *action) +{ +struct ti_eqep_cnt *priv = counter->priv; +size_t function; +u32 qdecctl; +int err; + + err =ti_eqep_function_get (counter, count, &function); +if (err) +return err; + +switch (function) { +case TI_EQEP_COUNT_FUNC_QUAD_COUNT : +/* In quadrature mode, the rising and falling edge of both +* QEPA and QEPB trigger QCLK. +*/ + *action =TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES ; +break ; +case TI_EQEP_COUNT_FUNC_DIR_COUNT : +/* 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 ; +break ; +default : + *action =TI_EQEP_SYNAPSE_ACTION_NONE ; +break ; + } +break ; +case TI_EQEP_COUNT_FUNC_UP_COUNT : +case TI_EQEP_COUNT_FUNC_DOWN_COUNT : +/* In up/down-count modes only QEPA is counted and QEPB is not +* used. +*/ +switch (synapse->signal->id) { +case TI_EQEP_SIGNAL_QEPA : + err =regmap_read (priv->regmap16,QDECCTL , &qdecctl); +if (err) +return err; + +if (qdecctl &QDECCTL_XCR ) + *action =TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES ; +else + *action =TI_EQEP_SYNAPSE_ACTION_RISING_EDGE ; +break ; +default : + *action =TI_EQEP_SYNAPSE_ACTION_NONE ; +break ; + } +break ; + } + +return 0 ; +} + +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, +}; + +static ssize_t ti_eqep_position_ceiling_read (struct counter_device *counter, +struct counter_count *count, +void *ext_priv,char *buf) +{ +struct ti_eqep_cnt *priv = counter->priv; +u32 qposmax; + +regmap_read (priv->regmap32,QPOSMAX , &qposmax); + +return sprintf (buf,"%u\n" , qposmax); +} + +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) +{ +struct ti_eqep_cnt *priv = counter->priv; +int err; +u32 res; + + err =kstrtouint (buf,0 , &res); +if (err <0 ) +return err; + +regmap_write (priv->regmap32,QPOSMAX , res); + +return len; +} + +static ssize_t ti_eqep_position_floor_read (struct counter_device *counter, +struct counter_count *count, +void *ext_priv,char *buf) +{ +struct ti_eqep_cnt *priv = counter->priv; +u32 qposinit; + +regmap_read (priv->regmap32,QPOSINIT , &qposinit); + +return sprintf (buf,"%u\n" , qposinit); +} + +static ssize_t ti_eqep_position_floor_write (struct counter_device *counter, +struct counter_count *count, +void *ext_priv,const char *buf, +size_t len) +{ +struct ti_eqep_cnt *priv = counter->priv; +int err; +u32 res; + + err =kstrtouint (buf,0 , &res); +if (err <0 ) +return err; + +regmap_write (priv->regmap32,QPOSINIT , res); + +return len; +} + +static ssize_t ti_eqep_position_enable_read (struct counter_device *counter, +struct counter_count *count, +void *ext_priv,char *buf) +{ +struct ti_eqep_cnt *priv = counter->priv; +u32 qepctl; + +regmap_read (priv->regmap16,QEPCTL , &qepctl); + +return sprintf (buf,"%u\n" , !!(qepctl &QEPCTL_PHEN )); +} + +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) +{ +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 ); + +return len; +} + +static struct counter_count_ext ti_eqep_position_ext[] = { + { + .name ="ceiling" , + .read = ti_eqep_position_ceiling_read, + .write = ti_eqep_position_ceiling_write, + }, + { + .name ="floor" , + .read = ti_eqep_position_floor_read, + .write = ti_eqep_position_floor_write, + }, + { + .name ="enable" , + .read = ti_eqep_position_enable_read, + .write = ti_eqep_position_enable_write, + }, +}; + +static struct counter_signal ti_eqep_signals[] = { + [TI_EQEP_SIGNAL_QEPA ] = { + .id =TI_EQEP_SIGNAL_QEPA , + .name ="QEPA" + }, + [TI_EQEP_SIGNAL_QEPB ] = { + .id =TI_EQEP_SIGNAL_QEPB , + .name ="QEPB" + }, +}; + +static const enum counter_count_function ti_eqep_position_functions[] = { + [TI_EQEP_COUNT_FUNC_QUAD_COUNT ] =COUNTER_COUNT_FUNCTION_QUADRATURE_X4 , + [TI_EQEP_COUNT_FUNC_DIR_COUNT ] =COUNTER_COUNT_FUNCTION_PULSE_DIRECTION , + [TI_EQEP_COUNT_FUNC_UP_COUNT ] =COUNTER_COUNT_FUNCTION_INCREASE , + [TI_EQEP_COUNT_FUNC_DOWN_COUNT ] =COUNTER_COUNT_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 , +}; + +static struct counter_synapse ti_eqep_position_synapses[] = { + { + .action =TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES , + .actions_list = ti_eqep_position_synapse_actions, + .num_actions =ARRAY_SIZE (ti_eqep_position_synapse_actions), + .signal = &ti_eqep_signals[TI_EQEP_SIGNAL_QEPA ], + }, + { + .action =TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES , + .actions_list = ti_eqep_position_synapse_actions, + .num_actions =ARRAY_SIZE (ti_eqep_position_synapse_actions), + .signal = &ti_eqep_signals[TI_EQEP_SIGNAL_QEPB ], + }, +}; + +static struct counter_count ti_eqep_counts[] = { + { + .id =0 , + .name ="QPOSCNT" , + .functions_list = ti_eqep_position_functions, + .num_functions =ARRAY_SIZE (ti_eqep_position_functions), + .synapses = ti_eqep_position_synapses, + .num_synapses =ARRAY_SIZE (ti_eqep_position_synapses), + .ext = ti_eqep_position_ext, + .num_ext =ARRAY_SIZE (ti_eqep_position_ext), + }, +}; + +static const struct regmap_config ti_eqep_regmap32_config = { + .name ="32-bit" , + .reg_bits =32 , + .val_bits =32 , + .reg_stride =4 , + .max_register =0x24 , +}; + +static const struct regmap_config ti_eqep_regmap16_config = { + .name ="16-bit" , + .reg_bits =16 , + .val_bits =16 , + .reg_stride =2 , + .max_register =0x1e , +}; + +static int ti_eqep_probe (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; +struct ti_eqep_cnt *priv; +struct resource *res; +void __iomem *base; + + priv =devm_kzalloc (dev,sizeof (*priv),GFP_KERNEL ); +if (!priv) +return -ENOMEM ; + + res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); + base =devm_ioremap_resource (dev, res); +if (IS_ERR (base)) +return PTR_ERR (base); + + priv->regmap32 =devm_regmap_init_mmio (dev, base, + &ti_eqep_regmap32_config); +if (IS_ERR (priv->regmap32)) +return PTR_ERR (priv->regmap32); + + priv->regmap16 =devm_regmap_init_mmio (dev, base +0x24 , + &ti_eqep_regmap16_config); +if (IS_ERR (priv->regmap16)) +return PTR_ERR (priv->regmap16); + + priv->counter.name =dev_name (dev);
drivers/counter/ti-eqep.c line 427
Quoted 10 lines
drivers/counter/ti-eqep.c new file +10
-0+ priv->counter.parent = dev; + priv->counter.ops = &ti_eqep_counter_ops; + priv->counter.counts = ti_eqep_counts; + priv->counter.num_counts =ARRAY_SIZE (ti_eqep_counts); + priv->counter.signals = ti_eqep_signals; + priv->counter.num_signals =ARRAY_SIZE (ti_eqep_signals); + priv->counter.priv = priv; + +pm_runtime_enable (dev); +pm_runtime_get (dev);
I'm a little confused on the flow here.
pm_runtime_enable turns on runtime pm in general.
pm_runtime_get basically calls runtime_resume to ensrue the device has power.
drivers/counter/ti-eqep.c line 429
Quoted 2 lines
drivers/counter/ti-eqep.c new file +2
-0+ +return devm_counter_register (dev, &priv->counter);
This registers the userspace interfaces and exposes the device.
drivers/counter/ti-eqep.c line 437
Quoted 8 lines
drivers/counter/ti-eqep.c new file +8
-0+} + +static int ti_eqep_remove (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; + +pm_runtime_put (dev), +pm_runtime_disable (dev);
pm_runtime_put notifies the system that the device is idle (and hence potentially turns it off).
Not good if the counter is still registered.
I'm assuming the presence of runtime pm at all is to interact with a parent driver and hence stop that turning off if this driver is probed? That's probably fine, but add a few comments to make it clear that this driver itself doesn't really do runtime pm at all.
Thanks,
J
Quoted 23 lines
drivers/counter/ti-eqep.c new file +23
-0+ +return 0 ; +} + +static const struct of_device_id ti_eqep_of_match[] = { + { .compatible ="ti,am3352-eqep" , }, + { }, +}; +MODULE_DEVICE_TABLE (of, ti_eqep_of_match); + +static struct platform_driver ti_eqep_driver = { + .probe = ti_eqep_probe, + .remove = ti_eqep_remove, + .driver = { + .name ="ti-eqep-cnt" , + .of_match_table = ti_eqep_of_match, + }, +}; +module_platform_driver (ti_eqep_driver); + +MODULE_AUTHOR ("David Lechner <[email protected]>" ); +MODULE_DESCRIPTION ("TI eQEP counter driver" ); +MODULE_LICENSE ("GPL v2" );
David Lechner
reviewerQuoted 29 lines · Jonathan Cameron
drivers/counter/ti-eqep.c line 427
Quoted 3 lines · On Wed, 7 Aug 2019 14:40:21 -0500 David Lechner <[email protected]> wrote:
drivers/counter/ti-eqep.c new file +3
-0+ +pm_runtime_enable (dev); +pm_runtime_get (dev);I'm a little confused on the flow here.
pm_runtime_enableturns on runtime pm in general.
pm_runtime_getbasically callsruntime_resumeto ensrue the device has power.drivers/counter/ti-eqep.c line 429
Quoted 2 lines
drivers/counter/ti-eqep.c new file +2
-0+ +return devm_counter_register (dev, &priv->counter);This registers the userspace interfaces and exposes the device.
drivers/counter/ti-eqep.c line 437
Quoted 8 lines
drivers/counter/ti-eqep.c new file +8
-0+} + +static int ti_eqep_remove (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; + +pm_runtime_put (dev), +pm_runtime_disable (dev);
pm_runtime_putnotifies the system that the device is idle (and hence potentially turns it off).
Quoted 8 lines
Not good if the counter is still registered.
I'm assuming the presence of runtime pm at all is to interact with a parent driver and hence stop that turning off if this driver is probed? That's probably fine, but add a few comments to make it clear that this driver itself doesn't really do runtime pm at all.
To be honest, despite reading the runtime PM docs more than once, I still don't feel like I have a good grasp on how it is supposed to work. I just know that we get a crash if it is omitted because the IP block is not powered on.
I will fix the ordering in _remove() and add a comment in v3.
Quoted 4 lines · David Lechner
drivers/counter/ti-eqep.c new file +4
-0+static struct counter_synapse ti_eqep_position_synapses[] = { + { + .action =TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES , + .actions_list = ti_eqep_position_synapse_actions,
drivers/counter/ti-eqep.c line 360
Quoted 10 lines
drivers/counter/ti-eqep.c new file +10
-0+ .num_actions = ARRAY_SIZE(ti_eqep_position_synapse_actions), + .signal = &ti_eqep_signals[TI_EQEP_SIGNAL_QEPA], + }, + { + .action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES, + .actions_list = ti_eqep_position_synapse_actions, + .num_actions = ARRAY_SIZE(ti_eqep_position_synapse_actions), + .signal = &ti_eqep_signals[TI_EQEP_SIGNAL_QEPB], + }, +};
Hi David,
Just a minor suggestion for your v3: you don't need to initialize "action" here since it'll be automatically updated in the core counter_action_show function to the value returned by your action_get callback function. So you can safely delete those two ".action =" lines.
William Breathitt Gray
David Lechner
reviewerPATCH v2 4/5 ARM: dts: am33xx: Add nodes for eQEP
In reply to David LechnerThis adds new nodes for the Texas Instruments Enhanced Quadrature Encoder Pulse (eQEP) module in the PWM subsystem on AM33XX.
---
v2 changes:
- clocks renamed to "sysclkout"
arch/arm/boot/dts/am33xx-l4.dtsi | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
arch/arm/boot/dts/am33xx-l4.dtsi +27 -0
@@ -1908,6 +1908,15 @@ status = "disabled"; }; + eqep0: eqep@180 { + compatible = "ti,am3352-eqep"; + reg = <0x180 0x80>; + clocks = <&l4ls_gclk>; + clock-names = "sysclkout"; + interrupts = <79>; + status = "disabled"; + }; + ehrpwm0: pwm@200 { compatible = "ti,am3352-ehrpwm", "ti,am33xx-ehrpwm";
@@ -1961,6 +1970,15 @@ status = "disabled"; }; + eqep1: eqep@180 { + compatible = "ti,am3352-eqep"; + reg = <0x180 0x80>; + clocks = <&l4ls_gclk>; + clock-names = "sysclkout"; + interrupts = <88>; + status = "disabled"; + }; + ehrpwm1: pwm@200 { compatible = "ti,am3352-ehrpwm", "ti,am33xx-ehrpwm";
@@ -2014,6 +2032,15 @@ status = "disabled"; }; + eqep2: eqep@180 { + compatible = "ti,am3352-eqep"; + reg = <0x180 0x80>; + clocks = <&l4ls_gclk>; + clock-names = "sysclkout"; + interrupts = <89>; + status = "disabled"; + }; + ehrpwm2: pwm@200 { compatible = "ti,am3352-ehrpwm", "ti,am33xx-ehrpwm";
Signature · 1 line
2.17.1
David Lechner
reviewerPATCH v2 5/5 ARM: dts: am335x-boneblue: Enable eQEP
In reply to David LechnerThis enables the Enhanced Quadrature Encoder Pulse (eQEP) module for connectors E1, E2 and E3 on BeagleBone Blue.
---
v2 changes:
- none
arch/arm/boot/dts/am335x-boneblue.dts | 54 +++++++++++++++++++++++++++ 1 file changed, 54 insertions(+)
arch/arm/boot/dts/am335x-boneblue.dts +54 -0
@@ -258,6 +258,30 @@ AM33XX_PADCONF(AM335X_PIN_MII1_RXD0, PIN_OUTPUT, MUX_MODE7) /* (M16) gmii1_rxd0.gpio2[21] */ >; }; + + /* E1 */ + eqep0_pins: pinmux_eqep0_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_AXR0, PIN_INPUT, MUX_MODE1) /* (B12) mcasp0_aclkr.eQEP0A_in */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSR, PIN_INPUT, MUX_MODE1) /* (C13) mcasp0_fsr.eQEP0B_in */ + >; + }; + + /* E2 */ + eqep1_pins: pinmux_eqep1_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_INPUT, MUX_MODE2) /* (V2) lcd_data12.eQEP1A_in */ + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_INPUT, MUX_MODE2) /* (V3) lcd_data13.eQEP1B_in */ + >; + }; + + /* E3 */ + eqep2_pins: pinmux_eqep2_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_AD12, PIN_INPUT, MUX_MODE4) /* (T12) gpmc_ad12.eQEP2A_in */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD13, PIN_INPUT, MUX_MODE4) /* (R12) gpmc_ad13.eQEP2B_in */ + >; + }; }; &uart0 {
@@ -530,3 +554,33 @@ line-name = "LS_BUF_EN"; }; }; + +&epwmss0 { + status = "okay"; +}; + +&eqep0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&eqep0_pins>; +}; + +&epwmss1 { + status = "okay"; +}; + +&eqep1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&eqep1_pins>; +}; + +&epwmss2 { + status = "okay"; +}; + +&eqep2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&eqep2_pins>; +};
Signature · 1 line
2.17.1