diff options
author | Inaky Perez-Gonzalez <inaky@linux.intel.com> | 2009-09-17 18:20:45 -0400 |
---|---|---|
committer | Inaky Perez-Gonzalez <inaky@linux.intel.com> | 2009-10-19 02:56:10 -0400 |
commit | 3725d8c97436aeaa03eeb0c25361a7ec0f3f5bd2 (patch) | |
tree | c773759b4757473172ff6ba3ec26c134f5fc3d42 /drivers | |
parent | 2869da8587604e3fea5f85aeade486a08e8313bf (diff) |
wimax/i2400m: Implement pre/post reset support in the USB driver
The USB stack can callback a driver is about to be reset by an
external entity and right after it, so the driver can save state and
then restore it.
This commit implements said support; it is implemented actually in the
core, bus-generic driver [i2400m_{pre,post}_reset()] and used by the
bus-specific drivers. This way the SDIO driver can also use it once
said support is brought to the SDIO stack.
Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wimax/i2400m/driver.c | 81 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/i2400m.h | 2 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/usb.c | 33 |
3 files changed, 116 insertions, 0 deletions
diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c index 4fcdb18261fd..1f6aa2a55429 100644 --- a/drivers/net/wimax/i2400m/driver.c +++ b/drivers/net/wimax/i2400m/driver.c | |||
@@ -620,6 +620,87 @@ int i2400m_pm_notifier(struct notifier_block *notifier, | |||
620 | 620 | ||
621 | 621 | ||
622 | /* | 622 | /* |
623 | * pre-reset is called before a device is going on reset | ||
624 | * | ||
625 | * This has to be followed by a call to i2400m_post_reset(), otherwise | ||
626 | * bad things might happen. | ||
627 | */ | ||
628 | int i2400m_pre_reset(struct i2400m *i2400m) | ||
629 | { | ||
630 | int result; | ||
631 | struct device *dev = i2400m_dev(i2400m); | ||
632 | |||
633 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | ||
634 | d_printf(1, dev, "pre-reset shut down\n"); | ||
635 | |||
636 | result = 0; | ||
637 | mutex_lock(&i2400m->init_mutex); | ||
638 | if (i2400m->updown) { | ||
639 | netif_tx_disable(i2400m->wimax_dev.net_dev); | ||
640 | __i2400m_dev_stop(i2400m); | ||
641 | result = 0; | ||
642 | /* down't set updown to zero -- this way | ||
643 | * post_reset can restore properly */ | ||
644 | } | ||
645 | mutex_unlock(&i2400m->init_mutex); | ||
646 | if (i2400m->bus_release) | ||
647 | i2400m->bus_release(i2400m); | ||
648 | d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); | ||
649 | return result; | ||
650 | } | ||
651 | EXPORT_SYMBOL_GPL(i2400m_pre_reset); | ||
652 | |||
653 | |||
654 | /* | ||
655 | * Restore device state after a reset | ||
656 | * | ||
657 | * Do the work needed after a device reset to bring it up to the same | ||
658 | * state as it was before the reset. | ||
659 | * | ||
660 | * NOTE: this requires i2400m->init_mutex taken | ||
661 | */ | ||
662 | int i2400m_post_reset(struct i2400m *i2400m) | ||
663 | { | ||
664 | int result = 0; | ||
665 | struct device *dev = i2400m_dev(i2400m); | ||
666 | |||
667 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | ||
668 | d_printf(1, dev, "post-reset start\n"); | ||
669 | if (i2400m->bus_setup) { | ||
670 | result = i2400m->bus_setup(i2400m); | ||
671 | if (result < 0) { | ||
672 | dev_err(dev, "bus-specific setup failed: %d\n", | ||
673 | result); | ||
674 | goto error_bus_setup; | ||
675 | } | ||
676 | } | ||
677 | mutex_lock(&i2400m->init_mutex); | ||
678 | if (i2400m->updown) { | ||
679 | result = __i2400m_dev_start( | ||
680 | i2400m, I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT); | ||
681 | if (result < 0) | ||
682 | goto error_dev_start; | ||
683 | } | ||
684 | mutex_unlock(&i2400m->init_mutex); | ||
685 | d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); | ||
686 | return result; | ||
687 | |||
688 | error_dev_start: | ||
689 | if (i2400m->bus_release) | ||
690 | i2400m->bus_release(i2400m); | ||
691 | error_bus_setup: | ||
692 | /* even if the device was up, it could not be recovered, so we | ||
693 | * mark it as down. */ | ||
694 | i2400m->updown = 0; | ||
695 | wmb(); /* see i2400m->updown's documentation */ | ||
696 | mutex_unlock(&i2400m->init_mutex); | ||
697 | d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); | ||
698 | return result; | ||
699 | } | ||
700 | EXPORT_SYMBOL_GPL(i2400m_post_reset); | ||
701 | |||
702 | |||
703 | /* | ||
623 | * The device has rebooted; fix up the device and the driver | 704 | * The device has rebooted; fix up the device and the driver |
624 | * | 705 | * |
625 | * Tear down the driver communication with the device, reload the | 706 | * Tear down the driver communication with the device, reload the |
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h index 1724955e0fe9..8fc8a0ca5126 100644 --- a/drivers/net/wimax/i2400m/i2400m.h +++ b/drivers/net/wimax/i2400m/i2400m.h | |||
@@ -817,6 +817,8 @@ void i2400m_put(struct i2400m *i2400m) | |||
817 | } | 817 | } |
818 | 818 | ||
819 | extern int i2400m_dev_reset_handle(struct i2400m *, const char *); | 819 | extern int i2400m_dev_reset_handle(struct i2400m *, const char *); |
820 | extern int i2400m_pre_reset(struct i2400m *); | ||
821 | extern int i2400m_post_reset(struct i2400m *); | ||
820 | 822 | ||
821 | /* | 823 | /* |
822 | * _setup()/_release() are called by the probe/disconnect functions of | 824 | * _setup()/_release() are called by the probe/disconnect functions of |
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c index 77567970fe9a..8b246cc498b1 100644 --- a/drivers/net/wimax/i2400m/usb.c +++ b/drivers/net/wimax/i2400m/usb.c | |||
@@ -637,6 +637,37 @@ int i2400mu_reset_resume(struct usb_interface *iface) | |||
637 | } | 637 | } |
638 | 638 | ||
639 | 639 | ||
640 | /* | ||
641 | * Another driver or user space is triggering a reset on the device | ||
642 | * which contains the interface passed as an argument. Cease IO and | ||
643 | * save any device state you need to restore. | ||
644 | * | ||
645 | * If you need to allocate memory here, use GFP_NOIO or GFP_ATOMIC, if | ||
646 | * you are in atomic context. | ||
647 | */ | ||
648 | static | ||
649 | int i2400mu_pre_reset(struct usb_interface *iface) | ||
650 | { | ||
651 | struct i2400mu *i2400mu = usb_get_intfdata(iface); | ||
652 | return i2400m_pre_reset(&i2400mu->i2400m); | ||
653 | } | ||
654 | |||
655 | |||
656 | /* | ||
657 | * The reset has completed. Restore any saved device state and begin | ||
658 | * using the device again. | ||
659 | * | ||
660 | * If you need to allocate memory here, use GFP_NOIO or GFP_ATOMIC, if | ||
661 | * you are in atomic context. | ||
662 | */ | ||
663 | static | ||
664 | int i2400mu_post_reset(struct usb_interface *iface) | ||
665 | { | ||
666 | struct i2400mu *i2400mu = usb_get_intfdata(iface); | ||
667 | return i2400m_post_reset(&i2400mu->i2400m); | ||
668 | } | ||
669 | |||
670 | |||
640 | static | 671 | static |
641 | struct usb_device_id i2400mu_id_table[] = { | 672 | struct usb_device_id i2400mu_id_table[] = { |
642 | { USB_DEVICE(0x8086, USB_DEVICE_ID_I6050) }, | 673 | { USB_DEVICE(0x8086, USB_DEVICE_ID_I6050) }, |
@@ -660,6 +691,8 @@ struct usb_driver i2400mu_driver = { | |||
660 | .reset_resume = i2400mu_reset_resume, | 691 | .reset_resume = i2400mu_reset_resume, |
661 | .probe = i2400mu_probe, | 692 | .probe = i2400mu_probe, |
662 | .disconnect = i2400mu_disconnect, | 693 | .disconnect = i2400mu_disconnect, |
694 | .pre_reset = i2400mu_pre_reset, | ||
695 | .post_reset = i2400mu_post_reset, | ||
663 | .id_table = i2400mu_id_table, | 696 | .id_table = i2400mu_id_table, |
664 | .supports_autosuspend = 1, | 697 | .supports_autosuspend = 1, |
665 | }; | 698 | }; |