1 /*
2 * Copyright (c) 2023 Cypress Semiconductor Corporation (an Infineon company) or
3 * an affiliate of Cypress Semiconductor Corporation
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /**
9 * @brief ADC driver for Infineon CAT1 MCU family.
10 */
11
12 #define DT_DRV_COMPAT infineon_cat1_pwm
13
14 #include <zephyr/drivers/pwm.h>
15 #include <zephyr/drivers/pinctrl.h>
16
17 #include <cy_tcpwm_pwm.h>
18 #include <cy_gpio.h>
19 #include <cy_sysclk.h>
20 #include <cyhal_hw_resources.h>
21 #include <cyhal_hw_types.h>
22
23 #include <zephyr/logging/log.h>
24 LOG_MODULE_REGISTER(pwm_ifx_cat1, CONFIG_PWM_LOG_LEVEL);
25
26 #define PWM_REG_BASE TCPWM0
27
28 struct ifx_cat1_pwm_data {
29 uint32_t pwm_num;
30 };
31
32 struct ifx_cat1_pwm_config {
33 TCPWM_GRP_CNT_Type *reg_addr;
34 const struct pinctrl_dev_config *pcfg;
35 bool resolution_32_bits;
36 cy_en_divider_types_t divider_type;
37 uint32_t divider_sel;
38 uint32_t divider_val;
39 };
40
ifx_cat1_pwm_init(const struct device * dev)41 static int ifx_cat1_pwm_init(const struct device *dev)
42 {
43 struct ifx_cat1_pwm_data *data = dev->data;
44 const struct ifx_cat1_pwm_config *config = dev->config;
45 cy_en_tcpwm_status_t status;
46 int ret;
47 uint32_t addr_offset = (uint32_t)config->reg_addr - TCPWM0_BASE;
48 uint32_t clk_connection;
49
50 const cy_stc_tcpwm_pwm_config_t pwm_config = {
51 .pwmMode = CY_TCPWM_PWM_MODE_PWM,
52 .clockPrescaler = CY_TCPWM_PWM_PRESCALER_DIVBY_1,
53 .pwmAlignment = CY_TCPWM_PWM_LEFT_ALIGN,
54 .runMode = CY_TCPWM_PWM_CONTINUOUS,
55 .countInputMode = CY_TCPWM_INPUT_LEVEL,
56 .countInput = CY_TCPWM_INPUT_1,
57 };
58
59 /* Configure PWM clock */
60 Cy_SysClk_PeriphDisableDivider(config->divider_type, config->divider_sel);
61 Cy_SysClk_PeriphSetDivider(config->divider_type, config->divider_sel, config->divider_val);
62 Cy_SysClk_PeriphEnableDivider(config->divider_type, config->divider_sel);
63
64 /* This is very specific to the cyw920829m2evk_02 and may need to be modified
65 * for other boards.
66 */
67 if (addr_offset < sizeof(TCPWM_GRP_Type)) {
68 clk_connection =
69 PCLK_TCPWM0_CLOCK_COUNTER_EN0 + (addr_offset / sizeof(TCPWM_GRP_CNT_Type));
70 } else {
71 addr_offset -= sizeof(TCPWM_GRP_Type);
72 clk_connection = PCLK_TCPWM0_CLOCK_COUNTER_EN256 +
73 (addr_offset / sizeof(TCPWM_GRP_CNT_Type));
74 }
75 Cy_SysClk_PeriphAssignDivider(clk_connection, config->divider_type, config->divider_sel);
76
77 ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
78 if (ret < 0) {
79 return ret;
80 }
81
82 /* Configure the TCPWM to be a PWM */
83 data->pwm_num += addr_offset / sizeof(TCPWM_GRP_CNT_Type);
84 status = Cy_TCPWM_PWM_Init(PWM_REG_BASE, data->pwm_num, &pwm_config);
85 if (status != CY_TCPWM_SUCCESS) {
86 return -ENOTSUP;
87 }
88
89 return 0;
90 }
91
ifx_cat1_pwm_set_cycles(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)92 static int ifx_cat1_pwm_set_cycles(const struct device *dev, uint32_t channel,
93 uint32_t period_cycles, uint32_t pulse_cycles, pwm_flags_t flags)
94 {
95 struct ifx_cat1_pwm_data *data = dev->data;
96 const struct ifx_cat1_pwm_config *config = dev->config;
97
98 if (!config->resolution_32_bits &&
99 ((period_cycles > UINT16_MAX) || (pulse_cycles > UINT16_MAX))) {
100 /* 16-bit resolution */
101 if (period_cycles > UINT16_MAX) {
102 LOG_ERR("Period cycles more than 16-bits (%u)", period_cycles);
103 }
104 if (pulse_cycles > UINT16_MAX) {
105 LOG_ERR("Pulse cycles more than 16-bits (%u)", pulse_cycles);
106 }
107 return -EINVAL;
108 }
109
110 if ((period_cycles == 0) || (pulse_cycles == 0)) {
111 Cy_TCPWM_PWM_Disable(PWM_REG_BASE, data->pwm_num);
112 } else {
113 Cy_TCPWM_PWM_SetPeriod0(PWM_REG_BASE, data->pwm_num, period_cycles);
114 Cy_TCPWM_PWM_SetCompare0Val(PWM_REG_BASE, data->pwm_num, pulse_cycles);
115
116 if ((flags & PWM_POLARITY_MASK) == PWM_POLARITY_INVERTED) {
117 config->reg_addr->CTRL &= ~TCPWM_GRP_CNT_V2_CTRL_QUAD_ENCODING_MODE_Msk;
118 config->reg_addr->CTRL |= _VAL2FLD(TCPWM_GRP_CNT_V2_CTRL_QUAD_ENCODING_MODE,
119 CY_TCPWM_PWM_INVERT_ENABLE);
120 }
121
122 /* TODO: Add 2-bit field to top 8 bits of pwm_flags_t to set this.
123 * #define CY_TCPWM_PWM_OUTPUT_HIGHZ (0U)
124 * #define CY_TCPWM_PWM_OUTPUT_RETAIN (1U)
125 * #define CY_TCPWM_PWM_OUTPUT_LOW (2U)
126 * #define CY_TCPWM_PWM_OUTPUT_HIGH (3U)
127 * if ((flags & __) == __) {
128 * config->reg_addr->CTRL &= ~TCPWM_GRP_CNT_V2_CTRL_PWM_DISABLE_MODE_Msk;
129 * config->reg_addr->CTRL |= _VAL2FLD(TCPWM_GRP_CNT_V2_CTRL_PWM_DISABLE_MODE,
130 * __);
131 * }
132 */
133
134 /* Enable the TCPWM for PWM mode of operation */
135 Cy_TCPWM_PWM_Enable(PWM_REG_BASE, data->pwm_num);
136
137 /* Start the TCPWM block */
138 Cy_TCPWM_TriggerStart_Single(PWM_REG_BASE, data->pwm_num);
139 }
140
141 return 0;
142 }
143
ifx_cat1_pwm_get_cycles_per_sec(const struct device * dev,uint32_t channel,uint64_t * cycles)144 static int ifx_cat1_pwm_get_cycles_per_sec(const struct device *dev, uint32_t channel,
145 uint64_t *cycles)
146 {
147 const struct ifx_cat1_pwm_config *config = dev->config;
148
149 *cycles = Cy_SysClk_PeriphGetFrequency(config->divider_type, config->divider_sel);
150
151 return 0;
152 }
153
154 static DEVICE_API(pwm, ifx_cat1_pwm_api) = {
155 .set_cycles = ifx_cat1_pwm_set_cycles,
156 .get_cycles_per_sec = ifx_cat1_pwm_get_cycles_per_sec,
157 };
158
159 #define INFINEON_CAT1_PWM_INIT(n) \
160 PINCTRL_DT_INST_DEFINE(n); \
161 \
162 static struct ifx_cat1_pwm_data pwm_cat1_data_##n; \
163 \
164 static struct ifx_cat1_pwm_config pwm_cat1_config_##n = { \
165 .reg_addr = (TCPWM_GRP_CNT_Type *)DT_INST_REG_ADDR(n), \
166 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
167 .resolution_32_bits = (DT_INST_PROP(n, resolution) == 32) ? true : false, \
168 .divider_type = DT_INST_PROP(n, divider_type), \
169 .divider_sel = DT_INST_PROP(n, divider_sel), \
170 .divider_val = DT_INST_PROP(n, divider_val), \
171 }; \
172 \
173 DEVICE_DT_INST_DEFINE(n, ifx_cat1_pwm_init, NULL, &pwm_cat1_data_##n, \
174 &pwm_cat1_config_##n, POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \
175 &ifx_cat1_pwm_api);
176
177 DT_INST_FOREACH_STATUS_OKAY(INFINEON_CAT1_PWM_INIT)
178