Loading drivers/net/wireless/ath/wil6210/boot_loader.h +8 −1 Original line number Diff line number Diff line /* Copyright (c) 2015 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above Loading Loading @@ -39,7 +40,8 @@ struct bl_dedicated_registers_v1 { /* valid only for version 2 and above */ __le32 bl_assert_code; /* 0x880A58 BL Assert code */ __le32 bl_assert_blink; /* 0x880A5C BL Assert Branch */ __le32 bl_reserved[22]; /* 0x880A60 - 0x880AB4 */ __le32 bl_shutdown_handshake; /* 0x880A60 BL cleaner shutdown */ __le32 bl_reserved[21]; /* 0x880A64 - 0x880AB4 */ __le32 bl_magic_number; /* 0x880AB8 BL Magic number */ } __packed; Loading @@ -58,4 +60,9 @@ struct bl_dedicated_registers_v0 { u8 mac_address[6]; /* 0x880A4c BL mac address */ } __packed; /* bits for bl_shutdown_handshake */ #define BL_SHUTDOWN_HS_GRTD BIT(0) #define BL_SHUTDOWN_HS_RTD BIT(1) #define BL_SHUTDOWN_HS_PROT_VER(x) WIL_GET_BITS(x, 28, 31) #endif /* BOOT_LOADER_EXPORT_H_ */ drivers/net/wireless/ath/wil6210/fw.h +17 −1 Original line number Diff line number Diff line /* * Copyright (c) 2014,2016 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above Loading Loading @@ -58,15 +59,30 @@ struct wil_fw_record_comment { /* type == wil_fw_type_comment */ u8 data[0]; /* free-form data [data_size], see above */ } __packed; /* Comment header - common for all comment record types */ struct wil_fw_record_comment_hdr { __le32 magic; }; /* FW capabilities encoded inside a comment record */ #define WIL_FW_CAPABILITIES_MAGIC (0xabcddcba) struct wil_fw_record_capabilities { /* type == wil_fw_type_comment */ /* identifies capabilities record */ __le32 magic; struct wil_fw_record_comment_hdr hdr; /* capabilities (variable size), see enum wmi_fw_capability */ u8 capabilities[0]; }; /* brd file info encoded inside a comment record */ #define WIL_BRD_FILE_MAGIC (0xabcddcbb) struct wil_fw_record_brd_file { /* type == wil_fw_type_comment */ /* identifies brd file record */ struct wil_fw_record_comment_hdr hdr; __le32 version; __le32 base_addr; __le32 max_size_bytes; } __packed; /* perform action * data_size = @head.size - offsetof(struct wil_fw_record_action, data) */ Loading drivers/net/wireless/ath/wil6210/fw_inc.c +158 −9 Original line number Diff line number Diff line /* * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above Loading Loading @@ -128,14 +129,13 @@ static int fw_ignore_section(struct wil6210_priv *wil, const void *data, } static int fw_handle_comment(struct wil6210_priv *wil, const void *data, fw_handle_capabilities(struct wil6210_priv *wil, const void *data, size_t size) { const struct wil_fw_record_capabilities *rec = data; size_t capa_size; if (size < sizeof(*rec) || le32_to_cpu(rec->magic) != WIL_FW_CAPABILITIES_MAGIC) { if (size < sizeof(*rec)) { wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, data, size, true); return 0; Loading @@ -151,8 +151,56 @@ fw_handle_comment(struct wil6210_priv *wil, const void *data, return 0; } static int fw_handle_data(struct wil6210_priv *wil, const void *data, static int fw_handle_brd_file(struct wil6210_priv *wil, const void *data, size_t size) { const struct wil_fw_record_brd_file *rec = data; if (size < sizeof(*rec)) { wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, data, size, true); return 0; } wil->brd_file_addr = le32_to_cpu(rec->base_addr); wil->brd_file_max_size = le32_to_cpu(rec->max_size_bytes); wil_dbg_fw(wil, "brd_file_addr 0x%x, brd_file_max_size %d\n", wil->brd_file_addr, wil->brd_file_max_size); return 0; } static int fw_handle_comment(struct wil6210_priv *wil, const void *data, size_t size) { const struct wil_fw_record_comment_hdr *hdr = data; u32 magic; int rc = 0; if (size < sizeof(*hdr)) return 0; magic = le32_to_cpu(hdr->magic); switch (magic) { case WIL_FW_CAPABILITIES_MAGIC: wil_dbg_fw(wil, "magic is WIL_FW_CAPABILITIES_MAGIC\n"); rc = fw_handle_capabilities(wil, data, size); break; case WIL_BRD_FILE_MAGIC: wil_dbg_fw(wil, "magic is WIL_BRD_FILE_MAGIC\n"); rc = fw_handle_brd_file(wil, data, size); break; } return rc; } static int __fw_handle_data(struct wil6210_priv *wil, const void *data, size_t size, __le32 addr) { const struct wil_fw_record_data *d = data; void __iomem *dst; Loading @@ -163,16 +211,23 @@ static int fw_handle_data(struct wil6210_priv *wil, const void *data, return -EINVAL; } if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address")) if (!wil_fw_addr_check(wil, &dst, addr, s, "address")) return -EINVAL; wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(d->addr), s); wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(addr), s); wil_memcpy_toio_32(dst, d->data, s); wmb(); /* finish before processing next record */ return 0; } static int fw_handle_data(struct wil6210_priv *wil, const void *data, size_t size) { const struct wil_fw_record_data *d = data; return __fw_handle_data(wil, data, size, d->addr); } static int fw_handle_fill(struct wil6210_priv *wil, const void *data, size_t size) { Loading Loading @@ -551,6 +606,100 @@ int wil_request_firmware(struct wil6210_priv *wil, const char *name, return rc; } /** * wil_brd_process - process section from BRD file * * Return error code */ static int wil_brd_process(struct wil6210_priv *wil, const void *data, size_t size) { int rc = 0; const struct wil_fw_record_head *hdr = data; size_t s, hdr_sz; u16 type; /* Assuming the board file includes only one header record and one data * record. Each record starts with wil_fw_record_head. */ if (size < sizeof(*hdr)) return -EINVAL; s = sizeof(*hdr) + le32_to_cpu(hdr->size); if (s > size) return -EINVAL; /* Skip the header record and handle the data record */ hdr = (const void *)hdr + s; size -= s; if (size < sizeof(*hdr)) return -EINVAL; hdr_sz = le32_to_cpu(hdr->size); if (wil->brd_file_max_size && hdr_sz > wil->brd_file_max_size) return -EINVAL; if (sizeof(*hdr) + hdr_sz > size) return -EINVAL; if (hdr_sz % 4) { wil_err_fw(wil, "unaligned record size: %zu\n", hdr_sz); return -EINVAL; } type = le16_to_cpu(hdr->type); if (type != wil_fw_type_data) { wil_err_fw(wil, "invalid record type for board file: %d\n", type); return -EINVAL; } if (hdr_sz < sizeof(struct wil_fw_record_data)) { wil_err_fw(wil, "data record too short: %zu\n", hdr_sz); return -EINVAL; } wil_dbg_fw(wil, "using addr from fw file: [0x%08x]\n", wil->brd_file_addr); rc = __fw_handle_data(wil, &hdr[1], hdr_sz, cpu_to_le32(wil->brd_file_addr)); return rc; } /** * wil_request_board - Request board file * * Request board image from the file * board file address and max size are read from FW file * during initialization. * brd file shall include one header and one data section. * * Return error code */ int wil_request_board(struct wil6210_priv *wil, const char *name) { int rc, dlen; const struct firmware *brd; rc = request_firmware(&brd, name, wil_to_dev(wil)); if (rc) { wil_err_fw(wil, "Failed to load brd %s\n", name); return rc; } wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, brd->size); /* Verify the header */ dlen = wil_fw_verify(wil, brd->data, brd->size); if (dlen < 0) { rc = dlen; goto out; } /* Process the data record */ rc = wil_brd_process(wil, brd->data, dlen); out: release_firmware(brd); return rc; } /** * wil_fw_verify_file_exists - checks if firmware file exist * Loading drivers/net/wireless/ath/wil6210/interrupt.c +4 −2 Original line number Diff line number Diff line /* * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above Loading Loading @@ -395,8 +396,9 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie) wil6210_mask_irq_misc(wil, false); if (isr & ISR_MISC_FW_ERROR) { u32 fw_assert_code = wil_r(wil, RGF_FW_ASSERT_CODE); u32 ucode_assert_code = wil_r(wil, RGF_UCODE_ASSERT_CODE); u32 fw_assert_code = wil_r(wil, wil->rgf_fw_assert_code_addr); u32 ucode_assert_code = wil_r(wil, wil->rgf_ucode_assert_code_addr); wil_err(wil, "Firmware error detected, assert codes FW 0x%08x, UCODE 0x%08x\n", Loading drivers/net/wireless/ath/wil6210/main.c +191 −26 Original line number Diff line number Diff line /* * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above Loading Loading @@ -642,6 +643,98 @@ void wil_priv_deinit(struct wil6210_priv *wil) destroy_workqueue(wil->wmi_wq); } static void wil_shutdown_bl(struct wil6210_priv *wil) { u32 val; wil_s(wil, RGF_USER_BL + offsetof(struct bl_dedicated_registers_v1, bl_shutdown_handshake), BL_SHUTDOWN_HS_GRTD); usleep_range(100, 150); val = wil_r(wil, RGF_USER_BL + offsetof(struct bl_dedicated_registers_v1, bl_shutdown_handshake)); if (val & BL_SHUTDOWN_HS_RTD) { wil_dbg_misc(wil, "BL is ready for halt\n"); return; } wil_err(wil, "BL did not report ready for halt\n"); } /* this format is used by ARC embedded CPU for instruction memory */ static inline u32 ARC_me_imm32(u32 d) { return ((d & 0xffff0000) >> 16) | ((d & 0x0000ffff) << 16); } /* defines access to interrupt vectors for wil_freeze_bl */ #define ARC_IRQ_VECTOR_OFFSET(N) ((N) * 8) /* ARC long jump instruction */ #define ARC_JAL_INST (0x20200f80) static void wil_freeze_bl(struct wil6210_priv *wil) { u32 jal, upc, saved; u32 ivt3 = ARC_IRQ_VECTOR_OFFSET(3); jal = wil_r(wil, wil->iccm_base + ivt3); if (jal != ARC_me_imm32(ARC_JAL_INST)) { wil_dbg_misc(wil, "invalid IVT entry found, skipping\n"); return; } /* prevent the target from entering deep sleep * and disabling memory access */ saved = wil_r(wil, RGF_USER_USAGE_8); wil_w(wil, RGF_USER_USAGE_8, saved | BIT_USER_PREVENT_DEEP_SLEEP); usleep_range(20, 25); /* let the BL process the bit */ /* redirect to endless loop in the INT_L1 context and let it trap */ wil_w(wil, wil->iccm_base + ivt3 + 4, ARC_me_imm32(ivt3)); usleep_range(20, 25); /* let the BL get into the trap */ /* verify the BL is frozen */ upc = wil_r(wil, RGF_USER_CPU_PC); if (upc < ivt3 || (upc > (ivt3 + 8))) wil_dbg_misc(wil, "BL freeze failed, PC=0x%08X\n", upc); wil_w(wil, RGF_USER_USAGE_8, saved); } static void wil_bl_prepare_halt(struct wil6210_priv *wil) { u32 tmp, ver; /* before halting device CPU driver must make sure BL is not accessing * host memory. This is done differently depending on BL version: * 1. For very old BL versions the procedure is skipped * (not supported). * 2. For old BL version we use a special trick to freeze the BL * 3. For new BL versions we shutdown the BL using handshake procedure. */ tmp = wil_r(wil, RGF_USER_BL + offsetof(struct bl_dedicated_registers_v0, boot_loader_struct_version)); if (!tmp) { wil_dbg_misc(wil, "old BL, skipping halt preperation\n"); return; } tmp = wil_r(wil, RGF_USER_BL + offsetof(struct bl_dedicated_registers_v1, bl_shutdown_handshake)); ver = BL_SHUTDOWN_HS_PROT_VER(tmp); if (ver > 0) wil_shutdown_bl(wil); else wil_freeze_bl(wil); } static inline void wil_halt_cpu(struct wil6210_priv *wil) { wil_w(wil, RGF_USER_USER_CPU_0, BIT_USER_USER_CPU_MAN_RST); Loading Loading @@ -675,7 +768,7 @@ static void wil_set_oob_mode(struct wil6210_priv *wil, u8 mode) } } static int wil_target_reset(struct wil6210_priv *wil) static int wil_target_reset(struct wil6210_priv *wil, int no_flash) { int delay = 0; u32 x, x1 = 0; Loading @@ -689,9 +782,16 @@ static int wil_target_reset(struct wil6210_priv *wil) wil_halt_cpu(wil); if (!no_flash) { /* clear all boot loader "ready" bits */ wil_w(wil, RGF_USER_BL + offsetof(struct bl_dedicated_registers_v0, boot_loader_ready), 0); offsetof(struct bl_dedicated_registers_v0, boot_loader_ready), 0); /* this should be safe to write even with old BLs */ wil_w(wil, RGF_USER_BL + offsetof(struct bl_dedicated_registers_v1, bl_shutdown_handshake), 0); } /* Clear Fw Download notification */ wil_c(wil, RGF_USER_USAGE_6, BIT(0)); Loading Loading @@ -732,13 +832,25 @@ static int wil_target_reset(struct wil6210_priv *wil) wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); /* wait until device ready. typical time is 20..80 msec */ if (no_flash) do { msleep(RST_DELAY); x = wil_r(wil, USER_EXT_USER_PMU_3); if (delay++ > RST_COUNT) { wil_err(wil, "Reset not completed, PMU_3 0x%08x\n", x); return -ETIME; } } while ((x & BIT_PMU_DEVICE_RDY) == 0); else do { msleep(RST_DELAY); x = wil_r(wil, RGF_USER_BL + offsetof(struct bl_dedicated_registers_v0, boot_loader_ready)); if (x1 != x) { wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n", x1, x); wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n", x1, x); x1 = x; } if (delay++ > RST_COUNT) { Loading @@ -754,6 +866,21 @@ static int wil_target_reset(struct wil6210_priv *wil) wil_s(wil, RGF_DMA_OFUL_NID_0, BIT_DMA_OFUL_NID_0_RX_EXT_TR_EN | BIT_DMA_OFUL_NID_0_RX_EXT_A3_SRC); if (no_flash) { /* Reset OTP HW vectors to fit 40MHz */ wil_w(wil, RGF_USER_XPM_IFC_RD_TIME1, 0x60001); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME2, 0x20027); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME3, 0x1); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME4, 0x20027); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME5, 0x30003); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME6, 0x20002); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME7, 0x60001); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME8, 0x60001); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME9, 0x60001); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME10, 0x60001); wil_w(wil, RGF_USER_XPM_RD_DOUT_SAMPLE_TIME, 0x57); } wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY); return 0; } Loading Loading @@ -911,6 +1038,27 @@ static void wil_bl_crash_info(struct wil6210_priv *wil, bool is_err) } } static int wil_get_otp_info(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); struct wiphy *wiphy = wil_to_wiphy(wil); u8 mac[8]; wil_memcpy_fromio_32(mac, wil->csr + HOSTADDR(RGF_OTP_MAC), sizeof(mac)); if (!is_valid_ether_addr(mac)) { wil_err(wil, "Invalid MAC %pM\n", mac); return -EINVAL; } ether_addr_copy(ndev->perm_addr, mac); ether_addr_copy(wiphy->perm_addr, mac); if (!is_valid_ether_addr(ndev->dev_addr)) ether_addr_copy(ndev->dev_addr, mac); return 0; } static int wil_wait_for_fw_ready(struct wil6210_priv *wil) { ulong to = msecs_to_jiffies(1000); Loading Loading @@ -1004,6 +1152,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) { int rc; unsigned long status_flags = BIT(wil_status_resetting); int no_flash; wil_dbg_misc(wil, "reset\n"); Loading Loading @@ -1082,20 +1231,28 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) flush_workqueue(wil->wq_service); flush_workqueue(wil->wmi_wq); no_flash = test_bit(hw_capa_no_flash, wil->hw_capa); if (!no_flash) wil_bl_crash_info(wil, false); wil_disable_irq(wil); rc = wil_target_reset(wil); rc = wil_target_reset(wil, no_flash); wil6210_clear_irq(wil); wil_enable_irq(wil); wil_rx_fini(wil); if (rc) { if (!no_flash) wil_bl_crash_info(wil, true); goto out; } if (no_flash) { rc = wil_get_otp_info(wil); } else { rc = wil_get_bl_info(wil); if (rc == -EAGAIN && !load_fw) /* ignore RF error if not going up */ if (rc == -EAGAIN && !load_fw) /* ignore RF error if not going up */ rc = 0; } if (rc) goto out; Loading @@ -1104,13 +1261,21 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) wil_info(wil, "Use firmware <%s> + board <%s>\n", wil->wil_fw_name, WIL_BOARD_FILE_NAME); if (!no_flash) wil_bl_prepare_halt(wil); wil_halt_cpu(wil); memset(wil->fw_version, 0, sizeof(wil->fw_version)); /* Loading f/w from the file */ rc = wil_request_firmware(wil, wil->wil_fw_name, true); if (rc) goto out; rc = wil_request_firmware(wil, WIL_BOARD_FILE_NAME, true); if (wil->brd_file_addr) rc = wil_request_board(wil, WIL_BOARD_FILE_NAME); else rc = wil_request_firmware(wil, WIL_BOARD_FILE_NAME, true); if (rc) goto out; Loading Loading
drivers/net/wireless/ath/wil6210/boot_loader.h +8 −1 Original line number Diff line number Diff line /* Copyright (c) 2015 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above Loading Loading @@ -39,7 +40,8 @@ struct bl_dedicated_registers_v1 { /* valid only for version 2 and above */ __le32 bl_assert_code; /* 0x880A58 BL Assert code */ __le32 bl_assert_blink; /* 0x880A5C BL Assert Branch */ __le32 bl_reserved[22]; /* 0x880A60 - 0x880AB4 */ __le32 bl_shutdown_handshake; /* 0x880A60 BL cleaner shutdown */ __le32 bl_reserved[21]; /* 0x880A64 - 0x880AB4 */ __le32 bl_magic_number; /* 0x880AB8 BL Magic number */ } __packed; Loading @@ -58,4 +60,9 @@ struct bl_dedicated_registers_v0 { u8 mac_address[6]; /* 0x880A4c BL mac address */ } __packed; /* bits for bl_shutdown_handshake */ #define BL_SHUTDOWN_HS_GRTD BIT(0) #define BL_SHUTDOWN_HS_RTD BIT(1) #define BL_SHUTDOWN_HS_PROT_VER(x) WIL_GET_BITS(x, 28, 31) #endif /* BOOT_LOADER_EXPORT_H_ */
drivers/net/wireless/ath/wil6210/fw.h +17 −1 Original line number Diff line number Diff line /* * Copyright (c) 2014,2016 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above Loading Loading @@ -58,15 +59,30 @@ struct wil_fw_record_comment { /* type == wil_fw_type_comment */ u8 data[0]; /* free-form data [data_size], see above */ } __packed; /* Comment header - common for all comment record types */ struct wil_fw_record_comment_hdr { __le32 magic; }; /* FW capabilities encoded inside a comment record */ #define WIL_FW_CAPABILITIES_MAGIC (0xabcddcba) struct wil_fw_record_capabilities { /* type == wil_fw_type_comment */ /* identifies capabilities record */ __le32 magic; struct wil_fw_record_comment_hdr hdr; /* capabilities (variable size), see enum wmi_fw_capability */ u8 capabilities[0]; }; /* brd file info encoded inside a comment record */ #define WIL_BRD_FILE_MAGIC (0xabcddcbb) struct wil_fw_record_brd_file { /* type == wil_fw_type_comment */ /* identifies brd file record */ struct wil_fw_record_comment_hdr hdr; __le32 version; __le32 base_addr; __le32 max_size_bytes; } __packed; /* perform action * data_size = @head.size - offsetof(struct wil_fw_record_action, data) */ Loading
drivers/net/wireless/ath/wil6210/fw_inc.c +158 −9 Original line number Diff line number Diff line /* * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above Loading Loading @@ -128,14 +129,13 @@ static int fw_ignore_section(struct wil6210_priv *wil, const void *data, } static int fw_handle_comment(struct wil6210_priv *wil, const void *data, fw_handle_capabilities(struct wil6210_priv *wil, const void *data, size_t size) { const struct wil_fw_record_capabilities *rec = data; size_t capa_size; if (size < sizeof(*rec) || le32_to_cpu(rec->magic) != WIL_FW_CAPABILITIES_MAGIC) { if (size < sizeof(*rec)) { wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, data, size, true); return 0; Loading @@ -151,8 +151,56 @@ fw_handle_comment(struct wil6210_priv *wil, const void *data, return 0; } static int fw_handle_data(struct wil6210_priv *wil, const void *data, static int fw_handle_brd_file(struct wil6210_priv *wil, const void *data, size_t size) { const struct wil_fw_record_brd_file *rec = data; if (size < sizeof(*rec)) { wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, data, size, true); return 0; } wil->brd_file_addr = le32_to_cpu(rec->base_addr); wil->brd_file_max_size = le32_to_cpu(rec->max_size_bytes); wil_dbg_fw(wil, "brd_file_addr 0x%x, brd_file_max_size %d\n", wil->brd_file_addr, wil->brd_file_max_size); return 0; } static int fw_handle_comment(struct wil6210_priv *wil, const void *data, size_t size) { const struct wil_fw_record_comment_hdr *hdr = data; u32 magic; int rc = 0; if (size < sizeof(*hdr)) return 0; magic = le32_to_cpu(hdr->magic); switch (magic) { case WIL_FW_CAPABILITIES_MAGIC: wil_dbg_fw(wil, "magic is WIL_FW_CAPABILITIES_MAGIC\n"); rc = fw_handle_capabilities(wil, data, size); break; case WIL_BRD_FILE_MAGIC: wil_dbg_fw(wil, "magic is WIL_BRD_FILE_MAGIC\n"); rc = fw_handle_brd_file(wil, data, size); break; } return rc; } static int __fw_handle_data(struct wil6210_priv *wil, const void *data, size_t size, __le32 addr) { const struct wil_fw_record_data *d = data; void __iomem *dst; Loading @@ -163,16 +211,23 @@ static int fw_handle_data(struct wil6210_priv *wil, const void *data, return -EINVAL; } if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address")) if (!wil_fw_addr_check(wil, &dst, addr, s, "address")) return -EINVAL; wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(d->addr), s); wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(addr), s); wil_memcpy_toio_32(dst, d->data, s); wmb(); /* finish before processing next record */ return 0; } static int fw_handle_data(struct wil6210_priv *wil, const void *data, size_t size) { const struct wil_fw_record_data *d = data; return __fw_handle_data(wil, data, size, d->addr); } static int fw_handle_fill(struct wil6210_priv *wil, const void *data, size_t size) { Loading Loading @@ -551,6 +606,100 @@ int wil_request_firmware(struct wil6210_priv *wil, const char *name, return rc; } /** * wil_brd_process - process section from BRD file * * Return error code */ static int wil_brd_process(struct wil6210_priv *wil, const void *data, size_t size) { int rc = 0; const struct wil_fw_record_head *hdr = data; size_t s, hdr_sz; u16 type; /* Assuming the board file includes only one header record and one data * record. Each record starts with wil_fw_record_head. */ if (size < sizeof(*hdr)) return -EINVAL; s = sizeof(*hdr) + le32_to_cpu(hdr->size); if (s > size) return -EINVAL; /* Skip the header record and handle the data record */ hdr = (const void *)hdr + s; size -= s; if (size < sizeof(*hdr)) return -EINVAL; hdr_sz = le32_to_cpu(hdr->size); if (wil->brd_file_max_size && hdr_sz > wil->brd_file_max_size) return -EINVAL; if (sizeof(*hdr) + hdr_sz > size) return -EINVAL; if (hdr_sz % 4) { wil_err_fw(wil, "unaligned record size: %zu\n", hdr_sz); return -EINVAL; } type = le16_to_cpu(hdr->type); if (type != wil_fw_type_data) { wil_err_fw(wil, "invalid record type for board file: %d\n", type); return -EINVAL; } if (hdr_sz < sizeof(struct wil_fw_record_data)) { wil_err_fw(wil, "data record too short: %zu\n", hdr_sz); return -EINVAL; } wil_dbg_fw(wil, "using addr from fw file: [0x%08x]\n", wil->brd_file_addr); rc = __fw_handle_data(wil, &hdr[1], hdr_sz, cpu_to_le32(wil->brd_file_addr)); return rc; } /** * wil_request_board - Request board file * * Request board image from the file * board file address and max size are read from FW file * during initialization. * brd file shall include one header and one data section. * * Return error code */ int wil_request_board(struct wil6210_priv *wil, const char *name) { int rc, dlen; const struct firmware *brd; rc = request_firmware(&brd, name, wil_to_dev(wil)); if (rc) { wil_err_fw(wil, "Failed to load brd %s\n", name); return rc; } wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, brd->size); /* Verify the header */ dlen = wil_fw_verify(wil, brd->data, brd->size); if (dlen < 0) { rc = dlen; goto out; } /* Process the data record */ rc = wil_brd_process(wil, brd->data, dlen); out: release_firmware(brd); return rc; } /** * wil_fw_verify_file_exists - checks if firmware file exist * Loading
drivers/net/wireless/ath/wil6210/interrupt.c +4 −2 Original line number Diff line number Diff line /* * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above Loading Loading @@ -395,8 +396,9 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie) wil6210_mask_irq_misc(wil, false); if (isr & ISR_MISC_FW_ERROR) { u32 fw_assert_code = wil_r(wil, RGF_FW_ASSERT_CODE); u32 ucode_assert_code = wil_r(wil, RGF_UCODE_ASSERT_CODE); u32 fw_assert_code = wil_r(wil, wil->rgf_fw_assert_code_addr); u32 ucode_assert_code = wil_r(wil, wil->rgf_ucode_assert_code_addr); wil_err(wil, "Firmware error detected, assert codes FW 0x%08x, UCODE 0x%08x\n", Loading
drivers/net/wireless/ath/wil6210/main.c +191 −26 Original line number Diff line number Diff line /* * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above Loading Loading @@ -642,6 +643,98 @@ void wil_priv_deinit(struct wil6210_priv *wil) destroy_workqueue(wil->wmi_wq); } static void wil_shutdown_bl(struct wil6210_priv *wil) { u32 val; wil_s(wil, RGF_USER_BL + offsetof(struct bl_dedicated_registers_v1, bl_shutdown_handshake), BL_SHUTDOWN_HS_GRTD); usleep_range(100, 150); val = wil_r(wil, RGF_USER_BL + offsetof(struct bl_dedicated_registers_v1, bl_shutdown_handshake)); if (val & BL_SHUTDOWN_HS_RTD) { wil_dbg_misc(wil, "BL is ready for halt\n"); return; } wil_err(wil, "BL did not report ready for halt\n"); } /* this format is used by ARC embedded CPU for instruction memory */ static inline u32 ARC_me_imm32(u32 d) { return ((d & 0xffff0000) >> 16) | ((d & 0x0000ffff) << 16); } /* defines access to interrupt vectors for wil_freeze_bl */ #define ARC_IRQ_VECTOR_OFFSET(N) ((N) * 8) /* ARC long jump instruction */ #define ARC_JAL_INST (0x20200f80) static void wil_freeze_bl(struct wil6210_priv *wil) { u32 jal, upc, saved; u32 ivt3 = ARC_IRQ_VECTOR_OFFSET(3); jal = wil_r(wil, wil->iccm_base + ivt3); if (jal != ARC_me_imm32(ARC_JAL_INST)) { wil_dbg_misc(wil, "invalid IVT entry found, skipping\n"); return; } /* prevent the target from entering deep sleep * and disabling memory access */ saved = wil_r(wil, RGF_USER_USAGE_8); wil_w(wil, RGF_USER_USAGE_8, saved | BIT_USER_PREVENT_DEEP_SLEEP); usleep_range(20, 25); /* let the BL process the bit */ /* redirect to endless loop in the INT_L1 context and let it trap */ wil_w(wil, wil->iccm_base + ivt3 + 4, ARC_me_imm32(ivt3)); usleep_range(20, 25); /* let the BL get into the trap */ /* verify the BL is frozen */ upc = wil_r(wil, RGF_USER_CPU_PC); if (upc < ivt3 || (upc > (ivt3 + 8))) wil_dbg_misc(wil, "BL freeze failed, PC=0x%08X\n", upc); wil_w(wil, RGF_USER_USAGE_8, saved); } static void wil_bl_prepare_halt(struct wil6210_priv *wil) { u32 tmp, ver; /* before halting device CPU driver must make sure BL is not accessing * host memory. This is done differently depending on BL version: * 1. For very old BL versions the procedure is skipped * (not supported). * 2. For old BL version we use a special trick to freeze the BL * 3. For new BL versions we shutdown the BL using handshake procedure. */ tmp = wil_r(wil, RGF_USER_BL + offsetof(struct bl_dedicated_registers_v0, boot_loader_struct_version)); if (!tmp) { wil_dbg_misc(wil, "old BL, skipping halt preperation\n"); return; } tmp = wil_r(wil, RGF_USER_BL + offsetof(struct bl_dedicated_registers_v1, bl_shutdown_handshake)); ver = BL_SHUTDOWN_HS_PROT_VER(tmp); if (ver > 0) wil_shutdown_bl(wil); else wil_freeze_bl(wil); } static inline void wil_halt_cpu(struct wil6210_priv *wil) { wil_w(wil, RGF_USER_USER_CPU_0, BIT_USER_USER_CPU_MAN_RST); Loading Loading @@ -675,7 +768,7 @@ static void wil_set_oob_mode(struct wil6210_priv *wil, u8 mode) } } static int wil_target_reset(struct wil6210_priv *wil) static int wil_target_reset(struct wil6210_priv *wil, int no_flash) { int delay = 0; u32 x, x1 = 0; Loading @@ -689,9 +782,16 @@ static int wil_target_reset(struct wil6210_priv *wil) wil_halt_cpu(wil); if (!no_flash) { /* clear all boot loader "ready" bits */ wil_w(wil, RGF_USER_BL + offsetof(struct bl_dedicated_registers_v0, boot_loader_ready), 0); offsetof(struct bl_dedicated_registers_v0, boot_loader_ready), 0); /* this should be safe to write even with old BLs */ wil_w(wil, RGF_USER_BL + offsetof(struct bl_dedicated_registers_v1, bl_shutdown_handshake), 0); } /* Clear Fw Download notification */ wil_c(wil, RGF_USER_USAGE_6, BIT(0)); Loading Loading @@ -732,13 +832,25 @@ static int wil_target_reset(struct wil6210_priv *wil) wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); /* wait until device ready. typical time is 20..80 msec */ if (no_flash) do { msleep(RST_DELAY); x = wil_r(wil, USER_EXT_USER_PMU_3); if (delay++ > RST_COUNT) { wil_err(wil, "Reset not completed, PMU_3 0x%08x\n", x); return -ETIME; } } while ((x & BIT_PMU_DEVICE_RDY) == 0); else do { msleep(RST_DELAY); x = wil_r(wil, RGF_USER_BL + offsetof(struct bl_dedicated_registers_v0, boot_loader_ready)); if (x1 != x) { wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n", x1, x); wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n", x1, x); x1 = x; } if (delay++ > RST_COUNT) { Loading @@ -754,6 +866,21 @@ static int wil_target_reset(struct wil6210_priv *wil) wil_s(wil, RGF_DMA_OFUL_NID_0, BIT_DMA_OFUL_NID_0_RX_EXT_TR_EN | BIT_DMA_OFUL_NID_0_RX_EXT_A3_SRC); if (no_flash) { /* Reset OTP HW vectors to fit 40MHz */ wil_w(wil, RGF_USER_XPM_IFC_RD_TIME1, 0x60001); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME2, 0x20027); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME3, 0x1); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME4, 0x20027); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME5, 0x30003); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME6, 0x20002); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME7, 0x60001); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME8, 0x60001); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME9, 0x60001); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME10, 0x60001); wil_w(wil, RGF_USER_XPM_RD_DOUT_SAMPLE_TIME, 0x57); } wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY); return 0; } Loading Loading @@ -911,6 +1038,27 @@ static void wil_bl_crash_info(struct wil6210_priv *wil, bool is_err) } } static int wil_get_otp_info(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); struct wiphy *wiphy = wil_to_wiphy(wil); u8 mac[8]; wil_memcpy_fromio_32(mac, wil->csr + HOSTADDR(RGF_OTP_MAC), sizeof(mac)); if (!is_valid_ether_addr(mac)) { wil_err(wil, "Invalid MAC %pM\n", mac); return -EINVAL; } ether_addr_copy(ndev->perm_addr, mac); ether_addr_copy(wiphy->perm_addr, mac); if (!is_valid_ether_addr(ndev->dev_addr)) ether_addr_copy(ndev->dev_addr, mac); return 0; } static int wil_wait_for_fw_ready(struct wil6210_priv *wil) { ulong to = msecs_to_jiffies(1000); Loading Loading @@ -1004,6 +1152,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) { int rc; unsigned long status_flags = BIT(wil_status_resetting); int no_flash; wil_dbg_misc(wil, "reset\n"); Loading Loading @@ -1082,20 +1231,28 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) flush_workqueue(wil->wq_service); flush_workqueue(wil->wmi_wq); no_flash = test_bit(hw_capa_no_flash, wil->hw_capa); if (!no_flash) wil_bl_crash_info(wil, false); wil_disable_irq(wil); rc = wil_target_reset(wil); rc = wil_target_reset(wil, no_flash); wil6210_clear_irq(wil); wil_enable_irq(wil); wil_rx_fini(wil); if (rc) { if (!no_flash) wil_bl_crash_info(wil, true); goto out; } if (no_flash) { rc = wil_get_otp_info(wil); } else { rc = wil_get_bl_info(wil); if (rc == -EAGAIN && !load_fw) /* ignore RF error if not going up */ if (rc == -EAGAIN && !load_fw) /* ignore RF error if not going up */ rc = 0; } if (rc) goto out; Loading @@ -1104,13 +1261,21 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) wil_info(wil, "Use firmware <%s> + board <%s>\n", wil->wil_fw_name, WIL_BOARD_FILE_NAME); if (!no_flash) wil_bl_prepare_halt(wil); wil_halt_cpu(wil); memset(wil->fw_version, 0, sizeof(wil->fw_version)); /* Loading f/w from the file */ rc = wil_request_firmware(wil, wil->wil_fw_name, true); if (rc) goto out; rc = wil_request_firmware(wil, WIL_BOARD_FILE_NAME, true); if (wil->brd_file_addr) rc = wil_request_board(wil, WIL_BOARD_FILE_NAME); else rc = wil_request_firmware(wil, WIL_BOARD_FILE_NAME, true); if (rc) goto out; Loading