1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28c0984e5SSebastian Reichel /*
38c0984e5SSebastian Reichel  * TI LP8788 MFD - battery charger driver
48c0984e5SSebastian Reichel  *
58c0984e5SSebastian Reichel  * Copyright 2012 Texas Instruments
68c0984e5SSebastian Reichel  *
78c0984e5SSebastian Reichel  * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
88c0984e5SSebastian Reichel  */
98c0984e5SSebastian Reichel 
108c0984e5SSebastian Reichel #include <linux/err.h>
118c0984e5SSebastian Reichel #include <linux/iio/consumer.h>
128c0984e5SSebastian Reichel #include <linux/interrupt.h>
138c0984e5SSebastian Reichel #include <linux/irqdomain.h>
148c0984e5SSebastian Reichel #include <linux/mfd/lp8788.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/slab.h>
198c0984e5SSebastian Reichel #include <linux/workqueue.h>
208c0984e5SSebastian Reichel 
218c0984e5SSebastian Reichel /* register address */
228c0984e5SSebastian Reichel #define LP8788_CHG_STATUS		0x07
238c0984e5SSebastian Reichel #define LP8788_CHG_IDCIN		0x13
248c0984e5SSebastian Reichel #define LP8788_CHG_IBATT		0x14
258c0984e5SSebastian Reichel #define LP8788_CHG_VTERM		0x15
268c0984e5SSebastian Reichel #define LP8788_CHG_EOC			0x16
278c0984e5SSebastian Reichel 
288c0984e5SSebastian Reichel /* mask/shift bits */
298c0984e5SSebastian Reichel #define LP8788_CHG_INPUT_STATE_M	0x03	/* Addr 07h */
308c0984e5SSebastian Reichel #define LP8788_CHG_STATE_M		0x3C
318c0984e5SSebastian Reichel #define LP8788_CHG_STATE_S		2
328c0984e5SSebastian Reichel #define LP8788_NO_BATT_M		BIT(6)
338c0984e5SSebastian Reichel #define LP8788_BAD_BATT_M		BIT(7)
348c0984e5SSebastian Reichel #define LP8788_CHG_IBATT_M		0x1F	/* Addr 14h */
358c0984e5SSebastian Reichel #define LP8788_CHG_VTERM_M		0x0F	/* Addr 15h */
368c0984e5SSebastian Reichel #define LP8788_CHG_EOC_LEVEL_M		0x30	/* Addr 16h */
378c0984e5SSebastian Reichel #define LP8788_CHG_EOC_LEVEL_S		4
388c0984e5SSebastian Reichel #define LP8788_CHG_EOC_TIME_M		0x0E
398c0984e5SSebastian Reichel #define LP8788_CHG_EOC_TIME_S		1
408c0984e5SSebastian Reichel #define LP8788_CHG_EOC_MODE_M		BIT(0)
418c0984e5SSebastian Reichel 
428c0984e5SSebastian Reichel #define LP8788_CHARGER_NAME		"charger"
438c0984e5SSebastian Reichel #define LP8788_BATTERY_NAME		"main_batt"
448c0984e5SSebastian Reichel 
458c0984e5SSebastian Reichel #define LP8788_CHG_START		0x11
468c0984e5SSebastian Reichel #define LP8788_CHG_END			0x1C
478c0984e5SSebastian Reichel 
488c0984e5SSebastian Reichel #define LP8788_ISEL_MAX			23
498c0984e5SSebastian Reichel #define LP8788_ISEL_STEP		50
508c0984e5SSebastian Reichel #define LP8788_VTERM_MIN		4100
518c0984e5SSebastian Reichel #define LP8788_VTERM_STEP		25
528c0984e5SSebastian Reichel #define LP8788_MAX_BATT_CAPACITY	100
538c0984e5SSebastian Reichel #define LP8788_MAX_CHG_IRQS		11
548c0984e5SSebastian Reichel 
558c0984e5SSebastian Reichel enum lp8788_charging_state {
568c0984e5SSebastian Reichel 	LP8788_OFF,
578c0984e5SSebastian Reichel 	LP8788_WARM_UP,
588c0984e5SSebastian Reichel 	LP8788_LOW_INPUT = 0x3,
598c0984e5SSebastian Reichel 	LP8788_PRECHARGE,
608c0984e5SSebastian Reichel 	LP8788_CC,
618c0984e5SSebastian Reichel 	LP8788_CV,
628c0984e5SSebastian Reichel 	LP8788_MAINTENANCE,
638c0984e5SSebastian Reichel 	LP8788_BATTERY_FAULT,
648c0984e5SSebastian Reichel 	LP8788_SYSTEM_SUPPORT = 0xC,
658c0984e5SSebastian Reichel 	LP8788_HIGH_CURRENT = 0xF,
668c0984e5SSebastian Reichel 	LP8788_MAX_CHG_STATE,
678c0984e5SSebastian Reichel };
688c0984e5SSebastian Reichel 
698c0984e5SSebastian Reichel enum lp8788_charger_adc_sel {
708c0984e5SSebastian Reichel 	LP8788_VBATT,
718c0984e5SSebastian Reichel 	LP8788_BATT_TEMP,
728c0984e5SSebastian Reichel 	LP8788_NUM_CHG_ADC,
738c0984e5SSebastian Reichel };
748c0984e5SSebastian Reichel 
758c0984e5SSebastian Reichel enum lp8788_charger_input_state {
768c0984e5SSebastian Reichel 	LP8788_SYSTEM_SUPPLY = 1,
778c0984e5SSebastian Reichel 	LP8788_FULL_FUNCTION,
788c0984e5SSebastian Reichel };
798c0984e5SSebastian Reichel 
808c0984e5SSebastian Reichel /*
818c0984e5SSebastian Reichel  * struct lp8788_chg_irq
828c0984e5SSebastian Reichel  * @which        : lp8788 interrupt id
838c0984e5SSebastian Reichel  * @virq         : Linux IRQ number from irq_domain
848c0984e5SSebastian Reichel  */
858c0984e5SSebastian Reichel struct lp8788_chg_irq {
868c0984e5SSebastian Reichel 	enum lp8788_int_id which;
878c0984e5SSebastian Reichel 	int virq;
888c0984e5SSebastian Reichel };
898c0984e5SSebastian Reichel 
908c0984e5SSebastian Reichel /*
918c0984e5SSebastian Reichel  * struct lp8788_charger
928c0984e5SSebastian Reichel  * @lp           : used for accessing the registers of mfd lp8788 device
938c0984e5SSebastian Reichel  * @charger      : power supply driver for the battery charger
948c0984e5SSebastian Reichel  * @battery      : power supply driver for the battery
958c0984e5SSebastian Reichel  * @charger_work : work queue for charger input interrupts
968c0984e5SSebastian Reichel  * @chan         : iio channels for getting adc values
978c0984e5SSebastian Reichel  *                 eg) battery voltage, capacity and temperature
988c0984e5SSebastian Reichel  * @irqs         : charger dedicated interrupts
998c0984e5SSebastian Reichel  * @num_irqs     : total numbers of charger interrupts
1008c0984e5SSebastian Reichel  * @pdata        : charger platform specific data
1018c0984e5SSebastian Reichel  */
1028c0984e5SSebastian Reichel struct lp8788_charger {
1038c0984e5SSebastian Reichel 	struct lp8788 *lp;
1048c0984e5SSebastian Reichel 	struct power_supply *charger;
1058c0984e5SSebastian Reichel 	struct power_supply *battery;
1068c0984e5SSebastian Reichel 	struct work_struct charger_work;
1078c0984e5SSebastian Reichel 	struct iio_channel *chan[LP8788_NUM_CHG_ADC];
1088c0984e5SSebastian Reichel 	struct lp8788_chg_irq irqs[LP8788_MAX_CHG_IRQS];
1098c0984e5SSebastian Reichel 	int num_irqs;
1108c0984e5SSebastian Reichel 	struct lp8788_charger_platform_data *pdata;
1118c0984e5SSebastian Reichel };
1128c0984e5SSebastian Reichel 
1138c0984e5SSebastian Reichel static char *battery_supplied_to[] = {
1148c0984e5SSebastian Reichel 	LP8788_BATTERY_NAME,
1158c0984e5SSebastian Reichel };
1168c0984e5SSebastian Reichel 
1178c0984e5SSebastian Reichel static enum power_supply_property lp8788_charger_prop[] = {
1188c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_ONLINE,
1198c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CURRENT_MAX,
1208c0984e5SSebastian Reichel };
1218c0984e5SSebastian Reichel 
1228c0984e5SSebastian Reichel static enum power_supply_property lp8788_battery_prop[] = {
1238c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_STATUS,
1248c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_HEALTH,
1258c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_PRESENT,
1268c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
1278c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CAPACITY,
1288c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
1298c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
1308c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_TEMP,
1318c0984e5SSebastian Reichel };
1328c0984e5SSebastian Reichel 
lp8788_is_charger_detected(struct lp8788_charger * pchg)1338c0984e5SSebastian Reichel static bool lp8788_is_charger_detected(struct lp8788_charger *pchg)
1348c0984e5SSebastian Reichel {
1358c0984e5SSebastian Reichel 	u8 data;
1368c0984e5SSebastian Reichel 
1378c0984e5SSebastian Reichel 	lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
1388c0984e5SSebastian Reichel 	data &= LP8788_CHG_INPUT_STATE_M;
1398c0984e5SSebastian Reichel 
1408c0984e5SSebastian Reichel 	return data == LP8788_SYSTEM_SUPPLY || data == LP8788_FULL_FUNCTION;
1418c0984e5SSebastian Reichel }
1428c0984e5SSebastian Reichel 
lp8788_charger_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)1438c0984e5SSebastian Reichel static int lp8788_charger_get_property(struct power_supply *psy,
1448c0984e5SSebastian Reichel 					enum power_supply_property psp,
1458c0984e5SSebastian Reichel 					union power_supply_propval *val)
1468c0984e5SSebastian Reichel {
1478c0984e5SSebastian Reichel 	struct lp8788_charger *pchg = dev_get_drvdata(psy->dev.parent);
1488c0984e5SSebastian Reichel 	u8 read;
1498c0984e5SSebastian Reichel 
1508c0984e5SSebastian Reichel 	switch (psp) {
1518c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_ONLINE:
1528c0984e5SSebastian Reichel 		val->intval = lp8788_is_charger_detected(pchg);
1538c0984e5SSebastian Reichel 		break;
1548c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CURRENT_MAX:
1558c0984e5SSebastian Reichel 		lp8788_read_byte(pchg->lp, LP8788_CHG_IDCIN, &read);
1568c0984e5SSebastian Reichel 		val->intval = LP8788_ISEL_STEP *
1578c0984e5SSebastian Reichel 				(min_t(int, read, LP8788_ISEL_MAX) + 1);
1588c0984e5SSebastian Reichel 		break;
1598c0984e5SSebastian Reichel 	default:
1608c0984e5SSebastian Reichel 		return -EINVAL;
1618c0984e5SSebastian Reichel 	}
1628c0984e5SSebastian Reichel 
1638c0984e5SSebastian Reichel 	return 0;
1648c0984e5SSebastian Reichel }
1658c0984e5SSebastian Reichel 
lp8788_get_battery_status(struct lp8788_charger * pchg,union power_supply_propval * val)1668c0984e5SSebastian Reichel static int lp8788_get_battery_status(struct lp8788_charger *pchg,
1678c0984e5SSebastian Reichel 				union power_supply_propval *val)
1688c0984e5SSebastian Reichel {
1698c0984e5SSebastian Reichel 	enum lp8788_charging_state state;
1708c0984e5SSebastian Reichel 	u8 data;
1718c0984e5SSebastian Reichel 	int ret;
1728c0984e5SSebastian Reichel 
1738c0984e5SSebastian Reichel 	ret = lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
1748c0984e5SSebastian Reichel 	if (ret)
1758c0984e5SSebastian Reichel 		return ret;
1768c0984e5SSebastian Reichel 
1778c0984e5SSebastian Reichel 	state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S;
1788c0984e5SSebastian Reichel 	switch (state) {
1798c0984e5SSebastian Reichel 	case LP8788_OFF:
1808c0984e5SSebastian Reichel 		val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
1818c0984e5SSebastian Reichel 		break;
1828c0984e5SSebastian Reichel 	case LP8788_PRECHARGE:
1838c0984e5SSebastian Reichel 	case LP8788_CC:
1848c0984e5SSebastian Reichel 	case LP8788_CV:
1858c0984e5SSebastian Reichel 	case LP8788_HIGH_CURRENT:
1868c0984e5SSebastian Reichel 		val->intval = POWER_SUPPLY_STATUS_CHARGING;
1878c0984e5SSebastian Reichel 		break;
1888c0984e5SSebastian Reichel 	case LP8788_MAINTENANCE:
1898c0984e5SSebastian Reichel 		val->intval = POWER_SUPPLY_STATUS_FULL;
1908c0984e5SSebastian Reichel 		break;
1918c0984e5SSebastian Reichel 	default:
1928c0984e5SSebastian Reichel 		val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
1938c0984e5SSebastian Reichel 		break;
1948c0984e5SSebastian Reichel 	}
1958c0984e5SSebastian Reichel 
1968c0984e5SSebastian Reichel 	return 0;
1978c0984e5SSebastian Reichel }
1988c0984e5SSebastian Reichel 
lp8788_get_battery_health(struct lp8788_charger * pchg,union power_supply_propval * val)1998c0984e5SSebastian Reichel static int lp8788_get_battery_health(struct lp8788_charger *pchg,
2008c0984e5SSebastian Reichel 				union power_supply_propval *val)
2018c0984e5SSebastian Reichel {
2028c0984e5SSebastian Reichel 	u8 data;
2038c0984e5SSebastian Reichel 	int ret;
2048c0984e5SSebastian Reichel 
2058c0984e5SSebastian Reichel 	ret = lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
2068c0984e5SSebastian Reichel 	if (ret)
2078c0984e5SSebastian Reichel 		return ret;
2088c0984e5SSebastian Reichel 
2098c0984e5SSebastian Reichel 	if (data & LP8788_NO_BATT_M)
2108c0984e5SSebastian Reichel 		val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
2118c0984e5SSebastian Reichel 	else if (data & LP8788_BAD_BATT_M)
2128c0984e5SSebastian Reichel 		val->intval = POWER_SUPPLY_HEALTH_DEAD;
2138c0984e5SSebastian Reichel 	else
2148c0984e5SSebastian Reichel 		val->intval = POWER_SUPPLY_HEALTH_GOOD;
2158c0984e5SSebastian Reichel 
2168c0984e5SSebastian Reichel 	return 0;
2178c0984e5SSebastian Reichel }
2188c0984e5SSebastian Reichel 
lp8788_get_battery_present(struct lp8788_charger * pchg,union power_supply_propval * val)2198c0984e5SSebastian Reichel static int lp8788_get_battery_present(struct lp8788_charger *pchg,
2208c0984e5SSebastian Reichel 				union power_supply_propval *val)
2218c0984e5SSebastian Reichel {
2228c0984e5SSebastian Reichel 	u8 data;
2238c0984e5SSebastian Reichel 	int ret;
2248c0984e5SSebastian Reichel 
2258c0984e5SSebastian Reichel 	ret = lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
2268c0984e5SSebastian Reichel 	if (ret)
2278c0984e5SSebastian Reichel 		return ret;
2288c0984e5SSebastian Reichel 
2298c0984e5SSebastian Reichel 	val->intval = !(data & LP8788_NO_BATT_M);
2308c0984e5SSebastian Reichel 	return 0;
2318c0984e5SSebastian Reichel }
2328c0984e5SSebastian Reichel 
lp8788_get_vbatt_adc(struct lp8788_charger * pchg,int * result)2338c0984e5SSebastian Reichel static int lp8788_get_vbatt_adc(struct lp8788_charger *pchg, int *result)
2348c0984e5SSebastian Reichel {
2358c0984e5SSebastian Reichel 	struct iio_channel *channel = pchg->chan[LP8788_VBATT];
2368c0984e5SSebastian Reichel 
2378c0984e5SSebastian Reichel 	if (!channel)
2388c0984e5SSebastian Reichel 		return -EINVAL;
2398c0984e5SSebastian Reichel 
2408c0984e5SSebastian Reichel 	return iio_read_channel_processed(channel, result);
2418c0984e5SSebastian Reichel }
2428c0984e5SSebastian Reichel 
lp8788_get_battery_voltage(struct lp8788_charger * pchg,union power_supply_propval * val)2438c0984e5SSebastian Reichel static int lp8788_get_battery_voltage(struct lp8788_charger *pchg,
2448c0984e5SSebastian Reichel 				union power_supply_propval *val)
2458c0984e5SSebastian Reichel {
2468c0984e5SSebastian Reichel 	return lp8788_get_vbatt_adc(pchg, &val->intval);
2478c0984e5SSebastian Reichel }
2488c0984e5SSebastian Reichel 
lp8788_get_battery_capacity(struct lp8788_charger * pchg,union power_supply_propval * val)2498c0984e5SSebastian Reichel static int lp8788_get_battery_capacity(struct lp8788_charger *pchg,
2508c0984e5SSebastian Reichel 				union power_supply_propval *val)
2518c0984e5SSebastian Reichel {
2528c0984e5SSebastian Reichel 	struct lp8788 *lp = pchg->lp;
2538c0984e5SSebastian Reichel 	struct lp8788_charger_platform_data *pdata = pchg->pdata;
2548c0984e5SSebastian Reichel 	unsigned int max_vbatt;
2558c0984e5SSebastian Reichel 	int vbatt;
2568c0984e5SSebastian Reichel 	enum lp8788_charging_state state;
2578c0984e5SSebastian Reichel 	u8 data;
2588c0984e5SSebastian Reichel 	int ret;
2598c0984e5SSebastian Reichel 
2608c0984e5SSebastian Reichel 	if (!pdata)
2618c0984e5SSebastian Reichel 		return -EINVAL;
2628c0984e5SSebastian Reichel 
2638c0984e5SSebastian Reichel 	max_vbatt = pdata->max_vbatt_mv;
2648c0984e5SSebastian Reichel 	if (max_vbatt == 0)
2658c0984e5SSebastian Reichel 		return -EINVAL;
2668c0984e5SSebastian Reichel 
2678c0984e5SSebastian Reichel 	ret = lp8788_read_byte(lp, LP8788_CHG_STATUS, &data);
2688c0984e5SSebastian Reichel 	if (ret)
2698c0984e5SSebastian Reichel 		return ret;
2708c0984e5SSebastian Reichel 
2718c0984e5SSebastian Reichel 	state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S;
2728c0984e5SSebastian Reichel 
2738c0984e5SSebastian Reichel 	if (state == LP8788_MAINTENANCE) {
2748c0984e5SSebastian Reichel 		val->intval = LP8788_MAX_BATT_CAPACITY;
2758c0984e5SSebastian Reichel 	} else {
2768c0984e5SSebastian Reichel 		ret = lp8788_get_vbatt_adc(pchg, &vbatt);
2778c0984e5SSebastian Reichel 		if (ret)
2788c0984e5SSebastian Reichel 			return ret;
2798c0984e5SSebastian Reichel 
2808c0984e5SSebastian Reichel 		val->intval = (vbatt * LP8788_MAX_BATT_CAPACITY) / max_vbatt;
2818c0984e5SSebastian Reichel 		val->intval = min(val->intval, LP8788_MAX_BATT_CAPACITY);
2828c0984e5SSebastian Reichel 	}
2838c0984e5SSebastian Reichel 
2848c0984e5SSebastian Reichel 	return 0;
2858c0984e5SSebastian Reichel }
2868c0984e5SSebastian Reichel 
lp8788_get_battery_temperature(struct lp8788_charger * pchg,union power_supply_propval * val)2878c0984e5SSebastian Reichel static int lp8788_get_battery_temperature(struct lp8788_charger *pchg,
2888c0984e5SSebastian Reichel 				union power_supply_propval *val)
2898c0984e5SSebastian Reichel {
2908c0984e5SSebastian Reichel 	struct iio_channel *channel = pchg->chan[LP8788_BATT_TEMP];
2918c0984e5SSebastian Reichel 	int result;
2928c0984e5SSebastian Reichel 	int ret;
2938c0984e5SSebastian Reichel 
2948c0984e5SSebastian Reichel 	if (!channel)
2958c0984e5SSebastian Reichel 		return -EINVAL;
2968c0984e5SSebastian Reichel 
2978c0984e5SSebastian Reichel 	ret = iio_read_channel_processed(channel, &result);
2988c0984e5SSebastian Reichel 	if (ret < 0)
2998c0984e5SSebastian Reichel 		return -EINVAL;
3008c0984e5SSebastian Reichel 
3018c0984e5SSebastian Reichel 	/* unit: 0.1 'C */
3028c0984e5SSebastian Reichel 	val->intval = result * 10;
3038c0984e5SSebastian Reichel 
3048c0984e5SSebastian Reichel 	return 0;
3058c0984e5SSebastian Reichel }
3068c0984e5SSebastian Reichel 
lp8788_get_battery_charging_current(struct lp8788_charger * pchg,union power_supply_propval * val)3078c0984e5SSebastian Reichel static int lp8788_get_battery_charging_current(struct lp8788_charger *pchg,
3088c0984e5SSebastian Reichel 				union power_supply_propval *val)
3098c0984e5SSebastian Reichel {
3108c0984e5SSebastian Reichel 	u8 read;
3118c0984e5SSebastian Reichel 
3128c0984e5SSebastian Reichel 	lp8788_read_byte(pchg->lp, LP8788_CHG_IBATT, &read);
3138c0984e5SSebastian Reichel 	read &= LP8788_CHG_IBATT_M;
3148c0984e5SSebastian Reichel 	val->intval = LP8788_ISEL_STEP *
3158c0984e5SSebastian Reichel 			(min_t(int, read, LP8788_ISEL_MAX) + 1);
3168c0984e5SSebastian Reichel 
3178c0984e5SSebastian Reichel 	return 0;
3188c0984e5SSebastian Reichel }
3198c0984e5SSebastian Reichel 
lp8788_get_charging_termination_voltage(struct lp8788_charger * pchg,union power_supply_propval * val)3208c0984e5SSebastian Reichel static int lp8788_get_charging_termination_voltage(struct lp8788_charger *pchg,
3218c0984e5SSebastian Reichel 				union power_supply_propval *val)
3228c0984e5SSebastian Reichel {
3238c0984e5SSebastian Reichel 	u8 read;
3248c0984e5SSebastian Reichel 
3258c0984e5SSebastian Reichel 	lp8788_read_byte(pchg->lp, LP8788_CHG_VTERM, &read);
3268c0984e5SSebastian Reichel 	read &= LP8788_CHG_VTERM_M;
3278c0984e5SSebastian Reichel 	val->intval = LP8788_VTERM_MIN + LP8788_VTERM_STEP * read;
3288c0984e5SSebastian Reichel 
3298c0984e5SSebastian Reichel 	return 0;
3308c0984e5SSebastian Reichel }
3318c0984e5SSebastian Reichel 
lp8788_battery_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)3328c0984e5SSebastian Reichel static int lp8788_battery_get_property(struct power_supply *psy,
3338c0984e5SSebastian Reichel 					enum power_supply_property psp,
3348c0984e5SSebastian Reichel 					union power_supply_propval *val)
3358c0984e5SSebastian Reichel {
3368c0984e5SSebastian Reichel 	struct lp8788_charger *pchg = dev_get_drvdata(psy->dev.parent);
3378c0984e5SSebastian Reichel 
3388c0984e5SSebastian Reichel 	switch (psp) {
3398c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_STATUS:
3408c0984e5SSebastian Reichel 		return lp8788_get_battery_status(pchg, val);
3418c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_HEALTH:
3428c0984e5SSebastian Reichel 		return lp8788_get_battery_health(pchg, val);
3438c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_PRESENT:
3448c0984e5SSebastian Reichel 		return lp8788_get_battery_present(pchg, val);
3458c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
3468c0984e5SSebastian Reichel 		return lp8788_get_battery_voltage(pchg, val);
3478c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CAPACITY:
3488c0984e5SSebastian Reichel 		return lp8788_get_battery_capacity(pchg, val);
3498c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_TEMP:
3508c0984e5SSebastian Reichel 		return lp8788_get_battery_temperature(pchg, val);
3518c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
3528c0984e5SSebastian Reichel 		return lp8788_get_battery_charging_current(pchg, val);
3538c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
3548c0984e5SSebastian Reichel 		return lp8788_get_charging_termination_voltage(pchg, val);
3558c0984e5SSebastian Reichel 	default:
3568c0984e5SSebastian Reichel 		return -EINVAL;
3578c0984e5SSebastian Reichel 	}
3588c0984e5SSebastian Reichel }
3598c0984e5SSebastian Reichel 
lp8788_is_valid_charger_register(u8 addr)3608c0984e5SSebastian Reichel static inline bool lp8788_is_valid_charger_register(u8 addr)
3618c0984e5SSebastian Reichel {
3628c0984e5SSebastian Reichel 	return addr >= LP8788_CHG_START && addr <= LP8788_CHG_END;
3638c0984e5SSebastian Reichel }
3648c0984e5SSebastian Reichel 
lp8788_update_charger_params(struct platform_device * pdev,struct lp8788_charger * pchg)3658c0984e5SSebastian Reichel static int lp8788_update_charger_params(struct platform_device *pdev,
3668c0984e5SSebastian Reichel 					struct lp8788_charger *pchg)
3678c0984e5SSebastian Reichel {
3688c0984e5SSebastian Reichel 	struct lp8788 *lp = pchg->lp;
3698c0984e5SSebastian Reichel 	struct lp8788_charger_platform_data *pdata = pchg->pdata;
3708c0984e5SSebastian Reichel 	struct lp8788_chg_param *param;
3718c0984e5SSebastian Reichel 	int i;
3728c0984e5SSebastian Reichel 	int ret;
3738c0984e5SSebastian Reichel 
3748c0984e5SSebastian Reichel 	if (!pdata || !pdata->chg_params) {
3758c0984e5SSebastian Reichel 		dev_info(&pdev->dev, "skip updating charger parameters\n");
3768c0984e5SSebastian Reichel 		return 0;
3778c0984e5SSebastian Reichel 	}
3788c0984e5SSebastian Reichel 
379d5408765SJulia Lawall 	/* setting charging parameters */
3808c0984e5SSebastian Reichel 	for (i = 0; i < pdata->num_chg_params; i++) {
3818c0984e5SSebastian Reichel 		param = pdata->chg_params + i;
3828c0984e5SSebastian Reichel 
3838c0984e5SSebastian Reichel 		if (lp8788_is_valid_charger_register(param->addr)) {
3848c0984e5SSebastian Reichel 			ret = lp8788_write_byte(lp, param->addr, param->val);
3858c0984e5SSebastian Reichel 			if (ret)
3868c0984e5SSebastian Reichel 				return ret;
3878c0984e5SSebastian Reichel 		}
3888c0984e5SSebastian Reichel 	}
3898c0984e5SSebastian Reichel 
3908c0984e5SSebastian Reichel 	return 0;
3918c0984e5SSebastian Reichel }
3928c0984e5SSebastian Reichel 
3938c0984e5SSebastian Reichel static const struct power_supply_desc lp8788_psy_charger_desc = {
3948c0984e5SSebastian Reichel 	.name		= LP8788_CHARGER_NAME,
3958c0984e5SSebastian Reichel 	.type		= POWER_SUPPLY_TYPE_MAINS,
3968c0984e5SSebastian Reichel 	.properties	= lp8788_charger_prop,
3978c0984e5SSebastian Reichel 	.num_properties	= ARRAY_SIZE(lp8788_charger_prop),
3988c0984e5SSebastian Reichel 	.get_property	= lp8788_charger_get_property,
3998c0984e5SSebastian Reichel };
4008c0984e5SSebastian Reichel 
4018c0984e5SSebastian Reichel static const struct power_supply_desc lp8788_psy_battery_desc = {
4028c0984e5SSebastian Reichel 	.name		= LP8788_BATTERY_NAME,
4038c0984e5SSebastian Reichel 	.type		= POWER_SUPPLY_TYPE_BATTERY,
4048c0984e5SSebastian Reichel 	.properties	= lp8788_battery_prop,
4058c0984e5SSebastian Reichel 	.num_properties	= ARRAY_SIZE(lp8788_battery_prop),
4068c0984e5SSebastian Reichel 	.get_property	= lp8788_battery_get_property,
4078c0984e5SSebastian Reichel };
4088c0984e5SSebastian Reichel 
lp8788_psy_unregister(struct lp8788_charger * pchg)4098c0984e5SSebastian Reichel static void lp8788_psy_unregister(struct lp8788_charger *pchg)
4108c0984e5SSebastian Reichel {
4118c0984e5SSebastian Reichel 	power_supply_unregister(pchg->battery);
4128c0984e5SSebastian Reichel 	power_supply_unregister(pchg->charger);
4138c0984e5SSebastian Reichel }
4148c0984e5SSebastian Reichel 
lp8788_charger_event(struct work_struct * work)4158c0984e5SSebastian Reichel static void lp8788_charger_event(struct work_struct *work)
4168c0984e5SSebastian Reichel {
4178c0984e5SSebastian Reichel 	struct lp8788_charger *pchg =
4188c0984e5SSebastian Reichel 		container_of(work, struct lp8788_charger, charger_work);
4198c0984e5SSebastian Reichel 	struct lp8788_charger_platform_data *pdata = pchg->pdata;
4208c0984e5SSebastian Reichel 	enum lp8788_charger_event event = lp8788_is_charger_detected(pchg);
4218c0984e5SSebastian Reichel 
4228c0984e5SSebastian Reichel 	pdata->charger_event(pchg->lp, event);
4238c0984e5SSebastian Reichel }
4248c0984e5SSebastian Reichel 
lp8788_find_irq_id(struct lp8788_charger * pchg,int virq,int * id)4258c0984e5SSebastian Reichel static bool lp8788_find_irq_id(struct lp8788_charger *pchg, int virq, int *id)
4268c0984e5SSebastian Reichel {
4278c0984e5SSebastian Reichel 	bool found = false;
4288c0984e5SSebastian Reichel 	int i;
4298c0984e5SSebastian Reichel 
4308c0984e5SSebastian Reichel 	for (i = 0; i < pchg->num_irqs; i++) {
4318c0984e5SSebastian Reichel 		if (pchg->irqs[i].virq == virq) {
4328c0984e5SSebastian Reichel 			*id = pchg->irqs[i].which;
4338c0984e5SSebastian Reichel 			found = true;
4348c0984e5SSebastian Reichel 			break;
4358c0984e5SSebastian Reichel 		}
4368c0984e5SSebastian Reichel 	}
4378c0984e5SSebastian Reichel 
4388c0984e5SSebastian Reichel 	return found;
4398c0984e5SSebastian Reichel }
4408c0984e5SSebastian Reichel 
lp8788_charger_irq_thread(int virq,void * ptr)4418c0984e5SSebastian Reichel static irqreturn_t lp8788_charger_irq_thread(int virq, void *ptr)
4428c0984e5SSebastian Reichel {
4438c0984e5SSebastian Reichel 	struct lp8788_charger *pchg = ptr;
4448c0984e5SSebastian Reichel 	struct lp8788_charger_platform_data *pdata = pchg->pdata;
4458c0984e5SSebastian Reichel 	int id = -1;
4468c0984e5SSebastian Reichel 
4478c0984e5SSebastian Reichel 	if (!lp8788_find_irq_id(pchg, virq, &id))
4488c0984e5SSebastian Reichel 		return IRQ_NONE;
4498c0984e5SSebastian Reichel 
4508c0984e5SSebastian Reichel 	switch (id) {
4518c0984e5SSebastian Reichel 	case LP8788_INT_CHG_INPUT_STATE:
4528c0984e5SSebastian Reichel 	case LP8788_INT_CHG_STATE:
4538c0984e5SSebastian Reichel 	case LP8788_INT_EOC:
4548c0984e5SSebastian Reichel 	case LP8788_INT_BATT_LOW:
4558c0984e5SSebastian Reichel 	case LP8788_INT_NO_BATT:
4568c0984e5SSebastian Reichel 		power_supply_changed(pchg->charger);
4578c0984e5SSebastian Reichel 		power_supply_changed(pchg->battery);
4588c0984e5SSebastian Reichel 		break;
4598c0984e5SSebastian Reichel 	default:
4608c0984e5SSebastian Reichel 		break;
4618c0984e5SSebastian Reichel 	}
4628c0984e5SSebastian Reichel 
4638c0984e5SSebastian Reichel 	/* report charger dectection event if used */
4648c0984e5SSebastian Reichel 	if (!pdata)
4658c0984e5SSebastian Reichel 		goto irq_handled;
4668c0984e5SSebastian Reichel 
4678c0984e5SSebastian Reichel 	if (pdata->charger_event && id == LP8788_INT_CHG_INPUT_STATE)
4688c0984e5SSebastian Reichel 		schedule_work(&pchg->charger_work);
4698c0984e5SSebastian Reichel 
4708c0984e5SSebastian Reichel irq_handled:
4718c0984e5SSebastian Reichel 	return IRQ_HANDLED;
4728c0984e5SSebastian Reichel }
4738c0984e5SSebastian Reichel 
lp8788_set_irqs(struct platform_device * pdev,struct lp8788_charger * pchg,const char * name)4748c0984e5SSebastian Reichel static int lp8788_set_irqs(struct platform_device *pdev,
4758c0984e5SSebastian Reichel 			struct lp8788_charger *pchg, const char *name)
4768c0984e5SSebastian Reichel {
4778c0984e5SSebastian Reichel 	struct resource *r;
4788c0984e5SSebastian Reichel 	struct irq_domain *irqdm = pchg->lp->irqdm;
4798c0984e5SSebastian Reichel 	int irq_start;
4808c0984e5SSebastian Reichel 	int irq_end;
4818c0984e5SSebastian Reichel 	int virq;
4828c0984e5SSebastian Reichel 	int nr_irq;
4838c0984e5SSebastian Reichel 	int i;
4848c0984e5SSebastian Reichel 	int ret;
4858c0984e5SSebastian Reichel 
4868c0984e5SSebastian Reichel 	/* no error even if no irq resource */
4878c0984e5SSebastian Reichel 	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, name);
4888c0984e5SSebastian Reichel 	if (!r)
4898c0984e5SSebastian Reichel 		return 0;
4908c0984e5SSebastian Reichel 
4918c0984e5SSebastian Reichel 	irq_start = r->start;
4928c0984e5SSebastian Reichel 	irq_end = r->end;
4938c0984e5SSebastian Reichel 
4948c0984e5SSebastian Reichel 	for (i = irq_start; i <= irq_end; i++) {
4958c0984e5SSebastian Reichel 		nr_irq = pchg->num_irqs;
4968c0984e5SSebastian Reichel 
4978c0984e5SSebastian Reichel 		virq = irq_create_mapping(irqdm, i);
4988c0984e5SSebastian Reichel 		pchg->irqs[nr_irq].virq = virq;
4998c0984e5SSebastian Reichel 		pchg->irqs[nr_irq].which = i;
5008c0984e5SSebastian Reichel 		pchg->num_irqs++;
5018c0984e5SSebastian Reichel 
5028c0984e5SSebastian Reichel 		ret = request_threaded_irq(virq, NULL,
5038c0984e5SSebastian Reichel 					lp8788_charger_irq_thread,
5042469b836Sdongjian 					IRQF_ONESHOT, name, pchg);
5058c0984e5SSebastian Reichel 		if (ret)
5068c0984e5SSebastian Reichel 			break;
5078c0984e5SSebastian Reichel 	}
5088c0984e5SSebastian Reichel 
5098c0984e5SSebastian Reichel 	if (i <= irq_end)
5108c0984e5SSebastian Reichel 		goto err_free_irq;
5118c0984e5SSebastian Reichel 
5128c0984e5SSebastian Reichel 	return 0;
5138c0984e5SSebastian Reichel 
5148c0984e5SSebastian Reichel err_free_irq:
5158c0984e5SSebastian Reichel 	for (i = 0; i < pchg->num_irqs; i++)
5168c0984e5SSebastian Reichel 		free_irq(pchg->irqs[i].virq, pchg);
5178c0984e5SSebastian Reichel 	return ret;
5188c0984e5SSebastian Reichel }
5198c0984e5SSebastian Reichel 
lp8788_irq_register(struct platform_device * pdev,struct lp8788_charger * pchg)5208c0984e5SSebastian Reichel static int lp8788_irq_register(struct platform_device *pdev,
5218c0984e5SSebastian Reichel 				struct lp8788_charger *pchg)
5228c0984e5SSebastian Reichel {
5232e5632aeSColin Ian King 	static const char * const name[] = {
5248c0984e5SSebastian Reichel 		LP8788_CHG_IRQ, LP8788_PRSW_IRQ, LP8788_BATT_IRQ
5258c0984e5SSebastian Reichel 	};
5268c0984e5SSebastian Reichel 	int i;
5278c0984e5SSebastian Reichel 	int ret;
5288c0984e5SSebastian Reichel 
5298c0984e5SSebastian Reichel 	INIT_WORK(&pchg->charger_work, lp8788_charger_event);
5308c0984e5SSebastian Reichel 	pchg->num_irqs = 0;
5318c0984e5SSebastian Reichel 
5328c0984e5SSebastian Reichel 	for (i = 0; i < ARRAY_SIZE(name); i++) {
5338c0984e5SSebastian Reichel 		ret = lp8788_set_irqs(pdev, pchg, name[i]);
5348c0984e5SSebastian Reichel 		if (ret) {
5358c0984e5SSebastian Reichel 			dev_warn(&pdev->dev, "irq setup failed: %s\n", name[i]);
5368c0984e5SSebastian Reichel 			return ret;
5378c0984e5SSebastian Reichel 		}
5388c0984e5SSebastian Reichel 	}
5398c0984e5SSebastian Reichel 
5408c0984e5SSebastian Reichel 	if (pchg->num_irqs > LP8788_MAX_CHG_IRQS) {
5418c0984e5SSebastian Reichel 		dev_err(&pdev->dev, "invalid total number of irqs: %d\n",
5428c0984e5SSebastian Reichel 			pchg->num_irqs);
5438c0984e5SSebastian Reichel 		return -EINVAL;
5448c0984e5SSebastian Reichel 	}
5458c0984e5SSebastian Reichel 
5468c0984e5SSebastian Reichel 
5478c0984e5SSebastian Reichel 	return 0;
5488c0984e5SSebastian Reichel }
5498c0984e5SSebastian Reichel 
lp8788_irq_unregister(struct platform_device * pdev,struct lp8788_charger * pchg)5508c0984e5SSebastian Reichel static void lp8788_irq_unregister(struct platform_device *pdev,
5518c0984e5SSebastian Reichel 				  struct lp8788_charger *pchg)
5528c0984e5SSebastian Reichel {
5538c0984e5SSebastian Reichel 	int i;
5548c0984e5SSebastian Reichel 	int irq;
5558c0984e5SSebastian Reichel 
5568c0984e5SSebastian Reichel 	for (i = 0; i < pchg->num_irqs; i++) {
5578c0984e5SSebastian Reichel 		irq = pchg->irqs[i].virq;
5588c0984e5SSebastian Reichel 		if (!irq)
5598c0984e5SSebastian Reichel 			continue;
5608c0984e5SSebastian Reichel 
5618c0984e5SSebastian Reichel 		free_irq(irq, pchg);
5628c0984e5SSebastian Reichel 	}
5638c0984e5SSebastian Reichel }
5648c0984e5SSebastian Reichel 
lp8788_setup_adc_channel(struct device * dev,struct lp8788_charger * pchg)5658c0984e5SSebastian Reichel static void lp8788_setup_adc_channel(struct device *dev,
5668c0984e5SSebastian Reichel 				struct lp8788_charger *pchg)
5678c0984e5SSebastian Reichel {
5688c0984e5SSebastian Reichel 	struct lp8788_charger_platform_data *pdata = pchg->pdata;
5698c0984e5SSebastian Reichel 	struct iio_channel *chan;
5708c0984e5SSebastian Reichel 
5718c0984e5SSebastian Reichel 	if (!pdata)
5728c0984e5SSebastian Reichel 		return;
5738c0984e5SSebastian Reichel 
5748c0984e5SSebastian Reichel 	/* ADC channel for battery voltage */
575934ed384SChristophe JAILLET 	chan = devm_iio_channel_get(dev, pdata->adc_vbatt);
5768c0984e5SSebastian Reichel 	pchg->chan[LP8788_VBATT] = IS_ERR(chan) ? NULL : chan;
5778c0984e5SSebastian Reichel 
5788c0984e5SSebastian Reichel 	/* ADC channel for battery temperature */
579934ed384SChristophe JAILLET 	chan = devm_iio_channel_get(dev, pdata->adc_batt_temp);
5808c0984e5SSebastian Reichel 	pchg->chan[LP8788_BATT_TEMP] = IS_ERR(chan) ? NULL : chan;
5818c0984e5SSebastian Reichel }
5828c0984e5SSebastian Reichel 
lp8788_show_charger_status(struct device * dev,struct device_attribute * attr,char * buf)5838c0984e5SSebastian Reichel static ssize_t lp8788_show_charger_status(struct device *dev,
5848c0984e5SSebastian Reichel 				struct device_attribute *attr, char *buf)
5858c0984e5SSebastian Reichel {
5868c0984e5SSebastian Reichel 	struct lp8788_charger *pchg = dev_get_drvdata(dev);
5878c0984e5SSebastian Reichel 	enum lp8788_charging_state state;
588810e006aSColin Ian King 	static const char * const desc[LP8788_MAX_CHG_STATE] = {
5898c0984e5SSebastian Reichel 		[LP8788_OFF] = "CHARGER OFF",
5908c0984e5SSebastian Reichel 		[LP8788_WARM_UP] = "WARM UP",
5918c0984e5SSebastian Reichel 		[LP8788_LOW_INPUT] = "LOW INPUT STATE",
5928c0984e5SSebastian Reichel 		[LP8788_PRECHARGE] = "CHARGING - PRECHARGE",
5938c0984e5SSebastian Reichel 		[LP8788_CC] = "CHARGING - CC",
5948c0984e5SSebastian Reichel 		[LP8788_CV] = "CHARGING - CV",
5958c0984e5SSebastian Reichel 		[LP8788_MAINTENANCE] = "NO CHARGING - MAINTENANCE",
5968c0984e5SSebastian Reichel 		[LP8788_BATTERY_FAULT] = "BATTERY FAULT",
5978c0984e5SSebastian Reichel 		[LP8788_SYSTEM_SUPPORT] = "SYSTEM SUPPORT",
5988c0984e5SSebastian Reichel 		[LP8788_HIGH_CURRENT] = "HIGH CURRENT",
5998c0984e5SSebastian Reichel 	};
6008c0984e5SSebastian Reichel 	u8 data;
6018c0984e5SSebastian Reichel 
6028c0984e5SSebastian Reichel 	lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
6038c0984e5SSebastian Reichel 	state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S;
6048c0984e5SSebastian Reichel 
605*35aa0628Sye xingchen 	return sysfs_emit(buf, "%s\n", desc[state]);
6068c0984e5SSebastian Reichel }
6078c0984e5SSebastian Reichel 
lp8788_show_eoc_time(struct device * dev,struct device_attribute * attr,char * buf)6088c0984e5SSebastian Reichel static ssize_t lp8788_show_eoc_time(struct device *dev,
6098c0984e5SSebastian Reichel 				struct device_attribute *attr, char *buf)
6108c0984e5SSebastian Reichel {
6118c0984e5SSebastian Reichel 	struct lp8788_charger *pchg = dev_get_drvdata(dev);
612810e006aSColin Ian King 	static const char * const stime[] = {
613810e006aSColin Ian King 		"400ms", "5min", "10min", "15min",
614810e006aSColin Ian King 		"20min", "25min", "30min", "No timeout"
615810e006aSColin Ian King 	};
6168c0984e5SSebastian Reichel 	u8 val;
6178c0984e5SSebastian Reichel 
6188c0984e5SSebastian Reichel 	lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val);
6198c0984e5SSebastian Reichel 	val = (val & LP8788_CHG_EOC_TIME_M) >> LP8788_CHG_EOC_TIME_S;
6208c0984e5SSebastian Reichel 
621*35aa0628Sye xingchen 	return sysfs_emit(buf, "End Of Charge Time: %s\n", stime[val]);
6228c0984e5SSebastian Reichel }
6238c0984e5SSebastian Reichel 
lp8788_show_eoc_level(struct device * dev,struct device_attribute * attr,char * buf)6248c0984e5SSebastian Reichel static ssize_t lp8788_show_eoc_level(struct device *dev,
6258c0984e5SSebastian Reichel 				struct device_attribute *attr, char *buf)
6268c0984e5SSebastian Reichel {
6278c0984e5SSebastian Reichel 	struct lp8788_charger *pchg = dev_get_drvdata(dev);
628810e006aSColin Ian King 	static const char * const abs_level[] = {
629810e006aSColin Ian King 			"25mA", "49mA", "75mA", "98mA"
630810e006aSColin Ian King 	};
631810e006aSColin Ian King 	static const char * const relative_level[] = {
632810e006aSColin Ian King 			"5%", "10%", "15%", "20%"
633810e006aSColin Ian King 	};
634810e006aSColin Ian King 	const char *level;
6358c0984e5SSebastian Reichel 	u8 val;
6368c0984e5SSebastian Reichel 	u8 mode;
6378c0984e5SSebastian Reichel 
6388c0984e5SSebastian Reichel 	lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val);
6398c0984e5SSebastian Reichel 
6408c0984e5SSebastian Reichel 	mode = val & LP8788_CHG_EOC_MODE_M;
6418c0984e5SSebastian Reichel 	val = (val & LP8788_CHG_EOC_LEVEL_M) >> LP8788_CHG_EOC_LEVEL_S;
6428c0984e5SSebastian Reichel 	level = mode ? abs_level[val] : relative_level[val];
6438c0984e5SSebastian Reichel 
644*35aa0628Sye xingchen 	return sysfs_emit(buf, "End Of Charge Level: %s\n", level);
6458c0984e5SSebastian Reichel }
6468c0984e5SSebastian Reichel 
6478c0984e5SSebastian Reichel static DEVICE_ATTR(charger_status, S_IRUSR, lp8788_show_charger_status, NULL);
6488c0984e5SSebastian Reichel static DEVICE_ATTR(eoc_time, S_IRUSR, lp8788_show_eoc_time, NULL);
6498c0984e5SSebastian Reichel static DEVICE_ATTR(eoc_level, S_IRUSR, lp8788_show_eoc_level, NULL);
6508c0984e5SSebastian Reichel 
651b1f7ee89SSebastian Reichel static struct attribute *lp8788_charger_sysfs_attrs[] = {
6528c0984e5SSebastian Reichel 	&dev_attr_charger_status.attr,
6538c0984e5SSebastian Reichel 	&dev_attr_eoc_time.attr,
6548c0984e5SSebastian Reichel 	&dev_attr_eoc_level.attr,
6558c0984e5SSebastian Reichel 	NULL,
6568c0984e5SSebastian Reichel };
6578c0984e5SSebastian Reichel 
658b1f7ee89SSebastian Reichel ATTRIBUTE_GROUPS(lp8788_charger_sysfs);
659b1f7ee89SSebastian Reichel 
lp8788_psy_register(struct platform_device * pdev,struct lp8788_charger * pchg)660b1f7ee89SSebastian Reichel static int lp8788_psy_register(struct platform_device *pdev,
661b1f7ee89SSebastian Reichel 				struct lp8788_charger *pchg)
662b1f7ee89SSebastian Reichel {
663b1f7ee89SSebastian Reichel 	struct power_supply_config charger_cfg = {};
664b1f7ee89SSebastian Reichel 
665b1f7ee89SSebastian Reichel 	charger_cfg.attr_grp = lp8788_charger_sysfs_groups;
666b1f7ee89SSebastian Reichel 	charger_cfg.supplied_to = battery_supplied_to;
667b1f7ee89SSebastian Reichel 	charger_cfg.num_supplicants = ARRAY_SIZE(battery_supplied_to);
668b1f7ee89SSebastian Reichel 
669b1f7ee89SSebastian Reichel 	pchg->charger = power_supply_register(&pdev->dev,
670b1f7ee89SSebastian Reichel 					      &lp8788_psy_charger_desc,
671b1f7ee89SSebastian Reichel 					      &charger_cfg);
672b1f7ee89SSebastian Reichel 	if (IS_ERR(pchg->charger))
673b1f7ee89SSebastian Reichel 		return -EPERM;
674b1f7ee89SSebastian Reichel 
675b1f7ee89SSebastian Reichel 	pchg->battery = power_supply_register(&pdev->dev,
676b1f7ee89SSebastian Reichel 					      &lp8788_psy_battery_desc, NULL);
677b1f7ee89SSebastian Reichel 	if (IS_ERR(pchg->battery)) {
678b1f7ee89SSebastian Reichel 		power_supply_unregister(pchg->charger);
679b1f7ee89SSebastian Reichel 		return -EPERM;
680b1f7ee89SSebastian Reichel 	}
681b1f7ee89SSebastian Reichel 
682b1f7ee89SSebastian Reichel 	return 0;
683b1f7ee89SSebastian Reichel }
6848c0984e5SSebastian Reichel 
lp8788_charger_probe(struct platform_device * pdev)6858c0984e5SSebastian Reichel static int lp8788_charger_probe(struct platform_device *pdev)
6868c0984e5SSebastian Reichel {
6878c0984e5SSebastian Reichel 	struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
6888c0984e5SSebastian Reichel 	struct lp8788_charger *pchg;
6898c0984e5SSebastian Reichel 	struct device *dev = &pdev->dev;
6908c0984e5SSebastian Reichel 	int ret;
6918c0984e5SSebastian Reichel 
6928c0984e5SSebastian Reichel 	pchg = devm_kzalloc(dev, sizeof(struct lp8788_charger), GFP_KERNEL);
6938c0984e5SSebastian Reichel 	if (!pchg)
6948c0984e5SSebastian Reichel 		return -ENOMEM;
6958c0984e5SSebastian Reichel 
6968c0984e5SSebastian Reichel 	pchg->lp = lp;
6978c0984e5SSebastian Reichel 	pchg->pdata = lp->pdata ? lp->pdata->chg_pdata : NULL;
6988c0984e5SSebastian Reichel 	platform_set_drvdata(pdev, pchg);
6998c0984e5SSebastian Reichel 
7008c0984e5SSebastian Reichel 	ret = lp8788_update_charger_params(pdev, pchg);
7018c0984e5SSebastian Reichel 	if (ret)
7028c0984e5SSebastian Reichel 		return ret;
7038c0984e5SSebastian Reichel 
7048c0984e5SSebastian Reichel 	lp8788_setup_adc_channel(&pdev->dev, pchg);
7058c0984e5SSebastian Reichel 
7068c0984e5SSebastian Reichel 	ret = lp8788_psy_register(pdev, pchg);
7078c0984e5SSebastian Reichel 	if (ret)
7088c0984e5SSebastian Reichel 		return ret;
7098c0984e5SSebastian Reichel 
7108c0984e5SSebastian Reichel 	ret = lp8788_irq_register(pdev, pchg);
7118c0984e5SSebastian Reichel 	if (ret)
7128c0984e5SSebastian Reichel 		dev_warn(dev, "failed to register charger irq: %d\n", ret);
7138c0984e5SSebastian Reichel 
7148c0984e5SSebastian Reichel 	return 0;
7158c0984e5SSebastian Reichel }
7168c0984e5SSebastian Reichel 
lp8788_charger_remove(struct platform_device * pdev)7178c0984e5SSebastian Reichel static int lp8788_charger_remove(struct platform_device *pdev)
7188c0984e5SSebastian Reichel {
7198c0984e5SSebastian Reichel 	struct lp8788_charger *pchg = platform_get_drvdata(pdev);
7208c0984e5SSebastian Reichel 
7218c0984e5SSebastian Reichel 	flush_work(&pchg->charger_work);
7228c0984e5SSebastian Reichel 	lp8788_irq_unregister(pdev, pchg);
7238c0984e5SSebastian Reichel 	lp8788_psy_unregister(pchg);
7248c0984e5SSebastian Reichel 
7258c0984e5SSebastian Reichel 	return 0;
7268c0984e5SSebastian Reichel }
7278c0984e5SSebastian Reichel 
7288c0984e5SSebastian Reichel static struct platform_driver lp8788_charger_driver = {
7298c0984e5SSebastian Reichel 	.probe = lp8788_charger_probe,
7308c0984e5SSebastian Reichel 	.remove = lp8788_charger_remove,
7318c0984e5SSebastian Reichel 	.driver = {
7328c0984e5SSebastian Reichel 		.name = LP8788_DEV_CHARGER,
7338c0984e5SSebastian Reichel 	},
7348c0984e5SSebastian Reichel };
7358c0984e5SSebastian Reichel module_platform_driver(lp8788_charger_driver);
7368c0984e5SSebastian Reichel 
7378c0984e5SSebastian Reichel MODULE_DESCRIPTION("TI LP8788 Charger Driver");
7388c0984e5SSebastian Reichel MODULE_AUTHOR("Milo Kim");
7398c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
7408c0984e5SSebastian Reichel MODULE_ALIAS("platform:lp8788-charger");
741