/* * Copyright (c) 2024 ENE Technology Inc. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT ene_kb1200_pwm #include #include #include /* Device config */ struct pwm_kb1200_config { /* pwm controller base address */ struct pwm_regs *pwm; const struct pinctrl_dev_config *pcfg; }; /* Driver data */ struct pwm_kb1200_data { /* PWM cycles per second */ uint32_t cycles_per_sec; }; /* PWM api functions */ static int pwm_kb1200_set_cycles(const struct device *dev, uint32_t channel, uint32_t period_cycles, uint32_t pulse_cycles, pwm_flags_t flags) { /* Single channel for each pwm device */ ARG_UNUSED(channel); const struct pwm_kb1200_config *config = dev->config; int prescaler; uint32_t high_len; uint32_t cycle_len; /* * Calculate PWM prescaler that let period_cycles map to * maximum pwm period cycles and won't exceed it. * Then prescaler = ceil (period_cycles / pwm_max_period_cycles) */ prescaler = DIV_ROUND_UP(period_cycles, PWM_MAX_CYCLES); if (prescaler > PWM_MAX_PRESCALER) { return -EINVAL; } /* If pulse_cycles is 0, switch PWM off and return. */ if (pulse_cycles == 0) { config->pwm->PWMCFG &= ~PWM_ENABLE; return 0; } high_len = (pulse_cycles / prescaler); cycle_len = (period_cycles / prescaler); /* Select PWM inverted polarity (ie. active-low pulse). */ if (flags & PWM_POLARITY_INVERTED) { high_len = cycle_len - high_len; } /* Set PWM prescaler. */ config->pwm->PWMCFG = (config->pwm->PWMCFG & ~GENMASK(13, 8)) | ((prescaler - 1) << 8); /* * period_cycles: PWM Cycle Length * pulse_cycles : PWM High Length */ config->pwm->PWMHIGH = high_len; config->pwm->PWMCYC = cycle_len; /* Start pwm */ config->pwm->PWMCFG |= PWM_ENABLE; return 0; } static int pwm_kb1200_get_cycles_per_sec(const struct device *dev, uint32_t channel, uint64_t *cycles) { /* Single channel for each pwm device */ ARG_UNUSED(channel); ARG_UNUSED(dev); if (cycles) { /* User does not have to know about lowest clock, * the driver will select the most relevant one. */ *cycles = PWM_INPUT_FREQ_HI; /*32Mhz*/ } return 0; } static const struct pwm_driver_api pwm_kb1200_driver_api = { .set_cycles = pwm_kb1200_set_cycles, .get_cycles_per_sec = pwm_kb1200_get_cycles_per_sec, }; static int pwm_kb1200_init(const struct device *dev) { int ret; const struct pwm_kb1200_config *config = dev->config; ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); if (ret != 0) { return ret; } config->pwm->PWMCFG = PWM_SOURCE_CLK_32M | PWM_RULE1 | PWM_PUSHPULL; return 0; } #define KB1200_PWM_INIT(inst) \ PINCTRL_DT_INST_DEFINE(inst); \ static const struct pwm_kb1200_config pwm_kb1200_cfg_##inst = { \ .pwm = (struct pwm_regs *)DT_INST_REG_ADDR(inst), \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ }; \ static struct pwm_kb1200_data pwm_kb1200_data_##inst; \ DEVICE_DT_INST_DEFINE(inst, &pwm_kb1200_init, NULL, &pwm_kb1200_data_##inst, \ &pwm_kb1200_cfg_##inst, PRE_KERNEL_1, CONFIG_PWM_INIT_PRIORITY, \ &pwm_kb1200_driver_api); DT_INST_FOREACH_STATUS_OKAY(KB1200_PWM_INIT)