]>
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> | |
20 | #include <linux/pci.h> | |
21 | #include <linux/pci_ids.h> | |
22 | #include <linux/sched.h> | |
23 | #include <linux/completion.h> | |
24 | #include <linux/mmc/sdio.h> | |
25 | #include <linux/mmc/sdio_func.h> | |
26 | #include <linux/mmc/card.h> | |
27 | ||
28 | #include <defs.h> | |
29 | #include <brcm_hw_ids.h> | |
30 | #include <brcmu_utils.h> | |
31 | #include <brcmu_wifi.h> | |
32 | #include <soc.h> | |
33 | #include "dhd.h" | |
34 | #include "dhd_bus.h" | |
35 | #include "dhd_dbg.h" | |
36 | #include "sdio_host.h" | |
37 | ||
38 | #define SDIOH_API_ACCESS_RETRY_LIMIT 2 | |
39 | ||
40 | static void brcmf_sdioh_irqhandler(struct sdio_func *func) | |
41 | { | |
42 | struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(&func->card->dev); | |
43 | ||
44 | brcmf_dbg(TRACE, "***IRQHandler\n"); | |
45 | ||
46 | sdio_release_host(func); | |
47 | ||
48 | brcmf_sdbrcm_isr(sdiodev->bus); | |
49 | ||
50 | sdio_claim_host(func); | |
51 | } | |
52 | ||
53 | int brcmf_sdcard_intr_reg(struct brcmf_sdio_dev *sdiodev) | |
54 | { | |
55 | brcmf_dbg(TRACE, "Entering\n"); | |
56 | ||
57 | sdio_claim_host(sdiodev->func[1]); | |
58 | sdio_claim_irq(sdiodev->func[1], brcmf_sdioh_irqhandler); | |
59 | sdio_release_host(sdiodev->func[1]); | |
60 | ||
61 | return 0; | |
62 | } | |
63 | ||
64 | int brcmf_sdcard_intr_dereg(struct brcmf_sdio_dev *sdiodev) | |
65 | { | |
66 | brcmf_dbg(TRACE, "Entering\n"); | |
67 | ||
68 | sdio_claim_host(sdiodev->func[1]); | |
69 | sdio_release_irq(sdiodev->func[1]); | |
70 | sdio_release_host(sdiodev->func[1]); | |
71 | ||
72 | return 0; | |
73 | } | |
74 | ||
75 | u8 brcmf_sdcard_cfg_read(struct brcmf_sdio_dev *sdiodev, uint fnc_num, u32 addr, | |
76 | int *err) | |
77 | { | |
78 | int status; | |
79 | s32 retry = 0; | |
80 | u8 data = 0; | |
81 | ||
82 | do { | |
83 | if (retry) /* wait for 1 ms till bus get settled down */ | |
84 | udelay(1000); | |
85 | status = brcmf_sdioh_request_byte(sdiodev, SDIOH_READ, fnc_num, | |
86 | addr, (u8 *) &data); | |
87 | } while (status != 0 | |
88 | && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT)); | |
89 | if (err) | |
90 | *err = status; | |
91 | ||
92 | brcmf_dbg(INFO, "fun = %d, addr = 0x%x, u8data = 0x%x\n", | |
93 | fnc_num, addr, data); | |
94 | ||
95 | return data; | |
96 | } | |
97 | ||
98 | void | |
99 | brcmf_sdcard_cfg_write(struct brcmf_sdio_dev *sdiodev, uint fnc_num, u32 addr, | |
100 | u8 data, int *err) | |
101 | { | |
102 | int status; | |
103 | s32 retry = 0; | |
104 | ||
105 | do { | |
106 | if (retry) /* wait for 1 ms till bus get settled down */ | |
107 | udelay(1000); | |
108 | status = brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, fnc_num, | |
109 | addr, (u8 *) &data); | |
110 | } while (status != 0 | |
111 | && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT)); | |
112 | if (err) | |
113 | *err = status; | |
114 | ||
115 | brcmf_dbg(INFO, "fun = %d, addr = 0x%x, u8data = 0x%x\n", | |
116 | fnc_num, addr, data); | |
117 | } | |
118 | ||
119 | int | |
120 | brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) | |
121 | { | |
122 | int err = 0; | |
123 | brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW, | |
124 | (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err); | |
125 | if (!err) | |
126 | brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1, | |
127 | SBSDIO_FUNC1_SBADDRMID, | |
128 | (address >> 16) & SBSDIO_SBADDRMID_MASK, | |
129 | &err); | |
130 | if (!err) | |
131 | brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1, | |
132 | SBSDIO_FUNC1_SBADDRHIGH, | |
133 | (address >> 24) & SBSDIO_SBADDRHIGH_MASK, | |
134 | &err); | |
135 | ||
136 | return err; | |
137 | } | |
138 | ||
139 | u32 brcmf_sdcard_reg_read(struct brcmf_sdio_dev *sdiodev, u32 addr, uint size) | |
140 | { | |
141 | int status; | |
142 | u32 word = 0; | |
143 | uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK; | |
144 | ||
145 | brcmf_dbg(INFO, "fun = 1, addr = 0x%x\n", addr); | |
146 | ||
147 | if (bar0 != sdiodev->sbwad) { | |
148 | if (brcmf_sdcard_set_sbaddr_window(sdiodev, bar0)) | |
149 | return 0xFFFFFFFF; | |
150 | ||
151 | sdiodev->sbwad = bar0; | |
152 | } | |
153 | ||
154 | addr &= SBSDIO_SB_OFT_ADDR_MASK; | |
155 | if (size == 4) | |
156 | addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; | |
157 | ||
158 | status = brcmf_sdioh_request_word(sdiodev, SDIOH_READ, SDIO_FUNC_1, | |
159 | addr, &word, size); | |
160 | ||
161 | sdiodev->regfail = (status != 0); | |
162 | ||
163 | brcmf_dbg(INFO, "u32data = 0x%x\n", word); | |
164 | ||
165 | /* if ok, return appropriately masked word */ | |
166 | if (status == 0) { | |
167 | switch (size) { | |
168 | case sizeof(u8): | |
169 | return word & 0xff; | |
170 | case sizeof(u16): | |
171 | return word & 0xffff; | |
172 | case sizeof(u32): | |
173 | return word; | |
174 | default: | |
175 | sdiodev->regfail = true; | |
176 | ||
177 | } | |
178 | } | |
179 | ||
180 | /* otherwise, bad sdio access or invalid size */ | |
181 | brcmf_dbg(ERROR, "error reading addr 0x%04x size %d\n", addr, size); | |
182 | return 0xFFFFFFFF; | |
183 | } | |
184 | ||
185 | u32 brcmf_sdcard_reg_write(struct brcmf_sdio_dev *sdiodev, u32 addr, uint size, | |
186 | u32 data) | |
187 | { | |
188 | int status; | |
189 | uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK; | |
190 | int err = 0; | |
191 | ||
192 | brcmf_dbg(INFO, "fun = 1, addr = 0x%x, uint%ddata = 0x%x\n", | |
193 | addr, size * 8, data); | |
194 | ||
195 | if (bar0 != sdiodev->sbwad) { | |
196 | err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0); | |
197 | if (err) | |
198 | return err; | |
199 | ||
200 | sdiodev->sbwad = bar0; | |
201 | } | |
202 | ||
203 | addr &= SBSDIO_SB_OFT_ADDR_MASK; | |
204 | if (size == 4) | |
205 | addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; | |
206 | status = | |
207 | brcmf_sdioh_request_word(sdiodev, SDIOH_WRITE, SDIO_FUNC_1, | |
208 | addr, &data, size); | |
209 | sdiodev->regfail = (status != 0); | |
210 | ||
211 | if (status == 0) | |
212 | return 0; | |
213 | ||
214 | brcmf_dbg(ERROR, "error writing 0x%08x to addr 0x%04x size %d\n", | |
215 | data, addr, size); | |
216 | return 0xFFFFFFFF; | |
217 | } | |
218 | ||
219 | bool brcmf_sdcard_regfail(struct brcmf_sdio_dev *sdiodev) | |
220 | { | |
221 | return sdiodev->regfail; | |
222 | } | |
223 | ||
224 | int | |
225 | brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, | |
226 | uint flags, | |
227 | u8 *buf, uint nbytes, struct sk_buff *pkt) | |
228 | { | |
229 | int status; | |
230 | uint incr_fix; | |
231 | uint width; | |
232 | uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK; | |
233 | int err = 0; | |
234 | ||
235 | brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, nbytes); | |
236 | ||
237 | /* Async not implemented yet */ | |
238 | if (flags & SDIO_REQ_ASYNC) | |
239 | return -ENOTSUPP; | |
240 | ||
241 | if (bar0 != sdiodev->sbwad) { | |
242 | err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0); | |
243 | if (err) | |
244 | return err; | |
245 | ||
246 | sdiodev->sbwad = bar0; | |
247 | } | |
248 | ||
249 | addr &= SBSDIO_SB_OFT_ADDR_MASK; | |
250 | ||
251 | incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; | |
252 | width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; | |
253 | if (width == 4) | |
254 | addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; | |
255 | ||
256 | status = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_READ, | |
257 | fn, addr, width, nbytes, buf, pkt); | |
258 | ||
259 | return status; | |
260 | } | |
261 | ||
262 | int | |
263 | brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, | |
264 | uint flags, u8 *buf, uint nbytes, struct sk_buff *pkt) | |
265 | { | |
266 | uint incr_fix; | |
267 | uint width; | |
268 | uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK; | |
269 | int err = 0; | |
270 | ||
271 | brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, nbytes); | |
272 | ||
273 | /* Async not implemented yet */ | |
274 | if (flags & SDIO_REQ_ASYNC) | |
275 | return -ENOTSUPP; | |
276 | ||
277 | if (bar0 != sdiodev->sbwad) { | |
278 | err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0); | |
279 | if (err) | |
280 | return err; | |
281 | ||
282 | sdiodev->sbwad = bar0; | |
283 | } | |
284 | ||
285 | addr &= SBSDIO_SB_OFT_ADDR_MASK; | |
286 | ||
287 | incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; | |
288 | width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; | |
289 | if (width == 4) | |
290 | addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; | |
291 | ||
292 | return brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_WRITE, fn, | |
293 | addr, width, nbytes, buf, pkt); | |
294 | } | |
295 | ||
296 | int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, u32 addr, | |
297 | u8 *buf, uint nbytes) | |
298 | { | |
299 | addr &= SBSDIO_SB_OFT_ADDR_MASK; | |
300 | addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; | |
301 | ||
302 | return brcmf_sdioh_request_buffer(sdiodev, SDIOH_DATA_INC, | |
303 | (rw ? SDIOH_WRITE : SDIOH_READ), SDIO_FUNC_1, | |
304 | addr, 4, nbytes, buf, NULL); | |
305 | } | |
306 | ||
307 | int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn) | |
308 | { | |
309 | char t_func = (char)fn; | |
310 | brcmf_dbg(TRACE, "Enter\n"); | |
311 | ||
312 | /* issue abort cmd52 command through F0 */ | |
313 | brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0, | |
314 | SDIO_CCCR_ABORT, &t_func); | |
315 | ||
316 | brcmf_dbg(TRACE, "Exit\n"); | |
317 | return 0; | |
318 | } | |
319 | ||
320 | int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) | |
321 | { | |
322 | u32 regs = 0; | |
323 | int ret = 0; | |
324 | ||
325 | ret = brcmf_sdioh_attach(sdiodev); | |
326 | if (ret) | |
327 | goto out; | |
328 | ||
329 | regs = SI_ENUM_BASE; | |
330 | ||
331 | /* Report the BAR, to fix if needed */ | |
332 | sdiodev->sbwad = SI_ENUM_BASE; | |
333 | ||
334 | /* try to attach to the target device */ | |
335 | sdiodev->bus = brcmf_sdbrcm_probe(0, 0, 0, 0, regs, sdiodev); | |
336 | if (!sdiodev->bus) { | |
337 | brcmf_dbg(ERROR, "device attach failed\n"); | |
338 | ret = -ENODEV; | |
339 | goto out; | |
340 | } | |
341 | ||
342 | out: | |
343 | if (ret) | |
344 | brcmf_sdio_remove(sdiodev); | |
345 | ||
346 | return ret; | |
347 | } | |
348 | EXPORT_SYMBOL(brcmf_sdio_probe); | |
349 | ||
350 | int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev) | |
351 | { | |
352 | if (sdiodev->bus) { | |
353 | brcmf_sdbrcm_disconnect(sdiodev->bus); | |
354 | sdiodev->bus = NULL; | |
355 | } | |
356 | ||
357 | brcmf_sdioh_detach(sdiodev); | |
358 | ||
359 | sdiodev->sbwad = 0; | |
360 | ||
361 | return 0; | |
362 | } | |
363 | EXPORT_SYMBOL(brcmf_sdio_remove); | |
364 | ||
365 | void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, bool enable) | |
366 | { | |
367 | if (enable) | |
368 | brcmf_sdbrcm_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS); | |
369 | else | |
370 | brcmf_sdbrcm_wd_timer(sdiodev->bus, 0); | |
371 | } |