Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions boards/ti/lp_am13e230/lp_am13e230.dts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <ti/am13/am13e23019-pinctrl.dtsi>
#include <zephyr/dt-bindings/gpio/gpio.h>
#include <zephyr/dt-bindings/input/input-event-codes.h>
#include <zephyr/dt-bindings/mux/ti_am13e2_xbar_in.h>
#include <zephyr/dt-bindings/mux/ti_am13e2_xbar_out.h>

/ {
model = "TI AM13E230X Launchpad";
Expand Down Expand Up @@ -132,3 +134,44 @@
pinctrl-0 = <&uc4_tx_sda_pico_pa0 &uc4_rx_scl_sclk_pa1>;
pinctrl-names = "default";
};

&gpio_pb4 {
input-enable;
bias-pull-down;
};

&outputxbar8_pb3 {
bias-pull-down;
};

&xbar1 {
status = "okay";
};

&xbar2 {
status = "okay";
};

&ecap0 {
status = "okay";
interrupts = <29 0>;

pinctrl-0 = <&outputxbar8_pb3>;
pinctrl-names = "default";

/* ECAP0 -> OUTPUTXBAR8 (idx 7) -> G1SEL bit 4 -> PB3 */
mux-states = <&xbar2 AM13E2_XBAR_OUT_CTRL(7, 1, 4) 1>;
mux-state-names = "output";
};

&ecap1 {
status = "okay";
interrupts = <30 0>;

pinctrl-0 = <&gpio_pb4>;
pinctrl-names = "default";

/* PB4 (GPIO36) -> INPUTXBAR1 -> ECAP1 ECCTL0.INPUTSEL (raw=1) */
mux-states = <&xbar1 0 AM13E2_XBAR_IN_STATE(36, 1)>;
mux-state-names = "input";
};
1 change: 1 addition & 0 deletions boards/ti/lp_am13e230/lp_am13e230.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ supported:
- uart
- i2c
- spi
- pwm
vendor: ti
12 changes: 12 additions & 0 deletions drivers/pwm/Kconfig.ti_am3352_ecap
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,15 @@ config PWM_TI_AM3352_ECAP
select PINCTRL
help
Enable ECAP controller for TI SoCs

if PWM_TI_AM3352_ECAP
config PWM_TI_AM3352_ECAP_VARIANT_AM13
bool "TI ECAP AM13E variant"
depends on SOC_SERIES_AM13E
default y
select MUX
select SYSCON
help
Enable AM13E ECAP support : mux-states xbar routing
and sysctl for setting PWREN bits.
endif
208 changes: 183 additions & 25 deletions drivers/pwm/pwm_ti_am3352_ecap.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,34 @@
#include <zephyr/drivers/clock_control/tisci_clock_control.h>
#endif

#ifdef CONFIG_CLOCK_CONTROL_MSPM0
#include <zephyr/drivers/clock_control/mspm0_clock_control.h>
#endif

#ifdef CONFIG_PWM_TI_AM3352_ECAP_VARIANT_AM13
#include <zephyr/drivers/mux.h>
#include <zephyr/drivers/syscon.h>
#include <soc.h>

#define TI_ECAP_SYSCTL_PWREN_KEY (0x26U << 24)
#endif

LOG_MODULE_REGISTER(ti_ecap);

#define DT_DRV_COMPAT ti_am3352_ecap

