178 lines
6.2 KiB
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)
|