From: Hector Martin <[email protected]>

Shared protocol version 6 and later allows mailbox messages over the
control ring as an alternative to the shared-memory mailbox.

Advertise host capabilities using the negotiated shared protocol
version. Select the control-ring transport when firmware does not set
BRCMF_PCIE_SHARED_USE_MAILBOX, and dispatch received mailbox data
through bus operations.

Signed-off-by: Hector Martin <[email protected]>
Co-developed-by: Michael Reeves <[email protected]>
Signed-off-by: Michael Reeves <[email protected]>
---
 .../net/wireless/broadcom/brcm80211/brcmfmac/bus.h |  9 +++
 .../wireless/broadcom/brcm80211/brcmfmac/msgbuf.c  | 57 +++++++++++++++
 .../wireless/broadcom/brcm80211/brcmfmac/msgbuf.h  |  5 ++
 .../wireless/broadcom/brcm80211/brcmfmac/pcie.c    | 83 ++++++++++++++++++----
 4 files changed, 139 insertions(+), 15 deletions(-)

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h 
b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
index 9371c14899..421039759b 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
@@ -86,6 +86,8 @@ struct brcmf_bus_dcmd {
  * @get_ramsize: obtain size of device memory.
  * @get_memdump: obtain device memory dump in provided buffer.
  * @get_blob: obtain a firmware blob.
+ * @d2h_mb_rx: handle device-to-host mailbox data received through a transport
+ *     specific side channel.
  * @remove: initiate unbind of the device.
  *
  * This structure provides an abstract interface towards the
@@ -108,6 +110,7 @@ struct brcmf_bus_ops {
        void (*debugfs_create)(struct device *dev);
        int (*reset)(struct device *dev);
        void (*remove)(struct device *dev);
+       void (*d2h_mb_rx)(struct device *dev, u32 data);
 };
 
 
@@ -292,6 +295,12 @@ static inline void brcmf_bus_remove(struct brcmf_bus *bus)
        bus->ops->remove(bus->dev);
 }
 
+static inline void brcmf_bus_d2h_mb_rx(struct brcmf_bus *bus, u32 data)
+{
+       if (bus->ops->d2h_mb_rx)
+               bus->ops->d2h_mb_rx(bus->dev, data);
+}
+
 /*
  * interface functions from common layer
  */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c 
b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
index 069ba70166..8e5c697e7f 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
@@ -47,6 +47,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
@@ -218,6 +220,19 @@ struct msgbuf_flowring_flush_resp {
        __le32                          rsvd0[3];
 };
 
+struct msgbuf_h2d_mailbox_data {
+       struct msgbuf_common_hdr        msg;
+       __le32                          data;
+       __le32                          rsvd0[7];
+};
+
+struct msgbuf_d2h_mailbox_data {
+       struct msgbuf_common_hdr        msg;
+       struct msgbuf_completion_hdr    compl_hdr;
+       __le32                          data;
+       __le32                          rsvd0[2];
+};
+
 struct brcmf_msgbuf_work_item {
        struct list_head queue;
        u32 flowid;
@@ -1319,6 +1334,15 @@ brcmf_msgbuf_process_flow_ring_delete_response(struct 
brcmf_msgbuf *msgbuf,
        brcmf_msgbuf_remove_flowring(msgbuf, flowid);
 }
 
+static void brcmf_msgbuf_process_d2h_mailbox_data(struct brcmf_msgbuf *msgbuf,
+                                                 void *buf)
+{
+       struct msgbuf_d2h_mailbox_data *d2h_mb_data = buf;
+
+       brcmf_bus_d2h_mb_rx(msgbuf->drvr->bus_if,
+                           le32_to_cpu(d2h_mb_data->data));
+}
+
 
 static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void 
*buf)
 {
@@ -1362,6 +1386,10 @@ 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_mailbox_data(msgbuf, buf);
+               break;
        default:
                bphy_err(drvr, "Unsupported msgtype %d\n", msg->msgtype);
                break;
@@ -1500,6 +1528,35 @@ void brcmf_msgbuf_delete_flowring(struct brcmf_pub 
*drvr, u16 flowid)
        }
 }
 
+int brcmf_msgbuf_h2d_mb_write(struct brcmf_pub *drvr, u32 data)
+{
+       struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+       struct brcmf_commonring *commonring;
+       struct msgbuf_h2d_mailbox_data *request;
+       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) {
+               bphy_err(drvr, "Failed to reserve space in commonring\n");
+               brcmf_commonring_unlock(commonring);
+               return -ENOMEM;
+       }
+
+       request = (struct msgbuf_h2d_mailbox_data *)ret_ptr;
+       memset(request, 0, sizeof(*request));
+       request->msg.msgtype = MSGBUF_TYPE_H2D_MAILBOX_DATA;
+       request->msg.ifidx = 0xff;
+       request->data = cpu_to_le32(data);
+
+       err = brcmf_commonring_write_complete(commonring);
+       brcmf_commonring_unlock(commonring);
+
+       return err;
+}
+
 #ifdef DEBUG
 static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data)
 {
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h 
b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
index 6a849f4a94..18b56d0789 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
@@ -32,12 +32,17 @@ int brcmf_proto_msgbuf_rx_trigger(struct device *dev);
 void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid);
 int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr);
 void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr);
+int brcmf_msgbuf_h2d_mb_write(struct brcmf_pub *drvr, u32 data);
 #else
 static inline int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
 {
        return 0;
 }
 static inline void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) {}
+static inline int brcmf_msgbuf_h2d_mb_write(struct brcmf_pub *drvr, u32 data)
+{
+       return -EOPNOTSUPP;
+}
 #endif
 
 #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 5917ee5fd7..ad8567e2b9 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -219,9 +219,13 @@ static const struct brcmf_firmware_mapping 
