aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLior David <liord@codeaurora.org>2018-01-21 04:14:41 -0500
committerKalle Valo <kvalo@codeaurora.org>2018-01-25 00:32:23 -0500
commit70bcc658c0b13d9e89033e1981daf0f431f39522 (patch)
tree8614a39e73bab6381a2a212fcbe732f5e7bc9a3c
parentb8e13b87b15d2dde8c61ad7de5626d07a9d4da01 (diff)
wil6210: fix random failure to bring network interface up
Currently when we want to bring the interface up, we first reset the device which causes the boot loader to run. Then we halt the device CPU, load FW image and resume the device CPU. There are some boot loader versions which perform redundant memory accesses even when idle. Halting the device CPU while boot loader access memory can cause the device memory controller to get stuck, the FW will fail to load and the network interface will not come up. For such boot loaders implement a workaround where we freeze the boot loader before halting the device CPU, so it will not perform any memory accesses. Signed-off-by: Lior David <liord@codeaurora.org> Signed-off-by: Maya Erez <merez@codeaurora.org> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
-rw-r--r--drivers/net/wireless/ath/wil6210/boot_loader.h9
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c102
-rw-r--r--drivers/net/wireless/ath/wil6210/pcie_bus.c8
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h3
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c18
5 files changed, 138 insertions, 2 deletions
diff --git a/drivers/net/wireless/ath/wil6210/boot_loader.h b/drivers/net/wireless/ath/wil6210/boot_loader.h
index c131b5e1292f..d32c1f4e533a 100644
--- a/drivers/net/wireless/ath/wil6210/boot_loader.h
+++ b/drivers/net/wireless/ath/wil6210/boot_loader.h
@@ -1,4 +1,5 @@
1/* Copyright (c) 2015 Qualcomm Atheros, Inc. 1/* Copyright (c) 2015 Qualcomm Atheros, Inc.
2 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
2 * 3 *
3 * Permission to use, copy, modify, and/or distribute this software for any 4 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above 5 * purpose with or without fee is hereby granted, provided that the above
@@ -39,7 +40,8 @@ struct bl_dedicated_registers_v1 {
39 /* valid only for version 2 and above */ 40 /* valid only for version 2 and above */
40 __le32 bl_assert_code; /* 0x880A58 BL Assert code */ 41 __le32 bl_assert_code; /* 0x880A58 BL Assert code */
41 __le32 bl_assert_blink; /* 0x880A5C BL Assert Branch */ 42 __le32 bl_assert_blink; /* 0x880A5C BL Assert Branch */
42 __le32 bl_reserved[22]; /* 0x880A60 - 0x880AB4 */ 43 __le32 bl_shutdown_handshake; /* 0x880A60 BL cleaner shutdown */
44 __le32 bl_reserved[21]; /* 0x880A64 - 0x880AB4 */
43 __le32 bl_magic_number; /* 0x880AB8 BL Magic number */ 45 __le32 bl_magic_number; /* 0x880AB8 BL Magic number */
44} __packed; 46} __packed;
45 47
@@ -58,4 +60,9 @@ struct bl_dedicated_registers_v0 {
58 u8 mac_address[6]; /* 0x880A4c BL mac address */ 60 u8 mac_address[6]; /* 0x880A4c BL mac address */
59} __packed; 61} __packed;
60 62
63/* bits for bl_shutdown_handshake */
64#define BL_SHUTDOWN_HS_GRTD BIT(0)
65#define BL_SHUTDOWN_HS_RTD BIT(1)
66#define BL_SHUTDOWN_HS_PROT_VER(x) WIL_GET_BITS(x, 28, 31)
67
61#endif /* BOOT_LOADER_EXPORT_H_ */ 68#endif /* BOOT_LOADER_EXPORT_H_ */
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index d95ff742e386..575aafe149a6 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -638,6 +638,98 @@ void wil_priv_deinit(struct wil6210_priv *wil)
638 destroy_workqueue(wil->wmi_wq); 638 destroy_workqueue(wil->wmi_wq);
639} 639}
640 640
641static void wil_shutdown_bl(struct wil6210_priv *wil)
642{
643 u32 val;
644
645 wil_s(wil, RGF_USER_BL +
646 offsetof(struct bl_dedicated_registers_v1,
647 bl_shutdown_handshake), BL_SHUTDOWN_HS_GRTD);
648
649 usleep_range(100, 150);
650
651 val = wil_r(wil, RGF_USER_BL +
652 offsetof(struct bl_dedicated_registers_v1,
653 bl_shutdown_handshake));
654 if (val & BL_SHUTDOWN_HS_RTD) {
655 wil_dbg_misc(wil, "BL is ready for halt\n");
656 return;
657 }
658
659 wil_err(wil, "BL did not report ready for halt\n");
660}
661
662/* this format is used by ARC embedded CPU for instruction memory */
663static inline u32 ARC_me_imm32(u32 d)
664{
665 return ((d & 0xffff0000) >> 16) | ((d & 0x0000ffff) << 16);
666}
667
668/* defines access to interrupt vectors for wil_freeze_bl */
669#define ARC_IRQ_VECTOR_OFFSET(N) ((N) * 8)
670/* ARC long jump instruction */
671#define ARC_JAL_INST (0x20200f80)
672
673static void wil_freeze_bl(struct wil6210_priv *wil)
674{
675 u32 jal, upc, saved;
676 u32 ivt3 = ARC_IRQ_VECTOR_OFFSET(3);
677
678 jal = wil_r(wil, wil->iccm_base + ivt3);
679 if (jal != ARC_me_imm32(ARC_JAL_INST)) {
680 wil_dbg_misc(wil, "invalid IVT entry found, skipping\n");
681 return;
682 }
683
684 /* prevent the target from entering deep sleep
685 * and disabling memory access
686 */
687 saved = wil_r(wil, RGF_USER_USAGE_8);
688 wil_w(wil, RGF_USER_USAGE_8, saved | BIT_USER_PREVENT_DEEP_SLEEP);
689 usleep_range(20, 25); /* let the BL process the bit */
690
691 /* redirect to endless loop in the INT_L1 context and let it trap */
692 wil_w(wil, wil->iccm_base + ivt3 + 4, ARC_me_imm32(ivt3));
693 usleep_range(20, 25); /* let the BL get into the trap */
694
695 /* verify the BL is frozen */
696 upc = wil_r(wil, RGF_USER_CPU_PC);
697 if (upc < ivt3 || (upc > (ivt3 + 8)))
698 wil_dbg_misc(wil, "BL freeze failed, PC=0x%08X\n", upc);
699
700 wil_w(wil, RGF_USER_USAGE_8, saved);
701}
702
703static void wil_bl_prepare_halt(struct wil6210_priv *wil)
704{
705 u32 tmp, ver;
706
707 /* before halting device CPU driver must make sure BL is not accessing
708 * host memory. This is done differently depending on BL version:
709 * 1. For very old BL versions the procedure is skipped
710 * (not supported).
711 * 2. For old BL version we use a special trick to freeze the BL
712 * 3. For new BL versions we shutdown the BL using handshake procedure.
713 */
714 tmp = wil_r(wil, RGF_USER_BL +
715 offsetof(struct bl_dedicated_registers_v0,
716 boot_loader_struct_version));
717 if (!tmp) {
718 wil_dbg_misc(wil, "old BL, skipping halt preperation\n");
719 return;
720 }
721
722 tmp = wil_r(wil, RGF_USER_BL +
723 offsetof(struct bl_dedicated_registers_v1,
724 bl_shutdown_handshake));
725 ver = BL_SHUTDOWN_HS_PROT_VER(tmp);
726
727 if (ver > 0)
728 wil_shutdown_bl(wil);
729 else
730 wil_freeze_bl(wil);
731}
732
641static inline void wil_halt_cpu(struct wil6210_priv *wil) 733static inline void wil_halt_cpu(struct wil6210_priv *wil)
642{ 734{
643 wil_w(wil, RGF_USER_USER_CPU_0, BIT_USER_USER_CPU_MAN_RST); 735 wil_w(wil, RGF_USER_USER_CPU_0, BIT_USER_USER_CPU_MAN_RST);
@@ -685,11 +777,16 @@ static int wil_target_reset(struct wil6210_priv *wil, int no_flash)
685 777
686 wil_halt_cpu(wil); 778 wil_halt_cpu(wil);
687 779
688 if (!no_flash) 780 if (!no_flash) {
689 /* clear all boot loader "ready" bits */ 781 /* clear all boot loader "ready" bits */
690 wil_w(wil, RGF_USER_BL + 782 wil_w(wil, RGF_USER_BL +
691 offsetof(struct bl_dedicated_registers_v0, 783 offsetof(struct bl_dedicated_registers_v0,
692 boot_loader_ready), 0); 784 boot_loader_ready), 0);
785 /* this should be safe to write even with old BLs */
786 wil_w(wil, RGF_USER_BL +
787 offsetof(struct bl_dedicated_registers_v1,
788 bl_shutdown_handshake), 0);
789 }
693 /* Clear Fw Download notification */ 790 /* Clear Fw Download notification */
694 wil_c(wil, RGF_USER_USAGE_6, BIT(0)); 791 wil_c(wil, RGF_USER_USAGE_6, BIT(0));
695 792
@@ -1156,6 +1253,9 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
1156 wil_info(wil, "Use firmware <%s> + board <%s>\n", 1253 wil_info(wil, "Use firmware <%s> + board <%s>\n",
1157 wil->wil_fw_name, WIL_BOARD_FILE_NAME); 1254 wil->wil_fw_name, WIL_BOARD_FILE_NAME);
1158 1255
1256 if (!no_flash)
1257 wil_bl_prepare_halt(wil);
1258
1159 wil_halt_cpu(wil); 1259 wil_halt_cpu(wil);
1160 memset(wil->fw_version, 0, sizeof(wil->fw_version)); 1260 memset(wil->fw_version, 0, sizeof(wil->fw_version));
1161 /* Loading f/w from the file */ 1261 /* Loading f/w from the file */
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index ab8cb91b7984..750c34edd3f5 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -43,6 +43,7 @@ int wil_set_capabilities(struct wil6210_priv *wil)
43 u8 chip_revision = (wil_r(wil, RGF_USER_REVISION_ID) & 43 u8 chip_revision = (wil_r(wil, RGF_USER_REVISION_ID) &
44 RGF_USER_REVISION_ID_MASK); 44 RGF_USER_REVISION_ID_MASK);
45 int platform_capa; 45 int platform_capa;
46 struct fw_map *iccm_section;
46 47
47 bitmap_zero(wil->hw_capa, hw_capa_last); 48 bitmap_zero(wil->hw_capa, hw_capa_last);
48 bitmap_zero(wil->fw_capabilities, WMI_FW_CAPABILITY_MAX); 49 bitmap_zero(wil->fw_capabilities, WMI_FW_CAPABILITY_MAX);
@@ -95,6 +96,13 @@ int wil_set_capabilities(struct wil6210_priv *wil)
95 return -EINVAL; 96 return -EINVAL;
96 } 97 }
97 98
99 iccm_section = wil_find_fw_mapping("fw_code");
100 if (!iccm_section) {
101 wil_err(wil, "fw_code section not found in fw_mapping\n");
102 return -EINVAL;
103 }
104 wil->iccm_base = iccm_section->host;
105
98 wil_info(wil, "Board hardware is %s, flash %sexist\n", wil->hw_name, 106 wil_info(wil, "Board hardware is %s, flash %sexist\n", wil->hw_name,
99 test_bit(hw_capa_no_flash, wil->hw_capa) ? "doesn't " : ""); 107 test_bit(hw_capa_no_flash, wil->hw_capa) ? "doesn't " : "");
100 108
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index d243df395e0a..5f9bcfb9bb86 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -170,6 +170,7 @@ struct RGF_ICR {
170 #define HW_MACHINE_BOOT_DONE (0x3fffffd) 170 #define HW_MACHINE_BOOT_DONE (0x3fffffd)
171#define RGF_USER_USER_CPU_0 (0x8801e0) 171#define RGF_USER_USER_CPU_0 (0x8801e0)
172 #define BIT_USER_USER_CPU_MAN_RST BIT(1) /* user_cpu_man_rst */ 172 #define BIT_USER_USER_CPU_MAN_RST BIT(1) /* user_cpu_man_rst */
173#define RGF_USER_CPU_PC (0x8801e8)
173#define RGF_USER_MAC_CPU_0 (0x8801fc) 174#define RGF_USER_MAC_CPU_0 (0x8801fc)
174 #define BIT_USER_MAC_CPU_MAN_RST BIT(1) /* mac_cpu_man_rst */ 175 #define BIT_USER_MAC_CPU_MAN_RST BIT(1) /* mac_cpu_man_rst */
175#define RGF_USER_USER_SCRATCH_PAD (0x8802bc) 176#define RGF_USER_USER_SCRATCH_PAD (0x8802bc)
@@ -791,6 +792,7 @@ struct wil6210_priv {
791 792
792 u32 rgf_fw_assert_code_addr; 793 u32 rgf_fw_assert_code_addr;
793 u32 rgf_ucode_assert_code_addr; 794 u32 rgf_ucode_assert_code_addr;
795 u32 iccm_base;
794}; 796};
795 797
796#define wil_to_wiphy(i) (i->wdev->wiphy) 798#define wil_to_wiphy(i) (i->wdev->wiphy)
@@ -916,6 +918,7 @@ void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r);
916int wil_find_cid(struct wil6210_priv *wil, const u8 *mac); 918int wil_find_cid(struct wil6210_priv *wil, const u8 *mac);
917void wil_set_ethtoolops(struct net_device *ndev); 919void wil_set_ethtoolops(struct net_device *ndev);
918 920
921struct fw_map *wil_find_fw_mapping(const char *section);
919void __iomem *wmi_buffer_block(struct wil6210_priv *wil, __le32 ptr, u32 size); 922void __iomem *wmi_buffer_block(struct wil6210_priv *wil, __le32 ptr, u32 size);
920void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr); 923void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr);
921void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr); 924void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 69da3c256ad0..43c5803a35af 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -185,6 +185,24 @@ static u32 wmi_addr_remap(u32 x)
185} 185}
186 186
187/** 187/**
188 * find fw_mapping entry by section name
189 * @section - section name
190 *
191 * Return pointer to section or NULL if not found
192 */
193struct fw_map *wil_find_fw_mapping(const char *section)
194{
195 int i;
196
197 for (i = 0; i < ARRAY_SIZE(fw_mapping); i++)
198 if (fw_mapping[i].name &&
199 !strcmp(section, fw_mapping[i].name))
200 return &fw_mapping[i];
201
202 return NULL;
203}
204
205/**
188 * Check address validity for WMI buffer; remap if needed 206 * Check address validity for WMI buffer; remap if needed
189 * @ptr - internal (linker) fw/ucode address 207 * @ptr - internal (linker) fw/ucode address
190 * @size - if non zero, validate the block does not 208 * @size - if non zero, validate the block does not