]>
Commit | Line | Data |
---|---|---|
bdcd8170 KV |
1 | /* |
2 | * Copyright (c) 2004-2011 Atheros Communications Inc. | |
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 | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | */ | |
16 | ||
17 | #include "core.h" | |
18 | #include "hif-ops.h" | |
19 | #include "target.h" | |
20 | #include "debug.h" | |
21 | ||
22 | static int ath6kl_get_bmi_cmd_credits(struct ath6kl *ar) | |
23 | { | |
24 | u32 addr; | |
25 | unsigned long timeout; | |
26 | int ret; | |
27 | ||
28 | ar->bmi.cmd_credits = 0; | |
29 | ||
30 | /* Read the counter register to get the command credits */ | |
31 | addr = COUNT_DEC_ADDRESS + (HTC_MAILBOX_NUM_MAX + ENDPOINT1) * 4; | |
32 | ||
33 | timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT); | |
34 | while (time_before(jiffies, timeout) && !ar->bmi.cmd_credits) { | |
35 | ||
36 | /* | |
37 | * Hit the credit counter with a 4-byte access, the first byte | |
38 | * read will hit the counter and cause a decrement, while the | |
39 | * remaining 3 bytes has no effect. The rationale behind this | |
40 | * is to make all HIF accesses 4-byte aligned. | |
41 | */ | |
42 | ret = hif_read_write_sync(ar, addr, | |
43 | (u8 *)&ar->bmi.cmd_credits, 4, | |
44 | HIF_RD_SYNC_BYTE_INC); | |
45 | if (ret) { | |
46 | ath6kl_err("Unable to decrement the command credit count register: %d\n", | |
47 | ret); | |
48 | return ret; | |
49 | } | |
50 | ||
51 | /* The counter is only 8 bits. | |
52 | * Ignore anything in the upper 3 bytes | |
53 | */ | |
54 | ar->bmi.cmd_credits &= 0xFF; | |
55 | } | |
56 | ||
57 | if (!ar->bmi.cmd_credits) { | |
58 | ath6kl_err("bmi communication timeout\n"); | |
59 | return -ETIMEDOUT; | |
60 | } | |
61 | ||
62 | return 0; | |
63 | } | |
64 | ||
cfc301ed | 65 | static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar) |
bdcd8170 KV |
66 | { |
67 | unsigned long timeout; | |
68 | u32 rx_word = 0; | |
69 | int ret = 0; | |
70 | ||
71 | timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT); | |
cfc301ed | 72 | while (time_before(jiffies, timeout) && !rx_word) { |
bdcd8170 KV |
73 | ret = hif_read_write_sync(ar, RX_LOOKAHEAD_VALID_ADDRESS, |
74 | (u8 *)&rx_word, sizeof(rx_word), | |
75 | HIF_RD_SYNC_BYTE_INC); | |
76 | if (ret) { | |
77 | ath6kl_err("unable to read RX_LOOKAHEAD_VALID\n"); | |
78 | return ret; | |
79 | } | |
80 | ||
81 | /* all we really want is one bit */ | |
82 | rx_word &= (1 << ENDPOINT1); | |
83 | } | |
84 | ||
85 | if (!rx_word) { | |
86 | ath6kl_err("bmi_recv_buf FIFO empty\n"); | |
87 | return -EINVAL; | |
88 | } | |
89 | ||
90 | return ret; | |
91 | } | |
92 | ||
93 | static int ath6kl_bmi_send_buf(struct ath6kl *ar, u8 *buf, u32 len) | |
94 | { | |
95 | int ret; | |
96 | u32 addr; | |
97 | ||
98 | ret = ath6kl_get_bmi_cmd_credits(ar); | |
99 | if (ret) | |
100 | return ret; | |
101 | ||
102 | addr = ar->mbox_info.htc_addr; | |
103 | ||
104 | ret = hif_read_write_sync(ar, addr, buf, len, | |
105 | HIF_WR_SYNC_BYTE_INC); | |
106 | if (ret) | |
107 | ath6kl_err("unable to send the bmi data to the device\n"); | |
108 | ||
109 | return ret; | |
110 | } | |
111 | ||
cfc301ed | 112 | static int ath6kl_bmi_recv_buf(struct ath6kl *ar, u8 *buf, u32 len) |
bdcd8170 KV |
113 | { |
114 | int ret; | |
115 | u32 addr; | |
116 | ||
117 | /* | |
118 | * During normal bootup, small reads may be required. | |
119 | * Rather than issue an HIF Read and then wait as the Target | |
120 | * adds successive bytes to the FIFO, we wait here until | |
121 | * we know that response data is available. | |
122 | * | |
123 | * This allows us to cleanly timeout on an unexpected | |
124 | * Target failure rather than risk problems at the HIF level. | |
125 | * In particular, this avoids SDIO timeouts and possibly garbage | |
126 | * data on some host controllers. And on an interconnect | |
127 | * such as Compact Flash (as well as some SDIO masters) which | |
128 | * does not provide any indication on data timeout, it avoids | |
129 | * a potential hang or garbage response. | |
130 | * | |
131 | * Synchronization is more difficult for reads larger than the | |
132 | * size of the MBOX FIFO (128B), because the Target is unable | |
133 | * to push the 129th byte of data until AFTER the Host posts an | |
134 | * HIF Read and removes some FIFO data. So for large reads the | |
135 | * Host proceeds to post an HIF Read BEFORE all the data is | |
136 | * actually available to read. Fortunately, large BMI reads do | |
137 | * not occur in practice -- they're supported for debug/development. | |
138 | * | |
139 | * So Host/Target BMI synchronization is divided into these cases: | |
140 | * CASE 1: length < 4 | |
141 | * Should not happen | |
142 | * | |
143 | * CASE 2: 4 <= length <= 128 | |
144 | * Wait for first 4 bytes to be in FIFO | |
145 | * If CONSERVATIVE_BMI_READ is enabled, also wait for | |
146 | * a BMI command credit, which indicates that the ENTIRE | |
147 | * response is available in the the FIFO | |
148 | * | |
149 | * CASE 3: length > 128 | |
150 | * Wait for the first 4 bytes to be in FIFO | |
151 | * | |
152 | * For most uses, a small timeout should be sufficient and we will | |
153 | * usually see a response quickly; but there may be some unusual | |
154 | * (debug) cases of BMI_EXECUTE where we want an larger timeout. | |
155 | * For now, we use an unbounded busy loop while waiting for | |
156 | * BMI_EXECUTE. | |
157 | * | |
158 | * If BMI_EXECUTE ever needs to support longer-latency execution, | |
159 | * especially in production, this code needs to be enhanced to sleep | |
160 | * and yield. Also note that BMI_COMMUNICATION_TIMEOUT is currently | |
161 | * a function of Host processor speed. | |
162 | */ | |
163 | if (len >= 4) { /* NB: Currently, always true */ | |
cfc301ed | 164 | ret = ath6kl_bmi_get_rx_lkahd(ar); |
bdcd8170 KV |
165 | if (ret) |
166 | return ret; | |
167 | } | |
168 | ||
169 | addr = ar->mbox_info.htc_addr; | |
170 | ret = hif_read_write_sync(ar, addr, buf, len, | |
171 | HIF_RD_SYNC_BYTE_INC); | |
172 | if (ret) { | |
173 | ath6kl_err("Unable to read the bmi data from the device: %d\n", | |
174 | ret); | |
175 | return ret; | |
176 | } | |
177 | ||
178 | return 0; | |
179 | } | |
180 | ||
181 | int ath6kl_bmi_done(struct ath6kl *ar) | |
182 | { | |
183 | int ret; | |
184 | u32 cid = BMI_DONE; | |
185 | ||
186 | if (ar->bmi.done_sent) { | |
187 | ath6kl_dbg(ATH6KL_DBG_BMI, "bmi done skipped\n"); | |
188 | return 0; | |
189 | } | |
190 | ||
191 | ar->bmi.done_sent = true; | |
192 | ||
193 | ret = ath6kl_bmi_send_buf(ar, (u8 *)&cid, sizeof(cid)); | |
194 | if (ret) { | |
195 | ath6kl_err("Unable to send bmi done: %d\n", ret); | |
196 | return ret; | |
197 | } | |
198 | ||
199 | ath6kl_bmi_cleanup(ar); | |
200 | ||
201 | return 0; | |
202 | } | |
203 | ||
204 | int ath6kl_bmi_get_target_info(struct ath6kl *ar, | |
205 | struct ath6kl_bmi_target_info *targ_info) | |
206 | { | |
207 | int ret; | |
208 | u32 cid = BMI_GET_TARGET_INFO; | |
209 | ||
210 | if (ar->bmi.done_sent) { | |
211 | ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); | |
212 | return -EACCES; | |
213 | } | |
214 | ||
215 | ret = ath6kl_bmi_send_buf(ar, (u8 *)&cid, sizeof(cid)); | |
216 | if (ret) { | |
217 | ath6kl_err("Unable to send get target info: %d\n", ret); | |
218 | return ret; | |
219 | } | |
220 | ||
221 | ret = ath6kl_bmi_recv_buf(ar, (u8 *)&targ_info->version, | |
cfc301ed | 222 | sizeof(targ_info->version)); |
bdcd8170 KV |
223 | if (ret) { |
224 | ath6kl_err("Unable to recv target info: %d\n", ret); | |
225 | return ret; | |
226 | } | |
227 | ||
228 | if (le32_to_cpu(targ_info->version) == TARGET_VERSION_SENTINAL) { | |
229 | /* Determine how many bytes are in the Target's targ_info */ | |
230 | ret = ath6kl_bmi_recv_buf(ar, | |
231 | (u8 *)&targ_info->byte_count, | |
cfc301ed | 232 | sizeof(targ_info->byte_count)); |
bdcd8170 KV |
233 | if (ret) { |
234 | ath6kl_err("unable to read target info byte count: %d\n", | |
235 | ret); | |
236 | return ret; | |
237 | } | |
238 | ||
239 | /* | |
240 | * The target's targ_info doesn't match the host's targ_info. | |
241 | * We need to do some backwards compatibility to make this work. | |
242 | */ | |
243 | if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) { | |
244 | WARN_ON(1); | |
245 | return -EINVAL; | |
246 | } | |
247 | ||
248 | /* Read the remainder of the targ_info */ | |
249 | ret = ath6kl_bmi_recv_buf(ar, | |
250 | ((u8 *)targ_info) + | |
251 | sizeof(targ_info->byte_count), | |
252 | sizeof(*targ_info) - | |
cfc301ed | 253 | sizeof(targ_info->byte_count)); |
bdcd8170 KV |
254 | |
255 | if (ret) { | |
256 | ath6kl_err("Unable to read target info (%d bytes): %d\n", | |
257 | targ_info->byte_count, ret); | |
258 | return ret; | |
259 | } | |
260 | } | |
261 | ||
262 | ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n", | |
263 | targ_info->version, targ_info->type); | |
264 | ||
265 | return 0; | |
266 | } | |
267 | ||
268 | int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) | |
269 | { | |
270 | u32 cid = BMI_READ_MEMORY; | |
271 | int ret; | |
272 | u32 offset; | |
273 | u32 len_remain, rx_len; | |
274 | u16 size; | |
275 | ||
276 | if (ar->bmi.done_sent) { | |
277 | ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); | |
278 | return -EACCES; | |
279 | } | |
280 | ||
281 | size = BMI_DATASZ_MAX + sizeof(cid) + sizeof(addr) + sizeof(len); | |
282 | if (size > MAX_BMI_CMDBUF_SZ) { | |
283 | WARN_ON(1); | |
284 | return -EINVAL; | |
285 | } | |
286 | memset(ar->bmi.cmd_buf, 0, size); | |
287 | ||
288 | ath6kl_dbg(ATH6KL_DBG_BMI, | |
289 | "bmi read memory: device: addr: 0x%x, len: %d\n", | |
290 | addr, len); | |
291 | ||
292 | len_remain = len; | |
293 | ||
294 | while (len_remain) { | |
295 | rx_len = (len_remain < BMI_DATASZ_MAX) ? | |
296 | len_remain : BMI_DATASZ_MAX; | |
297 | offset = 0; | |
298 | memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); | |
299 | offset += sizeof(cid); | |
300 | memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); | |
301 | offset += sizeof(addr); | |
302 | memcpy(&(ar->bmi.cmd_buf[offset]), &rx_len, sizeof(rx_len)); | |
303 | offset += sizeof(len); | |
304 | ||
305 | ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); | |
306 | if (ret) { | |
307 | ath6kl_err("Unable to write to the device: %d\n", | |
308 | ret); | |
309 | return ret; | |
310 | } | |
cfc301ed | 311 | ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, rx_len); |
bdcd8170 KV |
312 | if (ret) { |
313 | ath6kl_err("Unable to read from the device: %d\n", | |
314 | ret); | |
315 | return ret; | |
316 | } | |
317 | memcpy(&buf[len - len_remain], ar->bmi.cmd_buf, rx_len); | |
318 | len_remain -= rx_len; addr += rx_len; | |
319 | } | |
320 | ||
321 | return 0; | |
322 | } | |
323 | ||
324 | int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) | |
325 | { | |
326 | u32 cid = BMI_WRITE_MEMORY; | |
327 | int ret; | |
328 | u32 offset; | |
329 | u32 len_remain, tx_len; | |
330 | const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len); | |
331 | u8 aligned_buf[BMI_DATASZ_MAX]; | |
332 | u8 *src; | |
333 | ||
334 | if (ar->bmi.done_sent) { | |
335 | ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); | |
336 | return -EACCES; | |
337 | } | |
338 | ||
339 | if ((BMI_DATASZ_MAX + header) > MAX_BMI_CMDBUF_SZ) { | |
340 | WARN_ON(1); | |
341 | return -EINVAL; | |
342 | } | |
343 | ||
344 | memset(ar->bmi.cmd_buf, 0, BMI_DATASZ_MAX + header); | |
345 | ||
346 | ath6kl_dbg(ATH6KL_DBG_BMI, | |
347 | "bmi write memory: addr: 0x%x, len: %d\n", addr, len); | |
348 | ||
349 | len_remain = len; | |
350 | while (len_remain) { | |
351 | src = &buf[len - len_remain]; | |
352 | ||
353 | if (len_remain < (BMI_DATASZ_MAX - header)) { | |
354 | if (len_remain & 3) { | |
355 | /* align it with 4 bytes */ | |
356 | len_remain = len_remain + | |
357 | (4 - (len_remain & 3)); | |
358 | memcpy(aligned_buf, src, len_remain); | |
359 | src = aligned_buf; | |
360 | } | |
361 | tx_len = len_remain; | |
362 | } else { | |
363 | tx_len = (BMI_DATASZ_MAX - header); | |
364 | } | |
365 | ||
366 | offset = 0; | |
367 | memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); | |
368 | offset += sizeof(cid); | |
369 | memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); | |
370 | offset += sizeof(addr); | |
371 | memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); | |
372 | offset += sizeof(tx_len); | |
373 | memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len); | |
374 | offset += tx_len; | |
375 | ||
376 | ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); | |
377 | if (ret) { | |
378 | ath6kl_err("Unable to write to the device: %d\n", | |
379 | ret); | |
380 | return ret; | |
381 | } | |
382 | len_remain -= tx_len; addr += tx_len; | |
383 | } | |
384 | ||
385 | return 0; | |
386 | } | |
387 | ||
388 | int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param) | |
389 | { | |
390 | u32 cid = BMI_EXECUTE; | |
391 | int ret; | |
392 | u32 offset; | |
393 | u16 size; | |
394 | ||
395 | if (ar->bmi.done_sent) { | |
396 | ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); | |
397 | return -EACCES; | |
398 | } | |
399 | ||
400 | size = sizeof(cid) + sizeof(addr) + sizeof(param); | |
401 | if (size > MAX_BMI_CMDBUF_SZ) { | |
402 | WARN_ON(1); | |
403 | return -EINVAL; | |
404 | } | |
405 | memset(ar->bmi.cmd_buf, 0, size); | |
406 | ||
407 | ath6kl_dbg(ATH6KL_DBG_BMI, "bmi execute: addr: 0x%x, param: %d)\n", | |
408 | addr, *param); | |
409 | ||
410 | offset = 0; | |
411 | memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); | |
412 | offset += sizeof(cid); | |
413 | memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); | |
414 | offset += sizeof(addr); | |
415 | memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param)); | |
416 | offset += sizeof(*param); | |
417 | ||
418 | ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); | |
419 | if (ret) { | |
420 | ath6kl_err("Unable to write to the device: %d\n", ret); | |
421 | return ret; | |
422 | } | |
423 | ||
cfc301ed | 424 | ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param)); |
bdcd8170 KV |
425 | if (ret) { |
426 | ath6kl_err("Unable to read from the device: %d\n", ret); | |
427 | return ret; | |
428 | } | |
429 | ||
430 | memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); | |
431 | ||
432 | return 0; | |
433 | } | |
434 | ||
435 | int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr) | |
436 | { | |
437 | u32 cid = BMI_SET_APP_START; | |
438 | int ret; | |
439 | u32 offset; | |
440 | u16 size; | |
441 | ||
442 | if (ar->bmi.done_sent) { | |
443 | ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); | |
444 | return -EACCES; | |
445 | } | |
446 | ||
447 | size = sizeof(cid) + sizeof(addr); | |
448 | if (size > MAX_BMI_CMDBUF_SZ) { | |
449 | WARN_ON(1); | |
450 | return -EINVAL; | |
451 | } | |
452 | memset(ar->bmi.cmd_buf, 0, size); | |
453 | ||
454 | ath6kl_dbg(ATH6KL_DBG_BMI, "bmi set app start: addr: 0x%x\n", addr); | |
455 | ||
456 | offset = 0; | |
457 | memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); | |
458 | offset += sizeof(cid); | |
459 | memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); | |
460 | offset += sizeof(addr); | |
461 | ||
462 | ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); | |
463 | if (ret) { | |
464 | ath6kl_err("Unable to write to the device: %d\n", ret); | |
465 | return ret; | |
466 | } | |
467 | ||
468 | return 0; | |
469 | } | |
470 | ||
471 | int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param) | |
472 | { | |
473 | u32 cid = BMI_READ_SOC_REGISTER; | |
474 | int ret; | |
475 | u32 offset; | |
476 | u16 size; | |
477 | ||
478 | if (ar->bmi.done_sent) { | |
479 | ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); | |
480 | return -EACCES; | |
481 | } | |
482 | ||
483 | size = sizeof(cid) + sizeof(addr); | |
484 | if (size > MAX_BMI_CMDBUF_SZ) { | |
485 | WARN_ON(1); | |
486 | return -EINVAL; | |
487 | } | |
488 | memset(ar->bmi.cmd_buf, 0, size); | |
489 | ||
490 | ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr); | |
491 | ||
492 | offset = 0; | |
493 | memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); | |
494 | offset += sizeof(cid); | |
495 | memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); | |
496 | offset += sizeof(addr); | |
497 | ||
498 | ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); | |
499 | if (ret) { | |
500 | ath6kl_err("Unable to write to the device: %d\n", ret); | |
501 | return ret; | |
502 | } | |
503 | ||
cfc301ed | 504 | ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param)); |
bdcd8170 KV |
505 | if (ret) { |
506 | ath6kl_err("Unable to read from the device: %d\n", ret); | |
507 | return ret; | |
508 | } | |
509 | memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); | |
510 | ||
511 | return 0; | |
512 | } | |
513 | ||
514 | int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param) | |
515 | { | |
516 | u32 cid = BMI_WRITE_SOC_REGISTER; | |
517 | int ret; | |
518 | u32 offset; | |
519 | u16 size; | |
520 | ||
521 | if (ar->bmi.done_sent) { | |
522 | ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); | |
523 | return -EACCES; | |
524 | } | |
525 | ||
526 | size = sizeof(cid) + sizeof(addr) + sizeof(param); | |
527 | if (size > MAX_BMI_CMDBUF_SZ) { | |
528 | WARN_ON(1); | |
529 | return -EINVAL; | |
530 | } | |
531 | memset(ar->bmi.cmd_buf, 0, size); | |
532 | ||
533 | ath6kl_dbg(ATH6KL_DBG_BMI, | |
534 | "bmi write SOC reg: addr: 0x%x, param: %d\n", | |
535 | addr, param); | |
536 | ||
537 | offset = 0; | |
538 | memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); | |
539 | offset += sizeof(cid); | |
540 | memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); | |
541 | offset += sizeof(addr); | |
542 | memcpy(&(ar->bmi.cmd_buf[offset]), ¶m, sizeof(param)); | |
543 | offset += sizeof(param); | |
544 | ||
545 | ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); | |
546 | if (ret) { | |
547 | ath6kl_err("Unable to write to the device: %d\n", ret); | |
548 | return ret; | |
549 | } | |
550 | ||
551 | return 0; | |
552 | } | |
553 | ||
554 | int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len) | |
555 | { | |
556 | u32 cid = BMI_LZ_DATA; | |
557 | int ret; | |
558 | u32 offset; | |
559 | u32 len_remain, tx_len; | |
560 | const u32 header = sizeof(cid) + sizeof(len); | |
561 | u16 size; | |
562 | ||
563 | if (ar->bmi.done_sent) { | |
564 | ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); | |
565 | return -EACCES; | |
566 | } | |
567 | ||
568 | size = BMI_DATASZ_MAX + header; | |
569 | if (size > MAX_BMI_CMDBUF_SZ) { | |
570 | WARN_ON(1); | |
571 | return -EINVAL; | |
572 | } | |
573 | memset(ar->bmi.cmd_buf, 0, size); | |
574 | ||
575 | ath6kl_dbg(ATH6KL_DBG_BMI, "bmi send LZ data: len: %d)\n", | |
576 | len); | |
577 | ||
578 | len_remain = len; | |
579 | while (len_remain) { | |
580 | tx_len = (len_remain < (BMI_DATASZ_MAX - header)) ? | |
581 | len_remain : (BMI_DATASZ_MAX - header); | |
582 | ||
583 | offset = 0; | |
584 | memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); | |
585 | offset += sizeof(cid); | |
586 | memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); | |
587 | offset += sizeof(tx_len); | |
588 | memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain], | |
589 | tx_len); | |
590 | offset += tx_len; | |
591 | ||
592 | ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); | |
593 | if (ret) { | |
594 | ath6kl_err("Unable to write to the device: %d\n", | |
595 | ret); | |
596 | return ret; | |
597 | } | |
598 | ||
599 | len_remain -= tx_len; | |
600 | } | |
601 | ||
602 | return 0; | |
603 | } | |
604 | ||
605 | int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr) | |
606 | { | |
607 | u32 cid = BMI_LZ_STREAM_START; | |
608 | int ret; | |
609 | u32 offset; | |
610 | u16 size; | |
611 | ||
612 | if (ar->bmi.done_sent) { | |
613 | ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); | |
614 | return -EACCES; | |
615 | } | |
616 | ||
617 | size = sizeof(cid) + sizeof(addr); | |
618 | if (size > MAX_BMI_CMDBUF_SZ) { | |
619 | WARN_ON(1); | |
620 | return -EINVAL; | |
621 | } | |
622 | memset(ar->bmi.cmd_buf, 0, size); | |
623 | ||
624 | ath6kl_dbg(ATH6KL_DBG_BMI, | |
625 | "bmi LZ stream start: addr: 0x%x)\n", | |
626 | addr); | |
627 | ||
628 | offset = 0; | |
629 | memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); | |
630 | offset += sizeof(cid); | |
631 | memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); | |
632 | offset += sizeof(addr); | |
633 | ||
634 | ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); | |
635 | if (ret) { | |
636 | ath6kl_err("Unable to start LZ stream to the device: %d\n", | |
637 | ret); | |
638 | return ret; | |
639 | } | |
640 | ||
641 | return 0; | |
642 | } | |
643 | ||
644 | int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) | |
645 | { | |
646 | int ret; | |
647 | u32 last_word = 0; | |
648 | u32 last_word_offset = len & ~0x3; | |
649 | u32 unaligned_bytes = len & 0x3; | |
650 | ||
651 | ret = ath6kl_bmi_lz_stream_start(ar, addr); | |
652 | if (ret) | |
653 | return ret; | |
654 | ||
655 | if (unaligned_bytes) { | |
656 | /* copy the last word into a zero padded buffer */ | |
657 | memcpy(&last_word, &buf[last_word_offset], unaligned_bytes); | |
658 | } | |
659 | ||
660 | ret = ath6kl_bmi_lz_data(ar, buf, last_word_offset); | |
661 | if (ret) | |
662 | return ret; | |
663 | ||
664 | if (unaligned_bytes) | |
665 | ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 4); | |
666 | ||
667 | if (!ret) { | |
668 | /* Close compressed stream and open a new (fake) one. | |
669 | * This serves mainly to flush Target caches. */ | |
670 | ret = ath6kl_bmi_lz_stream_start(ar, 0x00); | |
671 | } | |
672 | return ret; | |
673 | } | |
674 | ||
675 | int ath6kl_bmi_init(struct ath6kl *ar) | |
676 | { | |
677 | ar->bmi.cmd_buf = kzalloc(MAX_BMI_CMDBUF_SZ, GFP_ATOMIC); | |
678 | ||
679 | if (!ar->bmi.cmd_buf) | |
680 | return -ENOMEM; | |
681 | ||
682 | return 0; | |
683 | } | |
684 | ||
685 | void ath6kl_bmi_cleanup(struct ath6kl *ar) | |
686 | { | |
687 | kfree(ar->bmi.cmd_buf); | |
688 | ar->bmi.cmd_buf = NULL; | |
689 | } |