| /* |
| * 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) |