pm.c 13 KB

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