MLK-18675-12 brcmfmac: DS1 Exit should re download the firmware.
authorNaveen <nagu@cypress.com>
Tue, 9 Jan 2018 06:03:10 +0000 (11:33 +0530)
committerLeonard Crestez <leonard.crestez@nxp.com>
Wed, 17 Apr 2019 23:51:34 +0000 (02:51 +0300)
In Deep Sleep mode ARM is off and once Exit trigger comes than
Mail Box Interrupt comes to Host and whole Re Initiation should be done
in the ARM to start TX/RX.

Signed-off-by: Naveen Gupta <nagu@cypress.com>
Signed-off-by: Fugang Duan <fugang.duan@nxp.com>
Vipul: Fixed merge conflicts
Conflict:
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
Signed-off-by: Vipul Kumar <vipul_kumar@mentor.com>
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h

index 8ff0acb..7f23076 100644 (file)
 #include "core.h"
 #include "common.h"
 #include "bcdc.h"
+#include "fwil.h"
 
 #define DCMD_RESP_TIMEOUT      msecs_to_jiffies(2500)
 #define CTL_DONE_TIMEOUT       msecs_to_jiffies(2500)
+#define ULP_HUDI_PROC_DONE_TIME        msecs_to_jiffies(2500)
 
 #define DEFAULT_F2_WATERMARK    0x8
 #define CY_4373_F2_WATERMARK    0x40
@@ -323,6 +325,10 @@ struct rte_console {
 #define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US)
 #define BRCMF_SDIO_MAX_ACCESS_ERRORS   5
 
+static void brcmf_sdio_firmware_callback(struct device *dev, int err,
+                                        const struct firmware *code,
+                                        void *nvram, u32 nvram_len);
+
 /*
  * Conversion of 802.1D priority to precedence level
  */
@@ -2538,6 +2544,154 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
        return ret;
 }
 
