blob: 0c1339c045f437af6cb703c4a83435ab887d5605 [file] [log] [blame]
/*
* Copyright (c) 2025 Ambiq
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT ambiq_timer_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_timer_pwm, CONFIG_PWM_LOG_LEVEL);
#if defined(CONFIG_SOC_SERIES_APOLLO4X) || defined(CONFIG_SOC_SERIES_APOLLO5X)
typedef am_hal_timer_config_t pwm_timer_config_t;
#endif
struct pwm_ambiq_timer_data {
uint32_t cycles;
};
struct pwm_ambiq_timer_config {
uint32_t timer_num;
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 0:
ret = 24000000;
break;
case 1:
ret = 6000000;
break;
case 2:
ret = 1500000;
break;
case 3:
ret = 375000;
break;
case 4:
ret = 93750;
break;
case 5:
ret = 1000;
break;
case 6:
ret = 500;
break;
case 7:
ret = 31;
break;
case 8:
ret = 1;
break;
case 9:
ret = 256;
break;
case 10:
ret = 32768;
break;
case 11:
ret = 16384;
break;
case 12:
ret = 8192;
break;
case 13:
ret = 4096;
break;
case 14:
ret = 2048;
break;
case 15:
ret = 1024;
break;
case 16:
ret = 256;
break;
case 17:
ret = 100;
break;
default:
ret = 24000000;
break;
}
return ret;
}
static int ambiq_timer_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_timer_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) {
pulse_cycles = 1;
}
}
am_hal_timer_clear(config->timer_num);
am_hal_timer_compare0_set(config->timer_num, period_cycles);
am_hal_timer_compare1_set(config->timer_num, pulse_cycles);
return 0;
}
static int ambiq_timer_pwm_get_cycles_per_sec(const struct device *dev, uint32_t channel,
uint64_t *cycles)
{
struct pwm_ambiq_timer_data *data = dev->data;
/* cycles of the timer clock */
*cycles = (uint64_t)data->cycles;
return 0;
}
static int ambiq_timer_pwm_init(const struct device *dev)
{
const struct pwm_ambiq_timer_config *config = dev->config;
struct pwm_ambiq_timer_data *data = dev->data;
int err;
err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
if (err) {
return err;
}
pwm_timer_config_t pwm_timer_config;
am_hal_timer_default_config_set(&pwm_timer_config);
pwm_timer_config.eFunction = AM_HAL_TIMER_FN_PWM;
pwm_timer_config.eInputClock = config->clock_sel;
data->cycles = get_clock_cycles(config->clock_sel);
am_hal_timer_output_config(config->pincfg->states->pins->pin_num,
AM_HAL_TIMER_OUTPUT_TMR0_OUT0 + config->timer_num * 2);
am_hal_timer_config(config->timer_num, &pwm_timer_config);
am_hal_timer_clear(config->timer_num);
am_hal_timer_compare0_set(config->timer_num, 0);
am_hal_timer_compare1_set(config->timer_num, 1);
return 0;
}
static DEVICE_API(pwm, pwm_ambiq_timer_driver_api) = {
.set_cycles = ambiq_timer_pwm_set_cycles,
.get_cycles_per_sec = ambiq_timer_pwm_get_cycles_per_sec,
};
#define PWM_AMBIQ_TIMER_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_timer_data pwm_ambiq_timer_data_##n = { \
.cycles = 0, \
}; \
static const struct pwm_ambiq_timer_config pwm_ambiq_timer_config_##n = { \
.timer_num = (DT_REG_ADDR(DT_INST_PARENT(n)) - TIMER_BASE) / \
DT_REG_SIZE(DT_INST_PARENT(n)), \
.clock_sel = DT_ENUM_IDX(DT_INST_PARENT(n), clk_source), \
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n)}; \
\
DEVICE_DT_INST_DEFINE(n, ambiq_timer_pwm_init, NULL, &pwm_ambiq_timer_data_##n, \
&pwm_ambiq_timer_config_##n, POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \
&pwm_ambiq_timer_driver_api);
DT_INST_FOREACH_STATUS_OKAY(PWM_AMBIQ_TIMER_DEVICE_INIT)