18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Power supply driver for the RICOH RN5T618 power management chip family
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2020 Andreas Kemnade
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/device.h>
108c2ecf20Sopenharmony_ci#include <linux/bitops.h>
118c2ecf20Sopenharmony_ci#include <linux/errno.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/mfd/rn5t618.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/power_supply.h>
188c2ecf20Sopenharmony_ci#include <linux/regmap.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define CHG_STATE_ADP_INPUT 0x40
228c2ecf20Sopenharmony_ci#define CHG_STATE_USB_INPUT 0x80
238c2ecf20Sopenharmony_ci#define CHG_STATE_MASK	0x1f
248c2ecf20Sopenharmony_ci#define CHG_STATE_CHG_OFF	0
258c2ecf20Sopenharmony_ci#define CHG_STATE_CHG_READY_VADP	1
268c2ecf20Sopenharmony_ci#define CHG_STATE_CHG_TRICKLE	2
278c2ecf20Sopenharmony_ci#define CHG_STATE_CHG_RAPID	3
288c2ecf20Sopenharmony_ci#define CHG_STATE_CHG_COMPLETE	4
298c2ecf20Sopenharmony_ci#define CHG_STATE_SUSPEND	5
308c2ecf20Sopenharmony_ci#define CHG_STATE_VCHG_OVER_VOL	6
318c2ecf20Sopenharmony_ci#define CHG_STATE_BAT_ERROR	7
328c2ecf20Sopenharmony_ci#define CHG_STATE_NO_BAT	8
338c2ecf20Sopenharmony_ci#define CHG_STATE_BAT_OVER_VOL	9
348c2ecf20Sopenharmony_ci#define CHG_STATE_BAT_TEMP_ERR	10
358c2ecf20Sopenharmony_ci#define CHG_STATE_DIE_ERR	11
368c2ecf20Sopenharmony_ci#define CHG_STATE_DIE_SHUTDOWN	12
378c2ecf20Sopenharmony_ci#define CHG_STATE_NO_BAT2	13
388c2ecf20Sopenharmony_ci#define CHG_STATE_CHG_READY_VUSB	14
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define FG_ENABLE 1
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistruct rn5t618_power_info {
438c2ecf20Sopenharmony_ci	struct rn5t618 *rn5t618;
448c2ecf20Sopenharmony_ci	struct platform_device *pdev;
458c2ecf20Sopenharmony_ci	struct power_supply *battery;
468c2ecf20Sopenharmony_ci	struct power_supply *usb;
478c2ecf20Sopenharmony_ci	struct power_supply *adp;
488c2ecf20Sopenharmony_ci	int irq;
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic enum power_supply_property rn5t618_usb_props[] = {
528c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
538c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic enum power_supply_property rn5t618_adp_props[] = {
578c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
588c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic enum power_supply_property rn5t618_battery_props[] = {
638c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
648c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
658c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
668c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_NOW,
678c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CAPACITY,
688c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_TEMP,
698c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
708c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
718c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_TECHNOLOGY,
728c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_FULL,
738c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_NOW,
748c2ecf20Sopenharmony_ci};
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int rn5t618_battery_read_doublereg(struct rn5t618_power_info *info,
778c2ecf20Sopenharmony_ci					  u8 reg, u16 *result)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	int ret, i;
808c2ecf20Sopenharmony_ci	u8 data[2];
818c2ecf20Sopenharmony_ci	u16 old, new;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	old = 0;
848c2ecf20Sopenharmony_ci	/* Prevent races when registers are changing. */
858c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
868c2ecf20Sopenharmony_ci		ret = regmap_bulk_read(info->rn5t618->regmap,
878c2ecf20Sopenharmony_ci				       reg, data, sizeof(data));
888c2ecf20Sopenharmony_ci		if (ret)
898c2ecf20Sopenharmony_ci			return ret;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci		new = data[0] << 8;
928c2ecf20Sopenharmony_ci		new |= data[1];
938c2ecf20Sopenharmony_ci		if (new == old)
948c2ecf20Sopenharmony_ci			break;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci		old = new;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	*result = new;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return 0;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int rn5t618_decode_status(unsigned int status)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	switch (status & CHG_STATE_MASK) {
1078c2ecf20Sopenharmony_ci	case CHG_STATE_CHG_OFF:
1088c2ecf20Sopenharmony_ci	case CHG_STATE_SUSPEND:
1098c2ecf20Sopenharmony_ci	case CHG_STATE_VCHG_OVER_VOL:
1108c2ecf20Sopenharmony_ci	case CHG_STATE_DIE_SHUTDOWN:
1118c2ecf20Sopenharmony_ci		return POWER_SUPPLY_STATUS_DISCHARGING;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	case CHG_STATE_CHG_TRICKLE:
1148c2ecf20Sopenharmony_ci	case CHG_STATE_CHG_RAPID:
1158c2ecf20Sopenharmony_ci		return POWER_SUPPLY_STATUS_CHARGING;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	case CHG_STATE_CHG_COMPLETE:
1188c2ecf20Sopenharmony_ci		return POWER_SUPPLY_STATUS_FULL;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	default:
1218c2ecf20Sopenharmony_ci		return POWER_SUPPLY_STATUS_NOT_CHARGING;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic int rn5t618_battery_status(struct rn5t618_power_info *info,
1268c2ecf20Sopenharmony_ci				  union power_supply_propval *val)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	unsigned int v;
1298c2ecf20Sopenharmony_ci	int ret;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &v);
1328c2ecf20Sopenharmony_ci	if (ret)
1338c2ecf20Sopenharmony_ci		return ret;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (v & 0xc0) { /* USB or ADP plugged */
1388c2ecf20Sopenharmony_ci		val->intval = rn5t618_decode_status(v);
1398c2ecf20Sopenharmony_ci	} else
1408c2ecf20Sopenharmony_ci		val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return ret;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int rn5t618_battery_present(struct rn5t618_power_info *info,
1468c2ecf20Sopenharmony_ci				   union power_supply_propval *val)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	unsigned int v;
1498c2ecf20Sopenharmony_ci	int ret;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &v);
1528c2ecf20Sopenharmony_ci	if (ret)
1538c2ecf20Sopenharmony_ci		return ret;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	v &= CHG_STATE_MASK;
1568c2ecf20Sopenharmony_ci	if ((v == CHG_STATE_NO_BAT) || (v == CHG_STATE_NO_BAT2))
1578c2ecf20Sopenharmony_ci		val->intval = 0;
1588c2ecf20Sopenharmony_ci	else
1598c2ecf20Sopenharmony_ci		val->intval = 1;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	return ret;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic int rn5t618_battery_voltage_now(struct rn5t618_power_info *info,
1658c2ecf20Sopenharmony_ci				       union power_supply_propval *val)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	u16 res;
1688c2ecf20Sopenharmony_ci	int ret;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	ret = rn5t618_battery_read_doublereg(info, RN5T618_VOLTAGE_1, &res);
1718c2ecf20Sopenharmony_ci	if (ret)
1728c2ecf20Sopenharmony_ci		return ret;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	val->intval = res * 2 * 2500 / 4095 * 1000;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return 0;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic int rn5t618_battery_current_now(struct rn5t618_power_info *info,
1808c2ecf20Sopenharmony_ci				       union power_supply_propval *val)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	u16 res;
1838c2ecf20Sopenharmony_ci	int ret;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	ret = rn5t618_battery_read_doublereg(info, RN5T618_CC_AVEREG1, &res);
1868c2ecf20Sopenharmony_ci	if (ret)
1878c2ecf20Sopenharmony_ci		return ret;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/* current is negative when discharging */
1908c2ecf20Sopenharmony_ci	val->intval = sign_extend32(res, 13) * 1000;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return 0;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic int rn5t618_battery_capacity(struct rn5t618_power_info *info,
1968c2ecf20Sopenharmony_ci				    union power_supply_propval *val)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	unsigned int v;
1998c2ecf20Sopenharmony_ci	int ret;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	ret = regmap_read(info->rn5t618->regmap, RN5T618_SOC, &v);
2028c2ecf20Sopenharmony_ci	if (ret)
2038c2ecf20Sopenharmony_ci		return ret;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	val->intval = v;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	return 0;
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic int rn5t618_battery_temp(struct rn5t618_power_info *info,
2118c2ecf20Sopenharmony_ci				union power_supply_propval *val)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	u16 res;
2148c2ecf20Sopenharmony_ci	int ret;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	ret = rn5t618_battery_read_doublereg(info, RN5T618_TEMP_1, &res);
2178c2ecf20Sopenharmony_ci	if (ret)
2188c2ecf20Sopenharmony_ci		return ret;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	val->intval = sign_extend32(res, 11) * 10 / 16;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return 0;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic int rn5t618_battery_tte(struct rn5t618_power_info *info,
2268c2ecf20Sopenharmony_ci			       union power_supply_propval *val)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	u16 res;
2298c2ecf20Sopenharmony_ci	int ret;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	ret = rn5t618_battery_read_doublereg(info, RN5T618_TT_EMPTY_H, &res);
2328c2ecf20Sopenharmony_ci	if (ret)
2338c2ecf20Sopenharmony_ci		return ret;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (res == 65535)
2368c2ecf20Sopenharmony_ci		return -ENODATA;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	val->intval = res * 60;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return 0;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int rn5t618_battery_ttf(struct rn5t618_power_info *info,
2448c2ecf20Sopenharmony_ci			       union power_supply_propval *val)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	u16 res;
2478c2ecf20Sopenharmony_ci	int ret;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	ret = rn5t618_battery_read_doublereg(info, RN5T618_TT_FULL_H, &res);
2508c2ecf20Sopenharmony_ci	if (ret)
2518c2ecf20Sopenharmony_ci		return ret;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	if (res == 65535)
2548c2ecf20Sopenharmony_ci		return -ENODATA;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	val->intval = res * 60;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	return 0;
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic int rn5t618_battery_charge_full(struct rn5t618_power_info *info,
2628c2ecf20Sopenharmony_ci				       union power_supply_propval *val)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	u16 res;
2658c2ecf20Sopenharmony_ci	int ret;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	ret = rn5t618_battery_read_doublereg(info, RN5T618_FA_CAP_H, &res);
2688c2ecf20Sopenharmony_ci	if (ret)
2698c2ecf20Sopenharmony_ci		return ret;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	val->intval = res * 1000;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return 0;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int rn5t618_battery_charge_now(struct rn5t618_power_info *info,
2778c2ecf20Sopenharmony_ci				      union power_supply_propval *val)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	u16 res;
2808c2ecf20Sopenharmony_ci	int ret;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	ret = rn5t618_battery_read_doublereg(info, RN5T618_RE_CAP_H, &res);
2838c2ecf20Sopenharmony_ci	if (ret)
2848c2ecf20Sopenharmony_ci		return ret;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	val->intval = res * 1000;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return 0;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic int rn5t618_battery_get_property(struct power_supply *psy,
2928c2ecf20Sopenharmony_ci					enum power_supply_property psp,
2938c2ecf20Sopenharmony_ci					union power_supply_propval *val)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	int ret = 0;
2968c2ecf20Sopenharmony_ci	struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	switch (psp) {
2998c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
3008c2ecf20Sopenharmony_ci		ret = rn5t618_battery_status(info, val);
3018c2ecf20Sopenharmony_ci		break;
3028c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_PRESENT:
3038c2ecf20Sopenharmony_ci		ret = rn5t618_battery_present(info, val);
3048c2ecf20Sopenharmony_ci		break;
3058c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
3068c2ecf20Sopenharmony_ci		ret = rn5t618_battery_voltage_now(info, val);
3078c2ecf20Sopenharmony_ci		break;
3088c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_NOW:
3098c2ecf20Sopenharmony_ci		ret = rn5t618_battery_current_now(info, val);
3108c2ecf20Sopenharmony_ci		break;
3118c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY:
3128c2ecf20Sopenharmony_ci		ret = rn5t618_battery_capacity(info, val);
3138c2ecf20Sopenharmony_ci		break;
3148c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_TEMP:
3158c2ecf20Sopenharmony_ci		ret = rn5t618_battery_temp(info, val);
3168c2ecf20Sopenharmony_ci		break;
3178c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
3188c2ecf20Sopenharmony_ci		ret = rn5t618_battery_tte(info, val);
3198c2ecf20Sopenharmony_ci		break;
3208c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
3218c2ecf20Sopenharmony_ci		ret = rn5t618_battery_ttf(info, val);
3228c2ecf20Sopenharmony_ci		break;
3238c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_TECHNOLOGY:
3248c2ecf20Sopenharmony_ci		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
3258c2ecf20Sopenharmony_ci		break;
3268c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_FULL:
3278c2ecf20Sopenharmony_ci		ret = rn5t618_battery_charge_full(info, val);
3288c2ecf20Sopenharmony_ci		break;
3298c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_NOW:
3308c2ecf20Sopenharmony_ci		ret = rn5t618_battery_charge_now(info, val);
3318c2ecf20Sopenharmony_ci		break;
3328c2ecf20Sopenharmony_ci	default:
3338c2ecf20Sopenharmony_ci		return -EINVAL;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	return ret;
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic int rn5t618_adp_get_property(struct power_supply *psy,
3408c2ecf20Sopenharmony_ci				    enum power_supply_property psp,
3418c2ecf20Sopenharmony_ci				    union power_supply_propval *val)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
3448c2ecf20Sopenharmony_ci	unsigned int chgstate;
3458c2ecf20Sopenharmony_ci	bool online;
3468c2ecf20Sopenharmony_ci	int ret;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &chgstate);
3498c2ecf20Sopenharmony_ci	if (ret)
3508c2ecf20Sopenharmony_ci		return ret;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	online = !!(chgstate & CHG_STATE_ADP_INPUT);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	switch (psp) {
3558c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
3568c2ecf20Sopenharmony_ci		val->intval = online;
3578c2ecf20Sopenharmony_ci		break;
3588c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
3598c2ecf20Sopenharmony_ci		if (!online) {
3608c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
3618c2ecf20Sopenharmony_ci			break;
3628c2ecf20Sopenharmony_ci		}
3638c2ecf20Sopenharmony_ci		val->intval = rn5t618_decode_status(chgstate);
3648c2ecf20Sopenharmony_ci		if (val->intval != POWER_SUPPLY_STATUS_CHARGING)
3658c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci		break;
3688c2ecf20Sopenharmony_ci	default:
3698c2ecf20Sopenharmony_ci		return -EINVAL;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	return 0;
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic int rn5t618_usb_get_property(struct power_supply *psy,
3768c2ecf20Sopenharmony_ci				    enum power_supply_property psp,
3778c2ecf20Sopenharmony_ci				    union power_supply_propval *val)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
3808c2ecf20Sopenharmony_ci	unsigned int chgstate;
3818c2ecf20Sopenharmony_ci	bool online;
3828c2ecf20Sopenharmony_ci	int ret;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &chgstate);
3858c2ecf20Sopenharmony_ci	if (ret)
3868c2ecf20Sopenharmony_ci		return ret;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	online = !!(chgstate & CHG_STATE_USB_INPUT);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	switch (psp) {
3918c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
3928c2ecf20Sopenharmony_ci		val->intval = online;
3938c2ecf20Sopenharmony_ci		break;
3948c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
3958c2ecf20Sopenharmony_ci		if (!online) {
3968c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
3978c2ecf20Sopenharmony_ci			break;
3988c2ecf20Sopenharmony_ci		}
3998c2ecf20Sopenharmony_ci		val->intval = rn5t618_decode_status(chgstate);
4008c2ecf20Sopenharmony_ci		if (val->intval != POWER_SUPPLY_STATUS_CHARGING)
4018c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci		break;
4048c2ecf20Sopenharmony_ci	default:
4058c2ecf20Sopenharmony_ci		return -EINVAL;
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	return 0;
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic const struct power_supply_desc rn5t618_battery_desc = {
4128c2ecf20Sopenharmony_ci	.name                   = "rn5t618-battery",
4138c2ecf20Sopenharmony_ci	.type                   = POWER_SUPPLY_TYPE_BATTERY,
4148c2ecf20Sopenharmony_ci	.properties             = rn5t618_battery_props,
4158c2ecf20Sopenharmony_ci	.num_properties         = ARRAY_SIZE(rn5t618_battery_props),
4168c2ecf20Sopenharmony_ci	.get_property           = rn5t618_battery_get_property,
4178c2ecf20Sopenharmony_ci};
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic const struct power_supply_desc rn5t618_adp_desc = {
4208c2ecf20Sopenharmony_ci	.name                   = "rn5t618-adp",
4218c2ecf20Sopenharmony_ci	.type                   = POWER_SUPPLY_TYPE_MAINS,
4228c2ecf20Sopenharmony_ci	.properties             = rn5t618_adp_props,
4238c2ecf20Sopenharmony_ci	.num_properties         = ARRAY_SIZE(rn5t618_adp_props),
4248c2ecf20Sopenharmony_ci	.get_property           = rn5t618_adp_get_property,
4258c2ecf20Sopenharmony_ci};
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic const struct power_supply_desc rn5t618_usb_desc = {
4288c2ecf20Sopenharmony_ci	.name                   = "rn5t618-usb",
4298c2ecf20Sopenharmony_ci	.type                   = POWER_SUPPLY_TYPE_USB,
4308c2ecf20Sopenharmony_ci	.properties             = rn5t618_usb_props,
4318c2ecf20Sopenharmony_ci	.num_properties         = ARRAY_SIZE(rn5t618_usb_props),
4328c2ecf20Sopenharmony_ci	.get_property           = rn5t618_usb_get_property,
4338c2ecf20Sopenharmony_ci};
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic irqreturn_t rn5t618_charger_irq(int irq, void *data)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	struct device *dev = data;
4388c2ecf20Sopenharmony_ci	struct rn5t618_power_info *info = dev_get_drvdata(dev);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	unsigned int ctrl, stat1, stat2, err;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	regmap_read(info->rn5t618->regmap, RN5T618_CHGERR_IRR, &err);
4438c2ecf20Sopenharmony_ci	regmap_read(info->rn5t618->regmap, RN5T618_CHGCTRL_IRR, &ctrl);
4448c2ecf20Sopenharmony_ci	regmap_read(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR1, &stat1);
4458c2ecf20Sopenharmony_ci	regmap_read(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR2, &stat2);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	regmap_write(info->rn5t618->regmap, RN5T618_CHGERR_IRR, 0);
4488c2ecf20Sopenharmony_ci	regmap_write(info->rn5t618->regmap, RN5T618_CHGCTRL_IRR, 0);
4498c2ecf20Sopenharmony_ci	regmap_write(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR1, 0);
4508c2ecf20Sopenharmony_ci	regmap_write(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR2, 0);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	dev_dbg(dev, "chgerr: %x chgctrl: %x chgstat: %x chgstat2: %x\n",
4538c2ecf20Sopenharmony_ci		err, ctrl, stat1, stat2);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	power_supply_changed(info->usb);
4568c2ecf20Sopenharmony_ci	power_supply_changed(info->adp);
4578c2ecf20Sopenharmony_ci	power_supply_changed(info->battery);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic int rn5t618_power_probe(struct platform_device *pdev)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	int ret = 0;
4658c2ecf20Sopenharmony_ci	unsigned int v;
4668c2ecf20Sopenharmony_ci	struct power_supply_config psy_cfg = {};
4678c2ecf20Sopenharmony_ci	struct rn5t618_power_info *info;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
4708c2ecf20Sopenharmony_ci	if (!info)
4718c2ecf20Sopenharmony_ci		return -ENOMEM;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	info->pdev = pdev;
4748c2ecf20Sopenharmony_ci	info->rn5t618 = dev_get_drvdata(pdev->dev.parent);
4758c2ecf20Sopenharmony_ci	info->irq = -1;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, info);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	ret = regmap_read(info->rn5t618->regmap, RN5T618_CONTROL, &v);
4808c2ecf20Sopenharmony_ci	if (ret)
4818c2ecf20Sopenharmony_ci		return ret;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	if (!(v & FG_ENABLE)) {
4848c2ecf20Sopenharmony_ci		/* E.g. the vendor kernels of various Kobo and Tolino Ebook
4858c2ecf20Sopenharmony_ci		 * readers disable the fuel gauge on shutdown. If a kernel
4868c2ecf20Sopenharmony_ci		 * without fuel gauge support is booted after that, the fuel
4878c2ecf20Sopenharmony_ci		 * gauge will get decalibrated.
4888c2ecf20Sopenharmony_ci		 */
4898c2ecf20Sopenharmony_ci		dev_info(&pdev->dev, "Fuel gauge not enabled, enabling now\n");
4908c2ecf20Sopenharmony_ci		dev_info(&pdev->dev, "Expect imprecise results\n");
4918c2ecf20Sopenharmony_ci		regmap_update_bits(info->rn5t618->regmap, RN5T618_CONTROL,
4928c2ecf20Sopenharmony_ci				   FG_ENABLE, FG_ENABLE);
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	psy_cfg.drv_data = info;
4968c2ecf20Sopenharmony_ci	info->battery = devm_power_supply_register(&pdev->dev,
4978c2ecf20Sopenharmony_ci						   &rn5t618_battery_desc,
4988c2ecf20Sopenharmony_ci						   &psy_cfg);
4998c2ecf20Sopenharmony_ci	if (IS_ERR(info->battery)) {
5008c2ecf20Sopenharmony_ci		ret = PTR_ERR(info->battery);
5018c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to register battery: %d\n", ret);
5028c2ecf20Sopenharmony_ci		return ret;
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	info->adp = devm_power_supply_register(&pdev->dev,
5068c2ecf20Sopenharmony_ci					       &rn5t618_adp_desc,
5078c2ecf20Sopenharmony_ci					       &psy_cfg);
5088c2ecf20Sopenharmony_ci	if (IS_ERR(info->adp)) {
5098c2ecf20Sopenharmony_ci		ret = PTR_ERR(info->adp);
5108c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to register adp: %d\n", ret);
5118c2ecf20Sopenharmony_ci		return ret;
5128c2ecf20Sopenharmony_ci	}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	info->usb = devm_power_supply_register(&pdev->dev,
5158c2ecf20Sopenharmony_ci					       &rn5t618_usb_desc,
5168c2ecf20Sopenharmony_ci					       &psy_cfg);
5178c2ecf20Sopenharmony_ci	if (IS_ERR(info->usb)) {
5188c2ecf20Sopenharmony_ci		ret = PTR_ERR(info->usb);
5198c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to register usb: %d\n", ret);
5208c2ecf20Sopenharmony_ci		return ret;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	if (info->rn5t618->irq_data)
5248c2ecf20Sopenharmony_ci		info->irq = regmap_irq_get_virq(info->rn5t618->irq_data,
5258c2ecf20Sopenharmony_ci						RN5T618_IRQ_CHG);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	if (info->irq < 0)
5288c2ecf20Sopenharmony_ci		info->irq = -1;
5298c2ecf20Sopenharmony_ci	else {
5308c2ecf20Sopenharmony_ci		ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
5318c2ecf20Sopenharmony_ci						rn5t618_charger_irq,
5328c2ecf20Sopenharmony_ci						IRQF_ONESHOT,
5338c2ecf20Sopenharmony_ci						"rn5t618_power",
5348c2ecf20Sopenharmony_ci						&pdev->dev);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci		if (ret < 0) {
5378c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "request IRQ:%d fail\n",
5388c2ecf20Sopenharmony_ci				info->irq);
5398c2ecf20Sopenharmony_ci			info->irq = -1;
5408c2ecf20Sopenharmony_ci		}
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	return 0;
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic struct platform_driver rn5t618_power_driver = {
5478c2ecf20Sopenharmony_ci	.driver = {
5488c2ecf20Sopenharmony_ci		.name   = "rn5t618-power",
5498c2ecf20Sopenharmony_ci	},
5508c2ecf20Sopenharmony_ci	.probe = rn5t618_power_probe,
5518c2ecf20Sopenharmony_ci};
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_cimodule_platform_driver(rn5t618_power_driver);
5548c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:rn5t618-power");
5558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Power supply driver for RICOH RN5T618");
5568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
557