1d1e879ecSMiri Korenblit // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2d1e879ecSMiri Korenblit /*
3d1e879ecSMiri Korenblit * Copyright (C) 2024-2025 Intel Corporation
4d1e879ecSMiri Korenblit */
5d1e879ecSMiri Korenblit
6d1e879ecSMiri Korenblit #include "mld.h"
7d1e879ecSMiri Korenblit
8d1e879ecSMiri Korenblit #include "fw/api/alive.h"
9d1e879ecSMiri Korenblit #include "fw/api/scan.h"
10d1e879ecSMiri Korenblit #include "fw/api/rx.h"
111f263e63SMiri Korenblit #include "phy.h"
12d1e879ecSMiri Korenblit #include "fw/dbg.h"
13d1e879ecSMiri Korenblit #include "fw/pnvm.h"
14d1e879ecSMiri Korenblit #include "hcmd.h"
15d1e879ecSMiri Korenblit #include "power.h"
16d1e879ecSMiri Korenblit #include "mcc.h"
17d1e879ecSMiri Korenblit #include "led.h"
18d1e879ecSMiri Korenblit #include "coex.h"
19d1e879ecSMiri Korenblit #include "regulatory.h"
20d1e879ecSMiri Korenblit #include "thermal.h"
21d1e879ecSMiri Korenblit
iwl_mld_send_tx_ant_cfg(struct iwl_mld * mld)22d1e879ecSMiri Korenblit static int iwl_mld_send_tx_ant_cfg(struct iwl_mld *mld)
23d1e879ecSMiri Korenblit {
24d1e879ecSMiri Korenblit struct iwl_tx_ant_cfg_cmd cmd;
25d1e879ecSMiri Korenblit
26d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy);
27d1e879ecSMiri Korenblit
28d1e879ecSMiri Korenblit cmd.valid = cpu_to_le32(iwl_mld_get_valid_tx_ant(mld));
29d1e879ecSMiri Korenblit
30d1e879ecSMiri Korenblit IWL_DEBUG_FW(mld, "select valid tx ant: %u\n", cmd.valid);
31d1e879ecSMiri Korenblit
32d1e879ecSMiri Korenblit return iwl_mld_send_cmd_pdu(mld, TX_ANT_CONFIGURATION_CMD, &cmd);
33d1e879ecSMiri Korenblit }
34d1e879ecSMiri Korenblit
iwl_mld_send_rss_cfg_cmd(struct iwl_mld * mld)35d1e879ecSMiri Korenblit static int iwl_mld_send_rss_cfg_cmd(struct iwl_mld *mld)
36d1e879ecSMiri Korenblit {
37d1e879ecSMiri Korenblit struct iwl_rss_config_cmd cmd = {
38d1e879ecSMiri Korenblit .flags = cpu_to_le32(IWL_RSS_ENABLE),
39d1e879ecSMiri Korenblit .hash_mask = BIT(IWL_RSS_HASH_TYPE_IPV4_TCP) |
40d1e879ecSMiri Korenblit BIT(IWL_RSS_HASH_TYPE_IPV4_UDP) |
41d1e879ecSMiri Korenblit BIT(IWL_RSS_HASH_TYPE_IPV4_PAYLOAD) |
42d1e879ecSMiri Korenblit BIT(IWL_RSS_HASH_TYPE_IPV6_TCP) |
43d1e879ecSMiri Korenblit BIT(IWL_RSS_HASH_TYPE_IPV6_UDP) |
44d1e879ecSMiri Korenblit BIT(IWL_RSS_HASH_TYPE_IPV6_PAYLOAD),
45d1e879ecSMiri Korenblit };
46d1e879ecSMiri Korenblit
47d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy);
48d1e879ecSMiri Korenblit
49d1e879ecSMiri Korenblit /* Do not direct RSS traffic to Q 0 which is our fallback queue */
50d1e879ecSMiri Korenblit for (int i = 0; i < ARRAY_SIZE(cmd.indirection_table); i++)
51d1e879ecSMiri Korenblit cmd.indirection_table[i] =
52d5861378SJohannes Berg 1 + (i % (mld->trans->info.num_rxqs - 1));
53d1e879ecSMiri Korenblit netdev_rss_key_fill(cmd.secret_key, sizeof(cmd.secret_key));
54d1e879ecSMiri Korenblit
55d1e879ecSMiri Korenblit return iwl_mld_send_cmd_pdu(mld, RSS_CONFIG_CMD, &cmd);
56d1e879ecSMiri Korenblit }
57d1e879ecSMiri Korenblit
iwl_mld_config_scan(struct iwl_mld * mld)58d1e879ecSMiri Korenblit static int iwl_mld_config_scan(struct iwl_mld *mld)
59d1e879ecSMiri Korenblit {
60d1e879ecSMiri Korenblit struct iwl_scan_config cmd = {
61d1e879ecSMiri Korenblit .tx_chains = cpu_to_le32(iwl_mld_get_valid_tx_ant(mld)),
62d1e879ecSMiri Korenblit .rx_chains = cpu_to_le32(iwl_mld_get_valid_rx_ant(mld))
63d1e879ecSMiri Korenblit };
64d1e879ecSMiri Korenblit
65d1e879ecSMiri Korenblit return iwl_mld_send_cmd_pdu(mld, WIDE_ID(LONG_GROUP, SCAN_CFG_CMD),
66d1e879ecSMiri Korenblit &cmd);
67d1e879ecSMiri Korenblit }
68d1e879ecSMiri Korenblit
iwl_mld_alive_imr_data(struct iwl_trans * trans,const struct iwl_imr_alive_info * imr_info)69d1e879ecSMiri Korenblit static void iwl_mld_alive_imr_data(struct iwl_trans *trans,
70d1e879ecSMiri Korenblit const struct iwl_imr_alive_info *imr_info)
71d1e879ecSMiri Korenblit {
72d1e879ecSMiri Korenblit struct iwl_imr_data *imr_data = &trans->dbg.imr_data;
73d1e879ecSMiri Korenblit
74d1e879ecSMiri Korenblit imr_data->imr_enable = le32_to_cpu(imr_info->enabled);
75d1e879ecSMiri Korenblit imr_data->imr_size = le32_to_cpu(imr_info->size);
76d1e879ecSMiri Korenblit imr_data->imr2sram_remainbyte = imr_data->imr_size;
77d1e879ecSMiri Korenblit imr_data->imr_base_addr = imr_info->base_addr;
78d1e879ecSMiri Korenblit imr_data->imr_curr_addr = le64_to_cpu(imr_data->imr_base_addr);
79d1e879ecSMiri Korenblit
80d1e879ecSMiri Korenblit if (imr_data->imr_enable)
81d1e879ecSMiri Korenblit return;
82d1e879ecSMiri Korenblit
83d1e879ecSMiri Korenblit for (int i = 0; i < ARRAY_SIZE(trans->dbg.active_regions); i++) {
84d1e879ecSMiri Korenblit struct iwl_fw_ini_region_tlv *reg;
85d1e879ecSMiri Korenblit
86d1e879ecSMiri Korenblit if (!trans->dbg.active_regions[i])
87d1e879ecSMiri Korenblit continue;
88d1e879ecSMiri Korenblit
89d1e879ecSMiri Korenblit reg = (void *)trans->dbg.active_regions[i]->data;
90d1e879ecSMiri Korenblit
91d1e879ecSMiri Korenblit /* We have only one DRAM IMR region, so we
92d1e879ecSMiri Korenblit * can break as soon as we find the first
93d1e879ecSMiri Korenblit * one.
94d1e879ecSMiri Korenblit */
95d1e879ecSMiri Korenblit if (reg->type == IWL_FW_INI_REGION_DRAM_IMR) {
96d1e879ecSMiri Korenblit trans->dbg.unsupported_region_msk |= BIT(i);
97d1e879ecSMiri Korenblit break;
98d1e879ecSMiri Korenblit }
99d1e879ecSMiri Korenblit }
100d1e879ecSMiri Korenblit }
101d1e879ecSMiri Korenblit
1023a68ae0fSJohannes Berg struct iwl_mld_alive_data {
1033a68ae0fSJohannes Berg __le32 sku_id[3];
1043a68ae0fSJohannes Berg bool valid;
1053a68ae0fSJohannes Berg };
1063a68ae0fSJohannes Berg
iwl_alive_fn(struct iwl_notif_wait_data * notif_wait,struct iwl_rx_packet * pkt,void * data)107d1e879ecSMiri Korenblit static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
108d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt, void *data)
109d1e879ecSMiri Korenblit {
110d1e879ecSMiri Korenblit unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
1110bd6ede7SEmmanuel Grumbach unsigned int expected_sz;
112d1e879ecSMiri Korenblit struct iwl_mld *mld =
113d1e879ecSMiri Korenblit container_of(notif_wait, struct iwl_mld, notif_wait);
114d1e879ecSMiri Korenblit struct iwl_trans *trans = mld->trans;
115d1e879ecSMiri Korenblit u32 version = iwl_fw_lookup_notif_ver(mld->fw, LEGACY_GROUP,
116d1e879ecSMiri Korenblit UCODE_ALIVE_NTFY, 0);
1173a68ae0fSJohannes Berg struct iwl_mld_alive_data *alive_data = data;
1180bd6ede7SEmmanuel Grumbach struct iwl_alive_ntf *palive;
119d1e879ecSMiri Korenblit struct iwl_umac_alive *umac;
120d1e879ecSMiri Korenblit struct iwl_lmac_alive *lmac1;
121d1e879ecSMiri Korenblit struct iwl_lmac_alive *lmac2 = NULL;
122d1e879ecSMiri Korenblit u32 lmac_error_event_table;
123d1e879ecSMiri Korenblit u32 umac_error_table;
124d1e879ecSMiri Korenblit u16 status;
125d1e879ecSMiri Korenblit
1260bd6ede7SEmmanuel Grumbach switch (version) {
1270bd6ede7SEmmanuel Grumbach case 6:
1280bd6ede7SEmmanuel Grumbach case 7:
1290bd6ede7SEmmanuel Grumbach expected_sz = sizeof(struct iwl_alive_ntf_v6);
1300bd6ede7SEmmanuel Grumbach break;
1310bd6ede7SEmmanuel Grumbach case 8:
1320bd6ede7SEmmanuel Grumbach expected_sz = sizeof(struct iwl_alive_ntf);
1330bd6ede7SEmmanuel Grumbach break;
1340bd6ede7SEmmanuel Grumbach default:
1350bd6ede7SEmmanuel Grumbach return false;
1360bd6ede7SEmmanuel Grumbach }
1370bd6ede7SEmmanuel Grumbach
1380bd6ede7SEmmanuel Grumbach if (pkt_len != expected_sz)
139d1e879ecSMiri Korenblit return false;
140d1e879ecSMiri Korenblit
141d1e879ecSMiri Korenblit palive = (void *)pkt->data;
142d1e879ecSMiri Korenblit
143d1e879ecSMiri Korenblit iwl_mld_alive_imr_data(trans, &palive->imr);
144d1e879ecSMiri Korenblit
145d1e879ecSMiri Korenblit umac = &palive->umac_data;
146d1e879ecSMiri Korenblit lmac1 = &palive->lmac_data[0];
147d1e879ecSMiri Korenblit lmac2 = &palive->lmac_data[1];
148d1e879ecSMiri Korenblit status = le16_to_cpu(palive->status);
149d1e879ecSMiri Korenblit
1503a68ae0fSJohannes Berg BUILD_BUG_ON(sizeof(alive_data->sku_id) !=
1513a68ae0fSJohannes Berg sizeof(palive->sku_id.data));
1523a68ae0fSJohannes Berg memcpy(alive_data->sku_id, palive->sku_id.data,
1533a68ae0fSJohannes Berg sizeof(palive->sku_id.data));
154d1e879ecSMiri Korenblit
155d1e879ecSMiri Korenblit IWL_DEBUG_FW(mld, "Got sku_id: 0x0%x 0x0%x 0x0%x\n",
1563a68ae0fSJohannes Berg le32_to_cpu(alive_data->sku_id[0]),
1573a68ae0fSJohannes Berg le32_to_cpu(alive_data->sku_id[1]),
1583a68ae0fSJohannes Berg le32_to_cpu(alive_data->sku_id[2]));
159d1e879ecSMiri Korenblit
160d1e879ecSMiri Korenblit lmac_error_event_table =
161d1e879ecSMiri Korenblit le32_to_cpu(lmac1->dbg_ptrs.error_event_table_ptr);
162d1e879ecSMiri Korenblit iwl_fw_lmac1_set_alive_err_table(trans, lmac_error_event_table);
163d1e879ecSMiri Korenblit
164d1e879ecSMiri Korenblit if (lmac2)
165d1e879ecSMiri Korenblit trans->dbg.lmac_error_event_table[1] =
166d1e879ecSMiri Korenblit le32_to_cpu(lmac2->dbg_ptrs.error_event_table_ptr);
167d1e879ecSMiri Korenblit
168d1e879ecSMiri Korenblit umac_error_table = le32_to_cpu(umac->dbg_ptrs.error_info_addr) &
169d1e879ecSMiri Korenblit ~FW_ADDR_CACHE_CONTROL;
170d1e879ecSMiri Korenblit
171857ecb85SJohannes Berg if (umac_error_table >= trans->mac_cfg->base->min_umac_error_event_table)
172d1e879ecSMiri Korenblit iwl_fw_umac_set_alive_err_table(trans, umac_error_table);
173d1e879ecSMiri Korenblit else
174d1e879ecSMiri Korenblit IWL_ERR(mld, "Not valid error log pointer 0x%08X\n",
175d1e879ecSMiri Korenblit umac_error_table);
176d1e879ecSMiri Korenblit
1773a68ae0fSJohannes Berg alive_data->valid = status == IWL_ALIVE_STATUS_OK;
178d1e879ecSMiri Korenblit
179d1e879ecSMiri Korenblit IWL_DEBUG_FW(mld,
180d1e879ecSMiri Korenblit "Alive ucode status 0x%04x revision 0x%01X 0x%01X\n",
181d1e879ecSMiri Korenblit status, lmac1->ver_type, lmac1->ver_subtype);
182d1e879ecSMiri Korenblit
183d1e879ecSMiri Korenblit if (lmac2)
184d1e879ecSMiri Korenblit IWL_DEBUG_FW(mld, "Alive ucode CDB\n");
185d1e879ecSMiri Korenblit
186d1e879ecSMiri Korenblit IWL_DEBUG_FW(mld,
187d1e879ecSMiri Korenblit "UMAC version: Major - 0x%x, Minor - 0x%x\n",
188d1e879ecSMiri Korenblit le32_to_cpu(umac->umac_major),
189d1e879ecSMiri Korenblit le32_to_cpu(umac->umac_minor));
190d1e879ecSMiri Korenblit
191d1e879ecSMiri Korenblit if (version >= 7)
192d1e879ecSMiri Korenblit IWL_DEBUG_FW(mld, "FW alive flags 0x%x\n",
193d1e879ecSMiri Korenblit le16_to_cpu(palive->flags));
194d1e879ecSMiri Korenblit
1950bd6ede7SEmmanuel Grumbach if (version >= 8)
1960bd6ede7SEmmanuel Grumbach IWL_DEBUG_FW(mld, "platform_id 0x%llx\n",
1970bd6ede7SEmmanuel Grumbach le64_to_cpu(palive->platform_id));
1980bd6ede7SEmmanuel Grumbach
199d1e879ecSMiri Korenblit iwl_fwrt_update_fw_versions(&mld->fwrt, lmac1, umac);
200d1e879ecSMiri Korenblit
201d1e879ecSMiri Korenblit return true;
202d1e879ecSMiri Korenblit }
203d1e879ecSMiri Korenblit
204d1e879ecSMiri Korenblit #define MLD_ALIVE_TIMEOUT (2 * HZ)
205d1e879ecSMiri Korenblit #define MLD_INIT_COMPLETE_TIMEOUT (2 * HZ)
206d1e879ecSMiri Korenblit
iwl_mld_print_alive_notif_timeout(struct iwl_mld * mld)207d1e879ecSMiri Korenblit static void iwl_mld_print_alive_notif_timeout(struct iwl_mld *mld)
208d1e879ecSMiri Korenblit {
209d1e879ecSMiri Korenblit struct iwl_trans *trans = mld->trans;
210d1e879ecSMiri Korenblit struct iwl_pc_data *pc_data;
211d1e879ecSMiri Korenblit u8 count;
212d1e879ecSMiri Korenblit
213d1e879ecSMiri Korenblit IWL_ERR(mld,
214d1e879ecSMiri Korenblit "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n",
215d1e879ecSMiri Korenblit iwl_read_umac_prph(trans, UMAG_SB_CPU_1_STATUS),
216d1e879ecSMiri Korenblit iwl_read_umac_prph(trans,
217d1e879ecSMiri Korenblit UMAG_SB_CPU_2_STATUS));
218d1e879ecSMiri Korenblit #define IWL_FW_PRINT_REG_INFO(reg_name) \
219d1e879ecSMiri Korenblit IWL_ERR(mld, #reg_name ": 0x%x\n", iwl_read_umac_prph(trans, reg_name))
220d1e879ecSMiri Korenblit
221d1e879ecSMiri Korenblit IWL_FW_PRINT_REG_INFO(WFPM_LMAC1_PD_NOTIFICATION);
222d1e879ecSMiri Korenblit
223d1e879ecSMiri Korenblit IWL_FW_PRINT_REG_INFO(HPM_SECONDARY_DEVICE_STATE);
224d1e879ecSMiri Korenblit
225d1e879ecSMiri Korenblit /* print OTP info */
226d1e879ecSMiri Korenblit IWL_FW_PRINT_REG_INFO(WFPM_MAC_OTP_CFG7_ADDR);
227d1e879ecSMiri Korenblit IWL_FW_PRINT_REG_INFO(WFPM_MAC_OTP_CFG7_DATA);
228d1e879ecSMiri Korenblit #undef IWL_FW_PRINT_REG_INFO
229d1e879ecSMiri Korenblit
230d1e879ecSMiri Korenblit pc_data = trans->dbg.pc_data;
231d1e879ecSMiri Korenblit for (count = 0; count < trans->dbg.num_pc; count++, pc_data++)
232d1e879ecSMiri Korenblit IWL_ERR(mld, "%s: 0x%x\n", pc_data->pc_name,
233d1e879ecSMiri Korenblit pc_data->pc_address);
234d1e879ecSMiri Korenblit }
235d1e879ecSMiri Korenblit
iwl_mld_load_fw_wait_alive(struct iwl_mld * mld,struct iwl_mld_alive_data * alive_data)2363a68ae0fSJohannes Berg static int iwl_mld_load_fw_wait_alive(struct iwl_mld *mld,
2373a68ae0fSJohannes Berg struct iwl_mld_alive_data *alive_data)
238d1e879ecSMiri Korenblit {
239d1e879ecSMiri Korenblit static const u16 alive_cmd[] = { UCODE_ALIVE_NTFY };
240d1e879ecSMiri Korenblit struct iwl_notification_wait alive_wait;
241d1e879ecSMiri Korenblit int ret;
242d1e879ecSMiri Korenblit
243d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy);
244d1e879ecSMiri Korenblit
245d1e879ecSMiri Korenblit iwl_init_notification_wait(&mld->notif_wait, &alive_wait,
246d1e879ecSMiri Korenblit alive_cmd, ARRAY_SIZE(alive_cmd),
2473a68ae0fSJohannes Berg iwl_alive_fn, alive_data);
248d1e879ecSMiri Korenblit
249d1e879ecSMiri Korenblit iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_EARLY, NULL);
250d1e879ecSMiri Korenblit
251a94d0189SJohannes Berg ret = iwl_trans_start_fw(mld->trans, mld->fw, IWL_UCODE_REGULAR, true);
252d1e879ecSMiri Korenblit if (ret) {
253d1e879ecSMiri Korenblit iwl_remove_notification(&mld->notif_wait, &alive_wait);
254d1e879ecSMiri Korenblit return ret;
255d1e879ecSMiri Korenblit }
256d1e879ecSMiri Korenblit
257d1e879ecSMiri Korenblit ret = iwl_wait_notification(&mld->notif_wait, &alive_wait,
258d1e879ecSMiri Korenblit MLD_ALIVE_TIMEOUT);
259d1e879ecSMiri Korenblit
260d1e879ecSMiri Korenblit if (ret) {
261d1e879ecSMiri Korenblit if (ret == -ETIMEDOUT)
262d1e879ecSMiri Korenblit iwl_fw_dbg_error_collect(&mld->fwrt,
263d1e879ecSMiri Korenblit FW_DBG_TRIGGER_ALIVE_TIMEOUT);
264d1e879ecSMiri Korenblit iwl_mld_print_alive_notif_timeout(mld);
265fab65a1aSMiri Korenblit return ret;
266d1e879ecSMiri Korenblit }
267d1e879ecSMiri Korenblit
2683a68ae0fSJohannes Berg if (!alive_data->valid) {
269d1e879ecSMiri Korenblit IWL_ERR(mld, "Loaded firmware is not valid!\n");
270fab65a1aSMiri Korenblit return -EIO;
271d1e879ecSMiri Korenblit }
272d1e879ecSMiri Korenblit
273d43c01d3SJohannes Berg iwl_trans_fw_alive(mld->trans);
274d1e879ecSMiri Korenblit
275d1e879ecSMiri Korenblit return 0;
276d1e879ecSMiri Korenblit }
277d1e879ecSMiri Korenblit
iwl_mld_run_fw_init_sequence(struct iwl_mld * mld)278892998c7SMiri Korenblit static int iwl_mld_run_fw_init_sequence(struct iwl_mld *mld)
279d1e879ecSMiri Korenblit {
280d1e879ecSMiri Korenblit struct iwl_notification_wait init_wait;
2811f263e63SMiri Korenblit struct iwl_init_extended_cfg_cmd init_cfg = {
2821f263e63SMiri Korenblit .init_flags = cpu_to_le32(BIT(IWL_INIT_PHY)),
2831f263e63SMiri Korenblit };
2843a68ae0fSJohannes Berg struct iwl_mld_alive_data alive_data = {};
285d1e879ecSMiri Korenblit static const u16 init_complete[] = {
286d1e879ecSMiri Korenblit INIT_COMPLETE_NOTIF,
287d1e879ecSMiri Korenblit };
288d1e879ecSMiri Korenblit int ret;
289d1e879ecSMiri Korenblit
290d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy);
291d1e879ecSMiri Korenblit
2923a68ae0fSJohannes Berg ret = iwl_mld_load_fw_wait_alive(mld, &alive_data);
293d1e879ecSMiri Korenblit if (ret)
294d1e879ecSMiri Korenblit return ret;
295d1e879ecSMiri Korenblit
296d1e879ecSMiri Korenblit ret = iwl_pnvm_load(mld->trans, &mld->notif_wait,
2973a68ae0fSJohannes Berg &mld->fw->ucode_capa, alive_data.sku_id);
298d1e879ecSMiri Korenblit if (ret) {
299d1e879ecSMiri Korenblit IWL_ERR(mld, "Timeout waiting for PNVM load %d\n", ret);
300fab65a1aSMiri Korenblit return ret;
301d1e879ecSMiri Korenblit }
302d1e879ecSMiri Korenblit
303d1e879ecSMiri Korenblit iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE,
304d1e879ecSMiri Korenblit NULL);
305d1e879ecSMiri Korenblit
306d1e879ecSMiri Korenblit iwl_init_notification_wait(&mld->notif_wait,
307d1e879ecSMiri Korenblit &init_wait,
308d1e879ecSMiri Korenblit init_complete,
309d1e879ecSMiri Korenblit ARRAY_SIZE(init_complete),
310d1e879ecSMiri Korenblit NULL, NULL);
311d1e879ecSMiri Korenblit
312d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd_pdu(mld,
313d1e879ecSMiri Korenblit WIDE_ID(SYSTEM_GROUP, INIT_EXTENDED_CFG_CMD),
314d1e879ecSMiri Korenblit &init_cfg);
315d1e879ecSMiri Korenblit if (ret) {
316d1e879ecSMiri Korenblit IWL_ERR(mld, "Failed to send init config command: %d\n", ret);
317d1e879ecSMiri Korenblit iwl_remove_notification(&mld->notif_wait, &init_wait);
318fab65a1aSMiri Korenblit return ret;
319d1e879ecSMiri Korenblit }
320d1e879ecSMiri Korenblit
3211f263e63SMiri Korenblit ret = iwl_mld_send_phy_cfg_cmd(mld);
3221f263e63SMiri Korenblit if (ret) {
3231f263e63SMiri Korenblit IWL_ERR(mld, "Failed to send PHY config command: %d\n", ret);
3241f263e63SMiri Korenblit iwl_remove_notification(&mld->notif_wait, &init_wait);
3251f263e63SMiri Korenblit return ret;
3261f263e63SMiri Korenblit }
3271f263e63SMiri Korenblit
328d1e879ecSMiri Korenblit ret = iwl_wait_notification(&mld->notif_wait, &init_wait,
329d1e879ecSMiri Korenblit MLD_INIT_COMPLETE_TIMEOUT);
330d1e879ecSMiri Korenblit if (ret) {
331d1e879ecSMiri Korenblit IWL_ERR(mld, "Failed to get INIT_COMPLETE %d\n", ret);
332fab65a1aSMiri Korenblit return ret;
333d1e879ecSMiri Korenblit }
334d1e879ecSMiri Korenblit
335d1e879ecSMiri Korenblit return 0;
336d1e879ecSMiri Korenblit }
337d1e879ecSMiri Korenblit
iwl_mld_load_fw(struct iwl_mld * mld)338d1e879ecSMiri Korenblit int iwl_mld_load_fw(struct iwl_mld *mld)
339d1e879ecSMiri Korenblit {
340d1e879ecSMiri Korenblit int ret;
341d1e879ecSMiri Korenblit
342d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy);
343d1e879ecSMiri Korenblit
344d1e879ecSMiri Korenblit ret = iwl_trans_start_hw(mld->trans);
345d1e879ecSMiri Korenblit if (ret)
346d1e879ecSMiri Korenblit return ret;
347d1e879ecSMiri Korenblit
348d1e879ecSMiri Korenblit ret = iwl_mld_run_fw_init_sequence(mld);
349d1e879ecSMiri Korenblit if (ret)
350fab65a1aSMiri Korenblit goto err;
351d1e879ecSMiri Korenblit
352d1e879ecSMiri Korenblit mld->fw_status.running = true;
353d1e879ecSMiri Korenblit
354d1e879ecSMiri Korenblit return 0;
355fab65a1aSMiri Korenblit err:
356e1d35eabSMiri Korenblit iwl_mld_stop_fw(mld);
357fab65a1aSMiri Korenblit return ret;
358d1e879ecSMiri Korenblit }
359d1e879ecSMiri Korenblit
iwl_mld_stop_fw(struct iwl_mld * mld)360d1e879ecSMiri Korenblit void iwl_mld_stop_fw(struct iwl_mld *mld)
361d1e879ecSMiri Korenblit {
362d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy);
363d1e879ecSMiri Korenblit
364d1e879ecSMiri Korenblit iwl_abort_notification_waits(&mld->notif_wait);
365d1e879ecSMiri Korenblit
366d1e879ecSMiri Korenblit iwl_fw_dbg_stop_sync(&mld->fwrt);
367d1e879ecSMiri Korenblit
368d1e879ecSMiri Korenblit iwl_trans_stop_device(mld->trans);
369d1e879ecSMiri Korenblit
370b68df31cSMiri Korenblit /* HW is stopped, no more coming RX. Cancel all notifications in
371b68df31cSMiri Korenblit * case they were sent just before stopping the HW.
372b68df31cSMiri Korenblit */
373b68df31cSMiri Korenblit iwl_mld_cancel_async_notifications(mld);
374b68df31cSMiri Korenblit
375d1e879ecSMiri Korenblit mld->fw_status.running = false;
376d1e879ecSMiri Korenblit }
377d1e879ecSMiri Korenblit
iwl_mld_restart_disconnect_iter(void * data,u8 * mac,struct ieee80211_vif * vif)378d1e879ecSMiri Korenblit static void iwl_mld_restart_disconnect_iter(void *data, u8 *mac,
379d1e879ecSMiri Korenblit struct ieee80211_vif *vif)
380d1e879ecSMiri Korenblit {
381d1e879ecSMiri Korenblit if (vif->type == NL80211_IFTYPE_STATION)
382d1e879ecSMiri Korenblit ieee80211_hw_restart_disconnect(vif);
383d1e879ecSMiri Korenblit }
384d1e879ecSMiri Korenblit
iwl_mld_send_recovery_cmd(struct iwl_mld * mld,u32 flags)385d1e879ecSMiri Korenblit void iwl_mld_send_recovery_cmd(struct iwl_mld *mld, u32 flags)
386d1e879ecSMiri Korenblit {
387d1e879ecSMiri Korenblit u32 error_log_size = mld->fw->ucode_capa.error_log_size;
388d1e879ecSMiri Korenblit struct iwl_fw_error_recovery_cmd recovery_cmd = {
389d1e879ecSMiri Korenblit .flags = cpu_to_le32(flags),
390d1e879ecSMiri Korenblit };
391d1e879ecSMiri Korenblit struct iwl_host_cmd cmd = {
392d1e879ecSMiri Korenblit .id = WIDE_ID(SYSTEM_GROUP, FW_ERROR_RECOVERY_CMD),
393d1e879ecSMiri Korenblit .flags = CMD_WANT_SKB,
394d1e879ecSMiri Korenblit .data = {&recovery_cmd, },
395d1e879ecSMiri Korenblit .len = {sizeof(recovery_cmd), },
396d1e879ecSMiri Korenblit };
397d1e879ecSMiri Korenblit int ret;
398d1e879ecSMiri Korenblit
399d1e879ecSMiri Korenblit /* no error log was defined in TLV */
400d1e879ecSMiri Korenblit if (!error_log_size)
401d1e879ecSMiri Korenblit return;
402d1e879ecSMiri Korenblit
403d1e879ecSMiri Korenblit if (flags & ERROR_RECOVERY_UPDATE_DB) {
404d1e879ecSMiri Korenblit /* no buf was allocated upon NIC error */
405d1e879ecSMiri Korenblit if (!mld->error_recovery_buf)
406d1e879ecSMiri Korenblit return;
407d1e879ecSMiri Korenblit
408d1e879ecSMiri Korenblit cmd.data[1] = mld->error_recovery_buf;
409d1e879ecSMiri Korenblit cmd.len[1] = error_log_size;
410d1e879ecSMiri Korenblit cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
411d1e879ecSMiri Korenblit recovery_cmd.buf_size = cpu_to_le32(error_log_size);
412d1e879ecSMiri Korenblit }
413d1e879ecSMiri Korenblit
414d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd(mld, &cmd);
415d1e879ecSMiri Korenblit
416d1e879ecSMiri Korenblit /* we no longer need the recovery buffer */
417d1e879ecSMiri Korenblit kfree(mld->error_recovery_buf);
418d1e879ecSMiri Korenblit mld->error_recovery_buf = NULL;
419d1e879ecSMiri Korenblit
420d1e879ecSMiri Korenblit if (ret) {
421d1e879ecSMiri Korenblit IWL_ERR(mld, "Failed to send recovery cmd %d\n", ret);
422d1e879ecSMiri Korenblit return;
423d1e879ecSMiri Korenblit }
424d1e879ecSMiri Korenblit
425d1e879ecSMiri Korenblit if (flags & ERROR_RECOVERY_UPDATE_DB) {
426d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt = cmd.resp_pkt;
427d1e879ecSMiri Korenblit u32 pkt_len = iwl_rx_packet_payload_len(pkt);
428d1e879ecSMiri Korenblit u32 resp;
429d1e879ecSMiri Korenblit
430d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, pkt_len != sizeof(resp),
431d1e879ecSMiri Korenblit "Unexpected recovery cmd response size %u (expected %zu)\n",
432d1e879ecSMiri Korenblit pkt_len, sizeof(resp)))
433d1e879ecSMiri Korenblit goto out;
434d1e879ecSMiri Korenblit
435d1e879ecSMiri Korenblit resp = le32_to_cpup((__le32 *)cmd.resp_pkt->data);
436d1e879ecSMiri Korenblit if (!resp)
437d1e879ecSMiri Korenblit goto out;
438d1e879ecSMiri Korenblit
439d1e879ecSMiri Korenblit IWL_ERR(mld,
440d1e879ecSMiri Korenblit "Failed to send recovery cmd blob was invalid %d\n",
441d1e879ecSMiri Korenblit resp);
442d1e879ecSMiri Korenblit
443d1e879ecSMiri Korenblit ieee80211_iterate_interfaces(mld->hw, 0,
444d1e879ecSMiri Korenblit iwl_mld_restart_disconnect_iter,
445d1e879ecSMiri Korenblit NULL);
446d1e879ecSMiri Korenblit }
447d1e879ecSMiri Korenblit
448d1e879ecSMiri Korenblit out:
449d1e879ecSMiri Korenblit iwl_free_resp(&cmd);
450d1e879ecSMiri Korenblit }
451d1e879ecSMiri Korenblit
iwl_mld_config_fw(struct iwl_mld * mld)452d1e879ecSMiri Korenblit static int iwl_mld_config_fw(struct iwl_mld *mld)
453d1e879ecSMiri Korenblit {
454d1e879ecSMiri Korenblit int ret;
455d1e879ecSMiri Korenblit
456d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy);
457d1e879ecSMiri Korenblit
458d1e879ecSMiri Korenblit iwl_fw_disable_dbg_asserts(&mld->fwrt);
459d1e879ecSMiri Korenblit iwl_get_shared_mem_conf(&mld->fwrt);
460d1e879ecSMiri Korenblit
461d1e879ecSMiri Korenblit ret = iwl_mld_send_tx_ant_cfg(mld);
462d1e879ecSMiri Korenblit if (ret)
463d1e879ecSMiri Korenblit return ret;
464d1e879ecSMiri Korenblit
465d1e879ecSMiri Korenblit ret = iwl_mld_send_bt_init_conf(mld);
466d1e879ecSMiri Korenblit if (ret)
467d1e879ecSMiri Korenblit return ret;
468d1e879ecSMiri Korenblit
469d1e879ecSMiri Korenblit ret = iwl_set_soc_latency(&mld->fwrt);
470d1e879ecSMiri Korenblit if (ret)
471d1e879ecSMiri Korenblit return ret;
472d1e879ecSMiri Korenblit
473d1e879ecSMiri Korenblit iwl_mld_configure_lari(mld);
474d1e879ecSMiri Korenblit
475d1e879ecSMiri Korenblit ret = iwl_mld_config_temp_report_ths(mld);
476d1e879ecSMiri Korenblit if (ret)
477d1e879ecSMiri Korenblit return ret;
478d1e879ecSMiri Korenblit
479d1e879ecSMiri Korenblit #ifdef CONFIG_THERMAL
480d1e879ecSMiri Korenblit ret = iwl_mld_config_ctdp(mld, mld->cooling_dev.cur_state,
481d1e879ecSMiri Korenblit CTDP_CMD_OPERATION_START);
482d1e879ecSMiri Korenblit if (ret)
483d1e879ecSMiri Korenblit return ret;
484d1e879ecSMiri Korenblit #endif
485d1e879ecSMiri Korenblit
486d1e879ecSMiri Korenblit ret = iwl_configure_rxq(&mld->fwrt);
487d1e879ecSMiri Korenblit if (ret)
488d1e879ecSMiri Korenblit return ret;
489d1e879ecSMiri Korenblit
490d1e879ecSMiri Korenblit ret = iwl_mld_send_rss_cfg_cmd(mld);
491d1e879ecSMiri Korenblit if (ret)
492d1e879ecSMiri Korenblit return ret;
493d1e879ecSMiri Korenblit
494d1e879ecSMiri Korenblit ret = iwl_mld_config_scan(mld);
495d1e879ecSMiri Korenblit if (ret)
496d1e879ecSMiri Korenblit return ret;
497d1e879ecSMiri Korenblit
498d1e879ecSMiri Korenblit ret = iwl_mld_update_device_power(mld, false);
499d1e879ecSMiri Korenblit if (ret)
500d1e879ecSMiri Korenblit return ret;
501d1e879ecSMiri Korenblit
502d1e879ecSMiri Korenblit if (mld->fw_status.in_hw_restart) {
503d1e879ecSMiri Korenblit iwl_mld_send_recovery_cmd(mld, ERROR_RECOVERY_UPDATE_DB);
504d1e879ecSMiri Korenblit iwl_mld_time_sync_fw_config(mld);
505d1e879ecSMiri Korenblit }
506d1e879ecSMiri Korenblit
507d1e879ecSMiri Korenblit iwl_mld_led_config_fw(mld);
508d1e879ecSMiri Korenblit
509d1e879ecSMiri Korenblit ret = iwl_mld_init_ppag(mld);
510d1e879ecSMiri Korenblit if (ret)
511d1e879ecSMiri Korenblit return ret;
512d1e879ecSMiri Korenblit
513d1e879ecSMiri Korenblit ret = iwl_mld_init_sar(mld);
514d1e879ecSMiri Korenblit if (ret)
515d1e879ecSMiri Korenblit return ret;
516d1e879ecSMiri Korenblit
517d1e879ecSMiri Korenblit ret = iwl_mld_init_sgom(mld);
518d1e879ecSMiri Korenblit if (ret)
519d1e879ecSMiri Korenblit return ret;
520d1e879ecSMiri Korenblit
521d1e879ecSMiri Korenblit iwl_mld_init_tas(mld);
522d1e879ecSMiri Korenblit iwl_mld_init_uats(mld);
523d1e879ecSMiri Korenblit
524d1e879ecSMiri Korenblit return 0;
525d1e879ecSMiri Korenblit }
526d1e879ecSMiri Korenblit
iwl_mld_start_fw(struct iwl_mld * mld)527d1e879ecSMiri Korenblit int iwl_mld_start_fw(struct iwl_mld *mld)
528d1e879ecSMiri Korenblit {
529d1e879ecSMiri Korenblit int ret;
530d1e879ecSMiri Korenblit
531d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy);
532d1e879ecSMiri Korenblit
533d1e879ecSMiri Korenblit ret = iwl_mld_load_fw(mld);
534d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, ret, "Failed to start firmware %d\n", ret)) {
535d1e879ecSMiri Korenblit iwl_fw_dbg_error_collect(&mld->fwrt, FW_DBG_TRIGGER_DRIVER);
536e1d35eabSMiri Korenblit return ret;
537d1e879ecSMiri Korenblit }
538d1e879ecSMiri Korenblit
539d1e879ecSMiri Korenblit IWL_DEBUG_INFO(mld, "uCode started.\n");
540d1e879ecSMiri Korenblit
541d1e879ecSMiri Korenblit ret = iwl_mld_config_fw(mld);
542d1e879ecSMiri Korenblit if (ret)
543d1e879ecSMiri Korenblit goto error;
544d1e879ecSMiri Korenblit
545*f81aa834SIlan Peer ret = iwl_mld_init_mcc(mld);
546*f81aa834SIlan Peer if (ret)
547*f81aa834SIlan Peer goto error;
548*f81aa834SIlan Peer
549d1e879ecSMiri Korenblit return 0;
550d1e879ecSMiri Korenblit
551d1e879ecSMiri Korenblit error:
552d1e879ecSMiri Korenblit iwl_mld_stop_fw(mld);
553d1e879ecSMiri Korenblit return ret;
554d1e879ecSMiri Korenblit }
555