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 | { | |
43 | struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(&func->card->dev); | |
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 | ||
225 | int | |
226 | brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, | |
227 | uint flags, | |
228 | u8 *buf, uint nbytes, struct sk_buff *pkt) | |
229 | { | |
230 | int status; | |
231 | uint incr_fix; | |
232 | uint width; | |
233 | uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK; | |
234 | int err = 0; | |
235 | ||
236 | brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, nbytes); | |
237 | ||
238 | /* 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 | ||
250 | addr &= SBSDIO_SB_OFT_ADDR_MASK; | |
251 | ||
252 | incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; | |
253 | width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; | |
254 | if (width == 4) | |
255 | addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; | |
256 | ||
257 | status = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_READ, | |
258 | fn, addr, width, nbytes, buf, pkt); | |
259 | ||
260 | return status; | |
261 | } | |
262 | ||
263 | int | |
264 | brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, | |
265 | uint flags, u8 *buf, uint nbytes, struct sk_buff *pkt) | |
266 | { | |
267 | uint incr_fix; | |
268 | uint width; | |
269 | uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK; | |
270 | int err = 0; | |
271 | ||
272 | brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, nbytes); | |
273 | ||
274 | /* Async not implemented yet */ | |
275 | if (flags & SDIO_REQ_ASYNC) | |
276 | return -ENOTSUPP; | |
277 | ||
278 | if (bar0 != sdiodev->sbwad) { | |
279 | err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0); | |
280 | if (err) | |
281 | return err; | |
282 | ||
283 | sdiodev->sbwad = bar0; | |
284 | } | |
285 | ||
286 | addr &= SBSDIO_SB_OFT_ADDR_MASK; | |
287 | ||
288 | incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; | |
289 | width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; | |
290 | if (width == 4) | |
291 | addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; | |
292 | ||
293 | return brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_WRITE, fn, | |
294 | addr, width, nbytes, buf, pkt); | |
295 | } | |
296 | ||
297 | int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, u32 addr, | |
298 | u8 *buf, uint nbytes) | |
299 | { | |
300 | addr &= SBSDIO_SB_OFT_ADDR_MASK; | |
301 | addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; | |
302 | ||
303 | return brcmf_sdioh_request_buffer(sdiodev, SDIOH_DATA_INC, | |
304 | (rw ? SDIOH_WRITE : SDIOH_READ), SDIO_FUNC_1, | |
305 | addr, 4, nbytes, buf, NULL); | |
306 | } | |
307 | ||
308 | int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn) | |
309 | { | |
310 | char t_func = (char)fn; | |
311 | brcmf_dbg(TRACE, "Enter\n"); | |
312 | ||
313 | /* issue abort cmd52 command through F0 */ | |
314 | brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0, | |
315 | SDIO_CCCR_ABORT, &t_func); | |
316 | ||
317 | brcmf_dbg(TRACE, "Exit\n"); | |
318 | return 0; | |
319 | } | |
320 | ||
321 | int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) | |
322 | { | |
323 | u32 regs = 0; | |
324 | int ret = 0; | |
325 | ||
326 | ret = brcmf_sdioh_attach(sdiodev); | |
327 | if (ret) | |
328 | goto out; | |
329 | ||
330 | regs = SI_ENUM_BASE; | |
331 | ||
332 | /* Report the BAR, to fix if needed */ | |
333 | sdiodev->sbwad = SI_ENUM_BASE; | |
334 | ||
335 | /* try to attach to the target device */ | |
336 | sdiodev->bus = brcmf_sdbrcm_probe(0, 0, 0, 0, regs, sdiodev); | |
337 | if (!sdiodev->bus) { | |
338 | brcmf_dbg(ERROR, "device attach failed\n"); | |
339 | ret = -ENODEV; | |
340 | goto out; | |
341 | } | |
342 | ||
343 | out: | |
344 | if (ret) | |
345 | brcmf_sdio_remove(sdiodev); | |
346 | ||
347 | return ret; | |
348 | } | |
349 | EXPORT_SYMBOL(brcmf_sdio_probe); | |
350 | ||
351 | int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev) | |
352 | { | |
353 | if (sdiodev->bus) { | |
354 | brcmf_sdbrcm_disconnect(sdiodev->bus); | |
355 | sdiodev->bus = NULL; | |
356 | } | |
357 | ||
358 | brcmf_sdioh_detach(sdiodev); | |
359 | ||
360 | sdiodev->sbwad = 0; | |
361 | ||
362 | return 0; | |
363 | } | |
364 | EXPORT_SYMBOL(brcmf_sdio_remove); | |
365 | ||
366 | void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, bool enable) | |
367 | { | |
368 | if (enable) | |
369 | brcmf_sdbrcm_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS); | |
370 | else | |
371 | brcmf_sdbrcm_wd_timer(sdiodev->bus, 0); | |
372 | } |