From 667ff60d0161f1b7165fda6a0c074dac1b9082b6 Mon Sep 17 00:00:00 2001
From: Prasanna Kerekoppa <prasanna.kerekoppa@infineon.com>
Date: Thu, 11 Feb 2021 01:49:41 -0600
Subject: [PATCH 054/296] brcmfmac: Fix PCIE suspend/resume issue

This change fixes the issue of PCIE suspend issue for the chips
which has IPC version >= 6.
The root cause seems that the firmware uses PCIE IPC version >=6
which need the H2D_HOST_D3_INFORM message to enter into D3 suspend
state instead of the mailbox interrupt which puts the chip in suspend
state in PCIE IPC version 5.

Signed-off-by: Prasanna Kerekoppa <prasanna.kerekoppa@infineon.com>
Signed-off-by: Chung-Hsien Hsu <chung-hsien.hsu@infineon.com>
Signed-off-by: Chi-hsien Lin <chi-hsien.lin@infineon.com>
---
 .../broadcom/brcm80211/brcmfmac/msgbuf.c      |  66 ++++++++
 .../broadcom/brcm80211/brcmfmac/msgbuf.h      |   1 +
 .../broadcom/brcm80211/brcmfmac/pcie.c        | 144 ++++++++++++------
 .../broadcom/brcm80211/brcmfmac/pcie.h        |   2 +
 4 files changed, 170 insertions(+), 43 deletions(-)

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
index 2974dd5ba2ee..29ade00cbec0 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
@@ -24,6 +24,7 @@
 #include "flowring.h"
 #include "bus.h"
 #include "tracepoint.h"
+#include "pcie.h"
 
 
 #define MSGBUF_IOCTL_RESP_TIMEOUT		msecs_to_jiffies(2000)
@@ -48,6 +49,8 @@
 #define MSGBUF_TYPE_RX_CMPLT			0x12
 #define MSGBUF_TYPE_LPBK_DMAXFER		0x13
 #define MSGBUF_TYPE_LPBK_DMAXFER_CMPLT		0x14
+#define MSGBUF_TYPE_H2D_MAILBOX_DATA		0x23
+#define MSGBUF_TYPE_D2H_MAILBOX_DATA		0x24
 
 #define NR_TX_PKTIDS				2048
 #define NR_RX_PKTIDS				1024
@@ -105,6 +108,12 @@ struct msgbuf_tx_msghdr {
 	__le32				rsvd0;
 };
 
+struct msgbuf_h2d_mbdata {
+	struct msgbuf_common_hdr	msg;
+	__le32				mbdata;
+	__le16				rsvd0[7];
+};
+
 struct msgbuf_rx_bufpost {
 	struct msgbuf_common_hdr	msg;
 	__le16				metadata_buf_len;
@@ -219,6 +228,13 @@ struct msgbuf_flowring_flush_resp {
 	__le32				rsvd0[3];
 };
 
+struct msgbuf_d2h_mailbox_data {
+	struct msgbuf_common_hdr	msg;
+	struct msgbuf_completion_hdr	compl_hdr;
+	__le32				mbdata;
+	__le32				rsvd0[2];
+} d2h_mailbox_data_t;
+
 struct brcmf_msgbuf_work_item {
 	struct list_head queue;
 	u32 flowid;
@@ -291,6 +307,8 @@ struct brcmf_msgbuf_pktids {
 };
 
 static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf);
+static void brcmf_msgbuf_process_d2h_mbdata(struct brcmf_msgbuf *msgbuf,
+					    void *buf);
 
 
 static struct brcmf_msgbuf_pktids *
@@ -428,6 +446,34 @@ static void brcmf_msgbuf_release_pktids(struct brcmf_msgbuf *msgbuf)
 					   msgbuf->tx_pktids);
 }
 
