brintos

brintos / linux-shallow public Read only

0
0
Text · 15.1 KiB · 0d84c42 Raw
662 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Batttery Driver for Dialog DA9052 PMICs4 *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		280026#define DA9052_BAT_TSH			6200027#define DA9052_BAT_LOW_CAP		428#define DA9052_AVG_SZ			429#define DA9052_VC_TBL_SZ		6830#define DA9052_VC_TBL_REF_SZ		331 32#define DA9052_ISET_USB_MASK		0x0F33#define DA9052_CHG_USB_ILIM_MASK	0x4034#define DA9052_CHG_LIM_COLS		1635 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 greater252		 * than charging end limit then battery is charging253		*/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			else265				bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;266		} else {267			/* If Charging end flag is cleared then battery is268			 * charging269			*/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	else309		*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 can346	 * end up here. It might be OK, but might be not, no one knows for347	 * 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		else428			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	else447		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	else477		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	else610		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 = devm_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 void 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 649static struct platform_driver da9052_bat_driver = {650	.probe = da9052_bat_probe,651	.remove_new = da9052_bat_remove,652	.driver = {653		.name = "da9052-bat",654	},655};656module_platform_driver(da9052_bat_driver);657 658MODULE_DESCRIPTION("DA9052 BAT Device Driver");659MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");660MODULE_LICENSE("GPL");661MODULE_ALIAS("platform:da9052-bat");662