brcmfmac: add support for dongle ARM CR4 core
authorFranky Lin <frankyl@broadcom.com>
Thu, 11 Apr 2013 11:28:51 +0000 (13:28 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 12 Apr 2013 18:27:54 +0000 (14:27 -0400)
Newer WiFi chip use ARM CR4 core to achieve higher performance. Add necessary
code for host driver in order to support CR4 core.

Reviewed-by: Arend van Spriel <arend@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Franky Lin <frankyl@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c
drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h
include/linux/bcma/bcma.h
include/linux/bcma/bcma_regs.h

index 3147960ad9755db6a1d4b5cd477aa94cd5a9db47..d24eb66d324d13b1fb59bf9aa4bbbd236ab97083 100644 (file)
@@ -2652,7 +2652,7 @@ static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
        struct sdpcm_shared_le sh_le;
        __le32 addr_le;
 
-       shaddr = bus->ramsize - 4;
+       shaddr = bus->ci->rambase + bus->ramsize - 4;
 
        /*
         * Read last word in socram to determine
@@ -3030,10 +3030,11 @@ static int brcmf_sdbrcm_get_image(char *buf, int len, struct brcmf_sdio *bus)
 
 static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus)
 {
-       int offset = 0;
+       int offset;
        uint len;
        u8 *memblock = NULL, *memptr;
        int ret;
+       u8 idx;
 
        brcmf_dbg(INFO, "Enter\n");
 
@@ -3054,9 +3055,14 @@ static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus)
                memptr += (BRCMF_SDALIGN -
                           ((u32)(unsigned long)memblock % BRCMF_SDALIGN));
 
+       offset = bus->ci->rambase;
+
        /* Download image */
-       while ((len =
-               brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus))) {
+       len = brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus);
+       idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_ARM_CR4);
+       if (BRCMF_MAX_CORENUM != idx)
+               memcpy(&bus->ci->rst_vec, memptr, sizeof(bus->ci->rst_vec));
+       while (len) {
                ret = brcmf_sdio_ramrw(bus->sdiodev, true, offset, memptr, len);
                if (ret) {
                        brcmf_err("error %d on writing %d membytes at 0x%08x\n",
@@ -3065,6 +3071,7 @@ static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus)
                }
 
                offset += MEMBLOCK;
+               len = brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus);
        }
 
 err:
index 9818598f30ea856e1f7693201b506bf506f35e19..5db985cb0e0a91790153f912a7abb755ce996964 100644 (file)
@@ -52,6 +52,9 @@
 #define CIB_REV_MASK           0xff000000
 #define CIB_REV_SHIFT          24
 
+/* ARM CR4 core specific control flag bits */
+#define ARMCR4_BCMA_IOCTL_CPUHALT      0x0020
+
 #define SDIOD_DRVSTR_KEY(chip, pmu)     (((chip) << 16) | (pmu))
 /* SDIO Pad drive strength to select value mappings */
 struct sdiod_drive_str {
@@ -149,7 +152,7 @@ brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev,
 
 static void
 brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev,
-                         struct chip_info *ci, u16 coreid)
+                         struct chip_info *ci, u16 coreid, u32 core_bits)
 {
        u32 regdata, base;
        u8 idx;
@@ -235,7 +238,7 @@ brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev,
 
 static void
 brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev,
-                         struct chip_info *ci, u16 coreid)
+                         struct chip_info *ci, u16 coreid, u32 core_bits)
 {
        u8 idx;
        u32 regdata;
@@ -249,19 +252,36 @@ brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev,
        if ((regdata & BCMA_RESET_CTL_RESET) != 0)
                return;
 
-       brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, 0, NULL);
-       regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
+       /* ensure no pending backplane operation
+        * 300uc should be sufficient for backplane ops to be finish
+        * extra 10ms is taken into account for firmware load stage
+        * after 10300us carry on disabling the core anyway
+        */
+       SPINWAIT(brcmf_sdio_regrl(sdiodev,
+                                 ci->c_inf[idx].wrapbase+BCMA_RESET_ST,
+                                 NULL), 10300);
+       regdata = brcmf_sdio_regrl(sdiodev,
+                                  ci->c_inf[idx].wrapbase+BCMA_RESET_ST,
                                   NULL);
-       udelay(10);
+       if (regdata)
+               brcmf_err("disabling core 0x%x with reset status %x\n",
+                         coreid, regdata);
 
        brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
                         BCMA_RESET_CTL_RESET, NULL);
        udelay(1);
+
+       brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
+                        core_bits, NULL);
+       regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
+                                  NULL);
+       usleep_range(10, 20);
+
 }
 
 static void
 brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev,
