18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// max77693_charger.c - Battery charger driver for the Maxim 77693
48c2ecf20Sopenharmony_ci//
58c2ecf20Sopenharmony_ci// Copyright (C) 2014 Samsung Electronics
68c2ecf20Sopenharmony_ci// Krzysztof Kozlowski <krzk@kernel.org>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
108c2ecf20Sopenharmony_ci#include <linux/power_supply.h>
118c2ecf20Sopenharmony_ci#include <linux/regmap.h>
128c2ecf20Sopenharmony_ci#include <linux/mfd/max77693.h>
138c2ecf20Sopenharmony_ci#include <linux/mfd/max77693-common.h>
148c2ecf20Sopenharmony_ci#include <linux/mfd/max77693-private.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define MAX77693_CHARGER_NAME				"max77693-charger"
178c2ecf20Sopenharmony_cistatic const char *max77693_charger_model		= "MAX77693";
188c2ecf20Sopenharmony_cistatic const char *max77693_charger_manufacturer	= "Maxim Integrated";
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct max77693_charger {
218c2ecf20Sopenharmony_ci	struct device		*dev;
228c2ecf20Sopenharmony_ci	struct max77693_dev	*max77693;
238c2ecf20Sopenharmony_ci	struct power_supply	*charger;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	u32 constant_volt;
268c2ecf20Sopenharmony_ci	u32 min_system_volt;
278c2ecf20Sopenharmony_ci	u32 thermal_regulation_temp;
288c2ecf20Sopenharmony_ci	u32 batttery_overcurrent;
298c2ecf20Sopenharmony_ci	u32 charge_input_threshold_volt;
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int max77693_get_charger_state(struct regmap *regmap, int *val)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	int ret;
358c2ecf20Sopenharmony_ci	unsigned int data;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data);
388c2ecf20Sopenharmony_ci	if (ret < 0)
398c2ecf20Sopenharmony_ci		return ret;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	data &= CHG_DETAILS_01_CHG_MASK;
428c2ecf20Sopenharmony_ci	data >>= CHG_DETAILS_01_CHG_SHIFT;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	switch (data) {
458c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_PREQUALIFICATION:
468c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_FAST_CONST_CURRENT:
478c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_FAST_CONST_VOLTAGE:
488c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_TOP_OFF:
498c2ecf20Sopenharmony_ci	/* In high temp the charging current is reduced, but still charging */
508c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_HIGH_TEMP:
518c2ecf20Sopenharmony_ci		*val = POWER_SUPPLY_STATUS_CHARGING;
528c2ecf20Sopenharmony_ci		break;
538c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_DONE:
548c2ecf20Sopenharmony_ci		*val = POWER_SUPPLY_STATUS_FULL;
558c2ecf20Sopenharmony_ci		break;
568c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_TIMER_EXPIRED:
578c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_THERMISTOR_SUSPEND:
588c2ecf20Sopenharmony_ci		*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
598c2ecf20Sopenharmony_ci		break;
608c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_OFF:
618c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_OVER_TEMP:
628c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_WATCHDOG_EXPIRED:
638c2ecf20Sopenharmony_ci		*val = POWER_SUPPLY_STATUS_DISCHARGING;
648c2ecf20Sopenharmony_ci		break;
658c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_RESERVED:
668c2ecf20Sopenharmony_ci	default:
678c2ecf20Sopenharmony_ci		*val = POWER_SUPPLY_STATUS_UNKNOWN;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	return 0;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic int max77693_get_charge_type(struct regmap *regmap, int *val)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	int ret;
768c2ecf20Sopenharmony_ci	unsigned int data;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data);
798c2ecf20Sopenharmony_ci	if (ret < 0)
808c2ecf20Sopenharmony_ci		return ret;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	data &= CHG_DETAILS_01_CHG_MASK;
838c2ecf20Sopenharmony_ci	data >>= CHG_DETAILS_01_CHG_SHIFT;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	switch (data) {
868c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_PREQUALIFICATION:
878c2ecf20Sopenharmony_ci	/*
888c2ecf20Sopenharmony_ci	 * Top-off: trickle or fast? In top-off the current varies between
898c2ecf20Sopenharmony_ci	 * 100 and 250 mA. It is higher than prequalification current.
908c2ecf20Sopenharmony_ci	 */
918c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_TOP_OFF:
928c2ecf20Sopenharmony_ci		*val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
938c2ecf20Sopenharmony_ci		break;
948c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_FAST_CONST_CURRENT:
958c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_FAST_CONST_VOLTAGE:
968c2ecf20Sopenharmony_ci	/* In high temp the charging current is reduced, but still charging */
978c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_HIGH_TEMP:
988c2ecf20Sopenharmony_ci		*val = POWER_SUPPLY_CHARGE_TYPE_FAST;
998c2ecf20Sopenharmony_ci		break;
1008c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_DONE:
1018c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_TIMER_EXPIRED:
1028c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_THERMISTOR_SUSPEND:
1038c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_OFF:
1048c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_OVER_TEMP:
1058c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_WATCHDOG_EXPIRED:
1068c2ecf20Sopenharmony_ci		*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
1078c2ecf20Sopenharmony_ci		break;
1088c2ecf20Sopenharmony_ci	case MAX77693_CHARGING_RESERVED:
1098c2ecf20Sopenharmony_ci	default:
1108c2ecf20Sopenharmony_ci		*val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	return 0;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/*
1178c2ecf20Sopenharmony_ci * Supported health statuses:
1188c2ecf20Sopenharmony_ci *  - POWER_SUPPLY_HEALTH_DEAD
1198c2ecf20Sopenharmony_ci *  - POWER_SUPPLY_HEALTH_GOOD
1208c2ecf20Sopenharmony_ci *  - POWER_SUPPLY_HEALTH_OVERVOLTAGE
1218c2ecf20Sopenharmony_ci *  - POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE
1228c2ecf20Sopenharmony_ci *  - POWER_SUPPLY_HEALTH_UNKNOWN
1238c2ecf20Sopenharmony_ci *  - POWER_SUPPLY_HEALTH_UNSPEC_FAILURE
1248c2ecf20Sopenharmony_ci */
1258c2ecf20Sopenharmony_cistatic int max77693_get_battery_health(struct regmap *regmap, int *val)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	int ret;
1288c2ecf20Sopenharmony_ci	unsigned int data;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data);
1318c2ecf20Sopenharmony_ci	if (ret < 0)
1328c2ecf20Sopenharmony_ci		return ret;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	data &= CHG_DETAILS_01_BAT_MASK;
1358c2ecf20Sopenharmony_ci	data >>= CHG_DETAILS_01_BAT_SHIFT;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	switch (data) {
1388c2ecf20Sopenharmony_ci	case MAX77693_BATTERY_NOBAT:
1398c2ecf20Sopenharmony_ci		*val = POWER_SUPPLY_HEALTH_DEAD;
1408c2ecf20Sopenharmony_ci		break;
1418c2ecf20Sopenharmony_ci	case MAX77693_BATTERY_PREQUALIFICATION:
1428c2ecf20Sopenharmony_ci	case MAX77693_BATTERY_GOOD:
1438c2ecf20Sopenharmony_ci	case MAX77693_BATTERY_LOWVOLTAGE:
1448c2ecf20Sopenharmony_ci		*val = POWER_SUPPLY_HEALTH_GOOD;
1458c2ecf20Sopenharmony_ci		break;
1468c2ecf20Sopenharmony_ci	case MAX77693_BATTERY_TIMER_EXPIRED:
1478c2ecf20Sopenharmony_ci		/*
1488c2ecf20Sopenharmony_ci		 * Took longer to charge than expected, charging suspended.
1498c2ecf20Sopenharmony_ci		 * Damaged battery?
1508c2ecf20Sopenharmony_ci		 */
1518c2ecf20Sopenharmony_ci		*val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
1528c2ecf20Sopenharmony_ci		break;
1538c2ecf20Sopenharmony_ci	case MAX77693_BATTERY_OVERVOLTAGE:
1548c2ecf20Sopenharmony_ci		*val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
1558c2ecf20Sopenharmony_ci		break;
1568c2ecf20Sopenharmony_ci	case MAX77693_BATTERY_OVERCURRENT:
1578c2ecf20Sopenharmony_ci		*val = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
1588c2ecf20Sopenharmony_ci		break;
1598c2ecf20Sopenharmony_ci	case MAX77693_BATTERY_RESERVED:
1608c2ecf20Sopenharmony_ci	default:
1618c2ecf20Sopenharmony_ci		*val = POWER_SUPPLY_HEALTH_UNKNOWN;
1628c2ecf20Sopenharmony_ci		break;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	return 0;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic int max77693_get_present(struct regmap *regmap, int *val)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	unsigned int data;
1718c2ecf20Sopenharmony_ci	int ret;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	/*
1748c2ecf20Sopenharmony_ci	 * Read CHG_INT_OK register. High DETBAT bit here should be
1758c2ecf20Sopenharmony_ci	 * equal to value 0x0 in CHG_DETAILS_01/BAT field.
1768c2ecf20Sopenharmony_ci	 */
1778c2ecf20Sopenharmony_ci	ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data);
1788c2ecf20Sopenharmony_ci	if (ret < 0)
1798c2ecf20Sopenharmony_ci		return ret;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	*val = (data & CHG_INT_OK_DETBAT_MASK) ? 0 : 1;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	return 0;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int max77693_get_online(struct regmap *regmap, int *val)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	unsigned int data;
1898c2ecf20Sopenharmony_ci	int ret;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data);
1928c2ecf20Sopenharmony_ci	if (ret < 0)
1938c2ecf20Sopenharmony_ci		return ret;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	*val = (data & CHG_INT_OK_CHGIN_MASK) ? 1 : 0;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return 0;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic enum power_supply_property max77693_charger_props[] = {
2018c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
2028c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_TYPE,
2038c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_HEALTH,
2048c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
2058c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
2068c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_MODEL_NAME,
2078c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_MANUFACTURER,
2088c2ecf20Sopenharmony_ci};
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic int max77693_charger_get_property(struct power_supply *psy,
2118c2ecf20Sopenharmony_ci			    enum power_supply_property psp,
2128c2ecf20Sopenharmony_ci			    union power_supply_propval *val)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	struct max77693_charger *chg = power_supply_get_drvdata(psy);
2158c2ecf20Sopenharmony_ci	struct regmap *regmap = chg->max77693->regmap;
2168c2ecf20Sopenharmony_ci	int ret = 0;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	switch (psp) {
2198c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
2208c2ecf20Sopenharmony_ci		ret = max77693_get_charger_state(regmap, &val->intval);
2218c2ecf20Sopenharmony_ci		break;
2228c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_TYPE:
2238c2ecf20Sopenharmony_ci		ret = max77693_get_charge_type(regmap, &val->intval);
2248c2ecf20Sopenharmony_ci		break;
2258c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_HEALTH:
2268c2ecf20Sopenharmony_ci		ret = max77693_get_battery_health(regmap, &val->intval);
2278c2ecf20Sopenharmony_ci		break;
2288c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_PRESENT:
2298c2ecf20Sopenharmony_ci		ret = max77693_get_present(regmap, &val->intval);
2308c2ecf20Sopenharmony_ci		break;
2318c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
2328c2ecf20Sopenharmony_ci		ret = max77693_get_online(regmap, &val->intval);
2338c2ecf20Sopenharmony_ci		break;
2348c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_MODEL_NAME:
2358c2ecf20Sopenharmony_ci		val->strval = max77693_charger_model;
2368c2ecf20Sopenharmony_ci		break;
2378c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_MANUFACTURER:
2388c2ecf20Sopenharmony_ci		val->strval = max77693_charger_manufacturer;
2398c2ecf20Sopenharmony_ci		break;
2408c2ecf20Sopenharmony_ci	default:
2418c2ecf20Sopenharmony_ci		return -EINVAL;
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	return ret;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic const struct power_supply_desc max77693_charger_desc = {
2488c2ecf20Sopenharmony_ci	.name		= MAX77693_CHARGER_NAME,
2498c2ecf20Sopenharmony_ci	.type		= POWER_SUPPLY_TYPE_BATTERY,
2508c2ecf20Sopenharmony_ci	.properties	= max77693_charger_props,
2518c2ecf20Sopenharmony_ci	.num_properties	= ARRAY_SIZE(max77693_charger_props),
2528c2ecf20Sopenharmony_ci	.get_property	= max77693_charger_get_property,
2538c2ecf20Sopenharmony_ci};
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic ssize_t device_attr_store(struct device *dev,
2568c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count,
2578c2ecf20Sopenharmony_ci		int (*fn)(struct max77693_charger *, unsigned long))
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	struct max77693_charger *chg = dev_get_drvdata(dev);
2608c2ecf20Sopenharmony_ci	unsigned long val;
2618c2ecf20Sopenharmony_ci	int ret;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 10, &val);
2648c2ecf20Sopenharmony_ci	if (ret)
2658c2ecf20Sopenharmony_ci		return ret;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	ret = fn(chg, val);
2688c2ecf20Sopenharmony_ci	if (ret)
2698c2ecf20Sopenharmony_ci		return ret;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	return count;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic ssize_t fast_charge_timer_show(struct device *dev,
2758c2ecf20Sopenharmony_ci		struct device_attribute *attr, char *buf)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	struct max77693_charger *chg = dev_get_drvdata(dev);
2788c2ecf20Sopenharmony_ci	unsigned int data, val;
2798c2ecf20Sopenharmony_ci	int ret;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_01,
2828c2ecf20Sopenharmony_ci			&data);
2838c2ecf20Sopenharmony_ci	if (ret < 0)
2848c2ecf20Sopenharmony_ci		return ret;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	data &= CHG_CNFG_01_FCHGTIME_MASK;
2878c2ecf20Sopenharmony_ci	data >>= CHG_CNFG_01_FCHGTIME_SHIFT;
2888c2ecf20Sopenharmony_ci	switch (data) {
2898c2ecf20Sopenharmony_ci	case 0x1 ... 0x7:
2908c2ecf20Sopenharmony_ci		/* Starting from 4 hours, step by 2 hours */
2918c2ecf20Sopenharmony_ci		val = 4 + (data - 1) * 2;
2928c2ecf20Sopenharmony_ci		break;
2938c2ecf20Sopenharmony_ci	case 0x0:
2948c2ecf20Sopenharmony_ci	default:
2958c2ecf20Sopenharmony_ci		val = 0;
2968c2ecf20Sopenharmony_ci		break;
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%u\n", val);
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic int max77693_set_fast_charge_timer(struct max77693_charger *chg,
3038c2ecf20Sopenharmony_ci		unsigned long hours)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	unsigned int data;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	/*
3088c2ecf20Sopenharmony_ci	 * 0x00 - disable
3098c2ecf20Sopenharmony_ci	 * 0x01 - 4h
3108c2ecf20Sopenharmony_ci	 * 0x02 - 6h
3118c2ecf20Sopenharmony_ci	 * ...
3128c2ecf20Sopenharmony_ci	 * 0x07 - 16h
3138c2ecf20Sopenharmony_ci	 * Round down odd values.
3148c2ecf20Sopenharmony_ci	 */
3158c2ecf20Sopenharmony_ci	switch (hours) {
3168c2ecf20Sopenharmony_ci	case 4 ... 16:
3178c2ecf20Sopenharmony_ci		data = (hours - 4) / 2 + 1;
3188c2ecf20Sopenharmony_ci		break;
3198c2ecf20Sopenharmony_ci	case 0:
3208c2ecf20Sopenharmony_ci		/* Disable */
3218c2ecf20Sopenharmony_ci		data = 0;
3228c2ecf20Sopenharmony_ci		break;
3238c2ecf20Sopenharmony_ci	default:
3248c2ecf20Sopenharmony_ci		return -EINVAL;
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci	data <<= CHG_CNFG_01_FCHGTIME_SHIFT;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	return regmap_update_bits(chg->max77693->regmap,
3298c2ecf20Sopenharmony_ci			MAX77693_CHG_REG_CHG_CNFG_01,
3308c2ecf20Sopenharmony_ci			CHG_CNFG_01_FCHGTIME_MASK, data);
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistatic ssize_t fast_charge_timer_store(struct device *dev,
3348c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	return device_attr_store(dev, attr, buf, count,
3378c2ecf20Sopenharmony_ci			max77693_set_fast_charge_timer);
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic ssize_t top_off_threshold_current_show(struct device *dev,
3418c2ecf20Sopenharmony_ci		struct device_attribute *attr, char *buf)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	struct max77693_charger *chg = dev_get_drvdata(dev);
3448c2ecf20Sopenharmony_ci	unsigned int data, val;
3458c2ecf20Sopenharmony_ci	int ret;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03,
3488c2ecf20Sopenharmony_ci			&data);
3498c2ecf20Sopenharmony_ci	if (ret < 0)
3508c2ecf20Sopenharmony_ci		return ret;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	data &= CHG_CNFG_03_TOITH_MASK;
3538c2ecf20Sopenharmony_ci	data >>= CHG_CNFG_03_TOITH_SHIFT;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (data <= 0x04)
3568c2ecf20Sopenharmony_ci		val = 100000 + data * 25000;
3578c2ecf20Sopenharmony_ci	else
3588c2ecf20Sopenharmony_ci		val = data * 50000;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%u\n", val);
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic int max77693_set_top_off_threshold_current(struct max77693_charger *chg,
3648c2ecf20Sopenharmony_ci		unsigned long uamp)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	unsigned int data;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (uamp < 100000 || uamp > 350000)
3698c2ecf20Sopenharmony_ci		return -EINVAL;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (uamp <= 200000)
3728c2ecf20Sopenharmony_ci		data = (uamp - 100000) / 25000;
3738c2ecf20Sopenharmony_ci	else
3748c2ecf20Sopenharmony_ci		/* (200000, 350000> */
3758c2ecf20Sopenharmony_ci		data = uamp / 50000;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	data <<= CHG_CNFG_03_TOITH_SHIFT;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	return regmap_update_bits(chg->max77693->regmap,
3808c2ecf20Sopenharmony_ci			MAX77693_CHG_REG_CHG_CNFG_03,
3818c2ecf20Sopenharmony_ci			CHG_CNFG_03_TOITH_MASK, data);
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic ssize_t top_off_threshold_current_store(struct device *dev,
3858c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	return device_attr_store(dev, attr, buf, count,
3888c2ecf20Sopenharmony_ci			max77693_set_top_off_threshold_current);
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic ssize_t top_off_timer_show(struct device *dev,
3928c2ecf20Sopenharmony_ci		struct device_attribute *attr, char *buf)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct max77693_charger *chg = dev_get_drvdata(dev);
3958c2ecf20Sopenharmony_ci	unsigned int data, val;
3968c2ecf20Sopenharmony_ci	int ret;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03,
3998c2ecf20Sopenharmony_ci			&data);
4008c2ecf20Sopenharmony_ci	if (ret < 0)
4018c2ecf20Sopenharmony_ci		return ret;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	data &= CHG_CNFG_03_TOTIME_MASK;
4048c2ecf20Sopenharmony_ci	data >>= CHG_CNFG_03_TOTIME_SHIFT;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	val = data * 10;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%u\n", val);
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic int max77693_set_top_off_timer(struct max77693_charger *chg,
4128c2ecf20Sopenharmony_ci		unsigned long minutes)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	unsigned int data;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	if (minutes > 70)
4178c2ecf20Sopenharmony_ci		return -EINVAL;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	data = minutes / 10;
4208c2ecf20Sopenharmony_ci	data <<= CHG_CNFG_03_TOTIME_SHIFT;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	return regmap_update_bits(chg->max77693->regmap,
4238c2ecf20Sopenharmony_ci			MAX77693_CHG_REG_CHG_CNFG_03,
4248c2ecf20Sopenharmony_ci			CHG_CNFG_03_TOTIME_MASK, data);
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic ssize_t top_off_timer_store(struct device *dev,
4288c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	return device_attr_store(dev, attr, buf, count,
4318c2ecf20Sopenharmony_ci			max77693_set_top_off_timer);
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(fast_charge_timer);
4358c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(top_off_threshold_current);
4368c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(top_off_timer);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic int max77693_set_constant_volt(struct max77693_charger *chg,
4398c2ecf20Sopenharmony_ci		unsigned int uvolt)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	unsigned int data;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	/*
4448c2ecf20Sopenharmony_ci	 * 0x00 - 3.650 V
4458c2ecf20Sopenharmony_ci	 * 0x01 - 3.675 V
4468c2ecf20Sopenharmony_ci	 * ...
4478c2ecf20Sopenharmony_ci	 * 0x1b - 4.325 V
4488c2ecf20Sopenharmony_ci	 * 0x1c - 4.340 V
4498c2ecf20Sopenharmony_ci	 * 0x1d - 4.350 V
4508c2ecf20Sopenharmony_ci	 * 0x1e - 4.375 V
4518c2ecf20Sopenharmony_ci	 * 0x1f - 4.400 V
4528c2ecf20Sopenharmony_ci	 */
4538c2ecf20Sopenharmony_ci	if (uvolt >= 3650000 && uvolt < 4340000)
4548c2ecf20Sopenharmony_ci		data = (uvolt - 3650000) / 25000;
4558c2ecf20Sopenharmony_ci	else if (uvolt >= 4340000 && uvolt < 4350000)
4568c2ecf20Sopenharmony_ci		data = 0x1c;
4578c2ecf20Sopenharmony_ci	else if (uvolt >= 4350000 && uvolt <= 4400000)
4588c2ecf20Sopenharmony_ci		data = 0x1d + (uvolt - 4350000) / 25000;
4598c2ecf20Sopenharmony_ci	else {
4608c2ecf20Sopenharmony_ci		dev_err(chg->dev, "Wrong value for charging constant voltage\n");
4618c2ecf20Sopenharmony_ci		return -EINVAL;
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	data <<= CHG_CNFG_04_CHGCVPRM_SHIFT;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	dev_dbg(chg->dev, "Charging constant voltage: %u (0x%x)\n", uvolt,
4678c2ecf20Sopenharmony_ci			data);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	return regmap_update_bits(chg->max77693->regmap,
4708c2ecf20Sopenharmony_ci			MAX77693_CHG_REG_CHG_CNFG_04,
4718c2ecf20Sopenharmony_ci			CHG_CNFG_04_CHGCVPRM_MASK, data);
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistatic int max77693_set_min_system_volt(struct max77693_charger *chg,
4758c2ecf20Sopenharmony_ci		unsigned int uvolt)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	unsigned int data;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	if (uvolt < 3000000 || uvolt > 3700000) {
4808c2ecf20Sopenharmony_ci		dev_err(chg->dev, "Wrong value for minimum system regulation voltage\n");
4818c2ecf20Sopenharmony_ci		return -EINVAL;
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	data = (uvolt - 3000000) / 100000;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	data <<= CHG_CNFG_04_MINVSYS_SHIFT;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	dev_dbg(chg->dev, "Minimum system regulation voltage: %u (0x%x)\n",
4898c2ecf20Sopenharmony_ci			uvolt, data);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	return regmap_update_bits(chg->max77693->regmap,
4928c2ecf20Sopenharmony_ci			MAX77693_CHG_REG_CHG_CNFG_04,
4938c2ecf20Sopenharmony_ci			CHG_CNFG_04_MINVSYS_MASK, data);
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cistatic int max77693_set_thermal_regulation_temp(struct max77693_charger *chg,
4978c2ecf20Sopenharmony_ci		unsigned int cels)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	unsigned int data;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	switch (cels) {
5028c2ecf20Sopenharmony_ci	case 70:
5038c2ecf20Sopenharmony_ci	case 85:
5048c2ecf20Sopenharmony_ci	case 100:
5058c2ecf20Sopenharmony_ci	case 115:
5068c2ecf20Sopenharmony_ci		data = (cels - 70) / 15;
5078c2ecf20Sopenharmony_ci		break;
5088c2ecf20Sopenharmony_ci	default:
5098c2ecf20Sopenharmony_ci		dev_err(chg->dev, "Wrong value for thermal regulation loop temperature\n");
5108c2ecf20Sopenharmony_ci		return -EINVAL;
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	data <<= CHG_CNFG_07_REGTEMP_SHIFT;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	dev_dbg(chg->dev, "Thermal regulation loop temperature: %u (0x%x)\n",
5168c2ecf20Sopenharmony_ci			cels, data);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	return regmap_update_bits(chg->max77693->regmap,
5198c2ecf20Sopenharmony_ci			MAX77693_CHG_REG_CHG_CNFG_07,
5208c2ecf20Sopenharmony_ci			CHG_CNFG_07_REGTEMP_MASK, data);
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistatic int max77693_set_batttery_overcurrent(struct max77693_charger *chg,
5248c2ecf20Sopenharmony_ci		unsigned int uamp)
5258c2ecf20Sopenharmony_ci{
5268c2ecf20Sopenharmony_ci	unsigned int data;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	if (uamp && (uamp < 2000000 || uamp > 3500000)) {
5298c2ecf20Sopenharmony_ci		dev_err(chg->dev, "Wrong value for battery overcurrent\n");
5308c2ecf20Sopenharmony_ci		return -EINVAL;
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	if (uamp)
5348c2ecf20Sopenharmony_ci		data = ((uamp - 2000000) / 250000) + 1;
5358c2ecf20Sopenharmony_ci	else
5368c2ecf20Sopenharmony_ci		data = 0; /* disable */
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	data <<= CHG_CNFG_12_B2SOVRC_SHIFT;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	dev_dbg(chg->dev, "Battery overcurrent: %u (0x%x)\n", uamp, data);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	return regmap_update_bits(chg->max77693->regmap,
5438c2ecf20Sopenharmony_ci			MAX77693_CHG_REG_CHG_CNFG_12,
5448c2ecf20Sopenharmony_ci			CHG_CNFG_12_B2SOVRC_MASK, data);
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cistatic int max77693_set_charge_input_threshold_volt(struct max77693_charger *chg,
5488c2ecf20Sopenharmony_ci		unsigned int uvolt)
5498c2ecf20Sopenharmony_ci{
5508c2ecf20Sopenharmony_ci	unsigned int data;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	switch (uvolt) {
5538c2ecf20Sopenharmony_ci	case 4300000:
5548c2ecf20Sopenharmony_ci		data = 0x0;
5558c2ecf20Sopenharmony_ci		break;
5568c2ecf20Sopenharmony_ci	case 4700000:
5578c2ecf20Sopenharmony_ci	case 4800000:
5588c2ecf20Sopenharmony_ci	case 4900000:
5598c2ecf20Sopenharmony_ci		data = (uvolt - 4700000) / 100000;
5608c2ecf20Sopenharmony_ci		break;
5618c2ecf20Sopenharmony_ci	default:
5628c2ecf20Sopenharmony_ci		dev_err(chg->dev, "Wrong value for charge input voltage regulation threshold\n");
5638c2ecf20Sopenharmony_ci		return -EINVAL;
5648c2ecf20Sopenharmony_ci	}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	data <<= CHG_CNFG_12_VCHGINREG_SHIFT;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	dev_dbg(chg->dev, "Charge input voltage regulation threshold: %u (0x%x)\n",
5698c2ecf20Sopenharmony_ci			uvolt, data);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	return regmap_update_bits(chg->max77693->regmap,
5728c2ecf20Sopenharmony_ci			MAX77693_CHG_REG_CHG_CNFG_12,
5738c2ecf20Sopenharmony_ci			CHG_CNFG_12_VCHGINREG_MASK, data);
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci/*
5778c2ecf20Sopenharmony_ci * Sets charger registers to proper and safe default values.
5788c2ecf20Sopenharmony_ci */
5798c2ecf20Sopenharmony_cistatic int max77693_reg_init(struct max77693_charger *chg)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	int ret;
5828c2ecf20Sopenharmony_ci	unsigned int data;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	/* Unlock charger register protection */
5858c2ecf20Sopenharmony_ci	data = (0x3 << CHG_CNFG_06_CHGPROT_SHIFT);
5868c2ecf20Sopenharmony_ci	ret = regmap_update_bits(chg->max77693->regmap,
5878c2ecf20Sopenharmony_ci				MAX77693_CHG_REG_CHG_CNFG_06,
5888c2ecf20Sopenharmony_ci				CHG_CNFG_06_CHGPROT_MASK, data);
5898c2ecf20Sopenharmony_ci	if (ret) {
5908c2ecf20Sopenharmony_ci		dev_err(chg->dev, "Error unlocking registers: %d\n", ret);
5918c2ecf20Sopenharmony_ci		return ret;
5928c2ecf20Sopenharmony_ci	}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	ret = max77693_set_fast_charge_timer(chg, DEFAULT_FAST_CHARGE_TIMER);
5958c2ecf20Sopenharmony_ci	if (ret)
5968c2ecf20Sopenharmony_ci		return ret;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	ret = max77693_set_top_off_threshold_current(chg,
5998c2ecf20Sopenharmony_ci			DEFAULT_TOP_OFF_THRESHOLD_CURRENT);
6008c2ecf20Sopenharmony_ci	if (ret)
6018c2ecf20Sopenharmony_ci		return ret;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	ret = max77693_set_top_off_timer(chg, DEFAULT_TOP_OFF_TIMER);
6048c2ecf20Sopenharmony_ci	if (ret)
6058c2ecf20Sopenharmony_ci		return ret;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	ret = max77693_set_constant_volt(chg, chg->constant_volt);
6088c2ecf20Sopenharmony_ci	if (ret)
6098c2ecf20Sopenharmony_ci		return ret;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	ret = max77693_set_min_system_volt(chg, chg->min_system_volt);
6128c2ecf20Sopenharmony_ci	if (ret)
6138c2ecf20Sopenharmony_ci		return ret;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	ret = max77693_set_thermal_regulation_temp(chg,
6168c2ecf20Sopenharmony_ci			chg->thermal_regulation_temp);
6178c2ecf20Sopenharmony_ci	if (ret)
6188c2ecf20Sopenharmony_ci		return ret;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	ret = max77693_set_batttery_overcurrent(chg, chg->batttery_overcurrent);
6218c2ecf20Sopenharmony_ci	if (ret)
6228c2ecf20Sopenharmony_ci		return ret;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	return max77693_set_charge_input_threshold_volt(chg,
6258c2ecf20Sopenharmony_ci			chg->charge_input_threshold_volt);
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
6298c2ecf20Sopenharmony_cistatic int max77693_dt_init(struct device *dev, struct max77693_charger *chg)
6308c2ecf20Sopenharmony_ci{
6318c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	if (!np) {
6348c2ecf20Sopenharmony_ci		dev_err(dev, "no charger OF node\n");
6358c2ecf20Sopenharmony_ci		return -EINVAL;
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	if (of_property_read_u32(np, "maxim,constant-microvolt",
6398c2ecf20Sopenharmony_ci			&chg->constant_volt))
6408c2ecf20Sopenharmony_ci		chg->constant_volt = DEFAULT_CONSTANT_VOLT;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	if (of_property_read_u32(np, "maxim,min-system-microvolt",
6438c2ecf20Sopenharmony_ci			&chg->min_system_volt))
6448c2ecf20Sopenharmony_ci		chg->min_system_volt = DEFAULT_MIN_SYSTEM_VOLT;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	if (of_property_read_u32(np, "maxim,thermal-regulation-celsius",
6478c2ecf20Sopenharmony_ci			&chg->thermal_regulation_temp))
6488c2ecf20Sopenharmony_ci		chg->thermal_regulation_temp = DEFAULT_THERMAL_REGULATION_TEMP;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	if (of_property_read_u32(np, "maxim,battery-overcurrent-microamp",
6518c2ecf20Sopenharmony_ci			&chg->batttery_overcurrent))
6528c2ecf20Sopenharmony_ci		chg->batttery_overcurrent = DEFAULT_BATTERY_OVERCURRENT;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	if (of_property_read_u32(np, "maxim,charge-input-threshold-microvolt",
6558c2ecf20Sopenharmony_ci			&chg->charge_input_threshold_volt))
6568c2ecf20Sopenharmony_ci		chg->charge_input_threshold_volt =
6578c2ecf20Sopenharmony_ci			DEFAULT_CHARGER_INPUT_THRESHOLD_VOLT;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	return 0;
6608c2ecf20Sopenharmony_ci}
6618c2ecf20Sopenharmony_ci#else /* CONFIG_OF */
6628c2ecf20Sopenharmony_cistatic int max77693_dt_init(struct device *dev, struct max77693_charger *chg)
6638c2ecf20Sopenharmony_ci{
6648c2ecf20Sopenharmony_ci	return 0;
6658c2ecf20Sopenharmony_ci}
6668c2ecf20Sopenharmony_ci#endif /* CONFIG_OF */
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_cistatic int max77693_charger_probe(struct platform_device *pdev)
6698c2ecf20Sopenharmony_ci{
6708c2ecf20Sopenharmony_ci	struct max77693_charger *chg;
6718c2ecf20Sopenharmony_ci	struct power_supply_config psy_cfg = {};
6728c2ecf20Sopenharmony_ci	struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent);
6738c2ecf20Sopenharmony_ci	int ret;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
6768c2ecf20Sopenharmony_ci	if (!chg)
6778c2ecf20Sopenharmony_ci		return -ENOMEM;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, chg);
6808c2ecf20Sopenharmony_ci	chg->dev = &pdev->dev;
6818c2ecf20Sopenharmony_ci	chg->max77693 = max77693;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	ret = max77693_dt_init(&pdev->dev, chg);
6848c2ecf20Sopenharmony_ci	if (ret)
6858c2ecf20Sopenharmony_ci		return ret;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	ret = max77693_reg_init(chg);
6888c2ecf20Sopenharmony_ci	if (ret)
6898c2ecf20Sopenharmony_ci		return ret;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	psy_cfg.drv_data = chg;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer);
6948c2ecf20Sopenharmony_ci	if (ret) {
6958c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed: create fast charge timer sysfs entry\n");
6968c2ecf20Sopenharmony_ci		goto err;
6978c2ecf20Sopenharmony_ci	}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	ret = device_create_file(&pdev->dev,
7008c2ecf20Sopenharmony_ci			&dev_attr_top_off_threshold_current);
7018c2ecf20Sopenharmony_ci	if (ret) {
7028c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed: create top off current sysfs entry\n");
7038c2ecf20Sopenharmony_ci		goto err;
7048c2ecf20Sopenharmony_ci	}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	ret = device_create_file(&pdev->dev, &dev_attr_top_off_timer);
7078c2ecf20Sopenharmony_ci	if (ret) {
7088c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed: create top off timer sysfs entry\n");
7098c2ecf20Sopenharmony_ci		goto err;
7108c2ecf20Sopenharmony_ci	}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	chg->charger = power_supply_register(&pdev->dev,
7138c2ecf20Sopenharmony_ci						&max77693_charger_desc,
7148c2ecf20Sopenharmony_ci						&psy_cfg);
7158c2ecf20Sopenharmony_ci	if (IS_ERR(chg->charger)) {
7168c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed: power supply register\n");
7178c2ecf20Sopenharmony_ci		ret = PTR_ERR(chg->charger);
7188c2ecf20Sopenharmony_ci		goto err;
7198c2ecf20Sopenharmony_ci	}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	return 0;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_cierr:
7248c2ecf20Sopenharmony_ci	device_remove_file(&pdev->dev, &dev_attr_top_off_timer);
7258c2ecf20Sopenharmony_ci	device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current);
7268c2ecf20Sopenharmony_ci	device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	return ret;
7298c2ecf20Sopenharmony_ci}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_cistatic int max77693_charger_remove(struct platform_device *pdev)
7328c2ecf20Sopenharmony_ci{
7338c2ecf20Sopenharmony_ci	struct max77693_charger *chg = platform_get_drvdata(pdev);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	device_remove_file(&pdev->dev, &dev_attr_top_off_timer);
7368c2ecf20Sopenharmony_ci	device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current);
7378c2ecf20Sopenharmony_ci	device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	power_supply_unregister(chg->charger);
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	return 0;
7428c2ecf20Sopenharmony_ci}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_cistatic const struct platform_device_id max77693_charger_id[] = {
7458c2ecf20Sopenharmony_ci	{ "max77693-charger", 0, },
7468c2ecf20Sopenharmony_ci	{ }
7478c2ecf20Sopenharmony_ci};
7488c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, max77693_charger_id);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_cistatic struct platform_driver max77693_charger_driver = {
7518c2ecf20Sopenharmony_ci	.driver = {
7528c2ecf20Sopenharmony_ci		.name	= "max77693-charger",
7538c2ecf20Sopenharmony_ci	},
7548c2ecf20Sopenharmony_ci	.probe		= max77693_charger_probe,
7558c2ecf20Sopenharmony_ci	.remove		= max77693_charger_remove,
7568c2ecf20Sopenharmony_ci	.id_table	= max77693_charger_id,
7578c2ecf20Sopenharmony_ci};
7588c2ecf20Sopenharmony_cimodule_platform_driver(max77693_charger_driver);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ciMODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
7618c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Maxim 77693 charger driver");
7628c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
763