blob: 2a57d1ba0d94991c856f7e55d0de7d570e6a0459 [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/err.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mfd/rohm-bd2657.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/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/slab.h>
static const struct regulator_linear_range bd2657_buck_volts[] = {
REGULATOR_LINEAR_RANGE(500000, 0x01, 0xab, 5000),
REGULATOR_LINEAR_RANGE(1350000, 0xac, 0xff, 0),
};
/*
* BUCK3 has different voltages when the I2C address selection pin is
* connected to GND/HIGH. We check the I2C address and decide the voltage
* table for BUCK3 based on it
*/
static const struct regulator_linear_range bd2657_buck3_volts_2[] = {
REGULATOR_LINEAR_RANGE(1100000, 0x01, 0x97, 5000),
REGULATOR_LINEAR_RANGE(1850000, 0x98, 0xff, 0),
};
#define BD2675_I2C_2 0x6b
static unsigned int bd2657_ramp_table[] = {
2500, 5000, 2500, 2500,
};
struct bd2657_data {
struct regulator_desc desc;
int vsel_cache;
bool enabled;
};
static int bd2656_get_voltage_sel(struct regulator_dev *rdev)
{
struct bd2657_data *d;
d = container_of(rdev->desc, struct bd2657_data, desc);
return d->vsel_cache;
}
static int bd2656_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel)
{
int ret = 0;
struct bd2657_data *d;
d = container_of(rdev->desc, struct bd2657_data, desc);
if (d->enabled)
ret = regulator_set_voltage_sel_regmap(rdev, sel);
if (!ret)
d->vsel_cache = sel;
return ret;
}
static int bd2656_is_enabled(struct regulator_dev *rdev)
{
struct bd2657_data *d;
d = container_of(rdev->desc, struct bd2657_data, desc);
return d->enabled;
}
static int bd2656_disable(struct regulator_dev *rdev)
{
int ret;
struct bd2657_data *d;
d = container_of(rdev->desc, struct bd2657_data, desc);
if (!d->enabled)
return 0;
ret = regmap_write(rdev->regmap, rdev->desc->vsel_reg, 0);
if (!ret)
d->enabled = false;
return ret;
}
static int bd2656_enable(struct regulator_dev *rdev)
{
struct bd2657_data *d;
int ret;
d = container_of(rdev->desc, struct bd2657_data, desc);
if (d->enabled)
return 0;
if (!d->vsel_cache) {
dev_err(&rdev->dev, "Failed to enable '%s'\n",
rdev->desc->name);
return -EINVAL;
}
ret = regmap_write(rdev->regmap, rdev->desc->vsel_reg, d->vsel_cache);
if (!ret)
d->enabled = true;
return ret;
}
static const struct regulator_ops bd2657_buck_ops = {
.enable = bd2656_enable,
.disable = bd2656_disable,
.is_enabled = bd2656_is_enabled,
//.list_voltage = regulator_list_voltage_linear_range, //
.list_voltage = regulator_list_voltage_linear_range, //
.set_voltage_sel = bd2656_set_voltage_sel,
.get_voltage_sel = bd2656_get_voltage_sel,
//.set_voltage_time_sel = regulator_set_voltage_time_sel, //
//.set_ramp_delay = regulator_set_ramp_delay_regmap, //
};
static const struct bd2657_data bd2657_desc_template[] = {
{
.desc = {
.name = "buck1",
.of_match = of_match_ptr("BUCK1"),
.regulators_node = of_match_ptr("regulators"),
.id = BD2657_BUCK0,
.n_voltages = BD2657_BUCK_NUM_VOLTS,
.ops = &bd2657_buck_ops,
.type = REGULATOR_VOLTAGE,
.linear_ranges = bd2657_buck_volts,
.n_linear_ranges = ARRAY_SIZE(bd2657_buck_volts),
.vsel_reg = BD2657_REG_BUCK0_VID,
.vsel_mask = BD2657_MASK_BUCK_VOLT,
.ramp_delay_table = bd2657_ramp_table,
.n_ramp_values = ARRAY_SIZE(bd2657_ramp_table),
.ramp_mask = BD2657_MASK_RAMP_SLOW,
.ramp_reg = BD2657_REG_BUCK0_SLEW,
.owner = THIS_MODULE,
},
}, {
.desc = {
.name = "buck2",
.of_match = of_match_ptr("BUCK2"),
.regulators_node = of_match_ptr("regulators"),
.id = BD2657_BUCK1,
.n_voltages = BD2657_BUCK_NUM_VOLTS,
.ops = &bd2657_buck_ops,
.type = REGULATOR_VOLTAGE,
.linear_ranges = bd2657_buck_volts,
.n_linear_ranges = ARRAY_SIZE(bd2657_buck_volts),
.vsel_reg = BD2657_REG_BUCK1_VID,
.vsel_mask = BD2657_MASK_BUCK_VOLT,
.owner = THIS_MODULE,
},
}, {
.desc = {
.name = "buck3",
.of_match = of_match_ptr("BUCK3"),
.regulators_node = of_match_ptr("regulators"),
.id = BD2657_BUCK2,
.n_voltages = BD2657_BUCK_NUM_VOLTS,
.ops = &bd2657_buck_ops,
.type = REGULATOR_VOLTAGE,
.linear_ranges = bd2657_buck_volts,
.n_linear_ranges = ARRAY_SIZE(bd2657_buck_volts),
.vsel_reg = BD2657_REG_BUCK2_VID,
.vsel_mask = BD2657_MASK_BUCK_VOLT,
.owner = THIS_MODULE,
},
}, {
.desc = {
.name = "buck4",
.of_match = of_match_ptr("BUCK4"),
.regulators_node = of_match_ptr("regulators"),
.id = BD2657_BUCK3,
.type = REGULATOR_VOLTAGE,
.n_voltages = BD2657_BUCK_NUM_VOLTS,
.ops = &bd2657_buck_ops,
/*
* The voltage ranges may be overwritten
* at probe as they
* differ depending on I2C slave address
* pin connection.
*/
.linear_ranges = bd2657_buck_volts,
.n_linear_ranges = ARRAY_SIZE(bd2657_buck_volts),
.vsel_reg = BD2657_REG_BUCK3_VID,
.vsel_mask = BD2657_MASK_BUCK_VOLT,
.owner = THIS_MODULE,
},
},
};
static int bd2657_probe(struct platform_device *pdev)
{
int i, num_bucks, ret, val;
struct bd2657_data *d;
struct regulator_config c = {
.dev = pdev->dev.parent,
};
u32 i2c_addr;
num_bucks = ARRAY_SIZE(bd2657_desc_template);
c.regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!c.regmap)
return -ENODEV;
d = devm_kzalloc(&pdev->dev, sizeof(*d) * num_bucks, GFP_KERNEL);
if (!d)
return -ENOMEM;
/* Get our I2C address from reg as the BUCK3 voltages depend on that */
ret = of_property_read_u32(pdev->dev.parent->of_node, "reg", &i2c_addr);
if (ret) {
dev_err(&pdev->dev, "Failed to read I2C address\n");
return ret;
}
/* Check the register lock */
ret = regmap_read(c.regmap, BD2657_REG_REGLOCK, &val);
if (ret)
return ret;
if (val & BD2657_MASK_REGLOCK) {
dev_warn(&pdev->dev, "PMIC is locked, nothing to do\n");
return -ENODEV;
}
for (i = 0; i < num_bucks; i++) {
struct regulator_dev *rdev;
int val;
d[i] = bd2657_desc_template[i];
if (i2c_addr == BD2675_I2C_2 && d[i].desc.id == BD2657_BUCK3) {
d[i].desc.linear_ranges = bd2657_buck3_volts_2;
d[i].desc.n_linear_ranges =
ARRAY_SIZE(bd2657_buck3_volts_2);
}
/*
* BD2657 has special BOOT_VID register which contains voltage
* after the PMIC cold reset. Write this to vsel so we can
* use the vsel from now on.
*
* TODO: How to know this was cold-boot for PMIC?
*/
ret = regmap_read(c.regmap, d[i].desc.vsel_reg + 1, &val);
if (!ret)
ret = regmap_write(c.regmap, d[i].desc.vsel_reg, val);
else
return ret;
if (ret)
return ret;
/*
* BD2657 does not contain separate enable bit. If non-zero
* voltage is written to vsel_reg => regulator is enabled.
* In order to support setting voltage without enabling
* regulator - or to support enabling without setting voltage
* we do cache the voltage value and enable status here.
*
* If regulator is initially disabled, the we set the
* cached voltage to be the smallest supported voltage
*/
if (!val) {
d[i].vsel_cache = 1;
d[i].enabled = false;
} else {
d[i].vsel_cache = val;
d[i].enabled = true;
}
rdev = devm_regulator_register(&pdev->dev, &d[i].desc, &c);
if (IS_ERR(rdev)) {
dev_err(&pdev->dev,
"failed to register %s regulator\n",
d[i].desc.name);
return PTR_ERR(rdev);
}
}
return 0;
}
static struct platform_driver bd2657_regulator = {
.driver = {
.name = "bd2657-regulator"
},
.probe = bd2657_probe,
};
module_platform_driver(bd2657_regulator);
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
MODULE_DESCRIPTION("BD2657 voltage regulator driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:bd2657-regulator");