]>
Commit | Line | Data |
---|---|---|
514ec71f IPG |
1 | /* |
2 | * Intel Wireless WiMAX Connection 2400m | |
3 | * SDIO RX handling | |
4 | * | |
5 | * | |
6 | * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * | |
12 | * * Redistributions of source code must retain the above copyright | |
13 | * notice, this list of conditions and the following disclaimer. | |
14 | * * Redistributions in binary form must reproduce the above copyright | |
15 | * notice, this list of conditions and the following disclaimer in | |
16 | * the documentation and/or other materials provided with the | |
17 | * distribution. | |
18 | * * Neither the name of Intel Corporation nor the names of its | |
19 | * contributors may be used to endorse or promote products derived | |
20 | * from this software without specific prior written permission. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
26 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
27 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
28 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
33 | * | |
34 | * | |
35 | * Intel Corporation <linux-wimax@intel.com> | |
36 | * Dirk Brandewie <dirk.j.brandewie@intel.com> | |
37 | * - Initial implementation | |
38 | * | |
39 | * | |
40 | * This handles the RX path on SDIO. | |
41 | * | |
42 | * The SDIO bus driver calls the "irq" routine when data is available. | |
43 | * This is not a traditional interrupt routine since the SDIO bus | |
44 | * driver calls us from its irq thread context. Because of this | |
45 | * sleeping in the SDIO RX IRQ routine is okay. | |
46 | * | |
47 | * From there on, we obtain the size of the data that is available, | |
48 | * allocate an skb, copy it and then pass it to the generic driver's | |
49 | * RX routine [i2400m_rx()]. | |
50 | * | |
51 | * ROADMAP | |
52 | * | |
53 | * i2400ms_irq() | |
54 | * i2400ms_rx() | |
55 | * __i2400ms_rx_get_size() | |
aba3792a | 56 | * i2400m_is_boot_barker() |
514ec71f IPG |
57 | * i2400m_rx() |
58 | * | |
59 | * i2400ms_rx_setup() | |
60 | * | |
61 | * i2400ms_rx_release() | |
62 | */ | |
63 | #include <linux/workqueue.h> | |
64 | #include <linux/wait.h> | |
65 | #include <linux/skbuff.h> | |
66 | #include <linux/mmc/sdio.h> | |
67 | #include <linux/mmc/sdio_func.h> | |
5a0e3ad6 | 68 | #include <linux/slab.h> |
514ec71f IPG |
69 | #include "i2400m-sdio.h" |
70 | ||
71 | #define D_SUBMODULE rx | |
72 | #include "sdio-debug-levels.h" | |
73 | ||
16820c16 IPG |
74 | static const __le32 i2400m_ACK_BARKER[4] = { |
75 | __constant_cpu_to_le32(I2400M_ACK_BARKER), | |
76 | __constant_cpu_to_le32(I2400M_ACK_BARKER), | |
77 | __constant_cpu_to_le32(I2400M_ACK_BARKER), | |
78 | __constant_cpu_to_le32(I2400M_ACK_BARKER) | |
79 | }; | |
80 | ||
514ec71f IPG |
81 | |
82 | /* | |
83 | * Read and return the amount of bytes available for RX | |
84 | * | |
85 | * The RX size has to be read like this: byte reads of three | |
86 | * sequential locations; then glue'em together. | |
87 | * | |
88 | * sdio_readl() doesn't work. | |
89 | */ | |
90 | ssize_t __i2400ms_rx_get_size(struct i2400ms *i2400ms) | |
91 | { | |
92 | int ret, cnt, val; | |
93 | ssize_t rx_size; | |
94 | unsigned xfer_size_addr; | |
95 | struct sdio_func *func = i2400ms->func; | |
96 | struct device *dev = &i2400ms->func->dev; | |
97 | ||
98 | d_fnstart(7, dev, "(i2400ms %p)\n", i2400ms); | |
99 | xfer_size_addr = I2400MS_INTR_GET_SIZE_ADDR; | |
100 | rx_size = 0; | |
101 | for (cnt = 0; cnt < 3; cnt++) { | |
102 | val = sdio_readb(func, xfer_size_addr + cnt, &ret); | |
103 | if (ret < 0) { | |
104 | dev_err(dev, "RX: Can't read byte %d of RX size from " | |
105 | "0x%08x: %d\n", cnt, xfer_size_addr + cnt, ret); | |
106 | rx_size = ret; | |
107 | goto error_read; | |
108 | } | |
109 | rx_size = rx_size << 8 | (val & 0xff); | |
110 | } | |
111 | d_printf(6, dev, "RX: rx_size is %ld\n", (long) rx_size); | |
112 | error_read: | |
113 | d_fnend(7, dev, "(i2400ms %p) = %ld\n", i2400ms, (long) rx_size); | |
114 | return rx_size; | |
115 | } | |
116 | ||
117 | ||
118 | /* | |
119 | * Read data from the device (when in normal) | |
120 | * | |
121 | * Allocate an SKB of the right size, read the data in and then | |
122 | * deliver it to the generic layer. | |
123 | * | |
124 | * We also check for a reboot barker. That means the device died and | |
125 | * we have to reboot it. | |
126 | */ | |
127 | static | |
128 | void i2400ms_rx(struct i2400ms *i2400ms) | |
129 | { | |
130 | int ret; | |
131 | struct sdio_func *func = i2400ms->func; | |
132 | struct device *dev = &func->dev; | |
133 | struct i2400m *i2400m = &i2400ms->i2400m; | |
134 | struct sk_buff *skb; | |
135 | ssize_t rx_size; | |
136 | ||
137 | d_fnstart(7, dev, "(i2400ms %p)\n", i2400ms); | |
138 | rx_size = __i2400ms_rx_get_size(i2400ms); | |
139 | if (rx_size < 0) { | |
140 | ret = rx_size; | |
141 | goto error_get_size; | |
142 | } | |
339ccc36 CK |
143 | /* |
144 | * Hardware quirk: make sure to clear the INTR status register | |
145 | * AFTER getting the data transfer size. | |
146 | */ | |
147 | sdio_writeb(func, 1, I2400MS_INTR_CLEAR_ADDR, &ret); | |
16820c16 | 148 | |
514ec71f IPG |
149 | ret = -ENOMEM; |
150 | skb = alloc_skb(rx_size, GFP_ATOMIC); | |
151 | if (NULL == skb) { | |
152 | dev_err(dev, "RX: unable to alloc skb\n"); | |
153 | goto error_alloc_skb; | |
154 | } | |
514ec71f IPG |
155 | ret = sdio_memcpy_fromio(func, skb->data, |
156 | I2400MS_DATA_ADDR, rx_size); | |
157 | if (ret < 0) { | |
158 | dev_err(dev, "RX: SDIO data read failed: %d\n", ret); | |
159 | goto error_memcpy_fromio; | |
160 | } | |
16820c16 IPG |
161 | |
162 | rmb(); /* make sure we get boot_mode from dev_reset_handle */ | |
aba3792a | 163 | if (unlikely(i2400m->boot_mode == 1)) { |
16820c16 IPG |
164 | spin_lock(&i2400m->rx_lock); |
165 | i2400ms->bm_ack_size = rx_size; | |
166 | spin_unlock(&i2400m->rx_lock); | |
167 | memcpy(i2400m->bm_ack_buf, skb->data, rx_size); | |
168 | wake_up(&i2400ms->bm_wfa_wq); | |
e1633fd6 | 169 | d_printf(5, dev, "RX: SDIO boot mode message\n"); |
16820c16 | 170 | kfree_skb(skb); |
aba3792a IPG |
171 | goto out; |
172 | } | |
173 | ret = -EIO; | |
174 | if (unlikely(rx_size < sizeof(__le32))) { | |
175 | dev_err(dev, "HW BUG? only %zu bytes received\n", rx_size); | |
176 | goto error_bad_size; | |
177 | } | |
178 | if (likely(i2400m_is_d2h_barker(skb->data))) { | |
179 | skb_put(skb, rx_size); | |
180 | i2400m_rx(i2400m, skb); | |
181 | } else if (unlikely(i2400m_is_boot_barker(i2400m, | |
182 | skb->data, rx_size))) { | |
3ef6129e | 183 | ret = i2400m_dev_reset_handle(i2400m, "device rebooted"); |
16820c16 | 184 | dev_err(dev, "RX: SDIO reboot barker\n"); |
514ec71f IPG |
185 | kfree_skb(skb); |
186 | } else { | |
aba3792a IPG |
187 | i2400m_unknown_barker(i2400m, skb->data, rx_size); |
188 | kfree_skb(skb); | |
514ec71f | 189 | } |
aba3792a | 190 | out: |
514ec71f IPG |
191 | d_fnend(7, dev, "(i2400ms %p) = void\n", i2400ms); |
192 | return; | |
193 | ||
194 | error_memcpy_fromio: | |
195 | kfree_skb(skb); | |
196 | error_alloc_skb: | |
197 | error_get_size: | |
aba3792a | 198 | error_bad_size: |
514ec71f IPG |
199 | d_fnend(7, dev, "(i2400ms %p) = %d\n", i2400ms, ret); |
200 | return; | |
201 | } | |
202 | ||
203 | ||
204 | /* | |
205 | * Process an interrupt from the SDIO card | |
206 | * | |
207 | * FIXME: need to process other events that are not just ready-to-read | |
208 | * | |
209 | * Checks there is data ready and then proceeds to read it. | |
210 | */ | |
211 | static | |
212 | void i2400ms_irq(struct sdio_func *func) | |
213 | { | |
214 | int ret; | |
215 | struct i2400ms *i2400ms = sdio_get_drvdata(func); | |
514ec71f IPG |
216 | struct device *dev = &func->dev; |
217 | int val; | |
218 | ||
219 | d_fnstart(6, dev, "(i2400ms %p)\n", i2400ms); | |
220 | val = sdio_readb(func, I2400MS_INTR_STATUS_ADDR, &ret); | |
221 | if (ret < 0) { | |
222 | dev_err(dev, "RX: Can't read interrupt status: %d\n", ret); | |
223 | goto error_no_irq; | |
224 | } | |
225 | if (!val) { | |
226 | dev_err(dev, "RX: BUG? got IRQ but no interrupt ready?\n"); | |
227 | goto error_no_irq; | |
228 | } | |
16820c16 | 229 | i2400ms_rx(i2400ms); |
514ec71f IPG |
230 | error_no_irq: |
231 | d_fnend(6, dev, "(i2400ms %p) = void\n", i2400ms); | |
232 | return; | |
233 | } | |
234 | ||
235 | ||
236 | /* | |
237 | * Setup SDIO RX | |
238 | * | |
239 | * Hooks up the IRQ handler and then enables IRQs. | |
240 | */ | |
241 | int i2400ms_rx_setup(struct i2400ms *i2400ms) | |
242 | { | |
243 | int result; | |
244 | struct sdio_func *func = i2400ms->func; | |
245 | struct device *dev = &func->dev; | |
16820c16 | 246 | struct i2400m *i2400m = &i2400ms->i2400m; |
514ec71f IPG |
247 | |
248 | d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms); | |
16820c16 IPG |
249 | |
250 | init_waitqueue_head(&i2400ms->bm_wfa_wq); | |
251 | spin_lock(&i2400m->rx_lock); | |
252 | i2400ms->bm_wait_result = -EINPROGRESS; | |
5b45bfe5 CK |
253 | /* |
254 | * Before we are about to enable the RX interrupt, make sure | |
255 | * bm_ack_size is cleared to -EINPROGRESS which indicates | |
256 | * no RX interrupt happened yet or the previous interrupt | |
257 | * has been handled, we are ready to take the new interrupt | |
258 | */ | |
259 | i2400ms->bm_ack_size = -EINPROGRESS; | |
16820c16 IPG |
260 | spin_unlock(&i2400m->rx_lock); |
261 | ||
514ec71f IPG |
262 | sdio_claim_host(func); |
263 | result = sdio_claim_irq(func, i2400ms_irq); | |
264 | if (result < 0) { | |
265 | dev_err(dev, "Cannot claim IRQ: %d\n", result); | |
266 | goto error_irq_claim; | |
267 | } | |
268 | result = 0; | |
269 | sdio_writeb(func, 1, I2400MS_INTR_ENABLE_ADDR, &result); | |
270 | if (result < 0) { | |
271 | sdio_release_irq(func); | |
272 | dev_err(dev, "Failed to enable interrupts %d\n", result); | |
273 | } | |
274 | error_irq_claim: | |
275 | sdio_release_host(func); | |
276 | d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result); | |
277 | return result; | |
278 | } | |
279 | ||
280 | ||
281 | /* | |
282 | * Tear down SDIO RX | |
283 | * | |
284 | * Disables IRQs in the device and removes the IRQ handler. | |
285 | */ | |
286 | void i2400ms_rx_release(struct i2400ms *i2400ms) | |
287 | { | |
288 | int result; | |
289 | struct sdio_func *func = i2400ms->func; | |
290 | struct device *dev = &func->dev; | |
16820c16 | 291 | struct i2400m *i2400m = &i2400ms->i2400m; |
514ec71f IPG |
292 | |
293 | d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms); | |
16820c16 IPG |
294 | spin_lock(&i2400m->rx_lock); |
295 | i2400ms->bm_ack_size = -EINTR; | |
296 | spin_unlock(&i2400m->rx_lock); | |
297 | wake_up_all(&i2400ms->bm_wfa_wq); | |
514ec71f IPG |
298 | sdio_claim_host(func); |
299 | sdio_writeb(func, 0, I2400MS_INTR_ENABLE_ADDR, &result); | |
300 | sdio_release_irq(func); | |
301 | sdio_release_host(func); | |
302 | d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result); | |
303 | } |