diff options
Diffstat (limited to 'drivers/regulator/st-pwm.c')
| -rw-r--r-- | drivers/regulator/st-pwm.c | 190 | 
1 files changed, 190 insertions, 0 deletions
diff --git a/drivers/regulator/st-pwm.c b/drivers/regulator/st-pwm.c new file mode 100644 index 00000000000..5ea78df449f --- /dev/null +++ b/drivers/regulator/st-pwm.c @@ -0,0 +1,190 @@ +/* + * Regulator driver for ST's PWM Regulators + * + * Copyright (C) 2014 - STMicroelectronics Inc. + * + * Author: Lee Jones <lee.jones@linaro.org> + * + * 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. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pwm.h> + +#define ST_PWM_REG_PERIOD 8448 + +struct st_pwm_regulator_pdata { +	const struct regulator_desc *desc; +	struct st_pwm_voltages *duty_cycle_table; +}; + +struct st_pwm_regulator_data { +	const struct st_pwm_regulator_pdata *pdata; +	struct pwm_device *pwm; +	bool enabled; +	int state; +}; + +struct st_pwm_voltages { +	unsigned int uV; +	unsigned int dutycycle; +}; + +static int st_pwm_regulator_get_voltage_sel(struct regulator_dev *dev) +{ +	struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev); + +	return drvdata->state; +} + +static int st_pwm_regulator_set_voltage_sel(struct regulator_dev *dev, +					    unsigned selector) +{ +	struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev); +	int dutycycle; +	int ret; + +	dutycycle = (ST_PWM_REG_PERIOD / 100) * +		drvdata->pdata->duty_cycle_table[selector].dutycycle; + +	ret = pwm_config(drvdata->pwm, dutycycle, ST_PWM_REG_PERIOD); +	if (ret) { +		dev_err(&dev->dev, "Failed to configure PWM\n"); +		return ret; +	} + +	drvdata->state = selector; + +	if (!drvdata->enabled) { +		ret = pwm_enable(drvdata->pwm); +		if (ret) { +			dev_err(&dev->dev, "Failed to enable PWM\n"); +			return ret; +		} +		drvdata->enabled = true; +	} + +	return 0; +} + +static int st_pwm_regulator_list_voltage(struct regulator_dev *dev, +					 unsigned selector) +{ +	struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev); + +	if (selector >= dev->desc->n_voltages) +		return -EINVAL; + +	return drvdata->pdata->duty_cycle_table[selector].uV; +} + +static struct regulator_ops st_pwm_regulator_voltage_ops = { +	.set_voltage_sel = st_pwm_regulator_set_voltage_sel, +	.get_voltage_sel = st_pwm_regulator_get_voltage_sel, +	.list_voltage    = st_pwm_regulator_list_voltage, +	.map_voltage     = regulator_map_voltage_iterate, +}; + +static struct st_pwm_voltages b2105_duty_cycle_table[] = { +	{ .uV = 1114000, .dutycycle = 0,  }, +	{ .uV = 1095000, .dutycycle = 10, }, +	{ .uV = 1076000, .dutycycle = 20, }, +	{ .uV = 1056000, .dutycycle = 30, }, +	{ .uV = 1036000, .dutycycle = 40, }, +	{ .uV = 1016000, .dutycycle = 50, }, +	/* WARNING: Values above 50% duty-cycle cause boot failures. */ +}; + +static const struct regulator_desc b2105_desc = { +	.name		= "b2105-pwm-regulator", +	.ops		= &st_pwm_regulator_voltage_ops, +	.type		= REGULATOR_VOLTAGE, +	.owner		= THIS_MODULE, +	.n_voltages	= ARRAY_SIZE(b2105_duty_cycle_table), +	.supply_name    = "pwm", +}; + +static const struct st_pwm_regulator_pdata b2105_info = { +	.desc		  = &b2105_desc, +	.duty_cycle_table = b2105_duty_cycle_table, +}; + +static const struct of_device_id st_pwm_of_match[] = { +	{ .compatible = "st,b2105-pwm-regulator", .data = &b2105_info, }, +	{ }, +}; +MODULE_DEVICE_TABLE(of, st_pwm_of_match); + +static int st_pwm_regulator_probe(struct platform_device *pdev) +{ +	struct st_pwm_regulator_data *drvdata; +	struct regulator_dev *regulator; +	struct regulator_config config = { }; +	struct device_node *np = pdev->dev.of_node; +	const struct of_device_id *of_match; + +	if (!np) { +		dev_err(&pdev->dev, "Device Tree node missing\n"); +		return -EINVAL; +	} + +	drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); +	if (!drvdata) +		return -ENOMEM; + +	of_match = of_match_device(st_pwm_of_match, &pdev->dev); +	if (!of_match) { +		dev_err(&pdev->dev, "failed to match of device\n"); +		return -ENODEV; +	} +	drvdata->pdata = of_match->data; + +	config.init_data = of_get_regulator_init_data(&pdev->dev, np); +	if (!config.init_data) +		return -ENOMEM; + +	config.of_node = np; +	config.dev = &pdev->dev; +	config.driver_data = drvdata; + +	drvdata->pwm = devm_pwm_get(&pdev->dev, NULL); +	if (IS_ERR(drvdata->pwm)) { +		dev_err(&pdev->dev, "Failed to get PWM\n"); +		return PTR_ERR(drvdata->pwm); +	} + +	regulator = devm_regulator_register(&pdev->dev, +					    drvdata->pdata->desc, &config); +	if (IS_ERR(regulator)) { +		dev_err(&pdev->dev, "Failed to register regulator %s\n", +			drvdata->pdata->desc->name); +		return PTR_ERR(regulator); +	} + +	return 0; +} + +static struct platform_driver st_pwm_regulator_driver = { +	.driver = { +		.name		= "st-pwm-regulator", +		.owner		= THIS_MODULE, +		.of_match_table = of_match_ptr(st_pwm_of_match), +	}, +	.probe = st_pwm_regulator_probe, +}; + +module_platform_driver(st_pwm_regulator_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>"); +MODULE_DESCRIPTION("ST PWM Regulator Driver"); +MODULE_ALIAS("platform:st_pwm-regulator");  | 
