diff options
author | Inaky Perez-Gonzalez <inaky@linux.intel.com> | 2009-05-07 04:02:39 -0400 |
---|---|---|
committer | Inaky Perez-Gonzalez <inaky@linux.intel.com> | 2009-06-11 06:30:25 -0400 |
commit | 16820c166d3ad5973d388b5aa70ee7e535386657 (patch) | |
tree | 59ccd86e6530bbfc6aac3d55a4952f851c0bc3cc /drivers/net/wimax/i2400m/sdio-fw.c | |
parent | 6e053d6c79b3c1c9f6efc6563a811023c41be86a (diff) |
wimax/i2400m/sdio: Move all the RX code to a unified, IRQ based receive routine
The current SDIO code was working in polling mode for boot-mode
(firmware load) mode. This was causing issues on some hardware.
Moved all the RX code to use a unified IRQ handler that based on the
type of data the device is sending can discriminate and decide which
is the right destination.
As well, all the reads from the device are made to be at least the
block size (256); the driver will ignore the rest when not needed.
Signed-off-by: Dirk Brandewie <dirk.j.brandewie@intel.com>
Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
Diffstat (limited to 'drivers/net/wimax/i2400m/sdio-fw.c')
-rw-r--r-- | drivers/net/wimax/i2400m/sdio-fw.c | 109 |
1 files changed, 47 insertions, 62 deletions
diff --git a/drivers/net/wimax/i2400m/sdio-fw.c b/drivers/net/wimax/i2400m/sdio-fw.c index 3487205d8f50..7d6ec0f475f8 100644 --- a/drivers/net/wimax/i2400m/sdio-fw.c +++ b/drivers/net/wimax/i2400m/sdio-fw.c | |||
@@ -46,17 +46,24 @@ | |||
46 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | 46 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> |
47 | * - SDIO rehash for changes in the bus-driver model | 47 | * - SDIO rehash for changes in the bus-driver model |
48 | * | 48 | * |
49 | * Dirk Brandewie <dirk.j.brandewie@intel.com> | ||
50 | * - Make it IRQ based, not polling | ||
51 | * | ||
49 | * THE PROCEDURE | 52 | * THE PROCEDURE |
50 | * | 53 | * |
51 | * See fw.c for the generic description of this procedure. | 54 | * See fw.c for the generic description of this procedure. |
52 | * | 55 | * |
53 | * This file implements only the SDIO specifics. It boils down to how | 56 | * This file implements only the SDIO specifics. It boils down to how |
54 | * to send a command and waiting for an acknowledgement from the | 57 | * to send a command and waiting for an acknowledgement from the |
55 | * device. We do polled reads. | 58 | * device. |
59 | * | ||
60 | * All this code is sequential -- all i2400ms_bus_bm_*() functions are | ||
61 | * executed in the same thread, except i2400ms_bm_irq() [on its own by | ||
62 | * the SDIO driver]. This makes it possible to avoid locking. | ||
56 | * | 63 | * |
57 | * COMMAND EXECUTION | 64 | * COMMAND EXECUTION |
58 | * | 65 | * |
59 | * THe generic firmware upload code will call i2400m_bus_bm_cmd_send() | 66 | * The generic firmware upload code will call i2400m_bus_bm_cmd_send() |
60 | * to send commands. | 67 | * to send commands. |
61 | * | 68 | * |
62 | * The SDIO devices expects things in 256 byte blocks, so it will pad | 69 | * The SDIO devices expects things in 256 byte blocks, so it will pad |
@@ -64,12 +71,15 @@ | |||
64 | * | 71 | * |
65 | * ACK RECEPTION | 72 | * ACK RECEPTION |
66 | * | 73 | * |
67 | * This works in polling mode -- the fw loader says when to wait for | 74 | * This works in IRQ mode -- the fw loader says when to wait for data |
68 | * data and for that it calls i2400ms_bus_bm_wait_for_ack(). | 75 | * and for that it calls i2400ms_bus_bm_wait_for_ack(). |
69 | * | 76 | * |
70 | * This will poll the device for data until it is received. We need to | 77 | * This checks if there is any data available (RX size > 0); if not, |
71 | * receive at least as much bytes as where asked for (although it'll | 78 | * waits for the IRQ handler to notify about it. Once there is data, |
72 | * always be a multiple of 256 bytes). | 79 | * it is read and passed to the caller. Doing it this way we don't |
80 | * need much coordination/locking, and it makes it much more difficult | ||
81 | * for an interrupt to be lost and the wait_for_ack() function getting | ||
82 | * stuck even when data is pending. | ||
73 | */ | 83 | */ |
74 | #include <linux/mmc/sdio_func.h> | 84 | #include <linux/mmc/sdio_func.h> |
75 | #include "i2400m-sdio.h" | 85 | #include "i2400m-sdio.h" |
@@ -78,6 +88,7 @@ | |||
78 | #define D_SUBMODULE fw | 88 | #define D_SUBMODULE fw |
79 | #include "sdio-debug-levels.h" | 89 | #include "sdio-debug-levels.h" |
80 | 90 | ||
91 | |||
81 | /* | 92 | /* |
82 | * Send a boot-mode command to the SDIO function | 93 | * Send a boot-mode command to the SDIO function |
83 | * | 94 | * |
@@ -139,7 +150,7 @@ error_too_big: | |||
139 | 150 | ||
140 | 151 | ||
141 | /* | 152 | /* |
142 | * Read an ack from the device's boot-mode (polling) | 153 | * Read an ack from the device's boot-mode |
143 | * | 154 | * |
144 | * @i2400m: | 155 | * @i2400m: |
145 | * @_ack: pointer to where to store the read data | 156 | * @_ack: pointer to where to store the read data |
@@ -150,75 +161,49 @@ error_too_big: | |||
150 | * The ACK for a BM command is always at least sizeof(*ack) bytes, so | 161 | * The ACK for a BM command is always at least sizeof(*ack) bytes, so |
151 | * check for that. We don't need to check for device reboots | 162 | * check for that. We don't need to check for device reboots |
152 | * | 163 | * |
153 | * NOTE: We do an artificial timeout of 1 sec over the SDIO timeout; | ||
154 | * this way we have control over it...there is no way that I know | ||
155 | * of setting an SDIO transaction timeout. | ||
156 | */ | 164 | */ |
157 | ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m, | 165 | ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m, |
158 | struct i2400m_bootrom_header *ack, | 166 | struct i2400m_bootrom_header *ack, |
159 | size_t ack_size) | 167 | size_t ack_size) |
160 | { | 168 | { |
161 | int result; | 169 | ssize_t result; |
162 | ssize_t rx_size; | ||
163 | u64 timeout; | ||
164 | struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); | 170 | struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); |
165 | struct sdio_func *func = i2400ms->func; | 171 | struct sdio_func *func = i2400ms->func; |
166 | struct device *dev = &func->dev; | 172 | struct device *dev = &func->dev; |
173 | int size; | ||
167 | 174 | ||
168 | BUG_ON(sizeof(*ack) > ack_size); | 175 | BUG_ON(sizeof(*ack) > ack_size); |
169 | 176 | ||
170 | d_fnstart(5, dev, "(i2400m %p ack %p size %zu)\n", | 177 | d_fnstart(5, dev, "(i2400m %p ack %p size %zu)\n", |
171 | i2400m, ack, ack_size); | 178 | i2400m, ack, ack_size); |
172 | 179 | ||
173 | timeout = get_jiffies_64() + 2 * HZ; | 180 | spin_lock(&i2400m->rx_lock); |
174 | sdio_claim_host(func); | 181 | i2400ms->bm_ack_size = -EINPROGRESS; |
175 | while (1) { | 182 | spin_unlock(&i2400m->rx_lock); |
176 | if (time_after64(get_jiffies_64(), timeout)) { | ||
177 | rx_size = -ETIMEDOUT; | ||
178 | dev_err(dev, "timeout waiting for ack data\n"); | ||
179 | goto error_timedout; | ||
180 | } | ||
181 | 183 | ||
182 | /* Find the RX size, check if it fits or not -- it if | 184 | result = wait_event_timeout(i2400ms->bm_wfa_wq, |
183 | * doesn't fit, fail, as we have no way to dispose of | 185 | i2400ms->bm_ack_size != -EINPROGRESS, |
184 | * the extra data. */ | 186 | 2 * HZ); |
185 | rx_size = __i2400ms_rx_get_size(i2400ms); | 187 | if (result == 0) { |
186 | if (rx_size < 0) | 188 | result = -ETIMEDOUT; |
187 | goto error_rx_get_size; | 189 | dev_err(dev, "BM: error waiting for an ack\n"); |
188 | result = -ENOSPC; /* Check it fits */ | 190 | goto error_timeout; |
189 | if (rx_size < sizeof(*ack)) { | 191 | } |
190 | rx_size = -EIO; | ||
191 | dev_err(dev, "HW BUG? received is too small (%zu vs " | ||
192 | "%zu needed)\n", sizeof(*ack), rx_size); | ||
193 | goto error_too_small; | ||
194 | } | ||
195 | if (rx_size > I2400M_BM_ACK_BUF_SIZE) { | ||
196 | dev_err(dev, "SW BUG? BM_ACK_BUF is too small (%u vs " | ||
197 | "%zu needed)\n", I2400M_BM_ACK_BUF_SIZE, | ||
198 | rx_size); | ||
199 | goto error_too_small; | ||
200 | } | ||
201 | 192 | ||
202 | /* Read it */ | 193 | spin_lock(&i2400m->rx_lock); |
203 | result = sdio_memcpy_fromio(func, i2400m->bm_ack_buf, | 194 | result = i2400ms->bm_ack_size; |
204 | I2400MS_DATA_ADDR, rx_size); | 195 | BUG_ON(result == -EINPROGRESS); |
205 | if (result == -ETIMEDOUT || result == -ETIME) | 196 | if (result < 0) /* so we exit when rx_release() is called */ |
206 | continue; | 197 | dev_err(dev, "BM: %s failed: %zd\n", __func__, result); |
207 | if (result < 0) { | 198 | else { |
208 | dev_err(dev, "BM SDIO receive (%zu B) failed: %d\n", | 199 | size = min(ack_size, i2400ms->bm_ack_size); |
209 | rx_size, result); | 200 | memcpy(ack, i2400m->bm_ack_buf, size); |
210 | goto error_read; | ||
211 | } else | ||
212 | break; | ||
213 | } | 201 | } |
214 | rx_size = min((ssize_t)ack_size, rx_size); | 202 | i2400ms->bm_ack_size = -EINPROGRESS; |
215 | memcpy(ack, i2400m->bm_ack_buf, rx_size); | 203 | spin_unlock(&i2400m->rx_lock); |
216 | error_read: | 204 | |
217 | error_too_small: | 205 | error_timeout: |
218 | error_rx_get_size: | 206 | d_fnend(5, dev, "(i2400m %p ack %p size %zu) = %zd\n", |
219 | error_timedout: | 207 | i2400m, ack, ack_size, result); |
220 | sdio_release_host(func); | 208 | return result; |
221 | d_fnend(5, dev, "(i2400m %p ack %p size %zu) = %ld\n", | ||
222 | i2400m, ack, ack_size, (long) rx_size); | ||
223 | return rx_size; | ||
224 | } | 209 | } |