brintos

brintos / linux-shallow public Read only

0
0
Text · 15.5 KiB · 04e0f41 Raw
582 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Battery charger driver for Dialog Semiconductor DA90304 *5 * Copyright (C) 2008 Compulab, Ltd.6 * 	Mike Rapoport <mike@compulab.co.il>7 */8 9#include <linux/kernel.h>10#include <linux/slab.h>11#include <linux/init.h>12#include <linux/types.h>13#include <linux/device.h>14#include <linux/workqueue.h>15#include <linux/module.h>16#include <linux/platform_device.h>17#include <linux/power_supply.h>18#include <linux/mfd/da903x.h>19 20#include <linux/debugfs.h>21#include <linux/seq_file.h>22#include <linux/notifier.h>23 24#define DA9030_FAULT_LOG		0x0a25#define DA9030_FAULT_LOG_OVER_TEMP	(1 << 7)26#define DA9030_FAULT_LOG_VBAT_OVER	(1 << 4)27 28#define DA9030_CHARGE_CONTROL		0x2829#define DA9030_CHRG_CHARGER_ENABLE	(1 << 7)30 31#define DA9030_ADC_MAN_CONTROL		0x3032#define DA9030_ADC_TBATREF_ENABLE	(1 << 5)33#define DA9030_ADC_LDO_INT_ENABLE	(1 << 4)34 35#define DA9030_ADC_AUTO_CONTROL		0x3136#define DA9030_ADC_TBAT_ENABLE		(1 << 5)37#define DA9030_ADC_VBAT_IN_TXON		(1 << 4)38#define DA9030_ADC_VCH_ENABLE		(1 << 3)39#define DA9030_ADC_ICH_ENABLE		(1 << 2)40#define DA9030_ADC_VBAT_ENABLE		(1 << 1)41#define DA9030_ADC_AUTO_SLEEP_ENABLE	(1 << 0)42 43#define DA9030_VBATMON		0x3244#define DA9030_VBATMONTXON	0x3345#define DA9030_TBATHIGHP	0x3446#define DA9030_TBATHIGHN	0x3547#define DA9030_TBATLOW		0x3648 49#define DA9030_VBAT_RES		0x4150#define DA9030_VBATMIN_RES	0x4251#define DA9030_VBATMINTXON_RES	0x4352#define DA9030_ICHMAX_RES	0x4453#define DA9030_ICHMIN_RES	0x4554#define DA9030_ICHAVERAGE_RES	0x4655#define DA9030_VCHMAX_RES	0x4756#define DA9030_VCHMIN_RES	0x4857#define DA9030_TBAT_RES		0x4958 59struct da9030_adc_res {60	uint8_t vbat_res;61	uint8_t vbatmin_res;62	uint8_t vbatmintxon;63	uint8_t ichmax_res;64	uint8_t ichmin_res;65	uint8_t ichaverage_res;66	uint8_t vchmax_res;67	uint8_t vchmin_res;68	uint8_t tbat_res;69	uint8_t adc_in4_res;70	uint8_t adc_in5_res;71};72 73struct da9030_battery_thresholds {74	int tbat_low;75	int tbat_high;76	int tbat_restart;77 78	int vbat_low;79	int vbat_crit;80	int vbat_charge_start;81	int vbat_charge_stop;82	int vbat_charge_restart;83 84	int vcharge_min;85	int vcharge_max;86};87 88struct da9030_charger {89	struct power_supply *psy;90	struct power_supply_desc psy_desc;91 92	struct device *master;93 94	struct da9030_adc_res adc;95	struct delayed_work work;96	unsigned int interval;97 98	struct power_supply_info *battery_info;99 100	struct da9030_battery_thresholds thresholds;101 102	unsigned int charge_milliamp;103	unsigned int charge_millivolt;104 105	/* charger status */106	bool chdet;107	uint8_t fault;108	int mA;109	int mV;110	bool is_on;111 112	struct notifier_block nb;113 114	/* platform callbacks for battery low and critical events */115	void (*battery_low)(void);116	void (*battery_critical)(void);117 118	struct dentry *debug_file;119};120 121static inline int da9030_reg_to_mV(int reg)122{123	return ((reg * 2650) >> 8) + 2650;124}125 126static inline int da9030_millivolt_to_reg(int mV)127{128	return ((mV - 2650) << 8) / 2650;129}130 131static inline int da9030_reg_to_mA(int reg)132{133	return ((reg * 24000) >> 8) / 15;134}135 136#ifdef CONFIG_DEBUG_FS137static int bat_debug_show(struct seq_file *s, void *data)138{139	struct da9030_charger *charger = s->private;140 141	seq_printf(s, "charger is %s\n", charger->is_on ? "on" : "off");142	if (charger->chdet) {143		seq_printf(s, "iset = %dmA, vset = %dmV\n",144			   charger->mA, charger->mV);145	}146 147	seq_printf(s, "vbat_res = %d (%dmV)\n",148		   charger->adc.vbat_res,149		   da9030_reg_to_mV(charger->adc.vbat_res));150	seq_printf(s, "vbatmin_res = %d (%dmV)\n",151		   charger->adc.vbatmin_res,152		   da9030_reg_to_mV(charger->adc.vbatmin_res));153	seq_printf(s, "vbatmintxon = %d (%dmV)\n",154		   charger->adc.vbatmintxon,155		   da9030_reg_to_mV(charger->adc.vbatmintxon));156	seq_printf(s, "ichmax_res = %d (%dmA)\n",157		   charger->adc.ichmax_res,158		   da9030_reg_to_mV(charger->adc.ichmax_res));159	seq_printf(s, "ichmin_res = %d (%dmA)\n",160		   charger->adc.ichmin_res,161		   da9030_reg_to_mA(charger->adc.ichmin_res));162	seq_printf(s, "ichaverage_res = %d (%dmA)\n",163		   charger->adc.ichaverage_res,164		   da9030_reg_to_mA(charger->adc.ichaverage_res));165	seq_printf(s, "vchmax_res = %d (%dmV)\n",166		   charger->adc.vchmax_res,167		   da9030_reg_to_mA(charger->adc.vchmax_res));168	seq_printf(s, "vchmin_res = %d (%dmV)\n",169		   charger->adc.vchmin_res,170		   da9030_reg_to_mV(charger->adc.vchmin_res));171 172	return 0;173}174 175DEFINE_SHOW_ATTRIBUTE(bat_debug);176 177static struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)178{179	charger->debug_file = debugfs_create_file("charger", 0666, NULL,180						  charger, &bat_debug_fops);181	return charger->debug_file;182}183 184static void da9030_bat_remove_debugfs(struct da9030_charger *charger)185{186	debugfs_remove(charger->debug_file);187}188#else189static inline struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)190{191	return NULL;192}193static inline void da9030_bat_remove_debugfs(struct da9030_charger *charger)194{195}196#endif197 198static inline void da9030_read_adc(struct da9030_charger *charger,199				   struct da9030_adc_res *adc)200{201	da903x_reads(charger->master, DA9030_VBAT_RES,202		     sizeof(*adc), (uint8_t *)adc);203}204 205static void da9030_charger_update_state(struct da9030_charger *charger)206{207	uint8_t val;208 209	da903x_read(charger->master, DA9030_CHARGE_CONTROL, &val);210	charger->is_on = (val & DA9030_CHRG_CHARGER_ENABLE) ? 1 : 0;211	charger->mA = ((val >> 3) & 0xf) * 100;212	charger->mV = (val & 0x7) * 50 + 4000;213 214	da9030_read_adc(charger, &charger->adc);215	da903x_read(charger->master, DA9030_FAULT_LOG, &charger->fault);216	charger->chdet = da903x_query_status(charger->master,217						     DA9030_STATUS_CHDET);218}219 220static void da9030_set_charge(struct da9030_charger *charger, int on)221{222	uint8_t val;223 224	if (on) {225		val = DA9030_CHRG_CHARGER_ENABLE;226		val |= (charger->charge_milliamp / 100) << 3;227		val |= (charger->charge_millivolt - 4000) / 50;228		charger->is_on = 1;229	} else {230		val = 0;231		charger->is_on = 0;232	}233 234	da903x_write(charger->master, DA9030_CHARGE_CONTROL, val);235 236	power_supply_changed(charger->psy);237}238 239static void da9030_charger_check_state(struct da9030_charger *charger)240{241	da9030_charger_update_state(charger);242 243	/* we wake or boot with external power on */244	if (!charger->is_on) {245		if ((charger->chdet) &&246		    (charger->adc.vbat_res <247		     charger->thresholds.vbat_charge_start)) {248			da9030_set_charge(charger, 1);249		}250	} else {251		/* Charger has been pulled out */252		if (!charger->chdet) {253			da9030_set_charge(charger, 0);254			return;255		}256 257		if (charger->adc.vbat_res >=258		    charger->thresholds.vbat_charge_stop) {259			da9030_set_charge(charger, 0);260			da903x_write(charger->master, DA9030_VBATMON,261				       charger->thresholds.vbat_charge_restart);262		} else if (charger->adc.vbat_res >263			   charger->thresholds.vbat_low) {264			/* we are charging and passed LOW_THRESH,265			   so upate DA9030 VBAT threshold266			 */267			da903x_write(charger->master, DA9030_VBATMON,268				     charger->thresholds.vbat_low);269		}270		if (charger->adc.vchmax_res > charger->thresholds.vcharge_max ||271		    charger->adc.vchmin_res < charger->thresholds.vcharge_min ||272		    /* Tempreture readings are negative */273		    charger->adc.tbat_res < charger->thresholds.tbat_high ||274		    charger->adc.tbat_res > charger->thresholds.tbat_low) {275			/* disable charger */276			da9030_set_charge(charger, 0);277		}278	}279}280 281static void da9030_charging_monitor(struct work_struct *work)282{283	struct da9030_charger *charger;284 285	charger = container_of(work, struct da9030_charger, work.work);286 287	da9030_charger_check_state(charger);288 289	/* reschedule for the next time */290	schedule_delayed_work(&charger->work, charger->interval);291}292 293static enum power_supply_property da9030_battery_props[] = {294	POWER_SUPPLY_PROP_MODEL_NAME,295	POWER_SUPPLY_PROP_STATUS,296	POWER_SUPPLY_PROP_HEALTH,297	POWER_SUPPLY_PROP_TECHNOLOGY,298	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,299	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,300	POWER_SUPPLY_PROP_VOLTAGE_NOW,301	POWER_SUPPLY_PROP_CURRENT_AVG,302};303 304static void da9030_battery_check_status(struct da9030_charger *charger,305				    union power_supply_propval *val)306{307	if (charger->chdet) {308		if (charger->is_on)309			val->intval = POWER_SUPPLY_STATUS_CHARGING;310		else311			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;312	} else {313		val->intval = POWER_SUPPLY_STATUS_DISCHARGING;314	}315}316 317static void da9030_battery_check_health(struct da9030_charger *charger,318				    union power_supply_propval *val)319{320	if (charger->fault & DA9030_FAULT_LOG_OVER_TEMP)321		val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;322	else if (charger->fault & DA9030_FAULT_LOG_VBAT_OVER)323		val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;324	else325		val->intval = POWER_SUPPLY_HEALTH_GOOD;326}327 328static int da9030_battery_get_property(struct power_supply *psy,329				   enum power_supply_property psp,330				   union power_supply_propval *val)331{332	struct da9030_charger *charger = power_supply_get_drvdata(psy);333 334	switch (psp) {335	case POWER_SUPPLY_PROP_STATUS:336		da9030_battery_check_status(charger, val);337		break;338	case POWER_SUPPLY_PROP_HEALTH:339		da9030_battery_check_health(charger, val);340		break;341	case POWER_SUPPLY_PROP_TECHNOLOGY:342		val->intval = charger->battery_info->technology;343		break;344	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:345		val->intval = charger->battery_info->voltage_max_design;346		break;347	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:348		val->intval = charger->battery_info->voltage_min_design;349		break;350	case POWER_SUPPLY_PROP_VOLTAGE_NOW:351		val->intval = da9030_reg_to_mV(charger->adc.vbat_res) * 1000;352		break;353	case POWER_SUPPLY_PROP_CURRENT_AVG:354		val->intval =355			da9030_reg_to_mA(charger->adc.ichaverage_res) * 1000;356		break;357	case POWER_SUPPLY_PROP_MODEL_NAME:358		val->strval = charger->battery_info->name;359		break;360	default:361		break;362	}363 364	return 0;365}366 367static void da9030_battery_vbat_event(struct da9030_charger *charger)368{369	da9030_read_adc(charger, &charger->adc);370 371	if (charger->is_on)372		return;373 374	if (charger->adc.vbat_res < charger->thresholds.vbat_low) {375		/* set VBAT threshold for critical */376		da903x_write(charger->master, DA9030_VBATMON,377			     charger->thresholds.vbat_crit);378		if (charger->battery_low)379			charger->battery_low();380	} else if (charger->adc.vbat_res <381		   charger->thresholds.vbat_crit) {382		/* notify the system of battery critical */383		if (charger->battery_critical)384			charger->battery_critical();385	}386}387 388static int da9030_battery_event(struct notifier_block *nb, unsigned long event,389				void *data)390{391	struct da9030_charger *charger =392		container_of(nb, struct da9030_charger, nb);393 394	switch (event) {395	case DA9030_EVENT_CHDET:396		cancel_delayed_work_sync(&charger->work);397		schedule_work(&charger->work.work);398		break;399	case DA9030_EVENT_VBATMON:400		da9030_battery_vbat_event(charger);401		break;402	case DA9030_EVENT_CHIOVER:403	case DA9030_EVENT_TBAT:404		da9030_set_charge(charger, 0);405		break;406	}407 408	return 0;409}410 411static void da9030_battery_convert_thresholds(struct da9030_charger *charger,412					      struct da9030_battery_info *pdata)413{414	charger->thresholds.tbat_low = pdata->tbat_low;415	charger->thresholds.tbat_high = pdata->tbat_high;416	charger->thresholds.tbat_restart  = pdata->tbat_restart;417 418	charger->thresholds.vbat_low =419		da9030_millivolt_to_reg(pdata->vbat_low);420	charger->thresholds.vbat_crit =421		da9030_millivolt_to_reg(pdata->vbat_crit);422	charger->thresholds.vbat_charge_start =423		da9030_millivolt_to_reg(pdata->vbat_charge_start);424	charger->thresholds.vbat_charge_stop =425		da9030_millivolt_to_reg(pdata->vbat_charge_stop);426	charger->thresholds.vbat_charge_restart =427		da9030_millivolt_to_reg(pdata->vbat_charge_restart);428 429	charger->thresholds.vcharge_min =430		da9030_millivolt_to_reg(pdata->vcharge_min);431	charger->thresholds.vcharge_max =432		da9030_millivolt_to_reg(pdata->vcharge_max);433}434 435static void da9030_battery_setup_psy(struct da9030_charger *charger)436{437	struct power_supply_desc *psy_desc = &charger->psy_desc;438	struct power_supply_info *info = charger->battery_info;439 440	psy_desc->name = info->name;441	psy_desc->use_for_apm = info->use_for_apm;442	psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;443	psy_desc->get_property = da9030_battery_get_property;444 445	psy_desc->properties = da9030_battery_props;446	psy_desc->num_properties = ARRAY_SIZE(da9030_battery_props);447};448 449static int da9030_battery_charger_init(struct da9030_charger *charger)450{451	char v[5];452	int ret;453 454	v[0] = v[1] = charger->thresholds.vbat_low;455	v[2] = charger->thresholds.tbat_high;456	v[3] = charger->thresholds.tbat_restart;457	v[4] = charger->thresholds.tbat_low;458 459	ret = da903x_writes(charger->master, DA9030_VBATMON, 5, v);460	if (ret)461		return ret;462 463	/*464	 * Enable reference voltage supply for ADC from the LDO_INTERNAL465	 * regulator. Must be set before ADC measurements can be made.466	 */467	ret = da903x_write(charger->master, DA9030_ADC_MAN_CONTROL,468			   DA9030_ADC_LDO_INT_ENABLE |469			   DA9030_ADC_TBATREF_ENABLE);470	if (ret)471		return ret;472 473	/* enable auto ADC measuremnts */474	return da903x_write(charger->master, DA9030_ADC_AUTO_CONTROL,475			    DA9030_ADC_TBAT_ENABLE | DA9030_ADC_VBAT_IN_TXON |476			    DA9030_ADC_VCH_ENABLE | DA9030_ADC_ICH_ENABLE |477			    DA9030_ADC_VBAT_ENABLE |478			    DA9030_ADC_AUTO_SLEEP_ENABLE);479}480 481static int da9030_battery_probe(struct platform_device *pdev)482{483	struct da9030_charger *charger;484	struct power_supply_config psy_cfg = {};485	struct da9030_battery_info *pdata = pdev->dev.platform_data;486	int ret;487 488	if (pdata == NULL)489		return -EINVAL;490 491	if (pdata->charge_milliamp >= 1500 ||492	    pdata->charge_millivolt < 4000 ||493	    pdata->charge_millivolt > 4350)494		return -EINVAL;495 496	charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);497	if (charger == NULL)498		return -ENOMEM;499 500	charger->master = pdev->dev.parent;501 502	/* 10 seconds between monitor runs unless platform defines other503	   interval */504	charger->interval = msecs_to_jiffies(505		(pdata->batmon_interval ? : 10) * 1000);506 507	charger->charge_milliamp = pdata->charge_milliamp;508	charger->charge_millivolt = pdata->charge_millivolt;509	charger->battery_info = pdata->battery_info;510	charger->battery_low = pdata->battery_low;511	charger->battery_critical = pdata->battery_critical;512 513	da9030_battery_convert_thresholds(charger, pdata);514 515	ret = da9030_battery_charger_init(charger);516	if (ret)517		goto err_charger_init;518 519	INIT_DELAYED_WORK(&charger->work, da9030_charging_monitor);520	schedule_delayed_work(&charger->work, charger->interval);521 522	charger->nb.notifier_call = da9030_battery_event;523	ret = da903x_register_notifier(charger->master, &charger->nb,524				       DA9030_EVENT_CHDET |525				       DA9030_EVENT_VBATMON |526				       DA9030_EVENT_CHIOVER |527				       DA9030_EVENT_TBAT);528	if (ret)529		goto err_notifier;530 531	da9030_battery_setup_psy(charger);532	psy_cfg.drv_data = charger;533	charger->psy = devm_power_supply_register(&pdev->dev,534						  &charger->psy_desc,535						  &psy_cfg);536	if (IS_ERR(charger->psy)) {537		ret = PTR_ERR(charger->psy);538		goto err_ps_register;539	}540 541	charger->debug_file = da9030_bat_create_debugfs(charger);542	platform_set_drvdata(pdev, charger);543	return 0;544 545err_ps_register:546	da903x_unregister_notifier(charger->master, &charger->nb,547				   DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |548				   DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);549err_notifier:550	cancel_delayed_work(&charger->work);551 552err_charger_init:553	return ret;554}555 556static void da9030_battery_remove(struct platform_device *dev)557{558	struct da9030_charger *charger = platform_get_drvdata(dev);559 560	da9030_bat_remove_debugfs(charger);561 562	da903x_unregister_notifier(charger->master, &charger->nb,563				   DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |564				   DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);565	cancel_delayed_work_sync(&charger->work);566	da9030_set_charge(charger, 0);567}568 569static struct platform_driver da903x_battery_driver = {570	.driver	= {571		.name	= "da903x-battery",572	},573	.probe = da9030_battery_probe,574	.remove_new = da9030_battery_remove,575};576 577module_platform_driver(da903x_battery_driver);578 579MODULE_DESCRIPTION("DA9030 battery charger driver");580MODULE_AUTHOR("Mike Rapoport, CompuLab");581MODULE_LICENSE("GPL");582