+int brcmf_msgbuf_tx_mbdata(struct brcmf_pub *drvr, u32 mbdata)
+{
+	struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+	struct brcmf_commonring *commonring;
+	struct msgbuf_h2d_mbdata *h2d_mbdata;
+	void *ret_ptr;
+	int err;
+
+	commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
+	brcmf_commonring_lock(commonring);
+	ret_ptr = brcmf_commonring_reserve_for_write(commonring);
+	if (!ret_ptr) {
+		brcmf_err("Failed to reserve space in commonring\n");
+		brcmf_commonring_unlock(commonring);
+		return -ENOMEM;
+	}
+	h2d_mbdata = (struct msgbuf_h2d_mbdata *)ret_ptr;
+	memset(h2d_mbdata, 0, sizeof(*h2d_mbdata));
+
+	h2d_mbdata->msg.msgtype = MSGBUF_TYPE_H2D_MAILBOX_DATA;
+	h2d_mbdata->mbdata = cpu_to_le32(mbdata);
+
+	err = brcmf_commonring_write_complete(commonring);
+	brcmf_commonring_unlock(commonring);
+
+	return err;
+}
+
 
 static int brcmf_msgbuf_tx_ioctl(struct brcmf_pub *drvr, int ifidx,
 				 uint cmd, void *buf, uint len)
@@ -1315,6 +1361,21 @@ brcmf_msgbuf_process_flow_ring_delete_response(struct brcmf_msgbuf *msgbuf,
 	brcmf_msgbuf_remove_flowring(msgbuf, flowid);
 }
 
+static void
+brcmf_msgbuf_process_d2h_mbdata(struct brcmf_msgbuf *msgbuf,
+				void *buf)
+{
+	struct msgbuf_d2h_mailbox_data *d2h_mbdata;
+
+	d2h_mbdata = (struct msgbuf_d2h_mailbox_data *)buf;
+
+	if (!d2h_mbdata) {
+		brcmf_err("d2h_mbdata is null\n");
+		return;
+	}
+
+	brcmf_pcie_handle_mb_data(msgbuf->drvr->bus_if, d2h_mbdata->mbdata);
+}
 
 static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf)
 {
@@ -1358,6 +1419,11 @@ static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf)
 		brcmf_dbg(MSGBUF, "MSGBUF_TYPE_RX_CMPLT\n");
 		brcmf_msgbuf_process_rx_complete(msgbuf, buf);
 		break;
+	case MSGBUF_TYPE_D2H_MAILBOX_DATA:
+		brcmf_dbg(MSGBUF, "MSGBUF_TYPE_D2H_MAILBOX_DATA\n");
+		brcmf_msgbuf_process_d2h_mbdata(msgbuf, buf);
+		break;
+
 	default:
 		bphy_err(drvr, "Unsupported msgtype %d\n", msg->msgtype);
 		break;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
index 6a849f4a94dd..99359a152345 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
@@ -39,5 +39,6 @@ static inline int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
 }
 static inline void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) {}
 #endif
+int brcmf_msgbuf_tx_mbdata(struct brcmf_pub *drvr, u32 mbdata);
 
 #endif /* BRCMFMAC_MSGBUF_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
index d9effcbee672..7f4ad9063b02 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -211,12 +211,14 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
 						 BRCMF_PCIE_64_MB_INT_D2H7_DB0 | \
 						 BRCMF_PCIE_64_MB_INT_D2H7_DB1)
 
+#define BRCMF_PCIE_SHARED_VERSION_6		6
 #define BRCMF_PCIE_SHARED_VERSION_7		7
 #define BRCMF_PCIE_MIN_SHARED_VERSION		5
 #define BRCMF_PCIE_MAX_SHARED_VERSION		BRCMF_PCIE_SHARED_VERSION_7
 #define BRCMF_PCIE_SHARED_VERSION_MASK		0x00FF
 #define BRCMF_PCIE_SHARED_DMA_INDEX		0x10000
 #define BRCMF_PCIE_SHARED_DMA_2B_IDX		0x100000
+#define BRCMF_PCIE_SHARED_USE_MAILBOX		0x2000000
 #define BRCMF_PCIE_SHARED_HOSTRDY_DB1		0x10000000
 
 #define BRCMF_PCIE_FLAGS_HTOD_SPLIT		0x4000
