cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

max77976_charger.c (13755B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * max77976_charger.c - Driver for the Maxim MAX77976 battery charger
      4 *
      5 * Copyright (C) 2021 Luca Ceresoli
      6 * Author: Luca Ceresoli <luca@lucaceresoli.net>
      7 */
      8
      9#include <linux/i2c.h>
     10#include <linux/module.h>
     11#include <linux/power_supply.h>
     12#include <linux/regmap.h>
     13
     14#define MAX77976_DRIVER_NAME	"max77976-charger"
     15#define MAX77976_CHIP_ID	0x76
     16
     17static const char *max77976_manufacturer	= "Maxim Integrated";
     18static const char *max77976_model		= "MAX77976";
     19
     20/* --------------------------------------------------------------------------
     21 * Register map
     22 */
     23
     24#define MAX77976_REG_CHIP_ID		0x00
     25#define MAX77976_REG_CHIP_REVISION	0x01
     26#define MAX77976_REG_CHG_INT_OK		0x12
     27#define MAX77976_REG_CHG_DETAILS_01	0x14
     28#define MAX77976_REG_CHG_CNFG_00	0x16
     29#define MAX77976_REG_CHG_CNFG_02	0x18
     30#define MAX77976_REG_CHG_CNFG_06	0x1c
     31#define MAX77976_REG_CHG_CNFG_09	0x1f
     32
     33/* CHG_DETAILS_01.CHG_DTLS values */
     34enum max77976_charging_state {
     35	MAX77976_CHARGING_PREQUALIFICATION = 0x0,
     36	MAX77976_CHARGING_FAST_CONST_CURRENT,
     37	MAX77976_CHARGING_FAST_CONST_VOLTAGE,
     38	MAX77976_CHARGING_TOP_OFF,
     39	MAX77976_CHARGING_DONE,
     40	MAX77976_CHARGING_RESERVED_05,
     41	MAX77976_CHARGING_TIMER_FAULT,
     42	MAX77976_CHARGING_SUSPENDED_QBATT_OFF,
     43	MAX77976_CHARGING_OFF,
     44	MAX77976_CHARGING_RESERVED_09,
     45	MAX77976_CHARGING_THERMAL_SHUTDOWN,
     46	MAX77976_CHARGING_WATCHDOG_EXPIRED,
     47	MAX77976_CHARGING_SUSPENDED_JEITA,
     48	MAX77976_CHARGING_SUSPENDED_THM_REMOVAL,
     49	MAX77976_CHARGING_SUSPENDED_PIN,
     50	MAX77976_CHARGING_RESERVED_0F,
     51};
     52
     53/* CHG_DETAILS_01.BAT_DTLS values */
     54enum max77976_battery_state {
     55	MAX77976_BATTERY_BATTERY_REMOVAL = 0x0,
     56	MAX77976_BATTERY_PREQUALIFICATION,
     57	MAX77976_BATTERY_TIMER_FAULT,
     58	MAX77976_BATTERY_REGULAR_VOLTAGE,
     59	MAX77976_BATTERY_LOW_VOLTAGE,
     60	MAX77976_BATTERY_OVERVOLTAGE,
     61	MAX77976_BATTERY_RESERVED,
     62	MAX77976_BATTERY_BATTERY_ONLY, // No valid adapter is present
     63};
     64
     65/* CHG_CNFG_00.MODE values */
     66enum max77976_mode {
     67	MAX77976_MODE_CHARGER_BUCK		= 0x5,
     68	MAX77976_MODE_BOOST			= 0x9,
     69};
     70
     71/* CHG_CNFG_02.CHG_CC: charge current limit, 100..5500 mA, 50 mA steps */
     72#define MAX77976_CHG_CC_STEP			  50000U
     73#define MAX77976_CHG_CC_MIN			 100000U
     74#define MAX77976_CHG_CC_MAX			5500000U
     75
     76/* CHG_CNFG_09.CHGIN_ILIM: input current limit, 100..3200 mA, 100 mA steps */
     77#define MAX77976_CHGIN_ILIM_STEP		 100000U
     78#define MAX77976_CHGIN_ILIM_MIN			 100000U
     79#define MAX77976_CHGIN_ILIM_MAX			3200000U
     80
     81enum max77976_field_idx {
     82	VERSION, REVISION,                      /* CHIP_REVISION */
     83	CHGIN_OK,                               /* CHG_INT_OK */
     84	BAT_DTLS, CHG_DTLS,                     /* CHG_DETAILS_01 */
     85	MODE,                                   /* CHG_CNFG_00 */
     86	CHG_CC,                                 /* CHG_CNFG_02 */
     87	CHGPROT,                                /* CHG_CNFG_06 */
     88	CHGIN_ILIM,                             /* CHG_CNFG_09 */
     89	MAX77976_N_REGMAP_FIELDS
     90};
     91
     92static const struct reg_field max77976_reg_field[MAX77976_N_REGMAP_FIELDS] = {
     93	[VERSION]        = REG_FIELD(MAX77976_REG_CHIP_REVISION,   4, 7),
     94	[REVISION]       = REG_FIELD(MAX77976_REG_CHIP_REVISION,   0, 3),
     95	[CHGIN_OK]       = REG_FIELD(MAX77976_REG_CHG_INT_OK,      6, 6),
     96	[CHG_DTLS]       = REG_FIELD(MAX77976_REG_CHG_DETAILS_01,  0, 3),
     97	[BAT_DTLS]       = REG_FIELD(MAX77976_REG_CHG_DETAILS_01,  4, 6),
     98	[MODE]           = REG_FIELD(MAX77976_REG_CHG_CNFG_00,     0, 3),
     99	[CHG_CC]         = REG_FIELD(MAX77976_REG_CHG_CNFG_02,     0, 6),
    100	[CHGPROT]        = REG_FIELD(MAX77976_REG_CHG_CNFG_06,     2, 3),
    101	[CHGIN_ILIM]     = REG_FIELD(MAX77976_REG_CHG_CNFG_09,     0, 5),
    102};
    103
    104static const struct regmap_config max77976_regmap_config = {
    105	.reg_bits = 8,
    106	.val_bits = 8,
    107	.max_register = 0x24,
    108};
    109
    110/* --------------------------------------------------------------------------
    111 * Data structures
    112 */
    113
    114struct max77976 {
    115	struct i2c_client	*client;
    116	struct regmap		*regmap;
    117	struct regmap_field	*rfield[MAX77976_N_REGMAP_FIELDS];
    118};
    119
    120/* --------------------------------------------------------------------------
    121 * power_supply properties
    122 */
    123
    124static int max77976_get_status(struct max77976 *chg, int *val)
    125{
    126	unsigned int regval;
    127	int err;
    128
    129	err = regmap_field_read(chg->rfield[CHG_DTLS], &regval);
    130	if (err < 0)
    131		return err;
    132
    133	switch (regval) {
    134	case MAX77976_CHARGING_PREQUALIFICATION:
    135	case MAX77976_CHARGING_FAST_CONST_CURRENT:
    136	case MAX77976_CHARGING_FAST_CONST_VOLTAGE:
    137	case MAX77976_CHARGING_TOP_OFF:
    138		*val = POWER_SUPPLY_STATUS_CHARGING;
    139		break;
    140	case MAX77976_CHARGING_DONE:
    141		*val = POWER_SUPPLY_STATUS_FULL;
    142		break;
    143	case MAX77976_CHARGING_TIMER_FAULT:
    144	case MAX77976_CHARGING_SUSPENDED_QBATT_OFF:
    145	case MAX77976_CHARGING_SUSPENDED_JEITA:
    146	case MAX77976_CHARGING_SUSPENDED_THM_REMOVAL:
    147	case MAX77976_CHARGING_SUSPENDED_PIN:
    148		*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
    149		break;
    150	case MAX77976_CHARGING_OFF:
    151	case MAX77976_CHARGING_THERMAL_SHUTDOWN:
    152	case MAX77976_CHARGING_WATCHDOG_EXPIRED:
    153		*val = POWER_SUPPLY_STATUS_DISCHARGING;
    154		break;
    155	default:
    156		*val = POWER_SUPPLY_STATUS_UNKNOWN;
    157	}
    158
    159	return 0;
    160}
    161
    162static int max77976_get_charge_type(struct max77976 *chg, int *val)
    163{
    164	unsigned int regval;
    165	int err;
    166
    167	err = regmap_field_read(chg->rfield[CHG_DTLS], &regval);
    168	if (err < 0)
    169		return err;
    170
    171	switch (regval) {
    172	case MAX77976_CHARGING_PREQUALIFICATION:
    173		*val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
    174		break;
    175	case MAX77976_CHARGING_FAST_CONST_CURRENT:
    176	case MAX77976_CHARGING_FAST_CONST_VOLTAGE:
    177		*val = POWER_SUPPLY_CHARGE_TYPE_FAST;
    178		break;
    179	case MAX77976_CHARGING_TOP_OFF:
    180		*val = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
    181		break;
    182	case MAX77976_CHARGING_DONE:
    183	case MAX77976_CHARGING_TIMER_FAULT:
    184	case MAX77976_CHARGING_SUSPENDED_QBATT_OFF:
    185	case MAX77976_CHARGING_OFF:
    186	case MAX77976_CHARGING_THERMAL_SHUTDOWN:
    187	case MAX77976_CHARGING_WATCHDOG_EXPIRED:
    188	case MAX77976_CHARGING_SUSPENDED_JEITA:
    189	case MAX77976_CHARGING_SUSPENDED_THM_REMOVAL:
    190	case MAX77976_CHARGING_SUSPENDED_PIN:
    191		*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
    192		break;
    193	default:
    194		*val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
    195	}
    196
    197	return 0;
    198}
    199
    200static int max77976_get_health(struct max77976 *chg, int *val)
    201{
    202	unsigned int regval;
    203	int err;
    204
    205	err = regmap_field_read(chg->rfield[BAT_DTLS], &regval);
    206	if (err < 0)
    207		return err;
    208
    209	switch (regval) {
    210	case MAX77976_BATTERY_BATTERY_REMOVAL:
    211		*val = POWER_SUPPLY_HEALTH_NO_BATTERY;
    212		break;
    213	case MAX77976_BATTERY_LOW_VOLTAGE:
    214	case MAX77976_BATTERY_REGULAR_VOLTAGE:
    215		*val = POWER_SUPPLY_HEALTH_GOOD;
    216		break;
    217	case MAX77976_BATTERY_TIMER_FAULT:
    218		*val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
    219		break;
    220	case MAX77976_BATTERY_OVERVOLTAGE:
    221		*val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
    222		break;
    223	case MAX77976_BATTERY_PREQUALIFICATION:
    224	case MAX77976_BATTERY_BATTERY_ONLY:
    225		*val = POWER_SUPPLY_HEALTH_UNKNOWN;
    226		break;
    227	default:
    228		*val = POWER_SUPPLY_HEALTH_UNKNOWN;
    229	}
    230
    231	return 0;
    232}
    233
    234static int max77976_get_online(struct max77976 *chg, int *val)
    235{
    236	unsigned int regval;
    237	int err;
    238
    239	err = regmap_field_read(chg->rfield[CHGIN_OK], &regval);
    240	if (err < 0)
    241		return err;
    242
    243	*val = (regval ? 1 : 0);
    244
    245	return 0;
    246}
    247
    248static int max77976_get_integer(struct max77976 *chg, enum max77976_field_idx fidx,
    249				unsigned int clamp_min, unsigned int clamp_max,
    250				unsigned int mult, int *val)
    251{
    252	unsigned int regval;
    253	int err;
    254
    255	err = regmap_field_read(chg->rfield[fidx], &regval);
    256	if (err < 0)
    257		return err;
    258
    259	*val = clamp_val(regval * mult, clamp_min, clamp_max);
    260
    261	return 0;
    262}
    263
    264static int max77976_set_integer(struct max77976 *chg, enum max77976_field_idx fidx,
    265				unsigned int clamp_min, unsigned int clamp_max,
    266				unsigned int div, int val)
    267{
    268	unsigned int regval;
    269
    270	regval = clamp_val(val, clamp_min, clamp_max) / div;
    271
    272	return regmap_field_write(chg->rfield[fidx], regval);
    273}
    274
    275static int max77976_get_property(struct power_supply *psy,
    276				 enum power_supply_property psp,
    277				 union power_supply_propval *val)
    278{
    279	struct max77976 *chg = power_supply_get_drvdata(psy);
    280	int err = 0;
    281
    282	switch (psp) {
    283	case POWER_SUPPLY_PROP_STATUS:
    284		err = max77976_get_status(chg, &val->intval);
    285		break;
    286	case POWER_SUPPLY_PROP_CHARGE_TYPE:
    287		err = max77976_get_charge_type(chg, &val->intval);
    288		break;
    289	case POWER_SUPPLY_PROP_HEALTH:
    290		err = max77976_get_health(chg, &val->intval);
    291		break;
    292	case POWER_SUPPLY_PROP_ONLINE:
    293		err = max77976_get_online(chg, &val->intval);
    294		break;
    295	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
    296		val->intval = MAX77976_CHG_CC_MAX;
    297		break;
    298	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
    299		err = max77976_get_integer(chg, CHG_CC,
    300					   MAX77976_CHG_CC_MIN,
    301					   MAX77976_CHG_CC_MAX,
    302					   MAX77976_CHG_CC_STEP,
    303					   &val->intval);
    304		break;
    305	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
    306		err = max77976_get_integer(chg, CHGIN_ILIM,
    307					   MAX77976_CHGIN_ILIM_MIN,
    308					   MAX77976_CHGIN_ILIM_MAX,
    309					   MAX77976_CHGIN_ILIM_STEP,
    310					   &val->intval);
    311		break;
    312	case POWER_SUPPLY_PROP_MODEL_NAME:
    313		val->strval = max77976_model;
    314		break;
    315	case POWER_SUPPLY_PROP_MANUFACTURER:
    316		val->strval = max77976_manufacturer;
    317		break;
    318	default:
    319		err = -EINVAL;
    320	}
    321
    322	return err;
    323}
    324
    325static int max77976_set_property(struct power_supply *psy,
    326				 enum power_supply_property psp,
    327				 const union power_supply_propval *val)
    328{
    329	struct max77976 *chg = power_supply_get_drvdata(psy);
    330	int err = 0;
    331
    332	switch (psp) {
    333	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
    334		err = max77976_set_integer(chg, CHG_CC,
    335					   MAX77976_CHG_CC_MIN,
    336					   MAX77976_CHG_CC_MAX,
    337					   MAX77976_CHG_CC_STEP,
    338					   val->intval);
    339		break;
    340	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
    341		err = max77976_set_integer(chg, CHGIN_ILIM,
    342					   MAX77976_CHGIN_ILIM_MIN,
    343					   MAX77976_CHGIN_ILIM_MAX,
    344					   MAX77976_CHGIN_ILIM_STEP,
    345					   val->intval);
    346		break;
    347	default:
    348		err = -EINVAL;
    349	}
    350
    351	return err;
    352};
    353
    354static int max77976_property_is_writeable(struct power_supply *psy,
    355					  enum power_supply_property psp)
    356{
    357	switch (psp) {
    358	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
    359	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
    360		return true;
    361	default:
    362		return false;
    363	}
    364}
    365
    366static enum power_supply_property max77976_psy_props[] = {
    367	POWER_SUPPLY_PROP_STATUS,
    368	POWER_SUPPLY_PROP_CHARGE_TYPE,
    369	POWER_SUPPLY_PROP_HEALTH,
    370	POWER_SUPPLY_PROP_ONLINE,
    371	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
    372	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
    373	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
    374	POWER_SUPPLY_PROP_MODEL_NAME,
    375	POWER_SUPPLY_PROP_MANUFACTURER,
    376};
    377
    378static const struct power_supply_desc max77976_psy_desc = {
    379	.name			= MAX77976_DRIVER_NAME,
    380	.type			= POWER_SUPPLY_TYPE_USB,
    381	.properties		= max77976_psy_props,
    382	.num_properties		= ARRAY_SIZE(max77976_psy_props),
    383	.get_property		= max77976_get_property,
    384	.set_property		= max77976_set_property,
    385	.property_is_writeable	= max77976_property_is_writeable,
    386};
    387
    388/* --------------------------------------------------------------------------
    389 * Entry point
    390 */
    391
    392static int max77976_detect(struct max77976 *chg)
    393{
    394	struct device *dev = &chg->client->dev;
    395	unsigned int id, ver, rev;
    396	int err;
    397
    398	err = regmap_read(chg->regmap, MAX77976_REG_CHIP_ID, &id);
    399	if (err)
    400		return dev_err_probe(dev, err, "cannot read chip ID\n");
    401
    402	if (id != MAX77976_CHIP_ID)
    403		return dev_err_probe(dev, -ENXIO, "unknown model ID 0x%02x\n", id);
    404
    405	err = regmap_field_read(chg->rfield[VERSION], &ver);
    406	if (!err)
    407		err = regmap_field_read(chg->rfield[REVISION], &rev);
    408	if (err)
    409		return dev_err_probe(dev, -ENXIO, "cannot read version/revision\n");
    410
    411	dev_info(dev, "detected model MAX779%02x ver %u rev %u", id, ver, rev);
    412
    413	return 0;
    414}
    415
    416static int max77976_configure(struct max77976 *chg)
    417{
    418	struct device *dev = &chg->client->dev;
    419	int err;
    420
    421	/* Magic value to unlock writing to some registers */
    422	err = regmap_field_write(chg->rfield[CHGPROT], 0x3);
    423	if (err)
    424		goto err;
    425
    426	/*
    427	 * Mode 5 = Charger ON, OTG OFF, buck ON, boost OFF.
    428	 * Other modes are not implemented by this driver.
    429	 */
    430	err = regmap_field_write(chg->rfield[MODE], MAX77976_MODE_CHARGER_BUCK);
    431	if (err)
    432		goto err;
    433
    434	return 0;
    435
    436err:
    437	return dev_err_probe(dev, err, "error while configuring");
    438}
    439
    440static int max77976_probe(struct i2c_client *client)
    441{
    442	struct device *dev = &client->dev;
    443	struct power_supply_config psy_cfg = {};
    444	struct power_supply *psy;
    445	struct max77976 *chg;
    446	int err;
    447	int i;
    448
    449	chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL);
    450	if (!chg)
    451		return -ENOMEM;
    452
    453	i2c_set_clientdata(client, chg);
    454	psy_cfg.drv_data = chg;
    455	chg->client = client;
    456
    457	chg->regmap = devm_regmap_init_i2c(client, &max77976_regmap_config);
    458	if (IS_ERR(chg->regmap))
    459		return dev_err_probe(dev, PTR_ERR(chg->regmap),
    460				     "cannot allocate regmap\n");
    461
    462	for (i = 0; i < MAX77976_N_REGMAP_FIELDS; i++) {
    463		chg->rfield[i] = devm_regmap_field_alloc(dev, chg->regmap,
    464							 max77976_reg_field[i]);
    465		if (IS_ERR(chg->rfield[i]))
    466			return dev_err_probe(dev, PTR_ERR(chg->rfield[i]),
    467					     "cannot allocate regmap field\n");
    468	}
    469
    470	err = max77976_detect(chg);
    471	if (err)
    472		return err;
    473
    474	err = max77976_configure(chg);
    475	if (err)
    476		return err;
    477
    478	psy = devm_power_supply_register_no_ws(dev, &max77976_psy_desc, &psy_cfg);
    479	if (IS_ERR(psy))
    480		return dev_err_probe(dev, PTR_ERR(psy), "cannot register\n");
    481
    482	return 0;
    483}
    484
    485static const struct i2c_device_id max77976_i2c_id[] = {
    486	{ MAX77976_DRIVER_NAME, 0 },
    487	{ },
    488};
    489MODULE_DEVICE_TABLE(i2c, max77976_i2c_id);
    490
    491static const struct of_device_id max77976_of_id[] = {
    492	{ .compatible = "maxim,max77976" },
    493	{ },
    494};
    495MODULE_DEVICE_TABLE(of, max77976_of_id);
    496
    497static struct i2c_driver max77976_driver = {
    498	.driver = {
    499		.name		= MAX77976_DRIVER_NAME,
    500		.of_match_table	= max77976_of_id,
    501	},
    502	.probe_new	= max77976_probe,
    503	.id_table	= max77976_i2c_id,
    504};
    505module_i2c_driver(max77976_driver);
    506
    507MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>");
    508MODULE_DESCRIPTION("Maxim MAX77976 charger driver");
    509MODULE_LICENSE("GPL v2");