diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c index 45fbcbdc7d9e..00d53d659685 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c @@ -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; 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 075619aec4cd..4fd0df5cfd4d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -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"); 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 */