// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2023 MediaTek Inc.

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/version.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/mutex.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
#include <linux/regmap.h>

#define RT5738_DRV_VERSION	"1.1.0_MTK"

#define RT5738_REG_VSEL0	(0x00)
#define RT5738_REG_VSEL1	(0x01)
#define RT5738_REG_CTRL1	(0x02)
#define RT5738_REG_ID1		(0x03)
#define RT5738_REG_ID2		(0x04)
#define RT5738_REG_MONITOR	(0x05)
#define RT5738_REG_CTRL2	(0x06)
#define RT5738_REG_CTRL3	(0x07)
#define RT5738_REG_CTRL4	(0x08)

#define RT5738_VID_MASK		(0xe0)
#define RT5738_VID_SHIFT	(5)
#define RT5738_DID_MASK		(0x0f)
#define RT5738_DID_SHIFT	(0)

#define RT5738_INFO(format, args...) pr_info(format, ##args)
#define RT5738_ERR(format, args...)	pr_err(format, ##args)

#define RT5738_MIN_VOLTAGE	(300000)
#define RT5738_MAX_VOLTAGE	(1300000)
#define RT5738_VSEL_STEP	(5000)

#define RT5744_MIN_VOLTAGE	(270000)
#define RT5744_MAX_VOLTAGE	(1400000)
#define RT5744_VSEL_STEP	(6250)

#define rt5738_vsell_vol_reg	(RT5738_REG_VSEL0)
#define rt5738_vsell_vol_mask	(0xff)
#define rt5738_vsell_vol_shift	(0)
#define rt5738_vsell_enable_reg	(RT5738_REG_CTRL2)
#define rt5738_vsell_enable_mask	(0x01)
#define rt5738_vsell_enable_shift	(0x00)
#define rt5738_vsell_mode_reg	(RT5738_REG_CTRL1)
#define rt5738_vsell_mode_mask	(0x01)
#define rt5738_vsell_mode_shift	(0x00)

#define rt5738_vselh_vol_reg	(RT5738_REG_VSEL1)
#define rt5738_vselh_vol_mask	(0xff)
#define rt5738_vselh_vol_shift	(0)
#define rt5738_vselh_enable_reg	(RT5738_REG_CTRL2)
#define rt5738_vselh_enable_mask (0x02)
#define rt5738_vselh_enable_shift (0x01)
#define rt5738_vselh_mode_reg	(RT5738_REG_CTRL1)
#define rt5738_vselh_mode_mask	(0x02)
#define rt5738_vselh_mode_shift	(0x01)

#define BUCK_VEN_ENABLE  0
#define BUCK_VEN_DISABLE 1

#define rt5738_chip_data_decl(_name) \
{ \
	.vol_reg = _name##_vol_reg, \
	.vol_mask = _name##_vol_mask, \
	.vol_shift = _name##_vol_shift, \
	.enable_reg = _name##_enable_reg, \
	.enable_mask = _name##_enable_mask, \
	.enable_shift = _name##_enable_shift, \
	.mode_reg = _name##_mode_reg, \
	.mode_mask = _name##_mode_mask, \
	.mode_shift = _name##_mode_shift, \
	.ramp_up_reg = RT5738_REG_CTRL1, \
	.ramp_down_reg = RT5738_REG_CTRL2, \
	.ramp_up_mask = (0x70), \
	.ramp_down_mask = (0xe0), \
	.ramp_up_shift = 4, \
	.ramp_down_shift = 5, \
}

enum {
	VSEL_LOW_DEFAULT,
	VSEL_HIGH_DEFAULT,
	VSEL_ALWAYS_LOW,
	VSEL_ALWAYS_HIGH,
	VSEL_MAX,
};