struct ti_ecap_regs {
uint8_t RESERVED_1[0x10]; /**< Reserved, offset: 0x00 - 0x10 */
volatile uint32_t CAP3; /**< Capture-3 Register, offset: 0x10 */
volatile uint32_t CAP4; /**< Capture-4 Register, offset: 0x14 */
uint8_t RESERVED_2[0x10]; /**< Reserved, offset: 0x18 - 0x28 */
volatile uint32_t TSCTR; /**< Time-Stamp Counter, offset: 0x00 */
volatile uint32_t CTRPHS; /**< Counter Phase Offset, offset: 0x04 */
volatile uint32_t CAP1; /**< Capture-1 / APWM period, offset: 0x08 */
volatile uint32_t CAP2; /**< Capture-2 / APWM compare, offset: 0x0C */
volatile uint32_t CAP3; /**< Capture-3 / APWM shadow period, offset: 0x10 */
volatile uint32_t CAP4; /**< Capture-4 / APWM shadow compare, offset: 0x14 */
uint8_t RESERVED_2[0xC]; /**< Reserved, offset: 0x18 - 0x24 */
#ifdef CONFIG_PWM_TI_AM3352_ECAP_VARIANT_AM13
volatile uint32_t ECCTL0; /**< Input mux select, AM13E-only, offset: 0x24 */
#else
uint8_t RESERVED_ECCTL0[0x4]; /**< Reserved, offset: 0x24 - 0x28 */
#endif
volatile uint32_t ECCTL; /**< ECAP Control Register, offset: 0x28 */
volatile uint32_t ECINT_EN_FLG; /**< ECAP Interrupt Enable & Flag Register, offset: 0x2C */
volatile uint32_t ECINT_CLR_FRC; /**< ECAP Interrupt Clear & Force Register, offset: 0x30 */
Expand Down Expand Up @@ -58,6 +77,11 @@ struct ti_ecap_regs {
#define TI_ECAP_ECINT_CLR_CEVT4 BIT(4)
#define TI_ECAP_ECINT_CLR_INT BIT(0)

#ifdef CONFIG_PWM_TI_AM3352_ECAP_VARIANT_AM13
/* ECAP Input Mux Select Register (ECCTL0, AM13E-only) */
#define TI_ECAP_ECCTL0_INPUTSEL_MASK GENMASK(6, 0)
#endif

#define DEV_CFG(dev) ((const struct ti_ecap_cfg *)(dev)->config)
#define DEV_DATA(dev) ((struct ti_ecap_data *)(dev)->data)
#define DEV_REGS(dev) ((struct ti_ecap_regs *)DEVICE_MMIO_GET(dev))
Expand All @@ -70,13 +94,30 @@ struct ti_ecap_capture_data {
bool continuous;
};

#ifdef CONFIG_PWM_TI_AM3352_ECAP_VARIANT_AM13
struct ti_ecap_mux_entry {
const struct device *dev;
const struct mux_state *state;
};
#endif

struct ti_ecap_cfg {
DEVICE_MMIO_ROM;
void (*irq_config_func)();
const struct device *clock_dev;
clock_control_subsys_t clock_subsys;
uint32_t clock_frequency;
const struct pinctrl_dev_config *pcfg;
#ifdef CONFIG_PWM_TI_AM3352_ECAP_VARIANT_AM13
const struct ti_ecap_mux_entry *mux_entries;
uint8_t mux_entries_count;
bool has_input_xbar;
uint8_t input_xbar_channel;
const struct device *pwren_syscon;
uint32_t pwren_offset;
uint8_t pwren_bit;
bool has_pwren;
#endif
};

struct ti_ecap_data {
Expand All @@ -94,8 +135,9 @@ static int ti_ecap_set_cycles(const struct device *dev, uint32_t channel, uint32

ecctl = regs->ECCTL;

/* enable apwm and counter */
ecctl |= (TI_ECAP_ECCTL_TSCNTSTP | TI_ECAP_ECCTL_CAP_APWM);
/* Stop the counter while reconfiguring APWM. */
ecctl &= ~TI_ECAP_ECCTL_TSCNTSTP;
ecctl |= TI_ECAP_ECCTL_CAP_APWM;

/* polarity */
if ((flags & PWM_POLARITY_MASK) == PWM_POLARITY_NORMAL) {
Expand All @@ -104,12 +146,15 @@ static int ti_ecap_set_cycles(const struct device *dev, uint32_t channel, uint32
ecctl |= TI_ECAP_ECCTL_APWMPOL;
}

/* update shadow period and shadow cmp registers in apwm mode */
regs->ECCTL = ecctl;

regs->CAP1 = period_cycles;
regs->CAP2 = pulse_cycles;
regs->CAP3 = period_cycles;
regs->CAP4 = pulse_cycles;

/* update ecctl */
regs->ECCTL = ecctl;
regs->TSCTR = 0;
regs->ECCTL |= TI_ECAP_ECCTL_TSCNTSTP;

return 0;
}
Expand Down Expand Up @@ -189,8 +234,10 @@ static int ti_ecap_configure_capture(const struct device *dev, uint32_t channel,
/* disable interrupts */
regs->ECINT_EN_FLG &= ~(TI_ECAP_ECINT_EN_CEVT4 | TI_ECAP_ECINT_EN_CNTOVF);

/* clear event flags */
regs->ECINT_CLR_FRC |=
/* ECCLR is write-1-to-clear; ECFRC sits in the upper half of this
* 32-bit view — never RMW or a stale force bit can be rewritten.
*/
regs->ECINT_CLR_FRC =
(TI_ECAP_ECINT_CLR_CNTOVF | TI_ECAP_ECINT_CLR_CEVT4 | TI_ECAP_ECINT_CLR_INT);

ecctl = regs->ECCTL;
Expand Down Expand Up @@ -236,11 +283,48 @@ static int ti_ecap_configure_capture(const struct device *dev, uint32_t channel,
}
#endif /* CONFIG_PWM_CAPTURE */

#ifdef CONFIG_PWM_TI_AM3352_ECAP_VARIANT_AM13
static int ti_ecap_pwren_enable(const struct ti_ecap_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_ECAP_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_PWM_TI_AM3352_ECAP_VARIANT_AM13 */

static int ti_ecap_init(const struct device *dev)
{
const struct ti_ecap_cfg *cfg = DEV_CFG(dev);
int ret;

#ifdef CONFIG_PWM_TI_AM3352_ECAP_VARIANT_AM13
ret = ti_ecap_pwren_enable(cfg);
if (ret < 0) {
return ret;
}
#endif

DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);

ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
Expand All @@ -249,6 +333,30 @@ static int ti_ecap_init(const struct device *dev)
return ret;
}

#ifdef CONFIG_PWM_TI_AM3352_ECAP_VARIANT_AM13
for (uint8_t i = 0; i < cfg->mux_entries_count; i++) {
const struct ti_ecap_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_input_xbar) {
struct ti_ecap_regs *regs = DEV_REGS(dev);

regs->ECCTL0 = (regs->ECCTL0 & ~TI_ECAP_ECCTL0_INPUTSEL_MASK) |
FIELD_PREP(TI_ECAP_ECCTL0_INPUTSEL_MASK, cfg->input_xbar_channel);
}
#endif /* CONFIG_PWM_TI_AM3352_ECAP_VARIANT_AM13 */

cfg->irq_config_func();

return 0;
Expand All @@ -268,8 +376,7 @@ static void ti_ecap_isr(const struct device *dev)
cpt->callback(dev, 0, period, pulse, -ERANGE, cpt->user_data);
}

/* clear event */
regs->ECINT_CLR_FRC |= TI_ECAP_ECINT_CLR_CNTOVF;
regs->ECINT_CLR_FRC = TI_ECAP_ECINT_CLR_CNTOVF | TI_ECAP_ECINT_CLR_INT;
} else if (ecint_en_flg & TI_ECAP_ECINT_EN_CEVT4_FLG) {
uint32_t cap3 = regs->CAP3;
uint32_t cap4 = regs->CAP4;
Expand All @@ -286,12 +393,10 @@ static void ti_ecap_isr(const struct device *dev)
cpt->callback(dev, 0, period, pulse, 0, cpt->user_data);
}

/* clear event */
regs->ECINT_CLR_FRC |= TI_ECAP_ECINT_CLR_CEVT4;
regs->ECINT_CLR_FRC = TI_ECAP_ECINT_CLR_CEVT4 | TI_ECAP_ECINT_CLR_INT;
} else {
regs->ECINT_CLR_FRC = TI_ECAP_ECINT_CLR_INT;
}

/* clear global interrupt */
regs->ECINT_CLR_FRC |= TI_ECAP_ECINT_CLR_INT;
}

static DEVICE_API(pwm, ti_ecap_api) = {
Expand All @@ -313,24 +418,77 @@ static DEVICE_API(pwm, ti_ecap_api) = {
), (COND_CODE_1(CONFIG_CLOCK_CONTROL_ARM_SCMI, \
(static const clock_control_subsys_t ti_ecap_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_ecap_mspm0_sys_clock_##n = \
MSPM0_CLOCK_SUBSYS_FN(n); \
static const clock_control_subsys_t ti_ecap_clk_subsys_##n = \
(clock_control_subsys_t)&ti_ecap_mspm0_sys_clock_##n; \
), (BUILD_ASSERT(0, "Unsupported clock controller");))))))