brcmf_pcie_fwnames[] = {
 #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_SHARED_DAR                  0x80000000
 
+#define BRCMF_HOSTCAP_H2D_ENABLE_HOSTRDY       0x400
+#define BRCMF_HOSTCAP_DS_NO_OOB_DW             0x1000
+#define BRCMF_HOSTCAP_H2D_DAR                  0x10000
 
 #define BRCMF_PCIE_FLAGS_HTOD_SPLIT            0x4000
 #define BRCMF_PCIE_FLAGS_DTOH_SPLIT            0x8000
@@ -237,6 +241,8 @@ 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_SHARED_HOST_CAP2_OFFSET          112
 
 #define BRCMF_RING_H2D_RING_COUNT_OFFSET       0
 #define BRCMF_RING_D2H_RING_COUNT_OFFSET       1
@@ -318,6 +324,7 @@ struct brcmf_pcie_shared_info {
        void *ringupd;
        dma_addr_t ringupd_dmahandle;
        u8 version;
+       bool mb_via_ctl;
 };
 
 #define BRCMF_OTP_MAX_PARAM_LEN 16
@@ -843,6 +850,18 @@ brcmf_pcie_send_mb_data(struct brcmf_pciedev_info 
*devinfo, u32 htod_mb_data)
        u32 i;
 
        shared = &devinfo->shared;
+       if (shared->mb_via_ctl) {
+               struct pci_dev *pdev = devinfo->pdev;
+               struct brcmf_bus *bus = dev_get_drvdata(&pdev->dev);
+               int ret;
+
+               ret = brcmf_msgbuf_h2d_mb_write(bus->drvr, htod_mb_data);
+               if (ret < 0)
+                       brcmf_err(bus, "Failed to send H2D mailbox data (%d)\n",
+                                 ret);
+               return ret;
+       }
+
        addr = shared->htod_mb_data_addr;
        cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
 
@@ -871,21 +890,9 @@ brcmf_pcie_send_mb_data(struct brcmf_pciedev_info 
*devinfo, u32 htod_mb_data)
 }
 
 
-static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo)
+static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo,
+                                     u32 dtoh_mb_data)
 {
-       struct brcmf_pcie_shared_info *shared;
-       u32 addr;
-       u32 dtoh_mb_data;
-
-       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);
-
        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");
@@ -906,6 +913,32 @@ static void brcmf_pcie_handle_mb_data(struct 
brcmf_pciedev_info *devinfo)
 }
 
 
+static void brcmf_pcie_poll_mb_data(struct brcmf_pciedev_info *devinfo)
+{
+       struct brcmf_pcie_shared_info *shared;
+       u32 addr;
+       u32 dtoh_mb_data;
+
+       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);
+
+       brcmf_pcie_handle_mb_data(devinfo, dtoh_mb_data);
+}
+
+static void brcmf_pcie_d2h_mb_rx(struct device *dev, u32 data)
+{
+       struct brcmf_bus *bus = dev_get_drvdata(dev);
+       struct brcmf_pciedev *buspub = bus->bus_priv.pcie;
+
+       brcmf_pcie_handle_mb_data(buspub->devinfo, data);
+}
+
 static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo)
 {
        struct brcmf_pcie_shared_info *shared;
@@ -1042,7 +1075,7 @@ static irqreturn_t brcmf_pcie_isr_thread(int irq, void 
*arg)
        if (status) {
                brcmf_pcie_write_pcie32(devinfo, devinfo->reginfo->mailboxint, 
status);
                if (status & devinfo->reginfo->int_fn0)
-                       brcmf_pcie_handle_mb_data(devinfo);
+                       brcmf_pcie_poll_mb_data(devinfo);
        }
        if (devinfo->have_msi || status & devinfo->reginfo->int_d2h_db) {
                if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
@@ -1695,6 +1728,7 @@ static const struct brcmf_bus_ops brcmf_pcie_bus_ops = {
        .get_blob = brcmf_pcie_get_blob,
        .reset = brcmf_pcie_reset,
        .debugfs_create = brcmf_pcie_debugfs_create,
+       .d2h_mb_rx = brcmf_pcie_d2h_mb_rx,
 };
 
 
@@ -1726,6 +1760,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 host_cap;
        u32 addr;
 
        shared = &devinfo->shared;
@@ -1766,6 +1801,24 @@ 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 >= 6) {
+               shared->mb_via_ctl =
+                       !(shared->flags & BRCMF_PCIE_SHARED_USE_MAILBOX);
+
+               host_cap = shared->version;
+               if (shared->flags & BRCMF_PCIE_SHARED_HOSTRDY_DB1)
+                       host_cap |= BRCMF_HOSTCAP_H2D_ENABLE_HOSTRDY;
+               if (shared->flags & BRCMF_PCIE_SHARED_DAR)
+                       host_cap |= BRCMF_HOSTCAP_H2D_DAR;
+               host_cap |= BRCMF_HOSTCAP_DS_NO_OOB_DW;
+
+               brcmf_pcie_write_tcm32(devinfo, sharedram_addr +
+                                      BRCMF_SHARED_HOST_CAP_OFFSET,
+                                      host_cap);
+               brcmf_pcie_write_tcm32(devinfo, sharedram_addr +
+                                      BRCMF_SHARED_HOST_CAP2_OFFSET, 0);
+       }
+
        brcmf_dbg(PCIE, "max rx buf post %d, rx dataoffset %d\n",
                  shared->max_rxbufpost, shared->rx_dataoffset);
 

-- 
2.55.0



Reply via email to