-                       struct chip_info *ci, u16 coreid)
+                       struct chip_info *ci, u16 coreid, u32 core_bits)
 {
        u32 regdata;
        u8 idx;
@@ -272,7 +292,7 @@ brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev,
         * Must do the disable sequence first to work for
         * arbitrary current core state.
         */
-       brcmf_sdio_sb_coredisable(sdiodev, ci, coreid);
+       brcmf_sdio_sb_coredisable(sdiodev, ci, coreid, 0);
 
        /*
         * Now do the initialization sequence.
@@ -325,7 +345,7 @@ brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev,
 
 static void
 brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev,
-                       struct chip_info *ci, u16 coreid)
+                       struct chip_info *ci, u16 coreid, u32 core_bits)
 {
        u8 idx;
        u32 regdata;
@@ -333,28 +353,67 @@ brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev,
        idx = brcmf_sdio_chip_getinfidx(ci, coreid);
 
        /* must disable first to work for arbitrary current core state */
-       brcmf_sdio_ai_coredisable(sdiodev, ci, coreid);
+       brcmf_sdio_ai_coredisable(sdiodev, ci, coreid, core_bits);
 
        /* now do initialization sequence */
        brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
-                        BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL);
+                        core_bits | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL);
        regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
                                   NULL);
        brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
                         0, NULL);
+       regdata = brcmf_sdio_regrl(sdiodev,
+                                  ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
+                                  NULL);
        udelay(1);
 
        brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
-                        BCMA_IOCTL_CLK, NULL);
+                        core_bits | BCMA_IOCTL_CLK, NULL);
        regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
                                   NULL);
        udelay(1);
 }
 
+#ifdef DEBUG
+/* safety check for chipinfo */
+static int brcmf_sdio_chip_cichk(struct chip_info *ci)
+{
+       u8 core_idx;
+
+       /* check RAM core presence for ARM CM3 core */
+       core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3);
+       if (BRCMF_MAX_CORENUM != core_idx) {
+               core_idx = brcmf_sdio_chip_getinfidx(ci,
+                                                    BCMA_CORE_INTERNAL_MEM);
+               if (BRCMF_MAX_CORENUM == core_idx) {
+                       brcmf_err("RAM core not provided with ARM CM3 core\n");
+                       return -ENODEV;
+               }
+       }
+
+       /* check RAM base for ARM CR4 core */
+       core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CR4);
+       if (BRCMF_MAX_CORENUM != core_idx) {
+               if (ci->rambase == 0) {
+                       brcmf_err("RAM base not provided with ARM CR4 core\n");
+                       return -ENOMEM;
+               }
+       }
+
+       return 0;
+}
+#else  /* DEBUG */
+static inline int brcmf_sdio_chip_cichk(struct chip_info *ci)
+{
+       return 0;
+}
+#endif
+
 static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev,
                                       struct chip_info *ci, u32 regs)
 {
        u32 regdata;
+       int ret;
 
        /* Get CC core rev
         * Chipid is assume to be at offset 0 from regs arg
@@ -439,6 +498,10 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev,
                return -ENODEV;
        }
 
+       ret = brcmf_sdio_chip_cichk(ci);
+       if (ret)
+               return ret;
+
        switch (ci->socitype) {
        case SOCI_SB:
                ci->iscoreup = brcmf_sdio_sb_iscoreup;
@@ -538,7 +601,7 @@ brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev,
         * Make sure any on-chip ARM is off (in case strapping is wrong),
         * or downloaded code was already running.
         */
-       ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3);
+       ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0);
 }
 
 int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev,
@@ -701,7 +764,7 @@ static bool brcmf_sdio_chip_writenvram(struct brcmf_sdio_dev *sdiodev,
        u32 token;
        __le32 token_le;
 
-       nvram_addr = (ci->ramsize - 4) - nvram_sz;
+       nvram_addr = (ci->ramsize - 4) - nvram_sz + ci->rambase;
 
        /* Write the vars list */
        err = brcmf_sdio_ramrw(sdiodev, true, nvram_addr, nvram_dat, nvram_sz);
@@ -728,7 +791,7 @@ static bool brcmf_sdio_chip_writenvram(struct brcmf_sdio_dev *sdiodev,
                  nvram_addr, nvram_sz, token);
 
        /* Write the length token to the last word */
-       if (brcmf_sdio_ramrw(sdiodev, true, (ci->ramsize - 4),
+       if (brcmf_sdio_ramrw(sdiodev, true, (ci->ramsize - 4 + ci->rambase),
                             (u8 *)&token_le, 4))
                return false;
 
@@ -741,8 +804,8 @@ brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev,
 {
        u32 zeros = 0;
 
-       ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3);
-       ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM);
+       ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0);
+       ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM, 0);
 
        /* clear length token */
        brcmf_sdio_ramrw(sdiodev, true, ci->ramsize - 4, (u8 *)&zeros, 4);
