blob: 2eab7c158e2d21fbd4aedbfc16d11f71aa7dd4c4 [file] [log] [blame]
/*
* OMAP Generic PMIC Regulator
*
* Idea based on arch/arm/mach-omap2/omap_twl.c
* Copyright (C) 2010 Texas Instruments Incorporated.
* Thara Gopinath
* Copyright (C) 2009 Texas Instruments Incorporated.
* Nishanth Menon
* Copyright (C) 2009 Nokia Corporation
* Paul Walmsley
*
* Copyright (C) 2013 Texas Instruments Incorporated
* Taras Kondratiuk
* Grygorii Strashko
* Nishanth Menon
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/omap-pmic-regulator.h>
#define DRIVER_NAME "omap-pmic"
static DEFINE_MUTEX(omap_pmic_cops_mutex);
static struct omap_pmic_controller_ops *pmic_cops;
/**
* omap_pmic_register_controller_ops() - Register voltage operations
* @cops: voltage operations
*
* It is expected that appropriate controller register it's functions
* with this driver using this interface, If this is not done, the probe
* for the corresponding device will defer till it fails.
*
* Return: -EBUSY if already registered, else returns 0
*/
int omap_pmic_register_controller_ops(struct omap_pmic_controller_ops *cops)
{
int ret = 0;
mutex_lock(&omap_pmic_cops_mutex);
if (pmic_cops) {
pr_err("Controller operations already registered\n");
ret = -EBUSY;
goto out;
}
if (!cops->devm_pmic_register || !cops->voltage_set ||
!cops->voltage_get || !cops->voltage_get_range) {
pr_err("Missing operations!\n");
ret = -EINVAL;
goto out;
}
pmic_cops = cops;
out:
mutex_unlock(&omap_pmic_cops_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(omap_pmic_register_controller_ops);
/**
* omap_pmic_vsel_to_uv() - Convert voltage selector(vsel) to microvolts
* @pmic: pointer to pmic struct
* @vsel: voltage selector(vsel)
* @uv: If conversion is successful, returns the voltage in micro volts
*
* Return: 0 if conversion is successful and *uv has proper value, else
* appropriate error value for failure.
*/
static int omap_pmic_vsel_to_uv(struct omap_pmic *pmic, u8 vsel, u32 *uv)
{
u32 tmp = vsel;
const struct omap_pmic_info *info;
if (!pmic || !uv) {
pr_err("Bad parameters pmic=%p uv=%p!\n", pmic, uv);
return -EINVAL;
}
info = pmic->info;
if (info->voltage_selector_mask) {
tmp &= info->voltage_selector_mask;
tmp >>= __ffs(info->voltage_selector_mask);
}
if (!tmp && info->voltage_selector_zero)
goto out;
tmp -= info->voltage_selector_offset;
tmp *= info->step_size_uV;
tmp += info->min_uV;
if (tmp < info->min_uV || tmp > info->max_uV) {
dev_dbg(pmic->dev, "%s: Out of range 0x%02x[%d] (%d <-> %d)\n",
__func__, vsel, tmp, info->min_uV, info->max_uV);
return -ERANGE;
}
out:
*uv = tmp;
dev_dbg(pmic->dev, "%s: uv=%d vsel=0x%02x\n", __func__, *uv, vsel);
return 0;
}
/**
* omap_pmic_uv_to_vsel() - Convert microvolts to voltage selector(vsel)
* @pmic: pointer to pmic struct
* @uv: voltage in micro volts
* @vsel: If conversion is successful, voltage selector(vsel)
*
* Return: 0 if conversion is successful and *vsel has proper value, else
* appropriate error value for failure.
*/
static int omap_pmic_uv_to_vsel(struct omap_pmic *pmic, u32 uv, u8 *vsel)
{
u32 tmp = uv;
const struct omap_pmic_info *info;
if (!pmic || !vsel) {
pr_err("Bad parameters pmic=%p vsel=%p!\n", pmic, vsel);
return -EINVAL;
}
info = pmic->info;
if (!tmp && info->voltage_selector_zero)
goto skip_convert;
if (tmp > info->max_uV)
goto skip_convert;
tmp -= info->min_uV;
tmp = DIV_ROUND_UP(tmp, info->step_size_uV);
tmp += info->voltage_selector_offset;
skip_convert:
if (tmp > 0xFF) {
dev_dbg(pmic->dev, "%s: Out of range 0x%04x[%d] (%d - %d)\n",
__func__, tmp, uv, info->min_uV, info->max_uV);
return -ERANGE;
}
if (info->voltage_selector_mask) {
tmp <<= __ffs(info->voltage_selector_mask);
if (tmp > 0xFF) {
dev_warn(pmic->dev, "%s: Out of range 0x%04x[%d]\n",
__func__, tmp, uv);
return -ERANGE;
}
tmp &= info->voltage_selector_mask;
}
tmp |= info->voltage_selector_setbits;
*vsel = tmp;
dev_dbg(pmic->dev, "%s: uv=%d vsel=0x%02x\n", __func__, uv, *vsel);
return 0;
}
/**
* omap_pmic_set_voltage() - regulator interface to set voltage
* @rdev: regulator device
* @min_uV: min voltage in micro-volts
* @max_uV: max voltage in micro-volts
* @unused: unused.. we dont use sel
*
* Return: -ERANGE for out of range values, appropriate error code if conversion
* fails, else returns 0.
*/
static int omap_pmic_set_voltage(struct regulator_dev *rdev, int min_uV,
int max_uV, unsigned *unused)
{
struct omap_pmic *pmic = rdev_get_drvdata(rdev);
return pmic_cops->voltage_set(pmic->v_dev, min_uV);
}
/**
* omap_pmic_get_voltage() - regulator interface to get voltage
* @rdev: regulator device
*
* Return: current voltage set on PMIC OR appropriate error value
*/
static int omap_pmic_get_voltage(struct regulator_dev *rdev)
{
struct omap_pmic *pmic = rdev_get_drvdata(rdev);
int ret;
u32 uv;
ret = pmic_cops->voltage_get(pmic->v_dev, &uv);
if (ret)
return ret;
return uv;
}
static struct omap_pmic_ops omap_generic_pmic_ops = {
.vsel_to_uv = omap_pmic_vsel_to_uv,
.uv_to_vsel = omap_pmic_uv_to_vsel,
};
static struct regulator_ops omap_pmic_reg_ops = {
.list_voltage = regulator_list_voltage_linear,
.set_voltage = omap_pmic_set_voltage,
.get_voltage = omap_pmic_get_voltage,
};
/**
* omap_pmic_of_setup_gpios() - Setup GPIO array if needed.
* @dev: device to pick up the gpios from
*/
static int omap_pmic_of_setup_gpios(struct device *dev)
{
struct device_node *node = dev->of_node;
int num_gpios, i, ret;
num_gpios = of_gpio_count(node);
if (num_gpios < 0)
return 0;
for (i = 0; i < num_gpios; i++) {
int gpio, level;
enum of_gpio_flags flags;
gpio = of_get_gpio_flags(node, i, &flags);
if (!gpio_is_valid(gpio)) {
dev_err(dev, "Invalid GPIO[%d]: %d\n", i, gpio);
return -EINVAL;
}
ret = devm_gpio_request(dev, gpio, dev_name(dev));
if (ret) {
dev_err(dev, "Unable to get GPIO %d (%d)\n", gpio, ret);
return ret;
}
level = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
ret = gpio_direction_output(gpio, level);
if (ret) {
dev_err(dev, "Failed to set GPIO %d to %d (%d)\n",
gpio, level, ret);
return ret;
}
dev_dbg(dev, "GPIO=%d set_to=%d flags=0x%08x\n", gpio,
level, flags);
}
return 0;
}
/**
* omap_pmic_parse_of() - Do DT OF node parsing
* @pmic: pointer to PMIC
*/
static int omap_pmic_parse_of(struct omap_pmic *pmic)
{
struct device *dev = pmic->dev;
struct device_node *node = dev->of_node;
u32 val = 0;
char *pname;
int ret;
pname = "ti,boot-voltage-micro-volts";
ret = of_property_read_u32(node, pname, &val);
if (!ret) {
if (!val)
goto invalid_of_property;
pmic->boot_voltage_uV = val;
}
return ret;
invalid_of_property:
if (!ret) {
dev_err(dev, "Invalid value 0x%x[%d] in '%s' property.\n",
val, val, pname);
ret = -EINVAL;
} else {
dev_err(dev, "Missing/Invalid '%s' property - error(%d)\n",
pname, ret);
}
return ret;
}
static const struct omap_pmic_info omap_twl4030_vdd1 = {
.slave_addr = 0x12,
.voltage_reg_addr = 0x00,
.cmd_reg_addr = 0x00,
.i2c_timeout_us = 200,
.slew_rate_uV = 4000,
.step_size_uV = 12500,
.min_uV = 600000,
.max_uV = 1450000,
.voltage_selector_offset = 0,
.voltage_selector_mask = 0x7F,
.voltage_selector_setbits = 0x0,
.voltage_selector_zero = false,
};
static const struct omap_pmic_info omap_twl4030_vdd2 = {
.slave_addr = 0x12,
.voltage_reg_addr = 0x01,
.cmd_reg_addr = 0x01,
.i2c_timeout_us = 200,
.slew_rate_uV = 4000,
.step_size_uV = 12500,
.min_uV = 600000,
.max_uV = 1450000,
.voltage_selector_offset = 0,
.voltage_selector_mask = 0x7F,
.voltage_selector_setbits = 0x0,
.voltage_selector_zero = false,
};
static const struct omap_pmic_info omap_twl6030_vcore1 = {
.slave_addr = 0x12,
.voltage_reg_addr = 0x55,
.cmd_reg_addr = 0x56,
.i2c_timeout_us = 200,
.slew_rate_uV = 9000,
.step_size_uV = 12660,
.min_uV = 709000,
.max_uV = 1418000,
.voltage_selector_offset = 0x1,
.voltage_selector_mask = 0x7F,
.voltage_selector_setbits = 0x0,
.voltage_selector_zero = true,
};
static const struct omap_pmic_info omap_twl6030_vcore2 = {
.slave_addr = 0x12,
.voltage_reg_addr = 0x5b,
.cmd_reg_addr = 0x5c,
.i2c_timeout_us = 200,
.slew_rate_uV = 9000,
.step_size_uV = 12660,
.min_uV = 709000,
.max_uV = 1418000,
.voltage_selector_offset = 0x1,
.voltage_selector_mask = 0x7F,
.voltage_selector_setbits = 0x0,
.voltage_selector_zero = true,
};
static const struct omap_pmic_info omap_twl6030_vcore3 = {
.slave_addr = 0x12,
.voltage_reg_addr = 0x61,
.cmd_reg_addr = 0x62,
.i2c_timeout_us = 200,
.slew_rate_uV = 9000,
.step_size_uV = 12660,
.min_uV = 709000,
.max_uV = 1418000,
.voltage_selector_offset = 0x1,
.voltage_selector_mask = 0x7F,
.voltage_selector_setbits = 0x0,
.voltage_selector_zero = true,
};
static const struct omap_pmic_setup_commands omap_tps62361_cmds[] = {
{.reg = 0x06, .cmd_val = 0x06}, /* TPS6236X_RAMP_CTRL 32mV/uS */
{.reg = 0x04, .cmd_val = 0xc0}, /* TPS6236X_CTRL VSEL0 pull down */
{.reg = 0x05, .cmd_val = 0x00}, /* REG_TPS6236X_TEMP enable tshut */
};
static const struct omap_pmic_info omap_tps62361 = {
.slave_addr = 0x60,
.voltage_reg_addr = 0x01,
.cmd_reg_addr = 0x01,
.i2c_timeout_us = 200,
.slew_rate_uV = 32000,
.step_size_uV = 10000,
.min_uV = 500000,
.max_uV = 1770000,
.voltage_selector_offset = 0x0,
.voltage_selector_mask = 0x7F,
.voltage_selector_setbits = 0x80, /* PFM mode */
.voltage_selector_zero = false,
.setup_command_list = omap_tps62361_cmds,
.setup_num_commands = ARRAY_SIZE(omap_tps62361_cmds),
};
static const struct omap_pmic_info omap_twl6032_smps1 = {
.slave_addr = 0x12,
.voltage_reg_addr = 0x55,
.cmd_reg_addr = 0x56,
.i2c_timeout_us = 200,
.slew_rate_uV = 9000,
.step_size_uV = 12660,
.min_uV = 709000,
.max_uV = 1418000,
.voltage_selector_offset = 0x1,
.voltage_selector_mask = 0x7F,
.voltage_selector_setbits = 0x0,
.voltage_selector_zero = true,
};
static const struct omap_pmic_info omap_twl6032_smps2 = {
.slave_addr = 0x12,
.voltage_reg_addr = 0x5b,
.cmd_reg_addr = 0x5c,
.i2c_timeout_us = 200,
.slew_rate_uV = 9000,
.step_size_uV = 12660,
.min_uV = 709000,
.max_uV = 1418000,
.voltage_selector_offset = 0x1,
.voltage_selector_mask = 0x7F,
.voltage_selector_setbits = 0x0,
.voltage_selector_zero = true,
};
static const struct omap_pmic_info omap_twl6032_smps5 = {
.slave_addr = 0x12,
.voltage_reg_addr = 0x49,
.cmd_reg_addr = 0x4a,
.i2c_timeout_us = 200,
.slew_rate_uV = 9000,
.step_size_uV = 12660,
.min_uV = 709000,
.max_uV = 1418000,
.voltage_selector_offset = 0x1,
.voltage_selector_mask = 0x7F,
.voltage_selector_setbits = 0x0,
.voltage_selector_zero = true,
};
static const struct omap_pmic_info omap_twl6035_smps1 = {
.slave_addr = 0x12,
.voltage_reg_addr = 0x23,
.cmd_reg_addr = 0x22,
.i2c_timeout_us = 200,
.slew_rate_uV = 220,
.step_size_uV = 10000,
.min_uV = 500000,
.max_uV = 1650000,
.voltage_selector_offset = 0x6,
.voltage_selector_mask = 0x7F,
.voltage_selector_setbits = 0x0,
.voltage_selector_zero = true,
};
static const struct omap_pmic_info omap_twl6035_smps4 = {
.slave_addr = 0x12,
.voltage_reg_addr = 0x2b,
.cmd_reg_addr = 0x2a,
.i2c_timeout_us = 200,
.slew_rate_uV = 220,
.step_size_uV = 10000,
.min_uV = 500000,
.max_uV = 1650000,
.voltage_selector_offset = 0x6,
.voltage_selector_mask = 0x7F,
.voltage_selector_setbits = 0x0,
.voltage_selector_zero = true,
};
static const struct omap_pmic_info omap_twl6035_smps8 = {
.slave_addr = 0x12,
.voltage_reg_addr = 0x37,
.cmd_reg_addr = 0x36,
.i2c_timeout_us = 200,
.slew_rate_uV = 220,
.step_size_uV = 10000,
.min_uV = 500000,
.max_uV = 1650000,
.voltage_selector_offset = 0x6,
.voltage_selector_mask = 0x7F,
.voltage_selector_setbits = 0x0,
.voltage_selector_zero = true,
};
static const struct omap_pmic_info omap_tps65912_dcdc1 = {
.slave_addr = 0x13,
.voltage_reg_addr = 0x04,
.cmd_reg_addr = 0x04,
.i2c_timeout_us = 200,
.slew_rate_uV = 6250,
.step_size_uV = 12500,
.min_uV = 700000,
.max_uV = 1487500,
.voltage_selector_offset = 0,
.voltage_selector_mask = 0x3F,
.voltage_selector_setbits = 0x0,
.voltage_selector_zero = true,
};
static const struct omap_pmic_info omap_tps65912_dcdc4 = {
.slave_addr = 0x13,
.voltage_reg_addr = 0x0d,
.cmd_reg_addr = 0x0d,
.i2c_timeout_us = 200,
.slew_rate_uV = 6250,
.step_size_uV = 12500,
.min_uV = 500000,
.max_uV = 1287500,
.voltage_selector_offset = 0,
.voltage_selector_mask = 0x3F,
.voltage_selector_setbits = 0x0,
.voltage_selector_zero = true,
};
static const struct of_device_id omap_pmic_of_match_tbl[] = {
{.compatible = "ti,omap-twl4030-vdd1", .data = &omap_twl4030_vdd1,},
{.compatible = "ti,omap-twl4030-vdd2", .data = &omap_twl4030_vdd2,},
{.compatible = "ti,omap-twl6030-vcore1", .data = &omap_twl6030_vcore1,},
{.compatible = "ti,omap-twl6030-vcore2", .data = &omap_twl6030_vcore2,},
{.compatible = "ti,omap-twl6030-vcore3", .data = &omap_twl6030_vcore3,},
{.compatible = "ti,omap-tps62361", .data = &omap_tps62361,},
{.compatible = "ti,omap-twl6032-smps1", .data = &omap_twl6032_smps1,},
{.compatible = "ti,omap-twl6032-smps2", .data = &omap_twl6032_smps2,},
{.compatible = "ti,omap-twl6032-smps5", .data = &omap_twl6032_smps5,},
{.compatible = "ti,omap-twl6035-smps1", .data = &omap_twl6035_smps1,},
{.compatible = "ti,omap-twl6035-smps4", .data = &omap_twl6035_smps4,},
{.compatible = "ti,omap-twl6035-smps8", .data = &omap_twl6035_smps8,},
{.compatible = "ti,omap-tps65912-dcdc1", .data = &omap_tps65912_dcdc1,},
{.compatible = "ti,omap-tps65912-dcdc4", .data = &omap_tps65912_dcdc4,},
{},
};
MODULE_DEVICE_TABLE(of, omap_pmic_of_match_tbl);
static int omap_pmic_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
const struct of_device_id *match;
struct omap_pmic *pmic;
struct regulator_desc *desc;
struct regulation_constraints *c;
struct regulator_config config = { };
struct regulator_init_data *initdata = NULL;
struct regulator_dev *rdev = NULL;
int ret = 0;
bool ops_ready;
if (!node) {
dev_err(dev, "%s: missing device tree nodes?\n", __func__);
return -EINVAL;
}
mutex_lock(&omap_pmic_cops_mutex);
ops_ready = pmic_cops ? true : false;
mutex_unlock(&omap_pmic_cops_mutex);
if (!ops_ready) {
dev_dbg(dev, "Voltage Operations not ready yet..\n");
return -EPROBE_DEFER;
}
match = of_match_device(omap_pmic_of_match_tbl, dev);
if (!match) {
/* We do not expect this to happen */
dev_err(dev, "%s: Unable to match device\n", __func__);
return -ENODEV;
}
if (!match->data) {
dev_err(dev, "%s: Bad data in match\n", __func__);
return -EINVAL;
}
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc) {
dev_err(dev, "%s: unable to allocate desc\n", __func__);
return -ENOMEM;
}
pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
if (!pmic) {
dev_err(dev, "%s: unable to allocate pmic\n", __func__);
return -ENOMEM;
}
/* Read mandatory OF parameters */
pmic->dev = dev;
pmic->ops = &omap_generic_pmic_ops;
pmic->info = match->data;
initdata = of_get_regulator_init_data(dev, node);
if (!initdata) {
dev_err(dev, "%s: Unable to alloc regulator init data\n",
__func__);
return -ENOMEM;
}
c = &initdata->constraints;
/* Constraint to PMIC limits */
if (pmic->info->min_uV > c->min_uV)
c->min_uV = pmic->info->min_uV;
if (pmic->info->max_uV < c->max_uV)
c->max_uV = pmic->info->max_uV;
ret = omap_pmic_parse_of(pmic);
if (ret)
return ret;
ret = omap_pmic_of_setup_gpios(dev);
if (ret)
return ret;
pmic->v_dev = pmic_cops->devm_pmic_register(dev, pmic);
if (IS_ERR(pmic->v_dev)) {
dev_dbg(dev, "Registration of pmic failed (%d)\n", ret);
ret = PTR_ERR(pmic->v_dev);
return ret;
}
desc->name = dev_name(dev);
desc->owner = THIS_MODULE;
desc->type = REGULATOR_VOLTAGE;
desc->ops = &omap_pmic_reg_ops;
desc->uV_step = pmic->info->step_size_uV;
desc->ramp_delay = pmic->info->slew_rate_uV;
c->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE;
c->always_on = true;
ret = pmic_cops->voltage_get_range(pmic->v_dev, &c->min_uV, &c->max_uV);
if (ret) {
dev_err(dev, "Voltage Range get failed (%d)\n", ret);
return ret;
}
config.dev = dev;
config.init_data = initdata;
config.driver_data = pmic;
config.of_node = node;
rdev = regulator_register(desc, &config);
if (IS_ERR(rdev)) {
ret = PTR_ERR(rdev);
dev_err(dev, "%s: failed to register regulator(%d)\n",
__func__, ret);
return ret;
}
platform_set_drvdata(pdev, rdev);
return ret;
}
static struct platform_driver omap_pmic_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(omap_pmic_of_match_tbl),
},
.probe = omap_pmic_probe,
};
static int __init omap_pmic_init(void)
{
int ret;
ret = platform_driver_register(&omap_pmic_driver);
if (ret)
pr_err("driver register failed for omap_pmic (%d)\n", ret);
return ret;
}
device_initcall_sync(omap_pmic_init);
static void __exit omap_pmic_exit(void)
{
platform_driver_unregister(&omap_pmic_driver);
}
module_exit(omap_pmic_exit);
MODULE_DESCRIPTION("OMAP Generic PMIC Regulator");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRIVER_NAME);
MODULE_AUTHOR("Texas Instruments Inc.");