2018-10-08 09:56:04

by Sergey Matyukevich

[permalink] [raw]
Subject: [PATCH 0/5] qtnfmac: add support for QSR1000/QSR2000 (aka Topaz) chipsets

Hello Kalle and all,

Here is the next update for qtnfmac driver. This patch set adds
support for the previous generation of Quantenna wireless cards,
namely QSR1000/QSR2000 family of devices.

The summary of changes is as follows:
- the last bits of pcie layer unification work:
-- extract remaining code that can be shared across various PCIe chips
-- cleanup/rename: get ready to support another chipset
- add support for QSR1000/QSR2000 devices
- minor cleanups: Kconfig, SPDX, unused register definitions

Regards,
Sergey

Kconfig | 2
qtnfmac/Kconfig | 31 -
qtnfmac/Makefile | 11
qtnfmac/pcie/pcie.c | 286 ++++++---
qtnfmac/pcie/pcie_priv.h | 20
qtnfmac/pcie/pearl_pcie.c | 228 ++-----
qtnfmac/pcie/pearl_pcie_ipc.h | 22
qtnfmac/pcie/pearl_pcie_regs.h | 245 --------
qtnfmac/pcie/topaz_pcie.c | 1219 +++++++++++++++++++++++++++++++++++++++++
qtnfmac/pcie/topaz_pcie_ipc.h | 94 +++
qtnfmac/pcie/topaz_pcie_regs.h | 45 +
qtnfmac/qtn_hw_ids.h | 3
qtnfmac/util.c | 18
qtnfmac/util.h | 2
14 files changed, 1697 insertions(+), 529 deletions(-)


2018-10-08 09:56:05

by Sergey Matyukevich

[permalink] [raw]
Subject: [PATCH 1/5] qtnfmac: use 'help' in Kconfig

Fix checkpatch warning: use preferred 'help' option in Kconfig.

Signed-off-by: Sergey Matyukevich <[email protected]>
---
drivers/net/wireless/quantenna/Kconfig | 2 +-
drivers/net/wireless/quantenna/qtnfmac/Kconfig | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/quantenna/Kconfig b/drivers/net/wireless/quantenna/Kconfig
index de84ce125c26..7628d9c1ea6a 100644
--- a/drivers/net/wireless/quantenna/Kconfig
+++ b/drivers/net/wireless/quantenna/Kconfig
@@ -1,7 +1,7 @@
config WLAN_VENDOR_QUANTENNA
bool "Quantenna wireless cards support"
default y
- ---help---
+ help
If you have a wireless card belonging to this class, say Y.

Note that the answer to this question doesn't directly affect the
diff --git a/drivers/net/wireless/quantenna/qtnfmac/Kconfig b/drivers/net/wireless/quantenna/qtnfmac/Kconfig
index 8d1492a90bd1..b8c12a5f16b4 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/Kconfig
+++ b/drivers/net/wireless/quantenna/qtnfmac/Kconfig
@@ -11,7 +11,7 @@ config QTNFMAC_PEARL_PCIE
select QTNFMAC
select FW_LOADER
select CRC32
- ---help---
+ help
This option adds support for wireless adapters based on Quantenna
802.11ac QSR10g (aka Pearl) FullMAC chipset running over PCIe.

--
2.11.0


2018-10-08 09:56:06

by Sergey Matyukevich

[permalink] [raw]
Subject: [PATCH 2/5] qtnfmac: use SPDX identifier for pcie bus layer files

Change pcie bus layer licensing information to SPDX format.

Signed-off-by: Sergey Matyukevich <[email protected]>
---
.../net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c | 17 ++---------------
.../wireless/quantenna/qtnfmac/pcie/pearl_pcie_ipc.h | 17 ++---------------
.../wireless/quantenna/qtnfmac/pcie/pearl_pcie_regs.h | 17 ++---------------
3 files changed, 6 insertions(+), 45 deletions(-)

diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
index 5aca12a51fe3..95c7b95c6f8a 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2018 Quantenna Communications */

#include <linux/kernel.h>
#include <linux/module.h>
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_ipc.h b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_ipc.h
index f21e97ede090..7ecaa69fe018 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_ipc.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_ipc.h
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2015-2016 Quantenna Communications */

#ifndef _QTN_FMAC_PCIE_IPC_H_
#define _QTN_FMAC_PCIE_IPC_H_
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_regs.h b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_regs.h
index 0bfe285b6b48..1e6693c17006 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_regs.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_regs.h
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2015 Quantenna Communications */

#ifndef __PEARL_PCIE_H
#define __PEARL_PCIE_H
--
2.11.0


2018-10-08 09:56:08

by Sergey Matyukevich

[permalink] [raw]
Subject: [PATCH 3/5] qtnfmac_pcie: cleanup Pearl platform headers

Remove redundant information from Pearl platform headers.

Signed-off-by: Sergey Matyukevich <[email protected]>
---
.../quantenna/qtnfmac/pcie/pearl_pcie_ipc.h | 5 -
.../quantenna/qtnfmac/pcie/pearl_pcie_regs.h | 228 +--------------------
2 files changed, 3 insertions(+), 230 deletions(-)

diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_ipc.h b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_ipc.h
index 7ecaa69fe018..634480fe6a64 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_ipc.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_ipc.h
@@ -72,11 +72,6 @@

#define QTN_EP_LHOST_TQE_PORT 4

