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

88pm860x_charger.c (19152B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Battery driver for Marvell 88PM860x PMIC
      4 *
      5 * Copyright (c) 2012 Marvell International Ltd.
      6 * Author:	Jett Zhou <jtzhou@marvell.com>
      7 *		Haojian Zhuang <haojian.zhuang@marvell.com>
      8 */
      9
     10#include <linux/kernel.h>
     11#include <linux/module.h>
     12#include <linux/platform_device.h>
     13#include <linux/slab.h>
     14#include <linux/power_supply.h>
     15#include <linux/mfd/88pm860x.h>
     16#include <linux/delay.h>
     17#include <linux/uaccess.h>
     18#include <asm/div64.h>
     19
     20/* bit definitions of Status Query Interface 2 */
     21#define STATUS2_CHG		(1 << 2)
     22
     23/* bit definitions of Reset Out Register */
     24#define RESET_SW_PD		(1 << 7)
     25
     26/* bit definitions of PreReg 1 */
     27#define PREREG1_90MA		(0x0)
     28#define PREREG1_180MA		(0x1)
     29#define PREREG1_450MA		(0x4)
     30#define PREREG1_540MA		(0x5)
     31#define PREREG1_1350MA		(0xE)
     32#define PREREG1_VSYS_4_5V	(3 << 4)
     33
     34/* bit definitions of Charger Control 1 Register */
     35#define CC1_MODE_OFF		(0)
     36#define CC1_MODE_PRECHARGE	(1)
     37#define CC1_MODE_FASTCHARGE	(2)
     38#define CC1_MODE_PULSECHARGE	(3)
     39#define CC1_ITERM_20MA		(0 << 2)
     40#define CC1_ITERM_60MA		(2 << 2)
     41#define CC1_VFCHG_4_2V		(9 << 4)
     42
     43/* bit definitions of Charger Control 2 Register */
     44#define CC2_ICHG_100MA		(0x1)
     45#define CC2_ICHG_500MA		(0x9)
     46#define CC2_ICHG_1000MA		(0x13)
     47
     48/* bit definitions of Charger Control 3 Register */
     49#define CC3_180MIN_TIMEOUT	(0x6 << 4)
     50#define CC3_270MIN_TIMEOUT	(0x7 << 4)
     51#define CC3_360MIN_TIMEOUT	(0xA << 4)
     52#define CC3_DISABLE_TIMEOUT	(0xF << 4)
     53
     54/* bit definitions of Charger Control 4 Register */
     55#define CC4_IPRE_40MA		(7)
     56#define CC4_VPCHG_3_2V		(3 << 4)
     57#define CC4_IFCHG_MON_EN	(1 << 6)
     58#define CC4_BTEMP_MON_EN	(1 << 7)
     59
     60/* bit definitions of Charger Control 6 Register */
     61#define CC6_BAT_OV_EN		(1 << 2)
     62#define CC6_BAT_UV_EN		(1 << 3)
     63#define CC6_UV_VBAT_SET		(0x3 << 6)	/* 2.8v */
     64
     65/* bit definitions of Charger Control 7 Register */
     66#define CC7_BAT_REM_EN		(1 << 3)
     67#define CC7_IFSM_EN		(1 << 7)
     68
     69/* bit definitions of Measurement Enable 1 Register */
     70#define MEAS1_VBAT		(1 << 0)
     71
     72/* bit definitions of Measurement Enable 3 Register */
     73#define MEAS3_IBAT_EN		(1 << 0)
     74#define MEAS3_CC_EN		(1 << 2)
     75
     76#define FSM_INIT		0
     77#define FSM_DISCHARGE		1
     78#define FSM_PRECHARGE		2
     79#define FSM_FASTCHARGE		3
     80
     81#define PRECHARGE_THRESHOLD	3100
     82#define POWEROFF_THRESHOLD	3400
     83#define CHARGE_THRESHOLD	4000
     84#define DISCHARGE_THRESHOLD	4180
     85
     86/* over-temperature on PM8606 setting */
     87#define OVER_TEMP_FLAG		(1 << 6)
     88#define OVTEMP_AUTORECOVER	(1 << 3)
     89
     90/* over-voltage protect on vchg setting mv */
     91#define VCHG_NORMAL_LOW		4200
     92#define VCHG_NORMAL_CHECK	5800
     93#define VCHG_NORMAL_HIGH	6000
     94#define VCHG_OVP_LOW		5500
     95
     96struct pm860x_charger_info {
     97	struct pm860x_chip *chip;
     98	struct i2c_client *i2c;
     99	struct i2c_client *i2c_8606;
    100	struct device *dev;
    101
    102	struct power_supply *usb;
    103	struct mutex lock;
    104	int irq_nums;
    105	int irq[7];
    106	unsigned state:3;	/* fsm state */
    107	unsigned online:1;	/* usb charger */
    108	unsigned present:1;	/* battery present */
    109	unsigned allowed:1;
    110};
    111
    112static char *pm860x_supplied_to[] = {
    113	"battery-monitor",
    114};
    115
    116static int measure_vchg(struct pm860x_charger_info *info, int *data)
    117{
    118	unsigned char buf[2];
    119	int ret = 0;
    120
    121	ret = pm860x_bulk_read(info->i2c, PM8607_VCHG_MEAS1, 2, buf);
    122	if (ret < 0)
    123		return ret;
    124
    125	*data = ((buf[0] & 0xff) << 4) | (buf[1] & 0x0f);
    126	/* V_BATT_MEAS(mV) = value * 5 * 1.8 * 1000 / (2^12) */
    127	*data = ((*data & 0xfff) * 9 * 125) >> 9;
    128
    129	dev_dbg(info->dev, "%s, vchg: %d mv\n", __func__, *data);
    130
    131	return ret;
    132}
    133
    134static void set_vchg_threshold(struct pm860x_charger_info *info,
    135			       int min, int max)
    136{
    137	int data;
    138
    139	/* (tmp << 8) * / 5 / 1800 */
    140	if (min <= 0)
    141		data = 0;
    142	else
    143		data = (min << 5) / 1125;
    144	pm860x_reg_write(info->i2c, PM8607_VCHG_LOWTH, data);
    145	dev_dbg(info->dev, "VCHG_LOWTH:%dmv, 0x%x\n", min, data);
    146
    147	if (max <= 0)
    148		data = 0xff;
    149	else
    150		data = (max << 5) / 1125;
    151	pm860x_reg_write(info->i2c, PM8607_VCHG_HIGHTH, data);
    152	dev_dbg(info->dev, "VCHG_HIGHTH:%dmv, 0x%x\n", max, data);
    153
    154}
    155
    156static void set_vbatt_threshold(struct pm860x_charger_info *info,
    157				int min, int max)
    158{
    159	int data;
    160
    161	/* (tmp << 8) * 3 / 1800 */
    162	if (min <= 0)
    163		data = 0;
    164	else
    165		data = (min << 5) / 675;
    166	pm860x_reg_write(info->i2c, PM8607_VBAT_LOWTH, data);
    167	dev_dbg(info->dev, "VBAT Min:%dmv, LOWTH:0x%x\n", min, data);
    168
    169	if (max <= 0)
    170		data = 0xff;
    171	else
    172		data = (max << 5) / 675;
    173	pm860x_reg_write(info->i2c, PM8607_VBAT_HIGHTH, data);
    174	dev_dbg(info->dev, "VBAT Max:%dmv, HIGHTH:0x%x\n", max, data);
    175
    176	return;
    177}
    178
    179static int start_precharge(struct pm860x_charger_info *info)
    180{
    181	int ret;
    182
    183	dev_dbg(info->dev, "Start Pre-charging!\n");
    184	set_vbatt_threshold(info, 0, 0);
    185
    186	ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA,
    187			       PREREG1_1350MA | PREREG1_VSYS_4_5V);
    188	if (ret < 0)
    189		goto out;
    190	/* stop charging */
    191	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3,
    192			      CC1_MODE_OFF);
    193	if (ret < 0)
    194		goto out;
    195	/* set 270 minutes timeout */
    196	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4),
    197			      CC3_270MIN_TIMEOUT);
    198	if (ret < 0)
    199		goto out;
    200	/* set precharge current, termination voltage, IBAT & TBAT monitor */
    201	ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL4,
    202			       CC4_IPRE_40MA | CC4_VPCHG_3_2V |
    203			       CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN);
    204	if (ret < 0)
    205		goto out;
    206	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7,
    207			      CC7_BAT_REM_EN | CC7_IFSM_EN,
    208			      CC7_BAT_REM_EN | CC7_IFSM_EN);
    209	if (ret < 0)
    210		goto out;
    211	/* trigger precharge */
    212	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3,
    213			      CC1_MODE_PRECHARGE);
    214out:
    215	return ret;
    216}
    217
    218static int start_fastcharge(struct pm860x_charger_info *info)
    219{
    220	int ret;
    221
    222	dev_dbg(info->dev, "Start Fast-charging!\n");
    223
    224	/* set fastcharge termination current & voltage, disable charging */
    225	ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL1,
    226			       CC1_MODE_OFF | CC1_ITERM_60MA |
    227			       CC1_VFCHG_4_2V);
    228	if (ret < 0)
    229		goto out;
    230	ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA,
    231			       PREREG1_540MA | PREREG1_VSYS_4_5V);
    232	if (ret < 0)
    233		goto out;
    234	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f,
    235			      CC2_ICHG_500MA);
    236	if (ret < 0)
    237		goto out;
    238	/* set 270 minutes timeout */
    239	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4),
    240			      CC3_270MIN_TIMEOUT);
    241	if (ret < 0)
    242		goto out;
    243	/* set IBAT & TBAT monitor */
    244	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL4,
    245			      CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN,
    246			      CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN);
    247	if (ret < 0)
    248		goto out;
    249	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL6,
    250			      CC6_BAT_OV_EN | CC6_BAT_UV_EN |
    251			      CC6_UV_VBAT_SET,
    252			      CC6_BAT_OV_EN | CC6_BAT_UV_EN |
    253			      CC6_UV_VBAT_SET);
    254	if (ret < 0)
    255		goto out;
    256	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7,
    257			      CC7_BAT_REM_EN | CC7_IFSM_EN,
    258			      CC7_BAT_REM_EN | CC7_IFSM_EN);
    259	if (ret < 0)
    260		goto out;
    261	/* launch fast-charge */
    262	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3,
    263			      CC1_MODE_FASTCHARGE);
    264	/* vchg threshold setting */
    265	set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_NORMAL_HIGH);
    266out:
    267	return ret;
    268}
    269
    270static void stop_charge(struct pm860x_charger_info *info, int vbatt)
    271{
    272	dev_dbg(info->dev, "Stop charging!\n");
    273	pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, CC1_MODE_OFF);
    274	if (vbatt > CHARGE_THRESHOLD && info->online)
    275		set_vbatt_threshold(info, CHARGE_THRESHOLD, 0);
    276}
    277
    278static void power_off_notification(struct pm860x_charger_info *info)
    279{
    280	dev_dbg(info->dev, "Power-off notification!\n");
    281}
    282
    283static int set_charging_fsm(struct pm860x_charger_info *info)
    284{
    285	struct power_supply *psy;
    286	union power_supply_propval data;
    287	unsigned char fsm_state[][16] = { "init", "discharge", "precharge",
    288		"fastcharge",
    289	};
    290	int ret;
    291	int vbatt;
    292
    293	psy = power_supply_get_by_name(pm860x_supplied_to[0]);
    294	if (!psy)
    295		return -EINVAL;
    296	ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW,
    297			&data);
    298	if (ret) {
    299		power_supply_put(psy);
    300		return ret;
    301	}
    302	vbatt = data.intval / 1000;
    303
    304	ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &data);
    305	if (ret) {
    306		power_supply_put(psy);
    307		return ret;
    308	}
    309	power_supply_put(psy);
    310
    311	mutex_lock(&info->lock);
    312	info->present = data.intval;
    313
    314	dev_dbg(info->dev, "Entering FSM:%s, Charger:%s, Battery:%s, "
    315		"Allowed:%d\n",
    316		&fsm_state[info->state][0],
    317		(info->online) ? "online" : "N/A",
    318		(info->present) ? "present" : "N/A", info->allowed);
    319	dev_dbg(info->dev, "set_charging_fsm:vbatt:%d(mV)\n", vbatt);
    320
    321	switch (info->state) {
    322	case FSM_INIT:
    323		if (info->online && info->present && info->allowed) {
    324			if (vbatt < PRECHARGE_THRESHOLD) {
    325				info->state = FSM_PRECHARGE;
    326				start_precharge(info);
    327			} else if (vbatt > DISCHARGE_THRESHOLD) {
    328				info->state = FSM_DISCHARGE;
    329				stop_charge(info, vbatt);
    330			} else if (vbatt < DISCHARGE_THRESHOLD) {
    331				info->state = FSM_FASTCHARGE;
    332				start_fastcharge(info);
    333			}
    334		} else {
    335			if (vbatt < POWEROFF_THRESHOLD) {
    336				power_off_notification(info);
    337			} else {
    338				info->state = FSM_DISCHARGE;
    339				stop_charge(info, vbatt);
    340			}
    341		}
    342		break;
    343	case FSM_PRECHARGE:
    344		if (info->online && info->present && info->allowed) {
    345			if (vbatt > PRECHARGE_THRESHOLD) {
    346				info->state = FSM_FASTCHARGE;
    347				start_fastcharge(info);
    348			}
    349		} else {
    350			info->state = FSM_DISCHARGE;
    351			stop_charge(info, vbatt);
    352		}
    353		break;
    354	case FSM_FASTCHARGE:
    355		if (info->online && info->present && info->allowed) {
    356			if (vbatt < PRECHARGE_THRESHOLD) {
    357				info->state = FSM_PRECHARGE;
    358				start_precharge(info);
    359			}
    360		} else {
    361			info->state = FSM_DISCHARGE;
    362			stop_charge(info, vbatt);
    363		}
    364		break;
    365	case FSM_DISCHARGE:
    366		if (info->online && info->present && info->allowed) {
    367			if (vbatt < PRECHARGE_THRESHOLD) {
    368				info->state = FSM_PRECHARGE;
    369				start_precharge(info);
    370			} else if (vbatt < DISCHARGE_THRESHOLD) {
    371				info->state = FSM_FASTCHARGE;
    372				start_fastcharge(info);
    373			}
    374		} else {
    375			if (vbatt < POWEROFF_THRESHOLD)
    376				power_off_notification(info);
    377			else if (vbatt > CHARGE_THRESHOLD && info->online)
    378				set_vbatt_threshold(info, CHARGE_THRESHOLD, 0);
    379		}
    380		break;
    381	default:
    382		dev_warn(info->dev, "FSM meets wrong state:%d\n",
    383			 info->state);
    384		break;
    385	}
    386	dev_dbg(info->dev,
    387		"Out FSM:%s, Charger:%s, Battery:%s, Allowed:%d\n",
    388		&fsm_state[info->state][0],
    389		(info->online) ? "online" : "N/A",
    390		(info->present) ? "present" : "N/A", info->allowed);
    391	mutex_unlock(&info->lock);
    392
    393	return 0;
    394}
    395
    396static irqreturn_t pm860x_charger_handler(int irq, void *data)
    397{
    398	struct pm860x_charger_info *info = data;
    399	int ret;
    400
    401	mutex_lock(&info->lock);
    402	ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
    403	if (ret < 0) {
    404		mutex_unlock(&info->lock);
    405		goto out;
    406	}
    407	if (ret & STATUS2_CHG) {
    408		info->online = 1;
    409		info->allowed = 1;
    410	} else {
    411		info->online = 0;
    412		info->allowed = 0;
    413	}
    414	mutex_unlock(&info->lock);
    415	dev_dbg(info->dev, "%s, Charger:%s, Allowed:%d\n", __func__,
    416		(info->online) ? "online" : "N/A", info->allowed);
    417
    418	set_charging_fsm(info);
    419
    420	power_supply_changed(info->usb);
    421out:
    422	return IRQ_HANDLED;
    423}
    424
    425static irqreturn_t pm860x_temp_handler(int irq, void *data)
    426{
    427	struct power_supply *psy;
    428	struct pm860x_charger_info *info = data;
    429	union power_supply_propval temp;
    430	int value;
    431	int ret;
    432
    433	psy = power_supply_get_by_name(pm860x_supplied_to[0]);
    434	if (!psy)
    435		return IRQ_HANDLED;
    436	ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_TEMP, &temp);
    437	if (ret)
    438		goto out;
    439	value = temp.intval / 10;
    440
    441	mutex_lock(&info->lock);
    442	/* Temperature < -10 C or >40 C, Will not allow charge */
    443	if (value < -10 || value > 40)
    444		info->allowed = 0;
    445	else
    446		info->allowed = 1;
    447	dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
    448	mutex_unlock(&info->lock);
    449
    450	set_charging_fsm(info);
    451out:
    452	power_supply_put(psy);
    453	return IRQ_HANDLED;
    454}
    455
    456static irqreturn_t pm860x_exception_handler(int irq, void *data)
    457{
    458	struct pm860x_charger_info *info = data;
    459
    460	mutex_lock(&info->lock);
    461	info->allowed = 0;
    462	mutex_unlock(&info->lock);
    463	dev_dbg(info->dev, "%s, irq: %d\n", __func__, irq);
    464
    465	set_charging_fsm(info);
    466	return IRQ_HANDLED;
    467}
    468
    469static irqreturn_t pm860x_done_handler(int irq, void *data)
    470{
    471	struct pm860x_charger_info *info = data;
    472	struct power_supply *psy;
    473	union power_supply_propval val;
    474	int ret;
    475	int vbatt;
    476
    477	mutex_lock(&info->lock);
    478	/* pre-charge done, will transimit to fast-charge stage */
    479	if (info->state == FSM_PRECHARGE) {
    480		info->allowed = 1;
    481		goto out;
    482	}
    483	/*
    484	 * Fast charge done, delay to read
    485	 * the correct status of CHG_DET.
    486	 */
    487	mdelay(5);
    488	info->allowed = 0;
    489	psy = power_supply_get_by_name(pm860x_supplied_to[0]);
    490	if (!psy)
    491		goto out;
    492	ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW,
    493			&val);
    494	if (ret)
    495		goto out_psy_put;
    496	vbatt = val.intval / 1000;
    497	/*
    498	 * CHG_DONE interrupt is faster than CHG_DET interrupt when
    499	 * plug in/out usb, So we can not rely on info->online, we
    500	 * need check pm8607 status register to check usb is online
    501	 * or not, then we can decide it is real charge done
    502	 * automatically or it is triggered by usb plug out;
    503	 */
    504	ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
    505	if (ret < 0)
    506		goto out_psy_put;
    507	if (vbatt > CHARGE_THRESHOLD && ret & STATUS2_CHG)
    508		power_supply_set_property(psy, POWER_SUPPLY_PROP_CHARGE_FULL,
    509				&val);
    510
    511out_psy_put:
    512	power_supply_put(psy);
    513out:
    514	mutex_unlock(&info->lock);
    515	dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
    516	set_charging_fsm(info);
    517
    518	return IRQ_HANDLED;
    519}
    520
    521static irqreturn_t pm860x_vbattery_handler(int irq, void *data)
    522{
    523	struct pm860x_charger_info *info = data;
    524
    525	mutex_lock(&info->lock);
    526
    527	set_vbatt_threshold(info, 0, 0);
    528
    529	if (info->present && info->online)
    530		info->allowed = 1;
    531	else
    532		info->allowed = 0;
    533	mutex_unlock(&info->lock);
    534	dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
    535
    536	set_charging_fsm(info);
    537
    538	return IRQ_HANDLED;
    539}
    540
    541static irqreturn_t pm860x_vchg_handler(int irq, void *data)
    542{
    543	struct pm860x_charger_info *info = data;
    544	int vchg = 0;
    545
    546	if (info->present)
    547		goto out;
    548
    549	measure_vchg(info, &vchg);
    550
    551	mutex_lock(&info->lock);
    552	if (!info->online) {
    553		int status;
    554		/* check if over-temp on pm8606 or not */
    555		status = pm860x_reg_read(info->i2c_8606, PM8606_FLAGS);
    556		if (status & OVER_TEMP_FLAG) {
    557			/* clear over temp flag and set auto recover */
    558			pm860x_set_bits(info->i2c_8606, PM8606_FLAGS,
    559					OVER_TEMP_FLAG, OVER_TEMP_FLAG);
    560			pm860x_set_bits(info->i2c_8606,
    561					PM8606_VSYS,
    562					OVTEMP_AUTORECOVER,
    563					OVTEMP_AUTORECOVER);
    564			dev_dbg(info->dev,
    565				"%s, pm8606 over-temp occurred\n", __func__);
    566		}
    567	}
    568
    569	if (vchg > VCHG_NORMAL_CHECK) {
    570		set_vchg_threshold(info, VCHG_OVP_LOW, 0);
    571		info->allowed = 0;
    572		dev_dbg(info->dev,
    573			"%s,pm8607 over-vchg occurred,vchg = %dmv\n",
    574			__func__, vchg);
    575	} else if (vchg < VCHG_OVP_LOW) {
    576		set_vchg_threshold(info, VCHG_NORMAL_LOW,
    577				   VCHG_NORMAL_HIGH);
    578		info->allowed = 1;
    579		dev_dbg(info->dev,
    580			"%s,pm8607 over-vchg recover,vchg = %dmv\n",
    581			__func__, vchg);
    582	}
    583	mutex_unlock(&info->lock);
    584
    585	dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
    586	set_charging_fsm(info);
    587out:
    588	return IRQ_HANDLED;
    589}
    590
    591static int pm860x_usb_get_prop(struct power_supply *psy,
    592			       enum power_supply_property psp,
    593			       union power_supply_propval *val)
    594{
    595	struct pm860x_charger_info *info = power_supply_get_drvdata(psy);
    596
    597	switch (psp) {
    598	case POWER_SUPPLY_PROP_STATUS:
    599		if (info->state == FSM_FASTCHARGE ||
    600				info->state == FSM_PRECHARGE)
    601			val->intval = POWER_SUPPLY_STATUS_CHARGING;
    602		else
    603			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
    604		break;
    605	case POWER_SUPPLY_PROP_ONLINE:
    606		val->intval = info->online;
    607		break;
    608	default:
    609		return -ENODEV;
    610	}
    611	return 0;
    612}
    613
    614static enum power_supply_property pm860x_usb_props[] = {
    615	POWER_SUPPLY_PROP_STATUS,
    616	POWER_SUPPLY_PROP_ONLINE,
    617};
    618
    619static int pm860x_init_charger(struct pm860x_charger_info *info)
    620{
    621	int ret;
    622
    623	ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
    624	if (ret < 0)
    625		return ret;
    626
    627	mutex_lock(&info->lock);
    628	info->state = FSM_INIT;
    629	if (ret & STATUS2_CHG) {
    630		info->online = 1;
    631		info->allowed = 1;
    632	} else {
    633		info->online = 0;
    634		info->allowed = 0;
    635	}
    636	mutex_unlock(&info->lock);
    637
    638	set_charging_fsm(info);
    639	return 0;
    640}
    641
    642static struct pm860x_irq_desc {
    643	const char *name;
    644	irqreturn_t (*handler)(int irq, void *data);
    645} pm860x_irq_descs[] = {
    646	{ "usb supply detect", pm860x_charger_handler },
    647	{ "charge done", pm860x_done_handler },
    648	{ "charge timeout", pm860x_exception_handler },
    649	{ "charge fault", pm860x_exception_handler },
    650	{ "temperature", pm860x_temp_handler },
    651	{ "vbatt", pm860x_vbattery_handler },
    652	{ "vchg", pm860x_vchg_handler },
    653};
    654
    655static const struct power_supply_desc pm860x_charger_desc = {
    656	.name		= "usb",
    657	.type		= POWER_SUPPLY_TYPE_USB,
    658	.properties	= pm860x_usb_props,
    659	.num_properties	= ARRAY_SIZE(pm860x_usb_props),
    660	.get_property	= pm860x_usb_get_prop,
    661};
    662
    663static int pm860x_charger_probe(struct platform_device *pdev)
    664{
    665	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
    666	struct power_supply_config psy_cfg = {};
    667	struct pm860x_charger_info *info;
    668	int ret;
    669	int count;
    670	int i;
    671	int j;
    672
    673	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
    674	if (!info)
    675		return -ENOMEM;
    676
    677	count = pdev->num_resources;
    678	for (i = 0, j = 0; i < count; i++) {
    679		info->irq[j] = platform_get_irq(pdev, i);
    680		if (info->irq[j] < 0)
    681			continue;
    682		j++;
    683	}
    684	info->irq_nums = j;
    685
    686	info->chip = chip;
    687	info->i2c =
    688	    (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
    689	info->i2c_8606 =
    690	    (chip->id == CHIP_PM8607) ? chip->companion : chip->client;
    691	if (!info->i2c_8606) {
    692		dev_err(&pdev->dev, "Missed I2C address of 88PM8606!\n");
    693		ret = -EINVAL;
    694		goto out;
    695	}
    696	info->dev = &pdev->dev;
    697
    698	/* set init value for the case we are not using battery */
    699	set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_OVP_LOW);
    700
    701	mutex_init(&info->lock);
    702	platform_set_drvdata(pdev, info);
    703
    704	psy_cfg.drv_data = info;
    705	psy_cfg.supplied_to = pm860x_supplied_to;
    706	psy_cfg.num_supplicants = ARRAY_SIZE(pm860x_supplied_to);
    707	info->usb = power_supply_register(&pdev->dev, &pm860x_charger_desc,
    708					  &psy_cfg);
    709	if (IS_ERR(info->usb)) {
    710		ret = PTR_ERR(info->usb);
    711		goto out;
    712	}
    713
    714	pm860x_init_charger(info);
    715
    716	for (i = 0; i < ARRAY_SIZE(info->irq); i++) {
    717		ret = request_threaded_irq(info->irq[i], NULL,
    718			pm860x_irq_descs[i].handler,
    719			IRQF_ONESHOT, pm860x_irq_descs[i].name, info);
    720		if (ret < 0) {
    721			dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
    722				info->irq[i], ret);
    723			goto out_irq;
    724		}
    725	}
    726	return 0;
    727
    728out_irq:
    729	power_supply_unregister(info->usb);
    730	while (--i >= 0)
    731		free_irq(info->irq[i], info);
    732out:
    733	return ret;
    734}
    735
    736static int pm860x_charger_remove(struct platform_device *pdev)
    737{
    738	struct pm860x_charger_info *info = platform_get_drvdata(pdev);
    739	int i;
    740
    741	power_supply_unregister(info->usb);
    742	for (i = 0; i < info->irq_nums; i++)
    743		free_irq(info->irq[i], info);
    744	return 0;
    745}
    746
    747static struct platform_driver pm860x_charger_driver = {
    748	.driver = {
    749		   .name = "88pm860x-charger",
    750	},
    751	.probe = pm860x_charger_probe,
    752	.remove = pm860x_charger_remove,
    753};
    754module_platform_driver(pm860x_charger_driver);
    755
    756MODULE_DESCRIPTION("Marvell 88PM860x Charger driver");
    757MODULE_LICENSE("GPL");