0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include "core.h"
0012 #include "bmi.h"
0013 #include "debug.h"
0014
0015 static int ath10k_swap_code_seg_fill(struct ath10k *ar,
0016 struct ath10k_swap_code_seg_info *seg_info,
0017 const void *data, size_t data_len)
0018 {
0019 u8 *virt_addr = seg_info->virt_address[0];
0020 u8 swap_magic[ATH10K_SWAP_CODE_SEG_MAGIC_BYTES_SZ] = {};
0021 const u8 *fw_data = data;
0022 union ath10k_swap_code_seg_item *swap_item;
0023 u32 length = 0;
0024 u32 payload_len;
0025 u32 total_payload_len = 0;
0026 u32 size_left = data_len;
0027
0028
0029
0030
0031
0032 seg_info->target_addr = -1;
0033 while (size_left >= sizeof(*swap_item)) {
0034 swap_item = (union ath10k_swap_code_seg_item *)fw_data;
0035 payload_len = __le32_to_cpu(swap_item->tlv.length);
0036 if ((payload_len > size_left) ||
0037 (payload_len == 0 &&
0038 size_left != sizeof(struct ath10k_swap_code_seg_tail))) {
0039 ath10k_err(ar, "refusing to parse invalid tlv length %d\n",
0040 payload_len);
0041 return -EINVAL;
0042 }
0043
0044 if (payload_len == 0) {
0045 if (memcmp(swap_item->tail.magic_signature, swap_magic,
0046 ATH10K_SWAP_CODE_SEG_MAGIC_BYTES_SZ)) {
0047 ath10k_err(ar, "refusing an invalid swap file\n");
0048 return -EINVAL;
0049 }
0050 seg_info->target_addr =
0051 __le32_to_cpu(swap_item->tail.bmi_write_addr);
0052 break;
0053 }
0054
0055 memcpy(virt_addr, swap_item->tlv.data, payload_len);
0056 virt_addr += payload_len;
0057 length = payload_len + sizeof(struct ath10k_swap_code_seg_tlv);
0058 size_left -= length;
0059 fw_data += length;
0060 total_payload_len += payload_len;
0061 }
0062
0063 if (seg_info->target_addr == -1) {
0064 ath10k_err(ar, "failed to parse invalid swap file\n");
0065 return -EINVAL;
0066 }
0067 seg_info->seg_hw_info.swap_size = __cpu_to_le32(total_payload_len);
0068
0069 return 0;
0070 }
0071
0072 static void
0073 ath10k_swap_code_seg_free(struct ath10k *ar,
0074 struct ath10k_swap_code_seg_info *seg_info)
0075 {
0076 u32 seg_size;
0077
0078 if (!seg_info)
0079 return;
0080
0081 if (!seg_info->virt_address[0])
0082 return;
0083
0084 seg_size = __le32_to_cpu(seg_info->seg_hw_info.size);
0085 dma_free_coherent(ar->dev, seg_size, seg_info->virt_address[0],
0086 seg_info->paddr[0]);
0087 }
0088
0089 static struct ath10k_swap_code_seg_info *
0090 ath10k_swap_code_seg_alloc(struct ath10k *ar, size_t swap_bin_len)
0091 {
0092 struct ath10k_swap_code_seg_info *seg_info;
0093 void *virt_addr;
0094 dma_addr_t paddr;
0095
0096 swap_bin_len = roundup(swap_bin_len, 2);
0097 if (swap_bin_len > ATH10K_SWAP_CODE_SEG_BIN_LEN_MAX) {
0098 ath10k_err(ar, "refusing code swap bin because it is too big %zu > %d\n",
0099 swap_bin_len, ATH10K_SWAP_CODE_SEG_BIN_LEN_MAX);
0100 return NULL;
0101 }
0102
0103 seg_info = devm_kzalloc(ar->dev, sizeof(*seg_info), GFP_KERNEL);
0104 if (!seg_info)
0105 return NULL;
0106
0107 virt_addr = dma_alloc_coherent(ar->dev, swap_bin_len, &paddr,
0108 GFP_KERNEL);
0109 if (!virt_addr)
0110 return NULL;
0111
0112 seg_info->seg_hw_info.bus_addr[0] = __cpu_to_le32(paddr);
0113 seg_info->seg_hw_info.size = __cpu_to_le32(swap_bin_len);
0114 seg_info->seg_hw_info.swap_size = __cpu_to_le32(swap_bin_len);
0115 seg_info->seg_hw_info.num_segs =
0116 __cpu_to_le32(ATH10K_SWAP_CODE_SEG_NUM_SUPPORTED);
0117 seg_info->seg_hw_info.size_log2 = __cpu_to_le32(ilog2(swap_bin_len));
0118 seg_info->virt_address[0] = virt_addr;
0119 seg_info->paddr[0] = paddr;
0120
0121 return seg_info;
0122 }
0123
0124 int ath10k_swap_code_seg_configure(struct ath10k *ar,
0125 const struct ath10k_fw_file *fw_file)
0126 {
0127 int ret;
0128 struct ath10k_swap_code_seg_info *seg_info = NULL;
0129
0130 if (!fw_file->firmware_swap_code_seg_info)
0131 return 0;
0132
0133 ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot found firmware code swap binary\n");
0134
0135 seg_info = fw_file->firmware_swap_code_seg_info;
0136
0137 ret = ath10k_bmi_write_memory(ar, seg_info->target_addr,
0138 &seg_info->seg_hw_info,
0139 sizeof(seg_info->seg_hw_info));
0140 if (ret) {
0141 ath10k_err(ar, "failed to write Code swap segment information (%d)\n",
0142 ret);
0143 return ret;
0144 }
0145
0146 return 0;
0147 }
0148
0149 void ath10k_swap_code_seg_release(struct ath10k *ar,
0150 struct ath10k_fw_file *fw_file)
0151 {
0152 ath10k_swap_code_seg_free(ar, fw_file->firmware_swap_code_seg_info);
0153
0154
0155
0156
0157 fw_file->codeswap_data = NULL;
0158 fw_file->codeswap_len = 0;
0159
0160 fw_file->firmware_swap_code_seg_info = NULL;
0161 }
0162
0163 int ath10k_swap_code_seg_init(struct ath10k *ar, struct ath10k_fw_file *fw_file)
0164 {
0165 int ret;
0166 struct ath10k_swap_code_seg_info *seg_info;
0167 const void *codeswap_data;
0168 size_t codeswap_len;
0169
0170 codeswap_data = fw_file->codeswap_data;
0171 codeswap_len = fw_file->codeswap_len;
0172
0173 if (!codeswap_len || !codeswap_data)
0174 return 0;
0175
0176 seg_info = ath10k_swap_code_seg_alloc(ar, codeswap_len);
0177 if (!seg_info) {
0178 ath10k_err(ar, "failed to allocate fw code swap segment\n");
0179 return -ENOMEM;
0180 }
0181
0182 ret = ath10k_swap_code_seg_fill(ar, seg_info,
0183 codeswap_data, codeswap_len);
0184
0185 if (ret) {
0186 ath10k_warn(ar, "failed to initialize fw code swap segment: %d\n",
0187 ret);
0188 ath10k_swap_code_seg_free(ar, seg_info);
0189 return ret;
0190 }
0191
0192 fw_file->firmware_swap_code_seg_info = seg_info;
0193
0194 return 0;
0195 }