2 * Copyright (c) 2015 Qualcomm Atheros, Inc.
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 /* This file has implementation for code swap logic. With code swap feature,
18 * target can run the fw binary with even smaller IRAM size by using host
19 * memory to store some of the code segments.
26 static int ath10k_swap_code_seg_fill(struct ath10k
*ar
,
27 struct ath10k_swap_code_seg_info
*seg_info
,
28 const void *data
, size_t data_len
)
30 u8
*virt_addr
= seg_info
->virt_address
[0];
31 u8 swap_magic
[ATH10K_SWAP_CODE_SEG_MAGIC_BYTES_SZ
] = {};
32 const u8
*fw_data
= data
;
33 union ath10k_swap_code_seg_item
*swap_item
;
36 u32 total_payload_len
= 0;
37 u32 size_left
= data_len
;
39 /* Parse swap bin and copy the content to host allocated memory.
40 * The format is Address, length and value. The last 4-bytes is
41 * target write address. Currently address field is not used.
43 seg_info
->target_addr
= -1;
44 while (size_left
>= sizeof(*swap_item
)) {
45 swap_item
= (union ath10k_swap_code_seg_item
*)fw_data
;
46 payload_len
= __le32_to_cpu(swap_item
->tlv
.length
);
47 if ((payload_len
> size_left
) ||
49 size_left
!= sizeof(struct ath10k_swap_code_seg_tail
))) {
50 ath10k_err(ar
, "refusing to parse invalid tlv length %d\n",
55 if (payload_len
== 0) {
56 if (memcmp(swap_item
->tail
.magic_signature
, swap_magic
,
57 ATH10K_SWAP_CODE_SEG_MAGIC_BYTES_SZ
)) {
58 ath10k_err(ar
, "refusing an invalid swap file\n");
61 seg_info
->target_addr
=
62 __le32_to_cpu(swap_item
->tail
.bmi_write_addr
);
66 memcpy(virt_addr
, swap_item
->tlv
.data
, payload_len
);
67 virt_addr
+= payload_len
;
68 length
= payload_len
+ sizeof(struct ath10k_swap_code_seg_tlv
);
71 total_payload_len
+= payload_len
;
74 if (seg_info
->target_addr
== -1) {
75 ath10k_err(ar
, "failed to parse invalid swap file\n");
78 seg_info
->seg_hw_info
.swap_size
= __cpu_to_le32(total_payload_len
);
84 ath10k_swap_code_seg_free(struct ath10k
*ar
,
85 struct ath10k_swap_code_seg_info
*seg_info
)
92 if (!seg_info
->virt_address
[0])
95 seg_size
= __le32_to_cpu(seg_info
->seg_hw_info
.size
);
96 dma_free_coherent(ar
->dev
, seg_size
, seg_info
->virt_address
[0],
100 static struct ath10k_swap_code_seg_info
*
101 ath10k_swap_code_seg_alloc(struct ath10k
*ar
, size_t swap_bin_len
)
103 struct ath10k_swap_code_seg_info
*seg_info
;
107 swap_bin_len
= roundup(swap_bin_len
, 2);
108 if (swap_bin_len
> ATH10K_SWAP_CODE_SEG_BIN_LEN_MAX
) {
109 ath10k_err(ar
, "refusing code swap bin because it is too big %zu > %d\n",
110 swap_bin_len
, ATH10K_SWAP_CODE_SEG_BIN_LEN_MAX
);
114 seg_info
= devm_kzalloc(ar
->dev
, sizeof(*seg_info
), GFP_KERNEL
);
118 virt_addr
= dma_alloc_coherent(ar
->dev
, swap_bin_len
, &paddr
,
121 ath10k_err(ar
, "failed to allocate dma coherent memory\n");
125 seg_info
->seg_hw_info
.bus_addr
[0] = __cpu_to_le32(paddr
);
126 seg_info
->seg_hw_info
.size
= __cpu_to_le32(swap_bin_len
);
127 seg_info
->seg_hw_info
.swap_size
= __cpu_to_le32(swap_bin_len
);
128 seg_info
->seg_hw_info
.num_segs
=
129 __cpu_to_le32(ATH10K_SWAP_CODE_SEG_NUM_SUPPORTED
);
130 seg_info
->seg_hw_info
.size_log2
= __cpu_to_le32(ilog2(swap_bin_len
));
131 seg_info
->virt_address
[0] = virt_addr
;
132 seg_info
->paddr
[0] = paddr
;
137 int ath10k_swap_code_seg_configure(struct ath10k
*ar
,
138 enum ath10k_swap_code_seg_bin_type type
)
141 struct ath10k_swap_code_seg_info
*seg_info
= NULL
;
144 case ATH10K_SWAP_CODE_SEG_BIN_TYPE_FW
:
145 if (!ar
->swap
.firmware_swap_code_seg_info
)
148 ath10k_dbg(ar
, ATH10K_DBG_BOOT
, "boot found firmware code swap binary\n");
149 seg_info
= ar
->swap
.firmware_swap_code_seg_info
;
152 case ATH10K_SWAP_CODE_SEG_BIN_TYPE_OTP
:
153 case ATH10K_SWAP_CODE_SEG_BIN_TYPE_UTF
:
154 ath10k_warn(ar
, "ignoring unknown code swap binary type %d\n",
159 ret
= ath10k_bmi_write_memory(ar
, seg_info
->target_addr
,
160 &seg_info
->seg_hw_info
,
161 sizeof(seg_info
->seg_hw_info
));
163 ath10k_err(ar
, "failed to write Code swap segment information (%d)\n",
171 void ath10k_swap_code_seg_release(struct ath10k
*ar
)
173 ath10k_swap_code_seg_free(ar
, ar
->swap
.firmware_swap_code_seg_info
);
174 ar
->swap
.firmware_codeswap_data
= NULL
;
175 ar
->swap
.firmware_codeswap_len
= 0;
176 ar
->swap
.firmware_swap_code_seg_info
= NULL
;
179 int ath10k_swap_code_seg_init(struct ath10k
*ar
)
182 struct ath10k_swap_code_seg_info
*seg_info
;
184 if (!ar
->swap
.firmware_codeswap_len
|| !ar
->swap
.firmware_codeswap_data
)
187 seg_info
= ath10k_swap_code_seg_alloc(ar
,
188 ar
->swap
.firmware_codeswap_len
);
190 ath10k_err(ar
, "failed to allocate fw code swap segment\n");
194 ret
= ath10k_swap_code_seg_fill(ar
, seg_info
,
195 ar
->swap
.firmware_codeswap_data
,
196 ar
->swap
.firmware_codeswap_len
);
199 ath10k_warn(ar
, "failed to initialize fw code swap segment: %d\n",
201 ath10k_swap_code_seg_free(ar
, seg_info
);
205 ar
->swap
.firmware_swap_code_seg_info
= seg_info
;