.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Battery driver for One Laptop Per Child board. |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright © 2006-2010 David Woodhouse <dwmw2@infradead.org> |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify |
---|
7 | | - * it under the terms of the GNU General Public License version 2 as |
---|
8 | | - * published by the Free Software Foundation. |
---|
9 | 6 | */ |
---|
10 | 7 | |
---|
11 | 8 | #include <linux/kernel.h> |
---|
.. | .. |
---|
14 | 11 | #include <linux/types.h> |
---|
15 | 12 | #include <linux/err.h> |
---|
16 | 13 | #include <linux/device.h> |
---|
| 14 | +#include <linux/of.h> |
---|
17 | 15 | #include <linux/platform_device.h> |
---|
18 | 16 | #include <linux/power_supply.h> |
---|
19 | 17 | #include <linux/jiffies.h> |
---|
20 | 18 | #include <linux/sched.h> |
---|
21 | 19 | #include <linux/olpc-ec.h> |
---|
22 | | -#include <asm/olpc.h> |
---|
23 | 20 | |
---|
24 | 21 | |
---|
25 | 22 | #define EC_BAT_VOLTAGE 0x10 /* uint16_t, *9.76/32, mV */ |
---|
.. | .. |
---|
52 | 49 | |
---|
53 | 50 | #define BAT_ADDR_MFR_TYPE 0x5F |
---|
54 | 51 | |
---|
| 52 | +struct olpc_battery_data { |
---|
| 53 | + struct power_supply *olpc_ac; |
---|
| 54 | + struct power_supply *olpc_bat; |
---|
| 55 | + char bat_serial[17]; |
---|
| 56 | + bool new_proto; |
---|
| 57 | + bool little_endian; |
---|
| 58 | +}; |
---|
| 59 | + |
---|
55 | 60 | /********************************************************************* |
---|
56 | 61 | * Power |
---|
57 | 62 | *********************************************************************/ |
---|
.. | .. |
---|
83 | 88 | }; |
---|
84 | 89 | |
---|
85 | 90 | static const struct power_supply_desc olpc_ac_desc = { |
---|
86 | | - .name = "olpc-ac", |
---|
| 91 | + .name = "olpc_ac", |
---|
87 | 92 | .type = POWER_SUPPLY_TYPE_MAINS, |
---|
88 | 93 | .properties = olpc_ac_props, |
---|
89 | 94 | .num_properties = ARRAY_SIZE(olpc_ac_props), |
---|
90 | 95 | .get_property = olpc_ac_get_prop, |
---|
91 | 96 | }; |
---|
92 | 97 | |
---|
93 | | -static struct power_supply *olpc_ac; |
---|
94 | | - |
---|
95 | | -static char bat_serial[17]; /* Ick */ |
---|
96 | | - |
---|
97 | | -static int olpc_bat_get_status(union power_supply_propval *val, uint8_t ec_byte) |
---|
| 98 | +static int olpc_bat_get_status(struct olpc_battery_data *data, |
---|
| 99 | + union power_supply_propval *val, uint8_t ec_byte) |
---|
98 | 100 | { |
---|
99 | | - if (olpc_platform_info.ecver > 0x44) { |
---|
| 101 | + if (data->new_proto) { |
---|
100 | 102 | if (ec_byte & (BAT_STAT_CHARGING | BAT_STAT_TRICKLE)) |
---|
101 | 103 | val->intval = POWER_SUPPLY_STATUS_CHARGING; |
---|
102 | 104 | else if (ec_byte & BAT_STAT_DISCHARGING) |
---|
.. | .. |
---|
318 | 320 | return ret; |
---|
319 | 321 | } |
---|
320 | 322 | |
---|
| 323 | +static u16 ecword_to_cpu(struct olpc_battery_data *data, u16 ec_word) |
---|
| 324 | +{ |
---|
| 325 | + if (data->little_endian) |
---|
| 326 | + return le16_to_cpu((__force __le16)ec_word); |
---|
| 327 | + else |
---|
| 328 | + return be16_to_cpu((__force __be16)ec_word); |
---|
| 329 | +} |
---|
| 330 | + |
---|
321 | 331 | /********************************************************************* |
---|
322 | 332 | * Battery properties |
---|
323 | 333 | *********************************************************************/ |
---|
.. | .. |
---|
325 | 335 | enum power_supply_property psp, |
---|
326 | 336 | union power_supply_propval *val) |
---|
327 | 337 | { |
---|
| 338 | + struct olpc_battery_data *data = power_supply_get_drvdata(psy); |
---|
328 | 339 | int ret = 0; |
---|
329 | | - __be16 ec_word; |
---|
| 340 | + u16 ec_word; |
---|
330 | 341 | uint8_t ec_byte; |
---|
331 | 342 | __be64 ser_buf; |
---|
332 | 343 | |
---|
.. | .. |
---|
346 | 357 | |
---|
347 | 358 | switch (psp) { |
---|
348 | 359 | case POWER_SUPPLY_PROP_STATUS: |
---|
349 | | - ret = olpc_bat_get_status(val, ec_byte); |
---|
| 360 | + ret = olpc_bat_get_status(data, val, ec_byte); |
---|
350 | 361 | if (ret) |
---|
351 | 362 | return ret; |
---|
352 | 363 | break; |
---|
.. | .. |
---|
389 | 400 | if (ret) |
---|
390 | 401 | return ret; |
---|
391 | 402 | |
---|
392 | | - val->intval = (s16)be16_to_cpu(ec_word) * 9760L / 32; |
---|
| 403 | + val->intval = ecword_to_cpu(data, ec_word) * 9760L / 32; |
---|
393 | 404 | break; |
---|
394 | 405 | case POWER_SUPPLY_PROP_CURRENT_AVG: |
---|
395 | 406 | case POWER_SUPPLY_PROP_CURRENT_NOW: |
---|
.. | .. |
---|
397 | 408 | if (ret) |
---|
398 | 409 | return ret; |
---|
399 | 410 | |
---|
400 | | - val->intval = (s16)be16_to_cpu(ec_word) * 15625L / 120; |
---|
| 411 | + val->intval = ecword_to_cpu(data, ec_word) * 15625L / 120; |
---|
401 | 412 | break; |
---|
402 | 413 | case POWER_SUPPLY_PROP_CAPACITY: |
---|
403 | 414 | ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1); |
---|
.. | .. |
---|
428 | 439 | if (ret) |
---|
429 | 440 | return ret; |
---|
430 | 441 | |
---|
431 | | - val->intval = (s16)be16_to_cpu(ec_word) * 10 / 256; |
---|
| 442 | + val->intval = ecword_to_cpu(data, ec_word) * 10 / 256; |
---|
432 | 443 | break; |
---|
433 | 444 | case POWER_SUPPLY_PROP_TEMP_AMBIENT: |
---|
434 | 445 | ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2); |
---|
435 | 446 | if (ret) |
---|
436 | 447 | return ret; |
---|
437 | 448 | |
---|
438 | | - val->intval = (int)be16_to_cpu(ec_word) * 10 / 256; |
---|
| 449 | + val->intval = (int)ecword_to_cpu(data, ec_word) * 10 / 256; |
---|
439 | 450 | break; |
---|
440 | 451 | case POWER_SUPPLY_PROP_CHARGE_COUNTER: |
---|
441 | 452 | ret = olpc_ec_cmd(EC_BAT_ACR, NULL, 0, (void *)&ec_word, 2); |
---|
442 | 453 | if (ret) |
---|
443 | 454 | return ret; |
---|
444 | 455 | |
---|
445 | | - val->intval = (s16)be16_to_cpu(ec_word) * 6250 / 15; |
---|
| 456 | + val->intval = ecword_to_cpu(data, ec_word) * 6250 / 15; |
---|
446 | 457 | break; |
---|
447 | 458 | case POWER_SUPPLY_PROP_SERIAL_NUMBER: |
---|
448 | 459 | ret = olpc_ec_cmd(EC_BAT_SERIAL, NULL, 0, (void *)&ser_buf, 8); |
---|
449 | 460 | if (ret) |
---|
450 | 461 | return ret; |
---|
451 | 462 | |
---|
452 | | - sprintf(bat_serial, "%016llx", (long long)be64_to_cpu(ser_buf)); |
---|
453 | | - val->strval = bat_serial; |
---|
| 463 | + sprintf(data->bat_serial, "%016llx", (long long)be64_to_cpu(ser_buf)); |
---|
| 464 | + val->strval = data->bat_serial; |
---|
454 | 465 | break; |
---|
455 | 466 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: |
---|
456 | 467 | ret = olpc_bat_get_voltage_max_design(val); |
---|
.. | .. |
---|
536 | 547 | return count; |
---|
537 | 548 | } |
---|
538 | 549 | |
---|
539 | | -static const struct bin_attribute olpc_bat_eeprom = { |
---|
| 550 | +static struct bin_attribute olpc_bat_eeprom = { |
---|
540 | 551 | .attr = { |
---|
541 | 552 | .name = "eeprom", |
---|
542 | 553 | .mode = S_IRUGO, |
---|
.. | .. |
---|
560 | 571 | return sprintf(buf, "%d\n", ec_byte); |
---|
561 | 572 | } |
---|
562 | 573 | |
---|
563 | | -static const struct device_attribute olpc_bat_error = { |
---|
| 574 | +static struct device_attribute olpc_bat_error = { |
---|
564 | 575 | .attr = { |
---|
565 | 576 | .name = "error", |
---|
566 | 577 | .mode = S_IRUGO, |
---|
.. | .. |
---|
568 | 579 | .show = olpc_bat_error_read, |
---|
569 | 580 | }; |
---|
570 | 581 | |
---|
| 582 | +static struct attribute *olpc_bat_sysfs_attrs[] = { |
---|
| 583 | + &olpc_bat_error.attr, |
---|
| 584 | + NULL |
---|
| 585 | +}; |
---|
| 586 | + |
---|
| 587 | +static struct bin_attribute *olpc_bat_sysfs_bin_attrs[] = { |
---|
| 588 | + &olpc_bat_eeprom, |
---|
| 589 | + NULL |
---|
| 590 | +}; |
---|
| 591 | + |
---|
| 592 | +static const struct attribute_group olpc_bat_sysfs_group = { |
---|
| 593 | + .attrs = olpc_bat_sysfs_attrs, |
---|
| 594 | + .bin_attrs = olpc_bat_sysfs_bin_attrs, |
---|
| 595 | + |
---|
| 596 | +}; |
---|
| 597 | + |
---|
| 598 | +static const struct attribute_group *olpc_bat_sysfs_groups[] = { |
---|
| 599 | + &olpc_bat_sysfs_group, |
---|
| 600 | + NULL |
---|
| 601 | +}; |
---|
| 602 | + |
---|
571 | 603 | /********************************************************************* |
---|
572 | 604 | * Initialisation |
---|
573 | 605 | *********************************************************************/ |
---|
574 | 606 | |
---|
575 | 607 | static struct power_supply_desc olpc_bat_desc = { |
---|
576 | | - .name = "olpc-battery", |
---|
| 608 | + .name = "olpc_battery", |
---|
577 | 609 | .get_property = olpc_bat_get_property, |
---|
578 | 610 | .use_for_apm = 1, |
---|
579 | 611 | }; |
---|
580 | 612 | |
---|
581 | | -static struct power_supply *olpc_bat; |
---|
582 | | - |
---|
583 | 613 | static int olpc_battery_suspend(struct platform_device *pdev, |
---|
584 | 614 | pm_message_t state) |
---|
585 | 615 | { |
---|
586 | | - if (device_may_wakeup(&olpc_ac->dev)) |
---|
| 616 | + struct olpc_battery_data *data = platform_get_drvdata(pdev); |
---|
| 617 | + |
---|
| 618 | + if (device_may_wakeup(&data->olpc_ac->dev)) |
---|
587 | 619 | olpc_ec_wakeup_set(EC_SCI_SRC_ACPWR); |
---|
588 | 620 | else |
---|
589 | 621 | olpc_ec_wakeup_clear(EC_SCI_SRC_ACPWR); |
---|
590 | 622 | |
---|
591 | | - if (device_may_wakeup(&olpc_bat->dev)) |
---|
| 623 | + if (device_may_wakeup(&data->olpc_bat->dev)) |
---|
592 | 624 | olpc_ec_wakeup_set(EC_SCI_SRC_BATTERY | EC_SCI_SRC_BATSOC |
---|
593 | 625 | | EC_SCI_SRC_BATERR); |
---|
594 | 626 | else |
---|
.. | .. |
---|
600 | 632 | |
---|
601 | 633 | static int olpc_battery_probe(struct platform_device *pdev) |
---|
602 | 634 | { |
---|
603 | | - int ret; |
---|
| 635 | + struct power_supply_config bat_psy_cfg = {}; |
---|
| 636 | + struct power_supply_config ac_psy_cfg = {}; |
---|
| 637 | + struct olpc_battery_data *data; |
---|
604 | 638 | uint8_t status; |
---|
| 639 | + uint8_t ecver; |
---|
| 640 | + int ret; |
---|
605 | 641 | |
---|
606 | | - /* |
---|
607 | | - * We've seen a number of EC protocol changes; this driver requires |
---|
608 | | - * the latest EC protocol, supported by 0x44 and above. |
---|
609 | | - */ |
---|
610 | | - if (olpc_platform_info.ecver < 0x44) { |
---|
| 642 | + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); |
---|
| 643 | + if (!data) |
---|
| 644 | + return -ENOMEM; |
---|
| 645 | + platform_set_drvdata(pdev, data); |
---|
| 646 | + |
---|
| 647 | + /* See if the EC is already there and get the EC revision */ |
---|
| 648 | + ret = olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0, &ecver, 1); |
---|
| 649 | + if (ret) |
---|
| 650 | + return ret; |
---|
| 651 | + |
---|
| 652 | + if (of_find_compatible_node(NULL, NULL, "olpc,xo1.75-ec")) { |
---|
| 653 | + /* XO 1.75 */ |
---|
| 654 | + data->new_proto = true; |
---|
| 655 | + data->little_endian = true; |
---|
| 656 | + } else if (ecver > 0x44) { |
---|
| 657 | + /* XO 1 or 1.5 with a new EC firmware. */ |
---|
| 658 | + data->new_proto = true; |
---|
| 659 | + } else if (ecver < 0x44) { |
---|
| 660 | + /* |
---|
| 661 | + * We've seen a number of EC protocol changes; this driver |
---|
| 662 | + * requires the latest EC protocol, supported by 0x44 and above. |
---|
| 663 | + */ |
---|
611 | 664 | printk(KERN_NOTICE "OLPC EC version 0x%02x too old for " |
---|
612 | | - "battery driver.\n", olpc_platform_info.ecver); |
---|
| 665 | + "battery driver.\n", ecver); |
---|
613 | 666 | return -ENXIO; |
---|
614 | 667 | } |
---|
615 | 668 | |
---|
.. | .. |
---|
619 | 672 | |
---|
620 | 673 | /* Ignore the status. It doesn't actually matter */ |
---|
621 | 674 | |
---|
622 | | - olpc_ac = power_supply_register(&pdev->dev, &olpc_ac_desc, NULL); |
---|
623 | | - if (IS_ERR(olpc_ac)) |
---|
624 | | - return PTR_ERR(olpc_ac); |
---|
| 675 | + ac_psy_cfg.of_node = pdev->dev.of_node; |
---|
| 676 | + ac_psy_cfg.drv_data = data; |
---|
625 | 677 | |
---|
626 | | - if (olpc_board_at_least(olpc_board_pre(0xd0))) { /* XO-1.5 */ |
---|
| 678 | + data->olpc_ac = devm_power_supply_register(&pdev->dev, &olpc_ac_desc, |
---|
| 679 | + &ac_psy_cfg); |
---|
| 680 | + if (IS_ERR(data->olpc_ac)) |
---|
| 681 | + return PTR_ERR(data->olpc_ac); |
---|
| 682 | + |
---|
| 683 | + if (of_device_is_compatible(pdev->dev.of_node, "olpc,xo1.5-battery")) { |
---|
| 684 | + /* XO-1.5 */ |
---|
627 | 685 | olpc_bat_desc.properties = olpc_xo15_bat_props; |
---|
628 | 686 | olpc_bat_desc.num_properties = ARRAY_SIZE(olpc_xo15_bat_props); |
---|
629 | | - } else { /* XO-1 */ |
---|
| 687 | + } else { |
---|
| 688 | + /* XO-1 */ |
---|
630 | 689 | olpc_bat_desc.properties = olpc_xo1_bat_props; |
---|
631 | 690 | olpc_bat_desc.num_properties = ARRAY_SIZE(olpc_xo1_bat_props); |
---|
632 | 691 | } |
---|
633 | 692 | |
---|
634 | | - olpc_bat = power_supply_register(&pdev->dev, &olpc_bat_desc, NULL); |
---|
635 | | - if (IS_ERR(olpc_bat)) { |
---|
636 | | - ret = PTR_ERR(olpc_bat); |
---|
637 | | - goto battery_failed; |
---|
638 | | - } |
---|
| 693 | + bat_psy_cfg.of_node = pdev->dev.of_node; |
---|
| 694 | + bat_psy_cfg.drv_data = data; |
---|
| 695 | + bat_psy_cfg.attr_grp = olpc_bat_sysfs_groups; |
---|
639 | 696 | |
---|
640 | | - ret = device_create_bin_file(&olpc_bat->dev, &olpc_bat_eeprom); |
---|
641 | | - if (ret) |
---|
642 | | - goto eeprom_failed; |
---|
643 | | - |
---|
644 | | - ret = device_create_file(&olpc_bat->dev, &olpc_bat_error); |
---|
645 | | - if (ret) |
---|
646 | | - goto error_failed; |
---|
| 697 | + data->olpc_bat = devm_power_supply_register(&pdev->dev, &olpc_bat_desc, |
---|
| 698 | + &bat_psy_cfg); |
---|
| 699 | + if (IS_ERR(data->olpc_bat)) |
---|
| 700 | + return PTR_ERR(data->olpc_bat); |
---|
647 | 701 | |
---|
648 | 702 | if (olpc_ec_wakeup_available()) { |
---|
649 | | - device_set_wakeup_capable(&olpc_ac->dev, true); |
---|
650 | | - device_set_wakeup_capable(&olpc_bat->dev, true); |
---|
| 703 | + device_set_wakeup_capable(&data->olpc_ac->dev, true); |
---|
| 704 | + device_set_wakeup_capable(&data->olpc_bat->dev, true); |
---|
651 | 705 | } |
---|
652 | 706 | |
---|
653 | | - return 0; |
---|
654 | | - |
---|
655 | | -error_failed: |
---|
656 | | - device_remove_bin_file(&olpc_bat->dev, &olpc_bat_eeprom); |
---|
657 | | -eeprom_failed: |
---|
658 | | - power_supply_unregister(olpc_bat); |
---|
659 | | -battery_failed: |
---|
660 | | - power_supply_unregister(olpc_ac); |
---|
661 | | - return ret; |
---|
662 | | -} |
---|
663 | | - |
---|
664 | | -static int olpc_battery_remove(struct platform_device *pdev) |
---|
665 | | -{ |
---|
666 | | - device_remove_file(&olpc_bat->dev, &olpc_bat_error); |
---|
667 | | - device_remove_bin_file(&olpc_bat->dev, &olpc_bat_eeprom); |
---|
668 | | - power_supply_unregister(olpc_bat); |
---|
669 | | - power_supply_unregister(olpc_ac); |
---|
670 | 707 | return 0; |
---|
671 | 708 | } |
---|
672 | 709 | |
---|
673 | 710 | static const struct of_device_id olpc_battery_ids[] = { |
---|
674 | 711 | { .compatible = "olpc,xo1-battery" }, |
---|
| 712 | + { .compatible = "olpc,xo1.5-battery" }, |
---|
675 | 713 | {} |
---|
676 | 714 | }; |
---|
677 | 715 | MODULE_DEVICE_TABLE(of, olpc_battery_ids); |
---|
.. | .. |
---|
682 | 720 | .of_match_table = olpc_battery_ids, |
---|
683 | 721 | }, |
---|
684 | 722 | .probe = olpc_battery_probe, |
---|
685 | | - .remove = olpc_battery_remove, |
---|
686 | 723 | .suspend = olpc_battery_suspend, |
---|
687 | 724 | }; |
---|
688 | 725 | |
---|