blob: f36c494b80f18be8e5584ed824943471158759e6 [file] [log] [blame]
Haojian Zhuangd50f8f32010-01-08 12:29:23 +01001/*
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
Haojian Zhuang1ad99892010-01-08 12:43:29 -050023static 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
32static 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
41static 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
50static 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
66static 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 = &regulator_resources[MAX8925_ID_##_id], \
97 .id = MAX8925_ID_##_id, \
98}
99
100static 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
Haojian Zhuangd50f8f32010-01-08 12:29:23 +0100126static 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;
155out:
156 dev_err(chip->dev, "Wrong irq #%d is assigned\n", irq);
157 return -EINVAL;
158}
159
160static int __check_irq(int irq)
161{
162 if ((irq < 0) || (irq >= MAX8925_NUM_IRQ))
163 return -EINVAL;
164 return 0;
165}
166
167int 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
178int 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
191static 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 }
252out:
253 return IRQ_HANDLED;
254}
255
256int 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}
268EXPORT_SYMBOL(max8925_request_irq);
269
270int 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}
281EXPORT_SYMBOL(max8925_free_irq);
282
283static 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);
Haojian Zhuang1ad99892010-01-08 12:43:29 -0500316
317 ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
318 ARRAY_SIZE(regulator_devs),
319 &regulator_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;
335out_dev:
336 mfd_remove_devices(chip->dev);
337out_irq:
338 if (chip->chip_irq)
339 free_irq(chip->chip_irq, chip);
Haojian Zhuangd50f8f32010-01-08 12:29:23 +0100340out:
341 return ret;
342}
343
344static 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;
Haojian Zhuang1ad99892010-01-08 12:43:29 -0500363
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;
374out_irq:
375 if (chip->chip_irq)
376 free_irq(chip->chip_irq, chip);
Haojian Zhuangd50f8f32010-01-08 12:29:23 +0100377out:
378 return ret;
379}
380
381int __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
395void max8925_device_exit(struct max8925_chip *chip)
396{
397 if (chip->chip_irq >= 0)
398 free_irq(chip->chip_irq, chip);
Haojian Zhuang1ad99892010-01-08 12:43:29 -0500399 mfd_remove_devices(chip->dev);
Haojian Zhuangd50f8f32010-01-08 12:29:23 +0100400}
401
402MODULE_DESCRIPTION("PMIC Driver for Maxim MAX8925");
403MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com");
404MODULE_LICENSE("GPL");