Date: | 2011-07-06 22:36:21 (12 years 8 months ago) |
---|---|
Author: | Werner Almesberger |
Commit: | 7fa3bd331029facef4464626aee9f351b386e45d |
Message: | atusb: added tentative interrupt support Added the USB-to-interrupt logic. The approach is basically to have a bulk URB that waits for an incoming interrupt notification and that gets resubmitted whenever the interrupt is acknowledged. We map enable/disable to an atomic count of the masking depth. To do: - convert printks to dev_dbg or remove them - remove bulk URB on driver unload - implement interrupt synchronization (via an 10 ms timer) - don't make atusb_irq_enable call atusb_do_irq directly but start a tasklet instead. Otherwise, we recurse on a spin lock held somewhere in the interrupt handling infrastructure. We send our first interrupt before at86rf230.c is ready to handle it. This causes all further interrupts to be lost. As a work-around, just clear IRQ_STATUS at the end of at86rf230_probe. Signed-off-by: Werner Almesberger <werner@almesberger.net> |
Files: |
drivers/ieee802154/at86rf230.c (1 diff) drivers/spi/atusb.c (6 diffs) |
Change Details
drivers/ieee802154/at86rf230.c | ||
---|---|---|
796 | 796 | if (rc) |
797 | 797 | goto err_irq; |
798 | 798 | |
799 | { | |
800 | uint8_t val; | |
801 | at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &val); | |
802 | } | |
799 | 803 | return rc; |
800 | 804 | |
801 | 805 | err_irq: |
drivers/spi/atusb.c | ||
---|---|---|
19 | 19 | #include <linux/module.h> |
20 | 20 | #include <linux/platform_device.h> |
21 | 21 | #include <linux/interrupt.h> |
22 | #include <linux/atomic.h> | |
22 | 23 | #include <linux/usb.h> |
23 | 24 | #include <linux/spi/spi.h> |
24 | 25 | #include <linux/spi/at86rf230.h> |
... | ... | |
44 | 45 | uint8_t atusb_hw_type; |
45 | 46 | struct spi_master *master; |
46 | 47 | int slave_irq; |
48 | struct urb *irq_urb; | |
49 | uint8_t irq_buf; /* scratch space */ | |
50 | atomic_t pending; /* pending interrupts */ | |
51 | atomic_t masked; /* masking levels */ | |
47 | 52 | struct at86rf230_platform_data platform_data; |
48 | 53 | /* copy platform_data so that we can adapt .reset_data */ |
49 | 54 | struct spi_device *spi; |
... | ... | |
352 | 357 | /* ----- Interrupt handling ------------------------------------------------ */ |
353 | 358 | |
354 | 359 | |
355 | #if 0 | |
356 | static irqreturn_t atusb_irq(int irq, void *data) | |
360 | static void atusb_do_irq(struct atusb_local *atusb); | |
361 | ||
362 | static void atusb_irq_enable(struct atusb_local *atusb) | |
357 | 363 | { |
358 | struct atusb_local *atusb = data; | |
364 | printk(KERN_INFO "atusb_irq_enable\n"); | |
365 | if (atomic_dec_return(&atusb->masked)) | |
366 | return; | |
367 | if (!atomic_add_unless(&atusb->pending, -1, 0)) | |
368 | return; | |
369 | /* @@@ probably racy around here. | |
370 | need to think this through some more - wa */ | |
371 | atusb_do_irq(atusb); | |
372 | } | |
359 | 373 | |
360 | generic_handle_irq(atusb->slave_irq); | |
361 | return IRQ_HANDLED; | |
374 | static void atusb_do_irq(struct atusb_local *atusb) | |
375 | { | |
376 | printk(KERN_INFO "atusb_do_irq\n"); | |
377 | if (atomic_inc_return(&atusb->masked) == 1) | |
378 | generic_handle_irq(atusb->slave_irq); | |
379 | else | |
380 | atomic_set(&atusb->pending, 1); | |
381 | printk(KERN_INFO "atusb_do_irq out\n"); | |
382 | atusb_irq_enable(atusb); | |
362 | 383 | } |
363 | #endif | |
384 | ||
385 | static void atusb_irq(struct urb *urb) | |
386 | { | |
387 | struct atusb_local *atusb = urb->context; | |
388 | ||
389 | printk(KERN_INFO "atusb_irq (%d)\n", urb->status); | |
390 | usb_free_urb(urb); | |
391 | atusb->irq_urb = NULL; | |
392 | atusb_do_irq(atusb); | |
393 | } | |
394 | ||
395 | static int atusb_arm_interrupt(struct atusb_local *atusb) | |
396 | { | |
397 | struct usb_device *dev = atusb->udev; | |
398 | struct urb *urb; | |
399 | int retval = -ENOMEM; | |
400 | ||
401 | BUG_ON(atusb->irq_urb); | |
402 | ||
403 | printk(KERN_INFO "atusb_arm_interrupt\n"); | |
404 | urb = usb_alloc_urb(0, GFP_KERNEL); | |
405 | if (!urb) { | |
406 | dev_err(&dev->dev, | |
407 | "atusb_arm_interrupt: usb_alloc_urb failed\n"); | |
408 | return -ENOMEM; | |
409 | } | |
410 | ||
411 | usb_fill_bulk_urb(urb, dev, usb_rcvbulkpipe(dev, 1), | |
412 | &atusb->irq_buf, 1, atusb_irq, atusb); | |
413 | atusb->irq_urb = urb; | |
414 | retval = usb_submit_urb(urb, GFP_KERNEL); | |
415 | if (!retval) | |
416 | return 0; | |
417 | ||
418 | dev_err(&dev->dev, "failed submitting bulk urb, error %d", retval); | |
419 | retval = retval == -ENOMEM ? retval : -EIO; | |
420 | ||
421 | usb_free_urb(urb); | |
422 | ||
423 | return retval; | |
424 | } | |
425 | ||
426 | static void atusb_irq_ack(struct irq_data *data) | |
427 | { | |
428 | struct atusb_local *atusb = irq_data_get_irq_chip_data(data); | |
429 | ||
430 | printk(KERN_INFO "atusb_irq_ack\n"); | |
431 | atusb_arm_interrupt(atusb); | |
432 | } | |
433 | ||
364 | 434 | static void atusb_irq_mask(struct irq_data *data) |
365 | 435 | { |
366 | // struct atben_local *atusb = irq_data_get_irq_chip_data(data); | |
436 | struct atusb_local *atusb = irq_data_get_irq_chip_data(data); | |
367 | 437 | |
368 | // disable_irq_nosync(atusb->usb_irq); | |
438 | printk(KERN_INFO "atusb_irq_mask\n"); | |
439 | atomic_inc(&atusb->masked); | |
369 | 440 | } |
370 | 441 | |
371 | 442 | static void atusb_irq_unmask(struct irq_data *data) |
372 | 443 | { |
373 | // struct atben_local *atusb = irq_data_get_irq_chip_data(data); | |
444 | struct atusb_local *atusb = irq_data_get_irq_chip_data(data); | |
374 | 445 | |
375 | // enable_irq(atusb->usb_irq); | |
446 | printk(KERN_INFO "atusb_irq_unmask\n"); | |
447 | atusb_irq_enable(atusb); | |
376 | 448 | } |
377 | 449 | |
378 | 450 | static struct irq_chip atusb_irq_chip = { |
379 | 451 | .name = "atusb-slave", |
380 | 452 | .irq_mask = atusb_irq_mask, |
381 | 453 | .irq_unmask = atusb_irq_unmask, |
454 | .irq_ack = atusb_irq_ack, | |
382 | 455 | }; |
383 | 456 | |
384 | 457 | |
... | ... | |
534 | 607 | board_info.platform_data = &atusb->platform_data; |
535 | 608 | board_info.irq = atusb->slave_irq; |
536 | 609 | |
610 | atomic_set(&atusb->masked, 0); | |
611 | atomic_set(&atusb->pending, 0); | |
612 | atusb_arm_interrupt(atusb); | |
613 | ||
537 | 614 | atusb->spi = spi_new_device(master, &board_info); |
538 | 615 | if (!atusb->spi) { |
539 | 616 | dev_info(&udev->dev, "can't create new device for %s\n", |
... | ... | |
552 | 629 | return 0; |
553 | 630 | |
554 | 631 | err_master: |
632 | /* @@@ kill interrupt URB */ | |
555 | 633 | spi_master_put(atusb->master); |
556 | 634 | err_slave_irq: |
557 | 635 | set_irq_chained_handler(atusb->slave_irq, NULL); |
... | ... | |
566 | 644 | struct atusb_local *atusb = usb_get_intfdata(interface); |
567 | 645 | struct spi_master *master = atusb->master; |
568 | 646 | |
647 | /* @@@ kill interrupt URB */ | |
569 | 648 | usb_set_intfdata(interface, NULL); |
570 | 649 | usb_put_dev(atusb->udev); |
571 | 650 |
Branches:
ben-wpan
ben-wpan-stefan
5396a9238205f20f811ea57898980d3ca82df0b6
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9