blob: 8c6019ff6493ecaf44cb8be40ed743fa921c9652 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
/* Register definitions */
#define RT5739_REG_VSEL0 0x00
#define RT5739_REG_VSEL1 0x01
#define RT5739_REG_CONTROL1 0x02
#define RT5739_REG_ID1 0x03
#define RT5739_REG_ID2 0x04
#define RT5739_REG_MONITOR 0x05
#define RT5739_REG_CONTROL2 0x06
#define RT5739_REG_CONTROL4 0x08
#define RT5739_NUM_REGS (RT5739_REG_CONTROL4 + 1)
#define RT5739_VSEL_MASK GENMASK(7, 0)
#define RT5739_VSEL0_FPWM_MASK BIT(0)
#define RT5739_VSEL1_FPWM_MASK BIT(1)
#define RT5739_UP_RAMP_DELAY_MASK GENMASK(6, 4)
#define RT5739_UP_RAMP_DELAY_SHIFT 4
#define RT5739_DN_RAMP_DELAY_MASK GENMASK(7, 5)
#define RT5739_DN_RAMP_DELAY_SHIFT 5
#define RT5739_RAMP_DELAY_24000UV_PER_US 0x0
#define RT5739_RAMP_DELAY_12000UV_PER_US 0x1
#define RT5739_RAMP_DELAY_6000UV_PER_US 0x2
#define RT5739_RAMP_DELAY_3000UV_PER_US 0x3
#define RT5739_RAMP_DELAY_1500UV_PER_US 0x4
#define RT5739_RAMP_DELAY_750UV_PER_US 0x5
#define RT5739_RAMP_DELAY_375UV_PER_US 0x6
#define RT5739_RAMP_DELAY_187UV_PER_US 0x7
/* Output voltage settings */
#define RT5739_VOUT_MINUV 300000
#define RT5739_VOUT_MAXUV 1300000
#define RT5739_VOUT_STPUV 5000
#define RT5739_N_VOUTS ((RT5739_VOUT_MAXUV - RT5739_VOUT_MINUV) / RT5739_VOUT_STPUV + 1)
#define RT5739_MODE_AUTO 0
#define RT5739_MODE_FORCED_PWM 1
struct rt5739_priv {
struct regulator_desc desc;
struct regmap *regmap;
};
static int rt5739_dcdc_get_voltage_sel(struct regulator_dev *rdev)
{
struct rt5739_priv *priv = rdev_get_drvdata(rdev);
unsigned int data = 0;
unsigned int vsel_reg;
int ret;
if (priv->desc.vsel_reg == RT5739_REG_VSEL0)
vsel_reg = RT5739_REG_VSEL0;
else
vsel_reg = RT5739_REG_VSEL1;
ret = regmap_read(priv->regmap, vsel_reg, &data);
if (ret < 0) {
dev_err(&rdev->dev, "%s(): register %d read failed with err %d\n",
__func__, vsel_reg, ret);
return ret;
}
return (int)(data & RT5739_VSEL_MASK);
}
static int rt5739_dcdc_set_voltage_sel(struct regulator_dev *rdev,
unsigned int selector)
{
struct rt5739_priv *priv = rdev_get_drvdata(rdev);
unsigned int vsel_reg;
int ret;
if (priv->desc.vsel_reg == RT5739_REG_VSEL0)
vsel_reg = RT5739_REG_VSEL0;
else
vsel_reg = RT5739_REG_VSEL1;
ret = regmap_update_bits(priv->regmap, vsel_reg,
RT5739_VSEL_MASK, selector);
if (ret < 0) {
dev_err(&rdev->dev, "%s(): register %d update failed with err %d\n",
__func__, vsel_reg, ret);
return ret;
}
return ret;
}
static unsigned int rt5739_of_map_mode(unsigned int mode)
{
switch (mode) {
case RT5739_MODE_AUTO:
return REGULATOR_MODE_NORMAL;
case RT5739_MODE_FORCED_PWM:
return REGULATOR_MODE_FAST;
}
return REGULATOR_MODE_INVALID;
}
static int rt5739_set_mode(struct regulator_dev *rdev, unsigned int mode)
{
struct rt5739_priv *priv = rdev_get_drvdata(rdev);
unsigned int mode_mask;
unsigned int mode_val;
int ret;
if (priv->desc.vsel_reg == RT5739_REG_VSEL0)
mode_mask = RT5739_VSEL0_FPWM_MASK;
else
mode_mask = RT5739_VSEL1_FPWM_MASK;
switch (mode) {
case REGULATOR_MODE_FAST:
mode_val = mode_mask;
break;
case REGULATOR_MODE_NORMAL:
mode_val = 0;
break;
default:
dev_err(&rdev->dev, "%s(): mode not supported\n", __func__);
return -EINVAL;
}
ret = regmap_update_bits(priv->regmap, RT5739_REG_CONTROL1, mode_mask, mode_val);
if (ret < 0) {
dev_err(&rdev->dev, "%s(): register %d update failed with err %d\n",
__func__, RT5739_REG_CONTROL1, ret);
}
return ret;
}
static unsigned int rt5739_get_mode(struct regulator_dev *rdev)
{
struct rt5739_priv *priv = rdev_get_drvdata(rdev);
unsigned int mode_mask;
unsigned int val;
int ret;
if (priv->desc.vsel_reg == RT5739_REG_VSEL0)
mode_mask = RT5739_VSEL0_FPWM_MASK;
else
mode_mask = RT5739_VSEL1_FPWM_MASK;
ret = regmap_read(priv->regmap, RT5739_REG_CONTROL1, &val);
if (ret) {
dev_err(&rdev->dev, "%s(): register %d read failed with err %d\n",
__func__, RT5739_REG_CONTROL1, ret);
return ret;
}
if (val & mode_mask)
return REGULATOR_MODE_FAST;
else
return REGULATOR_MODE_NORMAL;
}
static int rt5739_set_ramp_delay(struct regulator_dev *rdev,
int ramp_delay)
{
struct rt5739_priv *priv = rdev_get_drvdata(rdev);
unsigned int val;
int ret;
/* Set ramp delay */
if (ramp_delay <= 187)
val = RT5739_RAMP_DELAY_187UV_PER_US;
else if (ramp_delay <= 375)
val = RT5739_RAMP_DELAY_375UV_PER_US;
else if (ramp_delay <= 750)
val = RT5739_RAMP_DELAY_750UV_PER_US;
else if (ramp_delay <= 1500)
val = RT5739_RAMP_DELAY_1500UV_PER_US;
else if (ramp_delay <= 3000)
val = RT5739_RAMP_DELAY_3000UV_PER_US;
else if (ramp_delay <= 6000)
val = RT5739_RAMP_DELAY_6000UV_PER_US;
else if (ramp_delay <= 12000)
val = RT5739_RAMP_DELAY_12000UV_PER_US;
else if (ramp_delay <= 24000)
val = RT5739_RAMP_DELAY_24000UV_PER_US;
else
return -EINVAL;
ret = regmap_update_bits(priv->regmap, RT5739_REG_CONTROL1,
RT5739_UP_RAMP_DELAY_MASK, (val << RT5739_UP_RAMP_DELAY_SHIFT));
if (ret < 0) {
dev_err(&rdev->dev, "%s(): register %d update failed with err %d",
__func__, RT5739_REG_CONTROL1, ret);
}
ret = regmap_update_bits(priv->regmap, RT5739_REG_CONTROL2,
RT5739_DN_RAMP_DELAY_MASK, (val << RT5739_DN_RAMP_DELAY_SHIFT));
if (ret < 0) {
dev_err(&rdev->dev, "%s(): register %d update failed with err %d",
__func__, RT5739_REG_CONTROL2, ret);
}
return ret;
}
static const struct regulator_ops rt5739_regulator_ops = {
.list_voltage = regulator_list_voltage_linear,
.get_voltage_sel = rt5739_dcdc_get_voltage_sel,
.set_voltage_sel = rt5739_dcdc_set_voltage_sel,
.set_mode = rt5739_set_mode,
.get_mode = rt5739_get_mode,
.set_ramp_delay = rt5739_set_ramp_delay,
};
static bool rt5739_is_accessible_reg(struct device *dev, unsigned int reg)
{
if (reg >= RT5739_REG_VSEL0 && reg <= RT5739_REG_CONTROL4)
return true;
return false;
}
static bool rt5739_is_volatile_reg(struct device *dev, unsigned int reg)
{
if (reg == RT5739_REG_MONITOR)
return true;
return false;
}
static const struct regmap_config rt5739_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = RT5739_REG_CONTROL4,
.cache_type = REGCACHE_RBTREE,
.writeable_reg = rt5739_is_accessible_reg,
.readable_reg = rt5739_is_accessible_reg,
.volatile_reg = rt5739_is_volatile_reg,
};
static int rt5739_probe(struct i2c_client *i2c)
{
struct rt5739_priv *priv;
struct regulator_config regulator_cfg = {};
struct regulator_dev *rdev;
bool vsel_active_low;
int ret;
priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
vsel_active_low = device_property_present(&i2c->dev, "richtek,vsel-active-low");
priv->regmap = devm_regmap_init_i2c(i2c, &rt5739_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;
}
priv->desc.name = "rt5739-regulator";
priv->desc.type = REGULATOR_VOLTAGE;
priv->desc.owner = THIS_MODULE;
priv->desc.min_uV = RT5739_VOUT_MINUV;
priv->desc.uV_step = RT5739_VOUT_STPUV;
if (vsel_active_low)
priv->desc.vsel_reg = RT5739_REG_VSEL0;
else
priv->desc.vsel_reg = RT5739_REG_VSEL1;
//priv->desc.vsel_mask = RT5739_VSEL_MASK;
priv->desc.n_voltages = RT5739_N_VOUTS;
priv->desc.of_map_mode = rt5739_of_map_mode;
priv->desc.ops = &rt5739_regulator_ops;
regulator_cfg.dev = &i2c->dev;
regulator_cfg.of_node = i2c->dev.of_node;
regulator_cfg.regmap = priv->regmap;
regulator_cfg.driver_data = priv;
regulator_cfg.init_data = of_get_regulator_init_data(&i2c->dev, i2c->dev.of_node,
&priv->desc);
rdev = devm_regulator_register(&i2c->dev, &priv->desc, &regulator_cfg);
if (IS_ERR(rdev)) {
dev_err(&i2c->dev, "%s():Failed to register regulator\n", __func__);
return PTR_ERR(rdev);
}
return 0;
}
static const struct of_device_id __maybe_unused rt5739_of_match_table[] = {
{ .compatible = "richtek,rt5739", },
{}
};
MODULE_DEVICE_TABLE(of, rt5739_of_match_table);
static struct i2c_driver rt5739_driver = {
.driver = {
.name = "rt5739",
.of_match_table = rt5739_of_match_table,
},
.probe_new = rt5739_probe,
};
module_i2c_driver(rt5739_driver);
MODULE_AUTHOR("Edward Ho <Edward.Ho@amlogic.com>");
MODULE_DESCRIPTION("rt5739 voltage regulator driver");
MODULE_LICENSE("GPL v2");