xref: /linux/drivers/mfd/adp5520.c (revision 4e8b70265c069544beb5a63b38544b53035b395e)
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");
380