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