blob: 34b2e13e07c0431c6caeee622ad4b4a349205d9c [file] [log] [blame]
/*
* Copyright (c) 2025 Ambiq
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT ambiq_ctimer_pwm
#include <errno.h>
#include <zephyr/drivers/pwm.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/clock_control.h>
#include <am_mcu_apollo.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(ambiq_ctimer_pwm, CONFIG_PWM_LOG_LEVEL);
struct pwm_ambiq_ctimer_data {
uint32_t cycles;
};
struct pwm_ambiq_ctimer_config {
uint32_t timer_num;
uint32_t timer_seg;
uint32_t pwm_type;
uint32_t clock_sel;
const struct pinctrl_dev_config *pincfg;
};
static uint32_t get_clock_cycles(uint32_t clock_sel)
{
uint32_t ret = 0;
switch (clock_sel) {
case 1:
ret = 12000000;
break;
case 2:
ret = 3000000;
break;
case 3:
ret = 187500;
break;
case 4:
ret = 47000;
break;
case 5:
ret = 12000;
break;
case 6:
ret = 32768;
break;
case 7:
ret = 16384;
break;
case 8:
ret = 2048;
break;
case 9:
ret = 256;
break;
case 10:
ret = 512;
break;
case 11:
ret = 32;
break;
case 12:
ret = 1000;
break;
case 13:
ret = 116;
break;
case 14:
ret = 100;
break;
case 15: /* todo: check on value */
ret = 0;
break;
case 16:
ret = 8192;
break;
case 17:
ret = 4096;
break;
case 18:
ret = 1024;
break;
default:
ret = 12000000;
break;
}
return ret;
}
static void start_clock(uint32_t clock_sel)
{
switch (clock_sel) {
default:
break;
case 1:
case 2:
case 3:
case 4:
case 5:
am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0);
break;
case 6:
case 7:
case 8:
case 9:
case 14: /* RTC will assume XTAL since LFRC isn't as accurate */
case 16:
case 17:
case 18:
am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_XTAL_START, 0);
break;
case 10:
case 11:
case 12:
case 13:
am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_LFRC_START, 0);
break;
}
}
static int ambiq_ctimer_pwm_set_cycles(const struct device *dev, uint32_t channel,
uint32_t period_cycles, uint32_t pulse_cycles,
pwm_flags_t flags)
{
const struct pwm_ambiq_ctimer_config *config = dev->config;
if (period_cycles == 0) {
LOG_ERR("period_cycles can not be set to zero");
return -ENOTSUP;
}
if (flags & PWM_POLARITY_INVERTED) {
if (pulse_cycles == 0) {
/* make pulse cycles greater than period so event never occurs */
pulse_cycles = period_cycles + 1;
} else {
pulse_cycles = period_cycles - pulse_cycles;
}
} else {
if (pulse_cycles == period_cycles) {
--pulse_cycles;
} else if (pulse_cycles == 0) {
period_cycles = 0;
pulse_cycles = 1;
}
}
uint32_t seg = 0;
if (config->timer_seg == 0) {
seg = 0x0000FFFF;
} else if (config->timer_seg == 1) {
seg = 0xFFFF0000U;
} else {
seg = 0xFFFFFFFFU;
}
/* todo: need to check if all of this is required */
am_hal_ctimer_clear(config->timer_num, seg);
am_hal_ctimer_period_set(config->timer_num, seg, period_cycles, pulse_cycles);
am_hal_ctimer_start(config->timer_num, seg);
return 0;
}
static int ambiq_ctimer_pwm_get_cycles_per_sec(const struct device *dev, uint32_t channel,
uint64_t *cycles)
{
struct pwm_ambiq_ctimer_data *data = dev->data;
/* cycles of the timer clock */
*cycles = (uint64_t)data->cycles;
return 0;
}
static int ambiq_ctimer_pwm_init(const struct device *dev)
{
const struct pwm_ambiq_ctimer_config *config = dev->config;
struct pwm_ambiq_ctimer_data *data = dev->data;
int err;
err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
if (err) {
return err;
}
uint32_t seg = 0;
if (config->timer_seg == 0) {
seg = 0x0000FFFF;
} else if (config->timer_seg == 1) {
seg = 0xFFFF0000U;
} else {
seg = 0xFFFFFFFFU;
}
data->cycles = get_clock_cycles(config->clock_sel);
start_clock(config->clock_sel);
am_hal_ctimer_output_config(config->timer_num, seg, config->pincfg->states->pins->pin_num,
AM_HAL_CTIMER_OUTPUT_NORMAL,
AM_HAL_GPIO_PIN_DRIVESTRENGTH_12MA);
am_hal_ctimer_config_single(config->timer_num, seg,
(_VAL2FLD(CTIMER_CTRL0_TMRA0FN, config->pwm_type + 2) |
_VAL2FLD(CTIMER_CTRL0_TMRA0CLK, config->clock_sel) |
AM_HAL_CTIMER_INT_ENABLE));
return 0;
}
static DEVICE_API(pwm, pwm_ambiq_ctimer_driver_api) = {
.set_cycles = ambiq_ctimer_pwm_set_cycles,
.get_cycles_per_sec = ambiq_ctimer_pwm_get_cycles_per_sec,
};
#define PWM_AMBIQ_CTIMER_DEVICE_INIT(n) \
BUILD_ASSERT(DT_CHILD_NUM_STATUS_OKAY(DT_INST_PARENT(n)) == 1, \
"Too many children for Timer!"); \
PINCTRL_DT_INST_DEFINE(n); \
static struct pwm_ambiq_ctimer_data pwm_ambiq_ctimer_data_##n = { \
.cycles = 0, \
}; \
static const struct pwm_ambiq_ctimer_config pwm_ambiq_ctimer_config_##n = { \
.timer_num = (DT_REG_ADDR(DT_INST_PARENT(n)) - CTIMER_BASE) / \
DT_REG_SIZE(DT_INST_PARENT(n)), \
.timer_seg = DT_INST_ENUM_IDX(n, timer_segment), \
.clock_sel = DT_ENUM_IDX(DT_INST_PARENT(n), clk_source), \
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
.pwm_type = DT_INST_ENUM_IDX(n, pwm_type)}; \
\
DEVICE_DT_INST_DEFINE(n, ambiq_ctimer_pwm_init, NULL, &pwm_ambiq_ctimer_data_##n, \
&pwm_ambiq_ctimer_config_##n, POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \
&pwm_ambiq_ctimer_driver_api);
DT_INST_FOREACH_STATUS_OKAY(PWM_AMBIQ_CTIMER_DEVICE_INIT)