Blame view

drivers/regulator/wm8400-regulator.c 7.63 KB
362af7365   Axel Lin   regulator: wm8400...
1
2
3
4
5
6
7
  // SPDX-License-Identifier: GPL-2.0+
  //
  // Regulator support for WM8400
  //
  // Copyright 2008 Wolfson Microelectronics PLC.
  //
  // Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
42fad570b   Mark Brown   regulator: Add WM...
8
9
10
11
  
  #include <linux/bug.h>
  #include <linux/err.h>
  #include <linux/kernel.h>
65602c32e   Paul Gortmaker   regulator: Add mo...
12
  #include <linux/module.h>
42fad570b   Mark Brown   regulator: Add WM...
13
14
  #include <linux/regulator/driver.h>
  #include <linux/mfd/wm8400-private.h>
60ab7f415   Matti Vaittinen   regulator: use li...
15
  static const struct linear_range wm8400_ldo_ranges[] = {
8828bae46   Axel Lin   regulator: Add RE...
16
17
  	REGULATOR_LINEAR_RANGE(900000, 0, 14, 50000),
  	REGULATOR_LINEAR_RANGE(1700000, 15, 31, 100000),
6692e432a   Mark Brown   regulator: wm8400...
18
  };
42fad570b   Mark Brown   regulator: Add WM...
19

b0d6dd3ba   Julia Lawall   regulator: wm8*: ...
20
  static const struct regulator_ops wm8400_ldo_ops = {
c54a155d4   Mark Brown   regulator: wm8400...
21
22
23
  	.is_enabled = regulator_is_enabled_regmap,
  	.enable = regulator_enable_regmap,
  	.disable = regulator_disable_regmap,
6692e432a   Mark Brown   regulator: wm8400...
24
  	.list_voltage = regulator_list_voltage_linear_range,
c54a155d4   Mark Brown   regulator: wm8400...
25
26
  	.get_voltage_sel = regulator_get_voltage_sel_regmap,
  	.set_voltage_sel = regulator_set_voltage_sel_regmap,
6692e432a   Mark Brown   regulator: wm8400...
27
  	.map_voltage = regulator_map_voltage_linear_range,
42fad570b   Mark Brown   regulator: Add WM...
28
  };
42fad570b   Mark Brown   regulator: Add WM...
29
30
  static unsigned int wm8400_dcdc_get_mode(struct regulator_dev *dev)
  {
e08abeca3   Axel Lin   regulator: wm8400...
31
  	struct regmap *rmap = rdev_get_regmap(dev);
42fad570b   Mark Brown   regulator: Add WM...
32
33
34
  	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
  	u16 data[2];
  	int ret;
e08abeca3   Axel Lin   regulator: wm8400...
35
  	ret = regmap_bulk_read(rmap, WM8400_DCDC1_CONTROL_1 + offset, data, 2);
42fad570b   Mark Brown   regulator: Add WM...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  	if (ret != 0)
  		return 0;
  
  	/* Datasheet: hibernate */
  	if (data[0] & WM8400_DC1_SLEEP)
  		return REGULATOR_MODE_STANDBY;
  
  	/* Datasheet: standby */
  	if (!(data[0] & WM8400_DC1_ACTIVE))
  		return REGULATOR_MODE_IDLE;
  
  	/* Datasheet: active with or without force PWM */
  	if (data[1] & WM8400_DC1_FRC_PWM)
  		return REGULATOR_MODE_FAST;
  	else
  		return REGULATOR_MODE_NORMAL;
  }
  
  static int wm8400_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode)
  {
e08abeca3   Axel Lin   regulator: wm8400...
56
  	struct regmap *rmap = rdev_get_regmap(dev);
42fad570b   Mark Brown   regulator: Add WM...
57
58
59
60
61
62
  	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
  	int ret;
  
  	switch (mode) {
  	case REGULATOR_MODE_FAST:
  		/* Datasheet: active with force PWM */
e08abeca3   Axel Lin   regulator: wm8400...
63
  		ret = regmap_update_bits(rmap, WM8400_DCDC1_CONTROL_2 + offset,
42fad570b   Mark Brown   regulator: Add WM...
64
65
66
  				      WM8400_DC1_FRC_PWM, WM8400_DC1_FRC_PWM);
  		if (ret != 0)
  			return ret;
e08abeca3   Axel Lin   regulator: wm8400...
67
  		return regmap_update_bits(rmap, WM8400_DCDC1_CONTROL_1 + offset,
42fad570b   Mark Brown   regulator: Add WM...
68
69
70
71
72
  				       WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP,
  				       WM8400_DC1_ACTIVE);
  
  	case REGULATOR_MODE_NORMAL:
  		/* Datasheet: active */
e08abeca3   Axel Lin   regulator: wm8400...
73
  		ret = regmap_update_bits(rmap, WM8400_DCDC1_CONTROL_2 + offset,
42fad570b   Mark Brown   regulator: Add WM...
74
75
76
  				      WM8400_DC1_FRC_PWM, 0);
  		if (ret != 0)
  			return ret;
e08abeca3   Axel Lin   regulator: wm8400...
77
  		return regmap_update_bits(rmap, WM8400_DCDC1_CONTROL_1 + offset,
42fad570b   Mark Brown   regulator: Add WM...
78
79
80
81
82
  				       WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP,
  				       WM8400_DC1_ACTIVE);
  
  	case REGULATOR_MODE_IDLE:
  		/* Datasheet: standby */
e08abeca3   Axel Lin   regulator: wm8400...
83
  		return regmap_update_bits(rmap, WM8400_DCDC1_CONTROL_1 + offset,
4001376ef   Axel Lin   regulator: wm8400...
84
  				       WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, 0);
42fad570b   Mark Brown   regulator: Add WM...
85
86
87
88
89
90
91
92
93
94
95
  	default:
  		return -EINVAL;
  	}
  }
  
  static unsigned int wm8400_dcdc_get_optimum_mode(struct regulator_dev *dev,
  						 int input_uV, int output_uV,
  						 int load_uA)
  {
  	return REGULATOR_MODE_NORMAL;
  }
