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