xref: /openbmc/linux/drivers/net/wireless/ath/ath10k/bmi.c (revision c4ee0af3)
1 /*
2  * Copyright (c) 2005-2011 Atheros Communications Inc.
3  * Copyright (c) 2011-2013 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 "bmi.h"
19 #include "hif.h"
20 #include "debug.h"
21 #include "htc.h"
22 
23 void ath10k_bmi_start(struct ath10k *ar)
24 {
25 	ath10k_dbg(ATH10K_DBG_BMI, "bmi start\n");
26 
27 	ar->bmi.done_sent = false;
28 }
29 
30 int ath10k_bmi_done(struct ath10k *ar)
31 {
32 	struct bmi_cmd cmd;
33 	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done);
34 	int ret;
35 
36 	ath10k_dbg(ATH10K_DBG_BMI, "bmi done\n");
37 
38 	if (ar->bmi.done_sent) {
39 		ath10k_dbg(ATH10K_DBG_BMI, "bmi skipped\n");
40 		return 0;
41 	}
42 
43 	ar->bmi.done_sent = true;
44 	cmd.id = __cpu_to_le32(BMI_DONE);
45 
46 	ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
47 	if (ret) {
48 		ath10k_warn("unable to write to the device: %d\n", ret);
49 		return ret;
50 	}
51 
52 	return 0;
53 }
54 
55 int ath10k_bmi_get_target_info(struct ath10k *ar,
56 			       struct bmi_target_info *target_info)
57 {
58 	struct bmi_cmd cmd;
59 	union bmi_resp resp;
60 	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);
61 	u32 resplen = sizeof(resp.get_target_info);
62 	int ret;
63 
64 	ath10k_dbg(ATH10K_DBG_BMI, "bmi get target info\n");
65 
66 	if (ar->bmi.done_sent) {
67 		ath10k_warn("BMI Get Target Info Command disallowed\n");
68 		return -EBUSY;
69 	}
70 
71 	cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);
72 
73 	ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
74 	if (ret) {
75 		ath10k_warn("unable to get target info from device\n");
76 		return ret;
77 	}
78 
79 	if (resplen < sizeof(resp.get_target_info)) {
80 		ath10k_warn("invalid get_target_info response length (%d)\n",
81 			    resplen);
82 		return -EIO;
83 	}
84 
85 	target_info->version = __le32_to_cpu(resp.get_target_info.version);
86 	target_info->type    = __le32_to_cpu(resp.get_target_info.type);
87 
88 	return 0;
89 }
90 
91 int ath10k_bmi_read_memory(struct ath10k *ar,
92 			   u32 address, void *buffer, u32 length)
93 {
94 	struct bmi_cmd cmd;
95 	union bmi_resp resp;
96 	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_mem);
97 	u32 rxlen;
98 	int ret;
99 
100 	ath10k_dbg(ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n",
101 		   address, length);
102 
103 	if (ar->bmi.done_sent) {
104 		ath10k_warn("command disallowed\n");
105 		return -EBUSY;
106 	}
107 
108 	while (length) {
109 		rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE);
110 
111 		cmd.id            = __cpu_to_le32(BMI_READ_MEMORY);
112 		cmd.read_mem.addr = __cpu_to_le32(address);
113 		cmd.read_mem.len  = __cpu_to_le32(rxlen);
114 
115 		ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
116 						  &resp, &rxlen);
117 		if (ret) {
118 			ath10k_warn("unable to read from the device (%d)\n",
119 				    ret);
120 			return ret;
121 		}
122 
123 		memcpy(buffer, resp.read_mem.payload, rxlen);
124 		address += rxlen;
125 		buffer  += rxlen;
126 		length  -= rxlen;
127 	}
128 
129 	return 0;
130 }
131 
132 int ath10k_bmi_write_memory(struct ath10k *ar,
133 			    u32 address, const void *buffer, u32 length)
134 {
135 	struct bmi_cmd cmd;
136 	u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.write_mem);
137 	u32 txlen;
138 	int ret;
139 
140 	ath10k_dbg(ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n",
141 		   address, length);
142 
143 	if (ar->bmi.done_sent) {
144 		ath10k_warn("command disallowed\n");
145 		return -EBUSY;
146 	}
147 
148 	while (length) {
149 		txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
150 
151 		/* copy before roundup to avoid reading beyond buffer*/
152 		memcpy(cmd.write_mem.payload, buffer, txlen);
153 		txlen = roundup(txlen, 4);
154 
155 		cmd.id             = __cpu_to_le32(BMI_WRITE_MEMORY);
156 		cmd.write_mem.addr = __cpu_to_le32(address);
157 		cmd.write_mem.len  = __cpu_to_le32(txlen);
158 
159 		ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
160 						  NULL, NULL);
161 		if (ret) {
162 			ath10k_warn("unable to write to the device (%d)\n",
163 				    ret);
164 			return ret;
165 		}
166 
167 		/* fixup roundup() so `length` zeroes out for last chunk */
168 		txlen = min(txlen, length);
169 
170 		address += txlen;
171 		buffer  += txlen;
172 		length  -= txlen;
173 	}
174 
175 	return 0;
176 }
177 
178 int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
179 {
180 	struct bmi_cmd cmd;
181 	union bmi_resp resp;
182 	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.execute);
183 	u32 resplen = sizeof(resp.execute);
184 	int ret;
185 
186 	ath10k_dbg(ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n",
187 		   address, *param);
188 
189 	if (ar->bmi.done_sent) {
190 		ath10k_warn("command disallowed\n");
191 		return -EBUSY;
192 	}
193 
194 	cmd.id            = __cpu_to_le32(BMI_EXECUTE);
195 	cmd.execute.addr  = __cpu_to_le32(address);
196 	cmd.execute.param = __cpu_to_le32(*param);
197 
198 	ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
199 	if (ret) {
200 		ath10k_warn("unable to read from the device\n");
201 		return ret;
202 	}
203 
204 	if (resplen < sizeof(resp.execute)) {
205 		ath10k_warn("invalid execute response length (%d)\n",
206 			    resplen);
207 		return ret;
208 	}
209 
210 	*param = __le32_to_cpu(resp.execute.result);
211 	return 0;
212 }
213 
214 int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length)
215 {
216 	struct bmi_cmd cmd;
217 	u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data);
218 	u32 txlen;
219 	int ret;
220 
221 	ath10k_dbg(ATH10K_DBG_BMI, "bmi lz data buffer 0x%p length %d\n",
222 		   buffer, length);
223 
224 	if (ar->bmi.done_sent) {
225 		ath10k_warn("command disallowed\n");
226 		return -EBUSY;
227 	}
228 
229 	while (length) {
230 		txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
231 
232 		WARN_ON_ONCE(txlen & 3);
233 
234 		cmd.id          = __cpu_to_le32(BMI_LZ_DATA);
235 		cmd.lz_data.len = __cpu_to_le32(txlen);
236 		memcpy(cmd.lz_data.payload, buffer, txlen);
237 
238 		ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
239 						  NULL, NULL);
240 		if (ret) {
241 			ath10k_warn("unable to write to the device\n");
242 			return ret;
243 		}
244 
245 		buffer += txlen;
246 		length -= txlen;
247 	}
248 
249 	return 0;
250 }
251 
252 int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address)
253 {
254 	struct bmi_cmd cmd;
255 	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start);
256 	int ret;
257 
258 	ath10k_dbg(ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n",
259 		   address);
260 
261 	if (ar->bmi.done_sent) {
262 		ath10k_warn("command disallowed\n");
263 		return -EBUSY;
264 	}
265 
266 	cmd.id            = __cpu_to_le32(BMI_LZ_STREAM_START);
267 	cmd.lz_start.addr = __cpu_to_le32(address);
268 
269 	ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
270 	if (ret) {
271 		ath10k_warn("unable to Start LZ Stream to the device\n");
272 		return ret;
273 	}
274 
275 	return 0;
276 }
277 
278 int ath10k_bmi_fast_download(struct ath10k *ar,
279 			     u32 address, const void *buffer, u32 length)
280 {
281 	u8 trailer[4] = {};
282 	u32 head_len = rounddown(length, 4);
283 	u32 trailer_len = length - head_len;
284 	int ret;
285 
286 	ath10k_dbg(ATH10K_DBG_BMI,
287 		   "bmi fast download address 0x%x buffer 0x%p length %d\n",
288 		   address, buffer, length);
289 
290 	ret = ath10k_bmi_lz_stream_start(ar, address);
291 	if (ret)
292 		return ret;
293 
294 	/* copy the last word into a zero padded buffer */
295 	if (trailer_len > 0)
296 		memcpy(trailer, buffer + head_len, trailer_len);
297 
298 	ret = ath10k_bmi_lz_data(ar, buffer, head_len);
299 	if (ret)
300 		return ret;
301 
302 	if (trailer_len > 0)
303 		ret = ath10k_bmi_lz_data(ar, trailer, 4);
304 
305 	if (ret != 0)
306 		return ret;
307 
308 	/*
309 	 * Close compressed stream and open a new (fake) one.
310 	 * This serves mainly to flush Target caches.
311 	 */
312 	ret = ath10k_bmi_lz_stream_start(ar, 0x00);
313 
314 	return ret;
315 }
316