11426dffaSAndreas Kemnade // SPDX-License-Identifier: GPL-2.0+
21426dffaSAndreas Kemnade /*
31426dffaSAndreas Kemnade  * Power supply driver for the RICOH RN5T618 power management chip family
41426dffaSAndreas Kemnade  *
51426dffaSAndreas Kemnade  * Copyright (C) 2020 Andreas Kemnade
61426dffaSAndreas Kemnade  */
71426dffaSAndreas Kemnade 
81426dffaSAndreas Kemnade #include <linux/kernel.h>
91426dffaSAndreas Kemnade #include <linux/device.h>
101426dffaSAndreas Kemnade #include <linux/bitops.h>
111426dffaSAndreas Kemnade #include <linux/errno.h>
12*2f5caa26SAndreas Kemnade #include <linux/iio/consumer.h>
131426dffaSAndreas Kemnade #include <linux/init.h>
141426dffaSAndreas Kemnade #include <linux/interrupt.h>
151426dffaSAndreas Kemnade #include <linux/module.h>
161426dffaSAndreas Kemnade #include <linux/mfd/rn5t618.h>
171426dffaSAndreas Kemnade #include <linux/platform_device.h>
181426dffaSAndreas Kemnade #include <linux/power_supply.h>
191426dffaSAndreas Kemnade #include <linux/regmap.h>
201426dffaSAndreas Kemnade #include <linux/slab.h>
211426dffaSAndreas Kemnade 
221426dffaSAndreas Kemnade #define CHG_STATE_ADP_INPUT 0x40
231426dffaSAndreas Kemnade #define CHG_STATE_USB_INPUT 0x80
241426dffaSAndreas Kemnade #define CHG_STATE_MASK	0x1f
251426dffaSAndreas Kemnade #define CHG_STATE_CHG_OFF	0
261426dffaSAndreas Kemnade #define CHG_STATE_CHG_READY_VADP	1
271426dffaSAndreas Kemnade #define CHG_STATE_CHG_TRICKLE	2
281426dffaSAndreas Kemnade #define CHG_STATE_CHG_RAPID	3
291426dffaSAndreas Kemnade #define CHG_STATE_CHG_COMPLETE	4
301426dffaSAndreas Kemnade #define CHG_STATE_SUSPEND	5
311426dffaSAndreas Kemnade #define CHG_STATE_VCHG_OVER_VOL	6
321426dffaSAndreas Kemnade #define CHG_STATE_BAT_ERROR	7
331426dffaSAndreas Kemnade #define CHG_STATE_NO_BAT	8
341426dffaSAndreas Kemnade #define CHG_STATE_BAT_OVER_VOL	9
351426dffaSAndreas Kemnade #define CHG_STATE_BAT_TEMP_ERR	10
361426dffaSAndreas Kemnade #define CHG_STATE_DIE_ERR	11
371426dffaSAndreas Kemnade #define CHG_STATE_DIE_SHUTDOWN	12
381426dffaSAndreas Kemnade #define CHG_STATE_NO_BAT2	13
391426dffaSAndreas Kemnade #define CHG_STATE_CHG_READY_VUSB	14
401426dffaSAndreas Kemnade 
417d763677SAndreas Kemnade #define GCHGDET_TYPE_MASK 0x30
427d763677SAndreas Kemnade #define GCHGDET_TYPE_SDP 0x00
437d763677SAndreas Kemnade #define GCHGDET_TYPE_CDP 0x10
447d763677SAndreas Kemnade #define GCHGDET_TYPE_DCP 0x20
457d763677SAndreas Kemnade 
461426dffaSAndreas Kemnade #define FG_ENABLE 1
471426dffaSAndreas Kemnade 
4820a3c8b5SAndreas Kemnade /*
4920a3c8b5SAndreas Kemnade  * Formula seems accurate for battery current, but for USB current around 70mA
5020a3c8b5SAndreas Kemnade  * per step was seen on Kobo Clara HD but all sources show the same formula
5120a3c8b5SAndreas Kemnade  * also fur USB current. To avoid accidentially unwanted high currents we stick
5220a3c8b5SAndreas Kemnade  * to that formula
5320a3c8b5SAndreas Kemnade  */
5420a3c8b5SAndreas Kemnade #define TO_CUR_REG(x) ((x) / 100000 - 1)
5520a3c8b5SAndreas Kemnade #define FROM_CUR_REG(x) ((((x) & 0x1f) + 1) * 100000)
5620a3c8b5SAndreas Kemnade #define CHG_MIN_CUR 100000
5720a3c8b5SAndreas Kemnade #define CHG_MAX_CUR 1800000
5820a3c8b5SAndreas Kemnade #define ADP_MAX_CUR 2500000
5920a3c8b5SAndreas Kemnade #define USB_MAX_CUR 1400000
6020a3c8b5SAndreas Kemnade 
6120a3c8b5SAndreas Kemnade 
621426dffaSAndreas Kemnade struct rn5t618_power_info {
631426dffaSAndreas Kemnade 	struct rn5t618 *rn5t618;
641426dffaSAndreas Kemnade 	struct platform_device *pdev;
651426dffaSAndreas Kemnade 	struct power_supply *battery;
661426dffaSAndreas Kemnade 	struct power_supply *usb;
671426dffaSAndreas Kemnade 	struct power_supply *adp;
68*2f5caa26SAndreas Kemnade 	struct iio_channel *channel_vusb;
69*2f5caa26SAndreas Kemnade 	struct iio_channel *channel_vadp;
701426dffaSAndreas Kemnade 	int irq;
711426dffaSAndreas Kemnade };
721426dffaSAndreas Kemnade 
737d763677SAndreas Kemnade static enum power_supply_usb_type rn5t618_usb_types[] = {
747d763677SAndreas Kemnade 	POWER_SUPPLY_USB_TYPE_SDP,
757d763677SAndreas Kemnade 	POWER_SUPPLY_USB_TYPE_DCP,
767d763677SAndreas Kemnade 	POWER_SUPPLY_USB_TYPE_CDP,
777d763677SAndreas Kemnade 	POWER_SUPPLY_USB_TYPE_UNKNOWN
787d763677SAndreas Kemnade };
797d763677SAndreas Kemnade 
801426dffaSAndreas Kemnade static enum power_supply_property rn5t618_usb_props[] = {
8120a3c8b5SAndreas Kemnade 	/* input current limit is not very accurate */
8220a3c8b5SAndreas Kemnade 	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
83*2f5caa26SAndreas Kemnade 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
841426dffaSAndreas Kemnade 	POWER_SUPPLY_PROP_STATUS,
857d763677SAndreas Kemnade 	POWER_SUPPLY_PROP_USB_TYPE,
861426dffaSAndreas Kemnade 	POWER_SUPPLY_PROP_ONLINE,
871426dffaSAndreas Kemnade };
881426dffaSAndreas Kemnade 
891426dffaSAndreas Kemnade static enum power_supply_property rn5t618_adp_props[] = {
9020a3c8b5SAndreas Kemnade 	/* input current limit is not very accurate */
9120a3c8b5SAndreas Kemnade 	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
92*2f5caa26SAndreas Kemnade 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
931426dffaSAndreas Kemnade 	POWER_SUPPLY_PROP_STATUS,
941426dffaSAndreas Kemnade 	POWER_SUPPLY_PROP_ONLINE,
951426dffaSAndreas Kemnade };
961426dffaSAndreas Kemnade 
971426dffaSAndreas Kemnade 
981426dffaSAndreas Kemnade static enum power_supply_property rn5t618_battery_props[] = {
991426dffaSAndreas Kemnade 	POWER_SUPPLY_PROP_STATUS,
1001426dffaSAndreas Kemnade 	POWER_SUPPLY_PROP_PRESENT,
1011426dffaSAndreas Kemnade 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
1021426dffaSAndreas Kemnade 	POWER_SUPPLY_PROP_CURRENT_NOW,
1031426dffaSAndreas Kemnade 	POWER_SUPPLY_PROP_CAPACITY,
1041426dffaSAndreas Kemnade 	POWER_SUPPLY_PROP_TEMP,
1051426dffaSAndreas Kemnade 	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
1061426dffaSAndreas Kemnade 	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
1071426dffaSAndreas Kemnade 	POWER_SUPPLY_PROP_TECHNOLOGY,
10820a3c8b5SAndreas Kemnade 	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
1091426dffaSAndreas Kemnade 	POWER_SUPPLY_PROP_CHARGE_FULL,
1101426dffaSAndreas Kemnade 	POWER_SUPPLY_PROP_CHARGE_NOW,
1111426dffaSAndreas Kemnade };
1121426dffaSAndreas Kemnade 
rn5t618_battery_read_doublereg(struct rn5t618_power_info * info,u8 reg,u16 * result)1131426dffaSAndreas Kemnade static int rn5t618_battery_read_doublereg(struct rn5t618_power_info *info,
1141426dffaSAndreas Kemnade 					  u8 reg, u16 *result)
1151426dffaSAndreas Kemnade {
1161426dffaSAndreas Kemnade 	int ret, i;
1171426dffaSAndreas Kemnade 	u8 data[2];
1181426dffaSAndreas Kemnade 	u16 old, new;
1191426dffaSAndreas Kemnade 
1201426dffaSAndreas Kemnade 	old = 0;
1211426dffaSAndreas Kemnade 	/* Prevent races when registers are changing. */
1221426dffaSAndreas Kemnade 	for (i = 0; i < 3; i++) {
1231426dffaSAndreas Kemnade 		ret = regmap_bulk_read(info->rn5t618->regmap,
1241426dffaSAndreas Kemnade 				       reg, data, sizeof(data));
1251426dffaSAndreas Kemnade 		if (ret)
1261426dffaSAndreas Kemnade 			return ret;
1271426dffaSAndreas Kemnade 
1281426dffaSAndreas Kemnade 		new = data[0] << 8;
1291426dffaSAndreas Kemnade 		new |= data[1];
1301426dffaSAndreas Kemnade 		if (new == old)
1311426dffaSAndreas Kemnade 			break;
1321426dffaSAndreas Kemnade 
1331426dffaSAndreas Kemnade 		old = new;
1341426dffaSAndreas Kemnade 	}
1351426dffaSAndreas Kemnade 
1361426dffaSAndreas Kemnade 	*result = new;
1371426dffaSAndreas Kemnade 
1381426dffaSAndreas Kemnade 	return 0;
1391426dffaSAndreas Kemnade }
1401426dffaSAndreas Kemnade 
rn5t618_decode_status(unsigned int status)1411426dffaSAndreas Kemnade static int rn5t618_decode_status(unsigned int status)
1421426dffaSAndreas Kemnade {
1431426dffaSAndreas Kemnade 	switch (status & CHG_STATE_MASK) {
1441426dffaSAndreas Kemnade 	case CHG_STATE_CHG_OFF:
1451426dffaSAndreas Kemnade 	case CHG_STATE_SUSPEND:
1461426dffaSAndreas Kemnade 	case CHG_STATE_VCHG_OVER_VOL:
1471426dffaSAndreas Kemnade 	case CHG_STATE_DIE_SHUTDOWN:
1481426dffaSAndreas Kemnade 		return POWER_SUPPLY_STATUS_DISCHARGING;
1491426dffaSAndreas Kemnade 
1501426dffaSAndreas Kemnade 	case CHG_STATE_CHG_TRICKLE:
1511426dffaSAndreas Kemnade 	case CHG_STATE_CHG_RAPID:
1521426dffaSAndreas Kemnade 		return POWER_SUPPLY_STATUS_CHARGING;
1531426dffaSAndreas Kemnade 
1541426dffaSAndreas Kemnade 	case CHG_STATE_CHG_COMPLETE:
1551426dffaSAndreas Kemnade 		return POWER_SUPPLY_STATUS_FULL;
1561426dffaSAndreas Kemnade 
1571426dffaSAndreas Kemnade 	default:
1581426dffaSAndreas Kemnade 		return POWER_SUPPLY_STATUS_NOT_CHARGING;
1591426dffaSAndreas Kemnade 	}
1601426dffaSAndreas Kemnade }
1611426dffaSAndreas Kemnade 
rn5t618_battery_status(struct rn5t618_power_info * info,union power_supply_propval * val)1621426dffaSAndreas Kemnade static int rn5t618_battery_status(struct rn5t618_power_info *info,
1631426dffaSAndreas Kemnade 				  union power_supply_propval *val)
1641426dffaSAndreas Kemnade {
1651426dffaSAndreas Kemnade 	unsigned int v;
1661426dffaSAndreas Kemnade 	int ret;
1671426dffaSAndreas Kemnade 
1681426dffaSAndreas Kemnade 	ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &v);
1691426dffaSAndreas Kemnade 	if (ret)
1701426dffaSAndreas Kemnade 		return ret;
1711426dffaSAndreas Kemnade 
1721426dffaSAndreas Kemnade 	val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
1731426dffaSAndreas Kemnade 
1741426dffaSAndreas Kemnade 	if (v & 0xc0) { /* USB or ADP plugged */
1751426dffaSAndreas Kemnade 		val->intval = rn5t618_decode_status(v);
1761426dffaSAndreas Kemnade 	} else
1771426dffaSAndreas Kemnade 		val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
1781426dffaSAndreas Kemnade 
1791426dffaSAndreas Kemnade 	return ret;
1801426dffaSAndreas Kemnade }
1811426dffaSAndreas Kemnade 
rn5t618_battery_present(struct rn5t618_power_info * info,union power_supply_propval * val)1821426dffaSAndreas Kemnade static int rn5t618_battery_present(struct rn5t618_power_info *info,
1831426dffaSAndreas Kemnade 				   union power_supply_propval *val)
1841426dffaSAndreas Kemnade {
1851426dffaSAndreas Kemnade 	unsigned int v;
1861426dffaSAndreas Kemnade 	int ret;
1871426dffaSAndreas Kemnade 
1881426dffaSAndreas Kemnade 	ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &v);
1891426dffaSAndreas Kemnade 	if (ret)
1901426dffaSAndreas Kemnade 		return ret;
1911426dffaSAndreas Kemnade 
1921426dffaSAndreas Kemnade 	v &= CHG_STATE_MASK;
1931426dffaSAndreas Kemnade 	if ((v == CHG_STATE_NO_BAT) || (v == CHG_STATE_NO_BAT2))
1941426dffaSAndreas Kemnade 		val->intval = 0;
1951426dffaSAndreas Kemnade 	else
1961426dffaSAndreas Kemnade 		val->intval = 1;
1971426dffaSAndreas Kemnade 
1981426dffaSAndreas Kemnade 	return ret;
1991426dffaSAndreas Kemnade }
2001426dffaSAndreas Kemnade 
rn5t618_battery_voltage_now(struct rn5t618_power_info * info,union power_supply_propval * val)2011426dffaSAndreas Kemnade static int rn5t618_battery_voltage_now(struct rn5t618_power_info *info,
2021426dffaSAndreas Kemnade 				       union power_supply_propval *val)
2031426dffaSAndreas Kemnade {
2041426dffaSAndreas Kemnade 	u16 res;
2051426dffaSAndreas Kemnade 	int ret;
2061426dffaSAndreas Kemnade 
2071426dffaSAndreas Kemnade 	ret = rn5t618_battery_read_doublereg(info, RN5T618_VOLTAGE_1, &res);
2081426dffaSAndreas Kemnade 	if (ret)
2091426dffaSAndreas Kemnade 		return ret;
2101426dffaSAndreas Kemnade 
2111426dffaSAndreas Kemnade 	val->intval = res * 2 * 2500 / 4095 * 1000;
2121426dffaSAndreas Kemnade 
2131426dffaSAndreas Kemnade 	return 0;
2141426dffaSAndreas Kemnade }
2151426dffaSAndreas Kemnade 
rn5t618_battery_current_now(struct rn5t618_power_info * info,union power_supply_propval * val)2161426dffaSAndreas Kemnade static int rn5t618_battery_current_now(struct rn5t618_power_info *info,
2171426dffaSAndreas Kemnade 				       union power_supply_propval *val)
2181426dffaSAndreas Kemnade {
2191426dffaSAndreas Kemnade 	u16 res;
2201426dffaSAndreas Kemnade 	int ret;
2211426dffaSAndreas Kemnade 
2221426dffaSAndreas Kemnade 	ret = rn5t618_battery_read_doublereg(info, RN5T618_CC_AVEREG1, &res);
2231426dffaSAndreas Kemnade 	if (ret)
2241426dffaSAndreas Kemnade 		return ret;
2251426dffaSAndreas Kemnade 
2261426dffaSAndreas Kemnade 	/* current is negative when discharging */
2271426dffaSAndreas Kemnade 	val->intval = sign_extend32(res, 13) * 1000;
2281426dffaSAndreas Kemnade 
2291426dffaSAndreas Kemnade 	return 0;
2301426dffaSAndreas Kemnade }
2311426dffaSAndreas Kemnade 
rn5t618_battery_capacity(struct rn5t618_power_info * info,union power_supply_propval * val)2321426dffaSAndreas Kemnade static int rn5t618_battery_capacity(struct rn5t618_power_info *info,
2331426dffaSAndreas Kemnade 				    union power_supply_propval *val)
2341426dffaSAndreas Kemnade {
2351426dffaSAndreas Kemnade 	unsigned int v;
2361426dffaSAndreas Kemnade 	int ret;
2371426dffaSAndreas Kemnade 
2381426dffaSAndreas Kemnade 	ret = regmap_read(info->rn5t618->regmap, RN5T618_SOC, &v);
2391426dffaSAndreas Kemnade 	if (ret)
2401426dffaSAndreas Kemnade 		return ret;
2411426dffaSAndreas Kemnade 
2421426dffaSAndreas Kemnade 	val->intval = v;
2431426dffaSAndreas Kemnade 
2441426dffaSAndreas Kemnade 	return 0;
2451426dffaSAndreas Kemnade }
2461426dffaSAndreas Kemnade 
rn5t618_battery_temp(struct rn5t618_power_info * info,union power_supply_propval * val)2471426dffaSAndreas Kemnade static int rn5t618_battery_temp(struct rn5t618_power_info *info,
2481426dffaSAndreas Kemnade 				union power_supply_propval *val)
2491426dffaSAndreas Kemnade {
2501426dffaSAndreas Kemnade 	u16 res;
2511426dffaSAndreas Kemnade 	int ret;
2521426dffaSAndreas Kemnade 
2531426dffaSAndreas Kemnade 	ret = rn5t618_battery_read_doublereg(info, RN5T618_TEMP_1, &res);
2541426dffaSAndreas Kemnade 	if (ret)
2551426dffaSAndreas Kemnade 		return ret;
2561426dffaSAndreas Kemnade 
2571426dffaSAndreas Kemnade 	val->intval = sign_extend32(res, 11) * 10 / 16;
2581426dffaSAndreas Kemnade 
2591426dffaSAndreas Kemnade 	return 0;
2601426dffaSAndreas Kemnade }
2611426dffaSAndreas Kemnade 
rn5t618_battery_tte(struct rn5t618_power_info * info,union power_supply_propval * val)2621426dffaSAndreas Kemnade static int rn5t618_battery_tte(struct rn5t618_power_info *info,
2631426dffaSAndreas Kemnade 			       union power_supply_propval *val)
2641426dffaSAndreas Kemnade {
2651426dffaSAndreas Kemnade 	u16 res;
2661426dffaSAndreas Kemnade 	int ret;
2671426dffaSAndreas Kemnade 
2681426dffaSAndreas Kemnade 	ret = rn5t618_battery_read_doublereg(info, RN5T618_TT_EMPTY_H, &res);
2691426dffaSAndreas Kemnade 	if (ret)
2701426dffaSAndreas Kemnade 		return ret;
2711426dffaSAndreas Kemnade 
2721426dffaSAndreas Kemnade 	if (res == 65535)
2731426dffaSAndreas Kemnade 		return -ENODATA;
2741426dffaSAndreas Kemnade 
2751426dffaSAndreas Kemnade 	val->intval = res * 60;
2761426dffaSAndreas Kemnade 
2771426dffaSAndreas Kemnade 	return 0;
2781426dffaSAndreas Kemnade }
2791426dffaSAndreas Kemnade 
rn5t618_battery_ttf(struct rn5t618_power_info * info,union power_supply_propval * val)2801426dffaSAndreas Kemnade static int rn5t618_battery_ttf(struct rn5t618_power_info *info,
2811426dffaSAndreas Kemnade 			       union power_supply_propval *val)
2821426dffaSAndreas Kemnade {
2831426dffaSAndreas Kemnade 	u16 res;
2841426dffaSAndreas Kemnade 	int ret;
2851426dffaSAndreas Kemnade 
2861426dffaSAndreas Kemnade 	ret = rn5t618_battery_read_doublereg(info, RN5T618_TT_FULL_H, &res);
2871426dffaSAndreas Kemnade 	if (ret)
2881426dffaSAndreas Kemnade 		return ret;
2891426dffaSAndreas Kemnade 
2901426dffaSAndreas Kemnade 	if (res == 65535)
2911426dffaSAndreas Kemnade 		return -ENODATA;
2921426dffaSAndreas Kemnade 
2931426dffaSAndreas Kemnade 	val->intval = res * 60;
2941426dffaSAndreas Kemnade 
2951426dffaSAndreas Kemnade 	return 0;
2961426dffaSAndreas Kemnade }
2971426dffaSAndreas Kemnade 
rn5t618_battery_set_current_limit(struct rn5t618_power_info * info,const union power_supply_propval * val)29820a3c8b5SAndreas Kemnade static int rn5t618_battery_set_current_limit(struct rn5t618_power_info *info,
29920a3c8b5SAndreas Kemnade 				const union power_supply_propval *val)
30020a3c8b5SAndreas Kemnade {
30120a3c8b5SAndreas Kemnade 	if (val->intval < CHG_MIN_CUR)
30220a3c8b5SAndreas Kemnade 		return -EINVAL;
30320a3c8b5SAndreas Kemnade 
30420a3c8b5SAndreas Kemnade 	if (val->intval >= CHG_MAX_CUR)
30520a3c8b5SAndreas Kemnade 		return -EINVAL;
30620a3c8b5SAndreas Kemnade 
30720a3c8b5SAndreas Kemnade 	return regmap_update_bits(info->rn5t618->regmap,
30820a3c8b5SAndreas Kemnade 				  RN5T618_CHGISET,
30920a3c8b5SAndreas Kemnade 				  0x1F, TO_CUR_REG(val->intval));
31020a3c8b5SAndreas Kemnade }
31120a3c8b5SAndreas Kemnade 
rn5t618_battery_get_current_limit(struct rn5t618_power_info * info,union power_supply_propval * val)31220a3c8b5SAndreas Kemnade static int rn5t618_battery_get_current_limit(struct rn5t618_power_info *info,
31320a3c8b5SAndreas Kemnade 					     union power_supply_propval *val)
31420a3c8b5SAndreas Kemnade {
31520a3c8b5SAndreas Kemnade 	unsigned int regval;
31620a3c8b5SAndreas Kemnade 	int ret;
31720a3c8b5SAndreas Kemnade 
31820a3c8b5SAndreas Kemnade 	ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGISET,
31920a3c8b5SAndreas Kemnade 			  &regval);
32020a3c8b5SAndreas Kemnade 	if (ret < 0)
32120a3c8b5SAndreas Kemnade 		return ret;
32220a3c8b5SAndreas Kemnade 
32320a3c8b5SAndreas Kemnade 	val->intval = FROM_CUR_REG(regval);
32420a3c8b5SAndreas Kemnade 
32520a3c8b5SAndreas Kemnade 	return 0;
32620a3c8b5SAndreas Kemnade }
32720a3c8b5SAndreas Kemnade 
rn5t618_battery_charge_full(struct rn5t618_power_info * info,union power_supply_propval * val)3281426dffaSAndreas Kemnade static int rn5t618_battery_charge_full(struct rn5t618_power_info *info,
3291426dffaSAndreas Kemnade 				       union power_supply_propval *val)
3301426dffaSAndreas Kemnade {
3311426dffaSAndreas Kemnade 	u16 res;
3321426dffaSAndreas Kemnade 	int ret;
3331426dffaSAndreas Kemnade 
3341426dffaSAndreas Kemnade 	ret = rn5t618_battery_read_doublereg(info, RN5T618_FA_CAP_H, &res);
3351426dffaSAndreas Kemnade 	if (ret)
3361426dffaSAndreas Kemnade 		return ret;
3371426dffaSAndreas Kemnade 
3381426dffaSAndreas Kemnade 	val->intval = res * 1000;
3391426dffaSAndreas Kemnade 
3401426dffaSAndreas Kemnade 	return 0;
3411426dffaSAndreas Kemnade }
3421426dffaSAndreas Kemnade 
rn5t618_battery_charge_now(struct rn5t618_power_info * info,union power_supply_propval * val)3431426dffaSAndreas Kemnade static int rn5t618_battery_charge_now(struct rn5t618_power_info *info,
3441426dffaSAndreas Kemnade 				      union power_supply_propval *val)
3451426dffaSAndreas Kemnade {
3461426dffaSAndreas Kemnade 	u16 res;
3471426dffaSAndreas Kemnade 	int ret;
3481426dffaSAndreas Kemnade 
3491426dffaSAndreas Kemnade 	ret = rn5t618_battery_read_doublereg(info, RN5T618_RE_CAP_H, &res);
3501426dffaSAndreas Kemnade 	if (ret)
3511426dffaSAndreas Kemnade 		return ret;
3521426dffaSAndreas Kemnade 
3531426dffaSAndreas Kemnade 	val->intval = res * 1000;
3541426dffaSAndreas Kemnade 
3551426dffaSAndreas Kemnade 	return 0;
3561426dffaSAndreas Kemnade }
3571426dffaSAndreas Kemnade 
rn5t618_battery_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)3581426dffaSAndreas Kemnade static int rn5t618_battery_get_property(struct power_supply *psy,
3591426dffaSAndreas Kemnade 					enum power_supply_property psp,
3601426dffaSAndreas Kemnade 					union power_supply_propval *val)
3611426dffaSAndreas Kemnade {
3621426dffaSAndreas Kemnade 	int ret = 0;
3631426dffaSAndreas Kemnade 	struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
3641426dffaSAndreas Kemnade 
3651426dffaSAndreas Kemnade 	switch (psp) {
3661426dffaSAndreas Kemnade 	case POWER_SUPPLY_PROP_STATUS:
3671426dffaSAndreas Kemnade 		ret = rn5t618_battery_status(info, val);
3681426dffaSAndreas Kemnade 		break;
3691426dffaSAndreas Kemnade 	case POWER_SUPPLY_PROP_PRESENT:
3701426dffaSAndreas Kemnade 		ret = rn5t618_battery_present(info, val);
3711426dffaSAndreas Kemnade 		break;
3721426dffaSAndreas Kemnade 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
3731426dffaSAndreas Kemnade 		ret = rn5t618_battery_voltage_now(info, val);
3741426dffaSAndreas Kemnade 		break;
3751426dffaSAndreas Kemnade 	case POWER_SUPPLY_PROP_CURRENT_NOW:
3761426dffaSAndreas Kemnade 		ret = rn5t618_battery_current_now(info, val);
3771426dffaSAndreas Kemnade 		break;
3781426dffaSAndreas Kemnade 	case POWER_SUPPLY_PROP_CAPACITY:
3791426dffaSAndreas Kemnade 		ret = rn5t618_battery_capacity(info, val);
3801426dffaSAndreas Kemnade 		break;
3811426dffaSAndreas Kemnade 	case POWER_SUPPLY_PROP_TEMP:
3821426dffaSAndreas Kemnade 		ret = rn5t618_battery_temp(info, val);
3831426dffaSAndreas Kemnade 		break;
3841426dffaSAndreas Kemnade 	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
3851426dffaSAndreas Kemnade 		ret = rn5t618_battery_tte(info, val);
3861426dffaSAndreas Kemnade 		break;
3871426dffaSAndreas Kemnade 	case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
3881426dffaSAndreas Kemnade 		ret = rn5t618_battery_ttf(info, val);
3891426dffaSAndreas Kemnade 		break;
3901426dffaSAndreas Kemnade 	case POWER_SUPPLY_PROP_TECHNOLOGY:
3911426dffaSAndreas Kemnade 		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
3921426dffaSAndreas Kemnade 		break;
39320a3c8b5SAndreas Kemnade 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
39420a3c8b5SAndreas Kemnade 		ret = rn5t618_battery_get_current_limit(info, val);
39520a3c8b5SAndreas Kemnade 		break;
3961426dffaSAndreas Kemnade 	case POWER_SUPPLY_PROP_CHARGE_FULL:
3971426dffaSAndreas Kemnade 		ret = rn5t618_battery_charge_full(info, val);
3981426dffaSAndreas Kemnade 		break;
3991426dffaSAndreas Kemnade 	case POWER_SUPPLY_PROP_CHARGE_NOW:
4001426dffaSAndreas Kemnade 		ret = rn5t618_battery_charge_now(info, val);
4011426dffaSAndreas Kemnade 		break;
4021426dffaSAndreas Kemnade 	default:
4031426dffaSAndreas Kemnade 		return -EINVAL;
4041426dffaSAndreas Kemnade 	}
4051426dffaSAndreas Kemnade 
4061426dffaSAndreas Kemnade 	return ret;
4071426dffaSAndreas Kemnade }
4081426dffaSAndreas Kemnade 
rn5t618_battery_set_property(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)40920a3c8b5SAndreas Kemnade static int rn5t618_battery_set_property(struct power_supply *psy,
41020a3c8b5SAndreas Kemnade 					enum power_supply_property psp,
41120a3c8b5SAndreas Kemnade 					const union power_supply_propval *val)
41220a3c8b5SAndreas Kemnade {
41320a3c8b5SAndreas Kemnade 	struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
41420a3c8b5SAndreas Kemnade 
41520a3c8b5SAndreas Kemnade 	switch (psp) {
41620a3c8b5SAndreas Kemnade 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
41720a3c8b5SAndreas Kemnade 		return rn5t618_battery_set_current_limit(info, val);
41820a3c8b5SAndreas Kemnade 	default:
41920a3c8b5SAndreas Kemnade 		return -EINVAL;
42020a3c8b5SAndreas Kemnade 	}
42120a3c8b5SAndreas Kemnade }
42220a3c8b5SAndreas Kemnade 
rn5t618_battery_property_is_writeable(struct power_supply * psy,enum power_supply_property psp)42320a3c8b5SAndreas Kemnade static int rn5t618_battery_property_is_writeable(struct power_supply *psy,
42420a3c8b5SAndreas Kemnade 						enum power_supply_property psp)
42520a3c8b5SAndreas Kemnade {
42620a3c8b5SAndreas Kemnade 	switch (psp) {
42720a3c8b5SAndreas Kemnade 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
42820a3c8b5SAndreas Kemnade 		return true;
42920a3c8b5SAndreas Kemnade 	default:
43020a3c8b5SAndreas Kemnade 		return false;
43120a3c8b5SAndreas Kemnade 	}
43220a3c8b5SAndreas Kemnade }
43320a3c8b5SAndreas Kemnade 
rn5t618_adp_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)4341426dffaSAndreas Kemnade static int rn5t618_adp_get_property(struct power_supply *psy,
4351426dffaSAndreas Kemnade 				    enum power_supply_property psp,
4361426dffaSAndreas Kemnade 				    union power_supply_propval *val)
4371426dffaSAndreas Kemnade {
4381426dffaSAndreas Kemnade 	struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
4391426dffaSAndreas Kemnade 	unsigned int chgstate;
44020a3c8b5SAndreas Kemnade 	unsigned int regval;
4411426dffaSAndreas Kemnade 	bool online;
4421426dffaSAndreas Kemnade 	int ret;
4431426dffaSAndreas Kemnade 
4441426dffaSAndreas Kemnade 	ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &chgstate);
4451426dffaSAndreas Kemnade 	if (ret)
4461426dffaSAndreas Kemnade 		return ret;
4471426dffaSAndreas Kemnade 
4481426dffaSAndreas Kemnade 	online = !!(chgstate & CHG_STATE_ADP_INPUT);
4491426dffaSAndreas Kemnade 
4501426dffaSAndreas Kemnade 	switch (psp) {
4511426dffaSAndreas Kemnade 	case POWER_SUPPLY_PROP_ONLINE:
4521426dffaSAndreas Kemnade 		val->intval = online;
4531426dffaSAndreas Kemnade 		break;
4541426dffaSAndreas Kemnade 	case POWER_SUPPLY_PROP_STATUS:
4551426dffaSAndreas Kemnade 		if (!online) {
4561426dffaSAndreas Kemnade 			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
4571426dffaSAndreas Kemnade 			break;
4581426dffaSAndreas Kemnade 		}
4591426dffaSAndreas Kemnade 		val->intval = rn5t618_decode_status(chgstate);
4601426dffaSAndreas Kemnade 		if (val->intval != POWER_SUPPLY_STATUS_CHARGING)
4611426dffaSAndreas Kemnade 			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
4621426dffaSAndreas Kemnade 
4631426dffaSAndreas Kemnade 		break;
46420a3c8b5SAndreas Kemnade 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
46520a3c8b5SAndreas Kemnade 		ret = regmap_read(info->rn5t618->regmap,
46620a3c8b5SAndreas Kemnade 				  RN5T618_REGISET1, &regval);
46720a3c8b5SAndreas Kemnade 		if (ret < 0)
46820a3c8b5SAndreas Kemnade 			return ret;
46920a3c8b5SAndreas Kemnade 
47020a3c8b5SAndreas Kemnade 		val->intval = FROM_CUR_REG(regval);
47120a3c8b5SAndreas Kemnade 		break;
472*2f5caa26SAndreas Kemnade 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
473*2f5caa26SAndreas Kemnade 		if (!info->channel_vadp)
474*2f5caa26SAndreas Kemnade 			return -ENODATA;
475*2f5caa26SAndreas Kemnade 
476*2f5caa26SAndreas Kemnade 		ret = iio_read_channel_processed_scale(info->channel_vadp, &val->intval, 1000);
477*2f5caa26SAndreas Kemnade 		if (ret < 0)
478*2f5caa26SAndreas Kemnade 			return ret;
479*2f5caa26SAndreas Kemnade 
480*2f5caa26SAndreas Kemnade 		break;
4811426dffaSAndreas Kemnade 	default:
4821426dffaSAndreas Kemnade 		return -EINVAL;
4831426dffaSAndreas Kemnade 	}
4841426dffaSAndreas Kemnade 
4851426dffaSAndreas Kemnade 	return 0;
4861426dffaSAndreas Kemnade }
4871426dffaSAndreas Kemnade 
rn5t618_adp_set_property(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)48820a3c8b5SAndreas Kemnade static int rn5t618_adp_set_property(struct power_supply *psy,
48920a3c8b5SAndreas Kemnade 				    enum power_supply_property psp,
49020a3c8b5SAndreas Kemnade 				    const union power_supply_propval *val)
49120a3c8b5SAndreas Kemnade {
49220a3c8b5SAndreas Kemnade 	struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
49320a3c8b5SAndreas Kemnade 	int ret;
49420a3c8b5SAndreas Kemnade 
49520a3c8b5SAndreas Kemnade 	switch (psp) {
49620a3c8b5SAndreas Kemnade 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
49720a3c8b5SAndreas Kemnade 		if (val->intval > ADP_MAX_CUR)
49820a3c8b5SAndreas Kemnade 			return -EINVAL;
49920a3c8b5SAndreas Kemnade 
50020a3c8b5SAndreas Kemnade 		if (val->intval < CHG_MIN_CUR)
50120a3c8b5SAndreas Kemnade 			return -EINVAL;
50220a3c8b5SAndreas Kemnade 
50320a3c8b5SAndreas Kemnade 		ret = regmap_write(info->rn5t618->regmap, RN5T618_REGISET1,
50420a3c8b5SAndreas Kemnade 				   TO_CUR_REG(val->intval));
50520a3c8b5SAndreas Kemnade 		if (ret < 0)
50620a3c8b5SAndreas Kemnade 			return ret;
50720a3c8b5SAndreas Kemnade 
50820a3c8b5SAndreas Kemnade 		break;
50920a3c8b5SAndreas Kemnade 	default:
51020a3c8b5SAndreas Kemnade 		return -EINVAL;
51120a3c8b5SAndreas Kemnade 	}
51220a3c8b5SAndreas Kemnade 
51320a3c8b5SAndreas Kemnade 	return 0;
51420a3c8b5SAndreas Kemnade }
51520a3c8b5SAndreas Kemnade 
rn5t618_adp_property_is_writeable(struct power_supply * psy,enum power_supply_property psp)51620a3c8b5SAndreas Kemnade static int rn5t618_adp_property_is_writeable(struct power_supply *psy,
51720a3c8b5SAndreas Kemnade 					     enum power_supply_property psp)
51820a3c8b5SAndreas Kemnade {
51920a3c8b5SAndreas Kemnade 	switch (psp) {
52020a3c8b5SAndreas Kemnade 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
52120a3c8b5SAndreas Kemnade 		return true;
52220a3c8b5SAndreas Kemnade 	default:
52320a3c8b5SAndreas Kemnade 		return false;
52420a3c8b5SAndreas Kemnade 	}
52520a3c8b5SAndreas Kemnade }
52620a3c8b5SAndreas Kemnade 
rc5t619_usb_get_type(struct rn5t618_power_info * info,union power_supply_propval * val)5277d763677SAndreas Kemnade static int rc5t619_usb_get_type(struct rn5t618_power_info *info,
5287d763677SAndreas Kemnade 				union power_supply_propval *val)
5297d763677SAndreas Kemnade {
5307d763677SAndreas Kemnade 	unsigned int regval;
5317d763677SAndreas Kemnade 	int ret;
5327d763677SAndreas Kemnade 
5337d763677SAndreas Kemnade 	ret = regmap_read(info->rn5t618->regmap, RN5T618_GCHGDET, &regval);
5347d763677SAndreas Kemnade 	if (ret < 0)
5357d763677SAndreas Kemnade 		return ret;
5367d763677SAndreas Kemnade 
5377d763677SAndreas Kemnade 	switch (regval & GCHGDET_TYPE_MASK) {
5387d763677SAndreas Kemnade 	case GCHGDET_TYPE_SDP:
5397d763677SAndreas Kemnade 		val->intval = POWER_SUPPLY_USB_TYPE_SDP;
5407d763677SAndreas Kemnade 		break;
5417d763677SAndreas Kemnade 	case GCHGDET_TYPE_CDP:
5427d763677SAndreas Kemnade 		val->intval = POWER_SUPPLY_USB_TYPE_CDP;
5437d763677SAndreas Kemnade 		break;
5447d763677SAndreas Kemnade 	case GCHGDET_TYPE_DCP:
5457d763677SAndreas Kemnade 		val->intval = POWER_SUPPLY_USB_TYPE_DCP;
5467d763677SAndreas Kemnade 		break;
5477d763677SAndreas Kemnade 	default:
5487d763677SAndreas Kemnade 		val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN;
5497d763677SAndreas Kemnade 	}
5507d763677SAndreas Kemnade 
5517d763677SAndreas Kemnade 	return 0;
5527d763677SAndreas Kemnade }
5537d763677SAndreas Kemnade 
rn5t618_usb_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)5541426dffaSAndreas Kemnade static int rn5t618_usb_get_property(struct power_supply *psy,
5551426dffaSAndreas Kemnade 				    enum power_supply_property psp,
5561426dffaSAndreas Kemnade 				    union power_supply_propval *val)
5571426dffaSAndreas Kemnade {
5581426dffaSAndreas Kemnade 	struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
5591426dffaSAndreas Kemnade 	unsigned int chgstate;
56020a3c8b5SAndreas Kemnade 	unsigned int regval;
5611426dffaSAndreas Kemnade 	bool online;
5621426dffaSAndreas Kemnade 	int ret;
5631426dffaSAndreas Kemnade 
5641426dffaSAndreas Kemnade 	ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &chgstate);
5651426dffaSAndreas Kemnade 	if (ret)
5661426dffaSAndreas Kemnade 		return ret;
5671426dffaSAndreas Kemnade 
5681426dffaSAndreas Kemnade 	online = !!(chgstate & CHG_STATE_USB_INPUT);
5691426dffaSAndreas Kemnade 
5701426dffaSAndreas Kemnade 	switch (psp) {
5711426dffaSAndreas Kemnade 	case POWER_SUPPLY_PROP_ONLINE:
5721426dffaSAndreas Kemnade 		val->intval = online;
5731426dffaSAndreas Kemnade 		break;
5741426dffaSAndreas Kemnade 	case POWER_SUPPLY_PROP_STATUS:
5751426dffaSAndreas Kemnade 		if (!online) {
5761426dffaSAndreas Kemnade 			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
5771426dffaSAndreas Kemnade 			break;
5781426dffaSAndreas Kemnade 		}
5791426dffaSAndreas Kemnade 		val->intval = rn5t618_decode_status(chgstate);
5801426dffaSAndreas Kemnade 		if (val->intval != POWER_SUPPLY_STATUS_CHARGING)
5811426dffaSAndreas Kemnade 			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
5821426dffaSAndreas Kemnade 
5831426dffaSAndreas Kemnade 		break;
5847d763677SAndreas Kemnade 	case POWER_SUPPLY_PROP_USB_TYPE:
5857d763677SAndreas Kemnade 		if (!online || (info->rn5t618->variant != RC5T619))
5867d763677SAndreas Kemnade 			return -ENODATA;
5877d763677SAndreas Kemnade 
5887d763677SAndreas Kemnade 		return rc5t619_usb_get_type(info, val);
58920a3c8b5SAndreas Kemnade 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
59020a3c8b5SAndreas Kemnade 		ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGCTL1,
59120a3c8b5SAndreas Kemnade 				  &regval);
59220a3c8b5SAndreas Kemnade 		if (ret < 0)
59320a3c8b5SAndreas Kemnade 			return ret;
59420a3c8b5SAndreas Kemnade 
59520a3c8b5SAndreas Kemnade 		val->intval = 0;
59620a3c8b5SAndreas Kemnade 		if (regval & 2) {
59720a3c8b5SAndreas Kemnade 			ret = regmap_read(info->rn5t618->regmap,
59820a3c8b5SAndreas Kemnade 					  RN5T618_REGISET2,
59920a3c8b5SAndreas Kemnade 					  &regval);
60020a3c8b5SAndreas Kemnade 			if (ret < 0)
60120a3c8b5SAndreas Kemnade 				return ret;
60220a3c8b5SAndreas Kemnade 
60320a3c8b5SAndreas Kemnade 			val->intval = FROM_CUR_REG(regval);
60420a3c8b5SAndreas Kemnade 		}
60520a3c8b5SAndreas Kemnade 		break;
606*2f5caa26SAndreas Kemnade 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
607*2f5caa26SAndreas Kemnade 		if (!info->channel_vusb)
608*2f5caa26SAndreas Kemnade 			return -ENODATA;
609*2f5caa26SAndreas Kemnade 
610*2f5caa26SAndreas Kemnade 		ret = iio_read_channel_processed_scale(info->channel_vusb, &val->intval, 1000);
611*2f5caa26SAndreas Kemnade 		if (ret < 0)
612*2f5caa26SAndreas Kemnade 			return ret;
613*2f5caa26SAndreas Kemnade 
614*2f5caa26SAndreas Kemnade 		break;
6151426dffaSAndreas Kemnade 	default:
6161426dffaSAndreas Kemnade 		return -EINVAL;
6171426dffaSAndreas Kemnade 	}
6181426dffaSAndreas Kemnade 
6191426dffaSAndreas Kemnade 	return 0;
6201426dffaSAndreas Kemnade }
6211426dffaSAndreas Kemnade 
rn5t618_usb_set_property(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)62220a3c8b5SAndreas Kemnade static int rn5t618_usb_set_property(struct power_supply *psy,
62320a3c8b5SAndreas Kemnade 				    enum power_supply_property psp,
62420a3c8b5SAndreas Kemnade 				    const union power_supply_propval *val)
62520a3c8b5SAndreas Kemnade {
62620a3c8b5SAndreas Kemnade 	struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
62720a3c8b5SAndreas Kemnade 	int ret;
62820a3c8b5SAndreas Kemnade 
62920a3c8b5SAndreas Kemnade 	switch (psp) {
63020a3c8b5SAndreas Kemnade 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
63120a3c8b5SAndreas Kemnade 		if (val->intval > USB_MAX_CUR)
63220a3c8b5SAndreas Kemnade 			return -EINVAL;
63320a3c8b5SAndreas Kemnade 
63420a3c8b5SAndreas Kemnade 		if (val->intval < CHG_MIN_CUR)
63520a3c8b5SAndreas Kemnade 			return -EINVAL;
63620a3c8b5SAndreas Kemnade 
63720a3c8b5SAndreas Kemnade 		ret = regmap_write(info->rn5t618->regmap, RN5T618_REGISET2,
63820a3c8b5SAndreas Kemnade 				   0xE0 | TO_CUR_REG(val->intval));
63920a3c8b5SAndreas Kemnade 		if (ret < 0)
64020a3c8b5SAndreas Kemnade 			return ret;
64120a3c8b5SAndreas Kemnade 
64220a3c8b5SAndreas Kemnade 		break;
64320a3c8b5SAndreas Kemnade 	default:
64420a3c8b5SAndreas Kemnade 		return -EINVAL;
64520a3c8b5SAndreas Kemnade 	}
64620a3c8b5SAndreas Kemnade 
64720a3c8b5SAndreas Kemnade 	return 0;
64820a3c8b5SAndreas Kemnade }
64920a3c8b5SAndreas Kemnade 
rn5t618_usb_property_is_writeable(struct power_supply * psy,enum power_supply_property psp)65020a3c8b5SAndreas Kemnade static int rn5t618_usb_property_is_writeable(struct power_supply *psy,
65120a3c8b5SAndreas Kemnade 					     enum power_supply_property psp)
65220a3c8b5SAndreas Kemnade {
65320a3c8b5SAndreas Kemnade 	switch (psp) {
65420a3c8b5SAndreas Kemnade 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
65520a3c8b5SAndreas Kemnade 		return true;
65620a3c8b5SAndreas Kemnade 	default:
65720a3c8b5SAndreas Kemnade 		return false;
65820a3c8b5SAndreas Kemnade 	}
65920a3c8b5SAndreas Kemnade }
66020a3c8b5SAndreas Kemnade 
6611426dffaSAndreas Kemnade static const struct power_supply_desc rn5t618_battery_desc = {
6621426dffaSAndreas Kemnade 	.name                   = "rn5t618-battery",
6631426dffaSAndreas Kemnade 	.type                   = POWER_SUPPLY_TYPE_BATTERY,
6641426dffaSAndreas Kemnade 	.properties             = rn5t618_battery_props,
6651426dffaSAndreas Kemnade 	.num_properties         = ARRAY_SIZE(rn5t618_battery_props),
6661426dffaSAndreas Kemnade 	.get_property           = rn5t618_battery_get_property,
66720a3c8b5SAndreas Kemnade 	.set_property           = rn5t618_battery_set_property,
66820a3c8b5SAndreas Kemnade 	.property_is_writeable  = rn5t618_battery_property_is_writeable,
6691426dffaSAndreas Kemnade };
6701426dffaSAndreas Kemnade 
6711426dffaSAndreas Kemnade static const struct power_supply_desc rn5t618_adp_desc = {
6721426dffaSAndreas Kemnade 	.name                   = "rn5t618-adp",
6731426dffaSAndreas Kemnade 	.type                   = POWER_SUPPLY_TYPE_MAINS,
6741426dffaSAndreas Kemnade 	.properties             = rn5t618_adp_props,
6751426dffaSAndreas Kemnade 	.num_properties         = ARRAY_SIZE(rn5t618_adp_props),
6761426dffaSAndreas Kemnade 	.get_property           = rn5t618_adp_get_property,
67720a3c8b5SAndreas Kemnade 	.set_property           = rn5t618_adp_set_property,
67820a3c8b5SAndreas Kemnade 	.property_is_writeable  = rn5t618_adp_property_is_writeable,
6791426dffaSAndreas Kemnade };
6801426dffaSAndreas Kemnade 
6811426dffaSAndreas Kemnade static const struct power_supply_desc rn5t618_usb_desc = {
6821426dffaSAndreas Kemnade 	.name                   = "rn5t618-usb",
6831426dffaSAndreas Kemnade 	.type                   = POWER_SUPPLY_TYPE_USB,
6847d763677SAndreas Kemnade 	.usb_types		= rn5t618_usb_types,
6857d763677SAndreas Kemnade 	.num_usb_types		= ARRAY_SIZE(rn5t618_usb_types),
6861426dffaSAndreas Kemnade 	.properties             = rn5t618_usb_props,
6871426dffaSAndreas Kemnade 	.num_properties         = ARRAY_SIZE(rn5t618_usb_props),
6881426dffaSAndreas Kemnade 	.get_property           = rn5t618_usb_get_property,
68920a3c8b5SAndreas Kemnade 	.set_property           = rn5t618_usb_set_property,
69020a3c8b5SAndreas Kemnade 	.property_is_writeable  = rn5t618_usb_property_is_writeable,
6911426dffaSAndreas Kemnade };
6921426dffaSAndreas Kemnade 
rn5t618_charger_irq(int irq,void * data)6931426dffaSAndreas Kemnade static irqreturn_t rn5t618_charger_irq(int irq, void *data)
6941426dffaSAndreas Kemnade {
6951426dffaSAndreas Kemnade 	struct device *dev = data;
6961426dffaSAndreas Kemnade 	struct rn5t618_power_info *info = dev_get_drvdata(dev);
6971426dffaSAndreas Kemnade 
6981426dffaSAndreas Kemnade 	unsigned int ctrl, stat1, stat2, err;
6991426dffaSAndreas Kemnade 
7001426dffaSAndreas Kemnade 	regmap_read(info->rn5t618->regmap, RN5T618_CHGERR_IRR, &err);
7011426dffaSAndreas Kemnade 	regmap_read(info->rn5t618->regmap, RN5T618_CHGCTRL_IRR, &ctrl);
7021426dffaSAndreas Kemnade 	regmap_read(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR1, &stat1);
7031426dffaSAndreas Kemnade 	regmap_read(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR2, &stat2);
7041426dffaSAndreas Kemnade 
7051426dffaSAndreas Kemnade 	regmap_write(info->rn5t618->regmap, RN5T618_CHGERR_IRR, 0);
7061426dffaSAndreas Kemnade 	regmap_write(info->rn5t618->regmap, RN5T618_CHGCTRL_IRR, 0);
7071426dffaSAndreas Kemnade 	regmap_write(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR1, 0);
7081426dffaSAndreas Kemnade 	regmap_write(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR2, 0);
7091426dffaSAndreas Kemnade 
7101426dffaSAndreas Kemnade 	dev_dbg(dev, "chgerr: %x chgctrl: %x chgstat: %x chgstat2: %x\n",
7111426dffaSAndreas Kemnade 		err, ctrl, stat1, stat2);
7121426dffaSAndreas Kemnade 
7131426dffaSAndreas Kemnade 	power_supply_changed(info->usb);
7141426dffaSAndreas Kemnade 	power_supply_changed(info->adp);
7151426dffaSAndreas Kemnade 	power_supply_changed(info->battery);
7161426dffaSAndreas Kemnade 
7171426dffaSAndreas Kemnade 	return IRQ_HANDLED;
7181426dffaSAndreas Kemnade }
7191426dffaSAndreas Kemnade 
rn5t618_power_probe(struct platform_device * pdev)7201426dffaSAndreas Kemnade static int rn5t618_power_probe(struct platform_device *pdev)
7211426dffaSAndreas Kemnade {
7221426dffaSAndreas Kemnade 	int ret = 0;
7231426dffaSAndreas Kemnade 	unsigned int v;
7241426dffaSAndreas Kemnade 	struct power_supply_config psy_cfg = {};
7251426dffaSAndreas Kemnade 	struct rn5t618_power_info *info;
7261426dffaSAndreas Kemnade 
7271426dffaSAndreas Kemnade 	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
7281426dffaSAndreas Kemnade 	if (!info)
7291426dffaSAndreas Kemnade 		return -ENOMEM;
7301426dffaSAndreas Kemnade 
7311426dffaSAndreas Kemnade 	info->pdev = pdev;
7321426dffaSAndreas Kemnade 	info->rn5t618 = dev_get_drvdata(pdev->dev.parent);
7331426dffaSAndreas Kemnade 	info->irq = -1;
7341426dffaSAndreas Kemnade 
7351426dffaSAndreas Kemnade 	platform_set_drvdata(pdev, info);
7361426dffaSAndreas Kemnade 
737*2f5caa26SAndreas Kemnade 	info->channel_vusb = devm_iio_channel_get(&pdev->dev, "vusb");
738*2f5caa26SAndreas Kemnade 	if (IS_ERR(info->channel_vusb)) {
739*2f5caa26SAndreas Kemnade 		if (PTR_ERR(info->channel_vusb) == -ENODEV)
740*2f5caa26SAndreas Kemnade 			return -EPROBE_DEFER;
741*2f5caa26SAndreas Kemnade 		return PTR_ERR(info->channel_vusb);
742*2f5caa26SAndreas Kemnade 	}
743*2f5caa26SAndreas Kemnade 
744*2f5caa26SAndreas Kemnade 	info->channel_vadp = devm_iio_channel_get(&pdev->dev, "vadp");
745*2f5caa26SAndreas Kemnade 	if (IS_ERR(info->channel_vadp)) {
746*2f5caa26SAndreas Kemnade 		if (PTR_ERR(info->channel_vadp) == -ENODEV)
747*2f5caa26SAndreas Kemnade 			return -EPROBE_DEFER;
748*2f5caa26SAndreas Kemnade 		return PTR_ERR(info->channel_vadp);
749*2f5caa26SAndreas Kemnade 	}
750*2f5caa26SAndreas Kemnade 
7511426dffaSAndreas Kemnade 	ret = regmap_read(info->rn5t618->regmap, RN5T618_CONTROL, &v);
7521426dffaSAndreas Kemnade 	if (ret)
7531426dffaSAndreas Kemnade 		return ret;
7541426dffaSAndreas Kemnade 
7551426dffaSAndreas Kemnade 	if (!(v & FG_ENABLE)) {
7561426dffaSAndreas Kemnade 		/* E.g. the vendor kernels of various Kobo and Tolino Ebook
7571426dffaSAndreas Kemnade 		 * readers disable the fuel gauge on shutdown. If a kernel
7581426dffaSAndreas Kemnade 		 * without fuel gauge support is booted after that, the fuel
7591426dffaSAndreas Kemnade 		 * gauge will get decalibrated.
7601426dffaSAndreas Kemnade 		 */
7611426dffaSAndreas Kemnade 		dev_info(&pdev->dev, "Fuel gauge not enabled, enabling now\n");
762304bff2fSColin Ian King 		dev_info(&pdev->dev, "Expect imprecise results\n");
7631426dffaSAndreas Kemnade 		regmap_update_bits(info->rn5t618->regmap, RN5T618_CONTROL,
7641426dffaSAndreas Kemnade 				   FG_ENABLE, FG_ENABLE);
7651426dffaSAndreas Kemnade 	}
7661426dffaSAndreas Kemnade 
7671426dffaSAndreas Kemnade 	psy_cfg.drv_data = info;
7681426dffaSAndreas Kemnade 	info->battery = devm_power_supply_register(&pdev->dev,
7691426dffaSAndreas Kemnade 						   &rn5t618_battery_desc,
7701426dffaSAndreas Kemnade 						   &psy_cfg);
7711426dffaSAndreas Kemnade 	if (IS_ERR(info->battery)) {
7721426dffaSAndreas Kemnade 		ret = PTR_ERR(info->battery);
7731426dffaSAndreas Kemnade 		dev_err(&pdev->dev, "failed to register battery: %d\n", ret);
7741426dffaSAndreas Kemnade 		return ret;
7751426dffaSAndreas Kemnade 	}
7761426dffaSAndreas Kemnade 
7771426dffaSAndreas Kemnade 	info->adp = devm_power_supply_register(&pdev->dev,
7781426dffaSAndreas Kemnade 					       &rn5t618_adp_desc,
7791426dffaSAndreas Kemnade 					       &psy_cfg);
7801426dffaSAndreas Kemnade 	if (IS_ERR(info->adp)) {
7811426dffaSAndreas Kemnade 		ret = PTR_ERR(info->adp);
7821426dffaSAndreas Kemnade 		dev_err(&pdev->dev, "failed to register adp: %d\n", ret);
7831426dffaSAndreas Kemnade 		return ret;
7841426dffaSAndreas Kemnade 	}
7851426dffaSAndreas Kemnade 
7861426dffaSAndreas Kemnade 	info->usb = devm_power_supply_register(&pdev->dev,
7871426dffaSAndreas Kemnade 					       &rn5t618_usb_desc,
7881426dffaSAndreas Kemnade 					       &psy_cfg);
7891426dffaSAndreas Kemnade 	if (IS_ERR(info->usb)) {
7901426dffaSAndreas Kemnade 		ret = PTR_ERR(info->usb);
7911426dffaSAndreas Kemnade 		dev_err(&pdev->dev, "failed to register usb: %d\n", ret);
7921426dffaSAndreas Kemnade 		return ret;
7931426dffaSAndreas Kemnade 	}
7941426dffaSAndreas Kemnade 
7951426dffaSAndreas Kemnade 	if (info->rn5t618->irq_data)
7961426dffaSAndreas Kemnade 		info->irq = regmap_irq_get_virq(info->rn5t618->irq_data,
7971426dffaSAndreas Kemnade 						RN5T618_IRQ_CHG);
7981426dffaSAndreas Kemnade 
7991426dffaSAndreas Kemnade 	if (info->irq < 0)
8001426dffaSAndreas Kemnade 		info->irq = -1;
8011426dffaSAndreas Kemnade 	else {
8021426dffaSAndreas Kemnade 		ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
8031426dffaSAndreas Kemnade 						rn5t618_charger_irq,
8041426dffaSAndreas Kemnade 						IRQF_ONESHOT,
8051426dffaSAndreas Kemnade 						"rn5t618_power",
8061426dffaSAndreas Kemnade 						&pdev->dev);
8071426dffaSAndreas Kemnade 
8081426dffaSAndreas Kemnade 		if (ret < 0) {
8091426dffaSAndreas Kemnade 			dev_err(&pdev->dev, "request IRQ:%d fail\n",
8101426dffaSAndreas Kemnade 				info->irq);
8111426dffaSAndreas Kemnade 			info->irq = -1;
8121426dffaSAndreas Kemnade 		}
8131426dffaSAndreas Kemnade 	}
8141426dffaSAndreas Kemnade 
8151426dffaSAndreas Kemnade 	return 0;
8161426dffaSAndreas Kemnade }
8171426dffaSAndreas Kemnade 
8181426dffaSAndreas Kemnade static struct platform_driver rn5t618_power_driver = {
8191426dffaSAndreas Kemnade 	.driver = {
8201426dffaSAndreas Kemnade 		.name   = "rn5t618-power",
8211426dffaSAndreas Kemnade 	},
8221426dffaSAndreas Kemnade 	.probe = rn5t618_power_probe,
8231426dffaSAndreas Kemnade };
8241426dffaSAndreas Kemnade 
8251426dffaSAndreas Kemnade module_platform_driver(rn5t618_power_driver);
8261426dffaSAndreas Kemnade MODULE_ALIAS("platform:rn5t618-power");
8271426dffaSAndreas Kemnade MODULE_DESCRIPTION("Power supply driver for RICOH RN5T618");
8281426dffaSAndreas Kemnade MODULE_LICENSE("GPL");
829