struct rt5738_chip_data {
	unsigned char vol_reg;
	unsigned char vol_mask;
	unsigned char vol_shift;
	unsigned char enable_reg;
	unsigned char enable_mask;
	unsigned char enable_shift;
	unsigned char mode_reg;
	unsigned char mode_mask;
	unsigned char mode_shift;
	unsigned char ramp_up_reg;
	unsigned char ramp_up_mask;
	unsigned char ramp_up_shift;
	unsigned char ramp_down_reg;
	unsigned char ramp_down_mask;
	unsigned char ramp_down_shift;
};

static struct rt5738_chip_data rt5738_chip_data_map[VSEL_MAX] = {
	rt5738_chip_data_decl(rt5738_vsell),
	rt5738_chip_data_decl(rt5738_vselh),
};

struct rt5738_regulator_info {
	struct device *dev;
	struct regmap *regmap;
	struct i2c_client *i2c;
	struct rt5738_chip_data *chip_data;
	struct regulator_desc *desc;
	struct regulator_dev *regulator;
	int vsel_gpio;
	int enable_gpio;
	int ramp_up_val;
	int ramp_down_val;
};

static const struct regmap_config rt5738_regmap_config = {
	.reg_bits = 8,
	.val_bits = 8,
	.max_register = RT5738_REG_CTRL4,
};

#define RW_SYNC_WITH_MCU

#ifdef RW_SYNC_WITH_MCU
#define PMIC_SYNC_S		(0x0010BC00 + 0x02F8)   /* 190 */
#define OFFS_MCU		0	/* 190 */
#define OFFS_KR			4	/* 191 */
#define csram_read(offs)	__raw_readl(csram_base + (offs))
#define csram_write(offs, val)	__raw_writel(val, csram_base + (offs))
static void __iomem *csram_base;
#endif

static int __regmap_update_bits(struct regmap *map, unsigned int reg,
					unsigned int mask, unsigned int val)
{
	int ret = 0;
#ifdef RW_SYNC_WITH_MCU
	int flag = 0;

	do {
		flag = csram_read(OFFS_MCU);
		if (flag == 0) {
			csram_write(OFFS_KR, 1);
			udelay(20);
			break;
		}
	} while (flag);
	ret = regmap_update_bits(map, reg, mask, val);
	csram_write(OFFS_KR, 0);
#else
	ret = regmap_write_bits(map, reg, mask, val);
#endif
	return ret;
}

static int __regmap_write(struct regmap *map,
					unsigned int reg, unsigned int val)
{
	int ret = 0;
#ifdef RW_SYNC_WITH_MCU
	int flag = 0;

	do {
		flag = csram_read(OFFS_MCU);
		if (flag == 0) {
			csram_write(OFFS_KR, 1);
			udelay(20);
			break;
		}
	} while (flag);
	ret = regmap_write(map, reg, val);
	csram_write(OFFS_KR, 0);
#else
	ret = regmap_write(map, reg, val);
#endif
	return ret;
}

static int __regmap_read(struct regmap *map,
					unsigned int reg, unsigned int *val)
{
	int ret = 0;
#ifdef RW_SYNC_WITH_MCU
	int flag = 0;

	do {
		flag = csram_read(OFFS_MCU);
		if (flag == 0) {
			csram_write(OFFS_KR, 1);
			udelay(20);
			break;
		}
	} while (flag);
	ret = regmap_read(map, reg, val);
	csram_write(OFFS_KR, 0);
#else
	ret = regmap_read(map, reg, val);
#endif
	return ret;
}

static int rt5738_set_voltage_sel(struct regulator_dev *rdev,
							unsigned int selector)
{
	struct rt5738_regulator_info *info = rdev_get_drvdata(rdev);
	struct rt5738_chip_data *chip = info->chip_data;
	const int count = rdev->desc->n_voltages;

	if (selector > count)
		return -EINVAL;

	return __regmap_write(info->regmap, chip->vol_reg, selector);
}