b0d6dd3ba   Julia Lawall   regulator: wm8*: ...
96
  static const struct regulator_ops wm8400_dcdc_ops = {
c54a155d4   Mark Brown   regulator: wm8400...
97
98
99
100
  	.is_enabled = regulator_is_enabled_regmap,
  	.enable = regulator_enable_regmap,
  	.disable = regulator_disable_regmap,
  	.list_voltage = regulator_list_voltage_linear,
27eeabb7a   Axel Lin   regulator: wm8400...
101
  	.map_voltage = regulator_map_voltage_linear,
c54a155d4   Mark Brown   regulator: wm8400...
102
103
  	.get_voltage_sel = regulator_get_voltage_sel_regmap,
  	.set_voltage_sel = regulator_set_voltage_sel_regmap,
42fad570b   Mark Brown   regulator: Add WM...
104
105
106
107
108
109
110
111
112
113
  	.get_mode = wm8400_dcdc_get_mode,
  	.set_mode = wm8400_dcdc_set_mode,
  	.get_optimum_mode = wm8400_dcdc_get_optimum_mode,
  };
  
  static struct regulator_desc regulators[] = {
  	{
  		.name = "LDO1",
  		.id = WM8400_LDO1,
  		.ops = &wm8400_ldo_ops,
c54a155d4   Mark Brown   regulator: wm8400...
114
115
  		.enable_reg = WM8400_LDO1_CONTROL,
  		.enable_mask = WM8400_LDO1_ENA,
216765d92   Mark Brown   regulator: Implem...
116
  		.n_voltages = WM8400_LDO1_VSEL_MASK + 1,
6692e432a   Mark Brown   regulator: wm8400...
117
118
  		.linear_ranges = wm8400_ldo_ranges,
  		.n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),
c54a155d4   Mark Brown   regulator: wm8400...
119
120
  		.vsel_reg = WM8400_LDO1_CONTROL,
  		.vsel_mask = WM8400_LDO1_VSEL_MASK,
42fad570b   Mark Brown   regulator: Add WM...
121
122
123
124
125
126
127
  		.type = REGULATOR_VOLTAGE,
  		.owner = THIS_MODULE,
  	},
  	{
  		.name = "LDO2",
  		.id = WM8400_LDO2,
  		.ops = &wm8400_ldo_ops,
c54a155d4   Mark Brown   regulator: wm8400...
128
129
  		.enable_reg = WM8400_LDO2_CONTROL,
  		.enable_mask = WM8400_LDO2_ENA,
216765d92   Mark Brown   regulator: Implem...
130
  		.n_voltages = WM8400_LDO2_VSEL_MASK + 1,
6692e432a   Mark Brown   regulator: wm8400...
131
132
  		.linear_ranges = wm8400_ldo_ranges,
  		.n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),
