528 lines
13 KiB
C
528 lines
13 KiB
C
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||
|
/*
|
||
|
* Copyright (c) 2021 MediaTek Inc.
|
||
|
*/
|
||
|
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/err.h>
|
||
|
#include <linux/i2c.h>
|
||
|
#include <linux/version.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/of.h>
|
||
|
#include <linux/regulator/driver.h>
|
||
|
#include <linux/regulator/machine.h>
|
||
|
#include <linux/regulator/of_regulator.h>
|
||
|
#ifdef CONFIG_RT_REGMAP
|
||
|
#include <mt-plat/rt-regmap.h>
|
||
|
#endif /* CONFIG_RT_REGMAP */
|
||
|
#include <mt-plat/upmu_common.h>
|
||
|
#include "rt5738-regulator.h"
|
||
|
|
||
|
static const char * const rt5738_text[] = {
|
||
|
"rt57380",
|
||
|
"rt57381",
|
||
|
"rt57382",
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
* Exported API
|
||
|
*/
|
||
|
static int g_is_rt5738_exist;
|
||
|
|
||
|
int is_rt5738_exist(void)
|
||
|
{
|
||
|
#if defined(RT5738_IS_EXIST_NAME)
|
||
|
struct regulator *reg;
|
||
|
|
||
|
reg = regulator_get(NULL, RT5738_IS_EXIST_NAME);
|
||
|
pr_info("%s: regulator_get=%s\n", __func__, RT5738_IS_EXIST_NAME);
|
||
|
if (reg == NULL)
|
||
|
return 0;
|
||
|
regulator_put(reg);
|
||
|
return 1;
|
||
|
#else
|
||
|
pr_notice("g_is_rt5738_exist=%d\n", g_is_rt5738_exist);
|
||
|
return g_is_rt5738_exist;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static int rt5738_read_device(void *client, u32 addr,
|
||
|
int len, void *dst)
|
||
|
{
|
||
|
struct i2c_client *i2c = (struct i2c_client *)client;
|
||
|
|
||
|
return i2c_smbus_read_i2c_block_data(i2c, addr, len, dst);
|
||
|
}
|
||
|
|
||
|
static int rt5738_write_device(void *client, u32 addr,
|
||
|
int len, const void *src)
|
||
|
{
|
||
|
struct i2c_client *i2c = (struct i2c_client *)client;
|
||
|
|
||
|
return i2c_smbus_write_i2c_block_data(i2c, addr, len, src);
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_RT_REGMAP
|
||
|
RT_REG_DECL(RT5738_REG_VSEL0, 1, RT_WBITS_WR_ONCE, {});
|
||
|
RT_REG_DECL(RT5738_REG_VSEL1, 1, RT_WBITS_WR_ONCE, {});
|
||
|
RT_REG_DECL(RT5738_REG_CTRL1, 1, RT_WBITS_WR_ONCE, {});
|
||
|
RT_REG_DECL(RT5738_REG_ID1, 1, RT_VOLATILE, {});
|
||
|
RT_REG_DECL(RT5738_REG_ID2, 1, RT_VOLATILE, {});
|
||
|
RT_REG_DECL(RT5738_REG_MONITOR, 1, RT_VOLATILE, {});
|
||
|
RT_REG_DECL(RT5738_REG_CTRL2, 1, RT_VOLATILE, {});
|
||
|
RT_REG_DECL(RT5738_REG_CTRL3, 1, RT_WBITS_WR_ONCE, {});
|
||
|
RT_REG_DECL(RT5738_REG_CTRL4, 1, RT_WBITS_WR_ONCE, {});
|
||
|
|
||
|
static const rt_register_map_t rt5738_regmap[] = {
|
||
|
RT_REG(RT5738_REG_VSEL0),
|
||
|
RT_REG(RT5738_REG_VSEL1),
|
||
|
RT_REG(RT5738_REG_CTRL1),
|
||
|
RT_REG(RT5738_REG_ID1),
|
||
|
RT_REG(RT5738_REG_ID2),
|
||
|
RT_REG(RT5738_REG_MONITOR),
|
||
|
RT_REG(RT5738_REG_CTRL2),
|
||
|
RT_REG(RT5738_REG_CTRL3),
|
||
|
RT_REG(RT5738_REG_CTRL4),
|
||
|
};
|
||
|
|
||
|
static struct rt_regmap_fops rt5738_regmap_fops = {
|
||
|
.read_device = rt5738_read_device,
|
||
|
.write_device = rt5738_write_device,
|
||
|
};
|
||
|
#endif /* CONFIG_RT_REGMAP */
|
||
|
|
||
|
int rt5738_read_byte(void *client,
|
||
|
uint32_t addr, uint32_t *val)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct i2c_client *i2c = (struct i2c_client *)client;
|
||
|
|
||
|
ret = rt5738_read_device(i2c, addr, 1, val);
|
||
|
if (ret < 0)
|
||
|
pr_notice("%s read 0x%02x fail\n", __func__, addr);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int rt5738_write_byte(void *client,
|
||
|
uint32_t addr, uint32_t value)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct i2c_client *i2c = (struct i2c_client *)client;
|
||
|
|
||
|
ret = rt5738_write_device(i2c, addr, 1, &value);
|
||
|
if (ret < 0)
|
||
|
pr_notice("%s write 0x%02x fail\n", __func__, addr);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int rt5738_assign_bit(void *client, uint32_t reg,
|
||
|
uint32_t mask, uint32_t data)
|
||
|
{
|
||
|
struct i2c_client *i2c = (struct i2c_client *)client;
|
||
|
struct rt5738_regulator_info *ri = i2c_get_clientdata(i2c);
|
||
|
unsigned char tmp = 0;
|
||
|
uint32_t regval = 0;
|
||
|
int ret = 0;
|
||
|
|
||
|
mutex_lock(&ri->io_lock);
|
||
|
ret = rt5738_read_byte(i2c, reg, ®val);
|
||
|
if (ret < 0) {
|
||
|
pr_notice("%s read fail reg0x%02x data0x%02x\n",
|
||
|
__func__, reg, data);
|
||
|
goto OUT_ASSIGN;
|
||
|
}
|
||
|
|
||
|
tmp = ((regval & 0xff) & ~mask);
|
||
|
tmp |= (data & mask);
|
||
|
ret = rt5738_write_byte(i2c, reg, tmp);
|
||
|
if (ret < 0)
|
||
|
pr_notice("%s write fail reg0x%02x data0x%02x\n",
|
||
|
__func__, reg, tmp);
|
||
|
OUT_ASSIGN:
|
||
|
mutex_unlock(&ri->io_lock);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#define rt5738_set_bit(i2c, reg, mask) \
|
||
|
rt5738_assign_bit(i2c, reg, mask, mask)
|
||
|
#define rt5738_clr_bit(i2c, reg, mask) \
|
||
|
rt5738_assign_bit(i2c, reg, mask, 0x00)
|
||
|
|
||
|
static int rt5738_regmap_init(struct rt5738_regulator_info *info)
|
||
|
{
|
||
|
#ifdef CONFIG_RT_REGMAP
|
||
|
info->regmap_props = devm_kzalloc(info->dev,
|
||
|
sizeof(struct rt_regmap_properties), GFP_KERNEL);
|
||
|
|
||
|
info->regmap_props->name = rt5738_text[info->id];
|
||
|
info->regmap_props->aliases = rt5738_text[info->id];
|
||
|
info->regmap_props->register_num = ARRAY_SIZE(rt5738_regmap);
|
||
|
info->regmap_props->rm = rt5738_regmap;
|
||
|
info->regmap_props->rt_regmap_mode = RT_CACHE_WR_THROUGH;
|
||
|
|
||
|
info->regmap_dev = rt_regmap_device_register(info->regmap_props,
|
||
|
&rt5738_regmap_fops, info->dev, info->i2c, info);
|
||
|
if (!info->regmap_dev)
|
||
|
return -EINVAL;
|
||
|
#endif /* CONFIG_RT_REGMAP */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct regulator_chip rt5738_datas[] = {
|
||
|
{
|
||
|
.vol_reg = RT5738_VSEL_0,
|
||
|
.mode_reg = RT5738_CTRL_0,
|
||
|
.mode_bit = RT5738_CTRL_BIT_0,
|
||
|
.enable_reg = RT5738_EN_0,
|
||
|
.enable_bit = RT5738_EN_BIT_0,
|
||
|
},
|
||
|
{
|
||
|
.vol_reg = RT5738_VSEL_1,
|
||
|
.mode_reg = RT5738_CTRL_1,
|
||
|
.mode_bit = RT5738_CTRL_BIT_1,
|
||
|
.enable_reg = RT5738_EN_1,
|
||
|
.enable_bit = RT5738_EN_BIT_1,
|
||
|
},
|
||
|
#if defined(RT5738_NAME_2)
|
||
|
{
|
||
|
.vol_reg = RT5738_VSEL_2,
|
||
|
.mode_reg = RT5738_CTRL_2,
|
||
|
.mode_bit = RT5738_CTRL_BIT_2,
|
||
|
.enable_reg = RT5738_EN_2,
|
||
|
.enable_bit = RT5738_EN_BIT_2,
|
||
|
},
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
static int rt5738_set_voltage(struct regulator_dev *rdev,
|
||
|
int min_uV, int max_uV, unsigned int *selector)
|
||
|
{
|
||
|
struct rt5738_regulator_info *info = rdev_get_drvdata(rdev);
|
||
|
struct regulator_chip *chip = info->reg_chip;
|
||
|
|
||
|
if (min_uV < 1300000)
|
||
|
*selector = (min_uV - 300000) / 5000;
|
||
|
else
|
||
|
*selector = (min_uV - 1300000) / 10000 + 200;
|
||
|
|
||
|
if (*selector > 255)
|
||
|
*selector = 255;
|
||
|
return rt5738_write_byte(info->i2c, chip->vol_reg, *selector);
|
||
|
}
|
||
|
|
||
|
static int rt5738_get_voltage(struct regulator_dev *rdev)
|
||
|
{
|
||
|
struct rt5738_regulator_info *info = rdev_get_drvdata(rdev);
|
||
|
struct regulator_chip *chip = info->reg_chip;
|
||
|
int ret;
|
||
|
uint32_t reg_val = 0;
|
||
|
|
||
|
ret = rt5738_read_byte(info->i2c, chip->vol_reg, ®_val);
|
||
|
if (ret < 0) {
|
||
|
pr_notice("%s read voltage fail\n", __func__);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
if (reg_val > 200)
|
||
|
return 1300000 + (reg_val - 200) * 10000;
|
||
|
return 300000 + reg_val * 5000;
|
||
|
}
|
||
|
|
||
|
static int rt5738_set_mode(struct regulator_dev *rdev, unsigned int mode)
|
||
|
{
|
||
|
struct rt5738_regulator_info *info = rdev_get_drvdata(rdev);
|
||
|
struct regulator_chip *chip = info->reg_chip;
|
||
|
int ret;
|
||
|
|
||
|
switch (mode) {
|
||
|
case REGULATOR_MODE_FAST: /* force pwm mode */
|
||
|
ret = rt5738_set_bit(info->i2c, chip->mode_reg, chip->mode_bit);
|
||
|
break;
|
||
|
case REGULATOR_MODE_NORMAL:
|
||
|
default:
|
||
|
ret = rt5738_clr_bit(info->i2c, chip->mode_reg, chip->mode_bit);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static unsigned int rt5738_get_mode(struct regulator_dev *rdev)
|
||
|
{
|
||
|
struct rt5738_regulator_info *info = rdev_get_drvdata(rdev);
|
||
|
struct regulator_chip *chip = info->reg_chip;
|
||
|
int ret;
|
||
|
uint32_t regval = 0;
|
||
|
|
||
|
ret = rt5738_read_byte(info->i2c, chip->mode_reg, ®val);
|
||
|
if (ret < 0) {
|
||
|
pr_notice("%s read mode fail\n", __func__);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
if (regval & chip->mode_bit)
|
||
|
return REGULATOR_MODE_FAST;
|
||
|
return REGULATOR_MODE_NORMAL;
|
||
|
}
|
||
|
|
||
|
static int rt5738_enable(struct regulator_dev *rdev)
|
||
|
{
|
||
|
struct rt5738_regulator_info *info = rdev_get_drvdata(rdev);
|
||
|
struct regulator_chip *chip = info->reg_chip;
|
||
|
|
||
|
return rt5738_set_bit(info->i2c, chip->enable_reg, chip->enable_bit);
|
||
|
}
|
||
|
|
||
|
static int rt5738_disable(struct regulator_dev *rdev)
|
||
|
{
|
||
|
struct rt5738_regulator_info *info = rdev_get_drvdata(rdev);
|
||
|
struct regulator_chip *chip = info->reg_chip;
|
||
|
|
||
|
if (rdev->use_count == 0) {
|
||
|
pr_info("ext_buck should not be disable (use_count=%d)\n"
|
||
|
, rdev->use_count);
|
||
|
return -1;
|
||
|
}
|
||
|
return rt5738_clr_bit(info->i2c, chip->enable_reg, chip->enable_bit);
|
||
|
}
|
||
|
|
||
|
static int rt5738_is_enabled(struct regulator_dev *rdev)
|
||
|
{
|
||
|
struct rt5738_regulator_info *info = rdev_get_drvdata(rdev);
|
||
|
struct regulator_chip *chip = info->reg_chip;
|
||
|
int ret;
|
||
|
uint32_t reg_val;
|
||
|
|
||
|
ret = rt5738_read_byte(info->i2c, chip->enable_reg, ®_val);
|
||
|
if (ret < 0)
|
||
|
return ret;
|
||
|
|
||
|
return (reg_val&chip->enable_bit) ? 1 : 0;
|
||
|
}
|
||
|
|
||
|
static struct regulator_ops rt5738_regulator_ops = {
|
||
|
.set_voltage = rt5738_set_voltage,
|
||
|
.get_voltage = rt5738_get_voltage,
|
||
|
.set_mode = rt5738_set_mode,
|
||
|
.get_mode = rt5738_get_mode,
|
||
|
.enable = rt5738_enable,
|
||
|
.disable = rt5738_disable,
|
||
|
.is_enabled = rt5738_is_enabled,
|
||
|
};
|
||
|
|
||
|
#define REG_DESC(_name, _id) { \
|
||
|
.id = _id, \
|
||
|
.name = _name, \
|
||
|
.n_voltages = 201, \
|
||
|
.ops = &rt5738_regulator_ops, \
|
||
|
.type = REGULATOR_VOLTAGE, \
|
||
|
.owner = THIS_MODULE, \
|
||
|
}
|
||
|
|
||
|
static struct regulator_desc rt5738_regulator_desc[] = {
|
||
|
REG_DESC("rt5738_buck0", 0),
|
||
|
REG_DESC("rt5738_buck1", 1),
|
||
|
#if defined(RT5738_NAME_2)
|
||
|
REG_DESC("rt5738_buck2", 2),
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
static int rt5738_parse_dt(
|
||
|
struct rt5738_regulator_info *info, struct device *dev)
|
||
|
{
|
||
|
struct device_node *np = dev->of_node;
|
||
|
u32 val;
|
||
|
int ret;
|
||
|
|
||
|
if (!np) {
|
||
|
pr_notice("%s cant find node (0x%02x)\n",
|
||
|
__func__, info->i2c->addr);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
ret = of_property_read_u32(np, "vsel_pin", &val);
|
||
|
if (ret >= 0) {
|
||
|
pr_info("%s set vsel_pin(%x)\n", __func__, val);
|
||
|
info->pin_sel = val;
|
||
|
info->id = val;
|
||
|
} else {
|
||
|
pr_notice("%s use chip default vsel_pin(0)\n", __func__);
|
||
|
info->pin_sel = 0;
|
||
|
info->id = 0;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
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,
|
||
|
};
|
||
|
return regulator_register(desc, &config);
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
int ret;
|
||
|
|
||
|
pr_info("%s ver(%s) slv(0x%02x)\n",
|
||
|
__func__, RT5738_DRV_VERSION, i2c->addr);
|
||
|
|
||
|
switch (i2c->addr) {
|
||
|
case 0x50: /* RT5738_A */
|
||
|
case 0x57: /* RT5738_B */
|
||
|
return -ENODEV;
|
||
|
case 0x52: /* RT5738_C */
|
||
|
case 0x51: /* RT5738_G */
|
||
|
case 0x53: /* RT5738_H */
|
||
|
break;
|
||
|
case 0x55: /* RT5738_F */
|
||
|
return -ENODEV;
|
||
|
default:
|
||
|
pr_notice("%s invalid Slave Addr\n", __func__);
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
info = devm_kzalloc(&i2c->dev,
|
||
|
sizeof(struct rt5738_regulator_info), GFP_KERNEL);
|
||
|
if (!info)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
ret = rt5738_parse_dt(info, &i2c->dev);
|
||
|
if (ret < 0) {
|
||
|
pr_notice("%s parse dt (0x%02x) fail\n", __func__, i2c->addr);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
init_data = of_get_regulator_init_data(&i2c->dev,
|
||
|
i2c->dev.of_node,
|
||
|
NULL);
|
||
|
if (init_data) {
|
||
|
dev_info(&i2c->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);
|
||
|
pr_info("rt5738 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_info(&i2c->dev, "%s: no init data\n", __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
info->i2c = i2c;
|
||
|
info->dev = &i2c->dev;
|
||
|
info->desc = &rt5738_regulator_desc[info->pin_sel];
|
||
|
info->reg_chip = &rt5738_datas[info->pin_sel];
|
||
|
mutex_init(&info->io_lock);
|
||
|
|
||
|
i2c_set_clientdata(i2c, info);
|
||
|
|
||
|
ret = rt5738_regmap_init(info);
|
||
|
if (ret < 0) {
|
||
|
pr_notice("%s rt5738 regmap init fail\n", __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
#if 1
|
||
|
g_is_rt5738_exist = 0;
|
||
|
if (rt5738_read_byte(info->i2c, RT5738_REG_MONITOR, &ret) >= 0)
|
||
|
g_is_rt5738_exist = 1;
|
||
|
pr_notice("i2c_addr=%d ret=%d g_is_rt5738_exist=%d\n"
|
||
|
, i2c->addr
|
||
|
, ret
|
||
|
, g_is_rt5738_exist);
|
||
|
return 0;
|
||
|
#else
|
||
|
info->regulator = rt5738_regulator_register(info->desc,
|
||
|
&i2c->dev,
|
||
|
init_data,
|
||
|
info);
|
||
|
|
||
|
if (!info->regulator) {
|
||
|
pr_notice("%s rt5738 register regulator fail\n", __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
info->regulator->constraints->valid_modes_mask |=
|
||
|
(REGULATOR_MODE_NORMAL|REGULATOR_MODE_FAST);
|
||
|
info->regulator->constraints->valid_ops_mask |=
|
||
|
REGULATOR_CHANGE_MODE;
|
||
|
|
||
|
pr_info("%s Successfully\n", __func__);
|
||
|
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static int rt5738_i2c_remove(struct i2c_client *i2c)
|
||
|
{
|
||
|
struct rt5738_regulator_info *info = i2c_get_clientdata(i2c);
|
||
|
|
||
|
if (info) {
|
||
|
regulator_unregister(info->regulator);
|
||
|
#ifdef CONFIG_RT_REGMAP
|
||
|
rt_regmap_device_unregister(info->regmap_dev);
|
||
|
#endif /* CONFIG_RT_REGMAP */
|
||
|
mutex_destroy(&info->io_lock);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Must reserve one empty object at the end */
|
||
|
static const struct of_device_id rt_match_table[] = {
|
||
|
{ .compatible = RT5738_CMPT_STR_0, },
|
||
|
{ .compatible = RT5738_CMPT_STR_1, },
|
||
|
#if defined(RT5738_NAME_2)
|
||
|
{ .compatible = RT5738_CMPT_STR_2, },
|
||
|
#endif
|
||
|
{ },
|
||
|
};
|
||
|
|
||
|
static const struct i2c_device_id rt_dev_id[] = {
|
||
|
{"rt5738", 0},
|
||
|
{ },
|
||
|
};
|
||
|
|
||
|
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,
|
||
|
.id_table = rt_dev_id,
|
||
|
};
|
||
|
|
||
|
static int __init rt5738_i2c_init(void)
|
||
|
{
|
||
|
pr_info("%s\n", __func__);
|
||
|
return i2c_add_driver(&rt5738_i2c_driver);
|
||
|
}
|
||
|
subsys_initcall(rt5738_i2c_init);
|
||
|
|
||
|
static void __exit rt5738_i2c_exit(void)
|
||
|
{
|
||
|
i2c_del_driver(&rt5738_i2c_driver);
|
||
|
}
|
||
|
module_exit(rt5738_i2c_exit);
|
||
|
|
||
|
MODULE_LICENSE("GPL");
|
||
|
MODULE_AUTHOR("Jeff Chang <jeff_chang@richtek.com>");
|
||
|
MODULE_VERSION(RT5738_DRV_VERSION);
|
||
|
MODULE_DESCRIPTION("Regulator driver for RT5738");
|