blob: aeb36fd3b528fb08d54b8b75ddd0052e5aeaab5e [file] [log] [blame]
/*
* pwm-regulator: PWM based regulator configuration.
*
* Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved.
*
* Author: Laxman Dewangan <ldewangan@nvidia.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
struct pwm_regulator {
struct device *dev;
struct regulator_desc desc;
struct regulator_dev *rdev;
struct pwm_device *pwm;
struct regulator_init_data *rinit_data;
unsigned int period;
unsigned int pulse_time;
unsigned int min_uV;
unsigned int max_uV;
unsigned int uV_step;
unsigned int n_voltages;
unsigned int curr_selector;
int enable_gpio;
int idle_gpio;
int standby_gpio;
unsigned int voltage_time_sel;
};
static int pwm_regulator_set_voltage_sel(
struct regulator_dev *rdev, unsigned selector)
{
struct pwm_regulator *preg = rdev_get_drvdata(rdev);
int ret;
int duty_cycle;
duty_cycle = selector * preg->pulse_time;
ret = pwm_config(preg->pwm, duty_cycle, preg->period);
if (ret < 0) {
dev_err(preg->dev, "pwm config failed: %d\n", ret);
return ret;
}
if (duty_cycle) {
ret = pwm_enable(preg->pwm);
if (ret < 0) {
dev_err(preg->dev, "pwm enable failed: %d\n", ret);
return ret;
}
} else {
pwm_disable(preg->pwm);
}
preg->curr_selector = selector;
return ret;
}
static int pwm_regulator_get_voltage_sel(struct regulator_dev *rdev)
{
struct pwm_regulator *preg = rdev_get_drvdata(rdev);
return preg->curr_selector;
}
static int pwm_regulator_set_mode(struct regulator_dev *rdev,
unsigned int mode)
{
struct pwm_regulator *preg = rdev_get_drvdata(rdev);
if (!gpio_is_valid(preg->idle_gpio))
return -EINVAL;
switch (mode) {
case REGULATOR_MODE_IDLE:
gpio_set_value(preg->idle_gpio, 0);
break;
case REGULATOR_MODE_NORMAL:
gpio_set_value(preg->idle_gpio, 1);
break;
default:
return -EINVAL;
}
return 0;
}
static unsigned int pwm_regulator_get_mode(struct regulator_dev *rdev)
{
struct pwm_regulator *preg = rdev_get_drvdata(rdev);
if (!gpio_is_valid(preg->idle_gpio))
return 0;
if (gpio_get_value(preg->idle_gpio))
return REGULATOR_MODE_NORMAL;
else
return REGULATOR_MODE_IDLE;
}
static int pwm_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
unsigned int old_selector, unsigned int new_selector)
{
struct pwm_regulator *preg = rdev_get_drvdata(rdev);
if (preg->voltage_time_sel)
return preg->voltage_time_sel;
return regulator_set_voltage_time_sel(rdev, old_selector, new_selector);
}
static struct regulator_ops pwm_regulator_ops = {
.set_voltage_sel = pwm_regulator_set_voltage_sel,
.get_voltage_sel = pwm_regulator_get_voltage_sel,
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.set_voltage_time_sel = pwm_regulator_set_voltage_time_sel,
.set_mode = pwm_regulator_set_mode,
.get_mode = pwm_regulator_get_mode,
};
static int pwm_regulator_parse_dt(struct device *dev,
struct pwm_regulator *preg)
{
struct device_node *node = dev->of_node;
u32 pval;
int ret;
preg->rinit_data = of_get_regulator_init_data(dev, dev->of_node);
if (!preg->rinit_data) {
dev_err(dev, "Not able to get OF regulator init data\n");
return -EINVAL;
}
ret = of_property_read_u32(node, "regulator-n-voltages", &pval);
if (!ret) {
preg->n_voltages = pval;
} else {
dev_err(dev, "Number of voltages is missing\n");
return ret;
}
ret = of_property_read_u32(node, "voltage-time-sel", &pval);
if (!ret)
preg->voltage_time_sel = pval;
preg->enable_gpio = of_get_named_gpio(node, "enable-gpio", 0);
if ((preg->enable_gpio < 0) && (preg->enable_gpio != -ENOENT)) {
dev_err(dev, "Enable gpio not available, %d\n",
preg->enable_gpio);
return preg->enable_gpio;
}
preg->idle_gpio = of_get_named_gpio(node, "idle-gpio", 0);
if ((preg->idle_gpio < 0) && (preg->idle_gpio != -ENOENT)) {
dev_err(dev, "Idle gpio not available, %d\n", preg->idle_gpio);
return preg->idle_gpio;
}
preg->standby_gpio = of_get_named_gpio(node, "standby-gpio", 0);
if ((preg->standby_gpio < 0) && (preg->standby_gpio != -ENOENT)) {
dev_err(dev, "Standby gpio not available, %d\n",
preg->standby_gpio);
return preg->standby_gpio;
}
preg->min_uV = preg->rinit_data->constraints.min_uV;
preg->max_uV = preg->rinit_data->constraints.max_uV;
return 0;
}
static int pwm_regulator_verify_patform_data(struct pwm_regulator *preg)
{
struct device *dev = preg->dev;
preg->period = pwm_get_period(preg->pwm);
if (!preg->period)
return -EINVAL;
if (preg->n_voltages < 2) {
dev_err(dev, "Number of volatges is not correct\n");
return -EINVAL;
}
if (preg->period % (preg->n_voltages - 1)) {
dev_err(dev, "PWM Period must multiple of n_voltages\n");
return -EINVAL;
}
preg->pulse_time = preg->period / (preg->n_voltages - 1);
if (!preg->pulse_time) {
dev_err(dev, "Pulse time is invalid\n");
return -EINVAL;
}
if ((preg->max_uV - preg->min_uV) % (preg->n_voltages - 1)) {
dev_err(dev, "Min/Max is not proper to get step voltage\n");
return -EINVAL;
}
preg->uV_step = (preg->max_uV - preg->min_uV) / (preg->n_voltages - 1);
return 0;
}
static int pwm_regulator_probe(struct platform_device *pdev)
{
struct pwm_regulator *preg;
struct regulator_config config = { };
int ret;
if (!pdev->dev.of_node) {
dev_err(&pdev->dev, "Not DT registartion\n");
return -ENODEV;
}
preg = devm_kzalloc(&pdev->dev, sizeof(*preg), GFP_KERNEL);
if (!preg) {
dev_err(&pdev->dev, "Memory allocation failed\n");
return -ENOMEM;
}
preg->dev = &pdev->dev;
ret = pwm_regulator_parse_dt(&pdev->dev, preg);
if (ret < 0) {
dev_err(&pdev->dev, "DT parsing is failed: %d\n", ret);
return ret;
}
preg->pwm = devm_pwm_get(&pdev->dev, NULL);
if (IS_ERR(preg->pwm)) {
ret = PTR_ERR(preg->pwm);
if (ret == -EPROBE_DEFER)
dev_info(&pdev->dev, "PWM request deferred\n");
else
dev_err(&pdev->dev, "PWM request failed: %d\n", ret);
return ret;
}
ret = pwm_regulator_verify_patform_data(preg);
if (ret < 0) {
dev_err(&pdev->dev,
"PWM regulator param verification failed: %d\n", ret);
return ret;
}
if (gpio_is_valid(preg->idle_gpio)) {
ret = devm_gpio_request_one(&pdev->dev, preg->idle_gpio,
GPIOF_OUT_INIT_HIGH, "pwm-reg-idle-gpio");
if (ret < 0) {
dev_err(&pdev->dev, "Idle gpio request failed\n");
return ret;
}
preg->rinit_data->constraints.valid_modes_mask |=
REGULATOR_MODE_IDLE |
REGULATOR_MODE_NORMAL;
preg->rinit_data->constraints.valid_ops_mask |=
REGULATOR_CHANGE_MODE;
}
preg->desc.name = "regulator-pwm";
preg->desc.id = -1;
preg->desc.ops = &pwm_regulator_ops;
preg->desc.type = REGULATOR_VOLTAGE;
preg->desc.owner = THIS_MODULE;
preg->desc.min_uV = preg->min_uV;
preg->desc.uV_step = preg->uV_step;
preg->desc.linear_min_sel = 0;
preg->desc.n_voltages = preg->n_voltages;
if (gpio_is_valid(preg->enable_gpio)) {
config.ena_gpio = preg->enable_gpio;
if (preg->rinit_data->constraints.always_on ||
preg->rinit_data->constraints.boot_on)
config.ena_gpio_flags = GPIOF_OUT_INIT_HIGH;
}
config.dev = &pdev->dev;
config.init_data = preg->rinit_data;
config.driver_data = preg;
preg->rdev = devm_regulator_register(&pdev->dev, &preg->desc, &config);
if (IS_ERR(preg->rdev)) {
dev_err(preg->dev, "failed to register regulator %s\n",
preg->desc.name);
return PTR_ERR(preg->rdev);
}
platform_set_drvdata(pdev, preg);
return 0;
}
static const struct of_device_id pwm_regulator_of_match[] = {
{ .compatible = "regulator-pwm", },
{},
};
MODULE_DEVICE_TABLE(of, pwm_regulator_of_match);
static struct platform_driver pwm_regulator_driver = {
.driver = {
.name = "regulator-pwm",
.owner = THIS_MODULE,
.of_match_table = pwm_regulator_of_match,
},
.probe = pwm_regulator_probe,
};
static int __init pwm_regulator_init(void)
{
return platform_driver_register(&pwm_regulator_driver);
}
subsys_initcall_sync(pwm_regulator_init);
static void __exit pwm_regulator_exit(void)
{
platform_driver_unregister(&pwm_regulator_driver);
}
module_exit(pwm_regulator_exit);
MODULE_DESCRIPTION("PWM based regulator Driver");
MODULE_ALIAS("platform:regulator-pwm");
MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
MODULE_LICENSE("GPL v2");