drivers: pwm: add driver for NXP Kinetis TPM module
The TPM (Timer/PWM Module) is a 2- to 8-channel timer which supports input capture, output compare, and the generation of PWM signals to control electric motor and power management applications. This patch adds the driver and the binding necessary for instantiating the driver. The work is based on the RV32M1 driver for TPM done by Henrik Brix Andersen. A later patch will enable this driver to be used for the KW41Z SoC, if PWM support is requested. Signed-off-by: Alex Porosanu <alexandru.porosanu@nxp.com>
This commit is contained in:
parent
7ee9a0328b
commit
f4c36b7beb
5 changed files with 241 additions and 0 deletions
|
@ -16,6 +16,7 @@ zephyr_library_sources_ifdef(CONFIG_PWM_MCUX pwm_mcux.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_PWM_XEC pwm_mchp_xec.c)
|
zephyr_library_sources_ifdef(CONFIG_PWM_XEC pwm_mchp_xec.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_PWM_LITEX pwm_litex.c)
|
zephyr_library_sources_ifdef(CONFIG_PWM_LITEX pwm_litex.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_PWM_RV32M1_TPM pwm_rv32m1_tpm.c)
|
zephyr_library_sources_ifdef(CONFIG_PWM_RV32M1_TPM pwm_rv32m1_tpm.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_PWM_MCUX_TPM pwm_mcux_tpm.c)
|
||||||
|
|
||||||
zephyr_library_sources_ifdef(CONFIG_USERSPACE pwm_handlers.c)
|
zephyr_library_sources_ifdef(CONFIG_USERSPACE pwm_handlers.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_PWM_SHELL pwm_shell.c)
|
zephyr_library_sources_ifdef(CONFIG_PWM_SHELL pwm_shell.c)
|
||||||
|
|
|
@ -48,4 +48,6 @@ source "drivers/pwm/Kconfig.litex"
|
||||||
|
|
||||||
source "drivers/pwm/Kconfig.rv32m1_tpm"
|
source "drivers/pwm/Kconfig.rv32m1_tpm"
|
||||||
|
|
||||||
|
source "drivers/pwm/Kconfig.mcux_tpm"
|
||||||
|
|
||||||
endif # PWM
|
endif # PWM
|
||||||
|
|
10
drivers/pwm/Kconfig.mcux_tpm
Normal file
10
drivers/pwm/Kconfig.mcux_tpm
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# Copyright 2020 NXP
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
# MCUX TPM PWM
|
||||||
|
|
||||||
|
config PWM_MCUX_TPM
|
||||||
|
bool "MCUX TPM PWM driver"
|
||||||
|
depends on HAS_MCUX_TPM && CLOCK_CONTROL
|
||||||
|
help
|
||||||
|
Enable the MCUX TPM PWM driver.
|
204
drivers/pwm/pwm_mcux_tpm.c
Normal file
204
drivers/pwm/pwm_mcux_tpm.c
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 Henrik Brix Andersen <henrik@brixandersen.dk>
|
||||||
|
* Copyright 2020 NXP
|
||||||
|
*
|
||||||
|
* Heavily based on pwm_mcux_ftm.c, which is:
|
||||||
|
* Copyright (c) 2017, NXP
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT nxp_kinetis_tpm
|
||||||
|
|
||||||
|
#include <drivers/clock_control.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <drivers/pwm.h>
|
||||||
|
#include <soc.h>
|
||||||
|
#include <fsl_tpm.h>
|
||||||
|
#include <fsl_clock.h>
|
||||||
|
|
||||||
|
#define LOG_LEVEL CONFIG_PWM_LOG_LEVEL
|
||||||
|
#include <logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(pwm_mcux_tpm);
|
||||||
|
|
||||||
|
#define MAX_CHANNELS ARRAY_SIZE(TPM0->CONTROLS)
|
||||||
|
|
||||||
|
struct mcux_tpm_config {
|
||||||
|
TPM_Type *base;
|
||||||
|
char *clock_name;
|
||||||
|
clock_control_subsys_t clock_subsys;
|
||||||
|
tpm_clock_source_t tpm_clock_source;
|
||||||
|
tpm_clock_prescale_t prescale;
|
||||||
|
u8_t channel_count;
|
||||||
|
tpm_pwm_mode_t mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mcux_tpm_data {
|
||||||
|
u32_t clock_freq;
|
||||||
|
u32_t period_cycles;
|
||||||
|
tpm_chnl_pwm_signal_param_t channel[MAX_CHANNELS];
|
||||||
|
};
|
||||||
|
|
||||||
|
static int mcux_tpm_pin_set(struct device *dev, u32_t pwm,
|
||||||
|
u32_t period_cycles, u32_t pulse_cycles,
|
||||||
|
pwm_flags_t flags)
|
||||||
|
{
|
||||||
|
const struct mcux_tpm_config *config = dev->config->config_info;
|
||||||
|
struct mcux_tpm_data *data = dev->driver_data;
|
||||||
|
u8_t duty_cycle;
|
||||||
|
|
||||||
|
if ((period_cycles == 0U) || (pulse_cycles > period_cycles)) {
|
||||||
|
LOG_ERR("Invalid combination: period_cycles=%d, "
|
||||||
|
"pulse_cycles=%d", period_cycles, pulse_cycles);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pwm >= config->channel_count) {
|
||||||
|
LOG_ERR("Invalid channel");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
duty_cycle = pulse_cycles * 100U / period_cycles;
|
||||||
|
data->channel[pwm].dutyCyclePercent = duty_cycle;
|
||||||
|
|
||||||
|
if ((flags & PWM_POLARITY_INVERTED) == 0) {
|
||||||
|
data->channel[pwm].level = kTPM_HighTrue;
|
||||||
|
} else {
|
||||||
|
data->channel[pwm].level = kTPM_LowTrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("pulse_cycles=%d, period_cycles=%d, duty_cycle=%d, flags=%d",
|
||||||
|
pulse_cycles, period_cycles, duty_cycle, flags);
|
||||||
|
|
||||||
|
if (period_cycles != data->period_cycles) {
|
||||||
|
u32_t pwm_freq;
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
if (data->period_cycles != 0) {
|
||||||
|
/* Only warn when not changing from zero */
|
||||||
|
LOG_WRN("Changing period cycles from %d to %d"
|
||||||
|
" affects all %d channels in %s",
|
||||||
|
data->period_cycles, period_cycles,
|
||||||
|
config->channel_count, dev->config->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
data->period_cycles = period_cycles;
|
||||||
|
|
||||||
|
pwm_freq = (data->clock_freq >> config->prescale) /
|
||||||
|
period_cycles;
|
||||||
|
|
||||||
|
LOG_DBG("pwm_freq=%d, clock_freq=%d", pwm_freq,
|
||||||
|
data->clock_freq);
|
||||||
|
|
||||||
|
if (pwm_freq == 0U) {
|
||||||
|
LOG_ERR("Could not set up pwm_freq=%d", pwm_freq);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
TPM_StopTimer(config->base);
|
||||||
|
|
||||||
|
status = TPM_SetupPwm(config->base, data->channel,
|
||||||
|
config->channel_count, config->mode,
|
||||||
|
pwm_freq, data->clock_freq);
|
||||||
|
|
||||||
|
if (status != kStatus_Success) {
|
||||||
|
LOG_ERR("Could not set up pwm");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
TPM_StartTimer(config->base, config->tpm_clock_source);
|
||||||
|
} else {
|
||||||
|
TPM_UpdateChnlEdgeLevelSelect(config->base, pwm,
|
||||||
|
data->channel[pwm].level);
|
||||||
|
TPM_UpdatePwmDutycycle(config->base, pwm, config->mode,
|
||||||
|
duty_cycle);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mcux_tpm_get_cycles_per_sec(struct device *dev, u32_t pwm,
|
||||||
|
u64_t *cycles)
|
||||||
|
{
|
||||||
|
const struct mcux_tpm_config *config = dev->config->config_info;
|
||||||
|
struct mcux_tpm_data *data = dev->driver_data;
|
||||||
|
|
||||||
|
*cycles = data->clock_freq >> config->prescale;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mcux_tpm_init(struct device *dev)
|
||||||
|
{
|
||||||
|
const struct mcux_tpm_config *config = dev->config->config_info;
|
||||||
|
struct mcux_tpm_data *data = dev->driver_data;
|
||||||
|
tpm_chnl_pwm_signal_param_t *channel = data->channel;
|
||||||
|
struct device *clock_dev;
|
||||||
|
tpm_config_t tpm_config;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (config->channel_count > ARRAY_SIZE(data->channel)) {
|
||||||
|
LOG_ERR("Invalid channel count");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
clock_dev = device_get_binding(config->clock_name);
|
||||||
|
if (clock_dev == NULL) {
|
||||||
|
LOG_ERR("Could not get clock device");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clock_control_on(clock_dev, config->clock_subsys)) {
|
||||||
|
LOG_ERR("Could not turn on clock");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clock_control_get_rate(clock_dev, config->clock_subsys,
|
||||||
|
&data->clock_freq)) {
|
||||||
|
LOG_ERR("Could not get clock frequency");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < config->channel_count; i++) {
|
||||||
|
channel->chnlNumber = i;
|
||||||
|
channel->level = kTPM_NoPwmSignal;
|
||||||
|
channel->dutyCyclePercent = 0;
|
||||||
|
channel->firstEdgeDelayPercent = 0;
|
||||||
|
channel++;
|
||||||
|
}
|
||||||
|
|
||||||
|
TPM_GetDefaultConfig(&tpm_config);
|
||||||
|
tpm_config.prescale = config->prescale;
|
||||||
|
|
||||||
|
TPM_Init(config->base, &tpm_config);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct pwm_driver_api mcux_tpm_driver_api = {
|
||||||
|
.pin_set = mcux_tpm_pin_set,
|
||||||
|
.get_cycles_per_sec = mcux_tpm_get_cycles_per_sec,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TPM_DEVICE(n) \
|
||||||
|
static const struct mcux_tpm_config mcux_tpm_config_##n = { \
|
||||||
|
.base = (TPM_Type *) \
|
||||||
|
DT_INST_REG_ADDR(n), \
|
||||||
|
.clock_name = \
|
||||||
|
DT_INST_CLOCKS_LABEL(n), \
|
||||||
|
.clock_subsys = (clock_control_subsys_t) \
|
||||||
|
DT_INST_CLOCKS_CELL(n, name), \
|
||||||
|
.tpm_clock_source = kTPM_SystemClock, \
|
||||||
|
.prescale = kTPM_Prescale_Divide_16, \
|
||||||
|
.channel_count = FSL_FEATURE_TPM_CHANNEL_COUNTn((TPM_Type *) \
|
||||||
|
DT_INST_REG_ADDR(n)), \
|
||||||
|
.mode = kTPM_EdgeAlignedPwm, \
|
||||||
|
}; \
|
||||||
|
static struct mcux_tpm_data mcux_tpm_data_##n; \
|
||||||
|
DEVICE_AND_API_INIT(mcux_tpm_##n, \
|
||||||
|
DT_INST_LABEL(n), \
|
||||||
|
&mcux_tpm_init, &mcux_tpm_data_##n, \
|
||||||
|
&mcux_tpm_config_##n, \
|
||||||
|
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
|
||||||
|
&mcux_tpm_driver_api)
|
||||||
|
|
||||||
|
DT_INST_FOREACH(TPM_DEVICE)
|
24
dts/bindings/pwm/nxp,kinetis-tpm.yaml
Normal file
24
dts/bindings/pwm/nxp,kinetis-tpm.yaml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Copyright (c) 2019 Henrik Brix Andersen <henrik@brixandersen.dk>
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
description: MCUX Timer/PWM Module (TPM)
|
||||||
|
|
||||||
|
compatible: "nxp,kinetis-tpm"
|
||||||
|
|
||||||
|
include: [pwm-controller.yaml, base.yaml]
|
||||||
|
|
||||||
|
properties:
|
||||||
|
reg:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
"#pwm-cells":
|
||||||
|
const: 3
|
||||||
|
|
||||||
|
pwm-cells:
|
||||||
|
- channel
|
||||||
|
# period in terms of nanoseconds
|
||||||
|
- period
|
||||||
|
- flags
|
Loading…
Add table
Add a link
Reference in a new issue