aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wimax/i2400m/driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wimax/i2400m/driver.c')
-rw-r--r--drivers/net/wimax/i2400m/driver.c167
1 files changed, 128 insertions, 39 deletions
diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c
index 94dc83c3969d..9c8b78d4abd2 100644
--- a/drivers/net/wimax/i2400m/driver.c
+++ b/drivers/net/wimax/i2400m/driver.c
@@ -75,25 +75,6 @@
75#include "debug-levels.h" 75#include "debug-levels.h"
76 76
77 77
78int i2400m_idle_mode_disabled; /* 0 (idle mode enabled) by default */
79module_param_named(idle_mode_disabled, i2400m_idle_mode_disabled, int, 0644);
80MODULE_PARM_DESC(idle_mode_disabled,
81 "If true, the device will not enable idle mode negotiation "
82 "with the base station (when connected) to save power.");
83
84int i2400m_rx_reorder_disabled; /* 0 (rx reorder enabled) by default */
85module_param_named(rx_reorder_disabled, i2400m_rx_reorder_disabled, int, 0644);
86MODULE_PARM_DESC(rx_reorder_disabled,
87 "If true, RX reordering will be disabled.");
88
89int i2400m_power_save_disabled; /* 0 (power saving enabled) by default */
90module_param_named(power_save_disabled, i2400m_power_save_disabled, int, 0644);
91MODULE_PARM_DESC(power_save_disabled,
92 "If true, the driver will not tell the device to enter "
93 "power saving mode when it reports it is ready for it. "
94 "False by default (so the device is told to do power "
95 "saving).");
96
97static char i2400m_debug_params[128]; 78static char i2400m_debug_params[128];
98module_param_string(debug, i2400m_debug_params, sizeof(i2400m_debug_params), 79module_param_string(debug, i2400m_debug_params, sizeof(i2400m_debug_params),
99 0644); 80 0644);
@@ -395,6 +376,16 @@ retry:
395 result = i2400m_dev_initialize(i2400m); 376 result = i2400m_dev_initialize(i2400m);
396 if (result < 0) 377 if (result < 0)
397 goto error_dev_initialize; 378 goto error_dev_initialize;
379
380 /* We don't want any additional unwanted error recovery triggered
381 * from any other context so if anything went wrong before we come
382 * here, let's keep i2400m->error_recovery untouched and leave it to
383 * dev_reset_handle(). See dev_reset_handle(). */
384
385 atomic_dec(&i2400m->error_recovery);
386 /* Every thing works so far, ok, now we are ready to
387 * take error recovery if it's required. */
388
398 /* At this point, reports will come for the device and set it 389 /* At this point, reports will come for the device and set it
399 * to the right state if it is different than UNINITIALIZED */ 390 * to the right state if it is different than UNINITIALIZED */
400 d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n", 391 d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n",
@@ -403,10 +394,10 @@ retry:
403 394
404error_dev_initialize: 395error_dev_initialize:
405error_check_mac_addr: 396error_check_mac_addr:
397error_fw_check:
406 i2400m->ready = 0; 398 i2400m->ready = 0;
407 wmb(); /* see i2400m->ready's documentation */ 399 wmb(); /* see i2400m->ready's documentation */
408 flush_workqueue(i2400m->work_queue); 400 flush_workqueue(i2400m->work_queue);
409error_fw_check:
410 if (i2400m->bus_dev_stop) 401 if (i2400m->bus_dev_stop)
411 i2400m->bus_dev_stop(i2400m); 402 i2400m->bus_dev_stop(i2400m);
412error_bus_dev_start: 403error_bus_dev_start:
@@ -436,7 +427,8 @@ int i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri bm_flags)
436 result = __i2400m_dev_start(i2400m, bm_flags); 427 result = __i2400m_dev_start(i2400m, bm_flags);
437 if (result >= 0) { 428 if (result >= 0) {
438 i2400m->updown = 1; 429 i2400m->updown = 1;
439 wmb(); /* see i2400m->updown's documentation */ 430 i2400m->alive = 1;
431 wmb();/* see i2400m->updown and i2400m->alive's doc */
440 } 432 }
441 } 433 }
442 mutex_unlock(&i2400m->init_mutex); 434 mutex_unlock(&i2400m->init_mutex);
@@ -497,7 +489,8 @@ void i2400m_dev_stop(struct i2400m *i2400m)
497 if (i2400m->updown) { 489 if (i2400m->updown) {
498 __i2400m_dev_stop(i2400m); 490 __i2400m_dev_stop(i2400m);
499 i2400m->updown = 0; 491 i2400m->updown = 0;
500 wmb(); /* see i2400m->updown's documentation */ 492 i2400m->alive = 0;
493 wmb(); /* see i2400m->updown and i2400m->alive's doc */
501 } 494 }
502 mutex_unlock(&i2400m->init_mutex); 495 mutex_unlock(&i2400m->init_mutex);
503} 496}
@@ -617,12 +610,12 @@ int i2400m_post_reset(struct i2400m *i2400m)
617error_dev_start: 610error_dev_start:
618 if (i2400m->bus_release) 611 if (i2400m->bus_release)
619 i2400m->bus_release(i2400m); 612 i2400m->bus_release(i2400m);
620error_bus_setup:
621 /* even if the device was up, it could not be recovered, so we 613 /* even if the device was up, it could not be recovered, so we
622 * mark it as down. */ 614 * mark it as down. */
623 i2400m->updown = 0; 615 i2400m->updown = 0;
624 wmb(); /* see i2400m->updown's documentation */ 616 wmb(); /* see i2400m->updown's documentation */
625 mutex_unlock(&i2400m->init_mutex); 617 mutex_unlock(&i2400m->init_mutex);
618error_bus_setup:
626 d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); 619 d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
627 return result; 620 return result;
628} 621}
@@ -669,6 +662,9 @@ void __i2400m_dev_reset_handle(struct work_struct *ws)
669 662
670 d_fnstart(3, dev, "(ws %p i2400m %p reason %s)\n", ws, i2400m, reason); 663 d_fnstart(3, dev, "(ws %p i2400m %p reason %s)\n", ws, i2400m, reason);
671 664
665 i2400m->boot_mode = 1;
666 wmb(); /* Make sure i2400m_msg_to_dev() sees boot_mode */
667
672 result = 0; 668 result = 0;
673 if (mutex_trylock(&i2400m->init_mutex) == 0) { 669 if (mutex_trylock(&i2400m->init_mutex) == 0) {
674 /* We are still in i2400m_dev_start() [let it fail] or 670 /* We are still in i2400m_dev_start() [let it fail] or
@@ -679,39 +675,68 @@ void __i2400m_dev_reset_handle(struct work_struct *ws)
679 complete(&i2400m->msg_completion); 675 complete(&i2400m->msg_completion);
680 goto out; 676 goto out;
681 } 677 }
682 if (i2400m->updown == 0) { 678
683 dev_info(dev, "%s: device is down, doing nothing\n", reason);
684 goto out_unlock;
685 }
686 dev_err(dev, "%s: reinitializing driver\n", reason); 679 dev_err(dev, "%s: reinitializing driver\n", reason);
687 __i2400m_dev_stop(i2400m); 680 rmb();
688 result = __i2400m_dev_start(i2400m, 681 if (i2400m->updown) {
689 I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT); 682 __i2400m_dev_stop(i2400m);
690 if (result < 0) {
691 i2400m->updown = 0; 683 i2400m->updown = 0;
692 wmb(); /* see i2400m->updown's documentation */ 684 wmb(); /* see i2400m->updown's documentation */
693 dev_err(dev, "%s: cannot start the device: %d\n",
694 reason, result);
695 result = -EUCLEAN;
696 } 685 }
697out_unlock: 686
687 if (i2400m->alive) {
688 result = __i2400m_dev_start(i2400m,
689 I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT);
690 if (result < 0) {
691 dev_err(dev, "%s: cannot start the device: %d\n",
692 reason, result);
693 result = -EUCLEAN;
694 if (atomic_read(&i2400m->bus_reset_retries)
695 >= I2400M_BUS_RESET_RETRIES) {
696 result = -ENODEV;
697 dev_err(dev, "tried too many times to "
698 "reset the device, giving up\n");
699 }
700 }
701 }
702
698 if (i2400m->reset_ctx) { 703 if (i2400m->reset_ctx) {
699 ctx->result = result; 704 ctx->result = result;
700 complete(&ctx->completion); 705 complete(&ctx->completion);
701 } 706 }
702 mutex_unlock(&i2400m->init_mutex); 707 mutex_unlock(&i2400m->init_mutex);
703 if (result == -EUCLEAN) { 708 if (result == -EUCLEAN) {
709 /*
710 * We come here because the reset during operational mode
711 * wasn't successully done and need to proceed to a bus
712 * reset. For the dev_reset_handle() to be able to handle
713 * the reset event later properly, we restore boot_mode back
714 * to the state before previous reset. ie: just like we are
715 * issuing the bus reset for the first time
716 */
717 i2400m->boot_mode = 0;
718 wmb();
719
720 atomic_inc(&i2400m->bus_reset_retries);
704 /* ops, need to clean up [w/ init_mutex not held] */ 721 /* ops, need to clean up [w/ init_mutex not held] */
705 result = i2400m_reset(i2400m, I2400M_RT_BUS); 722 result = i2400m_reset(i2400m, I2400M_RT_BUS);
706 if (result >= 0) 723 if (result >= 0)
707 result = -ENODEV; 724 result = -ENODEV;
725 } else {
726 rmb();
727 if (i2400m->alive) {
728 /* great, we expect the device state up and
729 * dev_start() actually brings the device state up */
730 i2400m->updown = 1;
731 wmb();
732 atomic_set(&i2400m->bus_reset_retries, 0);
733 }
708 } 734 }
709out: 735out:
710 i2400m_put(i2400m); 736 i2400m_put(i2400m);
711 kfree(iw); 737 kfree(iw);
712 d_fnend(3, dev, "(ws %p i2400m %p reason %s) = void\n", 738 d_fnend(3, dev, "(ws %p i2400m %p reason %s) = void\n",
713 ws, i2400m, reason); 739 ws, i2400m, reason);
714 return;
715} 740}
716 741
717 742
@@ -729,14 +754,72 @@ out:
729 */ 754 */
730int i2400m_dev_reset_handle(struct i2400m *i2400m, const char *reason) 755int i2400m_dev_reset_handle(struct i2400m *i2400m, const char *reason)
731{ 756{
732 i2400m->boot_mode = 1;
733 wmb(); /* Make sure i2400m_msg_to_dev() sees boot_mode */
734 return i2400m_schedule_work(i2400m, __i2400m_dev_reset_handle, 757 return i2400m_schedule_work(i2400m, __i2400m_dev_reset_handle,
735 GFP_ATOMIC, &reason, sizeof(reason)); 758 GFP_ATOMIC, &reason, sizeof(reason));
736} 759}
737EXPORT_SYMBOL_GPL(i2400m_dev_reset_handle); 760EXPORT_SYMBOL_GPL(i2400m_dev_reset_handle);
738 761
739 762
763 /*
764 * The actual work of error recovery.
765 *
766 * The current implementation of error recovery is to trigger a bus reset.
767 */
768static
769void __i2400m_error_recovery(struct work_struct *ws)
770{
771 struct i2400m_work *iw = container_of(ws, struct i2400m_work, ws);
772 struct i2400m *i2400m = iw->i2400m;
773
774 i2400m_reset(i2400m, I2400M_RT_BUS);
775
776 i2400m_put(i2400m);
777 kfree(iw);
778 return;
779}
780
781/*
782 * Schedule a work struct for error recovery.
783 *
784 * The intention of error recovery is to bring back the device to some
785 * known state whenever TX sees -110 (-ETIMEOUT) on copying the data to
786 * the device. The TX failure could mean a device bus stuck, so the current
787 * error recovery implementation is to trigger a bus reset to the device
788 * and hopefully it can bring back the device.
789 *
790 * The actual work of error recovery has to be in a thread context because
791 * it is kicked off in the TX thread (i2400ms->tx_workqueue) which is to be
792 * destroyed by the error recovery mechanism (currently a bus reset).
793 *
794 * Also, there may be already a queue of TX works that all hit
795 * the -ETIMEOUT error condition because the device is stuck already.
796 * Since bus reset is used as the error recovery mechanism and we don't
797 * want consecutive bus resets simply because the multiple TX works
798 * in the queue all hit the same device erratum, the flag "error_recovery"
799 * is introduced for preventing unwanted consecutive bus resets.
800 *
801 * Error recovery shall only be invoked again if previous one was completed.
802 * The flag error_recovery is set when error recovery mechanism is scheduled,
803 * and is checked when we need to schedule another error recovery. If it is
804 * in place already, then we shouldn't schedule another one.
805 */
806void i2400m_error_recovery(struct i2400m *i2400m)
807{
808 struct device *dev = i2400m_dev(i2400m);
809
810 if (atomic_add_return(1, &i2400m->error_recovery) == 1) {
811 if (i2400m_schedule_work(i2400m, __i2400m_error_recovery,
812 GFP_ATOMIC, NULL, 0) < 0) {
813 dev_err(dev, "run out of memory for "
814 "scheduling an error recovery ?\n");
815 atomic_dec(&i2400m->error_recovery);
816 }
817 } else
818 atomic_dec(&i2400m->error_recovery);
819 return;
820}
821EXPORT_SYMBOL_GPL(i2400m_error_recovery);
822
740/* 823/*
741 * Alloc the command and ack buffers for boot mode 824 * Alloc the command and ack buffers for boot mode
742 * 825 *
@@ -803,6 +886,13 @@ void i2400m_init(struct i2400m *i2400m)
803 886
804 mutex_init(&i2400m->init_mutex); 887 mutex_init(&i2400m->init_mutex);
805 /* wake_tx_ws is initialized in i2400m_tx_setup() */ 888 /* wake_tx_ws is initialized in i2400m_tx_setup() */
889 atomic_set(&i2400m->bus_reset_retries, 0);
890
891 i2400m->alive = 0;
892
893 /* initialize error_recovery to 1 for denoting we
894 * are not yet ready to take any error recovery */
895 atomic_set(&i2400m->error_recovery, 1);
806} 896}
807EXPORT_SYMBOL_GPL(i2400m_init); 897EXPORT_SYMBOL_GPL(i2400m_init);
808 898
@@ -996,7 +1086,6 @@ void __exit i2400m_driver_exit(void)
996 /* for scheds i2400m_dev_reset_handle() */ 1086 /* for scheds i2400m_dev_reset_handle() */
997 flush_scheduled_work(); 1087 flush_scheduled_work();
998 i2400m_barker_db_exit(); 1088 i2400m_barker_db_exit();
999 return;
1000} 1089}
1001module_exit(i2400m_driver_exit); 1090module_exit(i2400m_driver_exit);
1002 1091