+/* This Function is used to retrieve important
+ * details from dongle related to ULP mode Mostly
+ * values/SHM details that will be vary depending
+ * on the firmware branches
+ */
+static void
+brcmf_sdio_ulp_preinit(struct device *dev)
+{
+       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+       struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+       struct brcmf_if *ifp = bus_if->drvr->iflist[0];
+       int err = 0;
+       char iovbuf[BRCMF_DCMD_SMLEN];
+
+       brcmf_dbg(TRACE, "Enter\n");
+       memset(iovbuf, 0, sizeof(iovbuf));
+
+       /* Query ulp_sdioctrl iovar to get the ULP related SHM offsets */
+       err = brcmf_fil_iovar_data_get(ifp, "ulp_sdioctrl", iovbuf,
+                                      sizeof(iovbuf));
+       if (err)
+               brcmf_err("fail to get ulp_sdioctrl err:%d\n", err);
+
+       sdiodev->ulp = false;
+
+       /* Copy the data shared by dongle to FMAC structure */
+       memcpy(&sdiodev->shm_ulp, iovbuf, sizeof(struct ulp_shm_info));
+
+       brcmf_dbg(TRACE, "m_ulp_ctrl_sdio[%x] m_ulp_wakeevt_ind [%x]\n",
+                 M_DS1_CTRL_SDIO(sdiodev->shm_ulp),
+                 M_WAKEEVENT_IND(sdiodev->shm_ulp));
+       brcmf_dbg(TRACE, "m_ulp_wakeind [%x]\n",
+                 M_ULP_WAKE_IND(sdiodev->shm_ulp));
+}
+
+/* Reinitialize ARM because In DS1 mode ARM got off */
+static int
+brcmf_sdio_ulp_reinit_fw(struct brcmf_sdio *bus)
+{
+       struct brcmf_sdio_dev *sdiodev = bus->sdiodev;
+       int err = 0;
+
+       /* After firmware redownload tx/rx seq are reset accordingly
+        * these values are reset on FMAC side tx_max is initially set to 4,
+        * which later is updated by FW.
+        */
+       bus->tx_seq = 0;
+       bus->rx_seq = 0;
+       bus->tx_max = 4;
+
+       err = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM,
+                                    sdiodev->fw_name, sdiodev->nvram_name,
+                                    brcmf_sdio_firmware_callback);
+       if (err != 0)
+               brcmf_err("async firmware request failed: %d\n", err);
+       return err;
+}
+
+/* Check if device is in DS1 mode and handshake with ULP UCODE */
+static bool
+brcmf_sdio_ulp_pre_redownload_check(struct brcmf_sdio *bus)
+{
+       int err = 0;
+       u32 value = 0;
+       u32 val32, ulp_wake_ind, wowl_wake_ind;
+       int reg_addr;
+       unsigned long timeout;
+
+       value = brcmf_sdiod_regrb(bus->sdiodev, SDIO_CCCR_IOEx, &err);
+
+       if (value == SDIO_FUNC_ENABLE_1) {
+               brcmf_dbg(SDIO, "GOT THE INTERRUPT FROM UCODE\n");
+               bus->sdiodev->ulp = true;
+               ulp_wake_ind = D11SHM_RD(bus->sdiodev, M_ULP_WAKE_IND(
+                                 bus->sdiodev->shm_ulp), &err) >> 16;
+               wowl_wake_ind = D11SHM_RD(bus->sdiodev, M_WAKEEVENT_IND(
+                                 bus->sdiodev->shm_ulp), &err) >> 16;
+
+               brcmf_dbg(SDIO, "wowl_wake_ind: 0x%08x, ulp_wake_ind: 0x%08x\n",
+                         wowl_wake_ind, ulp_wake_ind);
+
+               if (wowl_wake_ind || ulp_wake_ind) {
+                       /* TX wake Don't do anything.
+                        * Just bail out and re-download firmware.
+                        */
+               } else {
+                       /* RX wake negotiate with MAC */
+                       brcmf_dbg(SDIO, "M_DS1_CTRL_SDIO: 0x%08x\n",
+                                 (u32)D11SHM_RD(bus->sdiodev,
+                                 M_DS1_CTRL_SDIO(bus->sdiodev->shm_ulp),
+                                 &err));
+                       D11SHM_WR(bus->sdiodev, M_DS1_CTRL_SDIO(
+                                 bus->sdiodev->shm_ulp),
+                                 C_DS1_CTRL_SDIO_DS1_EXIT |
+                                 C_DS1_CTRL_REQ_VALID,
+                                 &err);
+                       val32 = D11REG_RD(bus->sdiodev,
+                                         D11_MACCONTROL_REG, &err);
+                       val32 = val32 | D11_MACCONTROL_REG_WAKE;
+                       D11REG_WR(bus->sdiodev,
+                                 D11_MACCONTROL_REG, val32, &err);
+
+                       /* Poll for PROC_DONE to be set by ucode */
+                       value = D11SHM_RD(bus->sdiodev,
+                                         M_DS1_CTRL_SDIO(
+                                         bus->sdiodev->shm_ulp), &err);
+                       /* Wait here (polling) for C_DS1_CTRL_PROC_DONE */
+                       timeout = jiffies + ULP_HUDI_PROC_DONE_TIME;
+                       while (!(value & C_DS1_CTRL_PROC_DONE)) {
+                               value = D11SHM_RD(bus->sdiodev,
+                                                 M_DS1_CTRL_SDIO(
+                                                 bus->sdiodev->shm_ulp), &err);
+                               if (time_after(jiffies, timeout))
+                                       break;
+                               usleep_range(1000, 2000);
+                       }
+                       brcmf_dbg(SDIO, "M_DS1_CTRL_SDIO: 0x%08x\n",
+                                 (u32)D11SHM_RD(bus->sdiodev,
+                                 M_DS1_CTRL_SDIO(
+                                 bus->sdiodev->shm_ulp), &err));
+                       value = D11SHM_RD(bus->sdiodev,
+                                         M_DS1_CTRL_SDIO(
+                                         bus->sdiodev->shm_ulp), &err);
+                       if (!(value & C_DS1_CTRL_PROC_DONE)) {
+                               brcmf_err("%s: timeout Failed to enter DS1 Exit state!\n",
+                                         __func__);
+                               return false;
+                       }
+               }
+               ulp_wake_ind = D11SHM_RD(bus->sdiodev, M_ULP_WAKE_IND(
+                                 bus->sdiodev->shm_ulp), &err) >> 16;
+               wowl_wake_ind = D11SHM_RD(bus->sdiodev, M_WAKEEVENT_IND(
+                                 bus->sdiodev->shm_ulp), &err) >> 16;
+               brcmf_dbg(SDIO, "wowl_wake_ind: 0x%08x, ulp_wake_ind: 0x%08x\n",
+                         wowl_wake_ind, ulp_wake_ind);
+               reg_addr = CORE_CC_REG(
+                         brcmf_chip_get_pmu(bus->ci)->base, min_res_mask);
+               brcmf_sdiod_regwl(bus->sdiodev, reg_addr,
+                                 DEFAULT_43012_MIN_RES_MASK, &err);
+               if (err)
+                       brcmf_err("min_res_mask failed\n");
+
+               return true;
+       }
+
+       return false;
+}
+
 static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
 {
        struct brcmf_sdio_dev *sdiod = bus->sdiodev;
@@ -2611,6 +2765,8 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
        if (intstatus & I_HMB_HOST_INT) {
                intstatus &= ~I_HMB_HOST_INT;
                intstatus |= brcmf_sdio_hostmail(bus);
+               if (brcmf_sdio_ulp_pre_redownload_check(bus))
+                       brcmf_sdio_ulp_reinit_fw(bus);
        }
 
        sdio_release_host(bus->sdiodev->func1);
@@ -3484,6 +3640,10 @@ static int brcmf_sdio_bus_preinit(struct device *dev)
        if (err < 0)
                goto done;
 
+       /* initialize SHM address from firmware for DS1 */
+       if (!bus->sdiodev->ulp)
+               brcmf_sdio_ulp_preinit(dev);
+
        bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;
        if (sdiodev->sg_support) {
                bus->txglom = false;
@@ -4204,6 +4364,18 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
 
        sdio_release_host(sdiod->func1);
 
+       /* Waking up from deep sleep don't requirerd to reint the sdio bus
+        * as all sdiod core registers will get restored by Firmware using
+        * FCBS engine.
+        */
+       if (!bus->sdiodev->ulp) {
+               err = brcmf_bus_started(dev);
+               if (err != 0) {
+                       brcmf_err("dongle is not responding\n");
+                       goto fail;
+               }
+       }
+
        /* Assign bus interface call back */
        sdiod->bus_if->dev = sdiod->dev;
        sdiod->bus_if->ops = &brcmf_sdio_bus_ops;
index d9b7f27..faf8af7 100644 (file)
@@ -169,6 +169,17 @@ struct brcmf_sdreg {
 struct brcmf_sdio;
 struct brcmf_sdiod_freezer;
 
+/* ULP SHM Offsets info */
+struct ulp_shm_info {
+       u32 m_ulp_ctrl_sdio;
+       u32 m_ulp_wakeevt_ind;
+       u32 m_ulp_wakeind;
+};
+
+struct fmac_ulp {
+       struct ulp_shm_info ulp_shm_offset;
+};
+
 struct brcmf_sdio_dev {
        struct sdio_func *func0;
        struct sdio_func *func1;
@@ -195,6 +206,8 @@ struct brcmf_sdio_dev {
        bool wowl_enabled;
        enum brcmf_sdiod_state state;
        struct brcmf_sdiod_freezer *freezer;
+       struct fmac_ulp shm_ulp;
+       bool ulp;
 };
 
 /* sdio core registers */
@@ -382,4 +395,51 @@ void brcmf_sdio_wowl_config(struct device *dev, bool enabled);
 int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep);
 void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus);
 
+/* SHM offsets */
+#define M_DS1_CTRL_SDIO(ptr)   ((ptr).ulp_shm_offset.m_ulp_ctrl_sdio)
+#define M_WAKEEVENT_IND(ptr)   ((ptr).ulp_shm_offset.m_ulp_wakeevt_ind)
+#define M_ULP_WAKE_IND(ptr)            ((ptr).ulp_shm_offset.m_ulp_wakeind)
+
+#define D11_BASE_ADDR                  0x18001000
+#define D11_AXI_BASE_ADDR              0xE8000000
+#define D11_SHM_BASE_ADDR              (D11_AXI_BASE_ADDR + 0x4000)
+
+#define D11REG_ADDR(offset)    (D11_BASE_ADDR + (offset))
+#define D11IHR_ADDR(offset)    (D11_AXI_BASE_ADDR + 0x400 + (2 * (offset)))
+#define D11SHM_ADDR(offset)    (D11_SHM_BASE_ADDR + (offset))
+
+/* MacControl register */
+#define D11_MACCONTROL_REG                     D11REG_ADDR(0x120)
+#define D11_MACCONTROL_REG_WAKE                0x4000000
+
+/* Following are the offsets in M_DRVR_UCODE_IF_PTR block. Start address of
+ * M_DRVR_UCODE_IF_PTR block is present in M_DRVR_UCODE_IF_PTR.
+ */
+
+/* M_ULP_WAKE_IND bits */
+#define ULP_WAKE_IND_WATCHDOG_EXP              0x1
+#define ULP_WAKE_IND_FCBS_ERROR                        0x2
+#define ULP_WAKE_IND_RE_TRANSMIT_ERR   0x4
+#define ULP_WAKE_IND_HOST_WKUP                 0x8
+#define ULP_WAKE_IND_INVALID_FCBS_BLK  0x10
+
+#define C_DS1_CTRL_SDIO_DS1_SLEEP              0x1
+#define C_DS1_CTRL_SDIO_MAC_ON                 0x2
+#define C_DS1_CTRL_SDIO_RADIO_PHY_ON   0x4
+#define C_DS1_CTRL_SDIO_DS1_EXIT               0x8
+#define C_DS1_CTRL_PROC_DONE                   0x100
+#define C_DS1_CTRL_REQ_VALID                   0x200
+
+#define D11SHM_WR(sdh, offset, val, ret) \
+       brcmf_sdiod_regwl(sdh, D11SHM_ADDR(offset), val, ret)
+
+#define D11SHM_RD(sdh, offset, ret) \
+       brcmf_sdiod_regrl(sdh, D11SHM_ADDR(offset), ret)
+
+#define D11REG_WR(sdh, addr, val, ret) \
+       brcmf_sdiod_regwl(sdh, addr, val, ret)
+
+#define D11REG_RD(sdh, addr, ret) \
+       brcmf_sdiod_regrl(sdh, addr, ret)
+
 #endif /* BRCMFMAC_SDIO_H */
index e1fd499..1f23092 100644 (file)
@@ -298,6 +298,8 @@ struct chipcregs {
 * Maximum delay for the PMU state transition in us.
 * This is an upper bound intended for spinwaits etc.
 */
-#define PMU_MAX_TRANSITION_DLY 15000
+#define PMU_MAX_TRANSITION_DLY 15000
+
+#define DEFAULT_43012_MIN_RES_MASK 0x0f8bfe77
 
 #endif                         /* _SBCHIPC_H */