Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/mfd/adp5520.c
15109 views
1
/*
2
* Base driver for Analog Devices ADP5520/ADP5501 MFD PMICs
3
* LCD Backlight: drivers/video/backlight/adp5520_bl
4
* LEDs : drivers/led/leds-adp5520
5
* GPIO : drivers/gpio/adp5520-gpio (ADP5520 only)
6
* Keys : drivers/input/keyboard/adp5520-keys (ADP5520 only)
7
*
8
* Copyright 2009 Analog Devices Inc.
9
*
10
* Derived from da903x:
11
* Copyright (C) 2008 Compulab, Ltd.
12
* Mike Rapoport <[email protected]>
13
*
14
* Copyright (C) 2006-2008 Marvell International Ltd.
15
* Eric Miao <[email protected]>
16
*
17
* Licensed under the GPL-2 or later.
18
*/
19
20
#include <linux/kernel.h>
21
#include <linux/module.h>
22
#include <linux/platform_device.h>
23
#include <linux/init.h>
24
#include <linux/slab.h>
25
#include <linux/interrupt.h>
26
#include <linux/irq.h>
27
#include <linux/err.h>
28
#include <linux/i2c.h>
29
30
#include <linux/mfd/adp5520.h>
31
32
struct adp5520_chip {
33
struct i2c_client *client;
34
struct device *dev;
35
struct mutex lock;
36
struct blocking_notifier_head notifier_list;
37
int irq;
38
unsigned long id;
39
};
40
41
static int __adp5520_read(struct i2c_client *client,
42
int reg, uint8_t *val)
43
{
44
int ret;
45
46
ret = i2c_smbus_read_byte_data(client, reg);
47
if (ret < 0) {
48
dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
49
return ret;
50
}
51
52
*val = (uint8_t)ret;
53
return 0;
54
}
55
56
static int __adp5520_write(struct i2c_client *client,
57
int reg, uint8_t val)
58
{
59
int ret;
60
61
ret = i2c_smbus_write_byte_data(client, reg, val);
62
if (ret < 0) {
63
dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
64
val, reg);
65
return ret;
66
}
67
return 0;
68
}
69
70
static int __adp5520_ack_bits(struct i2c_client *client, int reg,
71
uint8_t bit_mask)
72
{
73
struct adp5520_chip *chip = i2c_get_clientdata(client);
74
uint8_t reg_val;
75
int ret;
76
77
mutex_lock(&chip->lock);
78
79
ret = __adp5520_read(client, reg, &reg_val);
80
81
if (!ret) {
82
reg_val |= bit_mask;
83
ret = __adp5520_write(client, reg, reg_val);
84
}
85
86
mutex_unlock(&chip->lock);
87
return ret;
88
}
89
90
int adp5520_write(struct device *dev, int reg, uint8_t val)
91
{
92
return __adp5520_write(to_i2c_client(dev), reg, val);
93
}
94
EXPORT_SYMBOL_GPL(adp5520_write);
95
96
int adp5520_read(struct device *dev, int reg, uint8_t *val)
97
{
98
return __adp5520_read(to_i2c_client(dev), reg, val);
99
}
100
EXPORT_SYMBOL_GPL(adp5520_read);
101
102
int adp5520_set_bits(struct device *dev, int reg, uint8_t bit_mask)
103
{
104
struct adp5520_chip *chip = dev_get_drvdata(dev);
105
uint8_t reg_val;
106
int ret;
107
108
mutex_lock(&chip->lock);
109
110
ret = __adp5520_read(chip->client, reg, &reg_val);
111
112
if (!ret && ((reg_val & bit_mask) == 0)) {
113
reg_val |= bit_mask;
114
ret = __adp5520_write(chip->client, reg, reg_val);
115
}
116
117
mutex_unlock(&chip->lock);
118
return ret;
119
}
120
EXPORT_SYMBOL_GPL(adp5520_set_bits);
121
122
int adp5520_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
123
{
124
struct adp5520_chip *chip = dev_get_drvdata(dev);
125
uint8_t reg_val;
126
int ret;
127
128
mutex_lock(&chip->lock);
129
130
ret = __adp5520_read(chip->client, reg, &reg_val);
131
132
if (!ret && (reg_val & bit_mask)) {
133
reg_val &= ~bit_mask;
134
ret = __adp5520_write(chip->client, reg, reg_val);
135
}
136
137
mutex_unlock(&chip->lock);
138
return ret;
139
}
140
EXPORT_SYMBOL_GPL(adp5520_clr_bits);
141
142
int adp5520_register_notifier(struct device *dev, struct notifier_block *nb,
143
unsigned int events)
144
{
145
struct adp5520_chip *chip = dev_get_drvdata(dev);
146
147
if (chip->irq) {
148
adp5520_set_bits(chip->dev, ADP5520_INTERRUPT_ENABLE,
149
events & (ADP5520_KP_IEN | ADP5520_KR_IEN |
150
ADP5520_OVP_IEN | ADP5520_CMPR_IEN));
151
152
return blocking_notifier_chain_register(&chip->notifier_list,
153
nb);
154
}
155
156
return -ENODEV;
157
}
158
EXPORT_SYMBOL_GPL(adp5520_register_notifier);
159
160
int adp5520_unregister_notifier(struct device *dev, struct notifier_block *nb,
161
unsigned int events)
162
{
163
struct adp5520_chip *chip = dev_get_drvdata(dev);
164
165
adp5520_clr_bits(chip->dev, ADP5520_INTERRUPT_ENABLE,
166
events & (ADP5520_KP_IEN | ADP5520_KR_IEN |
167
ADP5520_OVP_IEN | ADP5520_CMPR_IEN));
168
169
return blocking_notifier_chain_unregister(&chip->notifier_list, nb);
170
}
171
EXPORT_SYMBOL_GPL(adp5520_unregister_notifier);
172
173
static irqreturn_t adp5520_irq_thread(int irq, void *data)
174
{
175
struct adp5520_chip *chip = data;
176
unsigned int events;
177
uint8_t reg_val;
178
int ret;
179
180
ret = __adp5520_read(chip->client, ADP5520_MODE_STATUS, &reg_val);
181
if (ret)
182
goto out;
183
184
events = reg_val & (ADP5520_OVP_INT | ADP5520_CMPR_INT |
185
ADP5520_GPI_INT | ADP5520_KR_INT | ADP5520_KP_INT);
186
187
blocking_notifier_call_chain(&chip->notifier_list, events, NULL);
188
/* ACK, Sticky bits are W1C */
189
__adp5520_ack_bits(chip->client, ADP5520_MODE_STATUS, events);
190
191
out:
192
return IRQ_HANDLED;
193
}
194
195
static int __remove_subdev(struct device *dev, void *unused)
196
{
197
platform_device_unregister(to_platform_device(dev));
198
return 0;
199
}
200
201
static int adp5520_remove_subdevs(struct adp5520_chip *chip)
202
{
203
return device_for_each_child(chip->dev, NULL, __remove_subdev);
204
}
205
206
static int __devinit adp5520_probe(struct i2c_client *client,
207
const struct i2c_device_id *id)
208
{
209
struct adp5520_platform_data *pdata = client->dev.platform_data;
210
struct platform_device *pdev;
211
struct adp5520_chip *chip;
212
int ret;
213
214
if (!i2c_check_functionality(client->adapter,
215
I2C_FUNC_SMBUS_BYTE_DATA)) {
216
dev_err(&client->dev, "SMBUS Word Data not Supported\n");
217
return -EIO;
218
}
219
220
if (pdata == NULL) {
221
dev_err(&client->dev, "missing platform data\n");
222
return -ENODEV;
223
}
224
225
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
226
if (!chip)
227
return -ENOMEM;
228
229
i2c_set_clientdata(client, chip);
230
chip->client = client;
231
232
chip->dev = &client->dev;
233
chip->irq = client->irq;
234
chip->id = id->driver_data;
235
mutex_init(&chip->lock);
236
237
if (chip->irq) {
238
BLOCKING_INIT_NOTIFIER_HEAD(&chip->notifier_list);
239
240
ret = request_threaded_irq(chip->irq, NULL, adp5520_irq_thread,
241
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
242
"adp5520", chip);
243
if (ret) {
244
dev_err(&client->dev, "failed to request irq %d\n",
245
chip->irq);
246
goto out_free_chip;
247
}
248
}
249
250
ret = adp5520_write(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY);
251
if (ret) {
252
dev_err(&client->dev, "failed to write\n");
253
goto out_free_irq;
254
}
255
256
if (pdata->keys) {
257
pdev = platform_device_register_data(chip->dev, "adp5520-keys",
258
chip->id, pdata->keys, sizeof(*pdata->keys));
259
if (IS_ERR(pdev)) {
260
ret = PTR_ERR(pdev);
261
goto out_remove_subdevs;
262
}
263
}
264
265
if (pdata->gpio) {
266
pdev = platform_device_register_data(chip->dev, "adp5520-gpio",
267
chip->id, pdata->gpio, sizeof(*pdata->gpio));
268
if (IS_ERR(pdev)) {
269
ret = PTR_ERR(pdev);
270
goto out_remove_subdevs;
271
}
272
}
273
274
if (pdata->leds) {
275
pdev = platform_device_register_data(chip->dev, "adp5520-led",
276
chip->id, pdata->leds, sizeof(*pdata->leds));
277
if (IS_ERR(pdev)) {
278
ret = PTR_ERR(pdev);
279
goto out_remove_subdevs;
280
}
281
}
282
283
if (pdata->backlight) {
284
pdev = platform_device_register_data(chip->dev,
285
"adp5520-backlight",
286
chip->id,
287
pdata->backlight,
288
sizeof(*pdata->backlight));
289
if (IS_ERR(pdev)) {
290
ret = PTR_ERR(pdev);
291
goto out_remove_subdevs;
292
}
293
}
294
295
return 0;
296
297
out_remove_subdevs:
298
adp5520_remove_subdevs(chip);
299
300
out_free_irq:
301
if (chip->irq)
302
free_irq(chip->irq, chip);
303
304
out_free_chip:
305
kfree(chip);
306
307
return ret;
308
}
309
310
static int __devexit adp5520_remove(struct i2c_client *client)
311
{
312
struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
313
314
if (chip->irq)
315
free_irq(chip->irq, chip);
316
317
adp5520_remove_subdevs(chip);
318
adp5520_write(chip->dev, ADP5520_MODE_STATUS, 0);
319
kfree(chip);
320
return 0;
321
}
322
323
#ifdef CONFIG_PM
324
static int adp5520_suspend(struct device *dev)
325
{
326
struct i2c_client *client = to_i2c_client(dev);
327
struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
328
329
adp5520_clr_bits(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY);
330
return 0;
331
}
332
333
static int adp5520_resume(struct device *dev)
334
{
335
struct i2c_client *client = to_i2c_client(dev);
336
struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
337
338
adp5520_set_bits(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY);
339
return 0;
340
}
341
#endif
342
343
static SIMPLE_DEV_PM_OPS(adp5520_pm, adp5520_suspend, adp5520_resume);
344
345
static const struct i2c_device_id adp5520_id[] = {
346
{ "pmic-adp5520", ID_ADP5520 },
347
{ "pmic-adp5501", ID_ADP5501 },
348
{ }
349
};
350
MODULE_DEVICE_TABLE(i2c, adp5520_id);
351
352
static struct i2c_driver adp5520_driver = {
353
.driver = {
354
.name = "adp5520",
355
.owner = THIS_MODULE,
356
.pm = &adp5520_pm,
357
},
358
.probe = adp5520_probe,
359
.remove = __devexit_p(adp5520_remove),
360
.id_table = adp5520_id,
361
};
362
363
static int __init adp5520_init(void)
364
{
365
return i2c_add_driver(&adp5520_driver);
366
}
367
module_init(adp5520_init);
368
369
static void __exit adp5520_exit(void)
370
{
371
i2c_del_driver(&adp5520_driver);
372
}
373
module_exit(adp5520_exit);
374
375
MODULE_AUTHOR("Michael Hennerich <[email protected]>");
376
MODULE_DESCRIPTION("ADP5520(01) PMIC-MFD Driver");
377
MODULE_LICENSE("GPL");
378
379