/* * 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 #include #include #include #include #include #include #include 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 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)