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