162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright (C) 2022 MediaTek Inc. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Author: Lorenzo Bianconi <lorenzo@kernel.org> 562306a36Sopenharmony_ci * Sujuan Chen <sujuan.chen@mediatek.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/firmware.h> 962306a36Sopenharmony_ci#include <linux/of_address.h> 1062306a36Sopenharmony_ci#include <linux/of_reserved_mem.h> 1162306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1262306a36Sopenharmony_ci#include <linux/soc/mediatek/mtk_wed.h> 1362306a36Sopenharmony_ci#include <asm/unaligned.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "mtk_wed_regs.h" 1662306a36Sopenharmony_ci#include "mtk_wed_wo.h" 1762306a36Sopenharmony_ci#include "mtk_wed.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic u32 wo_r32(struct mtk_wed_wo *wo, u32 reg) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci return readl(wo->boot.addr + reg); 2262306a36Sopenharmony_ci} 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic void wo_w32(struct mtk_wed_wo *wo, u32 reg, u32 val) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci writel(val, wo->boot.addr + reg); 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic struct sk_buff * 3062306a36Sopenharmony_cimtk_wed_mcu_msg_alloc(const void *data, int data_len) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci int length = sizeof(struct mtk_wed_mcu_hdr) + data_len; 3362306a36Sopenharmony_ci struct sk_buff *skb; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci skb = alloc_skb(length, GFP_KERNEL); 3662306a36Sopenharmony_ci if (!skb) 3762306a36Sopenharmony_ci return NULL; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci memset(skb->head, 0, length); 4062306a36Sopenharmony_ci skb_reserve(skb, sizeof(struct mtk_wed_mcu_hdr)); 4162306a36Sopenharmony_ci if (data && data_len) 4262306a36Sopenharmony_ci skb_put_data(skb, data, data_len); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci return skb; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic struct sk_buff * 4862306a36Sopenharmony_cimtk_wed_mcu_get_response(struct mtk_wed_wo *wo, unsigned long expires) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci if (!time_is_after_jiffies(expires)) 5162306a36Sopenharmony_ci return NULL; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci wait_event_timeout(wo->mcu.wait, !skb_queue_empty(&wo->mcu.res_q), 5462306a36Sopenharmony_ci expires - jiffies); 5562306a36Sopenharmony_ci return skb_dequeue(&wo->mcu.res_q); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_civoid mtk_wed_mcu_rx_event(struct mtk_wed_wo *wo, struct sk_buff *skb) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci skb_queue_tail(&wo->mcu.res_q, skb); 6162306a36Sopenharmony_ci wake_up(&wo->mcu.wait); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void 6562306a36Sopenharmony_cimtk_wed_update_rx_stats(struct mtk_wed_device *wed, struct sk_buff *skb) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci u32 count = get_unaligned_le32(skb->data); 6862306a36Sopenharmony_ci struct mtk_wed_wo_rx_stats *stats; 6962306a36Sopenharmony_ci int i; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (count * sizeof(*stats) > skb->len - sizeof(u32)) 7262306a36Sopenharmony_ci return; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci stats = (struct mtk_wed_wo_rx_stats *)(skb->data + sizeof(u32)); 7562306a36Sopenharmony_ci for (i = 0 ; i < count ; i++) 7662306a36Sopenharmony_ci wed->wlan.update_wo_rx_stats(wed, &stats[i]); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_civoid mtk_wed_mcu_rx_unsolicited_event(struct mtk_wed_wo *wo, 8062306a36Sopenharmony_ci struct sk_buff *skb) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct mtk_wed_mcu_hdr *hdr = (struct mtk_wed_mcu_hdr *)skb->data; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci skb_pull(skb, sizeof(*hdr)); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci switch (hdr->cmd) { 8762306a36Sopenharmony_ci case MTK_WED_WO_EVT_LOG_DUMP: 8862306a36Sopenharmony_ci dev_notice(wo->hw->dev, "%s\n", skb->data); 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci case MTK_WED_WO_EVT_PROFILING: { 9162306a36Sopenharmony_ci struct mtk_wed_wo_log_info *info = (void *)skb->data; 9262306a36Sopenharmony_ci u32 count = skb->len / sizeof(*info); 9362306a36Sopenharmony_ci int i; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci for (i = 0 ; i < count ; i++) 9662306a36Sopenharmony_ci dev_notice(wo->hw->dev, 9762306a36Sopenharmony_ci "SN:%u latency: total=%u, rro:%u, mod:%u\n", 9862306a36Sopenharmony_ci le32_to_cpu(info[i].sn), 9962306a36Sopenharmony_ci le32_to_cpu(info[i].total), 10062306a36Sopenharmony_ci le32_to_cpu(info[i].rro), 10162306a36Sopenharmony_ci le32_to_cpu(info[i].mod)); 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci case MTK_WED_WO_EVT_RXCNT_INFO: 10562306a36Sopenharmony_ci mtk_wed_update_rx_stats(wo->hw->wed_dev, skb); 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci default: 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci dev_kfree_skb(skb); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic int 11562306a36Sopenharmony_cimtk_wed_mcu_skb_send_msg(struct mtk_wed_wo *wo, struct sk_buff *skb, 11662306a36Sopenharmony_ci int id, int cmd, u16 *wait_seq, bool wait_resp) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct mtk_wed_mcu_hdr *hdr; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* TODO: make it dynamic based on cmd */ 12162306a36Sopenharmony_ci wo->mcu.timeout = 20 * HZ; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci hdr = (struct mtk_wed_mcu_hdr *)skb_push(skb, sizeof(*hdr)); 12462306a36Sopenharmony_ci hdr->cmd = cmd; 12562306a36Sopenharmony_ci hdr->length = cpu_to_le16(skb->len); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (wait_resp && wait_seq) { 12862306a36Sopenharmony_ci u16 seq = ++wo->mcu.seq; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (!seq) 13162306a36Sopenharmony_ci seq = ++wo->mcu.seq; 13262306a36Sopenharmony_ci *wait_seq = seq; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci hdr->flag |= cpu_to_le16(MTK_WED_WARP_CMD_FLAG_NEED_RSP); 13562306a36Sopenharmony_ci hdr->seq = cpu_to_le16(seq); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci if (id == MTK_WED_MODULE_ID_WO) 13862306a36Sopenharmony_ci hdr->flag |= cpu_to_le16(MTK_WED_WARP_CMD_FLAG_FROM_TO_WO); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return mtk_wed_wo_queue_tx_skb(wo, &wo->q_tx, skb); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int 14462306a36Sopenharmony_cimtk_wed_mcu_parse_response(struct mtk_wed_wo *wo, struct sk_buff *skb, 14562306a36Sopenharmony_ci int cmd, int seq) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct mtk_wed_mcu_hdr *hdr; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (!skb) { 15062306a36Sopenharmony_ci dev_err(wo->hw->dev, "Message %08x (seq %d) timeout\n", 15162306a36Sopenharmony_ci cmd, seq); 15262306a36Sopenharmony_ci return -ETIMEDOUT; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci hdr = (struct mtk_wed_mcu_hdr *)skb->data; 15662306a36Sopenharmony_ci if (le16_to_cpu(hdr->seq) != seq) 15762306a36Sopenharmony_ci return -EAGAIN; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci skb_pull(skb, sizeof(*hdr)); 16062306a36Sopenharmony_ci switch (cmd) { 16162306a36Sopenharmony_ci case MTK_WED_WO_CMD_RXCNT_INFO: 16262306a36Sopenharmony_ci mtk_wed_update_rx_stats(wo->hw->wed_dev, skb); 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci default: 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ciint mtk_wed_mcu_send_msg(struct mtk_wed_wo *wo, int id, int cmd, 17262306a36Sopenharmony_ci const void *data, int len, bool wait_resp) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci unsigned long expires; 17562306a36Sopenharmony_ci struct sk_buff *skb; 17662306a36Sopenharmony_ci u16 seq; 17762306a36Sopenharmony_ci int ret; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci skb = mtk_wed_mcu_msg_alloc(data, len); 18062306a36Sopenharmony_ci if (!skb) 18162306a36Sopenharmony_ci return -ENOMEM; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci mutex_lock(&wo->mcu.mutex); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci ret = mtk_wed_mcu_skb_send_msg(wo, skb, id, cmd, &seq, wait_resp); 18662306a36Sopenharmony_ci if (ret || !wait_resp) 18762306a36Sopenharmony_ci goto unlock; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci expires = jiffies + wo->mcu.timeout; 19062306a36Sopenharmony_ci do { 19162306a36Sopenharmony_ci skb = mtk_wed_mcu_get_response(wo, expires); 19262306a36Sopenharmony_ci ret = mtk_wed_mcu_parse_response(wo, skb, cmd, seq); 19362306a36Sopenharmony_ci dev_kfree_skb(skb); 19462306a36Sopenharmony_ci } while (ret == -EAGAIN); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ciunlock: 19762306a36Sopenharmony_ci mutex_unlock(&wo->mcu.mutex); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return ret; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ciint mtk_wed_mcu_msg_update(struct mtk_wed_device *dev, int id, void *data, 20362306a36Sopenharmony_ci int len) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct mtk_wed_wo *wo = dev->hw->wed_wo; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (dev->hw->version == 1) 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (WARN_ON(!wo)) 21162306a36Sopenharmony_ci return -ENODEV; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, id, data, len, 21462306a36Sopenharmony_ci true); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic int 21862306a36Sopenharmony_cimtk_wed_get_memory_region(struct mtk_wed_wo *wo, 21962306a36Sopenharmony_ci struct mtk_wed_wo_memory_region *region) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct reserved_mem *rmem; 22262306a36Sopenharmony_ci struct device_node *np; 22362306a36Sopenharmony_ci int index; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci index = of_property_match_string(wo->hw->node, "memory-region-names", 22662306a36Sopenharmony_ci region->name); 22762306a36Sopenharmony_ci if (index < 0) 22862306a36Sopenharmony_ci return index; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci np = of_parse_phandle(wo->hw->node, "memory-region", index); 23162306a36Sopenharmony_ci if (!np) 23262306a36Sopenharmony_ci return -ENODEV; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci rmem = of_reserved_mem_lookup(np); 23562306a36Sopenharmony_ci of_node_put(np); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (!rmem) 23862306a36Sopenharmony_ci return -ENODEV; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci region->phy_addr = rmem->base; 24162306a36Sopenharmony_ci region->size = rmem->size; 24262306a36Sopenharmony_ci region->addr = devm_ioremap(wo->hw->dev, region->phy_addr, region->size); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return !region->addr ? -EINVAL : 0; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int 24862306a36Sopenharmony_cimtk_wed_mcu_run_firmware(struct mtk_wed_wo *wo, const struct firmware *fw, 24962306a36Sopenharmony_ci struct mtk_wed_wo_memory_region *region) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci const u8 *first_region_ptr, *region_ptr, *trailer_ptr, *ptr = fw->data; 25262306a36Sopenharmony_ci const struct mtk_wed_fw_trailer *trailer; 25362306a36Sopenharmony_ci const struct mtk_wed_fw_region *fw_region; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci trailer_ptr = fw->data + fw->size - sizeof(*trailer); 25662306a36Sopenharmony_ci trailer = (const struct mtk_wed_fw_trailer *)trailer_ptr; 25762306a36Sopenharmony_ci region_ptr = trailer_ptr - trailer->num_region * sizeof(*fw_region); 25862306a36Sopenharmony_ci first_region_ptr = region_ptr; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci while (region_ptr < trailer_ptr) { 26162306a36Sopenharmony_ci u32 length; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci fw_region = (const struct mtk_wed_fw_region *)region_ptr; 26462306a36Sopenharmony_ci length = le32_to_cpu(fw_region->len); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (region->phy_addr != le32_to_cpu(fw_region->addr)) 26762306a36Sopenharmony_ci goto next; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (region->size < length) 27062306a36Sopenharmony_ci goto next; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (first_region_ptr < ptr + length) 27362306a36Sopenharmony_ci goto next; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (region->shared && region->consumed) 27662306a36Sopenharmony_ci return 0; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (!region->shared || !region->consumed) { 27962306a36Sopenharmony_ci memcpy_toio(region->addr, ptr, length); 28062306a36Sopenharmony_ci region->consumed = true; 28162306a36Sopenharmony_ci return 0; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_cinext: 28462306a36Sopenharmony_ci region_ptr += sizeof(*fw_region); 28562306a36Sopenharmony_ci ptr += length; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return -EINVAL; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic int 29262306a36Sopenharmony_cimtk_wed_mcu_load_firmware(struct mtk_wed_wo *wo) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci static struct mtk_wed_wo_memory_region mem_region[] = { 29562306a36Sopenharmony_ci [MTK_WED_WO_REGION_EMI] = { 29662306a36Sopenharmony_ci .name = "wo-emi", 29762306a36Sopenharmony_ci }, 29862306a36Sopenharmony_ci [MTK_WED_WO_REGION_ILM] = { 29962306a36Sopenharmony_ci .name = "wo-ilm", 30062306a36Sopenharmony_ci }, 30162306a36Sopenharmony_ci [MTK_WED_WO_REGION_DATA] = { 30262306a36Sopenharmony_ci .name = "wo-data", 30362306a36Sopenharmony_ci .shared = true, 30462306a36Sopenharmony_ci }, 30562306a36Sopenharmony_ci }; 30662306a36Sopenharmony_ci const struct mtk_wed_fw_trailer *trailer; 30762306a36Sopenharmony_ci const struct firmware *fw; 30862306a36Sopenharmony_ci const char *fw_name; 30962306a36Sopenharmony_ci u32 val, boot_cr; 31062306a36Sopenharmony_ci int ret, i; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* load firmware region metadata */ 31362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mem_region); i++) { 31462306a36Sopenharmony_ci ret = mtk_wed_get_memory_region(wo, &mem_region[i]); 31562306a36Sopenharmony_ci if (ret) 31662306a36Sopenharmony_ci return ret; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci wo->boot.name = "wo-boot"; 32062306a36Sopenharmony_ci ret = mtk_wed_get_memory_region(wo, &wo->boot); 32162306a36Sopenharmony_ci if (ret) 32262306a36Sopenharmony_ci return ret; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* set dummy cr */ 32562306a36Sopenharmony_ci wed_w32(wo->hw->wed_dev, MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_FWDL, 32662306a36Sopenharmony_ci wo->hw->index + 1); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* load firmware */ 32962306a36Sopenharmony_ci if (of_device_is_compatible(wo->hw->node, "mediatek,mt7981-wed")) 33062306a36Sopenharmony_ci fw_name = MT7981_FIRMWARE_WO; 33162306a36Sopenharmony_ci else 33262306a36Sopenharmony_ci fw_name = wo->hw->index ? MT7986_FIRMWARE_WO1 : MT7986_FIRMWARE_WO0; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci ret = request_firmware(&fw, fw_name, wo->hw->dev); 33562306a36Sopenharmony_ci if (ret) 33662306a36Sopenharmony_ci return ret; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci trailer = (void *)(fw->data + fw->size - 33962306a36Sopenharmony_ci sizeof(struct mtk_wed_fw_trailer)); 34062306a36Sopenharmony_ci dev_info(wo->hw->dev, 34162306a36Sopenharmony_ci "MTK WED WO Firmware Version: %.10s, Build Time: %.15s\n", 34262306a36Sopenharmony_ci trailer->fw_ver, trailer->build_date); 34362306a36Sopenharmony_ci dev_info(wo->hw->dev, "MTK WED WO Chip ID %02x Region %d\n", 34462306a36Sopenharmony_ci trailer->chip_id, trailer->num_region); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mem_region); i++) { 34762306a36Sopenharmony_ci ret = mtk_wed_mcu_run_firmware(wo, fw, &mem_region[i]); 34862306a36Sopenharmony_ci if (ret) 34962306a36Sopenharmony_ci goto out; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci /* set the start address */ 35362306a36Sopenharmony_ci boot_cr = wo->hw->index ? MTK_WO_MCU_CFG_LS_WA_BOOT_ADDR_ADDR 35462306a36Sopenharmony_ci : MTK_WO_MCU_CFG_LS_WM_BOOT_ADDR_ADDR; 35562306a36Sopenharmony_ci wo_w32(wo, boot_cr, mem_region[MTK_WED_WO_REGION_EMI].phy_addr >> 16); 35662306a36Sopenharmony_ci /* wo firmware reset */ 35762306a36Sopenharmony_ci wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR, 0xc00); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci val = wo_r32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR); 36062306a36Sopenharmony_ci val |= wo->hw->index ? MTK_WO_MCU_CFG_LS_WF_WM_WA_WA_CPU_RSTB_MASK 36162306a36Sopenharmony_ci : MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK; 36262306a36Sopenharmony_ci wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR, val); 36362306a36Sopenharmony_ciout: 36462306a36Sopenharmony_ci release_firmware(fw); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return ret; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic u32 37062306a36Sopenharmony_cimtk_wed_mcu_read_fw_dl(struct mtk_wed_wo *wo) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci return wed_r32(wo->hw->wed_dev, 37362306a36Sopenharmony_ci MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_FWDL); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ciint mtk_wed_mcu_init(struct mtk_wed_wo *wo) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci u32 val; 37962306a36Sopenharmony_ci int ret; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci skb_queue_head_init(&wo->mcu.res_q); 38262306a36Sopenharmony_ci init_waitqueue_head(&wo->mcu.wait); 38362306a36Sopenharmony_ci mutex_init(&wo->mcu.mutex); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci ret = mtk_wed_mcu_load_firmware(wo); 38662306a36Sopenharmony_ci if (ret) 38762306a36Sopenharmony_ci return ret; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return readx_poll_timeout(mtk_wed_mcu_read_fw_dl, wo, val, !val, 39062306a36Sopenharmony_ci 100, MTK_FW_DL_TIMEOUT); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ciMODULE_FIRMWARE(MT7981_FIRMWARE_WO); 39462306a36Sopenharmony_ciMODULE_FIRMWARE(MT7986_FIRMWARE_WO0); 39562306a36Sopenharmony_ciMODULE_FIRMWARE(MT7986_FIRMWARE_WO1); 396