@@ -233,6 +235,7 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
 #define BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET	56
 #define BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET	64
 #define BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET	68
+#define BRCMF_SHARED_HOST_CAP_OFFSET		84
 
 #define BRCMF_RING_H2D_RING_COUNT_OFFSET	0
 #define BRCMF_RING_D2H_RING_COUNT_OFFSET	1
@@ -247,6 +250,8 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
 
 #define BRCMF_DEF_MAX_RXBUFPOST			255
 
+#define BRCMF_H2D_ENABLE_HOSTRDY		0x400
+
 #define BRCMF_CONSOLE_BUFADDR_OFFSET		8
 #define BRCMF_CONSOLE_BUFSIZE_OFFSET		12
 #define BRCMF_CONSOLE_WRITEIDX_OFFSET		16
@@ -347,6 +352,9 @@ struct brcmf_pciedev_info {
 	struct brcmf_chip *ci;
 	u32 coreid;
 	struct brcmf_pcie_shared_info shared;
+	u8 hostready;
+	bool use_mailbox;
+	bool use_d0_inform;
 	wait_queue_head_t mbdata_resp_wait;
 	bool mbdata_completed;
 	bool irq_allocated;
@@ -1020,41 +1028,53 @@ static int
 brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data)
 {
 	struct brcmf_pcie_shared_info *shared;
+	struct brcmf_bus *bus;
+	int err;
 	struct brcmf_core *core;
 	u32 addr;
 	u32 cur_htod_mb_data;
 	u32 i;
 
 	shared = &devinfo->shared;
-	addr = shared->htod_mb_data_addr;
-	cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
-
-	if (cur_htod_mb_data != 0)
-		brcmf_dbg(PCIE, "MB transaction is already pending 0x%04x\n",
-			  cur_htod_mb_data);
-
-	i = 0;
-	while (cur_htod_mb_data != 0) {
-		msleep(10);
-		i++;
-		if (i > 100)
-			return -EIO;
+	bus = dev_get_drvdata(&devinfo->pdev->dev);
+	if (shared->version >= BRCMF_PCIE_SHARED_VERSION_6 &&
+	    !devinfo->use_mailbox) {
+		err = brcmf_msgbuf_tx_mbdata(bus->drvr, htod_mb_data);
+		if (err) {
+			brcmf_err(bus, "sendimg mbdata failed err=%d\n", err);
+			return err;
+		}
+	} else {
+		addr = shared->htod_mb_data_addr;
 		cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
-	}
 
-	brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data);
-	pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1);
+		if (cur_htod_mb_data != 0)
+			brcmf_dbg(PCIE, "MB transaction is already pending 0x%04x\n",
+				  cur_htod_mb_data);
+
+		i = 0;
+		while (cur_htod_mb_data != 0) {
+			msleep(10);
+			i++;
+			if (i > 100)
+				return -EIO;
+			cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
+		}
 
-	/* Send mailbox interrupt twice as a hardware workaround */
-	core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2);
-	if (core->rev <= 13)
+		brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data);
 		pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1);
 
+		/* Send mailbox interrupt twice as a hardware workaround */
+		core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2);
+		if (core->rev <= 13)
+			pci_write_config_dword(devinfo->pdev,
+					       BRCMF_PCIE_REG_SBMBX, 1);
+	}
 	return 0;
 }
 
 
-static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo)
+static u32 brcmf_pcie_read_mb_data(struct brcmf_pciedev_info *devinfo)
 {
 	struct brcmf_pcie_shared_info *shared;
 	u32 addr;
@@ -1063,32 +1083,37 @@ static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo)
 	shared = &devinfo->shared;
 	addr = shared->dtoh_mb_data_addr;
 	dtoh_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
+	brcmf_pcie_write_tcm32(devinfo, addr, 0);
+	return dtoh_mb_data;
+}
 
