pm.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  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. * 2019-04-28 Zero-Free improve PM mode and device ops interface
  11. * 2020-11-23 zhangsz update pm mode select
  12. */
  13. #include <rthw.h>
  14. #include <rtthread.h>
  15. #include <drivers/pm.h>
  16. #ifdef RT_USING_PM
  17. static struct rt_pm _pm;
  18. static rt_uint8_t _pm_default_sleep = RT_PM_DEFAULT_SLEEP_MODE;
  19. static struct rt_pm_notify _pm_notify;
  20. static rt_uint8_t _pm_init_flag = 0;
  21. #define RT_PM_TICKLESS_THRESH (2)
  22. RT_WEAK rt_uint32_t rt_pm_enter_critical(rt_uint8_t sleep_mode)
  23. {
  24. return rt_hw_interrupt_disable();
  25. }
  26. RT_WEAK void rt_pm_exit_critical(rt_uint32_t ctx, rt_uint8_t sleep_mode)
  27. {
  28. rt_hw_interrupt_enable(ctx);
  29. }
  30. /**
  31. * This function will suspend all registered devices
  32. */
  33. static int _pm_device_suspend(rt_uint8_t mode)
  34. {
  35. int index, ret = RT_EOK;
  36. for (index = 0; index < _pm.device_pm_number; index++)
  37. {
  38. if (_pm.device_pm[index].ops->suspend != RT_NULL)
  39. {
  40. ret = _pm.device_pm[index].ops->suspend(_pm.device_pm[index].device, mode);
  41. if(ret != RT_EOK)
  42. break;
  43. }
  44. }
  45. return ret;
  46. }
  47. /**
  48. * This function will resume all registered devices
  49. */
  50. static void _pm_device_resume(rt_uint8_t mode)
  51. {
  52. int index;
  53. for (index = 0; index < _pm.device_pm_number; index++)
  54. {
  55. if (_pm.device_pm[index].ops->resume != RT_NULL)
  56. {
  57. _pm.device_pm[index].ops->resume(_pm.device_pm[index].device, mode);
  58. }
  59. }
  60. }
  61. /**
  62. * This function will update the frequency of all registered devices
  63. */
  64. static void _pm_device_frequency_change(rt_uint8_t mode)
  65. {
  66. rt_uint32_t index;
  67. /* make the frequency change */
  68. for (index = 0; index < _pm.device_pm_number; index ++)
  69. {
  70. if (_pm.device_pm[index].ops->frequency_change != RT_NULL)
  71. _pm.device_pm[index].ops->frequency_change(_pm.device_pm[index].device, mode);
  72. }
  73. }
  74. /**
  75. * This function will update the system clock frequency when idle
  76. */
  77. static void _pm_frequency_scaling(struct rt_pm *pm)
  78. {
  79. rt_base_t level;
  80. if (pm->flags & RT_PM_FREQUENCY_PENDING)
  81. {
  82. level = rt_hw_interrupt_disable();
  83. /* change system runing mode */
  84. pm->ops->run(pm, pm->run_mode);
  85. /* changer device frequency */
  86. _pm_device_frequency_change(pm->run_mode);
  87. pm->flags &= ~RT_PM_FREQUENCY_PENDING;
  88. rt_hw_interrupt_enable(level);
  89. }
  90. }
  91. /**
  92. * This function selects the sleep mode according to the rt_pm_request/rt_pm_release count.
  93. */
  94. static rt_uint8_t _pm_select_sleep_mode(struct rt_pm *pm)
  95. {
  96. int index;
  97. rt_uint8_t mode;
  98. mode = _pm_default_sleep;
  99. for (index = PM_SLEEP_MODE_NONE; index < PM_SLEEP_MODE_MAX; index ++)
  100. {
  101. if (pm->modes[index])
  102. {
  103. mode = index;
  104. break;
  105. }
  106. }
  107. pm->sleep_mode = mode;
  108. return mode;
  109. }
  110. /**
  111. * This function changes the power sleep mode base on the result of selection
  112. */
  113. static void _pm_change_sleep_mode(struct rt_pm *pm, rt_uint8_t mode)
  114. {
  115. rt_tick_t timeout_tick, delta_tick;
  116. rt_base_t level;
  117. int ret = RT_EOK;
  118. if (mode == PM_SLEEP_MODE_NONE)
  119. {
  120. pm->sleep_mode = mode;
  121. pm->ops->sleep(pm, PM_SLEEP_MODE_NONE);
  122. }
  123. else
  124. {
  125. level = rt_pm_enter_critical(mode);
  126. /* Notify app will enter sleep mode */
  127. if (_pm_notify.notify)
  128. _pm_notify.notify(RT_PM_ENTER_SLEEP, mode, _pm_notify.data);
  129. /* Suspend all peripheral device */
  130. ret = _pm_device_suspend(mode);
  131. if (ret != RT_EOK)
  132. {
  133. _pm_device_resume(mode);
  134. if (_pm_notify.notify)
  135. _pm_notify.notify(RT_PM_EXIT_SLEEP, mode, _pm_notify.data);
  136. rt_pm_exit_critical(level, mode);
  137. return;
  138. }
  139. /* Tickless*/
  140. if (pm->timer_mask & (0x01 << mode))
  141. {
  142. timeout_tick = rt_timer_next_timeout_tick();
  143. if (timeout_tick == RT_TICK_MAX)
  144. {
  145. if (pm->ops->timer_start)
  146. {
  147. pm->ops->timer_start(pm, RT_TICK_MAX);
  148. }
  149. }
  150. else
  151. {
  152. timeout_tick = timeout_tick - rt_tick_get();
  153. if (timeout_tick < RT_PM_TICKLESS_THRESH)
  154. {
  155. mode = PM_SLEEP_MODE_IDLE;
  156. }
  157. else
  158. {
  159. pm->ops->timer_start(pm, timeout_tick);
  160. }
  161. }
  162. }
  163. /* enter lower power state */
  164. pm->ops->sleep(pm, mode);
  165. /* wake up from lower power state*/
  166. if (pm->timer_mask & (0x01 << mode))
  167. {
  168. delta_tick = pm->ops->timer_get_tick(pm);
  169. pm->ops->timer_stop(pm);
  170. if (delta_tick)
  171. {
  172. rt_tick_set(rt_tick_get() + delta_tick);
  173. rt_timer_check();
  174. }
  175. }
  176. /* resume all device */
  177. _pm_device_resume(pm->sleep_mode);
  178. if (_pm_notify.notify)
  179. _pm_notify.notify(RT_PM_EXIT_SLEEP, mode, _pm_notify.data);
  180. rt_pm_exit_critical(level, mode);
  181. }
  182. }
  183. /**
  184. * This function will enter corresponding power mode.
  185. */
  186. void rt_system_power_manager(void)
  187. {
  188. if (_pm_init_flag == 0)
  189. return;
  190. /* CPU frequency scaling according to the runing mode settings */
  191. _pm_frequency_scaling(&_pm);
  192. /* Low Power Mode Processing */
  193. _pm_change_sleep_mode(&_pm, _pm.sleep_mode);
  194. }
  195. /**
  196. * Upper application or device driver requests the system
  197. * stall in corresponding power mode.
  198. *
  199. * @param parameter the parameter of run mode or sleep mode
  200. */
  201. void rt_pm_request(rt_uint8_t mode)
  202. {
  203. rt_base_t level;
  204. struct rt_pm *pm;
  205. if (_pm_init_flag == 0)
  206. return;
  207. if (mode > (PM_SLEEP_MODE_MAX - 1))
  208. return;
  209. level = rt_hw_interrupt_disable();
  210. pm = &_pm;
  211. if (pm->modes[mode] < 255)
  212. pm->modes[mode] ++;
  213. _pm_select_sleep_mode(pm);
  214. rt_hw_interrupt_enable(level);
  215. }
  216. /**
  217. * Upper application or device driver releases the stall
  218. * of corresponding power mode.
  219. *
  220. * @param parameter the parameter of run mode or sleep mode
  221. *
  222. */
  223. void rt_pm_release(rt_uint8_t mode)
  224. {
  225. rt_ubase_t level;
  226. struct rt_pm *pm;
  227. if (_pm_init_flag == 0)
  228. return;
  229. if (mode > (PM_SLEEP_MODE_MAX - 1))
  230. return;
  231. level = rt_hw_interrupt_disable();
  232. pm = &_pm;
  233. if (pm->modes[mode] > 0)
  234. pm->modes[mode] --;
  235. _pm_select_sleep_mode(pm);
  236. rt_hw_interrupt_enable(level);
  237. }
  238. /**
  239. * Register a device with PM feature
  240. *
  241. * @param device the device with PM feature
  242. * @param ops the PM ops for device
  243. */
  244. void rt_pm_device_register(struct rt_device *device, const struct rt_device_pm_ops *ops)
  245. {
  246. rt_base_t level;
  247. struct rt_device_pm *device_pm;
  248. RT_DEBUG_NOT_IN_INTERRUPT;
  249. level = rt_hw_interrupt_disable();
  250. device_pm = (struct rt_device_pm *)RT_KERNEL_REALLOC(_pm.device_pm,
  251. (_pm.device_pm_number + 1) * sizeof(struct rt_device_pm));
  252. if (device_pm != RT_NULL)
  253. {
  254. _pm.device_pm = device_pm;
  255. _pm.device_pm[_pm.device_pm_number].device = device;
  256. _pm.device_pm[_pm.device_pm_number].ops = ops;
  257. _pm.device_pm_number += 1;
  258. }
  259. rt_hw_interrupt_enable(level);
  260. }
  261. /**
  262. * Unregister device from PM manager.
  263. *
  264. * @param device the device with PM feature
  265. */
  266. void rt_pm_device_unregister(struct rt_device *device)
  267. {
  268. rt_ubase_t level;
  269. rt_uint32_t index;
  270. RT_DEBUG_NOT_IN_INTERRUPT;
  271. level = rt_hw_interrupt_disable();
  272. for (index = 0; index < _pm.device_pm_number; index ++)
  273. {
  274. if (_pm.device_pm[index].device == device)
  275. {
  276. /* remove current entry */
  277. for (; index < _pm.device_pm_number - 1; index ++)
  278. {
  279. _pm.device_pm[index] = _pm.device_pm[index + 1];
  280. }
  281. _pm.device_pm[_pm.device_pm_number - 1].device = RT_NULL;
  282. _pm.device_pm[_pm.device_pm_number - 1].ops = RT_NULL;
  283. _pm.device_pm_number -= 1;
  284. /* break out and not touch memory */
  285. break;
  286. }
  287. }
  288. rt_hw_interrupt_enable(level);
  289. }
  290. /**
  291. * This function set notification callback for application
  292. */
  293. void rt_pm_notify_set(void (*notify)(rt_uint8_t event, rt_uint8_t mode, void *data), void *data)
  294. {
  295. _pm_notify.notify = notify;
  296. _pm_notify.data = data;
  297. }
  298. /**
  299. * This function set default sleep mode when no pm_request
  300. */
  301. void rt_pm_default_set(rt_uint8_t sleep_mode)
  302. {
  303. _pm_default_sleep = sleep_mode;
  304. }
  305. /**
  306. * RT-Thread device interface for PM device
  307. */
  308. static rt_size_t _rt_pm_device_read(rt_device_t dev,
  309. rt_off_t pos,
  310. void *buffer,
  311. rt_size_t size)
  312. {
  313. struct rt_pm *pm;
  314. rt_size_t length;
  315. length = 0;
  316. pm = (struct rt_pm *)dev;
  317. RT_ASSERT(pm != RT_NULL);
  318. if (pos < PM_SLEEP_MODE_MAX)
  319. {
  320. int mode;
  321. mode = pm->modes[pos];
  322. length = rt_snprintf(buffer, size, "%d", mode);
  323. }
  324. return length;
  325. }
  326. static rt_size_t _rt_pm_device_write(rt_device_t dev,
  327. rt_off_t pos,
  328. const void *buffer,
  329. rt_size_t size)
  330. {
  331. unsigned char request;
  332. if (size)
  333. {
  334. /* get request */
  335. request = *(unsigned char *)buffer;
  336. if (request == 0x01)
  337. {
  338. rt_pm_request(pos);
  339. }
  340. else if (request == 0x00)
  341. {
  342. rt_pm_release(pos);
  343. }
  344. }
  345. return 1;
  346. }
  347. static rt_err_t _rt_pm_device_control(rt_device_t dev,
  348. int cmd,
  349. void *args)
  350. {
  351. rt_uint32_t mode;
  352. switch (cmd)
  353. {
  354. case RT_PM_DEVICE_CTRL_REQUEST:
  355. mode = (rt_uint32_t)args;
  356. rt_pm_request(mode);
  357. break;
  358. case RT_PM_DEVICE_CTRL_RELEASE:
  359. mode = (rt_uint32_t)args;
  360. rt_pm_release(mode);
  361. break;
  362. }
  363. return RT_EOK;
  364. }
  365. int rt_pm_run_enter(rt_uint8_t mode)
  366. {
  367. rt_base_t level;
  368. struct rt_pm *pm;
  369. if (_pm_init_flag == 0)
  370. return -RT_EIO;
  371. if (mode > PM_RUN_MODE_MAX)
  372. return -RT_EINVAL;
  373. level = rt_hw_interrupt_disable();
  374. pm = &_pm;
  375. if (mode < pm->run_mode)
  376. {
  377. /* change system runing mode */
  378. pm->ops->run(pm, mode);
  379. /* changer device frequency */
  380. _pm_device_frequency_change(mode);
  381. }
  382. else
  383. {
  384. pm->flags |= RT_PM_FREQUENCY_PENDING;
  385. }
  386. pm->run_mode = mode;
  387. rt_hw_interrupt_enable(level);
  388. return RT_EOK;
  389. }
  390. #ifdef RT_USING_DEVICE_OPS
  391. const static struct rt_device_ops pm_ops =
  392. {
  393. RT_NULL,
  394. RT_NULL,
  395. RT_NULL,
  396. _rt_pm_device_read,
  397. _rt_pm_device_write,
  398. _rt_pm_device_control,
  399. };
  400. #endif
  401. /**
  402. * This function will initialize power management.
  403. *
  404. * @param ops the PM operations.
  405. * @param timer_mask indicates which mode has timer feature.
  406. * @param user_data user data
  407. */
  408. void rt_system_pm_init(const struct rt_pm_ops *ops,
  409. rt_uint8_t timer_mask,
  410. void *user_data)
  411. {
  412. struct rt_device *device;
  413. struct rt_pm *pm;
  414. pm = &_pm;
  415. device = &(_pm.parent);
  416. device->type = RT_Device_Class_PM;
  417. device->rx_indicate = RT_NULL;
  418. device->tx_complete = RT_NULL;
  419. #ifdef RT_USING_DEVICE_OPS
  420. device->ops = &pm_ops;
  421. #else
  422. device->init = RT_NULL;
  423. device->open = RT_NULL;
  424. device->close = RT_NULL;
  425. device->read = _rt_pm_device_read;
  426. device->write = _rt_pm_device_write;
  427. device->control = _rt_pm_device_control;
  428. #endif
  429. device->user_data = user_data;
  430. /* register PM device to the system */
  431. rt_device_register(device, "pm", RT_DEVICE_FLAG_RDWR);
  432. rt_memset(pm->modes, 0, sizeof(pm->modes));
  433. pm->sleep_mode = _pm_default_sleep;
  434. pm->run_mode = RT_PM_DEFAULT_RUN_MODE;
  435. pm->timer_mask = timer_mask;
  436. pm->ops = ops;
  437. pm->device_pm = RT_NULL;
  438. pm->device_pm_number = 0;
  439. #if IDLE_THREAD_STACK_SIZE <= 256
  440. #error "[pm.c ERR] IDLE Stack Size Too Small!"
  441. #endif
  442. _pm_init_flag = 1;
  443. }
  444. #ifdef RT_USING_FINSH
  445. #include <finsh.h>
  446. static const char *_pm_sleep_str[] = PM_SLEEP_MODE_NAMES;
  447. static const char *_pm_run_str[] = PM_RUN_MODE_NAMES;
  448. static void rt_pm_release_mode(int argc, char **argv)
  449. {
  450. int mode = 0;
  451. if (argc >= 2)
  452. {
  453. mode = atoi(argv[1]);
  454. }
  455. rt_pm_release(mode);
  456. }
  457. MSH_CMD_EXPORT_ALIAS(rt_pm_release_mode, pm_release, release power management mode);
  458. static void rt_pm_request_mode(int argc, char **argv)
  459. {
  460. int mode = 0;
  461. if (argc >= 2)
  462. {
  463. mode = atoi(argv[1]);
  464. }
  465. rt_pm_request(mode);
  466. }
  467. MSH_CMD_EXPORT_ALIAS(rt_pm_request_mode, pm_request, request power management mode);
  468. static void rt_pm_run_mode_switch(int argc, char **argv)
  469. {
  470. int mode = 0;
  471. if (argc >= 2)
  472. {
  473. mode = atoi(argv[1]);
  474. }
  475. rt_pm_run_enter(mode);
  476. }
  477. MSH_CMD_EXPORT_ALIAS(rt_pm_run_mode_switch, pm_run, switch power management run mode);
  478. static void rt_pm_dump_status(void)
  479. {
  480. rt_uint32_t index;
  481. struct rt_pm *pm;
  482. pm = &_pm;
  483. rt_kprintf("| Power Management Mode | Counter | Timer |\n");
  484. rt_kprintf("+-----------------------+---------+-------+\n");
  485. for (index = 0; index < PM_SLEEP_MODE_MAX; index ++)
  486. {
  487. int has_timer = 0;
  488. if (pm->timer_mask & (1 << index))
  489. has_timer = 1;
  490. rt_kprintf("| %021s | %7d | %5d |\n", _pm_sleep_str[index], pm->modes[index], has_timer);
  491. }
  492. rt_kprintf("+-----------------------+---------+-------+\n");
  493. rt_kprintf("pm current sleep mode: %s\n", _pm_sleep_str[pm->sleep_mode]);
  494. rt_kprintf("pm current run mode: %s\n", _pm_run_str[pm->run_mode]);
  495. }
  496. FINSH_FUNCTION_EXPORT_ALIAS(rt_pm_dump_status, pm_dump, dump power management status);
  497. MSH_CMD_EXPORT_ALIAS(rt_pm_dump_status, pm_dump, dump power management status);
  498. #endif
  499. #endif /* RT_USING_PM */