Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PWM support to TI cc23x0 SoC #84530

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
56 changes: 56 additions & 0 deletions boards/ti/lp_em_cc2340r5/lp_em_cc2340r5-pinctrl.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
};
39 changes: 39 additions & 0 deletions boards/ti/lp_em_cc2340r5/lp_em_cc2340r5.dts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "boosterpack_connector.dtsi"
#include "lp_em_cc2340r5-pinctrl.dtsi"
#include <zephyr/dt-bindings/gpio/gpio.h>
#include <zephyr/dt-bindings/pwm/pwm.h>
#include <zephyr/dt-bindings/input/input-event-codes.h>

/ {
Expand All @@ -30,6 +31,8 @@
led1 = &led1;
sw0 = &btn0;
sw1 = &btn1;
pwm-led0 = &pwm_green;
pwm-led1 = &pwm_red;
};

leds {
Expand All @@ -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";

Expand Down Expand Up @@ -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";
};
1 change: 1 addition & 0 deletions drivers/pwm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions drivers/pwm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
10 changes: 10 additions & 0 deletions drivers/pwm/Kconfig.cc23x0_timer
Original file line number Diff line number Diff line change
@@ -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.
167 changes: 167 additions & 0 deletions drivers/pwm/pwm_cc23x0_timer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* Copyright (c) 2024 BayLibre, SAS
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT ti_cc23x0_lgpt_pwm

#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/pwm.h>

#include <driverlib/gpio.h>
#include <driverlib/clkctl.h>
#include <inc/hw_lgpt.h>
#include <inc/hw_lgpt1.h>
#include <inc/hw_lgpt3.h>
#include <inc/hw_types.h>
#include <inc/hw_evtsvt.h>
#include <inc/hw_memmap.h>

#include <zephyr/logging/log.h>
#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);
Loading
Loading