-enum qtnf_pcie_bda_ipc_flags {
- QTN_PCIE_IPC_FLAG_HBM_MAGIC = BIT(0),
- QTN_PCIE_IPC_FLAG_SHM_PIO = BIT(1),
-};
-
enum qtnf_fw_loadtype {
QTN_FW_DBEGIN,
QTN_FW_DSUB,
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_regs.h b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_regs.h
index 1e6693c17006..6e9a5c61d46f 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_regs.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_regs.h
@@ -4,12 +4,7 @@
#ifndef __PEARL_PCIE_H
#define __PEARL_PCIE_H

-#define PCIE_GEN2_BASE (0xe9000000)
-#define PCIE_GEN3_BASE (0xe7000000)
-
-#define PEARL_CUR_PCIE_BASE (PCIE_GEN2_BASE)
-#define PCIE_HDP_OFFSET (0x2000)
-
+/* Pearl PCIe HDP registers */
#define PCIE_HDP_CTRL(base) ((base) + 0x2c00)
#define PCIE_HDP_AXI_CTRL(base) ((base) + 0x2c04)
#define PCIE_HDP_HOST_WR_DESC0(base) ((base) + 0x2c10)
@@ -73,7 +68,7 @@
#define PCIE_HDP_TX_HOST_Q_RD_PTR(base) ((base) + 0x2d3c)
#define PCIE_HDP_TX_HOST_Q_STS(base) ((base) + 0x2d40)

-/* Host HBM pool registers */
+/* Pearl PCIe HBM pool registers */
#define PCIE_HHBM_CSR_REG(base) ((base) + 0x2e00)
#define PCIE_HHBM_Q_BASE_REG(base) ((base) + 0x2e04)
#define PCIE_HHBM_Q_LIMIT_REG(base) ((base) + 0x2e08)
@@ -91,230 +86,13 @@
#define HBM_INT_STATUS(base) ((base) + 0x2f9c)
#define PCIE_HHBM_POOL_CNFIG(base) ((base) + 0x2f9c)

-/* host HBM bit field definition */
+/* Pearl PCIe HBM bit field definitions */
#define HHBM_CONFIG_SOFT_RESET (BIT(8))
#define HHBM_WR_REQ (BIT(0))
#define HHBM_RD_REQ (BIT(1))
#define HHBM_DONE (BIT(31))
#define HHBM_64BIT (BIT(10))

-/* offsets for dual PCIE */
-#define PCIE_PORT_LINK_CTL(base) ((base) + 0x0710)
-#define PCIE_GEN2_CTL(base) ((base) + 0x080C)
-#define PCIE_GEN3_OFF(base) ((base) + 0x0890)
-#define PCIE_ATU_CTRL1(base) ((base) + 0x0904)
-#define PCIE_ATU_CTRL2(base) ((base) + 0x0908)
-#define PCIE_ATU_BASE_LOW(base) ((base) + 0x090C)
-#define PCIE_ATU_BASE_HIGH(base) ((base) + 0x0910)
-#define PCIE_ATU_BASE_LIMIT(base) ((base) + 0x0914)
-#define PCIE_ATU_TGT_LOW(base) ((base) + 0x0918)
-#define PCIE_ATU_TGT_HIGH(base) ((base) + 0x091C)
-#define PCIE_DMA_WR_ENABLE(base) ((base) + 0x097C)
-#define PCIE_DMA_WR_CHWTLOW(base) ((base) + 0x0988)
-#define PCIE_DMA_WR_CHWTHIG(base) ((base) + 0x098C)
-#define PCIE_DMA_WR_INTSTS(base) ((base) + 0x09BC)
-#define PCIE_DMA_WR_INTMASK(base) ((base) + 0x09C4)
-#define PCIE_DMA_WR_INTCLER(base) ((base) + 0x09C8)
-#define PCIE_DMA_WR_DONE_IMWR_ADDR_L(base) ((base) + 0x09D0)
-#define PCIE_DMA_WR_DONE_IMWR_ADDR_H(base) ((base) + 0x09D4)
-#define PCIE_DMA_WR_ABORT_IMWR_ADDR_L(base) ((base) + 0x09D8)
-#define PCIE_DMA_WR_ABORT_IMWR_ADDR_H(base) ((base) + 0x09DC)
-#define PCIE_DMA_WR_IMWR_DATA(base) ((base) + 0x09E0)
-#define PCIE_DMA_WR_LL_ERR_EN(base) ((base) + 0x0A00)
-#define PCIE_DMA_WR_DOORBELL(base) ((base) + 0x0980)
-#define PCIE_DMA_RD_ENABLE(base) ((base) + 0x099C)
-#define PCIE_DMA_RD_DOORBELL(base) ((base) + 0x09A0)
-#define PCIE_DMA_RD_CHWTLOW(base) ((base) + 0x09A8)
-#define PCIE_DMA_RD_CHWTHIG(base) ((base) + 0x09AC)
-#define PCIE_DMA_RD_INTSTS(base) ((base) + 0x0A10)
-#define PCIE_DMA_RD_INTMASK(base) ((base) + 0x0A18)
-#define PCIE_DMA_RD_INTCLER(base) ((base) + 0x0A1C)
-#define PCIE_DMA_RD_ERR_STS_L(base) ((base) + 0x0A24)
-#define PCIE_DMA_RD_ERR_STS_H(base) ((base) + 0x0A28)
-#define PCIE_DMA_RD_LL_ERR_EN(base) ((base) + 0x0A34)
-#define PCIE_DMA_RD_DONE_IMWR_ADDR_L(base) ((base) + 0x0A3C)
-#define PCIE_DMA_RD_DONE_IMWR_ADDR_H(base) ((base) + 0x0A40)
-#define PCIE_DMA_RD_ABORT_IMWR_ADDR_L(base) ((base) + 0x0A44)
-#define PCIE_DMA_RD_ABORT_IMWR_ADDR_H(base) ((base) + 0x0A48)
-#define PCIE_DMA_RD_IMWR_DATA(base) ((base) + 0x0A4C)
-#define PCIE_DMA_CHNL_CONTEXT(base) ((base) + 0x0A6C)
-#define PCIE_DMA_CHNL_CNTRL(base) ((base) + 0x0A70)
-#define PCIE_DMA_XFR_SIZE(base) ((base) + 0x0A78)
-#define PCIE_DMA_SAR_LOW(base) ((base) + 0x0A7C)
-#define PCIE_DMA_SAR_HIGH(base) ((base) + 0x0A80)
-#define PCIE_DMA_DAR_LOW(base) ((base) + 0x0A84)
-#define PCIE_DMA_DAR_HIGH(base) ((base) + 0x0A88)
-#define PCIE_DMA_LLPTR_LOW(base) ((base) + 0x0A8C)
-#define PCIE_DMA_LLPTR_HIGH(base) ((base) + 0x0A90)
-#define PCIE_DMA_WRLL_ERR_ENB(base) ((base) + 0x0A00)
-#define PCIE_DMA_RDLL_ERR_ENB(base) ((base) + 0x0A34)
-#define PCIE_DMABD_CHNL_CNTRL(base) ((base) + 0x8000)
-#define PCIE_DMABD_XFR_SIZE(base) ((base) + 0x8004)
-#define PCIE_DMABD_SAR_LOW(base) ((base) + 0x8008)
-#define PCIE_DMABD_SAR_HIGH(base) ((base) + 0x800c)
-#define PCIE_DMABD_DAR_LOW(base) ((base) + 0x8010)
-#define PCIE_DMABD_DAR_HIGH(base) ((base) + 0x8014)
-#define PCIE_DMABD_LLPTR_LOW(base) ((base) + 0x8018)
-#define PCIE_DMABD_LLPTR_HIGH(base) ((base) + 0x801c)
-#define PCIE_WRDMA0_CHNL_CNTRL(base) ((base) + 0x8000)
-#define PCIE_WRDMA0_XFR_SIZE(base) ((base) + 0x8004)
-#define PCIE_WRDMA0_SAR_LOW(base) ((base) + 0x8008)
-#define PCIE_WRDMA0_SAR_HIGH(base) ((base) + 0x800c)
-#define PCIE_WRDMA0_DAR_LOW(base) ((base) + 0x8010)
-#define PCIE_WRDMA0_DAR_HIGH(base) ((base) + 0x8014)
-#define PCIE_WRDMA0_LLPTR_LOW(base) ((base) + 0x8018)
-#define PCIE_WRDMA0_LLPTR_HIGH(base) ((base) + 0x801c)
-#define PCIE_WRDMA1_CHNL_CNTRL(base) ((base) + 0x8020)
-#define PCIE_WRDMA1_XFR_SIZE(base) ((base) + 0x8024)
-#define PCIE_WRDMA1_SAR_LOW(base) ((base) + 0x8028)
-#define PCIE_WRDMA1_SAR_HIGH(base) ((base) + 0x802c)
-#define PCIE_WRDMA1_DAR_LOW(base) ((base) + 0x8030)
-#define PCIE_WRDMA1_DAR_HIGH(base) ((base) + 0x8034)
-#define PCIE_WRDMA1_LLPTR_LOW(base) ((base) + 0x8038)
-#define PCIE_WRDMA1_LLPTR_HIGH(base) ((base) + 0x803c)
-#define PCIE_RDDMA0_CHNL_CNTRL(base) ((base) + 0x8040)
-#define PCIE_RDDMA0_XFR_SIZE(base) ((base) + 0x8044)
-#define PCIE_RDDMA0_SAR_LOW(base) ((base) + 0x8048)
-#define PCIE_RDDMA0_SAR_HIGH(base) ((base) + 0x804c)
-#define PCIE_RDDMA0_DAR_LOW(base) ((base) + 0x8050)
-#define PCIE_RDDMA0_DAR_HIGH(base) ((base) + 0x8054)
-#define PCIE_RDDMA0_LLPTR_LOW(base) ((base) + 0x8058)
-#define PCIE_RDDMA0_LLPTR_HIGH(base) ((base) + 0x805c)
-#define PCIE_RDDMA1_CHNL_CNTRL(base) ((base) + 0x8060)
-#define PCIE_RDDMA1_XFR_SIZE(base) ((base) + 0x8064)
-#define PCIE_RDDMA1_SAR_LOW(base) ((base) + 0x8068)
-#define PCIE_RDDMA1_SAR_HIGH(base) ((base) + 0x806c)
-#define PCIE_RDDMA1_DAR_LOW(base) ((base) + 0x8070)
-#define PCIE_RDDMA1_DAR_HIGH(base) ((base) + 0x8074)
-#define PCIE_RDDMA1_LLPTR_LOW(base) ((base) + 0x8078)
-#define PCIE_RDDMA1_LLPTR_HIGH(base) ((base) + 0x807c)
-
-#define PCIE_ID(base) ((base) + 0x0000)
-#define PCIE_CMD(base) ((base) + 0x0004)
-#define PCIE_BAR(base, n) ((base) + 0x0010 + ((n) << 2))
-#define PCIE_CAP_PTR(base) ((base) + 0x0034)
-#define PCIE_MSI_LBAR(base) ((base) + 0x0054)
-#define PCIE_MSI_CTRL(base) ((base) + 0x0050)
-#define PCIE_MSI_ADDR_L(base) ((base) + 0x0054)
-#define PCIE_MSI_ADDR_H(base) ((base) + 0x0058)
-#define PCIE_MSI_DATA(base) ((base) + 0x005C)
-#define PCIE_MSI_MASK_BIT(base) ((base) + 0x0060)
-#define PCIE_MSI_PEND_BIT(base) ((base) + 0x0064)
-#define PCIE_DEVCAP(base) ((base) + 0x0074)
-#define PCIE_DEVCTLSTS(base) ((base) + 0x0078)
-
-#define PCIE_CMDSTS(base) ((base) + 0x0004)
-#define PCIE_LINK_STAT(base) ((base) + 0x80)
-#define PCIE_LINK_CTL2(base) ((base) + 0xa0)
-#define PCIE_ASPM_L1_CTRL(base) ((base) + 0x70c)
-#define PCIE_ASPM_LINK_CTRL(base) (PCIE_LINK_STAT)
-#define PCIE_ASPM_L1_SUBSTATE_TIMING(base) ((base) + 0xB44)
-#define PCIE_L1SUB_CTRL1(base) ((base) + 0x150)
-#define PCIE_PMCSR(base) ((base) + 0x44)
-#define PCIE_CFG_SPACE_LIMIT(base) ((base) + 0x100)
-
-/* PCIe link defines */
-#define PEARL_PCIE_LINKUP (0x7)
-#define PEARL_PCIE_DATA_LINK (BIT(0))
-#define PEARL_PCIE_PHY_LINK (BIT(1))
-#define PEARL_PCIE_LINK_RST (BIT(3))
-#define PEARL_PCIE_FATAL_ERR (BIT(5))
-#define PEARL_PCIE_NONFATAL_ERR (BIT(6))
-
-/* PCIe Lane defines */
-#define PCIE_G2_LANE_X1 ((BIT(0)) << 16)
-#define PCIE_G2_LANE_X2 ((BIT(0) | BIT(1)) << 16)
-
-/* PCIe DLL link enable */
-#define PCIE_DLL_LINK_EN ((BIT(0)) << 5)
-
-#define PCIE_LINK_GEN1 (BIT(0))
-#define PCIE_LINK_GEN2 (BIT(1))
-#define PCIE_LINK_GEN3 (BIT(2))
-#define PCIE_LINK_MODE(x) (((x) >> 16) & 0x7)
-
-#define MSI_EN (BIT(0))
-#define MSI_64_EN (BIT(7))
-#define PCIE_MSI_ADDR_OFFSET(a) ((a) & 0xFFFF)
-#define PCIE_MSI_ADDR_ALIGN(a) ((a) & (~0xFFFF))
-
-#define PCIE_BAR_MASK(base, n) ((base) + 0x1010 + ((n) << 2))
-#define PCIE_MAX_BAR (6)
-
-#define PCIE_ATU_VIEW(base) ((base) + 0x0900)
-#define PCIE_ATU_CTL1(base) ((base) + 0x0904)
-#define PCIE_ATU_CTL2(base) ((base) + 0x0908)
-#define PCIE_ATU_LBAR(base) ((base) + 0x090c)
-#define PCIE_ATU_UBAR(base) ((base) + 0x0910)
-#define PCIE_ATU_LAR(base) ((base) + 0x0914)
-#define PCIE_ATU_LTAR(base) ((base) + 0x0918)
-#define PCIE_ATU_UTAR(base) ((base) + 0x091c)
-
-#define PCIE_MSI_ADDR_LOWER(base) ((base) + 0x0820)
-#define PCIE_MSI_ADDR_UPPER(base) ((base) + 0x0824)
-#define PCIE_MSI_ENABLE(base) ((base) + 0x0828)
-#define PCIE_MSI_MASK_RC(base) ((base) + 0x082c)
-#define PCIE_MSI_STATUS(base) ((base) + 0x0830)
-#define PEARL_PCIE_MSI_REGION (0xce000000)
-#define PEARL_PCIE_MSI_DATA (0)
-#define PCIE_MSI_GPIO(base) ((base) + 0x0888)
-
-#define PCIE_HDP_HOST_QUEUE_FULL (BIT(17))
-#define USE_BAR_MATCH_MODE
-#define PCIE_ATU_OB_REGION (BIT(0))
-#define PCIE_ATU_EN_REGION (BIT(31))
-#define PCIE_ATU_EN_MATCH (BIT(30))
-#define PCIE_BASE_REGION (0xb0000000)
-#define PCIE_MEM_MAP_SIZE (512 * 1024)
-
-#define PCIE_OB_REG_REGION (0xcf000000)
-#define PCIE_CONFIG_REGION (0xcf000000)
-#define PCIE_CONFIG_SIZE (4096)
-#define PCIE_CONFIG_CH (1)
-
-/* inbound mapping */
-#define PCIE_IB_BAR0 (0x00000000) /* ddr */
-#define PCIE_IB_BAR0_CH (0)
-#define PCIE_IB_BAR3 (0xe0000000) /* sys_reg */
-#define PCIE_IB_BAR3_CH (1)
-
-/* outbound mapping */
-#define PCIE_MEM_CH (0)
-#define PCIE_REG_CH (1)
-#define PCIE_MEM_REGION (0xc0000000)
-#define PCIE_MEM_SIZE (0x000fffff)
-#define PCIE_MEM_TAR (0x80000000)
-
-#define PCIE_MSI_REGION (0xce000000)
-#define PCIE_MSI_SIZE (KBYTE(4) - 1)
-#define PCIE_MSI_CH (1)
-
-/* size of config region */
-#define PCIE_CFG_SIZE (0x0000ffff)
-
-#define PCIE_ATU_DIR_IB (BIT(31))
-#define PCIE_ATU_DIR_OB (0)
-#define PCIE_ATU_DIR_CFG (2)
-#define PCIE_ATU_DIR_MATCH_IB (BIT(31) | BIT(30))
-
-#define PCIE_DMA_WR_0 (0)
-#define PCIE_DMA_WR_1 (1)
-#define PCIE_DMA_RD_0 (2)
-#define PCIE_DMA_RD_1 (3)
-
-#define PCIE_DMA_CHNL_CNTRL_CB (BIT(0))
-#define PCIE_DMA_CHNL_CNTRL_TCB (BIT(1))
-#define PCIE_DMA_CHNL_CNTRL_LLP (BIT(2))
-#define PCIE_DMA_CHNL_CNTRL_LIE (BIT(3))
-#define PCIE_DMA_CHNL_CNTRL_RIE (BIT(4))
-#define PCIE_DMA_CHNL_CNTRL_CSS (BIT(8))
-#define PCIE_DMA_CHNL_CNTRL_LLE (BIT(9))
-#define PCIE_DMA_CHNL_CNTRL_TLP (BIT(26))
-
-#define PCIE_DMA_CHNL_CONTEXT_RD (BIT(31))
-#define PCIE_DMA_CHNL_CONTEXT_WR (0)
-#define PCIE_MAX_BAR (6)
-
/* PCIe HDP interrupt status definition */
#define PCIE_HDP_INT_EP_RXDMA (BIT(0))
#define PCIE_HDP_INT_HBM_UF (BIT(1))
--
2.11.0


2018-10-08 09:56:10

by Sergey Matyukevich

[permalink] [raw]
Subject: [PATCH 4/5] qtnfmac_pcie: use single PCIe driver for all platforms

From: Igor Mitsyanko <[email protected]>

Single PCIe driver can identify hardware type by reading CHIP ID at
probe time and invoking a correct initialization sequence.

Signed-off-by: Igor Mitsyanko <[email protected]>
---
drivers/net/wireless/quantenna/qtnfmac/Kconfig | 8 +-
drivers/net/wireless/quantenna/qtnfmac/Makefile | 10 +-
drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c | 281 +++++++++++++++------
.../wireless/quantenna/qtnfmac/pcie/pcie_priv.h | 19 +-
.../wireless/quantenna/qtnfmac/pcie/pearl_pcie.c | 211 +++++-----------
drivers/net/wireless/quantenna/qtnfmac/util.c | 16 ++
drivers/net/wireless/quantenna/qtnfmac/util.h | 2 +
7 files changed, 298 insertions(+), 249 deletions(-)

diff --git a/drivers/net/wireless/quantenna/qtnfmac/Kconfig b/drivers/net/wireless/quantenna/qtnfmac/Kconfig
index b8c12a5f16b4..f43b236b7c0b 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/Kconfig
+++ b/drivers/net/wireless/quantenna/qtnfmac/Kconfig
@@ -4,6 +4,12 @@ config QTNFMAC
default m if QTNFMAC_PEARL_PCIE=m
default y if QTNFMAC_PEARL_PCIE=y

+config QTNFMAC_PCIE
+ tristate
+ depends on QTNFMAC_PEARL_PCIE
+ default m if QTNFMAC_PEARL_PCIE=m
+ default y if QTNFMAC_PEARL_PCIE=y
+
config QTNFMAC_PEARL_PCIE
tristate "Quantenna QSR10g PCIe support"
default n
@@ -16,4 +22,4 @@ config QTNFMAC_PEARL_PCIE
802.11ac QSR10g (aka Pearl) FullMAC chipset running over PCIe.

If you choose to build it as a module, two modules will be built:
- qtnfmac.ko and qtnfmac_pearl_pcie.ko.
+ qtnfmac.ko and qtnfmac_pcie.ko.
diff --git a/drivers/net/wireless/quantenna/qtnfmac/Makefile b/drivers/net/wireless/quantenna/qtnfmac/Makefile
index 17cd7adb4109..e295aa55619f 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/Makefile
+++ b/drivers/net/wireless/quantenna/qtnfmac/Makefile
@@ -19,11 +19,11 @@ qtnfmac-objs += \

#

-obj-$(CONFIG_QTNFMAC_PEARL_PCIE) += qtnfmac_pearl_pcie.o
+obj-$(CONFIG_QTNFMAC_PCIE) += qtnfmac_pcie.o

-qtnfmac_pearl_pcie-objs += \
+qtnfmac_pcie-objs += \
shm_ipc.o \
- pcie/pcie.o \
- pcie/pearl_pcie.o
+ pcie/pcie.o

-qtnfmac_pearl_pcie-$(CONFIG_DEBUG_FS) += debug.o
+qtnfmac_pcie-$(CONFIG_DEBUG_FS) += debug.o
+qtnfmac_pcie-$(CONFIG_QTNFMAC_PEARL_PCIE) += pcie/pearl_pcie.o
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c
index 16795dbe475b..b589c01d9bbe 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/* Copyright (c) 2018 Quantenna Communications, Inc. All rights reserved. */

+#include <linux/module.h>
#include <linux/printk.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
@@ -15,14 +16,37 @@
#include "shm_ipc.h"
#include "core.h"
#include "debug.h"
-
-#undef pr_fmt
-#define pr_fmt(fmt) "qtnf_pcie: %s: " fmt, __func__
+#include "util.h"
+#include "qtn_hw_ids.h"

#define QTN_SYSCTL_BAR 0
#define QTN_SHMEM_BAR 2
#define QTN_DMA_BAR 3

+#define QTN_PCIE_MAX_FW_BUFSZ (1 * 1024 * 1024)
+
+static bool use_msi = true;
+module_param(use_msi, bool, 0644);
+MODULE_PARM_DESC(use_msi, "set 0 to use legacy interrupt");
+
+static unsigned int tx_bd_size_param;
+module_param(tx_bd_size_param, uint, 0644);
+MODULE_PARM_DESC(tx_bd_size_param, "Tx descriptors queue size");
+
+static unsigned int rx_bd_size_param = 256;
+module_param(rx_bd_size_param, uint, 0644);
+MODULE_PARM_DESC(rx_bd_size_param, "Rx descriptors queue size");
+
+static u8 flashboot = 1;
+module_param(flashboot, byte, 0644);
+MODULE_PARM_DESC(flashboot, "set to 0 to use FW binary file on FS");
+
+static unsigned int fw_blksize_param = QTN_PCIE_MAX_FW_BUFSZ;
+module_param(fw_blksize_param, uint, 0644);
+MODULE_PARM_DESC(fw_blksize_param, "firmware loading block size in bytes");
+
+#define DRV_NAME "qtnfmac_pcie"
+
int qtnf_pcie_control_tx(struct qtnf_bus *bus, struct sk_buff *skb)
{
struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
@@ -58,7 +82,7 @@ int qtnf_pcie_alloc_skb_array(struct qtnf_pcie_bus_priv *priv)
return 0;
}

-void qtnf_pcie_bringup_fw_async(struct qtnf_bus *bus)
+static void qtnf_pcie_bringup_fw_async(struct qtnf_bus *bus)
{
struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
struct pci_dev *pdev = priv->pdev;
@@ -72,7 +96,7 @@ static int qtnf_dbg_mps_show(struct seq_file *s, void *data)
struct qtnf_bus *bus = dev_get_drvdata(s->private);
struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);

- seq_printf(s, "%d\n", priv->mps);
+ seq_printf(s, "%d\n", pcie_get_mps(priv->pdev));

return 0;
}
@@ -104,8 +128,7 @@ static int qtnf_dbg_shm_stats(struct seq_file *s, void *data)
return 0;
}

-void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success,
- const char *drv_name)
+void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success)
{
struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
struct pci_dev *pdev = priv->pdev;
@@ -122,7 +145,7 @@ void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success,
}

if (boot_success) {
- qtnf_debugfs_init(bus, drv_name);
+ qtnf_debugfs_init(bus, DRV_NAME);
qtnf_debugfs_add_entry(bus, "mps", qtnf_dbg_mps_show);
qtnf_debugfs_add_entry(bus, "msi_enabled", qtnf_dbg_msi_show);
qtnf_debugfs_add_entry(bus, "shm_stats", qtnf_dbg_shm_stats);
@@ -133,9 +156,8 @@ void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success,
put_device(&pdev->dev);
}

-static void qtnf_tune_pcie_mps(struct qtnf_pcie_bus_priv *priv)
+static void qtnf_tune_pcie_mps(struct pci_dev *pdev)
{
- struct pci_dev *pdev = priv->pdev;
struct pci_dev *parent;
int mps_p, mps_o, mps_m, mps;
int ret;
@@ -163,12 +185,10 @@ static void qtnf_tune_pcie_mps(struct qtnf_pcie_bus_priv *priv)
if (ret) {
pr_err("failed to set mps to %d, keep using current %d\n",
mps, mps_o);
- priv->mps = mps_o;
return;
}

pr_debug("set mps to %d (was %d, max %d)\n", mps, mps_o, mps_m);
- priv->mps = mps;
}

static void qtnf_pcie_init_irq(struct qtnf_pcie_bus_priv *priv, bool use_msi)
@@ -194,20 +214,20 @@ static void qtnf_pcie_init_irq(struct qtnf_pcie_bus_priv *priv, bool use_msi)
}
}

