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.
117 lines
3.4 KiB
117 lines
3.4 KiB
/* |
|
* Copyright (c) 2025 ENE Technology Inc. |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT ene_kb106x_pwm |
|
#include <zephyr/kernel.h> |
|
#include <zephyr/drivers/pwm.h> |
|
#include <zephyr/drivers/pinctrl.h> |
|
#include <reg/pwm.h> |
|
|
|
/* Device config */ |
|
struct pwm_kb106x_config { |
|
/* pwm controller base address */ |
|
struct pwm_regs *pwm; |
|
const struct pinctrl_dev_config *pcfg; |
|
}; |
|
|
|
/* Driver data */ |
|
struct pwm_kb106x_data { |
|
/* PWM cycles per second */ |
|
uint32_t cycles_per_sec; |
|
}; |
|
|
|
/* PWM api functions */ |
|
static int pwm_kb106x_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_kb106x_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; |
|
} |
|
|
|
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_kb106x_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 DEVICE_API(pwm, pwm_kb106x_driver_api) = { |
|
.set_cycles = pwm_kb106x_set_cycles, |
|
.get_cycles_per_sec = pwm_kb106x_get_cycles_per_sec, |
|
}; |
|
|
|
static int pwm_kb106x_init(const struct device *dev) |
|
{ |
|
int ret; |
|
const struct pwm_kb106x_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 KB106X_PWM_INIT(inst) \ |
|
PINCTRL_DT_INST_DEFINE(inst); \ |
|
static const struct pwm_kb106x_config pwm_kb106x_cfg_##inst = { \ |
|
.pwm = (struct pwm_regs *)DT_INST_REG_ADDR(inst), \ |
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ |
|
}; \ |
|
static struct pwm_kb106x_data pwm_kb106x_data_##inst; \ |
|
DEVICE_DT_INST_DEFINE(inst, &pwm_kb106x_init, NULL, &pwm_kb106x_data_##inst, \ |
|
&pwm_kb106x_cfg_##inst, PRE_KERNEL_1, CONFIG_PWM_INIT_PRIORITY, \ |
|
&pwm_kb106x_driver_api); |
|
|
|
DT_INST_FOREACH_STATUS_OKAY(KB106X_PWM_INIT)
|
|
|