1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Battery driver for Acer Iconia Tab A500. 4 * 5 * Copyright 2020 GRATE-driver project. 6 * 7 * Based on downstream driver from Acer Inc. 8 * Based on NVIDIA Gas Gauge driver for SBS Compliant Batteries. 9 * 10 * Copyright (c) 2010, NVIDIA Corporation. 11 */ 12 13 #include <linux/module.h> 14 #include <linux/platform_device.h> 15 #include <linux/power_supply.h> 16 #include <linux/regmap.h> 17 #include <linux/sched.h> 18 #include <linux/slab.h> 19 #include <linux/workqueue.h> 20 21 enum { 22 REG_CAPACITY, 23 REG_VOLTAGE, 24 REG_CURRENT, 25 REG_DESIGN_CAPACITY, 26 REG_TEMPERATURE, 27 }; 28 29 #define EC_DATA(_reg, _psp) { \ 30 .psp = POWER_SUPPLY_PROP_ ## _psp, \ 31 .reg = _reg, \ 32 } 33 34 static const struct battery_register { 35 enum power_supply_property psp; 36 unsigned int reg; 37 } ec_data[] = { 38 [REG_CAPACITY] = EC_DATA(0x00, CAPACITY), 39 [REG_VOLTAGE] = EC_DATA(0x01, VOLTAGE_NOW), 40 [REG_CURRENT] = EC_DATA(0x03, CURRENT_NOW), 41 [REG_DESIGN_CAPACITY] = EC_DATA(0x08, CHARGE_FULL_DESIGN), 42 [REG_TEMPERATURE] = EC_DATA(0x0a, TEMP), 43 }; 44 45 static const enum power_supply_property a500_battery_properties[] = { 46 POWER_SUPPLY_PROP_CAPACITY, 47 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 48 POWER_SUPPLY_PROP_CURRENT_NOW, 49 POWER_SUPPLY_PROP_PRESENT, 50 POWER_SUPPLY_PROP_STATUS, 51 POWER_SUPPLY_PROP_TECHNOLOGY, 52 POWER_SUPPLY_PROP_TEMP, 53 POWER_SUPPLY_PROP_VOLTAGE_NOW, 54 }; 55 56 struct a500_battery { 57 struct delayed_work poll_work; 58 struct power_supply *psy; 59 struct regmap *regmap; 60 unsigned int capacity; 61 }; 62 63 static bool a500_battery_update_capacity(struct a500_battery *bat) 64 { 65 unsigned int capacity; 66 int err; 67 68 err = regmap_read(bat->regmap, ec_data[REG_CAPACITY].reg, &capacity); 69 if (err) 70 return false; 71 72 /* capacity can be >100% even if max value is 100% */ 73 capacity = min(capacity, 100u); 74 75 if (bat->capacity != capacity) { 76 bat->capacity = capacity; 77 return true; 78 } 79 80 return false; 81 } 82 83 static int a500_battery_get_status(struct a500_battery *bat) 84 { 85 if (bat->capacity < 100) { 86 if (power_supply_am_i_supplied(bat->psy)) 87 return POWER_SUPPLY_STATUS_CHARGING; 88 else 89 return POWER_SUPPLY_STATUS_DISCHARGING; 90 } 91 92 return POWER_SUPPLY_STATUS_FULL; 93 } 94 95 static void a500_battery_unit_adjustment(struct device *dev, 96 enum power_supply_property psp, 97 union power_supply_propval *val) 98 { 99 const unsigned int base_unit_conversion = 1000; 100 const unsigned int temp_kelvin_to_celsius = 2731; 101 102 switch (psp) { 103 case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 104 case POWER_SUPPLY_PROP_CURRENT_NOW: 105 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 106 val->intval *= base_unit_conversion; 107 break; 108 109 case POWER_SUPPLY_PROP_TEMP: 110 val->intval -= temp_kelvin_to_celsius; 111 break; 112 113 case POWER_SUPPLY_PROP_PRESENT: 114 val->intval = !!val->intval; 115 break; 116 117 default: 118 dev_dbg(dev, 119 "%s: no need for unit conversion %d\n", __func__, psp); 120 } 121 } 122 123 static int a500_battery_get_ec_data_index(struct device *dev, 124 enum power_supply_property psp) 125 { 126 unsigned int i; 127 128 /* 129 * DESIGN_CAPACITY register always returns a non-zero value if 130 * battery is connected and zero if disconnected, hence we'll use 131 * it for judging the battery presence. 132 */ 133 if (psp == POWER_SUPPLY_PROP_PRESENT) 134 psp = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN; 135 136 for (i = 0; i < ARRAY_SIZE(ec_data); i++) 137 if (psp == ec_data[i].psp) 138 return i; 139 140 dev_dbg(dev, "%s: invalid property %u\n", __func__, psp); 141 142 return -EINVAL; 143 } 144 145 static int a500_battery_get_property(struct power_supply *psy, 146 enum power_supply_property psp, 147 union power_supply_propval *val) 148 { 149 struct a500_battery *bat = power_supply_get_drvdata(psy); 150 struct device *dev = psy->dev.parent; 151 int ret = 0; 152 153 switch (psp) { 154 case POWER_SUPPLY_PROP_STATUS: 155 val->intval = a500_battery_get_status(bat); 156 break; 157 158 case POWER_SUPPLY_PROP_TECHNOLOGY: 159 val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 160 break; 161 162 case POWER_SUPPLY_PROP_CAPACITY: 163 a500_battery_update_capacity(bat); 164 val->intval = bat->capacity; 165 break; 166 167 case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 168 case POWER_SUPPLY_PROP_CURRENT_NOW: 169 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 170 case POWER_SUPPLY_PROP_PRESENT: 171 case POWER_SUPPLY_PROP_TEMP: 172 ret = a500_battery_get_ec_data_index(dev, psp); 173 if (ret < 0) 174 break; 175 176 ret = regmap_read(bat->regmap, ec_data[ret].reg, &val->intval); 177 break; 178 179 default: 180 dev_err(dev, "%s: invalid property %u\n", __func__, psp); 181 return -EINVAL; 182 } 183 184 if (!ret) { 185 /* convert units to match requirements of power supply class */ 186 a500_battery_unit_adjustment(dev, psp, val); 187 } 188 189 dev_dbg(dev, "%s: property = %d, value = %x\n", 190 __func__, psp, val->intval); 191 192 /* return NODATA for properties if battery not presents */ 193 if (ret) 194 return -ENODATA; 195 196 return 0; 197 } 198 199 static void a500_battery_poll_work(struct work_struct *work) 200 { 201 struct a500_battery *bat; 202 bool capacity_changed; 203 204 bat = container_of(work, struct a500_battery, poll_work.work); 205 capacity_changed = a500_battery_update_capacity(bat); 206 207 if (capacity_changed) 208 power_supply_changed(bat->psy); 209 210 /* continuously send uevent notification */ 211 schedule_delayed_work(&bat->poll_work, 30 * HZ); 212 } 213 214 static const struct power_supply_desc a500_battery_desc = { 215 .name = "ec-battery", 216 .type = POWER_SUPPLY_TYPE_BATTERY, 217 .properties = a500_battery_properties, 218 .get_property = a500_battery_get_property, 219 .num_properties = ARRAY_SIZE(a500_battery_properties), 220 .external_power_changed = power_supply_changed, 221 }; 222 223 static int a500_battery_probe(struct platform_device *pdev) 224 { 225 struct power_supply_config psy_cfg = {}; 226 struct a500_battery *bat; 227 228 bat = devm_kzalloc(&pdev->dev, sizeof(*bat), GFP_KERNEL); 229 if (!bat) 230 return -ENOMEM; 231 232 platform_set_drvdata(pdev, bat); 233 234 psy_cfg.of_node = pdev->dev.parent->of_node; 235 psy_cfg.drv_data = bat; 236 psy_cfg.no_wakeup_source = true; 237 238 bat->regmap = dev_get_regmap(pdev->dev.parent, "KB930"); 239 if (!bat->regmap) 240 return -EINVAL; 241 242 bat->psy = devm_power_supply_register(&pdev->dev, 243 &a500_battery_desc, 244 &psy_cfg); 245 if (IS_ERR(bat->psy)) 246 return dev_err_probe(&pdev->dev, PTR_ERR(bat->psy), 247 "failed to register battery\n"); 248 249 INIT_DELAYED_WORK(&bat->poll_work, a500_battery_poll_work); 250 schedule_delayed_work(&bat->poll_work, HZ); 251 252 return 0; 253 } 254 255 static void a500_battery_remove(struct platform_device *pdev) 256 { 257 struct a500_battery *bat = dev_get_drvdata(&pdev->dev); 258 259 cancel_delayed_work_sync(&bat->poll_work); 260 } 261 262 static int __maybe_unused a500_battery_suspend(struct device *dev) 263 { 264 struct a500_battery *bat = dev_get_drvdata(dev); 265 266 cancel_delayed_work_sync(&bat->poll_work); 267 268 return 0; 269 } 270 271 static int __maybe_unused a500_battery_resume(struct device *dev) 272 { 273 struct a500_battery *bat = dev_get_drvdata(dev); 274 275 schedule_delayed_work(&bat->poll_work, HZ); 276 277 return 0; 278 } 279 280 static SIMPLE_DEV_PM_OPS(a500_battery_pm_ops, 281 a500_battery_suspend, a500_battery_resume); 282 283 static struct platform_driver a500_battery_driver = { 284 .driver = { 285 .name = "acer-a500-iconia-battery", 286 .pm = &a500_battery_pm_ops, 287 }, 288 .probe = a500_battery_probe, 289 .remove = a500_battery_remove, 290 }; 291 module_platform_driver(a500_battery_driver); 292 293 MODULE_DESCRIPTION("Battery gauge driver for Acer Iconia Tab A500"); 294 MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>"); 295 MODULE_ALIAS("platform:acer-a500-iconia-battery"); 296 MODULE_LICENSE("GPL"); 297