@@ -769,7 +832,41 @@ brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci,
        reg_addr += offsetof(struct sdpcmd_regs, intstatus);
        brcmf_sdio_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
 
-       ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CM3);
+       ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CM3, 0);
+
+       return true;
+}
+
+static inline void
+brcmf_sdio_chip_cr4_enterdl(struct brcmf_sdio_dev *sdiodev,
+                           struct chip_info *ci)
+{
+       ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4,
+                     ARMCR4_BCMA_IOCTL_CPUHALT);
+}
+
+static bool
+brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci,
+                          char *nvram_dat, uint nvram_sz)
+{
+       u8 core_idx;
+       u32 reg_addr;
+
+       if (!brcmf_sdio_chip_writenvram(sdiodev, ci, nvram_dat, nvram_sz))
+               return false;
+
+       /* clear all interrupts */
+       core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV);
+       reg_addr = ci->c_inf[core_idx].base;
+       reg_addr += offsetof(struct sdpcmd_regs, intstatus);
+       brcmf_sdio_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
+
+       /* Write reset vector to address 0 */
+       brcmf_sdio_ramrw(sdiodev, true, 0, (void *)&ci->rst_vec,
+                        sizeof(ci->rst_vec));
+
+       /* restore ARM */
+       ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, 0);
 
        return true;
 }
@@ -777,12 +874,27 @@ brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci,
 void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev,
                                    struct chip_info *ci)
 {
-       brcmf_sdio_chip_cm3_enterdl(sdiodev, ci);
+       u8 arm_core_idx;
+
+       arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3);
+       if (BRCMF_MAX_CORENUM != arm_core_idx) {
+               brcmf_sdio_chip_cm3_enterdl(sdiodev, ci);
+               return;
+       }
+
+       brcmf_sdio_chip_cr4_enterdl(sdiodev, ci);
 }
 
 bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev,
                                   struct chip_info *ci, char *nvram_dat,
                                   uint nvram_sz)
 {
-       return brcmf_sdio_chip_cm3_exitdl(sdiodev, ci, nvram_dat, nvram_sz);
+       u8 arm_core_idx;
+
+       arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3);
+       if (BRCMF_MAX_CORENUM != arm_core_idx)
+               return brcmf_sdio_chip_cm3_exitdl(sdiodev, ci, nvram_dat,
+                                                 nvram_sz);
+
+       return brcmf_sdio_chip_cr4_exitdl(sdiodev, ci, nvram_dat, nvram_sz);
 }
index 2123ea71d1279c460b4097d22648a4394cf46ddf..83c041f1bf4ad154afb0822d734818a7561f9ad6 100644 (file)
@@ -73,15 +73,17 @@ struct chip_info {
        u32 pmurev;
        u32 pmucaps;
        u32 ramsize;
+       u32 rambase;
+       u32 rst_vec;    /* reset vertor for ARM CR4 core */
 
        bool (*iscoreup)(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci,
                         u16 coreid);
        u32 (*corerev)(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci,
                         u16 coreid);
        void (*coredisable)(struct brcmf_sdio_dev *sdiodev,
-                       struct chip_info *ci, u16 coreid);
+                       struct chip_info *ci, u16 coreid, u32 core_bits);
        void (*resetcore)(struct brcmf_sdio_dev *sdiodev,
-                       struct chip_info *ci, u16 coreid);
+                       struct chip_info *ci, u16 coreid, u32 core_bits);
 };
 
 struct sbconfig {
index 0ab6712fd76b0c7e6adb46e5d9ff014b44c24cd7..f14a98a79c9d8b276d3407050d08f00b87c9c6be 100644 (file)
@@ -134,6 +134,7 @@ struct bcma_host_ops {
 #define BCMA_CORE_I2S                  0x834
 #define BCMA_CORE_SDR_DDR1_MEM_CTL     0x835   /* SDR/DDR1 memory controller core */
 #define BCMA_CORE_SHIM                 0x837   /* SHIM component in ubus/6362 */
+#define BCMA_CORE_ARM_CR4              0x83e
 #define BCMA_CORE_DEFAULT              0xFFF
 
 #define BCMA_MAX_NR_CORES              16
index 7e8104bb7a7eb11f412fb504df6552e6595ef40b..917dcd7965e7fb9b148029b2675cf1ee25f93671 100644 (file)
@@ -37,6 +37,7 @@
 #define  BCMA_IOST_BIST_DONE           0x8000
 #define BCMA_RESET_CTL                 0x0800
 #define  BCMA_RESET_CTL_RESET          0x0001
+#define BCMA_RESET_ST                  0x0804
 
 /* BCMA PCI config space registers. */
 #define BCMA_PCI_PMCSR                 0x44