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.

at v6.7-rc7 317 lines 8.3 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. 4 * Copyright (c) 2023, Linaro Limited 5 */ 6#include <linux/delay.h> 7#include <linux/i2c.h> 8#include <linux/power_supply.h> 9#include <linux/regmap.h> 10 11#define REG_BATID 0x00 /* This one is very unclear */ 12 #define BATID_101 0x0101 /* 107kOhm */ 13 #define BATID_102 0x0102 /* 10kOhm */ 14#define REG_TEMPERATURE 0x06 15#define REG_VOLTAGE 0x08 16#define REG_FLAGS 0x0a 17 #define MM8013_FLAG_OTC BIT(15) 18 #define MM8013_FLAG_OTD BIT(14) 19 #define MM8013_FLAG_BATHI BIT(13) 20 #define MM8013_FLAG_BATLOW BIT(12) 21 #define MM8013_FLAG_CHG_INH BIT(11) 22 #define MM8013_FLAG_FC BIT(9) 23 #define MM8013_FLAG_CHG BIT(8) 24 #define MM8013_FLAG_OCC BIT(6) 25 #define MM8013_FLAG_ODC BIT(5) 26 #define MM8013_FLAG_OT BIT(4) 27 #define MM8013_FLAG_UT BIT(3) 28 #define MM8013_FLAG_DSG BIT(0) 29#define REG_FULL_CHARGE_CAPACITY 0x0e 30#define REG_NOMINAL_CHARGE_CAPACITY 0x0c 31#define REG_AVERAGE_CURRENT 0x14 32#define REG_AVERAGE_TIME_TO_EMPTY 0x16 33#define REG_AVERAGE_TIME_TO_FULL 0x18 34#define REG_MAX_LOAD_CURRENT 0x1e 35#define REG_CYCLE_COUNT 0x2a 36#define REG_STATE_OF_CHARGE 0x2c 37#define REG_DESIGN_CAPACITY 0x3c 38/* TODO: 0x62-0x68 seem to contain 'MM8013C' in a length-prefixed, non-terminated string */ 39 40#define DECIKELVIN_TO_DECIDEGC(t) (t - 2731) 41 42struct mm8013_chip { 43 struct i2c_client *client; 44 struct regmap *regmap; 45}; 46 47static int mm8013_checkdevice(struct mm8013_chip *chip) 48{ 49 int battery_id, ret; 50 u32 val; 51 52 ret = regmap_write(chip->regmap, REG_BATID, 0x0008); 53 if (ret < 0) 54 return ret; 55 56 ret = regmap_read(chip->regmap, REG_BATID, &val); 57 if (ret < 0) 58 return ret; 59 60 if (val == BATID_102) 61 battery_id = 2; 62 else if (val == BATID_101) 63 battery_id = 1; 64 else 65 return -EINVAL; 66 67 dev_dbg(&chip->client->dev, "battery_id: %d\n", battery_id); 68 69 return 0; 70} 71 72static enum power_supply_property mm8013_battery_props[] = { 73 POWER_SUPPLY_PROP_CAPACITY, 74 POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, 75 POWER_SUPPLY_PROP_CHARGE_FULL, 76 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 77 POWER_SUPPLY_PROP_CHARGE_NOW, 78 POWER_SUPPLY_PROP_CURRENT_MAX, 79 POWER_SUPPLY_PROP_CURRENT_NOW, 80 POWER_SUPPLY_PROP_CYCLE_COUNT, 81 POWER_SUPPLY_PROP_HEALTH, 82 POWER_SUPPLY_PROP_PRESENT, 83 POWER_SUPPLY_PROP_STATUS, 84 POWER_SUPPLY_PROP_TEMP, 85 POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 86 POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 87 POWER_SUPPLY_PROP_VOLTAGE_NOW, 88}; 89 90static int mm8013_get_property(struct power_supply *psy, 91 enum power_supply_property psp, 92 union power_supply_propval *val) 93{ 94 struct mm8013_chip *chip = psy->drv_data; 95 int ret = 0; 96 u32 regval; 97 98 switch (psp) { 99 case POWER_SUPPLY_PROP_CAPACITY: 100 ret = regmap_read(chip->regmap, REG_STATE_OF_CHARGE, &regval); 101 if (ret < 0) 102 return ret; 103 104 val->intval = regval; 105 break; 106 case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: 107 ret = regmap_read(chip->regmap, REG_FLAGS, &regval); 108 if (ret < 0) 109 return ret; 110 111 if (regval & MM8013_FLAG_CHG_INH) 112 val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE; 113 else 114 val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; 115 break; 116 case POWER_SUPPLY_PROP_CHARGE_FULL: 117 ret = regmap_read(chip->regmap, REG_FULL_CHARGE_CAPACITY, &regval); 118 if (ret < 0) 119 return ret; 120 121 val->intval = 1000 * regval; 122 break; 123 case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 124 ret = regmap_read(chip->regmap, REG_DESIGN_CAPACITY, &regval); 125 if (ret < 0) 126 return ret; 127 128 val->intval = 1000 * regval; 129 break; 130 case POWER_SUPPLY_PROP_CHARGE_NOW: 131 ret = regmap_read(chip->regmap, REG_NOMINAL_CHARGE_CAPACITY, &regval); 132 if (ret < 0) 133 return ret; 134 135 val->intval = 1000 * regval; 136 break; 137 case POWER_SUPPLY_PROP_CURRENT_MAX: 138 ret = regmap_read(chip->regmap, REG_MAX_LOAD_CURRENT, &regval); 139 if (ret < 0) 140 return ret; 141 142 val->intval = -1000 * (s16)regval; 143 break; 144 case POWER_SUPPLY_PROP_CURRENT_NOW: 145 ret = regmap_read(chip->regmap, REG_AVERAGE_CURRENT, &regval); 146 if (ret < 0) 147 return ret; 148 149 val->intval = -1000 * (s16)regval; 150 break; 151 case POWER_SUPPLY_PROP_CYCLE_COUNT: 152 ret = regmap_read(chip->regmap, REG_CYCLE_COUNT, &regval); 153 if (ret < 0) 154 return ret; 155 156 val->intval = regval; 157 break; 158 case POWER_SUPPLY_PROP_HEALTH: 159 ret = regmap_read(chip->regmap, REG_FLAGS, &regval); 160 if (ret < 0) 161 return ret; 162 163 if (regval & MM8013_FLAG_UT) 164 val->intval = POWER_SUPPLY_HEALTH_COLD; 165 else if (regval & (MM8013_FLAG_ODC | MM8013_FLAG_OCC)) 166 val->intval = POWER_SUPPLY_HEALTH_OVERCURRENT; 167 else if (regval & (MM8013_FLAG_BATLOW)) 168 val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 169 else if (regval & MM8013_FLAG_BATHI) 170 val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 171 else if (regval & (MM8013_FLAG_OT | MM8013_FLAG_OTD | MM8013_FLAG_OTC)) 172 val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 173 else 174 val->intval = POWER_SUPPLY_HEALTH_GOOD; 175 break; 176 case POWER_SUPPLY_PROP_PRESENT: 177 ret = regmap_read(chip->regmap, REG_TEMPERATURE, &regval); 178 if (ret < 0) 179 return ret; 180 181 val->intval = ((s16)regval > 0); 182 break; 183 case POWER_SUPPLY_PROP_STATUS: 184 ret = regmap_read(chip->regmap, REG_FLAGS, &regval); 185 if (ret < 0) 186 return ret; 187 188 if (regval & MM8013_FLAG_DSG) 189 val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 190 else if (regval & MM8013_FLAG_CHG) 191 val->intval = POWER_SUPPLY_STATUS_CHARGING; 192 else if (regval & MM8013_FLAG_FC) 193 val->intval = POWER_SUPPLY_STATUS_FULL; 194 else 195 val->intval = POWER_SUPPLY_STATUS_UNKNOWN; 196 break; 197 case POWER_SUPPLY_PROP_TEMP: 198 ret = regmap_read(chip->regmap, REG_TEMPERATURE, &regval); 199 if (ret < 0) 200 return ret; 201 202 val->intval = DECIKELVIN_TO_DECIDEGC(regval); 203 break; 204 case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: 205 ret = regmap_read(chip->regmap, REG_AVERAGE_TIME_TO_EMPTY, &regval); 206 if (ret < 0) 207 return ret; 208 209 /* The estimation is not yet ready */ 210 if (regval == U16_MAX) 211 return -ENODATA; 212 213 val->intval = regval; 214 break; 215 case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: 216 ret = regmap_read(chip->regmap, REG_AVERAGE_TIME_TO_FULL, &regval); 217 if (ret < 0) 218 return ret; 219 220 /* The estimation is not yet ready */ 221 if (regval == U16_MAX) 222 return -ENODATA; 223 224 val->intval = regval; 225 break; 226 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 227 ret = regmap_read(chip->regmap, REG_VOLTAGE, &regval); 228 if (ret < 0) 229 return ret; 230 231 val->intval = 1000 * regval; 232 break; 233 default: 234 return -EINVAL; 235 } 236 237 return 0; 238} 239 240static const struct power_supply_desc mm8013_desc = { 241 .name = "mm8013", 242 .type = POWER_SUPPLY_TYPE_BATTERY, 243 .properties = mm8013_battery_props, 244 .num_properties = ARRAY_SIZE(mm8013_battery_props), 245 .get_property = mm8013_get_property, 246}; 247 248static const struct regmap_config mm8013_regmap_config = { 249 .reg_bits = 8, 250 .val_bits = 16, 251 .max_register = 0x68, 252 .use_single_read = true, 253 .use_single_write = true, 254 .val_format_endian = REGMAP_ENDIAN_LITTLE, 255}; 256 257static int mm8013_probe(struct i2c_client *client) 258{ 259 struct power_supply_config psy_cfg = {}; 260 struct device *dev = &client->dev; 261 struct power_supply *psy; 262 struct mm8013_chip *chip; 263 int ret = 0; 264 265 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) 266 return dev_err_probe(dev, -EIO, 267 "I2C_FUNC_SMBUS_WORD_DATA not supported\n"); 268 269 chip = devm_kzalloc(dev, sizeof(struct mm8013_chip), GFP_KERNEL); 270 if (!chip) 271 return -ENOMEM; 272 273 chip->client = client; 274 275 chip->regmap = devm_regmap_init_i2c(client, &mm8013_regmap_config); 276 if (IS_ERR(chip->regmap)) { 277 ret = PTR_ERR(chip->regmap); 278 return dev_err_probe(dev, ret, "Couldn't initialize regmap\n"); 279 } 280 281 ret = mm8013_checkdevice(chip); 282 if (ret) 283 return dev_err_probe(dev, ret, "MM8013 not found\n"); 284 285 psy_cfg.drv_data = chip; 286 psy_cfg.of_node = dev->of_node; 287 288 psy = devm_power_supply_register(dev, &mm8013_desc, &psy_cfg); 289 if (IS_ERR(psy)) 290 return PTR_ERR(psy); 291 292 return 0; 293} 294 295static const struct i2c_device_id mm8013_id_table[] = { 296 { "mm8013", 0 }, 297 {} 298}; 299MODULE_DEVICE_TABLE(i2c, mm8013_id_table); 300 301static const struct of_device_id mm8013_match_table[] = { 302 { .compatible = "mitsumi,mm8013" }, 303 {} 304}; 305 306static struct i2c_driver mm8013_i2c_driver = { 307 .probe = mm8013_probe, 308 .id_table = mm8013_id_table, 309 .driver = { 310 .name = "mm8013", 311 .of_match_table = mm8013_match_table, 312 }, 313}; 314module_i2c_driver(mm8013_i2c_driver); 315 316MODULE_DESCRIPTION("MM8013 fuel gauge driver"); 317MODULE_LICENSE("GPL");