-	if (!dtoh_mb_data)
-		return;
+void brcmf_pcie_handle_mb_data(struct brcmf_bus *bus_if, u32 d2h_mb_data)
+{
+	struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
+	struct brcmf_pciedev_info *devinfo = buspub->devinfo;
 
-	brcmf_pcie_write_tcm32(devinfo, addr, 0);
+	brcmf_dbg(INFO, "D2H_MB_DATA: 0x%04x\n", d2h_mb_data);
 
-	brcmf_dbg(PCIE, "D2H_MB_DATA: 0x%04x\n", dtoh_mb_data);
-	if (dtoh_mb_data & BRCMF_D2H_DEV_DS_ENTER_REQ)  {
-		brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP REQ\n");
+	if (d2h_mb_data & BRCMF_D2H_DEV_DS_ENTER_REQ) {
+		brcmf_dbg(INFO, "D2H_MB_DATA: DEEP SLEEP REQ\n");
 		brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_DS_ACK);
-		brcmf_dbg(PCIE, "D2H_MB_DATA: sent DEEP SLEEP ACK\n");
+		brcmf_dbg(INFO, "D2H_MB_DATA: sent DEEP SLEEP ACK\n");
 	}
-	if (dtoh_mb_data & BRCMF_D2H_DEV_DS_EXIT_NOTE)
-		brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP EXIT\n");
-	if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) {
-		brcmf_dbg(PCIE, "D2H_MB_DATA: D3 ACK\n");
+
+	if (d2h_mb_data & BRCMF_D2H_DEV_DS_EXIT_NOTE)
+		brcmf_dbg(INFO, "D2H_MB_DATA: DEEP SLEEP EXIT\n");
+	if (d2h_mb_data & BRCMF_D2H_DEV_D3_ACK) {
+		brcmf_dbg(INFO, "D2H_MB_DATA: D3 ACK\n");
 		devinfo->mbdata_completed = true;
 		wake_up(&devinfo->mbdata_resp_wait);
 	}
-	if (dtoh_mb_data & BRCMF_D2H_DEV_FWHALT) {
-		brcmf_dbg(PCIE, "D2H_MB_DATA: FW HALT\n");
+
+	if (d2h_mb_data & BRCMF_D2H_DEV_FWHALT) {
+		brcmf_dbg(INFO, "D2H_MB_DATA: FW HALT\n");
 		brcmf_fw_crashed(&devinfo->pdev->dev);
 	}
 }
 
-
 static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo)
 {
 	struct brcmf_pcie_shared_info *shared;
@@ -1199,6 +1224,9 @@ static irqreturn_t brcmf_pcie_isr_thread(int irq, void *arg)
 {
 	struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
 	u32 status;
+	u32 d2h_mbdata;
+	struct pci_dev *pdev = devinfo->pdev;
+	struct brcmf_bus *bus = dev_get_drvdata(&pdev->dev);
 
 	devinfo->in_irq = true;
 	status = brcmf_pcie_read_reg32(devinfo, devinfo->reginfo->mailboxint);
@@ -1206,8 +1234,10 @@ static irqreturn_t brcmf_pcie_isr_thread(int irq, void *arg)
 	if (status) {
 		brcmf_pcie_write_reg32(devinfo, devinfo->reginfo->mailboxint,
 				       status);
-		if (status & devinfo->reginfo->int_fn0)
-			brcmf_pcie_handle_mb_data(devinfo);
+		if (status & devinfo->reginfo->int_fn0) {
+			d2h_mbdata = brcmf_pcie_read_mb_data(devinfo);
+			brcmf_pcie_handle_mb_data(bus, d2h_mbdata);
+		}
 		if (status & devinfo->reginfo->int_d2h_db) {
 			if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
 				brcmf_proto_msgbuf_rx_trigger(
@@ -1889,6 +1919,7 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo,
 	struct brcmf_bus *bus = dev_get_drvdata(&devinfo->pdev->dev);
 	struct brcmf_pcie_shared_info *shared;
 	u32 addr;
+	u32 host_cap;
 
 	shared = &devinfo->shared;
 	shared->tcm_base_address = sharedram_addr;
@@ -1928,6 +1959,26 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo,
 	addr = sharedram_addr + BRCMF_SHARED_RING_INFO_ADDR_OFFSET;
 	shared->ring_info_addr = brcmf_pcie_read_tcm32(devinfo, addr);
 
+	if (shared->version >= BRCMF_PCIE_SHARED_VERSION_6) {
+		host_cap = shared->version;
+
+		devinfo->hostready =
+			((shared->flags & BRCMF_PCIE_SHARED_HOSTRDY_DB1)
+			 == BRCMF_PCIE_SHARED_HOSTRDY_DB1);
+		if (devinfo->hostready) {
+			brcmf_dbg(PCIE, "HostReady supported by dongle.\n");
+			host_cap = host_cap | BRCMF_H2D_ENABLE_HOSTRDY;
+		}
+		devinfo->use_mailbox =
+			((shared->flags & BRCMF_PCIE_SHARED_USE_MAILBOX)
+			 == BRCMF_PCIE_SHARED_USE_MAILBOX);
+		devinfo->use_d0_inform = false;
+		addr = sharedram_addr + BRCMF_SHARED_HOST_CAP_OFFSET;
+		brcmf_pcie_write_tcm32(devinfo, addr, host_cap);
+	} else {
+		devinfo->use_d0_inform = true;
+	}
+
 	brcmf_dbg(PCIE, "max rx buf post %d, rx dataoffset %d\n",
 		  shared->max_rxbufpost, shared->rx_dataoffset);
 
@@ -2904,14 +2955,25 @@ static int brcmf_pcie_pm_leave_D3(struct device *dev)
 	/* Check if device is still up and running, if so we are ready */
 	if (brcmf_pcie_read_reg32(devinfo, devinfo->reginfo->intmask) != 0) {
 		brcmf_dbg(PCIE, "Try to wakeup device....\n");
-		if (brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM))
-			goto cleanup;
+		if (devinfo->use_d0_inform) {
+			if (brcmf_pcie_send_mb_data(devinfo,
+						    BRCMF_H2D_HOST_D0_INFORM))
+				goto cleanup;
+		} else {
+			brcmf_pcie_hostready(devinfo);
+		}
+
 		brcmf_dbg(PCIE, "Hot resume, continue....\n");
 		devinfo->state = BRCMFMAC_PCIE_STATE_UP;
 		brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
 		brcmf_bus_change_state(bus, BRCMF_BUS_UP);
 		brcmf_pcie_intr_enable(devinfo);
-		brcmf_pcie_hostready(devinfo);
+		if (devinfo->use_d0_inform) {
+			brcmf_dbg(TRACE, "sending brcmf_pcie_hostready since use_d0_inform=%d\n",
+				  devinfo->use_d0_inform);
+			brcmf_pcie_hostready(devinfo);
+		}
+
 		brcmf_pcie_fwcon_timer(devinfo, true);
 		return 0;
 	}
@@ -2988,10 +3050,8 @@ static const struct pci_device_id brcmf_pcie_devid_table[] = {
 	{ /* end: all zeroes */ }
 };
 
-
 MODULE_DEVICE_TABLE(pci, brcmf_pcie_devid_table);
 
-
 static struct pci_driver brcmf_pciedrvr = {
 	.node = {},
 	.name = KBUILD_MODNAME,
@@ -3004,14 +3064,12 @@ static struct pci_driver brcmf_pciedrvr = {
 	.driver.coredump = brcmf_dev_coredump,
 };
 
-
 int brcmf_pcie_register(void)
 {
 	brcmf_dbg(PCIE, "Enter\n");
 	return pci_register_driver(&brcmf_pciedrvr);
 }
 
-
 void brcmf_pcie_exit(void)
 {
 	brcmf_dbg(PCIE, "Enter\n");
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h
index 8e6c227e8315..8e4f48637894 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h
@@ -11,4 +11,6 @@ struct brcmf_pciedev {
 	struct brcmf_pciedev_info *devinfo;
 };
 
+void brcmf_pcie_handle_mb_data(struct brcmf_bus *bus_if, u32 d2h_mb_data);
+
 #endif /* BRCMFMAC_PCIE_H */
-- 
2.25.1

