1 /*
2 * Copyright (c) 2004-2011 Atheros Communications Inc.
3 * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include "core.h"
19 #include "hif-ops.h"
20 #include "target.h"
21 #include "debug.h"
22
ath6kl_bmi_done(struct ath6kl * ar)23 int ath6kl_bmi_done(struct ath6kl *ar)
24 {
25 int ret;
26 u32 cid = BMI_DONE;
27
28 if (ar->bmi.done_sent) {
29 ath6kl_dbg(ATH6KL_DBG_BMI, "bmi done skipped\n");
30 return 0;
31 }
32
33 ar->bmi.done_sent = true;
34
35 ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid));
36 if (ret) {
37 ath6kl_err("Unable to send bmi done: %d\n", ret);
38 return ret;
39 }
40
41 return 0;
42 }
43
ath6kl_bmi_get_target_info(struct ath6kl * ar,struct ath6kl_bmi_target_info * targ_info)44 int ath6kl_bmi_get_target_info(struct ath6kl *ar,
45 struct ath6kl_bmi_target_info *targ_info)
46 {
47 int ret;
48 u32 cid = BMI_GET_TARGET_INFO;
49
50 if (ar->bmi.done_sent) {
51 ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
52 return -EACCES;
53 }
54
55 ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid));
56 if (ret) {
57 ath6kl_err("Unable to send get target info: %d\n", ret);
58 return ret;
59 }
60
61 if (ar->hif_type == ATH6KL_HIF_TYPE_USB) {
62 ret = ath6kl_hif_bmi_read(ar, (u8 *)targ_info,
63 sizeof(*targ_info));
64 } else {
65 ret = ath6kl_hif_bmi_read(ar, (u8 *)&targ_info->version,
66 sizeof(targ_info->version));
67 }
68
69 if (ret) {
70 ath6kl_err("Unable to recv target info: %d\n", ret);
71 return ret;
72 }
73
74 if (le32_to_cpu(targ_info->version) == TARGET_VERSION_SENTINAL) {
75 /* Determine how many bytes are in the Target's targ_info */
76 ret = ath6kl_hif_bmi_read(ar,
77 (u8 *)&targ_info->byte_count,
78 sizeof(targ_info->byte_count));
79 if (ret) {
80 ath6kl_err("unable to read target info byte count: %d\n",
81 ret);
82 return ret;
83 }
84
85 /*
86 * The target's targ_info doesn't match the host's targ_info.
87 * We need to do some backwards compatibility to make this work.
88 */
89 if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) {
90 ath6kl_err("mismatched byte count %d vs. expected %zd\n",
91 le32_to_cpu(targ_info->byte_count),
92 sizeof(*targ_info));
93 return -EINVAL;
94 }
95
96 /* Read the remainder of the targ_info */
97 ret = ath6kl_hif_bmi_read(ar,
98 ((u8 *)targ_info) +
99 sizeof(targ_info->byte_count),
100 sizeof(*targ_info) -
101 sizeof(targ_info->byte_count));
102
103 if (ret) {
104 ath6kl_err("Unable to read target info (%d bytes): %d\n",
105 targ_info->byte_count, ret);
106 return ret;
107 }
108 }
109
110 ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n",
111 targ_info->version, targ_info->type);
112
113 return 0;
114 }
115
ath6kl_bmi_read(struct ath6kl * ar,u32 addr,u8 * buf,u32 len)116 int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
117 {
118 u32 cid = BMI_READ_MEMORY;
119 int ret;
120 u32 offset;
121 u32 len_remain, rx_len;
122 u16 size;
123
124 if (ar->bmi.done_sent) {
125 ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
126 return -EACCES;
127 }
128
129 size = ar->bmi.max_data_size + sizeof(cid) + sizeof(addr) + sizeof(len);
130 if (size > ar->bmi.max_cmd_size) {
131 WARN_ON(1);
132 return -EINVAL;
133 }
134 memset(ar->bmi.cmd_buf, 0, size);
135
136 ath6kl_dbg(ATH6KL_DBG_BMI,
137 "bmi read memory: device: addr: 0x%x, len: %d\n",
138 addr, len);
139
140 len_remain = len;
141
142 while (len_remain) {
143 rx_len = (len_remain < ar->bmi.max_data_size) ?
144 len_remain : ar->bmi.max_data_size;
145 offset = 0;
146 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
147 offset += sizeof(cid);
148 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
149 offset += sizeof(addr);
150 memcpy(&(ar->bmi.cmd_buf[offset]), &rx_len, sizeof(rx_len));
151 offset += sizeof(len);
152
153 ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
154 if (ret) {
155 ath6kl_err("Unable to write to the device: %d\n",
156 ret);
157 return ret;
158 }
159 ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, rx_len);
160 if (ret) {
161 ath6kl_err("Unable to read from the device: %d\n",
162 ret);
163 return ret;
164 }
165 memcpy(&buf[len - len_remain], ar->bmi.cmd_buf, rx_len);
166 len_remain -= rx_len; addr += rx_len;
167 }
168
169 return 0;
170 }
171
ath6kl_bmi_write(struct ath6kl * ar,u32 addr,u8 * buf,u32 len)172 int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
173 {
174 u32 cid = BMI_WRITE_MEMORY;
175 int ret;
176 u32 offset;
177 u32 len_remain, tx_len;
178 const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len);
179 u8 aligned_buf[400];
180 u8 *src;
181
182 if (ar->bmi.done_sent) {
183 ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
184 return -EACCES;
185 }
186
187 if ((ar->bmi.max_data_size + header) > ar->bmi.max_cmd_size) {
188 WARN_ON(1);
189 return -EINVAL;
190 }
191
192 if (WARN_ON(ar->bmi.max_data_size > sizeof(aligned_buf)))
193 return -E2BIG;
194
195 memset(ar->bmi.cmd_buf, 0, ar->bmi.max_data_size + header);
196
197 ath6kl_dbg(ATH6KL_DBG_BMI,
198 "bmi write memory: addr: 0x%x, len: %d\n", addr, len);
199
200 len_remain = len;
201 while (len_remain) {
202 src = &buf[len - len_remain];
203
204 if (len_remain < (ar->bmi.max_data_size - header)) {
205 if (len_remain & 3) {
206 /* align it with 4 bytes */
207 len_remain = len_remain +
208 (4 - (len_remain & 3));
209 memcpy(aligned_buf, src, len_remain);
210 src = aligned_buf;
211 }
212 tx_len = len_remain;
213 } else {
214 tx_len = (ar->bmi.max_data_size - header);
215 }
216
217 offset = 0;
218 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
219 offset += sizeof(cid);
220 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
221 offset += sizeof(addr);
222 memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
223 offset += sizeof(tx_len);
224 memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len);
225 offset += tx_len;
226
227 ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
228 if (ret) {
229 ath6kl_err("Unable to write to the device: %d\n",
230 ret);
231 return ret;
232 }
233 len_remain -= tx_len; addr += tx_len;
234 }
235
236 return 0;
237 }
238
ath6kl_bmi_execute(struct ath6kl * ar,u32 addr,u32 * param)239 int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param)
240 {
241 u32 cid = BMI_EXECUTE;
242 int ret;
243 u32 offset;
244 u16 size;
245
246 if (ar->bmi.done_sent) {
247 ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
248 return -EACCES;
249 }
250
251 size = sizeof(cid) + sizeof(addr) + sizeof(*param);
252 if (size > ar->bmi.max_cmd_size) {
253 WARN_ON(1);
254 return -EINVAL;
255 }
256 memset(ar->bmi.cmd_buf, 0, size);
257
258 ath6kl_dbg(ATH6KL_DBG_BMI, "bmi execute: addr: 0x%x, param: %d)\n",
259 addr, *param);
260
261 offset = 0;
262 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
263 offset += sizeof(cid);
264 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
265 offset += sizeof(addr);
266 memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param));
267 offset += sizeof(*param);
268
269 ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
270 if (ret) {
271 ath6kl_err("Unable to write to the device: %d\n", ret);
272 return ret;
273 }
274
275 ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param));
276 if (ret) {
277 ath6kl_err("Unable to read from the device: %d\n", ret);
278 return ret;
279 }
280
281 memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
282
283 return 0;
284 }
285
ath6kl_bmi_set_app_start(struct ath6kl * ar,u32 addr)286 int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr)
287 {
288 u32 cid = BMI_SET_APP_START;
289 int ret;
290 u32 offset;
291 u16 size;
292
293 if (ar->bmi.done_sent) {
294 ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
295 return -EACCES;
296 }
297
298 size = sizeof(cid) + sizeof(addr);
299 if (size > ar->bmi.max_cmd_size) {
300 WARN_ON(1);
301 return -EINVAL;
302 }
303 memset(ar->bmi.cmd_buf, 0, size);
304
305 ath6kl_dbg(ATH6KL_DBG_BMI, "bmi set app start: addr: 0x%x\n", addr);
306
307 offset = 0;
308 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
309 offset += sizeof(cid);
310 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
311 offset += sizeof(addr);
312
313 ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
314 if (ret) {
315 ath6kl_err("Unable to write to the device: %d\n", ret);
316 return ret;
317 }
318
319 return 0;
320 }
321
ath6kl_bmi_reg_read(struct ath6kl * ar,u32 addr,u32 * param)322 int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param)
323 {
324 u32 cid = BMI_READ_SOC_REGISTER;
325 int ret;
326 u32 offset;
327 u16 size;
328
329 if (ar->bmi.done_sent) {
330 ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
331 return -EACCES;
332 }
333
334 size = sizeof(cid) + sizeof(addr);
335 if (size > ar->bmi.max_cmd_size) {
336 WARN_ON(1);
337 return -EINVAL;
338 }
339 memset(ar->bmi.cmd_buf, 0, size);
340
341 ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr);
342
343 offset = 0;
344 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
345 offset += sizeof(cid);
346 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
347 offset += sizeof(addr);
348
349 ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
350 if (ret) {
351 ath6kl_err("Unable to write to the device: %d\n", ret);
352 return ret;
353 }
354
355 ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param));
356 if (ret) {
357 ath6kl_err("Unable to read from the device: %d\n", ret);
358 return ret;
359 }
360 memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
361
362 return 0;
363 }
364
ath6kl_bmi_reg_write(struct ath6kl * ar,u32 addr,u32 param)365 int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param)
366 {
367 u32 cid = BMI_WRITE_SOC_REGISTER;
368 int ret;
369 u32 offset;
370 u16 size;
371
372 if (ar->bmi.done_sent) {
373 ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
374 return -EACCES;
375 }
376
377 size = sizeof(cid) + sizeof(addr) + sizeof(param);
378 if (size > ar->bmi.max_cmd_size) {
379 WARN_ON(1);
380 return -EINVAL;
381 }
382 memset(ar->bmi.cmd_buf, 0, size);
383
384 ath6kl_dbg(ATH6KL_DBG_BMI,
385 "bmi write SOC reg: addr: 0x%x, param: %d\n",
386 addr, param);
387
388 offset = 0;
389 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
390 offset += sizeof(cid);
391 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
392 offset += sizeof(addr);
393 memcpy(&(ar->bmi.cmd_buf[offset]), ¶m, sizeof(param));
394 offset += sizeof(param);
395
396 ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
397 if (ret) {
398 ath6kl_err("Unable to write to the device: %d\n", ret);
399 return ret;
400 }
401
402 return 0;
403 }
404
ath6kl_bmi_lz_data(struct ath6kl * ar,u8 * buf,u32 len)405 int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len)
406 {
407 u32 cid = BMI_LZ_DATA;
408 int ret;
409 u32 offset;
410 u32 len_remain, tx_len;
411 const u32 header = sizeof(cid) + sizeof(len);
412 u16 size;
413
414 if (ar->bmi.done_sent) {
415 ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
416 return -EACCES;
417 }
418
419 size = ar->bmi.max_data_size + header;
420 if (size > ar->bmi.max_cmd_size) {
421 WARN_ON(1);
422 return -EINVAL;
423 }
424 memset(ar->bmi.cmd_buf, 0, size);
425
426 ath6kl_dbg(ATH6KL_DBG_BMI, "bmi send LZ data: len: %d)\n",
427 len);
428
429 len_remain = len;
430 while (len_remain) {
431 tx_len = (len_remain < (ar->bmi.max_data_size - header)) ?
432 len_remain : (ar->bmi.max_data_size - header);
433
434 offset = 0;
435 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
436 offset += sizeof(cid);
437 memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
438 offset += sizeof(tx_len);
439 memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain],
440 tx_len);
441 offset += tx_len;
442
443 ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
444 if (ret) {
445 ath6kl_err("Unable to write to the device: %d\n",
446 ret);
447 return ret;
448 }
449
450 len_remain -= tx_len;
451 }
452
453 return 0;
454 }
455
ath6kl_bmi_lz_stream_start(struct ath6kl * ar,u32 addr)456 int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr)
457 {
458 u32 cid = BMI_LZ_STREAM_START;
459 int ret;
460 u32 offset;
461 u16 size;
462
463 if (ar->bmi.done_sent) {
464 ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
465 return -EACCES;
466 }
467
468 size = sizeof(cid) + sizeof(addr);
469 if (size > ar->bmi.max_cmd_size) {
470 WARN_ON(1);
471 return -EINVAL;
472 }
473 memset(ar->bmi.cmd_buf, 0, size);
474
475 ath6kl_dbg(ATH6KL_DBG_BMI,
476 "bmi LZ stream start: addr: 0x%x)\n",
477 addr);
478
479 offset = 0;
480 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
481 offset += sizeof(cid);
482 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
483 offset += sizeof(addr);
484
485 ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
486 if (ret) {
487 ath6kl_err("Unable to start LZ stream to the device: %d\n",
488 ret);
489 return ret;
490 }
491
492 return 0;
493 }
494
ath6kl_bmi_fast_download(struct ath6kl * ar,u32 addr,u8 * buf,u32 len)495 int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
496 {
497 int ret;
498 u32 last_word = 0;
499 u32 last_word_offset = len & ~0x3;
500 u32 unaligned_bytes = len & 0x3;
501
502 ret = ath6kl_bmi_lz_stream_start(ar, addr);
503 if (ret)
504 return ret;
505
506 if (unaligned_bytes) {
507 /* copy the last word into a zero padded buffer */
508 memcpy(&last_word, &buf[last_word_offset], unaligned_bytes);
509 }
510
511 ret = ath6kl_bmi_lz_data(ar, buf, last_word_offset);
512 if (ret)
513 return ret;
514
515 if (unaligned_bytes)
516 ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 4);
517
518 if (!ret) {
519 /* Close compressed stream and open a new (fake) one.
520 * This serves mainly to flush Target caches. */
521 ret = ath6kl_bmi_lz_stream_start(ar, 0x00);
522 }
523 return ret;
524 }
525
ath6kl_bmi_reset(struct ath6kl * ar)526 void ath6kl_bmi_reset(struct ath6kl *ar)
527 {
528 ar->bmi.done_sent = false;
529 }
530
ath6kl_bmi_init(struct ath6kl * ar)531 int ath6kl_bmi_init(struct ath6kl *ar)
532 {
533 if (WARN_ON(ar->bmi.max_data_size == 0))
534 return -EINVAL;
535
536 /* cmd + addr + len + data_size */
537 ar->bmi.max_cmd_size = ar->bmi.max_data_size + (sizeof(u32) * 3);
538
539 ar->bmi.cmd_buf = kzalloc(ar->bmi.max_cmd_size, GFP_KERNEL);
540 if (!ar->bmi.cmd_buf)
541 return -ENOMEM;
542
543 return 0;
544 }
545
ath6kl_bmi_cleanup(struct ath6kl * ar)546 void ath6kl_bmi_cleanup(struct ath6kl *ar)
547 {
548 kfree(ar->bmi.cmd_buf);
549 ar->bmi.cmd_buf = NULL;
550 }
551