diff options
| author | Cindy H Kao <cindy.h.kao@intel.com> | 2010-04-07 22:42:42 -0400 |
|---|---|---|
| committer | Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | 2010-05-11 17:05:30 -0400 |
| commit | f4e413458104210bc29aa5c437882c68b4b20100 (patch) | |
| tree | 72508e9a5d3620986120304db7fe78f0326b0743 | |
| parent | 49d72df3f6cd63b49528306a2577ae7a6a0e9d6b (diff) | |
wimax/i2400m: fix for missed reset events if triggered by dev_reset_handle()
The problem is only seen on SDIO interface since on USB, a bus reset would
really re-probe the driver, but on SDIO interface, a bus reset will not
re-enumerate the SDIO bus, so no driver re-probe is happening. Therefore,
on SDIO interface, the reset event should be still detected and handled by
dev_reset_handle().
Problem description:
Whenever a reboot barker is received during operational mode (i2400m->boot_mode == 0),
dev_reset_handle() is invoked to handle that function reset event.
dev_reset_handle() then sets the flag i2400m->boot_mode to 1 indicating the device is
back to bootmode before proceeding to dev_stop() and dev_start().
If dev_start() returns failure, a bus reset is triggered by dev_reset_handle().
The flag i2400m->boot_mode then remains 1 when the second reboot barker arrives.
However the interrupt service routine i2400ms_rx() instead of invoking dev_reset_handle()
to handle that reset event, it filters out that boot event to bootmode because it sees
the flag i2400m->boot_mode equal to 1.
The fix:
Maintain the flag i2400m->boot_mode within dev_reset_handle() and set the flag
i2400m->boot_mode to 1 when entering dev_reset_handle(). It remains 1
until the dev_reset_handle() issues a bus reset. ie: the bus reset is
taking place just like it happens for the first time during operational mode.
To denote the actual device state and the state we expect, a flag i2400m->alive
is introduced in addition to the existing flag i2400m->updown.
It's maintained with the same way for i2400m->updown but instead of reflecting
the actual state like i2400m->updown does, i2400m->alive maintains the state
we expect. i2400m->alive is set 1 just like whenever i2400m->updown is set 1.
Yet i2400m->alive remains 1 since we expect the device to be up all the time
until the driver is removed. See the doc for @alive in i2400m.h.
An enumeration I2400M_BUS_RESET_RETRIES is added to define the maximum number of
bus resets that a device reboot can retry.
A counter i2400m->bus_reset_retries is added to track how many bus resets
have been retried in one device reboot. If I2400M_BUS_RESET_RETRIES bus resets
were retried in this boot, we give up any further retrying so the device would enter
low power state. The counter i2400m->bus_reset_retries is incremented whenever
dev_reset_handle() is issuing a bus reset and is cleared to 0 when dev_start() is
successfully done, ie: a successful reboot.
Signed-off-by: Cindy H Kao <cindy.h.kao@intel.com>
| -rw-r--r-- | drivers/net/wimax/i2400m/driver.c | 68 | ||||
| -rw-r--r-- | drivers/net/wimax/i2400m/i2400m.h | 34 |
2 files changed, 86 insertions, 16 deletions
diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c index 3a6c8ddb5353..1674dba43f83 100644 --- a/drivers/net/wimax/i2400m/driver.c +++ b/drivers/net/wimax/i2400m/driver.c | |||
| @@ -436,7 +436,8 @@ int i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri bm_flags) | |||
| 436 | result = __i2400m_dev_start(i2400m, bm_flags); | 436 | result = __i2400m_dev_start(i2400m, bm_flags); |
| 437 | if (result >= 0) { | 437 | if (result >= 0) { |
| 438 | i2400m->updown = 1; | 438 | i2400m->updown = 1; |
| 439 | wmb(); /* see i2400m->updown's documentation */ | 439 | i2400m->alive = 1; |
| 440 | wmb();/* see i2400m->updown and i2400m->alive's doc */ | ||
| 440 | } | 441 | } |
| 441 | } | 442 | } |
| 442 | mutex_unlock(&i2400m->init_mutex); | 443 | mutex_unlock(&i2400m->init_mutex); |
| @@ -497,7 +498,8 @@ void i2400m_dev_stop(struct i2400m *i2400m) | |||
| 497 | if (i2400m->updown) { | 498 | if (i2400m->updown) { |
| 498 | __i2400m_dev_stop(i2400m); | 499 | __i2400m_dev_stop(i2400m); |
| 499 | i2400m->updown = 0; | 500 | i2400m->updown = 0; |
| 500 | wmb(); /* see i2400m->updown's documentation */ | 501 | i2400m->alive = 0; |
| 502 | wmb(); /* see i2400m->updown and i2400m->alive's doc */ | ||
| 501 | } | 503 | } |
| 502 | mutex_unlock(&i2400m->init_mutex); | 504 | mutex_unlock(&i2400m->init_mutex); |
| 503 | } | 505 | } |
| @@ -669,6 +671,9 @@ void __i2400m_dev_reset_handle(struct work_struct *ws) | |||
| 669 | 671 | ||
| 670 | d_fnstart(3, dev, "(ws %p i2400m %p reason %s)\n", ws, i2400m, reason); | 672 | d_fnstart(3, dev, "(ws %p i2400m %p reason %s)\n", ws, i2400m, reason); |
| 671 | 673 | ||
| 674 | i2400m->boot_mode = 1; | ||
| 675 | wmb(); /* Make sure i2400m_msg_to_dev() sees boot_mode */ | ||
| 676 | |||
| 672 | result = 0; | 677 | result = 0; |
| 673 | if (mutex_trylock(&i2400m->init_mutex) == 0) { | 678 | if (mutex_trylock(&i2400m->init_mutex) == 0) { |
| 674 | /* We are still in i2400m_dev_start() [let it fail] or | 679 | /* We are still in i2400m_dev_start() [let it fail] or |
| @@ -679,32 +684,62 @@ void __i2400m_dev_reset_handle(struct work_struct *ws) | |||
| 679 | complete(&i2400m->msg_completion); | 684 | complete(&i2400m->msg_completion); |
| 680 | goto out; | 685 | goto out; |
| 681 | } | 686 | } |
| 682 | if (i2400m->updown == 0) { | 687 | |
| 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); | 688 | dev_err(dev, "%s: reinitializing driver\n", reason); |
| 687 | __i2400m_dev_stop(i2400m); | 689 | rmb(); |
| 688 | result = __i2400m_dev_start(i2400m, | 690 | if (i2400m->updown) { |
| 689 | I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT); | 691 | __i2400m_dev_stop(i2400m); |
| 690 | if (result < 0) { | ||
| 691 | i2400m->updown = 0; | 692 | i2400m->updown = 0; |
| 692 | wmb(); /* see i2400m->updown's documentation */ | 693 | wmb(); /* see i2400m->updown's documentation */ |
| 693 | dev_err(dev, "%s: cannot start the device: %d\n", | ||
| 694 | reason, result); | ||
| 695 | result = -EUCLEAN; | ||
| 696 | } | 694 | } |
| 697 | out_unlock: | 695 | |
| 696 | if (i2400m->alive) { | ||
| 697 | result = __i2400m_dev_start(i2400m, | ||
| 698 | I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT); | ||
| 699 | if (result < 0) { | ||
| 700 | dev_err(dev, "%s: cannot start the device: %d\n", | ||
| 701 | reason, result); | ||
| 702 | result = -EUCLEAN; | ||
| 703 | if (atomic_read(&i2400m->bus_reset_retries) | ||
| 704 | >= I2400M_BUS_RESET_RETRIES) { | ||
| 705 | result = -ENODEV; | ||
| 706 | dev_err(dev, "tried too many times to " | ||
| 707 | "reset the device, giving up\n"); | ||
| 708 | } | ||
| 709 | } | ||
| 710 | } | ||
| 711 | |||
| 698 | if (i2400m->reset_ctx) { | 712 | if (i2400m->reset_ctx) { |
| 699 | ctx->result = result; | 713 | ctx->result = result; |
| 700 | complete(&ctx->completion); | 714 | complete(&ctx->completion); |
| 701 | } | 715 | } |
| 702 | mutex_unlock(&i2400m->init_mutex); | 716 | mutex_unlock(&i2400m->init_mutex); |
| 703 | if (result == -EUCLEAN) { | 717 | if (result == -EUCLEAN) { |
| 718 | /* | ||
| 719 | * We come here because the reset during operational mode | ||
| 720 | * wasn't successully done and need to proceed to a bus | ||
| 721 | * reset. For the dev_reset_handle() to be able to handle | ||
| 722 | * the reset event later properly, we restore boot_mode back | ||
| 723 | * to the state before previous reset. ie: just like we are | ||
| 724 | * issuing the bus reset for the first time | ||
| 725 | */ | ||
| 726 | i2400m->boot_mode = 0; | ||
| 727 | wmb(); | ||
| 728 | |||
| 729 | atomic_inc(&i2400m->bus_reset_retries); | ||
| 704 | /* ops, need to clean up [w/ init_mutex not held] */ | 730 | /* ops, need to clean up [w/ init_mutex not held] */ |
| 705 | result = i2400m_reset(i2400m, I2400M_RT_BUS); | 731 | result = i2400m_reset(i2400m, I2400M_RT_BUS); |
| 706 | if (result >= 0) | 732 | if (result >= 0) |
| 707 | result = -ENODEV; | 733 | result = -ENODEV; |
| 734 | } else { | ||
| 735 | rmb(); | ||
| 736 | if (i2400m->alive) { | ||
| 737 | /* great, we expect the device state up and | ||
| 738 | * dev_start() actually brings the device state up */ | ||
| 739 | i2400m->updown = 1; | ||
| 740 | wmb(); | ||
| 741 | atomic_set(&i2400m->bus_reset_retries, 0); | ||
| 742 | } | ||
| 708 | } | 743 | } |
| 709 | out: | 744 | out: |
| 710 | i2400m_put(i2400m); | 745 | i2400m_put(i2400m); |
| @@ -729,8 +764,6 @@ out: | |||
| 729 | */ | 764 | */ |
| 730 | int i2400m_dev_reset_handle(struct i2400m *i2400m, const char *reason) | 765 | int i2400m_dev_reset_handle(struct i2400m *i2400m, const char *reason) |
| 731 | { | 766 | { |
| 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, | 767 | return i2400m_schedule_work(i2400m, __i2400m_dev_reset_handle, |
| 735 | GFP_ATOMIC, &reason, sizeof(reason)); | 768 | GFP_ATOMIC, &reason, sizeof(reason)); |
| 736 | } | 769 | } |
| @@ -803,6 +836,9 @@ void i2400m_init(struct i2400m *i2400m) | |||
| 803 | 836 | ||
| 804 | mutex_init(&i2400m->init_mutex); | 837 | mutex_init(&i2400m->init_mutex); |
| 805 | /* wake_tx_ws is initialized in i2400m_tx_setup() */ | 838 | /* wake_tx_ws is initialized in i2400m_tx_setup() */ |
| 839 | atomic_set(&i2400m->bus_reset_retries, 0); | ||
| 840 | |||
| 841 | i2400m->alive = 0; | ||
| 806 | } | 842 | } |
| 807 | EXPORT_SYMBOL_GPL(i2400m_init); | 843 | EXPORT_SYMBOL_GPL(i2400m_init); |
| 808 | 844 | ||
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h index da218b98e27f..ad8e6a3be1e3 100644 --- a/drivers/net/wimax/i2400m/i2400m.h +++ b/drivers/net/wimax/i2400m/i2400m.h | |||
| @@ -177,6 +177,11 @@ enum { | |||
| 177 | I2400M_BM_ACK_BUF_SIZE = 256, | 177 | I2400M_BM_ACK_BUF_SIZE = 256, |
| 178 | }; | 178 | }; |
| 179 | 179 | ||
| 180 | enum { | ||
| 181 | /* Maximum number of bus reset can be retried */ | ||
| 182 | I2400M_BUS_RESET_RETRIES = 3, | ||
| 183 | }; | ||
| 184 | |||
| 180 | /** | 185 | /** |
| 181 | * struct i2400m_poke_table - Hardware poke table for the Intel 2400m | 186 | * struct i2400m_poke_table - Hardware poke table for the Intel 2400m |
| 182 | * | 187 | * |
| @@ -517,6 +522,29 @@ struct i2400m_barker_db; | |||
| 517 | * same. | 522 | * same. |
| 518 | * | 523 | * |
| 519 | * @pm_notifier: used to register for PM events | 524 | * @pm_notifier: used to register for PM events |
| 525 | * | ||
| 526 | * @bus_reset_retries: counter for the number of bus resets attempted for | ||
| 527 | * this boot. It's not for tracking the number of bus resets during | ||
| 528 | * the whole driver life cycle (from insmod to rmmod) but for the | ||
| 529 | * number of dev_start() executed until dev_start() returns a success | ||
| 530 | * (ie: a good boot means a dev_stop() followed by a successful | ||
| 531 | * dev_start()). dev_reset_handler() increments this counter whenever | ||
| 532 | * it is triggering a bus reset. It checks this counter to decide if a | ||
| 533 | * sub | ||
