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