Commit | Line | Data |
---|---|---|
5b435de0 AS |
1 | /* |
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> | |
ee40fa06 | 20 | #include <linux/export.h> |
5b435de0 AS |
21 | #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 | ||
41 | static void brcmf_sdioh_irqhandler(struct sdio_func *func) | |
42 | { | |
d76d1c8c | 43 | struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(&func->card->dev); |
5b435de0 AS |
44 | |
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 | ||
54 | int brcmf_sdcard_intr_reg(struct brcmf_sdio_dev *sdiodev) | |
55 | { | |
56 | brcmf_dbg(TRACE, "Entering\n"); | |
57 | ||
58 | sdio_claim_host(sdiodev->func[1]); | |
59 | sdio_claim_irq(sdiodev->func[1], brcmf_sdioh_irqhandler); | |
60 | sdio_release_host(sdiodev->func[1]); | |
61 | ||
62 | return 0; | |
63 | } | |
64 | ||
65 | int brcmf_sdcard_intr_dereg(struct brcmf_sdio_dev *sdiodev) | |
66 | { | |
67 | brcmf_dbg(TRACE, "Entering\n"); | |
68 | ||
69 | sdio_claim_host(sdiodev->func[1]); | |
70 | sdio_release_irq(sdiodev->func[1]); | |
71 | sdio_release_host(sdiodev->func[1]); | |
72 | ||
73 | return 0; | |
74 | } | |
75 | ||
76 | u8 brcmf_sdcard_cfg_read(struct brcmf_sdio_dev *sdiodev, uint fnc_num, u32 addr, | |
77 | int *err) | |
78 | { | |
79 | int status; | |
80 | s32 retry = 0; | |
81 | u8 data = 0; | |
82 | ||
83 | do { | |
84 | if (retry) /* wait for 1 ms till bus get settled down */ | |
85 | udelay(1000); | |
86 | status = brcmf_sdioh_request_byte(sdiodev, SDIOH_READ, fnc_num, | |
87 | addr, (u8 *) &data); | |
88 | } while (status != 0 | |
89 | && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT)); | |
90 | if (err) | |
91 | *err = status; | |
92 | ||
93 | brcmf_dbg(INFO, "fun = %d, addr = 0x%x, u8data = 0x%x\n", | |
94 | fnc_num, addr, data); | |
95 | ||
96 | return data; | |
97 | } | |
98 | ||
99 | void | |
100 | brcmf_sdcard_cfg_write(struct brcmf_sdio_dev *sdiodev, uint fnc_num, u32 addr, | |
101 | u8 data, int *err) | |
102 | { | |
103 | int status; | |
104 | s32 retry = 0; | |
105 | ||
106 | do { | |
107 | if (retry) /* wait for 1 ms till bus get settled down */ | |
108 | udelay(1000); | |
109 | status = brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, fnc_num, | |
110 | addr, (u8 *) &data); | |
111 | } while (status != 0 | |
112 | && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT)); | |
113 | if (err) | |
114 | *err = status; | |
115 | ||
116 | brcmf_dbg(INFO, "fun = %d, addr = 0x%x, u8data = 0x%x\n", | |
117 | fnc_num, addr, data); | |
118 | } | |
119 | ||
120 | int | |
121 | brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) | |
122 | { | |
123 | int err = 0; | |
124 | brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW, | |
125 | (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err); | |
126 | if (!err) | |
127 | brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1, | |
128 | SBSDIO_FUNC1_SBADDRMID, | |
129 | (address >> 16) & SBSDIO_SBADDRMID_MASK, | |
130 | &err); | |
131 | if (!err) | |
132 | brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1, | |
133 | SBSDIO_FUNC1_SBADDRHIGH, | |
134 | (address >> 24) & SBSDIO_SBADDRHIGH_MASK, | |
135 | &err); | |
136 | ||
137 | return err; | |
138 | } | |
139 | ||
140 | u32 brcmf_sdcard_reg_read(struct brcmf_sdio_dev *sdiodev, u32 addr, uint size) | |
141 | { | |
142 | int status; | |
143 | u32 word = 0; | |
144 | uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK; | |
145 | ||
146 | brcmf_dbg(INFO, "fun = 1, addr = 0x%x\n", addr); | |
147 | ||
148 | if (bar0 != sdiodev->sbwad) { | |
149 | if (brcmf_sdcard_set_sbaddr_window(sdiodev, bar0)) | |
150 | return 0xFFFFFFFF; | |
151 | ||
152 | sdiodev->sbwad = bar0; | |
153 | } | |
154 | ||
155 | addr &= SBSDIO_SB_OFT_ADDR_MASK; | |
156 | if (size == 4) | |
157 | addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; | |
158 | ||
159 | status = brcmf_sdioh_request_word(sdiodev, SDIOH_READ, SDIO_FUNC_1, | |
160 | addr, &word, size); | |
161 | ||
162 | sdiodev->regfail = (status != 0); | |
163 | ||
164 | brcmf_dbg(INFO, "u32data = 0x%x\n", word); | |
165 | ||
166 | /* if ok, return appropriately masked word */ | |
167 | if (status == 0) { | |
168 | switch (size) { | |
169 | case sizeof(u8): | |
170 | return word & 0xff; | |
171 | case sizeof(u16): | |
172 | return word & 0xffff; | |
173 | case sizeof(u32): | |
174 | return word; | |
175 | default: | |
176 | sdiodev->regfail = true; | |
177 | ||
178 | } | |
179 | } | |
180 | ||
181 | /* otherwise, bad sdio access or invalid size */ | |
182 | brcmf_dbg(ERROR, "error reading addr 0x%04x size %d\n", addr, size); | |
183 | return 0xFFFFFFFF; | |
184 | } | |
185 | ||
186 | u32 brcmf_sdcard_reg_write(struct brcmf_sdio_dev *sdiodev, u32 addr, uint size, | |
187 | u32 data) | |
188 | { | |
189 | int status; | |
190 | uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK; | |
191 | int err = 0; | |
192 | ||
193 | brcmf_dbg(INFO, "fun = 1, addr = 0x%x, uint%ddata = 0x%x\n", | |
194 | addr, size * 8, data); | |
195 | ||
196 | if (bar0 != sdiodev->sbwad) { | |
197 | err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0); | |
198 | if (err) | |
199 | return err; | |
200 | ||
201 | sdiodev->sbwad = bar0; | |
202 | } | |
203 | ||
204 | addr &= SBSDIO_SB_OFT_ADDR_MASK; | |
205 | if (size == 4) | |
206 | addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; | |
207 | status = | |
208 | brcmf_sdioh_request_word(sdiodev, SDIOH_WRITE, SDIO_FUNC_1, | |
209 | addr, &data, size); | |
210 | sdiodev->regfail = (status != 0); | |
211 | ||
212 | if (status == 0) | |
213 | return 0; | |
214 | ||
215 | brcmf_dbg(ERROR, "error writing 0x%08x to addr 0x%04x size %d\n", | |
216 | data, addr, size); | |
217 | return 0xFFFFFFFF; | |
218 | } | |
219 | ||
220 | bool brcmf_sdcard_regfail(struct brcmf_sdio_dev *sdiodev) | |
221 | { | |
222 | return sdiodev->regfail; | |
223 | } | |
224 | ||
5adfeb63 AS |
225 | static int brcmf_sdcard_recv_prepare(struct brcmf_sdio_dev *sdiodev, uint fn, |
226 | uint flags, uint width, u32 *addr) | |
5b435de0 | 227 | { |
5adfeb63 | 228 | uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK; |
5b435de0 AS |
229 | int err = 0; |
230 | ||
5b435de0 AS |
231 | /* Async not implemented yet */ |
232 | if (flags & SDIO_REQ_ASYNC) | |
233 | return -ENOTSUPP; | |
234 | ||
235 | if (bar0 != sdiodev->sbwad) { | |
236 | err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0); | |
237 | if (err) | |
238 | return err; | |
239 | ||
240 | sdiodev->sbwad = bar0; | |
241 | } | |
242 | ||
5adfeb63 AS |
243 | *addr &= SBSDIO_SB_OFT_ADDR_MASK; |
244 | ||
245 | if (width == 4) | |
246 | *addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; | |
247 | ||
248 | return 0; | |
249 | } | |
250 | ||
251 | int | |
252 | brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, | |
253 | uint flags, u8 *buf, uint nbytes) | |
254 | { | |
255 | struct sk_buff *mypkt; | |
256 | int err; | |
257 | ||
258 | mypkt = brcmu_pkt_buf_get_skb(nbytes); | |
259 | if (!mypkt) { | |
260 | brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed: len %d\n", | |
261 | nbytes); | |
262 | return -EIO; | |
263 | } | |
264 | ||
265 | err = brcmf_sdcard_recv_pkt(sdiodev, addr, fn, flags, mypkt); | |
266 | if (!err) | |
267 | memcpy(buf, mypkt->data, nbytes); | |
268 | ||
269 | brcmu_pkt_buf_free_skb(mypkt); | |
270 | return err; | |
271 | } | |
272 | ||
273 | int | |
274 | brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, | |
275 | uint flags, struct sk_buff *pkt) | |
276 | { | |
277 | uint incr_fix; | |
278 | uint width; | |
279 | int err = 0; | |
280 | ||
281 | brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", | |
282 | fn, addr, pkt->len); | |
283 | ||
284 | width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; | |
285 | err = brcmf_sdcard_recv_prepare(sdiodev, fn, flags, width, &addr); | |
286 | if (err) | |
287 | return err; | |
5b435de0 AS |
288 | |
289 | incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; | |
5adfeb63 | 290 | err = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_READ, |
4c6e869d | 291 | fn, addr, pkt); |
5adfeb63 AS |
292 | |
293 | return err; | |
294 | } | |
295 | ||
296 | int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, | |
297 | uint flags, struct sk_buff_head *pktq) | |
298 | { | |
299 | uint incr_fix; | |
300 | uint width; | |
301 | int err = 0; | |
302 | ||
303 | brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", | |
304 | fn, addr, pktq->qlen); | |
305 | ||
5b435de0 | 306 | width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; |
5adfeb63 AS |
307 | err = brcmf_sdcard_recv_prepare(sdiodev, fn, flags, width, &addr); |
308 | if (err) | |
309 | return err; | |
5b435de0 | 310 | |
5adfeb63 AS |
311 | incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; |
312 | err = brcmf_sdioh_request_chain(sdiodev, incr_fix, SDIOH_READ, fn, addr, | |
313 | pktq); | |
5b435de0 | 314 | |
5adfeb63 | 315 | return err; |
5b435de0 AS |
316 | } |
317 | ||
318 | int | |
319 | brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, | |
5adfeb63 AS |
320 | uint flags, u8 *buf, uint nbytes) |
321 | { | |
322 | struct sk_buff *mypkt; | |
323 | int err; | |
324 | ||
325 | mypkt = brcmu_pkt_buf_get_skb(nbytes); | |
326 | if (!mypkt) { | |
327 | brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed: len %d\n", | |
328 | nbytes); | |
329 | return -EIO; | |
330 | } | |
331 | ||
332 | memcpy(mypkt->data, buf, nbytes); | |
333 | err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, mypkt); | |
334 | ||
335 | brcmu_pkt_buf_free_skb(mypkt); | |
336 | return err; | |
337 | ||
338 | } | |
339 | ||
340 | int | |
341 | brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, | |
342 | uint flags, struct sk_buff *pkt) | |
5b435de0 AS |
343 | { |
344 | uint incr_fix; | |
345 | uint width; | |
346 | uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK; | |
347 | int err = 0; | |
348 | ||
5adfeb63 AS |
349 | brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", |
350 | fn, addr, pkt->len); | |
5b435de0 AS |
351 | |
352 | /* Async not implemented yet */ | |
353 | if (flags & SDIO_REQ_ASYNC) | |
354 | return -ENOTSUPP; | |
355 | ||
356 | if (bar0 != sdiodev->sbwad) { | |
357 | err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0); | |
358 | if (err) | |
359 | return err; | |
360 | ||
361 | sdiodev->sbwad = bar0; | |
362 | } | |
363 | ||
364 | addr &= SBSDIO_SB_OFT_ADDR_MASK; | |
365 | ||
366 | incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; | |
367 | width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; | |
368 | if (width == 4) | |
369 | addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; | |
370 | ||
371 | return brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_WRITE, fn, | |
4c6e869d | 372 | addr, pkt); |
5b435de0 AS |
373 | } |
374 | ||
375 | int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, u32 addr, | |
376 | u8 *buf, uint nbytes) | |
377 | { | |
4c6e869d AS |
378 | struct sk_buff *mypkt; |
379 | bool write = rw ? SDIOH_WRITE : SDIOH_READ; | |
380 | int err; | |
381 | ||
5b435de0 AS |
382 | addr &= SBSDIO_SB_OFT_ADDR_MASK; |
383 | addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; | |
384 | ||
4c6e869d AS |
385 | mypkt = brcmu_pkt_buf_get_skb(nbytes); |
386 | if (!mypkt) { | |
387 | brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed: len %d\n", | |
388 | nbytes); | |
389 | return -EIO; | |
390 | } | |
391 | ||
392 | /* For a write, copy the buffer data into the packet. */ | |
393 | if (write) | |
394 | memcpy(mypkt->data, buf, nbytes); | |
395 | ||
396 | err = brcmf_sdioh_request_buffer(sdiodev, SDIOH_DATA_INC, write, | |
397 | SDIO_FUNC_1, addr, mypkt); | |
398 | ||
399 | /* For a read, copy the packet data back to the buffer. */ | |
400 | if (!err && !write) | |
401 | memcpy(buf, mypkt->data, nbytes); | |
402 | ||
403 | brcmu_pkt_buf_free_skb(mypkt); | |
404 | return err; | |
5b435de0 AS |
405 | } |
406 | ||
407 | int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn) | |
408 | { | |
409 | char t_func = (char)fn; | |
410 | brcmf_dbg(TRACE, "Enter\n"); | |
411 | ||
412 | /* issue abort cmd52 command through F0 */ | |
413 | brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0, | |
414 | SDIO_CCCR_ABORT, &t_func); | |
415 | ||
416 | brcmf_dbg(TRACE, "Exit\n"); | |
417 | return 0; | |
418 | } | |
419 | ||
420 | int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) | |
421 | { | |
422 | u32 regs = 0; | |
423 | int ret = 0; | |
424 | ||
425 | ret = brcmf_sdioh_attach(sdiodev); | |
426 | if (ret) | |
427 | goto out; | |
428 | ||
429 | regs = SI_ENUM_BASE; | |
430 | ||
431 | /* Report the BAR, to fix if needed */ | |
432 | sdiodev->sbwad = SI_ENUM_BASE; | |
433 | ||
434 | /* try to attach to the target device */ | |
4175b88b | 435 | sdiodev->bus = brcmf_sdbrcm_probe(regs, sdiodev); |
5b435de0 AS |
436 | if (!sdiodev->bus) { |
437 | brcmf_dbg(ERROR, "device attach failed\n"); | |
438 | ret = -ENODEV; | |
439 | goto out; | |
440 | } | |
441 | ||
442 | out: | |
443 | if (ret) | |
444 | brcmf_sdio_remove(sdiodev); | |
445 | ||
446 | return ret; | |
447 | } | |
448 | EXPORT_SYMBOL(brcmf_sdio_probe); | |
449 | ||
450 | int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev) | |
451 | { | |
452 | if (sdiodev->bus) { | |
453 | brcmf_sdbrcm_disconnect(sdiodev->bus); | |
454 | sdiodev->bus = NULL; | |
455 | } | |
456 | ||
457 | brcmf_sdioh_detach(sdiodev); | |
458 | ||
459 | sdiodev->sbwad = 0; | |
460 | ||
461 | return 0; | |
462 | } | |
463 | EXPORT_SYMBOL(brcmf_sdio_remove); | |
464 | ||
465 | void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, bool enable) | |
466 | { | |
467 | if (enable) | |
468 | brcmf_sdbrcm_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS); | |
469 | else | |
470 | brcmf_sdbrcm_wd_timer(sdiodev->bus, 0); | |
471 | } |