1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * ChromeOS EC driver for charge control 4 * 5 * Copyright (C) 2024 Thomas Weißschuh <linux@weissschuh.net> 6 */ 7 #include <acpi/battery.h> 8 #include <linux/container_of.h> 9 #include <linux/dmi.h> 10 #include <linux/lockdep.h> 11 #include <linux/mod_devicetable.h> 12 #include <linux/module.h> 13 #include <linux/mutex.h> 14 #include <linux/platform_data/cros_ec_commands.h> 15 #include <linux/platform_data/cros_ec_proto.h> 16 #include <linux/platform_device.h> 17 #include <linux/types.h> 18 19 #define EC_CHARGE_CONTROL_BEHAVIOURS (BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) | \ 20 BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE) | \ 21 BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE)) 22 23 enum CROS_CHCTL_ATTR { 24 CROS_CHCTL_ATTR_START_THRESHOLD, 25 CROS_CHCTL_ATTR_END_THRESHOLD, 26 CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR, 27 _CROS_CHCTL_ATTR_COUNT 28 }; 29 30 /* 31 * Semantics of data *returned* from the EC API and Linux sysfs differ 32 * slightly, also the v1 API can not return any data. 33 * To match the expected sysfs API, data is never read back from the EC but 34 * cached in the driver. 35 * 36 * Changes to the EC bypassing the driver will not be reflected in sysfs. 37 * Any change to "charge_behaviour" will synchronize the EC with the driver state. 38 */ 39 40 struct cros_chctl_priv { 41 struct cros_ec_device *cros_ec; 42 struct acpi_battery_hook battery_hook; 43 struct power_supply *hooked_battery; 44 u8 cmd_version; 45 46 /* The callbacks need to access this priv structure. 47 * As neither the struct device nor power_supply are under the drivers 48 * control, embed the attributes within priv to use with container_of(). 49 */ 50 struct device_attribute device_attrs[_CROS_CHCTL_ATTR_COUNT]; 51 struct attribute *attributes[_CROS_CHCTL_ATTR_COUNT]; 52 struct attribute_group group; 53 54 struct mutex lock; /* protects fields below and cros_ec */ 55 enum power_supply_charge_behaviour current_behaviour; 56 u8 current_start_threshold, current_end_threshold; 57 }; 58 59 static int cros_chctl_send_charge_control_cmd(struct cros_ec_device *cros_ec, 60 u8 cmd_version, struct ec_params_charge_control *req) 61 { 62 static const u8 outsizes[] = { 63 [1] = offsetof(struct ec_params_charge_control, cmd), 64 [2] = sizeof(struct ec_params_charge_control), 65 [3] = sizeof(struct ec_params_charge_control), 66 }; 67 68 struct { 69 struct cros_ec_command msg; 70 union { 71 struct ec_params_charge_control req; 72 struct ec_response_charge_control resp; 73 } __packed data; 74 } __packed buf = { 75 .msg = { 76 .command = EC_CMD_CHARGE_CONTROL, 77 .version = cmd_version, 78 .insize = 0, 79 .outsize = outsizes[cmd_version], 80 }, 81 .data.req = *req, 82 }; 83 84 return cros_ec_cmd_xfer_status(cros_ec, &buf.msg); 85 } 86 87 static int cros_chctl_configure_ec(struct cros_chctl_priv *priv) 88 { 89 struct ec_params_charge_control req = {}; 90 91 lockdep_assert_held(&priv->lock); 92 93 req.cmd = EC_CHARGE_CONTROL_CMD_SET; 94 95 switch (priv->current_behaviour) { 96 case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO: 97 req.mode = CHARGE_CONTROL_NORMAL; 98 break; 99 case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE: 100 req.mode = CHARGE_CONTROL_IDLE; 101 break; 102 case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE: 103 req.mode = CHARGE_CONTROL_DISCHARGE; 104 break; 105 default: 106 return -EINVAL; 107 } 108 109 if (priv->current_behaviour == POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO && 110 !(priv->current_start_threshold == 0 && priv->current_end_threshold == 100)) { 111 req.sustain_soc.lower = priv->current_start_threshold; 112 req.sustain_soc.upper = priv->current_end_threshold; 113 } else { 114 /* Disable charging limits */ 115 req.sustain_soc.lower = -1; 116 req.sustain_soc.upper = -1; 117 } 118 119 return cros_chctl_send_charge_control_cmd(priv->cros_ec, priv->cmd_version, &req); 120 } 121 122 static struct cros_chctl_priv *cros_chctl_attr_to_priv(struct attribute *attr, 123 enum CROS_CHCTL_ATTR idx) 124 { 125 struct device_attribute *dev_attr = container_of(attr, struct device_attribute, attr); 126 127 return container_of(dev_attr, struct cros_chctl_priv, device_attrs[idx]); 128 } 129 130 static ssize_t cros_chctl_store_threshold(struct device *dev, struct cros_chctl_priv *priv, 131 int is_end_threshold, const char *buf, size_t count) 132 { 133 int ret, val; 134 135 ret = kstrtoint(buf, 10, &val); 136 if (ret < 0) 137 return ret; 138 if (val < 0 || val > 100) 139 return -EINVAL; 140 141 if (is_end_threshold) { 142 /* Start threshold is not exposed, use fixed value */ 143 if (priv->cmd_version == 2) 144 priv->current_start_threshold = val == 100 ? 0 : val; 145 146 if (val < priv->current_start_threshold) 147 return -EINVAL; 148 priv->current_end_threshold = val; 149 } else { 150 if (val > priv->current_end_threshold) 151 return -EINVAL; 152 priv->current_start_threshold = val; 153 } 154 155 if (priv->current_behaviour == POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) { 156 ret = cros_chctl_configure_ec(priv); 157 if (ret < 0) 158 return ret; 159 } 160 161 return count; 162 } 163 164 static ssize_t charge_control_start_threshold_show(struct device *dev, 165 struct device_attribute *attr, 166 char *buf) 167 { 168 struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, 169 CROS_CHCTL_ATTR_START_THRESHOLD); 170 171 guard(mutex)(&priv->lock); 172 return sysfs_emit(buf, "%u\n", (unsigned int)priv->current_start_threshold); 173 } 174 175 static ssize_t charge_control_start_threshold_store(struct device *dev, 176 struct device_attribute *attr, 177 const char *buf, size_t count) 178 { 179 struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, 180 CROS_CHCTL_ATTR_START_THRESHOLD); 181 182 guard(mutex)(&priv->lock); 183 return cros_chctl_store_threshold(dev, priv, 0, buf, count); 184 } 185 186 static ssize_t charge_control_end_threshold_show(struct device *dev, struct device_attribute *attr, 187 char *buf) 188 { 189 struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, 190 CROS_CHCTL_ATTR_END_THRESHOLD); 191 192 guard(mutex)(&priv->lock); 193 return sysfs_emit(buf, "%u\n", (unsigned int)priv->current_end_threshold); 194 } 195 196 static ssize_t charge_control_end_threshold_store(struct device *dev, struct device_attribute *attr, 197 const char *buf, size_t count) 198 { 199 struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, 200 CROS_CHCTL_ATTR_END_THRESHOLD); 201 202 guard(mutex)(&priv->lock); 203 return cros_chctl_store_threshold(dev, priv, 1, buf, count); 204 } 205 206 static ssize_t charge_behaviour_show(struct device *dev, struct device_attribute *attr, char *buf) 207 { 208 struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, 209 CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR); 210 211 guard(mutex)(&priv->lock); 212 return power_supply_charge_behaviour_show(dev, EC_CHARGE_CONTROL_BEHAVIOURS, 213 priv->current_behaviour, buf); 214 } 215 216 static ssize_t charge_behaviour_store(struct device *dev, struct device_attribute *attr, 217 const char *buf, size_t count) 218 { 219 struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr, 220 CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR); 221 int ret; 222 223 ret = power_supply_charge_behaviour_parse(EC_CHARGE_CONTROL_BEHAVIOURS, buf); 224 if (ret < 0) 225 return ret; 226 227 guard(mutex)(&priv->lock); 228 priv->current_behaviour = ret; 229 230 ret = cros_chctl_configure_ec(priv); 231 if (ret < 0) 232 return ret; 233 234 return count; 235 } 236 237 static umode_t cros_chtl_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) 238 { 239 struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(attr, n); 240 241 if (n == CROS_CHCTL_ATTR_START_THRESHOLD && priv->cmd_version < 3) 242 return 0; 243 else if (n == CROS_CHCTL_ATTR_END_THRESHOLD && priv->cmd_version < 2) 244 return 0; 245 246 return attr->mode; 247 } 248 249 static int cros_chctl_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook) 250 { 251 struct cros_chctl_priv *priv = container_of(hook, struct cros_chctl_priv, battery_hook); 252 253 if (priv->hooked_battery) 254 return 0; 255 256 priv->hooked_battery = battery; 257 return device_add_group(&battery->dev, &priv->group); 258 } 259 260 static int cros_chctl_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook) 261 { 262 struct cros_chctl_priv *priv = container_of(hook, struct cros_chctl_priv, battery_hook); 263 264 if (priv->hooked_battery == battery) { 265 device_remove_group(&battery->dev, &priv->group); 266 priv->hooked_battery = NULL; 267 } 268 269 return 0; 270 } 271 272 static bool probe_with_fwk_charge_control; 273 module_param(probe_with_fwk_charge_control, bool, 0644); 274 MODULE_PARM_DESC(probe_with_fwk_charge_control, 275 "Probe the driver in the presence of the custom Framework EC charge control"); 276 277 static int cros_chctl_fwk_charge_control_versions(struct cros_ec_device *cros_ec) 278 { 279 if (!dmi_match(DMI_SYS_VENDOR, "Framework")) 280 return 0; 281 282 return cros_ec_get_cmd_versions(cros_ec, 0x3E03 /* FW_EC_CMD_CHARGE_LIMIT */); 283 } 284 285 static int cros_chctl_probe(struct platform_device *pdev) 286 { 287 struct device *dev = &pdev->dev; 288 struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent); 289 struct cros_ec_device *cros_ec = ec_dev->ec_dev; 290 struct cros_chctl_priv *priv; 291 size_t i; 292 int ret; 293 294 ret = cros_chctl_fwk_charge_control_versions(cros_ec); 295 if (ret < 0) 296 return ret; 297 if (ret > 0 && !probe_with_fwk_charge_control) { 298 dev_info(dev, "Framework charge control detected, preventing load\n"); 299 return -ENODEV; 300 } 301 302 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 303 if (!priv) 304 return -ENOMEM; 305 306 ret = devm_mutex_init(dev, &priv->lock); 307 if (ret) 308 return ret; 309 310 ret = cros_ec_get_cmd_versions(cros_ec, EC_CMD_CHARGE_CONTROL); 311 if (ret < 0) 312 return ret; 313 else if (ret & EC_VER_MASK(3)) 314 priv->cmd_version = 3; 315 else if (ret & EC_VER_MASK(2)) 316 priv->cmd_version = 2; 317 else if (ret & EC_VER_MASK(1)) 318 priv->cmd_version = 1; 319 else 320 return -ENODEV; 321 322 dev_dbg(dev, "Command version: %u\n", (unsigned int)priv->cmd_version); 323 324 priv->cros_ec = cros_ec; 325 priv->device_attrs[CROS_CHCTL_ATTR_START_THRESHOLD] = 326 (struct device_attribute)__ATTR_RW(charge_control_start_threshold); 327 priv->device_attrs[CROS_CHCTL_ATTR_END_THRESHOLD] = 328 (struct device_attribute)__ATTR_RW(charge_control_end_threshold); 329 priv->device_attrs[CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR] = 330 (struct device_attribute)__ATTR_RW(charge_behaviour); 331 for (i = 0; i < _CROS_CHCTL_ATTR_COUNT; i++) { 332 sysfs_attr_init(&priv->device_attrs[i].attr); 333 priv->attributes[i] = &priv->device_attrs[i].attr; 334 } 335 priv->group.is_visible = cros_chtl_attr_is_visible; 336 priv->group.attrs = priv->attributes; 337 338 priv->battery_hook.name = dev_name(dev); 339 priv->battery_hook.add_battery = cros_chctl_add_battery; 340 priv->battery_hook.remove_battery = cros_chctl_remove_battery; 341 342 priv->current_behaviour = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; 343 priv->current_start_threshold = 0; 344 priv->current_end_threshold = 100; 345 346 /* Bring EC into well-known state */ 347 scoped_guard(mutex, &priv->lock) 348 ret = cros_chctl_configure_ec(priv); 349 if (ret < 0) 350 return ret; 351 352 return devm_battery_hook_register(dev, &priv->battery_hook); 353 } 354 355 static const struct platform_device_id cros_chctl_id[] = { 356 { "cros-charge-control", 0 }, 357 {} 358 }; 359 360 static struct platform_driver cros_chctl_driver = { 361 .driver.name = "cros-charge-control", 362 .probe = cros_chctl_probe, 363 .id_table = cros_chctl_id, 364 }; 365 module_platform_driver(cros_chctl_driver); 366 367 MODULE_DEVICE_TABLE(platform, cros_chctl_id); 368 MODULE_DESCRIPTION("ChromeOS EC charge control"); 369 MODULE_AUTHOR("Thomas Weißschuh <linux@weissschuh.net>"); 370 MODULE_LICENSE("GPL"); 371