-static void __iomem *qtnf_map_bar(struct qtnf_pcie_bus_priv *priv, u8 index)
+static void __iomem *qtnf_map_bar(struct pci_dev *pdev, u8 index)
{
void __iomem *vaddr;
dma_addr_t busaddr;
size_t len;
int ret;

- ret = pcim_iomap_regions(priv->pdev, 1 << index, "qtnfmac_pcie");
+ ret = pcim_iomap_regions(pdev, 1 << index, "qtnfmac_pcie");
if (ret)
return IOMEM_ERR_PTR(ret);

- busaddr = pci_resource_start(priv->pdev, index);
- len = pci_resource_len(priv->pdev, index);
- vaddr = pcim_iomap_table(priv->pdev)[index];
+ busaddr = pci_resource_start(pdev, index);
+ len = pci_resource_len(pdev, index);
+ vaddr = pcim_iomap_table(pdev)[index];
if (!vaddr)
return IOMEM_ERR_PTR(-ENOMEM);

@@ -217,31 +237,6 @@ static void __iomem *qtnf_map_bar(struct qtnf_pcie_bus_priv *priv, u8 index)
return vaddr;
}

-static int qtnf_pcie_init_memory(struct qtnf_pcie_bus_priv *priv)
-{
- int ret = -ENOMEM;
-
- priv->sysctl_bar = qtnf_map_bar(priv, QTN_SYSCTL_BAR);
- if (IS_ERR(priv->sysctl_bar)) {
- pr_err("failed to map BAR%u\n", QTN_SYSCTL_BAR);
- return ret;
- }
-
- priv->dmareg_bar = qtnf_map_bar(priv, QTN_DMA_BAR);
- if (IS_ERR(priv->dmareg_bar)) {
- pr_err("failed to map BAR%u\n", QTN_DMA_BAR);
- return ret;
- }
-
- priv->epmem_bar = qtnf_map_bar(priv, QTN_SHMEM_BAR);
- if (IS_ERR(priv->epmem_bar)) {
- pr_err("failed to map BAR%u\n", QTN_SHMEM_BAR);
- return ret;
- }
-
- return 0;
-}
-
static void qtnf_pcie_control_rx_callback(void *arg, const u8 __iomem *buf,
size_t len)
{
@@ -282,27 +277,82 @@ void qtnf_pcie_init_shm_ipc(struct qtnf_pcie_bus_priv *priv,
ipc_int, &rx_callback);
}

-int qtnf_pcie_probe(struct pci_dev *pdev, size_t priv_size,
- const struct qtnf_bus_ops *bus_ops, u64 dma_mask,
- bool use_msi)
+static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct qtnf_pcie_bus_priv *pcie_priv;
struct qtnf_bus *bus;
+ void __iomem *sysctl_bar;
+ void __iomem *epmem_bar;
+ void __iomem *dmareg_bar;
+ unsigned int chipid;
int ret;

- bus = devm_kzalloc(&pdev->dev,
- sizeof(*bus) + priv_size, GFP_KERNEL);
+ if (!pci_is_pcie(pdev)) {
+ pr_err("device %s is not PCI Express\n", pci_name(pdev));
+ return -EIO;
+ }
+
+ qtnf_tune_pcie_mps(pdev);
+
+ ret = pcim_enable_device(pdev);
+ if (ret) {
+ pr_err("failed to init PCI device %x\n", pdev->device);
+ return ret;
+ }
+
+ pci_set_master(pdev);
+
+ sysctl_bar = qtnf_map_bar(pdev, QTN_SYSCTL_BAR);
+ if (IS_ERR(sysctl_bar)) {
+ pr_err("failed to map BAR%u\n", QTN_SYSCTL_BAR);
+ return ret;
+ }
+
+ dmareg_bar = qtnf_map_bar(pdev, QTN_DMA_BAR);
+ if (IS_ERR(dmareg_bar)) {
+ pr_err("failed to map BAR%u\n", QTN_DMA_BAR);
+ return ret;
+ }
+
+ epmem_bar = qtnf_map_bar(pdev, QTN_SHMEM_BAR);
+ if (IS_ERR(epmem_bar)) {
+ pr_err("failed to map BAR%u\n", QTN_SHMEM_BAR);
+ return ret;
+ }
+
+ chipid = qtnf_chip_id_get(sysctl_bar);
+
+ pr_info("identified device: %s\n", qtnf_chipid_to_string(chipid));
+
+ switch (chipid) {
+#if IS_ENABLED(CONFIG_QTNFMAC_PEARL_PCIE)
+ case QTN_CHIP_ID_PEARL:
+ case QTN_CHIP_ID_PEARL_B:
+ case QTN_CHIP_ID_PEARL_C:
+ bus = qtnf_pcie_pearl_alloc(pdev);
+ break;
+#endif
+ default:
+ pr_err("unsupported chip ID 0x%x\n", chipid);
+ return -ENOTSUPP;
+ }
+
if (!bus)
return -ENOMEM;

pcie_priv = get_bus_priv(bus);
-
pci_set_drvdata(pdev, bus);
- bus->bus_ops = bus_ops;
bus->dev = &pdev->dev;
bus->fw_state = QTNF_FW_STATE_RESET;
pcie_priv->pdev = pdev;
pcie_priv->tx_stopped = 0;
+ pcie_priv->rx_bd_num = rx_bd_size_param;
+ pcie_priv->flashboot = flashboot;
+
+ if (fw_blksize_param > QTN_PCIE_MAX_FW_BUFSZ)
+ pcie_priv->fw_blksize = QTN_PCIE_MAX_FW_BUFSZ;
+ else
+ pcie_priv->fw_blksize = fw_blksize_param;

mutex_init(&bus->bus_lock);
spin_lock_init(&pcie_priv->tx_lock);
@@ -317,53 +367,35 @@ int qtnf_pcie_probe(struct pci_dev *pdev, size_t priv_size,
pcie_priv->workqueue = create_singlethread_workqueue("QTNF_PCIE");
if (!pcie_priv->workqueue) {
pr_err("failed to alloc bus workqueue\n");
- ret = -ENODEV;
- goto err_init;
- }
-
- init_dummy_netdev(&bus->mux_dev);
-
- if (!pci_is_pcie(pdev)) {
- pr_err("device %s is not PCI Express\n", pci_name(pdev));
- ret = -EIO;
- goto err_base;
- }
-
- qtnf_tune_pcie_mps(pcie_priv);
-
- ret = pcim_enable_device(pdev);
- if (ret) {
- pr_err("failed to init PCI device %x\n", pdev->device);
- goto err_base;
- } else {
- pr_debug("successful init of PCI device %x\n", pdev->device);
+ return -ENODEV;
}

- ret = dma_set_mask_and_coherent(&pdev->dev, dma_mask);
+ ret = dma_set_mask_and_coherent(&pdev->dev,
+ pcie_priv->dma_mask_get_cb());
if (ret) {
- pr_err("PCIE DMA coherent mask init failed\n");
- goto err_base;
+ pr_err("PCIE DMA coherent mask init failed 0x%llx\n",
+ pcie_priv->dma_mask_get_cb());
+ goto error;
}

- pci_set_master(pdev);
+ init_dummy_netdev(&bus->mux_dev);
qtnf_pcie_init_irq(pcie_priv, use_msi);
-
- ret = qtnf_pcie_init_memory(pcie_priv);
- if (ret < 0) {
- pr_err("PCIE memory init failed\n");
- goto err_base;
- }
-
+ pcie_priv->sysctl_bar = sysctl_bar;
+ pcie_priv->dmareg_bar = dmareg_bar;
+ pcie_priv->epmem_bar = epmem_bar;
pci_save_state(pdev);

+ ret = pcie_priv->probe_cb(bus, tx_bd_size_param);
+ if (ret)
+ goto error;
+
+ qtnf_pcie_bringup_fw_async(bus);
return 0;

-err_base:
+error:
flush_workqueue(pcie_priv->workqueue);
destroy_workqueue(pcie_priv->workqueue);
-err_init:
pci_set_drvdata(pdev, NULL);
-
return ret;
}

@@ -373,8 +405,17 @@ static void qtnf_pcie_free_shm_ipc(struct qtnf_pcie_bus_priv *priv)
qtnf_shm_ipc_free(&priv->shm_ipc_ep_out);
}

