1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2022-2024, Linaro Ltd 4 * Authors: 5 * Bjorn Andersson 6 * Dmitry Baryshkov 7 */ 8 #include <linux/auxiliary_bus.h> 9 #include <linux/bits.h> 10 #include <linux/cleanup.h> 11 #include <linux/delay.h> 12 #include <linux/jiffies.h> 13 #include <linux/module.h> 14 #include <linux/mutex.h> 15 #include <linux/notifier.h> 16 #include <linux/power_supply.h> 17 #include <linux/platform_data/lenovo-yoga-c630.h> 18 19 struct yoga_c630_psy { 20 struct yoga_c630_ec *ec; 21 struct device *dev; 22 struct fwnode_handle *fwnode; 23 struct notifier_block nb; 24 25 /* guards all battery properties and registration of power supplies */ 26 struct mutex lock; 27 28 struct power_supply *adp_psy; 29 struct power_supply *bat_psy; 30 31 unsigned long last_status_update; 32 33 bool adapter_online; 34 35 bool unit_mA; 36 37 bool bat_present; 38 unsigned int bat_status; 39 unsigned int design_capacity; 40 unsigned int design_voltage; 41 unsigned int full_charge_capacity; 42 43 unsigned int capacity_now; 44 unsigned int voltage_now; 45 46 int current_now; 47 int rate_now; 48 }; 49 50 #define LENOVO_EC_CACHE_TIME (10 * HZ) 51 52 #define LENOVO_EC_ADPT_STATUS 0xa3 53 #define LENOVO_EC_ADPT_STATUS_PRESENT BIT(7) 54 #define LENOVO_EC_BAT_ATTRIBUTES 0xc0 55 #define LENOVO_EC_BAT_ATTRIBUTES_UNIT_IS_MA BIT(1) 56 #define LENOVO_EC_BAT_STATUS 0xc1 57 #define LENOVO_EC_BAT_STATUS_DISCHARGING BIT(0) 58 #define LENOVO_EC_BAT_STATUS_CHARGING BIT(1) 59 #define LENOVO_EC_BAT_REMAIN_CAPACITY 0xc2 60 #define LENOVO_EC_BAT_VOLTAGE 0xc6 61 #define LENOVO_EC_BAT_DESIGN_VOLTAGE 0xc8 62 #define LENOVO_EC_BAT_DESIGN_CAPACITY 0xca 63 #define LENOVO_EC_BAT_FULL_CAPACITY 0xcc 64 #define LENOVO_EC_BAT_CURRENT 0xd2 65 #define LENOVO_EC_BAT_FULL_FACTORY 0xd6 66 #define LENOVO_EC_BAT_PRESENT 0xda 67 #define LENOVO_EC_BAT_PRESENT_IS_PRESENT BIT(0) 68 #define LENOVO_EC_BAT_FULL_REGISTER 0xdb 69 #define LENOVO_EC_BAT_FULL_REGISTER_IS_FACTORY BIT(0) 70 71 static int yoga_c630_psy_update_bat_info(struct yoga_c630_psy *ecbat) 72 { 73 struct yoga_c630_ec *ec = ecbat->ec; 74 int val; 75 76 lockdep_assert_held(&ecbat->lock); 77 78 val = yoga_c630_ec_read8(ec, LENOVO_EC_BAT_PRESENT); 79 if (val < 0) 80 return val; 81 ecbat->bat_present = !!(val & LENOVO_EC_BAT_PRESENT_IS_PRESENT); 82 if (!ecbat->bat_present) 83 return val; 84 85 val = yoga_c630_ec_read8(ec, LENOVO_EC_BAT_ATTRIBUTES); 86 if (val < 0) 87 return val; 88 ecbat->unit_mA = val & LENOVO_EC_BAT_ATTRIBUTES_UNIT_IS_MA; 89 90 val = yoga_c630_ec_read16(ec, LENOVO_EC_BAT_DESIGN_CAPACITY); 91 if (val < 0) 92 return val; 93 ecbat->design_capacity = val * 1000; 94 95 /* 96 * DSDT has delays after most of EC reads in these methods. 97 * Having no documentation for the EC we have to follow and sleep here. 98 */ 99 msleep(50); 100 101 val = yoga_c630_ec_read16(ec, LENOVO_EC_BAT_DESIGN_VOLTAGE); 102 if (val < 0) 103 return val; 104 ecbat->design_voltage = val; 105 106 msleep(50); 107 108 val = yoga_c630_ec_read8(ec, LENOVO_EC_BAT_FULL_REGISTER); 109 if (val < 0) 110 return val; 111 val = yoga_c630_ec_read16(ec, 112 val & LENOVO_EC_BAT_FULL_REGISTER_IS_FACTORY ? 113 LENOVO_EC_BAT_FULL_FACTORY : 114 LENOVO_EC_BAT_FULL_CAPACITY); 115 if (val < 0) 116 return val; 117 118 ecbat->full_charge_capacity = val * 1000; 119 120 if (!ecbat->unit_mA) { 121 ecbat->design_capacity *= 10; 122 ecbat->full_charge_capacity *= 10; 123 } 124 125 return 0; 126 } 127 128 static int yoga_c630_psy_maybe_update_bat_status(struct yoga_c630_psy *ecbat) 129 { 130 struct yoga_c630_ec *ec = ecbat->ec; 131 int current_mA; 132 int val; 133 134 guard(mutex)(&ecbat->lock); 135 if (time_before(jiffies, ecbat->last_status_update + LENOVO_EC_CACHE_TIME)) 136 return 0; 137 138 val = yoga_c630_ec_read8(ec, LENOVO_EC_BAT_STATUS); 139 if (val < 0) 140 return val; 141 ecbat->bat_status = val; 142 143 msleep(50); 144 145 val = yoga_c630_ec_read16(ec, LENOVO_EC_BAT_REMAIN_CAPACITY); 146 if (val < 0) 147 return val; 148 ecbat->capacity_now = val * 1000; 149 150 msleep(50); 151 152 val = yoga_c630_ec_read16(ec, LENOVO_EC_BAT_VOLTAGE); 153 if (val < 0) 154 return val; 155 ecbat->voltage_now = val * 1000; 156 157 msleep(50); 158 159 val = yoga_c630_ec_read16(ec, LENOVO_EC_BAT_CURRENT); 160 if (val < 0) 161 return val; 162 current_mA = sign_extend32(val, 15); 163 ecbat->current_now = current_mA * 1000; 164 ecbat->rate_now = current_mA * (ecbat->voltage_now / 1000); 165 166 msleep(50); 167 168 if (!ecbat->unit_mA) 169 ecbat->capacity_now *= 10; 170 171 ecbat->last_status_update = jiffies; 172 173 return 0; 174 } 175 176 static int yoga_c630_psy_update_adapter_status(struct yoga_c630_psy *ecbat) 177 { 178 struct yoga_c630_ec *ec = ecbat->ec; 179 int val; 180 181 guard(mutex)(&ecbat->lock); 182 183 val = yoga_c630_ec_read8(ec, LENOVO_EC_ADPT_STATUS); 184 if (val < 0) 185 return val; 186 187 ecbat->adapter_online = !!(val & LENOVO_EC_ADPT_STATUS_PRESENT); 188 189 return 0; 190 } 191 192 static bool yoga_c630_psy_is_charged(struct yoga_c630_psy *ecbat) 193 { 194 if (ecbat->bat_status != 0) 195 return false; 196 197 if (ecbat->full_charge_capacity <= ecbat->capacity_now) 198 return true; 199 200 if (ecbat->design_capacity <= ecbat->capacity_now) 201 return true; 202 203 return false; 204 } 205 206 static int yoga_c630_psy_bat_get_property(struct power_supply *psy, 207 enum power_supply_property psp, 208 union power_supply_propval *val) 209 { 210 struct yoga_c630_psy *ecbat = power_supply_get_drvdata(psy); 211 int rc = 0; 212 213 if (!ecbat->bat_present && psp != POWER_SUPPLY_PROP_PRESENT) 214 return -ENODEV; 215 216 rc = yoga_c630_psy_maybe_update_bat_status(ecbat); 217 if (rc) 218 return rc; 219 220 switch (psp) { 221 case POWER_SUPPLY_PROP_STATUS: 222 if (ecbat->bat_status & LENOVO_EC_BAT_STATUS_DISCHARGING) 223 val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 224 else if (ecbat->bat_status & LENOVO_EC_BAT_STATUS_CHARGING) 225 val->intval = POWER_SUPPLY_STATUS_CHARGING; 226 else if (yoga_c630_psy_is_charged(ecbat)) 227 val->intval = POWER_SUPPLY_STATUS_FULL; 228 else 229 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 230 break; 231 case POWER_SUPPLY_PROP_PRESENT: 232 val->intval = ecbat->bat_present; 233 break; 234 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 235 val->intval = ecbat->design_voltage; 236 break; 237 case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 238 case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: 239 val->intval = ecbat->design_capacity; 240 break; 241 case POWER_SUPPLY_PROP_CHARGE_FULL: 242 case POWER_SUPPLY_PROP_ENERGY_FULL: 243 val->intval = ecbat->full_charge_capacity; 244 break; 245 case POWER_SUPPLY_PROP_CHARGE_NOW: 246 case POWER_SUPPLY_PROP_ENERGY_NOW: 247 val->intval = ecbat->capacity_now; 248 break; 249 case POWER_SUPPLY_PROP_CURRENT_NOW: 250 val->intval = ecbat->current_now; 251 break; 252 case POWER_SUPPLY_PROP_POWER_NOW: 253 val->intval = ecbat->rate_now; 254 break; 255 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 256 val->intval = ecbat->voltage_now; 257 break; 258 case POWER_SUPPLY_PROP_TECHNOLOGY: 259 val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 260 break; 261 case POWER_SUPPLY_PROP_MODEL_NAME: 262 val->strval = "PABAS0241231"; 263 break; 264 case POWER_SUPPLY_PROP_MANUFACTURER: 265 val->strval = "Compal"; 266 break; 267 case POWER_SUPPLY_PROP_SCOPE: 268 val->intval = POWER_SUPPLY_SCOPE_SYSTEM; 269 break; 270 default: 271 rc = -EINVAL; 272 break; 273 } 274 275 return rc; 276 } 277 278 static enum power_supply_property yoga_c630_psy_bat_mA_properties[] = { 279 POWER_SUPPLY_PROP_STATUS, 280 POWER_SUPPLY_PROP_PRESENT, 281 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 282 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 283 POWER_SUPPLY_PROP_CHARGE_FULL, 284 POWER_SUPPLY_PROP_CHARGE_NOW, 285 POWER_SUPPLY_PROP_CURRENT_NOW, 286 POWER_SUPPLY_PROP_POWER_NOW, 287 POWER_SUPPLY_PROP_VOLTAGE_NOW, 288 POWER_SUPPLY_PROP_TECHNOLOGY, 289 POWER_SUPPLY_PROP_MODEL_NAME, 290 POWER_SUPPLY_PROP_MANUFACTURER, 291 POWER_SUPPLY_PROP_SCOPE, 292 }; 293 294 static enum power_supply_property yoga_c630_psy_bat_mWh_properties[] = { 295 POWER_SUPPLY_PROP_STATUS, 296 POWER_SUPPLY_PROP_PRESENT, 297 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 298 POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 299 POWER_SUPPLY_PROP_ENERGY_FULL, 300 POWER_SUPPLY_PROP_ENERGY_NOW, 301 POWER_SUPPLY_PROP_CURRENT_NOW, 302 POWER_SUPPLY_PROP_POWER_NOW, 303 POWER_SUPPLY_PROP_VOLTAGE_NOW, 304 POWER_SUPPLY_PROP_TECHNOLOGY, 305 POWER_SUPPLY_PROP_MODEL_NAME, 306 POWER_SUPPLY_PROP_MANUFACTURER, 307 POWER_SUPPLY_PROP_SCOPE, 308 }; 309 310 static const struct power_supply_desc yoga_c630_psy_bat_psy_desc_mA = { 311 .name = "yoga-c630-battery", 312 .type = POWER_SUPPLY_TYPE_BATTERY, 313 .properties = yoga_c630_psy_bat_mA_properties, 314 .num_properties = ARRAY_SIZE(yoga_c630_psy_bat_mA_properties), 315 .get_property = yoga_c630_psy_bat_get_property, 316 }; 317 318 static const struct power_supply_desc yoga_c630_psy_bat_psy_desc_mWh = { 319 .name = "yoga-c630-battery", 320 .type = POWER_SUPPLY_TYPE_BATTERY, 321 .properties = yoga_c630_psy_bat_mWh_properties, 322 .num_properties = ARRAY_SIZE(yoga_c630_psy_bat_mWh_properties), 323 .get_property = yoga_c630_psy_bat_get_property, 324 }; 325 326 static int yoga_c630_psy_adpt_get_property(struct power_supply *psy, 327 enum power_supply_property psp, 328 union power_supply_propval *val) 329 { 330 struct yoga_c630_psy *ecbat = power_supply_get_drvdata(psy); 331 int ret = 0; 332 333 ret = yoga_c630_psy_update_adapter_status(ecbat); 334 if (ret < 0) 335 return ret; 336 337 switch (psp) { 338 case POWER_SUPPLY_PROP_ONLINE: 339 val->intval = ecbat->adapter_online; 340 break; 341 case POWER_SUPPLY_PROP_USB_TYPE: 342 val->intval = POWER_SUPPLY_USB_TYPE_C; 343 break; 344 default: 345 return -EINVAL; 346 } 347 348 return 0; 349 } 350 351 static enum power_supply_property yoga_c630_psy_adpt_properties[] = { 352 POWER_SUPPLY_PROP_ONLINE, 353 POWER_SUPPLY_PROP_USB_TYPE, 354 }; 355 356 static const struct power_supply_desc yoga_c630_psy_adpt_psy_desc = { 357 .name = "yoga-c630-adapter", 358 .type = POWER_SUPPLY_TYPE_USB, 359 .usb_types = BIT(POWER_SUPPLY_USB_TYPE_C), 360 .properties = yoga_c630_psy_adpt_properties, 361 .num_properties = ARRAY_SIZE(yoga_c630_psy_adpt_properties), 362 .get_property = yoga_c630_psy_adpt_get_property, 363 }; 364 365 static int yoga_c630_psy_register_bat_psy(struct yoga_c630_psy *ecbat) 366 { 367 struct power_supply_config bat_cfg = {}; 368 369 bat_cfg.drv_data = ecbat; 370 bat_cfg.fwnode = ecbat->fwnode; 371 ecbat->bat_psy = power_supply_register_no_ws(ecbat->dev, 372 ecbat->unit_mA ? 373 &yoga_c630_psy_bat_psy_desc_mA : 374 &yoga_c630_psy_bat_psy_desc_mWh, 375 &bat_cfg); 376 if (IS_ERR(ecbat->bat_psy)) { 377 dev_err(ecbat->dev, "failed to register battery supply\n"); 378 return PTR_ERR(ecbat->bat_psy); 379 } 380 381 return 0; 382 } 383 384 static void yoga_c630_ec_refresh_bat_info(struct yoga_c630_psy *ecbat) 385 { 386 bool current_unit; 387 388 guard(mutex)(&ecbat->lock); 389 390 current_unit = ecbat->unit_mA; 391 392 yoga_c630_psy_update_bat_info(ecbat); 393 394 if (current_unit != ecbat->unit_mA) { 395 power_supply_unregister(ecbat->bat_psy); 396 yoga_c630_psy_register_bat_psy(ecbat); 397 } 398 } 399 400 static int yoga_c630_psy_notify(struct notifier_block *nb, 401 unsigned long action, void *data) 402 { 403 struct yoga_c630_psy *ecbat = container_of(nb, struct yoga_c630_psy, nb); 404 405 switch (action) { 406 case LENOVO_EC_EVENT_BAT_INFO: 407 yoga_c630_ec_refresh_bat_info(ecbat); 408 break; 409 case LENOVO_EC_EVENT_BAT_ADPT_STATUS: 410 power_supply_changed(ecbat->adp_psy); 411 fallthrough; 412 case LENOVO_EC_EVENT_BAT_STATUS: 413 power_supply_changed(ecbat->bat_psy); 414 break; 415 } 416 417 return NOTIFY_OK; 418 } 419 420 static int yoga_c630_psy_probe(struct auxiliary_device *adev, 421 const struct auxiliary_device_id *id) 422 { 423 struct yoga_c630_ec *ec = adev->dev.platform_data; 424 struct power_supply_config adp_cfg = {}; 425 struct device *dev = &adev->dev; 426 struct yoga_c630_psy *ecbat; 427 int ret; 428 429 ecbat = devm_kzalloc(&adev->dev, sizeof(*ecbat), GFP_KERNEL); 430 if (!ecbat) 431 return -ENOMEM; 432 433 ecbat->ec = ec; 434 ecbat->dev = dev; 435 mutex_init(&ecbat->lock); 436 ecbat->fwnode = adev->dev.parent->fwnode; 437 ecbat->nb.notifier_call = yoga_c630_psy_notify; 438 439 auxiliary_set_drvdata(adev, ecbat); 440 441 adp_cfg.drv_data = ecbat; 442 adp_cfg.fwnode = ecbat->fwnode; 443 adp_cfg.supplied_to = (char **)&yoga_c630_psy_bat_psy_desc_mA.name; 444 adp_cfg.num_supplicants = 1; 445 ecbat->adp_psy = devm_power_supply_register_no_ws(dev, &yoga_c630_psy_adpt_psy_desc, &adp_cfg); 446 if (IS_ERR(ecbat->adp_psy)) { 447 dev_err(dev, "failed to register AC adapter supply\n"); 448 return PTR_ERR(ecbat->adp_psy); 449 } 450 451 scoped_guard(mutex, &ecbat->lock) { 452 ret = yoga_c630_psy_update_bat_info(ecbat); 453 if (ret) 454 goto err_unreg_bat; 455 456 ret = yoga_c630_psy_register_bat_psy(ecbat); 457 if (ret) 458 goto err_unreg_bat; 459 } 460 461 ret = yoga_c630_ec_register_notify(ecbat->ec, &ecbat->nb); 462 if (ret) 463 goto err_unreg_bat; 464 465 return 0; 466 467 err_unreg_bat: 468 power_supply_unregister(ecbat->bat_psy); 469 return ret; 470 } 471 472 static void yoga_c630_psy_remove(struct auxiliary_device *adev) 473 { 474 struct yoga_c630_psy *ecbat = auxiliary_get_drvdata(adev); 475 476 yoga_c630_ec_unregister_notify(ecbat->ec, &ecbat->nb); 477 power_supply_unregister(ecbat->bat_psy); 478 } 479 480 static const struct auxiliary_device_id yoga_c630_psy_id_table[] = { 481 { .name = YOGA_C630_MOD_NAME "." YOGA_C630_DEV_PSY, }, 482 {} 483 }; 484 MODULE_DEVICE_TABLE(auxiliary, yoga_c630_psy_id_table); 485 486 static struct auxiliary_driver yoga_c630_psy_driver = { 487 .name = YOGA_C630_DEV_PSY, 488 .id_table = yoga_c630_psy_id_table, 489 .probe = yoga_c630_psy_probe, 490 .remove = yoga_c630_psy_remove, 491 }; 492 493 module_auxiliary_driver(yoga_c630_psy_driver); 494 495 MODULE_DESCRIPTION("Lenovo Yoga C630 psy"); 496 MODULE_LICENSE("GPL"); 497