xref: /linux/drivers/power/supply/da9030_battery.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28c0984e5SSebastian Reichel /*
38c0984e5SSebastian Reichel  * Battery charger driver for Dialog Semiconductor DA9030
48c0984e5SSebastian Reichel  *
58c0984e5SSebastian Reichel  * Copyright (C) 2008 Compulab, Ltd.
68c0984e5SSebastian Reichel  * 	Mike Rapoport <mike@compulab.co.il>
78c0984e5SSebastian Reichel  */
88c0984e5SSebastian Reichel 
98c0984e5SSebastian Reichel #include <linux/kernel.h>
108c0984e5SSebastian Reichel #include <linux/slab.h>
118c0984e5SSebastian Reichel #include <linux/init.h>
128c0984e5SSebastian Reichel #include <linux/types.h>
138c0984e5SSebastian Reichel #include <linux/device.h>
148c0984e5SSebastian Reichel #include <linux/workqueue.h>
158c0984e5SSebastian Reichel #include <linux/module.h>
168c0984e5SSebastian Reichel #include <linux/platform_device.h>
178c0984e5SSebastian Reichel #include <linux/power_supply.h>
188c0984e5SSebastian Reichel #include <linux/mfd/da903x.h>
198c0984e5SSebastian Reichel 
208c0984e5SSebastian Reichel #include <linux/debugfs.h>
218c0984e5SSebastian Reichel #include <linux/seq_file.h>
228c0984e5SSebastian Reichel #include <linux/notifier.h>
238c0984e5SSebastian Reichel 
248c0984e5SSebastian Reichel #define DA9030_FAULT_LOG		0x0a
258c0984e5SSebastian Reichel #define DA9030_FAULT_LOG_OVER_TEMP	(1 << 7)
268c0984e5SSebastian Reichel #define DA9030_FAULT_LOG_VBAT_OVER	(1 << 4)
278c0984e5SSebastian Reichel 
288c0984e5SSebastian Reichel #define DA9030_CHARGE_CONTROL		0x28
298c0984e5SSebastian Reichel #define DA9030_CHRG_CHARGER_ENABLE	(1 << 7)
308c0984e5SSebastian Reichel 
318c0984e5SSebastian Reichel #define DA9030_ADC_MAN_CONTROL		0x30
328c0984e5SSebastian Reichel #define DA9030_ADC_TBATREF_ENABLE	(1 << 5)
338c0984e5SSebastian Reichel #define DA9030_ADC_LDO_INT_ENABLE	(1 << 4)
348c0984e5SSebastian Reichel 
358c0984e5SSebastian Reichel #define DA9030_ADC_AUTO_CONTROL		0x31
368c0984e5SSebastian Reichel #define DA9030_ADC_TBAT_ENABLE		(1 << 5)
378c0984e5SSebastian Reichel #define DA9030_ADC_VBAT_IN_TXON		(1 << 4)
388c0984e5SSebastian Reichel #define DA9030_ADC_VCH_ENABLE		(1 << 3)
398c0984e5SSebastian Reichel #define DA9030_ADC_ICH_ENABLE		(1 << 2)
408c0984e5SSebastian Reichel #define DA9030_ADC_VBAT_ENABLE		(1 << 1)
418c0984e5SSebastian Reichel #define DA9030_ADC_AUTO_SLEEP_ENABLE	(1 << 0)
428c0984e5SSebastian Reichel 
438c0984e5SSebastian Reichel #define DA9030_VBATMON		0x32
448c0984e5SSebastian Reichel #define DA9030_VBATMONTXON	0x33
458c0984e5SSebastian Reichel #define DA9030_TBATHIGHP	0x34
468c0984e5SSebastian Reichel #define DA9030_TBATHIGHN	0x35
478c0984e5SSebastian Reichel #define DA9030_TBATLOW		0x36
488c0984e5SSebastian Reichel 
498c0984e5SSebastian Reichel #define DA9030_VBAT_RES		0x41
508c0984e5SSebastian Reichel #define DA9030_VBATMIN_RES	0x42
518c0984e5SSebastian Reichel #define DA9030_VBATMINTXON_RES	0x43
528c0984e5SSebastian Reichel #define DA9030_ICHMAX_RES	0x44
538c0984e5SSebastian Reichel #define DA9030_ICHMIN_RES	0x45
548c0984e5SSebastian Reichel #define DA9030_ICHAVERAGE_RES	0x46
558c0984e5SSebastian Reichel #define DA9030_VCHMAX_RES	0x47
568c0984e5SSebastian Reichel #define DA9030_VCHMIN_RES	0x48
578c0984e5SSebastian Reichel #define DA9030_TBAT_RES		0x49
588c0984e5SSebastian Reichel 
598c0984e5SSebastian Reichel struct da9030_adc_res {
608c0984e5SSebastian Reichel 	uint8_t vbat_res;
618c0984e5SSebastian Reichel 	uint8_t vbatmin_res;
628c0984e5SSebastian Reichel 	uint8_t vbatmintxon;
638c0984e5SSebastian Reichel 	uint8_t ichmax_res;
648c0984e5SSebastian Reichel 	uint8_t ichmin_res;
658c0984e5SSebastian Reichel 	uint8_t ichaverage_res;
668c0984e5SSebastian Reichel 	uint8_t vchmax_res;
678c0984e5SSebastian Reichel 	uint8_t vchmin_res;
688c0984e5SSebastian Reichel 	uint8_t tbat_res;
698c0984e5SSebastian Reichel 	uint8_t adc_in4_res;
708c0984e5SSebastian Reichel 	uint8_t adc_in5_res;
718c0984e5SSebastian Reichel };
728c0984e5SSebastian Reichel 
738c0984e5SSebastian Reichel struct da9030_battery_thresholds {
748c0984e5SSebastian Reichel 	int tbat_low;
758c0984e5SSebastian Reichel 	int tbat_high;
768c0984e5SSebastian Reichel 	int tbat_restart;
778c0984e5SSebastian Reichel 
788c0984e5SSebastian Reichel 	int vbat_low;
798c0984e5SSebastian Reichel 	int vbat_crit;
808c0984e5SSebastian Reichel 	int vbat_charge_start;
818c0984e5SSebastian Reichel 	int vbat_charge_stop;
828c0984e5SSebastian Reichel 	int vbat_charge_restart;
838c0984e5SSebastian Reichel 
848c0984e5SSebastian Reichel 	int vcharge_min;
858c0984e5SSebastian Reichel 	int vcharge_max;
868c0984e5SSebastian Reichel };
878c0984e5SSebastian Reichel 
888c0984e5SSebastian Reichel struct da9030_charger {
898c0984e5SSebastian Reichel 	struct power_supply *psy;
908c0984e5SSebastian Reichel 	struct power_supply_desc psy_desc;
918c0984e5SSebastian Reichel 
928c0984e5SSebastian Reichel 	struct device *master;
938c0984e5SSebastian Reichel 
948c0984e5SSebastian Reichel 	struct da9030_adc_res adc;
958c0984e5SSebastian Reichel 	struct delayed_work work;
968c0984e5SSebastian Reichel 	unsigned int interval;
978c0984e5SSebastian Reichel 
988c0984e5SSebastian Reichel 	struct power_supply_info *battery_info;
998c0984e5SSebastian Reichel 
1008c0984e5SSebastian Reichel 	struct da9030_battery_thresholds thresholds;
1018c0984e5SSebastian Reichel 
1028c0984e5SSebastian Reichel 	unsigned int charge_milliamp;
1038c0984e5SSebastian Reichel 	unsigned int charge_millivolt;
1048c0984e5SSebastian Reichel 
1058c0984e5SSebastian Reichel 	/* charger status */
1068c0984e5SSebastian Reichel 	bool chdet;
1078c0984e5SSebastian Reichel 	uint8_t fault;
1088c0984e5SSebastian Reichel 	int mA;
1098c0984e5SSebastian Reichel 	int mV;
1108c0984e5SSebastian Reichel 	bool is_on;
1118c0984e5SSebastian Reichel 
1128c0984e5SSebastian Reichel 	struct notifier_block nb;
1138c0984e5SSebastian Reichel 
1148c0984e5SSebastian Reichel 	/* platform callbacks for battery low and critical events */
1158c0984e5SSebastian Reichel 	void (*battery_low)(void);
1168c0984e5SSebastian Reichel 	void (*battery_critical)(void);
1178c0984e5SSebastian Reichel 
1188c0984e5SSebastian Reichel 	struct dentry *debug_file;
1198c0984e5SSebastian Reichel };
1208c0984e5SSebastian Reichel 
da9030_reg_to_mV(int reg)1218c0984e5SSebastian Reichel static inline int da9030_reg_to_mV(int reg)
1228c0984e5SSebastian Reichel {
1238c0984e5SSebastian Reichel 	return ((reg * 2650) >> 8) + 2650;
1248c0984e5SSebastian Reichel }
1258c0984e5SSebastian Reichel 
da9030_millivolt_to_reg(int mV)1268c0984e5SSebastian Reichel static inline int da9030_millivolt_to_reg(int mV)
1278c0984e5SSebastian Reichel {
1288c0984e5SSebastian Reichel 	return ((mV - 2650) << 8) / 2650;
1298c0984e5SSebastian Reichel }
1308c0984e5SSebastian Reichel 
da9030_reg_to_mA(int reg)1318c0984e5SSebastian Reichel static inline int da9030_reg_to_mA(int reg)
1328c0984e5SSebastian Reichel {
1338c0984e5SSebastian Reichel 	return ((reg * 24000) >> 8) / 15;
1348c0984e5SSebastian Reichel }
1358c0984e5SSebastian Reichel 
1368c0984e5SSebastian Reichel #ifdef CONFIG_DEBUG_FS
bat_debug_show(struct seq_file * s,void * data)1378c0984e5SSebastian Reichel static int bat_debug_show(struct seq_file *s, void *data)
1388c0984e5SSebastian Reichel {
1398c0984e5SSebastian Reichel 	struct da9030_charger *charger = s->private;
1408c0984e5SSebastian Reichel 
1418c0984e5SSebastian Reichel 	seq_printf(s, "charger is %s\n", charger->is_on ? "on" : "off");
1428c0984e5SSebastian Reichel 	if (charger->chdet) {
1438c0984e5SSebastian Reichel 		seq_printf(s, "iset = %dmA, vset = %dmV\n",
1448c0984e5SSebastian Reichel 			   charger->mA, charger->mV);
1458c0984e5SSebastian Reichel 	}
1468c0984e5SSebastian Reichel 
1478c0984e5SSebastian Reichel 	seq_printf(s, "vbat_res = %d (%dmV)\n",
1488c0984e5SSebastian Reichel 		   charger->adc.vbat_res,
1498c0984e5SSebastian Reichel 		   da9030_reg_to_mV(charger->adc.vbat_res));
1508c0984e5SSebastian Reichel 	seq_printf(s, "vbatmin_res = %d (%dmV)\n",
1518c0984e5SSebastian Reichel 		   charger->adc.vbatmin_res,
1528c0984e5SSebastian Reichel 		   da9030_reg_to_mV(charger->adc.vbatmin_res));
1538c0984e5SSebastian Reichel 	seq_printf(s, "vbatmintxon = %d (%dmV)\n",
1548c0984e5SSebastian Reichel 		   charger->adc.vbatmintxon,
1558c0984e5SSebastian Reichel 		   da9030_reg_to_mV(charger->adc.vbatmintxon));
1568c0984e5SSebastian Reichel 	seq_printf(s, "ichmax_res = %d (%dmA)\n",
1578c0984e5SSebastian Reichel 		   charger->adc.ichmax_res,
1588c0984e5SSebastian Reichel 		   da9030_reg_to_mV(charger->adc.ichmax_res));
1598c0984e5SSebastian Reichel 	seq_printf(s, "ichmin_res = %d (%dmA)\n",
1608c0984e5SSebastian Reichel 		   charger->adc.ichmin_res,
1618c0984e5SSebastian Reichel 		   da9030_reg_to_mA(charger->adc.ichmin_res));
1628c0984e5SSebastian Reichel 	seq_printf(s, "ichaverage_res = %d (%dmA)\n",
1638c0984e5SSebastian Reichel 		   charger->adc.ichaverage_res,
1648c0984e5SSebastian Reichel 		   da9030_reg_to_mA(charger->adc.ichaverage_res));
1658c0984e5SSebastian Reichel 	seq_printf(s, "vchmax_res = %d (%dmV)\n",
1668c0984e5SSebastian Reichel 		   charger->adc.vchmax_res,
1678c0984e5SSebastian Reichel 		   da9030_reg_to_mA(charger->adc.vchmax_res));
1688c0984e5SSebastian Reichel 	seq_printf(s, "vchmin_res = %d (%dmV)\n",
1698c0984e5SSebastian Reichel 		   charger->adc.vchmin_res,
1708c0984e5SSebastian Reichel 		   da9030_reg_to_mV(charger->adc.vchmin_res));
1718c0984e5SSebastian Reichel 
1728c0984e5SSebastian Reichel 	return 0;
1738c0984e5SSebastian Reichel }
1748c0984e5SSebastian Reichel 
1759d832cd3SYongqiang Liu DEFINE_SHOW_ATTRIBUTE(bat_debug);
1768c0984e5SSebastian Reichel 
da9030_bat_create_debugfs(struct da9030_charger * charger)1778c0984e5SSebastian Reichel static struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)
1788c0984e5SSebastian Reichel {
1798c0984e5SSebastian Reichel 	charger->debug_file = debugfs_create_file("charger", 0666, NULL,
1808c0984e5SSebastian Reichel 						  charger, &bat_debug_fops);
1818c0984e5SSebastian Reichel 	return charger->debug_file;
1828c0984e5SSebastian Reichel }
1838c0984e5SSebastian Reichel 
da9030_bat_remove_debugfs(struct da9030_charger * charger)1848c0984e5SSebastian Reichel static void da9030_bat_remove_debugfs(struct da9030_charger *charger)
1858c0984e5SSebastian Reichel {
1868c0984e5SSebastian Reichel 	debugfs_remove(charger->debug_file);
1878c0984e5SSebastian Reichel }
1888c0984e5SSebastian Reichel #else
da9030_bat_create_debugfs(struct da9030_charger * charger)1898c0984e5SSebastian Reichel static inline struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)
1908c0984e5SSebastian Reichel {
1918c0984e5SSebastian Reichel 	return NULL;
1928c0984e5SSebastian Reichel }
da9030_bat_remove_debugfs(struct da9030_charger * charger)1938c0984e5SSebastian Reichel static inline void da9030_bat_remove_debugfs(struct da9030_charger *charger)
1948c0984e5SSebastian Reichel {
1958c0984e5SSebastian Reichel }
1968c0984e5SSebastian Reichel #endif
1978c0984e5SSebastian Reichel 
da9030_read_adc(struct da9030_charger * charger,struct da9030_adc_res * adc)1988c0984e5SSebastian Reichel static inline void da9030_read_adc(struct da9030_charger *charger,
1998c0984e5SSebastian Reichel 				   struct da9030_adc_res *adc)
2008c0984e5SSebastian Reichel {
2018c0984e5SSebastian Reichel 	da903x_reads(charger->master, DA9030_VBAT_RES,
2028c0984e5SSebastian Reichel 		     sizeof(*adc), (uint8_t *)adc);
2038c0984e5SSebastian Reichel }
2048c0984e5SSebastian Reichel 
da9030_charger_update_state(struct da9030_charger * charger)2058c0984e5SSebastian Reichel static void da9030_charger_update_state(struct da9030_charger *charger)
2068c0984e5SSebastian Reichel {
2078c0984e5SSebastian Reichel 	uint8_t val;
2088c0984e5SSebastian Reichel 
2098c0984e5SSebastian Reichel 	da903x_read(charger->master, DA9030_CHARGE_CONTROL, &val);
2108c0984e5SSebastian Reichel 	charger->is_on = (val & DA9030_CHRG_CHARGER_ENABLE) ? 1 : 0;
2118c0984e5SSebastian Reichel 	charger->mA = ((val >> 3) & 0xf) * 100;
2128c0984e5SSebastian Reichel 	charger->mV = (val & 0x7) * 50 + 4000;
2138c0984e5SSebastian Reichel 
2148c0984e5SSebastian Reichel 	da9030_read_adc(charger, &charger->adc);
2158c0984e5SSebastian Reichel 	da903x_read(charger->master, DA9030_FAULT_LOG, &charger->fault);
2168c0984e5SSebastian Reichel 	charger->chdet = da903x_query_status(charger->master,
2178c0984e5SSebastian Reichel 						     DA9030_STATUS_CHDET);
2188c0984e5SSebastian Reichel }
2198c0984e5SSebastian Reichel 
da9030_set_charge(struct da9030_charger * charger,int on)2208c0984e5SSebastian Reichel static void da9030_set_charge(struct da9030_charger *charger, int on)
2218c0984e5SSebastian Reichel {
2228c0984e5SSebastian Reichel 	uint8_t val;
2238c0984e5SSebastian Reichel 
2248c0984e5SSebastian Reichel 	if (on) {
2258c0984e5SSebastian Reichel 		val = DA9030_CHRG_CHARGER_ENABLE;
2268c0984e5SSebastian Reichel 		val |= (charger->charge_milliamp / 100) << 3;
2278c0984e5SSebastian Reichel 		val |= (charger->charge_millivolt - 4000) / 50;
2288c0984e5SSebastian Reichel 		charger->is_on = 1;
2298c0984e5SSebastian Reichel 	} else {
2308c0984e5SSebastian Reichel 		val = 0;
2318c0984e5SSebastian Reichel 		charger->is_on = 0;
2328c0984e5SSebastian Reichel 	}
2338c0984e5SSebastian Reichel 
2348c0984e5SSebastian Reichel 	da903x_write(charger->master, DA9030_CHARGE_CONTROL, val);
2358c0984e5SSebastian Reichel 
2368c0984e5SSebastian Reichel 	power_supply_changed(charger->psy);
2378c0984e5SSebastian Reichel }
2388c0984e5SSebastian Reichel 
da9030_charger_check_state(struct da9030_charger * charger)2398c0984e5SSebastian Reichel static void da9030_charger_check_state(struct da9030_charger *charger)
2408c0984e5SSebastian Reichel {
2418c0984e5SSebastian Reichel 	da9030_charger_update_state(charger);
2428c0984e5SSebastian Reichel 
2438c0984e5SSebastian Reichel 	/* we wake or boot with external power on */
2448c0984e5SSebastian Reichel 	if (!charger->is_on) {
2458c0984e5SSebastian Reichel 		if ((charger->chdet) &&
2468c0984e5SSebastian Reichel 		    (charger->adc.vbat_res <
2478c0984e5SSebastian Reichel 		     charger->thresholds.vbat_charge_start)) {
2488c0984e5SSebastian Reichel 			da9030_set_charge(charger, 1);
2498c0984e5SSebastian Reichel 		}
2508c0984e5SSebastian Reichel 	} else {
2518c0984e5SSebastian Reichel 		/* Charger has been pulled out */
2528c0984e5SSebastian Reichel 		if (!charger->chdet) {
2538c0984e5SSebastian Reichel 			da9030_set_charge(charger, 0);
2548c0984e5SSebastian Reichel 			return;
2558c0984e5SSebastian Reichel 		}
2568c0984e5SSebastian Reichel 
2578c0984e5SSebastian Reichel 		if (charger->adc.vbat_res >=
2588c0984e5SSebastian Reichel 		    charger->thresholds.vbat_charge_stop) {
2598c0984e5SSebastian Reichel 			da9030_set_charge(charger, 0);
2608c0984e5SSebastian Reichel 			da903x_write(charger->master, DA9030_VBATMON,
2618c0984e5SSebastian Reichel 				       charger->thresholds.vbat_charge_restart);
2628c0984e5SSebastian Reichel 		} else if (charger->adc.vbat_res >
2638c0984e5SSebastian Reichel 			   charger->thresholds.vbat_low) {
2648c0984e5SSebastian Reichel 			/* we are charging and passed LOW_THRESH,
2658c0984e5SSebastian Reichel 			   so upate DA9030 VBAT threshold
2668c0984e5SSebastian Reichel 			 */
2678c0984e5SSebastian Reichel 			da903x_write(charger->master, DA9030_VBATMON,
2688c0984e5SSebastian Reichel 				     charger->thresholds.vbat_low);
2698c0984e5SSebastian Reichel 		}
2708c0984e5SSebastian Reichel 		if (charger->adc.vchmax_res > charger->thresholds.vcharge_max ||
2718c0984e5SSebastian Reichel 		    charger->adc.vchmin_res < charger->thresholds.vcharge_min ||
2728c0984e5SSebastian Reichel 		    /* Tempreture readings are negative */
2738c0984e5SSebastian Reichel 		    charger->adc.tbat_res < charger->thresholds.tbat_high ||
2748c0984e5SSebastian Reichel 		    charger->adc.tbat_res > charger->thresholds.tbat_low) {
2758c0984e5SSebastian Reichel 			/* disable charger */
2768c0984e5SSebastian Reichel 			da9030_set_charge(charger, 0);
2778c0984e5SSebastian Reichel 		}
2788c0984e5SSebastian Reichel 	}
2798c0984e5SSebastian Reichel }
2808c0984e5SSebastian Reichel 
da9030_charging_monitor(struct work_struct * work)2818c0984e5SSebastian Reichel static void da9030_charging_monitor(struct work_struct *work)
2828c0984e5SSebastian Reichel {
2838c0984e5SSebastian Reichel 	struct da9030_charger *charger;
2848c0984e5SSebastian Reichel 
2858c0984e5SSebastian Reichel 	charger = container_of(work, struct da9030_charger, work.work);
2868c0984e5SSebastian Reichel 
2878c0984e5SSebastian Reichel 	da9030_charger_check_state(charger);
2888c0984e5SSebastian Reichel 
2898c0984e5SSebastian Reichel 	/* reschedule for the next time */
2908c0984e5SSebastian Reichel 	schedule_delayed_work(&charger->work, charger->interval);
2918c0984e5SSebastian Reichel }
2928c0984e5SSebastian Reichel 
2938c0984e5SSebastian Reichel static enum power_supply_property da9030_battery_props[] = {
2948c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_MODEL_NAME,
2958c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_STATUS,
2968c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_HEALTH,
2978c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_TECHNOLOGY,
2988c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
2998c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
3008c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
3018c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CURRENT_AVG,
3028c0984e5SSebastian Reichel };
3038c0984e5SSebastian Reichel 
da9030_battery_check_status(struct da9030_charger * charger,union power_supply_propval * val)3048c0984e5SSebastian Reichel static void da9030_battery_check_status(struct da9030_charger *charger,
3058c0984e5SSebastian Reichel 				    union power_supply_propval *val)
3068c0984e5SSebastian Reichel {
3078c0984e5SSebastian Reichel 	if (charger->chdet) {
3088c0984e5SSebastian Reichel 		if (charger->is_on)
3098c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_STATUS_CHARGING;
3108c0984e5SSebastian Reichel 		else
3118c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
3128c0984e5SSebastian Reichel 	} else {
3138c0984e5SSebastian Reichel 		val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
3148c0984e5SSebastian Reichel 	}
3158c0984e5SSebastian Reichel }
3168c0984e5SSebastian Reichel 
da9030_battery_check_health(struct da9030_charger * charger,union power_supply_propval * val)3178c0984e5SSebastian Reichel static void da9030_battery_check_health(struct da9030_charger *charger,
3188c0984e5SSebastian Reichel 				    union power_supply_propval *val)
3198c0984e5SSebastian Reichel {
3208c0984e5SSebastian Reichel 	if (charger->fault & DA9030_FAULT_LOG_OVER_TEMP)
3218c0984e5SSebastian Reichel 		val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
3228c0984e5SSebastian Reichel 	else if (charger->fault & DA9030_FAULT_LOG_VBAT_OVER)
3238c0984e5SSebastian Reichel 		val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
3248c0984e5SSebastian Reichel 	else
3258c0984e5SSebastian Reichel 		val->intval = POWER_SUPPLY_HEALTH_GOOD;
3268c0984e5SSebastian Reichel }
3278c0984e5SSebastian Reichel 
da9030_battery_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)3288c0984e5SSebastian Reichel static int da9030_battery_get_property(struct power_supply *psy,
3298c0984e5SSebastian Reichel 				   enum power_supply_property psp,
3308c0984e5SSebastian Reichel 				   union power_supply_propval *val)
3318c0984e5SSebastian Reichel {
3328c0984e5SSebastian Reichel 	struct da9030_charger *charger = power_supply_get_drvdata(psy);
3338c0984e5SSebastian Reichel 
3348c0984e5SSebastian Reichel 	switch (psp) {
3358c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_STATUS:
3368c0984e5SSebastian Reichel 		da9030_battery_check_status(charger, val);
3378c0984e5SSebastian Reichel 		break;
3388c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_HEALTH:
3398c0984e5SSebastian Reichel 		da9030_battery_check_health(charger, val);
3408c0984e5SSebastian Reichel 		break;
3418c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_TECHNOLOGY:
3428c0984e5SSebastian Reichel 		val->intval = charger->battery_info->technology;
3438c0984e5SSebastian Reichel 		break;
3448c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
3458c0984e5SSebastian Reichel 		val->intval = charger->battery_info->voltage_max_design;
3468c0984e5SSebastian Reichel 		break;
3478c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
3488c0984e5SSebastian Reichel 		val->intval = charger->battery_info->voltage_min_design;
3498c0984e5SSebastian Reichel 		break;
3508c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
3518c0984e5SSebastian Reichel 		val->intval = da9030_reg_to_mV(charger->adc.vbat_res) * 1000;
3528c0984e5SSebastian Reichel 		break;
3538c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CURRENT_AVG:
3548c0984e5SSebastian Reichel 		val->intval =
3558c0984e5SSebastian Reichel 			da9030_reg_to_mA(charger->adc.ichaverage_res) * 1000;
3568c0984e5SSebastian Reichel 		break;
3578c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_MODEL_NAME:
3588c0984e5SSebastian Reichel 		val->strval = charger->battery_info->name;
3598c0984e5SSebastian Reichel 		break;
3608c0984e5SSebastian Reichel 	default:
3618c0984e5SSebastian Reichel 		break;
3628c0984e5SSebastian Reichel 	}
3638c0984e5SSebastian Reichel 
3648c0984e5SSebastian Reichel 	return 0;
3658c0984e5SSebastian Reichel }
3668c0984e5SSebastian Reichel 
da9030_battery_vbat_event(struct da9030_charger * charger)3678c0984e5SSebastian Reichel static void da9030_battery_vbat_event(struct da9030_charger *charger)
3688c0984e5SSebastian Reichel {
3698c0984e5SSebastian Reichel 	da9030_read_adc(charger, &charger->adc);
3708c0984e5SSebastian Reichel 
3718c0984e5SSebastian Reichel 	if (charger->is_on)
3728c0984e5SSebastian Reichel 		return;
3738c0984e5SSebastian Reichel 
3748c0984e5SSebastian Reichel 	if (charger->adc.vbat_res < charger->thresholds.vbat_low) {
3758c0984e5SSebastian Reichel 		/* set VBAT threshold for critical */
3768c0984e5SSebastian Reichel 		da903x_write(charger->master, DA9030_VBATMON,
3778c0984e5SSebastian Reichel 			     charger->thresholds.vbat_crit);
3788c0984e5SSebastian Reichel 		if (charger->battery_low)
3798c0984e5SSebastian Reichel 			charger->battery_low();
3808c0984e5SSebastian Reichel 	} else if (charger->adc.vbat_res <
3818c0984e5SSebastian Reichel 		   charger->thresholds.vbat_crit) {
3828c0984e5SSebastian Reichel 		/* notify the system of battery critical */
3838c0984e5SSebastian Reichel 		if (charger->battery_critical)
3848c0984e5SSebastian Reichel 			charger->battery_critical();
3858c0984e5SSebastian Reichel 	}
3868c0984e5SSebastian Reichel }
3878c0984e5SSebastian Reichel 
da9030_battery_event(struct notifier_block * nb,unsigned long event,void * data)3888c0984e5SSebastian Reichel static int da9030_battery_event(struct notifier_block *nb, unsigned long event,
3898c0984e5SSebastian Reichel 				void *data)
3908c0984e5SSebastian Reichel {
3918c0984e5SSebastian Reichel 	struct da9030_charger *charger =
3928c0984e5SSebastian Reichel 		container_of(nb, struct da9030_charger, nb);
3938c0984e5SSebastian Reichel 
3948c0984e5SSebastian Reichel 	switch (event) {
3958c0984e5SSebastian Reichel 	case DA9030_EVENT_CHDET:
3968c0984e5SSebastian Reichel 		cancel_delayed_work_sync(&charger->work);
3978c0984e5SSebastian Reichel 		schedule_work(&charger->work.work);
3988c0984e5SSebastian Reichel 		break;
3998c0984e5SSebastian Reichel 	case DA9030_EVENT_VBATMON:
4008c0984e5SSebastian Reichel 		da9030_battery_vbat_event(charger);
4018c0984e5SSebastian Reichel 		break;
4028c0984e5SSebastian Reichel 	case DA9030_EVENT_CHIOVER:
4038c0984e5SSebastian Reichel 	case DA9030_EVENT_TBAT:
4048c0984e5SSebastian Reichel 		da9030_set_charge(charger, 0);
4058c0984e5SSebastian Reichel 		break;
4068c0984e5SSebastian Reichel 	}
4078c0984e5SSebastian Reichel 
4088c0984e5SSebastian Reichel 	return 0;
4098c0984e5SSebastian Reichel }
4108c0984e5SSebastian Reichel 
da9030_battery_convert_thresholds(struct da9030_charger * charger,struct da9030_battery_info * pdata)4118c0984e5SSebastian Reichel static void da9030_battery_convert_thresholds(struct da9030_charger *charger,
4128c0984e5SSebastian Reichel 					      struct da9030_battery_info *pdata)
4138c0984e5SSebastian Reichel {
4148c0984e5SSebastian Reichel 	charger->thresholds.tbat_low = pdata->tbat_low;
4158c0984e5SSebastian Reichel 	charger->thresholds.tbat_high = pdata->tbat_high;
4168c0984e5SSebastian Reichel 	charger->thresholds.tbat_restart  = pdata->tbat_restart;
4178c0984e5SSebastian Reichel 
4188c0984e5SSebastian Reichel 	charger->thresholds.vbat_low =
4198c0984e5SSebastian Reichel 		da9030_millivolt_to_reg(pdata->vbat_low);
4208c0984e5SSebastian Reichel 	charger->thresholds.vbat_crit =
4218c0984e5SSebastian Reichel 		da9030_millivolt_to_reg(pdata->vbat_crit);
4228c0984e5SSebastian Reichel 	charger->thresholds.vbat_charge_start =
4238c0984e5SSebastian Reichel 		da9030_millivolt_to_reg(pdata->vbat_charge_start);
4248c0984e5SSebastian Reichel 	charger->thresholds.vbat_charge_stop =
4258c0984e5SSebastian Reichel 		da9030_millivolt_to_reg(pdata->vbat_charge_stop);
4268c0984e5SSebastian Reichel 	charger->thresholds.vbat_charge_restart =
4278c0984e5SSebastian Reichel 		da9030_millivolt_to_reg(pdata->vbat_charge_restart);
4288c0984e5SSebastian Reichel 
4298c0984e5SSebastian Reichel 	charger->thresholds.vcharge_min =
4308c0984e5SSebastian Reichel 		da9030_millivolt_to_reg(pdata->vcharge_min);
4318c0984e5SSebastian Reichel 	charger->thresholds.vcharge_max =
4328c0984e5SSebastian Reichel 		da9030_millivolt_to_reg(pdata->vcharge_max);
4338c0984e5SSebastian Reichel }
4348c0984e5SSebastian Reichel 
da9030_battery_setup_psy(struct da9030_charger * charger)4358c0984e5SSebastian Reichel static void da9030_battery_setup_psy(struct da9030_charger *charger)
4368c0984e5SSebastian Reichel {
4378c0984e5SSebastian Reichel 	struct power_supply_desc *psy_desc = &charger->psy_desc;
4388c0984e5SSebastian Reichel 	struct power_supply_info *info = charger->battery_info;
4398c0984e5SSebastian Reichel 
4408c0984e5SSebastian Reichel 	psy_desc->name = info->name;
4418c0984e5SSebastian Reichel 	psy_desc->use_for_apm = info->use_for_apm;
4428c0984e5SSebastian Reichel 	psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
4438c0984e5SSebastian Reichel 	psy_desc->get_property = da9030_battery_get_property;
4448c0984e5SSebastian Reichel 
4458c0984e5SSebastian Reichel 	psy_desc->properties = da9030_battery_props;
4468c0984e5SSebastian Reichel 	psy_desc->num_properties = ARRAY_SIZE(da9030_battery_props);
4478c0984e5SSebastian Reichel };
4488c0984e5SSebastian Reichel 
da9030_battery_charger_init(struct da9030_charger * charger)4498c0984e5SSebastian Reichel static int da9030_battery_charger_init(struct da9030_charger *charger)
4508c0984e5SSebastian Reichel {
4518c0984e5SSebastian Reichel 	char v[5];
4528c0984e5SSebastian Reichel 	int ret;
4538c0984e5SSebastian Reichel 
4548c0984e5SSebastian Reichel 	v[0] = v[1] = charger->thresholds.vbat_low;
4558c0984e5SSebastian Reichel 	v[2] = charger->thresholds.tbat_high;
4568c0984e5SSebastian Reichel 	v[3] = charger->thresholds.tbat_restart;
4578c0984e5SSebastian Reichel 	v[4] = charger->thresholds.tbat_low;
4588c0984e5SSebastian Reichel 
4598c0984e5SSebastian Reichel 	ret = da903x_writes(charger->master, DA9030_VBATMON, 5, v);
4608c0984e5SSebastian Reichel 	if (ret)
4618c0984e5SSebastian Reichel 		return ret;
4628c0984e5SSebastian Reichel 
4638c0984e5SSebastian Reichel 	/*
4648c0984e5SSebastian Reichel 	 * Enable reference voltage supply for ADC from the LDO_INTERNAL
4658c0984e5SSebastian Reichel 	 * regulator. Must be set before ADC measurements can be made.
4668c0984e5SSebastian Reichel 	 */
4678c0984e5SSebastian Reichel 	ret = da903x_write(charger->master, DA9030_ADC_MAN_CONTROL,
4688c0984e5SSebastian Reichel 			   DA9030_ADC_LDO_INT_ENABLE |
4698c0984e5SSebastian Reichel 			   DA9030_ADC_TBATREF_ENABLE);
4708c0984e5SSebastian Reichel 	if (ret)
4718c0984e5SSebastian Reichel 		return ret;
4728c0984e5SSebastian Reichel 
4738c0984e5SSebastian Reichel 	/* enable auto ADC measuremnts */
4748c0984e5SSebastian Reichel 	return da903x_write(charger->master, DA9030_ADC_AUTO_CONTROL,
4758c0984e5SSebastian Reichel 			    DA9030_ADC_TBAT_ENABLE | DA9030_ADC_VBAT_IN_TXON |
4768c0984e5SSebastian Reichel 			    DA9030_ADC_VCH_ENABLE | DA9030_ADC_ICH_ENABLE |
4778c0984e5SSebastian Reichel 			    DA9030_ADC_VBAT_ENABLE |
4788c0984e5SSebastian Reichel 			    DA9030_ADC_AUTO_SLEEP_ENABLE);
4798c0984e5SSebastian Reichel }
4808c0984e5SSebastian Reichel 
da9030_battery_probe(struct platform_device * pdev)4818c0984e5SSebastian Reichel static int da9030_battery_probe(struct platform_device *pdev)
4828c0984e5SSebastian Reichel {
4838c0984e5SSebastian Reichel 	struct da9030_charger *charger;
4848c0984e5SSebastian Reichel 	struct power_supply_config psy_cfg = {};
4858c0984e5SSebastian Reichel 	struct da9030_battery_info *pdata = pdev->dev.platform_data;
4868c0984e5SSebastian Reichel 	int ret;
4878c0984e5SSebastian Reichel 
4888c0984e5SSebastian Reichel 	if (pdata == NULL)
4898c0984e5SSebastian Reichel 		return -EINVAL;
4908c0984e5SSebastian Reichel 
4918c0984e5SSebastian Reichel 	if (pdata->charge_milliamp >= 1500 ||
4928c0984e5SSebastian Reichel 	    pdata->charge_millivolt < 4000 ||
4938c0984e5SSebastian Reichel 	    pdata->charge_millivolt > 4350)
4948c0984e5SSebastian Reichel 		return -EINVAL;
4958c0984e5SSebastian Reichel 
4968c0984e5SSebastian Reichel 	charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
4978c0984e5SSebastian Reichel 	if (charger == NULL)
4988c0984e5SSebastian Reichel 		return -ENOMEM;
4998c0984e5SSebastian Reichel 
5008c0984e5SSebastian Reichel 	charger->master = pdev->dev.parent;
5018c0984e5SSebastian Reichel 
5028c0984e5SSebastian Reichel 	/* 10 seconds between monitor runs unless platform defines other
5038c0984e5SSebastian Reichel 	   interval */
5048c0984e5SSebastian Reichel 	charger->interval = msecs_to_jiffies(
5058c0984e5SSebastian Reichel 		(pdata->batmon_interval ? : 10) * 1000);
5068c0984e5SSebastian Reichel 
5078c0984e5SSebastian Reichel 	charger->charge_milliamp = pdata->charge_milliamp;
5088c0984e5SSebastian Reichel 	charger->charge_millivolt = pdata->charge_millivolt;
5098c0984e5SSebastian Reichel 	charger->battery_info = pdata->battery_info;
5108c0984e5SSebastian Reichel 	charger->battery_low = pdata->battery_low;
5118c0984e5SSebastian Reichel 	charger->battery_critical = pdata->battery_critical;
5128c0984e5SSebastian Reichel 
5138c0984e5SSebastian Reichel 	da9030_battery_convert_thresholds(charger, pdata);
5148c0984e5SSebastian Reichel 
5158c0984e5SSebastian Reichel 	ret = da9030_battery_charger_init(charger);
5168c0984e5SSebastian Reichel 	if (ret)
5178c0984e5SSebastian Reichel 		goto err_charger_init;
5188c0984e5SSebastian Reichel 
5198c0984e5SSebastian Reichel 	INIT_DELAYED_WORK(&charger->work, da9030_charging_monitor);
5208c0984e5SSebastian Reichel 	schedule_delayed_work(&charger->work, charger->interval);
5218c0984e5SSebastian Reichel 
5228c0984e5SSebastian Reichel 	charger->nb.notifier_call = da9030_battery_event;
5238c0984e5SSebastian Reichel 	ret = da903x_register_notifier(charger->master, &charger->nb,
5248c0984e5SSebastian Reichel 				       DA9030_EVENT_CHDET |
5258c0984e5SSebastian Reichel 				       DA9030_EVENT_VBATMON |
5268c0984e5SSebastian Reichel 				       DA9030_EVENT_CHIOVER |
5278c0984e5SSebastian Reichel 				       DA9030_EVENT_TBAT);
5288c0984e5SSebastian Reichel 	if (ret)
5298c0984e5SSebastian Reichel 		goto err_notifier;
5308c0984e5SSebastian Reichel 
5318c0984e5SSebastian Reichel 	da9030_battery_setup_psy(charger);
5328c0984e5SSebastian Reichel 	psy_cfg.drv_data = charger;
533*98be59bdSAndrew Davis 	charger->psy = devm_power_supply_register(&pdev->dev,
534*98be59bdSAndrew Davis 						  &charger->psy_desc,
5358c0984e5SSebastian Reichel 						  &psy_cfg);
5368c0984e5SSebastian Reichel 	if (IS_ERR(charger->psy)) {
5378c0984e5SSebastian Reichel 		ret = PTR_ERR(charger->psy);
5388c0984e5SSebastian Reichel 		goto err_ps_register;
5398c0984e5SSebastian Reichel 	}
5408c0984e5SSebastian Reichel 
5418c0984e5SSebastian Reichel 	charger->debug_file = da9030_bat_create_debugfs(charger);
5428c0984e5SSebastian Reichel 	platform_set_drvdata(pdev, charger);
5438c0984e5SSebastian Reichel 	return 0;
5448c0984e5SSebastian Reichel 
5458c0984e5SSebastian Reichel err_ps_register:
5468c0984e5SSebastian Reichel 	da903x_unregister_notifier(charger->master, &charger->nb,
5478c0984e5SSebastian Reichel 				   DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |
5488c0984e5SSebastian Reichel 				   DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);
5498c0984e5SSebastian Reichel err_notifier:
5508c0984e5SSebastian Reichel 	cancel_delayed_work(&charger->work);
5518c0984e5SSebastian Reichel 
5528c0984e5SSebastian Reichel err_charger_init:
5538c0984e5SSebastian Reichel 	return ret;
5548c0984e5SSebastian Reichel }
5558c0984e5SSebastian Reichel 
da9030_battery_remove(struct platform_device * dev)556b5ba26abSUwe Kleine-König static void da9030_battery_remove(struct platform_device *dev)
5578c0984e5SSebastian Reichel {
5588c0984e5SSebastian Reichel 	struct da9030_charger *charger = platform_get_drvdata(dev);
5598c0984e5SSebastian Reichel 
5608c0984e5SSebastian Reichel 	da9030_bat_remove_debugfs(charger);
5618c0984e5SSebastian Reichel 
5628c0984e5SSebastian Reichel 	da903x_unregister_notifier(charger->master, &charger->nb,
5638c0984e5SSebastian Reichel 				   DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |
5648c0984e5SSebastian Reichel 				   DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);
5658c0984e5SSebastian Reichel 	cancel_delayed_work_sync(&charger->work);
5668c0984e5SSebastian Reichel 	da9030_set_charge(charger, 0);
5678c0984e5SSebastian Reichel }
5688c0984e5SSebastian Reichel 
5698c0984e5SSebastian Reichel static struct platform_driver da903x_battery_driver = {
5708c0984e5SSebastian Reichel 	.driver	= {
5718c0984e5SSebastian Reichel 		.name	= "da903x-battery",
5728c0984e5SSebastian Reichel 	},
5738c0984e5SSebastian Reichel 	.probe = da9030_battery_probe,
574b5ba26abSUwe Kleine-König 	.remove_new = da9030_battery_remove,
5758c0984e5SSebastian Reichel };
5768c0984e5SSebastian Reichel 
5778c0984e5SSebastian Reichel module_platform_driver(da903x_battery_driver);
5788c0984e5SSebastian Reichel 
5798c0984e5SSebastian Reichel MODULE_DESCRIPTION("DA9030 battery charger driver");
5808c0984e5SSebastian Reichel MODULE_AUTHOR("Mike Rapoport, CompuLab");
5818c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
582