Blame view

drivers/regulator/wm831x-isink.c 5.17 KB
3cad5fc89   Axel Lin   regulator: wm831x...
1
2
3
4
5
6
7
  // SPDX-License-Identifier: GPL-2.0+
  //
  // wm831x-isink.c  --  Current sink driver for the WM831x series
  //
  // Copyright 2009 Wolfson Microelectronics PLC.
  //
  // Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
d4d6b722e   Mark Brown   regulator: Add WM...
8
9
10
11
12
13
14
15
16
  
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/init.h>
  #include <linux/bitops.h>
  #include <linux/err.h>
  #include <linux/i2c.h>
  #include <linux/platform_device.h>
  #include <linux/regulator/driver.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
17
  #include <linux/slab.h>
d4d6b722e   Mark Brown   regulator: Add WM...
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  
  #include <linux/mfd/wm831x/core.h>
  #include <linux/mfd/wm831x/regulator.h>
  #include <linux/mfd/wm831x/pdata.h>
  
  #define WM831X_ISINK_MAX_NAME 7
  
  struct wm831x_isink {
  	char name[WM831X_ISINK_MAX_NAME];
  	struct regulator_desc desc;
  	int reg;
  	struct wm831x *wm831x;
  	struct regulator_dev *regulator;
  };
  
  static int wm831x_isink_enable(struct regulator_dev *rdev)
  {
  	struct wm831x_isink *isink = rdev_get_drvdata(rdev);
  	struct wm831x *wm831x = isink->wm831x;
  	int ret;
  
  	/* We have a two stage enable: first start the ISINK... */
  	ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA,
  			      WM831X_CS1_ENA);
  	if (ret != 0)
  		return ret;
  
  	/* ...then enable drive */
  	ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_DRIVE,
  			      WM831X_CS1_DRIVE);
  	if (ret != 0)
  		wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA, 0);
  
  	return ret;
  
  }
  
  static int wm831x_isink_disable(struct regulator_dev *rdev)
  {
  	struct wm831x_isink *isink = rdev_get_drvdata(rdev);
  	struct wm831x *wm831x = isink->wm831x;
  	int ret;
  
  	ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_DRIVE, 0);
  	if (ret < 0)
  		return ret;
  
  	ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA, 0);
  	if (ret < 0)
  		return ret;
  
  	return ret;
  
  }
  
  static int wm831x_isink_is_enabled(struct regulator_dev *rdev)
  {
  	struct wm831x_isink *isink = rdev_get_drvdata(rdev);
  	struct wm831x *wm831x = isink->wm831x;
  	int ret;
  
  	ret = wm831x_reg_read(wm831x, isink->reg);
  	if (ret < 0)
  		return ret;
  
  	if ((ret & (WM831X_CS1_ENA | WM831X_CS1_DRIVE)) ==
  	    (WM831X_CS1_ENA | WM831X_CS1_DRIVE))
  		return 1;
  	else
  		return 0;
  }
b0d6dd3ba   Julia Lawall   regulator: wm8*: ...
89
  static const struct regulator_ops wm831x_isink_ops = {
d4d6b722e   Mark Brown   regulator: Add WM...
90
91
92
  	.is_enabled = wm831x_isink_is_enabled,
  	.enable = wm831x_isink_enable,
  	.disable = wm831x_isink_disable,
d48acfd03   Axel Lin   regulator: wm831x...
93
94
  	.set_current_limit = regulator_set_current_limit_regmap,
  	.get_current_limit = regulator_get_current_limit_regmap,
d4d6b722e   Mark Brown   regulator: Add WM...
95
96
97
98
99
100
101
102
103
104
105
106
  };
  
  static irqreturn_t wm831x_isink_irq(int irq, void *data)
  {
  	struct wm831x_isink *isink = data;
  
  	regulator_notifier_call_chain(isink->regulator,
  				      REGULATOR_EVENT_OVER_CURRENT,
  				      NULL);
  
  	return IRQ_HANDLED;
  }
a5023574d   Bill Pemberton   regulator: remove...
107
  static int wm831x_isink_probe(struct platform_device *pdev)
d4d6b722e   Mark Brown   regulator: Add WM...
108
109
  {
  	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
dff91d0b7   Jingoo Han   regulator: use de...
110
  	struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
d4d6b722e   Mark Brown   regulator: Add WM...
111
112
  	struct wm831x_isink *isink;
  	int id = pdev->id % ARRAY_SIZE(pdata->isink);
c172708d3   Mark Brown   regulator: core: ...
113
  	struct regulator_config config = { };
d4d6b722e   Mark Brown   regulator: Add WM...
114
115
116
117
118
119
120
121
  	struct resource *res;
  	int ret, irq;
  
  	dev_dbg(&pdev->dev, "Probing ISINK%d
  ", id + 1);
  
  	if (pdata == NULL || pdata->isink[id] == NULL)
  		return -ENODEV;
fded2f4fa   Mark Brown   regulator: Conver...
122
123
  	isink = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_isink),
  			     GFP_KERNEL);
