// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2022 MediaTek Inc.
 */

#include <linux/bits.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/linear_range.h>
#include <linux/module.h>
#include <linux/power_supply.h>
#include <linux/property.h>
#include <linux/regmap.h>

#include "charger_class.h"
#include "mtk_charger.h"

static bool dbg_log_en;
module_param(dbg_log_en, bool, 0644);
#define mt_dbg(dev, fmt, ...) \
	do { \
		if (dbg_log_en) \
			dev_info(dev, "%s " fmt, __func__, ##__VA_ARGS__); \
	} while (0)
#define RT9758_REG_DEVINFO	0x00
#define RT9758_REG_FLAG1	0x01
#define RT9758_REG_FLAG2	0x02
#define RT9758_REG_FLAG3	0x03
#define RT9758_REG_ICSTAT	0x04
#define RT9758_REG_STAT1	0x08
#define RT9758_REG_CTRL1	0x0A
#define RT9758_REG_CTRL2	0x0B
#define RT9758_REG_CTRL3	0x0C
#define RT9758_REG_CTRL4	0x0D
#define RT9758_REG_CTRL5	0x0E
#define RT9758_REG_CTRL6	0x0F
#define RT9758_REG_CTRL7	0x10
#define RT9758_REG_CTRL8	0x11
#define RT9758_REG_CTRL9	0x12
#define RT9758_REG_CTRL10	0x13

#define RT9758_DEVID_MASK	GENMASK(3, 0)
#define RT9758_WRXIN_MASK	BIT(2)

#define RT9758_DEVICE_ID	0x03
#define RT9758_RSTRG_VAL	BIT(2)
#define RT9758_RESET_WAITUS	1000
#define RT9758_Q0CTRL_OFF	0
#define RT9758_Q0CTRL_ON	2

enum {
	RT9758_SYNC_OFF = 0,
	RT9758_SYNC_MASTER,
	RT9758_SYNC_SLAVE = 3,
	RT9758_MAX_SYNC
};

enum {
	RT9758_PRESENT_MODE = 0,
	RT9758_STANDBY_MODE,
	RT9758_FORWARD_DIV2_MODE,
	RT9758_FORWARD_BYPASS_MODE,
	RT9758_REVERSE_DIV2_MODE,
	RT9758_REVERSE_BYPASS_MODE,
	RT9758_CHARGE_FAULT,
};

enum rt9758_fields {
	F_IC_STAT = 0,
	F_VLERR_STAT,
	F_WRXIN_STAT,
	F_SWITCH_STAT,
	F_VOUT_OVP,
	F_VBUS_OVP,
	F_IBUS_OCP,
	F_SYNC_MODE,
	F_Q0_CTRL,
	F_WDT,
	F_WDT_EN,
	F_CHG_EN,
	F_OP_MODE,
	F_ATEN,
	F_BA_WDT,
	F_TDIE_EN,
	F_MAX_FIELDS
};

enum {
	RT9758_RANGE_VOUTOVP = 0,
	RT9758_RANGE_VBUSOVP,
	RT9758_RANGE_IBUSOCP,
	RT9758_MAX_RANGES
};

enum rt9758_chg_dtprop_type {
	DTPROP_U32,
	DTPROP_BOOL,
};

struct rt9758_priv {
	struct device *dev;
	struct regmap *regmap;
	struct regmap_field *rm_field[F_MAX_FIELDS];
	struct gpio_desc *enable_gpio;
	struct power_supply *psy;
	struct power_supply_desc desc;
	struct charger_device *chg_dev;
	const char *chg_name;
	struct charger_properties chg_prop;
	bool charge_enabled;
	bool bypass_enabled;
};

static struct reg_field rt9758_reg_fields[F_MAX_FIELDS] = {
	[F_IC_STAT]	= REG_FIELD(RT9758_REG_ICSTAT, 0, 2),
	[F_VLERR_STAT]	= REG_FIELD(RT9758_REG_STAT1, 3, 3),
	[F_WRXIN_STAT]	= REG_FIELD(RT9758_REG_STAT1, 2, 2),
	[F_SWITCH_STAT]	= REG_FIELD(RT9758_REG_STAT1, 1, 1),
	[F_VOUT_OVP]	= REG_FIELD(RT9758_REG_CTRL1, 0, 2),
	[F_VBUS_OVP]	= REG_FIELD(RT9758_REG_CTRL2, 0, 5),
	[F_IBUS_OCP]	= REG_FIELD(RT9758_REG_CTRL3, 4, 7),
	[F_SYNC_MODE]	= REG_FIELD(RT9758_REG_CTRL4, 2, 3),
	[F_Q0_CTRL]	= REG_FIELD(RT9758_REG_CTRL4, 0, 1),
	[F_WDT]		= REG_FIELD(RT9758_REG_CTRL5, 4, 6),
	[F_WDT_EN]	= REG_FIELD(RT9758_REG_CTRL5, 3, 3),
	[F_CHG_EN]	= REG_FIELD(RT9758_REG_CTRL6, 2, 2),
	[F_OP_MODE]	= REG_FIELD(RT9758_REG_CTRL6, 0, 0),
	[F_ATEN]	= REG_FIELD(RT9758_REG_CTRL7, 3, 3),
	[F_BA_WDT]	= REG_FIELD(RT9758_REG_CTRL8, 0, 0),
	[F_TDIE_EN]	= REG_FIELD(RT9758_REG_CTRL9, 5, 5)
};

static const struct linear_range rt9758_ranges[RT9758_MAX_RANGES] = {
	[RT9758_RANGE_VOUTOVP] = { 7000000, 0, 7, 1000000 },
	[RT9758_RANGE_VBUSOVP] = { 7250000, 0, 59, 250000 },
	[RT9758_RANGE_IBUSOCP] = { 2000000, 2, 10, 500000 }
};

static int rt9758_get_status(struct rt9758_priv *priv,
			     union power_supply_propval *val)
{
	unsigned int switching;
	int ret;

	ret = regmap_field_read(priv->rm_field[F_SWITCH_STAT], &switching);
	if (ret)
		return ret;

	if (switching)
		val->intval = POWER_SUPPLY_STATUS_CHARGING;
	else
		val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;

	return 0;
}

static int rt9758_get_charge_type(struct rt9758_priv *priv,
				  union power_supply_propval *val)
{
	if (!priv->charge_enabled)
		val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
	else if (!priv->bypass_enabled)
		val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
	else
		val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;

	return 0;
}

static int rt9758_get_online(struct rt9758_priv *priv,
			     union power_supply_propval *val)
{
	unsigned int wrxin_stat;
	int ret;

	ret = regmap_field_read(priv->rm_field[F_WRXIN_STAT], &wrxin_stat);
	if (ret)
		return ret;

	val->intval = wrxin_stat;
	return 0;
}

static int rt9758_get_const_charge_volt(struct rt9758_priv *priv,
					union power_supply_propval *val)
{
	const struct linear_range *range = rt9758_ranges + RT9758_RANGE_VOUTOVP;
	unsigned int vout_ovp_sel, vout_ovp_val;
	int ret;

	ret = regmap_field_read(priv->rm_field[F_VOUT_OVP], &vout_ovp_sel);
	if (ret)
		return ret;

	ret = linear_range_get_value(range, vout_ovp_sel, &vout_ovp_val);
	if (ret)
		return ret;

	val->intval = vout_ovp_val;
	return 0;
}

static int rt9758_get_input_curr_lim(struct rt9758_priv *priv,
				     union power_supply_propval *val)
{
	const struct linear_range *range = rt9758_ranges + RT9758_RANGE_IBUSOCP;
	unsigned int ibus_ocp_sel, ibus_ocp_val;
	int ret;

	ret = regmap_field_read(priv->rm_field[F_IBUS_OCP], &ibus_ocp_sel);
	if (ret)
		return ret;

	ret = linear_range_get_value(range, ibus_ocp_sel, &ibus_ocp_val);
	if (ret)
		return ret;

	val->intval = ibus_ocp_val;
	return 0;
}

static int rt9758_get_input_volt_lim(struct rt9758_priv *priv,
				     union power_supply_propval *val)
{
	const struct linear_range *range = rt9758_ranges + RT9758_RANGE_VBUSOVP;
	unsigned int vbus_ovp_sel, vbus_ovp_val;
	int ret;

	ret = regmap_field_read(priv->rm_field[F_VBUS_OVP], &vbus_ovp_sel);
	if (ret)
		return ret;

	ret = linear_range_get_value(range, vbus_ovp_sel, &vbus_ovp_val);
	if (ret)
		return ret;

	val->intval = vbus_ovp_val;
	return 0;
}

static int rt9758_get_manufacturer(struct rt9758_priv *priv,
				   union power_supply_propval *val)
{
	val->strval = "Richtek Technology Corp.";
	return 0;
}

static int rt9758_set_charge_enable(struct rt9758_priv *priv,
				    const union power_supply_propval *val)
{
	unsigned int q0ctrl_val, chgen;
	int ret;

	chgen = !!val->intval;
	if (chgen)
		q0ctrl_val = RT9758_Q0CTRL_ON;
	else
		q0ctrl_val = RT9758_Q0CTRL_OFF;

	ret = regmap_field_write(priv->rm_field[F_Q0_CTRL], q0ctrl_val);
	if (ret)
		return ret;

	ret = regmap_field_write(priv->rm_field[F_CHG_EN], chgen);
	if (ret)
		return ret;

	ret = regmap_field_write(priv->rm_field[F_WDT_EN], chgen);
	if (ret)
		return ret;

	if (priv->enable_gpio)
		gpiod_set_value(priv->enable_gpio, !chgen);
	mdelay(1);

	priv->charge_enabled = chgen;

	return 0;
}

static int rt9758_set_bypass_enable(struct rt9758_priv *priv,
				    const union power_supply_propval *val)
{
	unsigned int bypass_enabled;
	int ret;

	bypass_enabled = !!val->intval;
	ret = regmap_field_write(priv->rm_field[F_OP_MODE], bypass_enabled);
	if (ret)
		return ret;

	priv->bypass_enabled = bypass_enabled;
	return regmap_field_write(priv->rm_field[F_WDT], 0);
}

static int rt9758_set_const_charge_volt(struct rt9758_priv *priv,
				    const union power_supply_propval *val)
{
	const struct linear_range *range = rt9758_ranges + RT9758_RANGE_VOUTOVP;
	unsigned int sel = 0;

	linear_range_get_selector_within(range, val->intval, &sel);
	return regmap_field_write(priv->rm_field[F_VOUT_OVP], sel);
}

static int rt9758_set_input_curr_lim(struct rt9758_priv *priv,
				     const union power_supply_propval *val)
{
	const struct linear_range *range = rt9758_ranges + RT9758_RANGE_IBUSOCP;
	unsigned int sel = 0;

	linear_range_get_selector_within(range, val->intval, &sel);
	return regmap_field_write(priv->rm_field[F_IBUS_OCP], sel);
}

static int rt9758_set_input_volt_lim(struct rt9758_priv *priv,
				     const union power_supply_propval *val)
{
	const struct linear_range *range = rt9758_ranges + RT9758_RANGE_VBUSOVP;
	unsigned int sel = 0;

	linear_range_get_selector_within(range, val->intval, &sel);
	return regmap_field_write(priv->rm_field[F_VBUS_OVP], sel);
}

static int rt9758_charger_get_property(struct power_supply *psy,
				       enum power_supply_property psp,
				       union power_supply_propval *val)
{
	struct rt9758_priv *priv = power_supply_get_drvdata(psy);

	if (!priv)
		return -ENODATA;

	switch (psp) {
	case POWER_SUPPLY_PROP_STATUS:
		return rt9758_get_status(priv, val);
	case POWER_SUPPLY_PROP_CHARGE_TYPE:
		return rt9758_get_charge_type(priv, val);
	case POWER_SUPPLY_PROP_ONLINE:
		return rt9758_get_online(priv, val);
	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
		return rt9758_get_const_charge_volt(priv, val);
	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
		return rt9758_get_input_curr_lim(priv, val);
	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
		return rt9758_get_input_volt_lim(priv, val);
	case POWER_SUPPLY_PROP_MANUFACTURER:
		return rt9758_get_manufacturer(priv, val);
	default:
		return -ENODATA;
	}
}

static int rt9758_charger_set_property(struct power_supply *psy,
				       enum power_supply_property psp,
				       const union power_supply_propval *val)
{
	struct rt9758_priv *priv = power_supply_get_drvdata(psy);

	if (!priv)
		return -ENODATA;

	switch (psp) {
	case POWER_SUPPLY_PROP_STATUS:
		return rt9758_set_charge_enable(priv, val);
	case POWER_SUPPLY_PROP_CHARGE_TYPE:
		return rt9758_set_bypass_enable(priv, val);
	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
		return rt9758_set_const_charge_volt(priv, val);
	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
		return rt9758_set_input_curr_lim(priv, val);
	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
		return rt9758_set_input_volt_lim(priv, val);
	default:
		return -EINVAL;
	}
}

static const enum power_supply_property rt9758_charger_properties[] = {
	POWER_SUPPLY_PROP_STATUS,
	POWER_SUPPLY_PROP_CHARGE_TYPE,
	POWER_SUPPLY_PROP_ONLINE,
	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
	POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
	POWER_SUPPLY_PROP_MANUFACTURER
};

static int rt9758_init_chg_properties(struct rt9758_priv *priv)
{
	unsigned int sync_mode = RT9758_SYNC_OFF;
	int ret;

	ret = regmap_write(priv->regmap, RT9758_REG_CTRL9, RT9758_RSTRG_VAL);
	if (ret)
		return ret;

	usleep_range(RT9758_RESET_WAITUS, RT9758_RESET_WAITUS + 100);

	/* mediatek chgdev name */
	ret = device_property_read_string(priv->dev, "chg_name",
					  &priv->chg_name);
	if (ret) {
		dev_notice(priv->dev, "failed to get chg_name\n");
		priv->chg_name = "hvdiv2_chg1";
	}

	device_property_read_u32(priv->dev, "richtek,dv2-sync-mode",
				 &sync_mode);

	if (sync_mode >= RT9758_MAX_SYNC) {
		dev_err(priv->dev, "Not valid mode [%d]\n", sync_mode);
		return -EINVAL;
	}

	ret = regmap_field_write(priv->rm_field[F_BA_WDT], 0);
	if (ret)
		return ret;

	ret = regmap_field_write(priv->rm_field[F_SYNC_MODE], sync_mode);
	if (ret)
		return ret;

	ret = regmap_field_write(priv->rm_field[F_Q0_CTRL], RT9758_Q0CTRL_OFF);
	if (ret)
		return ret;

	return regmap_field_write(priv->rm_field[F_CHG_EN], 0);
}

static int rt9758_check_device_info(struct rt9758_priv *priv)
{
	unsigned int dev_info;
	int ret;

	ret = regmap_read(priv->regmap, RT9758_REG_DEVINFO, &dev_info);
	if (ret)
		return ret;

	if ((dev_info & RT9758_DEVID_MASK) != RT9758_DEVICE_ID) {
		dev_err(priv->dev, "Failed to match devid 0x%02x\n", dev_info);
		return -ENODEV;
	}

	dev_info(priv->dev, "devid = 0x%02X\n", dev_info);
	return 0;
}

static int rt9758_psy_set_prop(struct rt9758_priv *priv,
			       enum power_supply_property psp, uint32_t data)
{
	union power_supply_propval val;

	val.intval = data;
	return power_supply_set_property(priv->psy, psp, &val);
}

static int rt9758_enable_chg(struct charger_device *chg_dev, bool en)
{
	uint32_t set_val;
	struct rt9758_priv *priv = charger_get_data(chg_dev);

	dev_info(priv->dev, "%s %d\n", __func__, en);
	set_val = en;
	return rt9758_psy_set_prop(priv, POWER_SUPPLY_PROP_STATUS, set_val);
}

static int rt9758_is_chg_enabled(struct charger_device *chg_dev, bool *en)
{
	int ret;
	unsigned int get_val;
	struct rt9758_priv *priv = charger_get_data(chg_dev);

	ret = regmap_field_read(priv->rm_field[F_CHG_EN], &get_val);
	if (ret)
		return ret;

	*en = get_val;
	dev_info(priv->dev, "%s %d\n", __func__, *en);
	return 0;
}

static int rt9758_init_chip(struct charger_device *chg_dev)
{
	return 0;
}

static int rt9758_set_vbusovp(struct charger_device *chg_dev, uint32_t uV)
{
	uint32_t set_val;
	struct rt9758_priv *priv = charger_get_data(chg_dev);

	dev_info(priv->dev, "%s %d\n", __func__, uV);
	set_val = uV;
	return rt9758_psy_set_prop(priv, POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
				   set_val);
}

static int rt9758_set_ibusocp(struct charger_device *chg_dev, uint32_t uA)
{
	uint32_t set_val;
	struct rt9758_priv *priv = charger_get_data(chg_dev);

	dev_info(priv->dev, "%s %d\n", __func__, uA);
	set_val = uA;
	return rt9758_psy_set_prop(priv, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
				   set_val);
}

static int rt9758_set_voutovp(struct charger_device *chg_dev, uint32_t uV)
{
	uint32_t set_val;
	struct rt9758_priv *priv = charger_get_data(chg_dev);

	dev_info(priv->dev, "%s %d\n", __func__, uV);
	set_val = uV;
	return rt9758_psy_set_prop(priv,
				   POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
				   set_val);
}

static int rt9758_is_vbuslowerr(struct charger_device *chg_dev, bool *err)
{
	int ret;
	unsigned int get_val;
	struct rt9758_priv *priv = charger_get_data(chg_dev);

	ret = regmap_field_read(priv->rm_field[F_VLERR_STAT], &get_val);
	if (ret)
		return ret;

	*err = get_val;
	dev_info(priv->dev, "%s %d\n", __func__, *err);
	return 0;
}

static int rt9758_enable_auto_trans(struct charger_device *chg_dev, bool en)
{
	uint32_t set_val;
	struct rt9758_priv *priv = charger_get_data(chg_dev);

	dev_info(priv->dev, "%s %d\n", __func__, en);
	set_val = en;
	return regmap_field_write(priv->rm_field[F_ATEN], set_val);
}

static int rt9758_set_auto_trans(struct charger_device *chg_dev, uint32_t uV,
				 bool en)
{
	return 0;
}

static int rt9758_operation_mode_select(struct charger_device *chg_dev,
					bool div2)
{
	unsigned int set_val;
	struct rt9758_priv *priv = charger_get_data(chg_dev);

	dev_info(priv->dev, "div2 = %d\n", div2);
	set_val = div2;
	return rt9758_psy_set_prop(priv, POWER_SUPPLY_PROP_CHARGE_TYPE,
				   set_val);
}

#define DUMP_REG_BUF_SIZE	1024
static int rt9758_dump_registers(struct charger_device *chg_dev)
{
	struct rt9758_priv *priv = charger_get_data(chg_dev);
	int ret, i;
	u32 val;
	char buf[DUMP_REG_BUF_SIZE] = "\0";
	static const struct {
		const u8 reg;
		const char *name;
	} regs[] = {
		{ .reg = RT9758_REG_FLAG1, .name = "FLAG1"},
		{ .reg = RT9758_REG_FLAG2, .name = "FLAG2"},
		{ .reg = RT9758_REG_FLAG3, .name = "FLAG3"},
		{ .reg = RT9758_REG_ICSTAT, .name = "IC_STAT"},
		{ .reg = RT9758_REG_CTRL1, .name = "CTRL1"},
		{ .reg = RT9758_REG_CTRL2, .name = "CTRL2"},
		{ .reg = RT9758_REG_CTRL3, .name = "CTRL3"},
		{ .reg = RT9758_REG_CTRL4, .name = "CTRL4"},
		{ .reg = RT9758_REG_CTRL5, .name = "CTRL5"},
		{ .reg = RT9758_REG_CTRL6, .name = "CTRL6"},
	};

	for (i = 0; i < ARRAY_SIZE(regs); i++) {
		ret = regmap_read(priv->regmap, regs[i].reg, &val);
		if (ret) {
			dev_err(priv->dev, "failed to get %s\n", regs[i].name);
			return ret;
		}
		if (i == ARRAY_SIZE(regs) - 1)
			scnprintf(buf + strlen(buf), DUMP_REG_BUF_SIZE,
				  "%s = 0x%02X\n", regs[i].name, val);
		else
			scnprintf(buf + strlen(buf), DUMP_REG_BUF_SIZE,
				  "%s = 0x%02X, ", regs[i].name, val);
	}

	dev_info(priv->dev, "%s %s\n", __func__, buf);
	dev_info(priv->dev, "%s enable_gpio = %d\n", __func__,
		 gpiod_get_value(priv->enable_gpio));
	return 0;
}

static const struct charger_ops rt9758_chg_ops = {
	.enable = rt9758_enable_chg,
	.is_enabled = rt9758_is_chg_enabled,
	.init_chip = rt9758_init_chip,
	.set_vbusovp = rt9758_set_vbusovp,
	.set_ibusocp = rt9758_set_ibusocp,
	.set_vbatovp = rt9758_set_voutovp,
	.is_vbuslowerr = rt9758_is_vbuslowerr,
	.enable_auto_trans = rt9758_enable_auto_trans,
	.set_auto_trans = rt9758_set_auto_trans,
	.set_operation_mode = rt9758_operation_mode_select,
	.dump_registers = rt9758_dump_registers,
};

static const struct regmap_config rt9758_regmap_config = {
	.reg_bits = 8,
	.val_bits = 8,
	.max_register = RT9758_REG_CTRL10,
};

static int rt9758_charger_probe(struct i2c_client *i2c)
{
	struct rt9758_priv *priv;
	struct power_supply_config cfg = {};
	int ret;

	priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	priv->dev = &i2c->dev;
	i2c_set_clientdata(i2c, priv);

	priv->regmap = devm_regmap_init_i2c(i2c, &rt9758_regmap_config);
	if (IS_ERR(priv->regmap)) {
		ret = PTR_ERR(priv->regmap);
		dev_err(&i2c->dev, "Failed to init regmap [%d]\n", ret);
		return ret;
	}

	ret = devm_regmap_field_bulk_alloc(&i2c->dev, priv->regmap,
					   priv->rm_field, rt9758_reg_fields,
					   ARRAY_SIZE(rt9758_reg_fields));
	if (ret) {
		dev_err(&i2c->dev, "Failed to alloc regmap fields\n");
		return ret;
	}

	ret = rt9758_check_device_info(priv);
	if (ret) {
		dev_err(&i2c->dev, "Failed to check device info\n");
		return ret;
	}

	/* If specified, initial control 'enable' high to enter lowest IQ */
	priv->enable_gpio = devm_gpiod_get_optional(&i2c->dev, "enable",
						    GPIOD_OUT_HIGH);
	if (IS_ERR(priv->enable_gpio)) {
		dev_err(&i2c->dev, "Failed to init enable gpio\n");
		return PTR_ERR(priv->enable_gpio);
	}

	ret = rt9758_init_chg_properties(priv);
	if (ret) {
		dev_err(&i2c->dev, "Failed to init charger properties\n");
		return ret;
	}

	priv->desc.name = dev_name(&i2c->dev);
	priv->desc.type = POWER_SUPPLY_TYPE_UNKNOWN;
	priv->desc.properties = rt9758_charger_properties;
	priv->desc.num_properties = ARRAY_SIZE(rt9758_charger_properties);
	priv->desc.get_property = rt9758_charger_get_property;
	priv->desc.set_property = rt9758_charger_set_property;

	cfg.of_node = i2c->dev.of_node;
	cfg.drv_data = priv;

	priv->psy = devm_power_supply_register(&i2c->dev, &priv->desc, &cfg);
	if (IS_ERR(priv->psy)) {
		dev_err(&i2c->dev, "Failed to register psy\n");
		return PTR_ERR(priv->psy);
	}

	priv->chg_prop.alias_name = priv->chg_name;
	priv->chg_dev = charger_device_register(priv->chg_name, priv->dev,
						priv, &rt9758_chg_ops,
						&priv->chg_prop);
	if (IS_ERR(priv->chg_dev)) {
		dev_err(&i2c->dev, "Failed to register chgdev\n");
		return PTR_ERR(priv->chg_dev);
	}

	dev_info(&i2c->dev, "%s ok\n", __func__);
	return 0;
}

static void rt9758_charger_remove(struct i2c_client *i2c)
{
	struct rt9758_priv *priv = i2c_get_clientdata(i2c);

	charger_device_unregister(priv->chg_dev);
}

static const struct of_device_id rt9758_charger_of_match_table[] = {
	{ .compatible = "richtek,rt9758", },
	{ }
};
MODULE_DEVICE_TABLE(of, rt9758_charger_of_match_table);

static struct i2c_driver rt9758_charger_driver = {
	.driver = {
		.name = "rt9758-charger",
		.of_match_table = rt9758_charger_of_match_table,
	},
	.probe_new = rt9758_charger_probe,
	.remove = rt9758_charger_remove,
};
module_i2c_driver(rt9758_charger_driver);

MODULE_DESCRIPTION("Richtek RT9758 charger driver");
MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
MODULE_LICENSE("GPL");
