blob: aacb8634aabdb35cae677f7e4632a6ac270a6f67 [file] [log] [blame]
/*
* Hisilicon HI655X PMIC regulator driver
*
* Copyright (c) 2015 Hisilicon Co. Ltd.
*
* Author:
* Dongbin Yu <yudongbin@huawei.com>
* Bintian Wang <bintian.wang@huawei.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <linux/regulator/hi655x-regulator.h>
#include <linux/mfd/hi655x-pmic.h>
#define FORMAT_SSI_PMUREG(base, reg) (*(volatile unsigned int *)((char *)base + (reg)))
#define BSP_REG_SETBITS(base, reg, pos, bits, val) \
((FORMAT_SSI_PMUREG(base, reg) = (FORMAT_SSI_PMUREG(base, reg) \
& (~((((unsigned int)1 << (bits)) - 1) << (pos)))) \
| ((unsigned int)((val) & (((unsigned int)1 << (bits)) - 1)) << (pos))))
#define BSP_REG_GETBITS(base, reg, pos, bits) \
(((FORMAT_SSI_PMUREG(base, reg) >> (pos)) \
&(((unsigned int)1 << (bits)) - 1)))
struct hi655x_regulator {
int status; /* this property in child node */
unsigned int off_on_delay; /* this property in parent node */
enum hi655x_regulator_type type; /* this property in child node */
int regulator_id;
/* this property must be unify which is in child node */
struct hi655x_regulator_ctrl_regs ctrl_regs;
struct hi655x_regulator_ctrl_data ctrl_data;
struct hi655x_regulator_vset_regs vset_regs;
struct hi655x_regulator_vset_data vset_data;
unsigned int vol_numb;
unsigned int *vset_table;
struct regulator_desc rdesc;
int (*dt_parse)(struct hi655x_regulator*, struct platform_device*);
};
static int hi655x_regulator_pmic_is_enabled(struct regulator_dev *rdev)
{
int ret = 0;
unsigned char value_u8 = 0;
struct hi655x_regulator *sreg = rdev_get_drvdata(rdev);
struct hi655x_regulator_ctrl_regs *ctrl_regs = &(sreg->ctrl_regs);
struct hi655x_regulator_ctrl_data *ctrl_data = &(sreg->ctrl_data);
/*
* regulator is all set,but the pmu is only subset.
* maybe this "buck"/"ldo"/"lvs" is not contrl by a core.
* and in regulator have a "status" member ("okey" or "disable").
*/
value_u8 = hi655x_pmic_reg_read(ctrl_regs->status_reg);
ret = ((int)BSP_REG_GETBITS(&value_u8, 0, ctrl_data->shift, ctrl_data->mask) & 0xff);
return ret;
}
static int hi655x_regulator_pmic_enable(struct regulator_dev *rdev)
{
int ret = 0;
unsigned char value_u8 = 0;
unsigned int value_u32 = 0;
struct hi655x_regulator *sreg = rdev_get_drvdata(rdev);
struct hi655x_regulator_ctrl_regs *ctrl_regs = &(sreg->ctrl_regs);
struct hi655x_regulator_ctrl_data *ctrl_data = &(sreg->ctrl_data);
BSP_REG_SETBITS(&value_u32, 0, ctrl_data->shift, ctrl_data->mask, 0x1);
value_u8 = (unsigned char)value_u32;
hi655x_pmic_reg_write(ctrl_regs->enable_reg, value_u8);
udelay(sreg->off_on_delay);
return ret;
}
static int hi655x_regulator_pmic_disable(struct regulator_dev *rdev)
{
int ret = 0;
int flag = 1;
unsigned char value_u8 = 0;
unsigned int value_u32 = 0;
struct hi655x_regulator *sreg = rdev_get_drvdata(rdev);
struct hi655x_regulator_ctrl_regs *ctrl_regs = &(sreg->ctrl_regs);
struct hi655x_regulator_ctrl_data *ctrl_data = &(sreg->ctrl_data);
/*
* regulator is all set,but the pmu is only subset.
* maybe this "buck"/"ldo"/"lvs" is not contrl by a core.
* and in regulator have a "status" member (okey or disable).
* maybe we can del some regulator which is not contrl by core.
*/
if (sreg->type == PMIC_BOOST_TYPE)
flag = 0;
BSP_REG_SETBITS(&value_u32, 0, ctrl_data->shift, ctrl_data->mask, flag);
value_u8 = (unsigned char)value_u32;
hi655x_pmic_reg_write(ctrl_regs->disable_reg, value_u8);
return ret;
}
static int hi655x_regulator_pmic_list_voltage_linear(struct regulator_dev *rdev,
unsigned int selector)
{
struct hi655x_regulator *sreg = rdev_get_drvdata(rdev);
/*
* regulator is all set,but the pmu is only subset.
* maybe this "buck"/"ldo"/"lvs" is not contrl by a core.
* and in regulator have a "status" member (okey or disable).
* maybe we can del some regulator which is not contrl by core.
* we will return min_uV
*/
if (sreg->type == PMIC_LVS_TYPE)
return 900000;
if (selector >= sreg->vol_numb) {
printk("selector err %s %d \n", __func__, __LINE__);
return -1;
}
return sreg->vset_table[selector];
}
static int hi655x_regulator_pmic_get_voltage(struct regulator_dev *rdev)
{
int index = 0;
unsigned int value_u32 = 0;
struct hi655x_regulator *sreg = rdev_get_drvdata(rdev);
struct hi655x_regulator_vset_regs *vset_regs = &(sreg->vset_regs);
struct hi655x_regulator_vset_data *vset_data = &(sreg->vset_data);
if (sreg->type == PMIC_LVS_TYPE)
return 900000;
value_u32 = (unsigned int)hi655x_pmic_reg_read(vset_regs->vset_reg) & 0xff;
index = ((unsigned int)BSP_REG_GETBITS(&value_u32, 0, vset_data->shift, vset_data->mask) & 0xff);
return sreg->vset_table[index];
}
static int hi655x_regulator_pmic_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV, unsigned *selector)
{
int i = 0;
int ret = 0;
int vol = 0;
unsigned char value_u8 = 0;
unsigned int value_u32 = 0;
struct hi655x_regulator *sreg = rdev_get_drvdata(rdev);
struct hi655x_regulator_vset_regs *vset_regs = &(sreg->vset_regs);
struct hi655x_regulator_vset_data *vset_data = &(sreg->vset_data);
if (sreg->type == PMIC_LVS_TYPE)
return 0;
/*
* search the matched vol and get its index
*/
for (i = 0; i < sreg->vol_numb; i++) {
vol = sreg->vset_table[i];
if ((vol >= min_uV) && (vol <= max_uV))
break;
}
if (i == sreg->vol_numb)
return -1;
value_u32 = (unsigned int)hi655x_pmic_reg_read(vset_regs->vset_reg) & 0xff;
BSP_REG_SETBITS(&value_u32, 0, vset_data->shift, vset_data->mask, i);
value_u8 = (unsigned char)value_u32;
hi655x_pmic_reg_write(vset_regs->vset_reg, value_u8);
*selector = i;
return ret;
}
static unsigned int hi655x_regulator_pmic_get_mode(struct regulator_dev *rdev)
{
return REGULATOR_MODE_NORMAL;
}
static int hi655x_regulator_pmic_set_mode(struct regulator_dev *rdev,
unsigned int mode)
{
return 0;
}
static unsigned int hi655x_regulator_pmic_get_optimum_mode(struct regulator_dev *rdev,
int input_uV, int output_uV, int load_uA)
{
return REGULATOR_MODE_NORMAL;
}
static struct regulator_ops hi655x_regulator_pmic_rops = {
.is_enabled = hi655x_regulator_pmic_is_enabled,
.enable = hi655x_regulator_pmic_enable,
.disable = hi655x_regulator_pmic_disable,
.list_voltage = hi655x_regulator_pmic_list_voltage_linear,
.get_voltage = hi655x_regulator_pmic_get_voltage,
.set_voltage = hi655x_regulator_pmic_set_voltage,
.get_mode = hi655x_regulator_pmic_get_mode,
.set_mode = hi655x_regulator_pmic_set_mode,
.get_optimum_mode = hi655x_regulator_pmic_get_optimum_mode,
};
static int hi655x_regualtor_dt_parse_common(struct hi655x_regulator *sreg,
struct platform_device *pdev)
{
return 0;
}
static int hi655x_regualtor_pmic_dt_parse(struct hi655x_regulator *sreg,
struct platform_device *pdev)
{
int ret;
ret = hi655x_regualtor_dt_parse_common(sreg, pdev);
return ret;
}
static const struct hi655x_regulator hi655x_regulator_pmic = {
.rdesc = {
.ops = &hi655x_regulator_pmic_rops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
},
.dt_parse = hi655x_regualtor_pmic_dt_parse,
};
static struct of_device_id of_hi655x_regulator_match_tbl[] = {
{
.compatible = "hisilicon,hi6552-regulator-pmic",
.data = &hi655x_regulator_pmic,
},
{ /* end */ }
};
static struct regulator_init_data *fake_of_get_regulator_init_data(struct device *dev,
struct device_node *np)
{
struct regulator_init_data *init_data = NULL;
const __be32 *num_consumer_supplies = NULL;
struct regulator_consumer_supply *consumer_supplies = NULL;
int consumer_id = 0;
init_data = devm_kzalloc(dev, sizeof(*init_data), GFP_KERNEL);
if (!init_data)
return NULL;
init_data->supply_regulator = of_get_property(np, "hisilicon,regulator-supply", NULL);
num_consumer_supplies = of_get_property(np, "hisilicon,num_consumer_supplies", NULL);
if ((NULL == num_consumer_supplies) || (0 == *num_consumer_supplies)) {
dev_warn(dev, "%s no consumer_supplies \n", __func__);
return init_data;
}
init_data->num_consumer_supplies = be32_to_cpu(*num_consumer_supplies);
init_data->consumer_supplies = (struct regulator_consumer_supply *)devm_kzalloc(dev,
init_data->num_consumer_supplies * sizeof(struct regulator_consumer_supply), GFP_KERNEL);
if (NULL == init_data->consumer_supplies) {
dev_err(dev, "%s devm_kzalloc consumer_supplies err\n", __func__);
return NULL;
}
consumer_supplies = init_data->consumer_supplies ;
for (consumer_id = 0; consumer_id < init_data->num_consumer_supplies;
consumer_id++, consumer_supplies++) {
int ret = of_property_read_string_index(np, "hisilicon,consumer-supplies", consumer_id, &consumer_supplies->supply);
if (ret)
dev_err(dev, "\n %s of_property_read_string_index consumer-supplies err\n", np->name);
}
return init_data;
}
static int fake_of_get_regulator_constraint(struct regulation_constraints *constraints,
struct device_node *np)
{
const __be32 *min_uV, *max_uV;
unsigned int *valid_modes_mask;
unsigned int *valid_ops_mask;
unsigned int *initial_mode;
if (!np)
return -1;
if (!constraints)
return -1;
constraints->name = of_get_property(np, "regulator-name", NULL);
min_uV = of_get_property(np, "regulator-min-microvolt", NULL);
if (min_uV) {
(constraints)->min_uV = be32_to_cpu(*min_uV);
(constraints)->min_uA = be32_to_cpu(*min_uV);
}
max_uV = of_get_property(np, "regulator-max-microvolt", NULL);
if (max_uV) {
constraints->max_uV = be32_to_cpu(*max_uV);
constraints->max_uA = be32_to_cpu(*max_uV);
}
valid_modes_mask = (unsigned int *)of_get_property(np, "hisilicon,valid-modes-mask", NULL);
if (valid_modes_mask)
constraints->valid_modes_mask = be32_to_cpu(*valid_modes_mask);
valid_ops_mask = (unsigned int *)of_get_property(np, "hisilicon,valid-ops-mask", NULL);
if (valid_ops_mask)
constraints->valid_ops_mask = be32_to_cpu(*valid_ops_mask);
initial_mode = (unsigned int *)of_get_property(np, "hisilicon,initial-mode", NULL);
if (initial_mode)
constraints->initial_mode = be32_to_cpu(*initial_mode);
constraints->always_on = !!(of_find_property(np, "regulator-always-on", NULL));
constraints->boot_on = !!(of_find_property(np, "regulator-boot-on", NULL));
return 0;
}
static int fake_of_get_regulator_sreg(struct hi655x_regulator *sreg, struct device *dev,
struct device_node *np)
{
int *vol_numb;
unsigned int *off_on_delay;
enum hi655x_regulator_type *regulator_type;
const char *status = NULL;
unsigned int *vset_table = NULL;
int *regulator_id;
status = of_get_property(np, "hisilicon,regulator-status", NULL);
if (status)
sreg->status = !(strcmp(status, "okey"));
regulator_type = (enum hi655x_regulator_type *)of_get_property(np, "hisilicon,regulator-type", NULL);
if (regulator_type)
sreg->type = be32_to_cpu(*regulator_type);
off_on_delay = (unsigned int *)of_get_property(np, "hisilicon,off-on-delay", NULL);
if (off_on_delay)
sreg->off_on_delay = be32_to_cpu(*off_on_delay);
of_property_read_u32_array(np, "hisilicon,ctrl-regs", (unsigned int *)(&sreg->ctrl_regs), 0x3);
of_property_read_u32_array(np, "hisilicon,ctrl-data", (unsigned int *)(&sreg->ctrl_data), 0x2);
of_property_read_u32_array(np, "hisilicon,vset-regs", (unsigned int *)(&sreg->vset_regs), 0x1);
of_property_read_u32_array(np, "hisilicon,vset-data", (unsigned int *)(&sreg->vset_data), 0x2);
vol_numb = (int *)of_get_property(np, "hisilicon,regulator-n-vol", NULL);
if (vol_numb)
sreg->vol_numb = be32_to_cpu(*vol_numb);
regulator_id = (int *)of_get_property(np, "hisilicon,hisi-scharger-regulator-id", NULL);
if (regulator_id)
sreg->regulator_id = be32_to_cpu(*regulator_id);
vset_table = devm_kzalloc(dev, sreg->vol_numb * sizeof(int), GFP_KERNEL);
if (!vset_table)
return -1;
of_property_read_u32_array(np, "hisilicon,vset-table", (unsigned int *)vset_table, sreg->vol_numb);
sreg->vset_table = vset_table;
return 0;
}
static int hi655x_regulator_probe(struct platform_device *pdev)
{
int ret = 0;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct regulator_dev *rdev = NULL;
struct regulator_desc *rdesc = NULL;
struct hi655x_regulator *sreg = NULL;
struct regulator_init_data *initdata = NULL;
const struct of_device_id *match = NULL;
const struct hi655x_regulator *template = NULL;
struct regulator_config config = { };
/* to check which type of regulator this is */
match = of_match_device(of_hi655x_regulator_match_tbl, &pdev->dev);
if (NULL == match) {
dev_err(dev, "of match hi655x regulator fail!\n\r");
return -EINVAL;
}
/*tempdev is regulator device*/
template = match->data;
/*just for getting "std regulator node" value-key about constraint*/
initdata = fake_of_get_regulator_init_data(dev, np);
if (!initdata) {
dev_err(dev, "get regulator init data error !\n");
return -EINVAL;
}
ret = fake_of_get_regulator_constraint(&initdata->constraints, np);
if (!!ret) {
dev_err(dev, "get regulator constraint error !\n");
return -EINVAL;
}
/* TODO:hi655x regulator supports two modes */
sreg = kmemdup(template, sizeof(*sreg), GFP_KERNEL);
if (!sreg)
return -ENOMEM;
if (0 != fake_of_get_regulator_sreg(sreg, dev, np)) {
kfree(sreg);
return -EINVAL;
}
rdesc = &sreg->rdesc;
rdesc->n_voltages = sreg->vol_numb;
rdesc->name = initdata->constraints.name;
rdesc->id = sreg->regulator_id;
rdesc->min_uV = initdata->constraints.min_uV;
/* to parse device tree data for regulator specific */
config.dev = &pdev->dev;
config.init_data = initdata;
config.driver_data = sreg;
config.of_node = pdev->dev.of_node;
/* register regulator */
rdev = regulator_register(rdesc, &config);
if (IS_ERR(rdev)) {
dev_err(dev, "regulator failed to register %s\n", rdesc->name);
ret = PTR_ERR(rdev);
return -EINVAL;
}
platform_set_drvdata(pdev, rdev);
return ret;
}
static int hi655x_regulator_remove(struct platform_device *pdev)
{
return 0;
}
static struct platform_driver hi655x_regulator_driver = {
.driver = {
.name = "hi655x_regulator",
.owner = THIS_MODULE,
.of_match_table = of_hi655x_regulator_match_tbl,
},
.probe = hi655x_regulator_probe,
.remove = hi655x_regulator_remove,
};
static int __init hi655x_regulator_init(void)
{
return platform_driver_register(&hi655x_regulator_driver);
}
static void __exit hi655x_regulator_exit(void)
{
platform_driver_unregister(&hi655x_regulator_driver);
}
fs_initcall(hi655x_regulator_init);
module_exit(hi655x_regulator_exit);
MODULE_AUTHOR("Dongbin Yu <yudongbin@huawei.com>");
MODULE_DESCRIPTION("Hisilicon HI655X PMIC regulator driver");
MODULE_LICENSE("GPL v2");