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 */
_pm_device_suspend(void)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 */
_pm_device_resume(void)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 */
_pm_device_frequency_change(void)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 */
rt_pm_enter(void)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 */
rt_pm_exit(void)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 */
rt_pm_request(rt_ubase_t mode)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 */
rt_pm_release(rt_ubase_t mode)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 */
rt_pm_register_device(struct rt_device * device,const struct rt_device_pm_ops * ops)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 */
rt_pm_unregister_device(struct rt_device * device)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 */
_rt_pm_device_read(rt_device_t dev,rt_off_t pos,void * buffer,rt_size_t size)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
_rt_pm_device_write(rt_device_t dev,rt_off_t pos,const void * buffer,rt_size_t size)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
_rt_pm_device_control(rt_device_t dev,int cmd,void * args)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 */
rt_system_pm_init(const struct rt_pm_ops * ops,rt_uint8_t timer_mask,void * user_data)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
rt_pm_release_mode(int argc,char ** argv)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
rt_pm_request_mode(int argc,char ** argv)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
rt_pm_dump_status(void)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