Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

regulator: Add new driver for ST's PWM controlled voltage regulators

On some STMicroelectronics hardware reside regulators consisting
partly of a PWM input connected to the feedback loop. As the PWM
duty-cycle is varied the output voltage adapts. This driver
allows us to vary the output voltage by adapting the PWM input
duty-cycle.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Mark Brown <broonie@linaro.org>

authored by

Lee Jones and committed by
Mark Brown
4a5d3013 38dbfb59

+200
+6
drivers/regulator/Kconfig
··· 432 432 via I2C bus. S5M8767A have 9 Bucks and 28 LDOs output and 433 433 supports DVS mode with 8bits of output voltage control. 434 434 435 + config REGULATOR_ST_PWM 436 + tristate "STMicroelectronics PWM voltage regulator" 437 + depends on ARCH_STI 438 + help 439 + This driver supports ST's PWM controlled voltage regulators. 440 + 435 441 config REGULATOR_TI_ABB 436 442 tristate "TI Adaptive Body Bias on-chip LDO" 437 443 depends on ARCH_OMAP
+1
drivers/regulator/Makefile
··· 59 59 obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o 60 60 obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o 61 61 obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o 62 + obj-$(CONFIG_REGULATOR_ST_PWM) += st-pwm.o 62 63 obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o 63 64 obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o 64 65 obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o
+193
drivers/regulator/st-pwm.c
··· 1 + /* 2 + * Regulator driver for ST's PWM Regulators 3 + * 4 + * Copyright (C) 2014 - STMicroelectronics Inc. 5 + * 6 + * Author: Lee Jones <lee.jones@linaro.org> 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License version 2 as 10 + * published by the Free Software Foundation. 11 + */ 12 + 13 + #include <linux/module.h> 14 + #include <linux/init.h> 15 + #include <linux/err.h> 16 + #include <linux/regulator/driver.h> 17 + #include <linux/regulator/machine.h> 18 + #include <linux/regulator/of_regulator.h> 19 + #include <linux/of.h> 20 + #include <linux/of_device.h> 21 + #include <linux/pwm.h> 22 + 23 + #define ST_PWM_REG_PERIOD 8448 24 + 25 + struct st_pwm_regulator_pdata { 26 + const struct regulator_desc *desc; 27 + struct st_pwm_voltages *duty_cycle_table; 28 + }; 29 + 30 + struct st_pwm_regulator_data { 31 + const struct st_pwm_regulator_pdata *pdata; 32 + struct pwm_device *pwm; 33 + bool enabled; 34 + int state; 35 + }; 36 + 37 + struct st_pwm_voltages { 38 + unsigned int uV; 39 + unsigned int dutycycle; 40 + }; 41 + 42 + static int st_pwm_regulator_get_voltage(struct regulator_dev *dev) 43 + { 44 + struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev); 45 + 46 + return drvdata->pdata->duty_cycle_table[drvdata->state].uV; 47 + } 48 + 49 + static int st_pwm_regulator_set_voltage_sel(struct regulator_dev *dev, 50 + unsigned selector) 51 + { 52 + struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev); 53 + int dutycycle; 54 + int ret; 55 + 56 + if (selector >= dev->desc->n_voltages) 57 + return -EINVAL; 58 + 59 + dutycycle = (ST_PWM_REG_PERIOD / 100) * 60 + drvdata->pdata->duty_cycle_table[selector].dutycycle; 61 + 62 + ret = pwm_config(drvdata->pwm, dutycycle, ST_PWM_REG_PERIOD); 63 + if (ret) { 64 + dev_err(&dev->dev, "Failed to configure PWM\n"); 65 + return ret; 66 + } 67 + 68 + drvdata->state = selector; 69 + 70 + if (!drvdata->enabled) { 71 + ret = pwm_enable(drvdata->pwm); 72 + if (ret) { 73 + dev_err(&dev->dev, "Failed to enable PWM\n"); 74 + return ret; 75 + } 76 + drvdata->enabled = true; 77 + } 78 + 79 + return 0; 80 + } 81 + 82 + static int st_pwm_regulator_list_voltage(struct regulator_dev *dev, 83 + unsigned selector) 84 + { 85 + struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev); 86 + 87 + if (selector >= dev->desc->n_voltages) 88 + return -EINVAL; 89 + 90 + return drvdata->pdata->duty_cycle_table[selector].uV; 91 + } 92 + 93 + static struct regulator_ops st_pwm_regulator_voltage_ops = { 94 + .set_voltage_sel = st_pwm_regulator_set_voltage_sel, 95 + .get_voltage = st_pwm_regulator_get_voltage, 96 + .list_voltage = st_pwm_regulator_list_voltage, 97 + .map_voltage = regulator_map_voltage_iterate, 98 + }; 99 + 100 + static struct st_pwm_voltages b2105_duty_cycle_table[] = { 101 + { .uV = 1114000, .dutycycle = 0, }, 102 + { .uV = 1095000, .dutycycle = 10, }, 103 + { .uV = 1076000, .dutycycle = 20, }, 104 + { .uV = 1056000, .dutycycle = 30, }, 105 + { .uV = 1036000, .dutycycle = 40, }, 106 + { .uV = 1016000, .dutycycle = 50, }, 107 + /* WARNING: Values above 50% duty-cycle cause boot failures. */ 108 + }; 109 + 110 + static const struct regulator_desc b2105_desc = { 111 + .name = "b2105-pwm-regulator", 112 + .ops = &st_pwm_regulator_voltage_ops, 113 + .type = REGULATOR_VOLTAGE, 114 + .owner = THIS_MODULE, 115 + .n_voltages = ARRAY_SIZE(b2105_duty_cycle_table), 116 + .supply_name = "pwm", 117 + }; 118 + 119 + static const struct st_pwm_regulator_pdata b2105_info = { 120 + .desc = &b2105_desc, 121 + .duty_cycle_table = b2105_duty_cycle_table, 122 + }; 123 + 124 + static struct of_device_id st_pwm_of_match[] = { 125 + { .compatible = "st,b2105-pwm-regulator", .data = &b2105_info, }, 126 + { }, 127 + }; 128 + MODULE_DEVICE_TABLE(of, st_pwm_of_match); 129 + 130 + static int st_pwm_regulator_probe(struct platform_device *pdev) 131 + { 132 + struct st_pwm_regulator_data *drvdata; 133 + struct regulator_dev *regulator; 134 + struct regulator_config config = { }; 135 + struct device_node *np = pdev->dev.of_node; 136 + const struct of_device_id *of_match; 137 + 138 + if (!np) { 139 + dev_err(&pdev->dev, "Device Tree node missing\n"); 140 + return -EINVAL; 141 + } 142 + 143 + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); 144 + if (!drvdata) 145 + return -ENOMEM; 146 + 147 + of_match = of_match_device(st_pwm_of_match, &pdev->dev); 148 + if (!of_match) { 149 + dev_err(&pdev->dev, "failed to match of device\n"); 150 + return -ENODEV; 151 + } 152 + drvdata->pdata = of_match->data; 153 + 154 + config.init_data = of_get_regulator_init_data(&pdev->dev, np); 155 + if (!config.init_data) 156 + return -ENOMEM; 157 + 158 + config.of_node = np; 159 + config.dev = &pdev->dev; 160 + config.driver_data = drvdata; 161 + 162 + drvdata->pwm = devm_pwm_get(&pdev->dev, NULL); 163 + if (IS_ERR(drvdata->pwm)) { 164 + dev_err(&pdev->dev, "Failed to get PWM\n"); 165 + return PTR_ERR(drvdata->pwm); 166 + } 167 + 168 + regulator = devm_regulator_register(&pdev->dev, 169 + drvdata->pdata->desc, &config); 170 + if (IS_ERR(regulator)) { 171 + dev_err(&pdev->dev, "Failed to register regulator %s\n", 172 + drvdata->pdata->desc->name); 173 + return PTR_ERR(regulator); 174 + } 175 + 176 + return 0; 177 + } 178 + 179 + static struct platform_driver st_pwm_regulator_driver = { 180 + .driver = { 181 + .name = "st-pwm-regulator", 182 + .owner = THIS_MODULE, 183 + .of_match_table = of_match_ptr(st_pwm_of_match), 184 + }, 185 + .probe = st_pwm_regulator_probe, 186 + }; 187 + 188 + module_platform_driver(st_pwm_regulator_driver); 189 + 190 + MODULE_LICENSE("GPL"); 191 + MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>"); 192 + MODULE_DESCRIPTION("ST PWM Regulator Driver"); 193 + MODULE_ALIAS("platform:st_pwm-regulator");