|
26 | 26 | * - binding to Capability Data 00 and Fan |
27 | 27 | */ |
28 | 28 |
|
| 29 | +#include <acpi/battery.h> |
29 | 30 | #include <linux/acpi.h> |
30 | 31 | #include <linux/bitfield.h> |
31 | 32 | #include <linux/cleanup.h> |
|
42 | 43 | #include <linux/module.h> |
43 | 44 | #include <linux/notifier.h> |
44 | 45 | #include <linux/platform_profile.h> |
| 46 | +#include <linux/power_supply.h> |
45 | 47 | #include <linux/types.h> |
46 | 48 | #include <linux/wmi.h> |
47 | 49 |
|
@@ -80,26 +82,38 @@ enum lwmi_feature_id_gpu { |
80 | 82 | LWMI_FEATURE_ID_GPU_NV_CPU_BOOST = 0x0b, |
81 | 83 | }; |
82 | 84 |
|
| 85 | +enum lwmi_feature_id_psu { |
| 86 | + LWMI_FEATURE_ID_PSU_INSTANT_MODE = 0x01, |
| 87 | + LWMI_FEATURE_ID_PSU_CHARGE_MODE = 0x02, |
| 88 | +}; |
| 89 | + |
83 | 90 | #define LWMI_FEATURE_ID_FAN_RPM 0x03 |
84 | 91 |
|
85 | 92 | #define LWMI_TYPE_ID_NONE 0x00 |
86 | 93 | #define LWMI_TYPE_ID_CROSSLOAD 0x01 |
| 94 | +#define LWMI_TYPE_ID_PSU_AC 0x01 |
| 95 | +#define LWMI_TYPE_ID_PSU_PD 0x02 |
87 | 96 |
|
88 | 97 | #define LWMI_FEATURE_VALUE_GET 17 |
89 | 98 | #define LWMI_FEATURE_VALUE_SET 18 |
90 | 99 |
|
91 | 100 | #define LWMI_FAN_ID_BASE 1 |
92 | 101 | #define LWMI_FAN_NR 4 |
93 | 102 | #define LWMI_FAN_ID(x) ((x) + LWMI_FAN_ID_BASE) |
| 103 | +#define LWMI_FAN_DIV 100 |
| 104 | + |
| 105 | +#define LWMI_CHARGE_MODE_ENABLED 0x00 |
| 106 | +#define LWMI_CHARGE_MODE_DISABLED 0x01 |
94 | 107 |
|
95 | 108 | #define LWMI_ATTR_ID_FAN_RPM(x) \ |
96 | 109 | LWMI_ATTR_ID(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_RPM, \ |
97 | 110 | LWMI_GZ_THERMAL_MODE_NONE, LWMI_FAN_ID(x)) |
98 | 111 |
|
99 | | -#define LWMI_FAN_DIV 100 |
| 112 | +#define LWMI_ATTR_ID_PSU(feat, type) \ |
| 113 | + LWMI_ATTR_ID(LWMI_DEVICE_ID_PSU, feat, \ |
| 114 | + LWMI_GZ_THERMAL_MODE_NONE, type) |
100 | 115 |
|
101 | | -#define LWMI_OM_FW_ATTR_BASE_PATH "lenovo-wmi-other" |
102 | | -#define LWMI_OM_HWMON_NAME "lenovo_wmi_other" |
| 116 | +#define LWMI_OM_NAME "lenovo-wmi-other" |
103 | 117 |
|
104 | 118 | static BLOCKING_NOTIFIER_HEAD(om_chain_head); |
105 | 119 | static DEFINE_IDA(lwmi_om_ida); |
@@ -139,6 +153,8 @@ struct lwmi_om_priv { |
139 | 153 | bool capdata00_collected : 1; |
140 | 154 | bool capdata_fan_collected : 1; |
141 | 155 | } fan_flags; |
| 156 | + |
| 157 | + struct acpi_battery_hook battery_hook; |
142 | 158 | }; |
143 | 159 |
|
144 | 160 | /* |
@@ -454,7 +470,7 @@ static void lwmi_om_hwmon_add(struct lwmi_om_priv *priv) |
454 | 470 | } |
455 | 471 |
|
456 | 472 | priv->hwmon_dev = hwmon_device_register_with_info(&priv->wdev->dev, |
457 | | - LWMI_OM_HWMON_NAME, priv, |
| 473 | + LWMI_OM_NAME, priv, |
458 | 474 | &lwmi_om_hwmon_chip_info, |
459 | 475 | NULL); |
460 | 476 | if (IS_ERR(priv->hwmon_dev)) { |
@@ -563,6 +579,216 @@ static void lwmi_om_fan_info_collect_cd_fan(struct device *dev, struct cd_list * |
563 | 579 | lwmi_om_hwmon_add(priv); |
564 | 580 | } |
565 | 581 |
|
| 582 | +/* ======== Power Supply Extension (component: lenovo-wmi-capdata 00) ======== */ |
| 583 | + |
| 584 | +/** |
| 585 | + * lwmi_psy_prop_is_writeable() - Get a power_supply_ext property |
| 586 | + * @ps: The battery that was extended |
| 587 | + * @ext: The extension |
| 588 | + * @ext_data: Pointer the lwmi_om_priv drvdata |
| 589 | + * @prop: The property to read |
| 590 | + * @val: The value to return |
| 591 | + * |
| 592 | + * Writes the given value to the power_supply_ext property |
| 593 | + * |
| 594 | + * Return: 0 on success, or an error |
| 595 | + */ |
| 596 | +static int lwmi_psy_ext_get_prop(struct power_supply *ps, |
| 597 | + const struct power_supply_ext *ext, |
| 598 | + void *data, |
| 599 | + enum power_supply_property prop, |
| 600 | + union power_supply_propval *val) |
| 601 | +{ |
| 602 | + struct lwmi_om_priv *priv = data; |
| 603 | + struct wmi_method_args_32 args; |
| 604 | + u32 retval; |
| 605 | + int ret; |
| 606 | + |
| 607 | + args.arg0 = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_INSTANT_MODE, LWMI_TYPE_ID_PSU_AC); |
| 608 | + |
| 609 | + ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET, |
| 610 | + (unsigned char *)&args, sizeof(args), |
| 611 | + &retval); |
| 612 | + if (ret) |
| 613 | + return ret; |
| 614 | + |
| 615 | + dev_dbg(&priv->wdev->dev, "Got return value %x for property %x\n", retval, prop); |
| 616 | + |
| 617 | + if (retval == LWMI_CHARGE_MODE_DISABLED) |
| 618 | + val->intval = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE; |
| 619 | + else |
| 620 | + val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; |
| 621 | + |
| 622 | + return 0; |
| 623 | +} |
| 624 | + |
| 625 | +/** |
| 626 | + * lwmi_psy_prop_is_writeable() - Set a power_supply_ext property |
| 627 | + * @ps: The battery that was extended |
| 628 | + * @ext: The extension |
| 629 | + * @ext_data: Pointer the lwmi_om_priv drvdata |
| 630 | + * @prop: The property to write |
| 631 | + * @val: The value to write |
| 632 | + * |
| 633 | + * Writes the given value to the power_supply_ext property |
| 634 | + * |
| 635 | + * Return: 0 on success, or an error |
| 636 | + */ |
| 637 | +static int lwmi_psy_ext_set_prop(struct power_supply *ps, |
| 638 | + const struct power_supply_ext *ext, |
| 639 | + void *ext_data, |
| 640 | + enum power_supply_property prop, |
| 641 | + const union power_supply_propval *val) |
| 642 | +{ |
| 643 | + struct lwmi_om_priv *priv = ext_data; |
| 644 | + struct wmi_method_args_32 args; |
| 645 | + |
| 646 | + args.arg0 = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_INSTANT_MODE, LWMI_TYPE_ID_PSU_AC); |
| 647 | + if (val->intval == POWER_SUPPLY_CHARGE_TYPE_LONGLIFE) |
| 648 | + args.arg1 = LWMI_CHARGE_MODE_DISABLED; |
| 649 | + else |
| 650 | + args.arg1 = LWMI_CHARGE_MODE_ENABLED; |
| 651 | + |
| 652 | + dev_dbg(&priv->wdev->dev, "Attempting to set %#08x for property %x to %x\n", |
| 653 | + args.arg0, prop, args.arg1); |
| 654 | + |
| 655 | + return lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET, |
| 656 | + (unsigned char *)&args, sizeof(args), NULL); |
| 657 | +} |
| 658 | + |
| 659 | +/** |
| 660 | + * lwmi_psy_prop_is_writeable() - Determine if the property is supported |
| 661 | + * @ps: The battery that was extended |
| 662 | + * @ext: The extension |
| 663 | + * @ext_data: Pointer the lwmi_om_priv drvdata |
| 664 | + * @prop: The property to check |
| 665 | + * |
| 666 | + * Checks capdata 00 to determine if the property is supported. |
| 667 | + * |
| 668 | + * Return: Support level, or false |
| 669 | + */ |
| 670 | +static int lwmi_psy_prop_is_writeable(struct power_supply *ps, |
| 671 | + const struct power_supply_ext *ext, |
| 672 | + void *ext_data, |
| 673 | + enum power_supply_property prop) |
| 674 | +{ |
| 675 | + struct lwmi_om_priv *priv = ext_data; |
| 676 | + struct capdata00 capdata; |
| 677 | + u32 attribute_id = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_INSTANT_MODE, LWMI_TYPE_ID_PSU_AC); |
| 678 | + int ret; |
| 679 | + |
| 680 | + ret = lwmi_cd00_get_data(priv->cd00_list, attribute_id, &capdata); |
| 681 | + if (ret) |
| 682 | + return false; |
| 683 | + |
| 684 | + dev_dbg(&priv->wdev->dev, "Battery charge mode (%#08x) support level: %x\n", |
| 685 | + attribute_id, capdata.supported); |
| 686 | + |
| 687 | + return capdata.supported; |
| 688 | +} |
| 689 | + |
| 690 | +static const enum power_supply_property lwmi_psy_ext_props[] = { |
| 691 | + POWER_SUPPLY_PROP_CHARGE_TYPES, |
| 692 | +}; |
| 693 | + |
| 694 | +static const struct power_supply_ext lwmi_psy_ext = { |
| 695 | + .name = LWMI_OM_NAME, |
| 696 | + .properties = lwmi_psy_ext_props, |
| 697 | + .num_properties = ARRAY_SIZE(lwmi_psy_ext_props), |
| 698 | + .charge_types = (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) | |
| 699 | + BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE)), |
| 700 | + .get_property = lwmi_psy_ext_get_prop, |
| 701 | + .set_property = lwmi_psy_ext_set_prop, |
| 702 | + .property_is_writeable = lwmi_psy_prop_is_writeable, |
| 703 | +}; |
| 704 | + |
| 705 | +/** |
| 706 | + * lwmi_add_battery() - Connect the power_supply_ext |
| 707 | + * @battery: The battery to extend |
| 708 | + * @hook: The driver hook used to extend the battery |
| 709 | + * |
| 710 | + * Return: 0 on success, or an error. |
| 711 | + */ |
| 712 | +static int lwmi_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook) |
| 713 | +{ |
| 714 | + struct lwmi_om_priv *priv = container_of(hook, struct lwmi_om_priv, battery_hook); |
| 715 | + |
| 716 | + return power_supply_register_extension(battery, &lwmi_psy_ext, &priv->wdev->dev, priv); |
| 717 | +} |
| 718 | + |
| 719 | +/** |
| 720 | + * lwmi_remove_battery() - Disconnect the power_supply_ext |
| 721 | + * @battery: The battery that was extended |
| 722 | + * @hook: The driver hook used to extend the battery |
| 723 | + * |
| 724 | + * Return: 0 on success, or an error. |
| 725 | + */ |
| 726 | +static int lwmi_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook) |
| 727 | +{ |
| 728 | + power_supply_unregister_extension(battery, &lwmi_psy_ext); |
| 729 | + return 0; |
| 730 | +} |
| 731 | + |
| 732 | +/** |
| 733 | + * lwmi_acpi_match() - Attempts to return the ideapad acpi handle |
| 734 | + * @acpi_handle: The ACPI handle that manages battery charging |
| 735 | + * @lvl: Unused |
| 736 | + * @context: Void pointer to the acpi_handle object to return |
| 737 | + * @retval: Unused |
| 738 | + * |
| 739 | + * Checks if the ideapad_laptop driver is going to manage charge_type first, |
| 740 | + * thenm if not, hooks the battery to our WMI methods. |
| 741 | + * |
| 742 | + * Return: AE_CTRL_TERMINATE if found, AE_OK if not found. |
| 743 | + */ |
| 744 | +static acpi_status lwmi_acpi_match(acpi_handle handle, u32 lvl, |
| 745 | + void *context, void **retval) |
| 746 | +{ |
| 747 | + if (!handle) |
| 748 | + return AE_OK; |
| 749 | + |
| 750 | + acpi_handle *ahand = context; |
| 751 | + *ahand = handle; |
| 752 | + |
| 753 | + return AE_CTRL_TERMINATE; |
| 754 | +} |
| 755 | + |
| 756 | +/** |
| 757 | + * lwmi_om_ps_ext_init() - Hooks power supply extension to device battery |
| 758 | + * @priv: Driver private data |
| 759 | + * |
| 760 | + * Checks if the ideapad_laptop driver is going to manage charge_type first, |
| 761 | + * thenm if not, hooks the battery to our WMI methods. |
| 762 | + */ |
| 763 | +static void lwmi_om_ps_ext_init(struct lwmi_om_priv *priv) |
| 764 | +{ |
| 765 | + static const char * const ideapad_hid = "VPC2004"; |
| 766 | + acpi_handle handle = NULL; |
| 767 | + int ret; |
| 768 | + |
| 769 | + /* Deconflict ideapad_laptop driver */ |
| 770 | + ret = acpi_get_devices(ideapad_hid, lwmi_acpi_match, &handle, NULL); |
| 771 | + if (ret) |
| 772 | + return; |
| 773 | + |
| 774 | + if (!handle) |
| 775 | + return; |
| 776 | + |
| 777 | + if (acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) { |
| 778 | + dev_dbg(&priv->wdev->dev, "ideapad_laptop driver manages battery for device.\n"); |
| 779 | + return; |
| 780 | + } |
| 781 | + |
| 782 | + /* Add battery hooks */ |
| 783 | + priv->battery_hook.add_battery = lwmi_add_battery, |
| 784 | + priv->battery_hook.remove_battery = lwmi_remove_battery, |
| 785 | + priv->battery_hook.name = "Lenovo WMI Other Battery Extension", |
| 786 | + |
| 787 | + ret = devm_battery_hook_register(&priv->wdev->dev, &priv->battery_hook); |
| 788 | + if (ret) |
| 789 | + dev_err(&priv->wdev->dev, "Error during battery hook: %i\n", ret); |
| 790 | +} |
| 791 | + |
566 | 792 | /* ======== fw_attributes (component: lenovo-wmi-capdata 01) ======== */ |
567 | 793 |
|
568 | 794 | struct tunable_attr_01 { |
@@ -1252,8 +1478,7 @@ static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv) |
1252 | 1478 |
|
1253 | 1479 | priv->fw_attr_dev = device_create(&firmware_attributes_class, NULL, |
1254 | 1480 | MKDEV(0, 0), NULL, "%s-%u", |
1255 | | - LWMI_OM_FW_ATTR_BASE_PATH, |
1256 | | - priv->ida_id); |
| 1481 | + LWMI_OM_NAME, priv->ida_id); |
1257 | 1482 | if (IS_ERR(priv->fw_attr_dev)) { |
1258 | 1483 | err = PTR_ERR(priv->fw_attr_dev); |
1259 | 1484 | goto err_free_ida; |
@@ -1345,6 +1570,7 @@ static int lwmi_om_master_bind(struct device *dev) |
1345 | 1570 | return -ENODEV; |
1346 | 1571 |
|
1347 | 1572 | lwmi_om_fan_info_collect_cd00(priv); |
| 1573 | + lwmi_om_ps_ext_init(priv); |
1348 | 1574 |
|
1349 | 1575 | return lwmi_om_fw_attr_add(priv); |
1350 | 1576 | } |
|
0 commit comments