13cad5fc8SAxel Lin // SPDX-License-Identifier: GPL-2.0+
23cad5fc8SAxel Lin //
33cad5fc8SAxel Lin // wm831x-ldo.c -- LDO driver for the WM831x series
43cad5fc8SAxel Lin //
53cad5fc8SAxel Lin // Copyright 2009 Wolfson Microelectronics PLC.
63cad5fc8SAxel Lin //
73cad5fc8SAxel Lin // Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
8d1c6b4feSMark Brown
9d1c6b4feSMark Brown #include <linux/module.h>
10d1c6b4feSMark Brown #include <linux/moduleparam.h>
11d1c6b4feSMark Brown #include <linux/init.h>
12d1c6b4feSMark Brown #include <linux/bitops.h>
13d1c6b4feSMark Brown #include <linux/err.h>
14d1c6b4feSMark Brown #include <linux/i2c.h>
15d1c6b4feSMark Brown #include <linux/platform_device.h>
16d1c6b4feSMark Brown #include <linux/regulator/driver.h>
175a0e3ad6STejun Heo #include <linux/slab.h>
18d1c6b4feSMark Brown
19d1c6b4feSMark Brown #include <linux/mfd/wm831x/core.h>
20d1c6b4feSMark Brown #include <linux/mfd/wm831x/regulator.h>
21d1c6b4feSMark Brown #include <linux/mfd/wm831x/pdata.h>
22d1c6b4feSMark Brown
23f1aba13fSMark Brown #define WM831X_LDO_MAX_NAME 9
24d1c6b4feSMark Brown
25d1c6b4feSMark Brown #define WM831X_LDO_CONTROL 0
26d1c6b4feSMark Brown #define WM831X_LDO_ON_CONTROL 1
27d1c6b4feSMark Brown #define WM831X_LDO_SLEEP_CONTROL 2
28d1c6b4feSMark Brown
29d1c6b4feSMark Brown #define WM831X_ALIVE_LDO_ON_CONTROL 0
30d1c6b4feSMark Brown #define WM831X_ALIVE_LDO_SLEEP_CONTROL 1
31d1c6b4feSMark Brown
32d1c6b4feSMark Brown struct wm831x_ldo {
33d1c6b4feSMark Brown char name[WM831X_LDO_MAX_NAME];
34f1aba13fSMark Brown char supply_name[WM831X_LDO_MAX_NAME];
35d1c6b4feSMark Brown struct regulator_desc desc;
36d1c6b4feSMark Brown int base;
37d1c6b4feSMark Brown struct wm831x *wm831x;
38d1c6b4feSMark Brown struct regulator_dev *regulator;
39d1c6b4feSMark Brown };
40d1c6b4feSMark Brown
41d1c6b4feSMark Brown /*
42d1c6b4feSMark Brown * Shared
43d1c6b4feSMark Brown */
44d1c6b4feSMark Brown
wm831x_ldo_uv_irq(int irq,void * data)45d1c6b4feSMark Brown static irqreturn_t wm831x_ldo_uv_irq(int irq, void *data)
46d1c6b4feSMark Brown {
47d1c6b4feSMark Brown struct wm831x_ldo *ldo = data;
48d1c6b4feSMark Brown
49d1c6b4feSMark Brown regulator_notifier_call_chain(ldo->regulator,
50d1c6b4feSMark Brown REGULATOR_EVENT_UNDER_VOLTAGE,
51d1c6b4feSMark Brown NULL);
52d1c6b4feSMark Brown
53d1c6b4feSMark Brown return IRQ_HANDLED;
54d1c6b4feSMark Brown }
55d1c6b4feSMark Brown
56d1c6b4feSMark Brown /*
57d1c6b4feSMark Brown * General purpose LDOs
58d1c6b4feSMark Brown */
59d1c6b4feSMark Brown
6060ab7f41SMatti Vaittinen static const struct linear_range wm831x_gp_ldo_ranges[] = {
618828bae4SAxel Lin REGULATOR_LINEAR_RANGE(900000, 0, 14, 50000),
628828bae4SAxel Lin REGULATOR_LINEAR_RANGE(1700000, 15, 31, 100000),
635ff26a14SMark Brown };
64d1c6b4feSMark Brown
wm831x_gp_ldo_set_suspend_voltage(struct regulator_dev * rdev,int uV)65d1c6b4feSMark Brown static int wm831x_gp_ldo_set_suspend_voltage(struct regulator_dev *rdev,
66d1c6b4feSMark Brown int uV)
67d1c6b4feSMark Brown {
68d1c6b4feSMark Brown struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
690a479689SAxel Lin struct wm831x *wm831x = ldo->wm831x;
700a479689SAxel Lin int sel, reg = ldo->base + WM831X_LDO_SLEEP_CONTROL;
71d1c6b4feSMark Brown
725ff26a14SMark Brown sel = regulator_map_voltage_linear_range(rdev, uV, uV);
730a479689SAxel Lin if (sel < 0)
740a479689SAxel Lin return sel;
750a479689SAxel Lin
760a479689SAxel Lin return wm831x_set_bits(wm831x, reg, WM831X_LDO1_ON_VSEL_MASK, sel);
77d1c6b4feSMark Brown }
78d1c6b4feSMark Brown
wm831x_gp_ldo_get_mode(struct regulator_dev * rdev)79d1c6b4feSMark Brown static unsigned int wm831x_gp_ldo_get_mode(struct regulator_dev *rdev)
80d1c6b4feSMark Brown {
81d1c6b4feSMark Brown struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
82d1c6b4feSMark Brown struct wm831x *wm831x = ldo->wm831x;
83d1c6b4feSMark Brown int ctrl_reg = ldo->base + WM831X_LDO_CONTROL;
84d1c6b4feSMark Brown int on_reg = ldo->base + WM831X_LDO_ON_CONTROL;
859a767d43SRoel Kluin int ret;
86d1c6b4feSMark Brown
87d1c6b4feSMark Brown ret = wm831x_reg_read(wm831x, on_reg);
88d1c6b4feSMark Brown if (ret < 0)
899a767d43SRoel Kluin return ret;
90d1c6b4feSMark Brown
91d1c6b4feSMark Brown if (!(ret & WM831X_LDO1_ON_MODE))
92d1c6b4feSMark Brown return REGULATOR_MODE_NORMAL;
93d1c6b4feSMark Brown
94d1c6b4feSMark Brown ret = wm831x_reg_read(wm831x, ctrl_reg);
95d1c6b4feSMark Brown if (ret < 0)
969a767d43SRoel Kluin return ret;
97d1c6b4feSMark Brown
98d1c6b4feSMark Brown if (ret & WM831X_LDO1_LP_MODE)
99d1c6b4feSMark Brown return REGULATOR_MODE_STANDBY;
100d1c6b4feSMark Brown else
101d1c6b4feSMark Brown return REGULATOR_MODE_IDLE;
102d1c6b4feSMark Brown }
103d1c6b4feSMark Brown
wm831x_gp_ldo_set_mode(struct regulator_dev * rdev,unsigned int mode)104d1c6b4feSMark Brown static int wm831x_gp_ldo_set_mode(struct regulator_dev *rdev,
105d1c6b4feSMark Brown unsigned int mode)
106d1c6b4feSMark Brown {
107d1c6b4feSMark Brown struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
108d1c6b4feSMark Brown struct wm831x *wm831x = ldo->wm831x;
109d1c6b4feSMark Brown int ctrl_reg = ldo->base + WM831X_LDO_CONTROL;
110d1c6b4feSMark Brown int on_reg = ldo->base + WM831X_LDO_ON_CONTROL;
111d1c6b4feSMark Brown int ret;
112d1c6b4feSMark Brown
113d1c6b4feSMark Brown
114d1c6b4feSMark Brown switch (mode) {
115d1c6b4feSMark Brown case REGULATOR_MODE_NORMAL:
116d1c6b4feSMark Brown ret = wm831x_set_bits(wm831x, on_reg,
117d1c6b4feSMark Brown WM831X_LDO1_ON_MODE, 0);
118d1c6b4feSMark Brown if (ret < 0)
119d1c6b4feSMark Brown return ret;
120d1c6b4feSMark Brown break;
121d1c6b4feSMark Brown
122d1c6b4feSMark Brown case REGULATOR_MODE_IDLE:
123d1c6b4feSMark Brown ret = wm831x_set_bits(wm831x, ctrl_reg,
124e260999cSAxel Lin WM831X_LDO1_LP_MODE, 0);
125d1c6b4feSMark Brown if (ret < 0)
126d1c6b4feSMark Brown return ret;
127d1c6b4feSMark Brown
128d1c6b4feSMark Brown ret = wm831x_set_bits(wm831x, on_reg,
129d1c6b4feSMark Brown WM831X_LDO1_ON_MODE,
130d1c6b4feSMark Brown WM831X_LDO1_ON_MODE);
131d1c6b4feSMark Brown if (ret < 0)
132d1c6b4feSMark Brown return ret;
133e260999cSAxel Lin break;
134d1c6b4feSMark Brown
135d1c6b4feSMark Brown case REGULATOR_MODE_STANDBY:
136d1c6b4feSMark Brown ret = wm831x_set_bits(wm831x, ctrl_reg,
137e260999cSAxel Lin WM831X_LDO1_LP_MODE,
138e260999cSAxel Lin WM831X_LDO1_LP_MODE);
139d1c6b4feSMark Brown if (ret < 0)
140d1c6b4feSMark Brown return ret;
141d1c6b4feSMark Brown
142d1c6b4feSMark Brown ret = wm831x_set_bits(wm831x, on_reg,
143d1c6b4feSMark Brown WM831X_LDO1_ON_MODE,
144d1c6b4feSMark Brown WM831X_LDO1_ON_MODE);
145d1c6b4feSMark Brown if (ret < 0)
146d1c6b4feSMark Brown return ret;
147d1c6b4feSMark Brown break;
148d1c6b4feSMark Brown
149d1c6b4feSMark Brown default:
150d1c6b4feSMark Brown return -EINVAL;
151d1c6b4feSMark Brown }
152d1c6b4feSMark Brown
153d1c6b4feSMark Brown return 0;
154d1c6b4feSMark Brown }
155d1c6b4feSMark Brown
wm831x_gp_ldo_get_status(struct regulator_dev * rdev)156d1c6b4feSMark Brown static int wm831x_gp_ldo_get_status(struct regulator_dev *rdev)
157d1c6b4feSMark Brown {
158d1c6b4feSMark Brown struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
159d1c6b4feSMark Brown struct wm831x *wm831x = ldo->wm831x;
160d1c6b4feSMark Brown int mask = 1 << rdev_get_id(rdev);
161d1c6b4feSMark Brown int ret;
162d1c6b4feSMark Brown
163d1c6b4feSMark Brown /* Is the regulator on? */
164d1c6b4feSMark Brown ret = wm831x_reg_read(wm831x, WM831X_LDO_STATUS);
165d1c6b4feSMark Brown if (ret < 0)
166d1c6b4feSMark Brown return ret;
167d1c6b4feSMark Brown if (!(ret & mask))
168d1c6b4feSMark Brown return REGULATOR_STATUS_OFF;
169d1c6b4feSMark Brown
170d1c6b4feSMark Brown /* Is it reporting under voltage? */
171d1c6b4feSMark Brown ret = wm831x_reg_read(wm831x, WM831X_LDO_UV_STATUS);
172363506cdSAxel Lin if (ret < 0)
173363506cdSAxel Lin return ret;
174d1c6b4feSMark Brown if (ret & mask)
175d1c6b4feSMark Brown return REGULATOR_STATUS_ERROR;
176d1c6b4feSMark Brown
177d1c6b4feSMark Brown ret = wm831x_gp_ldo_get_mode(rdev);
178d1c6b4feSMark Brown if (ret < 0)
179d1c6b4feSMark Brown return ret;
180d1c6b4feSMark Brown else
181d1c6b4feSMark Brown return regulator_mode_to_status(ret);
182d1c6b4feSMark Brown }
183d1c6b4feSMark Brown
wm831x_gp_ldo_get_optimum_mode(struct regulator_dev * rdev,int input_uV,int output_uV,int load_uA)184d1c6b4feSMark Brown static unsigned int wm831x_gp_ldo_get_optimum_mode(struct regulator_dev *rdev,
185d1c6b4feSMark Brown int input_uV,
186d1c6b4feSMark Brown int output_uV, int load_uA)
187d1c6b4feSMark Brown {
188d1c6b4feSMark Brown if (load_uA < 20000)
189d1c6b4feSMark Brown return REGULATOR_MODE_STANDBY;
190d1c6b4feSMark Brown if (load_uA < 50000)
191d1c6b4feSMark Brown return REGULATOR_MODE_IDLE;
192d1c6b4feSMark Brown return REGULATOR_MODE_NORMAL;
193d1c6b4feSMark Brown }
194d1c6b4feSMark Brown
195d1c6b4feSMark Brown
196b0d6dd3bSJulia Lawall static const struct regulator_ops wm831x_gp_ldo_ops = {
1975ff26a14SMark Brown .list_voltage = regulator_list_voltage_linear_range,
1985ff26a14SMark Brown .map_voltage = regulator_map_voltage_linear_range,
199ac663b47SMark Brown .get_voltage_sel = regulator_get_voltage_sel_regmap,
2000a479689SAxel Lin .set_voltage_sel = regulator_set_voltage_sel_regmap,
201d1c6b4feSMark Brown .set_suspend_voltage = wm831x_gp_ldo_set_suspend_voltage,
202d1c6b4feSMark Brown .get_mode = wm831x_gp_ldo_get_mode,
203d1c6b4feSMark Brown .set_mode = wm831x_gp_ldo_set_mode,
204d1c6b4feSMark Brown .get_status = wm831x_gp_ldo_get_status,
205d1c6b4feSMark Brown .get_optimum_mode = wm831x_gp_ldo_get_optimum_mode,
20622c5fb6aSMark Brown .get_bypass = regulator_get_bypass_regmap,
20722c5fb6aSMark Brown .set_bypass = regulator_set_bypass_regmap,
208d1c6b4feSMark Brown
209ca8c361bSMark Brown .is_enabled = regulator_is_enabled_regmap,
210ca8c361bSMark Brown .enable = regulator_enable_regmap,
211ca8c361bSMark Brown .disable = regulator_disable_regmap,
212d1c6b4feSMark Brown };
213d1c6b4feSMark Brown
wm831x_gp_ldo_probe(struct platform_device * pdev)214a5023574SBill Pemberton static int wm831x_gp_ldo_probe(struct platform_device *pdev)
215d1c6b4feSMark Brown {
216d1c6b4feSMark Brown struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
217dff91d0bSJingoo Han struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
218c172708dSMark Brown struct regulator_config config = { };
219137a6354SMark Brown int id;
220d1c6b4feSMark Brown struct wm831x_ldo *ldo;
221d1c6b4feSMark Brown struct resource *res;
222d1c6b4feSMark Brown int ret, irq;
223d1c6b4feSMark Brown
224137a6354SMark Brown if (pdata && pdata->wm831x_num)
225137a6354SMark Brown id = (pdata->wm831x_num * 10) + 1;
226137a6354SMark Brown else
227137a6354SMark Brown id = 0;
228137a6354SMark Brown id = pdev->id - id;
229137a6354SMark Brown
230d1c6b4feSMark Brown dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
231d1c6b4feSMark Brown
232fded2f4fSMark Brown ldo = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ldo), GFP_KERNEL);
233fae3b836SSachin Kamat if (!ldo)
234d1c6b4feSMark Brown return -ENOMEM;
235d1c6b4feSMark Brown
236d1c6b4feSMark Brown ldo->wm831x = wm831x;
237d1c6b4feSMark Brown
2385656098eSMark Brown res = platform_get_resource(pdev, IORESOURCE_REG, 0);
239d1c6b4feSMark Brown if (res == NULL) {
2405656098eSMark Brown dev_err(&pdev->dev, "No REG resource\n");
241d1c6b4feSMark Brown ret = -EINVAL;
242d1c6b4feSMark Brown goto err;
243d1c6b4feSMark Brown }
244d1c6b4feSMark Brown ldo->base = res->start;
245d1c6b4feSMark Brown
246d1c6b4feSMark Brown snprintf(ldo->name, sizeof(ldo->name), "LDO%d", id + 1);
247d1c6b4feSMark Brown ldo->desc.name = ldo->name;
248f1aba13fSMark Brown
249f1aba13fSMark Brown snprintf(ldo->supply_name, sizeof(ldo->supply_name),
250f1aba13fSMark Brown "LDO%dVDD", id + 1);
251f1aba13fSMark Brown ldo->desc.supply_name = ldo->supply_name;
252f1aba13fSMark Brown
253d1c6b4feSMark Brown ldo->desc.id = id;
254d1c6b4feSMark Brown ldo->desc.type = REGULATOR_VOLTAGE;
2555ff26a14SMark Brown ldo->desc.n_voltages = 32;
256d1c6b4feSMark Brown ldo->desc.ops = &wm831x_gp_ldo_ops;
257d1c6b4feSMark Brown ldo->desc.owner = THIS_MODULE;
258ac663b47SMark Brown ldo->desc.vsel_reg = ldo->base + WM831X_LDO_ON_CONTROL;
259ac663b47SMark Brown ldo->desc.vsel_mask = WM831X_LDO1_ON_VSEL_MASK;
260ca8c361bSMark Brown ldo->desc.enable_reg = WM831X_LDO_ENABLE;
261ca8c361bSMark Brown ldo->desc.enable_mask = 1 << id;
26222c5fb6aSMark Brown ldo->desc.bypass_reg = ldo->base;
26322c5fb6aSMark Brown ldo->desc.bypass_mask = WM831X_LDO1_SWI;
2645ff26a14SMark Brown ldo->desc.linear_ranges = wm831x_gp_ldo_ranges;
2655ff26a14SMark Brown ldo->desc.n_linear_ranges = ARRAY_SIZE(wm831x_gp_ldo_ranges);
266d1c6b4feSMark Brown
267c172708dSMark Brown config.dev = pdev->dev.parent;
268b7ca8788SMark Brown if (pdata)
269c172708dSMark Brown config.init_data = pdata->ldo[id];
270c172708dSMark Brown config.driver_data = ldo;
271ac663b47SMark Brown config.regmap = wm831x->regmap;
272c172708dSMark Brown
273fc7c60e3SMark Brown ldo->regulator = devm_regulator_register(&pdev->dev, &ldo->desc,
274fc7c60e3SMark Brown &config);
275d1c6b4feSMark Brown if (IS_ERR(ldo->regulator)) {
276d1c6b4feSMark Brown ret = PTR_ERR(ldo->regulator);
277d1c6b4feSMark Brown dev_err(wm831x->dev, "Failed to register LDO%d: %d\n",
278d1c6b4feSMark Brown id + 1, ret);
279d1c6b4feSMark Brown goto err;
280d1c6b4feSMark Brown }
281d1c6b4feSMark Brown
282cd99758bSMark Brown irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
28341c7a879SMark Brown ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
28441c7a879SMark Brown wm831x_ldo_uv_irq,
28529454738SFabio Estevam IRQF_TRIGGER_RISING | IRQF_ONESHOT,
28629454738SFabio Estevam ldo->name,
287d1c6b4feSMark Brown ldo);
288d1c6b4feSMark Brown if (ret != 0) {
289d1c6b4feSMark Brown dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
290d1c6b4feSMark Brown irq, ret);
291fc7c60e3SMark Brown goto err;
292d1c6b4feSMark Brown }
293d1c6b4feSMark Brown
294d1c6b4feSMark Brown platform_set_drvdata(pdev, ldo);
295d1c6b4feSMark Brown
296d1c6b4feSMark Brown return 0;
297d1c6b4feSMark Brown
298d1c6b4feSMark Brown err:
299d1c6b4feSMark Brown return ret;
300d1c6b4feSMark Brown }
301d1c6b4feSMark Brown
302d1c6b4feSMark Brown static struct platform_driver wm831x_gp_ldo_driver = {
303d1c6b4feSMark Brown .probe = wm831x_gp_ldo_probe,
304d1c6b4feSMark Brown .driver = {
305d1c6b4feSMark Brown .name = "wm831x-ldo",
306*259b93b2SDouglas Anderson .probe_type = PROBE_PREFER_ASYNCHRONOUS,
307d1c6b4feSMark Brown },
308d1c6b4feSMark Brown };
309d1c6b4feSMark Brown
310d1c6b4feSMark Brown /*
311d1c6b4feSMark Brown * Analogue LDOs
312d1c6b4feSMark Brown */
313d1c6b4feSMark Brown
31460ab7f41SMatti Vaittinen static const struct linear_range wm831x_aldo_ranges[] = {
3158828bae4SAxel Lin REGULATOR_LINEAR_RANGE(1000000, 0, 12, 50000),
3168828bae4SAxel Lin REGULATOR_LINEAR_RANGE(1700000, 13, 31, 100000),
3175ff26a14SMark Brown };
318d1c6b4feSMark Brown
wm831x_aldo_set_suspend_voltage(struct regulator_dev * rdev,int uV)319d1c6b4feSMark Brown static int wm831x_aldo_set_suspend_voltage(struct regulator_dev *rdev,
320d1c6b4feSMark Brown int uV)
321d1c6b4feSMark Brown {
322d1c6b4feSMark Brown struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
3230a479689SAxel Lin struct wm831x *wm831x = ldo->wm831x;
3240a479689SAxel Lin int sel, reg = ldo->base + WM831X_LDO_SLEEP_CONTROL;
325d1c6b4feSMark Brown
3265ff26a14SMark Brown sel = regulator_map_voltage_linear_range(rdev, uV, uV);
3270a479689SAxel Lin if (sel < 0)
3280a479689SAxel Lin return sel;
3290a479689SAxel Lin
3300a479689SAxel Lin return wm831x_set_bits(wm831x, reg, WM831X_LDO7_ON_VSEL_MASK, sel);
331d1c6b4feSMark Brown }
332d1c6b4feSMark Brown
wm831x_aldo_get_mode(struct regulator_dev * rdev)333d1c6b4feSMark Brown static unsigned int wm831x_aldo_get_mode(struct regulator_dev *rdev)
334d1c6b4feSMark Brown {
335d1c6b4feSMark Brown struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
336d1c6b4feSMark Brown struct wm831x *wm831x = ldo->wm831x;
337d1c6b4feSMark Brown int on_reg = ldo->base + WM831X_LDO_ON_CONTROL;
3386f17c652SRoel Kluin int ret;
339d1c6b4feSMark Brown
340d1c6b4feSMark Brown ret = wm831x_reg_read(wm831x, on_reg);
341d1c6b4feSMark Brown if (ret < 0)
342d1c6b4feSMark Brown return 0;
343d1c6b4feSMark Brown
344d1c6b4feSMark Brown if (ret & WM831X_LDO7_ON_MODE)
345d1c6b4feSMark Brown return REGULATOR_MODE_IDLE;
346d1c6b4feSMark Brown else
347d1c6b4feSMark Brown return REGULATOR_MODE_NORMAL;
348d1c6b4feSMark Brown }
349d1c6b4feSMark Brown
wm831x_aldo_set_mode(struct regulator_dev * rdev,unsigned int mode)350d1c6b4feSMark Brown static int wm831x_aldo_set_mode(struct regulator_dev *rdev,
351d1c6b4feSMark Brown unsigned int mode)
352d1c6b4feSMark Brown {
353d1c6b4feSMark Brown struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
354d1c6b4feSMark Brown struct wm831x *wm831x = ldo->wm831x;
355d1c6b4feSMark Brown int on_reg = ldo->base + WM831X_LDO_ON_CONTROL;
356d1c6b4feSMark Brown int ret;
357d1c6b4feSMark Brown
358d1c6b4feSMark Brown
359d1c6b4feSMark Brown switch (mode) {
360d1c6b4feSMark Brown case REGULATOR_MODE_NORMAL:
361e841a36aSAxel Lin ret = wm831x_set_bits(wm831x, on_reg, WM831X_LDO7_ON_MODE, 0);
362d1c6b4feSMark Brown if (ret < 0)
363d1c6b4feSMark Brown return ret;
364d1c6b4feSMark Brown break;
365d1c6b4feSMark Brown
366d1c6b4feSMark Brown case REGULATOR_MODE_IDLE:
367e841a36aSAxel Lin ret = wm831x_set_bits(wm831x, on_reg, WM831X_LDO7_ON_MODE,
368d1c6b4feSMark Brown WM831X_LDO7_ON_MODE);
369d1c6b4feSMark Brown if (ret < 0)
370d1c6b4feSMark Brown return ret;
371d1c6b4feSMark Brown break;
372d1c6b4feSMark Brown
373d1c6b4feSMark Brown default:
374d1c6b4feSMark Brown return -EINVAL;
375d1c6b4feSMark Brown }
376d1c6b4feSMark Brown
377d1c6b4feSMark Brown return 0;
378d1c6b4feSMark Brown }
379d1c6b4feSMark Brown
wm831x_aldo_get_status(struct regulator_dev * rdev)380d1c6b4feSMark Brown static int wm831x_aldo_get_status(struct regulator_dev *rdev)
381d1c6b4feSMark Brown {
382d1c6b4feSMark Brown struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
383d1c6b4feSMark Brown struct wm831x *wm831x = ldo->wm831x;
384d1c6b4feSMark Brown int mask = 1 << rdev_get_id(rdev);
385d1c6b4feSMark Brown int ret;
386d1c6b4feSMark Brown
387d1c6b4feSMark Brown /* Is the regulator on? */
388d1c6b4feSMark Brown ret = wm831x_reg_read(wm831x, WM831X_LDO_STATUS);
389d1c6b4feSMark Brown if (ret < 0)
390d1c6b4feSMark Brown return ret;
391d1c6b4feSMark Brown if (!(ret & mask))
392d1c6b4feSMark Brown return REGULATOR_STATUS_OFF;
393d1c6b4feSMark Brown
394d1c6b4feSMark Brown /* Is it reporting under voltage? */
395d1c6b4feSMark Brown ret = wm831x_reg_read(wm831x, WM831X_LDO_UV_STATUS);
396363506cdSAxel Lin if (ret < 0)
397363506cdSAxel Lin return ret;
398d1c6b4feSMark Brown if (ret & mask)
399d1c6b4feSMark Brown return REGULATOR_STATUS_ERROR;
400d1c6b4feSMark Brown
401d1c6b4feSMark Brown ret = wm831x_aldo_get_mode(rdev);
402d1c6b4feSMark Brown if (ret < 0)
403d1c6b4feSMark Brown return ret;
404d1c6b4feSMark Brown else
405d1c6b4feSMark Brown return regulator_mode_to_status(ret);
406d1c6b4feSMark Brown }
407d1c6b4feSMark Brown
408b0d6dd3bSJulia Lawall static const struct regulator_ops wm831x_aldo_ops = {
4095ff26a14SMark Brown .list_voltage = regulator_list_voltage_linear_range,
4105ff26a14SMark Brown .map_voltage = regulator_map_voltage_linear_range,
411ac663b47SMark Brown .get_voltage_sel = regulator_get_voltage_sel_regmap,
4120a479689SAxel Lin .set_voltage_sel = regulator_set_voltage_sel_regmap,
413d1c6b4feSMark Brown .set_suspend_voltage = wm831x_aldo_set_suspend_voltage,
414d1c6b4feSMark Brown .get_mode = wm831x_aldo_get_mode,
415d1c6b4feSMark Brown .set_mode = wm831x_aldo_set_mode,
416d1c6b4feSMark Brown .get_status = wm831x_aldo_get_status,
41722c5fb6aSMark Brown .set_bypass = regulator_set_bypass_regmap,
41822c5fb6aSMark Brown .get_bypass = regulator_get_bypass_regmap,
419d1c6b4feSMark Brown
420ca8c361bSMark Brown .is_enabled = regulator_is_enabled_regmap,
421ca8c361bSMark Brown .enable = regulator_enable_regmap,
422ca8c361bSMark Brown .disable = regulator_disable_regmap,
423d1c6b4feSMark Brown };
424d1c6b4feSMark Brown
wm831x_aldo_probe(struct platform_device * pdev)425a5023574SBill Pemberton static int wm831x_aldo_probe(struct platform_device *pdev)
426d1c6b4feSMark Brown {
427d1c6b4feSMark Brown struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
428dff91d0bSJingoo Han struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
429c172708dSMark Brown struct regulator_config config = { };
430137a6354SMark Brown int id;
431d1c6b4feSMark Brown struct wm831x_ldo *ldo;
432d1c6b4feSMark Brown struct resource *res;
433d1c6b4feSMark Brown int ret, irq;
434d1c6b4feSMark Brown
435137a6354SMark Brown if (pdata && pdata->wm831x_num)
436137a6354SMark Brown id = (pdata->wm831x_num * 10) + 1;
437137a6354SMark Brown else
438137a6354SMark Brown id = 0;
439137a6354SMark Brown id = pdev->id - id;
440137a6354SMark Brown
441d1c6b4feSMark Brown dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
442d1c6b4feSMark Brown
443fded2f4fSMark Brown ldo = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ldo), GFP_KERNEL);
444fae3b836SSachin Kamat if (!ldo)
445d1c6b4feSMark Brown return -ENOMEM;
446d1c6b4feSMark Brown
447d1c6b4feSMark Brown ldo->wm831x = wm831x;
448d1c6b4feSMark Brown
4495656098eSMark Brown res = platform_get_resource(pdev, IORESOURCE_REG, 0);
450d1c6b4feSMark Brown if (res == NULL) {
4515656098eSMark Brown dev_err(&pdev->dev, "No REG resource\n");
452d1c6b4feSMark Brown ret = -EINVAL;
453d1c6b4feSMark Brown goto err;
454d1c6b4feSMark Brown }
455d1c6b4feSMark Brown ldo->base = res->start;
456d1c6b4feSMark Brown
457d1c6b4feSMark Brown snprintf(ldo->name, sizeof(ldo->name), "LDO%d", id + 1);
458d1c6b4feSMark Brown ldo->desc.name = ldo->name;
459f1aba13fSMark Brown
460f1aba13fSMark Brown snprintf(ldo->supply_name, sizeof(ldo->supply_name),
461f1aba13fSMark Brown "LDO%dVDD", id + 1);
462f1aba13fSMark Brown ldo->desc.supply_name = ldo->supply_name;
463f1aba13fSMark Brown
464d1c6b4feSMark Brown ldo->desc.id = id;
465d1c6b4feSMark Brown ldo->desc.type = REGULATOR_VOLTAGE;
4665ff26a14SMark Brown ldo->desc.n_voltages = 32;
4675ff26a14SMark Brown ldo->desc.linear_ranges = wm831x_aldo_ranges;
4685ff26a14SMark Brown ldo->desc.n_linear_ranges = ARRAY_SIZE(wm831x_aldo_ranges);
469d1c6b4feSMark Brown ldo->desc.ops = &wm831x_aldo_ops;
470d1c6b4feSMark Brown ldo->desc.owner = THIS_MODULE;
471ac663b47SMark Brown ldo->desc.vsel_reg = ldo->base + WM831X_LDO_ON_CONTROL;
472ac663b47SMark Brown ldo->desc.vsel_mask = WM831X_LDO7_ON_VSEL_MASK;
473ca8c361bSMark Brown ldo->desc.enable_reg = WM831X_LDO_ENABLE;
474ca8c361bSMark Brown ldo->desc.enable_mask = 1 << id;
47522c5fb6aSMark Brown ldo->desc.bypass_reg = ldo->base;
47622c5fb6aSMark Brown ldo->desc.bypass_mask = WM831X_LDO7_SWI;
477d1c6b4feSMark Brown
478c172708dSMark Brown config.dev = pdev->dev.parent;
479b7ca8788SMark Brown if (pdata)
480c172708dSMark Brown config.init_data = pdata->ldo[id];
481c172708dSMark Brown config.driver_data = ldo;
482ac663b47SMark Brown config.regmap = wm831x->regmap;
483c172708dSMark Brown
484fc7c60e3SMark Brown ldo->regulator = devm_regulator_register(&pdev->dev, &ldo->desc,
485fc7c60e3SMark Brown &config);
486d1c6b4feSMark Brown if (IS_ERR(ldo->regulator)) {
487d1c6b4feSMark Brown ret = PTR_ERR(ldo->regulator);
488d1c6b4feSMark Brown dev_err(wm831x->dev, "Failed to register LDO%d: %d\n",
489d1c6b4feSMark Brown id + 1, ret);
490d1c6b4feSMark Brown goto err;
491d1c6b4feSMark Brown }
492d1c6b4feSMark Brown
493cd99758bSMark Brown irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
49441c7a879SMark Brown ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
49541c7a879SMark Brown wm831x_ldo_uv_irq,
49629454738SFabio Estevam IRQF_TRIGGER_RISING | IRQF_ONESHOT,
49729454738SFabio Estevam ldo->name, ldo);
498d1c6b4feSMark Brown if (ret != 0) {
499d1c6b4feSMark Brown dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
500d1c6b4feSMark Brown irq, ret);
501fc7c60e3SMark Brown goto err;
502d1c6b4feSMark Brown }
503d1c6b4feSMark Brown
504d1c6b4feSMark Brown platform_set_drvdata(pdev, ldo);
505d1c6b4feSMark Brown
506d1c6b4feSMark Brown return 0;
507d1c6b4feSMark Brown
508d1c6b4feSMark Brown err:
509d1c6b4feSMark Brown return ret;
510d1c6b4feSMark Brown }
511d1c6b4feSMark Brown
512d1c6b4feSMark Brown static struct platform_driver wm831x_aldo_driver = {
513d1c6b4feSMark Brown .probe = wm831x_aldo_probe,
514d1c6b4feSMark Brown .driver = {
515d1c6b4feSMark Brown .name = "wm831x-aldo",
516*259b93b2SDouglas Anderson .probe_type = PROBE_PREFER_ASYNCHRONOUS,
517d1c6b4feSMark Brown },
518d1c6b4feSMark Brown };
519d1c6b4feSMark Brown
520d1c6b4feSMark Brown /*
521d1c6b4feSMark Brown * Alive LDO
522d1c6b4feSMark Brown */
523d1c6b4feSMark Brown
524d1c6b4feSMark Brown #define WM831X_ALIVE_LDO_MAX_SELECTOR 0xf
525d1c6b4feSMark Brown
wm831x_alive_ldo_set_suspend_voltage(struct regulator_dev * rdev,int uV)526d1c6b4feSMark Brown static int wm831x_alive_ldo_set_suspend_voltage(struct regulator_dev *rdev,
527d1c6b4feSMark Brown int uV)
528d1c6b4feSMark Brown {
529d1c6b4feSMark Brown struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
5300a479689SAxel Lin struct wm831x *wm831x = ldo->wm831x;
5310a479689SAxel Lin int sel, reg = ldo->base + WM831X_ALIVE_LDO_SLEEP_CONTROL;
532d1c6b4feSMark Brown
5330a479689SAxel Lin sel = regulator_map_voltage_linear(rdev, uV, uV);
5340a479689SAxel Lin if (sel < 0)
5350a479689SAxel Lin return sel;
5360a479689SAxel Lin
5370a479689SAxel Lin return wm831x_set_bits(wm831x, reg, WM831X_LDO11_ON_VSEL_MASK, sel);
538d1c6b4feSMark Brown }
539d1c6b4feSMark Brown
wm831x_alive_ldo_get_status(struct regulator_dev * rdev)540d1c6b4feSMark Brown static int wm831x_alive_ldo_get_status(struct regulator_dev *rdev)
541d1c6b4feSMark Brown {
542d1c6b4feSMark Brown struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
543d1c6b4feSMark Brown struct wm831x *wm831x = ldo->wm831x;
544d1c6b4feSMark Brown int mask = 1 << rdev_get_id(rdev);
545d1c6b4feSMark Brown int ret;
546d1c6b4feSMark Brown
547d1c6b4feSMark Brown /* Is the regulator on? */
548d1c6b4feSMark Brown ret = wm831x_reg_read(wm831x, WM831X_LDO_STATUS);
549d1c6b4feSMark Brown if (ret < 0)
550d1c6b4feSMark Brown return ret;
551d1c6b4feSMark Brown if (ret & mask)
552d1c6b4feSMark Brown return REGULATOR_STATUS_ON;
553d1c6b4feSMark Brown else
554d1c6b4feSMark Brown return REGULATOR_STATUS_OFF;
555d1c6b4feSMark Brown }
556d1c6b4feSMark Brown
557b0d6dd3bSJulia Lawall static const struct regulator_ops wm831x_alive_ldo_ops = {
558d31e954eSMark Brown .list_voltage = regulator_list_voltage_linear,
559c2543b5fSAxel Lin .map_voltage = regulator_map_voltage_linear,
560ac663b47SMark Brown .get_voltage_sel = regulator_get_voltage_sel_regmap,
5610a479689SAxel Lin .set_voltage_sel = regulator_set_voltage_sel_regmap,
562d1c6b4feSMark Brown .set_suspend_voltage = wm831x_alive_ldo_set_suspend_voltage,
563d1c6b4feSMark Brown .get_status = wm831x_alive_ldo_get_status,
564d1c6b4feSMark Brown
565ca8c361bSMark Brown .is_enabled = regulator_is_enabled_regmap,
566ca8c361bSMark Brown .enable = regulator_enable_regmap,
567ca8c361bSMark Brown .disable = regulator_disable_regmap,
568d1c6b4feSMark Brown };
569d1c6b4feSMark Brown
wm831x_alive_ldo_probe(struct platform_device * pdev)570a5023574SBill Pemberton static int wm831x_alive_ldo_probe(struct platform_device *pdev)
571d1c6b4feSMark Brown {
572d1c6b4feSMark Brown struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
573dff91d0bSJingoo Han struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
574c172708dSMark Brown struct regulator_config config = { };
575137a6354SMark Brown int id;
576d1c6b4feSMark Brown struct wm831x_ldo *ldo;
577d1c6b4feSMark Brown struct resource *res;
578d1c6b4feSMark Brown int ret;
579d1c6b4feSMark Brown
580137a6354SMark Brown if (pdata && pdata->wm831x_num)
581137a6354SMark Brown id = (pdata->wm831x_num * 10) + 1;
582137a6354SMark Brown else
583137a6354SMark Brown id = 0;
584137a6354SMark Brown id = pdev->id - id;
585137a6354SMark Brown
586137a6354SMark Brown
587d1c6b4feSMark Brown dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
588d1c6b4feSMark Brown
589fded2f4fSMark Brown ldo = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ldo), GFP_KERNEL);
590fae3b836SSachin Kamat if (!ldo)
591d1c6b4feSMark Brown return -ENOMEM;
592d1c6b4feSMark Brown
593d1c6b4feSMark Brown ldo->wm831x = wm831x;
594d1c6b4feSMark Brown
5955656098eSMark Brown res = platform_get_resource(pdev, IORESOURCE_REG, 0);
596d1c6b4feSMark Brown if (res == NULL) {
5975656098eSMark Brown dev_err(&pdev->dev, "No REG resource\n");
598d1c6b4feSMark Brown ret = -EINVAL;
599d1c6b4feSMark Brown goto err;
600d1c6b4feSMark Brown }
601d1c6b4feSMark Brown ldo->base = res->start;
602d1c6b4feSMark Brown
603d1c6b4feSMark Brown snprintf(ldo->name, sizeof(ldo->name), "LDO%d", id + 1);
604d1c6b4feSMark Brown ldo->desc.name = ldo->name;
605f1aba13fSMark Brown
606f1aba13fSMark Brown snprintf(ldo->supply_name, sizeof(ldo->supply_name),
607f1aba13fSMark Brown "LDO%dVDD", id + 1);
608f1aba13fSMark Brown ldo->desc.supply_name = ldo->supply_name;
609f1aba13fSMark Brown
610d1c6b4feSMark Brown ldo->desc.id = id;
611d1c6b4feSMark Brown ldo->desc.type = REGULATOR_VOLTAGE;
612d1c6b4feSMark Brown ldo->desc.n_voltages = WM831X_ALIVE_LDO_MAX_SELECTOR + 1;
613d1c6b4feSMark Brown ldo->desc.ops = &wm831x_alive_ldo_ops;
614d1c6b4feSMark Brown ldo->desc.owner = THIS_MODULE;
615ac663b47SMark Brown ldo->desc.vsel_reg = ldo->base + WM831X_ALIVE_LDO_ON_CONTROL;
616ac663b47SMark Brown ldo->desc.vsel_mask = WM831X_LDO11_ON_VSEL_MASK;
617ca8c361bSMark Brown ldo->desc.enable_reg = WM831X_LDO_ENABLE;
618ca8c361bSMark Brown ldo->desc.enable_mask = 1 << id;
619d31e954eSMark Brown ldo->desc.min_uV = 800000;
620d31e954eSMark Brown ldo->desc.uV_step = 50000;
621eefaa3c6SMark Brown ldo->desc.enable_time = 1000;
622d1c6b4feSMark Brown
623c172708dSMark Brown config.dev = pdev->dev.parent;
624b7ca8788SMark Brown if (pdata)
625c172708dSMark Brown config.init_data = pdata->ldo[id];
626c172708dSMark Brown config.driver_data = ldo;
627ac663b47SMark Brown config.regmap = wm831x->regmap;
628c172708dSMark Brown
629fc7c60e3SMark Brown ldo->regulator = devm_regulator_register(&pdev->dev, &ldo->desc,
630fc7c60e3SMark Brown &config);
631d1c6b4feSMark Brown if (IS_ERR(ldo->regulator)) {
632d1c6b4feSMark Brown ret = PTR_ERR(ldo->regulator);
633d1c6b4feSMark Brown dev_err(wm831x->dev, "Failed to register LDO%d: %d\n",
634d1c6b4feSMark Brown id + 1, ret);
635d1c6b4feSMark Brown goto err;
636d1c6b4feSMark Brown }
637d1c6b4feSMark Brown
638d1c6b4feSMark Brown platform_set_drvdata(pdev, ldo);
639d1c6b4feSMark Brown
640d1c6b4feSMark Brown return 0;
641d1c6b4feSMark Brown
642d1c6b4feSMark Brown err:
643d1c6b4feSMark Brown return ret;
644d1c6b4feSMark Brown }
645d1c6b4feSMark Brown
646d1c6b4feSMark Brown static struct platform_driver wm831x_alive_ldo_driver = {
647d1c6b4feSMark Brown .probe = wm831x_alive_ldo_probe,
648d1c6b4feSMark Brown .driver = {
649d1c6b4feSMark Brown .name = "wm831x-alive-ldo",
650*259b93b2SDouglas Anderson .probe_type = PROBE_PREFER_ASYNCHRONOUS,
651d1c6b4feSMark Brown },
652d1c6b4feSMark Brown };
653d1c6b4feSMark Brown
65492a513b7SThierry Reding static struct platform_driver * const drivers[] = {
65592a513b7SThierry Reding &wm831x_gp_ldo_driver,
65692a513b7SThierry Reding &wm831x_aldo_driver,
65792a513b7SThierry Reding &wm831x_alive_ldo_driver,
65892a513b7SThierry Reding };
65992a513b7SThierry Reding
wm831x_ldo_init(void)660d1c6b4feSMark Brown static int __init wm831x_ldo_init(void)
661d1c6b4feSMark Brown {
66292a513b7SThierry Reding return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
663d1c6b4feSMark Brown }
664d1c6b4feSMark Brown subsys_initcall(wm831x_ldo_init);
665d1c6b4feSMark Brown
wm831x_ldo_exit(void)666d1c6b4feSMark Brown static void __exit wm831x_ldo_exit(void)
667d1c6b4feSMark Brown {
66892a513b7SThierry Reding platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
669d1c6b4feSMark Brown }
670d1c6b4feSMark Brown module_exit(wm831x_ldo_exit);
671d1c6b4feSMark Brown
672d1c6b4feSMark Brown /* Module information */
673d1c6b4feSMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
674d1c6b4feSMark Brown MODULE_DESCRIPTION("WM831x LDO driver");
675d1c6b4feSMark Brown MODULE_LICENSE("GPL");
676d1c6b4feSMark Brown MODULE_ALIAS("platform:wm831x-ldo");
677d1c6b4feSMark Brown MODULE_ALIAS("platform:wm831x-aldo");
678d1c6b4feSMark Brown MODULE_ALIAS("platform:wm831x-aliveldo");
679