42fad570b   Mark Brown   regulator: Add WM...
133
  		.type = REGULATOR_VOLTAGE,
c54a155d4   Mark Brown   regulator: wm8400...
134
135
  		.vsel_reg = WM8400_LDO2_CONTROL,
  		.vsel_mask = WM8400_LDO2_VSEL_MASK,
42fad570b   Mark Brown   regulator: Add WM...
136
137
138
139
140
141
  		.owner = THIS_MODULE,
  	},
  	{
  		.name = "LDO3",
  		.id = WM8400_LDO3,
  		.ops = &wm8400_ldo_ops,
c54a155d4   Mark Brown   regulator: wm8400...
142
143
  		.enable_reg = WM8400_LDO3_CONTROL,
  		.enable_mask = WM8400_LDO3_ENA,
216765d92   Mark Brown   regulator: Implem...
144
  		.n_voltages = WM8400_LDO3_VSEL_MASK + 1,
6692e432a   Mark Brown   regulator: wm8400...
145
146
  		.linear_ranges = wm8400_ldo_ranges,
  		.n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),
c54a155d4   Mark Brown   regulator: wm8400...
147
148
  		.vsel_reg = WM8400_LDO3_CONTROL,
  		.vsel_mask = WM8400_LDO3_VSEL_MASK,
42fad570b   Mark Brown   regulator: Add WM...
149
150
151
152
153
154
155
  		.type = REGULATOR_VOLTAGE,
  		.owner = THIS_MODULE,
  	},
  	{
  		.name = "LDO4",
  		.id = WM8400_LDO4,
  		.ops = &wm8400_ldo_ops,
c54a155d4   Mark Brown   regulator: wm8400...
156
157
  		.enable_reg = WM8400_LDO4_CONTROL,
  		.enable_mask = WM8400_LDO4_ENA,
216765d92   Mark Brown   regulator: Implem...
158
  		.n_voltages = WM8400_LDO4_VSEL_MASK + 1,
6692e432a   Mark Brown   regulator: wm8400...
159
160
  		.linear_ranges = wm8400_ldo_ranges,
  		.n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),
c54a155d4   Mark Brown   regulator: wm8400...
161
162
  		.vsel_reg = WM8400_LDO4_CONTROL,
  		.vsel_mask = WM8400_LDO4_VSEL_MASK,
42fad570b   Mark Brown   regulator: Add WM...
163
164
165
166
167
168
169
  		.type = REGULATOR_VOLTAGE,
  		.owner = THIS_MODULE,
  	},
  	{
  		.name = "DCDC1",
  		.id = WM8400_DCDC1,
  		.ops = &wm8400_dcdc_ops,
c54a155d4   Mark Brown   regulator: wm8400...
170
171
  		.enable_reg = WM8400_DCDC1_CONTROL_1,
  		.enable_mask = WM8400_DC1_ENA_MASK,
216765d92   Mark Brown   regulator: Implem...
172
  		.n_voltages = WM8400_DC1_VSEL_MASK + 1,
c54a155d4   Mark Brown   regulator: wm8400...
173
174
175
176
  		.vsel_reg = WM8400_DCDC1_CONTROL_1,
  		.vsel_mask = WM8400_DC1_VSEL_MASK,
  		.min_uV = 850000,
  		.uV_step = 25000,
42fad570b   Mark Brown   regulator: Add WM...
177
178
179
180
181
182
183
  		.type = REGULATOR_VOLTAGE,
  		.owner = THIS_MODULE,
  	},
  	{
  		.name = "DCDC2",
  		.id = WM8400_DCDC2,
  		.ops = &wm8400_dcdc_ops,
c54a155d4   Mark Brown   regulator: wm8400...
184
  		.enable_reg = WM8400_DCDC2_CONTROL_1,
178d08ea8   Axel Lin   regulator: wm8400...
185
  		.enable_mask = WM8400_DC2_ENA_MASK,
216765d92   Mark Brown   regulator: Implem...
186
  		.n_voltages = WM8400_DC2_VSEL_MASK + 1,
c54a155d4   Mark Brown   regulator: wm8400...
187
188
189
190
  		.vsel_reg = WM8400_DCDC2_CONTROL_1,
  		.vsel_mask = WM8400_DC2_VSEL_MASK,
  		.min_uV = 850000,
  		.uV_step = 25000,
42fad570b   Mark Brown   regulator: Add WM...
191
192
193
194
  		.type = REGULATOR_VOLTAGE,
  		.owner = THIS_MODULE,
  	},
  };
