File Index
Files and directories
Sign in
FILE

bmi.c

File preview with syntax highlighting.

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