static int rt5738_get_voltage_sel(struct regulator_dev *rdev)
{
	struct rt5738_regulator_info *info = rdev_get_drvdata(rdev);
	struct rt5738_chip_data *chip = info->chip_data;
	unsigned int regval = 0;
	int ret;

	ret = __regmap_read(info->regmap, chip->vol_reg, &regval);
	if (ret < 0)
		return ret;

	return regval;
}

static int rt5738_enable(struct regulator_dev *rdev)
{
	struct rt5738_regulator_info *info = rdev_get_drvdata(rdev);
	struct rt5738_chip_data *chip = info->chip_data;
	int ret = 0;

	if (info->enable_gpio >= 0) {
		gpio_set_value(info->enable_gpio, BUCK_VEN_ENABLE);
	} else
		ret = __regmap_update_bits(info->regmap, chip->enable_reg,
				  chip->enable_mask, 0x1 << chip->enable_shift);

	return ret;
}

static int rt5738_disable(struct regulator_dev *rdev)
{
	struct rt5738_regulator_info *info = rdev_get_drvdata(rdev);
	struct rt5738_chip_data *chip = info->chip_data;
	int ret = 0;

	if (info->enable_gpio >= 0) {
		gpio_set_value(info->enable_gpio, BUCK_VEN_DISABLE);
	} else
		ret = __regmap_update_bits(info->regmap, chip->enable_reg,
				  chip->enable_mask, 0x0 << chip->enable_shift);

	return ret;
}

static int rt5738_is_enabled(struct regulator_dev *rdev)
{
	struct rt5738_regulator_info *info = rdev_get_drvdata(rdev);
	struct rt5738_chip_data *chip = info->chip_data;
	unsigned int regval = 0;
	int ret;

	if (info->enable_gpio >= 0) {
		ret = gpio_get_value(info->enable_gpio);
		if (ret < 0)
			return ret;

		if (ret == BUCK_VEN_ENABLE)
			ret = 1;
		else
			ret = 0;
	} else {
		ret = __regmap_read(info->regmap, chip->enable_reg, &regval);
		if (ret < 0)
			return ret;

		ret = regval & chip->enable_mask ? 1 : 0;
	}
	return ret;
}

static int rt5738_set_mode(struct regulator_dev *rdev, unsigned int mode)
{
	struct rt5738_regulator_info *info = rdev_get_drvdata(rdev);
	struct rt5738_chip_data *chip = info->chip_data;
	int ret;
	unsigned int regval = 0;

	switch (mode) {
	case REGULATOR_MODE_FAST:	/* force pwm mode */
		ret = __regmap_update_bits(info->regmap,
					 chip->mode_reg, chip->mode_mask,
					 0x1 << chip->mode_shift);
		break;
	case REGULATOR_MODE_NORMAL:
	default:
		ret = __regmap_update_bits(info->regmap,
					 chip->mode_reg, chip->mode_mask,
					 0x0 << chip->mode_shift);
		break;
	}

	__regmap_read(info->regmap, chip->mode_reg, &regval);
	RT5738_INFO("%s: control1: %x\n", __func__, regval);

	return ret;
}

static unsigned int rt5738_get_mode(struct regulator_dev *rdev)
{
	struct rt5738_regulator_info *info = rdev_get_drvdata(rdev);
	struct rt5738_chip_data *chip = info->chip_data;
	int ret;
	unsigned int regval = 0;

	ret = __regmap_read(info->regmap, chip->mode_reg, &regval);
	if (ret < 0) {
		RT5738_ERR("%s read mode fail\n", __func__);
		return ret;
	}

	if (regval & chip->mode_mask)
		return REGULATOR_MODE_FAST;
	return REGULATOR_MODE_NORMAL;
}

