1 // SPDX-License-Identifier: ISC
3 * Copyright (c) 2015-2016 Qualcomm Atheros, Inc.
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.
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
)
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 union ath10k_swap_code_seg_item
*swap_item
;
25 u32 total_payload_len
= 0;
26 u32 size_left
= data_len
;
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.
32 seg_info
->target_addr
= -1;
33 while (size_left
>= sizeof(*swap_item
)) {
34 swap_item
= (union ath10k_swap_code_seg_item
*)fw_data
;
35 payload_len
= __le32_to_cpu(swap_item
->tlv
.length
);
36 if ((payload_len
> size_left
) ||
38 size_left
!= sizeof(struct ath10k_swap_code_seg_tail
))) {
39 ath10k_err(ar
, "refusing to parse invalid tlv length %d\n",
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");
50 seg_info
->target_addr
=
51 __le32_to_cpu(swap_item
->tail
.bmi_write_addr
);
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
);
60 total_payload_len
+= payload_len
;
63 if (seg_info
->target_addr
== -1) {
64 ath10k_err(ar
, "failed to parse invalid swap file\n");
67 seg_info
->seg_hw_info
.swap_size
= __cpu_to_le32(total_payload_len
);
73 ath10k_swap_code_seg_free(struct ath10k
*ar
,
74 struct ath10k_swap_code_seg_info
*seg_info
)
81 if (!seg_info
->virt_address
[0])
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],
89 static struct ath10k_swap_code_seg_info
*
90 ath10k_swap_code_seg_alloc(struct ath10k
*ar
, size_t swap_bin_len
)
92 struct ath10k_swap_code_seg_info
*seg_info
;
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
);
103 seg_info
= devm_kzalloc(ar
->dev
, sizeof(*seg_info
), GFP_KERNEL
);
107 virt_addr
= dma_alloc_coherent(ar
->dev
, swap_bin_len
, &paddr
,
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
;
124 int ath10k_swap_code_seg_configure(struct ath10k
*ar
,
125 const struct ath10k_fw_file
*fw_file
)
128 struct ath10k_swap_code_seg_info
*seg_info
= NULL
;
130 if (!fw_file
->firmware_swap_code_seg_info
)
133 ath10k_dbg(ar
, ATH10K_DBG_BOOT
, "boot found firmware code swap binary\n");
135 seg_info
= fw_file
->firmware_swap_code_seg_info
;
137 ret
= ath10k_bmi_write_memory(ar
, seg_info
->target_addr
,
138 &seg_info
->seg_hw_info
,
139 sizeof(seg_info
->seg_hw_info
));
141 ath10k_err(ar
, "failed to write Code swap segment information (%d)\n",
149 void ath10k_swap_code_seg_release(struct ath10k
*ar
,
150 struct ath10k_fw_file
*fw_file
)
152 ath10k_swap_code_seg_free(ar
, fw_file
->firmware_swap_code_seg_info
);
154 /* FIXME: these two assignments look to bein wrong place! Shouldn't
155 * they be in ath10k_core_free_firmware_files() like the rest?
157 fw_file
->codeswap_data
= NULL
;
158 fw_file
->codeswap_len
= 0;
160 fw_file
->firmware_swap_code_seg_info
= NULL
;
163 int ath10k_swap_code_seg_init(struct ath10k
*ar
, struct ath10k_fw_file
*fw_file
)
166 struct ath10k_swap_code_seg_info
*seg_info
;
167 const void *codeswap_data
;
170 codeswap_data
= fw_file
->codeswap_data
;
171 codeswap_len
= fw_file
->codeswap_len
;
173 if (!codeswap_len
|| !codeswap_data
)
176 seg_info
= ath10k_swap_code_seg_alloc(ar
, codeswap_len
);
178 ath10k_err(ar
, "failed to allocate fw code swap segment\n");
182 ret
= ath10k_swap_code_seg_fill(ar
, seg_info
,
183 codeswap_data
, codeswap_len
);
186 ath10k_warn(ar
, "failed to initialize fw code swap segment: %d\n",
188 ath10k_swap_code_seg_free(ar
, seg_info
);
192 fw_file
->firmware_swap_code_seg_info
= seg_info
;