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() | |
56 | * i2400m_rx() | |
57 | * | |
58 | * i2400ms_rx_setup() | |
59 | * | |
60 | * i2400ms_rx_release() | |
61 | */ | |
62 | #include <linux/workqueue.h> | |
63 | #include <linux/wait.h> | |
64 | #include <linux/skbuff.h> | |
65 | #include <linux/mmc/sdio.h> | |
66 | #include <linux/mmc/sdio_func.h> | |
67 | #include "i2400m-sdio.h" | |
68 | ||
69 | #define D_SUBMODULE rx | |
70 | #include "sdio-debug-levels.h" | |
71 | ||
16820c16 IPG |
72 | static const __le32 i2400m_ACK_BARKER[4] = { |
73 | __constant_cpu_to_le32(I2400M_ACK_BARKER), | |
74 | __constant_cpu_to_le32(I2400M_ACK_BARKER), | |
75 | __constant_cpu_to_le32(I2400M_ACK_BARKER), | |
76 | __constant_cpu_to_le32(I2400M_ACK_BARKER) | |
77 | }; | |
78 | ||
514ec71f IPG |
79 | |
80 | /* | |
81 | * Read and return the amount of bytes available for RX | |
82 | * | |
83 | * The RX size has to be read like this: byte reads of three | |
84 | * sequential locations; then glue'em together. | |
85 | * | |
86 | * sdio_readl() doesn't work. | |
87 | */ | |
88 | ssize_t __i2400ms_rx_get_size(struct i2400ms *i2400ms) | |
89 | { | |
90 | int ret, cnt, val; | |
91 | ssize_t rx_size; | |
92 | unsigned xfer_size_addr; | |
93 | struct sdio_func *func = i2400ms->func; | |
94 | struct device *dev = &i2400ms->func->dev; | |
95 | ||
96 | d_fnstart(7, dev, "(i2400ms %p)\n", i2400ms); | |
97 | xfer_size_addr = I2400MS_INTR_GET_SIZE_ADDR; | |
98 | rx_size = 0; | |
99 | for (cnt = 0; cnt < 3; cnt++) { | |
100 | val = sdio_readb(func, xfer_size_addr + cnt, &ret); | |
101 | if (ret < 0) { | |
102 | dev_err(dev, "RX: Can't read byte %d of RX size from " | |
103 | "0x%08x: %d\n", cnt, xfer_size_addr + cnt, ret); | |
104 | rx_size = ret; | |
105 | goto error_read; | |
106 | } | |
107 | rx_size = rx_size << 8 | (val & 0xff); | |
108 | } | |
109 | d_printf(6, dev, "RX: rx_size is %ld\n", (long) rx_size); | |
110 | error_read: | |
111 | d_fnend(7, dev, "(i2400ms %p) = %ld\n", i2400ms, (long) rx_size); | |
112 | return rx_size; | |
113 | } | |
114 | ||
115 | ||
116 | /* | |
117 | * Read data from the device (when in normal) | |
118 | * | |
119 | * Allocate an SKB of the right size, read the data in and then | |
120 | * deliver it to the generic layer. | |
121 | * | |
122 | * We also check for a reboot barker. That means the device died and | |
123 | * we have to reboot it. | |
124 | */ | |
125 | static | |
126 | void i2400ms_rx(struct i2400ms *i2400ms) | |
127 | { | |
128 | int ret; | |
129 | struct sdio_func *func = i2400ms->func; | |
130 | struct device *dev = &func->dev; | |
131 | struct i2400m *i2400m = &i2400ms->i2400m; | |
132 | struct sk_buff *skb; | |
133 | ssize_t rx_size; | |
134 | ||
135 | d_fnstart(7, dev, "(i2400ms %p)\n", i2400ms); | |
136 | rx_size = __i2400ms_rx_get_size(i2400ms); | |
137 | if (rx_size < 0) { | |
138 | ret = rx_size; | |
139 | goto error_get_size; | |
140 | } | |
16820c16 | 141 | |
514ec71f IPG |
142 | ret = -ENOMEM; |
143 | skb = alloc_skb(rx_size, GFP_ATOMIC); | |
144 | if (NULL == skb) { | |
145 | dev_err(dev, "RX: unable to alloc skb\n"); | |
146 | goto error_alloc_skb; | |
147 | } | |
514ec71f IPG |
148 | ret = sdio_memcpy_fromio(func, skb->data, |
149 | I2400MS_DATA_ADDR, rx_size); | |
150 | if (ret < 0) { | |
151 | dev_err(dev, "RX: SDIO data read failed: %d\n", ret); | |
152 | goto error_memcpy_fromio; | |
153 | } | |
16820c16 IPG |
154 | |
155 | rmb(); /* make sure we get boot_mode from dev_reset_handle */ | |
156 | if (i2400m->boot_mode == 1) { | |
157 | spin_lock(&i2400m->rx_lock); | |
158 | i2400ms->bm_ack_size = rx_size; | |
159 | spin_unlock(&i2400m->rx_lock); | |
160 | memcpy(i2400m->bm_ack_buf, skb->data, rx_size); | |
161 | wake_up(&i2400ms->bm_wfa_wq); | |
162 | dev_err(dev, "RX: SDIO boot mode message\n"); | |
163 | kfree_skb(skb); | |
164 | } else if (unlikely(!memcmp(skb->data, i2400m_NBOOT_BARKER, | |
165 | sizeof(i2400m_NBOOT_BARKER)) | |
166 | || !memcmp(skb->data, i2400m_SBOOT_BARKER, | |
167 | sizeof(i2400m_SBOOT_BARKER)))) { | |
514ec71f | 168 | ret = i2400m_dev_reset_handle(i2400m); |
16820c16 | 169 | dev_err(dev, "RX: SDIO reboot barker\n"); |
514ec71f IPG |
170 | kfree_skb(skb); |
171 | } else { | |
172 | skb_put(skb, rx_size); | |
173 | i2400m_rx(i2400m, skb); | |
174 | } | |
175 | d_fnend(7, dev, "(i2400ms %p) = void\n", i2400ms); | |
176 | return; | |
177 | ||
178 | error_memcpy_fromio: | |
179 | kfree_skb(skb); | |
180 | error_alloc_skb: | |
181 | error_get_size: | |
182 | d_fnend(7, dev, "(i2400ms %p) = %d\n", i2400ms, ret); | |
183 | return; | |
184 | } | |
185 | ||
186 | ||
187 | /* | |
188 | * Process an interrupt from the SDIO card | |
189 | * | |
190 | * FIXME: need to process other events that are not just ready-to-read | |
191 | * | |
192 | * Checks there is data ready and then proceeds to read it. | |
193 | */ | |
194 | static | |
195 | void i2400ms_irq(struct sdio_func *func) | |
196 | { | |
197 | int ret; | |
198 | struct i2400ms *i2400ms = sdio_get_drvdata(func); | |
514ec71f IPG |
199 | struct device *dev = &func->dev; |
200 | int val; | |
201 | ||
202 | d_fnstart(6, dev, "(i2400ms %p)\n", i2400ms); | |
203 | val = sdio_readb(func, I2400MS_INTR_STATUS_ADDR, &ret); | |
204 | if (ret < 0) { | |
205 | dev_err(dev, "RX: Can't read interrupt status: %d\n", ret); | |
206 | goto error_no_irq; | |
207 | } | |
208 | if (!val) { | |
209 | dev_err(dev, "RX: BUG? got IRQ but no interrupt ready?\n"); | |
210 | goto error_no_irq; | |
211 | } | |
212 | sdio_writeb(func, 1, I2400MS_INTR_CLEAR_ADDR, &ret); | |
16820c16 | 213 | i2400ms_rx(i2400ms); |
514ec71f IPG |
214 | error_no_irq: |
215 | d_fnend(6, dev, "(i2400ms %p) = void\n", i2400ms); | |
216 | return; | |
217 | } | |
218 | ||
219 | ||
220 | /* | |
221 | * Setup SDIO RX | |
222 | * | |
223 | * Hooks up the IRQ handler and then enables IRQs. | |
224 | */ | |
225 | int i2400ms_rx_setup(struct i2400ms *i2400ms) | |
226 | { | |
227 | int result; | |
228 | struct sdio_func *func = i2400ms->func; | |
229 | struct device *dev = &func->dev; | |
16820c16 | 230 | struct i2400m *i2400m = &i2400ms->i2400m; |
514ec71f IPG |
231 | |
232 | d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms); | |
16820c16 IPG |
233 | |
234 | init_waitqueue_head(&i2400ms->bm_wfa_wq); | |
235 | spin_lock(&i2400m->rx_lock); | |
236 | i2400ms->bm_wait_result = -EINPROGRESS; | |
237 | spin_unlock(&i2400m->rx_lock); | |
238 | ||
514ec71f IPG |
239 | sdio_claim_host(func); |
240 | result = sdio_claim_irq(func, i2400ms_irq); | |
241 | if (result < 0) { | |
242 | dev_err(dev, "Cannot claim IRQ: %d\n", result); | |
243 | goto error_irq_claim; | |
244 | } | |
245 | result = 0; | |
246 | sdio_writeb(func, 1, I2400MS_INTR_ENABLE_ADDR, &result); | |
247 | if (result < 0) { | |
248 | sdio_release_irq(func); | |
249 | dev_err(dev, "Failed to enable interrupts %d\n", result); | |
250 | } | |
251 | error_irq_claim: | |
252 | sdio_release_host(func); | |
253 | d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result); | |
254 | return result; | |
255 | } | |
256 | ||
257 | ||
258 | /* | |
259 | * Tear down SDIO RX | |
260 | * | |
261 | * Disables IRQs in the device and removes the IRQ handler. | |
262 | */ | |
263 | void i2400ms_rx_release(struct i2400ms *i2400ms) | |
264 | { | |
265 | int result; | |
266 | struct sdio_func *func = i2400ms->func; | |
267 | struct device *dev = &func->dev; | |
16820c16 | 268 | struct i2400m *i2400m = &i2400ms->i2400m; |
514ec71f IPG |
269 | |
270 | d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms); | |
16820c16 IPG |
271 | spin_lock(&i2400m->rx_lock); |
272 | i2400ms->bm_ack_size = -EINTR; | |
273 | spin_unlock(&i2400m->rx_lock); | |
274 | wake_up_all(&i2400ms->bm_wfa_wq); | |
514ec71f IPG |
275 | sdio_claim_host(func); |
276 | sdio_writeb(func, 0, I2400MS_INTR_ENABLE_ADDR, &result); | |
277 | sdio_release_irq(func); | |
278 | sdio_release_host(func); | |
279 | d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result); | |
280 | } |