diff --git a/boards/ti/lp_em_cc2340r5/lp_em_cc2340r5-pinctrl.dtsi b/boards/ti/lp_em_cc2340r5/lp_em_cc2340r5-pinctrl.dtsi index 1d929cfa6aca..b999cb8a758b 100644 --- a/boards/ti/lp_em_cc2340r5/lp_em_cc2340r5-pinctrl.dtsi +++ b/boards/ti/lp_em_cc2340r5/lp_em_cc2340r5-pinctrl.dtsi @@ -18,4 +18,60 @@ bias-disable; input-enable; }; + + /* LGPT0 - PWM */ + pwm0_default: lgpt0_chan0_default { + pinmux = <8 DIO8_T0_C0N>; + bias-disable; + }; + pwm1_default: lgpt0_chan1_default { + pinmux = <3 DIO3_T0_C1N>; + bias-disable; + }; + pwm2_default: lgpt0_chan2_default { + pinmux = <4 DIO4_T0_C2N>; + bias-disable; + }; + + /* LGPT1 - PWM */ + pwm3_default: lgpt1_chan0_default { + pinmux = <16 DIO16_T1_C0N>; + bias-disable; + }; + pwm4_default: lgpt1_chan2_default { + pinmux = <17 DIO17_T1_C1N>; + bias-disable; + }; + pwm5_default: lgpt1_chan3_default { + pinmux = <14 DIO14_T1_C2N>; + bias-disable; + }; + + /* LGPT2 - PWM */ + pwm6_default: lgpt2_chan0_default { + pinmux = <15 DIO15_T2_C0N>; + bias-disable; + }; + pwm7_default: lgpt2_chan1_default { + pinmux = <2 DIO2_T2_C1N>; + bias-disable; + }; + pwm8_default: lgpt2_chan2_default { + pinmux = <25 DIO25_T2_C2N>; + bias-disable; + }; + + /* LGPT3 - PWM */ + pwm9_default: lgpt3_chan01_default { + pinmux = <10 DIO10_T3_C0N>; + bias-disable; + }; + pwm10_default: lgpt3_chan1_default { + pinmux = <22 DIO22_T3_C1N>; + bias-disable; + }; + pwm11_default: lgpt3_chan2_default { + pinmux = <23 DIO23_T3_C2N>; + bias-disable; + }; }; diff --git a/boards/ti/lp_em_cc2340r5/lp_em_cc2340r5.dts b/boards/ti/lp_em_cc2340r5/lp_em_cc2340r5.dts index 64d3e1502075..9a7be0d59486 100644 --- a/boards/ti/lp_em_cc2340r5/lp_em_cc2340r5.dts +++ b/boards/ti/lp_em_cc2340r5/lp_em_cc2340r5.dts @@ -11,6 +11,7 @@ #include "boosterpack_connector.dtsi" #include "lp_em_cc2340r5-pinctrl.dtsi" #include +#include #include / { @@ -30,6 +31,8 @@ led1 = &led1; sw0 = &btn0; sw1 = &btn1; + pwm-led0 = &pwm_green; + pwm-led1 = &pwm_red; }; leds { @@ -46,6 +49,20 @@ }; }; + pwmleds { + compatible = "pwm-leds"; + + pwm_green: pwm_led_0 { + pwms = <&pwm6 0 1000 PWM_POLARITY_NORMAL>; + label = "Green PWM LED"; + }; + + pwm_red: pwm_led_1 { + pwms = <&pwm5 0 1000 PWM_POLARITY_NORMAL>; + label = "Red PWM LED"; + }; + }; + keys { compatible = "gpio-keys"; @@ -73,3 +90,25 @@ pinctrl-0 = <&uart0_rx_default &uart0_tx_default>; pinctrl-names = "default"; }; + +&lgpt1 { + clk-prescale = <0>; + status = "okay"; +}; + +&lgpt2 { + clk-prescale = <0>; + status = "okay"; +}; + +&pwm5 { + pinctrl-0 = <&pwm5_default>; + pinctrl-names = "default"; + status = "okay"; +}; + +&pwm6 { + pinctrl-0 = <&pwm6_default>; + pinctrl-names = "default"; + status = "okay"; +}; diff --git a/drivers/pwm/CMakeLists.txt b/drivers/pwm/CMakeLists.txt index e7596ed6d95c..563b6fd9fe70 100644 --- a/drivers/pwm/CMakeLists.txt +++ b/drivers/pwm/CMakeLists.txt @@ -6,6 +6,7 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_PWM_TELINK_B91 pwm_b91.c) zephyr_library_sources_ifdef(CONFIG_PWM_CC13XX_CC26XX_TIMER pwm_cc13xx_cc26xx_timer.c) +zephyr_library_sources_ifdef(CONFIG_PWM_CC23X0_TIMER pwm_cc23x0_timer.c) zephyr_library_sources_ifdef(CONFIG_PWM_STM32 pwm_stm32.c) zephyr_library_sources_ifdef(CONFIG_PWM_SIFIVE pwm_sifive.c) zephyr_library_sources_ifdef(CONFIG_PWM_NRF_SW pwm_nrf_sw.c) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index f4ccd04b383f..6c544a116dca 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -36,6 +36,8 @@ source "drivers/pwm/Kconfig.b91" source "drivers/pwm/Kconfig.cc13xx_cc26xx_timer" +source "drivers/pwm/Kconfig.cc23x0_timer" + source "drivers/pwm/Kconfig.stm32" source "drivers/pwm/Kconfig.sifive" diff --git a/drivers/pwm/Kconfig.cc23x0_timer b/drivers/pwm/Kconfig.cc23x0_timer new file mode 100644 index 000000000000..0ea5fa9bfd43 --- /dev/null +++ b/drivers/pwm/Kconfig.cc23x0_timer @@ -0,0 +1,10 @@ +# Copyright (c) 2024 BayLibre, SAS +# SPDX-License-Identifier: Apache-2.0 + +config PWM_CC23X0_TIMER + bool "TI SimpleLink CC23x0 LGPT PWM driver" + default y + depends on DT_HAS_TI_CC23X0_LGPT_PWM_ENABLED + select PINCTRL + help + Enables TI SimpleLink CC23x0 LGPT PWM driver. diff --git a/drivers/pwm/pwm_cc23x0_timer.c b/drivers/pwm/pwm_cc23x0_timer.c new file mode 100644 index 000000000000..d61fafbeea97 --- /dev/null +++ b/drivers/pwm/pwm_cc23x0_timer.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2024 BayLibre, SAS + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ti_cc23x0_lgpt_pwm + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#define LOG_MODULE_NAME pwm_cc23x0_lgpt +LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_PWM_LOG_LEVEL); + +#define LGPT_CLK_PRESCALE(pres) ((pres + 1) << 8) + +struct pwm_cc23x0_data { + uint32_t prescale; + uint32_t base_clk; +}; + +struct pwm_cc23x0_config { + const uint32_t base; /* LGPT register base address */ + const struct pinctrl_dev_config *pcfg; + uint8_t lgpt_id; +}; + +static int pwm_cc23x0_set_cycles(const struct device *dev, uint32_t channel, uint32_t period, + uint32_t pulse, pwm_flags_t flags) +{ + const struct pwm_cc23x0_config *config = dev->config; + + LOG_DBG("set cycles period[%x] pulse[%x]", period, pulse); + + if ((config->base != LGPT3_BASE) && (pulse > 0xffff || period > 0xffff || pulse > period)) { + /* LGPT0, LGPT1, LGPT2 - 16bit counters */ + LOG_ERR("Period of pulse out of range"); + return -EINVAL; + } else if (pulse > 0xffffff || period > 0xffffff || pulse > period) { + /* LGPT3 - 24bit counter */ + LOG_ERR("Period of pulse out of range"); + return -EINVAL; + } + + if (channel == 0) { + HWREG(config->base + LGPT_O_C0CC) = pulse; + HWREG(config->base + LGPT_O_C0CFG) = 0x100 | 0xB; + } else if (channel == 1) { + HWREG(config->base + LGPT_O_C1CC) = pulse; + HWREG(config->base + LGPT_O_C1CFG) = 0x200 | 0xB; + } else if (channel == 2) { + HWREG(config->base + LGPT_O_C2CC) = pulse; + HWREG(config->base + LGPT_O_C2CFG) = 0x400 | 0xB; + } else { + LOG_ERR("Invalid chan ID"); + return -ENOTSUP; + } + + /* get it from flags */ + HWREG(config->base + LGPT_O_CTL) = LGPT_CTL_MODE_UPDWN_PER; + HWREG(config->base + LGPT_O_TGT) = period; + + /* Activate LGPT */ + HWREG(config->base + LGPT_O_STARTCFG) = 0x1; + + return 0; +} + +static int pwm_cc23x0_get_cycles_per_sec(const struct device *dev, uint32_t channel, + uint64_t *cycles) +{ + struct pwm_cc23x0_data *data = dev->data; + + *cycles = data->base_clk / (data->prescale + 1); + + return 0; +} + +static const struct pwm_driver_api pwm_cc23x0_driver_api = { + .set_cycles = pwm_cc23x0_set_cycles, + .get_cycles_per_sec = pwm_cc23x0_get_cycles_per_sec, +}; + +static int pwm_cc23x0_activate_clock(const struct device *dev) +{ + const struct pwm_cc23x0_config *config = dev->config; + struct pwm_cc23x0_data *data = dev->data; + uint32_t lgpt_clk_id = 0; + + switch (config->base) { + case LGPT0_BASE: + lgpt_clk_id = CLKCTL_LGPT0; + break; + case LGPT1_BASE: + lgpt_clk_id = CLKCTL_LGPT1; + break; + case LGPT2_BASE: + lgpt_clk_id = CLKCTL_LGPT2; + break; + case LGPT3_BASE: + lgpt_clk_id = CLKCTL_LGPT3; + break; + default: + return -EINVAL; + } + + CLKCTLEnable(CLKCTL_BASE, lgpt_clk_id); + HWREG(config->base + LGPT_O_PRECFG) = LGPT_CLK_PRESCALE(data->prescale); + HWREG(EVTSVT_BASE + EVTSVT_O_LGPTSYNCSEL) = EVTSVT_LGPTSYNCSEL_PUBID_SYSTIM0; + + return 0; +} + +#define DT_TIMER(idx) DT_INST_PARENT(idx) +#define DT_TIMER_BASE_ADDR(idx) (DT_REG_ADDR(DT_TIMER(idx))) + +#define PWM_CC23X0_INIT_FUNC(idx) \ + static int pwm_cc23x0_init##idx(const struct device *dev) \ + { \ + const struct pwm_cc23x0_config *config = dev->config; \ + \ + int ret; \ + \ + LOG_DBG("PWM cc23x0 base=[%x]", config->base); \ + \ + ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); \ + if (ret < 0) { \ + LOG_ERR("[ERR] failed to setup PWM pinctrl"); \ + return ret; \ + } \ + \ + pwm_cc23x0_activate_clock(dev); \ + \ + return 0; \ + } + +#define PWM_DEVICE_INIT(idx) \ + PWM_CC23X0_INIT_FUNC(idx); \ + PINCTRL_DT_INST_DEFINE(idx); \ + LOG_INSTANCE_REGISTER(LOG_MODULE_NAME, idx, CONFIG_PWM_LOG_LEVEL); \ + \ + static const struct pwm_cc23x0_config pwm_cc23x0_##idx##_config = { \ + .base = DT_TIMER_BASE_ADDR(idx), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx), \ + .lgpt_id = (DT_TIMER_BASE_ADDR(idx) - LGPT0_BASE) >> 12, \ + }; \ + \ + static struct pwm_cc23x0_data pwm_cc23x0_##idx##_data = { \ + .prescale = DT_PROP(DT_INST_PARENT(idx), clk_prescale), \ + .base_clk = DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(idx, pwm_cc23x0_init##idx, NULL, &pwm_cc23x0_##idx##_data, \ + &pwm_cc23x0_##idx##_config, POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \ + &pwm_cc23x0_driver_api) + +DT_INST_FOREACH_STATUS_OKAY(PWM_DEVICE_INIT); diff --git a/dts/arm/ti/cc23x0.dtsi b/dts/arm/ti/cc23x0.dtsi index 6b307980ce3e..d731f2d8b35c 100644 --- a/dts/arm/ti/cc23x0.dtsi +++ b/dts/arm/ti/cc23x0.dtsi @@ -79,6 +79,106 @@ clocks = <&sysclk>; status = "disabled"; }; + + lgpt0: lgpt@40060000 { + compatible = "ti,cc23x0-lgpt"; + reg = <0x40060000 0x1d2>; + max-top-value = <0xffff>; + interrupts = <13 0>; + clk-prescale = <0>; + status = "disabled"; + + pwm0: pwm0 { + compatible = "ti,cc23x0-lgpt-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + pwm1: pwm1 { + compatible = "ti,cc23x0-lgpt-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + pwm2: pwm2 { + compatible = "ti,cc23x0-lgpt-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + }; + + lgpt1: lgpt@40061000 { + compatible = "ti,cc23x0-lgpt"; + reg = <0x40061000 0x1d2>; + max-top-value = <0xffff>; + interrupts = <14 0>; + clk-prescale = <0>; + status = "disabled"; + + pwm3: pwm0 { + compatible = "ti,cc23x0-lgpt-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + pwm4: pwm1 { + compatible = "ti,cc23x0-lgpt-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + pwm5: pwm2 { + compatible = "ti,cc23x0-lgpt-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + }; + + lgpt2: lgpt@40062000 { + compatible = "ti,cc23x0-lgpt"; + reg = <0x40062000 0x1d2>; + max-top-value = <0xffff>; + interrupts = <17 0>; + clk-prescale = <0>; + status = "disabled"; + + pwm6: pwm0 { + compatible = "ti,cc23x0-lgpt-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + pwm7: pwm1 { + compatible = "ti,cc23x0-lgpt-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + pwm8: pwm2 { + compatible = "ti,cc23x0-lgpt-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + }; + + lgpt3: lgpt@40063000 { + compatible = "ti,cc23x0-lgpt"; + reg = <0x40063000 0x1d2>; + max-top-value = <0xffffff>; + interrupts = <18 0>; + clk-prescale = <0>; + status = "disabled"; + + pwm9: pwm0 { + compatible = "ti,cc23x0-lgpt-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + pwm10: pwm1 { + compatible = "ti,cc23x0-lgpt-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + pwm11: pwm2 { + compatible = "ti,cc23x0-lgpt-pwm"; + #pwm-cells = <3>; + status = "disabled"; + }; + }; }; }; diff --git a/dts/bindings/pwm/ti,cc23x0-lgpt-pwm.yaml b/dts/bindings/pwm/ti,cc23x0-lgpt-pwm.yaml new file mode 100644 index 000000000000..57b5c9341dbf --- /dev/null +++ b/dts/bindings/pwm/ti,cc23x0-lgpt-pwm.yaml @@ -0,0 +1,34 @@ +# Copyright (c) 2024 BayLibre, SAS +# SPDX-License-Identifier: Apache-2.0 + +description: | + TI CC23x0 LGPT PWM Controller Node + + &lgpt0 { + status = "okay"; + }; + + &pwm0 { + status = "okay"; + pinctrl-0 = <&pwm0_default>; + pinctrl-names = "default"; + }; + + Note: PWM0-PWM8 are 16bit without prescale, max timeout is 1365100 ns + PWM9-PWM11 are 24bit without prescale, max timeout is 349519750 ns + +compatible: "ti,cc23x0-lgpt-pwm" + +include: [base.yaml, pwm-controller.yaml, pinctrl-device.yaml] + +properties: + pinctrl-0: + required: true + + pinctrl-names: + required: true + +pwm-cells: + - channel + - period + - flags