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
23int 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(mask: ATH6KL_DBG_BMI, fmt: "bmi done skipped\n");
30 return 0;
31 }
32
33 ar->bmi.done_sent = true;
34
35 ret = ath6kl_hif_bmi_write(ar, buf: (u8 *)&cid, len: sizeof(cid));
36 if (ret) {
37 ath6kl_err(fmt: "Unable to send bmi done: %d\n", ret);
38 return ret;
39 }
40
41 return 0;
42}
43
44int 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(fmt: "bmi done sent already, cmd %d disallowed\n", cid);
52 return -EACCES;
53 }
54
55 ret = ath6kl_hif_bmi_write(ar, buf: (u8 *)&cid, len: sizeof(cid));
56 if (ret) {
57 ath6kl_err(fmt: "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, buf: (u8 *)targ_info,
63 len: sizeof(*targ_info));
64 } else {
65 ret = ath6kl_hif_bmi_read(ar, buf: (u8 *)&targ_info->version,
66 len: sizeof(targ_info->version));
67 }
68
69 if (ret) {
70 ath6kl_err(fmt: "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 buf: (u8 *)&targ_info->byte_count,
78 len: sizeof(targ_info->byte_count));
79 if (ret) {
80 ath6kl_err(fmt: "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 WARN_ON(1);
91 return -EINVAL;
92 }
93
94 /* Read the remainder of the targ_info */
95 ret = ath6kl_hif_bmi_read(ar,
96 buf: ((u8 *)targ_info) +
97 sizeof(targ_info->byte_count),
98 len: sizeof(*targ_info) -
99 sizeof(targ_info->byte_count));
100
101 if (ret) {
102 ath6kl_err(fmt: "Unable to read target info (%d bytes): %d\n",
103 targ_info->byte_count, ret);
104 return ret;
105 }
106 }
107
108 ath6kl_dbg(mask: ATH6KL_DBG_BMI, fmt: "target info (ver: 0x%x type: 0x%x)\n",
109 targ_info->version, targ_info->type);
110
111 return 0;
112}
113
114int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
115{
116 u32 cid = BMI_READ_MEMORY;
117 int ret;
118 u32 offset;
119 u32 len_remain, rx_len;
120 u16 size;
121
122 if (ar->bmi.done_sent) {
123 ath6kl_err(fmt: "bmi done sent already, cmd %d disallowed\n", cid);
124 return -EACCES;
125 }
126
127 size = ar->bmi.max_data_size + sizeof(cid) + sizeof(addr) + sizeof(len);
128 if (size > ar->bmi.max_cmd_size) {
129 WARN_ON(1);
130 return -EINVAL;
131 }
132 memset(ar->bmi.cmd_buf, 0, size);
133
134 ath6kl_dbg(mask: ATH6KL_DBG_BMI,
135 fmt: "bmi read memory: device: addr: 0x%x, len: %d\n",
136 addr, len);
137
138 len_remain = len;
139
140 while (len_remain) {
141 rx_len = (len_remain < ar->bmi.max_data_size) ?
142 len_remain : ar->bmi.max_data_size;
143 offset = 0;
144 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
145 offset += sizeof(cid);
146 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
147 offset += sizeof(addr);
148 memcpy(&(ar->bmi.cmd_buf[offset]), &rx_len, sizeof(rx_len));
149 offset += sizeof(len);
150
151 ret = ath6kl_hif_bmi_write(ar, buf: ar->bmi.cmd_buf, len: offset);
152 if (ret) {
153 ath6kl_err(fmt: "Unable to write to the device: %d\n",
154 ret);
155 return ret;
156 }
157 ret = ath6kl_hif_bmi_read(ar, buf: ar->bmi.cmd_buf, len: rx_len);
158 if (ret) {
159 ath6kl_err(fmt: "Unable to read from the device: %d\n",
160 ret);
161 return ret;
162 }
163 memcpy(&buf[len - len_remain], ar->bmi.cmd_buf, rx_len);
164 len_remain -= rx_len; addr += rx_len;
165 }
166
167 return 0;
168}
169
170int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
171{
172 u32 cid = BMI_WRITE_MEMORY;
173 int ret;
174 u32 offset;
175 u32 len_remain, tx_len;
176 const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len);
177 u8 aligned_buf[400];
178 u8 *src;
179
180 if (ar->bmi.done_sent) {
181 ath6kl_err(fmt: "bmi done sent already, cmd %d disallowed\n", cid);
182 return -EACCES;
183 }
184
185 if ((ar->bmi.max_data_size + header) > ar->bmi.max_cmd_size) {
186 WARN_ON(1);
187 return -EINVAL;
188 }
189
190 if (WARN_ON(ar->bmi.max_data_size > sizeof(aligned_buf)))
191 return -E2BIG;
192
193 memset(ar->bmi.cmd_buf, 0, ar->bmi.max_data_size + header);
194
195 ath6kl_dbg(mask: ATH6KL_DBG_BMI,
196 fmt: "bmi write memory: addr: 0x%x, len: %d\n", addr, len);
197
198 len_remain = len;
199 while (len_remain) {
200 src = &buf[len - len_remain];
201
202 if (len_remain < (ar->bmi.max_data_size - header)) {
203 if (len_remain & 3) {
204 /* align it with 4 bytes */
205 len_remain = len_remain +
206 (4 - (len_remain & 3));
207 memcpy(aligned_buf, src, len_remain);
208 src = aligned_buf;
209 }
210 tx_len = len_remain;
211 } else {
212 tx_len = (ar->bmi.max_data_size - header);
213 }
214
215 offset = 0;
216 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
217 offset += sizeof(cid);
218 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
219 offset += sizeof(addr);
220 memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
221 offset += sizeof(tx_len);
222 memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len);
223 offset += tx_len;
224
225 ret = ath6kl_hif_bmi_write(ar, buf: ar->bmi.cmd_buf, len: offset);
226 if (ret) {
227 ath6kl_err(fmt: "Unable to write to the device: %d\n",
228 ret);
229 return ret;
230 }
231 len_remain -= tx_len; addr += tx_len;
232 }
233
234 return 0;
235}
236
237int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param)
238{
239 u32 cid = BMI_EXECUTE;
240 int ret;
241 u32 offset;
242 u16 size;
243
244 if (ar->bmi.done_sent) {
245 ath6kl_err(fmt: "bmi done sent already, cmd %d disallowed\n", cid);
246 return -EACCES;
247 }
248
249 size = sizeof(cid) + sizeof(addr) + sizeof(*param);
250 if (size > ar->bmi.max_cmd_size) {
251 WARN_ON(1);
252 return -EINVAL;
253 }
254 memset(ar->bmi.cmd_buf, 0, size);
255
256 ath6kl_dbg(mask: ATH6KL_DBG_BMI, fmt: "bmi execute: addr: 0x%x, param: %d)\n",
257 addr, *param);
258
259 offset = 0;
260 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
261 offset += sizeof(cid);
262 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
263 offset += sizeof(addr);
264 memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param));
265 offset += sizeof(*param);
266
267 ret = ath6kl_hif_bmi_write(ar, buf: ar->bmi.cmd_buf, len: offset);
268 if (ret) {
269 ath6kl_err(fmt: "Unable to write to the device: %d\n", ret);
270 return ret;
271 }
272
273 ret = ath6kl_hif_bmi_read(ar, buf: ar->bmi.cmd_buf, len: sizeof(*param));
274 if (ret) {
275 ath6kl_err(fmt: "Unable to read from the device: %d\n", ret);
276 return ret;
277 }
278
279 memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
280
281 return 0;
282}
283
284int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr)
285{
286 u32 cid = BMI_SET_APP_START;
287 int ret;
288 u32 offset;
289 u16 size;
290
291 if (ar->bmi.done_sent) {
292 ath6kl_err(fmt: "bmi done sent already, cmd %d disallowed\n", cid);
293 return -EACCES;
294 }
295
296 size = sizeof(cid) + sizeof(addr);
297 if (size > ar->bmi.max_cmd_size) {
298 WARN_ON(1);
299 return -EINVAL;
300 }
301 memset(ar->bmi.cmd_buf, 0, size);
302
303 ath6kl_dbg(mask: ATH6KL_DBG_BMI, fmt: "bmi set app start: addr: 0x%x\n", addr);
304
305 offset = 0;
306 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
307 offset += sizeof(cid);
308 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
309 offset += sizeof(addr);
310
311 ret = ath6kl_hif_bmi_write(ar, buf: ar->bmi.cmd_buf, len: offset);
312 if (ret) {
313 ath6kl_err(fmt: "Unable to write to the device: %d\n", ret);
314 return ret;
315 }
316
317 return 0;
318}
319
320int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param)
321{
322 u32 cid = BMI_READ_SOC_REGISTER;
323 int ret;
324 u32 offset;
325 u16 size;
326
327 if (ar->bmi.done_sent) {
328 ath6kl_err(fmt: "bmi done sent already, cmd %d disallowed\n", cid);
329 return -EACCES;
330 }
331
332 size = sizeof(cid) + sizeof(addr);
333 if (size > ar->bmi.max_cmd_size) {
334 WARN_ON(1);
335 return -EINVAL;
336 }
337 memset(ar->bmi.cmd_buf, 0, size);
338
339 ath6kl_dbg(mask: ATH6KL_DBG_BMI, fmt: "bmi read SOC reg: addr: 0x%x\n", addr);
340
341 offset = 0;
342 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
343 offset += sizeof(cid);
344 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
345 offset += sizeof(addr);
346
347 ret = ath6kl_hif_bmi_write(ar, buf: ar->bmi.cmd_buf, len: offset);
348 if (ret) {
349 ath6kl_err(fmt: "Unable to write to the device: %d\n", ret);
350 return ret;
351 }
352
353 ret = ath6kl_hif_bmi_read(ar, buf: ar->bmi.cmd_buf, len: sizeof(*param));
354 if (ret) {
355 ath6kl_err(fmt: "Unable to read from the device: %d\n", ret);
356 return ret;
357 }
358 memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
359
360 return 0;
361}
362
363int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param)
364{
365 u32 cid = BMI_WRITE_SOC_REGISTER;
366 int ret;
367 u32 offset;
368 u16 size;
369
370 if (ar->bmi.done_sent) {
371 ath6kl_err(fmt: "bmi done sent already, cmd %d disallowed\n", cid);
372 return -EACCES;
373 }
374
375 size = sizeof(cid) + sizeof(addr) + sizeof(param);
376 if (size > ar->bmi.max_cmd_size) {
377 WARN_ON(1);
378 return -EINVAL;
379 }
380 memset(ar->bmi.cmd_buf, 0, size);
381
382 ath6kl_dbg(mask: ATH6KL_DBG_BMI,
383 fmt: "bmi write SOC reg: addr: 0x%x, param: %d\n",
384 addr, param);
385
386 offset = 0;
387 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
388 offset += sizeof(cid);
389 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
390 offset += sizeof(addr);
391 memcpy(&(ar->bmi.cmd_buf[offset]), &param, sizeof(param));
392 offset += sizeof(param);
393
394 ret = ath6kl_hif_bmi_write(ar, buf: ar->bmi.cmd_buf, len: offset);
395 if (ret) {
396 ath6kl_err(fmt: "Unable to write to the device: %d\n", ret);
397 return ret;
398 }
399
400 return 0;
401}
402
403int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len)
404{
405 u32 cid = BMI_LZ_DATA;
406 int ret;
407 u32 offset;
408 u32 len_remain, tx_len;
409 const u32 header = sizeof(cid) + sizeof(len);
410 u16 size;
411
412 if (ar->bmi.done_sent) {
413 ath6kl_err(fmt: "bmi done sent already, cmd %d disallowed\n", cid);
414 return -EACCES;
415 }
416
417 size = ar->bmi.max_data_size + header;
418 if (size > ar->bmi.max_cmd_size) {
419 WARN_ON(1);
420 return -EINVAL;
421 }
422 memset(ar->bmi.cmd_buf, 0, size);
423
424 ath6kl_dbg(mask: ATH6KL_DBG_BMI, fmt: "bmi send LZ data: len: %d)\n",
425 len);
426
427 len_remain = len;
428 while (len_remain) {
429 tx_len = (len_remain < (ar->bmi.max_data_size - header)) ?
430 len_remain : (ar->bmi.max_data_size - header);
431
432 offset = 0;
433 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
434 offset += sizeof(cid);
435 memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
436 offset += sizeof(tx_len);
437 memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain],
438 tx_len);
439 offset += tx_len;
440
441 ret = ath6kl_hif_bmi_write(ar, buf: ar->bmi.cmd_buf, len: offset);
442 if (ret) {
443 ath6kl_err(fmt: "Unable to write to the device: %d\n",
444 ret);
445 return ret;
446 }
447
448 len_remain -= tx_len;
449 }
450
451 return 0;
452}
453
454int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr)
455{
456 u32 cid = BMI_LZ_STREAM_START;
457 int ret;
458 u32 offset;
459 u16 size;
460
461 if (ar->bmi.done_sent) {
462 ath6kl_err(fmt: "bmi done sent already, cmd %d disallowed\n", cid);
463 return -EACCES;
464 }
465
466 size = sizeof(cid) + sizeof(addr);
467 if (size > ar->bmi.max_cmd_size) {
468 WARN_ON(1);
469 return -EINVAL;
470 }
471 memset(ar->bmi.cmd_buf, 0, size);
472
473 ath6kl_dbg(mask: ATH6KL_DBG_BMI,
474 fmt: "bmi LZ stream start: addr: 0x%x)\n",
475 addr);
476
477 offset = 0;
478 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
479 offset += sizeof(cid);
480 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
481 offset += sizeof(addr);
482
483 ret = ath6kl_hif_bmi_write(ar, buf: ar->bmi.cmd_buf, len: offset);
484 if (ret) {
485 ath6kl_err(fmt: "Unable to start LZ stream to the device: %d\n",
486 ret);
487 return ret;
488 }
489
490 return 0;
491}
492
493int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
494{
495 int ret;
496 u32 last_word = 0;
497 u32 last_word_offset = len & ~0x3;
498 u32 unaligned_bytes = len & 0x3;
499
500 ret = ath6kl_bmi_lz_stream_start(ar, addr);
501 if (ret)
502 return ret;
503
504 if (unaligned_bytes) {
505 /* copy the last word into a zero padded buffer */
506 memcpy(&last_word, &buf[last_word_offset], unaligned_bytes);
507 }
508
509 ret = ath6kl_bmi_lz_data(ar, buf, len: last_word_offset);
510 if (ret)
511 return ret;
512
513 if (unaligned_bytes)
514 ret = ath6kl_bmi_lz_data(ar, buf: (u8 *)&last_word, len: 4);
515
516 if (!ret) {
517 /* Close compressed stream and open a new (fake) one.
518 * This serves mainly to flush Target caches. */
519 ret = ath6kl_bmi_lz_stream_start(ar, addr: 0x00);
520 }
521 return ret;
522}
523
524void ath6kl_bmi_reset(struct ath6kl *ar)
525{
526 ar->bmi.done_sent = false;
527}
528
529int ath6kl_bmi_init(struct ath6kl *ar)
530{
531 if (WARN_ON(ar->bmi.max_data_size == 0))
532 return -EINVAL;
533
534 /* cmd + addr + len + data_size */
535 ar->bmi.max_cmd_size = ar->bmi.max_data_size + (sizeof(u32) * 3);
536
537 ar->bmi.cmd_buf = kzalloc(size: ar->bmi.max_cmd_size, GFP_KERNEL);
538 if (!ar->bmi.cmd_buf)
539 return -ENOMEM;
540
541 return 0;
542}
543
544void ath6kl_bmi_cleanup(struct ath6kl *ar)
545{
546 kfree(objp: ar->bmi.cmd_buf);
547 ar->bmi.cmd_buf = NULL;
548}
549

source code of linux/drivers/net/wireless/ath/ath6kl/bmi.c