aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd/88pm860x-core.c
diff options
context:
space:
mode:
authorHaojian Zhuang <haojian.zhuang@marvell.com>2010-02-08 05:02:00 -0500
committerSamuel Ortiz <sameo@linux.intel.com>2010-03-07 16:17:20 -0500
commit2afa62ea76027b00e472ddb672191e6e15425b43 (patch)
tree3c70f220af28c859fea638e5eae4aed726f5dcdd /drivers/mfd/88pm860x-core.c
parent7731074ab21745cde00578148ce760df107eaf27 (diff)
mfd: Use genirq in 88pm860x
Use genirq to simplify IRQ handling in 88pm860x. Remove the interface of mask/free IRQs on 88pm860x. All these work is taken by genirq. Update the touchscreen driver of 88pm860x since IRQ handling is changed. Signed-off-by: Haojian Zhuang <haojian.zhuang@marvell.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd/88pm860x-core.c')
-rw-r--r--drivers/mfd/88pm860x-core.c408
1 files changed, 294 insertions, 114 deletions
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c
index 16f0dca707a7..6a14d2b1ccf0 100644
--- a/drivers/mfd/88pm860x-core.c
+++ b/drivers/mfd/88pm860x-core.c
@@ -12,11 +12,14 @@
12#include <linux/kernel.h> 12#include <linux/kernel.h>
13#include <linux/module.h> 13#include <linux/module.h>
14#include <linux/i2c.h> 14#include <linux/i2c.h>
15#include <linux/irq.h>
15#include <linux/interrupt.h> 16#include <linux/interrupt.h>
16#include <linux/platform_device.h> 17#include <linux/platform_device.h>
17#include <linux/mfd/core.h> 18#include <linux/mfd/core.h>
18#include <linux/mfd/88pm860x.h> 19#include <linux/mfd/88pm860x.h>
19 20
21#define INT_STATUS_NUM 3
22
20char pm860x_backlight_name[][MFD_NAME_SIZE] = { 23char pm860x_backlight_name[][MFD_NAME_SIZE] = {
21 "backlight-0", 24 "backlight-0",
22 "backlight-1", 25 "backlight-1",
@@ -119,6 +122,42 @@ static struct mfd_cell touch_devs[] = {
119 .flags = IORESOURCE_IO, \ 122 .flags = IORESOURCE_IO, \
120} 123}
121 124
125static struct resource power_supply_resources[] = {
126 {
127 .name = "88pm860x-power",
128 .start = PM8607_IRQ_CHG,
129 .end = PM8607_IRQ_CHG,
130 .flags = IORESOURCE_IRQ,
131 },
132};
133
134static struct mfd_cell power_devs[] = {
135 {
136 .name = "88pm860x-power",
137 .num_resources = 1,
138 .resources = &power_supply_resources[0],
139 .id = -1,
140 },
141};
142
143static struct resource onkey_resources[] = {
144 {
145 .name = "88pm860x-onkey",
146 .start = PM8607_IRQ_ONKEY,
147 .end = PM8607_IRQ_ONKEY,
148 .flags = IORESOURCE_IRQ,
149 },
150};
151
152static struct mfd_cell onkey_devs[] = {
153 {
154 .name = "88pm860x-onkey",
155 .num_resources = 1,
156 .resources = &onkey_resources[0],
157 .id = -1,
158 },
159};
160
122static struct resource regulator_resources[] = { 161static struct resource regulator_resources[] = {
123 PM8607_REG_RESOURCE(BUCK1, BUCK1), 162 PM8607_REG_RESOURCE(BUCK1, BUCK1),
124 PM8607_REG_RESOURCE(BUCK2, BUCK2), 163 PM8607_REG_RESOURCE(BUCK2, BUCK2),
@@ -163,129 +202,224 @@ static struct mfd_cell regulator_devs[] = {
163 PM8607_REG_DEVS(ldo14, LDO14), 202 PM8607_REG_DEVS(ldo14, LDO14),
164}; 203};
165 204
166#define CHECK_IRQ(irq) \ 205struct pm860x_irq_data {
167do { \ 206 int reg;
168 if ((irq < 0) || (irq >= PM860X_NUM_IRQ)) \ 207 int mask_reg;
169 return -EINVAL; \ 208 int enable; /* enable or not */
170} while (0) 209 int offs; /* bit offset in mask register */
171 210};
172/* IRQs only occur on 88PM8607 */
173int pm860x_mask_irq(struct pm860x_chip *chip, int irq)
174{
175 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
176 : chip->companion;
177 int offset, data, ret;
178
179 CHECK_IRQ(irq);
180
181 offset = (irq >> 3) + PM8607_INT_MASK_1;
182 data = 1 << (irq % 8);
183 ret = pm860x_set_bits(i2c, offset, data, 0);
184 211
185 return ret; 212static struct pm860x_irq_data pm860x_irqs[] = {
186} 213 [PM8607_IRQ_ONKEY] = {
187EXPORT_SYMBOL(pm860x_mask_irq); 214 .reg = PM8607_INT_STATUS1,
215 .mask_reg = PM8607_INT_MASK_1,
216 .offs = 1 << 0,
217 },
218 [PM8607_IRQ_EXTON] = {
219 .reg = PM8607_INT_STATUS1,
220 .mask_reg = PM8607_INT_MASK_1,
221 .offs = 1 << 1,
222 },
223 [PM8607_IRQ_CHG] = {
224 .reg = PM8607_INT_STATUS1,
225 .mask_reg = PM8607_INT_MASK_1,
226 .offs = 1 << 2,
227 },
228 [PM8607_IRQ_BAT] = {
229 .reg = PM8607_INT_STATUS1,
230 .mask_reg = PM8607_INT_MASK_1,
231 .offs = 1 << 3,
232 },
233 [PM8607_IRQ_RTC] = {
234 .reg = PM8607_INT_STATUS1,
235 .mask_reg = PM8607_INT_MASK_1,
236 .offs = 1 << 4,
237 },
238 [PM8607_IRQ_CC] = {
239 .reg = PM8607_INT_STATUS1,
240 .mask_reg = PM8607_INT_MASK_1,
241 .offs = 1 << 5,
242 },
243 [PM8607_IRQ_VBAT] = {
244 .reg = PM8607_INT_STATUS2,
245 .mask_reg = PM8607_INT_MASK_2,
246 .offs = 1 << 0,
247 },
248 [PM8607_IRQ_VCHG] = {
249 .reg = PM8607_INT_STATUS2,
250 .mask_reg = PM8607_INT_MASK_2,
251 .offs = 1 << 1,
252 },
253 [PM8607_IRQ_VSYS] = {
254 .reg = PM8607_INT_STATUS2,
255 .mask_reg = PM8607_INT_MASK_2,
256 .offs = 1 << 2,
257 },
258 [PM8607_IRQ_TINT] = {
259 .reg = PM8607_INT_STATUS2,
260 .mask_reg = PM8607_INT_MASK_2,
261 .offs = 1 << 3,
262 },
263 [PM8607_IRQ_GPADC0] = {
264 .reg = PM8607_INT_STATUS2,
265 .mask_reg = PM8607_INT_MASK_2,
266 .offs = 1 << 4,
267 },
268 [PM8607_IRQ_GPADC1] = {
269 .reg = PM8607_INT_STATUS2,
270 .mask_reg = PM8607_INT_MASK_2,
271 .offs = 1 << 5,
272 },
273 [PM8607_IRQ_GPADC2] = {
274 .reg = PM8607_INT_STATUS2,
275 .mask_reg = PM8607_INT_MASK_2,
276 .offs = 1 << 6,
277 },
278 [PM8607_IRQ_GPADC3] = {
279 .reg = PM8607_INT_STATUS2,
280 .mask_reg = PM8607_INT_MASK_2,
281 .offs = 1 << 7,
282 },
283 [PM8607_IRQ_AUDIO_SHORT] = {
284 .reg = PM8607_INT_STATUS3,
285 .mask_reg = PM8607_INT_MASK_3,
286 .offs = 1 << 0,
287 },
288 [PM8607_IRQ_PEN] = {
289 .reg = PM8607_INT_STATUS3,
290 .mask_reg = PM8607_INT_MASK_3,
291 .offs = 1 << 1,
292 },
293 [PM8607_IRQ_HEADSET] = {
294 .reg = PM8607_INT_STATUS3,
295 .mask_reg = PM8607_INT_MASK_3,
296 .offs = 1 << 2,
297 },
298 [PM8607_IRQ_HOOK] = {
299 .reg = PM8607_INT_STATUS3,
300 .mask_reg = PM8607_INT_MASK_3,
301 .offs = 1 << 3,
302 },
303 [PM8607_IRQ_MICIN] = {
304 .reg = PM8607_INT_STATUS3,
305 .mask_reg = PM8607_INT_MASK_3,
306 .offs = 1 << 4,
307 },
308 [PM8607_IRQ_CHG_FAIL] = {
309 .reg = PM8607_INT_STATUS3,
310 .mask_reg = PM8607_INT_MASK_3,
311 .offs = 1 << 5,
312 },
313 [PM8607_IRQ_CHG_DONE] = {
314 .reg = PM8607_INT_STATUS3,
315 .mask_reg = PM8607_INT_MASK_3,
316 .offs = 1 << 6,
317 },
318 [PM8607_IRQ_CHG_FAULT] = {
319 .reg = PM8607_INT_STATUS3,
320 .mask_reg = PM8607_INT_MASK_3,
321 .offs = 1 << 7,
322 },
323};
188 324
189int pm860x_unmask_irq(struct pm860x_chip *chip, int irq) 325static inline struct pm860x_irq_data *irq_to_pm860x(struct pm860x_chip *chip,
326 int irq)
190{ 327{
191 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \ 328 return &pm860x_irqs[irq - chip->irq_base];
192 : chip->companion;
193 int offset, data, ret;
194
195 CHECK_IRQ(irq);
196
197 offset = (irq >> 3) + PM8607_INT_MASK_1;
198 data = 1 << (irq % 8);
199 ret = pm860x_set_bits(i2c, offset, data, data);
200
201 return ret;
202} 329}
203EXPORT_SYMBOL(pm860x_unmask_irq);
204 330
205#define INT_STATUS_NUM (3) 331static irqreturn_t pm860x_irq(int irq, void *data)
206
207static irqreturn_t pm8607_irq_thread(int irq, void *data)
208{ 332{
209 DECLARE_BITMAP(irq_status, PM860X_NUM_IRQ);
210 struct pm860x_chip *chip = data; 333 struct pm860x_chip *chip = data;
211 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \ 334 struct pm860x_irq_data *irq_data;
212 : chip->companion; 335 struct i2c_client *i2c;
213 unsigned char status_buf[INT_STATUS_NUM << 1]; 336 int read_reg = -1, value = 0;
214 unsigned long value; 337 int i;
215 int i, ret; 338
216 339 i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
217 irq_status[0] = 0; 340 for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
218 341 irq_data = &pm860x_irqs[i];
219 /* read out status register */ 342 if (read_reg != irq_data->reg) {
220 ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1, 343 read_reg = irq_data->reg;
221 INT_STATUS_NUM << 1, status_buf); 344 value = pm860x_reg_read(i2c, irq_data->reg);
222 if (ret < 0)
223 goto out;
224 if (chip->irq_mode) {
225 /* 0, clear by read. 1, clear by write */
226 ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
227 INT_STATUS_NUM, status_buf);
228 if (ret < 0)
229 goto out;
230 }
231
232 /* clear masked interrupt status */
233 for (i = 0, value = 0; i < INT_STATUS_NUM; i++) {
234 status_buf[i] &= status_buf[i + INT_STATUS_NUM];
235 irq_status[0] |= status_buf[i] << (i * 8);
236 }
237
238 while (!bitmap_empty(irq_status, PM860X_NUM_IRQ)) {
239 irq = find_first_bit(irq_status, PM860X_NUM_IRQ);
240 clear_bit(irq, irq_status);
241 dev_dbg(chip->dev, "Servicing IRQ #%d\n", irq);
242
243 mutex_lock(&chip->irq_lock);
244 if (chip->irq[irq].handler)
245 chip->irq[irq].handler(irq, chip->irq[irq].data);
246 else {
247 pm860x_mask_irq(chip, irq);
248 dev_err(chip->dev, "Nobody cares IRQ %d. "
249 "Now mask it.\n", irq);
250 for (i = 0; i < (INT_STATUS_NUM << 1); i++) {
251 dev_err(chip->dev, "status[%d]:%x\n", i,
252 status_buf[i]);
253 }
254 } 345 }
255 mutex_unlock(&chip->irq_lock); 346 if (value & irq_data->enable)
347 handle_nested_irq(chip->irq_base + i);
256 } 348 }
257out:
258 return IRQ_HANDLED; 349 return IRQ_HANDLED;
259} 350}
260 351
261int pm860x_request_irq(struct pm860x_chip *chip, int irq, 352static void pm860x_irq_lock(unsigned int irq)
262 irq_handler_t handler, void *data)
263{ 353{
264 CHECK_IRQ(irq); 354 struct pm860x_chip *chip = get_irq_chip_data(irq);
265 if (!handler)
266 return -EINVAL;
267 355
268 mutex_lock(&chip->irq_lock); 356 mutex_lock(&chip->irq_lock);
269 chip->irq[irq].handler = handler;
270 chip->irq[irq].data = data;
271 mutex_unlock(&chip->irq_lock);
272
273 return 0;
274} 357}
275EXPORT_SYMBOL(pm860x_request_irq);
276 358
277int pm860x_free_irq(struct pm860x_chip *chip, int irq) 359static void pm860x_irq_sync_unlock(unsigned int irq)
278{ 360{
279 CHECK_IRQ(irq); 361 struct pm860x_chip *chip = get_irq_chip_data(irq);
362 struct pm860x_irq_data *irq_data;
363 struct i2c_client *i2c;
364 static unsigned char cached[3] = {0x0, 0x0, 0x0};
365 unsigned char mask[3];
366 int i;
367
368 i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
369 /* Load cached value. In initial, all IRQs are masked */
370 for (i = 0; i < 3; i++)
371 mask[i] = cached[i];
372 for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
373 irq_data = &pm860x_irqs[i];
374 switch (irq_data->mask_reg) {
375 case PM8607_INT_MASK_1:
376 mask[0] &= ~irq_data->offs;
377 mask[0] |= irq_data->enable;
378 break;
379 case PM8607_INT_MASK_2:
380 mask[1] &= ~irq_data->offs;
381 mask[1] |= irq_data->enable;
382 break;
383 case PM8607_INT_MASK_3:
384 mask[2] &= ~irq_data->offs;
385 mask[2] |= irq_data->enable;
386 break;
387 default:
388 dev_err(chip->dev, "wrong IRQ\n");
389 break;
390 }
391 }
392 /* update mask into registers */
393 for (i = 0; i < 3; i++) {
394 if (mask[i] != cached[i]) {
395 cached[i] = mask[i];
396 pm860x_reg_write(i2c, PM8607_INT_MASK_1 + i, mask[i]);
397 }
398 }
280 399
281 mutex_lock(&chip->irq_lock);
282 chip->irq[irq].handler = NULL;
283 chip->irq[irq].data = NULL;
284 mutex_unlock(&chip->irq_lock); 400 mutex_unlock(&chip->irq_lock);
401}
285 402
286 return 0; 403static void pm860x_irq_enable(unsigned int irq)
404{
405 struct pm860x_chip *chip = get_irq_chip_data(irq);
406 pm860x_irqs[irq - chip->irq_base].enable
407 = pm860x_irqs[irq - chip->irq_base].offs;
287} 408}
288EXPORT_SYMBOL(pm860x_free_irq); 409
410static void pm860x_irq_disable(unsigned int irq)
411{
412 struct pm860x_chip *chip = get_irq_chip_data(irq);
413 pm860x_irqs[irq - chip->irq_base].enable = 0;
414}
415
416static struct irq_chip pm860x_irq_chip = {
417 .name = "88pm860x",
418 .bus_lock = pm860x_irq_lock,
419 .bus_sync_unlock = pm860x_irq_sync_unlock,
420 .enable = pm860x_irq_enable,
421 .disable = pm860x_irq_disable,
422};
289 423
290static int __devinit device_gpadc_init(struct pm860x_chip *chip, 424static int __devinit device_gpadc_init(struct pm860x_chip *chip,
291 struct pm860x_platform_data *pdata) 425 struct pm860x_platform_data *pdata)
@@ -348,9 +482,15 @@ static int __devinit device_irq_init(struct pm860x_chip *chip,
348 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \ 482 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
349 : chip->companion; 483 : chip->companion;
350 unsigned char status_buf[INT_STATUS_NUM]; 484 unsigned char status_buf[INT_STATUS_NUM];
351 int data, mask, ret = -EINVAL; 485 unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
486 struct irq_desc *desc;
487 int i, data, mask, ret = -EINVAL;
488 int __irq;
352 489
353 mutex_init(&chip->irq_lock); 490 if (!pdata || !pdata->irq_base) {
491 dev_warn(chip->dev, "No interrupt support on IRQ base\n");
492 return -EINVAL;
493 }
354 494
355 mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR 495 mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR
356 | PM8607_B0_MISC1_INT_MASK; 496 | PM8607_B0_MISC1_INT_MASK;
@@ -389,25 +529,45 @@ static int __devinit device_irq_init(struct pm860x_chip *chip,
389 if (ret < 0) 529 if (ret < 0)
390 goto out; 530 goto out;
391 531
392 memset(chip->irq, 0, sizeof(struct pm860x_irq) * PM860X_NUM_IRQ); 532 mutex_init(&chip->irq_lock);
393 533 chip->irq_base = pdata->irq_base;
394 ret = request_threaded_irq(i2c->irq, NULL, pm8607_irq_thread, 534 chip->core_irq = i2c->irq;
395 IRQF_ONESHOT | IRQF_TRIGGER_LOW, 535 if (!chip->core_irq)
396 "88PM8607", chip);
397 if (ret < 0) {
398 dev_err(chip->dev, "Failed to request IRQ #%d.\n", i2c->irq);
399 goto out; 536 goto out;
537
538 desc = irq_to_desc(chip->core_irq);
539
540 /* register IRQ by genirq */
541 for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
542 __irq = i + chip->irq_base;
543 set_irq_chip_data(__irq, chip);
544 set_irq_chip_and_handler(__irq, &pm860x_irq_chip,
545 handle_edge_irq);
546 set_irq_nested_thread(__irq, 1);
547#ifdef CONFIG_ARM
548 set_irq_flags(__irq, IRQF_VALID);
549#else
550 set_irq_noprobe(__irq);
551#endif
400 } 552 }
401 chip->chip_irq = i2c->irq; 553
554 ret = request_threaded_irq(chip->core_irq, NULL, pm860x_irq, flags,
555 "88pm860x", chip);
556 if (ret) {
557 dev_err(chip->dev, "Failed to request IRQ: %d\n", ret);
558 chip->core_irq = 0;
559 }
560
402 return 0; 561 return 0;
403out: 562out:
563 chip->core_irq = 0;
404 return ret; 564 return ret;
405} 565}
406 566
407static void __devexit device_irq_exit(struct pm860x_chip *chip) 567static void __devexit device_irq_exit(struct pm860x_chip *chip)
408{ 568{
409 if (chip->chip_irq >= 0) 569 if (chip->core_irq)
410 free_irq(chip->chip_irq, chip); 570 free_irq(chip->core_irq, chip);
411} 571}
412 572
413static void __devinit device_8606_init(struct pm860x_chip *chip, 573static void __devinit device_8606_init(struct pm860x_chip *chip,
@@ -513,6 +673,26 @@ static void __devinit device_8607_init(struct pm860x_chip *chip,
513 goto out_dev; 673 goto out_dev;
514 } 674 }
515 } 675 }
676
677 if (pdata && pdata->power) {
678 ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
679 ARRAY_SIZE(power_devs),
680 &power_supply_resources[0], 0);
681 if (ret < 0) {
682 dev_err(chip->dev, "Failed to add power supply "
683 "subdev\n");
684 goto out_dev;
685 }
686 }
687
688 ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
689 ARRAY_SIZE(onkey_devs),
690 &onkey_resources[0], 0);
691 if (ret < 0) {
692 dev_err(chip->dev, "Failed to add onkey subdev\n");
693 goto out_dev;
694 }
695
516 return; 696 return;
517out_dev: 697out_dev:
518 mfd_remove_devices(chip->dev); 698 mfd_remove_devices(chip->dev);
@@ -524,7 +704,7 @@ out:
524int pm860x_device_init(struct pm860x_chip *chip, 704int pm860x_device_init(struct pm860x_chip *chip,
525 struct pm860x_platform_data *pdata) 705 struct pm860x_platform_data *pdata)
526{ 706{
527 chip->chip_irq = -EINVAL; 707 chip->core_irq = 0;
528 708
529 switch (chip->id) { 709 switch (chip->id) {
530 case CHIP_PM8606: 710 case CHIP_PM8606: