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.
185 lines
6.6 KiB
185 lines
6.6 KiB
/* |
|
* 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, |
|
.enableCompareSwap = true, |
|
.enablePeriodSwap = true, |
|
}; |
|
|
|
/* 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 { |
|
/* Update period and compare values using buffer registers so the new values |
|
* take effect on the next TC event |
|
*/ |
|
Cy_TCPWM_PWM_SetPeriod1(PWM_REG_BASE, data->pwm_num, period_cycles); |
|
Cy_TCPWM_PWM_SetCompare0BufVal(PWM_REG_BASE, data->pwm_num, pulse_cycles); |
|
|
|
/* Trigger the swap by writing to the SW trigger command register. */ |
|
Cy_TCPWM_TriggerCaptureOrSwap_Single(PWM_REG_BASE, data->pwm_num); |
|
|
|
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 DEVICE_API(pwm, 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)
|
|
|