-void qtnf_pcie_remove(struct qtnf_bus *bus, struct qtnf_pcie_bus_priv *priv)
+static void qtnf_pcie_remove(struct pci_dev *dev)
{
+ struct qtnf_pcie_bus_priv *priv;
+ struct qtnf_bus *bus;
+
+ bus = pci_get_drvdata(dev);
+ if (!bus)
+ return;
+
+ priv = get_bus_priv(bus);
+
cancel_work_sync(&bus->fw_work);

if (bus->fw_state == QTNF_FW_STATE_ACTIVE ||
@@ -388,5 +429,77 @@ void qtnf_pcie_remove(struct qtnf_bus *bus, struct qtnf_pcie_bus_priv *priv)

qtnf_pcie_free_shm_ipc(priv);
qtnf_debugfs_remove(bus);
+ priv->remove_cb(bus);
pci_set_drvdata(priv->pdev, NULL);
}
+
+#ifdef CONFIG_PM_SLEEP
+static int qtnf_pcie_suspend(struct device *dev)
+{
+ struct qtnf_pcie_bus_priv *priv;
+ struct qtnf_bus *bus;
+
+ bus = pci_get_drvdata(to_pci_dev(dev));
+ if (!bus)
+ return -EFAULT;
+
+ priv = get_bus_priv(bus);
+ return priv->suspend_cb(bus);
+}
+
+static int qtnf_pcie_resume(struct device *dev)
+{
+ struct qtnf_pcie_bus_priv *priv;
+ struct qtnf_bus *bus;
+
+ bus = pci_get_drvdata(to_pci_dev(dev));
+ if (!bus)
+ return -EFAULT;
+
+ priv = get_bus_priv(bus);
+ return priv->resume_cb(bus);
+}
+
+/* Power Management Hooks */
+static SIMPLE_DEV_PM_OPS(qtnf_pcie_pm_ops, qtnf_pcie_suspend,
+ qtnf_pcie_resume);
+#endif
+
+static const struct pci_device_id qtnf_pcie_devid_table[] = {
+ {
+ PCIE_VENDOR_ID_QUANTENNA, PCIE_DEVICE_ID_QTN_PEARL,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ },
+ { },
+};
+
+MODULE_DEVICE_TABLE(pci, qtnf_pcie_devid_table);
+
+static struct pci_driver qtnf_pcie_drv_data = {
+ .name = DRV_NAME,
+ .id_table = qtnf_pcie_devid_table,
+ .probe = qtnf_pcie_probe,
+ .remove = qtnf_pcie_remove,
+#ifdef CONFIG_PM_SLEEP
+ .driver = {
+ .pm = &qtnf_pcie_pm_ops,
+ },
+#endif
+};
+
+static int __init qtnf_pcie_register(void)
+{
+ return pci_register_driver(&qtnf_pcie_drv_data);
+}
+
+static void __exit qtnf_pcie_exit(void)
+{
+ pci_unregister_driver(&qtnf_pcie_drv_data);
+}
+
+module_init(qtnf_pcie_register);
+module_exit(qtnf_pcie_exit);
+
+MODULE_AUTHOR("Quantenna Communications");
+MODULE_DESCRIPTION("Quantenna PCIe bus driver for 802.11 wireless LAN.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie_priv.h b/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie_priv.h
index 5c70fb4c0f92..7c742c56efaf 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie_priv.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie_priv.h
@@ -23,9 +23,14 @@
struct qtnf_pcie_bus_priv {
struct pci_dev *pdev;

+ int (*probe_cb)(struct qtnf_bus *bus, unsigned int tx_bd_size);
+ void (*remove_cb)(struct qtnf_bus *bus);
+ int (*suspend_cb)(struct qtnf_bus *bus);
+ int (*resume_cb)(struct qtnf_bus *bus);
+ u64 (*dma_mask_get_cb)(void);
+
spinlock_t tx_reclaim_lock;
spinlock_t tx_lock;
- int mps;

struct workqueue_struct *workqueue;
struct tasklet_struct reclaim_tq;
@@ -43,6 +48,8 @@ struct qtnf_pcie_bus_priv {
struct sk_buff **tx_skb;
struct sk_buff **rx_skb;

+ unsigned int fw_blksize;
+
u32 rx_bd_w_index;
u32 rx_bd_r_index;

@@ -58,21 +65,17 @@ struct qtnf_pcie_bus_priv {

u8 msi_enabled;
u8 tx_stopped;
+ bool flashboot;
};

int qtnf_pcie_control_tx(struct qtnf_bus *bus, struct sk_buff *skb);
int qtnf_pcie_alloc_skb_array(struct qtnf_pcie_bus_priv *priv);
-void qtnf_pcie_bringup_fw_async(struct qtnf_bus *bus);
-void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success,
- const char *drv_name);
+void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success);
void qtnf_pcie_init_shm_ipc(struct qtnf_pcie_bus_priv *priv,
struct qtnf_shm_ipc_region __iomem *ipc_tx_reg,
struct qtnf_shm_ipc_region __iomem *ipc_rx_reg,
const struct qtnf_shm_ipc_int *ipc_int);
-int qtnf_pcie_probe(struct pci_dev *pdev, size_t priv_size,
- const struct qtnf_bus_ops *bus_ops, u64 dma_mask,
- bool use_msi);
-void qtnf_pcie_remove(struct qtnf_bus *bus, struct qtnf_pcie_bus_priv *priv);
+struct qtnf_bus *qtnf_pcie_pearl_alloc(struct pci_dev *pdev);

static inline void qtnf_non_posted_write(u32 val, void __iomem *basereg)
{
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
index 95c7b95c6f8a..1f5facbb8905 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
@@ -2,7 +2,6 @@
/* Copyright (c) 2018 Quantenna Communications */

#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/pci.h>
#include <linux/vmalloc.h>
@@ -24,23 +23,7 @@
#include "shm_ipc.h"
#include "debug.h"

-static bool use_msi = true;
-module_param(use_msi, bool, 0644);
-MODULE_PARM_DESC(use_msi, "set 0 to use legacy interrupt");
-
-static unsigned int tx_bd_size_param = 32;
-module_param(tx_bd_size_param, uint, 0644);
-MODULE_PARM_DESC(tx_bd_size_param, "Tx descriptors queue size, power of two");
-
-static unsigned int rx_bd_size_param = 256;
-module_param(rx_bd_size_param, uint, 0644);
-MODULE_PARM_DESC(rx_bd_size_param, "Rx descriptors queue size, power of two");
-
-static u8 flashboot = 1;
-module_param(flashboot, byte, 0644);
-MODULE_PARM_DESC(flashboot, "set to 0 to use FW binary file on FS");
-
-#define DRV_NAME "qtnfmac_pearl_pcie"
+#define PEARL_TX_BD_SIZE_DEFAULT 32

struct qtnf_pearl_bda {
__le16 bda_len;
@@ -415,30 +398,28 @@ static int pearl_hhbm_init(struct qtnf_pcie_pearl_state *ps)
return 0;
}

-static int qtnf_pcie_pearl_init_xfer(struct qtnf_pcie_pearl_state *ps)
+static int qtnf_pcie_pearl_init_xfer(struct qtnf_pcie_pearl_state *ps,
+ unsigned int tx_bd_size)
{
struct qtnf_pcie_bus_priv *priv = &ps->base;
int ret;
u32 val;

- priv->tx_bd_num = tx_bd_size_param;
- priv->rx_bd_num = rx_bd_size_param;
- priv->rx_bd_w_index = 0;
- priv->rx_bd_r_index = 0;
+ if (tx_bd_size == 0)
+ tx_bd_size = PEARL_TX_BD_SIZE_DEFAULT;

- if (!priv->tx_bd_num || !is_power_of_2(priv->tx_bd_num)) {
- pr_err("tx_bd_size_param %u is not power of two\n",
- priv->tx_bd_num);
- return -EINVAL;
- }
+ val = tx_bd_size * sizeof(struct qtnf_pearl_tx_bd);

- val = priv->tx_bd_num * sizeof(struct qtnf_pearl_tx_bd);
- if (val > PCIE_HHBM_MAX_SIZE) {
- pr_err("tx_bd_size_param %u is too large\n",
- priv->tx_bd_num);
- return -EINVAL;
+ if (!is_power_of_2(tx_bd_size) || val > PCIE_HHBM_MAX_SIZE) {
+ pr_warn("bad tx_bd_size value %u\n", tx_bd_size);
+ priv->tx_bd_num = PEARL_TX_BD_SIZE_DEFAULT;
+ } else {
+ priv->tx_bd_num = tx_bd_size;
}

+ priv->rx_bd_w_index = 0;
+ priv->rx_bd_r_index = 0;
+
if (!priv->rx_bd_num || !is_power_of_2(priv->rx_bd_num)) {
pr_err("rx_bd_size_param %u is not power of two\n",
priv->rx_bd_num);
@@ -1006,7 +987,7 @@ static void qtnf_pearl_fw_work_handler(struct work_struct *work)
const char *fwname = QTN_PCI_PEARL_FW_NAME;
bool fw_boot_success = false;

- if (flashboot) {
+ if (ps->base.flashboot) {
state |= QTN_RC_FW_FLASHBOOT;
} else {
ret = request_firmware(&fw, fwname, &pdev->dev);
@@ -1022,7 +1003,7 @@ static void qtnf_pearl_fw_work_handler(struct work_struct *work)
QTN_FW_DL_TIMEOUT_MS)) {
pr_err("card is not ready\n");

- if (!flashboot)
+ if (!ps->base.flashboot)
release_firmware(fw);

goto fw_load_exit;
@@ -1030,7 +1011,7 @@ static void qtnf_pearl_fw_work_handler(struct work_struct *work)

qtnf_clear_state(&ps->bda->bda_ep_state, QTN_EP_FW_LOADRDY);

- if (flashboot) {
+ if (ps->base.flashboot) {
pr_info("booting firmware from flash\n");

} else {
@@ -1061,7 +1042,7 @@ static void qtnf_pearl_fw_work_handler(struct work_struct *work)
fw_boot_success = true;

fw_load_exit:
- qtnf_pcie_fw_boot_done(bus, fw_boot_success, DRV_NAME);
+ qtnf_pcie_fw_boot_done(bus, fw_boot_success);

if (fw_boot_success) {
qtnf_debugfs_add_entry(bus, "hdp_stats", qtnf_dbg_hdp_stats);
@@ -1077,74 +1058,34 @@ static void qtnf_pearl_reclaim_tasklet_fn(unsigned long data)
qtnf_en_txdone_irq(ps);
}

-static int qtnf_pearl_check_chip_id(struct qtnf_pcie_pearl_state *ps)
+static u64 qtnf_pearl_dma_mask_get(void)
{
- unsigned int chipid;
-
- chipid = qtnf_chip_id_get(ps->base.sysctl_bar);
-
- switch (chipid) {
- case QTN_CHIP_ID_PEARL:
- case QTN_CHIP_ID_PEARL_B:
- case QTN_CHIP_ID_PEARL_C:
- pr_info("chip ID is 0x%x\n", chipid);
- break;
- default:
- pr_err("incorrect chip ID 0x%x\n", chipid);
- return -ENODEV;
- }
-
- return 0;
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+ return DMA_BIT_MASK(64);
+#else
+ return DMA_BIT_MASK(32);
+#endif
}

-static int qtnf_pcie_pearl_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
+static int qtnf_pcie_pearl_probe(struct qtnf_bus *bus, unsigned int tx_bd_size)
{
struct qtnf_shm_ipc_int ipc_int;
- struct qtnf_pcie_pearl_state *ps;
- struct qtnf_bus *bus;
+ struct qtnf_pcie_pearl_state *ps = get_bus_priv(bus);
+ struct pci_dev *pdev = ps->base.pdev;
int ret;
- u64 dma_mask;
-
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
- dma_mask = DMA_BIT_MASK(64);
-#else
- dma_mask = DMA_BIT_MASK(32);
-#endif
-
- ret = qtnf_pcie_probe(pdev, sizeof(*ps), &qtnf_pcie_pearl_bus_ops,
- dma_mask, use_msi);
- if (ret)
- return ret;
-
- bus = pci_get_drvdata(pdev);
- ps = get_bus_priv(bus);

+ bus->bus_ops = &qtnf_pcie_pearl_bus_ops;
spin_lock_init(&ps->irq_lock);
-
- tasklet_init(&ps->base.reclaim_tq, qtnf_pearl_reclaim_tasklet_fn,
- (unsigned long)ps);
- netif_napi_add(&bus->mux_dev, &bus->mux_napi,
- qtnf_pcie_pearl_rx_poll, 10);
INIT_WORK(&bus->fw_work, qtnf_pearl_fw_work_handler);

ps->pcie_reg_base = ps->base.dmareg_bar;
ps->bda = ps->base.epmem_bar;
writel(ps->base.msi_enabled, &ps->bda->bda_rc_msi_enabled);

- ipc_int.fn = qtnf_pcie_pearl_ipc_gen_ep_int;
- ipc_int.arg = ps;
- qtnf_pcie_init_shm_ipc(&ps->base, &ps->bda->bda_shm_reg1,
- &ps->bda->bda_shm_reg2, &ipc_int);
-
- ret = qtnf_pearl_check_chip_id(ps);
- if (ret)
- goto error;
-
- ret = qtnf_pcie_pearl_init_xfer(ps);
+ ret = qtnf_pcie_pearl_init_xfer(ps, tx_bd_size);
if (ret) {
pr_err("PCIE xfer init failed\n");
- goto error;
+ return ret;
}

/* init default irq settings */
@@ -1155,95 +1096,63 @@ static int qtnf_pcie_pearl_probe(struct pci_dev *pdev,

ret = devm_request_irq(&pdev->dev, pdev->irq,
&qtnf_pcie_pearl_interrupt, 0,
- "qtnf_pcie_irq", (void *)bus);
+ "qtnf_pearl_irq", (void *)bus);
if (ret) {
pr_err("failed to request pcie irq %d\n", pdev->irq);
- goto err_xfer;
+ qtnf_pearl_free_xfer_buffers(ps);
+ return ret;
}

- qtnf_pcie_bringup_fw_async(bus);
-
- return 0;
+ tasklet_init(&ps->base.reclaim_tq, qtnf_pearl_reclaim_tasklet_fn,
+ (unsigned long)ps);
+ netif_napi_add(&bus->mux_dev, &bus->mux_napi,
+ qtnf_pcie_pearl_rx_poll, 10);

-err_xfer:
- qtnf_pearl_free_xfer_buffers(ps);
-error:
- qtnf_pcie_remove(bus, &ps->base);
+ ipc_int.fn = qtnf_pcie_pearl_ipc_gen_ep_int;
+ ipc_int.arg = ps;
+ qtnf_pcie_init_shm_ipc(&ps->base, &ps->bda->bda_shm_reg1,
+ &ps->bda->bda_shm_reg2, &ipc_int);

- return ret;
+ return 0;
}

-static void qtnf_pcie_pearl_remove(struct pci_dev *pdev)
+static void qtnf_pcie_pearl_remove(struct qtnf_bus *bus)
{
- struct qtnf_pcie_pearl_state *ps;
- struct qtnf_bus *bus;
-
- bus = pci_get_drvdata(pdev);
- if (!bus)
- return;
-
- ps = get_bus_priv(bus);
+ struct qtnf_pcie_pearl_state *ps = get_bus_priv(bus);

- qtnf_pcie_remove(bus, &ps->base);
qtnf_pearl_reset_ep(ps);
qtnf_pearl_free_xfer_buffers(ps);
}

#ifdef CONFIG_PM_SLEEP
-static int qtnf_pcie_pearl_suspend(struct device *dev)
+static int qtnf_pcie_pearl_suspend(struct qtnf_bus *bus)
{
return -EOPNOTSUPP;
}

-static int qtnf_pcie_pearl_resume(struct device *dev)
+static int qtnf_pcie_pearl_resume(struct qtnf_bus *bus)
{
return 0;
}
-#endif /* CONFIG_PM_SLEEP */
-
-#ifdef CONFIG_PM_SLEEP
-/* Power Management Hooks */
-static SIMPLE_DEV_PM_OPS(qtnf_pcie_pearl_pm_ops, qtnf_pcie_pearl_suspend,
- qtnf_pcie_pearl_resume);
#endif

-static const struct pci_device_id qtnf_pcie_devid_table[] = {
- {
- PCIE_VENDOR_ID_QUANTENNA, PCIE_DEVICE_ID_QTN_PEARL,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- },
- { },
-};
+struct qtnf_bus *qtnf_pcie_pearl_alloc(struct pci_dev *pdev)
+{
+ struct qtnf_bus *bus;
+ struct qtnf_pcie_pearl_state *ps;

-MODULE_DEVICE_TABLE(pci, qtnf_pcie_devid_table);
+ bus = devm_kzalloc(&pdev->dev, sizeof(*bus) + sizeof(*ps), GFP_KERNEL);
+ if (!bus)
+ return NULL;

-static struct pci_driver qtnf_pcie_pearl_drv_data = {
- .name = DRV_NAME,
- .id_table = qtnf_pcie_devid_table,
- .probe = qtnf_pcie_pearl_probe,
- .remove = qtnf_pcie_pearl_remove,
+ ps = get_bus_priv(bus);
+ ps->base.probe_cb = qtnf_pcie_pearl_probe;
+ ps->base.remove_cb = qtnf_pcie_pearl_remove;
+ ps->base.dma_mask_get_cb = qtnf_pearl_dma_mask_get;
#ifdef CONFIG_PM_SLEEP
- .driver = {
- .pm = &qtnf_pcie_pearl_pm_ops,
- },
+ ps->base.resume_cb = qtnf_pcie_pearl_resume;
+ ps->base.suspend_cb = qtnf_pcie_pearl_suspend;
#endif
-};
-
-static int __init qtnf_pcie_pearl_register(void)
-{
- pr_info("register Quantenna QSR10g FullMAC PCIE driver\n");
- return pci_register_driver(&qtnf_pcie_pearl_drv_data);
-}

-static void __exit qtnf_pcie_pearl_exit(void)
-{
- pr_info("unregister Quantenna QSR10g FullMAC PCIE driver\n");
- pci_unregister_driver(&qtnf_pcie_pearl_drv_data);
+ return bus;
}
-
-module_init(qtnf_pcie_pearl_register);
-module_exit(qtnf_pcie_pearl_exit);
-
-MODULE_AUTHOR("Quantenna Communications");
-MODULE_DESCRIPTION("Quantenna QSR10g PCIe bus driver for 802.11 wireless LAN.");
-MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/quantenna/qtnfmac/util.c b/drivers/net/wireless/quantenna/qtnfmac/util.c
index e745733ba417..dfe3fe8a42c1 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/util.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/util.c
@@ -15,6 +15,7 @@
*/

#include "util.h"
+#include "qtn_hw_ids.h"

void qtnf_sta_list_init(struct qtnf_sta_list *list)
{
@@ -116,3 +117,18 @@ void qtnf_sta_list_free(struct qtnf_sta_list *list)

INIT_LIST_HEAD(&list->head);
}
+
+const char *qtnf_chipid_to_string(unsigned long chip_id)
+{
+ switch (chip_id) {
+ case QTN_CHIP_ID_PEARL:
+ return "Pearl revA";
+ case QTN_CHIP_ID_PEARL_B:
+ return "Pearl revB";
+ case QTN_CHIP_ID_PEARL_C:
+ return "Pearl revC";
+ default:
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL_GPL(qtnf_chipid_to_string);
diff --git a/drivers/net/wireless/quantenna/qtnfmac/util.h b/drivers/net/wireless/quantenna/qtnfmac/util.h
index 0d4d92b11540..b8744baac332 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/util.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/util.h
@@ -20,6 +20,8 @@
#include <linux/kernel.h>
#include "core.h"

+const char *qtnf_chipid_to_string(unsigned long chip_id);
+
void qtnf_sta_list_init(struct qtnf_sta_list *list);

struct qtnf_sta_node *qtnf_sta_list_lookup(struct qtnf_sta_list *list,
--
2.11.0


2018-10-08 09:56:15

by Sergey Matyukevich

[permalink] [raw]
Subject: [PATCH 5/5] qtnfmac: add support for Topaz chipsets

This patch adds support for QSR1000/QSR2000 family of chipsets
to qtnfmac_pcie platform driver.

QSR1000/QSR2000 (aka Topaz) is a family of 80MHz, 11ac Wave2,
4x4/2x4/2x2 chips, including single and dual band devices.
Depending on specific chip model and firmware in use, either
STA or both STA and AP modes are supported.

Patch adds Topaz support to qtnfmac_pcie driver. It is possible
to enable both Topaz and Pearl support in kernel configuration.
In that case proper platform bus will be selected on probing
based on chip ID.

Signed-off-by: Igor Mitsyanko <[email protected]>
Signed-off-by: Sergey Matyukevich <[email protected]>
Signed-off-by: Andrey Shevchenko <[email protected]>
---
drivers/net/wireless/quantenna/qtnfmac/Kconfig | 27 +-
drivers/net/wireless/quantenna/qtnfmac/Makefile | 1 +
drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c | 5 +
.../wireless/quantenna/qtnfmac/pcie/pcie_priv.h | 1 +
.../wireless/quantenna/qtnfmac/pcie/topaz_pcie.c | 1219 ++++++++++++++++++++
.../quantenna/qtnfmac/pcie/topaz_pcie_ipc.h | 94 ++
.../quantenna/qtnfmac/pcie/topaz_pcie_regs.h | 45 +
.../net/wireless/quantenna/qtnfmac/qtn_hw_ids.h | 3 +
drivers/net/wireless/quantenna/qtnfmac/util.c | 2 +
9 files changed, 1391 insertions(+), 6 deletions(-)
create mode 100644 drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
create mode 100644 drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_ipc.h
create mode 100644 drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_regs.h

diff --git a/drivers/net/wireless/quantenna/qtnfmac/Kconfig b/drivers/net/wireless/quantenna/qtnfmac/Kconfig
index f43b236b7c0b..df2fd0a1ab5c 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/Kconfig
+++ b/drivers/net/wireless/quantenna/qtnfmac/Kconfig
@@ -1,14 +1,14 @@
config QTNFMAC
tristate
- depends on QTNFMAC_PEARL_PCIE
- default m if QTNFMAC_PEARL_PCIE=m
- default y if QTNFMAC_PEARL_PCIE=y
+ depends on QTNFMAC_PEARL_PCIE || QTNFMAC_TOPAZ_PCIE
+ default m if QTNFMAC_PEARL_PCIE=m || QTNFMAC_TOPAZ_PCIE=m
+ default y if QTNFMAC_PEARL_PCIE=y || QTNFMAC_TOPAZ_PCIE=y

config QTNFMAC_PCIE
tristate
- depends on QTNFMAC_PEARL_PCIE
- default m if QTNFMAC_PEARL_PCIE=m
- default y if QTNFMAC_PEARL_PCIE=y
+ depends on QTNFMAC_PEARL_PCIE || QTNFMAC_TOPAZ_PCIE
+ default m if QTNFMAC_PEARL_PCIE=m || QTNFMAC_TOPAZ_PCIE=m
+ default y if QTNFMAC_PEARL_PCIE=y || QTNFMAC_TOPAZ_PCIE=y

config QTNFMAC_PEARL_PCIE
tristate "Quantenna QSR10g PCIe support"
@@ -23,3 +23,18 @@ config QTNFMAC_PEARL_PCIE

If you choose to build it as a module, two modules will be built:
qtnfmac.ko and qtnfmac_pcie.ko.
+
+config QTNFMAC_TOPAZ_PCIE
+ tristate "Quantenna QSR1000/QSR2000 PCIe support"
+ default n
+ depends on PCI && CFG80211
+ select QTNFMAC
+ select FW_LOADER
+ select CRC32
+ help
+ This option adds support for wireless adapters based on Quantenna
+ 802.11ac QSR1000/QSR2000 (aka Topaz) FullMAC chipset
+ running over PCIe.
+
+ If you choose to build it as a module, two modules will be built:
+ qtnfmac.ko and qtnfmac_pcie.ko.
diff --git a/drivers/net/wireless/quantenna/qtnfmac/Makefile b/drivers/net/wireless/quantenna/qtnfmac/Makefile
index e295aa55619f..4de95330e793 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/Makefile
+++ b/drivers/net/wireless/quantenna/qtnfmac/Makefile
@@ -27,3 +27,4 @@ qtnfmac_pcie-objs += \

qtnfmac_pcie-$(CONFIG_DEBUG_FS) += debug.o
qtnfmac_pcie-$(CONFIG_QTNFMAC_PEARL_PCIE) += pcie/pearl_pcie.o
+qtnfmac_pcie-$(CONFIG_QTNFMAC_TOPAZ_PCIE) += pcie/topaz_pcie.o
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c
index b589c01d9bbe..a139db398fd4 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c
@@ -332,6 +332,11 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
bus = qtnf_pcie_pearl_alloc(pdev);
break;
#endif
+#if IS_ENABLED(CONFIG_QTNFMAC_TOPAZ_PCIE)
+ case QTN_CHIP_ID_TOPAZ:
+ bus = qtnf_pcie_topaz_alloc(pdev);
+ break;
+#endif
default:
pr_err("unsupported chip ID 0x%x\n", chipid);
return -ENOTSUPP;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie_priv.h b/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie_priv.h
index 7c742c56efaf..bbc074e1f34d 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie_priv.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie_priv.h
@@ -76,6 +76,7 @@ void qtnf_pcie_init_shm_ipc(struct qtnf_pcie_bus_priv *priv,
struct qtnf_shm_ipc_region __iomem *ipc_rx_reg,
const struct qtnf_shm_ipc_int *ipc_int);
struct qtnf_bus *qtnf_pcie_pearl_alloc(struct pci_dev *pdev);
+struct qtnf_bus *qtnf_pcie_topaz_alloc(struct pci_dev *pdev);

static inline void qtnf_non_posted_write(u32 val, void __iomem *basereg)
{
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
new file mode 100644
index 000000000000..598edb814421
--- /dev/null
+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
@@ -0,0 +1,1219 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2018 Quantenna Communications */
+
+#include <linux/kernel.h>
+#include <linux/firmware.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/crc32.h>
+#include <linux/completion.h>
+#include <linux/spinlock.h>
+#include <linux/circ_buf.h>
+
+#include "pcie_priv.h"
+#include "topaz_pcie_regs.h"
+#include "topaz_pcie_ipc.h"
+#include "qtn_hw_ids.h"
+#include "core.h"
+#include "bus.h"
+#include "shm_ipc.h"
+#include "debug.h"
+
+#define TOPAZ_TX_BD_SIZE_DEFAULT 128
+
+struct qtnf_topaz_tx_bd {
+ __le32 addr;
+ __le32 info;
+} __packed;
+
+struct qtnf_topaz_rx_bd {
+ __le32 addr;
+ __le32 info;
+} __packed;
+
+struct qtnf_extra_bd_params {
+ __le32 param1;
+ __le32 param2;
+ __le32 param3;
+ __le32 param4;
+} __packed;
+
+#define QTNF_BD_PARAM_OFFSET(n) offsetof(struct qtnf_extra_bd_params, param##n)
+
+struct vmac_pkt_info {
+ __le32 addr;
+ __le32 info;
+};
+
+struct qtnf_topaz_bda {
+ __le16 bda_len;
+ __le16 bda_version;
+ __le32 bda_bootstate;
+ __le32 bda_dma_mask;
+ __le32 bda_dma_offset;
+ __le32 bda_flags;
+ __le32 bda_img;
+ __le32 bda_img_size;
+ __le32 bda_ep2h_irqstatus;
+ __le32 bda_h2ep_irqstatus;
+ __le32 bda_msi_addr;
+ u8 reserved1[56];
+ __le32 bda_flashsz;
+ u8 bda_boardname[PCIE_BDA_NAMELEN];
+ __le32 bda_pci_pre_status;
+ __le32 bda_pci_endian;
+ __le32 bda_pci_post_status;
+ __le32 bda_h2ep_txd_budget;
+ __le32 bda_ep2h_txd_budget;
+ __le32 bda_rc_rx_bd_base;
+ __le32 bda_rc_rx_bd_num;
+ __le32 bda_rc_tx_bd_base;
+ __le32 bda_rc_tx_bd_num;
+ u8 bda_ep_link_state;
+ u8 bda_rc_link_state;
+ u8 bda_rc_msi_enabled;
+ u8 reserved2;
+ __le32 bda_ep_next_pkt;
+ struct vmac_pkt_info request[QTN_PCIE_RC_TX_QUEUE_LEN];
+ struct qtnf_shm_ipc_region bda_shm_reg1 __aligned(4096);
+ struct qtnf_shm_ipc_region bda_shm_reg2 __aligned(4096);
+} __packed;
+
+struct qtnf_pcie_topaz_state {
+ struct qtnf_pcie_bus_priv base;
+ struct qtnf_topaz_bda __iomem *bda;
+
+ dma_addr_t dma_msi_dummy;
+ u32 dma_msi_imwr;
+
+ struct qtnf_topaz_tx_bd *tx_bd_vbase;
+ struct qtnf_topaz_rx_bd *rx_bd_vbase;
+
+ __le32 __iomem *ep_next_rx_pkt;
+ __le32 __iomem *txqueue_wake;
+ __le32 __iomem *ep_pmstate;
+
+ unsigned long rx_pkt_count;
+};
+
+static void qtnf_deassert_intx(struct qtnf_pcie_topaz_state *ts)
+{
+ void __iomem *reg = ts->base.sysctl_bar + TOPAZ_PCIE_CFG0_OFFSET;
+ u32 cfg;
+
+ cfg = readl(reg);
+ cfg &= ~TOPAZ_ASSERT_INTX;
+ qtnf_non_posted_write(cfg, reg);
+}
+
+static inline int qtnf_topaz_intx_asserted(struct qtnf_pcie_topaz_state *ts)
+{
+ void __iomem *reg = ts->base.sysctl_bar + TOPAZ_PCIE_CFG0_OFFSET;
+ u32 cfg = readl(reg);
+
+ return !!(cfg & TOPAZ_ASSERT_INTX);
+}
+
+static void qtnf_topaz_reset_ep(struct qtnf_pcie_topaz_state *ts)
+{
+ writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_RST_EP_IRQ),
+ TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
+ msleep(QTN_EP_RESET_WAIT_MS);
+ pci_restore_state(ts->base.pdev);
+}
+
+static void setup_rx_irqs(struct qtnf_pcie_topaz_state *ts)
+{
+ void __iomem *reg = PCIE_DMA_WR_DONE_IMWR_ADDR_LOW(ts->base.dmareg_bar);
+
+ ts->dma_msi_imwr = readl(reg);
+}
+
+static void enable_rx_irqs(struct qtnf_pcie_topaz_state *ts)
+{
+ void __iomem *reg = PCIE_DMA_WR_DONE_IMWR_ADDR_LOW(ts->base.dmareg_bar);
+
+ qtnf_non_posted_write(ts->dma_msi_imwr, reg);
+}
+
+static void disable_rx_irqs(struct qtnf_pcie_topaz_state *ts)
+{
+ void __iomem *reg = PCIE_DMA_WR_DONE_IMWR_ADDR_LOW(ts->base.dmareg_bar);
+
+ qtnf_non_posted_write(QTN_HOST_LO32(ts->dma_msi_dummy), reg);
+}
+
+static void qtnf_topaz_ipc_gen_ep_int(void *arg)
+{
+ struct qtnf_pcie_topaz_state *ts = arg;
+
+ writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_CTRL_IRQ),
+ TOPAZ_CTL_M2L_INT(ts->base.sysctl_bar));
+}
+
+static int qtnf_is_state(__le32 __iomem *reg, u32 state)
+{
+ u32 s = readl(reg);
+
+ return (s == state);
+}
+
+static void qtnf_set_state(__le32 __iomem *reg, u32 state)
+{
+ qtnf_non_posted_write(state, reg);
+}
+
+static int qtnf_poll_state(__le32 __iomem *reg, u32 state, u32 delay_in_ms)
+{
+ u32 timeout = 0;
+
+ while ((qtnf_is_state(reg, state) == 0)) {
+ usleep_range(1000, 1200);
+ if (++timeout > delay_in_ms)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int topaz_alloc_bd_table(struct qtnf_pcie_topaz_state *ts,
+ struct qtnf_topaz_bda __iomem *bda)
+{
+ struct qtnf_extra_bd_params __iomem *extra_params;
+ struct qtnf_pcie_bus_priv *priv = &ts->base;
+ dma_addr_t paddr;
+ void *vaddr;
+ int len;
+ int i;
+
+ /* bd table */
+
+ len = priv->tx_bd_num * sizeof(struct qtnf_topaz_tx_bd) +
+ priv->rx_bd_num * sizeof(struct qtnf_topaz_rx_bd) +
+ sizeof(struct qtnf_extra_bd_params);
+
+ vaddr = dmam_alloc_coherent(&priv->pdev->dev, len, &paddr, GFP_KERNEL);
+ if (!vaddr)
+ return -ENOMEM;
+
+ memset(vaddr, 0, len);
+
+ /* tx bd */
+
+ ts->tx_bd_vbase = vaddr;
+ qtnf_non_posted_write(paddr, &bda->bda_rc_tx_bd_base);
+
+ for (i = 0; i < priv->tx_bd_num; i++)
+ ts->tx_bd_vbase[i].info |= cpu_to_le32(QTN_BD_EMPTY);
+
+ pr_debug("TX descriptor table: vaddr=0x%p paddr=%pad\n", vaddr, &paddr);
+
+ priv->tx_bd_r_index = 0;
+ priv->tx_bd_w_index = 0;
+
+ /* rx bd */
+
+ vaddr = ((struct qtnf_topaz_tx_bd *)vaddr) + priv->tx_bd_num;
+ paddr += priv->tx_bd_num * sizeof(struct qtnf_topaz_tx_bd);
+
+ ts->rx_bd_vbase = vaddr;
+ qtnf_non_posted_write(paddr, &bda->bda_rc_rx_bd_base);
+
+ pr_debug("RX descriptor table: vaddr=0x%p paddr=%pad\n", vaddr, &paddr);
+
+ /* extra shared params */
+
+ vaddr = ((struct qtnf_topaz_rx_bd *)vaddr) + priv->rx_bd_num;
+ paddr += priv->rx_bd_num * sizeof(struct qtnf_topaz_rx_bd);
+
+ extra_params = (struct qtnf_extra_bd_params __iomem *)vaddr;
+
+ ts->ep_next_rx_pkt = &extra_params->param1;
+ qtnf_non_posted_write(paddr + QTNF_BD_PARAM_OFFSET(1),
+ &bda->bda_ep_next_pkt);
+ ts->txqueue_wake = &extra_params->param2;
+ ts->ep_pmstate = &extra_params->param3;
+ ts->dma_msi_dummy = paddr + QTNF_BD_PARAM_OFFSET(4);
+
+ return 0;
+}
+
+static int
+topaz_skb2rbd_attach(struct qtnf_pcie_topaz_state *ts, u16 index, u32 wrap)
+{
+ struct qtnf_topaz_rx_bd *rxbd = &ts->rx_bd_vbase[index];
+ struct sk_buff *skb;
+ dma_addr_t paddr;
+
+ skb = __netdev_alloc_skb_ip_align(NULL, SKB_BUF_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ ts->base.rx_skb[index] = NULL;
+ return -ENOMEM;
+ }
+
+ ts->base.rx_skb[index] = skb;
+
+ paddr = pci_map_single(ts->base.pdev, skb->data,
+ SKB_BUF_SIZE, PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(ts->base.pdev, paddr)) {
+ pr_err("skb mapping error: %pad\n", &paddr);
+ return -ENOMEM;
+ }
+
+ rxbd->addr = cpu_to_le32(QTN_HOST_LO32(paddr));
+ rxbd->info = cpu_to_le32(QTN_BD_EMPTY | wrap);
+
+ ts->base.rx_bd_w_index = index;
+
+ return 0;
+}
+
+static int topaz_alloc_rx_buffers(struct qtnf_pcie_topaz_state *ts)
+{
+ u16 i;
+ int ret = 0;
+
+ memset(ts->rx_bd_vbase, 0x0,
+ ts->base.rx_bd_num * sizeof(struct qtnf_topaz_rx_bd));
+
+ for (i = 0; i < ts->base.rx_bd_num; i++) {
+ ret = topaz_skb2rbd_attach(ts, i, 0);
+ if (ret)
+ break;
+ }
+
+ ts->rx_bd_vbase[ts->base.rx_bd_num - 1].info |=
+ cpu_to_le32(QTN_BD_WRAP);
+
+ return ret;
+}
+
+/* all rx/tx activity should have ceased before calling this function */
+static void qtnf_topaz_free_xfer_buffers(struct qtnf_pcie_topaz_state *ts)
+{
+ struct qtnf_pcie_bus_priv *priv = &ts->base;
+ struct qtnf_topaz_rx_bd *rxbd;
+ struct qtnf_topaz_tx_bd *txbd;
+ struct sk_buff *skb;
+ dma_addr_t paddr;
+ int i;
+
+ /* free rx buffers */
+ for (i = 0; i < priv->rx_bd_num; i++) {
+ if (priv->rx_skb && priv->rx_skb[i]) {
+ rxbd = &ts->rx_bd_vbase[i];
+ skb = priv->rx_skb[i];
+ paddr = QTN_HOST_ADDR(0x0, le32_to_cpu(rxbd->addr));
+ pci_unmap_single(priv->pdev, paddr, SKB_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb_any(skb);
+ priv->rx_skb[i] = NULL;
+ rxbd->addr = 0;
+ rxbd->info = 0;
+ }
+ }
+
+ /* free tx buffers */
+ for (i = 0; i < priv->tx_bd_num; i++) {
+ if (priv->tx_skb && priv->tx_skb[i]) {
+ txbd = &ts->tx_bd_vbase[i];
+ skb = priv->tx_skb[i];
+ paddr = QTN_HOST_ADDR(0x0, le32_to_cpu(txbd->addr));
+ pci_unmap_single(priv->pdev, paddr, SKB_BUF_SIZE,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb_any(skb);
+ priv->tx_skb[i] = NULL;
+ txbd->addr = 0;
+ txbd->info = 0;
+ }
+ }
+}
+
+static int qtnf_pcie_topaz_init_xfer(struct qtnf_pcie_topaz_state *ts,
+ unsigned int tx_bd_size)
+{
+ struct qtnf_topaz_bda __iomem *bda = ts->bda;
+ struct qtnf_pcie_bus_priv *priv = &ts->base;
+ int ret;
+
+ if (tx_bd_size == 0)
+ tx_bd_size = TOPAZ_TX_BD_SIZE_DEFAULT;
+
+ /* check TX BD queue max length according to struct qtnf_topaz_bda */
+ if (tx_bd_size > QTN_PCIE_RC_TX_QUEUE_LEN) {
+ pr_warn("TX BD queue cannot exceed %d\n",
+ QTN_PCIE_RC_TX_QUEUE_LEN);
+ tx_bd_size = QTN_PCIE_RC_TX_QUEUE_LEN;
+ }
+
+ priv->tx_bd_num = tx_bd_size;
+ qtnf_non_posted_write(priv->tx_bd_num, &bda->bda_rc_tx_bd_num);
+ qtnf_non_posted_write(priv->rx_bd_num, &bda->bda_rc_rx_bd_num);
+
+ priv->rx_bd_w_index = 0;
+ priv->rx_bd_r_index = 0;
+
+ ret = qtnf_pcie_alloc_skb_array(priv);
+ if (ret) {
+ pr_err("failed to allocate skb array\n");
+ return ret;
+ }
+
+ ret = topaz_alloc_bd_table(ts, bda);
+ if (ret) {
+ pr_err("failed to allocate bd table\n");
+ return ret;
+ }
+
+ ret = topaz_alloc_rx_buffers(ts);
+ if (ret) {
+ pr_err("failed to allocate rx buffers\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static void qtnf_topaz_data_tx_reclaim(struct qtnf_pcie_topaz_state *ts)
+{
+ struct qtnf_pcie_bus_priv *priv = &ts->base;
+ struct qtnf_topaz_tx_bd *txbd;
+ struct sk_buff *skb;
+ unsigned long flags;
+ dma_addr_t paddr;
+ u32 tx_done_index;
+ int count = 0;
+ int i;
+
+ spin_lock_irqsave(&priv->tx_reclaim_lock, flags);
+
+ tx_done_index = readl(ts->ep_next_rx_pkt);
+ i = priv->tx_bd_r_index;
+
+ if (CIRC_CNT(priv->tx_bd_w_index, tx_done_index, priv->tx_bd_num))
+ writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_TX_DONE_IRQ),
+ TOPAZ_LH_IPC4_INT(priv->sysctl_bar));
+
+ while (CIRC_CNT(tx_done_index, i, priv->tx_bd_num)) {
+ skb = priv->tx_skb[i];
+
+ if (likely(skb)) {
+ txbd = &ts->tx_bd_vbase[i];
+ paddr = QTN_HOST_ADDR(0x0, le32_to_cpu(txbd->addr));
+ pci_unmap_single(priv->pdev, paddr, skb->len,
+ PCI_DMA_TODEVICE);
+
+ if (skb->dev) {
+ qtnf_update_tx_stats(skb->dev, skb);
+ if (unlikely(priv->tx_stopped)) {
+ qtnf_wake_all_queues(skb->dev);
+ priv->tx_stopped = 0;
+ }
+ }
+
+ dev_kfree_skb_any(skb);
+ }
+
+ priv->tx_skb[i] = NULL;
+ count++;
+
+ if (++i >= priv->tx_bd_num)
+ i = 0;
+ }
+
+ priv->tx_reclaim_done += count;
+ priv->tx_reclaim_req++;
+ priv->tx_bd_r_index = i;
+
+ spin_unlock_irqrestore(&priv->tx_reclaim_lock, flags);
+}
+
+static void qtnf_try_stop_xmit(struct qtnf_bus *bus, struct net_device *ndev)
+{
+ struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+
+ if (ndev) {
+ netif_tx_stop_all_queues(ndev);
+ ts->base.tx_stopped = 1;
+ }
+
+ writel(0x0, ts->txqueue_wake);
+
+ /* sync up tx queue status before generating interrupt */
+ dma_wmb();
+
+ /* send irq to card: tx stopped */
+ writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_TX_STOP_IRQ),
+ TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
+
+ /* schedule reclaim attempt */
+ tasklet_hi_schedule(&ts->base.reclaim_tq);
+}
+
+static void qtnf_try_wake_xmit(struct qtnf_bus *bus, struct net_device *ndev)
+{
+ struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+ int ready;
+
+ ready = readl(ts->txqueue_wake);
+ if (ready) {
+ netif_wake_queue(ndev);
+ } else {
+ /* re-send irq to card: tx stopped */
+ writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_TX_STOP_IRQ),
+ TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
+ }
+}
+
+static int qtnf_tx_queue_ready(struct qtnf_pcie_topaz_state *ts)
+{
+ struct qtnf_pcie_bus_priv *priv = &ts->base;
+
+ if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index,
+ priv->tx_bd_num)) {
+ qtnf_topaz_data_tx_reclaim(ts);
+
+ if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index,
+ priv->tx_bd_num)) {
+ priv->tx_full_count++;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int qtnf_pcie_data_tx(struct qtnf_bus *bus, struct sk_buff *skb)
+{
+ struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+ struct qtnf_pcie_bus_priv *priv = &ts->base;
+ struct qtnf_topaz_bda __iomem *bda = ts->bda;
+ struct qtnf_topaz_tx_bd *txbd;
+ dma_addr_t skb_paddr;
+ unsigned long flags;
+ int ret = 0;
+ int len;
+ int i;
+
+ spin_lock_irqsave(&priv->tx_lock, flags);
+
+ if (!qtnf_tx_queue_ready(ts)) {
+ qtnf_try_stop_xmit(bus, skb->dev);
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+ return NETDEV_TX_BUSY;
+ }
+
+ i = priv->tx_bd_w_index;
+ priv->tx_skb[i] = skb;
+ len = skb->len;
+
+ skb_paddr = pci_map_single(priv->pdev, skb->data,
+ skb->len, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(priv->pdev, skb_paddr)) {
+ ret = -ENOMEM;
+ goto tx_done;
+ }
+
+ txbd = &ts->tx_bd_vbase[i];
+ txbd->addr = cpu_to_le32(QTN_HOST_LO32(skb_paddr));
+
+ writel(QTN_HOST_LO32(skb_paddr), &bda->request[i].addr);
+ writel(len | QTN_PCIE_TX_VALID_PKT, &bda->request[i].info);
+
+ /* sync up descriptor updates before generating interrupt */
+ dma_wmb();
+
+ /* generate irq to card: tx done */
+ writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_TX_DONE_IRQ),
+ TOPAZ_LH_IPC4_INT(priv->sysctl_bar));
+
+ if (++i >= priv->tx_bd_num)
+ i = 0;
+
+ priv->tx_bd_w_index = i;
+
+tx_done:
+ if (ret) {
+ if (skb->dev)
+ skb->dev->stats.tx_dropped++;
+ dev_kfree_skb_any(skb);
+ }
+
+ priv->tx_done_count++;
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+ qtnf_topaz_data_tx_reclaim(ts);
+
+ return NETDEV_TX_OK;
+}
+
+static irqreturn_t qtnf_pcie_topaz_interrupt(int irq, void *data)
+{
+ struct qtnf_bus *bus = (struct qtnf_bus *)data;
+ struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+ struct qtnf_pcie_bus_priv *priv = &ts->base;
+
+ if (!priv->msi_enabled && !qtnf_topaz_intx_asserted(ts))
+ return IRQ_NONE;
+
+ priv->pcie_irq_count++;
+
+ qtnf_shm_ipc_irq_handler(&priv->shm_ipc_ep_in);
+ qtnf_shm_ipc_irq_handler(&priv->shm_ipc_ep_out);
+
+ if (napi_schedule_prep(&bus->mux_napi)) {
+ disable_rx_irqs(ts);
+ __napi_schedule(&bus->mux_napi);
+ }
+
+ tasklet_hi_schedule(&priv->reclaim_tq);
+
+ if (!priv->msi_enabled)
+ qtnf_deassert_intx(ts);
+
+ return IRQ_HANDLED;
+}
+
+static int qtnf_rx_data_ready(struct qtnf_pcie_topaz_state *ts)
+{
+ u16 index = ts->base.rx_bd_r_index;
+ struct qtnf_topaz_rx_bd *rxbd;
+ u32 descw;
+
+ rxbd = &ts->rx_bd_vbase[index];
+ descw = le32_to_cpu(rxbd->info);
+
+ if (descw & QTN_BD_EMPTY)
+ return 0;
+
+ return 1;
+}
+
+static int qtnf_topaz_rx_poll(struct napi_struct *napi, int budget)
+{
+ struct qtnf_bus *bus = container_of(napi, struct qtnf_bus, mux_napi);
+ struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+ struct qtnf_pcie_bus_priv *priv = &ts->base;
+ struct net_device *ndev = NULL;
+ struct sk_buff *skb = NULL;
+ int processed = 0;
+ struct qtnf_topaz_rx_bd *rxbd;
+ dma_addr_t skb_paddr;
+ int consume;
+ u32 descw;
+ u32 poffset;
+ u32 psize;
+ u16 r_idx;
+ u16 w_idx;
+ int ret;
+
+ while (processed < budget) {
+ if (!qtnf_rx_data_ready(ts))
+ goto rx_out;
+
+ r_idx = priv->rx_bd_r_index;
+ rxbd = &ts->rx_bd_vbase[r_idx];
+ descw = le32_to_cpu(rxbd->info);
+
+ skb = priv->rx_skb[r_idx];
+ poffset = QTN_GET_OFFSET(descw);
+ psize = QTN_GET_LEN(descw);
+ consume = 1;
+
+ if (descw & QTN_BD_EMPTY) {
+ pr_warn("skip invalid rxbd[%d]\n", r_idx);
+ consume = 0;
+ }
+
+ if (!skb) {
+ pr_warn("skip missing rx_skb[%d]\n", r_idx);
+ consume = 0;
+ }
+
+ if (skb && (skb_tailroom(skb) < psize)) {
+ pr_err("skip packet with invalid length: %u > %u\n",
+ psize, skb_tailroom(skb));
+ consume = 0;
+ }
+
+ if (skb) {
+ skb_paddr = QTN_HOST_ADDR(0x0, le32_to_cpu(rxbd->addr));
+ pci_unmap_single(priv->pdev, skb_paddr, SKB_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
+ }
+
+ if (consume) {
+ skb_reserve(skb, poffset);
+ skb_put(skb, psize);
+ ndev = qtnf_classify_skb(bus, skb);
+ if (likely(ndev)) {
+ qtnf_update_rx_stats(ndev, skb);
+ skb->protocol = eth_type_trans(skb, ndev);
+ netif_receive_skb(skb);
+ } else {
+ pr_debug("drop untagged skb\n");
+ bus->mux_dev.stats.rx_dropped++;
+ dev_kfree_skb_any(skb);
+ }
+ } else {
+ if (skb) {
+ bus->mux_dev.stats.rx_dropped++;
+ dev_kfree_skb_any(skb);
+ }
+ }
+
+ /* notify card about recv packets once per several packets */
+ if (((++ts->rx_pkt_count) & RX_DONE_INTR_MSK) == 0)
+ writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_RX_DONE_IRQ),
+ TOPAZ_LH_IPC4_INT(priv->sysctl_bar));
+
+ priv->rx_skb[r_idx] = NULL;
+ if (++r_idx >= priv->rx_bd_num)
+ r_idx = 0;
+
+ priv->rx_bd_r_index = r_idx;
+
+ /* repalce processed buffer by a new one */
+ w_idx = priv->rx_bd_w_index;
+ while (CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index,
+ priv->rx_bd_num) > 0) {
+ if (++w_idx >= priv->rx_bd_num)
+ w_idx = 0;
+
+ ret = topaz_skb2rbd_attach(ts, w_idx,
+ descw & QTN_BD_WRAP);
+ if (ret) {
+ pr_err("failed to allocate new rx_skb[%d]\n",
+ w_idx);
+ break;
+ }
+ }
+
+ processed++;
+ }
+
+rx_out:
+ if (processed < budget) {
+ napi_complete(napi);
+ enable_rx_irqs(ts);
+ }
+
+ return processed;
+}
+
+static void
+qtnf_pcie_data_tx_timeout(struct qtnf_bus *bus, struct net_device *ndev)
+{
+ struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+
+ qtnf_try_wake_xmit(bus, ndev);
+ tasklet_hi_schedule(&ts->base.reclaim_tq);
+}
+
+static void qtnf_pcie_data_rx_start(struct qtnf_bus *bus)
+{
+ struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+
+ napi_enable(&bus->mux_napi);
+ enable_rx_irqs(ts);
+}
+
+static void qtnf_pcie_data_rx_stop(struct qtnf_bus *bus)
+{
+ struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+
+ disable_rx_irqs(ts);
+ napi_disable(&bus->mux_napi);
+}
+
+static const struct qtnf_bus_ops qtnf_pcie_topaz_bus_ops = {
+ /* control path methods */
+ .control_tx = qtnf_pcie_control_tx,
+
+ /* data path methods */
+ .data_tx = qtnf_pcie_data_tx,
+ .data_tx_timeout = qtnf_pcie_data_tx_timeout,
+ .data_rx_start = qtnf_pcie_data_rx_start,
+ .data_rx_stop = qtnf_pcie_data_rx_stop,
+};
+
+static int qtnf_dbg_irq_stats(struct seq_file *s, void *data)
+{
+ struct qtnf_bus *bus = dev_get_drvdata(s->private);
+ struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+
+ seq_printf(s, "pcie_irq_count(%u)\n", ts->base.pcie_irq_count);
+
+ return 0;
+}
+
+static int qtnf_dbg_pkt_stats(struct seq_file *s, void *data)
+{
+ struct qtnf_bus *bus = dev_get_drvdata(s->private);
+ struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+ struct qtnf_pcie_bus_priv *priv = &ts->base;
+ u32 tx_done_index = readl(ts->ep_next_rx_pkt);
+
+ seq_printf(s, "tx_full_count(%u)\n", priv->tx_full_count);
+ seq_printf(s, "tx_done_count(%u)\n", priv->tx_done_count);
+ seq_printf(s, "tx_reclaim_done(%u)\n", priv->tx_reclaim_done);
+ seq_printf(s, "tx_reclaim_req(%u)\n", priv->tx_reclaim_req);
+
+ seq_printf(s, "tx_bd_r_index(%u)\n", priv->tx_bd_r_index);
+ seq_printf(s, "tx_done_index(%u)\n", tx_done_index);
+ seq_printf(s, "tx_bd_w_index(%u)\n", priv->tx_bd_w_index);
+
+ seq_printf(s, "tx host queue len(%u)\n",
+ CIRC_CNT(priv->tx_bd_w_index, priv->tx_bd_r_index,
+ priv->tx_bd_num));
+ seq_printf(s, "tx reclaim queue len(%u)\n",
+ CIRC_CNT(tx_done_index, priv->tx_bd_r_index,
+ priv->tx_bd_num));
+ seq_printf(s, "tx card queue len(%u)\n",
+ CIRC_CNT(priv->tx_bd_w_index, tx_done_index,
+ priv->tx_bd_num));
+
+ seq_printf(s, "rx_bd_r_index(%u)\n", priv->rx_bd_r_index);
+ seq_printf(s, "rx_bd_w_index(%u)\n", priv->rx_bd_w_index);
+ seq_printf(s, "rx alloc queue len(%u)\n",
+ CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index,
+ priv->rx_bd_num));
+
+ return 0;
+}
+
+static void qtnf_reset_dma_offset(struct qtnf_pcie_topaz_state *ts)
+{
+ struct qtnf_topaz_bda __iomem *bda = ts->bda;
+ u32 offset = readl(&bda->bda_dma_offset);
+
+ if ((offset & PCIE_DMA_OFFSET_ERROR_MASK) != PCIE_DMA_OFFSET_ERROR)
+ return;
+
+ writel(0x0, &bda->bda_dma_offset);
+}
+
+static int qtnf_pcie_endian_detect(struct qtnf_pcie_topaz_state *ts)
+{
+ struct qtnf_topaz_bda __iomem *bda = ts->bda;
+ u32 timeout = 0;
+ u32 endian;
+ int ret = 0;
+
+ writel(QTN_PCI_ENDIAN_DETECT_DATA, &bda->bda_pci_endian);
+
+ /* flush endian modifications before status update */
+ dma_wmb();
+
+ writel(QTN_PCI_ENDIAN_VALID_STATUS, &bda->bda_pci_pre_status);
+
+ while (readl(&bda->bda_pci_post_status) !=
+ QTN_PCI_ENDIAN_VALID_STATUS) {
+ usleep_range(1000, 1200);
+ if (++timeout > QTN_FW_DL_TIMEOUT_MS) {
+ pr_err("card endianness detection timed out\n");
+ ret = -ETIMEDOUT;
+ goto endian_out;
+ }
+ }
+
+ /* do not read before status is updated */
+ dma_rmb();
+
+ endian = readl(&bda->bda_pci_endian);
+ WARN(endian != QTN_PCI_LITTLE_ENDIAN,
+ "%s: unexpected card endianness", __func__);
+
+endian_out:
+ writel(0, &bda->bda_pci_pre_status);
+ writel(0, &bda->bda_pci_post_status);
+ writel(0, &bda->bda_pci_endian);
+
+ return ret;
+}
+
+static int qtnf_pre_init_ep(struct qtnf_bus *bus)
+{
+ struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+ struct qtnf_topaz_bda __iomem *bda = ts->bda;
+ u32 flags;
+ int ret;
+
+ ret = qtnf_pcie_endian_detect(ts);
+ if (ret < 0) {
+ pr_err("failed to detect card endianness\n");
+ return ret;
+ }
+
+ writeb(ts->base.msi_enabled, &ts->bda->bda_rc_msi_enabled);
+ qtnf_reset_dma_offset(ts);
+
+ /* notify card about driver type and boot mode */
+ flags = readl(&bda->bda_flags) | QTN_BDA_HOST_QLINK_DRV;
+
+ if (ts->base.flashboot)
+ flags |= QTN_BDA_FLASH_BOOT;
+ else
+ flags &= ~QTN_BDA_FLASH_BOOT;
+
+ writel(flags, &bda->bda_flags);
+
+ qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_HOST_RDY);
+ if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_TARGET_RDY,
+ QTN_FW_DL_TIMEOUT_MS)) {
+ pr_err("card is not ready to boot...\n");
+ return -ETIMEDOUT;
+ }
+
+ return ret;
+}
+
+static int qtnf_post_init_ep(struct qtnf_pcie_topaz_state *ts)
+{
+ struct pci_dev *pdev = ts->base.pdev;
+
+ setup_rx_irqs(ts);
+ disable_rx_irqs(ts);
+
+ if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_QLINK_DONE,
+ QTN_FW_QLINK_TIMEOUT_MS))
+ return -ETIMEDOUT;
+
+ enable_irq(pdev->irq);
+ return 0;
+}
+
+static int
+qtnf_ep_fw_load(struct qtnf_pcie_topaz_state *ts, const u8 *fw, u32 fw_size)
+{
+ struct qtnf_topaz_bda __iomem *bda = ts->bda;
+ struct pci_dev *pdev = ts->base.pdev;
+ u32 remaining = fw_size;
+ u8 *curr = (u8 *)fw;
+ u32 blksize;
+ u32 nblocks;
+ u32 offset;
+ u32 count;
+ u32 size;
+ dma_addr_t paddr;
+ void *data;
+ int ret = 0;
+
+ pr_debug("FW upload started: fw_addr = 0x%p, size=%d\n", fw, fw_size);
+
+ blksize = ts->base.fw_blksize;
+
+ if (blksize < PAGE_SIZE)
+ blksize = PAGE_SIZE;
+
+ while (blksize >= PAGE_SIZE) {
+ pr_debug("allocating %u bytes to upload FW\n", blksize);
+ data = dma_alloc_coherent(&pdev->dev, blksize,
+ &paddr, GFP_KERNEL);
+ if (data)
+ break;
+ blksize /= 2;
+ }
+
+ if (!data) {
+ pr_err("failed to allocate DMA buffer for FW upload\n");
+ ret = -ENOMEM;
+ goto fw_load_out;
+ }
+
+ nblocks = NBLOCKS(fw_size, blksize);
+ offset = readl(&bda->bda_dma_offset);
+
+ qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_HOST_LOAD);
+ if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_EP_RDY,
+ QTN_FW_DL_TIMEOUT_MS)) {
+ pr_err("card is not ready to download FW\n");
+ ret = -ETIMEDOUT;
+ goto fw_load_map;
+ }
+
+ for (count = 0 ; count < nblocks; count++) {
+ size = (remaining > blksize) ? blksize : remaining;
+
+ memcpy(data, curr, size);
+ qtnf_non_posted_write(paddr + offset, &bda->bda_img);
+ qtnf_non_posted_write(size, &bda->bda_img_size);
+
+ pr_debug("chunk[%u] VA[0x%p] PA[%pad] sz[%u]\n",
+ count, (void *)curr, &paddr, size);
+
+ qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_BLOCK_RDY);
+ if (qtnf_poll_state(&ts->bda->bda_bootstate,
+ QTN_BDA_FW_BLOCK_DONE,
+ QTN_FW_DL_TIMEOUT_MS)) {
+ pr_err("confirmation for block #%d timed out\n", count);
+ ret = -ETIMEDOUT;
+ goto fw_load_map;
+ }
+
+ remaining = (remaining < size) ? remaining : (remaining - size);
+ curr += size;
+ }
+
+ /* upload completion mark: zero-sized block */
+ qtnf_non_posted_write(0, &bda->bda_img);
+ qtnf_non_posted_write(0, &bda->bda_img_size);
+
+ qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_BLOCK_RDY);
+ if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_BLOCK_DONE,
+ QTN_FW_DL_TIMEOUT_MS)) {
+ pr_err("confirmation for the last block timed out\n");
+ ret = -ETIMEDOUT;
+ goto fw_load_map;
+ }
+
+ /* RC is done */
+ qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_BLOCK_END);
+ if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_LOAD_DONE,
+ QTN_FW_DL_TIMEOUT_MS)) {
+ pr_err("confirmation for FW upload completion timed out\n");
+ ret = -ETIMEDOUT;
+ goto fw_load_map;
+ }
+
+ pr_debug("FW upload completed: totally sent %d blocks\n", count);
+
+fw_load_map:
+ dma_free_coherent(&pdev->dev, blksize, data, paddr);
+
+fw_load_out:
+ return ret;
+}
+
+static int qtnf_topaz_fw_upload(struct qtnf_pcie_topaz_state *ts,
+ const char *fwname)
+{
+ const struct firmware *fw;
+ struct pci_dev *pdev = ts->base.pdev;
+ int ret;
+
+ if (qtnf_poll_state(&ts->bda->bda_bootstate,
+ QTN_BDA_FW_LOAD_RDY,
+ QTN_FW_DL_TIMEOUT_MS)) {
+ pr_err("%s: card is not ready\n", fwname);
+ return -1;
+ }
+
+ pr_info("starting firmware upload: %s\n", fwname);
+
+ ret = request_firmware(&fw, fwname, &pdev->dev);
+ if (ret < 0) {
+ pr_err("%s: request_firmware error %d\n", fwname, ret);
+ return -1;
+ }
+
+ ret = qtnf_ep_fw_load(ts, fw->data, fw->size);
+ release_firmware(fw);
+
+ if (ret)
+ pr_err("%s: FW upload error\n", fwname);
+
+ return ret;
+}
+
+static void qtnf_topaz_fw_work_handler(struct work_struct *work)
+{
+ struct qtnf_bus *bus = container_of(work, struct qtnf_bus, fw_work);
+ struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+ int ret;
+ int bootloader_needed = readl(&ts->bda->bda_flags) & QTN_BDA_XMIT_UBOOT;
+
+ qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_TARGET_BOOT);
+
+ if (bootloader_needed) {
+ ret = qtnf_topaz_fw_upload(ts, QTN_PCI_TOPAZ_BOOTLD_NAME);
+ if (ret)
+ goto fw_load_exit;
+
+ ret = qtnf_pre_init_ep(bus);
+ if (ret)
+ goto fw_load_exit;
+
+ qtnf_set_state(&ts->bda->bda_bootstate,
+ QTN_BDA_FW_TARGET_BOOT);
+ }
+
+ if (ts->base.flashboot) {
+ pr_info("booting firmware from flash\n");
+
+ ret = qtnf_poll_state(&ts->bda->bda_bootstate,
+ QTN_BDA_FW_FLASH_BOOT,
+ QTN_FW_DL_TIMEOUT_MS);
+ if (ret)
+ goto fw_load_exit;
+ } else {
+ ret = qtnf_topaz_fw_upload(ts, QTN_PCI_TOPAZ_FW_NAME);
+ if (ret)
+ goto fw_load_exit;
+
+ qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_START);
+ ret = qtnf_poll_state(&ts->bda->bda_bootstate,
+ QTN_BDA_FW_CONFIG,
+ QTN_FW_QLINK_TIMEOUT_MS);
+ if (ret) {
+ pr_err("FW bringup timed out\n");
+ goto fw_load_exit;
+ }
+
+ qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_RUN);
+ ret = qtnf_poll_state(&ts->bda->bda_bootstate,
+ QTN_BDA_FW_RUNNING,
+ QTN_FW_QLINK_TIMEOUT_MS);
+ if (ret) {
+ pr_err("card bringup timed out\n");
+ goto fw_load_exit;
+ }
+ }
+
+ pr_info("firmware is up and running\n");
+
+ ret = qtnf_post_init_ep(ts);
+ if (ret)
+ pr_err("FW runtime failure\n");
+
+fw_load_exit:
+ qtnf_pcie_fw_boot_done(bus, ret ? false : true);
+
+ if (ret == 0) {
+ qtnf_debugfs_add_entry(bus, "pkt_stats", qtnf_dbg_pkt_stats);
+ qtnf_debugfs_add_entry(bus, "irq_stats", qtnf_dbg_irq_stats);
+ }
+}
+
+static void qtnf_reclaim_tasklet_fn(unsigned long data)
+{
+ struct qtnf_pcie_topaz_state *ts = (void *)data;
+
+ qtnf_topaz_data_tx_reclaim(ts);
+}
+
+static u64 qtnf_topaz_dma_mask_get(void)
+{
+ return DMA_BIT_MASK(32);
+}
+
+static int qtnf_pcie_topaz_probe(struct qtnf_bus *bus, unsigned int tx_bd_num)
+{
+ struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+ struct pci_dev *pdev = ts->base.pdev;
+ struct qtnf_shm_ipc_int ipc_int;
+ unsigned long irqflags;
+ int ret;
+
+ bus->bus_ops = &qtnf_pcie_topaz_bus_ops;
+ INIT_WORK(&bus->fw_work, qtnf_topaz_fw_work_handler);
+ ts->bda = ts->base.epmem_bar;
+
+ /* assign host msi irq before card init */
+ if (ts->base.msi_enabled)
+ irqflags = IRQF_NOBALANCING;
+ else
+ irqflags = IRQF_NOBALANCING | IRQF_SHARED;
+
+ ret = devm_request_irq(&pdev->dev, pdev->irq,
+ &qtnf_pcie_topaz_interrupt,
+ irqflags, "qtnf_topaz_irq", (void *)bus);
+ if (ret) {
+ pr_err("failed to request pcie irq %d\n", pdev->irq);
+ return ret;
+ }
+
+ disable_irq(pdev->irq);
+
+ ret = qtnf_pre_init_ep(bus);
+ if (ret) {
+ pr_err("failed to init card\n");
+ return ret;
+ }
+
+ ret = qtnf_pcie_topaz_init_xfer(ts, tx_bd_num);
+ if (ret) {
+ pr_err("PCIE xfer init failed\n");
+ return ret;
+ }
+
+ tasklet_init(&ts->base.reclaim_tq, qtnf_reclaim_tasklet_fn,
+ (unsigned long)ts);
+ netif_napi_add(&bus->mux_dev, &bus->mux_napi,
+ qtnf_topaz_rx_poll, 10);
+
+ ipc_int.fn = qtnf_topaz_ipc_gen_ep_int;
+ ipc_int.arg = ts;
+ qtnf_pcie_init_shm_ipc(&ts->base, &ts->bda->bda_shm_reg1,
+ &ts->bda->bda_shm_reg2, &ipc_int);
+
+ return 0;
+}
+
+static void qtnf_pcie_topaz_remove(struct qtnf_bus *bus)
+{
+ struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+
+ qtnf_topaz_reset_ep(ts);
+ qtnf_topaz_free_xfer_buffers(ts);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int qtnf_pcie_topaz_suspend(struct qtnf_bus *bus)
+{
+ struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+ struct pci_dev *pdev = ts->base.pdev;
+
+ writel((u32 __force)PCI_D3hot, ts->ep_pmstate);
+ dma_wmb();
+ writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_PM_EP_IRQ),
+ TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
+
+ pci_save_state(pdev);
+ pci_enable_wake(pdev, PCI_D3hot, 1);
+ pci_set_power_state(pdev, PCI_D3hot);
+
+ return 0;
+}
+
+static int qtnf_pcie_topaz_resume(struct qtnf_bus *bus)
+{
+ struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+ struct pci_dev *pdev = ts->base.pdev;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ pci_enable_wake(pdev, PCI_D0, 0);
+
+ writel((u32 __force)PCI_D0, ts->ep_pmstate);
+ dma_wmb();
+ writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_PM_EP_IRQ),
+ TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
+
+ return 0;
+}
+#endif
+
+struct qtnf_bus *qtnf_pcie_topaz_alloc(struct pci_dev *pdev)
+{
+ struct qtnf_bus *bus;
+ struct qtnf_pcie_topaz_state *ts;
+
+ bus = devm_kzalloc(&pdev->dev, sizeof(*bus) + sizeof(*ts), GFP_KERNEL);
+ if (!bus)
+ return NULL;
+
+ ts = get_bus_priv(bus);
+ ts->base.probe_cb = qtnf_pcie_topaz_probe;
+ ts->base.remove_cb = qtnf_pcie_topaz_remove;
+ ts->base.dma_mask_get_cb = qtnf_topaz_dma_mask_get;
+#ifdef CONFIG_PM_SLEEP
+ ts->base.resume_cb = qtnf_pcie_topaz_resume;
+ ts->base.suspend_cb = qtnf_pcie_topaz_suspend;
+#endif
+
+ return bus;
+}
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_ipc.h b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_ipc.h
new file mode 100644
index 000000000000..eb30e9d08de2
--- /dev/null
+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_ipc.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2018 Quantenna Communications */
+
+#ifndef _QTN_FMAC_PCIE_IPC_H_
+#define _QTN_FMAC_PCIE_IPC_H_
+
+#include <linux/types.h>
+
+#include "shm_ipc_defs.h"
+
+/* EP/RC status and flags */
+#define QTN_BDA_PCIE_INIT 0x01
+#define QTN_BDA_PCIE_RDY 0x02
+#define QTN_BDA_FW_LOAD_RDY 0x03
+#define QTN_BDA_FW_LOAD_DONE 0x04
+#define QTN_BDA_FW_START 0x05
+#define QTN_BDA_FW_RUN 0x06
+#define QTN_BDA_FW_HOST_RDY 0x07
+#define QTN_BDA_FW_TARGET_RDY 0x11
+#define QTN_BDA_FW_TARGET_BOOT 0x12
+#define QTN_BDA_FW_FLASH_BOOT 0x13
+#define QTN_BDA_FW_QLINK_DONE 0x14
+#define QTN_BDA_FW_HOST_LOAD 0x08
+#define QTN_BDA_FW_BLOCK_DONE 0x09
+#define QTN_BDA_FW_BLOCK_RDY 0x0A
+#define QTN_BDA_FW_EP_RDY 0x0B
+#define QTN_BDA_FW_BLOCK_END 0x0C
+#define QTN_BDA_FW_CONFIG 0x0D
+#define QTN_BDA_FW_RUNNING 0x0E
+#define QTN_BDA_PCIE_FAIL 0x82
+#define QTN_BDA_FW_LOAD_FAIL 0x85
+
+#define QTN_BDA_RCMODE BIT(1)
+#define QTN_BDA_MSI BIT(2)
+#define QTN_BDA_HOST_CALCMD BIT(3)
+#define QTN_BDA_FLASH_PRESENT BIT(4)
+#define QTN_BDA_FLASH_BOOT BIT(5)
+#define QTN_BDA_XMIT_UBOOT BIT(6)
+#define QTN_BDA_HOST_QLINK_DRV BIT(7)
+#define QTN_BDA_TARGET_FBOOT_ERR BIT(8)
+#define QTN_BDA_TARGET_FWLOAD_ERR BIT(9)
+#define QTN_BDA_HOST_NOFW_ERR BIT(12)
+#define QTN_BDA_HOST_MEMALLOC_ERR BIT(13)
+#define QTN_BDA_HOST_MEMMAP_ERR BIT(14)
+#define QTN_BDA_VER(x) (((x) >> 4) & 0xFF)
+#define QTN_BDA_ERROR_MASK 0xFF00
+
+/* registers and shmem address macros */
+#if BITS_PER_LONG == 64
+#define QTN_HOST_HI32(a) ((u32)(((u64)a) >> 32))
+#define QTN_HOST_LO32(a) ((u32)(((u64)a) & 0xffffffffUL))
+#define QTN_HOST_ADDR(h, l) ((((u64)h) << 32) | ((u64)l))
+#elif BITS_PER_LONG == 32
+#define QTN_HOST_HI32(a) 0
+#define QTN_HOST_LO32(a) ((u32)(((u32)a) & 0xffffffffUL))
+#define QTN_HOST_ADDR(h, l) ((u32)l)
+#else
+#error Unexpected BITS_PER_LONG value
+#endif
+
+#define QTN_PCIE_BDA_VERSION 0x1001
+
+#define PCIE_BDA_NAMELEN 32
+
+#define QTN_PCIE_RC_TX_QUEUE_LEN 256
+#define QTN_PCIE_TX_VALID_PKT 0x80000000
+#define QTN_PCIE_PKT_LEN_MASK 0xffff
+
+#define QTN_BD_EMPTY ((uint32_t)0x00000001)
+#define QTN_BD_WRAP ((uint32_t)0x00000002)
+#define QTN_BD_MASK_LEN ((uint32_t)0xFFFF0000)
+#define QTN_BD_MASK_OFFSET ((uint32_t)0x0000FF00)
+
+#define QTN_GET_LEN(x) (((x) >> 16) & 0xFFFF)
+#define QTN_GET_OFFSET(x) (((x) >> 8) & 0xFF)
+#define QTN_SET_LEN(len) (((len) & 0xFFFF) << 16)
+#define QTN_SET_OFFSET(of) (((of) & 0xFF) << 8)
+
+#define RX_DONE_INTR_MSK ((0x1 << 6) - 1)
+
+#define PCIE_DMA_OFFSET_ERROR 0xFFFF
+#define PCIE_DMA_OFFSET_ERROR_MASK 0xFFFF
+
+#define QTN_PCI_ENDIAN_DETECT_DATA 0x12345678
+#define QTN_PCI_ENDIAN_REVERSE_DATA 0x78563412
+#define QTN_PCI_ENDIAN_VALID_STATUS 0x3c3c3c3c
+#define QTN_PCI_ENDIAN_INVALID_STATUS 0
+#define QTN_PCI_LITTLE_ENDIAN 0
+#define QTN_PCI_BIG_ENDIAN 0xffffffff
+
+#define NBLOCKS(size, blksize) \
+ ((size) / (blksize) + (((size) % (blksize) > 0) ? 1 : 0))
+
+#endif /* _QTN_FMAC_PCIE_IPC_H_ */
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_regs.h b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_regs.h
new file mode 100644
index 000000000000..4782e1ed3c2c
--- /dev/null
+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_regs.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2018 Quantenna Communications */
+
+#ifndef __TOPAZ_PCIE_H
+#define __TOPAZ_PCIE_H
+
+/* Topaz PCIe DMA registers */
+#define PCIE_DMA_WR_INTR_STATUS(base) ((base) + 0x9bc)
+#define PCIE_DMA_WR_INTR_MASK(base) ((base) + 0x9c4)
+#define PCIE_DMA_WR_INTR_CLR(base) ((base) + 0x9c8)
+#define PCIE_DMA_WR_ERR_STATUS(base) ((base) + 0x9cc)
+#define PCIE_DMA_WR_DONE_IMWR_ADDR_LOW(base) ((base) + 0x9D0)
+#define PCIE_DMA_WR_DONE_IMWR_ADDR_HIGH(base) ((base) + 0x9d4)
+
+#define PCIE_DMA_RD_INTR_STATUS(base) ((base) + 0x310)
+#define PCIE_DMA_RD_INTR_MASK(base) ((base) + 0x319)
+#define PCIE_DMA_RD_INTR_CLR(base) ((base) + 0x31c)
+#define PCIE_DMA_RD_ERR_STATUS_LOW(base) ((base) + 0x324)
+#define PCIE_DMA_RD_ERR_STATUS_HIGH(base) ((base) + 0x328)
+#define PCIE_DMA_RD_DONE_IMWR_ADDR_LOW(base) ((base) + 0x33c)
+#define PCIE_DMA_RD_DONE_IMWR_ADDR_HIGH(base) ((base) + 0x340)
+
+/* Topaz LHost IPC4 interrupt */
+#define TOPAZ_LH_IPC4_INT(base) ((base) + 0x13C)
+#define TOPAZ_LH_IPC4_INT_MASK(base) ((base) + 0x140)
+
+#define TOPAZ_RC_TX_DONE_IRQ (0)
+#define TOPAZ_RC_RST_EP_IRQ (1)
+#define TOPAZ_RC_TX_STOP_IRQ (2)
+#define TOPAZ_RC_RX_DONE_IRQ (3)
+#define TOPAZ_RC_PM_EP_IRQ (4)
+
+/* Topaz LHost M2L interrupt */
+#define TOPAZ_CTL_M2L_INT(base) ((base) + 0x2C)
+#define TOPAZ_CTL_M2L_INT_MASK(base) ((base) + 0x30)
+
+#define TOPAZ_RC_CTRL_IRQ (6)
+
+#define TOPAZ_IPC_IRQ_WORD(irq) (BIT(irq) | BIT(irq + 16))
+
+/* PCIe legacy INTx */
+#define TOPAZ_PCIE_CFG0_OFFSET (0x6C)
+#define TOPAZ_ASSERT_INTX BIT(9)
+
+#endif /* __TOPAZ_PCIE_H */
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qtn_hw_ids.h b/drivers/net/wireless/quantenna/qtnfmac/qtn_hw_ids.h
index 1fe798a9a667..165fcbb41255 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qtn_hw_ids.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/qtn_hw_ids.h
@@ -24,6 +24,7 @@
/* PCIE Device IDs */