d718debcd   Sachin Kamat   regulator: wm831x...
124
  	if (!isink)
d4d6b722e   Mark Brown   regulator: Add WM...
125
  		return -ENOMEM;
d4d6b722e   Mark Brown   regulator: Add WM...
126

e8092da92   Mark Brown   regulator: Initia...
127
  	isink->wm831x = wm831x;
5656098e1   Mark Brown   mfd: wm831x: Conv...
128
  	res = platform_get_resource(pdev, IORESOURCE_REG, 0);
d4d6b722e   Mark Brown   regulator: Add WM...
129
  	if (res == NULL) {
5656098e1   Mark Brown   mfd: wm831x: Conv...
130
131
  		dev_err(&pdev->dev, "No REG resource
  ");
d4d6b722e   Mark Brown   regulator: Add WM...
132
133
134
135
136
137
138
139
140
141
142
143
144
145
  		ret = -EINVAL;
  		goto err;
  	}
  	isink->reg = res->start;
  
  	/* For current parts this is correct; probably need to revisit
  	 * in future.
  	 */
  	snprintf(isink->name, sizeof(isink->name), "ISINK%d", id + 1);
  	isink->desc.name = isink->name;
  	isink->desc.id = id;
  	isink->desc.ops = &wm831x_isink_ops;
  	isink->desc.type = REGULATOR_CURRENT;
  	isink->desc.owner = THIS_MODULE;
d48acfd03   Axel Lin   regulator: wm831x...
146
147
148
149
  	isink->desc.curr_table = wm831x_isinkv_values,
  	isink->desc.n_current_limits = ARRAY_SIZE(wm831x_isinkv_values),
  	isink->desc.csel_reg = isink->reg,
  	isink->desc.csel_mask = WM831X_CS1_ISEL_MASK,
d4d6b722e   Mark Brown   regulator: Add WM...
150

c172708d3   Mark Brown   regulator: core: ...
151
152
153
  	config.dev = pdev->dev.parent;
  	config.init_data = pdata->isink[id];
  	config.driver_data = isink;
d48acfd03   Axel Lin   regulator: wm831x...
154
  	config.regmap = wm831x->regmap;
c172708d3   Mark Brown   regulator: core: ...
155

af151ded1   Mark Brown   regulator: wm831x...
156
157
  	isink->regulator = devm_regulator_register(&pdev->dev, &isink->desc,
  						   &config);
d4d6b722e   Mark Brown   regulator: Add WM...
158
159
160
161
162
163
164
  	if (IS_ERR(isink->regulator)) {
  		ret = PTR_ERR(isink->regulator);
  		dev_err(wm831x->dev, "Failed to register ISINK%d: %d
  ",
  			id + 1, ret);
  		goto err;
  	}
cd99758ba   Mark Brown   mfd: Convert wm83...
165
  	irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0));
63fb3149c   Mark Brown   regulator: wm831x...
166
167
  	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
  					wm831x_isink_irq,
29454738f   Fabio Estevam   regulator: wm831x...
168
169
  					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
  					isink->name,
63fb3149c   Mark Brown   regulator: wm831x...
170
  					isink);
d4d6b722e   Mark Brown   regulator: Add WM...
171
172
173
174
  	if (ret != 0) {
  		dev_err(&pdev->dev, "Failed to request ISINK IRQ %d: %d
  ",
  			irq, ret);
af151ded1   Mark Brown   regulator: wm831x...
175
  		goto err;
d4d6b722e   Mark Brown   regulator: Add WM...
176
177
178
179
180
  	}
  
  	platform_set_drvdata(pdev, isink);
  
  	return 0;
d4d6b722e   Mark Brown   regulator: Add WM...
181
  err:
d4d6b722e   Mark Brown   regulator: Add WM...
182
183
  	return ret;
  }
d4d6b722e   Mark Brown   regulator: Add WM...
184
185
  static struct platform_driver wm831x_isink_driver = {
  	.probe = wm831x_isink_probe,
d4d6b722e   Mark Brown   regulator: Add WM...
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
  	.driver		= {
  		.name	= "wm831x-isink",
  	},
  };
  
  static int __init wm831x_isink_init(void)
  {
  	int ret;
  	ret = platform_driver_register(&wm831x_isink_driver);
  	if (ret != 0)
  		pr_err("Failed to register WM831x ISINK driver: %d
  ", ret);
  
  	return ret;
  }
  subsys_initcall(wm831x_isink_init);
  
  static void __exit wm831x_isink_exit(void)
  {
  	platform_driver_unregister(&wm831x_isink_driver);
  }
  module_exit(wm831x_isink_exit);
  
  /* Module information */
  MODULE_AUTHOR("Mark Brown");
  MODULE_DESCRIPTION("WM831x current sink driver");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:wm831x-isink");