1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /******************************************************************************
4 * Copyright(c) 2020 Intel Corporation
6 *****************************************************************************/
12 #include "fw/api/commands.h"
13 #include "fw/api/nvm-reg.h"
14 #include "fw/api/alive.h"
16 struct iwl_pnvm_section
{
21 static bool iwl_pnvm_complete_fn(struct iwl_notif_wait_data
*notif_wait
,
22 struct iwl_rx_packet
*pkt
, void *data
)
24 struct iwl_trans
*trans
= (struct iwl_trans
*)data
;
25 struct iwl_pnvm_init_complete_ntfy
*pnvm_ntf
= (void *)pkt
->data
;
28 "PNVM complete notification received with status %d\n",
29 le32_to_cpu(pnvm_ntf
->status
));
34 static int iwl_pnvm_handle_section(struct iwl_trans
*trans
, const u8
*data
,
37 struct iwl_ucode_tlv
*tlv
;
39 u16 mac_type
= 0, rf_id
= 0;
40 u8
*pnvm_data
= NULL
, *tmp
;
44 IWL_DEBUG_FW(trans
, "Handling PNVM section\n");
46 while (len
>= sizeof(*tlv
)) {
47 u32 tlv_len
, tlv_type
;
52 tlv_len
= le32_to_cpu(tlv
->length
);
53 tlv_type
= le32_to_cpu(tlv
->type
);
56 IWL_ERR(trans
, "invalid TLV len: %zd/%u\n",
65 case IWL_UCODE_TLV_PNVM_VERSION
:
66 if (tlv_len
< sizeof(__le32
)) {
68 "Invalid size for IWL_UCODE_TLV_PNVM_VERSION (expected %zd, got %d)\n",
69 sizeof(__le32
), tlv_len
);
73 sha1
= le32_to_cpup((__le32
*)data
);
76 "Got IWL_UCODE_TLV_PNVM_VERSION %0x\n",
79 case IWL_UCODE_TLV_HW_TYPE
:
80 if (tlv_len
< 2 * sizeof(__le16
)) {
82 "Invalid size for IWL_UCODE_TLV_HW_TYPE (expected %zd, got %d)\n",
83 2 * sizeof(__le16
), tlv_len
);
87 mac_type
= le16_to_cpup((__le16
*)data
);
88 rf_id
= le16_to_cpup((__le16
*)(data
+ sizeof(__le16
)));
91 "Got IWL_UCODE_TLV_HW_TYPE mac_type 0x%0x rf_id 0x%0x\n",
94 if (mac_type
!= CSR_HW_REV_TYPE(trans
->hw_rev
) ||
95 rf_id
!= CSR_HW_RFID_TYPE(trans
->hw_rf_id
)) {
97 "HW mismatch, skipping PNVM section, mac_type 0x%0x, rf_id 0x%0x.\n",
98 CSR_HW_REV_TYPE(trans
->hw_rev
), trans
->hw_rf_id
);
104 case IWL_UCODE_TLV_SEC_RT
: {
105 struct iwl_pnvm_section
*section
= (void *)data
;
106 u32 data_len
= tlv_len
- sizeof(*section
);
109 "Got IWL_UCODE_TLV_SEC_RT len %d\n",
112 /* TODO: remove, this is a deprecated separator */
113 if (le32_to_cpup((__le32
*)data
) == 0xddddeeee) {
114 IWL_DEBUG_FW(trans
, "Ignoring separator.\n");
118 IWL_DEBUG_FW(trans
, "Adding data (size %d)\n",
121 tmp
= krealloc(pnvm_data
, size
+ data_len
, GFP_KERNEL
);
124 "Couldn't allocate (more) pnvm_data\n");
132 memcpy(pnvm_data
+ size
, section
->data
, data_len
);
138 case IWL_UCODE_TLV_PNVM_SKU
:
140 "New PNVM section started, stop parsing.\n");
143 IWL_DEBUG_FW(trans
, "Found TLV 0x%0x, len %d\n",
148 len
-= ALIGN(tlv_len
, 4);
149 data
+= ALIGN(tlv_len
, 4);
154 IWL_DEBUG_FW(trans
, "Empty PNVM, skipping.\n");
159 IWL_INFO(trans
, "loaded PNVM version 0x%0x\n", sha1
);
161 ret
= iwl_trans_set_pnvm(trans
, pnvm_data
, size
);
167 static int iwl_pnvm_parse(struct iwl_trans
*trans
, const u8
*data
,
170 struct iwl_ucode_tlv
*tlv
;
172 IWL_DEBUG_FW(trans
, "Parsing PNVM file\n");
174 while (len
>= sizeof(*tlv
)) {
175 u32 tlv_len
, tlv_type
;
180 tlv_len
= le32_to_cpu(tlv
->length
);
181 tlv_type
= le32_to_cpu(tlv
->type
);
184 IWL_ERR(trans
, "invalid TLV len: %zd/%u\n",
189 if (tlv_type
== IWL_UCODE_TLV_PNVM_SKU
) {
190 struct iwl_sku_id
*sku_id
=
191 (void *)(data
+ sizeof(*tlv
));
194 "Got IWL_UCODE_TLV_PNVM_SKU len %d\n",
196 IWL_DEBUG_FW(trans
, "sku_id 0x%0x 0x%0x 0x%0x\n",
197 le32_to_cpu(sku_id
->data
[0]),
198 le32_to_cpu(sku_id
->data
[1]),
199 le32_to_cpu(sku_id
->data
[2]));
201 if (trans
->sku_id
[0] == le32_to_cpu(sku_id
->data
[0]) &&
202 trans
->sku_id
[1] == le32_to_cpu(sku_id
->data
[1]) &&
203 trans
->sku_id
[2] == le32_to_cpu(sku_id
->data
[2])) {
206 data
+= sizeof(*tlv
) + ALIGN(tlv_len
, 4);
207 len
-= ALIGN(tlv_len
, 4);
209 ret
= iwl_pnvm_handle_section(trans
, data
, len
);
213 IWL_DEBUG_FW(trans
, "SKU ID didn't match!\n");
216 data
+= sizeof(*tlv
) + ALIGN(tlv_len
, 4);
217 len
-= ALIGN(tlv_len
, 4);
224 int iwl_pnvm_load(struct iwl_trans
*trans
,
225 struct iwl_notif_wait_data
*notif_wait
)
227 const struct firmware
*pnvm
;
228 struct iwl_notification_wait pnvm_wait
;
229 static const u16 ntf_cmds
[] = { WIDE_ID(REGULATORY_AND_NVM_GROUP
,
230 PNVM_INIT_COMPLETE_NTFY
) };
234 /* if the SKU_ID is empty, there's nothing to do */
235 if (!trans
->sku_id
[0] && !trans
->sku_id
[1] && !trans
->sku_id
[2])
238 /* if we already have it, nothing to do either */
239 if (trans
->pnvm_loaded
)
243 * The prefix unfortunately includes a hyphen at the end, so
244 * don't add the dot here...
246 snprintf(pnvm_name
, sizeof(pnvm_name
), "%spnvm",
247 trans
->cfg
->fw_name_pre
);
249 /* ...but replace the hyphen with the dot here. */
250 if (strlen(trans
->cfg
->fw_name_pre
) < sizeof(pnvm_name
))
251 pnvm_name
[strlen(trans
->cfg
->fw_name_pre
) - 1] = '.';
253 ret
= firmware_request_nowarn(&pnvm
, pnvm_name
, trans
->dev
);
255 IWL_DEBUG_FW(trans
, "PNVM file %s not found %d\n",
258 iwl_pnvm_parse(trans
, pnvm
->data
, pnvm
->size
);
260 release_firmware(pnvm
);
263 iwl_init_notification_wait(notif_wait
, &pnvm_wait
,
264 ntf_cmds
, ARRAY_SIZE(ntf_cmds
),
265 iwl_pnvm_complete_fn
, trans
);
267 /* kick the doorbell */
268 iwl_write_umac_prph(trans
, UREG_DOORBELL_TO_ISR6
,
269 UREG_DOORBELL_TO_ISR6_PNVM
);
271 return iwl_wait_notification(notif_wait
, &pnvm_wait
,
272 MVM_UCODE_PNVM_TIMEOUT
);
274 IWL_EXPORT_SYMBOL(iwl_pnvm_load
);