#define PCIE_DEVICE_ID_QTN_PEARL (0x0008)
+#define PCIE_DEVICE_ID_QTN_TOPAZ (0x0008)

#define QTN_REG_SYS_CTRL_CSR 0x14
#define QTN_CHIP_ID_MASK 0xF0
@@ -35,6 +36,8 @@
/* FW names */

#define QTN_PCI_PEARL_FW_NAME "qtn/fmac_qsr10g.img"
+#define QTN_PCI_TOPAZ_FW_NAME "qtn/fmac_qsr1000.img"
+#define QTN_PCI_TOPAZ_BOOTLD_NAME "qtn/uboot_qsr1000.img"

static inline unsigned int qtnf_chip_id_get(const void __iomem *regs_base)
{
diff --git a/drivers/net/wireless/quantenna/qtnfmac/util.c b/drivers/net/wireless/quantenna/qtnfmac/util.c
index dfe3fe8a42c1..3bc96b264769 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/util.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/util.c
@@ -121,6 +121,8 @@ void qtnf_sta_list_free(struct qtnf_sta_list *list)
const char *qtnf_chipid_to_string(unsigned long chip_id)
{
switch (chip_id) {
+ case QTN_CHIP_ID_TOPAZ:
+ return "Topaz";
case QTN_CHIP_ID_PEARL:
return "Pearl revA";
case QTN_CHIP_ID_PEARL_B:
--
2.11.0


2018-10-08 11:36:30

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH 5/5] qtnfmac: add support for Topaz chipsets

Sergey Matyukevich <[email protected]> writes:

> This patch adds support for QSR1000/QSR2000 family of chipsets
> to qtnfmac_pcie platform driver.
>
> QSR1000/QSR2000 (aka Topaz) is a family of 80MHz, 11ac Wave2,
> 4x4/2x4/2x2 chips, including single and dual band devices.
> Depending on specific chip model and firmware in use, either
> STA or both STA and AP modes are supported.
>
> Patch adds Topaz support to qtnfmac_pcie driver. It is possible
> to enable both Topaz and Pearl support in kernel configuration.
> In that case proper platform bus will be selected on probing
> based on chip ID.
>
> Signed-off-by: Igor Mitsyanko <[email protected]>
> Signed-off-by: Sergey Matyukevich <[email protected]>
> Signed-off-by: Andrey Shevchenko <[email protected]>

[...]

> --- a/drivers/net/wireless/quantenna/qtnfmac/qtn_hw_ids.h
> +++ b/drivers/net/wireless/quantenna/qtnfmac/qtn_hw_ids.h
> @@ -24,6 +24,7 @@
> /* PCIE Device IDs */
>
> #define PCIE_DEVICE_ID_QTN_PEARL (0x0008)
> +#define PCIE_DEVICE_ID_QTN_TOPAZ (0x0008)

Same ids for both, is this really correct?

--
Kalle Valo

2018-10-08 13:36:13

by Sergey Matyukevich

[permalink] [raw]
Subject: Re: [PATCH 5/5] qtnfmac: add support for Topaz chipsets

> > This patch adds support for QSR1000/QSR2000 family of chipsets
> > to qtnfmac_pcie platform driver.
> >
> > QSR1000/QSR2000 (aka Topaz) is a family of 80MHz, 11ac Wave2,
> > 4x4/2x4/2x2 chips, including single and dual band devices.
> > Depending on specific chip model and firmware in use, either
> > STA or both STA and AP modes are supported.
> >
> > Patch adds Topaz support to qtnfmac_pcie driver. It is possible
> > to enable both Topaz and Pearl support in kernel configuration.
> > In that case proper platform bus will be selected on probing
> > based on chip ID.
> >
> > Signed-off-by: Igor Mitsyanko <[email protected]>
> > Signed-off-by: Sergey Matyukevich <[email protected]>
> > Signed-off-by: Andrey Shevchenko <[email protected]>
>
> [...]
>
> > --- a/drivers/net/wireless/quantenna/qtnfmac/qtn_hw_ids.h
> > +++ b/drivers/net/wireless/quantenna/qtnfmac/qtn_hw_ids.h
> > @@ -24,6 +24,7 @@
> > /* PCIE Device IDs */
> >
> > #define PCIE_DEVICE_ID_QTN_PEARL (0x0008)
> > +#define PCIE_DEVICE_ID_QTN_TOPAZ (0x0008)
>
> Same ids for both, is this really correct?

Yes, that is correct. This is one of the reasons why we switched
to chip ID for card identification. In fact, it can be configured
in firmware, but default PCI ID values are the same for both chipsets.

Regards,
Sergey

2018-10-13 12:14:52

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH 5/5] qtnfmac: add support for Topaz chipsets

Sergey Matyukevich <[email protected]> writes:

> This patch adds support for QSR1000/QSR2000 family of chipsets
> to qtnfmac_pcie platform driver.
>
> QSR1000/QSR2000 (aka Topaz) is a family of 80MHz, 11ac Wave2,
> 4x4/2x4/2x2 chips, including single and dual band devices.
> Depending on specific chip model and firmware in use, either
> STA or both STA and AP modes are supported.
>
> Patch adds Topaz support to qtnfmac_pcie driver. It is possible
> to enable both Topaz and Pearl support in kernel configuration.
> In that case proper platform bus will be selected on probing
> based on chip ID.
>
> Signed-off-by: Igor Mitsyanko <[email protected]>
> Signed-off-by: Sergey Matyukevich <[email protected]>
> Signed-off-by: Andrey Shevchenko <[email protected]>

[...]

> +config QTNFMAC_TOPAZ_PCIE
> + tristate "Quantenna QSR1000/QSR2000 PCIe support"
> + default n
> + depends on PCI && CFG80211
> + select QTNFMAC
> + select FW_LOADER
> + select CRC32
> + help
> + This option adds support for wireless adapters based on Quantenna
> + 802.11ac QSR1000/QSR2000 (aka Topaz) FullMAC chipset
> + running over PCIe.
> +
> + If you choose to build it as a module, two modules will be built:
> + qtnfmac.ko and qtnfmac_pcie.ko.

I'm not really fond of adding a Kconfig option for every supported
hardware version unless there are very good reasons (memory savings
etc). So is this really needed?

A much better approach would be to have a generic QTNFMAC_PCIE option
which can be used to include or exclude all PCI code.

--
Kalle Valo

2018-10-13 17:05:12

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH 1/5] qtnfmac: use 'help' in Kconfig

