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