xref: /nrf52832-nimble/rt-thread/components/drivers/pm/pm.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
1 /*
2  * Copyright (c) 2006-2018, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2012-06-02     Bernard      the first version
9  * 2018-08-02     Tanek        split run and sleep modes, support custom mode
10  */
11 
12 #include <rthw.h>
13 #include <rtthread.h>
14 #include <drivers/pm.h>
15 
16 #ifdef RT_USING_PM
17 
18 static struct rt_pm _pm;
19 
20 /**
21  * This function will suspend all registered devices
22  */
23 static void _pm_device_suspend(void)
24 {
25     int index;
26 
27     for (index = 0; index < _pm.device_pm_number; index++)
28     {
29         if (_pm.device_pm[index].ops->suspend != RT_NULL)
30         {
31             _pm.device_pm[index].ops->suspend(_pm.device_pm[index].device);
32         }
33     }
34 }
35 
36 /**
37  * This function will resume all registered devices
38  */
39 static void _pm_device_resume(void)
40 {
41     int index;
42 
43     for (index = 0; index < _pm.device_pm_number; index++)
44     {
45         if (_pm.device_pm[index].ops->resume != RT_NULL)
46         {
47             _pm.device_pm[index].ops->resume(_pm.device_pm[index].device);
48         }
49     }
50 }
51 
52 #if PM_RUN_MODE_COUNT > 1
53 /**
54  * This function will update the frequency of all registered devices
55  */
56 static void _pm_device_frequency_change(void)
57 {
58     rt_uint32_t index;
59 
60     /* make the frequency change */
61     for (index = 0; index < _pm.device_pm_number; index ++)
62     {
63         if (_pm.device_pm[index].ops->frequency_change != RT_NULL)
64             _pm.device_pm[index].ops->frequency_change(_pm.device_pm[index].device);
65     }
66 }
67 #endif
68 
69 /**
70  * This function will enter corresponding power mode.
71  */
72 void rt_pm_enter(void)
73 {
74     rt_ubase_t level;
75     struct rt_pm *pm;
76     rt_uint32_t index;
77     rt_tick_t timeout_tick;
78 
79     pm = &_pm;
80 
81     /* disable interrupt before check run modes */
82     level = rt_hw_interrupt_disable();
83     /* check each run mode, and decide to swithc to run mode or sleep mode */
84     for (index = 0; index < PM_RUN_MODE_COUNT; index++)
85     {
86         if (pm->modes[index])
87         {
88             if (index > pm->current_mode)
89             {
90                 pm->ops->exit(pm);
91                 pm->current_mode = index;
92                 pm->ops->enter(pm);
93 #if PM_RUN_MODE_COUNT > 1
94                 pm->ops->frequency_change(pm, 0);
95                 _pm_device_frequency_change();
96 #endif
97             }
98 
99             rt_hw_interrupt_enable(level);
100             /* The current mode is run mode, no need to check sleep mode */
101             return ;
102         }
103     }
104     /* enable interrupt after check run modes */
105     rt_hw_interrupt_enable(level);
106 
107     level = rt_hw_interrupt_disable();
108     /* check each sleep mode to decide which mode can system sleep. */
109     for (index = PM_SLEEP_MODE_START; index < PM_SLEEP_MODE_START + PM_SLEEP_MODE_COUNT; index++)
110     {
111         if (pm->modes[index])
112         {
113             /* let mcu sleep when system is idle */
114 
115             /* run mode to sleep mode */
116             if (pm->current_mode < PM_SLEEP_MODE_START)
117             {
118                 /* exit run mode */
119                 pm->ops->exit(pm);
120             }
121 
122             /* set current power mode */
123             pm->current_mode = index;
124             pm->exit_count = 1;
125 
126             /* suspend all of devices with PM feature */
127             _pm_device_suspend();
128 
129             /* should start pm timer */
130             if (pm->timer_mask & (1 << index))
131             {
132                 /* get next os tick */
133                 timeout_tick = rt_timer_next_timeout_tick();
134                 if (timeout_tick != RT_TICK_MAX)
135                 {
136                     timeout_tick -= rt_tick_get();
137 
138 #if defined(PM_MIN_ENTER_SLEEP_TICK) && PM_MIN_ENTER_SLEEP_TICK > 0
139                     if (timeout_tick < PM_MIN_ENTER_SLEEP_TICK)
140                     {
141                         rt_hw_interrupt_enable(level);
142                         /* limit the minimum time to enter timer sleep mode */
143                         return ;
144                     }
145 #endif
146                 }
147                 /* startup pm timer */
148                 pm->ops->timer_start(pm, timeout_tick);
149             }
150 
151             /* enter sleep and wait to be waken up */
152             pm->ops->enter(pm);
153 
154             /* exit from low power mode */
155             rt_pm_exit();
156 
157             rt_hw_interrupt_enable(level);
158             return ;
159         }
160     }
161 
162     rt_hw_interrupt_enable(level);
163 }
164 
165 /**
166  * This function exits from sleep mode.
167  */
168 void rt_pm_exit(void)
169 {
170     rt_ubase_t level;
171     struct rt_pm *pm;
172     rt_tick_t delta_tick;
173 
174     pm = &_pm;
175 
176     level = rt_hw_interrupt_disable();
177 
178     if (pm->exit_count)
179     {
180         pm->exit_count = 0;
181 
182         if (pm->current_mode >= PM_SLEEP_MODE_START)
183         {
184             /* sleep mode with timer */
185             if (pm->timer_mask & (1 << pm->current_mode))
186             {
187                 /* get the tick of pm timer */
188                 delta_tick = pm->ops->timer_get_tick(pm);
189 
190                 /* stop pm timer */
191                 pm->ops->timer_stop(pm);
192 
193                 if (delta_tick)
194                 {
195                     /* adjust OS tick */
196                     rt_tick_set(rt_tick_get() + delta_tick);
197                     /* check system timer */
198                     rt_timer_check();
199                 }
200             }
201 
202             /* exit from sleep mode */
203             pm->ops->exit(pm);
204             /* resume the device with PM feature */
205             _pm_device_resume();
206         }
207     }
208 
209     rt_hw_interrupt_enable(level);
210 }
211 
212 /**
213  * Upper application or device driver requests the system
214  * stall in corresponding power mode.
215  *
216  * @param parameter the parameter of run mode or sleep mode
217  */
218 void rt_pm_request(rt_ubase_t mode)
219 {
220     rt_ubase_t level;
221     struct rt_pm *pm;
222 
223     pm = &_pm;
224 
225     if (mode > PM_MODE_MAX)
226         return;
227 
228     level = rt_hw_interrupt_disable();
229 
230     /* update pm modes table */
231     pm->modes[mode] ++;
232 
233     /* request higter mode with a smaller mode value*/
234     if (mode < pm->current_mode)
235     {
236         /* the old current mode is RUN mode, need to all pm->ops->exit(),
237          * if not, it has already called in rt_pm_exit()
238          */
239         if (pm->current_mode < PM_SLEEP_MODE_START)
240         {
241             pm->ops->exit(pm);
242         }
243         else if (pm->exit_count)
244         {
245             /* call exeit when global interrupt is disable */
246             pm->ops->exit(pm);
247             pm->exit_count = 0;
248         }
249 
250         /* update current mode */
251         pm->current_mode = mode;
252 
253         /* current mode is higher run mode */
254         if (mode < PM_SLEEP_MODE_START)
255         {
256             /* enter run mode */
257             pm->ops->enter(pm);
258 #if PM_RUN_MODE_COUNT > 1
259             /* frequency change */
260             pm->ops->frequency_change(pm, 0);
261             _pm_device_frequency_change();
262 #endif
263         }
264         else
265         {
266             /* do nothing when request higher sleep mode,
267              * and swithc to new sleep mode in rt_pm_enter()
268              */
269         }
270     }
271 
272     rt_hw_interrupt_enable(level);
273 }
274 
275 /**
276  * Upper application or device driver releases the stall
277  * of corresponding power mode.
278  *
279  * @param parameter the parameter of run mode or sleep mode
280  *
281  */
282 void rt_pm_release(rt_ubase_t mode)
283 {
284     rt_ubase_t level;
285     struct rt_pm *pm;
286 
287     pm = &_pm;
288 
289     if (mode > PM_MODE_MAX)
290         return;
291 
292     level = rt_hw_interrupt_disable();
293 
294     if (pm->modes[mode] > 0)
295         pm->modes[mode] --;
296 
297     rt_hw_interrupt_enable(level);
298 }
299 
300 /**
301  * Register a device with PM feature
302  *
303  * @param device the device with PM feature
304  * @param ops the PM ops for device
305  */
306 void rt_pm_register_device(struct rt_device *device, const struct rt_device_pm_ops *ops)
307 {
308     rt_ubase_t level;
309     struct rt_device_pm *device_pm;
310 
311     RT_DEBUG_NOT_IN_INTERRUPT;
312 
313     level = rt_hw_interrupt_disable();
314 
315     device_pm = (struct rt_device_pm *)RT_KERNEL_REALLOC(_pm.device_pm,
316                 (_pm.device_pm_number + 1) * sizeof(struct rt_device_pm));
317     if (device_pm != RT_NULL)
318     {
319         _pm.device_pm = device_pm;
320         _pm.device_pm[_pm.device_pm_number].device = device;
321         _pm.device_pm[_pm.device_pm_number].ops    = ops;
322         _pm.device_pm_number += 1;
323     }
324 
325     rt_sem_release(&(_pm.device_lock));
326 
327     rt_hw_interrupt_enable(level);
328 }
329 
330 /**
331  * Unregister device from PM manager.
332  *
333  * @param device the device with PM feature
334  */
335 void rt_pm_unregister_device(struct rt_device *device)
336 {
337     rt_ubase_t level;
338     rt_uint32_t index;
339     RT_DEBUG_NOT_IN_INTERRUPT;
340 
341     level = rt_hw_interrupt_disable();
342 
343     for (index = 0; index < _pm.device_pm_number; index ++)
344     {
345         if (_pm.device_pm[index].device == device)
346         {
347             /* remove current entry */
348             for (; index < _pm.device_pm_number - 1; index ++)
349             {
350                 _pm.device_pm[index] = _pm.device_pm[index + 1];
351             }
352 
353             _pm.device_pm[_pm.device_pm_number - 1].device = RT_NULL;
354             _pm.device_pm[_pm.device_pm_number - 1].ops = RT_NULL;
355 
356             _pm.device_pm_number -= 1;
357             /* break out and not touch memory */
358             break;
359         }
360     }
361 
362     rt_hw_interrupt_enable(level);
363 }
364 
365 /**
366  * RT-Thread device interface for PM device
367  */
368 static rt_size_t _rt_pm_device_read(rt_device_t dev,
369                                     rt_off_t    pos,
370                                     void       *buffer,
371                                     rt_size_t   size)
372 {
373     struct rt_pm *pm;
374     rt_size_t length;
375 
376     length = 0;
377     pm = (struct rt_pm *)dev;
378     RT_ASSERT(pm != RT_NULL);
379 
380     if (pos <= PM_MODE_MAX)
381     {
382         int mode;
383 
384         mode = pm->modes[pos];
385         length = rt_snprintf(buffer, size, "%d", mode);
386     }
387 
388     return length;
389 }
390 
391 static rt_size_t _rt_pm_device_write(rt_device_t dev,
392                                      rt_off_t    pos,
393                                      const void *buffer,
394                                      rt_size_t   size)
395 {
396     unsigned char request;
397 
398     if (size)
399     {
400         /* get request */
401         request = *(unsigned char *)buffer;
402         if (request == '1')
403         {
404             rt_pm_request(pos);
405         }
406         else if (request == '0')
407         {
408             rt_pm_release(pos);
409         }
410     }
411 
412     return 1;
413 }
414 
415 static rt_err_t _rt_pm_device_control(rt_device_t dev,
416                                       int         cmd,
417                                       void       *args)
418 {
419     rt_uint32_t mode;
420 
421     switch (cmd)
422     {
423     case RT_PM_DEVICE_CTRL_REQUEST:
424         mode = (rt_uint32_t)args;
425         rt_pm_request(mode);
426         break;
427 
428     case RT_PM_DEVICE_CTRL_RELEASE:
429         mode = (rt_uint32_t)args;
430         rt_pm_release(mode);
431         break;
432     }
433 
434     return RT_EOK;
435 }
436 
437 /**
438  * This function will initialize power management.
439  *
440  * @param ops the PM operations.
441  * @param timer_mask indicates which mode has timer feature.
442  * @param user_data user data
443  */
444 void rt_system_pm_init(const struct rt_pm_ops *ops,
445                        rt_uint8_t              timer_mask,
446                        void                   *user_data)
447 {
448     struct rt_device *device;
449     struct rt_pm *pm;
450 
451     pm = &_pm;
452     device = &(_pm.parent);
453 
454     device->type        = RT_Device_Class_PM;
455     device->rx_indicate = RT_NULL;
456     device->tx_complete = RT_NULL;
457 
458     device->init        = RT_NULL;
459     device->open        = RT_NULL;
460     device->close       = RT_NULL;
461     device->read        = _rt_pm_device_read;
462     device->write       = _rt_pm_device_write;
463     device->control     = _rt_pm_device_control;
464     device->user_data   = user_data;
465 
466     /* register PM device to the system */
467     rt_device_register(device, "pm", RT_DEVICE_FLAG_RDWR);
468 
469     /* todo : add to kernel source code */
470     rt_thread_idle_sethook(rt_pm_enter);
471 
472     rt_memset(pm->modes, 0, sizeof(pm->modes));
473     pm->current_mode = PM_RUN_MODE_DEFAULT;
474 
475     pm->timer_mask = timer_mask;
476 
477     pm->ops = ops;
478 
479     pm->device_pm = RT_NULL;
480     pm->device_pm_number = 0;
481 
482     /* initialize semaphore */
483     rt_sem_init(&(pm->device_lock), "pm", 1, RT_IPC_FLAG_FIFO);
484 
485     /* request in default running mode */
486     rt_pm_request(PM_RUN_MODE_DEFAULT);
487 
488 #ifdef PM_SLEEP_MODE_DEFAULT
489     /* request in default sleep mode */
490     rt_pm_request(PM_SLEEP_MODE_DEFAULT);
491 #endif
492 
493     /* must hold on deep shutdown mode */
494     rt_pm_request(PM_MODE_MAX);
495 }
496 
497 #ifdef RT_USING_FINSH
498 #include <finsh.h>
499 
500 static void rt_pm_release_mode(int argc, char **argv)
501 {
502     int mode = 0;
503     if (argc >= 2)
504     {
505         mode = atoi(argv[1]);
506     }
507 
508     rt_pm_release(mode);
509 }
510 MSH_CMD_EXPORT_ALIAS(rt_pm_release_mode, pm_release, release power management mode);
511 
512 static void rt_pm_request_mode(int argc, char **argv)
513 {
514     int mode = 0;
515     if (argc >= 2)
516     {
517         mode = atoi(argv[1]);
518     }
519 
520     rt_pm_request(mode);
521 }
522 MSH_CMD_EXPORT_ALIAS(rt_pm_request_mode, pm_request, request power management mode);
523 
524 static void rt_pm_dump_status(void)
525 {
526     static const char *pm_str[] = PM_MODE_NAMES;
527     rt_uint32_t index;
528     struct rt_pm *pm;
529 
530     pm = &_pm;
531 
532     rt_kprintf("| Power Management Mode | Counter | Timer |\n");
533     rt_kprintf("+-----------------------+---------+-------+\n");
534     for (index = 0; index <= PM_MODE_MAX; index ++)
535     {
536         int has_timer = 0;
537         if (pm->timer_mask & (1 << index))
538             has_timer = 1;
539 
540         rt_kprintf("| %021s | %7d | %5d |\n", pm_str[index], pm->modes[index], has_timer);
541     }
542     rt_kprintf("+-----------------------+---------+-------+\n");
543 
544     rt_kprintf("pm current mode: %s\n", pm_str[pm->current_mode]);
545 }
546 FINSH_FUNCTION_EXPORT_ALIAS(rt_pm_dump_status, pm_dump, dump power management status);
547 MSH_CMD_EXPORT_ALIAS(rt_pm_dump_status, pm_dump, dump power management status);
548 #endif
549 
550 #endif /* RT_USING_PM */
551