diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-01-21 09:58:28 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-02-18 18:15:43 -0500 |
commit | a3364409c4af8bae42d04def48dc11409787e503 (patch) | |
tree | 317d68920998bc5823d2879a49d49f27a8aed279 /drivers | |
parent | cf4abfcc0df2985ff6061f74e63b8353f2a1d0bc (diff) |
MFD: ucb1x00: convert to use genirq
Convert the ucb1x00 driver to use genirq's interrupt services, rather
than its own private implementation. This allows a wider range of
drivers to use the GPIO interrupts (such as the gpio_keys driver)
without being aware of the UCB1x00's private IRQ system.
This prevents the UCB1x00 core driver from being built as a module,
so adjust the configuration to add that restriction.
Acked-by: Jochen Friedrich <jochen@scram.de>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mfd/Kconfig | 5 | ||||
-rw-r--r-- | drivers/mfd/ucb1x00-core.c | 247 | ||||
-rw-r--r-- | drivers/mfd/ucb1x00-ts.c | 37 |
3 files changed, 128 insertions, 161 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index cd13e9f2f5e6..28a301b28579 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig | |||
@@ -847,8 +847,9 @@ config MCP_SA11X0 | |||
847 | 847 | ||
848 | # Chip drivers | 848 | # Chip drivers |
849 | config MCP_UCB1200 | 849 | config MCP_UCB1200 |
850 | tristate "Support for UCB1200 / UCB1300" | 850 | bool "Support for UCB1200 / UCB1300" |
851 | depends on MCP | 851 | depends on MCP_SA11X0 |
852 | select MCP | ||
852 | 853 | ||
853 | config MCP_UCB1200_TS | 854 | config MCP_UCB1200_TS |
854 | tristate "Touchscreen interface support" | 855 | tristate "Touchscreen interface support" |
diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 6fab82557543..400604d38780 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/init.h> | 23 | #include <linux/init.h> |
24 | #include <linux/errno.h> | 24 | #include <linux/errno.h> |
25 | #include <linux/interrupt.h> | 25 | #include <linux/interrupt.h> |
26 | #include <linux/irq.h> | ||
26 | #include <linux/device.h> | 27 | #include <linux/device.h> |
27 | #include <linux/mutex.h> | 28 | #include <linux/mutex.h> |
28 | #include <linux/mfd/ucb1x00.h> | 29 | #include <linux/mfd/ucb1x00.h> |
@@ -178,6 +179,13 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset | |||
178 | return 0; | 179 | return 0; |
179 | } | 180 | } |
180 | 181 | ||
182 | static int ucb1x00_to_irq(struct gpio_chip *chip, unsigned offset) | ||
183 | { | ||
184 | struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio); | ||
185 | |||
186 | return ucb->irq_base > 0 ? ucb->irq_base + offset : -ENXIO; | ||
187 | } | ||
188 | |||
181 | /* | 189 | /* |
182 | * UCB1300 data sheet says we must: | 190 | * UCB1300 data sheet says we must: |
183 | * 1. enable ADC => 5us (including reference startup time) | 191 | * 1. enable ADC => 5us (including reference startup time) |
@@ -274,10 +282,9 @@ void ucb1x00_adc_disable(struct ucb1x00 *ucb) | |||
274 | * SIBCLK to talk to the chip. We leave the clock running until | 282 | * SIBCLK to talk to the chip. We leave the clock running until |
275 | * we have finished processing all interrupts from the chip. | 283 | * we have finished processing all interrupts from the chip. |
276 | */ | 284 | */ |
277 | static irqreturn_t ucb1x00_irq(int irqnr, void *devid) | 285 | static void ucb1x00_irq(unsigned int irq, struct irq_desc *desc) |
278 | { | 286 | { |
279 | struct ucb1x00 *ucb = devid; | 287 | struct ucb1x00 *ucb = irq_desc_get_handler_data(desc); |
280 | struct ucb1x00_irq *irq; | ||
281 | unsigned int isr, i; | 288 | unsigned int isr, i; |
282 | 289 | ||
283 | ucb1x00_enable(ucb); | 290 | ucb1x00_enable(ucb); |
@@ -285,157 +292,84 @@ static irqreturn_t ucb1x00_irq(int irqnr, void *devid) | |||
285 | ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); | 292 | ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); |
286 | ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); | 293 | ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); |
287 | 294 | ||
288 | for (i = 0, irq = ucb->irq_handler; i < 16 && isr; i++, isr >>= 1, irq++) | 295 | for (i = 0; i < 16 && isr; i++, isr >>= 1, irq++) |
289 | if (isr & 1 && irq->fn) | 296 | if (isr & 1) |
290 | irq->fn(i, irq->devid); | 297 | generic_handle_irq(ucb->irq_base + i); |
291 | ucb1x00_disable(ucb); | 298 | ucb1x00_disable(ucb); |
292 | |||
293 | return IRQ_HANDLED; | ||
294 | } | 299 | } |
295 | 300 | ||
296 | /** | 301 | static void ucb1x00_irq_update(struct ucb1x00 *ucb, unsigned mask) |
297 | * ucb1x00_hook_irq - hook a UCB1x00 interrupt | ||
298 | * @ucb: UCB1x00 structure describing chip | ||
299 | * @idx: interrupt index | ||
300 | * @fn: function to call when interrupt is triggered | ||
301 | * @devid: device id to pass to interrupt handler | ||
302 | * | ||
303 | * Hook the specified interrupt. You can only register one handler | ||
304 | * for each interrupt source. The interrupt source is not enabled | ||
305 | * by this function; use ucb1x00_enable_irq instead. | ||
306 | * | ||
307 | * Interrupt handlers will be called with other interrupts enabled. | ||
308 | * | ||
309 | * Returns zero on success, or one of the following errors: | ||
310 | * -EINVAL if the interrupt index is invalid | ||
311 | * -EBUSY if the interrupt has already been hooked | ||
312 | */ | ||
313 | int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid) | ||
314 | { | 302 | { |
315 | struct ucb1x00_irq *irq; | 303 | ucb1x00_enable(ucb); |
316 | int ret = -EINVAL; | 304 | if (ucb->irq_ris_enbl & mask) |
317 | 305 | ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & | |
318 | if (idx < 16) { | 306 | ucb->irq_mask); |
319 | irq = ucb->irq_handler + idx; | 307 | if (ucb->irq_fal_enbl & mask) |
320 | ret = -EBUSY; | 308 | ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & |
321 | 309 | ucb->irq_mask); | |
322 | spin_lock_irq(&ucb->lock); | 310 | ucb1x00_disable(ucb); |
323 | if (irq->fn == NULL) { | ||
324 | irq->devid = devid; | ||
325 | irq->fn = fn; | ||
326 | ret = 0; | ||
327 | } | ||
328 | spin_unlock_irq(&ucb->lock); | ||
329 | } | ||
330 | return ret; | ||
331 | } | 311 | } |
332 | 312 | ||
333 | /** | 313 | static void ucb1x00_irq_noop(struct irq_data *data) |
334 | * ucb1x00_enable_irq - enable an UCB1x00 interrupt source | ||
335 | * @ucb: UCB1x00 structure describing chip | ||
336 | * @idx: interrupt index | ||
337 | * @edges: interrupt edges to enable | ||
338 | * | ||
339 | * Enable the specified interrupt to trigger on %UCB_RISING, | ||
340 | * %UCB_FALLING or both edges. The interrupt should have been | ||
341 | * hooked by ucb1x00_hook_irq. | ||
342 | */ | ||
343 | void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) | ||
344 | { | 314 | { |
345 | unsigned long flags; | ||
346 | |||
347 | if (idx < 16) { | ||
348 | spin_lock_irqsave(&ucb->lock, flags); | ||
349 | |||
350 | ucb1x00_enable(ucb); | ||
351 | if (edges & UCB_RISING) { | ||
352 | ucb->irq_ris_enbl |= 1 << idx; | ||
353 | ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); | ||
354 | } | ||
355 | if (edges & UCB_FALLING) { | ||
356 | ucb->irq_fal_enbl |= 1 << idx; | ||
357 | ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); | ||
358 | } | ||
359 | ucb1x00_disable(ucb); | ||
360 | spin_unlock_irqrestore(&ucb->lock, flags); | ||
361 | } | ||
362 | } | 315 | } |
363 | 316 | ||
364 | /** | 317 | static void ucb1x00_irq_mask(struct irq_data *data) |
365 | * ucb1x00_disable_irq - disable an UCB1x00 interrupt source | ||
366 | * @ucb: UCB1x00 structure describing chip | ||
367 | * @edges: interrupt edges to disable | ||
368 | * | ||
369 | * Disable the specified interrupt triggering on the specified | ||
370 | * (%UCB_RISING, %UCB_FALLING or both) edges. | ||
371 | */ | ||
372 | void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) | ||
373 | { | 318 | { |
374 | unsigned long flags; | 319 | struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); |
320 | unsigned mask = 1 << (data->irq - ucb->irq_base); | ||
375 | 321 | ||
376 | if (idx < 16) { | 322 | raw_spin_lock(&ucb->irq_lock); |
377 | spin_lock_irqsave(&ucb->lock, flags); | 323 | ucb->irq_mask &= ~mask; |
378 | 324 | ucb1x00_irq_update(ucb, mask); | |
379 | ucb1x00_enable(ucb); | 325 | raw_spin_unlock(&ucb->irq_lock); |
380 | if (edges & UCB_RISING) { | ||
381 | ucb->irq_ris_enbl &= ~(1 << idx); | ||
382 | ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); | ||
383 | } | ||
384 | if (edges & UCB_FALLING) { | ||
385 | ucb->irq_fal_enbl &= ~(1 << idx); | ||
386 | ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); | ||
387 | } | ||
388 | ucb1x00_disable(ucb); | ||
389 | spin_unlock_irqrestore(&ucb->lock, flags); | ||
390 | } | ||
391 | } | 326 | } |
392 | 327 | ||
393 | /** | 328 | static void ucb1x00_irq_unmask(struct irq_data *data) |
394 | * ucb1x00_free_irq - disable and free the specified UCB1x00 interrupt | ||
395 | * @ucb: UCB1x00 structure describing chip | ||
396 | * @idx: interrupt index | ||
397 | * @devid: device id. | ||
398 | * | ||
399 | * Disable the interrupt source and remove the handler. devid must | ||
400 | * match the devid passed when hooking the interrupt. | ||
401 | * | ||
402 | * Returns zero on success, or one of the following errors: | ||
403 | * -EINVAL if the interrupt index is invalid | ||
404 | * -ENOENT if devid does not match | ||
405 | */ | ||
406 | int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid) | ||
407 | { | 329 | { |
408 | struct ucb1x00_irq *irq; | 330 | struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); |
409 | int ret; | 331 | unsigned mask = 1 << (data->irq - ucb->irq_base); |
410 | 332 | ||
411 | if (idx >= 16) | 333 | raw_spin_lock(&ucb->irq_lock); |
412 | goto bad; | 334 | ucb->irq_mask |= mask; |
413 | 335 | ucb1x00_irq_update(ucb, mask); | |
414 | irq = ucb->irq_handler + idx; | 336 | raw_spin_unlock(&ucb->irq_lock); |
415 | ret = -ENOENT; | 337 | } |
416 | 338 | ||
417 | spin_lock_irq(&ucb->lock); | 339 | static int ucb1x00_irq_set_type(struct irq_data *data, unsigned int type) |
418 | if (irq->devid == devid) { | 340 | { |
419 | ucb->irq_ris_enbl &= ~(1 << idx); | 341 | struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); |
420 | ucb->irq_fal_enbl &= ~(1 << idx); | 342 | unsigned mask = 1 << (data->irq - ucb->irq_base); |
421 | 343 | ||
422 | ucb1x00_enable(ucb); | 344 | raw_spin_lock(&ucb->irq_lock); |
423 | ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); | 345 | if (type & IRQ_TYPE_EDGE_RISING) |
424 | ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); | 346 | ucb->irq_ris_enbl |= mask; |
425 | ucb1x00_disable(ucb); | 347 | else |
348 | ucb->irq_ris_enbl &= ~mask; | ||
426 | 349 | ||
427 | irq->fn = NULL; | 350 | if (type & IRQ_TYPE_EDGE_FALLING) |
428 | irq->devid = NULL; | 351 | ucb->irq_fal_enbl |= mask; |
429 | ret = 0; | 352 | else |
353 | ucb->irq_fal_enbl &= ~mask; | ||
354 | if (ucb->irq_mask & mask) { | ||
355 | ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & | ||
356 | ucb->irq_mask); | ||
357 | ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & | ||
358 | ucb->irq_mask); | ||
430 | } | 359 | } |
431 | spin_unlock_irq(&ucb->lock); | 360 | raw_spin_unlock(&ucb->irq_lock); |
432 | return ret; | ||
433 | 361 | ||
434 | bad: | 362 | return 0; |
435 | printk(KERN_ERR "Freeing bad UCB1x00 irq %d\n", idx); | ||
436 | return -EINVAL; | ||
437 | } | 363 | } |
438 | 364 | ||
365 | static struct irq_chip ucb1x00_irqchip = { | ||
366 | .name = "ucb1x00", | ||
367 | .irq_ack = ucb1x00_irq_noop, | ||
368 | .irq_mask = ucb1x00_irq_mask, | ||
369 | .irq_unmask = ucb1x00_irq_unmask, | ||
370 | .irq_set_type = ucb1x00_irq_set_type, | ||
371 | }; | ||
372 | |||
439 | static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv) | 373 | static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv) |
440 | { | 374 | { |
441 | struct ucb1x00_dev *dev; | 375 | struct ucb1x00_dev *dev; |
@@ -545,9 +479,8 @@ static int ucb1x00_probe(struct mcp *mcp) | |||
545 | struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data; | 479 | struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data; |
546 | struct ucb1x00_driver *drv; | 480 | struct ucb1x00_driver *drv; |
547 | struct ucb1x00 *ucb; | 481 | struct ucb1x00 *ucb; |
548 | unsigned int id; | 482 | unsigned id, i, irq_base; |
549 | int ret = -ENODEV; | 483 | int ret = -ENODEV; |
550 | int temp; | ||
551 | 484 | ||
552 | /* Tell the platform to deassert the UCB1x00 reset */ | 485 | /* Tell the platform to deassert the UCB1x00 reset */ |
553 | if (pdata && pdata->reset) | 486 | if (pdata && pdata->reset) |
@@ -572,7 +505,7 @@ static int ucb1x00_probe(struct mcp *mcp) | |||
572 | ucb->dev.parent = &mcp->attached_device; | 505 | ucb->dev.parent = &mcp->attached_device; |
573 | dev_set_name(&ucb->dev, "ucb1x00"); | 506 | dev_set_name(&ucb->dev, "ucb1x00"); |
574 | 507 | ||
575 | spin_lock_init(&ucb->lock); | 508 | raw_spin_lock_init(&ucb->irq_lock); |
576 | spin_lock_init(&ucb->io_lock); | 509 | spin_lock_init(&ucb->io_lock); |
577 | mutex_init(&ucb->adc_mutex); | 510 | mutex_init(&ucb->adc_mutex); |
578 | 511 | ||
@@ -593,6 +526,26 @@ static int ucb1x00_probe(struct mcp *mcp) | |||
593 | } | 526 | } |
594 | 527 | ||
595 | ucb->gpio.base = -1; | 528 | ucb->gpio.base = -1; |
529 | irq_base = pdata ? pdata->irq_base : 0; | ||
530 | ucb->irq_base = irq_alloc_descs(-1, irq_base, 16, -1); | ||
531 | if (ucb->irq_base < 0) { | ||
532 | dev_err(&ucb->dev, "unable to allocate 16 irqs: %d\n", | ||
533 | ucb->irq_base); | ||
534 | goto err_irq_alloc; | ||
535 | } | ||
536 | |||
537 | for (i = 0; i < 16; i++) { | ||
538 | unsigned irq = ucb->irq_base + i; | ||
539 | |||
540 | irq_set_chip_and_handler(irq, &ucb1x00_irqchip, handle_edge_irq); | ||
541 | irq_set_chip_data(irq, ucb); | ||
542 | set_irq_flags(irq, IRQF_VALID | IRQ_NOREQUEST); | ||
543 | } | ||
544 | |||
545 | irq_set_irq_type(ucb->irq, IRQ_TYPE_EDGE_RISING); | ||
546 | irq_set_handler_data(ucb->irq, ucb); | ||
547 | irq_set_chained_handler(ucb->irq, ucb1x00_irq); | ||
548 | |||
596 | if (pdata && pdata->gpio_base) { | 549 | if (pdata && pdata->gpio_base) { |
597 | ucb->gpio.label = dev_name(&ucb->dev); | 550 | ucb->gpio.label = dev_name(&ucb->dev); |
598 | ucb->gpio.dev = &ucb->dev; | 551 | ucb->gpio.dev = &ucb->dev; |
@@ -603,20 +556,13 @@ static int ucb1x00_probe(struct mcp *mcp) | |||
603 | ucb->gpio.get = ucb1x00_gpio_get; | 556 | ucb->gpio.get = ucb1x00_gpio_get; |
604 | ucb->gpio.direction_input = ucb1x00_gpio_direction_input; | 557 | ucb->gpio.direction_input = ucb1x00_gpio_direction_input; |
605 | ucb->gpio.direction_output = ucb1x00_gpio_direction_output; | 558 | ucb->gpio.direction_output = ucb1x00_gpio_direction_output; |
559 | ucb->gpio.to_irq = ucb1x00_to_irq; | ||
606 | ret = gpiochip_add(&ucb->gpio); | 560 | ret = gpiochip_add(&ucb->gpio); |
607 | if (ret) | 561 | if (ret) |
608 | goto err_gpio_add; | 562 | goto err_gpio_add; |
609 | } else | 563 | } else |
610 | dev_info(&ucb->dev, "gpio_base not set so no gpiolib support"); | 564 | dev_info(&ucb->dev, "gpio_base not set so no gpiolib support"); |
611 | 565 | ||
612 | ret = request_irq(ucb->irq, ucb1x00_irq, IRQF_TRIGGER_RISING, | ||
613 | "UCB1x00", ucb); | ||
614 | if (ret) { | ||
615 | dev_err(&ucb->dev, "ucb1x00: unable to grab irq%d: %d\n", | ||
616 | ucb->irq, ret); | ||
617 | goto err_irq; | ||
618 | } | ||
619 | |||
620 | mcp_set_drvdata(mcp, ucb); | 566 | mcp_set_drvdata(mcp, ucb); |
621 | 567 | ||
622 | INIT_LIST_HEAD(&ucb->devs); | 568 | INIT_LIST_HEAD(&ucb->devs); |
@@ -629,10 +575,11 @@ static int ucb1x00_probe(struct mcp *mcp) | |||
629 | 575 | ||
630 | return ret; | 576 | return ret; |
631 | 577 | ||
632 | err_irq: | ||
633 | if (ucb->gpio.base != -1) | ||
634 | temp = gpiochip_remove(&ucb->gpio); | ||
635 | err_gpio_add: | 578 | err_gpio_add: |
579 | irq_set_chained_handler(ucb->irq, NULL); | ||
580 | err_irq_alloc: | ||
581 | if (ucb->irq_base > 0) | ||
582 | irq_free_descs(ucb->irq_base, 16); | ||
636 | err_no_irq: | 583 | err_no_irq: |
637 | device_del(&ucb->dev); | 584 | device_del(&ucb->dev); |
638 | err_dev_add: | 585 | err_dev_add: |
@@ -664,7 +611,8 @@ static void ucb1x00_remove(struct mcp *mcp) | |||
664 | dev_err(&ucb->dev, "Can't remove gpio chip: %d\n", ret); | 611 | dev_err(&ucb->dev, "Can't remove gpio chip: %d\n", ret); |
665 | } | 612 | } |
666 | 613 | ||
667 | free_irq(ucb->irq, ucb); | 614 | irq_set_chained_handler(ucb->irq, NULL); |
615 | irq_free_descs(ucb->irq_base, 16); | ||
668 | device_unregister(&ucb->dev); | 616 | device_unregister(&ucb->dev); |
669 | 617 | ||
670 | if (pdata && pdata->reset) | 618 | if (pdata && pdata->reset) |
@@ -772,11 +720,6 @@ EXPORT_SYMBOL(ucb1x00_adc_enable); | |||
772 | EXPORT_SYMBOL(ucb1x00_adc_read); | 720 | EXPORT_SYMBOL(ucb1x00_adc_read); |
773 | EXPORT_SYMBOL(ucb1x00_adc_disable); | 721 | EXPORT_SYMBOL(ucb1x00_adc_disable); |
774 | 722 | ||
775 | EXPORT_SYMBOL(ucb1x00_hook_irq); | ||
776 | EXPORT_SYMBOL(ucb1x00_free_irq); | ||
777 | EXPORT_SYMBOL(ucb1x00_enable_irq); | ||
778 | EXPORT_SYMBOL(ucb1x00_disable_irq); | ||
779 | |||
780 | EXPORT_SYMBOL(ucb1x00_register_driver); | 723 | EXPORT_SYMBOL(ucb1x00_register_driver); |
781 | EXPORT_SYMBOL(ucb1x00_unregister_driver); | 724 | EXPORT_SYMBOL(ucb1x00_unregister_driver); |
782 | 725 | ||
diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index 742d0c7bbbc2..1e0e20c0e082 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c | |||
@@ -20,8 +20,9 @@ | |||
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | #include <linux/moduleparam.h> | 21 | #include <linux/moduleparam.h> |
22 | #include <linux/init.h> | 22 | #include <linux/init.h> |
23 | #include <linux/smp.h> | 23 | #include <linux/interrupt.h> |
24 | #include <linux/sched.h> | 24 | #include <linux/sched.h> |
25 | #include <linux/spinlock.h> | ||
25 | #include <linux/completion.h> | 26 | #include <linux/completion.h> |
26 | #include <linux/delay.h> | 27 | #include <linux/delay.h> |
27 | #include <linux/string.h> | 28 | #include <linux/string.h> |
@@ -41,6 +42,8 @@ struct ucb1x00_ts { | |||
41 | struct input_dev *idev; | 42 | struct input_dev *idev; |
42 | struct ucb1x00 *ucb; | 43 | struct ucb1x00 *ucb; |
43 | 44 | ||
45 | spinlock_t irq_lock; | ||
46 | unsigned irq_disabled; | ||
44 | wait_queue_head_t irq_wait; | 47 | wait_queue_head_t irq_wait; |
45 | struct task_struct *rtask; | 48 | struct task_struct *rtask; |
46 | u16 x_res; | 49 | u16 x_res; |
@@ -237,7 +240,12 @@ static int ucb1x00_thread(void *_ts) | |||
237 | if (ucb1x00_ts_pen_down(ts)) { | 240 | if (ucb1x00_ts_pen_down(ts)) { |
238 | set_current_state(TASK_INTERRUPTIBLE); | 241 | set_current_state(TASK_INTERRUPTIBLE); |
239 | 242 | ||
240 | ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING); | 243 | spin_lock_irq(&ts->irq_lock); |
244 | if (ts->irq_disabled) { | ||
245 | ts->irq_disabled = 0; | ||
246 | enable_irq(ts->ucb->irq_base + UCB_IRQ_TSPX); | ||
247 | } | ||
248 | spin_unlock_irq(&ts->irq_lock); | ||
241 | ucb1x00_disable(ts->ucb); | 249 | ucb1x00_disable(ts->ucb); |
242 | 250 | ||
243 | /* | 251 | /* |
@@ -280,23 +288,37 @@ static int ucb1x00_thread(void *_ts) | |||
280 | * We only detect touch screen _touches_ with this interrupt | 288 | * We only detect touch screen _touches_ with this interrupt |
281 | * handler, and even then we just schedule our task. | 289 | * handler, and even then we just schedule our task. |
282 | */ | 290 | */ |
283 | static void ucb1x00_ts_irq(int idx, void *id) | 291 | static irqreturn_t ucb1x00_ts_irq(int irq, void *id) |
284 | { | 292 | { |
285 | struct ucb1x00_ts *ts = id; | 293 | struct ucb1x00_ts *ts = id; |
286 | 294 | ||
287 | ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); | 295 | spin_lock(&ts->irq_lock); |
296 | ts->irq_disabled = 1; | ||
297 | disable_irq_nosync(ts->ucb->irq_base + UCB_IRQ_TSPX); | ||
298 | spin_unlock(&ts->irq_lock); | ||
288 | wake_up(&ts->irq_wait); | 299 | wake_up(&ts->irq_wait); |
300 | |||
301 | return IRQ_HANDLED; | ||
289 | } | 302 | } |
290 | 303 | ||
291 | static int ucb1x00_ts_open(struct input_dev *idev) | 304 | static int ucb1x00_ts_open(struct input_dev *idev) |
292 | { | 305 | { |
293 | struct ucb1x00_ts *ts = input_get_drvdata(idev); | 306 | struct ucb1x00_ts *ts = input_get_drvdata(idev); |
307 | unsigned long flags = 0; | ||
294 | int ret = 0; | 308 | int ret = 0; |
295 | 309 | ||
296 | BUG_ON(ts->rtask); | 310 | BUG_ON(ts->rtask); |
297 | 311 | ||
312 | if (machine_is_collie()) | ||
313 | flags = IRQF_TRIGGER_RISING; | ||
314 | else | ||
315 | flags = IRQF_TRIGGER_FALLING; | ||
316 | |||
317 | ts->irq_disabled = 0; | ||
318 | |||
298 | init_waitqueue_head(&ts->irq_wait); | 319 | init_waitqueue_head(&ts->irq_wait); |
299 | ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts); | 320 | ret = request_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ucb1x00_ts_irq, |
321 | flags, "ucb1x00-ts", ts); | ||
300 | if (ret < 0) | 322 | if (ret < 0) |
301 | goto out; | 323 | goto out; |
302 | 324 | ||
@@ -313,7 +335,7 @@ static int ucb1x00_ts_open(struct input_dev *idev) | |||
313 | if (!IS_ERR(ts->rtask)) { | 335 | if (!IS_ERR(ts->rtask)) { |
314 | ret = 0; | 336 | ret = 0; |
315 | } else { | 337 | } else { |
316 | ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); | 338 | free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts); |
317 | ts->rtask = NULL; | 339 | ts->rtask = NULL; |
318 | ret = -EFAULT; | 340 | ret = -EFAULT; |
319 | } | 341 | } |
@@ -333,7 +355,7 @@ static void ucb1x00_ts_close(struct input_dev *idev) | |||
333 | kthread_stop(ts->rtask); | 355 | kthread_stop(ts->rtask); |
334 | 356 | ||
335 | ucb1x00_enable(ts->ucb); | 357 | ucb1x00_enable(ts->ucb); |
336 | ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); | 358 | free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts); |
337 | ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); | 359 | ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); |
338 | ucb1x00_disable(ts->ucb); | 360 | ucb1x00_disable(ts->ucb); |
339 | } | 361 | } |
@@ -358,6 +380,7 @@ static int ucb1x00_ts_add(struct ucb1x00_dev *dev) | |||
358 | ts->ucb = dev->ucb; | 380 | ts->ucb = dev->ucb; |
359 | ts->idev = idev; | 381 | ts->idev = idev; |
360 | ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC; | 382 | ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC; |
383 | spin_lock_init(&ts->irq_lock); | ||
361 | 384 | ||
362 | idev->name = "Touchscreen panel"; | 385 | idev->name = "Touchscreen panel"; |
363 | idev->id.product = ts->ucb->id; | 386 | idev->id.product = ts->ucb->id; |