a5023574d   Bill Pemberton   regulator: remove...
195
  static int wm8400_regulator_probe(struct platform_device *pdev)
42fad570b   Mark Brown   regulator: Add WM...
196
  {
1ad02bbce   Dmitry Torokhov   Regulators: wm840...
197
  	struct wm8400 *wm8400 = container_of(pdev, struct wm8400, regulators[pdev->id]);
c172708d3   Mark Brown   regulator: core: ...
198
  	struct regulator_config config = { };
42fad570b   Mark Brown   regulator: Add WM...
199
  	struct regulator_dev *rdev;
c172708d3   Mark Brown   regulator: core: ...
200
  	config.dev = &pdev->dev;
dff91d0b7   Jingoo Han   regulator: use de...
201
  	config.init_data = dev_get_platdata(&pdev->dev);
c172708d3   Mark Brown   regulator: core: ...
202
  	config.driver_data = wm8400;
c54a155d4   Mark Brown   regulator: wm8400...
203
  	config.regmap = wm8400->regmap;
42fad570b   Mark Brown   regulator: Add WM...
204

eb8b3c836   Mark Brown   regulator: wm8400...
205
206
  	rdev = devm_regulator_register(&pdev->dev, &regulators[pdev->id],
  				       &config);
42fad570b   Mark Brown   regulator: Add WM...
207
208
  	if (IS_ERR(rdev))
  		return PTR_ERR(rdev);
1ad02bbce   Dmitry Torokhov   Regulators: wm840...
209
  	platform_set_drvdata(pdev, rdev);
42fad570b   Mark Brown   regulator: Add WM...
210
211
  	return 0;
  }
42fad570b   Mark Brown   regulator: Add WM...
212
213
214
215
216
  static struct platform_driver wm8400_regulator_driver = {
  	.driver = {
  		.name = "wm8400-regulator",
  	},
  	.probe = wm8400_regulator_probe,
42fad570b   Mark Brown   regulator: Add WM...
217
218
219
220
221
222
223
224
225
  };
  
  /**
   * wm8400_register_regulator - enable software control of a WM8400 regulator
   *
   * This function enables software control of a WM8400 regulator via
   * the regulator API.  It is intended to be called from the
   * platform_init() callback of the WM8400 MFD driver.
   *
0c5261663   Lee Jones   regulator: wm8400...
226
227
228
   * @dev:      The WM8400 device to operate on.
   * @reg:      The regulator to control.
   * @initdata: Regulator initdata for the regulator.
42fad570b   Mark Brown   regulator: Add WM...
229
230
231
232
   */
  int wm8400_register_regulator(struct device *dev, int reg,
  			      struct regulator_init_data *initdata)
  {
1909e2f65   Greg Kroah-Hartman   regulator: remove...
233
  	struct wm8400 *wm8400 = dev_get_drvdata(dev);
42fad570b   Mark Brown   regulator: Add WM...
234
235
236
237
238
239
240
241
242
  
  	if (wm8400->regulators[reg].name)
  		return -EBUSY;
  
  	initdata->driver_data = wm8400;
  
  	wm8400->regulators[reg].name = "wm8400-regulator";
  	wm8400->regulators[reg].id = reg;
  	wm8400->regulators[reg].dev.parent = dev;
42fad570b   Mark Brown   regulator: Add WM...
243
244
245
246
247
248
249
250
251
252
  	wm8400->regulators[reg].dev.platform_data = initdata;
  
  	return platform_device_register(&wm8400->regulators[reg]);
  }
  EXPORT_SYMBOL_GPL(wm8400_register_regulator);
  
  static int __init wm8400_regulator_init(void)
  {
  	return platform_driver_register(&wm8400_regulator_driver);
  }
5a1b22bee   Mark Brown   regulator: Move r...
253
  subsys_initcall(wm8400_regulator_init);
42fad570b   Mark Brown   regulator: Add WM...
254
255
256
257
258
259
260
261
262
263
264
  
  static void __exit wm8400_regulator_exit(void)
  {
  	platform_driver_unregister(&wm8400_regulator_driver);
  }
  module_exit(wm8400_regulator_exit);
  
  MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
  MODULE_DESCRIPTION("WM8400 regulator driver");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:wm8400-regulator");