]>
Commit | Line | Data |
---|---|---|
d50f8f33 HZ |
1 | /* |
2 | * Base driver for Maxim MAX8925 | |
3 | * | |
4 | * Copyright (C) 2009 Marvell International Ltd. | |
5 | * Haojian Zhuang <haojian.zhuang@marvell.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | */ | |
11 | ||
12 | #include <linux/kernel.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/i2c.h> | |
15 | #include <linux/interrupt.h> | |
16 | #include <linux/platform_device.h> | |
17 | #include <linux/mfd/core.h> | |
18 | #include <linux/mfd/max8925.h> | |
19 | ||
20 | #define IRQ_MODE_STATUS 0 | |
21 | #define IRQ_MODE_MASK 1 | |
22 | ||
1ad99893 HZ |
23 | static struct resource backlight_resources[] = { |
24 | { | |
25 | .name = "max8925-backlight", | |
26 | .start = MAX8925_WLED_MODE_CNTL, | |
27 | .end = MAX8925_WLED_CNTL, | |
28 | .flags = IORESOURCE_IO, | |
29 | }, | |
30 | }; | |
31 | ||
32 | static struct mfd_cell backlight_devs[] = { | |
33 | { | |
34 | .name = "max8925-backlight", | |
35 | .num_resources = 1, | |
36 | .resources = &backlight_resources[0], | |
37 | .id = -1, | |
38 | }, | |
39 | }; | |
40 | ||
41 | static struct resource touch_resources[] = { | |
42 | { | |
43 | .name = "max8925-tsc", | |
44 | .start = MAX8925_TSC_IRQ, | |
45 | .end = MAX8925_ADC_RES_END, | |
46 | .flags = IORESOURCE_IO, | |
47 | }, | |
48 | }; | |
49 | ||
50 | static struct mfd_cell touch_devs[] = { | |
51 | { | |
52 | .name = "max8925-touch", | |
53 | .num_resources = 1, | |
54 | .resources = &touch_resources[0], | |
55 | .id = -1, | |
56 | }, | |
57 | }; | |
58 | ||
59 | #define MAX8925_REG_RESOURCE(_start, _end) \ | |
60 | { \ | |
61 | .start = MAX8925_##_start, \ | |
62 | .end = MAX8925_##_end, \ | |
63 | .flags = IORESOURCE_IO, \ | |
64 | } | |
65 | ||
66 | static struct resource regulator_resources[] = { | |
67 | MAX8925_REG_RESOURCE(SDCTL1, SDCTL1), | |
68 | MAX8925_REG_RESOURCE(SDCTL2, SDCTL2), | |
69 | MAX8925_REG_RESOURCE(SDCTL3, SDCTL3), | |
70 | MAX8925_REG_RESOURCE(LDOCTL1, LDOCTL1), | |
71 | MAX8925_REG_RESOURCE(LDOCTL2, LDOCTL2), | |
72 | MAX8925_REG_RESOURCE(LDOCTL3, LDOCTL3), | |
73 | MAX8925_REG_RESOURCE(LDOCTL4, LDOCTL4), | |
74 | MAX8925_REG_RESOURCE(LDOCTL5, LDOCTL5), | |
75 | MAX8925_REG_RESOURCE(LDOCTL6, LDOCTL6), | |
76 | MAX8925_REG_RESOURCE(LDOCTL7, LDOCTL7), | |
77 | MAX8925_REG_RESOURCE(LDOCTL8, LDOCTL8), | |
78 | MAX8925_REG_RESOURCE(LDOCTL9, LDOCTL9), | |
79 | MAX8925_REG_RESOURCE(LDOCTL10, LDOCTL10), | |
80 | MAX8925_REG_RESOURCE(LDOCTL11, LDOCTL11), | |
81 | MAX8925_REG_RESOURCE(LDOCTL12, LDOCTL12), | |
82 | MAX8925_REG_RESOURCE(LDOCTL13, LDOCTL13), | |
83 | MAX8925_REG_RESOURCE(LDOCTL14, LDOCTL14), | |
84 | MAX8925_REG_RESOURCE(LDOCTL15, LDOCTL15), | |
85 | MAX8925_REG_RESOURCE(LDOCTL16, LDOCTL16), | |
86 | MAX8925_REG_RESOURCE(LDOCTL17, LDOCTL17), | |
87 | MAX8925_REG_RESOURCE(LDOCTL18, LDOCTL18), | |
88 | MAX8925_REG_RESOURCE(LDOCTL19, LDOCTL19), | |
89 | MAX8925_REG_RESOURCE(LDOCTL20, LDOCTL20), | |
90 | }; | |
91 | ||
92 | #define MAX8925_REG_DEVS(_id) \ | |
93 | { \ | |
94 | .name = "max8925-regulator", \ | |
95 | .num_resources = 1, \ | |
96 | .resources = ®ulator_resources[MAX8925_ID_##_id], \ | |
97 | .id = MAX8925_ID_##_id, \ | |
98 | } | |
99 | ||
100 | static struct mfd_cell regulator_devs[] = { | |
101 | MAX8925_REG_DEVS(SD1), | |
102 | MAX8925_REG_DEVS(SD2), | |
103 | MAX8925_REG_DEVS(SD3), | |
104 | MAX8925_REG_DEVS(LDO1), | |
105 | MAX8925_REG_DEVS(LDO2), | |
106 | MAX8925_REG_DEVS(LDO3), | |
107 | MAX8925_REG_DEVS(LDO4), | |
108 | MAX8925_REG_DEVS(LDO5), | |
109 | MAX8925_REG_DEVS(LDO6), | |
110 | MAX8925_REG_DEVS(LDO7), | |
111 | MAX8925_REG_DEVS(LDO8), | |
112 | MAX8925_REG_DEVS(LDO9), | |
113 | MAX8925_REG_DEVS(LDO10), | |
114 | MAX8925_REG_DEVS(LDO11), | |
115 | MAX8925_REG_DEVS(LDO12), | |
116 | MAX8925_REG_DEVS(LDO13), | |
117 | MAX8925_REG_DEVS(LDO14), | |
118 | MAX8925_REG_DEVS(LDO15), | |
119 | MAX8925_REG_DEVS(LDO16), | |
120 | MAX8925_REG_DEVS(LDO17), | |
121 | MAX8925_REG_DEVS(LDO18), | |
122 | MAX8925_REG_DEVS(LDO19), | |
123 | MAX8925_REG_DEVS(LDO20), | |
124 | }; | |
125 | ||
d50f8f33 HZ |
126 | static int __get_irq_offset(struct max8925_chip *chip, int irq, int mode, |
127 | int *offset, int *bit) | |
128 | { | |
129 | if (!offset || !bit) | |
130 | return -EINVAL; | |
131 | ||
132 | switch (chip->chip_id) { | |
133 | case MAX8925_GPM: | |
134 | *bit = irq % BITS_PER_BYTE; | |
135 | if (irq < (BITS_PER_BYTE << 1)) { /* irq = [0,15] */ | |
136 | *offset = (mode) ? MAX8925_CHG_IRQ1_MASK | |
137 | : MAX8925_CHG_IRQ1; | |
138 | if (irq >= BITS_PER_BYTE) | |
139 | (*offset)++; | |
140 | } else { /* irq = [16,31] */ | |
141 | *offset = (mode) ? MAX8925_ON_OFF_IRQ1_MASK | |
142 | : MAX8925_ON_OFF_IRQ1; | |
143 | if (irq >= (BITS_PER_BYTE * 3)) | |
144 | (*offset)++; | |
145 | } | |
146 | break; | |
147 | case MAX8925_ADC: | |
148 | *bit = irq % BITS_PER_BYTE; | |
149 | *offset = (mode) ? MAX8925_TSC_IRQ_MASK : MAX8925_TSC_IRQ; | |
150 | break; | |
151 | default: | |
152 | goto out; | |
153 | } | |
154 | return 0; | |
155 | out: | |
156 | dev_err(chip->dev, "Wrong irq #%d is assigned\n", irq); | |
157 | return -EINVAL; | |
158 | } | |
159 | ||
160 | static int __check_irq(int irq) | |
161 | { | |
162 | if ((irq < 0) || (irq >= MAX8925_NUM_IRQ)) | |
163 | return -EINVAL; | |
164 | return 0; | |
165 | } | |
166 | ||
167 | int max8925_mask_irq(struct max8925_chip *chip, int irq) | |
168 | { | |
169 | int offset, bit, ret; | |
170 | ||
171 | ret = __get_irq_offset(chip, irq, IRQ_MODE_MASK, &offset, &bit); | |
172 | if (ret < 0) | |
173 | return ret; | |
174 | ret = max8925_set_bits(chip->i2c, offset, 1 << bit, 1 << bit); | |
175 | return ret; | |
176 | } | |
177 | ||
178 | int max8925_unmask_irq(struct max8925_chip *chip, int irq) | |
179 | { | |
180 | int offset, bit, ret; | |
181 | ||
182 | ret = __get_irq_offset(chip, irq, IRQ_MODE_MASK, &offset, &bit); | |
183 | if (ret < 0) | |
184 | return ret; | |
185 | ret = max8925_set_bits(chip->i2c, offset, 1 << bit, 0); | |
186 | return ret; | |
187 | } | |
188 | ||
189 | #define INT_STATUS_NUM (MAX8925_NUM_IRQ / BITS_PER_BYTE) | |
190 | ||
191 | static irqreturn_t max8925_irq_thread(int irq, void *data) | |
192 | { | |
193 | struct max8925_chip *chip = data; | |
194 | unsigned long irq_status[INT_STATUS_NUM]; | |
195 | unsigned char status_buf[INT_STATUS_NUM << 1]; | |
196 | int i, ret; | |
197 | ||
198 | memset(irq_status, 0, sizeof(unsigned long) * INT_STATUS_NUM); | |
199 | ||
200 | /* all these interrupt status registers are read-only */ | |
201 | switch (chip->chip_id) { | |
202 | case MAX8925_GPM: | |
203 | ret = max8925_bulk_read(chip->i2c, MAX8925_CHG_IRQ1, | |
204 | 4, status_buf); | |
205 | if (ret < 0) | |
206 | goto out; | |
207 | ret = max8925_bulk_read(chip->i2c, MAX8925_ON_OFF_IRQ1, | |
208 | 2, &status_buf[4]); | |
209 | if (ret < 0) | |
210 | goto out; | |
211 | ret = max8925_bulk_read(chip->i2c, MAX8925_ON_OFF_IRQ2, | |
212 | 2, &status_buf[6]); | |
213 | if (ret < 0) | |
214 | goto out; | |
215 | /* clear masked interrupt status */ | |
216 | status_buf[0] &= (~status_buf[2] & CHG_IRQ1_MASK); | |
217 | irq_status[0] |= status_buf[0]; | |
218 | status_buf[1] &= (~status_buf[3] & CHG_IRQ2_MASK); | |
219 | irq_status[0] |= (status_buf[1] << BITS_PER_BYTE); | |
220 | status_buf[4] &= (~status_buf[5] & ON_OFF_IRQ1_MASK); | |
221 | irq_status[0] |= (status_buf[4] << (BITS_PER_BYTE * 2)); | |
222 | status_buf[6] &= (~status_buf[7] & ON_OFF_IRQ2_MASK); | |
223 | irq_status[0] |= (status_buf[6] << (BITS_PER_BYTE * 3)); | |
224 | break; | |
225 | case MAX8925_ADC: | |
226 | ret = max8925_bulk_read(chip->i2c, MAX8925_TSC_IRQ, | |
227 | 2, status_buf); | |
228 | if (ret < 0) | |
229 | goto out; | |
230 | /* clear masked interrupt status */ | |
231 | status_buf[0] &= (~status_buf[1] & TSC_IRQ_MASK); | |
232 | irq_status[0] |= status_buf[0]; | |
233 | break; | |
234 | default: | |
235 | goto out; | |
236 | } | |
237 | ||
238 | for_each_bit(i, &irq_status[0], MAX8925_NUM_IRQ) { | |
239 | clear_bit(i, irq_status); | |
240 | dev_dbg(chip->dev, "Servicing IRQ #%d in %s\n", i, chip->name); | |
241 | ||
242 | mutex_lock(&chip->irq_lock); | |
243 | if (chip->irq[i].handler) | |
244 | chip->irq[i].handler(i, chip->irq[i].data); | |
245 | else { | |
246 | max8925_mask_irq(chip, i); | |
247 | dev_err(chip->dev, "Noboday cares IRQ #%d in %s. " | |
248 | "Now mask it.\n", i, chip->name); | |
249 | } | |
250 | mutex_unlock(&chip->irq_lock); | |
251 | } | |
252 | out: | |
253 | return IRQ_HANDLED; | |
254 | } | |
255 | ||
256 | int max8925_request_irq(struct max8925_chip *chip, int irq, | |
257 | irq_handler_t handler, void *data) | |
258 | { | |
259 | if ((__check_irq(irq) < 0) || !handler) | |
260 | return -EINVAL; | |
261 | ||
262 | mutex_lock(&chip->irq_lock); | |
263 | chip->irq[irq].handler = handler; | |
264 | chip->irq[irq].data = data; | |
265 | mutex_unlock(&chip->irq_lock); | |
266 | return 0; | |
267 | } | |
268 | EXPORT_SYMBOL(max8925_request_irq); | |
269 | ||
270 | int max8925_free_irq(struct max8925_chip *chip, int irq) | |
271 | { | |
272 | if (__check_irq(irq) < 0) | |
273 | return -EINVAL; | |
274 | ||
275 | mutex_lock(&chip->irq_lock); | |
276 | chip->irq[irq].handler = NULL; | |
277 | chip->irq[irq].data = NULL; | |
278 | mutex_unlock(&chip->irq_lock); | |
279 | return 0; | |
280 | } | |
281 | EXPORT_SYMBOL(max8925_free_irq); | |
282 | ||
283 | static int __devinit device_gpm_init(struct max8925_chip *chip, | |
284 | struct i2c_client *i2c, | |
285 | struct max8925_platform_data *pdata) | |
286 | { | |
287 | int ret; | |
288 | ||
289 | /* mask all IRQs */ | |
290 | ret = max8925_set_bits(i2c, MAX8925_CHG_IRQ1_MASK, 0x7, 0x7); | |
291 | if (ret < 0) | |
292 | goto out; | |
293 | ret = max8925_set_bits(i2c, MAX8925_CHG_IRQ2_MASK, 0xff, 0xff); | |
294 | if (ret < 0) | |
295 | goto out; | |
296 | ret = max8925_set_bits(i2c, MAX8925_ON_OFF_IRQ1_MASK, 0xff, 0xff); | |
297 | if (ret < 0) | |
298 | goto out; | |
299 | ret = max8925_set_bits(i2c, MAX8925_ON_OFF_IRQ2_MASK, 0x3, 0x3); | |
300 | if (ret < 0) | |
301 | goto out; | |
302 | ||
303 | chip->name = "GPM"; | |
304 | memset(chip->irq, 0, sizeof(struct max8925_irq) * MAX8925_NUM_IRQ); | |
305 | ret = request_threaded_irq(i2c->irq, NULL, max8925_irq_thread, | |
306 | IRQF_ONESHOT | IRQF_TRIGGER_LOW, | |
307 | "max8925-gpm", chip); | |
308 | if (ret < 0) { | |
309 | dev_err(chip->dev, "Failed to request IRQ #%d.\n", i2c->irq); | |
310 | goto out; | |
311 | } | |
312 | chip->chip_irq = i2c->irq; | |
313 | ||
314 | /* enable hard-reset for ONKEY power-off */ | |
315 | max8925_set_bits(i2c, MAX8925_SYSENSEL, 0x80, 0x80); | |
1ad99893 HZ |
316 | |
317 | ret = mfd_add_devices(chip->dev, 0, ®ulator_devs[0], | |
318 | ARRAY_SIZE(regulator_devs), | |
319 | ®ulator_resources[0], 0); | |
320 | if (ret < 0) { | |
321 | dev_err(chip->dev, "Failed to add regulator subdev\n"); | |
322 | goto out_irq; | |
323 | } | |
324 | ||
325 | if (pdata && pdata->backlight) { | |
326 | ret = mfd_add_devices(chip->dev, 0, &backlight_devs[0], | |
327 | ARRAY_SIZE(backlight_devs), | |
328 | &backlight_resources[0], 0); | |
329 | if (ret < 0) { | |
330 | dev_err(chip->dev, "Failed to add backlight subdev\n"); | |
331 | goto out_dev; | |
332 | } | |
333 | } | |
334 | return 0; | |
335 | out_dev: | |
336 | mfd_remove_devices(chip->dev); | |
337 | out_irq: | |
338 | if (chip->chip_irq) | |
339 | free_irq(chip->chip_irq, chip); | |
d50f8f33 HZ |
340 | out: |
341 | return ret; | |
342 | } | |
343 | ||
344 | static int __devinit device_adc_init(struct max8925_chip *chip, | |
345 | struct i2c_client *i2c, | |
346 | struct max8925_platform_data *pdata) | |
347 | { | |
348 | int ret; | |
349 | ||
350 | /* mask all IRQs */ | |
351 | ret = max8925_set_bits(i2c, MAX8925_TSC_IRQ_MASK, 3, 3); | |
352 | ||
353 | chip->name = "ADC"; | |
354 | memset(chip->irq, 0, sizeof(struct max8925_irq) * MAX8925_NUM_IRQ); | |
355 | ret = request_threaded_irq(i2c->irq, NULL, max8925_irq_thread, | |
356 | IRQF_ONESHOT | IRQF_TRIGGER_LOW, | |
357 | "max8925-adc", chip); | |
358 | if (ret < 0) { | |
359 | dev_err(chip->dev, "Failed to request IRQ #%d.\n", i2c->irq); | |
360 | goto out; | |
361 | } | |
362 | chip->chip_irq = i2c->irq; | |
1ad99893 HZ |
363 | |
364 | if (pdata && pdata->touch) { | |
365 | ret = mfd_add_devices(chip->dev, 0, &touch_devs[0], | |
366 | ARRAY_SIZE(touch_devs), | |
367 | &touch_resources[0], 0); | |
368 | if (ret < 0) { | |
369 | dev_err(chip->dev, "Failed to add touch subdev\n"); | |
370 | goto out_irq; | |
371 | } | |
372 | } | |
373 | return 0; | |
374 | out_irq: | |
375 | if (chip->chip_irq) | |
376 | free_irq(chip->chip_irq, chip); | |
d50f8f33 HZ |
377 | out: |
378 | return ret; | |
379 | } | |
380 | ||
381 | int __devinit max8925_device_init(struct max8925_chip *chip, | |
382 | struct max8925_platform_data *pdata) | |
383 | { | |
384 | switch (chip->chip_id) { | |
385 | case MAX8925_GPM: | |
386 | device_gpm_init(chip, chip->i2c, pdata); | |
387 | break; | |
388 | case MAX8925_ADC: | |
389 | device_adc_init(chip, chip->i2c, pdata); | |
390 | break; | |
391 | } | |
392 | return 0; | |
393 | } | |
394 | ||
395 | void max8925_device_exit(struct max8925_chip *chip) | |
396 | { | |
397 | if (chip->chip_irq >= 0) | |
398 | free_irq(chip->chip_irq, chip); | |
1ad99893 | 399 | mfd_remove_devices(chip->dev); |
d50f8f33 HZ |
400 | } |
401 | ||
402 | MODULE_DESCRIPTION("PMIC Driver for Maxim MAX8925"); | |
403 | MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com"); | |
404 | MODULE_LICENSE("GPL"); |