Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/athk/ath10k/swap.c
48378 views
1
// SPDX-License-Identifier: ISC
2
/*
3
* Copyright (c) 2015-2016 Qualcomm Atheros, Inc.
4
*/
5
6
/* This file has implementation for code swap logic. With code swap feature,
7
* target can run the fw binary with even smaller IRAM size by using host
8
* memory to store some of the code segments.
9
*/
10
11
#include "core.h"
12
#include "bmi.h"
13
#include "debug.h"
14
15
static int ath10k_swap_code_seg_fill(struct ath10k *ar,
16
struct ath10k_swap_code_seg_info *seg_info,
17
const void *data, size_t data_len)
18
{
19
u8 *virt_addr = seg_info->virt_address[0];
20
u8 swap_magic[ATH10K_SWAP_CODE_SEG_MAGIC_BYTES_SZ] = {};
21
const u8 *fw_data = data;
22
const union ath10k_swap_code_seg_item *swap_item;
23
u32 length = 0;
24
u32 payload_len;
25
u32 total_payload_len = 0;
26
u32 size_left = data_len;
27
28
/* Parse swap bin and copy the content to host allocated memory.
29
* The format is Address, length and value. The last 4-bytes is
30
* target write address. Currently address field is not used.
31
*/
32
seg_info->target_addr = -1;
33
while (size_left >= sizeof(*swap_item)) {
34
swap_item = (const union ath10k_swap_code_seg_item *)fw_data;
35
payload_len = __le32_to_cpu(swap_item->tlv.length);
36
if ((payload_len > size_left) ||
37
(payload_len == 0 &&
38
size_left != sizeof(struct ath10k_swap_code_seg_tail))) {
39
ath10k_err(ar, "refusing to parse invalid tlv length %d\n",
40
payload_len);
41
return -EINVAL;
42
}
43
44
if (payload_len == 0) {
45
if (memcmp(swap_item->tail.magic_signature, swap_magic,
46
ATH10K_SWAP_CODE_SEG_MAGIC_BYTES_SZ)) {
47
ath10k_err(ar, "refusing an invalid swap file\n");
48
return -EINVAL;
49
}
50
seg_info->target_addr =
51
__le32_to_cpu(swap_item->tail.bmi_write_addr);
52
break;
53
}
54
55
memcpy(virt_addr, swap_item->tlv.data, payload_len);
56
virt_addr += payload_len;
57
length = payload_len + sizeof(struct ath10k_swap_code_seg_tlv);
58
size_left -= length;
59
fw_data += length;
60
total_payload_len += payload_len;
61
}
62
63
if (seg_info->target_addr == -1) {
64
ath10k_err(ar, "failed to parse invalid swap file\n");
65
return -EINVAL;
66
}
67
seg_info->seg_hw_info.swap_size = __cpu_to_le32(total_payload_len);
68
69
return 0;
70
}
71
72
static void
73
ath10k_swap_code_seg_free(struct ath10k *ar,
74
struct ath10k_swap_code_seg_info *seg_info)
75
{
76
u32 seg_size;
77
78
if (!seg_info)
79
return;
80
81
if (!seg_info->virt_address[0])
82
return;
83
84
seg_size = __le32_to_cpu(seg_info->seg_hw_info.size);
85
dma_free_coherent(ar->dev, seg_size, seg_info->virt_address[0],
86
seg_info->paddr[0]);
87
}
88
89
static struct ath10k_swap_code_seg_info *
90
ath10k_swap_code_seg_alloc(struct ath10k *ar, size_t swap_bin_len)
91
{
92
struct ath10k_swap_code_seg_info *seg_info;
93
void *virt_addr;
94
dma_addr_t paddr;
95
96
swap_bin_len = roundup(swap_bin_len, 2);
97
if (swap_bin_len > ATH10K_SWAP_CODE_SEG_BIN_LEN_MAX) {
98
ath10k_err(ar, "refusing code swap bin because it is too big %zu > %d\n",
99
swap_bin_len, ATH10K_SWAP_CODE_SEG_BIN_LEN_MAX);
100
return NULL;
101
}
102
103
seg_info = devm_kzalloc(ar->dev, sizeof(*seg_info), GFP_KERNEL);
104
if (!seg_info)
105
return NULL;
106
107
virt_addr = dma_alloc_coherent(ar->dev, swap_bin_len, &paddr,
108
GFP_KERNEL);
109
if (!virt_addr)
110
return NULL;
111
112
seg_info->seg_hw_info.bus_addr[0] = __cpu_to_le32(paddr);
113
seg_info->seg_hw_info.size = __cpu_to_le32(swap_bin_len);
114
seg_info->seg_hw_info.swap_size = __cpu_to_le32(swap_bin_len);
115
seg_info->seg_hw_info.num_segs =
116
__cpu_to_le32(ATH10K_SWAP_CODE_SEG_NUM_SUPPORTED);
117
seg_info->seg_hw_info.size_log2 = __cpu_to_le32(ilog2(swap_bin_len));
118
seg_info->virt_address[0] = virt_addr;
119
seg_info->paddr[0] = paddr;
120
121
return seg_info;
122
}
123
124
int ath10k_swap_code_seg_configure(struct ath10k *ar,
125
const struct ath10k_fw_file *fw_file)
126
{
127
int ret;
128
struct ath10k_swap_code_seg_info *seg_info = NULL;
129
130
if (!fw_file->firmware_swap_code_seg_info)
131
return 0;
132
133
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot found firmware code swap binary\n");
134
135
seg_info = fw_file->firmware_swap_code_seg_info;
136
137
ret = ath10k_bmi_write_memory(ar, seg_info->target_addr,
138
#if defined(__linux__)
139
&seg_info->seg_hw_info,
140
#elif defined(__FreeBSD__)
141
(void *)&seg_info->seg_hw_info,
142
#endif
143
sizeof(seg_info->seg_hw_info));
144
if (ret) {
145
ath10k_err(ar, "failed to write Code swap segment information (%d)\n",
146
ret);
147
return ret;
148
}
149
150
return 0;
151
}
152
153
void ath10k_swap_code_seg_release(struct ath10k *ar,
154
struct ath10k_fw_file *fw_file)
155
{
156
ath10k_swap_code_seg_free(ar, fw_file->firmware_swap_code_seg_info);
157
158
/* FIXME: these two assignments look to bein wrong place! Shouldn't
159
* they be in ath10k_core_free_firmware_files() like the rest?
160
*/
161
fw_file->codeswap_data = NULL;
162
fw_file->codeswap_len = 0;
163
164
fw_file->firmware_swap_code_seg_info = NULL;
165
}
166
167
int ath10k_swap_code_seg_init(struct ath10k *ar, struct ath10k_fw_file *fw_file)
168
{
169
int ret;
170
struct ath10k_swap_code_seg_info *seg_info;
171
const void *codeswap_data;
172
size_t codeswap_len;
173
174
codeswap_data = fw_file->codeswap_data;
175
codeswap_len = fw_file->codeswap_len;
176
177
if (!codeswap_len || !codeswap_data)
178
return 0;
179
180
seg_info = ath10k_swap_code_seg_alloc(ar, codeswap_len);
181
if (!seg_info) {
182
ath10k_err(ar, "failed to allocate fw code swap segment\n");
183
return -ENOMEM;
184
}
185
186
ret = ath10k_swap_code_seg_fill(ar, seg_info,
187
codeswap_data, codeswap_len);
188
189
if (ret) {
190
ath10k_warn(ar, "failed to initialize fw code swap segment: %d\n",
191
ret);
192
ath10k_swap_code_seg_free(ar, seg_info);
193
return ret;
194
}
195
196
fw_file->firmware_swap_code_seg_info = seg_info;
197
198
return 0;
199
}
200
201