blob: 7a4adf8589f473eeda8be7c361f7d1a8c94478f5 [file]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/amlogic/pmic/meson_pmic6.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
/* Voltage Values */
static const int pmic6_vdd_core[][450] = {};
static const unsigned int pmic6_vddq[] = {
1350000,
};
static const unsigned int pmic6_vddao3_3[] = {
3300000,
};
static const unsigned int pmic6_avdd1_8[] = {
1800000,
};
static const unsigned int pmic6_mic_bia[] = {
3300000,
};
static const unsigned int pmic6_vcc1_8[] = {
1800000,
};
static int get_dcdc1_current_voltage(struct regulator_dev *rdev)
{
struct regmap *regmap = rdev->regmap;
struct device *dev = rdev_get_dev(rdev);
u8 reg[2] = {0};
u64 val;
int ret;
u64 div;
ret = regmap_write(regmap, PMIC6_SAR_CNTL_REG14, 0x11);
if (ret < 0) {
dev_dbg(dev,
"failed in func: %s line: %d\n", __func__, __LINE__);
return ret;
}
ret = regmap_write(regmap, PMIC6_SAR_CNTL_REG15, 0x40);
if (ret < 0) {
dev_dbg(dev,
"failed in func: %s line: %d\n", __func__, __LINE__);
return ret;
}
ret = regmap_write(regmap, PMIC6_SAR_CNTL_REG0, 0x16);
if (ret < 0) {
dev_dbg(dev,
"failed in func: %s line: %d\n", __func__, __LINE__);
return ret;
}
ret = regmap_write(regmap, PMIC6_SAR_CNTL_REG2, 0x04);
if (ret < 0) {
dev_dbg(dev,
"failed in func: %s line: %d\n", __func__, __LINE__);
return ret;
}
ret = regmap_write(regmap, PMIC6_SAR_SW_EN_FIELD, 0x08);
if (ret < 0) {
dev_dbg(dev,
"failed in func: %s line: %d\n", __func__, __LINE__);
return ret;
}
ret = regmap_bulk_read(regmap, PMIC6_SAR_RD_RAW, reg, 2);
if (ret < 0) {
dev_dbg(dev,
"failed in func: %s line: %d\n", __func__, __LINE__);
return ret;
}
val = ((reg[1] & 0x1f) << 8) | reg[0];
div = val * 1600UL;
div *= 1000;
do_div(div, 4096);
return (int)(div);
}
static int pmic6_dcdc1_set_voltage(struct regulator_dev *rdev, int min_uV,
int max_uV, unsigned int *selector)
{
u8 reg[2] = {0};
unsigned int val;
int ret, current_uV;
unsigned int sel;
int min_reg_val;
if (max_uV < rdev->desc->min_uV)
return -EINVAL;
ret = regmap_bulk_read(rdev->regmap, rdev->desc->vsel_reg, reg, 2);
if (ret < 0) {
dev_dbg(rdev_get_dev(rdev),
"failed in func: %s line: %d\n", __func__, __LINE__);
return ret;
}
val = ((reg[1] & 0x7) << 8) | reg[0];
val >>= ffs(rdev->desc->vsel_mask) - 1;
dev_info(rdev_get_dev(rdev), "%s val: %d\n", __func__, val);
current_uV = get_dcdc1_current_voltage(rdev);
dev_info(rdev_get_dev(rdev), "%s current_uV: %d\n", __func__,
current_uV);
WARN_ON(current_uV < rdev->desc->min_uV);
min_reg_val = val - (current_uV - rdev->desc->min_uV) /
rdev->desc->uV_step;
*selector = (min_uV - rdev->desc->min_uV) / rdev->desc->uV_step;
sel = min_reg_val + *selector;
ret = regmap_bulk_write(rdev->regmap, rdev->desc->vsel_reg,
&sel, 2);
if (ret < 0) {
dev_dbg(rdev_get_dev(rdev),
"failed in func: %s line: %d\n", __func__, __LINE__);
return ret;
}
return ret;
}
static int pmic6_dcdc1_get_voltage(struct regulator_dev *rdev)
{
/*int id = rdev_get_id(rdev);*/
/*dev_info(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);*/
return get_dcdc1_current_voltage(rdev);
}
static const struct regulator_ops pmic6_dcdc1_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.set_voltage = pmic6_dcdc1_set_voltage,
.get_voltage = pmic6_dcdc1_get_voltage,
};
static int pmic6_set_voltage(struct regulator_dev *rdev, int min_uV,
int max_uV, unsigned int *selector)
{
int id = rdev_get_id(rdev);
dev_info(rdev_get_dev(rdev), "%s id: %d min_uV: %d max_uV: %d\n",
__func__, id, min_uV, max_uV);
if (min_uV <= rdev->desc->volt_table[0] &&
rdev->desc->volt_table[0] <= max_uV) {
*selector = 0;
return 0;
} else {
return -EINVAL;
}
}
static int pmic6_get_voltage(struct regulator_dev *rdev)
{
int id = rdev_get_id(rdev);
dev_info(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
return rdev->desc->volt_table[0];
}
static int pmic6_ldo1_get_voltage(struct regulator_dev *rdev)
{
int id = rdev_get_id(rdev);
dev_info(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
return rdev->desc->volt_table[0];
}
static const struct regulator_ops pmic6_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.set_voltage = pmic6_set_voltage,
.get_voltage = pmic6_get_voltage,
.list_voltage = regulator_list_voltage_table,
};
static const struct regulator_ops pmic6_ldo1_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.get_voltage = pmic6_ldo1_get_voltage,
.list_voltage = regulator_list_voltage_table,
};
#define REG_DCDC(rid, ereg, emask, vreg, vmask, min1, max, step1) \
{ \
.name = #rid, \
.of_match = of_match_ptr(#rid), \
.regulators_node = of_match_ptr("regulators"), \
.id = PMIC6_##rid, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
.ops = &pmic6_dcdc1_ops, \
.n_voltages = ((max) - (min1)) / (step1) + 1, \
.min_uV = (min1), \
.uV_step = (step1), \
.enable_reg = PMIC6_##ereg, \
.enable_mask = (emask), \
.vsel_reg = PMIC6_##vreg, \
.vsel_mask = (vmask), \
}
#define REG_DESC(rid, ereg, emask, vreg, vmask, voltages, ops1) \
{ \
.name = #rid, \
.of_match = of_match_ptr(#rid), \
.regulators_node = of_match_ptr("regulators"), \
.id = PMIC6_##rid, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
.ops = &(ops1), \
.n_voltages = ARRAY_SIZE(voltages), \
.volt_table = (voltages), \
.enable_reg = PMIC6_##ereg, \
.enable_mask = (emask), \
.vsel_reg = PMIC6_##vreg, \
.vsel_mask = (vmask), \
}
#define REG_LDO(rid, ereg, emask, voltage, ops2) \
{ \
.name = #rid, \
.of_match = of_match_ptr(#rid), \
.regulators_node = of_match_ptr("regulators"), \
.id = PMIC6_##rid, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
.ops = &(ops2), \
.n_voltages = ARRAY_SIZE(voltage), \
.volt_table = (voltage), \
.enable_reg = PMIC6_##ereg, \
.enable_mask = (emask), \
}
static struct regulator_desc pmic6_regulators[] = {
/* DCDC */
REG_DCDC(DCDC1, ANALOG_REG9, BIT(0), OTP_REG_0x50,
0x7ff, 600000, 1500000, 2000),
REG_DESC(DCDC2, ANALOG_REG9, BIT(1), OTP_REG_0x52, 0x7ff,
pmic6_vddq, pmic6_ops),
REG_DESC(DCDC3, ANALOG_REG9, BIT(2), OTP_REG_0x54,
0x7ff, pmic6_vddao3_3, pmic6_ops),
/* LDO */
REG_LDO(LDO1, ANALOG_REG9, BIT(3), pmic6_avdd1_8, pmic6_ldo1_ops),
REG_DESC(LDO2, ANALOG_REG9, BIT(4), OTP_REG_0x73, 0x3f,
pmic6_mic_bia, pmic6_ops),
REG_DESC(LDO3, ANALOG_REG9, BIT(5), OTP_REG_0x25, 0x3f,
pmic6_vcc1_8, pmic6_ops),
};
static int meson_pmic_regulator_probe(struct platform_device *pdev)
{
struct meson_pmic *meson_pmic = dev_get_drvdata(pdev->dev.parent);
struct regulator_config config = { };
struct regulator_dev *rdev;
struct regulator_desc *regulators;
int i;
int num_regulators = 0;
regulators = pmic6_regulators;
num_regulators = ARRAY_SIZE(pmic6_regulators);
config.dev = pdev->dev.parent;
config.regmap = meson_pmic->regmap;
for (i = 0; i < num_regulators; i++) {
rdev = devm_regulator_register(&pdev->dev,
&regulators[i],
&config);
if (IS_ERR(rdev)) {
dev_err(&pdev->dev, "failed to register %s regulator\n",
regulators[i].name);
}
}
return 0;
}
static struct platform_driver pmic6_regulator_driver = {
.probe = meson_pmic_regulator_probe,
.driver = {
.name = PMIC6_DRVNAME_REGULATORS,
},
};
static int __init meson_pmic6_regulator_init(void)
{
int ret;
ret = platform_driver_register(&pmic6_regulator_driver);
return ret;
}
static void __exit meson_pmic6_regulator_exit(void)
{
platform_driver_unregister(&pmic6_regulator_driver);
}
subsys_initcall(meson_pmic6_regulator_init);
module_exit(meson_pmic6_regulator_exit);
MODULE_AUTHOR("Amlogic");
MODULE_DESCRIPTION("PMIC6 regulator driver");
MODULE_LICENSE("GPL v2");