From dea21323ef892b8480da28e5351bbd1b738149a1 Mon Sep 17 00:00:00 2001 From: Aditya Chopra Date: Wed, 21 Oct 2026 13:25:10 +0000 Subject: [PATCH 1/8] UPSTREAM-PEND: dts: bindings: counter: ti,am3352-eqep: add AM13 mux/QEPS... PR: 121529 Document mux-states routing and ti,qep{a,b,i}-src, include mux-consumer, and add QEPSRCSEL source macros. Signed-off-by: Aditya Chopra --- drivers/counter/Kconfig.ti_am3352_eqep | 12 +++++ dts/bindings/counter/ti,am3352-eqep.yaml | 51 ++++++++++++++++++- .../dt-bindings/counter/ti_am3352_eqep.h | 27 ++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 include/zephyr/dt-bindings/counter/ti_am3352_eqep.h diff --git a/drivers/counter/Kconfig.ti_am3352_eqep b/drivers/counter/Kconfig.ti_am3352_eqep index 177dce381495..e007205fd82c 100644 --- a/drivers/counter/Kconfig.ti_am3352_eqep +++ b/drivers/counter/Kconfig.ti_am3352_eqep @@ -9,3 +9,15 @@ config COUNTER_TI_AM3352_EQEP select PINCTRL help Enables the TI EQEP counter driver. + +if COUNTER_TI_AM3352_EQEP +config COUNTER_TI_AM3352_EQEP_VARIANT_AM13 + bool "TI EQEP AM13E variant (QEPSRCSEL, Input X-BAR, PWREN)" + depends on SOC_SERIES_AM13E + default y + select MUX + select SYSCON + help + Enable AM13E EQEP support: QEPSRCSEL, mux-states X-BAR routing, + and SYSCTL PWREN via ti,pwren. +endif diff --git a/dts/bindings/counter/ti,am3352-eqep.yaml b/dts/bindings/counter/ti,am3352-eqep.yaml index f6a308858921..ee021c71968e 100644 --- a/dts/bindings/counter/ti,am3352-eqep.yaml +++ b/dts/bindings/counter/ti,am3352-eqep.yaml @@ -1,9 +1,17 @@ # SPDX-FileCopyrightText: Copyright (c) 2026 Texas Instruments # SPDX-License-Identifier: Apache-2.0 -description: TI Enhanced Quadrature Encoder Pulse based counter +description: | + TI Enhanced Quadrature Encoder Pulse based counter -include: [base.yaml, pinctrl-device.yaml] + On SoCs where the EQEP inputs are reachable only through a crossbar + (e.g. AM13E), reference the Input X-BAR via "mux-states" instead of + adding SoC-specific pinmux logic to this driver. Each EQEP input is + hard-wired to a fixed Input X-BAR channel (EQEP0A = INPUT17 / channel + 16, and so on); the board or overlay routes a GPIO into that channel + and names the entry. + +include: [base.yaml, pinctrl-device.yaml, mux-consumer.yaml] compatible: "ti,am3352-eqep" @@ -13,3 +21,42 @@ properties: interrupts: required: true + + ti,qepa-src: + type: int + enum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + description: | + QEPSRCSEL.QEPASEL value (AM13E-only): Input X-BAR (0, default), + CMPSS0-3 CTRIPH (1-4), or PWMXBAR1-8 (5-12). See + include/zephyr/dt-bindings/counter/ti_am3352_eqep.h and TRM + Table 25-1. When "mux-states" names a "qepa" entry and this + property is absent, 0 (Input X-BAR) is used. + + ti,qepb-src: + type: int + enum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + description: QEPSRCSEL.QEPBSEL value (AM13E-only). See ti,qepa-src. + + ti,qepi-src: + type: int + enum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + description: QEPSRCSEL.QEPISEL value (AM13E-only). See ti,qepa-src. + +examples: + - | + &eqep0 { + pinctrl-0 = <&gpio_pb11>; + pinctrl-names = "default"; + + /* PB11 (GPIO43) -> INPUTXBAR17 (ch 16) -> EQEP0A */ + mux-states = <&xbar1 16 AM13E2_XBAR_IN_STATE(43, 1)>; + mux-state-names = "qepa"; + }; + + QEPA/QEPB/QEPI can instead be sourced from an on-chip comparator or + the PWM crossbar (AM13E-only); these need no pinctrl or mux entry, + so set the QEPSRCSEL field directly: + + &eqep0 { + ti,qepa-src = ; + }; diff --git a/include/zephyr/dt-bindings/counter/ti_am3352_eqep.h b/include/zephyr/dt-bindings/counter/ti_am3352_eqep.h new file mode 100644 index 000000000000..4e921e91d8ce --- /dev/null +++ b/include/zephyr/dt-bindings/counter/ti_am3352_eqep.h @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026 Texas Instruments + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_COUNTER_TI_AM3352_EQEP_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_COUNTER_TI_AM3352_EQEP_H_ + +/* + * QEPSRCSEL.QEPASEL/QEPBSEL/QEPISEL values (AM13E-only). Matches + * DL_EQEP_SOURCE in the vendor driverlib and TRM Table 25-1. + */ +#define TI_EQEP_SRC_INPUT_XBAR 0 +#define TI_EQEP_SRC_CMPSS0 1 +#define TI_EQEP_SRC_CMPSS1 2 +#define TI_EQEP_SRC_CMPSS2 3 +#define TI_EQEP_SRC_CMPSS3 4 +#define TI_EQEP_SRC_PWMXBAR1 5 +#define TI_EQEP_SRC_PWMXBAR2 6 +#define TI_EQEP_SRC_PWMXBAR3 7 +#define TI_EQEP_SRC_PWMXBAR4 8 +#define TI_EQEP_SRC_PWMXBAR5 9 +#define TI_EQEP_SRC_PWMXBAR6 10 +#define TI_EQEP_SRC_PWMXBAR7 11 +#define TI_EQEP_SRC_PWMXBAR8 12 + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_COUNTER_TI_AM3352_EQEP_H_ */ From b07d34c6bc4d4b717ddf3451a3cbb541b0ae1b03 Mon Sep 17 00:00:00 2001 From: Aditya Chopra Date: Wed, 21 Oct 2026 13:25:10 +0000 Subject: [PATCH 2/8] UPSTREAM-PEND: dts: arm: ti: am13: add eQEP nodes PR: 121529 Describe eqep0/1/2 on AM13E; leave status disabled for board overlays. Signed-off-by: Aditya Chopra --- dts/arm/ti/am13/am13e.dtsi | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/dts/arm/ti/am13/am13e.dtsi b/dts/arm/ti/am13/am13e.dtsi index f813e3cb5c6b..7cfabc23fe1d 100644 --- a/dts/arm/ti/am13/am13e.dtsi +++ b/dts/arm/ti/am13/am13e.dtsi @@ -58,6 +58,30 @@ #pwm-cells = <3>; status = "disabled"; }; + + eqep0: counter@40448000 { + compatible = "ti,am3352-eqep"; + reg = <0x40448000 0x100>; + interrupts = <31 0>; + clocks = <&ckm MSPM0_CLOCK_MCLK>; + status = "disabled"; + }; + + eqep1: counter@40449000 { + compatible = "ti,am3352-eqep"; + reg = <0x40449000 0x100>; + interrupts = <32 0>; + clocks = <&ckm MSPM0_CLOCK_MCLK>; + status = "disabled"; + }; + + eqep2: counter@4044a000 { + compatible = "ti,am3352-eqep"; + reg = <0x4044a000 0x100>; + interrupts = <33 0>; + clocks = <&ckm MSPM0_CLOCK_MCLK>; + status = "disabled"; + }; }; cpus { From dd8186afe3481ba61f95005a0c4114a0ce4e6333 Mon Sep 17 00:00:00 2001 From: Aditya Chopra Date: Wed, 21 Oct 2026 13:25:10 +0000 Subject: [PATCH 3/8] UPSTREAM-PEND: counter: ti_am3352_eqep: add AM13 variant with mux and QE... PR: 121529 Gate AM13E QEPSRCSEL and Input X-BAR mux-states behind COUNTER_TI_AM3352_EQEP_VARIANT_AM13. Signed-off-by: Aditya Chopra --- drivers/counter/counter_ti_am3352_eqep.c | 165 ++++++++++++++++++++--- 1 file changed, 146 insertions(+), 19 deletions(-) diff --git a/drivers/counter/counter_ti_am3352_eqep.c b/drivers/counter/counter_ti_am3352_eqep.c index 4a2f25d16d88..e16df6ed79eb 100644 --- a/drivers/counter/counter_ti_am3352_eqep.c +++ b/drivers/counter/counter_ti_am3352_eqep.c @@ -10,6 +10,20 @@ #include #include +#ifdef CONFIG_CLOCK_CONTROL_TISCI +#include +#endif + +#ifdef CONFIG_CLOCK_CONTROL_MSPM0 +#include +#endif + +#ifdef CONFIG_COUNTER_TI_AM3352_EQEP_VARIANT_AM13 +#include +#include + +#endif + LOG_MODULE_REGISTER(ti_eqep); #define DT_DRV_COMPAT ti_am3352_eqep @@ -39,6 +53,10 @@ struct ti_eqep_regs { uint8_t RESERVED_4[0x4]; /**< Reserved, offset: 0x3A - 0x3E */ volatile uint16_t QCTLAT; /**< QEP Capture Timer Latch Register, offset: 0x3E */ volatile uint16_t QCPRDLAT; /**< QEP Capture Period Latch Register, offset: 0x40 */ +#ifdef CONFIG_COUNTER_TI_AM3352_EQEP_VARIANT_AM13 + uint8_t RESERVED_5[0x2A]; /**< Reserved, offset: 0x42 - 0x6C */ + volatile uint32_t QEPSRCSEL; /**< QEP Source Select, offset: 0x6C */ +#endif }; /* Quadrature Decoder Control Register */ @@ -47,14 +65,14 @@ struct ti_eqep_regs { #define TI_EQEP_QDECCTL_SWAP BIT(10) /* QEP Control Register */ -#define TI_EQEP_QEPCTL_PCRM GENMASK(13, 12) -#define TI_EQEP_QEPCTL_PCRM_MAX (0x1) -#define TI_EQEP_QEPCTL_SWI BIT(7) -#define TI_EQEP_QEPCTL_SEL BIT(6) -#define TI_EQEP_QEPCTL_IEL GENMASK(5, 4) -#define TI_EQEP_QEPCTL_QPEN BIT(3) -#define TI_EQEP_QEPCTL_QCLM BIT(2) -#define TI_EQEP_QEPCTL_UTE BIT(1) +#define TI_EQEP_QEPCTL_PCRM GENMASK(13, 12) +#define TI_EQEP_QEPCTL_PCRM_MAX (0x1) +#define TI_EQEP_QEPCTL_SWI BIT(7) +#define TI_EQEP_QEPCTL_SEL BIT(6) +#define TI_EQEP_QEPCTL_IEL GENMASK(5, 4) +#define TI_EQEP_QEPCTL_QPEN BIT(3) +#define TI_EQEP_QEPCTL_QCLM BIT(2) +#define TI_EQEP_QEPCTL_UTE BIT(1) /* Position Compare Control Register */ #define TI_EQEP_QPOSCTL_PCSHDW BIT(15) @@ -70,6 +88,12 @@ struct ti_eqep_regs { #define TI_EQEP_QCAPCTL_CCPS GENMASK(6, 4) #define TI_EQEP_QCAPCTL_UPPS GENMASK(3, 0) +#ifdef CONFIG_COUNTER_TI_AM3352_EQEP_VARIANT_AM13 +#define TI_EQEP_QEPSRCSEL_QEPASEL GENMASK(3, 0) +#define TI_EQEP_QEPSRCSEL_QEPBSEL GENMASK(7, 4) +#define TI_EQEP_QEPSRCSEL_QEPISEL GENMASK(11, 8) +#endif + /* Interrupt Types - used in multiple registers */ #define TI_EQEP_INT_UTOI BIT(11) /* Timeout Event */ #define TI_EQEP_INT_IELI BIT(10) /* Index Event */ @@ -84,6 +108,13 @@ struct ti_eqep_regs { #define DEV_DATA(dev) ((struct ti_eqep_data *)(dev)->data) #define DEV_REGS(dev) ((struct ti_eqep_regs *)DEVICE_MMIO_NAMED_GET(dev, base)) +#ifdef CONFIG_COUNTER_TI_AM3352_EQEP_VARIANT_AM13 +struct ti_eqep_mux_entry { + const struct device *dev; + const struct mux_state *state; +}; +#endif + struct ti_eqep_cfg { struct counter_config_info info; @@ -92,6 +123,14 @@ struct ti_eqep_cfg { clock_control_subsys_t clock_subsys; const struct pinctrl_dev_config *pcfg; void (*irq_config_func)(); +#ifdef CONFIG_COUNTER_TI_AM3352_EQEP_VARIANT_AM13 + const struct ti_eqep_mux_entry *mux_entries; + uint8_t mux_entries_count; + bool has_src_select; + uint8_t qepa_src; + uint8_t qepb_src; + uint8_t qepi_src; +#endif }; struct ti_eqep_data { @@ -415,6 +454,32 @@ static int ti_eqep_init(const struct device *dev) return ret; } +#ifdef CONFIG_COUNTER_TI_AM3352_EQEP_VARIANT_AM13 + for (uint8_t i = 0; i < cfg->mux_entries_count; i++) { + const struct ti_eqep_mux_entry *entry = &cfg->mux_entries[i]; + + if (!device_is_ready(entry->dev)) { + LOG_ERR("xbar device not ready"); + return -ENODEV; + } + + ret = mux_state_apply(entry->dev, entry->state); + if (ret < 0) { + LOG_ERR("failed to apply mux state %d: %d", i, ret); + return ret; + } + } + + if (cfg->has_src_select) { + regs->QEPSRCSEL = (regs->QEPSRCSEL & + ~(TI_EQEP_QEPSRCSEL_QEPASEL | TI_EQEP_QEPSRCSEL_QEPBSEL | + TI_EQEP_QEPSRCSEL_QEPISEL)) | + FIELD_PREP(TI_EQEP_QEPSRCSEL_QEPASEL, cfg->qepa_src) | + FIELD_PREP(TI_EQEP_QEPSRCSEL_QEPBSEL, cfg->qepb_src) | + FIELD_PREP(TI_EQEP_QEPSRCSEL_QEPISEL, cfg->qepi_src); + } +#endif /* CONFIG_COUNTER_TI_AM3352_EQEP_VARIANT_AM13 */ + /* irq connect */ cfg->irq_config_func(); @@ -435,7 +500,11 @@ static void ti_eqep_isr(const struct device *dev) struct ti_eqep_regs *regs = DEV_REGS(dev); struct ti_eqep_data *data = DEV_DATA(dev); uint16_t flg = regs->INTFLG; + uint16_t unknown = flg & ~(TI_EQEP_INT_PCOI | TI_EQEP_INT_PCUI | TI_EQEP_INT_PCMI | + TI_EQEP_INT_SELI | TI_EQEP_INT_IELI | TI_EQEP_INT_UTOI | + TI_EQEP_INT_GLOB); + /* Service every pending flag; multiple events can be set together. */ if (flg & (TI_EQEP_INT_PCOI | TI_EQEP_INT_PCUI)) { if (data->top_callback) { data->top_callback(dev, data->top_user_data); @@ -443,7 +512,9 @@ static void ti_eqep_isr(const struct device *dev) /* clear overflow/underflow */ regs->INTCLR |= (TI_EQEP_INT_PCOI | TI_EQEP_INT_PCUI); - } else if (flg & TI_EQEP_INT_PCMI) { + } + + if (flg & TI_EQEP_INT_PCMI) { enum ti_eqep_alarm_channel chan = TI_EQEP_ALARM_CHAN_COMPARE; counter_alarm_callback_t cb = data->alarm_callback[chan]; void *user_data = data->alarm_user_data[chan]; @@ -460,7 +531,9 @@ static void ti_eqep_isr(const struct device *dev) /* clear compare interrupt */ regs->INTCLR |= TI_EQEP_INT_PCMI; - } else if (flg & TI_EQEP_INT_SELI) { + } + + if (flg & TI_EQEP_INT_SELI) { enum ti_eqep_alarm_channel chan = TI_EQEP_ALARM_CHAN_STROBE; counter_alarm_callback_t cb = data->alarm_callback[chan]; void *user_data = data->alarm_user_data[chan]; @@ -474,7 +547,9 @@ static void ti_eqep_isr(const struct device *dev) /* clear strobe interrupt */ regs->INTCLR |= TI_EQEP_INT_SELI; - } else if (flg & TI_EQEP_INT_IELI) { + } + + if (flg & TI_EQEP_INT_IELI) { enum ti_eqep_alarm_channel chan = TI_EQEP_ALARM_CHAN_INDEX; counter_alarm_callback_t cb = data->alarm_callback[chan]; void *user_data = data->alarm_user_data[chan]; @@ -488,7 +563,9 @@ static void ti_eqep_isr(const struct device *dev) /* clear index interrupt */ regs->INTCLR |= TI_EQEP_INT_IELI; - } else if (flg & TI_EQEP_INT_UTOI) { + } + + if (flg & TI_EQEP_INT_UTOI) { enum ti_eqep_alarm_channel chan = TI_EQEP_ALARM_CHAN_TIMEOUT; counter_alarm_callback_t cb = data->alarm_callback[chan]; void *user_data = data->alarm_user_data[chan]; @@ -502,10 +579,12 @@ static void ti_eqep_isr(const struct device *dev) /* clear timeout interrupt */ regs->INTCLR |= TI_EQEP_INT_UTOI; - } else { - LOG_ERR("unknown interrupt %u encountered, clearing", flg); + } + + if (unknown != 0U) { + LOG_ERR("unknown interrupt %u encountered, clearing", unknown); - regs->INTCLR |= flg; + regs->INTCLR |= unknown; } /* clear global interrupt */ @@ -639,15 +718,62 @@ int z_impl_ti_eqep_get_latched_capture_values(const struct device *dev, uint32_t ), (COND_CODE_1(CONFIG_CLOCK_CONTROL_ARM_SCMI, \ (static const clock_control_subsys_t ti_eqep_clk_subsys_##n = \ (clock_control_subsys_t)DT_INST_PHA(n, clocks, name); \ - ), (BUILD_ASSERT(0, "Unsupported clock controller");)))) + ), (COND_CODE_1(CONFIG_CLOCK_CONTROL_MSPM0, \ + (static const struct mspm0_sys_clock ti_eqep_mspm0_sys_clock_##n = \ + MSPM0_CLOCK_SUBSYS_FN(n); \ + static const clock_control_subsys_t ti_eqep_clk_subsys_##n = \ + (clock_control_subsys_t)&ti_eqep_mspm0_sys_clock_##n; \ + ), (BUILD_ASSERT(0, "Unsupported clock controller");)))))) + +#ifdef CONFIG_COUNTER_TI_AM3352_EQEP_VARIANT_AM13 +#define TI_EQEP_MUX_ENTRY(node_id, prop, idx) \ + { \ + .dev = MUX_STATE_DT_DEV_GET_BY_IDX(node_id, idx), \ + .state = MUX_STATE_DT_GET_BY_IDX(node_id, idx), \ + } + +#define TI_EQEP_MUX_ENTRIES_DEFINE(n) \ + IF_ENABLED(DT_INST_NODE_HAS_PROP(n, mux_states), \ + (MUX_STATE_DT_INST_SPEC_DEFINE_ALL(n); \ + static const struct ti_eqep_mux_entry ti_eqep_mux_entries_##n[] = { \ + DT_INST_FOREACH_PROP_ELEM_SEP(n, mux_states, TI_EQEP_MUX_ENTRY, \ + (,))};)) + +#define TI_EQEP_MUX_ENTRIES_INIT(n) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(n, mux_states), \ + (.mux_entries = ti_eqep_mux_entries_##n, \ + .mux_entries_count = ARRAY_SIZE(ti_eqep_mux_entries_##n),), ()) + +#define TI_EQEP_HAS_SRC_SELECT(n) \ + UTIL_OR(DT_INST_NODE_HAS_PROP(n, mux_states), \ + UTIL_OR(DT_INST_NODE_HAS_PROP(n, ti_qepa_src), \ + UTIL_OR(DT_INST_NODE_HAS_PROP(n, ti_qepb_src), \ + DT_INST_NODE_HAS_PROP(n, ti_qepi_src)))) + +#define TI_EQEP_SRC_SELECT_INIT(n) \ + COND_CODE_1(TI_EQEP_HAS_SRC_SELECT(n), \ + (.has_src_select = true, \ + .qepa_src = DT_INST_PROP_OR(n, ti_qepa_src, TI_EQEP_SRC_INPUT_XBAR), \ + .qepb_src = DT_INST_PROP_OR(n, ti_qepb_src, TI_EQEP_SRC_INPUT_XBAR), \ + .qepi_src = DT_INST_PROP_OR(n, ti_qepi_src, TI_EQEP_SRC_INPUT_XBAR),), ()) + +#else /* !CONFIG_COUNTER_TI_AM3352_EQEP_VARIANT_AM13 */ +#define TI_EQEP_MUX_ENTRIES_DEFINE(n) +#define TI_EQEP_MUX_ENTRIES_INIT(n) +#define TI_EQEP_SRC_SELECT_INIT(n) +#endif /* CONFIG_COUNTER_TI_AM3352_EQEP_VARIANT_AM13 */ #define TI_EQEP_INIT(n) \ PINCTRL_DT_INST_DEFINE(n); \ TI_EQEP_DEFINE_CLK_SUBSYS(n); \ + TI_EQEP_MUX_ENTRIES_DEFINE(n); \ static void ti_eqep_irq_config_func_##n(void) \ { \ - IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), ti_eqep_isr, \ - DEVICE_DT_INST_GET(n), DT_INST_IRQ(n, flags)); \ + IRQ_CONNECT( \ + DT_INST_IRQN(n), DT_INST_IRQ(n, priority), ti_eqep_isr, \ + DEVICE_DT_INST_GET(n), \ + COND_CODE_1(DT_INST_IRQ_HAS_CELL(n, flags), \ + (DT_INST_IRQ(n, flags)), (0))); \ irq_enable(DT_INST_IRQN(n)); \ } \ static struct ti_eqep_cfg ti_eqep_config_##n = { \ @@ -661,7 +787,8 @@ int z_impl_ti_eqep_get_latched_capture_values(const struct device *dev, uint32_t .clock_subsys = ti_eqep_clk_subsys_##n, \ .irq_config_func = ti_eqep_irq_config_func_##n, \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ - }; \ + TI_EQEP_MUX_ENTRIES_INIT(n) \ + TI_EQEP_SRC_SELECT_INIT(n)}; \ \ static struct ti_eqep_data ti_eqep_data_##n; \ \ From cd94f8a3a7de73bca7c596616a7c10d6944dbc90 Mon Sep 17 00:00:00 2001 From: Aditya Chopra Date: Wed, 21 Oct 2026 13:25:10 +0000 Subject: [PATCH 4/8] UPSTREAM-PEND: counter: ti_am3352_eqep: pulse SWI to clear sticky softwa... PR: 121529 Signed-off-by: Aditya Chopra --- drivers/counter/counter_ti_am3352_eqep.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/counter/counter_ti_am3352_eqep.c b/drivers/counter/counter_ti_am3352_eqep.c index e16df6ed79eb..58a4d0fe5d9e 100644 --- a/drivers/counter/counter_ti_am3352_eqep.c +++ b/drivers/counter/counter_ti_am3352_eqep.c @@ -168,8 +168,9 @@ static void ti_eqep_reset_counter(const struct device *dev, uint32_t top_value) regs->QPOSINIT = top_value; } - /* initialize counter */ + /* SWI is sticky; pulse it so later QEPCTL writes do not re-init. */ regs->QEPCTL |= TI_EQEP_QEPCTL_SWI; + regs->QEPCTL &= ~TI_EQEP_QEPCTL_SWI; } static int ti_eqep_start(const struct device *dev) From 76ebc2efc651d1553909678929d6c4e239f300f7 Mon Sep 17 00:00:00 2001 From: Aditya Chopra Date: Wed, 21 Oct 2026 13:25:10 +0000 Subject: [PATCH 5/8] UPSTREAM-PEND: counter: ti_am3352_eqep: set FREE_SOFT run-free under debug PR: 121529 Signed-off-by: Aditya Chopra --- drivers/counter/counter_ti_am3352_eqep.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/counter/counter_ti_am3352_eqep.c b/drivers/counter/counter_ti_am3352_eqep.c index 58a4d0fe5d9e..96fe28755e26 100644 --- a/drivers/counter/counter_ti_am3352_eqep.c +++ b/drivers/counter/counter_ti_am3352_eqep.c @@ -65,6 +65,8 @@ struct ti_eqep_regs { #define TI_EQEP_QDECCTL_SWAP BIT(10) /* QEP Control Register */ +#define TI_EQEP_QEPCTL_FREE_SOFT GENMASK(15, 14) +#define TI_EQEP_QEPCTL_FREE_SOFT_RUNFREE 2 #define TI_EQEP_QEPCTL_PCRM GENMASK(13, 12) #define TI_EQEP_QEPCTL_PCRM_MAX (0x1) #define TI_EQEP_QEPCTL_SWI BIT(7) @@ -481,6 +483,10 @@ static int ti_eqep_init(const struct device *dev) } #endif /* CONFIG_COUNTER_TI_AM3352_EQEP_VARIANT_AM13 */ + /* FREE_SOFT=run-free: keep QUTMR running under debugger halt. */ + regs->QEPCTL = (regs->QEPCTL & ~TI_EQEP_QEPCTL_FREE_SOFT) | + FIELD_PREP(TI_EQEP_QEPCTL_FREE_SOFT, TI_EQEP_QEPCTL_FREE_SOFT_RUNFREE); + /* irq connect */ cfg->irq_config_func(); From b7b56ef8a6e7db824061019f563abe2c611e4154 Mon Sep 17 00:00:00 2001 From: Aditya Chopra Date: Wed, 21 Oct 2026 13:25:10 +0000 Subject: [PATCH 6/8] UPSTREAM-PEND: dts: bindings: counter: ti,am3352-eqep: add ti,pwren prop... PR: 121529 Allow EQEP nodes to consume SYSCTL PWREN via ti,pwren. Signed-off-by: Aditya Chopra --- dts/bindings/counter/ti,am3352-eqep.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dts/bindings/counter/ti,am3352-eqep.yaml b/dts/bindings/counter/ti,am3352-eqep.yaml index ee021c71968e..7a00ae3bb37f 100644 --- a/dts/bindings/counter/ti,am3352-eqep.yaml +++ b/dts/bindings/counter/ti,am3352-eqep.yaml @@ -42,6 +42,13 @@ properties: enum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] description: QEPSRCSEL.QEPISEL value (AM13E-only). See ti,qepa-src. + ti,pwren: + type: phandle-array + specifier-space: pwren + description: | + Power-enable this peripheral via SYSCTL.PWREN_*. + Format: <&sysctl offset bit>. + examples: - | &eqep0 { From c001fbe92e696799867421396b50ec543bf4913c Mon Sep 17 00:00:00 2001 From: Aditya Chopra Date: Wed, 21 Oct 2026 13:25:10 +0000 Subject: [PATCH 7/8] UPSTREAM-PEND: dts: arm: ti: am13: add ti,pwren for eQEP PR: 121529 Attach SYSCTL PWREN specifiers to eqep0/1/2. Signed-off-by: Aditya Chopra --- dts/arm/ti/am13/am13e.dtsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dts/arm/ti/am13/am13e.dtsi b/dts/arm/ti/am13/am13e.dtsi index 7cfabc23fe1d..4ceacdd722f8 100644 --- a/dts/arm/ti/am13/am13e.dtsi +++ b/dts/arm/ti/am13/am13e.dtsi @@ -64,6 +64,8 @@ reg = <0x40448000 0x100>; interrupts = <31 0>; clocks = <&ckm MSPM0_CLOCK_MCLK>; + ti,pwren = <&sysctl MSPM_SYSCTL_PWREN_MCPERIPH + MSPM_SYSCTL_PWREN_EQEP0_BIT>; status = "disabled"; }; @@ -72,6 +74,8 @@ reg = <0x40449000 0x100>; interrupts = <32 0>; clocks = <&ckm MSPM0_CLOCK_MCLK>; + ti,pwren = <&sysctl MSPM_SYSCTL_PWREN_MCPERIPH + MSPM_SYSCTL_PWREN_EQEP1_BIT>; status = "disabled"; }; @@ -80,6 +84,8 @@ reg = <0x4044a000 0x100>; interrupts = <33 0>; clocks = <&ckm MSPM0_CLOCK_MCLK>; + ti,pwren = <&sysctl MSPM_SYSCTL_PWREN_MCPERIPH + MSPM_SYSCTL_PWREN_EQEP2_BIT>; status = "disabled"; }; }; From f53ee8f4cdcd9a495dcab2dfdc2476660e194ee2 Mon Sep 17 00:00:00 2001 From: Aditya Chopra Date: Wed, 21 Oct 2026 13:25:10 +0000 Subject: [PATCH 8/8] UPSTREAM-PEND: counter: ti_am3352_eqep: enable PWREN via syscon PR: 121529 Power EQEP from driver init using ti,pwren instead of a SoC-level SYSCTL poke. Signed-off-by: Aditya Chopra --- drivers/counter/counter_ti_am3352_eqep.c | 56 +++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/drivers/counter/counter_ti_am3352_eqep.c b/drivers/counter/counter_ti_am3352_eqep.c index 96fe28755e26..83193ab40b8a 100644 --- a/drivers/counter/counter_ti_am3352_eqep.c +++ b/drivers/counter/counter_ti_am3352_eqep.c @@ -20,8 +20,13 @@ #ifdef CONFIG_COUNTER_TI_AM3352_EQEP_VARIANT_AM13 #include +#include #include +#include +#include + +#define TI_EQEP_SYSCTL_PWREN_KEY (0x26U << 24) #endif LOG_MODULE_REGISTER(ti_eqep); @@ -132,6 +137,10 @@ struct ti_eqep_cfg { uint8_t qepa_src; uint8_t qepb_src; uint8_t qepi_src; + const struct device *pwren_syscon; + uint32_t pwren_offset; + uint8_t pwren_bit; + bool has_pwren; #endif }; @@ -442,12 +451,49 @@ static int ti_eqep_set_guard_period(const struct device *dev, uint32_t ticks, ui return 0; } +#ifdef CONFIG_COUNTER_TI_AM3352_EQEP_VARIANT_AM13 +static int ti_eqep_pwren_enable(const struct ti_eqep_cfg *cfg) +{ + uint32_t bit; + uint32_t mask; + int ret; + + if (!cfg->has_pwren) { + return 0; + } + + if (!device_is_ready(cfg->pwren_syscon)) { + LOG_ERR("SYSCTL (pwren) device not ready"); + return -ENODEV; + } + + bit = BIT(cfg->pwren_bit); + mask = bit | TI_EQEP_SYSCTL_PWREN_KEY; + ret = syscon_update_bits(cfg->pwren_syscon, cfg->pwren_offset, mask, mask); + if (ret < 0) { + LOG_ERR("failed to set SYSCTL PWREN bit %u: %d", cfg->pwren_bit, ret); + return ret; + } + + msp_delay_peripheral_startup(); + + return 0; +} +#endif /* CONFIG_COUNTER_TI_AM3352_EQEP_VARIANT_AM13 */ + static int ti_eqep_init(const struct device *dev) { const struct ti_eqep_cfg *cfg = DEV_CFG(dev); struct ti_eqep_regs *regs; int ret; +#ifdef CONFIG_COUNTER_TI_AM3352_EQEP_VARIANT_AM13 + ret = ti_eqep_pwren_enable(cfg); + if (ret < 0) { + return ret; + } +#endif + DEVICE_MMIO_NAMED_MAP(dev, base, K_MEM_CACHE_NONE); regs = DEV_REGS(dev); @@ -764,10 +810,17 @@ int z_impl_ti_eqep_get_latched_capture_values(const struct device *dev, uint32_t .qepb_src = DT_INST_PROP_OR(n, ti_qepb_src, TI_EQEP_SRC_INPUT_XBAR), \ .qepi_src = DT_INST_PROP_OR(n, ti_qepi_src, TI_EQEP_SRC_INPUT_XBAR),), ()) +#define TI_EQEP_PWREN_INIT(n) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(n, ti_pwren), \ + (.has_pwren = true, \ + .pwren_syscon = DEVICE_DT_GET(DT_INST_PHANDLE(n, ti_pwren)), \ + .pwren_offset = DT_INST_PHA(n, ti_pwren, offset), \ + .pwren_bit = DT_INST_PHA(n, ti_pwren, bit),), ()) #else /* !CONFIG_COUNTER_TI_AM3352_EQEP_VARIANT_AM13 */ #define TI_EQEP_MUX_ENTRIES_DEFINE(n) #define TI_EQEP_MUX_ENTRIES_INIT(n) #define TI_EQEP_SRC_SELECT_INIT(n) +#define TI_EQEP_PWREN_INIT(n) #endif /* CONFIG_COUNTER_TI_AM3352_EQEP_VARIANT_AM13 */ #define TI_EQEP_INIT(n) \ @@ -795,7 +848,8 @@ int z_impl_ti_eqep_get_latched_capture_values(const struct device *dev, uint32_t .irq_config_func = ti_eqep_irq_config_func_##n, \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ TI_EQEP_MUX_ENTRIES_INIT(n) \ - TI_EQEP_SRC_SELECT_INIT(n)}; \ + TI_EQEP_SRC_SELECT_INIT(n) \ + TI_EQEP_PWREN_INIT(n)}; \ \ static struct ti_eqep_data ti_eqep_data_##n; \ \