diff options
Diffstat (limited to 'drivers/net/wimax/i2400m')
-rw-r--r-- | drivers/net/wimax/i2400m/driver.c | 74 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/i2400m.h | 14 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/sdio-tx.c | 4 |
3 files changed, 92 insertions, 0 deletions
diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c index 1674dba43f83..d83fe84407bf 100644 --- a/drivers/net/wimax/i2400m/driver.c +++ b/drivers/net/wimax/i2400m/driver.c | |||
@@ -395,6 +395,16 @@ retry: | |||
395 | result = i2400m_dev_initialize(i2400m); | 395 | result = i2400m_dev_initialize(i2400m); |
396 | if (result < 0) | 396 | if (result < 0) |
397 | goto error_dev_initialize; | 397 | goto error_dev_initialize; |
398 | |||
399 | /* We don't want any additional unwanted error recovery triggered | ||
400 | * from any other context so if anything went wrong before we come | ||
401 | * here, let's keep i2400m->error_recovery untouched and leave it to | ||
402 | * dev_reset_handle(). See dev_reset_handle(). */ | ||
403 | |||
404 | atomic_dec(&i2400m->error_recovery); | ||
405 | /* Every thing works so far, ok, now we are ready to | ||
406 | * take error recovery if it's required. */ | ||
407 | |||
398 | /* At this point, reports will come for the device and set it | 408 | /* At this point, reports will come for the device and set it |
399 | * to the right state if it is different than UNINITIALIZED */ | 409 | * to the right state if it is different than UNINITIALIZED */ |
400 | d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n", | 410 | d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n", |
@@ -770,6 +780,66 @@ int i2400m_dev_reset_handle(struct i2400m *i2400m, const char *reason) | |||
770 | EXPORT_SYMBOL_GPL(i2400m_dev_reset_handle); | 780 | EXPORT_SYMBOL_GPL(i2400m_dev_reset_handle); |
771 | 781 | ||
772 | 782 | ||
783 | /* | ||
784 | * The actual work of error recovery. | ||
785 | * | ||
786 | * The current implementation of error recovery is to trigger a bus reset. | ||
787 | */ | ||
788 | static | ||
789 | void __i2400m_error_recovery(struct work_struct *ws) | ||
790 | { | ||
791 | struct i2400m_work *iw = container_of(ws, struct i2400m_work, ws); | ||
792 | struct i2400m *i2400m = iw->i2400m; | ||
793 | |||
794 | i2400m_reset(i2400m, I2400M_RT_BUS); | ||
795 | |||
796 | i2400m_put(i2400m); | ||
797 | kfree(iw); | ||
798 | return; | ||
799 | } | ||
800 | |||
801 | /* | ||
802 | * Schedule a work struct for error recovery. | ||
803 | * | ||
804 | * The intention of error recovery is to bring back the device to some | ||
805 | * known state whenever TX sees -110 (-ETIMEOUT) on copying the data to | ||
806 | * the device. The TX failure could mean a device bus stuck, so the current | ||
807 | * error recovery implementation is to trigger a bus reset to the device | ||
808 | * and hopefully it can bring back the device. | ||
809 | * | ||
810 | * The actual work of error recovery has to be in a thread context because | ||
811 | * it is kicked off in the TX thread (i2400ms->tx_workqueue) which is to be | ||
812 | * destroyed by the error recovery mechanism (currently a bus reset). | ||
813 | * | ||
814 | * Also, there may be already a queue of TX works that all hit | ||
815 | * the -ETIMEOUT error condition because the device is stuck already. | ||
816 | * Since bus reset is used as the error recovery mechanism and we don't | ||
817 | * want consecutive bus resets simply because the multiple TX works | ||
818 | * in the queue all hit the same device erratum, the flag "error_recovery" | ||
819 | * is introduced for preventing unwanted consecutive bus resets. | ||
820 | * | ||
821 | * Error recovery shall only be invoked again if previous one was completed. | ||
822 | * The flag error_recovery is set when error recovery mechanism is scheduled, | ||
823 | * and is checked when we need to schedule another error recovery. If it is | ||
824 | * in place already, then we shouldn't schedule another one. | ||
825 | */ | ||
826 | void i2400m_error_recovery(struct i2400m *i2400m) | ||
827 | { | ||
828 | struct device *dev = i2400m_dev(i2400m); | ||
829 | |||
830 | if (atomic_add_return(1, &i2400m->error_recovery) == 1) { | ||
831 | if (i2400m_schedule_work(i2400m, __i2400m_error_recovery, | ||
832 | GFP_ATOMIC, NULL, 0) < 0) { | ||
833 | dev_err(dev, "run out of memory for " | ||
834 | "scheduling an error recovery ?\n"); | ||
835 | atomic_dec(&i2400m->error_recovery); | ||
836 | } | ||
837 | } else | ||
838 | atomic_dec(&i2400m->error_recovery); | ||
839 | return; | ||
840 | } | ||
841 | EXPORT_SYMBOL_GPL(i2400m_error_recovery); | ||
842 | |||
773 | /* | 843 | /* |
774 | * Alloc the command and ack buffers for boot mode | 844 | * Alloc the command and ack buffers for boot mode |
775 | * | 845 | * |
@@ -839,6 +909,10 @@ void i2400m_init(struct i2400m *i2400m) | |||
839 | atomic_set(&i2400m->bus_reset_retries, 0); | 909 | atomic_set(&i2400m->bus_reset_retries, 0); |
840 | 910 | ||
841 | i2400m->alive = 0; | 911 | i2400m->alive = 0; |
912 | |||
913 | /* initialize error_recovery to 1 for denoting we | ||
914 | * are not yet ready to take any error recovery */ | ||
915 | atomic_set(&i2400m->error_recovery, 1); | ||
842 | } | 916 | } |
843 | EXPORT_SYMBOL_GPL(i2400m_init); | 917 | EXPORT_SYMBOL_GPL(i2400m_init); |
844 | 918 | ||
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h index ad8e6a3be1e3..7a9c2c5b25cb 100644 --- a/drivers/net/wimax/i2400m/i2400m.h +++ b/drivers/net/wimax/i2400m/i2400m.h | |||
@@ -545,6 +545,15 @@ struct i2400m_barker_db; | |||
545 | * all the device reboot events detected can be still handled properly | 545 | * all the device reboot events detected can be still handled properly |
546 | * by either dev_reset_handle() or .pre_reset/.post_reset as long as | 546 | * by either dev_reset_handle() or .pre_reset/.post_reset as long as |
547 | * the driver presents. It is set 0 along with @updown in dev_stop(). | 547 | * the driver presents. It is set 0 along with @updown in dev_stop(). |
548 | * | ||
549 | * @error_recovery: flag to denote if we are ready to take an error recovery. | ||
550 | * 0 for ready to take an error recovery; 1 for not ready. It is | ||
551 | * initialized to 1 while probe() since we don't tend to take any error | ||
552 | * recovery during probe(). It is decremented by 1 whenever dev_start() | ||
553 | * succeeds to indicate we are ready to take error recovery from now on. | ||
554 | * It is checked every time we wanna schedule an error recovery. If an | ||
555 | * error recovery is already in place (error_recovery was set 1), we | ||
556 | * should not schedule another one until the last one is done. | ||
548 | */ | 557 | */ |
549 | struct i2400m { | 558 | struct i2400m { |
550 | struct wimax_dev wimax_dev; /* FIRST! See doc */ | 559 | struct wimax_dev wimax_dev; /* FIRST! See doc */ |
@@ -625,6 +634,10 @@ struct i2400m { | |||
625 | 634 | ||
626 | /* if the device is expected to be alive */ | 635 | /* if the device is expected to be alive */ |
627 | unsigned alive; | 636 | unsigned alive; |
637 | |||
638 | /* 0 if we are ready for error recovery; 1 if not ready */ | ||
639 | atomic_t error_recovery; | ||
640 | |||
628 | }; | 641 | }; |
629 | 642 | ||
630 | 643 | ||
@@ -847,6 +860,7 @@ void i2400m_put(struct i2400m *i2400m) | |||
847 | extern int i2400m_dev_reset_handle(struct i2400m *, const char *); | 860 | extern int i2400m_dev_reset_handle(struct i2400m *, const char *); |
848 | extern int i2400m_pre_reset(struct i2400m *); | 861 | extern int i2400m_pre_reset(struct i2400m *); |
849 | extern int i2400m_post_reset(struct i2400m *); | 862 | extern int i2400m_post_reset(struct i2400m *); |
863 | extern void i2400m_error_recovery(struct i2400m *); | ||
850 | 864 | ||
851 | /* | 865 | /* |
852 | * _setup()/_release() are called by the probe/disconnect functions of | 866 | * _setup()/_release() are called by the probe/disconnect functions of |
diff --git a/drivers/net/wimax/i2400m/sdio-tx.c b/drivers/net/wimax/i2400m/sdio-tx.c index 412b6a8eaef2..b53cd1c80e3e 100644 --- a/drivers/net/wimax/i2400m/sdio-tx.c +++ b/drivers/net/wimax/i2400m/sdio-tx.c | |||
@@ -98,6 +98,10 @@ void i2400ms_tx_submit(struct work_struct *ws) | |||
98 | tx_msg_size, result); | 98 | tx_msg_size, result); |
99 | } | 99 | } |
100 | 100 | ||
101 | if (result == -ETIMEDOUT) { | ||
102 | i2400m_error_recovery(i2400m); | ||
103 | break; | ||
104 | } | ||
101 | d_printf(2, dev, "TX: %zub submitted\n", tx_msg_size); | 105 | d_printf(2, dev, "TX: %zub submitted\n", tx_msg_size); |
102 | } | 106 | } |
103 | 107 | ||