495 lines
12 KiB
C
495 lines
12 KiB
C
/*
|
|
* Copyright (c) 2015 LGE Inc. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; 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) "%s: " fmt, __func__
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/regulator/of_regulator.h>
|
|
#include <linux/regulator/driver.h>
|
|
#include <linux/regulator/machine.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/delay.h>
|
|
|
|
#ifdef DW8768_DEBUG
|
|
#define dw_info(...) pr_info(__VA_ARGS__)
|
|
#else
|
|
#define dw_info(...) do { } while (0)
|
|
#endif
|
|
|
|
struct dw8768_regulator {
|
|
struct device *dev;
|
|
struct regulator_desc rdesc;
|
|
struct regulator_dev *rdev;
|
|
struct regulator_init_data *init_data;
|
|
struct regmap *i2c_regmap;
|
|
struct device_node *reg_node;
|
|
int ena_gpio;
|
|
int enm_gpio;
|
|
int pre_on_usleep;
|
|
int post_on_usleep;
|
|
int pre_off_usleep;
|
|
int post_off_usleep;
|
|
bool is_enabled;
|
|
int curr_uV;
|
|
u8 vol_set_val;
|
|
bool vol_set_postponed;
|
|
};
|
|
|
|
#define DW8768_REG_VPOS_ADDR 0x00
|
|
#define DW8768_REG_VNEG_ADDR 0x01
|
|
#define DW8768_REG_V_MASK 0x1f
|
|
#define DW8768_REG_EN_ADDR 0x05 /* when no enm*/
|
|
|
|
#define DW8768_VOLTAGE_MIN 4000000
|
|
#define DW8768_VOLTAGE_MAX 6000000
|
|
#define DW8768_VOLTAGE_STEP 100000
|
|
#define DW8768_VOLTAGE_LEVELS \
|
|
((DW8768_VOLTAGE_MAX - DW8768_VOLTAGE_MIN) \
|
|
/ DW8768_VOLTAGE_STEP + 1)
|
|
|
|
#define DW8768_VOLTAGE_DEFAULT 5500000
|
|
|
|
static struct of_regulator_match dw8768_reg_matches[] = {
|
|
{ .name = "dw8768-dsv", .driver_data = (void *)0 },
|
|
};
|
|
|
|
static int dw8768_regulator_enable(struct regulator_dev *rdev)
|
|
{
|
|
struct dw8768_regulator *reg_data = rdev_get_drvdata(rdev);
|
|
int rc = 0;
|
|
|
|
if (!rdev->regmap) {
|
|
pr_err("invalid regmap\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
dw_info("enable, postponed:%d\n", reg_data->vol_set_postponed);
|
|
|
|
if (reg_data->pre_on_usleep)
|
|
usleep(reg_data->pre_on_usleep);
|
|
|
|
if (gpio_is_valid(reg_data->ena_gpio)) {
|
|
gpio_set_value(reg_data->ena_gpio, 1);
|
|
if (gpio_is_valid(reg_data->enm_gpio)) {
|
|
gpio_set_value(reg_data->enm_gpio, 1);
|
|
} else {
|
|
rc = regmap_write(rdev->regmap,
|
|
DW8768_REG_EN_ADDR, 0x0F);
|
|
if (rc)
|
|
pr_err("Failed to write i2c. rc=%d\n", rc);
|
|
}
|
|
reg_data->is_enabled = true;
|
|
} else {
|
|
pr_err("gpio ena is not configured\n");
|
|
rc = -EINVAL;
|
|
}
|
|
|
|
if (reg_data->vol_set_postponed) {
|
|
rc = regmap_write(rdev->regmap, DW8768_REG_VPOS_ADDR,
|
|
reg_data->vol_set_val);
|
|
if (rc) {
|
|
pr_err("failed to write postponed +V(%d)\n",
|
|
reg_data->vol_set_val);
|
|
return rc;
|
|
}
|
|
rc = regmap_write(rdev->regmap, DW8768_REG_VNEG_ADDR,
|
|
reg_data->vol_set_val);
|
|
if (rc) {
|
|
pr_err("failed to write postponed -V(%d)\n",
|
|
reg_data->vol_set_val);
|
|
return rc;
|
|
}
|
|
reg_data->vol_set_postponed = false;
|
|
}
|
|
|
|
if (reg_data->post_on_usleep)
|
|
usleep(reg_data->post_on_usleep);
|
|
return rc;
|
|
}
|
|
|
|
static int dw8768_regulator_disable(struct regulator_dev *rdev)
|
|
{
|
|
struct dw8768_regulator *reg_data = rdev_get_drvdata(rdev);
|
|
int rc = 0;
|
|
|
|
if (!rdev->regmap) {
|
|
pr_err("invalid regmap\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
dw_info("disable, postponed:%d\n", reg_data->vol_set_postponed);
|
|
|
|
if (reg_data->pre_off_usleep)
|
|
usleep(reg_data->pre_off_usleep);
|
|
|
|
if (gpio_is_valid(reg_data->ena_gpio)) {
|
|
if (gpio_is_valid(reg_data->enm_gpio)) {
|
|
gpio_set_value(reg_data->enm_gpio, 0);
|
|
} else {
|
|
rc = regmap_write(rdev->regmap,
|
|
DW8768_REG_EN_ADDR, 0x07);
|
|
if (rc)
|
|
pr_err("Failed to write i2c. rc=%d\n", rc);
|
|
}
|
|
gpio_set_value(reg_data->ena_gpio, 0);
|
|
reg_data->is_enabled = false;
|
|
} else {
|
|
pr_err("gpio ena is not configured\n");
|
|
rc = -EINVAL;
|
|
}
|
|
|
|
if (reg_data->post_off_usleep)
|
|
usleep(reg_data->post_off_usleep);
|
|
return rc;
|
|
}
|
|
|
|
static int dw8768_regulator_is_enabled(struct regulator_dev *rdev)
|
|
{
|
|
struct dw8768_regulator *reg_data = rdev_get_drvdata(rdev);
|
|
|
|
return reg_data->is_enabled ? 1 : 0;
|
|
}
|
|
|
|
static int dw8768_regulator_set_voltage(struct regulator_dev *rdev,
|
|
int min_uV, int max_uV, unsigned *selector)
|
|
{
|
|
struct dw8768_regulator *reg_data = rdev_get_drvdata(rdev);
|
|
int rc, val, new_uV;
|
|
|
|
if (!rdev->regmap) {
|
|
pr_err("invalid regmap\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
val = DIV_ROUND_UP(min_uV - DW8768_VOLTAGE_MIN, DW8768_VOLTAGE_STEP);
|
|
val = val & DW8768_REG_V_MASK;
|
|
new_uV = DW8768_VOLTAGE_MIN + (val * DW8768_VOLTAGE_STEP);
|
|
if (new_uV == reg_data->curr_uV) {
|
|
dw_info("curV and newV are same\n");
|
|
return 0;
|
|
}
|
|
if (new_uV > max_uV) {
|
|
pr_err("failed to set voltage (%d %d)\n", min_uV, max_uV);
|
|
return -EINVAL;
|
|
}
|
|
|
|
reg_data->vol_set_val = val;
|
|
if (!reg_data->is_enabled) {
|
|
reg_data->vol_set_postponed = true;
|
|
} else {
|
|
rc = regmap_write(rdev->regmap, DW8768_REG_VPOS_ADDR, val);
|
|
if (rc) {
|
|
pr_err("failed to write +V(%d %d)\n", min_uV, max_uV);
|
|
return rc;
|
|
}
|
|
rc = regmap_write(rdev->regmap, DW8768_REG_VNEG_ADDR, val);
|
|
if (rc) {
|
|
pr_err("failed to write -V(%d %d)\n", min_uV, max_uV);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
reg_data->curr_uV = new_uV;
|
|
*selector = val;
|
|
|
|
dw_info("uV:%d reg:%x postponed:%d\n", new_uV, val,
|
|
reg_data->vol_set_postponed);
|
|
return 0;
|
|
}
|
|
|
|
static int dw8768_regulator_get_voltage(struct regulator_dev *rdev)
|
|
{
|
|
struct dw8768_regulator *reg_data = rdev_get_drvdata(rdev);
|
|
int rc, posval, negval;
|
|
|
|
if (!rdev->regmap) {
|
|
pr_err("invalid regmap\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!reg_data->is_enabled) {
|
|
dw_info("curr_uV:%d (no_enabled)\n", reg_data->curr_uV);
|
|
return reg_data->curr_uV;
|
|
}
|
|
|
|
rc = regmap_read(rdev->regmap, DW8768_REG_VPOS_ADDR, &posval);
|
|
if (rc) {
|
|
pr_err("failed to read +V\n");
|
|
return rc;
|
|
}
|
|
rc = regmap_read(rdev->regmap, DW8768_REG_VNEG_ADDR, &negval);
|
|
if (rc) {
|
|
pr_err("failed to read -V\n");
|
|
return rc;
|
|
}
|
|
if (posval != negval) {
|
|
pr_err("mismatch between +V(%d) and -V(%d)\n",
|
|
posval, negval);
|
|
return -EINVAL;
|
|
}
|
|
reg_data->curr_uV = (posval & DW8768_REG_V_MASK)* DW8768_VOLTAGE_STEP
|
|
+ DW8768_VOLTAGE_MIN;
|
|
|
|
dw_info("curr_uV:%d\n", reg_data->curr_uV);
|
|
|
|
return reg_data->curr_uV;
|
|
}
|
|
|
|
static int dw8768_regulator_list_voltage(struct regulator_dev *rdev,
|
|
unsigned selector)
|
|
{
|
|
if (selector >= DW8768_VOLTAGE_LEVELS)
|
|
return 0;
|
|
return selector * DW8768_VOLTAGE_STEP + DW8768_VOLTAGE_MIN;
|
|
}
|
|
|
|
static struct regulator_ops dw8768_ops = {
|
|
.set_voltage = dw8768_regulator_set_voltage,
|
|
.get_voltage = dw8768_regulator_get_voltage,
|
|
.list_voltage = dw8768_regulator_list_voltage,
|
|
.enable = dw8768_regulator_enable,
|
|
.disable = dw8768_regulator_disable,
|
|
.is_enabled = dw8768_regulator_is_enabled,
|
|
};
|
|
|
|
static int dw8768_parse_dt_reg(struct dw8768_regulator *reg_data,
|
|
struct device_node *node)
|
|
{
|
|
int temp;
|
|
|
|
reg_data->ena_gpio = of_get_named_gpio(node, "dw,ena-gpio", 0);
|
|
if (!gpio_is_valid(reg_data->ena_gpio)) {
|
|
pr_err("ena-gpio not specified. rd=%d\n",
|
|
reg_data->ena_gpio);
|
|
return -EINVAL;
|
|
}
|
|
|
|
reg_data->enm_gpio = of_get_named_gpio(node, "dw,enm-gpio", 0);
|
|
if (!gpio_is_valid(reg_data->enm_gpio)) {
|
|
pr_err("enm-gpio not specified. rd=%d\n",
|
|
reg_data->enm_gpio);
|
|
}
|
|
|
|
if (!of_property_read_u32(node, "dw,pre-on-sleep-us", &temp))
|
|
reg_data->pre_on_usleep = temp;
|
|
if (!of_property_read_u32(node, "dw,post-on-sleep-us", &temp))
|
|
reg_data->post_on_usleep = temp;
|
|
else
|
|
reg_data->post_on_usleep = 3700;
|
|
if (!of_property_read_u32(node, "dw,pre-off-sleep-us", &temp))
|
|
reg_data->pre_off_usleep = temp;
|
|
if (!of_property_read_u32(node, "dw,post-off-sleep-us", &temp))
|
|
reg_data->post_off_usleep = temp;
|
|
|
|
dw_info("pre-on:%d post-on:%d pre-off:%d post-off:%d\n",
|
|
reg_data->pre_on_usleep, reg_data->post_on_usleep,
|
|
reg_data->pre_off_usleep, reg_data->post_off_usleep);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dw8768_parse_dt(struct dw8768_regulator *reg_data,
|
|
struct i2c_client *client)
|
|
{
|
|
struct device_node *node;
|
|
struct regulation_constraints *constraints;
|
|
int rc;
|
|
|
|
if (!client->dev.of_node) {
|
|
pr_err("Can't find device tree\n");
|
|
return -ENOTSUPP;
|
|
}
|
|
node = of_find_node_by_name(client->dev.of_node,
|
|
"regulators");
|
|
if (!node) {
|
|
pr_err("Can't find regulator node\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = of_regulator_match(&client->dev, node, dw8768_reg_matches,
|
|
ARRAY_SIZE(dw8768_reg_matches));
|
|
if (IS_ERR_VALUE(rc)) {
|
|
pr_err("Can't match regulator. rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
if (!dw8768_reg_matches[0].init_data) {
|
|
pr_err("Failed to match regulator\n");
|
|
return -EINVAL;
|
|
}
|
|
constraints = &dw8768_reg_matches[0].init_data->constraints;
|
|
if (!constraints) {
|
|
pr_err("Failed to init constraints\n");
|
|
return -EINVAL;
|
|
}
|
|
constraints->input_uV = constraints->max_uV;
|
|
constraints->valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
|
|
REGULATOR_CHANGE_STATUS;
|
|
|
|
reg_data->init_data = dw8768_reg_matches[0].init_data;
|
|
reg_data->reg_node = dw8768_reg_matches[0].of_node;
|
|
reg_data->rdesc.name = constraints->name;
|
|
|
|
rc = dw8768_parse_dt_reg(reg_data,
|
|
dw8768_reg_matches[0].of_node);
|
|
return rc;
|
|
}
|
|
|
|
static struct regmap_config dw8768_i2c_regmap = {
|
|
.reg_bits = 8,
|
|
.val_bits = 8,
|
|
};
|
|
|
|
static int dw8768_regulator_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
struct dw8768_regulator *reg_data;
|
|
struct regulator_config config = {};
|
|
struct regulator_desc *rdesc;
|
|
int rc;
|
|
|
|
reg_data = devm_kzalloc(&client->dev,
|
|
sizeof(struct dw8768_regulator),
|
|
GFP_KERNEL);
|
|
if (!reg_data) {
|
|
pr_err("Can't allocate dw8768_regulator memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
rc = dw8768_parse_dt(reg_data, client);
|
|
if (rc) {
|
|
pr_err("Failed to parse device tree. rc=%d\n", rc);
|
|
goto error;
|
|
}
|
|
|
|
if (!gpio_is_valid(reg_data->ena_gpio)) {
|
|
pr_err("Invalid gpio ena\n");
|
|
goto error;
|
|
}
|
|
rc = gpio_request_one(reg_data->ena_gpio,
|
|
GPIOF_OUT_INIT_HIGH, "dw8768_ena");
|
|
if (rc) {
|
|
pr_err("Failed to request gpio ena. rc=%d\n", rc);
|
|
goto error;
|
|
}
|
|
|
|
if (gpio_is_valid(reg_data->enm_gpio)) {
|
|
rc = gpio_request_one(reg_data->enm_gpio,
|
|
GPIOF_OUT_INIT_HIGH, "dw8768_enm");
|
|
if (rc) {
|
|
pr_err("Failed to request gpio enm. rc=%d\n", rc);
|
|
goto error_enm;
|
|
}
|
|
}
|
|
|
|
reg_data->i2c_regmap = devm_regmap_init_i2c(client,
|
|
&dw8768_i2c_regmap);
|
|
if (IS_ERR(reg_data->i2c_regmap)) {
|
|
rc = PTR_ERR(reg_data->i2c_regmap);
|
|
pr_err("Failed to init i2c. rc=%d\n", rc);
|
|
goto error_i2c;
|
|
}
|
|
|
|
i2c_set_clientdata(client, reg_data);
|
|
|
|
reg_data->dev = &client->dev;
|
|
|
|
config.dev = &client->dev;
|
|
config.init_data = reg_data->init_data;
|
|
config.driver_data = reg_data;
|
|
config.of_node = reg_data->reg_node;
|
|
config.regmap = reg_data->i2c_regmap;
|
|
|
|
rdesc = ®_data->rdesc;
|
|
rdesc->type = REGULATOR_VOLTAGE;
|
|
rdesc->owner = THIS_MODULE;
|
|
rdesc->n_voltages = DW8768_VOLTAGE_LEVELS;
|
|
rdesc->ops = &dw8768_ops;
|
|
|
|
reg_data->curr_uV = DW8768_VOLTAGE_DEFAULT;
|
|
reg_data->rdev = regulator_register(rdesc, &config);
|
|
if (IS_ERR(reg_data->rdev)) {
|
|
rc = PTR_ERR(reg_data->rdev);
|
|
pr_err("Failed to register regulator. rc=%d\n", rc);
|
|
goto error_i2c;
|
|
}
|
|
|
|
return rc;
|
|
|
|
error_i2c:
|
|
if (gpio_is_valid(reg_data->enm_gpio))
|
|
gpio_free(reg_data->enm_gpio);
|
|
error_enm:
|
|
if (gpio_is_valid(reg_data->ena_gpio))
|
|
gpio_free(reg_data->ena_gpio);
|
|
error:
|
|
if (reg_data) {
|
|
devm_kfree(&client->dev, reg_data);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int dw8768_regulator_remove(struct i2c_client *client)
|
|
{
|
|
struct dw8768_regulator *reg_data =
|
|
i2c_get_clientdata(client);
|
|
|
|
regulator_unregister(reg_data->rdev);
|
|
if (gpio_is_valid(reg_data->ena_gpio))
|
|
gpio_free(reg_data->ena_gpio);
|
|
if (gpio_is_valid(reg_data->enm_gpio))
|
|
gpio_free(reg_data->enm_gpio);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct of_device_id dw8768_match_table[] = {
|
|
{ .compatible = "dw,dw8768", },
|
|
{},
|
|
};
|
|
|
|
static const struct i2c_device_id dw8768_id[] = {
|
|
{ "dw8768", 0 },
|
|
{ },
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, dw8768_id);
|
|
|
|
static struct i2c_driver dw8768_regulator_driver = {
|
|
.driver = {
|
|
.name = "dw8768",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = dw8768_match_table,
|
|
},
|
|
.probe = dw8768_regulator_probe,
|
|
.remove = dw8768_regulator_remove,
|
|
.id_table = dw8768_id,
|
|
};
|
|
|
|
static int __init dw8768_init(void)
|
|
{
|
|
return i2c_add_driver(&dw8768_regulator_driver);
|
|
}
|
|
subsys_initcall(dw8768_init);
|
|
|
|
static void __exit dw8768_exit(void)
|
|
{
|
|
i2c_del_driver(&dw8768_regulator_driver);
|
|
}
|
|
module_exit(dw8768_exit);
|
|
|
|
MODULE_DESCRIPTION("DONGWOON DW8768 regulator driver");
|
|
MODULE_LICENSE("GPL v2");
|