1 /* SPDX-License-Identifier: BSD-3-Clause */
2 /*
3  * Copyright (c) 2020-2021 Rockchip Electronics Co., Ltd.
4  */
5 
6 #include "hal_base.h"
7 
8 #ifdef HAL_PWM_MODULE_ENABLED
9 
10 /** @addtogroup RK_HAL_Driver
11  *  @{
12  */
13 
14 /** @addtogroup PWM
15  *  @{
16  */
17 
18 /** @defgroup PWM_How_To_Use How To Use
19  *  @{
20 
21  The PWM HAL driver can be used as follows:
22 
23  - Declare a PWM_Handle handle structure, for example:
24    ```
25    PWM_Handle instance;
26    ```
27  - Invoke HAL_PWM_Init() API to initialize base address and clock frequency:
28      - Base register address;
29      - Input clock frequency.
30 
31  - Invoke HAL_PWM_SetConfig() API and HAL_PWM_SetEnable() to start/stop:
32      - Use HAL_PWM_SetConfig() to configurate the request mode;
33      - Use HAL_PWM_Enable() to start PWM;
34      - Use HAL_PWM_Disable() to stop PWM.
35 
36  - Invoke HAL_PWM_DeInit() if necessary.
37 
38  @} */
39 
40 /** @defgroup PWM_Private_Definition Private Definition
41  *  @{
42  */
43 /********************* Private MACRO Definition ******************************/
44 
45 #define PWM_CNT_REG(pPWM, ch)    (pPWM->pReg->CHANNELS[ch].CNT)
46 #define PWM_PERIOD_REG(pPWM, ch) (pPWM->pReg->CHANNELS[ch].PERIOD_HPR)
47 #define PWM_DUTY_REG(pPWM, ch)   (pPWM->pReg->CHANNELS[ch].DUTY_LPR)
48 #define PWM_CTRL_REG(pPWM, ch)   (pPWM->pReg->CHANNELS[ch].CTRL)
49 
50 #define PWM_INT_EN(ch)     (1 << (ch))
51 #define PWM_PWR_INT_EN(ch) (1 << ((ch) + 4 ))
52 
53 #define PWM_DISABLE (0 << PWM_PWM0_CTRL_PWM_EN_SHIFT)
54 #define PWM_ENABLE  (1 << PWM_PWM0_CTRL_PWM_EN_SHIFT)
55 
56 #define PWM_MODE_SHIFT (1)
57 #define PWM_MODE_MASK  (0x3U << PWM_MODE_SHIFT)
58 
59 #define PWM_DUTY_POSTIVE  (1 << PWM_PWM0_CTRL_DUTY_POL_SHIFT)
60 #define PWM_DUTY_NEGATIVE (0 << PWM_PWM0_CTRL_DUTY_POL_SHIFT)
61 #define PWM_DUTY_MASK     (1 << 3)
62 
63 #define PWM_INACTIVE_POSTIVE  (1 << PWM_PWM0_CTRL_INACTIVE_POL_SHIFT)
64 #define PWM_INACTIVE_NEGATIVE (0 << PWM_PWM0_CTRL_INACTIVE_POL_SHIFT)
65 #define PWM_INACTIVE_MASK     (1 << 4)
66 
67 #define PWM_OUTPUT_LEFT   (0 << PWM_PWM0_CTRL_OUTPUT_MODE_SHIFT)
68 #define PWM_OUTPUT_CENTER (1 << PWM_PWM0_CTRL_OUTPUT_MODE_SHIFT)
69 
70 #define PWM_UNLOCK (0 << PWM_PWM0_CTRL_CONLOCK_SHIFT)
71 #define PWM_LOCK   (1 << PWM_PWM0_CTRL_CONLOCK_SHIFT)
72 
73 #define PWM_LP_DISABLE (0 << PWM_PWM0_CTRL_FORCE_CLK_EN_SHIFT)
74 #define PWM_LP_ENABLE  (1 << PWM_PWM0_CTRL_FORCE_CLK_EN_SHIFT)
75 
76 #define PWM_SEL_SRC_CLK   (0 << PWM_PWM0_CTRL_CLK_SEL_SHIFT)
77 #define PWM_SEL_SCALE_CLK (1 << PWM_PWM0_CTRL_CLK_SEL_SHIFT)
78 
79 #define PWM_CTRL_SCALE_SHIFT (PWM_PWM0_CTRL_SCALE_SHIFT)
80 #define PWM_CTRL_SCALE_MASK  (PWM_PWM0_CTRL_SCALE_MASK)
81 
82 #define PWM_PWRMATCH_MAX_SHIFT (PWM_PWRMATCH_LPRE_CNT_MIN_SHIFT)
83 
84 /********************* Private Structure Definition **************************/
85 
86 /********************* Private Variable Definition ***************************/
87 
88 /********************* Private Function Definition ***************************/
89 
90 /** @} */
91 /********************* Public Function Definition ****************************/
92 
93 /** @defgroup PWM_Exported_Functions_Group3 IO Functions
94 
95  This section provides functions allowing to IO controlling:
96 
97  *  @{
98  */
99 
100 /**
101   * @brief  Handle PWM interrupt for capture/oneshot mode.
102   * @param  pPWM: pointer to a PWM_HANDLE structure that contains
103   *               the information for PWM module.
104   * @retval HAL status
105   */
HAL_PWM_IRQHandler(struct PWM_HANDLE * pPWM)106 HAL_Status HAL_PWM_IRQHandler(struct PWM_HANDLE *pPWM)
107 {
108     uint32_t status = READ_REG(pPWM->pReg->INTSTS);
109     uint32_t i;
110 
111     /* clean ipd */
112     WRITE_REG(pPWM->pReg->INTSTS, status & 0xf);
113 
114     if (status & 0xf) {
115         for (i = 0; i < HAL_PWM_NUM_CHANNELS; i++) {
116             if ((status & (1 << i)) &&
117                 (pPWM->mode[i] == HAL_PWM_CAPTURE)) {
118                 pPWM->result[i].active = true;
119                 pPWM->result[i].pol = status & (1 << (i + 8));
120                 if (pPWM->result[i].pol) {
121                     pPWM->result[i].period = READ_REG(PWM_PERIOD_REG(pPWM, i));
122                 } else {
123                     pPWM->result[i].period = READ_REG(PWM_DUTY_REG(pPWM, i));
124                 }
125             }
126         }
127     }
128 
129     return HAL_OK;
130 }
131 
132 /**
133  * @brief  Configurate PWM mode.
134  * @param  pPWM: pointer to a PWM_HANDLE structure that contains
135  *               the information for PWM module.
136  * @param  channel: PWM channle(0~3).
137  * @param  config: Configuration for PWM.
138  * @retval HAL status
139  */
HAL_PWM_SetConfig(struct PWM_HANDLE * pPWM,uint8_t channel,const struct HAL_PWM_CONFIG * config)140 HAL_Status HAL_PWM_SetConfig(struct PWM_HANDLE *pPWM, uint8_t channel,
141                              const struct HAL_PWM_CONFIG *config)
142 {
143     unsigned long period, duty;
144     uint32_t ctrl;
145 
146     HAL_ASSERT(pPWM != NULL);
147     HAL_ASSERT(channel < HAL_PWM_NUM_CHANNELS);
148     HAL_ASSERT(config != NULL);
149     HAL_DBG("channel=%d, period_ns=%ld, duty_ns=%ld\n",
150             channel, config->periodNS, config->dutyNS);
151 
152     period = HAL_DivU64((uint64_t)pPWM->freq * config->periodNS, 1000000000);
153     duty = HAL_DivU64((uint64_t)pPWM->freq * config->dutyNS, 1000000000);
154 
155     ctrl = READ_REG(PWM_CTRL_REG(pPWM, channel));
156     ctrl |= PWM_LOCK;
157     WRITE_REG(PWM_CTRL_REG(pPWM, channel), ctrl);
158 
159     WRITE_REG(PWM_PERIOD_REG(pPWM, channel), period);
160     WRITE_REG(PWM_DUTY_REG(pPWM, channel), duty);
161 
162     ctrl &= ~(PWM_DUTY_MASK | PWM_INACTIVE_MASK);
163     if (config->polarity) {
164         ctrl |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSTIVE;
165     } else {
166         ctrl |= PWM_DUTY_POSTIVE | PWM_INACTIVE_NEGATIVE;
167     }
168 
169     ctrl &= ~PWM_LOCK;
170     WRITE_REG(PWM_CTRL_REG(pPWM, channel), ctrl);
171 
172     HAL_DBG("channel=%d, period=%lu, duty=%lu, polarity=%d\n",
173             channel, period, duty, config->polarity);
174 
175     return HAL_OK;
176 }
177 
178 /**
179  * @brief  Configurate PWM oneshot count.
180  * @param  pPWM: pointer to a PWM_HANDLE structure that contains
181  *               the information for PWM module.
182  * @param  channel: PWM channle(0~3).
183  * @param  count: (count + 1)repeated effective periods of output waveform
184  * @retval HAL status
185  */
HAL_PWM_SetOneshot(struct PWM_HANDLE * pPWM,uint8_t channel,uint32_t count)186 HAL_Status HAL_PWM_SetOneshot(struct PWM_HANDLE *pPWM, uint8_t channel, uint32_t count)
187 {
188     uint32_t ctrl;
189 
190     HAL_ASSERT(pPWM != NULL);
191     HAL_ASSERT(channel < HAL_PWM_NUM_CHANNELS);
192     HAL_DBG("Oneshot count=%ld\n", count);
193 
194     ctrl = READ_REG(PWM_CTRL_REG(pPWM, channel));
195     ctrl |= (count << PWM_PWM0_CTRL_RPT_SHIFT) & PWM_PWM0_CTRL_RPT_MASK;
196     WRITE_REG(PWM_CTRL_REG(pPWM, channel), ctrl);
197 
198     return HAL_OK;
199 }
200 
201 /**
202  * @brief  Configurate PWM captured frequency.
203  * @param  pPWM: pointer to a PWM_HANDLE structure that contains
204  *               the information for PWM module.
205  * @param  channel: PWM channle(0~3).
206  * @param  freq: PWM use the frequency to capture data
207  * @retval HAL status
208  */
HAL_PWM_SetCapturedFreq(struct PWM_HANDLE * pPWM,uint8_t channel,uint32_t freq)209 HAL_Status HAL_PWM_SetCapturedFreq(struct PWM_HANDLE *pPWM, uint8_t channel, uint32_t freq)
210 {
211     uint32_t ctrl;
212 
213     HAL_ASSERT(pPWM != NULL);
214     HAL_ASSERT(channel < HAL_PWM_NUM_CHANNELS);
215     HAL_ASSERT(freq != 0);
216     HAL_DBG("Captured freq=%ld\n", freq);
217 
218     ctrl = READ_REG(PWM_CTRL_REG(pPWM, channel));
219     ctrl &= ~PWM_CTRL_SCALE_MASK;
220     ctrl |= PWM_LP_ENABLE | PWM_SEL_SCALE_CLK;
221     ctrl |= ((pPWM->freq / (2 * freq)) << PWM_CTRL_SCALE_SHIFT) & PWM_CTRL_SCALE_MASK;
222     WRITE_REG(PWM_CTRL_REG(pPWM, channel), ctrl);
223 
224     return HAL_OK;
225 }
226 
227 /**
228  * @brief  Configurate PWM matched setting.
229  * @param  pPWM: pointer to a PWM_HANDLE structure that contains
230  *               the information for PWM module.
231  * @param  channel: PWM channle(0~3).
232  * @param  data: matching configuration.
233  * @retval HAL status
234  */
HAL_PWM_SetMatch(struct PWM_HANDLE * pPWM,uint8_t channel,const struct PWM_MATCH * data)235 HAL_Status HAL_PWM_SetMatch(struct PWM_HANDLE *pPWM, uint8_t channel, const struct PWM_MATCH *data)
236 {
237     uint8_t i;
238 
239     HAL_ASSERT(pPWM != NULL);
240     HAL_ASSERT(channel < HAL_PWM_NUM_CHANNELS);
241     HAL_ASSERT(data != NULL);
242     HAL_ASSERT(data->matchCount <= PWM_PWRMATCH_MAX_COUNT);
243 
244     /* preloader low */
245     WRITE_REG(pPWM->pReg->PWRMATCH_LPRE, data->lpreMin | (data->lpreMax << PWM_PWRMATCH_MAX_SHIFT));
246     /* preloader high */
247     WRITE_REG(pPWM->pReg->PWRMATCH_HPRE, data->hpreMin | (data->hpreMax << PWM_PWRMATCH_MAX_SHIFT));
248     /* logic 0/1 low */
249     WRITE_REG(pPWM->pReg->PWRMATCH_LD, data->ldMin | (data->ldMax << PWM_PWRMATCH_MAX_SHIFT));
250     /* logic 0 high */
251     WRITE_REG(pPWM->pReg->PWRMATCH_HD_ZERO, data->hdZeroMin | (data->hdZeroMax << PWM_PWRMATCH_MAX_SHIFT));
252     /* logic 1 high */
253     WRITE_REG(pPWM->pReg->PWRMATCH_HD_ONE, data->hdOneMin | (data->hdOneMax << PWM_PWRMATCH_MAX_SHIFT));
254 
255     for (i = 0; i < data->matchCount; i++) {
256         WRITE_REG(pPWM->pReg->PWRMATCH_VALUE[i], data->match[i]);
257     }
258 
259     /* Enable pwr irq */
260     SET_BIT(pPWM->pReg->INT_EN, PWM_PWR_INT_EN(channel));
261     /* Enable pwr */
262     SET_BIT(pPWM->pReg->PWRMATCH_CTRL, channel);
263 
264     return HAL_OK;
265 }
266 
267 /**
268  * @brief  Get PWM mode.
269  * @param  pPWM: pointer to a PWM_HANDLE structure that contains
270  *               the information for PWM module.
271  * @param  channel: PWM channle(0~3).
272  * @retval ePWM_Mode
273  */
HAL_PWM_GetMode(struct PWM_HANDLE * pPWM,uint8_t channel)274 ePWM_Mode HAL_PWM_GetMode(struct PWM_HANDLE *pPWM, uint8_t channel)
275 {
276     uint32_t ctrl;
277 
278     HAL_ASSERT(pPWM != NULL);
279     HAL_ASSERT(channel < HAL_PWM_NUM_CHANNELS);
280     HAL_DBG("channel=%d\n", channel);
281 
282     ctrl = READ_REG(PWM_CTRL_REG(pPWM, channel));
283 
284     return (ePWM_Mode)((ctrl >> PWM_MODE_SHIFT) & PWM_MODE_MASK);
285 }
286 
287 /**
288  * @brief  Enable PWM.
289  * @param  pPWM: pointer to a PWM_HANDLE structure that contains
290  *               the information for PWM module.
291  * @param  channel: PWM channle(0~3).
292  * @param  mode: Current mode on for PWM.
293  * @retval HAL status
294  */
HAL_PWM_Enable(struct PWM_HANDLE * pPWM,uint8_t channel,ePWM_Mode mode)295 HAL_Status HAL_PWM_Enable(struct PWM_HANDLE *pPWM, uint8_t channel, ePWM_Mode mode)
296 {
297     uint32_t enableConf, intEnable;
298 
299     HAL_ASSERT(pPWM != NULL);
300     HAL_ASSERT(channel < HAL_PWM_NUM_CHANNELS);
301     HAL_DBG("Enable channel=%d\n", channel);
302 
303     pPWM->mode[channel] = mode;
304     if (pPWM->mode[channel] != HAL_PWM_CONTINUOUS) {
305         intEnable = READ_REG(pPWM->pReg->INT_EN);
306         /* Enable irq */
307         intEnable |= PWM_INT_EN(channel);
308         WRITE_REG(pPWM->pReg->INT_EN, intEnable);
309     }
310 
311     enableConf = READ_REG(PWM_CTRL_REG(pPWM, channel));
312     /* clean mode */
313     enableConf &= ~PWM_MODE_MASK;
314     enableConf |= (mode << PWM_MODE_SHIFT) | PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE;
315     WRITE_REG(PWM_CTRL_REG(pPWM, channel), enableConf);
316 
317     return HAL_OK;
318 }
319 
320 /**
321  * @brief  Disable PWM.
322  * @param  pPWM: pointer to a PWM_HANDLE structure that contains
323  *               the information for PWM module.
324  * @param  channel: PWM channle(0~3).
325  * @retval HAL status
326  */
HAL_PWM_Disable(struct PWM_HANDLE * pPWM,uint8_t channel)327 HAL_Status HAL_PWM_Disable(struct PWM_HANDLE *pPWM, uint8_t channel)
328 {
329     uint32_t ctrl, intEnable;
330 
331     HAL_ASSERT(pPWM != NULL);
332     HAL_ASSERT(channel < HAL_PWM_NUM_CHANNELS);
333     HAL_DBG("Disable channel=%d\n", channel);
334 
335     if (pPWM->mode[channel] != HAL_PWM_CONTINUOUS) {
336         intEnable = READ_REG(pPWM->pReg->INT_EN);
337         /* Disable irq */
338         intEnable &= ~PWM_INT_EN(channel);
339         WRITE_REG(pPWM->pReg->INT_EN, intEnable);
340     }
341 
342     ctrl = READ_REG(PWM_CTRL_REG(pPWM, channel));
343     ctrl &= ~PWM_ENABLE;
344     WRITE_REG(PWM_CTRL_REG(pPWM, channel), ctrl);
345 
346     return HAL_OK;
347 }
348 
349 /** @} */
350 
351 /** @defgroup PWM_Exported_Functions_Group4 Init and DeInit Functions
352 
353  This section provides functions allowing to init and deinit the module:
354 
355  *  @{
356  */
357 
358 /**
359  * @brief  Initialize the PWM according to the specified parameters.
360  * @param  pPWM: pointer to a PWM_HANDLE structure that contains
361  *               the information for PWM module.
362  * @param  pReg: PWM controller register base address.
363  * @param  freq: PWM bus input clock frequency.
364  * @return HAL_Status
365  */
HAL_PWM_Init(struct PWM_HANDLE * pPWM,struct PWM_REG * pReg,uint32_t freq)366 HAL_Status HAL_PWM_Init(struct PWM_HANDLE *pPWM, struct PWM_REG *pReg, uint32_t freq)
367 {
368     HAL_ASSERT(pPWM != NULL);
369 
370     pPWM->pReg = pReg;
371     HAL_ASSERT(IS_PWM_INSTANCE(pPWM->pReg));
372 
373     pPWM->freq = freq;
374 
375     return HAL_OK;
376 }
377 
378 /**
379   * @brief  De Initialize the PWM peripheral.
380   * @param  pPWM: pointer to a PWM_HANDLE structure that contains
381   *               the information for PWM module.
382   * @return HAL status
383   */
HAL_PWM_DeInit(struct PWM_HANDLE * pPWM)384 HAL_Status HAL_PWM_DeInit(struct PWM_HANDLE *pPWM)
385 {
386     /* ...to do */
387     return HAL_OK;
388 }
389 
390 /** @} */
391 
392 /** @} */
393 
394 /** @} */
395 
396 #endif /* HAL_PWM_MODULE_ENABLED */
397