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