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

da9052-battery.c (15462B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Batttery Driver for Dialog DA9052 PMICs
      4 *
      5 * Copyright(c) 2011 Dialog Semiconductor Ltd.
      6 *
      7 * Author: David Dajun Chen <dchen@diasemi.com>
      8 */
      9
     10#include <linux/delay.h>
     11#include <linux/freezer.h>
     12#include <linux/fs.h>
     13#include <linux/jiffies.h>
     14#include <linux/module.h>
     15#include <linux/timer.h>
     16#include <linux/uaccess.h>
     17#include <linux/platform_device.h>
     18#include <linux/power_supply.h>
     19
     20#include <linux/mfd/da9052/da9052.h>
     21#include <linux/mfd/da9052/pdata.h>
     22#include <linux/mfd/da9052/reg.h>
     23
     24/* STATIC CONFIGURATION */
     25#define DA9052_BAT_CUTOFF_VOLT		2800
     26#define DA9052_BAT_TSH			62000
     27#define DA9052_BAT_LOW_CAP		4
     28#define DA9052_AVG_SZ			4
     29#define DA9052_VC_TBL_SZ		68
     30#define DA9052_VC_TBL_REF_SZ		3
     31
     32#define DA9052_ISET_USB_MASK		0x0F
     33#define DA9052_CHG_USB_ILIM_MASK	0x40
     34#define DA9052_CHG_LIM_COLS		16
     35
     36#define DA9052_MEAN(x, y)		((x + y) / 2)
     37
     38enum charger_type_enum {
     39	DA9052_NOCHARGER = 1,
     40	DA9052_CHARGER,
     41};
     42
     43static const u16 da9052_chg_current_lim[2][DA9052_CHG_LIM_COLS] = {
     44	{70,  80,  90,  100, 110, 120, 400,  450,
     45	 500, 550, 600, 650, 700, 900, 1100, 1300},
     46	{80,  90,  100, 110,  120,  400,  450,  500,
     47	 550, 600, 800, 1000, 1200, 1400, 1600, 1800},
     48};
     49
     50static const u16 vc_tbl_ref[3] = {10, 25, 40};
     51/* Lookup table for voltage vs capacity */
     52static u32 const vc_tbl[3][68][2] = {
     53	/* For temperature 10 degree Celsius */
     54	{
     55	{4082, 100}, {4036, 98},
     56	{4020, 96}, {4008, 95},
     57	{3997, 93}, {3983, 91},
     58	{3964, 90}, {3943, 88},
     59	{3926, 87}, {3912, 85},
     60	{3900, 84}, {3890, 82},
     61	{3881, 80}, {3873, 79},
     62	{3865, 77}, {3857, 76},
     63	{3848, 74}, {3839, 73},
     64	{3829, 71}, {3820, 70},
     65	{3811, 68}, {3802, 67},
     66	{3794, 65}, {3785, 64},
     67	{3778, 62}, {3770, 61},
     68	{3763, 59}, {3756, 58},
     69	{3750, 56}, {3744, 55},
     70	{3738, 53}, {3732, 52},
     71	{3727, 50}, {3722, 49},
     72	{3717, 47}, {3712, 46},
     73	{3708, 44}, {3703, 43},
     74	{3700, 41}, {3696, 40},
     75	{3693, 38}, {3691, 37},
     76	{3688, 35}, {3686, 34},
     77	{3683, 32}, {3681, 31},
     78	{3678, 29}, {3675, 28},
     79	{3672, 26}, {3669, 25},
     80	{3665, 23}, {3661, 22},
     81	{3656, 21}, {3651, 19},
     82	{3645, 18}, {3639, 16},
     83	{3631, 15}, {3622, 13},
     84	{3611, 12}, {3600, 10},
     85	{3587, 9}, {3572, 7},
     86	{3548, 6}, {3503, 5},
     87	{3420, 3}, {3268, 2},
     88	{2992, 1}, {2746, 0}
     89	},
     90	/* For temperature 25 degree Celsius */
     91	{
     92	{4102, 100}, {4065, 98},
     93	{4048, 96}, {4034, 95},
     94	{4021, 93}, {4011, 92},
     95	{4001, 90}, {3986, 88},
     96	{3968, 87}, {3952, 85},
     97	{3938, 84}, {3926, 82},
     98	{3916, 81}, {3908, 79},
     99	{3900, 77}, {3892, 76},
    100	{3883, 74}, {3874, 73},
    101	{3864, 71}, {3855, 70},
    102	{3846, 68}, {3836, 67},
    103	{3827, 65}, {3819, 64},
    104	{3810, 62}, {3801, 61},
    105	{3793, 59}, {3786, 58},
    106	{3778, 56}, {3772, 55},
    107	{3765, 53}, {3759, 52},
    108	{3754, 50}, {3748, 49},
    109	{3743, 47}, {3738, 46},
    110	{3733, 44}, {3728, 43},
    111	{3724, 41}, {3720, 40},
    112	{3716, 38}, {3712, 37},
    113	{3709, 35}, {3706, 34},
    114	{3703, 33}, {3701, 31},
    115	{3698, 30}, {3696, 28},
    116	{3693, 27}, {3690, 25},
    117	{3687, 24}, {3683, 22},
    118	{3680, 21}, {3675, 19},
    119	{3671, 18}, {3666, 17},
    120	{3660, 15}, {3654, 14},
    121	{3647, 12}, {3639, 11},
    122	{3630, 9}, {3621, 8},
    123	{3613, 6}, {3606, 5},
    124	{3597, 4}, {3582, 2},
    125	{3546, 1}, {2747, 0}
    126	},
    127	/* For temperature 40 degree Celsius */
    128	{
    129	{4114, 100}, {4081, 98},
    130	{4065, 96}, {4050, 95},
    131	{4036, 93}, {4024, 92},
    132	{4013, 90}, {4002, 88},
    133	{3990, 87}, {3976, 85},
    134	{3962, 84}, {3950, 82},
    135	{3939, 81}, {3930, 79},
    136	{3921, 77}, {3912, 76},
    137	{3902, 74}, {3893, 73},
    138	{3883, 71}, {3874, 70},
    139	{3865, 68}, {3856, 67},
    140	{3847, 65}, {3838, 64},
    141	{3829, 62}, {3820, 61},
    142	{3812, 59}, {3803, 58},
    143	{3795, 56}, {3787, 55},
    144	{3780, 53}, {3773, 52},
    145	{3767, 50}, {3761, 49},
    146	{3756, 47}, {3751, 46},
    147	{3746, 44}, {3741, 43},
    148	{3736, 41}, {3732, 40},
    149	{3728, 38}, {3724, 37},
    150	{3720, 35}, {3716, 34},
    151	{3713, 33}, {3710, 31},
    152	{3707, 30}, {3704, 28},
    153	{3701, 27}, {3698, 25},
    154	{3695, 24}, {3691, 22},
    155	{3686, 21}, {3681, 19},
    156	{3676, 18}, {3671, 17},
    157	{3666, 15}, {3661, 14},
    158	{3655, 12}, {3648, 11},
    159	{3640, 9}, {3632, 8},
    160	{3622, 6}, {3616, 5},
    161	{3611, 4}, {3604, 2},
    162	{3594, 1}, {2747, 0}
    163	}
    164};
    165
    166struct da9052_battery {
    167	struct da9052 *da9052;
    168	struct power_supply *psy;
    169	struct notifier_block nb;
    170	int charger_type;
    171	int status;
    172	int health;
    173};
    174
    175static inline int volt_reg_to_mV(int value)
    176{
    177	return ((value * 1000) / 512) + 2500;
    178}
    179
    180static inline int ichg_reg_to_mA(int value)
    181{
    182	return (value * 3900) / 1000;
    183}
    184
    185static int da9052_read_chgend_current(struct da9052_battery *bat,
    186				       int *current_mA)
    187{
    188	int ret;
    189
    190	if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING)
    191		return -EINVAL;
    192
    193	ret = da9052_reg_read(bat->da9052, DA9052_ICHG_END_REG);
    194	if (ret < 0)
    195		return ret;
    196
    197	*current_mA = ichg_reg_to_mA(ret & DA9052_ICHGEND_ICHGEND);
    198
    199	return 0;
    200}
    201
    202static int da9052_read_chg_current(struct da9052_battery *bat, int *current_mA)
    203{
    204	int ret;
    205
    206	if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING)
    207		return -EINVAL;
    208
    209	ret = da9052_reg_read(bat->da9052, DA9052_ICHG_AV_REG);
    210	if (ret < 0)
    211		return ret;
    212
    213	*current_mA = ichg_reg_to_mA(ret & DA9052_ICHGAV_ICHGAV);
    214
    215	return 0;
    216}
    217
    218static int da9052_bat_check_status(struct da9052_battery *bat, int *status)
    219{
    220	u8 v[2] = {0, 0};
    221	u8 bat_status;
    222	u8 chg_end;
    223	int ret;
    224	int chg_current;
    225	int chg_end_current;
    226	bool dcinsel;
    227	bool dcindet;
    228	bool vbussel;
    229	bool vbusdet;
    230	bool dc;
    231	bool vbus;
    232
    233	ret = da9052_group_read(bat->da9052, DA9052_STATUS_A_REG, 2, v);
    234	if (ret < 0)
    235		return ret;
    236
    237	bat_status = v[0];
    238	chg_end = v[1];
    239
    240	dcinsel = bat_status & DA9052_STATUSA_DCINSEL;
    241	dcindet = bat_status & DA9052_STATUSA_DCINDET;
    242	vbussel = bat_status & DA9052_STATUSA_VBUSSEL;
    243	vbusdet = bat_status & DA9052_STATUSA_VBUSDET;
    244	dc = dcinsel && dcindet;
    245	vbus = vbussel && vbusdet;
    246
    247	/* Preference to WALL(DCIN) charger unit */
    248	if (dc || vbus) {
    249		bat->charger_type = DA9052_CHARGER;
    250
    251		/* If charging end flag is set and Charging current is greater
    252		 * than charging end limit then battery is charging
    253		*/
    254		if ((chg_end & DA9052_STATUSB_CHGEND) != 0) {
    255			ret = da9052_read_chg_current(bat, &chg_current);
    256			if (ret < 0)
    257				return ret;
    258			ret = da9052_read_chgend_current(bat, &chg_end_current);
    259			if (ret < 0)
    260				return ret;
    261
    262			if (chg_current >= chg_end_current)
    263				bat->status = POWER_SUPPLY_STATUS_CHARGING;
    264			else
    265				bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
    266		} else {
    267			/* If Charging end flag is cleared then battery is
    268			 * charging
    269			*/
    270			bat->status = POWER_SUPPLY_STATUS_CHARGING;
    271		}
    272	} else if (dcindet || vbusdet) {
    273			bat->charger_type = DA9052_CHARGER;
    274			bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
    275	} else {
    276		bat->charger_type = DA9052_NOCHARGER;
    277		bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
    278	}
    279
    280	if (status != NULL)
    281		*status = bat->status;
    282	return 0;
    283}
    284
    285static int da9052_bat_read_volt(struct da9052_battery *bat, int *volt_mV)
    286{
    287	int volt;
    288
    289	volt = da9052_adc_manual_read(bat->da9052, DA9052_ADC_MAN_MUXSEL_VBAT);
    290	if (volt < 0)
    291		return volt;
    292
    293	*volt_mV = volt_reg_to_mV(volt);
    294
    295	return 0;
    296}
    297
    298static int da9052_bat_check_presence(struct da9052_battery *bat, int *illegal)
    299{
    300	int bat_temp;
    301
    302	bat_temp = da9052_adc_read_temp(bat->da9052);
    303	if (bat_temp < 0)
    304		return bat_temp;
    305
    306	if (bat_temp > DA9052_BAT_TSH)
    307		*illegal = 1;
    308	else
    309		*illegal = 0;
    310
    311	return 0;
    312}
    313
    314static int da9052_bat_interpolate(int vbat_lower, int  vbat_upper,
    315				   int level_lower, int level_upper,
    316				   int bat_voltage)
    317{
    318	int tmp;
    319
    320	tmp = ((level_upper - level_lower) * 1000) / (vbat_upper - vbat_lower);
    321	tmp = level_lower + (((bat_voltage - vbat_lower) * tmp) / 1000);
    322
    323	return tmp;
    324}
    325
    326static unsigned char da9052_determine_vc_tbl_index(unsigned char adc_temp)
    327{
    328	int i;
    329
    330	if (adc_temp <= vc_tbl_ref[0])
    331		return 0;
    332
    333	if (adc_temp > vc_tbl_ref[DA9052_VC_TBL_REF_SZ - 1])
    334		return DA9052_VC_TBL_REF_SZ - 1;
    335
    336	for (i = 0; i < DA9052_VC_TBL_REF_SZ - 1; i++) {
    337		if ((adc_temp > vc_tbl_ref[i]) &&
    338		    (adc_temp <= DA9052_MEAN(vc_tbl_ref[i], vc_tbl_ref[i + 1])))
    339				return i;
    340		if ((adc_temp > DA9052_MEAN(vc_tbl_ref[i], vc_tbl_ref[i + 1]))
    341		     && (adc_temp <= vc_tbl_ref[i]))
    342				return i + 1;
    343	}
    344	/*
    345	 * For some reason authors of the driver didn't presume that we can
    346	 * end up here. It might be OK, but might be not, no one knows for
    347	 * sure. Go check your battery, is it on fire?
    348	 */
    349	WARN_ON(1);
    350	return 0;
    351}
    352
    353static int da9052_bat_read_capacity(struct da9052_battery *bat, int *capacity)
    354{
    355	int adc_temp;
    356	int bat_voltage;
    357	int vbat_lower;
    358	int vbat_upper;
    359	int level_upper;
    360	int level_lower;
    361	int ret;
    362	int flag;
    363	int i = 0;
    364	int j;
    365
    366	ret = da9052_bat_read_volt(bat, &bat_voltage);
    367	if (ret < 0)
    368		return ret;
    369
    370	adc_temp = da9052_adc_read_temp(bat->da9052);
    371	if (adc_temp < 0)
    372		return adc_temp;
    373
    374	i = da9052_determine_vc_tbl_index(adc_temp);
    375
    376	if (bat_voltage >= vc_tbl[i][0][0]) {
    377		*capacity = 100;
    378		return 0;
    379	}
    380	if (bat_voltage <= vc_tbl[i][DA9052_VC_TBL_SZ - 1][0]) {
    381		*capacity = 0;
    382		return 0;
    383	}
    384	flag = 0;
    385
    386	for (j = 0; j < (DA9052_VC_TBL_SZ-1); j++) {
    387		if ((bat_voltage <= vc_tbl[i][j][0]) &&
    388		    (bat_voltage >= vc_tbl[i][j + 1][0])) {
    389			vbat_upper = vc_tbl[i][j][0];
    390			vbat_lower = vc_tbl[i][j + 1][0];
    391			level_upper = vc_tbl[i][j][1];
    392			level_lower = vc_tbl[i][j + 1][1];
    393			flag = 1;
    394			break;
    395		}
    396	}
    397	if (!flag)
    398		return -EIO;
    399
    400	*capacity = da9052_bat_interpolate(vbat_lower, vbat_upper, level_lower,
    401					   level_upper, bat_voltage);
    402
    403	return 0;
    404}
    405
    406static int da9052_bat_check_health(struct da9052_battery *bat, int *health)
    407{
    408	int ret;
    409	int bat_illegal;
    410	int capacity;
    411
    412	ret = da9052_bat_check_presence(bat, &bat_illegal);
    413	if (ret < 0)
    414		return ret;
    415
    416	if (bat_illegal) {
    417		bat->health = POWER_SUPPLY_HEALTH_UNKNOWN;
    418		return 0;
    419	}
    420
    421	if (bat->health != POWER_SUPPLY_HEALTH_OVERHEAT) {
    422		ret = da9052_bat_read_capacity(bat, &capacity);
    423		if (ret < 0)
    424			return ret;
    425		if (capacity < DA9052_BAT_LOW_CAP)
    426			bat->health = POWER_SUPPLY_HEALTH_DEAD;
    427		else
    428			bat->health = POWER_SUPPLY_HEALTH_GOOD;
    429	}
    430
    431	*health = bat->health;
    432
    433	return 0;
    434}
    435
    436static irqreturn_t da9052_bat_irq(int irq, void *data)
    437{
    438	struct da9052_battery *bat = data;
    439	int virq;
    440
    441	virq = regmap_irq_get_virq(bat->da9052->irq_data, irq);
    442	irq -= virq;
    443
    444	if (irq == DA9052_IRQ_CHGEND)
    445		bat->status = POWER_SUPPLY_STATUS_FULL;
    446	else
    447		da9052_bat_check_status(bat, NULL);
    448
    449	if (irq == DA9052_IRQ_CHGEND || irq == DA9052_IRQ_DCIN ||
    450	    irq == DA9052_IRQ_VBUS || irq == DA9052_IRQ_TBAT) {
    451		power_supply_changed(bat->psy);
    452	}
    453
    454	return IRQ_HANDLED;
    455}
    456
    457static int da9052_USB_current_notifier(struct notifier_block *nb,
    458					unsigned long events, void *data)
    459{
    460	u8 row;
    461	u8 col;
    462	int *current_mA = data;
    463	int ret;
    464	struct da9052_battery *bat = container_of(nb, struct da9052_battery,
    465						  nb);
    466
    467	if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING)
    468		return -EPERM;
    469
    470	ret = da9052_reg_read(bat->da9052, DA9052_CHGBUCK_REG);
    471	if (ret & DA9052_CHG_USB_ILIM_MASK)
    472		return -EPERM;
    473
    474	if (bat->da9052->chip_id == DA9052)
    475		row = 0;
    476	else
    477		row = 1;
    478
    479	if (*current_mA < da9052_chg_current_lim[row][0] ||
    480	    *current_mA > da9052_chg_current_lim[row][DA9052_CHG_LIM_COLS - 1])
    481		return -EINVAL;
    482
    483	for (col = 0; col <= DA9052_CHG_LIM_COLS - 1 ; col++) {
    484		if (*current_mA <= da9052_chg_current_lim[row][col])
    485			break;
    486	}
    487
    488	return da9052_reg_update(bat->da9052, DA9052_ISET_REG,
    489				 DA9052_ISET_USB_MASK, col);
    490}
    491
    492static int da9052_bat_get_property(struct power_supply *psy,
    493				    enum power_supply_property psp,
    494				    union power_supply_propval *val)
    495{
    496	int ret;
    497	int illegal;
    498	struct da9052_battery *bat = power_supply_get_drvdata(psy);
    499
    500	ret = da9052_bat_check_presence(bat, &illegal);
    501	if (ret < 0)
    502		return ret;
    503
    504	if (illegal && psp != POWER_SUPPLY_PROP_PRESENT)
    505		return -ENODEV;
    506
    507	switch (psp) {
    508	case POWER_SUPPLY_PROP_STATUS:
    509		ret = da9052_bat_check_status(bat, &val->intval);
    510		break;
    511	case POWER_SUPPLY_PROP_ONLINE:
    512		val->intval =
    513			(bat->charger_type == DA9052_NOCHARGER) ? 0 : 1;
    514		break;
    515	case POWER_SUPPLY_PROP_PRESENT:
    516		ret = da9052_bat_check_presence(bat, &val->intval);
    517		break;
    518	case POWER_SUPPLY_PROP_HEALTH:
    519		ret = da9052_bat_check_health(bat, &val->intval);
    520		break;
    521	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
    522		val->intval = DA9052_BAT_CUTOFF_VOLT * 1000;
    523		break;
    524	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
    525		ret = da9052_bat_read_volt(bat, &val->intval);
    526		break;
    527	case POWER_SUPPLY_PROP_CURRENT_AVG:
    528		ret = da9052_read_chg_current(bat, &val->intval);
    529		break;
    530	case POWER_SUPPLY_PROP_CAPACITY:
    531		ret = da9052_bat_read_capacity(bat, &val->intval);
    532		break;
    533	case POWER_SUPPLY_PROP_TEMP:
    534		val->intval = da9052_adc_read_temp(bat->da9052);
    535		ret = val->intval;
    536		break;
    537	case POWER_SUPPLY_PROP_TECHNOLOGY:
    538		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
    539		break;
    540	default:
    541		return -EINVAL;
    542	}
    543	return ret;
    544}
    545
    546static enum power_supply_property da9052_bat_props[] = {
    547	POWER_SUPPLY_PROP_STATUS,
    548	POWER_SUPPLY_PROP_ONLINE,
    549	POWER_SUPPLY_PROP_PRESENT,
    550	POWER_SUPPLY_PROP_HEALTH,
    551	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
    552	POWER_SUPPLY_PROP_VOLTAGE_AVG,
    553	POWER_SUPPLY_PROP_CURRENT_AVG,
    554	POWER_SUPPLY_PROP_CAPACITY,
    555	POWER_SUPPLY_PROP_TEMP,
    556	POWER_SUPPLY_PROP_TECHNOLOGY,
    557};
    558
    559static struct power_supply_desc psy_desc = {
    560	.name		= "da9052-bat",
    561	.type		= POWER_SUPPLY_TYPE_BATTERY,
    562	.properties	= da9052_bat_props,
    563	.num_properties	= ARRAY_SIZE(da9052_bat_props),
    564	.get_property	= da9052_bat_get_property,
    565};
    566
    567static char *da9052_bat_irqs[] = {
    568	"BATT TEMP",
    569	"DCIN DET",
    570	"DCIN REM",
    571	"VBUS DET",
    572	"VBUS REM",
    573	"CHG END",
    574};
    575
    576static int da9052_bat_irq_bits[] = {
    577	DA9052_IRQ_TBAT,
    578	DA9052_IRQ_DCIN,
    579	DA9052_IRQ_DCINREM,
    580	DA9052_IRQ_VBUS,
    581	DA9052_IRQ_VBUSREM,
    582	DA9052_IRQ_CHGEND,
    583};
    584
    585static s32 da9052_bat_probe(struct platform_device *pdev)
    586{
    587	struct da9052_pdata *pdata;
    588	struct da9052_battery *bat;
    589	struct power_supply_config psy_cfg = {};
    590	int ret;
    591	int i;
    592
    593	bat = devm_kzalloc(&pdev->dev, sizeof(struct da9052_battery),
    594				GFP_KERNEL);
    595	if (!bat)
    596		return -ENOMEM;
    597
    598	psy_cfg.drv_data = bat;
    599
    600	bat->da9052 = dev_get_drvdata(pdev->dev.parent);
    601	bat->charger_type = DA9052_NOCHARGER;
    602	bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
    603	bat->health = POWER_SUPPLY_HEALTH_UNKNOWN;
    604	bat->nb.notifier_call = da9052_USB_current_notifier;
    605
    606	pdata = bat->da9052->dev->platform_data;
    607	if (pdata != NULL && pdata->use_for_apm)
    608		psy_desc.use_for_apm = pdata->use_for_apm;
    609	else
    610		psy_desc.use_for_apm = 1;
    611
    612	for (i = 0; i < ARRAY_SIZE(da9052_bat_irqs); i++) {
    613		ret = da9052_request_irq(bat->da9052,
    614				da9052_bat_irq_bits[i], da9052_bat_irqs[i],
    615				da9052_bat_irq, bat);
    616
    617		if (ret != 0) {
    618			dev_err(bat->da9052->dev,
    619				"DA9052 failed to request %s IRQ: %d\n",
    620				da9052_bat_irqs[i], ret);
    621			goto err;
    622		}
    623	}
    624
    625	bat->psy = power_supply_register(&pdev->dev, &psy_desc, &psy_cfg);
    626	if (IS_ERR(bat->psy)) {
    627		ret = PTR_ERR(bat->psy);
    628		goto err;
    629	}
    630
    631	platform_set_drvdata(pdev, bat);
    632	return 0;
    633
    634err:
    635	while (--i >= 0)
    636		da9052_free_irq(bat->da9052, da9052_bat_irq_bits[i], bat);
    637
    638	return ret;
    639}
    640static int da9052_bat_remove(struct platform_device *pdev)
    641{
    642	int i;
    643	struct da9052_battery *bat = platform_get_drvdata(pdev);
    644
    645	for (i = 0; i < ARRAY_SIZE(da9052_bat_irqs); i++)
    646		da9052_free_irq(bat->da9052, da9052_bat_irq_bits[i], bat);
    647
    648	power_supply_unregister(bat->psy);
    649
    650	return 0;
    651}
    652
    653static struct platform_driver da9052_bat_driver = {
    654	.probe = da9052_bat_probe,
    655	.remove = da9052_bat_remove,
    656	.driver = {
    657		.name = "da9052-bat",
    658	},
    659};
    660module_platform_driver(da9052_bat_driver);
    661
    662MODULE_DESCRIPTION("DA9052 BAT Device Driver");
    663MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
    664MODULE_LICENSE("GPL");
    665MODULE_ALIAS("platform:da9052-bat");