1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun //
3*4882a593Smuzhiyun // wm831x-isink.c -- Current sink driver for the WM831x series
4*4882a593Smuzhiyun //
5*4882a593Smuzhiyun // Copyright 2009 Wolfson Microelectronics PLC.
6*4882a593Smuzhiyun //
7*4882a593Smuzhiyun // Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/moduleparam.h>
11*4882a593Smuzhiyun #include <linux/init.h>
12*4882a593Smuzhiyun #include <linux/bitops.h>
13*4882a593Smuzhiyun #include <linux/err.h>
14*4882a593Smuzhiyun #include <linux/i2c.h>
15*4882a593Smuzhiyun #include <linux/platform_device.h>
16*4882a593Smuzhiyun #include <linux/regulator/driver.h>
17*4882a593Smuzhiyun #include <linux/slab.h>
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun #include <linux/mfd/wm831x/core.h>
20*4882a593Smuzhiyun #include <linux/mfd/wm831x/regulator.h>
21*4882a593Smuzhiyun #include <linux/mfd/wm831x/pdata.h>
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #define WM831X_ISINK_MAX_NAME 7
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun struct wm831x_isink {
26*4882a593Smuzhiyun char name[WM831X_ISINK_MAX_NAME];
27*4882a593Smuzhiyun struct regulator_desc desc;
28*4882a593Smuzhiyun int reg;
29*4882a593Smuzhiyun struct wm831x *wm831x;
30*4882a593Smuzhiyun struct regulator_dev *regulator;
31*4882a593Smuzhiyun };
32*4882a593Smuzhiyun
wm831x_isink_enable(struct regulator_dev * rdev)33*4882a593Smuzhiyun static int wm831x_isink_enable(struct regulator_dev *rdev)
34*4882a593Smuzhiyun {
35*4882a593Smuzhiyun struct wm831x_isink *isink = rdev_get_drvdata(rdev);
36*4882a593Smuzhiyun struct wm831x *wm831x = isink->wm831x;
37*4882a593Smuzhiyun int ret;
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun /* We have a two stage enable: first start the ISINK... */
40*4882a593Smuzhiyun ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA,
41*4882a593Smuzhiyun WM831X_CS1_ENA);
42*4882a593Smuzhiyun if (ret != 0)
43*4882a593Smuzhiyun return ret;
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun /* ...then enable drive */
46*4882a593Smuzhiyun ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_DRIVE,
47*4882a593Smuzhiyun WM831X_CS1_DRIVE);
48*4882a593Smuzhiyun if (ret != 0)
49*4882a593Smuzhiyun wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA, 0);
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun return ret;
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun
wm831x_isink_disable(struct regulator_dev * rdev)55*4882a593Smuzhiyun static int wm831x_isink_disable(struct regulator_dev *rdev)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun struct wm831x_isink *isink = rdev_get_drvdata(rdev);
58*4882a593Smuzhiyun struct wm831x *wm831x = isink->wm831x;
59*4882a593Smuzhiyun int ret;
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_DRIVE, 0);
62*4882a593Smuzhiyun if (ret < 0)
63*4882a593Smuzhiyun return ret;
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA, 0);
66*4882a593Smuzhiyun if (ret < 0)
67*4882a593Smuzhiyun return ret;
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun return ret;
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun
wm831x_isink_is_enabled(struct regulator_dev * rdev)73*4882a593Smuzhiyun static int wm831x_isink_is_enabled(struct regulator_dev *rdev)
74*4882a593Smuzhiyun {
75*4882a593Smuzhiyun struct wm831x_isink *isink = rdev_get_drvdata(rdev);
76*4882a593Smuzhiyun struct wm831x *wm831x = isink->wm831x;
77*4882a593Smuzhiyun int ret;
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun ret = wm831x_reg_read(wm831x, isink->reg);
80*4882a593Smuzhiyun if (ret < 0)
81*4882a593Smuzhiyun return ret;
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun if ((ret & (WM831X_CS1_ENA | WM831X_CS1_DRIVE)) ==
84*4882a593Smuzhiyun (WM831X_CS1_ENA | WM831X_CS1_DRIVE))
85*4882a593Smuzhiyun return 1;
86*4882a593Smuzhiyun else
87*4882a593Smuzhiyun return 0;
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun static const struct regulator_ops wm831x_isink_ops = {
91*4882a593Smuzhiyun .is_enabled = wm831x_isink_is_enabled,
92*4882a593Smuzhiyun .enable = wm831x_isink_enable,
93*4882a593Smuzhiyun .disable = wm831x_isink_disable,
94*4882a593Smuzhiyun .set_current_limit = regulator_set_current_limit_regmap,
95*4882a593Smuzhiyun .get_current_limit = regulator_get_current_limit_regmap,
96*4882a593Smuzhiyun };
97*4882a593Smuzhiyun
wm831x_isink_irq(int irq,void * data)98*4882a593Smuzhiyun static irqreturn_t wm831x_isink_irq(int irq, void *data)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun struct wm831x_isink *isink = data;
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun regulator_notifier_call_chain(isink->regulator,
103*4882a593Smuzhiyun REGULATOR_EVENT_OVER_CURRENT,
104*4882a593Smuzhiyun NULL);
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun return IRQ_HANDLED;
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun
wm831x_isink_probe(struct platform_device * pdev)110*4882a593Smuzhiyun static int wm831x_isink_probe(struct platform_device *pdev)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
113*4882a593Smuzhiyun struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
114*4882a593Smuzhiyun struct wm831x_isink *isink;
115*4882a593Smuzhiyun int id = pdev->id % ARRAY_SIZE(pdata->isink);
116*4882a593Smuzhiyun struct regulator_config config = { };
117*4882a593Smuzhiyun struct resource *res;
118*4882a593Smuzhiyun int ret, irq;
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun dev_dbg(&pdev->dev, "Probing ISINK%d\n", id + 1);
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun if (pdata == NULL || pdata->isink[id] == NULL)
123*4882a593Smuzhiyun return -ENODEV;
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun isink = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_isink),
126*4882a593Smuzhiyun GFP_KERNEL);
127*4882a593Smuzhiyun if (!isink)
128*4882a593Smuzhiyun return -ENOMEM;
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun isink->wm831x = wm831x;
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun res = platform_get_resource(pdev, IORESOURCE_REG, 0);
133*4882a593Smuzhiyun if (res == NULL) {
134*4882a593Smuzhiyun dev_err(&pdev->dev, "No REG resource\n");
135*4882a593Smuzhiyun ret = -EINVAL;
136*4882a593Smuzhiyun goto err;
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun isink->reg = res->start;
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun /* For current parts this is correct; probably need to revisit
141*4882a593Smuzhiyun * in future.
142*4882a593Smuzhiyun */
143*4882a593Smuzhiyun snprintf(isink->name, sizeof(isink->name), "ISINK%d", id + 1);
144*4882a593Smuzhiyun isink->desc.name = isink->name;
145*4882a593Smuzhiyun isink->desc.id = id;
146*4882a593Smuzhiyun isink->desc.ops = &wm831x_isink_ops;
147*4882a593Smuzhiyun isink->desc.type = REGULATOR_CURRENT;
148*4882a593Smuzhiyun isink->desc.owner = THIS_MODULE;
149*4882a593Smuzhiyun isink->desc.curr_table = wm831x_isinkv_values,
150*4882a593Smuzhiyun isink->desc.n_current_limits = ARRAY_SIZE(wm831x_isinkv_values),
151*4882a593Smuzhiyun isink->desc.csel_reg = isink->reg,
152*4882a593Smuzhiyun isink->desc.csel_mask = WM831X_CS1_ISEL_MASK,
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun config.dev = pdev->dev.parent;
155*4882a593Smuzhiyun config.init_data = pdata->isink[id];
156*4882a593Smuzhiyun config.driver_data = isink;
157*4882a593Smuzhiyun config.regmap = wm831x->regmap;
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun isink->regulator = devm_regulator_register(&pdev->dev, &isink->desc,
160*4882a593Smuzhiyun &config);
161*4882a593Smuzhiyun if (IS_ERR(isink->regulator)) {
162*4882a593Smuzhiyun ret = PTR_ERR(isink->regulator);
163*4882a593Smuzhiyun dev_err(wm831x->dev, "Failed to register ISINK%d: %d\n",
164*4882a593Smuzhiyun id + 1, ret);
165*4882a593Smuzhiyun goto err;
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0));
169*4882a593Smuzhiyun ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
170*4882a593Smuzhiyun wm831x_isink_irq,
171*4882a593Smuzhiyun IRQF_TRIGGER_RISING | IRQF_ONESHOT,
172*4882a593Smuzhiyun isink->name,
173*4882a593Smuzhiyun isink);
174*4882a593Smuzhiyun if (ret != 0) {
175*4882a593Smuzhiyun dev_err(&pdev->dev, "Failed to request ISINK IRQ %d: %d\n",
176*4882a593Smuzhiyun irq, ret);
177*4882a593Smuzhiyun goto err;
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun platform_set_drvdata(pdev, isink);
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun return 0;
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun err:
185*4882a593Smuzhiyun return ret;
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun static struct platform_driver wm831x_isink_driver = {
189*4882a593Smuzhiyun .probe = wm831x_isink_probe,
190*4882a593Smuzhiyun .driver = {
191*4882a593Smuzhiyun .name = "wm831x-isink",
192*4882a593Smuzhiyun },
193*4882a593Smuzhiyun };
194*4882a593Smuzhiyun
wm831x_isink_init(void)195*4882a593Smuzhiyun static int __init wm831x_isink_init(void)
196*4882a593Smuzhiyun {
197*4882a593Smuzhiyun int ret;
198*4882a593Smuzhiyun ret = platform_driver_register(&wm831x_isink_driver);
199*4882a593Smuzhiyun if (ret != 0)
200*4882a593Smuzhiyun pr_err("Failed to register WM831x ISINK driver: %d\n", ret);
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun return ret;
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun subsys_initcall(wm831x_isink_init);
205*4882a593Smuzhiyun
wm831x_isink_exit(void)206*4882a593Smuzhiyun static void __exit wm831x_isink_exit(void)
207*4882a593Smuzhiyun {
208*4882a593Smuzhiyun platform_driver_unregister(&wm831x_isink_driver);
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun module_exit(wm831x_isink_exit);
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun /* Module information */
213*4882a593Smuzhiyun MODULE_AUTHOR("Mark Brown");
214*4882a593Smuzhiyun MODULE_DESCRIPTION("WM831x current sink driver");
215*4882a593Smuzhiyun MODULE_LICENSE("GPL");
216*4882a593Smuzhiyun MODULE_ALIAS("platform:wm831x-isink");
217