Commit | Line | Data |
---|---|---|
5e3dd157 KV |
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 | ||
64d151d4 MK |
23 | void ath10k_bmi_start(struct ath10k *ar) |
24 | { | |
7aa7a72a | 25 | ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi start\n"); |
f0bbea99 | 26 | |
64d151d4 MK |
27 | ar->bmi.done_sent = false; |
28 | } | |
29 | ||
5e3dd157 KV |
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 | ||
7aa7a72a | 36 | ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi done\n"); |
f0bbea99 | 37 | |
5e3dd157 | 38 | if (ar->bmi.done_sent) { |
7aa7a72a | 39 | ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi skipped\n"); |
5e3dd157 KV |
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) { | |
7aa7a72a | 48 | ath10k_warn(ar, "unable to write to the device: %d\n", ret); |
5e3dd157 KV |
49 | return ret; |
50 | } | |
51 | ||
5e3dd157 KV |
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 | ||
7aa7a72a | 64 | ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info\n"); |
f0bbea99 | 65 | |
5e3dd157 | 66 | if (ar->bmi.done_sent) { |
7aa7a72a | 67 | ath10k_warn(ar, "BMI Get Target Info Command disallowed\n"); |
5e3dd157 KV |
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) { | |
7aa7a72a | 75 | ath10k_warn(ar, "unable to get target info from device\n"); |
5e3dd157 KV |
76 | return ret; |
77 | } | |
78 | ||
79 | if (resplen < sizeof(resp.get_target_info)) { | |
7aa7a72a | 80 | ath10k_warn(ar, "invalid get_target_info response length (%d)\n", |
5e3dd157 KV |
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); | |
f0bbea99 | 87 | |
5e3dd157 KV |
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 | ||
7aa7a72a | 100 | ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n", |
f0bbea99 KV |
101 | address, length); |
102 | ||
5e3dd157 | 103 | if (ar->bmi.done_sent) { |
7aa7a72a | 104 | ath10k_warn(ar, "command disallowed\n"); |
5e3dd157 KV |
105 | return -EBUSY; |
106 | } | |
107 | ||
5e3dd157 KV |
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) { | |
7aa7a72a | 118 | ath10k_warn(ar, "unable to read from the device (%d)\n", |
ed48b356 | 119 | ret); |
5e3dd157 KV |
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 | ||
7aa7a72a | 140 | ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n", |
f0bbea99 KV |
141 | address, length); |
142 | ||
5e3dd157 | 143 | if (ar->bmi.done_sent) { |
7aa7a72a | 144 | ath10k_warn(ar, "command disallowed\n"); |
5e3dd157 KV |
145 | return -EBUSY; |
146 | } | |
147 | ||
5e3dd157 KV |
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) { | |
7aa7a72a | 162 | ath10k_warn(ar, "unable to write to the device (%d)\n", |
ed48b356 | 163 | ret); |
5e3dd157 KV |
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 | ||
d6d4a58d | 178 | int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result) |
5e3dd157 KV |
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 | ||
7aa7a72a | 186 | ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n", |
d6d4a58d | 187 | address, param); |
f0bbea99 | 188 | |
5e3dd157 | 189 | if (ar->bmi.done_sent) { |
7aa7a72a | 190 | ath10k_warn(ar, "command disallowed\n"); |
5e3dd157 KV |
191 | return -EBUSY; |
192 | } | |
193 | ||
5e3dd157 KV |
194 | cmd.id = __cpu_to_le32(BMI_EXECUTE); |
195 | cmd.execute.addr = __cpu_to_le32(address); | |
d6d4a58d | 196 | cmd.execute.param = __cpu_to_le32(param); |
5e3dd157 KV |
197 | |
198 | ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen); | |
199 | if (ret) { | |
7aa7a72a | 200 | ath10k_warn(ar, "unable to read from the device\n"); |
5e3dd157 KV |
201 | return ret; |
202 | } | |
203 | ||
204 | if (resplen < sizeof(resp.execute)) { | |
7aa7a72a | 205 | ath10k_warn(ar, "invalid execute response length (%d)\n", |
5e3dd157 | 206 | resplen); |
d6d4a58d | 207 | return -EIO; |
5e3dd157 KV |
208 | } |
209 | ||
d6d4a58d KV |
210 | *result = __le32_to_cpu(resp.execute.result); |
211 | ||
7aa7a72a | 212 | ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute result 0x%x\n", *result); |
d6d4a58d | 213 | |
5e3dd157 KV |
214 | return 0; |
215 | } | |
216 | ||
217 | int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length) | |
218 | { | |
219 | struct bmi_cmd cmd; | |
220 | u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data); | |
221 | u32 txlen; | |
222 | int ret; | |
223 | ||
7aa7a72a | 224 | ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz data buffer 0x%p length %d\n", |
f0bbea99 KV |
225 | buffer, length); |
226 | ||
5e3dd157 | 227 | if (ar->bmi.done_sent) { |
7aa7a72a | 228 | ath10k_warn(ar, "command disallowed\n"); |
5e3dd157 KV |
229 | return -EBUSY; |
230 | } | |
231 | ||
232 | while (length) { | |
233 | txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen); | |
234 | ||
235 | WARN_ON_ONCE(txlen & 3); | |
236 | ||
237 | cmd.id = __cpu_to_le32(BMI_LZ_DATA); | |
238 | cmd.lz_data.len = __cpu_to_le32(txlen); | |
239 | memcpy(cmd.lz_data.payload, buffer, txlen); | |
240 | ||
241 | ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen, | |
242 | NULL, NULL); | |
243 | if (ret) { | |
7aa7a72a | 244 | ath10k_warn(ar, "unable to write to the device\n"); |
5e3dd157 KV |
245 | return ret; |
246 | } | |
247 | ||
248 | buffer += txlen; | |
249 | length -= txlen; | |
250 | } | |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
255 | int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address) | |
256 | { | |
257 | struct bmi_cmd cmd; | |
258 | u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start); | |
259 | int ret; | |
260 | ||
7aa7a72a | 261 | ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n", |
f0bbea99 KV |
262 | address); |
263 | ||
5e3dd157 | 264 | if (ar->bmi.done_sent) { |
7aa7a72a | 265 | ath10k_warn(ar, "command disallowed\n"); |
5e3dd157 KV |
266 | return -EBUSY; |
267 | } | |
268 | ||
269 | cmd.id = __cpu_to_le32(BMI_LZ_STREAM_START); | |
270 | cmd.lz_start.addr = __cpu_to_le32(address); | |
271 | ||
272 | ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL); | |
273 | if (ret) { | |
7aa7a72a | 274 | ath10k_warn(ar, "unable to Start LZ Stream to the device\n"); |
5e3dd157 KV |
275 | return ret; |
276 | } | |
277 | ||
278 | return 0; | |
279 | } | |
280 | ||
281 | int ath10k_bmi_fast_download(struct ath10k *ar, | |
282 | u32 address, const void *buffer, u32 length) | |
283 | { | |
284 | u8 trailer[4] = {}; | |
285 | u32 head_len = rounddown(length, 4); | |
286 | u32 trailer_len = length - head_len; | |
287 | int ret; | |
288 | ||
7aa7a72a | 289 | ath10k_dbg(ar, ATH10K_DBG_BMI, |
f0bbea99 KV |
290 | "bmi fast download address 0x%x buffer 0x%p length %d\n", |
291 | address, buffer, length); | |
292 | ||
5e3dd157 KV |
293 | ret = ath10k_bmi_lz_stream_start(ar, address); |
294 | if (ret) | |
295 | return ret; | |
296 | ||
297 | /* copy the last word into a zero padded buffer */ | |
298 | if (trailer_len > 0) | |
299 | memcpy(trailer, buffer + head_len, trailer_len); | |
300 | ||
301 | ret = ath10k_bmi_lz_data(ar, buffer, head_len); | |
302 | if (ret) | |
303 | return ret; | |
304 | ||
305 | if (trailer_len > 0) | |
306 | ret = ath10k_bmi_lz_data(ar, trailer, 4); | |
307 | ||
308 | if (ret != 0) | |
309 | return ret; | |
310 | ||
311 | /* | |
312 | * Close compressed stream and open a new (fake) one. | |
313 | * This serves mainly to flush Target caches. | |
314 | */ | |
315 | ret = ath10k_bmi_lz_stream_start(ar, 0x00); | |
316 | ||
317 | return ret; | |
318 | } |