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

max8925_power.c (15232B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Battery driver for Maxim MAX8925
      4 *
      5 * Copyright (c) 2009-2010 Marvell International Ltd.
      6 *	Haojian Zhuang <haojian.zhuang@marvell.com>
      7 */
      8
      9#include <linux/module.h>
     10#include <linux/err.h>
     11#include <linux/slab.h>
     12#include <linux/of.h>
     13#include <linux/i2c.h>
     14#include <linux/interrupt.h>
     15#include <linux/platform_device.h>
     16#include <linux/power_supply.h>
     17#include <linux/mfd/max8925.h>
     18
     19/* registers in GPM */
     20#define MAX8925_OUT5VEN			0x54
     21#define MAX8925_OUT3VEN			0x58
     22#define MAX8925_CHG_CNTL1		0x7c
     23
     24/* bits definition */
     25#define MAX8925_CHG_STAT_VSYSLOW	(1 << 0)
     26#define MAX8925_CHG_STAT_MODE_MASK	(3 << 2)
     27#define MAX8925_CHG_STAT_EN_MASK	(1 << 4)
     28#define MAX8925_CHG_MBDET		(1 << 1)
     29#define MAX8925_CHG_AC_RANGE_MASK	(3 << 6)
     30
     31/* registers in ADC */
     32#define MAX8925_ADC_RES_CNFG1		0x06
     33#define MAX8925_ADC_AVG_CNFG1		0x07
     34#define MAX8925_ADC_ACQ_CNFG1		0x08
     35#define MAX8925_ADC_ACQ_CNFG2		0x09
     36/* 2 bytes registers in below. MSB is 1st, LSB is 2nd. */
     37#define MAX8925_ADC_AUX2		0x62
     38#define MAX8925_ADC_VCHG		0x64
     39#define MAX8925_ADC_VBBATT		0x66
     40#define MAX8925_ADC_VMBATT		0x68
     41#define MAX8925_ADC_ISNS		0x6a
     42#define MAX8925_ADC_THM			0x6c
     43#define MAX8925_ADC_TDIE		0x6e
     44#define MAX8925_CMD_AUX2		0xc8
     45#define MAX8925_CMD_VCHG		0xd0
     46#define MAX8925_CMD_VBBATT		0xd8
     47#define MAX8925_CMD_VMBATT		0xe0
     48#define MAX8925_CMD_ISNS		0xe8
     49#define MAX8925_CMD_THM			0xf0
     50#define MAX8925_CMD_TDIE		0xf8
     51
     52enum {
     53	MEASURE_AUX2,
     54	MEASURE_VCHG,
     55	MEASURE_VBBATT,
     56	MEASURE_VMBATT,
     57	MEASURE_ISNS,
     58	MEASURE_THM,
     59	MEASURE_TDIE,
     60	MEASURE_MAX,
     61};
     62
     63struct max8925_power_info {
     64	struct max8925_chip	*chip;
     65	struct i2c_client	*gpm;
     66	struct i2c_client	*adc;
     67
     68	struct power_supply	*ac;
     69	struct power_supply	*usb;
     70	struct power_supply	*battery;
     71	int			irq_base;
     72	unsigned		ac_online:1;
     73	unsigned		usb_online:1;
     74	unsigned		bat_online:1;
     75	unsigned		chg_mode:2;
     76	unsigned		batt_detect:1;	/* detecing MB by ID pin */
     77	unsigned		topoff_threshold:2;
     78	unsigned		fast_charge:3;
     79	unsigned		no_temp_support:1;
     80	unsigned		no_insert_detect:1;
     81
     82	int (*set_charger) (int);
     83};
     84
     85static int __set_charger(struct max8925_power_info *info, int enable)
     86{
     87	struct max8925_chip *chip = info->chip;
     88	if (enable) {
     89		/* enable charger in platform */
     90		if (info->set_charger)
     91			info->set_charger(1);
     92		/* enable charger */
     93		max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 0);
     94	} else {
     95		/* disable charge */
     96		max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 1 << 7);
     97		if (info->set_charger)
     98			info->set_charger(0);
     99	}
    100	dev_dbg(chip->dev, "%s\n", (enable) ? "Enable charger"
    101		: "Disable charger");
    102	return 0;
    103}
    104
    105static irqreturn_t max8925_charger_handler(int irq, void *data)
    106{
    107	struct max8925_power_info *info = (struct max8925_power_info *)data;
    108	struct max8925_chip *chip = info->chip;
    109
    110	switch (irq - chip->irq_base) {
    111	case MAX8925_IRQ_VCHG_DC_R:
    112		info->ac_online = 1;
    113		__set_charger(info, 1);
    114		dev_dbg(chip->dev, "Adapter inserted\n");
    115		break;
    116	case MAX8925_IRQ_VCHG_DC_F:
    117		info->ac_online = 0;
    118		__set_charger(info, 0);
    119		dev_dbg(chip->dev, "Adapter removed\n");
    120		break;
    121	case MAX8925_IRQ_VCHG_THM_OK_F:
    122		/* Battery is not ready yet */
    123		dev_dbg(chip->dev, "Battery temperature is out of range\n");
    124		fallthrough;
    125	case MAX8925_IRQ_VCHG_DC_OVP:
    126		dev_dbg(chip->dev, "Error detection\n");
    127		__set_charger(info, 0);
    128		break;
    129	case MAX8925_IRQ_VCHG_THM_OK_R:
    130		/* Battery is ready now */
    131		dev_dbg(chip->dev, "Battery temperature is in range\n");
    132		break;
    133	case MAX8925_IRQ_VCHG_SYSLOW_R:
    134		/* VSYS is low */
    135		dev_info(chip->dev, "Sys power is too low\n");
    136		break;
    137	case MAX8925_IRQ_VCHG_SYSLOW_F:
    138		dev_dbg(chip->dev, "Sys power is above low threshold\n");
    139		break;
    140	case MAX8925_IRQ_VCHG_DONE:
    141		__set_charger(info, 0);
    142		dev_dbg(chip->dev, "Charging is done\n");
    143		break;
    144	case MAX8925_IRQ_VCHG_TOPOFF:
    145		dev_dbg(chip->dev, "Charging in top-off mode\n");
    146		break;
    147	case MAX8925_IRQ_VCHG_TMR_FAULT:
    148		__set_charger(info, 0);
    149		dev_dbg(chip->dev, "Safe timer is expired\n");
    150		break;
    151	case MAX8925_IRQ_VCHG_RST:
    152		__set_charger(info, 0);
    153		dev_dbg(chip->dev, "Charger is reset\n");
    154		break;
    155	}
    156	return IRQ_HANDLED;
    157}
    158
    159static int start_measure(struct max8925_power_info *info, int type)
    160{
    161	unsigned char buf[2] = {0, 0};
    162	int meas_cmd;
    163	int meas_reg = 0, ret;
    164
    165	switch (type) {
    166	case MEASURE_VCHG:
    167		meas_cmd = MAX8925_CMD_VCHG;
    168		meas_reg = MAX8925_ADC_VCHG;
    169		break;
    170	case MEASURE_VBBATT:
    171		meas_cmd = MAX8925_CMD_VBBATT;
    172		meas_reg = MAX8925_ADC_VBBATT;
    173		break;
    174	case MEASURE_VMBATT:
    175		meas_cmd = MAX8925_CMD_VMBATT;
    176		meas_reg = MAX8925_ADC_VMBATT;
    177		break;
    178	case MEASURE_ISNS:
    179		meas_cmd = MAX8925_CMD_ISNS;
    180		meas_reg = MAX8925_ADC_ISNS;
    181		break;
    182	default:
    183		return -EINVAL;
    184	}
    185
    186	max8925_reg_write(info->adc, meas_cmd, 0);
    187	max8925_bulk_read(info->adc, meas_reg, 2, buf);
    188	ret = ((buf[0]<<8) | buf[1]) >> 4;
    189
    190	return ret;
    191}
    192
    193static int max8925_ac_get_prop(struct power_supply *psy,
    194			       enum power_supply_property psp,
    195			       union power_supply_propval *val)
    196{
    197	struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent);
    198	int ret = 0;
    199
    200	switch (psp) {
    201	case POWER_SUPPLY_PROP_ONLINE:
    202		val->intval = info->ac_online;
    203		break;
    204	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
    205		if (info->ac_online) {
    206			ret = start_measure(info, MEASURE_VCHG);
    207			if (ret >= 0) {
    208				val->intval = ret * 2000;	/* unit is uV */
    209				goto out;
    210			}
    211		}
    212		ret = -ENODATA;
    213		break;
    214	default:
    215		ret = -ENODEV;
    216		break;
    217	}
    218out:
    219	return ret;
    220}
    221
    222static enum power_supply_property max8925_ac_props[] = {
    223	POWER_SUPPLY_PROP_ONLINE,
    224	POWER_SUPPLY_PROP_VOLTAGE_NOW,
    225};
    226
    227static int max8925_usb_get_prop(struct power_supply *psy,
    228				enum power_supply_property psp,
    229				union power_supply_propval *val)
    230{
    231	struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent);
    232	int ret = 0;
    233
    234	switch (psp) {
    235	case POWER_SUPPLY_PROP_ONLINE:
    236		val->intval = info->usb_online;
    237		break;
    238	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
    239		if (info->usb_online) {
    240			ret = start_measure(info, MEASURE_VCHG);
    241			if (ret >= 0) {
    242				val->intval = ret * 2000;	/* unit is uV */
    243				goto out;
    244			}
    245		}
    246		ret = -ENODATA;
    247		break;
    248	default:
    249		ret = -ENODEV;
    250		break;
    251	}
    252out:
    253	return ret;
    254}
    255
    256static enum power_supply_property max8925_usb_props[] = {
    257	POWER_SUPPLY_PROP_ONLINE,
    258	POWER_SUPPLY_PROP_VOLTAGE_NOW,
    259};
    260
    261static int max8925_bat_get_prop(struct power_supply *psy,
    262				enum power_supply_property psp,
    263				union power_supply_propval *val)
    264{
    265	struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent);
    266	int ret = 0;
    267
    268	switch (psp) {
    269	case POWER_SUPPLY_PROP_ONLINE:
    270		val->intval = info->bat_online;
    271		break;
    272	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
    273		if (info->bat_online) {
    274			ret = start_measure(info, MEASURE_VMBATT);
    275			if (ret >= 0) {
    276				val->intval = ret * 2000;	/* unit is uV */
    277				ret = 0;
    278				break;
    279			}
    280		}
    281		ret = -ENODATA;
    282		break;
    283	case POWER_SUPPLY_PROP_CURRENT_NOW:
    284		if (info->bat_online) {
    285			ret = start_measure(info, MEASURE_ISNS);
    286			if (ret >= 0) {
    287				/* assume r_sns is 0.02 */
    288				ret = ((ret * 6250) - 3125) /* uA */;
    289				val->intval = 0;
    290				if (ret > 0)
    291					val->intval = ret; /* unit is mA */
    292				ret = 0;
    293				break;
    294			}
    295		}
    296		ret = -ENODATA;
    297		break;
    298	case POWER_SUPPLY_PROP_CHARGE_TYPE:
    299		if (!info->bat_online) {
    300			ret = -ENODATA;
    301			break;
    302		}
    303		ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
    304		ret = (ret & MAX8925_CHG_STAT_MODE_MASK) >> 2;
    305		switch (ret) {
    306		case 1:
    307			val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
    308			break;
    309		case 0:
    310		case 2:
    311			val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
    312			break;
    313		case 3:
    314			val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
    315			break;
    316		}
    317		ret = 0;
    318		break;
    319	case POWER_SUPPLY_PROP_STATUS:
    320		if (!info->bat_online) {
    321			ret = -ENODATA;
    322			break;
    323		}
    324		ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
    325		if (info->usb_online || info->ac_online) {
    326			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
    327			if (ret & MAX8925_CHG_STAT_EN_MASK)
    328				val->intval = POWER_SUPPLY_STATUS_CHARGING;
    329		} else
    330			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
    331		ret = 0;
    332		break;
    333	default:
    334		ret = -ENODEV;
    335		break;
    336	}
    337	return ret;
    338}
    339
    340static enum power_supply_property max8925_battery_props[] = {
    341	POWER_SUPPLY_PROP_ONLINE,
    342	POWER_SUPPLY_PROP_VOLTAGE_NOW,
    343	POWER_SUPPLY_PROP_CURRENT_NOW,
    344	POWER_SUPPLY_PROP_CHARGE_TYPE,
    345	POWER_SUPPLY_PROP_STATUS,
    346};
    347
    348static const struct power_supply_desc ac_desc = {
    349	.name		= "max8925-ac",
    350	.type		= POWER_SUPPLY_TYPE_MAINS,
    351	.properties	= max8925_ac_props,
    352	.num_properties	= ARRAY_SIZE(max8925_ac_props),
    353	.get_property	= max8925_ac_get_prop,
    354};
    355
    356static const struct power_supply_desc usb_desc = {
    357	.name		= "max8925-usb",
    358	.type		= POWER_SUPPLY_TYPE_USB,
    359	.properties	= max8925_usb_props,
    360	.num_properties	= ARRAY_SIZE(max8925_usb_props),
    361	.get_property	= max8925_usb_get_prop,
    362};
    363
    364static const struct power_supply_desc battery_desc = {
    365	.name		= "max8925-battery",
    366	.type		= POWER_SUPPLY_TYPE_BATTERY,
    367	.properties	= max8925_battery_props,
    368	.num_properties	= ARRAY_SIZE(max8925_battery_props),
    369	.get_property	= max8925_bat_get_prop,
    370};
    371
    372#define REQUEST_IRQ(_irq, _name)					\
    373do {									\
    374	ret = request_threaded_irq(chip->irq_base + _irq, NULL,		\
    375				    max8925_charger_handler,		\
    376				    IRQF_ONESHOT, _name, info);		\
    377	if (ret)							\
    378		dev_err(chip->dev, "Failed to request IRQ #%d: %d\n",	\
    379			_irq, ret);					\
    380} while (0)
    381
    382static int max8925_init_charger(struct max8925_chip *chip,
    383					  struct max8925_power_info *info)
    384{
    385	int ret;
    386
    387	REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_OVP, "ac-ovp");
    388	if (!info->no_insert_detect) {
    389		REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_F, "ac-remove");
    390		REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_R, "ac-insert");
    391	}
    392	if (!info->no_temp_support) {
    393		REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_R, "batt-temp-in-range");
    394		REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_F, "batt-temp-out-range");
    395	}
    396	REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_F, "vsys-high");
    397	REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_R, "vsys-low");
    398	REQUEST_IRQ(MAX8925_IRQ_VCHG_RST, "charger-reset");
    399	REQUEST_IRQ(MAX8925_IRQ_VCHG_DONE, "charger-done");
    400	REQUEST_IRQ(MAX8925_IRQ_VCHG_TOPOFF, "charger-topoff");
    401	REQUEST_IRQ(MAX8925_IRQ_VCHG_TMR_FAULT, "charger-timer-expire");
    402
    403	info->usb_online = 0;
    404	info->bat_online = 0;
    405
    406	/* check for power - can miss interrupt at boot time */
    407	if (start_measure(info, MEASURE_VCHG) * 2000 > 500000)
    408		info->ac_online = 1;
    409	else
    410		info->ac_online = 0;
    411
    412	ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
    413	if (ret >= 0) {
    414		/*
    415		 * If battery detection is enabled, ID pin of battery is
    416		 * connected to MBDET pin of MAX8925. It could be used to
    417		 * detect battery presence.
    418		 * Otherwise, we have to assume that battery is always on.
    419		 */
    420		if (info->batt_detect)
    421			info->bat_online = (ret & MAX8925_CHG_MBDET) ? 0 : 1;
    422		else
    423			info->bat_online = 1;
    424		if (ret & MAX8925_CHG_AC_RANGE_MASK)
    425			info->ac_online = 1;
    426		else
    427			info->ac_online = 0;
    428	}
    429	/* disable charge */
    430	max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 1 << 7);
    431	/* set charging current in charge topoff mode */
    432	max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 3 << 5,
    433			 info->topoff_threshold << 5);
    434	/* set charing current in fast charge mode */
    435	max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 7, info->fast_charge);
    436
    437	return 0;
    438}
    439
    440static int max8925_deinit_charger(struct max8925_power_info *info)
    441{
    442	struct max8925_chip *chip = info->chip;
    443	int irq;
    444
    445	irq = chip->irq_base + MAX8925_IRQ_VCHG_DC_OVP;
    446	for (; irq <= chip->irq_base + MAX8925_IRQ_VCHG_TMR_FAULT; irq++)
    447		free_irq(irq, info);
    448
    449	return 0;
    450}
    451
    452#ifdef CONFIG_OF
    453static struct max8925_power_pdata *
    454max8925_power_dt_init(struct platform_device *pdev)
    455{
    456	struct device_node *nproot = pdev->dev.parent->of_node;
    457	struct device_node *np;
    458	int batt_detect;
    459	int topoff_threshold;
    460	int fast_charge;
    461	int no_temp_support;
    462	int no_insert_detect;
    463	struct max8925_power_pdata *pdata;
    464
    465	if (!nproot)
    466		return pdev->dev.platform_data;
    467
    468	np = of_get_child_by_name(nproot, "charger");
    469	if (!np) {
    470		dev_err(&pdev->dev, "failed to find charger node\n");
    471		return NULL;
    472	}
    473
    474	pdata = devm_kzalloc(&pdev->dev,
    475			sizeof(struct max8925_power_pdata),
    476			GFP_KERNEL);
    477	if (!pdata)
    478		goto ret;
    479
    480	of_property_read_u32(np, "topoff-threshold", &topoff_threshold);
    481	of_property_read_u32(np, "batt-detect", &batt_detect);
    482	of_property_read_u32(np, "fast-charge", &fast_charge);
    483	of_property_read_u32(np, "no-insert-detect", &no_insert_detect);
    484	of_property_read_u32(np, "no-temp-support", &no_temp_support);
    485
    486	pdata->batt_detect = batt_detect;
    487	pdata->fast_charge = fast_charge;
    488	pdata->topoff_threshold = topoff_threshold;
    489	pdata->no_insert_detect = no_insert_detect;
    490	pdata->no_temp_support = no_temp_support;
    491
    492ret:
    493	of_node_put(np);
    494	return pdata;
    495}
    496#else
    497static struct max8925_power_pdata *
    498max8925_power_dt_init(struct platform_device *pdev)
    499{
    500	return pdev->dev.platform_data;
    501}
    502#endif
    503
    504static int max8925_power_probe(struct platform_device *pdev)
    505{
    506	struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
    507	struct power_supply_config psy_cfg = {}; /* Only for ac and usb */
    508	struct max8925_power_pdata *pdata = NULL;
    509	struct max8925_power_info *info;
    510	int ret;
    511
    512	pdata = max8925_power_dt_init(pdev);
    513	if (!pdata) {
    514		dev_err(&pdev->dev, "platform data isn't assigned to "
    515			"power supply\n");
    516		return -EINVAL;
    517	}
    518
    519	info = devm_kzalloc(&pdev->dev, sizeof(struct max8925_power_info),
    520				GFP_KERNEL);
    521	if (!info)
    522		return -ENOMEM;
    523	info->chip = chip;
    524	info->gpm = chip->i2c;
    525	info->adc = chip->adc;
    526	platform_set_drvdata(pdev, info);
    527
    528	psy_cfg.supplied_to = pdata->supplied_to;
    529	psy_cfg.num_supplicants = pdata->num_supplicants;
    530
    531	info->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg);
    532	if (IS_ERR(info->ac)) {
    533		ret = PTR_ERR(info->ac);
    534		goto out;
    535	}
    536	info->ac->dev.parent = &pdev->dev;
    537
    538	info->usb = power_supply_register(&pdev->dev, &usb_desc, &psy_cfg);
    539	if (IS_ERR(info->usb)) {
    540		ret = PTR_ERR(info->usb);
    541		goto out_unregister_ac;
    542	}
    543	info->usb->dev.parent = &pdev->dev;
    544
    545	info->battery = power_supply_register(&pdev->dev, &battery_desc, NULL);
    546	if (IS_ERR(info->battery)) {
    547		ret = PTR_ERR(info->battery);
    548		goto out_unregister_usb;
    549	}
    550	info->battery->dev.parent = &pdev->dev;
    551
    552	info->batt_detect = pdata->batt_detect;
    553	info->topoff_threshold = pdata->topoff_threshold;
    554	info->fast_charge = pdata->fast_charge;
    555	info->set_charger = pdata->set_charger;
    556	info->no_temp_support = pdata->no_temp_support;
    557	info->no_insert_detect = pdata->no_insert_detect;
    558
    559	max8925_init_charger(chip, info);
    560	return 0;
    561out_unregister_usb:
    562	power_supply_unregister(info->usb);
    563out_unregister_ac:
    564	power_supply_unregister(info->ac);
    565out:
    566	return ret;
    567}
    568
    569static int max8925_power_remove(struct platform_device *pdev)
    570{
    571	struct max8925_power_info *info = platform_get_drvdata(pdev);
    572
    573	if (info) {
    574		power_supply_unregister(info->ac);
    575		power_supply_unregister(info->usb);
    576		power_supply_unregister(info->battery);
    577		max8925_deinit_charger(info);
    578	}
    579	return 0;
    580}
    581
    582static struct platform_driver max8925_power_driver = {
    583	.probe	= max8925_power_probe,
    584	.remove	= max8925_power_remove,
    585	.driver	= {
    586		.name	= "max8925-power",
    587	},
    588};
    589
    590module_platform_driver(max8925_power_driver);
    591
    592MODULE_LICENSE("GPL");
    593MODULE_DESCRIPTION("Power supply driver for MAX8925");
    594MODULE_ALIAS("platform:max8925-power");