1 /*
2  * Copyright (c) 2006-2022, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date         Author      Notes
8  * 2011-02-21   onelife     Initial creation for EFM32
9  * 2011-06-17   onelife     Modify init and control function for efm32lib v2 upgrading
10  */
11 
12 /***************************************************************************//**
13  * @addtogroup efm32
14  * @{
15  ******************************************************************************/
16 
17 /* Includes ------------------------------------------------------------------*/
18 #include "board.h"
19 #include "drv_acmp.h"
20 
21 #if (defined(RT_USING_ACMP0) || defined(RT_USING_ACMP1))
22 /* Private typedef -----------------------------------------------------------*/
23 /* Private define ------------------------------------------------------------*/
24 /* Private macro -------------------------------------------------------------*/
25 #ifdef RT_ACMP_DEBUG
26 #define acmp_debug(format,args...)          rt_kprintf(format, ##args)
27 #else
28 #define acmp_debug(format,args...)
29 #endif
30 
31 /* Private variables ---------------------------------------------------------*/
32 #ifdef RT_USING_ACMP0
33     static struct rt_device acmp0_device;
34 #endif
35 
36 #ifdef RT_USING_ACMP1
37     static struct rt_device acmp1_device;
38 #endif
39 
40 /* Private function prototypes -----------------------------------------------*/
41 ACMP_WarmTime_TypeDef efm32_acmp_WarmTimeCalc(rt_uint32_t hfperFreq);
42 
43 /* Private functions ---------------------------------------------------------*/
44 /***************************************************************************//**
45  * @brief
46  *   Initialize ACMP device
47  *
48  * @details
49  *
50  * @note
51  *
52  * @param[in] dev
53  *   Pointer to device descriptor
54  *
55  * @return
56  *   Error code
57  ******************************************************************************/
rt_acmp_init(rt_device_t dev)58  static rt_err_t rt_acmp_init(rt_device_t dev)
59 {
60     RT_ASSERT(dev != RT_NULL);
61 
62     struct efm32_acmp_device_t *acmp;
63 
64     acmp = (struct efm32_acmp_device_t *)(dev->user_data);
65 
66     acmp->hook.cbFunc   = RT_NULL;
67     acmp->hook.userPtr  = RT_NULL;
68 
69     return RT_EOK;
70 }
71 
72 /***************************************************************************//**
73  * @brief
74  *  Configure ACMP device
75  *
76  * @details
77  *
78  * @note
79  *
80  * @param[in] dev
81  *  Pointer to device descriptor
82  *
83  * @param[in] cmd
84  *  ACMP control command
85  *
86  * @param[in] args
87  *  Arguments
88  *
89  * @return
90  *  Error code
91  ******************************************************************************/
rt_acmp_control(rt_device_t dev,rt_uint8_t cmd,void * args)92 static rt_err_t rt_acmp_control(
93     rt_device_t     dev,
94     rt_uint8_t      cmd,
95     void            *args)
96 {
97     RT_ASSERT(dev != RT_NULL);
98 
99     struct efm32_acmp_device_t *acmp;
100 
101     acmp = (struct efm32_acmp_device_t *)(dev->user_data);
102 
103     switch (cmd)
104     {
105     case RT_DEVICE_CTRL_SUSPEND:
106         /* Suspend device */
107         dev->flag |= RT_DEVICE_FLAG_SUSPENDED;
108         ACMP_Disable(acmp->acmp_device);
109         break;
110 
111     case RT_DEVICE_CTRL_RESUME:
112         /* Resume device */
113         dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED;
114         ACMP_Enable(acmp->acmp_device);
115         break;
116 
117     case RT_DEVICE_CTRL_ACMP_INIT:
118         {
119             rt_bool_t int_en = false;
120             struct efm32_acmp_control_t *control;
121 
122             control = (struct efm32_acmp_control_t *)args;
123             acmp_debug("ACMP: control -> init start\n");
124 
125             /* Configure ACMPn */
126             if (control->init == RT_NULL)
127             {
128                 return -RT_ERROR;
129             }
130             ACMP_Init(acmp->acmp_device, control->init);
131             ACMP_ChannelSet(acmp->acmp_device, control->negInput, control->posInput);
132             if (control->output != RT_NULL)
133             {
134                 ACMP_GPIOSetup(
135                     acmp->acmp_device,
136                     control->output->location,
137                     control->output->enable,
138                     control->output->invert);
139                 int_en = true;
140             }
141             if (control->hook.cbFunc != RT_NULL)
142             {
143                 acmp->hook.cbFunc = control->hook.cbFunc;
144                 acmp->hook.userPtr = control->hook.userPtr;
145                 int_en = true;
146             }
147 
148             if (int_en)
149             {
150                 /* Enable edge interrupt */
151                 ACMP_IntEnable(acmp->acmp_device, ACMP_IEN_EDGE);
152                 ACMP_IntClear(acmp->acmp_device, ACMP_IFC_EDGE);
153 
154                 /* Enable ACMP0/1 interrupt vector in NVIC */
155                 NVIC_ClearPendingIRQ(ACMP0_IRQn);
156                 NVIC_SetPriority(ACMP0_IRQn, EFM32_IRQ_PRI_DEFAULT);
157                 NVIC_EnableIRQ(ACMP0_IRQn);
158             }
159         }
160         break;
161 
162     case RT_DEVICE_CTRL_ACMP_OUTPUT:
163         *((rt_bool_t *)args) = \
164             (acmp->acmp_device->STATUS & ACMP_STATUS_ACMPOUT) ? true : false;
165         break;
166 
167         default:
168             return -RT_ERROR;
169     }
170 
171     return RT_EOK;
172 }
173 
174 /***************************************************************************//**
175  * @brief
176  *  Register ACMP device
177  *
178  * @details
179  *
180  * @note
181  *
182  * @param[in] device
183  *  Pointer to device descriptor
184  *
185  * @param[in] name
186  *  Device name
187  *
188  * @param[in] flag
189  *  Configuration flags
190  *
191  * @param[in] acmp
192  *  Pointer to ACMP device descriptor
193  *
194  * @return
195  *  Error code
196  ******************************************************************************/
rt_hw_acmp_register(rt_device_t device,const char * name,rt_uint32_t flag,struct efm32_acmp_device_t * acmp)197 rt_err_t rt_hw_acmp_register(
198     rt_device_t     device,
199     const char      *name,
200     rt_uint32_t     flag,
201     struct efm32_acmp_device_t *acmp)
202 {
203     RT_ASSERT(device != RT_NULL);
204 
205     device->type        = RT_Device_Class_Char; /* fixme: should be acmp type */
206     device->rx_indicate = RT_NULL;
207     device->tx_complete = RT_NULL;
208     device->init        = rt_acmp_init;
209     device->open        = RT_NULL;
210     device->close       = RT_NULL;
211     device->read        = RT_NULL;
212     device->write       = RT_NULL;
213     device->control     = rt_acmp_control;
214     device->user_data   = acmp;
215 
216     /* register a character device */
217     return rt_device_register(device, name, flag);
218 }
219 
220 /***************************************************************************//**
221  * @brief
222  *  ACMP edge trigger interrupt handler
223  *
224  * @details
225  *
226  * @note
227  ******************************************************************************/
rt_hw_acmp_isr(rt_device_t dev)228 void rt_hw_acmp_isr(rt_device_t dev)
229 {
230     RT_ASSERT(dev != RT_NULL);
231 
232     struct efm32_acmp_device_t *acmp;
233 
234     acmp = (struct efm32_acmp_device_t *)(dev->user_data);
235 
236     if (acmp->hook.cbFunc != RT_NULL)
237     {
238         (acmp->hook.cbFunc)(acmp->hook.userPtr);
239     }
240 }
241 
242 /***************************************************************************//**
243  * @brief
244  *  Initialize the specified ACMP unit
245  *
246  * @details
247  *
248  * @note
249  *
250  * @param[in] device
251  *  Pointer to device descriptor
252  *
253  * @param[in] unitNumber
254  *  Unit number
255  *
256  * @return
257  *  Pointer to ACMP device
258  ******************************************************************************/
rt_hw_acmp_unit_init(rt_device_t device,rt_uint8_t unitNumber)259 static struct efm32_acmp_device_t *rt_hw_acmp_unit_init(
260     rt_device_t device,
261     rt_uint8_t  unitNumber)
262 {
263     struct efm32_acmp_device_t      *acmp;
264     efm32_irq_hook_init_t           hook;
265     CMU_Clock_TypeDef               acmpClock;
266 
267     do
268     {
269         /* Allocate device */
270         acmp = rt_malloc(sizeof(struct efm32_acmp_device_t));
271         if (acmp == RT_NULL)
272         {
273             acmp_debug("ACMP err: no mem for ACMP%d\n", unitNumber);
274             break;
275         }
276 
277         /* Initialization */
278         if (unitNumber >= ACMP_COUNT)
279         {
280             break;
281         }
282         switch (unitNumber)
283         {
284         case 0:
285             acmp->acmp_device   = ACMP0;
286             acmpClock           = (CMU_Clock_TypeDef)cmuClock_ACMP0;
287             break;
288 
289         case 1:
290             acmp->acmp_device   = ACMP1;
291             acmpClock           = (CMU_Clock_TypeDef)cmuClock_ACMP1;
292             break;
293 
294         default:
295             break;
296         }
297 
298         /* Enable ACMP clock */
299         CMU_ClockEnable(acmpClock, true);
300 
301         /* Reset */
302         ACMP_Reset(acmp->acmp_device);
303 
304         /* Config interrupt and NVIC */
305         hook.type           = efm32_irq_type_acmp;
306         hook.unit           = unitNumber;
307         hook.cbFunc         = rt_hw_acmp_isr;
308         hook.userPtr        = device;
309         efm32_irq_hook_register(&hook);
310 
311         return acmp;
312     } while(0);
313 
314     if (acmp)
315     {
316         rt_free(acmp);
317     }
318     rt_kprintf("ACMP: Init failed!\n");
319     return RT_NULL;
320 }
321 
322 /***************************************************************************//**
323  * @brief
324  *  Initialize all ACMP module related hardware and register ACMP device to
325  *  kernel
326  *
327  * @details
328  *
329  * @note
330  *
331  ******************************************************************************/
rt_hw_acmp_init(void)332 void rt_hw_acmp_init(void)
333 {
334     struct efm32_acmp_device_t  *acmp;
335 
336 #ifdef RT_USING_ACMP0
337     if ((acmp = rt_hw_acmp_unit_init(&acmp0_device, 0)) != RT_NULL)
338     {
339         rt_hw_acmp_register(&acmp0_device, RT_ACMP0_NAME, EFM32_NO_DATA, acmp);
340     }
341 #endif
342 
343 #ifdef RT_USING_ACMP1
344     if ((acmp = rt_hw_acmp_unit_init(&acmp1_device, 1)) != RT_NULL)
345     {
346         rt_hw_acmp_register(&acmp1_device, RT_ACMP1_NAME, EFM32_NO_DATA, acmp);
347     }
348 #endif
349 
350 }
351 
352 /***************************************************************************//**
353  * @brief
354  *    Calculate the warm-up time value providing at least 10us
355  *
356  * @param[in] hfperFreq
357  *  Frequency in Hz of reference HFPER clock. Set to 0 to use currently defined
358  *  HFPER clock setting
359  *
360  * @return
361  *  Warm-up time value to use for ACMP in order to achieve at least 10us
362  ******************************************************************************/
efm32_acmp_WarmTimeCalc(rt_uint32_t hfperFreq)363 ACMP_WarmTime_TypeDef efm32_acmp_WarmTimeCalc(rt_uint32_t hfperFreq)
364 {
365     if (!hfperFreq)
366     {
367         hfperFreq = CMU_ClockFreqGet(cmuClock_HFPER);
368 
369         /* Just in case, make sure we get non-zero freq for below calculation */
370         if (!hfperFreq)
371         {
372           hfperFreq = 1;
373         }
374     }
375 
376     /* Determine number of HFPERCLK cycle >= 10us */
377     if (4 * 1000000 / hfperFreq > 10)
378     {
379         return acmpWarmTime4;
380     }
381     else if (8 * 1000000 / hfperFreq > 10)
382     {
383         return acmpWarmTime8;
384     }
385     else if (16 * 1000000 / hfperFreq > 10)
386     {
387         return acmpWarmTime16;
388     }
389     else if (32 * 1000000 / hfperFreq > 10)
390     {
391         return acmpWarmTime32;
392     }
393     else if (64 * 1000000 / hfperFreq > 10)
394     {
395         return acmpWarmTime64;
396     }
397     else if (128 * 1000000 / hfperFreq > 10)
398     {
399         return acmpWarmTime128;
400     }
401     else if (256 * 1000000 / hfperFreq > 10)
402     {
403         return acmpWarmTime256;
404     }
405     else if (512 * 1000000 / hfperFreq > 10)
406     {
407         return acmpWarmTime512;
408     }
409 }
410 
411 #endif
412 /***************************************************************************//**
413  * @}
414  ******************************************************************************/
415