1 /* 2 * (C) Copyright 2017 Rockchip Electronics Co., Ltd 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <asm/io.h> 8 #include <common.h> 9 #include <boot_rkimg.h> 10 #include <console.h> 11 #include <dm.h> 12 #include <errno.h> 13 #include <key.h> 14 #include <rtc.h> 15 #include <pwm.h> 16 #include <asm/arch/rockchip_smccc.h> 17 #include <asm/suspend.h> 18 #include <linux/input.h> 19 #include <power/charge_display.h> 20 #include <power/charge_animation.h> 21 #include <power/rockchip_pm.h> 22 #include <power/fuel_gauge.h> 23 #include <power/pmic.h> 24 #include <power/rk8xx_pmic.h> 25 #include <power/regulator.h> 26 #include <video_rockchip.h> 27 #ifdef CONFIG_IRQ 28 #include <irq-generic.h> 29 #include <rk_timer_irq.h> 30 #endif 31 32 DECLARE_GLOBAL_DATA_PTR; 33 34 #define IMAGE_SHOW_RESET -1 35 #define FUEL_GAUGE_POLL_MS 1000 36 37 struct charge_image { 38 const char *name; 39 int soc; 40 int period; /* ms */ 41 }; 42 43 struct charge_animation_priv { 44 struct udevice *pmic; 45 struct udevice *fg; 46 struct udevice *rtc; 47 const struct charge_image *image; 48 int image_num; 49 50 int auto_wakeup_key_state; 51 ulong auto_screen_off_timeout; 52 }; 53 54 /* 55 * IF you want to use your own charge images, please: 56 * 57 * 1. Update the following 'image[]' to point to your own images; 58 * 2. You must set the failed image as last one and soc = -1 !!! 59 */ 60 static const struct charge_image image[] = { 61 { .name = "battery_0.bmp", .soc = 5, .period = 600 }, 62 { .name = "battery_1.bmp", .soc = 20, .period = 600 }, 63 { .name = "battery_2.bmp", .soc = 40, .period = 600 }, 64 { .name = "battery_3.bmp", .soc = 60, .period = 600 }, 65 { .name = "battery_4.bmp", .soc = 80, .period = 600 }, 66 { .name = "battery_5.bmp", .soc = 100, .period = 600 }, 67 { .name = "battery_fail.bmp", .soc = -1, .period = 1000 }, 68 }; 69 70 static int charge_animation_ofdata_to_platdata(struct udevice *dev) 71 { 72 struct charge_animation_pdata *pdata = dev_get_platdata(dev); 73 74 /* charge mode */ 75 pdata->uboot_charge = 76 dev_read_u32_default(dev, "rockchip,uboot-charge-on", 0); 77 pdata->android_charge = 78 dev_read_u32_default(dev, "rockchip,android-charge-on", 0); 79 80 pdata->exit_charge_level = 81 dev_read_u32_default(dev, "rockchip,uboot-exit-charge-level", 0); 82 pdata->exit_charge_voltage = 83 dev_read_u32_default(dev, "rockchip,uboot-exit-charge-voltage", 0); 84 85 pdata->low_power_voltage = 86 dev_read_u32_default(dev, "rockchip,uboot-low-power-voltage", 0); 87 88 pdata->screen_on_voltage = 89 dev_read_u32_default(dev, "rockchip,screen-on-voltage", 0); 90 pdata->system_suspend = 91 dev_read_u32_default(dev, "rockchip,system-suspend", 0); 92 93 pdata->auto_wakeup_interval = 94 dev_read_u32_default(dev, "rockchip,auto-wakeup-interval", 0); 95 pdata->auto_wakeup_screen_invert = 96 dev_read_u32_default(dev, "rockchip,auto-wakeup-screen-invert", 0); 97 98 pdata->auto_off_screen_interval = 99 dev_read_u32_default(dev, "rockchip,auto-off-screen-interval", 15); 100 101 if (pdata->screen_on_voltage > pdata->exit_charge_voltage) 102 pdata->screen_on_voltage = pdata->exit_charge_voltage; 103 104 debug("mode: uboot=%d, android=%d; exit: soc=%d%%, voltage=%dmv;\n" 105 "lp_voltage=%d%%, screen_on=%dmv\n", 106 pdata->uboot_charge, pdata->android_charge, 107 pdata->exit_charge_level, pdata->exit_charge_voltage, 108 pdata->low_power_voltage, pdata->screen_on_voltage); 109 110 return 0; 111 } 112 113 static int check_key_press(struct udevice *dev) 114 { 115 struct charge_animation_pdata *pdata = dev_get_platdata(dev); 116 struct charge_animation_priv *priv = dev_get_priv(dev); 117 u32 state, rtc_state = 0; 118 119 #ifdef CONFIG_DM_RTC 120 if (priv->rtc) 121 rtc_state = rtc_alarm_trigger(priv->rtc); 122 #endif 123 if (rtc_state) { 124 printf("rtc alarm trigger...\n"); 125 return KEY_PRESS_LONG_DOWN; 126 } 127 128 state = key_read(KEY_POWER); 129 if (state < 0) 130 printf("read power key failed: %d\n", state); 131 else if (state == KEY_PRESS_DOWN) 132 printf("power key pressed...\n"); 133 else if (state == KEY_PRESS_LONG_DOWN) 134 printf("power key long pressed...\n"); 135 136 /* Fixup key state for following cases */ 137 if (pdata->auto_wakeup_interval) { 138 if (pdata->auto_wakeup_screen_invert) { 139 if (priv->auto_wakeup_key_state == KEY_PRESS_DOWN) { 140 /* Value is updated in timer interrupt */ 141 priv->auto_wakeup_key_state = KEY_PRESS_NONE; 142 state = KEY_PRESS_DOWN; 143 } 144 } 145 } else if (pdata->auto_off_screen_interval) { 146 if (priv->auto_screen_off_timeout && 147 get_timer(priv->auto_screen_off_timeout) > 148 pdata->auto_off_screen_interval * 1000) { /* 1000ms */ 149 state = KEY_PRESS_DOWN; 150 printf("Auto screen off\n"); 151 } 152 } 153 154 return state; 155 } 156 157 /* 158 * If not enable CONFIG_IRQ, cpu can't suspend to ATF or wfi, so that wakeup 159 * period timer is useless. 160 */ 161 #ifndef CONFIG_IRQ 162 static int system_suspend_enter(struct charge_animation_pdata *pdata) 163 { 164 return 0; 165 } 166 167 static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds) {} 168 static void autowakeup_timer_uninit(void) {} 169 170 #else 171 static int system_suspend_enter(struct charge_animation_pdata *pdata) 172 { 173 if (pdata->system_suspend && IS_ENABLED(CONFIG_ARM_SMCCC)) { 174 printf("\nSystem suspend: "); 175 putc('0'); 176 regulators_enable_state_mem(false); 177 putc('1'); 178 local_irq_disable(); 179 putc('2'); 180 irqs_suspend(); 181 putc('3'); 182 device_suspend(); 183 putc('4'); 184 putc('\n'); 185 186 /* Trap into ATF for low power mode */ 187 cpu_suspend(0, psci_system_suspend); 188 189 putc('\n'); 190 putc('4'); 191 device_resume(); 192 putc('3'); 193 irqs_resume(); 194 putc('2'); 195 local_irq_enable(); 196 putc('1'); 197 putc('\n'); 198 } else { 199 printf("\nWfi\n"); 200 wfi(); 201 } 202 203 /* 204 * We must wait for key release event finish, otherwise 205 * we may read key state too early. 206 */ 207 mdelay(300); 208 209 return 0; 210 } 211 static void timer_irq_handler(int irq, void *data) 212 { 213 struct udevice *dev = data; 214 struct charge_animation_priv *priv = dev_get_priv(dev); 215 static long long count; 216 217 writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS); 218 219 priv->auto_wakeup_key_state = KEY_PRESS_DOWN; 220 printf("auto wakeup count: %lld\n", ++count); 221 } 222 223 static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds) 224 { 225 uint64_t period = 24000000ULL * seconds; 226 227 /* Disable before conifg */ 228 writel(0, TIMER_BASE + TIMER_CTRL); 229 230 /* Config */ 231 writel((uint32_t)period, TIMER_BASE + TIMER_LOAD_COUNT0); 232 writel((uint32_t)(period >> 32), TIMER_BASE + TIMER_LOAD_COUNT1); 233 writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS); 234 writel(TIMER_EN | TIMER_INT_EN, TIMER_BASE + TIMER_CTRL); 235 236 /* IRQ */ 237 irq_install_handler(TIMER_IRQ, timer_irq_handler, dev); 238 irq_handler_enable(TIMER_IRQ); 239 } 240 241 static void autowakeup_timer_uninit(void) 242 { 243 irq_free_handler(TIMER_IRQ); 244 } 245 #endif 246 247 #ifdef CONFIG_DRM_ROCKCHIP 248 static void charge_show_bmp(const char *name) 249 { 250 rockchip_show_bmp(name); 251 } 252 253 static void charge_show_logo(void) 254 { 255 rockchip_show_logo(); 256 } 257 #else 258 static void charge_show_bmp(const char *name) {} 259 static void charge_show_logo(void) {} 260 #endif 261 262 static int charge_extrem_low_power(struct udevice *dev) 263 { 264 struct charge_animation_pdata *pdata = dev_get_platdata(dev); 265 struct charge_animation_priv *priv = dev_get_priv(dev); 266 struct udevice *pmic = priv->pmic; 267 struct udevice *fg = priv->fg; 268 int voltage, soc, charging = 1; 269 static int timer_initialized; 270 271 voltage = fuel_gauge_get_voltage(fg); 272 if (voltage < 0) 273 return -EINVAL; 274 275 while (voltage < pdata->low_power_voltage + 50) { 276 /* Check charger online */ 277 charging = fuel_gauge_get_chrg_online(fg); 278 if (charging <= 0) { 279 printf("%s: Not charging, online=%d. Shutdown...\n", 280 __func__, charging); 281 /* wait uart flush before shutdown */ 282 mdelay(5); 283 /* PMIC shutdown */ 284 pmic_shutdown(pmic); 285 286 printf("Cpu should never reach here, shutdown failed !\n"); 287 continue; 288 } 289 290 /* Enable auto wakeup */ 291 if (!timer_initialized) { 292 timer_initialized = 1; 293 autowakeup_timer_init(dev, 5); 294 } 295 296 /* 297 * Just for fuel gauge to update something important, 298 * including charge current, coulometer or other. 299 */ 300 soc = fuel_gauge_get_soc(fg); 301 if (soc < 0 || soc > 100) { 302 printf("get soc failed: %d\n", soc); 303 continue; 304 } 305 306 printf("Extrem low power, force charging... threshold=%dmv, now=%dmv\n", 307 pdata->low_power_voltage, voltage); 308 309 /* System suspend */ 310 system_suspend_enter(pdata); 311 312 /* Update voltage */ 313 voltage = fuel_gauge_get_voltage(fg); 314 if (voltage < 0) { 315 printf("get voltage failed: %d\n", voltage); 316 continue; 317 } 318 } 319 320 autowakeup_timer_uninit(); 321 322 return 0; 323 } 324 325 static int charge_animation_show(struct udevice *dev) 326 { 327 struct charge_animation_pdata *pdata = dev_get_platdata(dev); 328 struct charge_animation_priv *priv = dev_get_priv(dev); 329 const struct charge_image *image = priv->image; 330 struct udevice *pmic = priv->pmic; 331 struct udevice *fg = priv->fg; 332 const char *preboot = env_get("preboot"); 333 int image_num = priv->image_num; 334 bool ever_lowpower_screen_off = false; 335 bool screen_on = true; 336 ulong show_start = 0, charge_start = 0, debug_start = 0; 337 ulong delta; 338 ulong ms = 0, sec = 0; 339 int start_idx = 0, show_idx = -1, old_show_idx = IMAGE_SHOW_RESET; 340 int soc, voltage, current, key_state; 341 int i, charging = 1, ret; 342 int boot_mode; 343 int first_poll_fg = 1; 344 345 /* 346 * Check sequence: 347 * 348 * 1. Extrem low power charge? 349 * 2. Preboot cmd? 350 * 3. Valid boot mode? 351 * 4. U-Boot charge enabled by dts config? 352 * 5. Screen off before charge? 353 * 6. Enter charge ! 354 * 355 */ 356 if (!fuel_gauge_bat_is_exist(fg)) { 357 printf("Exit charge: battery is not exist\n"); 358 return 0; 359 } 360 361 /* Extrem low power charge */ 362 ret = charge_extrem_low_power(dev); 363 if (ret < 0) { 364 printf("extrem low power charge failed, ret=%d\n", ret); 365 return ret; 366 } 367 368 /* If there is preboot command, exit */ 369 if (preboot && !strstr(preboot, "dvfs")) { 370 printf("Exit charge: due to preboot cmd '%s'\n", preboot); 371 return 0; 372 } 373 374 /* Not valid charge mode, exit */ 375 #ifdef CONFIG_RKIMG_BOOTLOADER 376 boot_mode = rockchip_get_boot_mode(); 377 if ((boot_mode != BOOT_MODE_CHARGING) && 378 (boot_mode != BOOT_MODE_UNDEFINE)) { 379 printf("Exit charge: due to boot mode\n"); 380 return 0; 381 } 382 #endif 383 384 /* Not charger online, exit */ 385 charging = fuel_gauge_get_chrg_online(fg); 386 if (charging <= 0) { 387 printf("Exit charge: due to charger offline\n"); 388 return 0; 389 } 390 391 /* Enter android charge, set property for kernel */ 392 if (pdata->android_charge) { 393 env_update("bootargs", "androidboot.mode=charger"); 394 printf("Android charge mode\n"); 395 } 396 397 /* Not enable U-Boot charge, exit */ 398 if (!pdata->uboot_charge) { 399 printf("Exit charge: due to not enable uboot charge\n"); 400 return 0; 401 } 402 403 voltage = fuel_gauge_get_voltage(fg); 404 if (voltage < 0) { 405 printf("get voltage failed: %d\n", voltage); 406 return -EINVAL; 407 } 408 409 /* If low power, turn off screen */ 410 if (voltage <= pdata->screen_on_voltage + 50) { 411 screen_on = false; 412 ever_lowpower_screen_off = true; 413 charge_show_bmp(NULL); 414 } 415 416 /* Auto wakeup */ 417 if (pdata->auto_wakeup_interval) { 418 printf("Auto wakeup: %dS\n", pdata->auto_wakeup_interval); 419 autowakeup_timer_init(dev, pdata->auto_wakeup_interval); 420 } 421 422 /* Give a message warning when CONFIG_IRQ is not enabled */ 423 #ifdef CONFIG_IRQ 424 printf("Enter U-Boot charging mode\n"); 425 #else 426 printf("Enter U-Boot charging mode(without IRQ)\n"); 427 #endif 428 429 charge_start = get_timer(0); 430 delta = get_timer(0); 431 432 /* Charging ! */ 433 while (1) { 434 /* 435 * At the most time, fuel gauge is usually a i2c device, we 436 * should avoid read/write all the time. We had better set 437 * poll seconds to update fuel gauge info. 438 */ 439 if (!first_poll_fg && get_timer(delta) < FUEL_GAUGE_POLL_MS) 440 goto show_images; 441 442 delta = get_timer(0); 443 444 debug("step1 (%d)... \n", screen_on); 445 446 /* 447 * Most fuel gauge is I2C interface, it shouldn't be interrupted 448 * during tansfer. The power key event depends on interrupt, so 449 * so we should disable local irq when update fuel gauge. 450 */ 451 local_irq_disable(); 452 453 /* Step1: Is charging now ? */ 454 charging = fuel_gauge_get_chrg_online(fg); 455 if (charging <= 0) { 456 printf("Not charging, online=%d. Shutdown...\n", 457 charging); 458 459 /* wait uart flush before shutdown */ 460 mdelay(5); 461 462 /* PMIC shutdown */ 463 pmic_shutdown(pmic); 464 465 printf("Cpu should never reach here, shutdown failed !\n"); 466 continue; 467 } 468 469 debug("step2 (%d)... show_idx=%d\n", screen_on, show_idx); 470 471 /* Step2: get soc and voltage */ 472 soc = fuel_gauge_get_soc(fg); 473 if (soc < 0 || soc > 100) { 474 printf("get soc failed: %d\n", soc); 475 continue; 476 } 477 478 voltage = fuel_gauge_get_voltage(fg); 479 if (voltage < 0) { 480 printf("get voltage failed: %d\n", voltage); 481 continue; 482 } 483 484 current = fuel_gauge_get_current(fg); 485 if (current == -ENOSYS) { 486 printf("get current failed: %d\n", current); 487 continue; 488 } 489 first_poll_fg = 0; 490 491 local_irq_enable(); 492 493 show_images: 494 /* 495 * Just for debug, otherwise there will be nothing output which 496 * is not good to know what happen. 497 */ 498 if (!debug_start) 499 debug_start = get_timer(0); 500 if (get_timer(debug_start) > 20000) { 501 debug_start = get_timer(0); 502 printf("[%8ld]: soc=%d%%, vol=%dmv, c=%dma, online=%d, screen_on=%d\n", 503 get_timer(0)/1000, soc, voltage, 504 current, charging, screen_on); 505 } 506 507 /* 508 * If ever lowpower screen off, force screen_on=false, which 509 * means key event can't modify screen_on, only voltage higher 510 * then threshold can update screen_on=true; 511 */ 512 if (ever_lowpower_screen_off) 513 screen_on = false; 514 515 /* 516 * Auto turn on screen when voltage higher than Vol screen on. 517 * 'ever_lowpower_screen_off' means enter while loop with 518 * screen off. 519 */ 520 if ((ever_lowpower_screen_off) && 521 (voltage > pdata->screen_on_voltage)) { 522 ever_lowpower_screen_off = false; 523 screen_on = true; 524 show_idx = IMAGE_SHOW_RESET; 525 } 526 527 /* 528 * IMAGE_SHOW_RESET means show_idx show be update by start_idx. 529 * When short key pressed event trigged, we will set show_idx 530 * as IMAGE_SHOW_RESET which updates images index from start_idx 531 * that calculate by current soc. 532 */ 533 if (show_idx == IMAGE_SHOW_RESET) { 534 for (i = 0; i < image_num - 2; i++) { 535 /* Find out which image we start to show */ 536 if ((soc >= image[i].soc) && 537 (soc < image[i + 1].soc)) { 538 start_idx = i; 539 break; 540 } 541 542 if (soc >= 100) { 543 start_idx = image_num - 2; 544 break; 545 } 546 } 547 548 debug("%s: show_idx=%d, screen_on=%d\n", 549 __func__, show_idx, screen_on); 550 551 /* Mark start index and start time */ 552 show_idx = start_idx; 553 show_start = get_timer(0); 554 } 555 556 debug("step3 (%d)... show_idx=%d\n", screen_on, show_idx); 557 558 /* Step3: show images */ 559 if (screen_on) { 560 /* Don't call 'charge_show_bmp' unless image changed */ 561 if (old_show_idx != show_idx) { 562 old_show_idx = show_idx; 563 debug("SHOW: %s\n", image[show_idx].name); 564 charge_show_bmp(image[show_idx].name); 565 } 566 /* Re calculate timeout to off screen */ 567 if (priv->auto_screen_off_timeout == 0) 568 priv->auto_screen_off_timeout = get_timer(0); 569 } else { 570 priv->auto_screen_off_timeout = 0; 571 572 system_suspend_enter(pdata); 573 } 574 575 mdelay(5); 576 577 /* Every image shows period */ 578 if (get_timer(show_start) > image[show_idx].period) { 579 show_start = get_timer(0); 580 /* Update to next image */ 581 show_idx++; 582 if (show_idx > (image_num - 2)) 583 show_idx = IMAGE_SHOW_RESET; 584 } 585 586 debug("step4 (%d)... \n", screen_on); 587 588 /* 589 * Step4: check key event. 590 * 591 * Short key event: turn on/off screen; 592 * Long key event: show logo and boot system or still charging. 593 */ 594 key_state = check_key_press(dev); 595 if (key_state == KEY_PRESS_DOWN) { 596 old_show_idx = IMAGE_SHOW_RESET; 597 598 /* NULL means show nothing, ie. turn off screen */ 599 if (screen_on) 600 charge_show_bmp(NULL); 601 602 /* 603 * Clear current image index, and show image 604 * from start_idx 605 */ 606 show_idx = IMAGE_SHOW_RESET; 607 608 /* 609 * We turn off screen by charge_show_bmp(NULL), so we 610 * should tell while loop to stop show images any more. 611 * 612 * If screen_on=false, means this short key pressed 613 * event turn on the screen and we need show images. 614 * 615 * If screen_on=true, means this short key pressed 616 * event turn off the screen and we never show images. 617 */ 618 if (screen_on) 619 screen_on = false; 620 else 621 screen_on = true; 622 } else if (key_state == KEY_PRESS_LONG_DOWN) { 623 /* Only long pressed while screen off needs screen_on true */ 624 if (!screen_on) 625 screen_on = true; 626 627 /* Is able to boot now ? */ 628 if (soc < pdata->exit_charge_level) { 629 printf("soc=%d%%, threshold soc=%d%%\n", 630 soc, pdata->exit_charge_level); 631 printf("Low power, unable to boot, charging...\n"); 632 show_idx = image_num - 1; 633 continue; 634 } 635 636 if (voltage < pdata->exit_charge_voltage) { 637 printf("voltage=%dmv, threshold voltage=%dmv\n", 638 voltage, pdata->exit_charge_voltage); 639 printf("Low power, unable to boot, charging...\n"); 640 show_idx = image_num - 1; 641 continue; 642 } 643 644 /* Success exit charging */ 645 printf("Exit charge animation...\n"); 646 charge_show_logo(); 647 break; 648 } else { 649 /* Do nothing */ 650 } 651 652 debug("step5 (%d)... \n", screen_on); 653 654 /* Step5: Exit by ctrl+c */ 655 if (ctrlc()) { 656 if (voltage >= pdata->screen_on_voltage) 657 charge_show_logo(); 658 printf("Exit charge, due to ctrl+c\n"); 659 break; 660 } 661 } 662 663 if (pdata->auto_wakeup_interval) 664 autowakeup_timer_uninit(); 665 666 ms = get_timer(charge_start); 667 if (ms >= 1000) { 668 sec = ms / 1000; 669 ms = ms % 1000; 670 } 671 672 printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv\n", 673 sec, ms, soc, voltage); 674 675 return 0; 676 } 677 678 static const struct dm_charge_display_ops charge_animation_ops = { 679 .show = charge_animation_show, 680 }; 681 682 static int charge_animation_probe(struct udevice *dev) 683 { 684 struct charge_animation_priv *priv = dev_get_priv(dev); 685 struct udevice *fg, *pmic, *rtc; 686 int ret, soc; 687 688 /* Get PMIC: used for power off system */ 689 ret = uclass_get_device(UCLASS_PMIC, 0, &pmic); 690 if (ret) { 691 if (ret == -ENODEV) 692 printf("Can't find PMIC\n"); 693 else 694 printf("Get UCLASS PMIC failed: %d\n", ret); 695 return ret; 696 } 697 priv->pmic = pmic; 698 699 /* Get fuel gauge: used for charging */ 700 ret = uclass_get_device(UCLASS_FG, 0, &fg); 701 if (ret) { 702 if (ret == -ENODEV) 703 debug("Can't find FG\n"); 704 else 705 debug("Get UCLASS FG failed: %d\n", ret); 706 return ret; 707 } 708 priv->fg = fg; 709 710 /* Get rtc: used for power on */ 711 ret = uclass_get_device(UCLASS_RTC, 0, &rtc); 712 if (ret) { 713 if (ret == -ENODEV) 714 debug("Can't find RTC\n"); 715 else 716 debug("Get UCLASS RTC failed: %d\n", ret); 717 } 718 priv->rtc = rtc; 719 720 /* Get PWRKEY: used for wakeup and turn off/on LCD */ 721 if (key_read(KEY_POWER) == KEY_NOT_EXIST) { 722 debug("Can't find power key\n"); 723 return -EINVAL; 724 } 725 726 /* Initialize charge current */ 727 soc = fuel_gauge_get_soc(fg); 728 if (soc < 0 || soc > 100) { 729 debug("get soc failed: %d\n", soc); 730 return -EINVAL; 731 } 732 733 /* Get charge images */ 734 priv->image = image; 735 priv->image_num = ARRAY_SIZE(image); 736 737 printf("Enable charge animation display\n"); 738 739 return 0; 740 } 741 742 static const struct udevice_id charge_animation_ids[] = { 743 { .compatible = "rockchip,uboot-charge" }, 744 { }, 745 }; 746 747 U_BOOT_DRIVER(charge_animation) = { 748 .name = "charge-animation", 749 .id = UCLASS_CHARGE_DISPLAY, 750 .probe = charge_animation_probe, 751 .of_match = charge_animation_ids, 752 .ops = &charge_animation_ops, 753 .ofdata_to_platdata = charge_animation_ofdata_to_platdata, 754 .platdata_auto_alloc_size = sizeof(struct charge_animation_pdata), 755 .priv_auto_alloc_size = sizeof(struct charge_animation_priv), 756 }; 757