static int rt5738_set_ramp_rate(struct regulator_dev *rdev)
{
	struct rt5738_regulator_info *info = rdev_get_drvdata(rdev);
	struct rt5738_chip_data *chip = info->chip_data;
	int ret;

	ret = __regmap_update_bits(info->regmap, chip->ramp_up_reg,
				 chip->ramp_up_mask,
				 info->ramp_up_val << chip->ramp_up_shift);

	if (ret < 0) {
		pr_err("%s_up fail, ret = %d\n", __func__, ret);
		return -EINVAL;
	}

	ret = __regmap_update_bits(info->regmap, chip->ramp_down_reg,
				 chip->ramp_down_mask,
				 info->ramp_down_val << chip->ramp_down_shift);
	if (ret < 0) {
		pr_err("%s_down fail, ret = %d\n", __func__, ret);
		return -EINVAL;
	}

	return 0;
}

static const struct regulator_ops rt5738_regulator_ops = {
	.list_voltage = regulator_list_voltage_linear,
	.set_voltage_sel = rt5738_set_voltage_sel,
	.get_voltage_sel = rt5738_get_voltage_sel,
	.enable = rt5738_enable,
	.disable = rt5738_disable,
	.is_enabled = rt5738_is_enabled,
	.set_mode = rt5738_set_mode,
	.get_mode = rt5738_get_mode,
};

static struct regulator_desc rt5738_regulator_desc = {
	.id = 0,
	.name = "rt5738",
	.min_uV = RT5738_MIN_VOLTAGE,
	.uV_step = RT5738_VSEL_STEP,
	.n_voltages = 201,
	.ops = &rt5738_regulator_ops,
	.type = REGULATOR_VOLTAGE,
	.owner = THIS_MODULE,
};

static struct regulator_desc rt5744_regulator_desc = {
	.id = 0,
	.name = "rt5744",
	.min_uV = RT5744_MIN_VOLTAGE,
	.uV_step = RT5744_VSEL_STEP,
	.n_voltages = 182,
	.ops = &rt5738_regulator_ops,
	.type = REGULATOR_VOLTAGE,
	.owner = THIS_MODULE,
};

static inline struct regulator_dev *rt5738_regulator_register(struct
						      regulator_desc
						      *desc,
						      struct device
						      *dev,
						      struct
						      regulator_init_data
						      *init_data,
						      void *driver_data)
{
	struct regulator_config config = {
		.dev = dev,
		.init_data = init_data,
		.driver_data = driver_data,
		.of_node = dev->of_node,
	};

	return devm_regulator_register(dev, desc, &config);
}

static int rt5738_parse_dt(struct device *dev,
			   struct rt5738_regulator_info *info)
{
	struct device_node *np = dev->of_node;
	u32 val;
	int ret = 0;

	if (!np) {
		RT5738_ERR("%s cant find node (0x%02x)\n",
			   __func__, info->i2c->addr);
		return -ENODEV;
	}

	ret = of_get_named_gpio(np, "rt,vsel_gpio", 0);
	if (ret < 0)
		pr_info("%s: no vsel_gpio info\n", __func__);
	info->vsel_gpio = ret;

	ret = of_get_named_gpio(np, "rt,enable_gpio", 0);
	if (ret < 0)
		pr_info("%s: no enable_gpio info\n", __func__);
	info->enable_gpio = ret;

	ret = of_property_read_u32(np, "ramp-up", &val);
	if (ret < 0) {
		pr_info("%s: no ramp-up info, use default rate\n", __func__);
		info->ramp_up_val = 1;	/* 001 : 12mV/us */
	} else
		info->ramp_up_val = val;

	ret = of_property_read_u32(np, "ramp-down", &val);
	if (ret < 0) {
		pr_info("%s: no ramp-down info, use default rate\n", __func__);
		info->ramp_down_val = 3;	/* 011 : 3mV/us */
	} else
		info->ramp_down_val = val;

	return 0;
}