Sergey Matyukevich <[email protected]> wrote:

> Fix checkpatch warning: use preferred 'help' option in Kconfig.
>
> Signed-off-by: Sergey Matyukevich <[email protected]>

3 patches applied to wireless-drivers-next.git, thanks.

db62abe51853 qtnfmac: use 'help' in Kconfig
b458a033ca2f qtnfmac: use SPDX identifier for pcie bus layer files
4cb5054957b2 qtnfmac_pcie: cleanup Pearl platform headers

--
https://patchwork.kernel.org/patch/10630323/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches


2018-10-15 19:46:50

by Sergey Matyukevich

[permalink] [raw]
Subject: Re: [PATCH 5/5] qtnfmac: add support for Topaz chipsets

> > +config QTNFMAC_TOPAZ_PCIE
> > + tristate "Quantenna QSR1000/QSR2000 PCIe support"
> > + default n
> > + depends on PCI && CFG80211
> > + select QTNFMAC
> > + select FW_LOADER
> > + select CRC32
> > + help
> > + This option adds support for wireless adapters based on Quantenna
> > + 802.11ac QSR1000/QSR2000 (aka Topaz) FullMAC chipset
> > + running over PCIe.
> > +
> > + If you choose to build it as a module, two modules will be built:
> > + qtnfmac.ko and qtnfmac_pcie.ko.

