zephyr/drivers/pwm/pwm_ifx_cat1.c

178 lines
6.2 KiB
C

/*
* Copyright (c) 2023 Cypress Semiconductor Corporation (an Infineon company) or
* an affiliate of Cypress Semiconductor Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief ADC driver for Infineon CAT1 MCU family.
*/
#define DT_DRV_COMPAT infineon_cat1_pwm
#include <zephyr/drivers/pwm.h>
#include <zephyr/drivers/pinctrl.h>
#include <cy_tcpwm_pwm.h>
#include <cy_gpio.h>
#include <cy_sysclk.h>
#include <cyhal_hw_resources.h>
#include <cyhal_hw_types.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(pwm_ifx_cat1, CONFIG_PWM_LOG_LEVEL);
#define PWM_REG_BASE TCPWM0
struct ifx_cat1_pwm_data {
uint32_t pwm_num;
};
struct ifx_cat1_pwm_config {
TCPWM_GRP_CNT_Type *reg_addr;
const struct pinctrl_dev_config *pcfg;
bool resolution_32_bits;
cy_en_divider_types_t divider_type;
uint32_t divider_sel;
uint32_t divider_val;
};
static int ifx_cat1_pwm_init(const struct device *dev)
{
struct ifx_cat1_pwm_data *data = dev->data;
const struct ifx_cat1_pwm_config *config = dev->config;
cy_en_tcpwm_status_t status;
int ret;
uint32_t addr_offset = (uint32_t)config->reg_addr - TCPWM0_BASE;
uint32_t clk_connection;
const cy_stc_tcpwm_pwm_config_t pwm_config = {
.pwmMode = CY_TCPWM_PWM_MODE_PWM,
.clockPrescaler = CY_TCPWM_PWM_PRESCALER_DIVBY_1,
.pwmAlignment = CY_TCPWM_PWM_LEFT_ALIGN,
.runMode = CY_TCPWM_PWM_CONTINUOUS,
.countInputMode = CY_TCPWM_INPUT_LEVEL,
.countInput = CY_TCPWM_INPUT_1,
};
/* Configure PWM clock */
Cy_SysClk_PeriphDisableDivider(config->divider_type, config->divider_sel);
Cy_SysClk_PeriphSetDivider(config->divider_type, config->divider_sel, config->divider_val);
Cy_SysClk_PeriphEnableDivider(config->divider_type, config->divider_sel);
/* This is very specific to the cyw920829m2evk_02 and may need to be modified
* for other boards.
*/
if (addr_offset < sizeof(TCPWM_GRP_Type)) {
clk_connection =
PCLK_TCPWM0_CLOCK_COUNTER_EN0 + (addr_offset / sizeof(TCPWM_GRP_CNT_Type));
} else {
addr_offset -= sizeof(TCPWM_GRP_Type);
clk_connection = PCLK_TCPWM0_CLOCK_COUNTER_EN256 +
(addr_offset / sizeof(TCPWM_GRP_CNT_Type));
}
Cy_SysClk_PeriphAssignDivider(clk_connection, config->divider_type, config->divider_sel);
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
if (ret < 0) {
return ret;
}
/* Configure the TCPWM to be a PWM */
data->pwm_num += addr_offset / sizeof(TCPWM_GRP_CNT_Type);
status = Cy_TCPWM_PWM_Init(PWM_REG_BASE, data->pwm_num, &pwm_config);
if (status != CY_TCPWM_SUCCESS) {
return -ENOTSUP;
}
return 0;
}
static int ifx_cat1_pwm_set_cycles(const struct device *dev, uint32_t channel,
uint32_t period_cycles, uint32_t pulse_cycles, pwm_flags_t flags)
{
struct ifx_cat1_pwm_data *data = dev->data;
const struct ifx_cat1_pwm_config *config = dev->config;
if (!config->resolution_32_bits &&
((period_cycles > UINT16_MAX) || (pulse_cycles > UINT16_MAX))) {
/* 16-bit resolution */
if (period_cycles > UINT16_MAX) {
LOG_ERR("Period cycles more than 16-bits (%u)", period_cycles);
}
if (pulse_cycles > UINT16_MAX) {
LOG_ERR("Pulse cycles more than 16-bits (%u)", pulse_cycles);
}
return -EINVAL;
}
if ((period_cycles == 0) || (pulse_cycles == 0)) {
Cy_TCPWM_PWM_Disable(PWM_REG_BASE, data->pwm_num);
} else {
Cy_TCPWM_PWM_SetPeriod0(PWM_REG_BASE, data->pwm_num, period_cycles);
Cy_TCPWM_PWM_SetCompare0Val(PWM_REG_BASE, data->pwm_num, pulse_cycles);
if ((flags & PWM_POLARITY_MASK) == PWM_POLARITY_INVERTED) {
config->reg_addr->CTRL &= ~TCPWM_GRP_CNT_V2_CTRL_QUAD_ENCODING_MODE_Msk;
config->reg_addr->CTRL |= _VAL2FLD(TCPWM_GRP_CNT_V2_CTRL_QUAD_ENCODING_MODE,
CY_TCPWM_PWM_INVERT_ENABLE);
}
/* TODO: Add 2-bit field to top 8 bits of pwm_flags_t to set this.
* #define CY_TCPWM_PWM_OUTPUT_HIGHZ (0U)
* #define CY_TCPWM_PWM_OUTPUT_RETAIN (1U)
* #define CY_TCPWM_PWM_OUTPUT_LOW (2U)
* #define CY_TCPWM_PWM_OUTPUT_HIGH (3U)
* if ((flags & __) == __) {
* config->reg_addr->CTRL &= ~TCPWM_GRP_CNT_V2_CTRL_PWM_DISABLE_MODE_Msk;
* config->reg_addr->CTRL |= _VAL2FLD(TCPWM_GRP_CNT_V2_CTRL_PWM_DISABLE_MODE,
* __);
* }
*/
/* Enable the TCPWM for PWM mode of operation */
Cy_TCPWM_PWM_Enable(PWM_REG_BASE, data->pwm_num);
/* Start the TCPWM block */
Cy_TCPWM_TriggerStart_Single(PWM_REG_BASE, data->pwm_num);
}
return 0;
}
static int ifx_cat1_pwm_get_cycles_per_sec(const struct device *dev, uint32_t channel,
uint64_t *cycles)
{
const struct ifx_cat1_pwm_config *config = dev->config;
*cycles = Cy_SysClk_PeriphGetFrequency(config->divider_type, config->divider_sel);
return 0;
}
static const struct pwm_driver_api ifx_cat1_pwm_api = {
.set_cycles = ifx_cat1_pwm_set_cycles,
.get_cycles_per_sec = ifx_cat1_pwm_get_cycles_per_sec,
};
#define INFINEON_CAT1_PWM_INIT(n) \
PINCTRL_DT_INST_DEFINE(n); \
\
static struct ifx_cat1_pwm_data pwm_cat1_data_##n; \
\
static struct ifx_cat1_pwm_config pwm_cat1_config_##n = { \
.reg_addr = (TCPWM_GRP_CNT_Type *)DT_INST_REG_ADDR(n), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
.resolution_32_bits = (DT_INST_PROP(n, resolution) == 32) ? true : false, \
.divider_type = DT_INST_PROP(n, divider_type), \
.divider_sel = DT_INST_PROP(n, divider_sel), \
.divider_val = DT_INST_PROP(n, divider_val), \
}; \
\
DEVICE_DT_INST_DEFINE(n, ifx_cat1_pwm_init, NULL, &pwm_cat1_data_##n, \
&pwm_cat1_config_##n, POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \
&ifx_cat1_pwm_api);
DT_INST_FOREACH_STATUS_OKAY(INFINEON_CAT1_PWM_INIT)