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(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
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("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			WARN_ON(1);
91			return -EINVAL;
92		}
93
94		/* Read the remainder of the targ_info */
95		ret = ath6kl_hif_bmi_read(ar,
96				   ((u8 *)targ_info) +
97				   sizeof(targ_info->byte_count),
98				   sizeof(*targ_info) -
99				   sizeof(targ_info->byte_count));
100
101		if (ret) {
102			ath6kl_err("Unable to read target info (%d bytes): %d\n",
103				   targ_info->byte_count, ret);
104			return ret;
105		}
106	}
107
108	ath6kl_dbg(ATH6KL_DBG_BMI, "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("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(ATH6KL_DBG_BMI,
135		   "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, ar->bmi.cmd_buf, offset);
152		if (ret) {
153			ath6kl_err("Unable to write to the device: %d\n",
154				   ret);
155			return ret;
156		}
157		ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, rx_len);
158		if (ret) {
159			ath6kl_err("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("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(ATH6KL_DBG_BMI,
196		   "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, ar->bmi.cmd_buf, offset);
226		if (ret) {
227			ath6kl_err("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("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(ATH6KL_DBG_BMI, "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, ar->bmi.cmd_buf, offset);
268	if (ret) {
269		ath6kl_err("Unable to write to the device: %d\n", ret);
270		return ret;
271	}
272
273	ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param));
274	if (ret) {
275		ath6kl_err("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("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(ATH6KL_DBG_BMI, "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, ar->bmi.cmd_buf, offset);
312	if (ret) {
313		ath6kl_err("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("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(ATH6KL_DBG_BMI, "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, ar->bmi.cmd_buf, offset);
348	if (ret) {
349		ath6kl_err("Unable to write to the device: %d\n", ret);
350		return ret;
351	}
352
353	ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param));
354	if (ret) {
355		ath6kl_err("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("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(ATH6KL_DBG_BMI,
383		   "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, ar->bmi.cmd_buf, offset);
395	if (ret) {
396		ath6kl_err("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("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(ATH6KL_DBG_BMI, "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, ar->bmi.cmd_buf, offset);
442		if (ret) {
443			ath6kl_err("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("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(ATH6KL_DBG_BMI,
474		   "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, ar->bmi.cmd_buf, offset);
484	if (ret) {
485		ath6kl_err("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, last_word_offset);
510	if (ret)
511		return ret;
512
513	if (unaligned_bytes)
514		ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 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, 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(ar->bmi.max_cmd_size, GFP_ATOMIC);
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(ar->bmi.cmd_buf);
547	ar->bmi.cmd_buf = NULL;
548}
549