static struct regulator_init_data *rt_parse_regulator_init_data(struct device
								*dev,
								const char
								*node_name)
{
	struct regulator_init_data *init_data;
	struct device_node *np = dev->of_node;

	init_data = of_get_regulator_init_data(dev, np, NULL);
	if (init_data) {
		dev_info(dev,
			 "regulator_name = %s, min_uV = %d, max_uV = %d\n",
			 init_data->constraints.name,
			 init_data->constraints.min_uV,
			 init_data->constraints.max_uV);
	} else {
		dev_err(dev, "no init data for %s\n", node_name);
		return NULL;
	}

	init_data->constraints.valid_modes_mask |=
	    (REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST);
	init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_MODE;

	return init_data;
}

static const struct of_device_id rt_match_table[] = {
	{.compatible = "richtek,rt5738_l", .data = (void *)VSEL_LOW_DEFAULT,},
	{.compatible = "richtek,rt5738_h", .data = (void *)VSEL_HIGH_DEFAULT,},
	{.compatible = "richtek,rt5738l", .data = (void *)VSEL_ALWAYS_LOW,},
	{.compatible = "richtek,rt5738h", .data = (void *)VSEL_ALWAYS_HIGH,},
	{.compatible = "richtek,rt5744_l", .data = (void *)VSEL_LOW_DEFAULT,},
	{.compatible = "richtek,rt5744_h", .data = (void *)VSEL_HIGH_DEFAULT,},
	{.compatible = "richtek,rt5744l", .data = (void *)VSEL_ALWAYS_LOW,},
	{.compatible = "richtek,rt5744h", .data = (void *)VSEL_ALWAYS_HIGH,},
	{},
};

MODULE_DEVICE_TABLE(of, rt_match_table);

static const struct i2c_device_id rt_dev_id[] = {
	{"rt5738_l", (kernel_ulong_t) VSEL_LOW_DEFAULT,},
	{"rt5738_h", (kernel_ulong_t) VSEL_HIGH_DEFAULT,},
	{"rt5738l", (kernel_ulong_t) VSEL_ALWAYS_LOW,},
	{"rt5738h", (kernel_ulong_t) VSEL_ALWAYS_HIGH,},
	{"rt5744_l", (kernel_ulong_t) VSEL_LOW_DEFAULT,},
	{"rt5744_h", (kernel_ulong_t) VSEL_HIGH_DEFAULT,},
	{"rt5744l", (kernel_ulong_t) VSEL_ALWAYS_LOW,},
	{"rt5744h", (kernel_ulong_t) VSEL_ALWAYS_HIGH,},
	{},
};

MODULE_DEVICE_TABLE(i2c, rt_dev_id);

