296 lines · c
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 21enum {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 34static 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 45static 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 56struct a500_battery {57 struct delayed_work poll_work;58 struct power_supply *psy;59 struct regmap *regmap;60 unsigned int capacity;61};62 63static 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 83static 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 else89 return POWER_SUPPLY_STATUS_DISCHARGING;90 }91 92 return POWER_SUPPLY_STATUS_FULL;93}94 95static 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 123static 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 if130 * battery is connected and zero if disconnected, hence we'll use131 * 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 145static 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 199static 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 214static 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 223static 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 237 bat->regmap = dev_get_regmap(pdev->dev.parent, "KB930");238 if (!bat->regmap)239 return -EINVAL;240 241 bat->psy = devm_power_supply_register_no_ws(&pdev->dev,242 &a500_battery_desc,243 &psy_cfg);244 if (IS_ERR(bat->psy))245 return dev_err_probe(&pdev->dev, PTR_ERR(bat->psy),246 "failed to register battery\n");247 248 INIT_DELAYED_WORK(&bat->poll_work, a500_battery_poll_work);249 schedule_delayed_work(&bat->poll_work, HZ);250 251 return 0;252}253 254static void a500_battery_remove(struct platform_device *pdev)255{256 struct a500_battery *bat = dev_get_drvdata(&pdev->dev);257 258 cancel_delayed_work_sync(&bat->poll_work);259}260 261static int __maybe_unused a500_battery_suspend(struct device *dev)262{263 struct a500_battery *bat = dev_get_drvdata(dev);264 265 cancel_delayed_work_sync(&bat->poll_work);266 267 return 0;268}269 270static int __maybe_unused a500_battery_resume(struct device *dev)271{272 struct a500_battery *bat = dev_get_drvdata(dev);273 274 schedule_delayed_work(&bat->poll_work, HZ);275 276 return 0;277}278 279static SIMPLE_DEV_PM_OPS(a500_battery_pm_ops,280 a500_battery_suspend, a500_battery_resume);281 282static struct platform_driver a500_battery_driver = {283 .driver = {284 .name = "acer-a500-iconia-battery",285 .pm = &a500_battery_pm_ops,286 },287 .probe = a500_battery_probe,288 .remove_new = a500_battery_remove,289};290module_platform_driver(a500_battery_driver);291 292MODULE_DESCRIPTION("Battery gauge driver for Acer Iconia Tab A500");293MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");294MODULE_ALIAS("platform:acer-a500-iconia-battery");295MODULE_LICENSE("GPL");296