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