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