diff options
Diffstat (limited to 'drivers/net/wimax/i2400m/driver.c')
-rw-r--r-- | drivers/net/wimax/i2400m/driver.c | 167 |
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 | ||
78 | int i2400m_idle_mode_disabled; /* 0 (idle mode enabled) by default */ | ||
79 | module_param_named(idle_mode_disabled, i2400m_idle_mode_disabled, int, 0644); | ||
80 | MODULE_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 | |||
84 | int i2400m_rx_reorder_disabled; /* 0 (rx reorder enabled) by default */ | ||
85 | module_param_named(rx_reorder_disabled, i2400m_rx_reorder_disabled, int, 0644); | ||
86 | MODULE_PARM_DESC(rx_reorder_disabled, | ||
87 | "If true, RX reordering will be disabled."); | ||
88 | |||
89 | int i2400m_power_save_disabled; /* 0 (power saving enabled) by default */ | ||
90 | module_param_named(power_save_disabled, i2400m_power_save_disabled, int, 0644); | ||
91 | MODULE_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 | |||
97 | static char i2400m_debug_params[128]; | 78 | static char i2400m_debug_params[128]; |
98 | module_param_string(debug, i2400m_debug_params, sizeof(i2400m_debug_params), | 79 | module_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 | ||
404 | error_dev_initialize: | 395 | error_dev_initialize: |
405 | error_check_mac_addr: | 396 | error_check_mac_addr: |
397 | error_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); |
409 | error_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); |
412 | error_bus_dev_start: | 403 | error_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) | |||
617 | error_dev_start: | 610 | error_dev_start: |
618 | if (i2400m->bus_release) | 611 | if (i2400m->bus_release) |
619 | i2400m->bus_release(i2400m); | 612 | i2400m->bus_release(i2400m); |
620 | error_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); |
618 | error_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 | } |
697 | out_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 | } |
709 | out: | 735 | out: |
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 | */ |
730 | int i2400m_dev_reset_handle(struct i2400m *i2400m, const char *reason) | 755 | int 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 | } |
737 | EXPORT_SYMBOL_GPL(i2400m_dev_reset_handle); | 760 | EXPORT_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 | */ | ||
768 | static | ||
769 | void __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 | */ | ||
806 | void 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 | } | ||
821 | EXPORT_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 | } |
807 | EXPORT_SYMBOL_GPL(i2400m_init); | 897 | EXPORT_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 | } |
1001 | module_exit(i2400m_driver_exit); | 1090 | module_exit(i2400m_driver_exit); |
1002 | 1091 | ||