Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'for-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply

Pull power supply and reset updates from Sebastian Reichel:
"Power-supply drivers:
- S2MU005: new battery fuel gauge driver
- macsmc-power: new driver for Apple Silicon
- qcom_battmgr: Add support for Glymur and Kaanapali
- max17042: add support for max77759
- qcom_smbx: allow disabling charging
- bd71828: add input current limit support
- multiple drivers: use new device managed workqueue allocation
function
- misc small cleanups and fixes

Reset core:
- Expose sysfs for registered reboot_modes

Reset drivers
- misc small cleanups and fixes"

* tag 'for-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (36 commits)
power: supply: qcom_smbx: allow disabling charging
power: reset: drop unneeded dependencies on OF_GPIO
power: supply: bd71828: add input current limit property
dt-bindings: power: reset: cortina,gemini-power-controller: convert to DT schema
power: supply: add support for S2MU005 battery fuel gauge device
dt-bindings: power: supply: document Samsung S2MU005 battery fuel gauge
power: reset: reboot-mode: fix -Wformat-security warning
power: supply: ipaq_micro: Simplify with devm
power: supply: mt6370: Simplify with devm_alloc_ordered_workqueue()
power: supply: max77705: Free allocated workqueue and fix removal order
power: supply: max77705: Drop duplicated IRQ error message
power: supply: cw2015: Free allocated workqueue
power: reset: keystone: Use register_sys_off_handler(SYS_OFF_MODE_RESTART)
power: supply: twl4030_madc: Drop unused header includes
power: supply: bq24190: Avoid rescheduling after cancelling work
power: supply: axp288_charger: Simplify returns of dev_err_probe()
power: supply: axp288_charger: Do not cancel work before initializing it
power: supply: cpcap-battery: pass static battery cell data from device tree
dt-bindings: power: supply: cpcap-battery: document monitored-battery property
power: supply: qcom_battmgr: Add support for Glymur and Kaanapali
...