static int rt5738_i2c_probe(struct i2c_client *i2c,
			    const struct i2c_device_id *id)
{
	struct rt5738_regulator_info *info;
	struct regulator_init_data *init_data = NULL;
	const struct of_device_id *of_id;
	const char *name;
	long dev_id;
	int ret;

	pr_info("%s ver(%s) slv(0x%02x)\n",
		    __func__, RT5738_DRV_VERSION, i2c->addr);

	info = devm_kzalloc(&i2c->dev,
			    sizeof(struct rt5738_regulator_info), GFP_KERNEL);
	if (!info)
		return -ENOMEM;

#ifdef RW_SYNC_WITH_MCU
	csram_base = ioremap(PMIC_SYNC_S, 8);
	if (csram_base == NULL) {
		dev_info(&i2c->dev, "[%s] ioremap failed\n", __func__);
		return -ENOMEM;
	}
#endif

	info->regmap = devm_regmap_init_i2c(i2c, &rt5738_regmap_config);
	if (IS_ERR(info->regmap)) {
		ret = PTR_ERR(info->regmap);
		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
			ret);
		return ret;
	}

	if (i2c->dev.of_node) {
		of_id = of_match_node(rt_match_table, i2c->dev.of_node);
		if (!of_id)
			return -EINVAL;
		dev_id = (long)of_id->data;
		name = of_id->compatible;
	} else {
		dev_id = (long)id->driver_data;
		name = id->name;
	}

	if (dev_id == VSEL_ALWAYS_HIGH || dev_id == VSEL_LOW_DEFAULT)
		info->chip_data = &rt5738_chip_data_map[1];
	else
		info->chip_data = &rt5738_chip_data_map[0];

	init_data = rt_parse_regulator_init_data(&i2c->dev, "rt5738-buck");
	if (init_data == NULL) {
		dev_err(&i2c->dev, "no init data\n");
		return -EINVAL;
	}

	info->i2c = i2c;
	info->dev = &i2c->dev;
	if (strstr(name, "rt5744") != NULL)
		info->desc = &rt5744_regulator_desc;
	else
		info->desc = &rt5738_regulator_desc;
	i2c_set_clientdata(i2c, info);

	ret = rt5738_parse_dt(&i2c->dev, info);
	if (ret < 0) {
		RT5738_ERR("%s parse dt (%x) fail\n", __func__, i2c->addr);
		return ret;
	}

	info->regulator = rt5738_regulator_register(info->desc,
						    &i2c->dev, init_data, info);
	if (IS_ERR(info->regulator)) {
		dev_err(&i2c->dev, "fail to register rt5738 regulator : %s\n",
			i2c->dev.of_node->name);
		return ret;
	}

	ret = rt5738_set_ramp_rate(info->regulator);
	if (ret < 0) {
		dev_err(&i2c->dev, "%s : fail to set ramp rate\n", __func__);
		return ret;
	}

	/* if compatible = VSEL_LOW_DEFAULT, default = VSEL_L,
	 * DVFS use VSEL_H, and vice visa.
	 */
	if (dev_id == VSEL_LOW_DEFAULT)
		devm_gpio_request_one(info->dev, info->vsel_gpio,
				      GPIOF_OUT_INIT_HIGH, "rt5738-VSEL");
	else if (dev_id == VSEL_HIGH_DEFAULT)
		devm_gpio_request_one(info->dev, info->vsel_gpio,
				      GPIOF_OUT_INIT_LOW, "rt5738-VSEL");

	if (info->enable_gpio >= 0) {
		ret = gpio_request(info->enable_gpio, "rt5738-enable");

		if (ret < 0) {
			dev_err(&i2c->dev,
				"%s : fail to request enable gpio\n", __func__);
			return ret;
		}

		gpio_direction_output(info->enable_gpio, BUCK_VEN_ENABLE);
	}

	pr_info("%s Successfully\n", __func__);

	return 0;
}

static int rt5738_i2c_remove(struct i2c_client *i2c)
{
	struct rt5738_regulator_info *info = i2c_get_clientdata(i2c);

	regulator_unregister(info->regulator);
	return 0;
}

static void rt5738_i2c_shutdown(struct i2c_client *i2c)
{
	struct rt5738_regulator_info *info = i2c_get_clientdata(i2c);
	int ret = 0;

	if (info == NULL)
		return;

	ret = regmap_write(info->regmap, RT5738_REG_CTRL4, 0x08);
	if (ret < 0)
		dev_err(&i2c->dev, "[%#x] Failed to enable power off sequence. %d\n", ret);
}

static struct i2c_driver rt5738_i2c_driver = {
	.driver = {
		   .name = "rt5738",
		   .owner = THIS_MODULE,
		   .of_match_table = rt_match_table,
		   },
	.probe = rt5738_i2c_probe,
	.remove = rt5738_i2c_remove,
	.shutdown = rt5738_i2c_shutdown,
	.id_table = rt_dev_id,
};

module_i2c_driver(rt5738_i2c_driver);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("gene_chen <gene_chen@richtek.com>");
MODULE_AUTHOR("menghui lin <menghui.lin@mediatek.com>");
MODULE_VERSION("1.1.0_MTK");
MODULE_DESCRIPTION("Regulator driver for RT5738");