> I'm not really fond of adding a Kconfig option for every supported
> hardware version unless there are very good reasons (memory savings
> etc). So is this really needed?

Yes, the idea was to save some memory building pcie backend for
the requested chip family only.

> A much better approach would be to have a generic QTNFMAC_PCIE option
> which can be used to include or exclude all PCI code.

Ok, sounds reasonable. Three Kconfig options for the single qtnfmac_pcie
module is way too much. We will drop chipset specific Kconfig knobs in
v2, keeping single Kconfig option for qtnfmac_pcie backend as a whole.

Thanks,
Sergey

2018-10-29 14:56:30

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH 5/5] qtnfmac: add support for Topaz chipsets

Sergey Matyukevich <[email protected]> writes:

>> > +config QTNFMAC_TOPAZ_PCIE
>> > + tristate "Quantenna QSR1000/QSR2000 PCIe support"
>> > + default n
>> > + depends on PCI && CFG80211
>> > + select QTNFMAC
>> > + select FW_LOADER
>> > + select CRC32
>> > + help
>> > + This option adds support for wireless adapters based on Quantenna
>> > + 802.11ac QSR1000/QSR2000 (aka Topaz) FullMAC chipset
>> > + running over PCIe.
>> > +
>> > + If you choose to build it as a module, two modules will be built:
>> > + qtnfmac.ko and qtnfmac_pcie.ko.
>
>> I'm not really fond of adding a Kconfig option for every supported
>> hardware version unless there are very good reasons (memory savings
>> etc). So is this really needed?
>
> Yes, the idea was to save some memory building pcie backend for
> the requested chip family only.
>
>> A much better approach would be to have a generic QTNFMAC_PCIE option
>> which can be used to include or exclude all PCI code.
>
> Ok, sounds reasonable. Three Kconfig options for the single qtnfmac_pcie
> module is way too much. We will drop chipset specific Kconfig knobs in
> v2, keeping single Kconfig option for qtnfmac_pcie backend as a whole.

Great, thanks.

--
Kalle Valo