xref: /linux/drivers/power/supply/act8945a_charger.c (revision 4f9786035f9e519db41375818e1d0b5f20da2f10)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25c0e09e0SWenyou Yang /*
35c0e09e0SWenyou Yang  * Power supply driver for the Active-semi ACT8945A PMIC
45c0e09e0SWenyou Yang  *
55c0e09e0SWenyou Yang  * Copyright (C) 2015 Atmel Corporation
65c0e09e0SWenyou Yang  *
75c0e09e0SWenyou Yang  * Author: Wenyou Yang <wenyou.yang@atmel.com>
85c0e09e0SWenyou Yang  */
9a09209acSWenyou Yang #include <linux/interrupt.h>
105c0e09e0SWenyou Yang #include <linux/module.h>
115c0e09e0SWenyou Yang #include <linux/of.h>
12a09209acSWenyou Yang #include <linux/of_irq.h>
135c0e09e0SWenyou Yang #include <linux/platform_device.h>
145c0e09e0SWenyou Yang #include <linux/power_supply.h>
155c0e09e0SWenyou Yang #include <linux/regmap.h>
16369eba09SWenyou Yang #include <linux/gpio/consumer.h>
175c0e09e0SWenyou Yang 
185c0e09e0SWenyou Yang static const char *act8945a_charger_model = "ACT8945A";
195c0e09e0SWenyou Yang static const char *act8945a_charger_manufacturer = "Active-semi";
205c0e09e0SWenyou Yang 
214667d52fSKrzysztof Kozlowski /*
225c0e09e0SWenyou Yang  * ACT8945A Charger Register Map
235c0e09e0SWenyou Yang  */
245c0e09e0SWenyou Yang 
255c0e09e0SWenyou Yang /* 0x70: Reserved */
265c0e09e0SWenyou Yang #define ACT8945A_APCH_CFG		0x71
275c0e09e0SWenyou Yang #define ACT8945A_APCH_STATUS		0x78
285c0e09e0SWenyou Yang #define ACT8945A_APCH_CTRL		0x79
295c0e09e0SWenyou Yang #define ACT8945A_APCH_STATE		0x7A
305c0e09e0SWenyou Yang 
315c0e09e0SWenyou Yang /* ACT8945A_APCH_CFG */
325c0e09e0SWenyou Yang #define APCH_CFG_OVPSET			(0x3 << 0)
335c0e09e0SWenyou Yang #define APCH_CFG_OVPSET_6V6		(0x0 << 0)
345c0e09e0SWenyou Yang #define APCH_CFG_OVPSET_7V		(0x1 << 0)
355c0e09e0SWenyou Yang #define APCH_CFG_OVPSET_7V5		(0x2 << 0)
365c0e09e0SWenyou Yang #define APCH_CFG_OVPSET_8V		(0x3 << 0)
375c0e09e0SWenyou Yang #define APCH_CFG_PRETIMO		(0x3 << 2)
385c0e09e0SWenyou Yang #define APCH_CFG_PRETIMO_40_MIN		(0x0 << 2)
395c0e09e0SWenyou Yang #define APCH_CFG_PRETIMO_60_MIN		(0x1 << 2)
405c0e09e0SWenyou Yang #define APCH_CFG_PRETIMO_80_MIN		(0x2 << 2)
415c0e09e0SWenyou Yang #define APCH_CFG_PRETIMO_DISABLED	(0x3 << 2)
425c0e09e0SWenyou Yang #define APCH_CFG_TOTTIMO		(0x3 << 4)
435c0e09e0SWenyou Yang #define APCH_CFG_TOTTIMO_3_HOUR		(0x0 << 4)
445c0e09e0SWenyou Yang #define APCH_CFG_TOTTIMO_4_HOUR		(0x1 << 4)
455c0e09e0SWenyou Yang #define APCH_CFG_TOTTIMO_5_HOUR		(0x2 << 4)
465c0e09e0SWenyou Yang #define APCH_CFG_TOTTIMO_DISABLED	(0x3 << 4)
475c0e09e0SWenyou Yang #define APCH_CFG_SUSCHG			(0x1 << 7)
485c0e09e0SWenyou Yang 
495c0e09e0SWenyou Yang #define APCH_STATUS_CHGDAT		BIT(0)
505c0e09e0SWenyou Yang #define APCH_STATUS_INDAT		BIT(1)
515c0e09e0SWenyou Yang #define APCH_STATUS_TEMPDAT		BIT(2)
525c0e09e0SWenyou Yang #define APCH_STATUS_TIMRDAT		BIT(3)
535c0e09e0SWenyou Yang #define APCH_STATUS_CHGSTAT		BIT(4)
545c0e09e0SWenyou Yang #define APCH_STATUS_INSTAT		BIT(5)
555c0e09e0SWenyou Yang #define APCH_STATUS_TEMPSTAT		BIT(6)
565c0e09e0SWenyou Yang #define APCH_STATUS_TIMRSTAT		BIT(7)
575c0e09e0SWenyou Yang 
585c0e09e0SWenyou Yang #define APCH_CTRL_CHGEOCOUT		BIT(0)
595c0e09e0SWenyou Yang #define APCH_CTRL_INDIS			BIT(1)
605c0e09e0SWenyou Yang #define APCH_CTRL_TEMPOUT		BIT(2)
615c0e09e0SWenyou Yang #define APCH_CTRL_TIMRPRE		BIT(3)
625c0e09e0SWenyou Yang #define APCH_CTRL_CHGEOCIN		BIT(4)
635c0e09e0SWenyou Yang #define APCH_CTRL_INCON			BIT(5)
645c0e09e0SWenyou Yang #define APCH_CTRL_TEMPIN		BIT(6)
655c0e09e0SWenyou Yang #define APCH_CTRL_TIMRTOT		BIT(7)
665c0e09e0SWenyou Yang 
675c0e09e0SWenyou Yang #define APCH_STATE_ACINSTAT		(0x1 << 1)
685c0e09e0SWenyou Yang #define APCH_STATE_CSTATE		(0x3 << 4)
695c0e09e0SWenyou Yang #define APCH_STATE_CSTATE_SHIFT		4
705c0e09e0SWenyou Yang #define APCH_STATE_CSTATE_DISABLED	0x00
715c0e09e0SWenyou Yang #define APCH_STATE_CSTATE_EOC		0x01
725c0e09e0SWenyou Yang #define APCH_STATE_CSTATE_FAST		0x02
735c0e09e0SWenyou Yang #define APCH_STATE_CSTATE_PRE		0x03
745c0e09e0SWenyou Yang 
755c0e09e0SWenyou Yang struct act8945a_charger {
76a09209acSWenyou Yang 	struct power_supply *psy;
7710ca08b0SWenyou Yang 	struct power_supply_desc desc;
785c0e09e0SWenyou Yang 	struct regmap *regmap;
79a09209acSWenyou Yang 	struct work_struct work;
80a09209acSWenyou Yang 
81a09209acSWenyou Yang 	bool init_done;
82369eba09SWenyou Yang 	struct gpio_desc *lbo_gpio;
83528e3504SWenyou Yang 	struct gpio_desc *chglev_gpio;
845c0e09e0SWenyou Yang };
855c0e09e0SWenyou Yang 
act8945a_get_charger_state(struct regmap * regmap,int * val)865c0e09e0SWenyou Yang static int act8945a_get_charger_state(struct regmap *regmap, int *val)
875c0e09e0SWenyou Yang {
885c0e09e0SWenyou Yang 	int ret;
895c0e09e0SWenyou Yang 	unsigned int status, state;
905c0e09e0SWenyou Yang 
915c0e09e0SWenyou Yang 	ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
925c0e09e0SWenyou Yang 	if (ret < 0)
935c0e09e0SWenyou Yang 		return ret;
945c0e09e0SWenyou Yang 
955c0e09e0SWenyou Yang 	ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
965c0e09e0SWenyou Yang 	if (ret < 0)
975c0e09e0SWenyou Yang 		return ret;
985c0e09e0SWenyou Yang 
995c0e09e0SWenyou Yang 	state &= APCH_STATE_CSTATE;
1005c0e09e0SWenyou Yang 	state >>= APCH_STATE_CSTATE_SHIFT;
1015c0e09e0SWenyou Yang 
1021f0ba406SWenyou Yang 	switch (state) {
1031f0ba406SWenyou Yang 	case APCH_STATE_CSTATE_PRE:
1041f0ba406SWenyou Yang 	case APCH_STATE_CSTATE_FAST:
1051f0ba406SWenyou Yang 		*val = POWER_SUPPLY_STATUS_CHARGING;
1061f0ba406SWenyou Yang 		break;
1071f0ba406SWenyou Yang 	case APCH_STATE_CSTATE_EOC:
1085c0e09e0SWenyou Yang 		if (status & APCH_STATUS_CHGDAT)
1095c0e09e0SWenyou Yang 			*val = POWER_SUPPLY_STATUS_FULL;
1105c0e09e0SWenyou Yang 		else
1115c0e09e0SWenyou Yang 			*val = POWER_SUPPLY_STATUS_CHARGING;
1121f0ba406SWenyou Yang 		break;
1131f0ba406SWenyou Yang 	case APCH_STATE_CSTATE_DISABLED:
1141f0ba406SWenyou Yang 	default:
1151f0ba406SWenyou Yang 		if (!(status & APCH_STATUS_INDAT))
1161f0ba406SWenyou Yang 			*val = POWER_SUPPLY_STATUS_DISCHARGING;
1171f0ba406SWenyou Yang 		else
1185c0e09e0SWenyou Yang 			*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
1191f0ba406SWenyou Yang 		break;
1205c0e09e0SWenyou Yang 	}
1215c0e09e0SWenyou Yang 
1225c0e09e0SWenyou Yang 	return 0;
1235c0e09e0SWenyou Yang }
1245c0e09e0SWenyou Yang 
act8945a_get_charge_type(struct regmap * regmap,int * val)1255c0e09e0SWenyou Yang static int act8945a_get_charge_type(struct regmap *regmap, int *val)
1265c0e09e0SWenyou Yang {
1275c0e09e0SWenyou Yang 	int ret;
1281f0ba406SWenyou Yang 	unsigned int status, state;
1291f0ba406SWenyou Yang 
1301f0ba406SWenyou Yang 	ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
1311f0ba406SWenyou Yang 	if (ret < 0)
1321f0ba406SWenyou Yang 		return ret;
1335c0e09e0SWenyou Yang 
1345c0e09e0SWenyou Yang 	ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
1355c0e09e0SWenyou Yang 	if (ret < 0)
1365c0e09e0SWenyou Yang 		return ret;
1375c0e09e0SWenyou Yang 
1385c0e09e0SWenyou Yang 	state &= APCH_STATE_CSTATE;
1395c0e09e0SWenyou Yang 	state >>= APCH_STATE_CSTATE_SHIFT;
1405c0e09e0SWenyou Yang 
1415c0e09e0SWenyou Yang 	switch (state) {
1425c0e09e0SWenyou Yang 	case APCH_STATE_CSTATE_PRE:
1435c0e09e0SWenyou Yang 		*val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
1445c0e09e0SWenyou Yang 		break;
1455c0e09e0SWenyou Yang 	case APCH_STATE_CSTATE_FAST:
1465c0e09e0SWenyou Yang 		*val = POWER_SUPPLY_CHARGE_TYPE_FAST;
1475c0e09e0SWenyou Yang 		break;
1485c0e09e0SWenyou Yang 	case APCH_STATE_CSTATE_EOC:
1491f0ba406SWenyou Yang 		*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
1501f0ba406SWenyou Yang 		break;
1515c0e09e0SWenyou Yang 	case APCH_STATE_CSTATE_DISABLED:
1525c0e09e0SWenyou Yang 	default:
1531f0ba406SWenyou Yang 		if (!(status & APCH_STATUS_INDAT))
1545c0e09e0SWenyou Yang 			*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
1551f0ba406SWenyou Yang 		else
1561f0ba406SWenyou Yang 			*val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
1571f0ba406SWenyou Yang 		break;
1585c0e09e0SWenyou Yang 	}
1595c0e09e0SWenyou Yang 
1605c0e09e0SWenyou Yang 	return 0;
1615c0e09e0SWenyou Yang }
1625c0e09e0SWenyou Yang 
act8945a_get_battery_health(struct regmap * regmap,int * val)1636b021fc9SWenyou Yang static int act8945a_get_battery_health(struct regmap *regmap, int *val)
1645c0e09e0SWenyou Yang {
1655c0e09e0SWenyou Yang 	int ret;
1661f0ba406SWenyou Yang 	unsigned int status, state, config;
1675c0e09e0SWenyou Yang 
1685c0e09e0SWenyou Yang 	ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
1695c0e09e0SWenyou Yang 	if (ret < 0)
1705c0e09e0SWenyou Yang 		return ret;
1715c0e09e0SWenyou Yang 
1721f0ba406SWenyou Yang 	ret = regmap_read(regmap, ACT8945A_APCH_CFG, &config);
1731f0ba406SWenyou Yang 	if (ret < 0)
1741f0ba406SWenyou Yang 		return ret;
1751f0ba406SWenyou Yang 
1761f0ba406SWenyou Yang 	ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
1771f0ba406SWenyou Yang 	if (ret < 0)
1781f0ba406SWenyou Yang 		return ret;
1791f0ba406SWenyou Yang 
1801f0ba406SWenyou Yang 	state &= APCH_STATE_CSTATE;
1811f0ba406SWenyou Yang 	state >>= APCH_STATE_CSTATE_SHIFT;
1821f0ba406SWenyou Yang 
1831f0ba406SWenyou Yang 	switch (state) {
1841f0ba406SWenyou Yang 	case APCH_STATE_CSTATE_DISABLED:
1851f0ba406SWenyou Yang 		if (config & APCH_CFG_SUSCHG) {
1861f0ba406SWenyou Yang 			*val = POWER_SUPPLY_HEALTH_UNKNOWN;
1871f0ba406SWenyou Yang 		} else if (status & APCH_STATUS_INDAT) {
1886b021fc9SWenyou Yang 			if (!(status & APCH_STATUS_TEMPDAT))
1895c0e09e0SWenyou Yang 				*val = POWER_SUPPLY_HEALTH_OVERHEAT;
1905c0e09e0SWenyou Yang 			else if (status & APCH_STATUS_TIMRDAT)
1915c0e09e0SWenyou Yang 				*val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
1925c0e09e0SWenyou Yang 			else
1931f0ba406SWenyou Yang 				*val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
1941f0ba406SWenyou Yang 		} else {
1955c0e09e0SWenyou Yang 			*val = POWER_SUPPLY_HEALTH_GOOD;
1961f0ba406SWenyou Yang 		}
1971f0ba406SWenyou Yang 		break;
1981f0ba406SWenyou Yang 	case APCH_STATE_CSTATE_PRE:
1991f0ba406SWenyou Yang 	case APCH_STATE_CSTATE_FAST:
2001f0ba406SWenyou Yang 	case APCH_STATE_CSTATE_EOC:
2011f0ba406SWenyou Yang 	default:
2021f0ba406SWenyou Yang 		*val = POWER_SUPPLY_HEALTH_GOOD;
2031f0ba406SWenyou Yang 		break;
2041f0ba406SWenyou Yang 	}
2055c0e09e0SWenyou Yang 
2065c0e09e0SWenyou Yang 	return 0;
2075c0e09e0SWenyou Yang }
2085c0e09e0SWenyou Yang 
act8945a_get_capacity_level(struct act8945a_charger * charger,struct regmap * regmap,int * val)209369eba09SWenyou Yang static int act8945a_get_capacity_level(struct act8945a_charger *charger,
210369eba09SWenyou Yang 				       struct regmap *regmap, int *val)
211369eba09SWenyou Yang {
212369eba09SWenyou Yang 	int ret;
213369eba09SWenyou Yang 	unsigned int status, state, config;
214369eba09SWenyou Yang 	int lbo_level = gpiod_get_value(charger->lbo_gpio);
215369eba09SWenyou Yang 
216369eba09SWenyou Yang 	ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
217369eba09SWenyou Yang 	if (ret < 0)
218369eba09SWenyou Yang 		return ret;
219369eba09SWenyou Yang 
220369eba09SWenyou Yang 	ret = regmap_read(regmap, ACT8945A_APCH_CFG, &config);
221369eba09SWenyou Yang 	if (ret < 0)
222369eba09SWenyou Yang 		return ret;
223369eba09SWenyou Yang 
224369eba09SWenyou Yang 	ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
225369eba09SWenyou Yang 	if (ret < 0)
226369eba09SWenyou Yang 		return ret;
227369eba09SWenyou Yang 
228369eba09SWenyou Yang 	state &= APCH_STATE_CSTATE;
229369eba09SWenyou Yang 	state >>= APCH_STATE_CSTATE_SHIFT;
230369eba09SWenyou Yang 
231369eba09SWenyou Yang 	switch (state) {
232369eba09SWenyou Yang 	case APCH_STATE_CSTATE_PRE:
233369eba09SWenyou Yang 		*val = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
234369eba09SWenyou Yang 		break;
235369eba09SWenyou Yang 	case APCH_STATE_CSTATE_FAST:
236369eba09SWenyou Yang 		if (lbo_level)
237369eba09SWenyou Yang 			*val = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
238369eba09SWenyou Yang 		else
239369eba09SWenyou Yang 			*val = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
240369eba09SWenyou Yang 		break;
241369eba09SWenyou Yang 	case APCH_STATE_CSTATE_EOC:
242369eba09SWenyou Yang 		if (status & APCH_STATUS_CHGDAT)
243369eba09SWenyou Yang 			*val = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
244369eba09SWenyou Yang 		else
245369eba09SWenyou Yang 			*val = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
246369eba09SWenyou Yang 		break;
247369eba09SWenyou Yang 	case APCH_STATE_CSTATE_DISABLED:
248369eba09SWenyou Yang 	default:
249369eba09SWenyou Yang 		if (config & APCH_CFG_SUSCHG) {
250369eba09SWenyou Yang 			*val = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
251369eba09SWenyou Yang 		} else {
252369eba09SWenyou Yang 			*val = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
253369eba09SWenyou Yang 			if (!(status & APCH_STATUS_INDAT)) {
254369eba09SWenyou Yang 				if (!lbo_level)
255369eba09SWenyou Yang 					*val = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
256369eba09SWenyou Yang 			}
257369eba09SWenyou Yang 		}
258369eba09SWenyou Yang 		break;
259369eba09SWenyou Yang 	}
260369eba09SWenyou Yang 
261369eba09SWenyou Yang 	return 0;
262369eba09SWenyou Yang }
263369eba09SWenyou Yang 
264528e3504SWenyou Yang #define MAX_CURRENT_USB_HIGH	450000
265528e3504SWenyou Yang #define MAX_CURRENT_USB_LOW	90000
266528e3504SWenyou Yang #define MAX_CURRENT_USB_PRE	45000
267528e3504SWenyou Yang /*
268528e3504SWenyou Yang  * Riset(K) = 2336 * (1V/Ichg(mA)) - 0.205
269528e3504SWenyou Yang  * Riset = 2.43K
270528e3504SWenyou Yang  */
271528e3504SWenyou Yang #define MAX_CURRENT_AC_HIGH		886527
272528e3504SWenyou Yang #define MAX_CURRENT_AC_LOW		117305
273528e3504SWenyou Yang #define MAX_CURRENT_AC_HIGH_PRE		88653
274528e3504SWenyou Yang #define MAX_CURRENT_AC_LOW_PRE		11731
275528e3504SWenyou Yang 
act8945a_get_current_max(struct act8945a_charger * charger,struct regmap * regmap,int * val)276528e3504SWenyou Yang static int act8945a_get_current_max(struct act8945a_charger *charger,
277528e3504SWenyou Yang 				    struct regmap *regmap, int *val)
278528e3504SWenyou Yang {
279528e3504SWenyou Yang 	int ret;
280528e3504SWenyou Yang 	unsigned int status, state;
281528e3504SWenyou Yang 	unsigned int acin_state;
282528e3504SWenyou Yang 	int chgin_level = gpiod_get_value(charger->chglev_gpio);
283528e3504SWenyou Yang 
284528e3504SWenyou Yang 	ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
285528e3504SWenyou Yang 	if (ret < 0)
286528e3504SWenyou Yang 		return ret;
287528e3504SWenyou Yang 
288528e3504SWenyou Yang 	ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
289528e3504SWenyou Yang 	if (ret < 0)
290528e3504SWenyou Yang 		return ret;
291528e3504SWenyou Yang 
292528e3504SWenyou Yang 	acin_state = (state & APCH_STATE_ACINSTAT) >> 1;
293528e3504SWenyou Yang 
294528e3504SWenyou Yang 	state &= APCH_STATE_CSTATE;
295528e3504SWenyou Yang 	state >>= APCH_STATE_CSTATE_SHIFT;
296528e3504SWenyou Yang 
297528e3504SWenyou Yang 	switch (state) {
298528e3504SWenyou Yang 	case APCH_STATE_CSTATE_PRE:
299528e3504SWenyou Yang 		if (acin_state) {
300528e3504SWenyou Yang 			if (chgin_level)
301528e3504SWenyou Yang 				*val = MAX_CURRENT_AC_HIGH_PRE;
302528e3504SWenyou Yang 			else
303528e3504SWenyou Yang 				*val = MAX_CURRENT_AC_LOW_PRE;
304528e3504SWenyou Yang 		} else {
305528e3504SWenyou Yang 			*val = MAX_CURRENT_USB_PRE;
306528e3504SWenyou Yang 		}
307528e3504SWenyou Yang 		break;
308528e3504SWenyou Yang 	case APCH_STATE_CSTATE_FAST:
309528e3504SWenyou Yang 		if (acin_state) {
310528e3504SWenyou Yang 			if (chgin_level)
311528e3504SWenyou Yang 				*val = MAX_CURRENT_AC_HIGH;
312528e3504SWenyou Yang 			else
313528e3504SWenyou Yang 				*val = MAX_CURRENT_AC_LOW;
314528e3504SWenyou Yang 		} else {
315528e3504SWenyou Yang 			if (chgin_level)
316528e3504SWenyou Yang 				*val = MAX_CURRENT_USB_HIGH;
317528e3504SWenyou Yang 			else
318528e3504SWenyou Yang 				*val = MAX_CURRENT_USB_LOW;
319528e3504SWenyou Yang 		}
320528e3504SWenyou Yang 		break;
321528e3504SWenyou Yang 	case APCH_STATE_CSTATE_EOC:
322528e3504SWenyou Yang 	case APCH_STATE_CSTATE_DISABLED:
323528e3504SWenyou Yang 	default:
324528e3504SWenyou Yang 		*val = 0;
325528e3504SWenyou Yang 		break;
326528e3504SWenyou Yang 	}
327528e3504SWenyou Yang 
328528e3504SWenyou Yang 	return 0;
329528e3504SWenyou Yang }
330528e3504SWenyou Yang 
3315c0e09e0SWenyou Yang static enum power_supply_property act8945a_charger_props[] = {
3325c0e09e0SWenyou Yang 	POWER_SUPPLY_PROP_STATUS,
3335c0e09e0SWenyou Yang 	POWER_SUPPLY_PROP_CHARGE_TYPE,
3345c0e09e0SWenyou Yang 	POWER_SUPPLY_PROP_TECHNOLOGY,
3355c0e09e0SWenyou Yang 	POWER_SUPPLY_PROP_HEALTH,
336369eba09SWenyou Yang 	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
337528e3504SWenyou Yang 	POWER_SUPPLY_PROP_CURRENT_MAX,
3385c0e09e0SWenyou Yang 	POWER_SUPPLY_PROP_MODEL_NAME,
3395c0e09e0SWenyou Yang 	POWER_SUPPLY_PROP_MANUFACTURER
3405c0e09e0SWenyou Yang };
3415c0e09e0SWenyou Yang 
act8945a_charger_get_property(struct power_supply * psy,enum power_supply_property prop,union power_supply_propval * val)3425c0e09e0SWenyou Yang static int act8945a_charger_get_property(struct power_supply *psy,
3435c0e09e0SWenyou Yang 					 enum power_supply_property prop,
3445c0e09e0SWenyou Yang 					 union power_supply_propval *val)
3455c0e09e0SWenyou Yang {
3465c0e09e0SWenyou Yang 	struct act8945a_charger *charger = power_supply_get_drvdata(psy);
3475c0e09e0SWenyou Yang 	struct regmap *regmap = charger->regmap;
3485c0e09e0SWenyou Yang 	int ret = 0;
3495c0e09e0SWenyou Yang 
3505c0e09e0SWenyou Yang 	switch (prop) {
3515c0e09e0SWenyou Yang 	case POWER_SUPPLY_PROP_STATUS:
3525c0e09e0SWenyou Yang 		ret = act8945a_get_charger_state(regmap, &val->intval);
3535c0e09e0SWenyou Yang 		break;
3545c0e09e0SWenyou Yang 	case POWER_SUPPLY_PROP_CHARGE_TYPE:
3555c0e09e0SWenyou Yang 		ret = act8945a_get_charge_type(regmap, &val->intval);
3565c0e09e0SWenyou Yang 		break;
3575c0e09e0SWenyou Yang 	case POWER_SUPPLY_PROP_TECHNOLOGY:
3585c0e09e0SWenyou Yang 		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
3595c0e09e0SWenyou Yang 		break;
3605c0e09e0SWenyou Yang 	case POWER_SUPPLY_PROP_HEALTH:
3616b021fc9SWenyou Yang 		ret = act8945a_get_battery_health(regmap, &val->intval);
3625c0e09e0SWenyou Yang 		break;
363369eba09SWenyou Yang 	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
364369eba09SWenyou Yang 		ret = act8945a_get_capacity_level(charger,
365369eba09SWenyou Yang 						  regmap, &val->intval);
366369eba09SWenyou Yang 		break;
367528e3504SWenyou Yang 	case POWER_SUPPLY_PROP_CURRENT_MAX:
368528e3504SWenyou Yang 		ret = act8945a_get_current_max(charger,
369528e3504SWenyou Yang 					       regmap, &val->intval);
370528e3504SWenyou Yang 		break;
3715c0e09e0SWenyou Yang 	case POWER_SUPPLY_PROP_MODEL_NAME:
3725c0e09e0SWenyou Yang 		val->strval = act8945a_charger_model;
3735c0e09e0SWenyou Yang 		break;
3745c0e09e0SWenyou Yang 	case POWER_SUPPLY_PROP_MANUFACTURER:
3755c0e09e0SWenyou Yang 		val->strval = act8945a_charger_manufacturer;
3765c0e09e0SWenyou Yang 		break;
3775c0e09e0SWenyou Yang 	default:
3785c0e09e0SWenyou Yang 		return -EINVAL;
3795c0e09e0SWenyou Yang 	}
3805c0e09e0SWenyou Yang 
3815c0e09e0SWenyou Yang 	return ret;
3825c0e09e0SWenyou Yang }
3835c0e09e0SWenyou Yang 
act8945a_enable_interrupt(struct act8945a_charger * charger)384a09209acSWenyou Yang static int act8945a_enable_interrupt(struct act8945a_charger *charger)
385a09209acSWenyou Yang {
386a09209acSWenyou Yang 	struct regmap *regmap = charger->regmap;
387a09209acSWenyou Yang 	unsigned char ctrl;
388a09209acSWenyou Yang 	int ret;
389a09209acSWenyou Yang 
390a09209acSWenyou Yang 	ctrl = APCH_CTRL_CHGEOCOUT | APCH_CTRL_CHGEOCIN |
391a09209acSWenyou Yang 	       APCH_CTRL_INDIS | APCH_CTRL_INCON |
392a09209acSWenyou Yang 	       APCH_CTRL_TEMPOUT | APCH_CTRL_TEMPIN |
393a09209acSWenyou Yang 	       APCH_CTRL_TIMRPRE | APCH_CTRL_TIMRTOT;
394a09209acSWenyou Yang 	ret = regmap_write(regmap, ACT8945A_APCH_CTRL, ctrl);
395a09209acSWenyou Yang 	if (ret)
396a09209acSWenyou Yang 		return ret;
397a09209acSWenyou Yang 
398a09209acSWenyou Yang 	ctrl = APCH_STATUS_CHGSTAT | APCH_STATUS_INSTAT |
399a09209acSWenyou Yang 	       APCH_STATUS_TEMPSTAT | APCH_STATUS_TIMRSTAT;
400a09209acSWenyou Yang 	ret = regmap_write(regmap, ACT8945A_APCH_STATUS, ctrl);
401a09209acSWenyou Yang 	if (ret)
402a09209acSWenyou Yang 		return ret;
403a09209acSWenyou Yang 
404a09209acSWenyou Yang 	return 0;
405a09209acSWenyou Yang }
406a09209acSWenyou Yang 
act8945a_set_supply_type(struct act8945a_charger * charger,unsigned int * type)40710ca08b0SWenyou Yang static unsigned int act8945a_set_supply_type(struct act8945a_charger *charger,
40810ca08b0SWenyou Yang 					     unsigned int *type)
40910ca08b0SWenyou Yang {
41010ca08b0SWenyou Yang 	unsigned int status, state;
41110ca08b0SWenyou Yang 	int ret;
41210ca08b0SWenyou Yang 
41310ca08b0SWenyou Yang 	ret = regmap_read(charger->regmap, ACT8945A_APCH_STATUS, &status);
41410ca08b0SWenyou Yang 	if (ret < 0)
41510ca08b0SWenyou Yang 		return ret;
41610ca08b0SWenyou Yang 
41710ca08b0SWenyou Yang 	ret = regmap_read(charger->regmap, ACT8945A_APCH_STATE, &state);
41810ca08b0SWenyou Yang 	if (ret < 0)
41910ca08b0SWenyou Yang 		return ret;
42010ca08b0SWenyou Yang 
42110ca08b0SWenyou Yang 	if (status & APCH_STATUS_INDAT) {
42210ca08b0SWenyou Yang 		if (state & APCH_STATE_ACINSTAT)
42310ca08b0SWenyou Yang 			*type = POWER_SUPPLY_TYPE_MAINS;
42410ca08b0SWenyou Yang 		else
42510ca08b0SWenyou Yang 			*type = POWER_SUPPLY_TYPE_USB;
42610ca08b0SWenyou Yang 	} else {
42710ca08b0SWenyou Yang 		*type = POWER_SUPPLY_TYPE_BATTERY;
42810ca08b0SWenyou Yang 	}
42910ca08b0SWenyou Yang 
43010ca08b0SWenyou Yang 	return 0;
43110ca08b0SWenyou Yang }
43210ca08b0SWenyou Yang 
act8945a_work(struct work_struct * work)433a09209acSWenyou Yang static void act8945a_work(struct work_struct *work)
434a09209acSWenyou Yang {
435a09209acSWenyou Yang 	struct act8945a_charger *charger =
436a09209acSWenyou Yang 			container_of(work, struct act8945a_charger, work);
437a09209acSWenyou Yang 
43810ca08b0SWenyou Yang 	act8945a_set_supply_type(charger, &charger->desc.type);
43910ca08b0SWenyou Yang 
440a09209acSWenyou Yang 	power_supply_changed(charger->psy);
441a09209acSWenyou Yang }
442a09209acSWenyou Yang 
act8945a_status_changed(int irq,void * dev_id)443a09209acSWenyou Yang static irqreturn_t act8945a_status_changed(int irq, void *dev_id)
444a09209acSWenyou Yang {
445a09209acSWenyou Yang 	struct act8945a_charger *charger = dev_id;
446a09209acSWenyou Yang 
447a09209acSWenyou Yang 	if (charger->init_done)
448a09209acSWenyou Yang 		schedule_work(&charger->work);
449a09209acSWenyou Yang 
450a09209acSWenyou Yang 	return IRQ_HANDLED;
451a09209acSWenyou Yang }
452a09209acSWenyou Yang 
4535c0e09e0SWenyou Yang #define DEFAULT_TOTAL_TIME_OUT		3
4545c0e09e0SWenyou Yang #define DEFAULT_PRE_TIME_OUT		40
4555c0e09e0SWenyou Yang #define DEFAULT_INPUT_OVP_THRESHOLD	6600
4565c0e09e0SWenyou Yang 
act8945a_charger_config(struct device * dev,struct act8945a_charger * charger)4575c0e09e0SWenyou Yang static int act8945a_charger_config(struct device *dev,
4585c0e09e0SWenyou Yang 				   struct act8945a_charger *charger)
4595c0e09e0SWenyou Yang {
4605c0e09e0SWenyou Yang 	struct device_node *np = dev->of_node;
4615c0e09e0SWenyou Yang 	struct regmap *regmap = charger->regmap;
4625c0e09e0SWenyou Yang 
4635c0e09e0SWenyou Yang 	u32 total_time_out;
4645c0e09e0SWenyou Yang 	u32 pre_time_out;
4655c0e09e0SWenyou Yang 	u32 input_voltage_threshold;
466369eba09SWenyou Yang 	int err, ret;
4675c0e09e0SWenyou Yang 
4681f0ba406SWenyou Yang 	unsigned int tmp;
4695c0e09e0SWenyou Yang 	unsigned int value = 0;
4705c0e09e0SWenyou Yang 
4715c0e09e0SWenyou Yang 	if (!np) {
4725c0e09e0SWenyou Yang 		dev_err(dev, "no charger of node\n");
4735c0e09e0SWenyou Yang 		return -EINVAL;
4745c0e09e0SWenyou Yang 	}
4755c0e09e0SWenyou Yang 
4761f0ba406SWenyou Yang 	ret = regmap_read(regmap, ACT8945A_APCH_CFG, &tmp);
4771f0ba406SWenyou Yang 	if (ret)
4781f0ba406SWenyou Yang 		return ret;
4791f0ba406SWenyou Yang 
4801f0ba406SWenyou Yang 	if (tmp & APCH_CFG_SUSCHG) {
4811f0ba406SWenyou Yang 		value |= APCH_CFG_SUSCHG;
4821f0ba406SWenyou Yang 		dev_info(dev, "have been suspended\n");
4831f0ba406SWenyou Yang 	}
4841f0ba406SWenyou Yang 
485369eba09SWenyou Yang 	charger->lbo_gpio = devm_gpiod_get_optional(dev, "active-semi,lbo",
486369eba09SWenyou Yang 						    GPIOD_IN);
487369eba09SWenyou Yang 	if (IS_ERR(charger->lbo_gpio)) {
488369eba09SWenyou Yang 		err = PTR_ERR(charger->lbo_gpio);
489369eba09SWenyou Yang 		dev_err(dev, "unable to claim gpio \"lbo\": %d\n", err);
490369eba09SWenyou Yang 		return err;
491369eba09SWenyou Yang 	}
492369eba09SWenyou Yang 
493369eba09SWenyou Yang 	ret = devm_request_irq(dev, gpiod_to_irq(charger->lbo_gpio),
494369eba09SWenyou Yang 			       act8945a_status_changed,
495369eba09SWenyou Yang 			       (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING),
496369eba09SWenyou Yang 			       "act8945a_lbo_detect", charger);
497369eba09SWenyou Yang 	if (ret)
498369eba09SWenyou Yang 		dev_info(dev, "failed to request gpio \"lbo\" IRQ\n");
499369eba09SWenyou Yang 
500528e3504SWenyou Yang 	charger->chglev_gpio = devm_gpiod_get_optional(dev,
501528e3504SWenyou Yang 						       "active-semi,chglev",
502528e3504SWenyou Yang 						       GPIOD_IN);
503528e3504SWenyou Yang 	if (IS_ERR(charger->chglev_gpio)) {
504528e3504SWenyou Yang 		err = PTR_ERR(charger->chglev_gpio);
505528e3504SWenyou Yang 		dev_err(dev, "unable to claim gpio \"chglev\": %d\n", err);
506528e3504SWenyou Yang 		return err;
5075c0e09e0SWenyou Yang 	}
5085c0e09e0SWenyou Yang 
5095c0e09e0SWenyou Yang 	if (of_property_read_u32(np,
5105c0e09e0SWenyou Yang 				 "active-semi,input-voltage-threshold-microvolt",
5115c0e09e0SWenyou Yang 				 &input_voltage_threshold))
5125c0e09e0SWenyou Yang 		input_voltage_threshold = DEFAULT_INPUT_OVP_THRESHOLD;
5135c0e09e0SWenyou Yang 
5145c0e09e0SWenyou Yang 	if (of_property_read_u32(np,
5155c0e09e0SWenyou Yang 				 "active-semi,precondition-timeout",
5165c0e09e0SWenyou Yang 				 &pre_time_out))
5175c0e09e0SWenyou Yang 		pre_time_out = DEFAULT_PRE_TIME_OUT;
5185c0e09e0SWenyou Yang 
5195c0e09e0SWenyou Yang 	if (of_property_read_u32(np, "active-semi,total-timeout",
5205c0e09e0SWenyou Yang 				 &total_time_out))
5215c0e09e0SWenyou Yang 		total_time_out = DEFAULT_TOTAL_TIME_OUT;
5225c0e09e0SWenyou Yang 
5235c0e09e0SWenyou Yang 	switch (input_voltage_threshold) {
5245c0e09e0SWenyou Yang 	case 8000:
5255c0e09e0SWenyou Yang 		value |= APCH_CFG_OVPSET_8V;
5265c0e09e0SWenyou Yang 		break;
5275c0e09e0SWenyou Yang 	case 7500:
5285c0e09e0SWenyou Yang 		value |= APCH_CFG_OVPSET_7V5;
5295c0e09e0SWenyou Yang 		break;
5305c0e09e0SWenyou Yang 	case 7000:
5315c0e09e0SWenyou Yang 		value |= APCH_CFG_OVPSET_7V;
5325c0e09e0SWenyou Yang 		break;
5335c0e09e0SWenyou Yang 	case 6600:
5345c0e09e0SWenyou Yang 	default:
5355c0e09e0SWenyou Yang 		value |= APCH_CFG_OVPSET_6V6;
5365c0e09e0SWenyou Yang 		break;
5375c0e09e0SWenyou Yang 	}
5385c0e09e0SWenyou Yang 
5395c0e09e0SWenyou Yang 	switch (pre_time_out) {
5405c0e09e0SWenyou Yang 	case 60:
5415c0e09e0SWenyou Yang 		value |= APCH_CFG_PRETIMO_60_MIN;
5425c0e09e0SWenyou Yang 		break;
5435c0e09e0SWenyou Yang 	case 80:
5445c0e09e0SWenyou Yang 		value |= APCH_CFG_PRETIMO_80_MIN;
5455c0e09e0SWenyou Yang 		break;
5465c0e09e0SWenyou Yang 	case 0:
5475c0e09e0SWenyou Yang 		value |= APCH_CFG_PRETIMO_DISABLED;
5485c0e09e0SWenyou Yang 		break;
5495c0e09e0SWenyou Yang 	case 40:
5505c0e09e0SWenyou Yang 	default:
5515c0e09e0SWenyou Yang 		value |= APCH_CFG_PRETIMO_40_MIN;
5525c0e09e0SWenyou Yang 		break;
5535c0e09e0SWenyou Yang 	}
5545c0e09e0SWenyou Yang 
5555c0e09e0SWenyou Yang 	switch (total_time_out) {
5565c0e09e0SWenyou Yang 	case 4:
5575c0e09e0SWenyou Yang 		value |= APCH_CFG_TOTTIMO_4_HOUR;
5585c0e09e0SWenyou Yang 		break;
5595c0e09e0SWenyou Yang 	case 5:
5605c0e09e0SWenyou Yang 		value |= APCH_CFG_TOTTIMO_5_HOUR;
5615c0e09e0SWenyou Yang 		break;
5625c0e09e0SWenyou Yang 	case 0:
5635c0e09e0SWenyou Yang 		value |= APCH_CFG_TOTTIMO_DISABLED;
5645c0e09e0SWenyou Yang 		break;
5655c0e09e0SWenyou Yang 	case 3:
5665c0e09e0SWenyou Yang 	default:
5675c0e09e0SWenyou Yang 		value |= APCH_CFG_TOTTIMO_3_HOUR;
5685c0e09e0SWenyou Yang 		break;
5695c0e09e0SWenyou Yang 	}
5705c0e09e0SWenyou Yang 
5715c0e09e0SWenyou Yang 	return regmap_write(regmap, ACT8945A_APCH_CFG, value);
5725c0e09e0SWenyou Yang }
5735c0e09e0SWenyou Yang 
act8945a_charger_probe(struct platform_device * pdev)5745c0e09e0SWenyou Yang static int act8945a_charger_probe(struct platform_device *pdev)
5755c0e09e0SWenyou Yang {
5765c0e09e0SWenyou Yang 	struct act8945a_charger *charger;
5775c0e09e0SWenyou Yang 	struct power_supply_config psy_cfg = {};
578a09209acSWenyou Yang 	int irq, ret;
5795c0e09e0SWenyou Yang 
5805c0e09e0SWenyou Yang 	charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
5815c0e09e0SWenyou Yang 	if (!charger)
5825c0e09e0SWenyou Yang 		return -ENOMEM;
5835c0e09e0SWenyou Yang 
5845c0e09e0SWenyou Yang 	charger->regmap = dev_get_regmap(pdev->dev.parent, NULL);
5855c0e09e0SWenyou Yang 	if (!charger->regmap) {
5865c0e09e0SWenyou Yang 		dev_err(&pdev->dev, "Parent did not provide regmap\n");
5875c0e09e0SWenyou Yang 		return -EINVAL;
5885c0e09e0SWenyou Yang 	}
5895c0e09e0SWenyou Yang 
5905da643b2SWenyou Yang 	ret = act8945a_charger_config(&pdev->dev, charger);
5915c0e09e0SWenyou Yang 	if (ret)
5925c0e09e0SWenyou Yang 		return ret;
5935c0e09e0SWenyou Yang 
594a09209acSWenyou Yang 	irq = of_irq_get(pdev->dev.of_node, 0);
595648b8ebaSSergei Shtylyov 	if (irq <= 0) {
596a09209acSWenyou Yang 		dev_err(&pdev->dev, "failed to find IRQ number\n");
597648b8ebaSSergei Shtylyov 		return irq ?: -ENXIO;
598a09209acSWenyou Yang 	}
599a09209acSWenyou Yang 
600a09209acSWenyou Yang 	ret = devm_request_irq(&pdev->dev, irq, act8945a_status_changed,
601a09209acSWenyou Yang 			       IRQF_TRIGGER_FALLING, "act8945a_interrupt",
602a09209acSWenyou Yang 			       charger);
603a09209acSWenyou Yang 	if (ret) {
604a09209acSWenyou Yang 		dev_err(&pdev->dev, "failed to request nIRQ pin IRQ\n");
605a09209acSWenyou Yang 		return ret;
606a09209acSWenyou Yang 	}
607a09209acSWenyou Yang 
60810ca08b0SWenyou Yang 	charger->desc.name = "act8945a-charger";
60910ca08b0SWenyou Yang 	charger->desc.get_property = act8945a_charger_get_property;
61010ca08b0SWenyou Yang 	charger->desc.properties = act8945a_charger_props;
61110ca08b0SWenyou Yang 	charger->desc.num_properties = ARRAY_SIZE(act8945a_charger_props);
61210ca08b0SWenyou Yang 
61310ca08b0SWenyou Yang 	ret = act8945a_set_supply_type(charger, &charger->desc.type);
61410ca08b0SWenyou Yang 	if (ret)
61510ca08b0SWenyou Yang 		return -EINVAL;
61610ca08b0SWenyou Yang 
617*46d0c03cSSebastian Reichel 	psy_cfg.fwnode	= dev_fwnode(&pdev->dev);
6185c0e09e0SWenyou Yang 	psy_cfg.drv_data = charger;
6195c0e09e0SWenyou Yang 
620a09209acSWenyou Yang 	charger->psy = devm_power_supply_register(&pdev->dev,
62110ca08b0SWenyou Yang 						  &charger->desc,
6225c0e09e0SWenyou Yang 						  &psy_cfg);
623a09209acSWenyou Yang 	if (IS_ERR(charger->psy)) {
6245c0e09e0SWenyou Yang 		dev_err(&pdev->dev, "failed to register power supply\n");
625a09209acSWenyou Yang 		return PTR_ERR(charger->psy);
6265c0e09e0SWenyou Yang 	}
6275c0e09e0SWenyou Yang 
628a09209acSWenyou Yang 	platform_set_drvdata(pdev, charger);
629a09209acSWenyou Yang 
630a09209acSWenyou Yang 	INIT_WORK(&charger->work, act8945a_work);
631a09209acSWenyou Yang 
632a09209acSWenyou Yang 	ret = act8945a_enable_interrupt(charger);
633a09209acSWenyou Yang 	if (ret)
634a09209acSWenyou Yang 		return -EIO;
635a09209acSWenyou Yang 
636a09209acSWenyou Yang 	charger->init_done = true;
637a09209acSWenyou Yang 
638a09209acSWenyou Yang 	return 0;
639a09209acSWenyou Yang }
640a09209acSWenyou Yang 
act8945a_charger_remove(struct platform_device * pdev)6417a9a4966SUwe Kleine-König static void act8945a_charger_remove(struct platform_device *pdev)
642a09209acSWenyou Yang {
643a09209acSWenyou Yang 	struct act8945a_charger *charger = platform_get_drvdata(pdev);
644a09209acSWenyou Yang 
645a09209acSWenyou Yang 	charger->init_done = false;
646a09209acSWenyou Yang 	cancel_work_sync(&charger->work);
6475c0e09e0SWenyou Yang }
6485c0e09e0SWenyou Yang 
6495c0e09e0SWenyou Yang static struct platform_driver act8945a_charger_driver = {
6505c0e09e0SWenyou Yang 	.driver	= {
6515c0e09e0SWenyou Yang 		.name = "act8945a-charger",
6525c0e09e0SWenyou Yang 	},
6535c0e09e0SWenyou Yang 	.probe	= act8945a_charger_probe,
65483bce344SUwe Kleine-König 	.remove	= act8945a_charger_remove,
6555c0e09e0SWenyou Yang };
6565c0e09e0SWenyou Yang module_platform_driver(act8945a_charger_driver);
6575c0e09e0SWenyou Yang 
6585c0e09e0SWenyou Yang MODULE_DESCRIPTION("Active-semi ACT8945A ActivePath charger driver");
6595c0e09e0SWenyou Yang MODULE_AUTHOR("Wenyou Yang <wenyou.yang@atmel.com>");
6605c0e09e0SWenyou Yang MODULE_LICENSE("GPL");
661