1da8fa4e3SBjoern A. Zeeb // SPDX-License-Identifier: ISC
2da8fa4e3SBjoern A. Zeeb /*
3da8fa4e3SBjoern A. Zeeb * Copyright (c) 2005-2011 Atheros Communications Inc.
4da8fa4e3SBjoern A. Zeeb * Copyright (c) 2011-2014,2016-2017 Qualcomm Atheros, Inc.
5da8fa4e3SBjoern A. Zeeb */
6da8fa4e3SBjoern A. Zeeb
7da8fa4e3SBjoern A. Zeeb #include "bmi.h"
8da8fa4e3SBjoern A. Zeeb #include "hif.h"
9da8fa4e3SBjoern A. Zeeb #include "debug.h"
10da8fa4e3SBjoern A. Zeeb #include "htc.h"
11da8fa4e3SBjoern A. Zeeb #include "hw.h"
12da8fa4e3SBjoern A. Zeeb
ath10k_bmi_start(struct ath10k * ar)13da8fa4e3SBjoern A. Zeeb void ath10k_bmi_start(struct ath10k *ar)
14da8fa4e3SBjoern A. Zeeb {
15da8fa4e3SBjoern A. Zeeb ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi start\n");
16da8fa4e3SBjoern A. Zeeb
17da8fa4e3SBjoern A. Zeeb ar->bmi.done_sent = false;
18da8fa4e3SBjoern A. Zeeb }
19da8fa4e3SBjoern A. Zeeb EXPORT_SYMBOL(ath10k_bmi_start);
20da8fa4e3SBjoern A. Zeeb
ath10k_bmi_done(struct ath10k * ar)21da8fa4e3SBjoern A. Zeeb int ath10k_bmi_done(struct ath10k *ar)
22da8fa4e3SBjoern A. Zeeb {
23da8fa4e3SBjoern A. Zeeb struct bmi_cmd cmd;
24da8fa4e3SBjoern A. Zeeb u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done);
25da8fa4e3SBjoern A. Zeeb int ret;
26da8fa4e3SBjoern A. Zeeb
27da8fa4e3SBjoern A. Zeeb ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi done\n");
28da8fa4e3SBjoern A. Zeeb
29da8fa4e3SBjoern A. Zeeb if (ar->bmi.done_sent) {
30da8fa4e3SBjoern A. Zeeb ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi skipped\n");
31da8fa4e3SBjoern A. Zeeb return 0;
32da8fa4e3SBjoern A. Zeeb }
33da8fa4e3SBjoern A. Zeeb
34da8fa4e3SBjoern A. Zeeb ar->bmi.done_sent = true;
35da8fa4e3SBjoern A. Zeeb cmd.id = __cpu_to_le32(BMI_DONE);
36da8fa4e3SBjoern A. Zeeb
37da8fa4e3SBjoern A. Zeeb ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
38da8fa4e3SBjoern A. Zeeb if (ret) {
39da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "unable to write to the device: %d\n", ret);
40da8fa4e3SBjoern A. Zeeb return ret;
41da8fa4e3SBjoern A. Zeeb }
42da8fa4e3SBjoern A. Zeeb
43da8fa4e3SBjoern A. Zeeb return 0;
44da8fa4e3SBjoern A. Zeeb }
45da8fa4e3SBjoern A. Zeeb
ath10k_bmi_get_target_info(struct ath10k * ar,struct bmi_target_info * target_info)46da8fa4e3SBjoern A. Zeeb int ath10k_bmi_get_target_info(struct ath10k *ar,
47da8fa4e3SBjoern A. Zeeb struct bmi_target_info *target_info)
48da8fa4e3SBjoern A. Zeeb {
49da8fa4e3SBjoern A. Zeeb struct bmi_cmd cmd;
50da8fa4e3SBjoern A. Zeeb union bmi_resp resp;
51da8fa4e3SBjoern A. Zeeb u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);
52da8fa4e3SBjoern A. Zeeb u32 resplen = sizeof(resp.get_target_info);
53da8fa4e3SBjoern A. Zeeb int ret;
54da8fa4e3SBjoern A. Zeeb
55da8fa4e3SBjoern A. Zeeb ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info\n");
56da8fa4e3SBjoern A. Zeeb
57da8fa4e3SBjoern A. Zeeb if (ar->bmi.done_sent) {
58da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "BMI Get Target Info Command disallowed\n");
59da8fa4e3SBjoern A. Zeeb return -EBUSY;
60da8fa4e3SBjoern A. Zeeb }
61da8fa4e3SBjoern A. Zeeb
62da8fa4e3SBjoern A. Zeeb cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);
63da8fa4e3SBjoern A. Zeeb
64da8fa4e3SBjoern A. Zeeb ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
65da8fa4e3SBjoern A. Zeeb if (ret) {
66da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "unable to get target info from device\n");
67da8fa4e3SBjoern A. Zeeb return ret;
68da8fa4e3SBjoern A. Zeeb }
69da8fa4e3SBjoern A. Zeeb
70da8fa4e3SBjoern A. Zeeb if (resplen < sizeof(resp.get_target_info)) {
71da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "invalid get_target_info response length (%d)\n",
72da8fa4e3SBjoern A. Zeeb resplen);
73da8fa4e3SBjoern A. Zeeb return -EIO;
74da8fa4e3SBjoern A. Zeeb }
75da8fa4e3SBjoern A. Zeeb
76da8fa4e3SBjoern A. Zeeb target_info->version = __le32_to_cpu(resp.get_target_info.version);
77da8fa4e3SBjoern A. Zeeb target_info->type = __le32_to_cpu(resp.get_target_info.type);
78da8fa4e3SBjoern A. Zeeb
79da8fa4e3SBjoern A. Zeeb return 0;
80da8fa4e3SBjoern A. Zeeb }
81da8fa4e3SBjoern A. Zeeb
82da8fa4e3SBjoern A. Zeeb #define TARGET_VERSION_SENTINAL 0xffffffffu
83da8fa4e3SBjoern A. Zeeb
ath10k_bmi_get_target_info_sdio(struct ath10k * ar,struct bmi_target_info * target_info)84da8fa4e3SBjoern A. Zeeb int ath10k_bmi_get_target_info_sdio(struct ath10k *ar,
85da8fa4e3SBjoern A. Zeeb struct bmi_target_info *target_info)
86da8fa4e3SBjoern A. Zeeb {
87da8fa4e3SBjoern A. Zeeb struct bmi_cmd cmd;
88da8fa4e3SBjoern A. Zeeb union bmi_resp resp;
89da8fa4e3SBjoern A. Zeeb u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);
90da8fa4e3SBjoern A. Zeeb u32 resplen, ver_len;
91da8fa4e3SBjoern A. Zeeb __le32 tmp;
92da8fa4e3SBjoern A. Zeeb int ret;
93da8fa4e3SBjoern A. Zeeb
94da8fa4e3SBjoern A. Zeeb ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info SDIO\n");
95da8fa4e3SBjoern A. Zeeb
96da8fa4e3SBjoern A. Zeeb if (ar->bmi.done_sent) {
97da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "BMI Get Target Info Command disallowed\n");
98da8fa4e3SBjoern A. Zeeb return -EBUSY;
99da8fa4e3SBjoern A. Zeeb }
100da8fa4e3SBjoern A. Zeeb
101da8fa4e3SBjoern A. Zeeb cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);
102da8fa4e3SBjoern A. Zeeb
103da8fa4e3SBjoern A. Zeeb /* Step 1: Read 4 bytes of the target info and check if it is
104*07724ba6SBjoern A. Zeeb * the special sentinel version word or the first word in the
105da8fa4e3SBjoern A. Zeeb * version response.
106da8fa4e3SBjoern A. Zeeb */
107da8fa4e3SBjoern A. Zeeb resplen = sizeof(u32);
108da8fa4e3SBjoern A. Zeeb ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &tmp, &resplen);
109da8fa4e3SBjoern A. Zeeb if (ret) {
110da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "unable to read from device\n");
111da8fa4e3SBjoern A. Zeeb return ret;
112da8fa4e3SBjoern A. Zeeb }
113da8fa4e3SBjoern A. Zeeb
114*07724ba6SBjoern A. Zeeb /* Some SDIO boards have a special sentinel byte before the real
115da8fa4e3SBjoern A. Zeeb * version response.
116da8fa4e3SBjoern A. Zeeb */
117da8fa4e3SBjoern A. Zeeb if (__le32_to_cpu(tmp) == TARGET_VERSION_SENTINAL) {
118da8fa4e3SBjoern A. Zeeb /* Step 1b: Read the version length */
119da8fa4e3SBjoern A. Zeeb resplen = sizeof(u32);
120da8fa4e3SBjoern A. Zeeb ret = ath10k_hif_exchange_bmi_msg(ar, NULL, 0, &tmp,
121da8fa4e3SBjoern A. Zeeb &resplen);
122da8fa4e3SBjoern A. Zeeb if (ret) {
123da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "unable to read from device\n");
124da8fa4e3SBjoern A. Zeeb return ret;
125da8fa4e3SBjoern A. Zeeb }
126da8fa4e3SBjoern A. Zeeb }
127da8fa4e3SBjoern A. Zeeb
128da8fa4e3SBjoern A. Zeeb ver_len = __le32_to_cpu(tmp);
129da8fa4e3SBjoern A. Zeeb
130da8fa4e3SBjoern A. Zeeb /* Step 2: Check the target info length */
131da8fa4e3SBjoern A. Zeeb if (ver_len != sizeof(resp.get_target_info)) {
132da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "Unexpected target info len: %u. Expected: %zu\n",
133da8fa4e3SBjoern A. Zeeb ver_len, sizeof(resp.get_target_info));
134da8fa4e3SBjoern A. Zeeb return -EINVAL;
135da8fa4e3SBjoern A. Zeeb }
136da8fa4e3SBjoern A. Zeeb
137da8fa4e3SBjoern A. Zeeb /* Step 3: Read the rest of the version response */
138da8fa4e3SBjoern A. Zeeb resplen = sizeof(resp.get_target_info) - sizeof(u32);
139da8fa4e3SBjoern A. Zeeb ret = ath10k_hif_exchange_bmi_msg(ar, NULL, 0,
140da8fa4e3SBjoern A. Zeeb &resp.get_target_info.version,
141da8fa4e3SBjoern A. Zeeb &resplen);
142da8fa4e3SBjoern A. Zeeb if (ret) {
143da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "unable to read from device\n");
144da8fa4e3SBjoern A. Zeeb return ret;
145da8fa4e3SBjoern A. Zeeb }
146da8fa4e3SBjoern A. Zeeb
147da8fa4e3SBjoern A. Zeeb target_info->version = __le32_to_cpu(resp.get_target_info.version);
148da8fa4e3SBjoern A. Zeeb target_info->type = __le32_to_cpu(resp.get_target_info.type);
149da8fa4e3SBjoern A. Zeeb
150da8fa4e3SBjoern A. Zeeb return 0;
151da8fa4e3SBjoern A. Zeeb }
152da8fa4e3SBjoern A. Zeeb
ath10k_bmi_read_memory(struct ath10k * ar,u32 address,void * buffer,u32 length)153da8fa4e3SBjoern A. Zeeb int ath10k_bmi_read_memory(struct ath10k *ar,
154da8fa4e3SBjoern A. Zeeb #if defined(__linux__)
155da8fa4e3SBjoern A. Zeeb u32 address, void *buffer, u32 length)
156da8fa4e3SBjoern A. Zeeb #elif defined(__FreeBSD__)
157da8fa4e3SBjoern A. Zeeb u32 address, u8 *buffer, u32 length)
158da8fa4e3SBjoern A. Zeeb #endif
159da8fa4e3SBjoern A. Zeeb {
160da8fa4e3SBjoern A. Zeeb struct bmi_cmd cmd;
161da8fa4e3SBjoern A. Zeeb union bmi_resp resp;
162da8fa4e3SBjoern A. Zeeb u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_mem);
163da8fa4e3SBjoern A. Zeeb u32 rxlen;
164da8fa4e3SBjoern A. Zeeb int ret;
165da8fa4e3SBjoern A. Zeeb
166da8fa4e3SBjoern A. Zeeb ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n",
167da8fa4e3SBjoern A. Zeeb address, length);
168da8fa4e3SBjoern A. Zeeb
169da8fa4e3SBjoern A. Zeeb if (ar->bmi.done_sent) {
170da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "command disallowed\n");
171da8fa4e3SBjoern A. Zeeb return -EBUSY;
172da8fa4e3SBjoern A. Zeeb }
173da8fa4e3SBjoern A. Zeeb
174da8fa4e3SBjoern A. Zeeb while (length) {
175da8fa4e3SBjoern A. Zeeb rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE);
176da8fa4e3SBjoern A. Zeeb
177da8fa4e3SBjoern A. Zeeb cmd.id = __cpu_to_le32(BMI_READ_MEMORY);
178da8fa4e3SBjoern A. Zeeb cmd.read_mem.addr = __cpu_to_le32(address);
179da8fa4e3SBjoern A. Zeeb cmd.read_mem.len = __cpu_to_le32(rxlen);
180da8fa4e3SBjoern A. Zeeb
181da8fa4e3SBjoern A. Zeeb ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
182da8fa4e3SBjoern A. Zeeb &resp, &rxlen);
183da8fa4e3SBjoern A. Zeeb if (ret) {
184da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "unable to read from the device (%d)\n",
185da8fa4e3SBjoern A. Zeeb ret);
186da8fa4e3SBjoern A. Zeeb return ret;
187da8fa4e3SBjoern A. Zeeb }
188da8fa4e3SBjoern A. Zeeb
189da8fa4e3SBjoern A. Zeeb memcpy(buffer, resp.read_mem.payload, rxlen);
190da8fa4e3SBjoern A. Zeeb address += rxlen;
191da8fa4e3SBjoern A. Zeeb buffer += rxlen;
192da8fa4e3SBjoern A. Zeeb length -= rxlen;
193da8fa4e3SBjoern A. Zeeb }
194da8fa4e3SBjoern A. Zeeb
195da8fa4e3SBjoern A. Zeeb return 0;
196da8fa4e3SBjoern A. Zeeb }
197da8fa4e3SBjoern A. Zeeb EXPORT_SYMBOL(ath10k_bmi_read_memory);
198da8fa4e3SBjoern A. Zeeb
ath10k_bmi_write_soc_reg(struct ath10k * ar,u32 address,u32 reg_val)199da8fa4e3SBjoern A. Zeeb int ath10k_bmi_write_soc_reg(struct ath10k *ar, u32 address, u32 reg_val)
200da8fa4e3SBjoern A. Zeeb {
201da8fa4e3SBjoern A. Zeeb struct bmi_cmd cmd;
202da8fa4e3SBjoern A. Zeeb u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.write_soc_reg);
203da8fa4e3SBjoern A. Zeeb int ret;
204da8fa4e3SBjoern A. Zeeb
205da8fa4e3SBjoern A. Zeeb ath10k_dbg(ar, ATH10K_DBG_BMI,
206da8fa4e3SBjoern A. Zeeb "bmi write soc register 0x%08x val 0x%08x\n",
207da8fa4e3SBjoern A. Zeeb address, reg_val);
208da8fa4e3SBjoern A. Zeeb
209da8fa4e3SBjoern A. Zeeb if (ar->bmi.done_sent) {
210da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "bmi write soc register command in progress\n");
211da8fa4e3SBjoern A. Zeeb return -EBUSY;
212da8fa4e3SBjoern A. Zeeb }
213da8fa4e3SBjoern A. Zeeb
214da8fa4e3SBjoern A. Zeeb cmd.id = __cpu_to_le32(BMI_WRITE_SOC_REGISTER);
215da8fa4e3SBjoern A. Zeeb cmd.write_soc_reg.addr = __cpu_to_le32(address);
216da8fa4e3SBjoern A. Zeeb cmd.write_soc_reg.value = __cpu_to_le32(reg_val);
217da8fa4e3SBjoern A. Zeeb
218da8fa4e3SBjoern A. Zeeb ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
219da8fa4e3SBjoern A. Zeeb if (ret) {
220da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "Unable to write soc register to device: %d\n",
221da8fa4e3SBjoern A. Zeeb ret);
222da8fa4e3SBjoern A. Zeeb return ret;
223da8fa4e3SBjoern A. Zeeb }
224da8fa4e3SBjoern A. Zeeb
225da8fa4e3SBjoern A. Zeeb return 0;
226da8fa4e3SBjoern A. Zeeb }
227da8fa4e3SBjoern A. Zeeb
ath10k_bmi_read_soc_reg(struct ath10k * ar,u32 address,u32 * reg_val)228da8fa4e3SBjoern A. Zeeb int ath10k_bmi_read_soc_reg(struct ath10k *ar, u32 address, u32 *reg_val)
229da8fa4e3SBjoern A. Zeeb {
230da8fa4e3SBjoern A. Zeeb struct bmi_cmd cmd;
231da8fa4e3SBjoern A. Zeeb union bmi_resp resp;
232da8fa4e3SBjoern A. Zeeb u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_soc_reg);
233da8fa4e3SBjoern A. Zeeb u32 resplen = sizeof(resp.read_soc_reg);
234da8fa4e3SBjoern A. Zeeb int ret;
235da8fa4e3SBjoern A. Zeeb
236da8fa4e3SBjoern A. Zeeb ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register 0x%08x\n",
237da8fa4e3SBjoern A. Zeeb address);
238da8fa4e3SBjoern A. Zeeb
239da8fa4e3SBjoern A. Zeeb if (ar->bmi.done_sent) {
240da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "bmi read soc register command in progress\n");
241da8fa4e3SBjoern A. Zeeb return -EBUSY;
242da8fa4e3SBjoern A. Zeeb }
243da8fa4e3SBjoern A. Zeeb
244da8fa4e3SBjoern A. Zeeb cmd.id = __cpu_to_le32(BMI_READ_SOC_REGISTER);
245da8fa4e3SBjoern A. Zeeb cmd.read_soc_reg.addr = __cpu_to_le32(address);
246da8fa4e3SBjoern A. Zeeb
247da8fa4e3SBjoern A. Zeeb ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
248da8fa4e3SBjoern A. Zeeb if (ret) {
249da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "Unable to read soc register from device: %d\n",
250da8fa4e3SBjoern A. Zeeb ret);
251da8fa4e3SBjoern A. Zeeb return ret;
252da8fa4e3SBjoern A. Zeeb }
253da8fa4e3SBjoern A. Zeeb
254da8fa4e3SBjoern A. Zeeb *reg_val = __le32_to_cpu(resp.read_soc_reg.value);
255da8fa4e3SBjoern A. Zeeb
256da8fa4e3SBjoern A. Zeeb ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register value 0x%08x\n",
257da8fa4e3SBjoern A. Zeeb *reg_val);
258da8fa4e3SBjoern A. Zeeb
259da8fa4e3SBjoern A. Zeeb return 0;
260da8fa4e3SBjoern A. Zeeb }
261da8fa4e3SBjoern A. Zeeb
ath10k_bmi_write_memory(struct ath10k * ar,u32 address,const void * buffer,u32 length)262da8fa4e3SBjoern A. Zeeb int ath10k_bmi_write_memory(struct ath10k *ar,
263da8fa4e3SBjoern A. Zeeb #if defined(__linux__)
264da8fa4e3SBjoern A. Zeeb u32 address, const void *buffer, u32 length)
265da8fa4e3SBjoern A. Zeeb #elif defined(__FreeBSD__)
266da8fa4e3SBjoern A. Zeeb u32 address, const u8 *buffer, u32 length)
267da8fa4e3SBjoern A. Zeeb #endif
268da8fa4e3SBjoern A. Zeeb {
269da8fa4e3SBjoern A. Zeeb struct bmi_cmd cmd;
270da8fa4e3SBjoern A. Zeeb u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.write_mem);
271da8fa4e3SBjoern A. Zeeb u32 txlen;
272da8fa4e3SBjoern A. Zeeb int ret;
273da8fa4e3SBjoern A. Zeeb
274da8fa4e3SBjoern A. Zeeb ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n",
275da8fa4e3SBjoern A. Zeeb address, length);
276da8fa4e3SBjoern A. Zeeb
277da8fa4e3SBjoern A. Zeeb if (ar->bmi.done_sent) {
278da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "command disallowed\n");
279da8fa4e3SBjoern A. Zeeb return -EBUSY;
280da8fa4e3SBjoern A. Zeeb }
281da8fa4e3SBjoern A. Zeeb
282da8fa4e3SBjoern A. Zeeb while (length) {
283da8fa4e3SBjoern A. Zeeb txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
284da8fa4e3SBjoern A. Zeeb
285da8fa4e3SBjoern A. Zeeb /* copy before roundup to avoid reading beyond buffer*/
286da8fa4e3SBjoern A. Zeeb memcpy(cmd.write_mem.payload, buffer, txlen);
287da8fa4e3SBjoern A. Zeeb txlen = roundup(txlen, 4);
288da8fa4e3SBjoern A. Zeeb
289da8fa4e3SBjoern A. Zeeb cmd.id = __cpu_to_le32(BMI_WRITE_MEMORY);
290da8fa4e3SBjoern A. Zeeb cmd.write_mem.addr = __cpu_to_le32(address);
291da8fa4e3SBjoern A. Zeeb cmd.write_mem.len = __cpu_to_le32(txlen);
292da8fa4e3SBjoern A. Zeeb
293da8fa4e3SBjoern A. Zeeb ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
294da8fa4e3SBjoern A. Zeeb NULL, NULL);
295da8fa4e3SBjoern A. Zeeb if (ret) {
296da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "unable to write to the device (%d)\n",
297da8fa4e3SBjoern A. Zeeb ret);
298da8fa4e3SBjoern A. Zeeb return ret;
299da8fa4e3SBjoern A. Zeeb }
300da8fa4e3SBjoern A. Zeeb
301da8fa4e3SBjoern A. Zeeb /* fixup roundup() so `length` zeroes out for last chunk */
302da8fa4e3SBjoern A. Zeeb txlen = min(txlen, length);
303da8fa4e3SBjoern A. Zeeb
304da8fa4e3SBjoern A. Zeeb address += txlen;
305da8fa4e3SBjoern A. Zeeb buffer += txlen;
306da8fa4e3SBjoern A. Zeeb length -= txlen;
307da8fa4e3SBjoern A. Zeeb }
308da8fa4e3SBjoern A. Zeeb
309da8fa4e3SBjoern A. Zeeb return 0;
310da8fa4e3SBjoern A. Zeeb }
311da8fa4e3SBjoern A. Zeeb
ath10k_bmi_execute(struct ath10k * ar,u32 address,u32 param,u32 * result)312da8fa4e3SBjoern A. Zeeb int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result)
313da8fa4e3SBjoern A. Zeeb {
314da8fa4e3SBjoern A. Zeeb struct bmi_cmd cmd;
315da8fa4e3SBjoern A. Zeeb union bmi_resp resp;
316da8fa4e3SBjoern A. Zeeb u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.execute);
317da8fa4e3SBjoern A. Zeeb u32 resplen = sizeof(resp.execute);
318da8fa4e3SBjoern A. Zeeb int ret;
319da8fa4e3SBjoern A. Zeeb
320da8fa4e3SBjoern A. Zeeb ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n",
321da8fa4e3SBjoern A. Zeeb address, param);
322da8fa4e3SBjoern A. Zeeb
323da8fa4e3SBjoern A. Zeeb if (ar->bmi.done_sent) {
324da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "command disallowed\n");
325da8fa4e3SBjoern A. Zeeb return -EBUSY;
326da8fa4e3SBjoern A. Zeeb }
327da8fa4e3SBjoern A. Zeeb
328da8fa4e3SBjoern A. Zeeb cmd.id = __cpu_to_le32(BMI_EXECUTE);
329da8fa4e3SBjoern A. Zeeb cmd.execute.addr = __cpu_to_le32(address);
330da8fa4e3SBjoern A. Zeeb cmd.execute.param = __cpu_to_le32(param);
331da8fa4e3SBjoern A. Zeeb
332da8fa4e3SBjoern A. Zeeb ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
333da8fa4e3SBjoern A. Zeeb if (ret) {
334da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "unable to read from the device\n");
335da8fa4e3SBjoern A. Zeeb return ret;
336da8fa4e3SBjoern A. Zeeb }
337da8fa4e3SBjoern A. Zeeb
338da8fa4e3SBjoern A. Zeeb if (resplen < sizeof(resp.execute)) {
339da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "invalid execute response length (%d)\n",
340da8fa4e3SBjoern A. Zeeb resplen);
341da8fa4e3SBjoern A. Zeeb return -EIO;
342da8fa4e3SBjoern A. Zeeb }
343da8fa4e3SBjoern A. Zeeb
344da8fa4e3SBjoern A. Zeeb *result = __le32_to_cpu(resp.execute.result);
345da8fa4e3SBjoern A. Zeeb
346da8fa4e3SBjoern A. Zeeb ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute result 0x%x\n", *result);
347da8fa4e3SBjoern A. Zeeb
348da8fa4e3SBjoern A. Zeeb return 0;
349da8fa4e3SBjoern A. Zeeb }
350da8fa4e3SBjoern A. Zeeb
351da8fa4e3SBjoern A. Zeeb #if defined(__linux__)
ath10k_bmi_lz_data_large(struct ath10k * ar,const void * buffer,u32 length)352da8fa4e3SBjoern A. Zeeb static int ath10k_bmi_lz_data_large(struct ath10k *ar, const void *buffer, u32 length)
353da8fa4e3SBjoern A. Zeeb #elif defined(__FreeBSD__)
354da8fa4e3SBjoern A. Zeeb static int ath10k_bmi_lz_data_large(struct ath10k *ar, const u8 *buffer, u32 length)
355da8fa4e3SBjoern A. Zeeb #endif
356da8fa4e3SBjoern A. Zeeb {
357da8fa4e3SBjoern A. Zeeb struct bmi_cmd *cmd;
358da8fa4e3SBjoern A. Zeeb u32 hdrlen = sizeof(cmd->id) + sizeof(cmd->lz_data);
359da8fa4e3SBjoern A. Zeeb u32 txlen;
360da8fa4e3SBjoern A. Zeeb int ret;
361da8fa4e3SBjoern A. Zeeb size_t buf_len;
362da8fa4e3SBjoern A. Zeeb
363da8fa4e3SBjoern A. Zeeb ath10k_dbg(ar, ATH10K_DBG_BMI, "large bmi lz data buffer 0x%pK length %d\n",
364da8fa4e3SBjoern A. Zeeb buffer, length);
365da8fa4e3SBjoern A. Zeeb
366da8fa4e3SBjoern A. Zeeb if (ar->bmi.done_sent) {
367da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "command disallowed\n");
368da8fa4e3SBjoern A. Zeeb return -EBUSY;
369da8fa4e3SBjoern A. Zeeb }
370da8fa4e3SBjoern A. Zeeb
371da8fa4e3SBjoern A. Zeeb buf_len = sizeof(*cmd) + BMI_MAX_LARGE_DATA_SIZE - BMI_MAX_DATA_SIZE;
372da8fa4e3SBjoern A. Zeeb cmd = kzalloc(buf_len, GFP_KERNEL);
373da8fa4e3SBjoern A. Zeeb if (!cmd)
374da8fa4e3SBjoern A. Zeeb return -ENOMEM;
375da8fa4e3SBjoern A. Zeeb
376da8fa4e3SBjoern A. Zeeb while (length) {
377da8fa4e3SBjoern A. Zeeb txlen = min(length, BMI_MAX_LARGE_DATA_SIZE - hdrlen);
378da8fa4e3SBjoern A. Zeeb
379da8fa4e3SBjoern A. Zeeb WARN_ON_ONCE(txlen & 3);
380da8fa4e3SBjoern A. Zeeb
381da8fa4e3SBjoern A. Zeeb cmd->id = __cpu_to_le32(BMI_LZ_DATA);
382da8fa4e3SBjoern A. Zeeb cmd->lz_data.len = __cpu_to_le32(txlen);
383da8fa4e3SBjoern A. Zeeb memcpy(cmd->lz_data.payload, buffer, txlen);
384da8fa4e3SBjoern A. Zeeb
385da8fa4e3SBjoern A. Zeeb ret = ath10k_hif_exchange_bmi_msg(ar, cmd, hdrlen + txlen,
386da8fa4e3SBjoern A. Zeeb NULL, NULL);
387da8fa4e3SBjoern A. Zeeb if (ret) {
388da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "unable to write to the device\n");
389da8fa4e3SBjoern A. Zeeb kfree(cmd);
390da8fa4e3SBjoern A. Zeeb return ret;
391da8fa4e3SBjoern A. Zeeb }
392da8fa4e3SBjoern A. Zeeb
393da8fa4e3SBjoern A. Zeeb buffer += txlen;
394da8fa4e3SBjoern A. Zeeb length -= txlen;
395da8fa4e3SBjoern A. Zeeb }
396da8fa4e3SBjoern A. Zeeb
397da8fa4e3SBjoern A. Zeeb kfree(cmd);
398da8fa4e3SBjoern A. Zeeb
399da8fa4e3SBjoern A. Zeeb return 0;
400da8fa4e3SBjoern A. Zeeb }
401da8fa4e3SBjoern A. Zeeb
402da8fa4e3SBjoern A. Zeeb #if defined(__linux__)
ath10k_bmi_lz_data(struct ath10k * ar,const void * buffer,u32 length)403da8fa4e3SBjoern A. Zeeb int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length)
404da8fa4e3SBjoern A. Zeeb #elif defined(__FreeBSD__)
405da8fa4e3SBjoern A. Zeeb static
406da8fa4e3SBjoern A. Zeeb int ath10k_bmi_lz_data(struct ath10k *ar, const u8 *buffer, u32 length)
407da8fa4e3SBjoern A. Zeeb #endif
408da8fa4e3SBjoern A. Zeeb {
409da8fa4e3SBjoern A. Zeeb struct bmi_cmd cmd;
410da8fa4e3SBjoern A. Zeeb u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data);
411da8fa4e3SBjoern A. Zeeb u32 txlen;
412da8fa4e3SBjoern A. Zeeb int ret;
413da8fa4e3SBjoern A. Zeeb
414da8fa4e3SBjoern A. Zeeb ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz data buffer 0x%pK length %d\n",
415da8fa4e3SBjoern A. Zeeb buffer, length);
416da8fa4e3SBjoern A. Zeeb
417da8fa4e3SBjoern A. Zeeb if (ar->bmi.done_sent) {
418da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "command disallowed\n");
419da8fa4e3SBjoern A. Zeeb return -EBUSY;
420da8fa4e3SBjoern A. Zeeb }
421da8fa4e3SBjoern A. Zeeb
422da8fa4e3SBjoern A. Zeeb while (length) {
423da8fa4e3SBjoern A. Zeeb txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
424da8fa4e3SBjoern A. Zeeb
425da8fa4e3SBjoern A. Zeeb WARN_ON_ONCE(txlen & 3);
426da8fa4e3SBjoern A. Zeeb
427da8fa4e3SBjoern A. Zeeb cmd.id = __cpu_to_le32(BMI_LZ_DATA);
428da8fa4e3SBjoern A. Zeeb cmd.lz_data.len = __cpu_to_le32(txlen);
429da8fa4e3SBjoern A. Zeeb memcpy(cmd.lz_data.payload, buffer, txlen);
430da8fa4e3SBjoern A. Zeeb
431da8fa4e3SBjoern A. Zeeb ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
432da8fa4e3SBjoern A. Zeeb NULL, NULL);
433da8fa4e3SBjoern A. Zeeb if (ret) {
434da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "unable to write to the device\n");
435da8fa4e3SBjoern A. Zeeb return ret;
436da8fa4e3SBjoern A. Zeeb }
437da8fa4e3SBjoern A. Zeeb
438da8fa4e3SBjoern A. Zeeb buffer += txlen;
439da8fa4e3SBjoern A. Zeeb length -= txlen;
440da8fa4e3SBjoern A. Zeeb }
441da8fa4e3SBjoern A. Zeeb
442da8fa4e3SBjoern A. Zeeb return 0;
443da8fa4e3SBjoern A. Zeeb }
444da8fa4e3SBjoern A. Zeeb
ath10k_bmi_lz_stream_start(struct ath10k * ar,u32 address)445da8fa4e3SBjoern A. Zeeb int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address)
446da8fa4e3SBjoern A. Zeeb {
447da8fa4e3SBjoern A. Zeeb struct bmi_cmd cmd;
448da8fa4e3SBjoern A. Zeeb u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start);
449da8fa4e3SBjoern A. Zeeb int ret;
450da8fa4e3SBjoern A. Zeeb
451da8fa4e3SBjoern A. Zeeb ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n",
452da8fa4e3SBjoern A. Zeeb address);
453da8fa4e3SBjoern A. Zeeb
454da8fa4e3SBjoern A. Zeeb if (ar->bmi.done_sent) {
455da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "command disallowed\n");
456da8fa4e3SBjoern A. Zeeb return -EBUSY;
457da8fa4e3SBjoern A. Zeeb }
458da8fa4e3SBjoern A. Zeeb
459da8fa4e3SBjoern A. Zeeb cmd.id = __cpu_to_le32(BMI_LZ_STREAM_START);
460da8fa4e3SBjoern A. Zeeb cmd.lz_start.addr = __cpu_to_le32(address);
461da8fa4e3SBjoern A. Zeeb
462da8fa4e3SBjoern A. Zeeb ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
463da8fa4e3SBjoern A. Zeeb if (ret) {
464da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "unable to Start LZ Stream to the device\n");
465da8fa4e3SBjoern A. Zeeb return ret;
466da8fa4e3SBjoern A. Zeeb }
467da8fa4e3SBjoern A. Zeeb
468da8fa4e3SBjoern A. Zeeb return 0;
469da8fa4e3SBjoern A. Zeeb }
470da8fa4e3SBjoern A. Zeeb
ath10k_bmi_fast_download(struct ath10k * ar,u32 address,const void * buffer,u32 length)471da8fa4e3SBjoern A. Zeeb int ath10k_bmi_fast_download(struct ath10k *ar,
472da8fa4e3SBjoern A. Zeeb u32 address, const void *buffer, u32 length)
473da8fa4e3SBjoern A. Zeeb {
474da8fa4e3SBjoern A. Zeeb u8 trailer[4] = {};
475da8fa4e3SBjoern A. Zeeb u32 head_len = rounddown(length, 4);
476da8fa4e3SBjoern A. Zeeb u32 trailer_len = length - head_len;
477da8fa4e3SBjoern A. Zeeb int ret;
478da8fa4e3SBjoern A. Zeeb
479da8fa4e3SBjoern A. Zeeb ath10k_dbg(ar, ATH10K_DBG_BMI,
480da8fa4e3SBjoern A. Zeeb "bmi fast download address 0x%x buffer 0x%pK length %d\n",
481da8fa4e3SBjoern A. Zeeb address, buffer, length);
482da8fa4e3SBjoern A. Zeeb
483da8fa4e3SBjoern A. Zeeb ret = ath10k_bmi_lz_stream_start(ar, address);
484da8fa4e3SBjoern A. Zeeb if (ret)
485da8fa4e3SBjoern A. Zeeb return ret;
486da8fa4e3SBjoern A. Zeeb
487da8fa4e3SBjoern A. Zeeb /* copy the last word into a zero padded buffer */
488da8fa4e3SBjoern A. Zeeb if (trailer_len > 0)
489da8fa4e3SBjoern A. Zeeb #if defined(__linux__)
490da8fa4e3SBjoern A. Zeeb memcpy(trailer, buffer + head_len, trailer_len);
491da8fa4e3SBjoern A. Zeeb #elif defined(__FreeBSD__)
492da8fa4e3SBjoern A. Zeeb memcpy(trailer, (const u8 *)buffer + head_len, trailer_len);
493da8fa4e3SBjoern A. Zeeb #endif
494da8fa4e3SBjoern A. Zeeb
495da8fa4e3SBjoern A. Zeeb if (ar->hw_params.bmi_large_size_download)
496da8fa4e3SBjoern A. Zeeb ret = ath10k_bmi_lz_data_large(ar, buffer, head_len);
497da8fa4e3SBjoern A. Zeeb else
498da8fa4e3SBjoern A. Zeeb ret = ath10k_bmi_lz_data(ar, buffer, head_len);
499da8fa4e3SBjoern A. Zeeb
500da8fa4e3SBjoern A. Zeeb if (ret)
501da8fa4e3SBjoern A. Zeeb return ret;
502da8fa4e3SBjoern A. Zeeb
503da8fa4e3SBjoern A. Zeeb if (trailer_len > 0)
504da8fa4e3SBjoern A. Zeeb ret = ath10k_bmi_lz_data(ar, trailer, 4);
505da8fa4e3SBjoern A. Zeeb
506da8fa4e3SBjoern A. Zeeb if (ret != 0)
507da8fa4e3SBjoern A. Zeeb return ret;
508da8fa4e3SBjoern A. Zeeb
509da8fa4e3SBjoern A. Zeeb /*
510da8fa4e3SBjoern A. Zeeb * Close compressed stream and open a new (fake) one.
511da8fa4e3SBjoern A. Zeeb * This serves mainly to flush Target caches.
512da8fa4e3SBjoern A. Zeeb */
513da8fa4e3SBjoern A. Zeeb ret = ath10k_bmi_lz_stream_start(ar, 0x00);
514da8fa4e3SBjoern A. Zeeb
515da8fa4e3SBjoern A. Zeeb return ret;
516da8fa4e3SBjoern A. Zeeb }
517da8fa4e3SBjoern A. Zeeb
ath10k_bmi_set_start(struct ath10k * ar,u32 address)518da8fa4e3SBjoern A. Zeeb int ath10k_bmi_set_start(struct ath10k *ar, u32 address)
519da8fa4e3SBjoern A. Zeeb {
520da8fa4e3SBjoern A. Zeeb struct bmi_cmd cmd;
521da8fa4e3SBjoern A. Zeeb u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.set_app_start);
522da8fa4e3SBjoern A. Zeeb int ret;
523da8fa4e3SBjoern A. Zeeb
524da8fa4e3SBjoern A. Zeeb if (ar->bmi.done_sent) {
525da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "bmi set start command disallowed\n");
526da8fa4e3SBjoern A. Zeeb return -EBUSY;
527da8fa4e3SBjoern A. Zeeb }
528da8fa4e3SBjoern A. Zeeb
529da8fa4e3SBjoern A. Zeeb cmd.id = __cpu_to_le32(BMI_SET_APP_START);
530da8fa4e3SBjoern A. Zeeb cmd.set_app_start.addr = __cpu_to_le32(address);
531da8fa4e3SBjoern A. Zeeb
532da8fa4e3SBjoern A. Zeeb ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
533da8fa4e3SBjoern A. Zeeb if (ret) {
534da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "unable to set start to the device:%d\n", ret);
535da8fa4e3SBjoern A. Zeeb return ret;
536da8fa4e3SBjoern A. Zeeb }
537da8fa4e3SBjoern A. Zeeb
538da8fa4e3SBjoern A. Zeeb return 0;
539da8fa4e3SBjoern A. Zeeb }
540