brintos

brintos / linux-shallow public Read only

0
0
Text · 12.8 KiB · af98e87 Raw
549 lines · 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 any6 * purpose with or without fee is hereby granted, provided that the above7 * copyright notice and this permission notice appear in all copies.8 *9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF15 * 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_KERNEL);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