You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
118 lines
3.3 KiB
118 lines
3.3 KiB
/* |
|
* Copyright (c) 2021 Sun Amar. |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT silabs_gecko_pwm |
|
|
|
#include <zephyr/drivers/pwm.h> |
|
#include <zephyr/dt-bindings/pwm/pwm.h> |
|
#include <em_cmu.h> |
|
#include <em_timer.h> |
|
|
|
/** PWM configuration. */ |
|
struct pwm_gecko_config { |
|
TIMER_TypeDef *timer; |
|
CMU_Clock_TypeDef clock; |
|
uint16_t prescaler; |
|
TIMER_Prescale_TypeDef prescale_enum; |
|
uint8_t channel; |
|
uint8_t location; |
|
uint8_t port; |
|
uint8_t pin; |
|
}; |
|
|
|
static int pwm_gecko_set_cycles(const struct device *dev, uint32_t channel, |
|
uint32_t period_cycles, uint32_t pulse_cycles, |
|
pwm_flags_t flags) |
|
{ |
|
TIMER_InitCC_TypeDef compare_config = TIMER_INITCC_DEFAULT; |
|
const struct pwm_gecko_config *cfg = dev->config; |
|
|
|
if (BUS_RegMaskedRead(&cfg->timer->CC[channel].CTRL, |
|
_TIMER_CC_CTRL_MODE_MASK) != timerCCModePWM) { |
|
|
|
#ifdef _TIMER_ROUTE_MASK |
|
BUS_RegMaskedWrite(&cfg->timer->ROUTE, |
|
_TIMER_ROUTE_LOCATION_MASK, |
|
cfg->location << _TIMER_ROUTE_LOCATION_SHIFT); |
|
BUS_RegMaskedSet(&cfg->timer->ROUTE, 1 << channel); |
|
#elif defined(_TIMER_ROUTELOC0_MASK) |
|
BUS_RegMaskedWrite(&cfg->timer->ROUTELOC0, |
|
_TIMER_ROUTELOC0_CC0LOC_MASK << |
|
(channel * _TIMER_ROUTELOC0_CC1LOC_SHIFT), |
|
cfg->location << (channel * _TIMER_ROUTELOC0_CC1LOC_SHIFT)); |
|
BUS_RegMaskedSet(&cfg->timer->ROUTEPEN, 1 << channel); |
|
#else |
|
#error Unsupported device |
|
#endif |
|
|
|
compare_config.mode = timerCCModePWM; |
|
TIMER_InitCC(cfg->timer, channel, &compare_config); |
|
} |
|
|
|
cfg->timer->CC[channel].CTRL |= (flags & PWM_POLARITY_INVERTED) ? |
|
TIMER_CC_CTRL_OUTINV : 0; |
|
|
|
TIMER_TopSet(cfg->timer, period_cycles); |
|
|
|
TIMER_CompareBufSet(cfg->timer, channel, pulse_cycles); |
|
|
|
return 0; |
|
} |
|
|
|
static int pwm_gecko_get_cycles_per_sec(const struct device *dev, |
|
uint32_t channel, uint64_t *cycles) |
|
{ |
|
const struct pwm_gecko_config *cfg = dev->config; |
|
|
|
*cycles = CMU_ClockFreqGet(cfg->clock) / cfg->prescaler; |
|
|
|
return 0; |
|
} |
|
|
|
static DEVICE_API(pwm, pwm_gecko_driver_api) = { |
|
.set_cycles = pwm_gecko_set_cycles, |
|
.get_cycles_per_sec = pwm_gecko_get_cycles_per_sec, |
|
}; |
|
|
|
static int pwm_gecko_init(const struct device *dev) |
|
{ |
|
TIMER_Init_TypeDef timer = TIMER_INIT_DEFAULT; |
|
const struct pwm_gecko_config *cfg = dev->config; |
|
|
|
CMU_ClockEnable(cfg->clock, true); |
|
|
|
CMU_ClockEnable(cmuClock_GPIO, true); |
|
GPIO_PinModeSet(cfg->port, cfg->pin, gpioModePushPull, 0); |
|
|
|
timer.prescale = cfg->prescale_enum; |
|
TIMER_Init(cfg->timer, &timer); |
|
|
|
return 0; |
|
} |
|
|
|
#define CLOCK_TIMER(id) _CONCAT(cmuClock_TIMER, id) |
|
#define PRESCALING_FACTOR(factor) \ |
|
((_CONCAT(timerPrescale, factor))) |
|
|
|
#define PWM_GECKO_INIT(index) \ |
|
static const struct pwm_gecko_config pwm_gecko_config_##index = { \ |
|
.timer = (TIMER_TypeDef *)DT_REG_ADDR(DT_INST_PARENT(index)), \ |
|
.clock = CLOCK_TIMER(index), \ |
|
.prescaler = DT_INST_PROP(index, prescaler), \ |
|
.prescale_enum = (TIMER_Prescale_TypeDef) \ |
|
PRESCALING_FACTOR(DT_INST_PROP(index, prescaler)), \ |
|
.location = DT_INST_PROP_BY_IDX(index, pin_location, 0), \ |
|
.port = (GPIO_Port_TypeDef) \ |
|
DT_INST_PROP_BY_IDX(index, pin_location, 1), \ |
|
.pin = DT_INST_PROP_BY_IDX(index, pin_location, 2), \ |
|
}; \ |
|
\ |
|
DEVICE_DT_INST_DEFINE(index, &pwm_gecko_init, NULL, NULL, \ |
|
&pwm_gecko_config_##index, POST_KERNEL, \ |
|
CONFIG_PWM_INIT_PRIORITY, \ |
|
&pwm_gecko_driver_api); |
|
|
|
DT_INST_FOREACH_STATUS_OKAY(PWM_GECKO_INIT)
|
|
|