#ifdef CONFIG_PWM_TI_AM3352_ECAP_VARIANT_AM13
/* One entry per "mux-states" phandle-array element: applies the crossbar
* routing (Input or Output XBAR) named by that entry through the mux
* subsystem.
*/
#define TI_ECAP_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_ECAP_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_ecap_mux_entry ti_ecap_mux_entries_##n[] = { \
DT_INST_FOREACH_PROP_ELEM_SEP(n, mux_states, TI_ECAP_MUX_ENTRY, \
(,))};))

/* If a "mux-states" entry is named "input", its control cell is the Input
* XBAR channel that feeds this eCAP's own ECCTL0.INPUTSEL field - a
* separate mux internal to the eCAP, indexing into its own 128-entry table
* (INPUTXBAR1-16 occupy indices 0-15), not the crossbar's own addressing.
*/
#define TI_ECAP_MUX_ENTRIES_INIT(n) \
COND_CODE_1(DT_INST_NODE_HAS_PROP(n, mux_states), \
(.mux_entries = ti_ecap_mux_entries_##n, \
.mux_entries_count = ARRAY_SIZE(ti_ecap_mux_entries_##n),), ()) \
COND_CODE_1(DT_INST_PROP_HAS_NAME(n, mux_states, input), \
(.has_input_xbar = true, \
.input_xbar_channel = DT_INST_PHA_BY_NAME(n, mux_states, input, channel),), ())

#define TI_ECAP_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_PWM_TI_AM3352_ECAP_VARIANT_AM13 */
#define TI_ECAP_MUX_ENTRIES_DEFINE(n)
#define TI_ECAP_MUX_ENTRIES_INIT(n)
#define TI_ECAP_PWREN_INIT(n)
#endif /* CONFIG_PWM_TI_AM3352_ECAP_VARIANT_AM13 */

#define TI_ECAP_INIT(n) \
TI_ECAP_DEFINE_CLK_SUBSYS(n); \
TI_ECAP_DEFINE_CLK_SUBSYS(n); \
PINCTRL_DT_INST_DEFINE(n); \
TI_ECAP_MUX_ENTRIES_DEFINE(n); \
static void ti_ecap_irq_config_func_##n(void) \
{ \
IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), ti_ecap_isr, \
DEVICE_DT_INST_GET(n), DT_INST_IRQ(n, flags)); \
IRQ_CONNECT( \
DT_INST_IRQN(n), DT_INST_IRQ(n, priority), ti_ecap_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_ecap_cfg ti_ecap_config_##n = { \
DEVICE_MMIO_ROM_INIT(DT_DRV_INST(n)), \
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
.clock_subsys = ti_ecap_clk_subsys_##n, \
.clock_subsys = ti_ecap_clk_subsys_##n, \
.irq_config_func = ti_ecap_irq_config_func_##n, \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
}; \
TI_ECAP_MUX_ENTRIES_INIT(n) \
TI_ECAP_PWREN_INIT(n)}; \
\
static struct ti_ecap_data ti_ecap_data_##n; \
\
Expand Down
Loading
Loading