NVIDIA: SAUCE: brcmfmac: Fix PCIE suspend/resume issue

BugLink: https://bugs.launchpad.net/bugs/2072591

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.

http://nvbugs/4005228

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>
Signed-off-by: Revanth Kumar Uppala <ruppala@nvidia.com>
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Acked-by: Jacob Martin <jacob.martin@canonical.com>
Acked-by: Noah Wager <noah.wager@canonical.com>
Signed-off-by: Noah Wager <noah.wager@canonical.com>
This commit is contained in:
Prasanna Kerekoppa
2022-04-12 03:00:47 +00:00
committed by Noah Wager
parent 4f62c8976f
commit 1e75606950
4 changed files with 173 additions and 44 deletions
@@ -23,6 +23,7 @@
#include "flowring.h"
#include "bus.h"
#include "tracepoint.h"
#include "pcie.h"
#define MSGBUF_IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000)
@@ -47,6 +48,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
@@ -104,6 +107,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;
@@ -218,6 +227,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;
@@ -290,6 +306,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 *
@@ -427,6 +445,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)
@@ -1284,6 +1330,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)
{
@@ -1327,6 +1388,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;
@@ -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 */
@@ -210,12 +210,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
@@ -232,6 +234,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
@@ -246,6 +249,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
@@ -346,6 +351,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;
@@ -762,41 +770,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);
/* Send mailbox interrupt twice as a hardware workaround */
core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2);
if (core->rev <= 13)
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);
}
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;
@@ -805,32 +825,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);
if (!dtoh_mb_data)
return;
brcmf_pcie_write_tcm32(devinfo, addr, 0);
return dtoh_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");
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_dbg(INFO, "D2H_MB_DATA: 0x%04x\n", d2h_mb_data);
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;
@@ -941,6 +966,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);
@@ -948,9 +976,12 @@ 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_d2h_db) {
if (status & devinfo->reginfo->int_fn0) {
d2h_mbdata = brcmf_pcie_read_mb_data(devinfo);
brcmf_pcie_handle_mb_data(bus, d2h_mbdata);
}
if (status & BRCMF_PCIE_MB_INT_D2H_DB) {
if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
brcmf_proto_msgbuf_rx_trigger(
&devinfo->pdev->dev);
@@ -1620,6 +1651,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;
@@ -1659,6 +1691,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);
@@ -2626,8 +2678,14 @@ 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);
@@ -2635,6 +2693,12 @@ static int brcmf_pcie_pm_leave_D3(struct device *dev)
brcmf_pcie_intr_enable(devinfo);
brcmf_pcie_hostready(devinfo);
brcmf_pcie_fwcon_timer(devinfo, true);
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);
}
return 0;
}
@@ -2710,10 +2774,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 = {
.name = KBUILD_MODNAME,
.id_table = brcmf_pcie_devid_table,
@@ -2725,14 +2787,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");
@@ -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 */