+1785 -181
+36
Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes
··· 1 + What: /sys/class/reboot-mode/<driver>/reboot_modes 2 + Date: March 2026(TBD) 3 + KernelVersion: TBD 4 + Contact: linux-pm@vger.kernel.org 5 + Description: 6 + This interface exposes the reboot-mode arguments 7 + registered with the reboot-mode framework. It is 8 + a read-only interface and provides a space 9 + separated list of reboot-mode arguments supported 10 + on the current platform. 11 + Example: 12 + recovery fastboot bootloader 13 + 14 + The exact sysfs path may vary depending on the 15 + name of the driver that registers the arguments. 16 + Example: 17 + /sys/class/reboot-mode/nvmem-reboot-mode/reboot_modes 18 + /sys/class/reboot-mode/syscon-reboot-mode/reboot_modes 19 + /sys/class/reboot-mode/qcom-pon/reboot_modes 20 + 21 + The supported arguments can be used by userspace to 22 + invoke device reset using the standard reboot() system 23 + call interface, with the "argument" as string to "*arg" 24 + parameter along with LINUX_REBOOT_CMD_RESTART2. 25 + 26 + A driver can expose the supported arguments by 27 + registering them with the reboot-mode framework 28 + using the property names that follow the 29 + mode-<argument> format. 30 + Example: 31 + mode-bootloader, mode-recovery. 32 + 33 + This attribute is useful for scripts or initramfs 34 + logic that need to programmatically determine 35 + which reboot-mode arguments are valid before 36 + triggering a reboot.
+42
Documentation/devicetree/bindings/power/reset/cortina,gemini-power-controller.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/power/reset/cortina,gemini-power-controller.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Cortina Systems Gemini Poweroff Controller 8 + 9 + maintainers: 10 + - Linus Walleij <linusw@kernel.org> 11 + 12 + description: | 13 + The Gemini power controller is a dedicated IP block in the Cortina Gemini SoC that 14 + controls system power-down operations. 15 + 16 + properties: 17 + compatible: 18 + const: cortina,gemini-power-controller 19 + 20 + reg: 21 + maxItems: 1 22 + 23 + interrupts: 24 + maxItems: 1 25 + 26 + required: 27 + - compatible 28 + - reg 29 + - interrupts 30 + 31 + additionalProperties: false 32 + 33 + examples: 34 + - | 35 + #include <dt-bindings/interrupt-controller/irq.h> 36 + 37 + poweroff@4b000000 { 38 + compatible = "cortina,gemini-power-controller"; 39 + reg = <0x4b000000 0x100>; 40 + interrupts = <26 IRQ_TYPE_EDGE_FALLING>; 41 + }; 42 + ...
-17
Documentation/devicetree/bindings/power/reset/gemini-poweroff.txt
··· 1 - * Device-Tree bindings for Cortina Systems Gemini Poweroff 2 - 3 - This is a special IP block in the Cortina Gemini SoC that only 4 - deals with different ways to power the system down. 5 - 6 - Required properties: 7 - - compatible: should be "cortina,gemini-power-controller" 8 - - reg: should contain the physical memory base and size 9 - - interrupts: should contain the power management interrupt 10 - 11 - Example: 12 - 13 - power-controller@4b000000 { 14 - compatible = "cortina,gemini-power-controller"; 15 - reg = <0x4b000000 0x100>; 16 - interrupts = <26 IRQ_TYPE_EDGE_FALLING>; 17 - };
+1
Documentation/devicetree/bindings/power/supply/cpcap-battery.yaml
··· 55 55 - const: chg_isense 56 56 - const: batti 57 57 58 + monitored-battery: true 58 59 power-supplies: true 59 60 60 61 required:
+14 -7
Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml
··· 20 20 - maxim,max17050 21 21 - maxim,max17055 22 22 - maxim,max77705-battery 23 + - maxim,max77759-fg 23 24 - maxim,max77849-battery 24 25 25 26 reg: ··· 28 27 29 28 interrupts: 30 29 maxItems: 1 31 - description: | 32 - The ALRT pin, an open-drain interrupt. 30 + description: 31 + The ALRT pin (or FG_INTB pin on MAX77759), an open-drain interrupt. 32 + 33 + shunt-resistor-micro-ohms: 34 + description: 35 + Resistance of rsns resistor in micro Ohms (datasheet-recommended value is 10000). 36 + Defining this property enables current-sense functionality. 33 37 34 38 maxim,rsns-microohm: 39 + deprecated: true 35 40 $ref: /schemas/types.yaml#/definitions/uint32 36 - description: | 41 + description: 37 42 Resistance of rsns resistor in micro Ohms (datasheet-recommended value is 10000). 38 43 Defining this property enables current-sense functionality. 39 44 40 45 maxim,cold-temp: 41 46 $ref: /schemas/types.yaml#/definitions/uint32 42 - description: | 47 + description: 43 48 Temperature threshold to report battery as cold (in tenths of degree Celsius). 44 49 Default is not to report cold events. 45 50 46 51 maxim,over-heat-temp: 47 52 $ref: /schemas/types.yaml#/definitions/uint32 48 - description: | 53 + description: 49 54 Temperature threshold to report battery as over heated (in tenths of degree Celsius). 50 55 Default is not to report over heating events. 51 56 52 57 maxim,dead-volt: 53 58 $ref: /schemas/types.yaml#/definitions/uint32 54 - description: | 59 + description: 55 60 Voltage threshold to report battery as dead (in mV). 56 61 Default is not to report dead battery events. 57 62 58 63 maxim,over-volt: 59 64 $ref: /schemas/types.yaml#/definitions/uint32 60 - description: | 65 + description: 61 66 Voltage threshold to report battery as over voltage (in mV). 62 67 Default is not to report over-voltage events. 63 68
+49
Documentation/devicetree/bindings/power/supply/samsung,s2mu005-fuel-gauge.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/power/supply/samsung,s2mu005-fuel-gauge.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Battery Fuel Gauge for Samsung S2M series PMICs 8 + 9 + maintainers: 10 + - Kaustabh Chakraborty <kauschluss@disroot.org> 11 + 12 + allOf: 13 + - $ref: power-supply.yaml# 14 + 15 + properties: 16 + compatible: 17 + enum: 18 + - samsung,s2mu005-fuel-gauge 19 + 20 + reg: 21 + maxItems: 1 22 + 23 + interrupts: 24 + maxItems: 1 25 + 26 + required: 27 + - compatible 28 + - reg 29 + 30 + unevaluatedProperties: false 31 + 32 + examples: 33 + - | 34 + #include <dt-bindings/interrupt-controller/irq.h> 35 + 36 + i2c { 37 + #address-cells = <1>; 38 + #size-cells = <0>; 39 + 40 + fuel-gauge@3b { 41 + compatible = "samsung,s2mu005-fuel-gauge"; 42 + reg = <0x3b>; 43 + 44 + interrupt-parent = <&gpa0>; 45 + interrupts = <3 IRQ_TYPE_EDGE_BOTH>; 46 + 47 + monitored-battery = <&battery>; 48 + }; 49 + };
+1
MAINTAINERS
··· 2547 2547 F: drivers/phy/apple/ 2548 2548 F: drivers/pinctrl/pinctrl-apple-gpio.c 2549 2549 F: drivers/power/reset/macsmc-reboot.c 2550 + F: drivers/power/supply/macsmc-power.c 2550 2551 F: drivers/pwm/pwm-apple.c 2551 2552 F: drivers/rtc/rtc-macsmc.c 2552 2553 F: drivers/soc/apple/*
+4 -4
drivers/power/reset/Kconfig
··· 97 97 98 98 config POWER_RESET_GPIO 99 99 bool "GPIO power-off driver" 100 - depends on OF_GPIO 100 + depends on OF 101 101 help 102 102 This driver supports turning off your board via a GPIO line. 103 103 If your board needs a GPIO high/low to power down, say Y and ··· 105 105 106 106 config POWER_RESET_GPIO_RESTART 107 107 bool "GPIO restart driver" 108 - depends on OF_GPIO 108 + depends on OF 109 109 help 110 110 This driver supports restarting your board via a GPIO line. 111 111 If your board needs a GPIO high/low to restart, say Y and ··· 181 181 182 182 config POWER_RESET_LTC2952 183 183 bool "LTC2952 PowerPath power-off driver" 184 - depends on OF_GPIO 184 + depends on OF 185 185 help 186 186 This driver supports an external powerdown trigger and board power 187 187 down via the LTC2952. Bindings are made in the device tree. ··· 198 198 199 199 config POWER_RESET_QNAP 200 200 bool "QNAP power-off driver" 201 - depends on OF_GPIO && PLAT_ORION 201 + depends on PLAT_ORION 202 202 help 203 203 This driver supports turning off QNAP NAS devices by sending 204 204 commands to the microcontroller which controls the main power.
+3 -8
drivers/power/reset/keystone-reset.c
··· 48 48 RSCTRL_KEY_MASK, RSCTRL_KEY); 49 49 } 50 50 51 - static int rsctrl_restart_handler(struct notifier_block *this, 52 - unsigned long mode, void *cmd) 51 + static int rsctrl_restart_handler(struct sys_off_data *data) 53 52 { 54 53 /* enable write access to RSTCTRL */ 55 54 rsctrl_enable_rspll_write(); ··· 59 60 60 61 return NOTIFY_DONE; 61 62 } 62 - 63 - static struct notifier_block rsctrl_restart_nb = { 64 - .notifier_call = rsctrl_restart_handler, 65 - .priority = 128, 66 - }; 67 63 68 64 static const struct of_device_id rsctrl_of_match[] = { 69 65 {.compatible = "ti,keystone-reset", }, ··· 134 140 return ret; 135 141 } 136 142 137 - ret = register_restart_handler(&rsctrl_restart_nb); 143 + ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART, 128, 144 + rsctrl_restart_handler, NULL); 138 145 if (ret) 139 146 dev_err(dev, "cannot register restart handler (err=%d)\n", ret); 140 147
+148 -3
drivers/power/reset/reboot-mode.c
··· 4 4 */ 5 5 6 6 #include <linux/device.h> 7 + #include <linux/err.h> 7 8 #include <linux/init.h> 8 9 #include <linux/kernel.h> 10 + #include <linux/list.h> 9 11 #include <linux/module.h> 10 12 #include <linux/of.h> 11 13 #include <linux/reboot.h> 12 14 #include <linux/reboot-mode.h> 15 + #include <linux/slab.h> 16 + #include <linux/string.h> 13 17 14 18 #define PREFIX "mode-" 15 19 ··· 21 17 const char *mode; 22 18 u32 magic; 23 19 struct list_head list; 20 + }; 21 + 22 + struct reboot_mode_sysfs_data { 23 + struct device *reboot_mode_device; 24 + struct list_head head; 25 + }; 26 + 27 + static inline void reboot_mode_release_list(struct reboot_mode_sysfs_data *priv) 28 + { 29 + struct mode_info *info; 30 + struct mode_info *next; 31 + 32 + list_for_each_entry_safe(info, next, &priv->head, list) { 33 + list_del(&info->list); 34 + kfree_const(info->mode); 35 + kfree(info); 36 + } 37 + } 38 + 39 + static ssize_t reboot_modes_show(struct device *dev, struct device_attribute *attr, char *buf) 40 + { 41 + struct reboot_mode_sysfs_data *priv; 42 + struct mode_info *sysfs_info; 43 + ssize_t size = 0; 44 + 45 + priv = dev_get_drvdata(dev); 46 + if (!priv) 47 + return -ENODATA; 48 + 49 + list_for_each_entry(sysfs_info, &priv->head, list) 50 + size += sysfs_emit_at(buf, size, "%s ", sysfs_info->mode); 51 + 52 + if (!size) 53 + return -ENODATA; 54 + 55 + return size + sysfs_emit_at(buf, size - 1, "\n"); 56 + } 57 + static DEVICE_ATTR_RO(reboot_modes); 58 + 59 + static struct attribute *reboot_mode_attrs[] = { 60 + &dev_attr_reboot_modes.attr, 61 + NULL, 62 + }; 63 + ATTRIBUTE_GROUPS(reboot_mode); 64 + 65 + static const struct class reboot_mode_class = { 66 + .name = "reboot-mode", 67 + .dev_groups = reboot_mode_groups, 24 68 }; 25 69 26 70 static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot, ··· 112 60 reboot->write(reboot, magic); 113 61 114 62 return NOTIFY_DONE; 63 + } 64 + 65 + static int reboot_mode_create_device(struct reboot_mode_driver *reboot) 66 + { 67 + struct reboot_mode_sysfs_data *priv; 68 + struct mode_info *sysfs_info; 69 + struct mode_info *info; 70 + int ret; 71 + 72 + priv = kzalloc_obj(*priv, GFP_KERNEL); 73 + if (!priv) 74 + return -ENOMEM; 75 + 76 + INIT_LIST_HEAD(&priv->head); 77 + 78 + list_for_each_entry(info, &reboot->head, list) { 79 + sysfs_info = kzalloc_obj(*sysfs_info, GFP_KERNEL); 80 + if (!sysfs_info) { 81 + ret = -ENOMEM; 82 + goto error; 83 + } 84 + 85 + sysfs_info->mode = kstrdup_const(info->mode, GFP_KERNEL); 86 + if (!sysfs_info->mode) { 87 + kfree(sysfs_info); 88 + ret = -ENOMEM; 89 + goto error; 90 + } 91 + 92 + list_add_tail(&sysfs_info->list, &priv->head); 93 + } 94 + 95 + priv->reboot_mode_device = device_create(&reboot_mode_class, NULL, 0, 96 + (void *)priv, "%s", 97 + reboot->dev->driver->name); 98 + if (IS_ERR(priv->reboot_mode_device)) { 99 + ret = PTR_ERR(priv->reboot_mode_device); 100 + goto error; 101 + } 102 + 103 + return 0; 104 + 105 + error: 106 + reboot_mode_release_list(priv); 107 + kfree(priv); 108 + return ret; 115 109 } 116 110 117 111 /** ··· 211 113 reboot->reboot_notifier.notifier_call = reboot_mode_notify; 212 114 register_reboot_notifier(&reboot->reboot_notifier); 213 115 116 + ret = reboot_mode_create_device(reboot); 117 + if (ret) 118 + goto error; 119 + 214 120 return 0; 215 121 216 122 error: 217 - list_for_each_entry(info, &reboot->head, list) 218 - kfree_const(info->mode); 219 - 123 + reboot_mode_unregister(reboot); 220 124 return ret; 221 125 } 222 126 EXPORT_SYMBOL_GPL(reboot_mode_register); 127 + 128 + static int reboot_mode_match_by_name(struct device *dev, const void *data) 129 + { 130 + const char *name = data; 131 + 132 + if (!dev || !data) 133 + return 0; 134 + 135 + return dev_name(dev) && strcmp(dev_name(dev), name) == 0; 136 + } 137 + 138 + static inline void reboot_mode_unregister_device(struct reboot_mode_driver *reboot) 139 + { 140 + struct reboot_mode_sysfs_data *priv; 141 + struct device *reboot_mode_device; 142 + 143 + reboot_mode_device = class_find_device(&reboot_mode_class, NULL, reboot->dev->driver->name, 144 + reboot_mode_match_by_name); 145 + 146 + if (!reboot_mode_device) 147 + return; 148 + 149 + priv = dev_get_drvdata(reboot_mode_device); 150 + device_unregister(reboot_mode_device); 151 + 152 + if (!priv) 153 + return; 154 + 155 + reboot_mode_release_list(priv); 156 + kfree(priv); 157 + } 223 158 224 159 /** 225 160 * reboot_mode_unregister - unregister a reboot mode driver ··· 263 132 struct mode_info *info; 264 133 265 134 unregister_reboot_notifier(&reboot->reboot_notifier); 135 + reboot_mode_unregister_device(reboot); 266 136 267 137 list_for_each_entry(info, &reboot->head, list) 268 138 kfree_const(info->mode); ··· 330 198 devm_reboot_mode_match, reboot)); 331 199 } 332 200 EXPORT_SYMBOL_GPL(devm_reboot_mode_unregister); 201 + 202 + static int __init reboot_mode_init(void) 203 + { 204 + return class_register(&reboot_mode_class); 205 + } 206 + 207 + static void __exit reboot_mode_exit(void) 208 + { 209 + class_unregister(&reboot_mode_class); 210 + } 211 + 212 + subsys_initcall(reboot_mode_init); 213 + module_exit(reboot_mode_exit); 333 214 334 215 MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>"); 335 216 MODULE_DESCRIPTION("System reboot mode core library");
+22
drivers/power/supply/Kconfig
··· 229 229 Say Y to enable support for Samsung SDI battery data. 230 230 These batteries are used in Samsung mobile phones. 231 231 232 + config BATTERY_S2MU005 233 + tristate "Samsung S2MU005 PMIC fuel gauge driver" 234 + depends on I2C 235 + select REGMAP_I2C 236 + help 237 + Say Y to enable support for the Samsung S2MU005 PMIC integrated 238 + fuel gauge, which works indepenently of the PMIC battery charger 239 + counterpart, and reports battery metrics. 240 + 241 + This driver, if built as a module, will be called s2mu005-fuel-gauge. 242 + 232 243 config BATTERY_COLLIE 233 244 tristate "Sharp SL-5500 (collie) battery" 234 245 depends on SA1100_COLLIE && MCP_UCB1200 ··· 1142 1131 It enables the monitoring of many battery parameters, including 1143 1132 the state of charge, temperature, cycle count, actual and design 1144 1133 capacity, etc. 1134 + 1135 + config MACSMC_POWER 1136 + tristate "Apple SMC Battery and Power Driver" 1137 + depends on MFD_MACSMC 1138 + help 1139 + This driver provides support for the battery and AC adapter on 1140 + Apple Silicon machines. It exposes battery telemetry (voltage, 1141 + current, health) and AC adapter status through the standard Linux 1142 + power supply framework. 1143 + 1144 + Say Y or M here if you have an Apple Silicon based Mac. 1145 1145 1146 1146 endif # POWER_SUPPLY
+2
drivers/power/supply/Makefile
··· 40 40 obj-$(CONFIG_BATTERY_QCOM_BATTMGR) += qcom_battmgr.o 41 41 obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o 42 42 obj-$(CONFIG_BATTERY_SAMSUNG_SDI) += samsung-sdi-battery.o 43 + obj-$(CONFIG_BATTERY_S2MU005) += s2mu005-battery.o 43 44 obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o 44 45 obj-$(CONFIG_BATTERY_INGENIC) += ingenic-battery.o 45 46 obj-$(CONFIG_BATTERY_INTEL_DC_TI) += intel_dc_ti_battery.o ··· 129 128 obj-$(CONFIG_BATTERY_UG3105) += ug3105_battery.o 130 129 obj-$(CONFIG_CHARGER_QCOM_SMB2) += qcom_smbx.o 131 130 obj-$(CONFIG_FUEL_GAUGE_MM8013) += mm8013.o 131 + obj-$(CONFIG_MACSMC_POWER) += macsmc-power.o
+30 -41
drivers/power/supply/axp288_charger.c
··· 10 10 #include <linux/acpi.h> 11 11 #include <linux/bitops.h> 12 12 #include <linux/module.h> 13 + #include <linux/devm-helpers.h> 13 14 #include <linux/device.h> 14 15 #include <linux/regmap.h> 15 16 #include <linux/workqueue.h> ··· 822 821 return 0; 823 822 } 824 823 825 - static void axp288_charger_cancel_work(void *data) 826 - { 827 - struct axp288_chrg_info *info = data; 828 - 829 - cancel_work_sync(&info->otg.work); 830 - cancel_work_sync(&info->cable.work); 831 - } 832 - 833 824 static int axp288_charger_probe(struct platform_device *pdev) 834 825 { 835 826 int ret, i, pirq; ··· 859 866 info->regmap_irqc = axp20x->regmap_irqc; 860 867 861 868 info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME); 862 - if (IS_ERR(info->cable.edev)) { 863 - dev_err_probe(dev, PTR_ERR(info->cable.edev), 864 - "extcon_get_extcon_dev(%s) failed\n", 865 - AXP288_EXTCON_DEV_NAME); 866 - return PTR_ERR(info->cable.edev); 867 - } 869 + if (IS_ERR(info->cable.edev)) 870 + return dev_err_probe(dev, PTR_ERR(info->cable.edev), 871 + "extcon_get_extcon_dev(%s) failed\n", 872 + AXP288_EXTCON_DEV_NAME); 868 873 869 874 /* 870 875 * On devices with broken ACPI GPIO event handlers there also is no ACPI ··· 876 885 877 886 if (extcon_name) { 878 887 info->otg.cable = extcon_get_extcon_dev(extcon_name); 879 - if (IS_ERR(info->otg.cable)) { 880 - dev_err_probe(dev, PTR_ERR(info->otg.cable), 881 - "extcon_get_extcon_dev(%s) failed\n", 882 - USB_HOST_EXTCON_NAME); 883 - return PTR_ERR(info->otg.cable); 884 - } 888 + if (IS_ERR(info->otg.cable)) 889 + return dev_err_probe(dev, PTR_ERR(info->otg.cable), 890 + "extcon_get_extcon_dev(%s) failed\n", 891 + USB_HOST_EXTCON_NAME); 892 + 885 893 dev_info(dev, "Using " USB_HOST_EXTCON_HID " extcon for usb-id\n"); 886 894 } 887 895 ··· 894 904 charger_cfg.drv_data = info; 895 905 info->psy_usb = devm_power_supply_register(dev, &axp288_charger_desc, 896 906 &charger_cfg); 897 - if (IS_ERR(info->psy_usb)) { 898 - ret = PTR_ERR(info->psy_usb); 899 - dev_err(dev, "failed to register power supply: %d\n", ret); 900 - return ret; 901 - } 907 + if (IS_ERR(info->psy_usb)) 908 + return dev_err_probe(dev, PTR_ERR(info->psy_usb), 909 + "failed to register power supply: %d\n", ret); 902 910 903 911 /* Cancel our work on cleanup, register this before the notifiers */ 904 - ret = devm_add_action(dev, axp288_charger_cancel_work, info); 912 + ret = devm_work_autocancel(dev, &info->cable.work, 913 + axp288_charger_extcon_evt_worker); 905 914 if (ret) 906 915 return ret; 907 916 908 917 /* Register for extcon notification */ 909 - INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker); 910 918 info->cable.nb.notifier_call = axp288_charger_handle_cable_evt; 911 919 ret = devm_extcon_register_notifier_all(dev, info->cable.edev, 912 920 &info->cable.nb); 913 - if (ret) { 914 - dev_err(dev, "failed to register cable extcon notifier\n"); 915 - return ret; 916 - } 921 + if (ret) 922 + return dev_err_probe(dev, ret, "failed to register cable extcon notifier\n"); 923 + 917 924 schedule_work(&info->cable.work); 918 925 926 + ret = devm_work_autocancel(dev, &info->otg.work, 927 + axp288_charger_otg_evt_worker); 928 + if (ret) 929 + return ret; 930 + 919 931 /* Register for OTG notification */ 920 - INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker); 921 932 info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt; 922 933 if (info->otg.cable) { 923 934 ret = devm_extcon_register_notifier(dev, info->otg.cable, 924 935 EXTCON_USB_HOST, &info->otg.id_nb); 925 - if (ret) { 926 - dev_err(dev, "failed to register EXTCON_USB_HOST notifier\n"); 927 - return ret; 928 - } 936 + if (ret) 937 + return dev_err_probe(dev, ret, 938 + "failed to register EXTCON_USB_HOST notifier\n"); 939 + 929 940 schedule_work(&info->otg.work); 930 941 } 931 942 ··· 945 954 ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i], 946 955 NULL, axp288_charger_irq_thread_handler, 947 956 IRQF_ONESHOT, info->pdev->name, info); 948 - if (ret) { 949 - dev_err(dev, "failed to request interrupt=%d\n", 950 - info->irq[i]); 951 - return ret; 952 - } 957 + if (ret) 958 + return dev_err_probe(dev, ret, "failed to request interrupt=%d\n", 959 + info->irq[i]); 953 960 } 954 961 955 962 return 0;
+62
drivers/power/supply/bd71828-power.c
··· 24 24 #define BD7182x_MASK_CONF_PON BIT(0) 25 25 #define BD71815_MASK_CONF_XSTB BIT(1) 26 26 #define BD7182x_MASK_BAT_STAT 0x3f 27 + #define BD7182x_MASK_ILIM 0x3f 27 28 #define BD7182x_MASK_DCIN_STAT 0x07 28 29 29 30 #define BD7182x_MASK_WDT_AUTO 0x40 ··· 49 48 unsigned int vbat_avg; 50 49 unsigned int ibat; 51 50 unsigned int ibat_avg; 51 + unsigned int ilim_stat; 52 52 unsigned int btemp_vth; 53 53 unsigned int chg_state; 54 54 unsigned int bat_temp; 55 + unsigned int dcin_set; 55 56 unsigned int dcin_stat; 56 57 unsigned int dcin_online_mask; 57 58 unsigned int dcin_collapse_limit; ··· 69 66 .vbat_avg = BD71828_REG_VBAT_U, 70 67 .ibat = BD71828_REG_IBAT_U, 71 68 .ibat_avg = BD71828_REG_IBAT_AVG_U, 69 + .ilim_stat = BD71828_REG_ILIM_STAT, 72 70 .btemp_vth = BD71828_REG_VM_BTMP_U, 73 71 .chg_state = BD71828_REG_CHG_STATE, 74 72 .bat_temp = BD71828_REG_BAT_TEMP, 73 + .dcin_set = BD71828_REG_DCIN_SET, 75 74 .dcin_stat = BD71828_REG_DCIN_STAT, 76 75 .dcin_online_mask = BD7182x_MASK_DCIN_DET, 77 76 .dcin_collapse_limit = BD71828_REG_DCIN_CLPS, ··· 446 441 struct bd71828_power *pwr = dev_get_drvdata(psy->dev.parent); 447 442 u32 vot; 448 443 u16 tmp; 444 + int t; 449 445 int online; 450 446 int ret; 451 447 ··· 466 460 /* 5 milli volt steps */ 467 461 val->intval = 5000 * vot; 468 462 break; 463 + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 464 + if (!pwr->regs->ilim_stat) 465 + return -ENODATA; 466 + 467 + ret = regmap_read(pwr->regmap, pwr->regs->ilim_stat, &t); 468 + if (ret) 469 + return ret; 470 + 471 + t++; 472 + val->intval = (t & BD7182x_MASK_ILIM) * 50000; 473 + if (val->intval > 2000000) 474 + val->intval = 2000000; 475 + 476 + break; 469 477 default: 470 478 return -EINVAL; 471 479 } 472 480 473 481 return 0; 482 + } 483 + 484 + static int bd71828_charger_set_property(struct power_supply *psy, 485 + enum power_supply_property psp, 486 + const union power_supply_propval *val) 487 + { 488 + struct bd71828_power *pwr = dev_get_drvdata(psy->dev.parent); 489 + 490 + switch (psp) { 491 + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 492 + if (val->intval > 2000000) 493 + return -EINVAL; 494 + 495 + if (val->intval < 50000) 496 + return -EINVAL; 497 + 498 + if (!pwr->regs->dcin_set) 499 + return -EINVAL; 500 + 501 + return regmap_update_bits(pwr->regmap, pwr->regs->dcin_set, 502 + BD7182x_MASK_ILIM, 503 + val->intval / 50000 - 1); 504 + break; 505 + default: 506 + return -EINVAL; 507 + } 508 + } 509 + 510 + static int bd71828_charger_property_is_writeable(struct power_supply *psy, 511 + enum power_supply_property psp) 512 + { 513 + struct bd71828_power *pwr = dev_get_drvdata(psy->dev.parent); 514 + 515 + switch (psp) { 516 + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 517 + return !!(pwr->regs->dcin_set); 518 + default: 519 + return false; 520 + } 474 521 } 475 522 476 523 static int bd71828_battery_get_property(struct power_supply *psy, ··· 630 571 631 572 /** @brief ac properties */ 632 573 static const enum power_supply_property bd71828_charger_props[] = { 574 + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, 633 575 POWER_SUPPLY_PROP_ONLINE, 634 576 POWER_SUPPLY_PROP_VOLTAGE_NOW, 635 577 }; ··· 660 600 .properties = bd71828_charger_props, 661 601 .num_properties = ARRAY_SIZE(bd71828_charger_props), 662 602 .get_property = bd71828_charger_get_property, 603 + .set_property = bd71828_charger_set_property, 604 + .property_is_writeable = bd71828_charger_property_is_writeable, 663 605 }; 664 606 665 607 static const struct power_supply_desc bd71828_bat_desc = {
+6 -3
drivers/power/supply/bq24190_charger.c
··· 9 9 #include <linux/module.h> 10 10 #include <linux/interrupt.h> 11 11 #include <linux/delay.h> 12 + #include <linux/devm-helpers.h> 12 13 #include <linux/pm_runtime.h> 13 14 #include <linux/power_supply.h> 14 15 #include <linux/power/bq24190_charger.h> ··· 2088 2087 bdi->charge_type = POWER_SUPPLY_CHARGE_TYPE_FAST; 2089 2088 bdi->f_reg = 0; 2090 2089 bdi->ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK; /* impossible state */ 2091 - INIT_DELAYED_WORK(&bdi->input_current_limit_work, 2092 - bq24190_input_current_limit_work); 2090 + 2091 + ret = devm_delayed_work_autocancel(dev, &bdi->input_current_limit_work, 2092 + bq24190_input_current_limit_work); 2093 + if (ret) 2094 + return ret; 2093 2095 2094 2096 i2c_set_clientdata(client, bdi); 2095 2097 ··· 2202 2198 struct bq24190_dev_info *bdi = i2c_get_clientdata(client); 2203 2199 int error; 2204 2200 2205 - cancel_delayed_work_sync(&bdi->input_current_limit_work); 2206 2201 error = pm_runtime_resume_and_get(bdi->dev); 2207 2202 if (error < 0) 2208 2203 dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", error);
+29 -2
drivers/power/supply/cpcap-battery.c
··· 387 387 * Safe values for any lipo battery likely to fit into a mapphone 388 388 * battery bay. 389 389 */ 390 - static const struct cpcap_battery_config cpcap_battery_unkown_data = { 390 + static const struct cpcap_battery_config cpcap_battery_unknown_data = { 391 391 .cd_factor = 0x3cc, 392 392 .info.technology = POWER_SUPPLY_TECHNOLOGY_LION, 393 393 .info.voltage_max_design = 4200000, ··· 402 402 return 1; 403 403 else 404 404 return 0; 405 + } 406 + 407 + static void cpcap_battery_update_battery_data(struct cpcap_battery_ddata *ddata) 408 + { 409 + struct power_supply_battery_info *info; 410 + 411 + if (power_supply_get_battery_info(ddata->psy, &info) < 0) 412 + return; 413 + 414 + if (info->technology > 0) 415 + ddata->config.info.technology = info->technology; 416 + 417 + if (info->voltage_max_design_uv > 0) 418 + ddata->config.info.voltage_max_design = info->voltage_max_design_uv; 419 + 420 + if (info->voltage_min_design_uv > 0) 421 + ddata->config.info.voltage_min_design = info->voltage_min_design_uv; 422 + 423 + if (info->charge_full_design_uah > 0) 424 + ddata->config.info.charge_full_design = info->charge_full_design_uah; 425 + 426 + if (info->constant_charge_voltage_max_uv > 0) 427 + ddata->config.bat.constant_charge_voltage_max_uv = 428 + info->constant_charge_voltage_max_uv; 405 429 } 406 430 407 431 static void cpcap_battery_detect_battery_type(struct cpcap_battery_ddata *ddata) ··· 453 429 ddata->config = cpcap_battery_bw8x_data; 454 430 break; 455 431 default: 456 - ddata->config = cpcap_battery_unkown_data; 432 + ddata->config = cpcap_battery_unknown_data; 457 433 } 434 + 435 + if (ddata->psy) 436 + cpcap_battery_update_battery_data(ddata); 458 437 } 459 438 460 439 /**
+2 -1
drivers/power/supply/cw2015_battery.c
··· 694 694 "No monitored battery, some properties will be missing\n"); 695 695 } 696 696 697 - cw_bat->battery_workqueue = create_singlethread_workqueue("rk_battery"); 697 + cw_bat->battery_workqueue = devm_alloc_ordered_workqueue(&client->dev, 698 + "rk_battery", 0); 698 699 if (!cw_bat->battery_workqueue) 699 700 return -ENOMEM; 700 701
+16 -34
drivers/power/supply/ipaq_micro_battery.c
··· 7 7 * Author : Linus Walleij <linus.walleij@linaro.org> 8 8 */ 9 9 10 + #include <linux/devm-helpers.h> 10 11 #include <linux/module.h> 11 12 #include <linux/init.h> 12 13 #include <linux/platform_device.h> ··· 233 232 return -ENOMEM; 234 233 235 234 mb->micro = dev_get_drvdata(pdev->dev.parent); 236 - mb->wq = alloc_workqueue("ipaq-battery-wq", 237 - WQ_MEM_RECLAIM | WQ_PERCPU, 0); 235 + mb->wq = devm_alloc_workqueue(&pdev->dev, "ipaq-battery-wq", 236 + WQ_MEM_RECLAIM | WQ_PERCPU, 0); 238 237 if (!mb->wq) 239 238 return -ENOMEM; 240 239 241 - INIT_DELAYED_WORK(&mb->update, micro_battery_work); 240 + ret = devm_delayed_work_autocancel(&pdev->dev, &mb->update, micro_battery_work); 241 + if (ret) 242 + return ret; 243 + 242 244 platform_set_drvdata(pdev, mb); 243 245 queue_delayed_work(mb->wq, &mb->update, 1); 244 246 245 - micro_batt_power = power_supply_register(&pdev->dev, 246 - &micro_batt_power_desc, NULL); 247 - if (IS_ERR(micro_batt_power)) { 248 - ret = PTR_ERR(micro_batt_power); 249 - goto batt_err; 250 - } 247 + micro_batt_power = devm_power_supply_register(&pdev->dev, 248 + &micro_batt_power_desc, 249 + NULL); 250 + if (IS_ERR(micro_batt_power)) 251 + return PTR_ERR(micro_batt_power); 251 252 252 - micro_ac_power = power_supply_register(&pdev->dev, 253 - &micro_ac_power_desc, NULL); 254 - if (IS_ERR(micro_ac_power)) { 255 - ret = PTR_ERR(micro_ac_power); 256 - goto ac_err; 257 - } 253 + micro_ac_power = devm_power_supply_register(&pdev->dev, 254 + &micro_ac_power_desc, NULL); 255 + if (IS_ERR(micro_ac_power)) 256 + return PTR_ERR(micro_ac_power); 258 257 259 258 dev_info(&pdev->dev, "iPAQ micro battery driver\n"); 260 259 return 0; 261 - 262 - ac_err: 263 - power_supply_unregister(micro_batt_power); 264 - batt_err: 265 - cancel_delayed_work_sync(&mb->update); 266 - destroy_workqueue(mb->wq); 267 - return ret; 268 - } 269 - 270 - static void micro_batt_remove(struct platform_device *pdev) 271 - 272 - { 273 - struct micro_battery *mb = platform_get_drvdata(pdev); 274 - 275 - power_supply_unregister(micro_ac_power); 276 - power_supply_unregister(micro_batt_power); 277 - cancel_delayed_work_sync(&mb->update); 278 - destroy_workqueue(mb->wq); 279 260 } 280 261 281 262 static int __maybe_unused micro_batt_suspend(struct device *dev) ··· 286 303 .pm = &micro_batt_dev_pm_ops, 287 304 }, 288 305 .probe = micro_batt_probe, 289 - .remove = micro_batt_remove, 290 306 }; 291 307 module_platform_driver(micro_batt_device_driver); 292 308
+855
drivers/power/supply/macsmc-power.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only OR MIT 2 + /* 3 + * Apple SMC Power/Battery Management Driver 4 + * 5 + * This driver exposes battery telemetry (voltage, current, temperature, health) 6 + * and AC adapter status provided by the Apple SMC (System Management Controller) 7 + * on Apple Silicon systems. 8 + * 9 + * Copyright The Asahi Linux Contributors 10 + */ 11 + 12 + #include <linux/ctype.h> 13 + #include <linux/delay.h> 14 + #include <linux/devm-helpers.h> 15 + #include <linux/limits.h> 16 + #include <linux/module.h> 17 + #include <linux/mfd/macsmc.h> 18 + #include <linux/notifier.h> 19 + #include <linux/of.h> 20 + #include <linux/platform_device.h> 21 + #include <linux/power_supply.h> 22 + #include <linux/reboot.h> 23 + #include <linux/workqueue.h> 24 + 25 + #define MAX_STRING_LENGTH 256 26 + 27 + /* 28 + * The SMC reports charge in mAh (Coulombs) but energy in mWh (Joules). 29 + * We lack a register for "Nominal Voltage" or "Energy Accumulator". 30 + * We use a fixed 3.8V/cell constant to approximate energy stats for userspace, 31 + * derived from empirical data across supported MacBook models. 32 + */ 33 + #define MACSMC_NOMINAL_CELL_VOLTAGE_MV 3800 34 + 35 + /* SMC Key Flags */ 36 + #define CHNC_BATTERY_FULL BIT(0) 37 + #define CHNC_NO_CHARGER BIT(7) 38 + #define CHNC_NOCHG_CH0C BIT(14) 39 + #define CHNC_NOCHG_CH0B_CH0K BIT(15) 40 + #define CHNC_BATTERY_FULL_2 BIT(18) 41 + #define CHNC_BMS_BUSY BIT(23) 42 + #define CHNC_CHLS_LIMIT BIT(24) 43 + #define CHNC_NOAC_CH0J BIT(53) 44 + #define CHNC_NOAC_CH0I BIT(54) 45 + 46 + #define CH0R_LOWER_FLAGS GENMASK(15, 0) 47 + #define CH0R_NOAC_CH0I BIT(0) 48 + #define CH0R_NOAC_DISCONNECTED BIT(4) 49 + #define CH0R_NOAC_CH0J BIT(5) 50 + #define CH0R_BMS_BUSY BIT(8) 51 + #define CH0R_NOAC_CH0K BIT(9) 52 + #define CH0R_NOAC_CHWA BIT(11) 53 + 54 + #define CH0X_CH0C BIT(0) 55 + #define CH0X_CH0B BIT(1) 56 + 57 + #define ACSt_CAN_BOOT_AP BIT(2) 58 + #define ACSt_CAN_BOOT_IBOOT BIT(1) 59 + 60 + #define CHWA_CHLS_FIXED_START_OFFSET 5 61 + #define CHLS_MIN_END_THRESHOLD 10 62 + #define CHLS_FORCE_DISCHARGE 0x100 63 + #define CHWA_FIXED_END_THRESHOLD 80 64 + #define CHWA_PROP_WRITE_THRESHOLD 95 65 + 66 + #define MACSMC_MAX_BATT_PROPS 50 67 + #define MACSMC_MAX_AC_PROPS 10 68 + 69 + struct macsmc_power { 70 + struct device *dev; 71 + struct apple_smc *smc; 72 + 73 + struct power_supply_desc ac_desc; 74 + struct power_supply_desc batt_desc; 75 + 76 + struct power_supply *batt; 77 + struct power_supply *ac; 78 + 79 + char model_name[MAX_STRING_LENGTH]; 80 + char serial_number[MAX_STRING_LENGTH]; 81 + char mfg_date[MAX_STRING_LENGTH]; 82 + 83 + /* Supported feature flags based on SMC key presence */ 84 + bool has_chwa; /* Charge limit (Modern firmware) */ 85 + bool has_chls; /* Charge limit (Older firmware) */ 86 + bool has_ch0i; /* Force discharge (Older firmware) */ 87 + bool has_ch0c; /* Inhibit charge (Older firmware) */ 88 + bool has_chte; /* Inhibit charge (Modern firmware) */ 89 + 90 + u8 num_cells; 91 + int nominal_voltage_mv; 92 + 93 + struct notifier_block nb; 94 + struct work_struct critical_work; 95 + bool emergency_shutdown_triggered; 96 + bool orderly_shutdown_triggered; 97 + }; 98 + 99 + static int macsmc_battery_get_status(struct macsmc_power *power) 100 + { 101 + u64 nocharge_flags; 102 + u32 nopower_flags; 103 + u16 ac_current; 104 + int charge_limit = 0; 105 + bool limited = false; 106 + bool flag; 107 + int ret; 108 + 109 + /* 110 + * B0AV (Voltage) is fundamental. If we can't read it, we assume the 111 + * battery is gone. CHCE (Hardware charger present) / CHCC (Hardware 112 + * charger capable) are fundamental status flags. 113 + * BSFC (System full charge) / CHSC (System charging) are fundamental 114 + * status flags. 115 + */ 116 + 117 + /* Check if power input is inhibited (e.g. BMS balancing cycle) */ 118 + ret = apple_smc_read_u32(power->smc, SMC_KEY(CH0R), &nopower_flags); 119 + if (!ret && (nopower_flags & CH0R_LOWER_FLAGS & ~CH0R_BMS_BUSY)) 120 + return POWER_SUPPLY_STATUS_DISCHARGING; 121 + 122 + /* Check if charger is present */ 123 + ret = apple_smc_read_flag(power->smc, SMC_KEY(CHCE), &flag); 124 + if (ret < 0) 125 + return ret; 126 + if (!flag) 127 + return POWER_SUPPLY_STATUS_DISCHARGING; 128 + 129 + /* Check if AC is charge capable */ 130 + ret = apple_smc_read_flag(power->smc, SMC_KEY(CHCC), &flag); 131 + if (ret < 0) 132 + return ret; 133 + if (!flag) 134 + return POWER_SUPPLY_STATUS_DISCHARGING; 135 + 136 + /* Check if AC input limit is too low */ 137 + ret = apple_smc_read_u16(power->smc, SMC_KEY(AC-i), &ac_current); 138 + if (!ret && ac_current < 100) 139 + return POWER_SUPPLY_STATUS_DISCHARGING; 140 + 141 + /* Check if battery is full */ 142 + ret = apple_smc_read_flag(power->smc, SMC_KEY(BSFC), &flag); 143 + if (ret < 0) 144 + return ret; 145 + if (flag) 146 + return POWER_SUPPLY_STATUS_FULL; 147 + 148 + /* Check for user-defined charge limits */ 149 + if (power->has_chls) { 150 + u16 vu16; 151 + 152 + ret = apple_smc_read_u16(power->smc, SMC_KEY(CHLS), &vu16); 153 + if (ret == 0 && (vu16 & 0xff) >= CHLS_MIN_END_THRESHOLD) 154 + charge_limit = (vu16 & 0xff) - CHWA_CHLS_FIXED_START_OFFSET; 155 + } else if (power->has_chwa) { 156 + ret = apple_smc_read_flag(power->smc, SMC_KEY(CHWA), &flag); 157 + if (ret == 0 && flag) 158 + charge_limit = CHWA_FIXED_END_THRESHOLD - CHWA_CHLS_FIXED_START_OFFSET; 159 + } 160 + 161 + if (charge_limit > 0) { 162 + u8 buic = 0; 163 + 164 + if (apple_smc_read_u8(power->smc, SMC_KEY(BUIC), &buic) >= 0 && 165 + buic >= charge_limit) 166 + limited = true; 167 + } 168 + 169 + /* Check charging inhibitors */ 170 + ret = apple_smc_read_u64(power->smc, SMC_KEY(CHNC), &nocharge_flags); 171 + if (!ret) { 172 + if (nocharge_flags & CHNC_BATTERY_FULL) 173 + return POWER_SUPPLY_STATUS_FULL; 174 + /* BMS busy shows up as inhibit, but we treat it as charging */ 175 + else if (nocharge_flags == CHNC_BMS_BUSY && !limited) 176 + return POWER_SUPPLY_STATUS_CHARGING; 177 + else if (nocharge_flags) 178 + return POWER_SUPPLY_STATUS_NOT_CHARGING; 179 + else 180 + return POWER_SUPPLY_STATUS_CHARGING; 181 + } 182 + 183 + /* Fallback: System charging flag */ 184 + ret = apple_smc_read_flag(power->smc, SMC_KEY(CHSC), &flag); 185 + if (ret < 0) 186 + return ret; 187 + if (!flag) 188 + return POWER_SUPPLY_STATUS_NOT_CHARGING; 189 + 190 + return POWER_SUPPLY_STATUS_CHARGING; 191 + } 192 + 193 + static int macsmc_battery_get_charge_behaviour(struct macsmc_power *power) 194 + { 195 + int ret; 196 + u8 val8; 197 + u8 chte_buf[4]; 198 + 199 + if (power->has_ch0i) { 200 + ret = apple_smc_read_u8(power->smc, SMC_KEY(CH0I), &val8); 201 + if (ret) 202 + return ret; 203 + if (val8 & CH0R_NOAC_CH0I) 204 + return POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE; 205 + } 206 + 207 + if (power->has_chte) { 208 + ret = apple_smc_read(power->smc, SMC_KEY(CHTE), chte_buf, 4); 209 + if (ret < 0) 210 + return ret; 211 + 212 + if (chte_buf[0] == 0x01) 213 + return POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE; 214 + } else if (power->has_ch0c) { 215 + ret = apple_smc_read_u8(power->smc, SMC_KEY(CH0C), &val8); 216 + if (ret) 217 + return ret; 218 + if (val8 & CH0X_CH0C) 219 + return POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE; 220 + } 221 + 222 + return POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; 223 + } 224 + 225 + static int macsmc_battery_set_charge_behaviour(struct macsmc_power *power, int val) 226 + { 227 + int ret; 228 + 229 + switch (val) { 230 + case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO: 231 + /* Reset all inhibitors to a known-good 'auto' state */ 232 + if (power->has_ch0i) { 233 + ret = apple_smc_write_u8(power->smc, SMC_KEY(CH0I), 0); 234 + if (ret) 235 + return ret; 236 + } 237 + 238 + if (power->has_chte) { 239 + ret = apple_smc_write_u32(power->smc, SMC_KEY(CHTE), 0); 240 + if (ret) 241 + return ret; 242 + } else if (power->has_ch0c) { 243 + ret = apple_smc_write_u8(power->smc, SMC_KEY(CH0C), 0); 244 + if (ret) 245 + return ret; 246 + } 247 + return 0; 248 + 249 + case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE: 250 + if (power->has_chte) 251 + return apple_smc_write_u32(power->smc, SMC_KEY(CHTE), 1); 252 + else if (power->has_ch0c) 253 + return apple_smc_write_u8(power->smc, SMC_KEY(CH0C), 1); 254 + else 255 + return -EOPNOTSUPP; 256 + 257 + case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE: 258 + if (!power->has_ch0i) 259 + return -EOPNOTSUPP; 260 + return apple_smc_write_u8(power->smc, SMC_KEY(CH0I), 1); 261 + 262 + default: 263 + return -EINVAL; 264 + } 265 + } 266 + 267 + static int macsmc_battery_get_date(const char *s, int *out) 268 + { 269 + if (!isdigit(s[0]) || !isdigit(s[1])) 270 + return -EOPNOTSUPP; 271 + 272 + *out = (s[0] - '0') * 10 + s[1] - '0'; 273 + return 0; 274 + } 275 + 276 + static int macsmc_battery_get_capacity_level(struct macsmc_power *power) 277 + { 278 + bool flag; 279 + u32 val; 280 + int ret; 281 + 282 + /* Check for emergency shutdown condition */ 283 + if (apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val) >= 0 && val) 284 + return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; 285 + 286 + /* Check AC status for whether we could boot in this state */ 287 + if (apple_smc_read_u32(power->smc, SMC_KEY(ACSt), &val) >= 0) { 288 + if (!(val & ACSt_CAN_BOOT_IBOOT)) 289 + return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; 290 + 291 + if (!(val & ACSt_CAN_BOOT_AP)) 292 + return POWER_SUPPLY_CAPACITY_LEVEL_LOW; 293 + } 294 + 295 + /* BSFC = Battery System Full Charge */ 296 + ret = apple_smc_read_flag(power->smc, SMC_KEY(BSFC), &flag); 297 + if (ret < 0) 298 + return POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; 299 + 300 + if (flag) 301 + return POWER_SUPPLY_CAPACITY_LEVEL_FULL; 302 + else 303 + return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; 304 + } 305 + 306 + static int macsmc_battery_get_property(struct power_supply *psy, 307 + enum power_supply_property psp, 308 + union power_supply_propval *val) 309 + { 310 + struct macsmc_power *power = power_supply_get_drvdata(psy); 311 + int ret = 0; 312 + u8 vu8; 313 + u16 vu16; 314 + s16 vs16; 315 + s32 vs32; 316 + s64 vs64; 317 + bool flag; 318 + 319 + switch (psp) { 320 + case POWER_SUPPLY_PROP_STATUS: 321 + val->intval = macsmc_battery_get_status(power); 322 + ret = val->intval < 0 ? val->intval : 0; 323 + break; 324 + case POWER_SUPPLY_PROP_PRESENT: 325 + val->intval = 1; 326 + break; 327 + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: 328 + val->intval = macsmc_battery_get_charge_behaviour(power); 329 + ret = val->intval < 0 ? val->intval : 0; 330 + break; 331 + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: 332 + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0TE), &vu16); 333 + val->intval = vu16 == 0xffff ? 0 : vu16 * 60; 334 + break; 335 + case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: 336 + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0TF), &vu16); 337 + val->intval = vu16 == 0xffff ? 0 : vu16 * 60; 338 + break; 339 + case POWER_SUPPLY_PROP_CAPACITY: 340 + ret = apple_smc_read_u8(power->smc, SMC_KEY(BUIC), &vu8); 341 + val->intval = vu8; 342 + break; 343 + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: 344 + val->intval = macsmc_battery_get_capacity_level(power); 345 + ret = val->intval < 0 ? val->intval : 0; 346 + break; 347 + case POWER_SUPPLY_PROP_VOLTAGE_NOW: 348 + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AV), &vu16); 349 + val->intval = vu16 * 1000; 350 + break; 351 + case POWER_SUPPLY_PROP_CURRENT_NOW: 352 + ret = apple_smc_read_s16(power->smc, SMC_KEY(B0AC), &vs16); 353 + val->intval = vs16 * 1000; 354 + break; 355 + case POWER_SUPPLY_PROP_POWER_NOW: 356 + ret = apple_smc_read_s32(power->smc, SMC_KEY(B0AP), &vs32); 357 + val->intval = vs32 * 1000; 358 + break; 359 + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 360 + ret = apple_smc_read_u16(power->smc, SMC_KEY(BITV), &vu16); 361 + val->intval = vu16 * 1000; 362 + break; 363 + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 364 + /* Calculate total max design voltage from per-cell maximum voltage */ 365 + ret = apple_smc_read_u16(power->smc, SMC_KEY(BVVN), &vu16); 366 + val->intval = vu16 * 1000 * power->num_cells; 367 + break; 368 + case POWER_SUPPLY_PROP_VOLTAGE_MIN: 369 + /* Lifetime min */ 370 + ret = apple_smc_read_s16(power->smc, SMC_KEY(BLPM), &vs16); 371 + val->intval = vs16 * 1000; 372 + break; 373 + case POWER_SUPPLY_PROP_VOLTAGE_MAX: 374 + /* Lifetime max */ 375 + ret = apple_smc_read_s16(power->smc, SMC_KEY(BLPX), &vs16); 376 + val->intval = vs16 * 1000; 377 + break; 378 + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: 379 + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RC), &vu16); 380 + val->intval = vu16 * 1000; 381 + break; 382 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 383 + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RI), &vu16); 384 + val->intval = vu16 * 1000; 385 + break; 386 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 387 + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RV), &vu16); 388 + val->intval = vu16 * 1000; 389 + break; 390 + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 391 + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0DC), &vu16); 392 + val->intval = vu16 * 1000; 393 + break; 394 + case POWER_SUPPLY_PROP_CHARGE_FULL: 395 + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0FC), &vu16); 396 + val->intval = vu16 * 1000; 397 + break; 398 + case POWER_SUPPLY_PROP_CHARGE_NOW: 399 + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RM), &vu16); 400 + /* B0RM is Big Endian, likely pass through from TI gas gauge */ 401 + val->intval = (s16)swab16(vu16) * 1000; 402 + break; 403 + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: 404 + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0DC), &vu16); 405 + val->intval = vu16 * power->nominal_voltage_mv; 406 + break; 407 + case POWER_SUPPLY_PROP_ENERGY_FULL: 408 + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0FC), &vu16); 409 + val->intval = vu16 * power->nominal_voltage_mv; 410 + break; 411 + case POWER_SUPPLY_PROP_ENERGY_NOW: 412 + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RM), &vu16); 413 + /* B0RM is Big Endian, likely pass through from TI gas gauge */ 414 + val->intval = (s16)swab16(vu16) * power->nominal_voltage_mv; 415 + break; 416 + case POWER_SUPPLY_PROP_TEMP: 417 + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AT), &vu16); 418 + val->intval = vu16 - 2732; /* Kelvin x10 to Celsius x10 */ 419 + break; 420 + case POWER_SUPPLY_PROP_CHARGE_COUNTER: 421 + ret = apple_smc_read_s64(power->smc, SMC_KEY(BAAC), &vs64); 422 + val->intval = vs64; 423 + break; 424 + case POWER_SUPPLY_PROP_CYCLE_COUNT: 425 + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0CT), &vu16); 426 + val->intval = vu16; 427 + break; 428 + case POWER_SUPPLY_PROP_SCOPE: 429 + val->intval = POWER_SUPPLY_SCOPE_SYSTEM; 430 + break; 431 + case POWER_SUPPLY_PROP_HEALTH: 432 + flag = false; 433 + ret = apple_smc_read_flag(power->smc, SMC_KEY(BBAD), &flag); 434 + val->intval = flag ? POWER_SUPPLY_HEALTH_DEAD : POWER_SUPPLY_HEALTH_GOOD; 435 + break; 436 + case POWER_SUPPLY_PROP_MODEL_NAME: 437 + val->strval = power->model_name; 438 + break; 439 + case POWER_SUPPLY_PROP_SERIAL_NUMBER: 440 + val->strval = power->serial_number; 441 + break; 442 + case POWER_SUPPLY_PROP_MANUFACTURE_YEAR: 443 + ret = macsmc_battery_get_date(&power->mfg_date[0], &val->intval); 444 + /* The SMC reports the manufacture year as an offset from 1992. */ 445 + val->intval += 1992; 446 + break; 447 + case POWER_SUPPLY_PROP_MANUFACTURE_MONTH: 448 + ret = macsmc_battery_get_date(&power->mfg_date[2], &val->intval); 449 + break; 450 + case POWER_SUPPLY_PROP_MANUFACTURE_DAY: 451 + ret = macsmc_battery_get_date(&power->mfg_date[4], &val->intval); 452 + break; 453 + default: 454 + return -EINVAL; 455 + } 456 + 457 + return ret; 458 + } 459 + 460 + static int macsmc_battery_set_property(struct power_supply *psy, 461 + enum power_supply_property psp, 462 + const union power_supply_propval *val) 463 + { 464 + struct macsmc_power *power = power_supply_get_drvdata(psy); 465 + 466 + switch (psp) { 467 + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: 468 + return macsmc_battery_set_charge_behaviour(power, val->intval); 469 + default: 470 + return -EINVAL; 471 + } 472 + } 473 + 474 + static int macsmc_battery_property_is_writeable(struct power_supply *psy, 475 + enum power_supply_property psp) 476 + { 477 + switch (psp) { 478 + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: 479 + return true; 480 + default: 481 + return false; 482 + } 483 + } 484 + 485 + static const struct power_supply_desc macsmc_battery_desc_template = { 486 + .name = "macsmc-battery", 487 + .type = POWER_SUPPLY_TYPE_BATTERY, 488 + .get_property = macsmc_battery_get_property, 489 + .set_property = macsmc_battery_set_property, 490 + .property_is_writeable = macsmc_battery_property_is_writeable, 491 + }; 492 + 493 + static int macsmc_ac_get_property(struct power_supply *psy, 494 + enum power_supply_property psp, 495 + union power_supply_propval *val) 496 + { 497 + struct macsmc_power *power = power_supply_get_drvdata(psy); 498 + int ret = 0; 499 + u16 vu16; 500 + u32 vu32; 501 + 502 + switch (psp) { 503 + case POWER_SUPPLY_PROP_ONLINE: 504 + ret = apple_smc_read_u32(power->smc, SMC_KEY(CHIS), &vu32); 505 + val->intval = !!vu32; 506 + break; 507 + case POWER_SUPPLY_PROP_VOLTAGE_NOW: 508 + ret = apple_smc_read_u16(power->smc, SMC_KEY(AC-n), &vu16); 509 + val->intval = vu16 * 1000; 510 + break; 511 + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 512 + ret = apple_smc_read_u16(power->smc, SMC_KEY(AC-i), &vu16); 513 + val->intval = vu16 * 1000; 514 + break; 515 + case POWER_SUPPLY_PROP_INPUT_POWER_LIMIT: 516 + ret = apple_smc_read_u32(power->smc, SMC_KEY(ACPW), &vu32); 517 + val->intval = vu32 * 1000; 518 + break; 519 + default: 520 + return -EINVAL; 521 + } 522 + 523 + return ret; 524 + } 525 + 526 + static const struct power_supply_desc macsmc_ac_desc_template = { 527 + .name = "macsmc-ac", 528 + .type = POWER_SUPPLY_TYPE_MAINS, 529 + .get_property = macsmc_ac_get_property, 530 + }; 531 + 532 + static void macsmc_power_critical_work(struct work_struct *wrk) 533 + { 534 + struct macsmc_power *power = container_of(wrk, struct macsmc_power, critical_work); 535 + u16 bitv, b0av; 536 + u32 bcf0; 537 + 538 + if (!power->batt) 539 + return; 540 + 541 + /* 542 + * Avoid duplicate atempts at emergency shutdown 543 + */ 544 + if (power->emergency_shutdown_triggered || system_state > SYSTEM_RUNNING) 545 + return; 546 + 547 + /* 548 + * EMERGENCY: Check voltage vs design minimum. 549 + * If we are below BITV, the battery is physically exhausted. 550 + * We must shut down NOW to protect the filesystem. 551 + */ 552 + if (apple_smc_read_u16(power->smc, SMC_KEY(BITV), &bitv) >= 0 && 553 + apple_smc_read_u16(power->smc, SMC_KEY(B0AV), &b0av) >= 0 && 554 + b0av < bitv) { 555 + power->emergency_shutdown_triggered = true; 556 + dev_emerg(power->dev, 557 + "Battery voltage (%d mV) below design minimum (%d mV)! Emergency shutdown.\n", 558 + b0av, bitv); 559 + 560 + /* 561 + * Shutdown is now imminent. Kick userspace again and give it some 562 + * brief time to (hopefully) flush what's needed, before forcing. 563 + */ 564 + hw_protection_trigger("Battery voltage below design minimum", 1500); 565 + } 566 + 567 + /* 568 + * Avoid duplicate attempts at orderly shutdown. 569 + * Voltage check is above this as we may want to 570 + * "upgrade" an orderly shutdown to a critical power 571 + * off if voltage drops. 572 + */ 573 + if (power->orderly_shutdown_triggered || system_state > SYSTEM_RUNNING) 574 + return; 575 + 576 + /* 577 + * Check if SMC flagged the battery as empty. 578 + * We trigger a graceful shutdown to let the OS save data. 579 + */ 580 + if (apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &bcf0) == 0 && bcf0 != 0) { 581 + power->orderly_shutdown_triggered = true; 582 + dev_crit(power->dev, "Battery critical (empty flag set). Triggering orderly shutdown.\n"); 583 + orderly_poweroff(true); 584 + } 585 + } 586 + 587 + static int macsmc_power_event(struct notifier_block *nb, unsigned long event, void *data) 588 + { 589 + struct macsmc_power *power = container_of(nb, struct macsmc_power, nb); 590 + 591 + /* 592 + * SMC Event IDs are correlated to physical events (e.g. charger 593 + * connect/disconnect) but the exact meaning of each ID is predicted. 594 + * 0x71... indicates power/battery events. 595 + */ 596 + if ((event & 0xffffff00) == 0x71010100 || /* Charger status change */ 597 + (event & 0xffff0000) == 0x71060000 || /* Port charge state change */ 598 + (event & 0xffff0000) == 0x71130000) { /* Connector insert/remove event */ 599 + if (power->batt) 600 + power_supply_changed(power->batt); 601 + if (power->ac) 602 + power_supply_changed(power->ac); 603 + return NOTIFY_OK; 604 + } else if (event == 0x71020000) { 605 + /* Critical battery warning */ 606 + if (power->batt) 607 + schedule_work(&power->critical_work); 608 + return NOTIFY_OK; 609 + } 610 + 611 + return NOTIFY_DONE; 612 + } 613 + 614 + static int macsmc_power_probe(struct platform_device *pdev) 615 + { 616 + struct device *dev = &pdev->dev; 617 + struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); 618 + struct power_supply_config psy_cfg = {}; 619 + struct macsmc_power *power; 620 + bool has_battery = false; 621 + bool has_ac_adapter = false; 622 + int ret = -ENODEV; 623 + bool flag; 624 + u16 vu16; 625 + u32 val32; 626 + enum power_supply_property *props; 627 + size_t nprops; 628 + 629 + if (!smc) 630 + return -ENODEV; 631 + 632 + power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL); 633 + if (!power) 634 + return -ENOMEM; 635 + 636 + power->dev = dev; 637 + power->smc = smc; 638 + dev_set_drvdata(dev, power); 639 + 640 + INIT_WORK(&power->critical_work, macsmc_power_critical_work); 641 + ret = devm_work_autocancel(dev, &power->critical_work, macsmc_power_critical_work); 642 + if (ret) 643 + return ret; 644 + 645 + /* 646 + * Check for battery presence. 647 + * B0AV is a fundamental key. 648 + */ 649 + if (apple_smc_read_u16(power->smc, SMC_KEY(B0AV), &vu16) == 0 && 650 + macsmc_battery_get_status(power) > POWER_SUPPLY_STATUS_UNKNOWN) 651 + has_battery = true; 652 + 653 + /* 654 + * Check for AC adapter presence. 655 + * CHIS is a fundamental key. 656 + */ 657 + if (apple_smc_key_exists(smc, SMC_KEY(CHIS))) 658 + has_ac_adapter = true; 659 + 660 + if (!has_battery && !has_ac_adapter) 661 + return -ENODEV; 662 + 663 + if (has_battery) { 664 + power->batt_desc = macsmc_battery_desc_template; 665 + props = devm_kcalloc(dev, MACSMC_MAX_BATT_PROPS, 666 + sizeof(enum power_supply_property), 667 + GFP_KERNEL); 668 + if (!props) 669 + return -ENOMEM; 670 + 671 + nprops = 0; 672 + 673 + /* Fundamental properties */ 674 + props[nprops++] = POWER_SUPPLY_PROP_STATUS; 675 + props[nprops++] = POWER_SUPPLY_PROP_PRESENT; 676 + props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_NOW; 677 + props[nprops++] = POWER_SUPPLY_PROP_CURRENT_NOW; 678 + props[nprops++] = POWER_SUPPLY_PROP_POWER_NOW; 679 + props[nprops++] = POWER_SUPPLY_PROP_CAPACITY; 680 + props[nprops++] = POWER_SUPPLY_PROP_CAPACITY_LEVEL; 681 + props[nprops++] = POWER_SUPPLY_PROP_TEMP; 682 + props[nprops++] = POWER_SUPPLY_PROP_CYCLE_COUNT; 683 + props[nprops++] = POWER_SUPPLY_PROP_HEALTH; 684 + props[nprops++] = POWER_SUPPLY_PROP_SCOPE; 685 + props[nprops++] = POWER_SUPPLY_PROP_MODEL_NAME; 686 + props[nprops++] = POWER_SUPPLY_PROP_SERIAL_NUMBER; 687 + props[nprops++] = POWER_SUPPLY_PROP_MANUFACTURE_YEAR; 688 + props[nprops++] = POWER_SUPPLY_PROP_MANUFACTURE_MONTH; 689 + props[nprops++] = POWER_SUPPLY_PROP_MANUFACTURE_DAY; 690 + 691 + /* Extended properties usually present */ 692 + props[nprops++] = POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW; 693 + props[nprops++] = POWER_SUPPLY_PROP_TIME_TO_FULL_NOW; 694 + props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN; 695 + props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN; 696 + props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MIN; 697 + props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MAX; 698 + props[nprops++] = POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT; 699 + props[nprops++] = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX; 700 + props[nprops++] = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE; 701 + props[nprops++] = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN; 702 + props[nprops++] = POWER_SUPPLY_PROP_CHARGE_FULL; 703 + props[nprops++] = POWER_SUPPLY_PROP_CHARGE_NOW; 704 + props[nprops++] = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; 705 + props[nprops++] = POWER_SUPPLY_PROP_ENERGY_FULL; 706 + props[nprops++] = POWER_SUPPLY_PROP_ENERGY_NOW; 707 + props[nprops++] = POWER_SUPPLY_PROP_CHARGE_COUNTER; 708 + 709 + /* Detect features based on key availability */ 710 + if (apple_smc_key_exists(smc, SMC_KEY(CHTE))) 711 + power->has_chte = true; 712 + if (apple_smc_key_exists(smc, SMC_KEY(CH0C))) 713 + power->has_ch0c = true; 714 + if (apple_smc_key_exists(smc, SMC_KEY(CH0I))) 715 + power->has_ch0i = true; 716 + 717 + /* Reset "Optimised Battery Charging" flags to default state */ 718 + if (power->has_chte) 719 + apple_smc_write_u32(smc, SMC_KEY(CHTE), 0); 720 + else if (power->has_ch0c) 721 + apple_smc_write_u8(smc, SMC_KEY(CH0C), 0); 722 + 723 + if (power->has_ch0i) 724 + apple_smc_write_u8(smc, SMC_KEY(CH0I), 0); 725 + 726 + apple_smc_write_u8(smc, SMC_KEY(CH0K), 0); 727 + apple_smc_write_u8(smc, SMC_KEY(CH0B), 0); 728 + 729 + /* Configure charge behaviour if supported */ 730 + if (power->has_ch0i || power->has_ch0c || power->has_chte) { 731 + props[nprops++] = POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR; 732 + 733 + power->batt_desc.charge_behaviours = 734 + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO); 735 + 736 + if (power->has_ch0i) 737 + power->batt_desc.charge_behaviours |= 738 + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE); 739 + 740 + if (power->has_chte || power->has_ch0c) 741 + power->batt_desc.charge_behaviours |= 742 + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE); 743 + } 744 + 745 + /* Detect charge limit method (CHWA vs CHLS) */ 746 + if (apple_smc_read_flag(power->smc, SMC_KEY(CHWA), &flag) == 0) 747 + power->has_chwa = true; 748 + else if (apple_smc_read_u16(power->smc, SMC_KEY(CHLS), &vu16) >= 0) 749 + power->has_chls = true; 750 + 751 + if (nprops > MACSMC_MAX_BATT_PROPS) 752 + return -ENOMEM; 753 + 754 + power->batt_desc.properties = props; 755 + power->batt_desc.num_properties = nprops; 756 + 757 + /* Fetch identity strings */ 758 + apple_smc_read(smc, SMC_KEY(BMDN), power->model_name, 759 + sizeof(power->model_name) - 1); 760 + apple_smc_read(smc, SMC_KEY(BMSN), power->serial_number, 761 + sizeof(power->serial_number) - 1); 762 + apple_smc_read(smc, SMC_KEY(BMDT), power->mfg_date, 763 + sizeof(power->mfg_date) - 1); 764 + 765 + apple_smc_read_u8(power->smc, SMC_KEY(BNCB), &power->num_cells); 766 + power->nominal_voltage_mv = MACSMC_NOMINAL_CELL_VOLTAGE_MV * power->num_cells; 767 + 768 + /* Enable critical shutdown notifications by reading status once */ 769 + apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val32); 770 + 771 + psy_cfg.drv_data = power; 772 + power->batt = devm_power_supply_register(dev, &power->batt_desc, &psy_cfg); 773 + if (IS_ERR(power->batt)) { 774 + dev_err_probe(dev, PTR_ERR(power->batt), 775 + "Failed to register battery\n"); 776 + /* Don't return failure yet; try AC registration first */ 777 + power->batt = NULL; 778 + } 779 + } 780 + 781 + if (has_ac_adapter) { 782 + power->ac_desc = macsmc_ac_desc_template; 783 + props = devm_kcalloc(dev, MACSMC_MAX_AC_PROPS, 784 + sizeof(enum power_supply_property), 785 + GFP_KERNEL); 786 + if (!props) 787 + return -ENOMEM; 788 + 789 + nprops = 0; 790 + 791 + /* Online status is fundamental */ 792 + props[nprops++] = POWER_SUPPLY_PROP_ONLINE; 793 + 794 + /* Input power limits are usually available */ 795 + if (apple_smc_key_exists(power->smc, SMC_KEY(ACPW))) 796 + props[nprops++] = POWER_SUPPLY_PROP_INPUT_POWER_LIMIT; 797 + 798 + /* macOS 15.4+ firmware dropped legacy AC keys (AC-n, AC-i) */ 799 + if (apple_smc_read_u16(power->smc, SMC_KEY(AC-n), &vu16) >= 0) { 800 + props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_NOW; 801 + props[nprops++] = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT; 802 + } 803 + 804 + if (nprops > MACSMC_MAX_AC_PROPS) 805 + return -ENOMEM; 806 + 807 + power->ac_desc.properties = props; 808 + power->ac_desc.num_properties = nprops; 809 + 810 + psy_cfg.drv_data = power; 811 + power->ac = devm_power_supply_register(dev, &power->ac_desc, &psy_cfg); 812 + if (IS_ERR(power->ac)) { 813 + dev_err_probe(dev, PTR_ERR(power->ac), 814 + "Failed to register AC adapter\n"); 815 + power->ac = NULL; 816 + } 817 + } 818 + 819 + /* Final check: did we register anything? */ 820 + if (!power->batt && !power->ac) 821 + return -ENODEV; 822 + 823 + power->nb.notifier_call = macsmc_power_event; 824 + blocking_notifier_chain_register(&smc->event_handlers, &power->nb); 825 + 826 + return 0; 827 + } 828 + 829 + static void macsmc_power_remove(struct platform_device *pdev) 830 + { 831 + struct macsmc_power *power = dev_get_drvdata(&pdev->dev); 832 + 833 + blocking_notifier_chain_unregister(&power->smc->event_handlers, &power->nb); 834 + } 835 + 836 + static const struct platform_device_id macsmc_power_id[] = { 837 + { "macsmc-power" }, 838 + { /* sentinel */ } 839 + }; 840 + MODULE_DEVICE_TABLE(platform, macsmc_power_id); 841 + 842 + static struct platform_driver macsmc_power_driver = { 843 + .driver = { 844 + .name = "macsmc-power", 845 + }, 846 + .id_table = macsmc_power_id, 847 + .probe = macsmc_power_probe, 848 + .remove = macsmc_power_remove, 849 + }; 850 + module_platform_driver(macsmc_power_driver); 851 + 852 + MODULE_LICENSE("Dual MIT/GPL"); 853 + MODULE_DESCRIPTION("Apple SMC battery and power management driver"); 854 + MODULE_AUTHOR("Hector Martin <marcan@marcan.st>"); 855 + MODULE_AUTHOR("Michael Reeves <michael.reeves077@gmail.com>");
+111 -19
drivers/power/supply/max17042_battery.c
··· 61 61 struct work_struct work; 62 62 int init_complete; 63 63 int irq; 64 + int task_period; 64 65 }; 65 66 66 67 static enum power_supply_property max17042_battery_props[] = { ··· 89 88 POWER_SUPPLY_PROP_HEALTH, 90 89 POWER_SUPPLY_PROP_SCOPE, 91 90 POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 91 + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 92 92 // these two have to be at the end on the list 93 93 POWER_SUPPLY_PROP_CURRENT_NOW, 94 94 POWER_SUPPLY_PROP_CURRENT_AVG, ··· 133 131 * FullCAP to match RepCap when it detects end of charging. 134 132 * 135 133 * When this cycle the battery gets charged to a higher (calculated) 136 - * capacity then the previous cycle then FullCAP will get updated 134 + * capacity than the previous cycle then FullCAP will get updated 137 135 * continuously once end-of-charge detection kicks in, so allow the 138 136 * 2 to differ a bit. 139 137 */ ··· 203 201 goto out; 204 202 } 205 203 206 - if (vbatt > chip->pdata->vmax + MAX17042_VMAX_TOLERANCE) { 204 + if (vbatt > size_add(chip->pdata->vmax, MAX17042_VMAX_TOLERANCE)) { 207 205 *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 208 206 goto out; 209 207 } ··· 333 331 return ret; 334 332 335 333 data64 = data * 5000000ll; 334 + data64 *= chip->task_period; 335 + do_div(data64, MAX17042_DEFAULT_TASK_PERIOD); 336 336 do_div(data64, chip->pdata->r_sns); 337 337 val->intval = data64; 338 338 break; ··· 344 340 return ret; 345 341 346 342 data64 = data * 5000000ll; 343 + data64 *= chip->task_period; 344 + do_div(data64, MAX17042_DEFAULT_TASK_PERIOD); 347 345 do_div(data64, chip->pdata->r_sns); 348 346 val->intval = data64; 349 347 break; ··· 355 349 return ret; 356 350 357 351 data64 = data * 5000000ll; 352 + data64 *= chip->task_period; 353 + do_div(data64, MAX17042_DEFAULT_TASK_PERIOD); 358 354 do_div(data64, chip->pdata->r_sns); 359 355 val->intval = data64; 360 356 break; ··· 366 358 return ret; 367 359 368 360 data64 = sign_extend64(data, 15) * 5000000ll; 361 + data64 *= chip->task_period; 362 + data64 = div_s64(data64, MAX17042_DEFAULT_TASK_PERIOD); 369 363 val->intval = div_s64(data64, chip->pdata->r_sns); 370 364 break; 371 365 case POWER_SUPPLY_PROP_TEMP: ··· 439 429 ret = regmap_read(map, MAX17042_TTE, &data); 440 430 if (ret < 0) 441 431 return ret; 432 + 433 + /* when charging, the value is not meaningful */ 434 + if (data == U16_MAX) 435 + return -ENODATA; 436 + 437 + val->intval = data * 5625 / 1000; 438 + break; 439 + case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: 440 + if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055 && 441 + chip->chip_type != MAXIM_DEVICE_TYPE_MAX77759) 442 + return -EINVAL; 443 + 444 + ret = regmap_read(map, MAX17055_TTF, &data); 445 + if (ret < 0) 446 + return ret; 447 + 448 + /* when discharging, the value is not meaningful */ 449 + if (data == U16_MAX) 450 + return -ENODATA; 442 451 443 452 val->intval = data * 5625 / 1000; 444 453 break; ··· 675 646 regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg); 676 647 if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047 || 677 648 chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050 || 678 - chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) 649 + chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055 || 650 + chip->chip_type == MAXIM_DEVICE_TYPE_MAX77759) 679 651 regmap_write(map, MAX17047_FullSOCThr, 680 652 config->full_soc_thresh); 681 653 } ··· 813 783 814 784 if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) || 815 785 (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) || 816 - (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050)) { 786 + (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050) || 787 + (chip->chip_type == MAXIM_DEVICE_TYPE_MAX77759)) { 817 788 max17042_override_por(map, MAX17042_IAvg_empty, config->iavg_empty); 818 789 max17042_override_por(map, MAX17042_TempNom, config->temp_nom); 819 790 max17042_override_por(map, MAX17042_TempLim, config->temp_lim); ··· 823 792 824 793 if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) || 825 794 (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050) || 826 - (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)) { 795 + (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) || 796 + (chip->chip_type == MAXIM_DEVICE_TYPE_MAX77759)) { 827 797 max17042_override_por(map, MAX17047_V_empty, config->vempty); 828 798 } 829 799 } ··· 953 921 /* 954 922 * Require current sense resistor value to be specified for 955 923 * current-sense functionality to be enabled at all. 924 + * maxim,rsns-microohm is the property name used by older DTs and kept 925 + * for compatibility. 956 926 */ 957 - if (of_property_read_u32(np, "maxim,rsns-microohm", &prop) == 0) { 927 + if ((of_property_read_u32(np, "shunt-resistor-micro-ohms", 928 + &prop) == 0) || 929 + (of_property_read_u32(np, "maxim,rsns-microohm", &prop) == 0)) { 958 930 pdata->r_sns = prop; 959 931 pdata->enable_current_sense = true; 960 932 } ··· 1047 1011 .val_format_endian = REGMAP_ENDIAN_NATIVE, 1048 1012 }; 1049 1013 1014 + static const struct regmap_range max77759_fg_registers[] = { 1015 + regmap_reg_range(MAX17042_STATUS, MAX77759_MixAtFull), 1016 + regmap_reg_range(MAX17042_VFSOC0Enable, MAX17042_VFSOC0Enable), 1017 + regmap_reg_range(MAX17042_MLOCKReg1, MAX17042_MLOCKReg2), 1018 + regmap_reg_range(MAX17042_MODELChrTbl, MAX17055_TimerH), 1019 + regmap_reg_range(MAX77759_IIn, MAX77759_IIn), 1020 + regmap_reg_range(MAX17055_AtQResidual, MAX17055_AtAvCap), 1021 + regmap_reg_range(MAX17042_OCVInternal, MAX17042_OCVInternal), 1022 + regmap_reg_range(MAX17042_VFSOC, MAX17042_VFSOC), 1023 + }; 1024 + 1025 + static const struct regmap_range max77759_fg_ro_registers[] = { 1026 + regmap_reg_range(MAX17042_FSTAT, MAX17042_FSTAT), 1027 + regmap_reg_range(MAX17042_OCVInternal, MAX17042_OCVInternal), 1028 + regmap_reg_range(MAX17042_VFSOC, MAX17042_VFSOC), 1029 + }; 1030 + 1031 + static const struct regmap_access_table max77759_fg_write_table = { 1032 + .yes_ranges = max77759_fg_registers, 1033 + .n_yes_ranges = ARRAY_SIZE(max77759_fg_registers), 1034 + .no_ranges = max77759_fg_ro_registers, 1035 + .n_no_ranges = ARRAY_SIZE(max77759_fg_ro_registers), 1036 + }; 1037 + 1038 + static const struct regmap_access_table max77759_fg_rd_table = { 1039 + .yes_ranges = max77759_fg_registers, 1040 + .n_yes_ranges = ARRAY_SIZE(max77759_fg_registers), 1041 + }; 1042 + 1043 + static const struct regmap_config max77759_fg_regmap_cfg = { 1044 + .reg_bits = 8, 1045 + .val_bits = 16, 1046 + .max_register = 0xff, 1047 + .wr_table = &max77759_fg_write_table, 1048 + .rd_table = &max77759_fg_rd_table, 1049 + .val_format_endian = REGMAP_ENDIAN_NATIVE, 1050 + .cache_type = REGCACHE_NONE, 1051 + }; 1052 + 1050 1053 static const struct power_supply_desc max17042_psy_desc = { 1051 1054 .name = "max170xx_battery", 1052 1055 .type = POWER_SUPPLY_TYPE_BATTERY, ··· 1112 1037 { 1113 1038 struct i2c_adapter *adapter = client->adapter; 1114 1039 const struct power_supply_desc *max17042_desc = &max17042_psy_desc; 1040 + const struct regmap_config *regmap_config; 1115 1041 struct power_supply_config psy_cfg = {}; 1116 1042 struct max17042_chip *chip; 1117 1043 int ret; ··· 1128 1052 1129 1053 chip->dev = dev; 1130 1054 chip->chip_type = chip_type; 1131 - chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config); 1132 - if (IS_ERR(chip->regmap)) { 1133 - dev_err(dev, "Failed to initialize regmap\n"); 1134 - return -EINVAL; 1135 - } 1055 + 1056 + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX77759) 1057 + regmap_config = &max77759_fg_regmap_cfg; 1058 + else 1059 + regmap_config = &max17042_regmap_config; 1060 + chip->regmap = devm_regmap_init_i2c(client, regmap_config); 1061 + if (IS_ERR(chip->regmap)) 1062 + return dev_err_probe(dev, PTR_ERR(chip->regmap), 1063 + "Failed to initialize regmap\n"); 1136 1064 1137 1065 chip->pdata = max17042_get_pdata(chip); 1138 - if (!chip->pdata) { 1139 - dev_err(dev, "no platform data provided\n"); 1140 - return -EINVAL; 1141 - } 1066 + if (!chip->pdata) 1067 + return dev_err_probe(dev, -EINVAL, 1068 + "no platform data provided\n"); 1142 1069 1143 1070 dev_set_drvdata(dev, chip); 1144 1071 psy_cfg.drv_data = chip; ··· 1167 1088 regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007); 1168 1089 } 1169 1090 1091 + chip->task_period = MAX17042_DEFAULT_TASK_PERIOD; 1092 + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX77759) { 1093 + ret = regmap_read(chip->regmap, MAX17042_TaskPeriod, &val); 1094 + if (ret) 1095 + return dev_err_probe(dev, ret, 1096 + "failed to read task period\n"); 1097 + chip->task_period = val; 1098 + } 1099 + dev_dbg(dev, "task period: %#.4x (%d)\n", chip->task_period, 1100 + chip->task_period); 1101 + 1170 1102 chip->battery = devm_power_supply_register(dev, max17042_desc, 1171 1103 &psy_cfg); 1172 - if (IS_ERR(chip->battery)) { 1173 - dev_err(dev, "failed: power supply register\n"); 1174 - return PTR_ERR(chip->battery); 1175 - } 1104 + if (IS_ERR(chip->battery)) 1105 + return dev_err_probe(dev, PTR_ERR(chip->battery), 1106 + "failed: power supply register\n"); 1176 1107 1177 1108 if (irq) { 1178 1109 unsigned int flags = IRQF_ONESHOT | IRQF_SHARED | IRQF_PROBE_SHARED; ··· 1325 1236 .data = (void *) MAXIM_DEVICE_TYPE_MAX17055 }, 1326 1237 { .compatible = "maxim,max77705-battery", 1327 1238 .data = (void *) MAXIM_DEVICE_TYPE_MAX17047 }, 1239 + { .compatible = "maxim,max77759-fg", 1240 + .data = (void *) MAXIM_DEVICE_TYPE_MAX77759 }, 1328 1241 { .compatible = "maxim,max77849-battery", 1329 1242 .data = (void *) MAXIM_DEVICE_TYPE_MAX17047 }, 1330 1243 { }, ··· 1339 1248 { "max17047", MAXIM_DEVICE_TYPE_MAX17047 }, 1340 1249 { "max17050", MAXIM_DEVICE_TYPE_MAX17050 }, 1341 1250 { "max17055", MAXIM_DEVICE_TYPE_MAX17055 }, 1251 + { "max77759-fg", MAXIM_DEVICE_TYPE_MAX77759 }, 1342 1252 { "max77849-battery", MAXIM_DEVICE_TYPE_MAX17047 }, 1343 1253 { } 1344 1254 };
+11 -25
drivers/power/supply/max77705_charger.c
··· 646 646 if (ret) 647 647 return dev_err_probe(dev, ret, "failed to add irq chip\n"); 648 648 649 - chg->wqueue = create_singlethread_workqueue(dev_name(dev)); 649 + chg->wqueue = devm_alloc_ordered_workqueue(dev, "%s", 0, dev_name(dev)); 650 650 if (!chg->wqueue) 651 651 return -ENOMEM; 652 652 653 653 ret = devm_work_autocancel(dev, &chg->chgin_work, max77705_chgin_isr_work); 654 - if (ret) { 655 - dev_err_probe(dev, ret, "failed to initialize interrupt work\n"); 656 - goto destroy_wq; 657 - } 654 + if (ret) 655 + return dev_err_probe(dev, ret, "failed to initialize interrupt work\n"); 658 656 659 657 ret = max77705_charger_initialize(chg); 660 - if (ret) { 661 - dev_err_probe(dev, ret, "failed to initialize charger IC\n"); 662 - goto destroy_wq; 663 - } 658 + if (ret) 659 + return dev_err_probe(dev, ret, "failed to initialize charger IC\n"); 664 660 665 661 ret = devm_request_threaded_irq(dev, regmap_irq_get_virq(irq_data, MAX77705_CHGIN_I), 666 662 NULL, max77705_chgin_irq, 667 663 IRQF_TRIGGER_NONE, 668 664 "chgin-irq", chg); 669 - if (ret) { 670 - dev_err_probe(dev, ret, "Failed to Request chgin IRQ\n"); 671 - goto destroy_wq; 672 - } 665 + if (ret) 666 + return ret; 673 667 674 668 ret = devm_request_threaded_irq(dev, regmap_irq_get_virq(irq_data, MAX77705_AICL_I), 675 669 NULL, max77705_aicl_irq, 676 670 IRQF_TRIGGER_NONE, 677 671 "aicl-irq", chg); 678 - if (ret) { 679 - dev_err_probe(dev, ret, "Failed to Request aicl IRQ\n"); 680 - goto destroy_wq; 681 - } 672 + if (ret) 673 + return ret; 682 674 683 675 ret = max77705_charger_enable(chg); 684 - if (ret) { 685 - dev_err_probe(dev, ret, "failed to enable charge\n"); 686 - goto destroy_wq; 687 - } 676 + if (ret) 677 + return dev_err_probe(dev, ret, "failed to enable charge\n"); 688 678 689 679 return devm_add_action_or_reset(dev, max77705_charger_disable, chg); 690 - 691 - destroy_wq: 692 - destroy_workqueue(chg->wqueue); 693 - return ret; 694 680 } 695 681 696 682 static const struct of_device_id max77705_charger_of_match[] = {
+1 -12
drivers/power/supply/mt6370-charger.c
··· 761 761 return PTR_ERR_OR_ZERO(priv->psy); 762 762 } 763 763 764 - static void mt6370_chg_destroy_wq(void *data) 765 - { 766 - struct workqueue_struct *wq = data; 767 - 768 - destroy_workqueue(wq); 769 - } 770 - 771 764 static irqreturn_t mt6370_attach_i_handler(int irq, void *data) 772 765 { 773 766 struct mt6370_priv *priv = data; ··· 886 893 887 894 priv->attach = MT6370_ATTACH_STAT_DETACH; 888 895 889 - priv->wq = create_singlethread_workqueue(dev_name(priv->dev)); 896 + priv->wq = devm_alloc_ordered_workqueue(dev, "%s", 0, dev_name(priv->dev)); 890 897 if (!priv->wq) 891 898 return -ENOMEM; 892 - 893 - ret = devm_add_action_or_reset(dev, mt6370_chg_destroy_wq, priv->wq); 894 - if (ret) 895 - return ret; 896 899 897 900 ret = devm_work_autocancel(dev, &priv->bc12_work, mt6370_chg_bc12_work_func); 898 901 if (ret)
+2
drivers/power/supply/qcom_battmgr.c
··· 1611 1611 } 1612 1612 1613 1613 static const struct of_device_id qcom_battmgr_of_variants[] = { 1614 + { .compatible = "qcom,glymur-pmic-glink", .data = (void *)QCOM_BATTMGR_X1E80100 }, 1615 + { .compatible = "qcom,kaanapali-pmic-glink", .data = (void *)QCOM_BATTMGR_SM8550 }, 1614 1616 { .compatible = "qcom,sc8180x-pmic-glink", .data = (void *)QCOM_BATTMGR_SC8280XP }, 1615 1617 { .compatible = "qcom,sc8280xp-pmic-glink", .data = (void *)QCOM_BATTMGR_SC8280XP }, 1616 1618 { .compatible = "qcom,sm8550-pmic-glink", .data = (void *)QCOM_BATTMGR_SM8550 },
+7
drivers/power/supply/qcom_smbx.c
··· 134 134 #define OCP_CHARGER_BIT BIT(1) 135 135 #define SDP_CHARGER_BIT BIT(0) 136 136 137 + #define USBIN_CMD_IL 0x340 138 + #define USBIN_SUSPEND_BIT BIT(0) 139 + 137 140 #define TYPE_C_STATUS_1 0x30B 138 141 #define UFP_TYPEC_MASK GENMASK(7, 5) 139 142 #define UFP_TYPEC_RDSTD_BIT BIT(7) ··· 696 693 struct smb_chip *chip = power_supply_get_drvdata(psy); 697 694 698 695 switch (psp) { 696 + case POWER_SUPPLY_PROP_STATUS: 697 + return regmap_update_bits(chip->regmap, chip->base + USBIN_CMD_IL, 698 + USBIN_SUSPEND_BIT, !val->intval); 699 699 case POWER_SUPPLY_PROP_CURRENT_MAX: 700 700 return smb_set_current_limit(chip, val->intval); 701 701 default: ··· 711 705 enum power_supply_property psp) 712 706 { 713 707 switch (psp) { 708 + case POWER_SUPPLY_PROP_STATUS: 714 709 case POWER_SUPPLY_PROP_CURRENT_MAX: 715 710 return 1; 716 711 default:
+307
drivers/power/supply/s2mu005-battery.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Battery Fuel Gauge Driver for Samsung S2MU005 PMIC. 4 + * 5 + * Copyright (C) 2015 Samsung Electronics 6 + * Copyright (C) 2023 Yassine Oudjana <y.oudjana@protonmail.com> 7 + * Copyright (C) 2025 Kaustabh Chakraborty <kauschluss@disroot.org> 8 + */ 9 + 10 + #include <linux/delay.h> 11 + #include <linux/i2c.h> 12 + #include <linux/interrupt.h> 13 + #include <linux/mod_devicetable.h> 14 + #include <linux/mutex.h> 15 + #include <linux/power_supply.h> 16 + #include <linux/property.h> 17 + #include <linux/regmap.h> 18 + #include <linux/units.h> 19 + 20 + #define S2MU005_FG_REG_STATUS 0x00 21 + #define S2MU005_FG_REG_IRQ 0x02 22 + #define S2MU005_FG_REG_RVBAT 0x04 23 + #define S2MU005_FG_REG_RCURCC 0x06 24 + #define S2MU005_FG_REG_RSOC 0x08 25 + #define S2MU005_FG_REG_MONOUT 0x0a 26 + #define S2MU005_FG_REG_MONOUTSEL 0x0c 27 + #define S2MU005_FG_REG_RBATCAP 0x0e 28 + #define S2MU005_FG_REG_RZADJ 0x12 29 + #define S2MU005_FG_REG_RBATZ0 0x16 30 + #define S2MU005_FG_REG_RBATZ1 0x18 31 + #define S2MU005_FG_REG_IRQLVL 0x1a 32 + #define S2MU005_FG_REG_START 0x1e 33 + 34 + #define S2MU005_FG_MONOUTSEL_AVGCURRENT 0x26 35 + #define S2MU005_FG_MONOUTSEL_AVGVOLTAGE 0x27 36 + 37 + struct s2mu005_fg { 38 + struct device *dev; 39 + struct regmap *regmap; 40 + struct power_supply *psy; 41 + struct mutex monout_mutex; 42 + }; 43 + 44 + static const struct regmap_config s2mu005_fg_regmap_config = { 45 + .reg_bits = 8, 46 + .val_bits = 16, 47 + .val_format_endian = REGMAP_ENDIAN_LITTLE, 48 + }; 49 + 50 + static irqreturn_t s2mu005_handle_irq(int irq, void *data) 51 + { 52 + struct s2mu005_fg *priv = data; 53 + 54 + msleep(100); 55 + power_supply_changed(priv->psy); 56 + 57 + return IRQ_HANDLED; 58 + } 59 + 60 + static int s2mu005_fg_get_voltage_now(struct s2mu005_fg *priv, int *value) 61 + { 62 + struct regmap *regmap = priv->regmap; 63 + u32 val; 64 + int ret; 65 + 66 + ret = regmap_read(regmap, S2MU005_FG_REG_RVBAT, &val); 67 + if (ret < 0) { 68 + dev_err(priv->dev, "failed to read voltage register (%d)\n", ret); 69 + return ret; 70 + } 71 + 72 + *value = (val * MICRO) >> 13; 73 + 74 + return 0; 75 + } 76 + 77 + static int s2mu005_fg_get_voltage_avg(struct s2mu005_fg *priv, int *value) 78 + { 79 + struct regmap *regmap = priv->regmap; 80 + u32 val; 81 + int ret; 82 + 83 + mutex_lock(&priv->monout_mutex); 84 + 85 + ret = regmap_write(regmap, S2MU005_FG_REG_MONOUTSEL, 86 + S2MU005_FG_MONOUTSEL_AVGVOLTAGE); 87 + if (ret < 0) { 88 + dev_err(priv->dev, "failed to enable average voltage monitoring (%d)\n", 89 + ret); 90 + goto unlock; 91 + } 92 + 93 + ret = regmap_read(regmap, S2MU005_FG_REG_MONOUT, &val); 94 + if (ret < 0) { 95 + dev_err(priv->dev, "failed to read current register (%d)\n", ret); 96 + goto unlock; 97 + } 98 + 99 + *value = (val * MICRO) >> 12; 100 + 101 + unlock: 102 + mutex_unlock(&priv->monout_mutex); 103 + 104 + return ret; 105 + } 106 + static int s2mu005_fg_get_current_now(struct s2mu005_fg *priv, int *value) 107 + { 108 + struct regmap *regmap = priv->regmap; 109 + u32 val; 110 + int ret; 111 + 112 + ret = regmap_read(regmap, S2MU005_FG_REG_RCURCC, &val); 113 + if (ret < 0) { 114 + dev_err(priv->dev, "failed to read current register (%d)\n", ret); 115 + return ret; 116 + } 117 + 118 + *value = -((s16)val * MICRO) >> 12; 119 + 120 + return 0; 121 + } 122 + 123 + static int s2mu005_fg_get_current_avg(struct s2mu005_fg *priv, int *value) 124 + { 125 + struct regmap *regmap = priv->regmap; 126 + u32 val; 127 + int ret; 128 + 129 + mutex_lock(&priv->monout_mutex); 130 + 131 + ret = regmap_write(regmap, S2MU005_FG_REG_MONOUTSEL, 132 + S2MU005_FG_MONOUTSEL_AVGCURRENT); 133 + if (ret < 0) { 134 + dev_err(priv->dev, "failed to enable average current monitoring (%d)\n", 135 + ret); 136 + goto unlock; 137 + } 138 + 139 + ret = regmap_read(regmap, S2MU005_FG_REG_MONOUT, &val); 140 + if (ret < 0) { 141 + dev_err(priv->dev, "failed to read current register (%d)\n", ret); 142 + goto unlock; 143 + } 144 + 145 + *value = -((s16)val * MICRO) >> 12; 146 + 147 + unlock: 148 + mutex_unlock(&priv->monout_mutex); 149 + 150 + return ret; 151 + } 152 + 153 + static int s2mu005_fg_get_capacity(struct s2mu005_fg *priv, int *value) 154 + { 155 + struct regmap *regmap = priv->regmap; 156 + u32 val; 157 + int ret; 158 + 159 + ret = regmap_read(regmap, S2MU005_FG_REG_RSOC, &val); 160 + if (ret < 0) { 161 + dev_err(priv->dev, "failed to read capacity register (%d)\n", ret); 162 + return ret; 163 + } 164 + 165 + *value = (val * CENTI) >> 14; 166 + 167 + return 0; 168 + } 169 + 170 + static int s2mu005_fg_get_status(struct s2mu005_fg *priv, int *value) 171 + { 172 + int current_now, current_avg, capacity; 173 + int ret; 174 + 175 + ret = s2mu005_fg_get_current_now(priv, &current_now); 176 + if (ret < 0) 177 + return ret; 178 + 179 + ret = s2mu005_fg_get_current_avg(priv, &current_avg); 180 + if (ret < 0) 181 + return ret; 182 + 183 + /* 184 + * Verify both current values reported to reduce inaccuracies due to 185 + * internal hysteresis. 186 + */ 187 + if (current_now < 0 && current_avg < 0) { 188 + *value = POWER_SUPPLY_STATUS_DISCHARGING; 189 + } else if (current_now == 0) { 190 + *value = POWER_SUPPLY_STATUS_NOT_CHARGING; 191 + } else { 192 + *value = POWER_SUPPLY_STATUS_CHARGING; 193 + 194 + ret = s2mu005_fg_get_capacity(priv, &capacity); 195 + if (!ret && capacity > 98) 196 + *value = POWER_SUPPLY_STATUS_FULL; 197 + return ret; 198 + } 199 + 200 + return 0; 201 + } 202 + 203 + static const enum power_supply_property s2mu005_fg_properties[] = { 204 + POWER_SUPPLY_PROP_VOLTAGE_NOW, 205 + POWER_SUPPLY_PROP_VOLTAGE_AVG, 206 + POWER_SUPPLY_PROP_CURRENT_NOW, 207 + POWER_SUPPLY_PROP_CURRENT_AVG, 208 + POWER_SUPPLY_PROP_CAPACITY, 209 + POWER_SUPPLY_PROP_STATUS, 210 + }; 211 + 212 + static int s2mu005_fg_get_property(struct power_supply *psy, 213 + enum power_supply_property psp, 214 + union power_supply_propval *val) 215 + { 216 + struct s2mu005_fg *priv = power_supply_get_drvdata(psy); 217 + 218 + switch (psp) { 219 + case POWER_SUPPLY_PROP_VOLTAGE_NOW: 220 + return s2mu005_fg_get_voltage_now(priv, &val->intval); 221 + case POWER_SUPPLY_PROP_VOLTAGE_AVG: 222 + return s2mu005_fg_get_voltage_avg(priv, &val->intval); 223 + case POWER_SUPPLY_PROP_CURRENT_NOW: 224 + return s2mu005_fg_get_current_now(priv, &val->intval); 225 + case POWER_SUPPLY_PROP_CURRENT_AVG: 226 + return s2mu005_fg_get_current_avg(priv, &val->intval); 227 + case POWER_SUPPLY_PROP_CAPACITY: 228 + return s2mu005_fg_get_capacity(priv, &val->intval); 229 + case POWER_SUPPLY_PROP_STATUS: 230 + return s2mu005_fg_get_status(priv, &val->intval); 231 + default: 232 + return -EINVAL; 233 + } 234 + } 235 + 236 + static const struct power_supply_desc s2mu005_fg_desc = { 237 + .name = "s2mu005-fuel-gauge", 238 + .type = POWER_SUPPLY_TYPE_BATTERY, 239 + .properties = s2mu005_fg_properties, 240 + .num_properties = ARRAY_SIZE(s2mu005_fg_properties), 241 + .get_property = s2mu005_fg_get_property, 242 + }; 243 + 244 + static int s2mu005_fg_i2c_probe(struct i2c_client *client) 245 + { 246 + struct device *dev = &client->dev; 247 + struct s2mu005_fg *priv; 248 + struct power_supply_config psy_cfg = {}; 249 + const struct power_supply_desc *psy_desc; 250 + int ret; 251 + 252 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 253 + if (!priv) 254 + return -ENOMEM; 255 + 256 + dev_set_drvdata(dev, priv); 257 + priv->dev = dev; 258 + 259 + priv->regmap = devm_regmap_init_i2c(client, &s2mu005_fg_regmap_config); 260 + if (IS_ERR(priv->regmap)) 261 + return dev_err_probe(dev, PTR_ERR(priv->regmap), 262 + "failed to initialize regmap\n"); 263 + 264 + ret = devm_mutex_init(dev, &priv->monout_mutex); 265 + if (ret) 266 + dev_err_probe(dev, ret, "failed to initialize MONOUT mutex\n"); 267 + 268 + psy_desc = device_get_match_data(dev); 269 + 270 + psy_cfg.drv_data = priv; 271 + psy_cfg.fwnode = dev_fwnode(dev); 272 + priv->psy = devm_power_supply_register(priv->dev, psy_desc, &psy_cfg); 273 + if (IS_ERR(priv->psy)) 274 + return dev_err_probe(dev, PTR_ERR(priv->psy), 275 + "failed to register power supply subsystem\n"); 276 + 277 + ret = devm_request_threaded_irq(priv->dev, client->irq, NULL, 278 + s2mu005_handle_irq, IRQF_ONESHOT, 279 + psy_desc->name, priv); 280 + if (ret) 281 + dev_err_probe(dev, ret, "failed to request IRQ\n"); 282 + 283 + return 0; 284 + } 285 + 286 + static const struct of_device_id s2mu005_fg_of_match_table[] = { 287 + { 288 + .compatible = "samsung,s2mu005-fuel-gauge", 289 + .data = &s2mu005_fg_desc, 290 + }, 291 + { } 292 + }; 293 + MODULE_DEVICE_TABLE(of, s2mu005_fg_of_match_table); 294 + 295 + static struct i2c_driver s2mu005_fg_i2c_driver = { 296 + .probe = s2mu005_fg_i2c_probe, 297 + .driver = { 298 + .name = "s2mu005-fuel-gauge", 299 + .of_match_table = s2mu005_fg_of_match_table, 300 + }, 301 + }; 302 + module_i2c_driver(s2mu005_fg_i2c_driver); 303 + 304 + MODULE_DESCRIPTION("Samsung S2MU005 PMIC Battery Fuel Gauge Driver"); 305 + MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>"); 306 + MODULE_AUTHOR("Kaustabh Chakraborty <kauschluss@disroot.org>"); 307 + MODULE_LICENSE("GPL");
+1 -1
drivers/power/supply/sbs-manager.c
··· 199 199 if (ret < 0) 200 200 return ret; 201 201 202 - return ret & BIT(off); 202 + return !!(ret & BIT(off)); 203 203 } 204 204 205 205 /*
-2
drivers/power/supply/twl4030_madc_battery.c
··· 11 11 */ 12 12 13 13 #include <linux/module.h> 14 - #include <linux/param.h> 15 14 #include <linux/delay.h> 16 - #include <linux/workqueue.h> 17 15 #include <linux/platform_device.h> 18 16 #include <linux/power_supply.h> 19 17 #include <linux/slab.h>
+23 -2
include/linux/power/max17042_battery.h
··· 17 17 #define MAX17042_DEFAULT_VMAX (4500) /* LiHV cell max */ 18 18 #define MAX17042_DEFAULT_TEMP_MIN (0) /* For sys without temp sensor */ 19 19 #define MAX17042_DEFAULT_TEMP_MAX (700) /* 70 degrees Celcius */ 20 + #define MAX17042_DEFAULT_TASK_PERIOD (5760) 20 21 21 22 /* Consider RepCap which is less then 10 units below FullCAP full */ 22 23 #define MAX17042_FULL_THRESHOLD 10 ··· 106 105 107 106 MAX17042_OCV = 0xEE, 108 107 109 - MAX17042_OCVInternal = 0xFB, /* MAX17055 VFOCV */ 108 + MAX17042_OCVInternal = 0xFB, /* MAX17055/77759 VFOCV */ 110 109 111 110 MAX17042_VFSOC = 0xFF, 112 111 }; ··· 157 156 MAX17055_AtAvCap = 0xDF, 158 157 }; 159 158 160 - /* Registers specific to max17047/50/55 */ 159 + /* Registers specific to max17047/50/55/77759 */ 161 160 enum max17047_register { 162 161 MAX17047_QRTbl00 = 0x12, 163 162 MAX17047_FullSOCThr = 0x13, ··· 168 167 MAX17047_QRTbl30 = 0x42, 169 168 }; 170 169 170 + enum max77759_register { 171 + MAX77759_AvgTA0 = 0x26, 172 + MAX77759_AtTTF = 0x33, 173 + MAX77759_Tconvert = 0x34, 174 + MAX77759_AvgCurrent0 = 0x3B, 175 + MAX77759_THMHOT = 0x40, 176 + MAX77759_CTESample = 0x41, 177 + MAX77759_ISys = 0x43, 178 + MAX77759_AvgVCell0 = 0x44, 179 + MAX77759_RlxSOC = 0x47, 180 + MAX77759_AvgISys = 0x4B, 181 + MAX77759_QH0 = 0x4C, 182 + MAX77759_MixAtFull = 0x4F, 183 + MAX77759_VSys = 0xB1, 184 + MAX77759_TAlrtTh2 = 0xB2, 185 + MAX77759_VByp = 0xB3, 186 + MAX77759_IIn = 0xD0, 187 + }; 188 + 171 189 enum max170xx_chip_type { 172 190 MAXIM_DEVICE_TYPE_UNKNOWN = 0, 173 191 MAXIM_DEVICE_TYPE_MAX17042, 174 192 MAXIM_DEVICE_TYPE_MAX17047, 175 193 MAXIM_DEVICE_TYPE_MAX17050, 176 194 MAXIM_DEVICE_TYPE_MAX17055, 195 + MAXIM_DEVICE_TYPE_MAX77759, 177 196 178 197 MAXIM_DEVICE_TYPE_NUM 179 198 };