aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wimax/i2400m/sdio-rx.c
diff options
context:
space:
mode:
authorInaky Perez-Gonzalez <inaky@linux.intel.com>2008-12-20 19:57:57 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2009-01-07 13:00:22 -0500
commit514ec71f7289c942f801bdbd309428c470bfc071 (patch)
tree854d76ed2b8e9eebc8d2eff0688790cce88df58b /drivers/net/wimax/i2400m/sdio-rx.c
parent020bb6f30b636d563d4268116107d592550ddd0c (diff)
i2400m/SDIO: TX and RX path backends
Implements the backend so that the generic driver can TX/RX to/from the SDIO device. For RX, when data is ready the SDIO IRQ is fired and that will allocate an skb, put all the data there and then pass it to the generic driver RX code for processing and delivery. TX, when kicked by the generic driver, will schedule work on a driver-specific workqueue that pulls data from the TX FIFO and sends it to the device until it drains it. Thread contexts are needed as SDIO functions are blocking. Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/net/wimax/i2400m/sdio-rx.c')
-rw-r--r--drivers/net/wimax/i2400m/sdio-rx.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/drivers/net/wimax/i2400m/sdio-rx.c b/drivers/net/wimax/i2400m/sdio-rx.c
new file mode 100644
index 000000000000..a3008b904f7d
--- /dev/null
+++ b/drivers/net/wimax/i2400m/sdio-rx.c
@@ -0,0 +1,255 @@
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
72
73/*
74 * Read and return the amount of bytes available for RX
75 *
76 * The RX size has to be read like this: byte reads of three
77 * sequential locations; then glue'em together.
78 *
79 * sdio_readl() doesn't work.
80 */
81ssize_t __i2400ms_rx_get_size(struct i2400ms *i2400ms)
82{
83 int ret, cnt, val;
84 ssize_t rx_size;
85 unsigned xfer_size_addr;
86 struct sdio_func *func = i2400ms->func;
87 struct device *dev = &i2400ms->func->dev;
88
89 d_fnstart(7, dev, "(i2400ms %p)\n", i2400ms);
90 xfer_size_addr = I2400MS_INTR_GET_SIZE_ADDR;
91 rx_size = 0;
92 for (cnt = 0; cnt < 3; cnt++) {
93 val = sdio_readb(func, xfer_size_addr + cnt, &ret);
94 if (ret < 0) {
95 dev_err(dev, "RX: Can't read byte %d of RX size from "
96 "0x%08x: %d\n", cnt, xfer_size_addr + cnt, ret);
97 rx_size = ret;
98 goto error_read;
99 }
100 rx_size = rx_size << 8 | (val & 0xff);
101 }
102 d_printf(6, dev, "RX: rx_size is %ld\n", (long) rx_size);
103error_read:
104 d_fnend(7, dev, "(i2400ms %p) = %ld\n", i2400ms, (long) rx_size);
105 return rx_size;
106}
107
108
109/*
110 * Read data from the device (when in normal)
111 *
112 * Allocate an SKB of the right size, read the data in and then
113 * deliver it to the generic layer.
114 *
115 * We also check for a reboot barker. That means the device died and
116 * we have to reboot it.
117 */
118static
119void i2400ms_rx(struct i2400ms *i2400ms)
120{
121 int ret;
122 struct sdio_func *func = i2400ms->func;
123 struct device *dev = &func->dev;
124 struct i2400m *i2400m = &i2400ms->i2400m;
125 struct sk_buff *skb;
126 ssize_t rx_size;
127
128 d_fnstart(7, dev, "(i2400ms %p)\n", i2400ms);
129 rx_size = __i2400ms_rx_get_size(i2400ms);
130 if (rx_size < 0) {
131 ret = rx_size;
132 goto error_get_size;
133 }
134 ret = -ENOMEM;
135 skb = alloc_skb(rx_size, GFP_ATOMIC);
136 if (NULL == skb) {
137 dev_err(dev, "RX: unable to alloc skb\n");
138 goto error_alloc_skb;
139 }
140
141 ret = sdio_memcpy_fromio(func, skb->data,
142 I2400MS_DATA_ADDR, rx_size);
143 if (ret < 0) {
144 dev_err(dev, "RX: SDIO data read failed: %d\n", ret);
145 goto error_memcpy_fromio;
146 }
147 /* Check if device has reset */
148 if (!memcmp(skb->data, i2400m_NBOOT_BARKER,
149 sizeof(i2400m_NBOOT_BARKER))
150 || !memcmp(skb->data, i2400m_SBOOT_BARKER,
151 sizeof(i2400m_SBOOT_BARKER))) {
152 ret = i2400m_dev_reset_handle(i2400m);
153 kfree_skb(skb);
154 } else {
155 skb_put(skb, rx_size);
156 i2400m_rx(i2400m, skb);
157 }
158 d_fnend(7, dev, "(i2400ms %p) = void\n", i2400ms);
159 return;
160
161error_memcpy_fromio:
162 kfree_skb(skb);
163error_alloc_skb:
164error_get_size:
165 d_fnend(7, dev, "(i2400ms %p) = %d\n", i2400ms, ret);
166 return;
167}
168
169
170/*
171 * Process an interrupt from the SDIO card
172 *
173 * FIXME: need to process other events that are not just ready-to-read
174 *
175 * Checks there is data ready and then proceeds to read it.
176 */
177static
178void i2400ms_irq(struct sdio_func *func)
179{
180 int ret;
181 struct i2400ms *i2400ms = sdio_get_drvdata(func);
182 struct i2400m *i2400m = &i2400ms->i2400m;
183 struct device *dev = &func->dev;
184 int val;
185
186 d_fnstart(6, dev, "(i2400ms %p)\n", i2400ms);
187 val = sdio_readb(func, I2400MS_INTR_STATUS_ADDR, &ret);
188 if (ret < 0) {
189 dev_err(dev, "RX: Can't read interrupt status: %d\n", ret);
190 goto error_no_irq;
191 }
192 if (!val) {
193 dev_err(dev, "RX: BUG? got IRQ but no interrupt ready?\n");
194 goto error_no_irq;
195 }
196 sdio_writeb(func, 1, I2400MS_INTR_CLEAR_ADDR, &ret);
197 if (WARN_ON(i2400m->boot_mode != 0))
198 dev_err(dev, "RX: SW BUG? boot mode and IRQ is up?\n");
199 else
200 i2400ms_rx(i2400ms);
201error_no_irq:
202 d_fnend(6, dev, "(i2400ms %p) = void\n", i2400ms);
203 return;
204}
205
206
207/*
208 * Setup SDIO RX
209 *
210 * Hooks up the IRQ handler and then enables IRQs.
211 */
212int i2400ms_rx_setup(struct i2400ms *i2400ms)
213{
214 int result;
215 struct sdio_func *func = i2400ms->func;
216 struct device *dev = &func->dev;
217
218 d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms);
219 sdio_claim_host(func);
220 result = sdio_claim_irq(func, i2400ms_irq);
221 if (result < 0) {
222 dev_err(dev, "Cannot claim IRQ: %d\n", result);
223 goto error_irq_claim;
224 }
225 result = 0;
226 sdio_writeb(func, 1, I2400MS_INTR_ENABLE_ADDR, &result);
227 if (result < 0) {
228 sdio_release_irq(func);
229 dev_err(dev, "Failed to enable interrupts %d\n", result);
230 }
231error_irq_claim:
232 sdio_release_host(func);
233 d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result);
234 return result;
235}
236
237
238/*
239 * Tear down SDIO RX
240 *
241 * Disables IRQs in the device and removes the IRQ handler.
242 */
243void i2400ms_rx_release(struct i2400ms *i2400ms)
244{
245 int result;
246 struct sdio_func *func = i2400ms->func;
247 struct device *dev = &func->dev;
248
249 d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms);
250 sdio_claim_host(func);
251 sdio_writeb(func, 0, I2400MS_INTR_ENABLE_ADDR, &result);
252 sdio_release_irq(func);
253 sdio_release_host(func);
254 d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result);
255}