From nobody Thu Mar 19 23:32:33 2026 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4fcMS6754gz6W8cT for ; Thu, 19 Mar 2026 23:32:38 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R12" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4fcMS16MpRz3bNw for ; Thu, 19 Mar 2026 23:32:33 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1773963153; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=IFInHHYRo+aym3uGQAwQs8467lxmsrwVRzniwQSe7bI=; b=qXPI+UixrtKNvIqYZf+F0ToRceIOfuZTre+kt9N3Q7A5vCpZ12gpit8AmTJ+mZuq73Pv3x +Uh5MfvCK6zqJpSY8beKucnm4nW8c8+0+FJG5qzkOEyitMKE0AEvESqm/MgbHmEWY9A7nk ml96osejB+AFPVmHo6bC0EqlCzk9VPcXXyfo+zZtp8V0gICpyF6rnUJ2YISsMTR0Si3Omu zH4hVidO1HMSiGHeAyY6nhHoK6RF3VOHphjbQbYhHnC1gBvvjQ5adqmrAPc/du9Dw0ZNpl z9IzKkgANZ3tXDKvqFSYalLALjxMtL18rPkgdBBDQ8wk/0fpQch0gBkIJc54Fw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1773963153; a=rsa-sha256; cv=none; b=a6hVBZ9jiuhpsW+sIk++8BV0eLFWJmo6dueGgHwyJZ72+riYp1rkH+n4Dsf7s6AEst3AtS nb+bA/EFh+UAzzJbXhJWfSoPm9IvnAOMrm/mohyeXACE+0Qqa0MckarQ9QbEesCyEcKAfx NZFAEfSNg1BNBQhOjNv86GVbe4gKad1/2fDwcOwdK59TgmLYrSkohk6EOOg9qnFKbS69BM TB7mYu5QYe1VSn8X0TkPcJlztUSWO23Go2quZz9NUP8xGksbDhIc88V1KOBtuDoKz9hx84 GyHNM9AqIwKq9spIsxeKAYwPoG8ohp+r1sMxEr1rWmtgShMA6UdjCDW9M0G+BQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1773963153; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=IFInHHYRo+aym3uGQAwQs8467lxmsrwVRzniwQSe7bI=; b=kVhelM45Nr9QCNam4ZEZE9f1Rw3PPWHkf+Obj7sAj4cjT3ZYe+Qd7V09xuu0Ki3ShMuWyz wak8QoHL3SQvkQrthXV06DBOZNzFvhPMfa5GEnz21SNjfaJvrXOmXdo89D9XgWhCMraBmC fM1JKDX4zmEpyYQFSE4hhPNleIHTtAh//z4z7oVhCshAoYKuaoc867d+++RtROBQu3Ib5Q 649bs26mBWpHN6TCOBp+NR3vQuxUoVAo/bcozON5Sxd/dQhiGVbXRulFnvCoKF9oEobNRc m9+oPbro2CLmGAbNHFGTll6QRoe3Xn+Nzha8HhZF7oWKwIhJlM3NBi2dttqynQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4fcMS15z09z1FZK for ; Thu, 19 Mar 2026 23:32:33 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 33154 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Thu, 19 Mar 2026 23:32:33 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Bjoern A. Zeeb Subject: git: 187d8a3ce55a - main - ath11k: update Atheros/QCA's ath11k driver List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: bz X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 187d8a3ce55a4e2d41fbe61465d5ff4ac0fc6bd5 Auto-Submitted: auto-generated Date: Thu, 19 Mar 2026 23:32:33 +0000 Message-Id: <69bc8791.33154.13c98230@gitrepo.freebsd.org> The branch main has been updated by bz: URL: https://cgit.FreeBSD.org/src/commit/?id=187d8a3ce55a4e2d41fbe61465d5ff4ac0fc6bd5 commit 187d8a3ce55a4e2d41fbe61465d5ff4ac0fc6bd5 Merge: 80aae8a3f8aa 4c115eaec2fd Author: Bjoern A. Zeeb AuthorDate: 2026-03-19 23:30:11 +0000 Commit: Bjoern A. Zeeb CommitDate: 2026-03-19 23:30:37 +0000 ath11k: update Atheros/QCA's ath11k driver This version is based on git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 24d479d26b25bce5faea3ddd9fa8f3a6c3129ea7 ( tag: v6.19-rc6 ). Sponsored by: The FreeBSD Foundation MFC after: 3 days sys/contrib/dev/athk/ath11k/hal.h | 38 ++-- sys/contrib/dev/athk/ath11k/mac.c | 455 ++++++++++++++++++++++++++------------ sys/contrib/dev/athk/ath11k/pci.c | 20 +- sys/contrib/dev/athk/ath11k/pci.h | 18 +- sys/contrib/dev/athk/ath11k/qmi.c | 2 +- sys/contrib/dev/athk/ath11k/wmi.c | 20 +- sys/contrib/dev/athk/ath11k/wmi.h | 18 +- 7 files changed, 380 insertions(+), 191 deletions(-) diff --cc sys/contrib/dev/athk/ath11k/pci.c index 5db3a5a2ff64,000000000000..978d40e54bf8 mode 100644,000000..100644 --- a/sys/contrib/dev/athk/ath11k/pci.c +++ b/sys/contrib/dev/athk/ath11k/pci.c @@@ -1,1324 -1,0 +1,1342 @@@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. ++ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#if defined(CONFIG_OF) +#include +#endif +#if defined(__FreeBSD__) +#include +#include +#endif +#include +#include + +#include "pci.h" +#include "core.h" +#include "hif.h" +#include "mhi.h" +#include "debug.h" +#include "pcic.h" +#include "qmi.h" + +#define ATH11K_PCI_BAR_NUM 0 +#define ATH11K_PCI_DMA_MASK 36 +#define ATH11K_PCI_COHERENT_DMA_MASK 32 + +#define TCSR_SOC_HW_VERSION 0x0224 +#define TCSR_SOC_HW_VERSION_MAJOR_MASK GENMASK(11, 8) +#define TCSR_SOC_HW_VERSION_MINOR_MASK GENMASK(7, 0) + +#define QCA6390_DEVICE_ID 0x1101 +#define QCN9074_DEVICE_ID 0x1104 +#define WCN6855_DEVICE_ID 0x1103 + +#define TCSR_SOC_HW_SUB_VER 0x1910010 + +static const struct pci_device_id ath11k_pci_id_table[] = { + { PCI_VDEVICE(QCOM, QCA6390_DEVICE_ID) }, + { PCI_VDEVICE(QCOM, WCN6855_DEVICE_ID) }, + { PCI_VDEVICE(QCOM, QCN9074_DEVICE_ID) }, + {} +}; + +MODULE_DEVICE_TABLE(pci, ath11k_pci_id_table); + +static int ath11k_pci_bus_wake_up(struct ath11k_base *ab) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + + return mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); +} + +static void ath11k_pci_bus_release(struct ath11k_base *ab) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + + mhi_device_put(ab_pci->mhi_ctrl->mhi_dev); +} + +static u32 ath11k_pci_get_window_start(struct ath11k_base *ab, u32 offset) +{ + if (!ab->hw_params.static_window_map) + return ATH11K_PCI_WINDOW_START; + + if ((offset ^ HAL_SEQ_WCSS_UMAC_OFFSET) < ATH11K_PCI_WINDOW_RANGE_MASK) + /* if offset lies within DP register range, use 3rd window */ + return 3 * ATH11K_PCI_WINDOW_START; + else if ((offset ^ HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab)) < + ATH11K_PCI_WINDOW_RANGE_MASK) + /* if offset lies within CE register range, use 2nd window */ + return 2 * ATH11K_PCI_WINDOW_START; + else + return ATH11K_PCI_WINDOW_START; +} + +static inline void ath11k_pci_select_window(struct ath11k_pci *ab_pci, u32 offset) +{ + struct ath11k_base *ab = ab_pci->ab; + + u32 window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, offset); + + lockdep_assert_held(&ab_pci->window_lock); + + if (window != ab_pci->register_window) { +#if defined(__linux__) + iowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | window, + ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); + ioread32(ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); +#elif defined(__FreeBSD__) + iowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | window, + (char *)ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); + ioread32((char *)ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); +#endif + ab_pci->register_window = window; + } +} + +static void +ath11k_pci_window_write32(struct ath11k_base *ab, u32 offset, u32 value) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + u32 window_start; + + window_start = ath11k_pci_get_window_start(ab, offset); + + if (window_start == ATH11K_PCI_WINDOW_START) { + spin_lock_bh(&ab_pci->window_lock); + ath11k_pci_select_window(ab_pci, offset); +#if defined(__linux__) + iowrite32(value, ab->mem + window_start + +#elif defined(__FreeBSD__) + iowrite32(value, (char *)ab->mem + window_start + +#endif + (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); + spin_unlock_bh(&ab_pci->window_lock); + } else { +#if defined(__linux__) + iowrite32(value, ab->mem + window_start + +#elif defined(__FreeBSD__) + iowrite32(value, (char *)ab->mem + window_start + +#endif + (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); + } +} + +static u32 ath11k_pci_window_read32(struct ath11k_base *ab, u32 offset) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + u32 window_start, val; + + window_start = ath11k_pci_get_window_start(ab, offset); + + if (window_start == ATH11K_PCI_WINDOW_START) { + spin_lock_bh(&ab_pci->window_lock); + ath11k_pci_select_window(ab_pci, offset); +#if defined(__linux__) + val = ioread32(ab->mem + window_start + +#elif defined(__FreeBSD__) + val = ioread32((char *)ab->mem + window_start + +#endif + (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); + spin_unlock_bh(&ab_pci->window_lock); + } else { +#if defined(__linux__) + val = ioread32(ab->mem + window_start + +#elif defined(__FreeBSD__) + val = ioread32((char *)ab->mem + window_start + +#endif + (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); + } + + return val; +} + +int ath11k_pci_get_msi_irq(struct ath11k_base *ab, unsigned int vector) +{ + struct pci_dev *pci_dev = to_pci_dev(ab->dev); + + return pci_irq_vector(pci_dev, vector); +} + +static const struct ath11k_pci_ops ath11k_pci_ops_qca6390 = { + .wakeup = ath11k_pci_bus_wake_up, + .release = ath11k_pci_bus_release, + .get_msi_irq = ath11k_pci_get_msi_irq, + .window_write32 = ath11k_pci_window_write32, + .window_read32 = ath11k_pci_window_read32, +}; + +static const struct ath11k_pci_ops ath11k_pci_ops_qcn9074 = { + .wakeup = NULL, + .release = NULL, + .get_msi_irq = ath11k_pci_get_msi_irq, + .window_write32 = ath11k_pci_window_write32, + .window_read32 = ath11k_pci_window_read32, +}; + +static const struct ath11k_msi_config msi_config_one_msi = { + .total_vectors = 1, + .total_users = 4, + .users = (struct ath11k_msi_user[]) { + { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, + { .name = "CE", .num_vectors = 1, .base_vector = 0 }, + { .name = "WAKE", .num_vectors = 1, .base_vector = 0 }, + { .name = "DP", .num_vectors = 1, .base_vector = 0 }, + }, +}; + +static inline void ath11k_pci_select_static_window(struct ath11k_pci *ab_pci) +{ + u32 umac_window; + u32 ce_window; + u32 window; + + umac_window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, HAL_SEQ_WCSS_UMAC_OFFSET); + ce_window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, HAL_CE_WFSS_CE_REG_BASE); + window = (umac_window << 12) | (ce_window << 6); + + iowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | window, +#if defined(__linux__) + ab_pci->ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); +#elif defined(__FreeBSD__) + (char *)ab_pci->ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); +#endif +} + ++static void ath11k_pci_restore_window(struct ath11k_base *ab) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ++ ++ spin_lock_bh(&ab_pci->window_lock); ++ ++ iowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | ab_pci->register_window, ++ ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); ++ ioread32(ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); ++ ++ spin_unlock_bh(&ab_pci->window_lock); ++} ++ +static void ath11k_pci_soc_global_reset(struct ath11k_base *ab) +{ + u32 val, delay; + + val = ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET); + + val |= PCIE_SOC_GLOBAL_RESET_V; + + ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + + /* TODO: exact time to sleep is uncertain */ + delay = 10; + mdelay(delay); + + /* Need to toggle V bit back otherwise stuck in reset status */ + val &= ~PCIE_SOC_GLOBAL_RESET_V; + + ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + + mdelay(delay); + + val = ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET); + if (val == 0xffffffff) + ath11k_warn(ab, "link down error during global reset\n"); ++ ++ /* Restore window register as its content is cleared during ++ * hardware global reset, such that it aligns with host cache. ++ */ ++ ath11k_pci_restore_window(ab); +} + +static void ath11k_pci_clear_dbg_registers(struct ath11k_base *ab) +{ + u32 val; + + /* read cookie */ + val = ath11k_pcic_read32(ab, PCIE_Q6_COOKIE_ADDR); + ath11k_dbg(ab, ATH11K_DBG_PCI, "pcie_q6_cookie_addr 0x%x\n", val); + + val = ath11k_pcic_read32(ab, WLAON_WARM_SW_ENTRY); + ath11k_dbg(ab, ATH11K_DBG_PCI, "wlaon_warm_sw_entry 0x%x\n", val); + + /* TODO: exact time to sleep is uncertain */ + mdelay(10); + + /* write 0 to WLAON_WARM_SW_ENTRY to prevent Q6 from + * continuing warm path and entering dead loop. + */ + ath11k_pcic_write32(ab, WLAON_WARM_SW_ENTRY, 0); + mdelay(10); + + val = ath11k_pcic_read32(ab, WLAON_WARM_SW_ENTRY); + ath11k_dbg(ab, ATH11K_DBG_PCI, "wlaon_warm_sw_entry 0x%x\n", val); + + /* A read clear register. clear the register to prevent + * Q6 from entering wrong code path. + */ + val = ath11k_pcic_read32(ab, WLAON_SOC_RESET_CAUSE_REG); + ath11k_dbg(ab, ATH11K_DBG_PCI, "soc reset cause %d\n", val); +} + +static int ath11k_pci_set_link_reg(struct ath11k_base *ab, + u32 offset, u32 value, u32 mask) +{ + u32 v; + int i; + + v = ath11k_pcic_read32(ab, offset); + if ((v & mask) == value) + return 0; + + for (i = 0; i < 10; i++) { + ath11k_pcic_write32(ab, offset, (v & ~mask) | value); + + v = ath11k_pcic_read32(ab, offset); + if ((v & mask) == value) + return 0; + + mdelay(2); + } + + ath11k_warn(ab, "failed to set pcie link register 0x%08x: 0x%08x != 0x%08x\n", + offset, v & mask, value); + + return -ETIMEDOUT; +} + +static int ath11k_pci_fix_l1ss(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_pci_set_link_reg(ab, + PCIE_QSERDES_COM_SYSCLK_EN_SEL_REG(ab), + PCIE_QSERDES_COM_SYSCLK_EN_SEL_VAL, + PCIE_QSERDES_COM_SYSCLK_EN_SEL_MSK); + if (ret) { + ath11k_warn(ab, "failed to set sysclk: %d\n", ret); + return ret; + } + + ret = ath11k_pci_set_link_reg(ab, + PCIE_PCS_OSC_DTCT_CONFIG1_REG(ab), + PCIE_PCS_OSC_DTCT_CONFIG1_VAL, + PCIE_PCS_OSC_DTCT_CONFIG_MSK); + if (ret) { + ath11k_warn(ab, "failed to set dtct config1 error: %d\n", ret); + return ret; + } + + ret = ath11k_pci_set_link_reg(ab, + PCIE_PCS_OSC_DTCT_CONFIG2_REG(ab), + PCIE_PCS_OSC_DTCT_CONFIG2_VAL, + PCIE_PCS_OSC_DTCT_CONFIG_MSK); + if (ret) { + ath11k_warn(ab, "failed to set dtct config2: %d\n", ret); + return ret; + } + + ret = ath11k_pci_set_link_reg(ab, + PCIE_PCS_OSC_DTCT_CONFIG4_REG(ab), + PCIE_PCS_OSC_DTCT_CONFIG4_VAL, + PCIE_PCS_OSC_DTCT_CONFIG_MSK); + if (ret) { + ath11k_warn(ab, "failed to set dtct config4: %d\n", ret); + return ret; + } + + return 0; +} + +static void ath11k_pci_enable_ltssm(struct ath11k_base *ab) +{ + u32 val; + int i; + + val = ath11k_pcic_read32(ab, PCIE_PCIE_PARF_LTSSM); + + /* PCIE link seems very unstable after the Hot Reset*/ + for (i = 0; val != PARM_LTSSM_VALUE && i < 5; i++) { + if (val == 0xffffffff) + mdelay(5); + + ath11k_pcic_write32(ab, PCIE_PCIE_PARF_LTSSM, PARM_LTSSM_VALUE); + val = ath11k_pcic_read32(ab, PCIE_PCIE_PARF_LTSSM); + } + + ath11k_dbg(ab, ATH11K_DBG_PCI, "ltssm 0x%x\n", val); + + val = ath11k_pcic_read32(ab, GCC_GCC_PCIE_HOT_RST); + val |= GCC_GCC_PCIE_HOT_RST_VAL; + ath11k_pcic_write32(ab, GCC_GCC_PCIE_HOT_RST, val); + val = ath11k_pcic_read32(ab, GCC_GCC_PCIE_HOT_RST); + + ath11k_dbg(ab, ATH11K_DBG_PCI, "pcie_hot_rst 0x%x\n", val); + + mdelay(5); +} + +static void ath11k_pci_clear_all_intrs(struct ath11k_base *ab) +{ + /* This is a WAR for PCIE Hotreset. + * When target receive Hotreset, but will set the interrupt. + * So when download SBL again, SBL will open Interrupt and + * receive it, and crash immediately. + */ + ath11k_pcic_write32(ab, PCIE_PCIE_INT_ALL_CLEAR, PCIE_INT_CLEAR_ALL); +} + +static void ath11k_pci_set_wlaon_pwr_ctrl(struct ath11k_base *ab) +{ + u32 val; + + val = ath11k_pcic_read32(ab, WLAON_QFPROM_PWR_CTRL_REG); + val &= ~QFPROM_PWR_CTRL_VDD4BLOW_MASK; + ath11k_pcic_write32(ab, WLAON_QFPROM_PWR_CTRL_REG, val); +} + +static void ath11k_pci_force_wake(struct ath11k_base *ab) +{ + ath11k_pcic_write32(ab, PCIE_SOC_WAKE_PCIE_LOCAL_REG, 1); + mdelay(5); +} + +static void ath11k_pci_sw_reset(struct ath11k_base *ab, bool power_on) +{ + mdelay(100); + + if (power_on) { + ath11k_pci_enable_ltssm(ab); + ath11k_pci_clear_all_intrs(ab); + ath11k_pci_set_wlaon_pwr_ctrl(ab); + if (ab->hw_params.fix_l1ss) + ath11k_pci_fix_l1ss(ab); + } + + ath11k_mhi_clear_vector(ab); + ath11k_pci_clear_dbg_registers(ab); + ath11k_pci_soc_global_reset(ab); + ath11k_mhi_set_mhictrl_reset(ab); +} + +static void ath11k_pci_init_qmi_ce_config(struct ath11k_base *ab) +{ + struct ath11k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg; + + cfg->tgt_ce = ab->hw_params.target_ce_config; + cfg->tgt_ce_len = ab->hw_params.target_ce_count; + + cfg->svc_to_ce_map = ab->hw_params.svc_to_ce_map; + cfg->svc_to_ce_map_len = ab->hw_params.svc_to_ce_map_len; + ab->qmi.service_ins_id = ab->hw_params.qmi_service_ins_id; + + ath11k_ce_get_shadow_config(ab, &cfg->shadow_reg_v2, + &cfg->shadow_reg_v2_len); +} + +static void ath11k_pci_msi_config(struct ath11k_pci *ab_pci, bool enable) +{ + struct pci_dev *dev = ab_pci->pdev; + u16 control; + + pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); + + if (enable) + control |= PCI_MSI_FLAGS_ENABLE; + else + control &= ~PCI_MSI_FLAGS_ENABLE; + + pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); +} + +static void ath11k_pci_msi_enable(struct ath11k_pci *ab_pci) +{ + ath11k_pci_msi_config(ab_pci, true); +} + +static void ath11k_pci_msi_disable(struct ath11k_pci *ab_pci) +{ + ath11k_pci_msi_config(ab_pci, false); +} + +static int ath11k_pci_alloc_msi(struct ath11k_pci *ab_pci) +{ + struct ath11k_base *ab = ab_pci->ab; + const struct ath11k_msi_config *msi_config = ab->pci.msi.config; + struct pci_dev *pci_dev = ab_pci->pdev; + struct msi_desc *msi_desc; + int num_vectors; + int ret; + + num_vectors = pci_alloc_irq_vectors(pci_dev, + msi_config->total_vectors, + msi_config->total_vectors, + PCI_IRQ_MSI); + if (num_vectors == msi_config->total_vectors) { + set_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags); + } else { + num_vectors = pci_alloc_irq_vectors(ab_pci->pdev, + 1, + 1, + PCI_IRQ_MSI); + if (num_vectors < 0) { + ret = -EINVAL; + goto reset_msi_config; + } + clear_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags); + ab->pci.msi.config = &msi_config_one_msi; + ath11k_dbg(ab, ATH11K_DBG_PCI, "request one msi vector\n"); + } + ath11k_info(ab, "MSI vectors: %d\n", num_vectors); + + ath11k_pci_msi_disable(ab_pci); + + msi_desc = irq_get_msi_desc(ab_pci->pdev->irq); + if (!msi_desc) { + ath11k_err(ab, "msi_desc is NULL!\n"); + ret = -EINVAL; + goto free_msi_vector; + } + + ab->pci.msi.ep_base_data = msi_desc->msg.data; + + pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_LO, + &ab->pci.msi.addr_lo); + + if (msi_desc->pci.msi_attrib.is_64) { + pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_HI, + &ab->pci.msi.addr_hi); + } else { + ab->pci.msi.addr_hi = 0; + } + + ath11k_dbg(ab, ATH11K_DBG_PCI, "msi base data is %d\n", ab->pci.msi.ep_base_data); + + return 0; + +free_msi_vector: + pci_free_irq_vectors(ab_pci->pdev); + +reset_msi_config: + return ret; +} + +static void ath11k_pci_free_msi(struct ath11k_pci *ab_pci) +{ + pci_free_irq_vectors(ab_pci->pdev); +} + +static int ath11k_pci_config_msi_data(struct ath11k_pci *ab_pci) +{ + struct msi_desc *msi_desc; + + msi_desc = irq_get_msi_desc(ab_pci->pdev->irq); + if (!msi_desc) { + ath11k_err(ab_pci->ab, "msi_desc is NULL!\n"); + pci_free_irq_vectors(ab_pci->pdev); + return -EINVAL; + } + + ab_pci->ab->pci.msi.ep_base_data = msi_desc->msg.data; + + ath11k_dbg(ab_pci->ab, ATH11K_DBG_PCI, "after request_irq msi_ep_base_data %d\n", + ab_pci->ab->pci.msi.ep_base_data); + + return 0; +} + +static int ath11k_pci_claim(struct ath11k_pci *ab_pci, struct pci_dev *pdev) +{ + struct ath11k_base *ab = ab_pci->ab; + u16 device_id; + int ret = 0; + + pci_read_config_word(pdev, PCI_DEVICE_ID, &device_id); + if (device_id != ab_pci->dev_id) { + ath11k_err(ab, "pci device id mismatch: 0x%x 0x%x\n", + device_id, ab_pci->dev_id); + ret = -EIO; + goto out; + } + + ret = pci_assign_resource(pdev, ATH11K_PCI_BAR_NUM); + if (ret) { + ath11k_err(ab, "failed to assign pci resource: %d\n", ret); + goto out; + } + + ret = pci_enable_device(pdev); + if (ret) { + ath11k_err(ab, "failed to enable pci device: %d\n", ret); + goto out; + } + + ret = pci_request_region(pdev, ATH11K_PCI_BAR_NUM, "ath11k_pci"); + if (ret) { + ath11k_err(ab, "failed to request pci region: %d\n", ret); + goto disable_device; + } + + ret = dma_set_mask(&pdev->dev, + DMA_BIT_MASK(ATH11K_PCI_DMA_MASK)); + if (ret) { + ath11k_err(ab, "failed to set pci dma mask to %d: %d\n", + ATH11K_PCI_DMA_MASK, ret); + goto release_region; + } + + ab_pci->dma_mask = DMA_BIT_MASK(ATH11K_PCI_DMA_MASK); + + ret = dma_set_coherent_mask(&pdev->dev, + DMA_BIT_MASK(ATH11K_PCI_COHERENT_DMA_MASK)); + if (ret) { + ath11k_err(ab, "failed to set pci coherent dma mask to %d: %d\n", + ATH11K_PCI_COHERENT_DMA_MASK, ret); + goto release_region; + } + + pci_set_master(pdev); + + ab->mem_len = pci_resource_len(pdev, ATH11K_PCI_BAR_NUM); + ab->mem = pci_iomap(pdev, ATH11K_PCI_BAR_NUM, 0); + if (!ab->mem) { + ath11k_err(ab, "failed to map pci bar %d\n", ATH11K_PCI_BAR_NUM); + ret = -EIO; + goto release_region; + } + + ab->mem_ce = ab->mem; + + ath11k_dbg(ab, ATH11K_DBG_BOOT, "pci_mem 0x%p\n", ab->mem); + return 0; + +release_region: + pci_release_region(pdev, ATH11K_PCI_BAR_NUM); +disable_device: + pci_disable_device(pdev); +out: + return ret; +} + +static void ath11k_pci_free_region(struct ath11k_pci *ab_pci) +{ + struct ath11k_base *ab = ab_pci->ab; + struct pci_dev *pci_dev = ab_pci->pdev; + + pci_iounmap(pci_dev, ab->mem); + ab->mem = NULL; + pci_release_region(pci_dev, ATH11K_PCI_BAR_NUM); + if (pci_is_enabled(pci_dev)) + pci_disable_device(pci_dev); +} + +static void ath11k_pci_aspm_disable(struct ath11k_pci *ab_pci) +{ + struct ath11k_base *ab = ab_pci->ab; + + pcie_capability_read_word(ab_pci->pdev, PCI_EXP_LNKCTL, + &ab_pci->link_ctl); + + ath11k_dbg(ab, ATH11K_DBG_PCI, "link_ctl 0x%04x L0s %d L1 %d\n", + ab_pci->link_ctl, + u16_get_bits(ab_pci->link_ctl, PCI_EXP_LNKCTL_ASPM_L0S), + u16_get_bits(ab_pci->link_ctl, PCI_EXP_LNKCTL_ASPM_L1)); + + /* disable L0s and L1 */ + pcie_capability_clear_word(ab_pci->pdev, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPMC); + + set_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags); +} + +static void ath11k_pci_aspm_restore(struct ath11k_pci *ab_pci) +{ + if (test_and_clear_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags)) + pcie_capability_clear_and_set_word(ab_pci->pdev, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPMC, + ab_pci->link_ctl & + PCI_EXP_LNKCTL_ASPMC); +} + +#ifdef CONFIG_DEV_COREDUMP +static int ath11k_pci_coredump_calculate_size(struct ath11k_base *ab, u32 *dump_seg_sz) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl; + struct image_info *rddm_img, *fw_img; + struct ath11k_tlv_dump_data *dump_tlv; + enum ath11k_fw_crash_dump_type mem_type; + u32 len = 0, rddm_tlv_sz = 0, paging_tlv_sz = 0; + struct ath11k_dump_file_data *file_data; + int i; + + rddm_img = mhi_ctrl->rddm_image; + if (!rddm_img) { + ath11k_err(ab, "No RDDM dump found\n"); + return 0; + } + + fw_img = mhi_ctrl->fbc_image; + + for (i = 0; i < fw_img->entries ; i++) { + if (!fw_img->mhi_buf[i].buf) + continue; + + paging_tlv_sz += fw_img->mhi_buf[i].len; + } + dump_seg_sz[FW_CRASH_DUMP_PAGING_DATA] = paging_tlv_sz; + + for (i = 0; i < rddm_img->entries; i++) { + if (!rddm_img->mhi_buf[i].buf) + continue; + + rddm_tlv_sz += rddm_img->mhi_buf[i].len; + } + dump_seg_sz[FW_CRASH_DUMP_RDDM_DATA] = rddm_tlv_sz; + + for (i = 0; i < ab->qmi.mem_seg_count; i++) { + mem_type = ath11k_coredump_get_dump_type(ab->qmi.target_mem[i].type); + + if (mem_type == FW_CRASH_DUMP_NONE) + continue; + + if (mem_type == FW_CRASH_DUMP_TYPE_MAX) { + ath11k_dbg(ab, ATH11K_DBG_PCI, + "target mem region type %d not supported", + ab->qmi.target_mem[i].type); + continue; + } + + if (!ab->qmi.target_mem[i].anyaddr) + continue; + + dump_seg_sz[mem_type] += ab->qmi.target_mem[i].size; + } + + for (i = 0; i < FW_CRASH_DUMP_TYPE_MAX; i++) { + if (!dump_seg_sz[i]) + continue; + + len += sizeof(*dump_tlv) + dump_seg_sz[i]; + } + + if (len) + len += sizeof(*file_data); + + return len; +} + +static void ath11k_pci_coredump_download(struct ath11k_base *ab) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl; + struct image_info *rddm_img, *fw_img; + struct timespec64 timestamp; + int i, len, mem_idx; + enum ath11k_fw_crash_dump_type mem_type; + struct ath11k_dump_file_data *file_data; + struct ath11k_tlv_dump_data *dump_tlv; + size_t hdr_len = sizeof(*file_data); + void *buf; + u32 dump_seg_sz[FW_CRASH_DUMP_TYPE_MAX] = {}; + + ath11k_mhi_coredump(mhi_ctrl, false); + + len = ath11k_pci_coredump_calculate_size(ab, dump_seg_sz); + if (!len) { + ath11k_warn(ab, "No crash dump data found for devcoredump"); + return; + } + + rddm_img = mhi_ctrl->rddm_image; + fw_img = mhi_ctrl->fbc_image; + + /* dev_coredumpv() requires vmalloc data */ + buf = vzalloc(len); + if (!buf) + return; + + ab->dump_data = buf; + ab->ath11k_coredump_len = len; + file_data = ab->dump_data; + strscpy(file_data->df_magic, "ATH11K-FW-DUMP", sizeof(file_data->df_magic)); + file_data->len = cpu_to_le32(len); + file_data->version = cpu_to_le32(ATH11K_FW_CRASH_DUMP_V2); + file_data->chip_id = cpu_to_le32(ab_pci->dev_id); + file_data->qrtr_id = cpu_to_le32(ab_pci->ab->qmi.service_ins_id); + file_data->bus_id = cpu_to_le32(pci_domain_nr(ab_pci->pdev->bus)); + guid_gen(&file_data->guid); + ktime_get_real_ts64(×tamp); + file_data->tv_sec = cpu_to_le64(timestamp.tv_sec); + file_data->tv_nsec = cpu_to_le64(timestamp.tv_nsec); + buf += hdr_len; + dump_tlv = buf; + dump_tlv->type = cpu_to_le32(FW_CRASH_DUMP_PAGING_DATA); + dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[FW_CRASH_DUMP_PAGING_DATA]); + buf += COREDUMP_TLV_HDR_SIZE; + + /* append all segments together as they are all part of a single contiguous + * block of memory + */ + for (i = 0; i < fw_img->entries ; i++) { + if (!fw_img->mhi_buf[i].buf) + continue; + + memcpy_fromio(buf, (void const __iomem *)fw_img->mhi_buf[i].buf, + fw_img->mhi_buf[i].len); + buf += fw_img->mhi_buf[i].len; + } + + dump_tlv = buf; + dump_tlv->type = cpu_to_le32(FW_CRASH_DUMP_RDDM_DATA); + dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[FW_CRASH_DUMP_RDDM_DATA]); + buf += COREDUMP_TLV_HDR_SIZE; + + /* append all segments together as they are all part of a single contiguous + * block of memory + */ + for (i = 0; i < rddm_img->entries; i++) { + if (!rddm_img->mhi_buf[i].buf) + continue; + + memcpy_fromio(buf, (void const __iomem *)rddm_img->mhi_buf[i].buf, + rddm_img->mhi_buf[i].len); + buf += rddm_img->mhi_buf[i].len; + } + + mem_idx = FW_CRASH_DUMP_REMOTE_MEM_DATA; + for (; mem_idx < FW_CRASH_DUMP_TYPE_MAX; mem_idx++) { + if (mem_idx == FW_CRASH_DUMP_NONE) + continue; + + for (i = 0; i < ab->qmi.mem_seg_count; i++) { + mem_type = ath11k_coredump_get_dump_type + (ab->qmi.target_mem[i].type); + + if (mem_type != mem_idx) + continue; + + if (!ab->qmi.target_mem[i].anyaddr) { + ath11k_dbg(ab, ATH11K_DBG_PCI, + "Skipping mem region type %d", + ab->qmi.target_mem[i].type); + continue; + } + + dump_tlv = buf; + dump_tlv->type = cpu_to_le32(mem_idx); + dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[mem_idx]); + buf += COREDUMP_TLV_HDR_SIZE; + + memcpy_fromio(buf, ab->qmi.target_mem[i].iaddr, + ab->qmi.target_mem[i].size); + + buf += ab->qmi.target_mem[i].size; + } + } + + queue_work(ab->workqueue, &ab->dump_work); +} +#endif + +static int ath11k_pci_power_up(struct ath11k_base *ab) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + int ret; + + ab_pci->register_window = 0; + clear_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags); + ath11k_pci_sw_reset(ab_pci->ab, true); + + /* Disable ASPM during firmware download due to problems switching + * to AMSS state. + */ + ath11k_pci_aspm_disable(ab_pci); + + ath11k_pci_msi_enable(ab_pci); + + ret = ath11k_mhi_start(ab_pci); + if (ret) { + ath11k_err(ab, "failed to start mhi: %d\n", ret); + return ret; + } + + if (ab->hw_params.static_window_map) + ath11k_pci_select_static_window(ab_pci); + + return 0; +} + +static void ath11k_pci_power_down(struct ath11k_base *ab, bool is_suspend) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + + /* restore aspm in case firmware bootup fails */ + ath11k_pci_aspm_restore(ab_pci); + + ath11k_pci_force_wake(ab_pci->ab); + + ath11k_pci_msi_disable(ab_pci); + + ath11k_mhi_stop(ab_pci, is_suspend); + clear_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags); + ath11k_pci_sw_reset(ab_pci->ab, false); +} + +static int ath11k_pci_hif_suspend(struct ath11k_base *ab) +{ + struct ath11k_pci *ar_pci = ath11k_pci_priv(ab); + + return ath11k_mhi_suspend(ar_pci); +} + +static int ath11k_pci_hif_resume(struct ath11k_base *ab) +{ + struct ath11k_pci *ar_pci = ath11k_pci_priv(ab); + + return ath11k_mhi_resume(ar_pci); +} + +static void ath11k_pci_hif_ce_irq_enable(struct ath11k_base *ab) +{ + ath11k_pcic_ce_irqs_enable(ab); +} + +static void ath11k_pci_hif_ce_irq_disable(struct ath11k_base *ab) +{ + ath11k_pcic_ce_irq_disable_sync(ab); +} + +static int ath11k_pci_start(struct ath11k_base *ab) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + + /* TODO: for now don't restore ASPM in case of single MSI + * vector as MHI register reading in M2 causes system hang. + */ + if (test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) + ath11k_pci_aspm_restore(ab_pci); + else + ath11k_info(ab, "leaving PCI ASPM disabled to avoid MHI M2 problems\n"); + + ath11k_pcic_start(ab); + + return 0; +} + +static const struct ath11k_hif_ops ath11k_pci_hif_ops = { + .start = ath11k_pci_start, + .stop = ath11k_pcic_stop, + .read32 = ath11k_pcic_read32, + .write32 = ath11k_pcic_write32, + .read = ath11k_pcic_read, + .power_down = ath11k_pci_power_down, + .power_up = ath11k_pci_power_up, + .suspend = ath11k_pci_hif_suspend, + .resume = ath11k_pci_hif_resume, + .irq_enable = ath11k_pcic_ext_irq_enable, + .irq_disable = ath11k_pcic_ext_irq_disable, + .get_msi_address = ath11k_pcic_get_msi_address, + .get_user_msi_vector = ath11k_pcic_get_user_msi_assignment, + .map_service_to_pipe = ath11k_pcic_map_service_to_pipe, + .ce_irq_enable = ath11k_pci_hif_ce_irq_enable, + .ce_irq_disable = ath11k_pci_hif_ce_irq_disable, + .get_ce_msi_idx = ath11k_pcic_get_ce_msi_idx, +#ifdef CONFIG_DEV_COREDUMP + .coredump_download = ath11k_pci_coredump_download, +#endif *** 20652 LINES SKIPPED ***