277 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// Regulator support for WM84004//5// Copyright 2008 Wolfson Microelectronics PLC.6//7// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>8 9#include <linux/bug.h>10#include <linux/err.h>11#include <linux/kernel.h>12#include <linux/module.h>13#include <linux/regulator/driver.h>14#include <linux/mfd/wm8400-private.h>15 16static const struct linear_range wm8400_ldo_ranges[] = {17 REGULATOR_LINEAR_RANGE(900000, 0, 14, 50000),18 REGULATOR_LINEAR_RANGE(1700000, 15, 31, 100000),19};20 21static const struct regulator_ops wm8400_ldo_ops = {22 .is_enabled = regulator_is_enabled_regmap,23 .enable = regulator_enable_regmap,24 .disable = regulator_disable_regmap,25 .list_voltage = regulator_list_voltage_linear_range,26 .get_voltage_sel = regulator_get_voltage_sel_regmap,27 .set_voltage_sel = regulator_set_voltage_sel_regmap,28 .map_voltage = regulator_map_voltage_linear_range,29};30 31static unsigned int wm8400_dcdc_get_mode(struct regulator_dev *dev)32{33 struct regmap *rmap = rdev_get_regmap(dev);34 int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;35 u16 data[2];36 int ret;37 38 ret = regmap_bulk_read(rmap, WM8400_DCDC1_CONTROL_1 + offset, data, 2);39 if (ret != 0)40 return 0;41 42 /* Datasheet: hibernate */43 if (data[0] & WM8400_DC1_SLEEP)44 return REGULATOR_MODE_STANDBY;45 46 /* Datasheet: standby */47 if (!(data[0] & WM8400_DC1_ACTIVE))48 return REGULATOR_MODE_IDLE;49 50 /* Datasheet: active with or without force PWM */51 if (data[1] & WM8400_DC1_FRC_PWM)52 return REGULATOR_MODE_FAST;53 else54 return REGULATOR_MODE_NORMAL;55}56 57static int wm8400_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode)58{59 struct regmap *rmap = rdev_get_regmap(dev);60 int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;61 int ret;62 63 switch (mode) {64 case REGULATOR_MODE_FAST:65 /* Datasheet: active with force PWM */66 ret = regmap_update_bits(rmap, WM8400_DCDC1_CONTROL_2 + offset,67 WM8400_DC1_FRC_PWM, WM8400_DC1_FRC_PWM);68 if (ret != 0)69 return ret;70 71 return regmap_update_bits(rmap, WM8400_DCDC1_CONTROL_1 + offset,72 WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP,73 WM8400_DC1_ACTIVE);74 75 case REGULATOR_MODE_NORMAL:76 /* Datasheet: active */77 ret = regmap_update_bits(rmap, WM8400_DCDC1_CONTROL_2 + offset,78 WM8400_DC1_FRC_PWM, 0);79 if (ret != 0)80 return ret;81 82 return regmap_update_bits(rmap, WM8400_DCDC1_CONTROL_1 + offset,83 WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP,84 WM8400_DC1_ACTIVE);85 86 case REGULATOR_MODE_IDLE:87 /* Datasheet: standby */88 return regmap_update_bits(rmap, WM8400_DCDC1_CONTROL_1 + offset,89 WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, 0);90 default:91 return -EINVAL;92 }93}94 95static unsigned int wm8400_dcdc_get_optimum_mode(struct regulator_dev *dev,96 int input_uV, int output_uV,97 int load_uA)98{99 return REGULATOR_MODE_NORMAL;100}101 102static const struct regulator_ops wm8400_dcdc_ops = {103 .is_enabled = regulator_is_enabled_regmap,104 .enable = regulator_enable_regmap,105 .disable = regulator_disable_regmap,106 .list_voltage = regulator_list_voltage_linear,107 .map_voltage = regulator_map_voltage_linear,108 .get_voltage_sel = regulator_get_voltage_sel_regmap,109 .set_voltage_sel = regulator_set_voltage_sel_regmap,110 .get_mode = wm8400_dcdc_get_mode,111 .set_mode = wm8400_dcdc_set_mode,112 .get_optimum_mode = wm8400_dcdc_get_optimum_mode,113};114 115static const struct regulator_desc regulators[] = {116 {117 .name = "LDO1",118 .id = WM8400_LDO1,119 .ops = &wm8400_ldo_ops,120 .enable_reg = WM8400_LDO1_CONTROL,121 .enable_mask = WM8400_LDO1_ENA,122 .n_voltages = WM8400_LDO1_VSEL_MASK + 1,123 .linear_ranges = wm8400_ldo_ranges,124 .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),125 .vsel_reg = WM8400_LDO1_CONTROL,126 .vsel_mask = WM8400_LDO1_VSEL_MASK,127 .type = REGULATOR_VOLTAGE,128 .owner = THIS_MODULE,129 },130 {131 .name = "LDO2",132 .id = WM8400_LDO2,133 .ops = &wm8400_ldo_ops,134 .enable_reg = WM8400_LDO2_CONTROL,135 .enable_mask = WM8400_LDO2_ENA,136 .n_voltages = WM8400_LDO2_VSEL_MASK + 1,137 .linear_ranges = wm8400_ldo_ranges,138 .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),139 .type = REGULATOR_VOLTAGE,140 .vsel_reg = WM8400_LDO2_CONTROL,141 .vsel_mask = WM8400_LDO2_VSEL_MASK,142 .owner = THIS_MODULE,143 },144 {145 .name = "LDO3",146 .id = WM8400_LDO3,147 .ops = &wm8400_ldo_ops,148 .enable_reg = WM8400_LDO3_CONTROL,149 .enable_mask = WM8400_LDO3_ENA,150 .n_voltages = WM8400_LDO3_VSEL_MASK + 1,151 .linear_ranges = wm8400_ldo_ranges,152 .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),153 .vsel_reg = WM8400_LDO3_CONTROL,154 .vsel_mask = WM8400_LDO3_VSEL_MASK,155 .type = REGULATOR_VOLTAGE,156 .owner = THIS_MODULE,157 },158 {159 .name = "LDO4",160 .id = WM8400_LDO4,161 .ops = &wm8400_ldo_ops,162 .enable_reg = WM8400_LDO4_CONTROL,163 .enable_mask = WM8400_LDO4_ENA,164 .n_voltages = WM8400_LDO4_VSEL_MASK + 1,165 .linear_ranges = wm8400_ldo_ranges,166 .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),167 .vsel_reg = WM8400_LDO4_CONTROL,168 .vsel_mask = WM8400_LDO4_VSEL_MASK,169 .type = REGULATOR_VOLTAGE,170 .owner = THIS_MODULE,171 },172 {173 .name = "DCDC1",174 .id = WM8400_DCDC1,175 .ops = &wm8400_dcdc_ops,176 .enable_reg = WM8400_DCDC1_CONTROL_1,177 .enable_mask = WM8400_DC1_ENA_MASK,178 .n_voltages = WM8400_DC1_VSEL_MASK + 1,179 .vsel_reg = WM8400_DCDC1_CONTROL_1,180 .vsel_mask = WM8400_DC1_VSEL_MASK,181 .min_uV = 850000,182 .uV_step = 25000,183 .type = REGULATOR_VOLTAGE,184 .owner = THIS_MODULE,185 },186 {187 .name = "DCDC2",188 .id = WM8400_DCDC2,189 .ops = &wm8400_dcdc_ops,190 .enable_reg = WM8400_DCDC2_CONTROL_1,191 .enable_mask = WM8400_DC2_ENA_MASK,192 .n_voltages = WM8400_DC2_VSEL_MASK + 1,193 .vsel_reg = WM8400_DCDC2_CONTROL_1,194 .vsel_mask = WM8400_DC2_VSEL_MASK,195 .min_uV = 850000,196 .uV_step = 25000,197 .type = REGULATOR_VOLTAGE,198 .owner = THIS_MODULE,199 },200};201 202static int wm8400_regulator_probe(struct platform_device *pdev)203{204 struct wm8400 *wm8400 = container_of(pdev, struct wm8400, regulators[pdev->id]);205 struct regulator_config config = { };206 struct regulator_dev *rdev;207 208 config.dev = &pdev->dev;209 config.init_data = dev_get_platdata(&pdev->dev);210 config.driver_data = wm8400;211 config.regmap = wm8400->regmap;212 213 rdev = devm_regulator_register(&pdev->dev, ®ulators[pdev->id],214 &config);215 if (IS_ERR(rdev))216 return PTR_ERR(rdev);217 218 platform_set_drvdata(pdev, rdev);219 220 return 0;221}222 223static struct platform_driver wm8400_regulator_driver = {224 .driver = {225 .name = "wm8400-regulator",226 .probe_type = PROBE_PREFER_ASYNCHRONOUS,227 },228 .probe = wm8400_regulator_probe,229};230 231/**232 * wm8400_register_regulator - enable software control of a WM8400 regulator233 *234 * This function enables software control of a WM8400 regulator via235 * the regulator API. It is intended to be called from the236 * platform_init() callback of the WM8400 MFD driver.237 *238 * @dev: The WM8400 device to operate on.239 * @reg: The regulator to control.240 * @initdata: Regulator initdata for the regulator.241 */242int wm8400_register_regulator(struct device *dev, int reg,243 struct regulator_init_data *initdata)244{245 struct wm8400 *wm8400 = dev_get_drvdata(dev);246 247 if (wm8400->regulators[reg].name)248 return -EBUSY;249 250 initdata->driver_data = wm8400;251 252 wm8400->regulators[reg].name = "wm8400-regulator";253 wm8400->regulators[reg].id = reg;254 wm8400->regulators[reg].dev.parent = dev;255 wm8400->regulators[reg].dev.platform_data = initdata;256 257 return platform_device_register(&wm8400->regulators[reg]);258}259EXPORT_SYMBOL_GPL(wm8400_register_regulator);260 261static int __init wm8400_regulator_init(void)262{263 return platform_driver_register(&wm8400_regulator_driver);264}265subsys_initcall(wm8400_regulator_init);266 267static void __exit wm8400_regulator_exit(void)268{269 platform_driver_unregister(&wm8400_regulator_driver);270}271module_exit(wm8400_regulator_exit);272 273MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");274MODULE_DESCRIPTION("WM8400 regulator driver");275MODULE_LICENSE("GPL");276MODULE_ALIAS("platform:wm8400-regulator");277