blob: 32d72a0bd988709e2c5cf171b7f2d5cd3ba6ebbd [file] [log] [blame]
Arend van Spriel5b435de2011-10-05 13:19:03 +02001/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16/* ****************** SDIO CARD Interface Functions **************************/
17
18#include <linux/types.h>
19#include <linux/netdevice.h>
Paul Gortmakeree40fa02011-05-27 16:14:23 -040020#include <linux/export.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020021#include <linux/pci.h>
22#include <linux/pci_ids.h>
23#include <linux/sched.h>
24#include <linux/completion.h>
25#include <linux/mmc/sdio.h>
26#include <linux/mmc/sdio_func.h>
27#include <linux/mmc/card.h>
28
29#include <defs.h>
30#include <brcm_hw_ids.h>
31#include <brcmu_utils.h>
32#include <brcmu_wifi.h>
33#include <soc.h>
34#include "dhd.h"
35#include "dhd_bus.h"
36#include "dhd_dbg.h"
37#include "sdio_host.h"
38
39#define SDIOH_API_ACCESS_RETRY_LIMIT 2
40
41static void brcmf_sdioh_irqhandler(struct sdio_func *func)
42{
Franky Lind76d1c82011-12-08 15:06:37 -080043 struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(&func->card->dev);
Arend van Spriel5b435de2011-10-05 13:19:03 +020044
45 brcmf_dbg(TRACE, "***IRQHandler\n");
46
47 sdio_release_host(func);
48
49 brcmf_sdbrcm_isr(sdiodev->bus);
50
51 sdio_claim_host(func);
52}
53
Franky Linfbf59102011-12-16 18:36:53 -080054/* dummy handler for SDIO function 2 interrupt */
55static void brcmf_sdioh_dummy_irq_handler(struct sdio_func *func)
56{
57}
58
Arend van Spriel5b435de2011-10-05 13:19:03 +020059int brcmf_sdcard_intr_reg(struct brcmf_sdio_dev *sdiodev)
60{
61 brcmf_dbg(TRACE, "Entering\n");
62
63 sdio_claim_host(sdiodev->func[1]);
64 sdio_claim_irq(sdiodev->func[1], brcmf_sdioh_irqhandler);
Franky Linfbf59102011-12-16 18:36:53 -080065 sdio_claim_irq(sdiodev->func[2], brcmf_sdioh_dummy_irq_handler);
Arend van Spriel5b435de2011-10-05 13:19:03 +020066 sdio_release_host(sdiodev->func[1]);
67
68 return 0;
69}
70
71int brcmf_sdcard_intr_dereg(struct brcmf_sdio_dev *sdiodev)
72{
73 brcmf_dbg(TRACE, "Entering\n");
74
75 sdio_claim_host(sdiodev->func[1]);
Franky Linfbf59102011-12-16 18:36:53 -080076 sdio_release_irq(sdiodev->func[2]);
Arend van Spriel5b435de2011-10-05 13:19:03 +020077 sdio_release_irq(sdiodev->func[1]);
78 sdio_release_host(sdiodev->func[1]);
79
80 return 0;
81}
82
83u8 brcmf_sdcard_cfg_read(struct brcmf_sdio_dev *sdiodev, uint fnc_num, u32 addr,
84 int *err)
85{
86 int status;
87 s32 retry = 0;
88 u8 data = 0;
89
90 do {
91 if (retry) /* wait for 1 ms till bus get settled down */
92 udelay(1000);
93 status = brcmf_sdioh_request_byte(sdiodev, SDIOH_READ, fnc_num,
94 addr, (u8 *) &data);
95 } while (status != 0
96 && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT));
97 if (err)
98 *err = status;
99
100 brcmf_dbg(INFO, "fun = %d, addr = 0x%x, u8data = 0x%x\n",
101 fnc_num, addr, data);
102
103 return data;
104}
105
106void
107brcmf_sdcard_cfg_write(struct brcmf_sdio_dev *sdiodev, uint fnc_num, u32 addr,
108 u8 data, int *err)
109{
110 int status;
111 s32 retry = 0;
112
113 do {
114 if (retry) /* wait for 1 ms till bus get settled down */
115 udelay(1000);
116 status = brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, fnc_num,
117 addr, (u8 *) &data);
118 } while (status != 0
119 && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT));
120 if (err)
121 *err = status;
122
123 brcmf_dbg(INFO, "fun = %d, addr = 0x%x, u8data = 0x%x\n",
124 fnc_num, addr, data);
125}
126
127int
128brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address)
129{
130 int err = 0;
131 brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW,
132 (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err);
133 if (!err)
134 brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1,
135 SBSDIO_FUNC1_SBADDRMID,
136 (address >> 16) & SBSDIO_SBADDRMID_MASK,
137 &err);
138 if (!err)
139 brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1,
140 SBSDIO_FUNC1_SBADDRHIGH,
141 (address >> 24) & SBSDIO_SBADDRHIGH_MASK,
142 &err);
143
144 return err;
145}
146
147u32 brcmf_sdcard_reg_read(struct brcmf_sdio_dev *sdiodev, u32 addr, uint size)
148{
149 int status;
150 u32 word = 0;
151 uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
152
153 brcmf_dbg(INFO, "fun = 1, addr = 0x%x\n", addr);
154
155 if (bar0 != sdiodev->sbwad) {
156 if (brcmf_sdcard_set_sbaddr_window(sdiodev, bar0))
157 return 0xFFFFFFFF;
158
159 sdiodev->sbwad = bar0;
160 }
161
162 addr &= SBSDIO_SB_OFT_ADDR_MASK;
163 if (size == 4)
164 addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
165
166 status = brcmf_sdioh_request_word(sdiodev, SDIOH_READ, SDIO_FUNC_1,
167 addr, &word, size);
168
169 sdiodev->regfail = (status != 0);
170
171 brcmf_dbg(INFO, "u32data = 0x%x\n", word);
172
173 /* if ok, return appropriately masked word */
174 if (status == 0) {
175 switch (size) {
176 case sizeof(u8):
177 return word & 0xff;
178 case sizeof(u16):
179 return word & 0xffff;
180 case sizeof(u32):
181 return word;
182 default:
183 sdiodev->regfail = true;
184
185 }
186 }
187
188 /* otherwise, bad sdio access or invalid size */
189 brcmf_dbg(ERROR, "error reading addr 0x%04x size %d\n", addr, size);
190 return 0xFFFFFFFF;
191}
192
193u32 brcmf_sdcard_reg_write(struct brcmf_sdio_dev *sdiodev, u32 addr, uint size,
194 u32 data)
195{
196 int status;
197 uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
198 int err = 0;
199
200 brcmf_dbg(INFO, "fun = 1, addr = 0x%x, uint%ddata = 0x%x\n",
201 addr, size * 8, data);
202
203 if (bar0 != sdiodev->sbwad) {
204 err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0);
205 if (err)
206 return err;
207
208 sdiodev->sbwad = bar0;
209 }
210
211 addr &= SBSDIO_SB_OFT_ADDR_MASK;
212 if (size == 4)
213 addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
214 status =
215 brcmf_sdioh_request_word(sdiodev, SDIOH_WRITE, SDIO_FUNC_1,
216 addr, &data, size);
217 sdiodev->regfail = (status != 0);
218
219 if (status == 0)
220 return 0;
221
222 brcmf_dbg(ERROR, "error writing 0x%08x to addr 0x%04x size %d\n",
223 data, addr, size);
224 return 0xFFFFFFFF;
225}
226
227bool brcmf_sdcard_regfail(struct brcmf_sdio_dev *sdiodev)
228{
229 return sdiodev->regfail;
230}
231
Arend van Spriel5adfeb62011-11-22 17:21:38 -0800232static int brcmf_sdcard_recv_prepare(struct brcmf_sdio_dev *sdiodev, uint fn,
233 uint flags, uint width, u32 *addr)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200234{
Arend van Spriel5adfeb62011-11-22 17:21:38 -0800235 uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200236 int err = 0;
237
Arend van Spriel5b435de2011-10-05 13:19:03 +0200238 /* Async not implemented yet */
239 if (flags & SDIO_REQ_ASYNC)
240 return -ENOTSUPP;
241
242 if (bar0 != sdiodev->sbwad) {
243 err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0);
244 if (err)
245 return err;
246
247 sdiodev->sbwad = bar0;
248 }
249
Arend van Spriel5adfeb62011-11-22 17:21:38 -0800250 *addr &= SBSDIO_SB_OFT_ADDR_MASK;
251
252 if (width == 4)
253 *addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
254
255 return 0;
256}
257
258int
259brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
260 uint flags, u8 *buf, uint nbytes)
261{
262 struct sk_buff *mypkt;
263 int err;
264
265 mypkt = brcmu_pkt_buf_get_skb(nbytes);
266 if (!mypkt) {
267 brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed: len %d\n",
268 nbytes);
269 return -EIO;
270 }
271
272 err = brcmf_sdcard_recv_pkt(sdiodev, addr, fn, flags, mypkt);
273 if (!err)
274 memcpy(buf, mypkt->data, nbytes);
275
276 brcmu_pkt_buf_free_skb(mypkt);
277 return err;
278}
279
280int
281brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
282 uint flags, struct sk_buff *pkt)
283{
284 uint incr_fix;
285 uint width;
286 int err = 0;
287
288 brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n",
289 fn, addr, pkt->len);
290
291 width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
292 err = brcmf_sdcard_recv_prepare(sdiodev, fn, flags, width, &addr);
293 if (err)
294 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200295
296 incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
Arend van Spriel5adfeb62011-11-22 17:21:38 -0800297 err = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_READ,
Arend van Spriel4c6e8692011-11-22 17:21:40 -0800298 fn, addr, pkt);
Arend van Spriel5adfeb62011-11-22 17:21:38 -0800299
300 return err;
301}
302
303int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
304 uint flags, struct sk_buff_head *pktq)
305{
306 uint incr_fix;
307 uint width;
308 int err = 0;
309
310 brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n",
311 fn, addr, pktq->qlen);
312
Arend van Spriel5b435de2011-10-05 13:19:03 +0200313 width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
Arend van Spriel5adfeb62011-11-22 17:21:38 -0800314 err = brcmf_sdcard_recv_prepare(sdiodev, fn, flags, width, &addr);
315 if (err)
316 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200317
Arend van Spriel5adfeb62011-11-22 17:21:38 -0800318 incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
319 err = brcmf_sdioh_request_chain(sdiodev, incr_fix, SDIOH_READ, fn, addr,
320 pktq);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200321
Arend van Spriel5adfeb62011-11-22 17:21:38 -0800322 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200323}
324
325int
326brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
Arend van Spriel5adfeb62011-11-22 17:21:38 -0800327 uint flags, u8 *buf, uint nbytes)
328{
329 struct sk_buff *mypkt;
330 int err;
331
332 mypkt = brcmu_pkt_buf_get_skb(nbytes);
333 if (!mypkt) {
334 brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed: len %d\n",
335 nbytes);
336 return -EIO;
337 }
338
339 memcpy(mypkt->data, buf, nbytes);
340 err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, mypkt);
341
342 brcmu_pkt_buf_free_skb(mypkt);
343 return err;
344
345}
346
347int
348brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
349 uint flags, struct sk_buff *pkt)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200350{
351 uint incr_fix;
352 uint width;
353 uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
354 int err = 0;
355
Arend van Spriel5adfeb62011-11-22 17:21:38 -0800356 brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n",
357 fn, addr, pkt->len);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200358
359 /* Async not implemented yet */
360 if (flags & SDIO_REQ_ASYNC)
361 return -ENOTSUPP;
362
363 if (bar0 != sdiodev->sbwad) {
364 err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0);
365 if (err)
366 return err;
367
368 sdiodev->sbwad = bar0;
369 }
370
371 addr &= SBSDIO_SB_OFT_ADDR_MASK;
372
373 incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
374 width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
375 if (width == 4)
376 addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
377
378 return brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_WRITE, fn,
Arend van Spriel4c6e8692011-11-22 17:21:40 -0800379 addr, pkt);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200380}
381
382int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, u32 addr,
383 u8 *buf, uint nbytes)
384{
Arend van Spriel4c6e8692011-11-22 17:21:40 -0800385 struct sk_buff *mypkt;
386 bool write = rw ? SDIOH_WRITE : SDIOH_READ;
387 int err;
388
Arend van Spriel5b435de2011-10-05 13:19:03 +0200389 addr &= SBSDIO_SB_OFT_ADDR_MASK;
390 addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
391
Arend van Spriel4c6e8692011-11-22 17:21:40 -0800392 mypkt = brcmu_pkt_buf_get_skb(nbytes);
393 if (!mypkt) {
394 brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed: len %d\n",
395 nbytes);
396 return -EIO;
397 }
398
399 /* For a write, copy the buffer data into the packet. */
400 if (write)
401 memcpy(mypkt->data, buf, nbytes);
402
403 err = brcmf_sdioh_request_buffer(sdiodev, SDIOH_DATA_INC, write,
404 SDIO_FUNC_1, addr, mypkt);
405
406 /* For a read, copy the packet data back to the buffer. */
407 if (!err && !write)
408 memcpy(buf, mypkt->data, nbytes);
409
410 brcmu_pkt_buf_free_skb(mypkt);
411 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200412}
413
414int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn)
415{
416 char t_func = (char)fn;
417 brcmf_dbg(TRACE, "Enter\n");
418
419 /* issue abort cmd52 command through F0 */
420 brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0,
421 SDIO_CCCR_ABORT, &t_func);
422
423 brcmf_dbg(TRACE, "Exit\n");
424 return 0;
425}
426
427int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
428{
429 u32 regs = 0;
430 int ret = 0;
431
432 ret = brcmf_sdioh_attach(sdiodev);
433 if (ret)
434 goto out;
435
436 regs = SI_ENUM_BASE;
437
438 /* Report the BAR, to fix if needed */
439 sdiodev->sbwad = SI_ENUM_BASE;
440
441 /* try to attach to the target device */
Franky Lin4175b882011-11-22 17:21:49 -0800442 sdiodev->bus = brcmf_sdbrcm_probe(regs, sdiodev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200443 if (!sdiodev->bus) {
444 brcmf_dbg(ERROR, "device attach failed\n");
445 ret = -ENODEV;
446 goto out;
447 }
448
449out:
450 if (ret)
451 brcmf_sdio_remove(sdiodev);
452
453 return ret;
454}
455EXPORT_SYMBOL(brcmf_sdio_probe);
456
457int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev)
458{
459 if (sdiodev->bus) {
460 brcmf_sdbrcm_disconnect(sdiodev->bus);
461 sdiodev->bus = NULL;
462 }
463
464 brcmf_sdioh_detach(sdiodev);
465
466 sdiodev->sbwad = 0;
467
468 return 0;
469}
470EXPORT_SYMBOL(brcmf_sdio_remove);
471
472void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, bool enable)
473{
474 if (enable)
475 brcmf_sdbrcm_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS);
476 else
477 brcmf_sdbrcm_wd_timer(sdiodev->bus, 0);
478}