summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/bcmdhd/dhd_sdio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/bcmdhd/dhd_sdio.c')
-rw-r--r--drivers/net/wireless/bcmdhd/dhd_sdio.c847
1 files changed, 709 insertions, 138 deletions
diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c
index d7823167cca..0b0377a8f47 100644
--- a/drivers/net/wireless/bcmdhd/dhd_sdio.c
+++ b/drivers/net/wireless/bcmdhd/dhd_sdio.c
@@ -1,9 +1,9 @@
/*
* DHD Bus Module for SDIO
*
- * Copyright (C) 1999-2011, Broadcom Corporation
+ * Copyright (C) 1999-2012, Broadcom Corporation
*
- * Unless you and Broadcom execute a separate written software license
+ * Unless you and Broadcom execute a separate written software license
* agreement governing use of this software, this software is licensed to you
* under the terms of the GNU General Public License version 2 (the "GPL"),
* available at http://www.broadcom.com/licenses/GPLv2.php, with the
@@ -21,7 +21,7 @@
* software in any way with any other Broadcom software provided under a license
* other than the GPL, without Broadcom's express prior written consent.
*
- * $Id: dhd_sdio.c 309234 2012-01-19 01:44:16Z $
+ * $Id: dhd_sdio.c 309548 2012-01-20 01:13:08Z $
*/
#include <typedefs.h>
@@ -159,6 +159,18 @@ typedef struct dhd_console {
} dhd_console_t;
#endif /* DHD_DEBUG */
+#define REMAP_ENAB(bus) ((bus)->remap)
+#define REMAP_ISADDR(bus, a) (((a) >= ((bus)->orig_ramsize)) && ((a) < ((bus)->ramsize)))
+#define KSO_ENAB(bus) ((bus)->kso)
+#define SLPAUTO_ENAB(bus) ((bus)->_slpauto)
+
+#define OOB_WAKEUP_ENAB(bus) ((bus)->_oobwakeup)
+#define GPIO_DEV_SRSTATE 16 /* Host gpio17 mapped to device gpio0 SR state */
+#define GPIO_DEV_SRSTATE_TIMEOUT 320000 /* 320ms */
+#define GPIO_DEV_WAKEUP 17 /* Host gpio17 mapped to device gpio1 wakeup */
+#define CC_CHIPCTRL2_GPIO1_WAKEUP (1 << 0)
+
+
/* Private data for SDIO bus interaction */
typedef struct dhd_bus {
dhd_pub_t *dhd;
@@ -183,8 +195,8 @@ typedef struct dhd_bus {
bool fcstate; /* State of dongle flow-control */
uint16 cl_devid; /* cached devid for dhdsdio_probe_attach() */
- char *fw_path; /* module_param: path to firmware image */
- char *nv_path; /* module_param: path to nvram vars file */
+ char *fw_path; /* module_param: path to firmware image */
+ char *nv_path; /* module_param: path to nvram vars file */
const char *nvram_params; /* user specified nvram params. */
uint blocksize; /* Block size of SDIO transfers */
@@ -242,7 +254,7 @@ typedef struct dhd_bus {
int32 sd_rxchain; /* If bcmsdh api accepts PKT chains */
bool use_rxchain; /* If dhd should use PKT chains */
bool sleeping; /* Is SDIO bus sleeping? */
- bool rxflow_mode; /* Rx flow control mode */
+ uint rxflow_mode; /* Rx flow control mode */
bool rxflow; /* Is rx flow control on */
uint prev_rxlim_hit; /* Is prev rx limit exceeded (per dpc schedule) */
bool alp_only; /* Don't use HT clock (ALP only) */
@@ -301,6 +313,13 @@ typedef struct dhd_bus {
uint32 ctrl_frame_len;
bool ctrl_frame_stat;
uint32 rxint_mode; /* rx interrupt mode */
+ bool remap; /* Contiguous 1MB RAM: 512K socram + 512K devram
+ * Available with socram rev 16
+ * Remap region not DMA-able
+ */
+ bool kso;
+ bool _slpauto;
+ bool _oobwakeup;
} dhd_bus_t;
/* clkstate */
@@ -340,6 +359,7 @@ static bool retrydata;
#define RETRYCHAN(chan) (((chan) == SDPCM_EVENT_CHANNEL) || retrydata)
static const uint watermark = 8;
+static const uint mesbusyctrl = 0;
static const uint firstread = DHD_FIRSTREAD;
#define HDATLEN (firstread - (SDPCM_HDRLEN))
@@ -380,6 +400,7 @@ static const uint max_roundup = 512;
/* Try doing readahead */
static bool dhd_readahead;
+
/* To check if there's window offered */
#define DATAOK(bus) \
(((uint8)(bus->tx_max - bus->tx_seq) > 1) && \
@@ -461,7 +482,6 @@ do { \
#define GSPI_PR55150_BAILOUT
-
#ifdef SDTEST
static void dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq);
static void dhdsdio_sdtest_set(dhd_bus_t *bus, uint8 count);
@@ -472,6 +492,7 @@ static int dhdsdio_checkdied(dhd_bus_t *bus, char *data, uint size);
static int dhd_serialconsole(dhd_bus_t *bus, bool get, bool enable, int *bcmerror);
#endif /* DHD_DEBUG */
+static int dhdsdio_devcap_set(dhd_bus_t *bus, uint8 cap);
static int dhdsdio_download_state(dhd_bus_t *bus, bool enter);
static void dhdsdio_release(dhd_bus_t *bus, osl_t *osh);
@@ -501,6 +522,9 @@ static int dhdsdio_download_nvram(dhd_bus_t *bus);
#ifdef BCMEMBEDIMAGE
static int dhdsdio_download_code_array(dhd_bus_t *bus);
#endif
+static int dhdsdio_bussleep(dhd_bus_t *bus, bool sleep);
+static int dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok);
+static uint8 dhdsdio_sleepcsr_get(dhd_bus_t *bus);
#ifdef WLMEDIA_HTSF
#include <htsf.h>
@@ -535,6 +559,281 @@ dhdsdio_set_siaddr_window(dhd_bus_t *bus, uint32 address)
}
+#ifdef USE_OOB_GPIO1
+static int
+dhdsdio_oobwakeup_init(dhd_bus_t *bus)
+{
+ uint32 val, addr, data;
+
+ bcmsdh_gpioouten(bus->sdh, GPIO_DEV_WAKEUP);
+
+ addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_addr);
+ data = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_data);
+
+ /* Set device for gpio1 wakeup */
+ bcmsdh_reg_write(bus->sdh, addr, 4, 2);
+ val = bcmsdh_reg_read(bus->sdh, data, 4);
+ val |= CC_CHIPCTRL2_GPIO1_WAKEUP;
+ bcmsdh_reg_write(bus->sdh, data, 4, val);
+
+ bus->_oobwakeup = TRUE;
+
+ return 0;
+}
+#endif /* USE_OOB_GPIO1 */
+
+
+/*
+ * FIX: Be sure KSO bit is enabled
+ * Currently, it's defaulting to 0 which should be 1.
+ */
+static int
+dhdsdio_clk_kso_init(dhd_bus_t *bus)
+{
+ uint8 val;
+ int err = 0;
+
+ /* set flag */
+ bus->kso = TRUE;
+
+ /*
+ * Enable KeepSdioOn (KSO) bit for normal operation
+ * Default is 0 (4334A0) so set it. Fixed in B0.
+ */
+ val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, NULL);
+ if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) {
+ val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
+ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, val, &err);
+ if (err)
+ DHD_ERROR(("%s: SBSDIO_FUNC1_SLEEPCSR err: 0x%x\n", __FUNCTION__, err));
+ }
+
+
+ return 0;
+}
+
+static int
+dhdsdio_clk_kso_enab(dhd_bus_t *bus, bool on)
+{
+ uint8 val = 0;
+ int err = 0;
+
+ /* Don't read here since sdio could be off so just write only */
+ val |= (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
+ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, val, &err);
+
+ if (err)
+ DHD_TRACE(("%s: KSO toggle %d failed: %d\n", __FUNCTION__, on, err));
+ return 0;
+}
+
+static int
+dhdsdio_clk_kso_iovar(dhd_bus_t *bus, bool on)
+{
+ int err = 0;
+
+ if (on == FALSE) {
+
+ BUS_WAKE(bus);
+ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+
+ DHD_ERROR(("%s: KSO disable clk: 0x%x\n", __FUNCTION__,
+ bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, &err)));
+ dhdsdio_clk_kso_enab(bus, FALSE);
+ } else {
+ DHD_ERROR(("%s: KSO enable\n", __FUNCTION__));
+
+ /* Make sure we have SD bus access */
+ if (bus->clkstate == CLK_NONE) {
+ DHD_ERROR(("%s: Request SD clk\n", __FUNCTION__));
+ dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
+ }
+
+ /* Double-write to be safe in case transition of AOS */
+ dhdsdio_clk_kso_enab(bus, TRUE);
+ dhdsdio_clk_kso_enab(bus, TRUE);
+ OSL_DELAY(4000);
+
+ /* Wait for device ready during transition to wake-up */
+ SPINWAIT(((dhdsdio_sleepcsr_get(bus)) !=
+ (SBSDIO_FUNC1_SLEEPCSR_KSO_MASK |
+ SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK)),
+ (10000));
+
+ DHD_ERROR(("%s: sleepcsr: 0x%x\n", __FUNCTION__,
+ dhdsdio_sleepcsr_get(bus)));
+ }
+
+ bus->kso = on;
+ BCM_REFERENCE(err);
+
+ return 0;
+}
+
+static uint8
+dhdsdio_sleepcsr_get(dhd_bus_t *bus)
+{
+ int err = 0;
+ uint8 val = 0;
+
+ val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, &err);
+ if (err)
+ DHD_TRACE(("Failed to read SLEEPCSR: %d\n", err));
+
+ return val;
+}
+
+uint8
+dhdsdio_devcap_get(dhd_bus_t *bus)
+{
+ return bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_BRCM_CARDCAP, NULL);
+}
+
+static int
+dhdsdio_devcap_set(dhd_bus_t *bus, uint8 cap)
+{
+ int err = 0;
+
+ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_BRCM_CARDCAP, cap, &err);
+ if (err)
+ DHD_ERROR(("%s: devcap set err: 0x%x\n", __FUNCTION__, err));
+
+ return 0;
+}
+
+static int
+dhdsdio_clk_devsleep_iovar(dhd_bus_t *bus, bool on)
+{
+ int err = 0, retry;
+ uint8 val;
+
+ retry = 0;
+ if (on == TRUE) {
+ /* Enter Sleep */
+
+ /* Be sure we request clk before going to sleep
+ * so we can wake-up with clk request already set
+ * else device can go back to sleep immediately
+ */
+ if (!SLPAUTO_ENAB(bus))
+ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+ else {
+ val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err);
+ if ((val & SBSDIO_CSR_MASK) == 0) {
+ DHD_ERROR(("%s: No clock before enter sleep:0x%x\n",
+ __FUNCTION__, val));
+
+ /* Reset clock request */
+ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+ SBSDIO_ALP_AVAIL_REQ, &err);
+ DHD_ERROR(("%s: clock before sleep:0x%x\n", __FUNCTION__,
+ bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, &err)));
+ }
+ }
+
+ DHD_TRACE(("%s: clk before sleep: 0x%x\n", __FUNCTION__,
+ bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, &err)));
+#ifdef USE_CMD14
+ err = bcmsdh_sleep(bus->sdh, TRUE);
+#else
+ err = dhdsdio_clk_kso_enab(bus, FALSE);
+ if (OOB_WAKEUP_ENAB(bus))
+ err = bcmsdh_gpioout(bus->sdh, GPIO_DEV_WAKEUP, FALSE); /* GPIO_1 is off */
+#endif
+ } else {
+ /* Exit Sleep */
+ /* Make sure we have SD bus access */
+ if (bus->clkstate == CLK_NONE) {
+ DHD_TRACE(("%s: Request SD clk\n", __FUNCTION__));
+ dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
+ }
+
+ if ((bus->sih->chip == BCM4334_CHIP_ID) && (bus->sih->chiprev == 2)) {
+ SPINWAIT((bcmsdh_gpioin(bus->sdh, GPIO_DEV_SRSTATE) != TRUE),
+ GPIO_DEV_SRSTATE_TIMEOUT);
+
+ if (bcmsdh_gpioin(bus->sdh, GPIO_DEV_SRSTATE) == FALSE) {
+ DHD_ERROR(("ERROR: GPIO_DEV_SRSTATE still low!\n"));
+ }
+ }
+#ifdef USE_CMD14
+ err = bcmsdh_sleep(bus->sdh, FALSE);
+ if (SLPAUTO_ENAB(bus) && (err != 0)) {
+ OSL_DELAY(10000);
+ DHD_TRACE(("%s: Resync device sleep\n", __FUNCTION__));
+
+ /* Toggle sleep to resync with host and device */
+ err = bcmsdh_sleep(bus->sdh, TRUE);
+ OSL_DELAY(10000);
+ err = bcmsdh_sleep(bus->sdh, FALSE);
+
+ if (err) {
+ OSL_DELAY(10000);
+ DHD_ERROR(("%s: CMD14 exit failed again!\n", __FUNCTION__));
+
+ /* Toggle sleep to resync with host and device */
+ err = bcmsdh_sleep(bus->sdh, TRUE);
+ OSL_DELAY(10000);
+ err = bcmsdh_sleep(bus->sdh, FALSE);
+ if (err) {
+ DHD_ERROR(("%s: CMD14 exit failed twice!\n", __FUNCTION__));
+ DHD_ERROR(("%s: FATAL: Device non-response!\n",
+ __FUNCTION__));
+ err = 0;
+ }
+ }
+ }
+#else
+ if (OOB_WAKEUP_ENAB(bus))
+ err = bcmsdh_gpioout(bus->sdh, GPIO_DEV_WAKEUP, TRUE); /* GPIO_1 is on */
+
+ do {
+ err = dhdsdio_clk_kso_enab(bus, TRUE);
+ OSL_DELAY(10000);
+ } while ((err != 0) && (++retry < 3));
+
+ if (err != 0) {
+ DHD_ERROR(("ERROR: kso set failed retry: %d\n", retry));
+ err = 0; /* continue anyway */
+ }
+#endif /* !USE_CMD14 */
+
+ if (err == 0) {
+ uint8 csr;
+
+ /* Wait for device ready during transition to wake-up */
+ SPINWAIT((((csr = dhdsdio_sleepcsr_get(bus)) &
+ SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK) !=
+ (SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK)), (10000));
+
+ DHD_TRACE(("%s: ExitSleep sleepcsr: 0x%x\n", __FUNCTION__, csr));
+
+ if (!(csr & SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK)) {
+ DHD_ERROR(("%s:ERROR: ExitSleep device NOT Ready! 0x%x\n",
+ __FUNCTION__, csr));
+ err = BCME_NODEVICE;
+ }
+
+ SPINWAIT((((csr = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, &err)) & SBSDIO_HT_AVAIL) !=
+ (SBSDIO_HT_AVAIL)), (10000));
+
+ }
+ }
+
+ /* Update if successful */
+ if (err == 0)
+ bus->kso = on ? FALSE : TRUE;
+ else {
+ DHD_ERROR(("%s: Sleep request failed: on:%d err:%d\n", __FUNCTION__, on, err));
+ }
+
+ return err;
+}
+
/* Turn backplane clock on or off */
static int
dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok)
@@ -546,19 +845,26 @@ dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok)
DHD_TRACE(("%s: Enter\n", __FUNCTION__));
#if defined(OOB_INTR_ONLY)
- pendok = FALSE;
+ pendok = FALSE;
#endif
clkctl = 0;
sdh = bus->sdh;
+ if (!KSO_ENAB(bus))
+ return BCME_OK;
+
+ if (SLPAUTO_ENAB(bus)) {
+ bus->clkstate = (on ? CLK_AVAIL : CLK_SDONLY);
+ return BCME_OK;
+ }
+
if (on) {
/* Request HT Avail */
clkreq = bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;
-
bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err);
if (err) {
DHD_ERROR(("%s: HT Avail request error: %d\n", __FUNCTION__, err));
@@ -569,6 +875,7 @@ dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok)
((bus->sih->buscoretype == PCMCIA_CORE_ID) && (bus->sih->buscorerev == 9))) {
uint32 dummy, retries;
R_SDREG(dummy, &bus->regs->clockctlstatus, retries);
+ BCM_REFERENCE(dummy);
}
/* Check current status */
@@ -617,7 +924,6 @@ dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok)
return BCME_ERROR;
}
-
/* Mark clock available */
bus->clkstate = CLK_AVAIL;
DHD_INFO(("CLKCTL: turned ON\n"));
@@ -639,7 +945,6 @@ dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok)
bus->activity = TRUE;
} else {
clkreq = 0;
-
if (bus->clkstate == CLK_PENDING) {
/* Cancel CA-only interrupt filter */
devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err);
@@ -648,6 +953,7 @@ dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok)
}
bus->clkstate = CLK_SDONLY;
+
bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err);
DHD_INFO(("CLKCTL: turned OFF\n"));
if (err) {
@@ -655,6 +961,7 @@ dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok)
__FUNCTION__, err));
return BCME_ERROR;
}
+
}
return BCME_OK;
}
@@ -774,7 +1081,7 @@ dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok)
ret = dhdsdio_htclk(bus, TRUE, pendok);
if (ret == BCME_OK) {
dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
- bus->activity = TRUE;
+ bus->activity = TRUE;
}
break;
@@ -801,7 +1108,7 @@ dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok)
#ifdef DHD_DEBUG
if (dhd_console_ms == 0)
#endif /* DHD_DEBUG */
- dhd_os_wd_timer(bus->dhd, 0);
+ dhd_os_wd_timer(bus->dhd, 0);
break;
}
#ifdef DHD_DEBUG
@@ -814,6 +1121,7 @@ dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok)
static int
dhdsdio_bussleep(dhd_bus_t *bus, bool sleep)
{
+ int err = 0;
bcmsdh_info_t *sdh = bus->sdh;
sdpcmd_regs_t *regs = bus->regs;
uint retries = 0;
@@ -833,27 +1141,35 @@ dhdsdio_bussleep(dhd_bus_t *bus, bool sleep)
return BCME_BUSY;
- /* Disable SDIO interrupts (no longer interested) */
- bcmsdh_intr_disable(bus->sdh);
+ if (!SLPAUTO_ENAB(bus)) {
+ /* Disable SDIO interrupts (no longer interested) */
+ bcmsdh_intr_disable(bus->sdh);
- /* Make sure the controller has the bus up */
- dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+ /* Make sure the controller has the bus up */
+ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
- /* Tell device to start using OOB wakeup */
- W_SDREG(SMB_USE_OOB, &regs->tosbmailbox, retries);
- if (retries > retry_limit)
- DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n"));
+ /* Tell device to start using OOB wakeup */
+ W_SDREG(SMB_USE_OOB, &regs->tosbmailbox, retries);
+ if (retries > retry_limit)
+ DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n"));
- /* Turn off our contribution to the HT clock request */
- dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
+ /* Turn off our contribution to the HT clock request */
+ dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
- bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
- SBSDIO_FORCE_HW_CLKREQ_OFF, NULL);
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+ SBSDIO_FORCE_HW_CLKREQ_OFF, NULL);
- /* Isolate the bus */
- if (bus->sih->chip != BCM4329_CHIP_ID && bus->sih->chip != BCM4319_CHIP_ID) {
- bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
- SBSDIO_DEVCTL_PADS_ISO, NULL);
+ /* Isolate the bus */
+ if (bus->sih->chip != BCM4329_CHIP_ID &&
+ bus->sih->chip != BCM4319_CHIP_ID) {
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
+ SBSDIO_DEVCTL_PADS_ISO, NULL);
+ }
+ } else {
+ /* Leave interrupts enabled since device can exit sleep and
+ * interrupt host
+ */
+ err = dhdsdio_clk_devsleep_iovar(bus, TRUE /* sleep */);
}
/* Change state */
@@ -862,38 +1178,43 @@ dhdsdio_bussleep(dhd_bus_t *bus, bool sleep)
} else {
/* Waking up: bus power up is ok, set local state */
- bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
- 0, NULL);
+ if (!SLPAUTO_ENAB(bus)) {
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, 0, &err);
- /* Force pad isolation off if possible (in case power never toggled) */
- bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, 0, NULL);
+ /* Force pad isolation off if possible (in case power never toggled) */
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, 0, NULL);
- /* Make sure the controller has the bus up */
- dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+ /* Make sure the controller has the bus up */
+ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
- /* Send misc interrupt to indicate OOB not needed */
- W_SDREG(0, &regs->tosbmailboxdata, retries);
- if (retries <= retry_limit)
- W_SDREG(SMB_DEV_INT, &regs->tosbmailbox, retries);
+ /* Send misc interrupt to indicate OOB not needed */
+ W_SDREG(0, &regs->tosbmailboxdata, retries);
+ if (retries <= retry_limit)
+ W_SDREG(SMB_DEV_INT, &regs->tosbmailbox, retries);
- if (retries > retry_limit)
- DHD_ERROR(("CANNOT SIGNAL CHIP TO CLEAR OOB!!\n"));
+ if (retries > retry_limit)
+ DHD_ERROR(("CANNOT SIGNAL CHIP TO CLEAR OOB!!\n"));
- /* Make sure we have SD bus access */
- dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
+ /* Make sure we have SD bus access */
+ dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
- /* Change state */
- bus->sleeping = FALSE;
+ /* Enable interrupts again */
+ if (bus->intr && (bus->dhd->busstate == DHD_BUS_DATA)) {
+ bus->intdis = FALSE;
+ bcmsdh_intr_enable(bus->sdh);
+ }
+ } else {
+ err = dhdsdio_clk_devsleep_iovar(bus, FALSE /* wake */);
+ }
- /* Enable interrupts again */
- if (bus->intr && (bus->dhd->busstate == DHD_BUS_DATA)) {
- bus->intdis = FALSE;
- bcmsdh_intr_enable(bus->sdh);
+ if (err == 0) {
+ /* Change state */
+ bus->sleeping = FALSE;
}
}
- return BCME_OK;
+ return err;
}
#if defined(OOB_INTR_ONLY)
@@ -1058,7 +1379,9 @@ dhdsdio_txpkt(dhd_bus_t *bus, void *pkt, uint chan, bool free_pkt)
bus->f2txdata++;
ASSERT(ret != BCME_PENDING);
- if (ret < 0) {
+ if (ret == BCME_NODEVICE) {
+ DHD_ERROR(("%s: Device asleep already\n", __FUNCTION__));
+ } else if (ret < 0) {
/* On failure, abort the command and terminate the frame */
DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n",
__FUNCTION__, ret));
@@ -1079,7 +1402,6 @@ dhdsdio_txpkt(dhd_bus_t *bus, void *pkt, uint chan, bool free_pkt)
if ((hi == 0) && (lo == 0))
break;
}
-
}
if (ret == 0) {
bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
@@ -1087,7 +1409,7 @@ dhdsdio_txpkt(dhd_bus_t *bus, void *pkt, uint chan, bool free_pkt)
} while ((ret < 0) && retrydata && retries++ < TXRETRIES);
done:
- /* restore pkt buffer pointer before calling tx complete routine */
+ /* restore pkt buffer pointer before calling tx complete routine */
PKTPULL(osh, pkt, SDPCM_HDRLEN + pad1);
#ifdef PROP_TXSTATUS
if (bus->dhd->wlfc_state) {
@@ -1175,7 +1497,7 @@ dhd_bus_txdata(struct dhd_bus *bus, void *pkt)
#endif
#ifdef PROP_TXSTATUS
/* let the caller decide whether to free the packet */
- if (!bus->dhd->wlfc_state)
+ if (!bus->dhd->wlfc_state)
#endif
PKTFREE(osh, pkt, TRUE);
ret = BCME_NORESOURCE;
@@ -1250,6 +1572,11 @@ dhdsdio_sendfromq(dhd_bus_t *bus, uint maxframes)
DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+ if (!KSO_ENAB(bus)) {
+ DHD_ERROR(("%s: Device asleep\n", __FUNCTION__));
+ return BCME_NODEVICE;
+ }
+
tx_prec_map = ~bus->flowcontrol;
/* Send frames until the limit or some other event */
@@ -1368,7 +1695,9 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen)
/* Send from dpc */
bus->ctrl_frame_buf = frame;
bus->ctrl_frame_len = len;
+
dhd_wait_for_event(bus->dhd, &bus->ctrl_frame_stat);
+
if (bus->ctrl_frame_stat == FALSE) {
DHD_INFO(("%s: ctrl_frame_stat == FALSE\n", __FUNCTION__));
ret = 0;
@@ -1399,7 +1728,9 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen)
frame, len, NULL, NULL, NULL);
ASSERT(ret != BCME_PENDING);
- if (ret < 0) {
+ if (ret == BCME_NODEVICE) {
+ DHD_ERROR(("%s: Device asleep already\n", __FUNCTION__));
+ } else if (ret < 0) {
/* On failure, abort the command and terminate the frame */
DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n",
__FUNCTION__, ret));
@@ -1421,7 +1752,6 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen)
if ((hi == 0) && (lo == 0))
break;
}
-
}
if (ret == 0) {
bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
@@ -1475,9 +1805,11 @@ dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen)
} else if (timeleft == 0) {
DHD_ERROR(("%s: resumed on timeout\n", __FUNCTION__));
#ifdef DHD_DEBUG
- dhd_os_sdlock(bus->dhd);
- dhdsdio_checkdied(bus, NULL, 0);
- dhd_os_sdunlock(bus->dhd);
+ if (!SLPAUTO_ENAB(bus)) {
+ dhd_os_sdlock(bus->dhd);
+ dhdsdio_checkdied(bus, NULL, 0);
+ dhd_os_sdunlock(bus->dhd);
+ }
#endif /* DHD_DEBUG */
} else if (pending == TRUE) {
DHD_CTL(("%s: canceled\n", __FUNCTION__));
@@ -1521,7 +1853,7 @@ enum {
IOV_CHECKDIED,
IOV_SERIALCONS,
#endif /* DHD_DEBUG */
- IOV_DOWNLOAD,
+ IOV_SET_DOWNLOAD_STATE,
IOV_SOCRAM_STATE,
IOV_FORCEEVEN,
IOV_SDIOD_DRIVE,
@@ -1544,6 +1876,9 @@ enum {
IOV_SD1IDLE,
IOV_SLEEP,
IOV_DONGLEISOLATION,
+ IOV_KSO,
+ IOV_DEVSLEEP,
+ IOV_DEVCAP,
IOV_VARS,
#ifdef SOFTAP
IOV_FWPATH
@@ -1559,7 +1894,7 @@ const bcm_iovar_t dhdsdio_iovars[] = {
{"sd1idle", IOV_SD1IDLE, 0, IOVT_BOOL, 0 },
{"membytes", IOV_MEMBYTES, 0, IOVT_BUFFER, 2 * sizeof(int) },
{"memsize", IOV_MEMSIZE, 0, IOVT_UINT32, 0 },
- {"download", IOV_DOWNLOAD, 0, IOVT_BOOL, 0 },
+ {"dwnldstate", IOV_SET_DOWNLOAD_STATE, 0, IOVT_BOOL, 0 },
{"socram_state", IOV_SOCRAM_STATE, 0, IOVT_BOOL, 0 },
{"vars", IOV_VARS, 0, IOVT_BUFFER, 0 },
{"sdiod_drive", IOV_SDIOD_DRIVE, 0, IOVT_UINT32, 0 },
@@ -1586,7 +1921,10 @@ const bcm_iovar_t dhdsdio_iovars[] = {
{"extloop", IOV_EXTLOOP, 0, IOVT_BOOL, 0 },
{"pktgen", IOV_PKTGEN, 0, IOVT_BUFFER, sizeof(dhd_pktgen_t) },
#endif /* SDTEST */
+ {"devcap", IOV_DEVCAP, 0, IOVT_UINT32, 0 },
{"dngl_isolation", IOV_DONGLEISOLATION, 0, IOVT_UINT32, 0 },
+ {"kso", IOV_KSO, 0, IOVT_UINT32, 0 },
+ {"devsleep", IOV_DEVSLEEP, 0, IOVT_UINT32, 0 },
#ifdef SOFTAP
{"fwpath", IOV_FWPATH, 0, IOVT_BUFFER, 0 },
#endif
@@ -1760,6 +2098,16 @@ dhdsdio_pktgen_set(dhd_bus_t *bus, uint8 *arg)
}
#endif /* SDTEST */
+static void
+dhdsdio_devram_remap(dhd_bus_t *bus, bool val)
+{
+ uint8 enable, protect, remap;
+
+ si_socdevram(bus->sih, FALSE, &enable, &protect, &remap);
+ remap = val ? TRUE : FALSE;
+ si_socdevram(bus->sih, TRUE, &enable, &protect, &remap);
+}
+
static int
dhdsdio_membytes(dhd_bus_t *bus, bool write, uint32 address, uint8 *data, uint size)
{
@@ -1767,6 +2115,15 @@ dhdsdio_membytes(dhd_bus_t *bus, bool write, uint32 address, uint8 *data, uint s
uint32 sdaddr;
uint dsize;
+ /* In remap mode, adjust address beyond socram and redirect
+ * to devram at SOCDEVRAM_BP_ADDR since remap address > orig_ramsize
+ * is not backplane accessible
+ */
+ if (REMAP_ENAB(bus) && REMAP_ISADDR(bus, address)) {
+ address -= bus->orig_ramsize;
+ address += SOCDEVRAM_BP_ADDR;
+ }
+
/* Determine initial transfer parameters */
sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK)
@@ -1865,6 +2222,7 @@ dhdsdio_readshared(dhd_bus_t *bus, sdpcm_shared_t *sh)
return BCME_OK;
}
+#define CONSOLE_LINE_MAX 192
static int
dhdsdio_readconsole(dhd_bus_t *bus)
@@ -1878,6 +2236,9 @@ dhdsdio_readconsole(dhd_bus_t *bus)
if (bus->console_addr == 0)
return 0;
+ if (!KSO_ENAB(bus))
+ return 0;
+
/* Read console log struct */
addr = bus->console_addr + OFFSETOF(hndrte_cons_t, log);
if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)&c->log, sizeof(c->log))) < 0)
@@ -2034,7 +2395,7 @@ dhdsdio_checkdied(dhd_bus_t *bus, char *data, uint size)
bcm_bprintf(&strbuf,
"Dongle trap type 0x%x @ epc 0x%x, cpsr 0x%x, spsr 0x%x, sp 0x%x,"
- "lp 0x%x, rpc 0x%x Trap offset 0x%x, "
+ "lp 0x%x, rpc 0x%x Trap offset 0x%x, "
"r0 0x%x, r1 0x%x, r2 0x%x, r3 0x%x, "
"r4 0x%x, r5 0x%x, r6 0x%x, r7 0x%x\n\n",
ltoh32(tr.type), ltoh32(tr.epc), ltoh32(tr.cpsr), ltoh32(tr.spsr),
@@ -2088,10 +2449,8 @@ dhdsdio_checkdied(dhd_bus_t *bus, char *data, uint size)
* will truncate a lot of the printfs
*/
- if (dhd_msg_level & DHD_ERROR_VAL) {
+ if (dhd_msg_level & DHD_ERROR_VAL)
printf("CONSOLE: %s\n", line);
- DHD_BLOG(line, strlen(line) + 1);
- }
}
}
}
@@ -2152,13 +2511,18 @@ err:
#ifdef DHD_DEBUG
-#define CC_PLL_CHIPCTRL_SERIAL_ENAB (1 << 24)
+#define CC_PLL_CHIPCTRL_SERIAL_ENAB (1 << 24)
+#define CC_CHIPCTRL_JTAG_SEL (1 << 3)
+#define CC_CHIPCTRL_GPIO_SEL (0x3)
+#define CC_PLL_CHIPCTRL_SERIAL_ENAB_4334 (1 << 28)
+
static int
dhd_serialconsole(dhd_bus_t *bus, bool set, bool enable, int *bcmerror)
{
int int_val;
- uint32 addr, data;
-
+ uint32 addr, data, uart_enab = 0;
+ uint32 jtag_sel = CC_CHIPCTRL_JTAG_SEL;
+ uint32 gpio_sel = CC_CHIPCTRL_GPIO_SEL;
addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_addr);
data = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_data);
@@ -2174,12 +2538,27 @@ dhd_serialconsole(dhd_bus_t *bus, bool set, bool enable, int *bcmerror)
*bcmerror = BCME_SDIO_ERROR;
return -1;
}
+ if (bus->sih->chip == BCM4330_CHIP_ID) {
+ uart_enab = CC_PLL_CHIPCTRL_SERIAL_ENAB;
+ }
+ else if (bus->sih->chip == BCM4334_CHIP_ID) {
+ if (enable) {
+ /* Moved to PMU chipcontrol 1 from 4330 */
+ int_val &= ~gpio_sel;
+ int_val |= jtag_sel;
+ } else {
+ int_val |= gpio_sel;
+ int_val &= ~jtag_sel;
+ }
+ uart_enab = CC_PLL_CHIPCTRL_SERIAL_ENAB_4334;
+ }
+
if (!set)
- return (int_val & CC_PLL_CHIPCTRL_SERIAL_ENAB);
+ return (int_val & uart_enab);
if (enable)
- int_val |= CC_PLL_CHIPCTRL_SERIAL_ENAB;
+ int_val |= uart_enab;
else
- int_val &= ~CC_PLL_CHIPCTRL_SERIAL_ENAB;
+ int_val &= ~uart_enab;
bcmsdh_reg_write(bus->sdh, data, 4, int_val);
if (bcmsdh_regfail(bus->sdh)) {
*bcmerror = BCME_SDIO_ERROR;
@@ -2189,15 +2568,15 @@ dhd_serialconsole(dhd_bus_t *bus, bool set, bool enable, int *bcmerror)
uint32 chipcontrol;
addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol);
chipcontrol = bcmsdh_reg_read(bus->sdh, addr, 4);
- chipcontrol &= ~0x8;
+ chipcontrol &= ~jtag_sel;
if (enable) {
- chipcontrol |= 0x8;
- chipcontrol &= ~0x3;
+ chipcontrol |= jtag_sel;
+ chipcontrol &= ~gpio_sel;
}
bcmsdh_reg_write(bus->sdh, addr, 4, chipcontrol);
}
- return (int_val & CC_PLL_CHIPCTRL_SERIAL_ENAB);
+ return (int_val & uart_enab);
}
#endif
@@ -2231,6 +2610,27 @@ dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const ch
goto exit;
}
+ /*
+ * Special handling for keepSdioOn: New SDIO Wake-up Mechanism
+ */
+ if ((vi->varid == IOV_KSO) && (IOV_ISSET(actionid))) {
+ dhdsdio_clk_kso_iovar(bus, bool_val);
+ goto exit;
+ } else if ((vi->varid == IOV_DEVSLEEP) && (IOV_ISSET(actionid))) {
+ {
+ dhdsdio_clk_devsleep_iovar(bus, bool_val);
+ if (!SLPAUTO_ENAB(bus) && (bool_val == FALSE) && (bus->ipend)) {
+ DHD_ERROR(("INT pending in devsleep 1, dpc_sched: %d\n",
+ bus->dpc_sched));
+ if (!bus->dpc_sched) {
+ bus->dpc_sched = TRUE;
+ dhd_sched_dpc(bus->dhd);
+ }
+ }
+ }
+ goto exit;
+ }
+
/* Handle sleep stuff before any clock mucking */
if (vi->varid == IOV_SLEEP) {
if (IOV_ISSET(actionid)) {
@@ -2341,8 +2741,8 @@ dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const ch
if ((bus->orig_ramsize) &&
((address > bus->orig_ramsize) || (address + size > bus->orig_ramsize)))
{
- uint8 enable, protect;
- si_socdevram(bus->sih, FALSE, &enable, &protect);
+ uint8 enable, protect, remap;
+ si_socdevram(bus->sih, FALSE, &enable, &protect, &remap);
if (!enable || protect) {
DHD_ERROR(("%s: ramsize 0x%08x doesn't have %d bytes at 0x%08x\n",
__FUNCTION__, bus->orig_ramsize, size, address));
@@ -2351,22 +2751,31 @@ dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const ch
bcmerror = BCME_BADARG;
break;
}
- if (enable && (bus->sih->chip == BCM4330_CHIP_ID)) {
+
+ if (!REMAP_ENAB(bus) && (address >= SOCDEVRAM_ARM_ADDR)) {
uint32 devramsize = si_socdevram_size(bus->sih);
- if ((address < SOCDEVRAM_4330_ARM_ADDR) ||
- (address + size > (SOCDEVRAM_4330_ARM_ADDR + devramsize))) {
+ if ((address < SOCDEVRAM_ARM_ADDR) ||
+ (address + size > (SOCDEVRAM_ARM_ADDR + devramsize))) {
DHD_ERROR(("%s: bad address 0x%08x, size 0x%08x\n",
__FUNCTION__, address, size));
DHD_ERROR(("%s: socram range 0x%08x,size 0x%08x\n",
- __FUNCTION__, SOCDEVRAM_4330_ARM_ADDR, devramsize));
+ __FUNCTION__, SOCDEVRAM_ARM_ADDR, devramsize));
bcmerror = BCME_BADARG;
break;
}
/* move it such that address is real now */
- address -= SOCDEVRAM_4330_ARM_ADDR;
- address += SOCDEVRAM_4330_BP_ADDR;
+ address -= SOCDEVRAM_ARM_ADDR;
+ address += SOCDEVRAM_BP_ADDR;
DHD_INFO(("%s: Request to %s %d bytes @ Mapped address 0x%08x\n",
__FUNCTION__, (set ? "write" : "read"), size, address));
+ } else if (REMAP_ENAB(bus) && REMAP_ISADDR(bus, address) && remap) {
+ /* Can not access remap region while devram remap bit is set
+ * ROM content would be returned in this case
+ */
+ DHD_ERROR(("%s: Need to disable remap for address 0x%08x\n",
+ __FUNCTION__, address));
+ bcmerror = BCME_ERROR;
+ break;
}
}
@@ -2394,7 +2803,7 @@ dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const ch
si_sdiod_drive_strength_init(bus->sih, bus->dhd->osh, dhd_sdiod_drive_strength);
break;
- case IOV_SVAL(IOV_DOWNLOAD):
+ case IOV_SVAL(IOV_SET_DOWNLOAD_STATE):
bcmerror = dhdsdio_download_state(bus, bool_val);
break;
@@ -2669,6 +3078,20 @@ dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const ch
break;
+ case IOV_GVAL(IOV_KSO):
+ int_val = dhdsdio_sleepcsr_get(bus);
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_GVAL(IOV_DEVCAP):
+ int_val = dhdsdio_devcap_get(bus);
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_DEVCAP):
+ dhdsdio_devcap_set(bus, (uint8) int_val);
+ break;
+
default:
bcmerror = BCME_UNSUPPORTED;
break;
@@ -2682,9 +3105,6 @@ exit:
dhd_os_sdunlock(bus->dhd);
- if (actionid == IOV_SVAL(IOV_DEVRESET) && bool_val == FALSE)
- dhd_preinit_ioctls((dhd_pub_t *) bus->dhd);
-
return bcmerror;
}
@@ -2692,7 +3112,7 @@ static int
dhdsdio_write_vars(dhd_bus_t *bus)
{
int bcmerror = 0;
- uint32 varsize;
+ uint32 varsize, phys_size;
uint32 varaddr;
uint8 *vbuffer;
uint32 varsizew;
@@ -2751,12 +3171,14 @@ dhdsdio_write_vars(dhd_bus_t *bus)
MFREE(bus->dhd->osh, vbuffer, varsize);
}
+ phys_size = REMAP_ENAB(bus) ? bus->ramsize : bus->orig_ramsize;
+
/* adjust to the user specified RAM */
DHD_INFO(("Physical memory size: %d, usable memory size: %d\n",
- bus->orig_ramsize, bus->ramsize));
+ phys_size, bus->ramsize));
DHD_INFO(("Vars are at %d, orig varsize is %d\n",
varaddr, varsize));
- varsize = ((bus->orig_ramsize - 4) - varaddr);
+ varsize = ((phys_size - 4) - varaddr);
/*
* Determine the length token:
@@ -2773,7 +3195,7 @@ dhdsdio_write_vars(dhd_bus_t *bus)
DHD_INFO(("New varsize is %d, length token=0x%08x\n", varsize, varsizew));
/* Write the length token to the last word */
- bcmerror = dhdsdio_membytes(bus, TRUE, (bus->orig_ramsize - 4),
+ bcmerror = dhdsdio_membytes(bus, TRUE, (phys_size - 4),
(uint8*)&varsizew, 4);
return bcmerror;
@@ -2785,9 +3207,6 @@ dhdsdio_download_state(dhd_bus_t *bus, bool enter)
uint retries;
int bcmerror = 0;
- if (!bus->sih)
- return BCME_ERROR;
-
/* To enter download state, disable ARM and reset SOCRAM.
* To exit download state, simply reset ARM (default is RAM boot).
*/
@@ -2820,6 +3239,10 @@ dhdsdio_download_state(dhd_bus_t *bus, bool enter)
goto fail;
}
+ /* Disable remap for download */
+ if (REMAP_ENAB(bus) && si_socdevram_remap_isenb(bus->sih))
+ dhdsdio_devram_remap(bus, FALSE);
+
/* Clear the top bit of memory */
if (bus->ramsize) {
uint32 zeros = 0;
@@ -2846,6 +3269,12 @@ dhdsdio_download_state(dhd_bus_t *bus, bool enter)
goto fail;
}
+ /* Enable remap before ARM reset but after vars.
+ * No backplane access in remap mode
+ */
+ if (REMAP_ENAB(bus) && !si_socdevram_remap_isenb(bus->sih))
+ dhdsdio_devram_remap(bus, TRUE);
+
if (!si_setcore(bus->sih, PCMCIA_CORE_ID, 0) &&
!si_setcore(bus->sih, SDIOD_CORE_ID, 0)) {
DHD_ERROR(("%s: Can't change back to SDIO core?\n", __FUNCTION__));
@@ -3012,6 +3441,8 @@ dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex)
/* Change our idea of bus state */
bus->dhd->busstate = DHD_BUS_DOWN;
+ if (KSO_ENAB(bus)) {
+
/* Enable clock for device interrupts */
dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
@@ -3037,6 +3468,7 @@ dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex)
/* Clear any pending interrupts now that F2 is disabled */
W_SDREG(local_hostintmask, &bus->regs->intstatus, retries);
+ }
/* Turn off the backplane clock (only) */
dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
@@ -3118,7 +3550,6 @@ dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex)
while (ready != enable && !dhd_timeout_expired(&tmo))
ready = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IORDY, NULL);
-
DHD_INFO(("%s: enable 0x%02x, ready 0x%02x (waited %uus)\n",
__FUNCTION__, enable, ready, tmo.elapsed));
@@ -3166,8 +3597,12 @@ dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex)
}
/* Restore previous clock setting */
- bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, saveclk, &err);
-
+ if (SLPAUTO_ENAB(bus))
+ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, SBSDIO_FORCE_HT, &err);
+ else
+ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, saveclk, &err);
/* If we didn't come up, turn off backplane clock */
if (dhdp->busstate != DHD_BUS_DATA)
@@ -3193,6 +3628,11 @@ dhdsdio_rxfail(dhd_bus_t *bus, bool abort, bool rtx)
DHD_ERROR(("%s: %sterminate frame%s\n", __FUNCTION__,
(abort ? "abort command, " : ""), (rtx ? ", send NAK" : "")));
+ if (!KSO_ENAB(bus)) {
+ DHD_ERROR(("%s: Device asleep\n", __FUNCTION__));
+ return;
+ }
+
if (abort) {
bcmsdh_abort(sdh, SDIO_FUNC_2);
}
@@ -3352,6 +3792,8 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq)
int errcode;
uint8 chan, seq, doff, sfdoff;
uint8 txmax;
+ uchar reorder_info_buf[WLHOST_REORDERDATA_TOTLEN];
+ uint reorder_info_len;
int ifidx = 0;
bool usechain = bus->use_rxchain;
@@ -3670,6 +4112,8 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq)
PKTSETLEN(osh, pfirst, sublen);
PKTPULL(osh, pfirst, doff);
+ reorder_info_len = sizeof(reorder_info_buf);
+
if (PKTLEN(osh, pfirst) == 0) {
PKTFREE(bus->dhd->osh, pfirst, FALSE);
if (plast) {
@@ -3679,7 +4123,8 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq)
save_pfirst = pnext;
}
continue;
- } else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pfirst) != 0) {
+ } else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pfirst, reorder_info_buf,
+ &reorder_info_len) != 0) {
DHD_ERROR(("%s: rx protocol error\n", __FUNCTION__));
bus->dhd->rx_errors++;
PKTFREE(osh, pfirst, FALSE);
@@ -3691,12 +4136,53 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq)
}
continue;
}
+ if (reorder_info_len) {
+ uint32 free_buf_count;
+ void *ppfirst;
+
+ ppfirst = pfirst;
+ /* Reordering info from the firmware */
+ dhd_process_pkt_reorder_info(bus->dhd, reorder_info_buf,
+ reorder_info_len, &ppfirst, &free_buf_count);
+
+ if (free_buf_count == 0) {
+ if (plast) {
+ PKTSETNEXT(osh, plast, pnext);
+ } else {
+ ASSERT(save_pfirst == pfirst);
+ save_pfirst = pnext;
+ }
+ continue;
+ }
+ else {
+ void *temp;
- /* this packet will go up, link back into chain and count it */
- PKTSETNEXT(osh, pfirst, pnext);
- plast = pfirst;
- num++;
+ /* go to the end of the chain and attach the pnext there */
+ temp = ppfirst;
+ while (PKTNEXT(osh, temp) != NULL) {
+ temp = PKTNEXT(osh, temp);
+ }
+ pfirst = temp;
+ if (plast) {
+ PKTSETNEXT(osh, plast, ppfirst);
+ }
+ else {
+ /* first one in the chain */
+ save_pfirst = ppfirst;
+ }
+
+ PKTSETNEXT(osh, pfirst, pnext);
+ plast = pfirst;
+ }
+ num += (uint8)free_buf_count;
+ }
+ else {
+ /* this packet will go up, link back into chain and count it */
+ PKTSETNEXT(osh, pfirst, pnext);
+ plast = pfirst;
+ num++;
+ }
#ifdef DHD_DEBUG
if (DHD_GLOM_ON()) {
DHD_GLOM(("%s subframe %d to stack, %p(%p/%d) nxt/lnk %p/%p\n",
@@ -3721,6 +4207,7 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq)
return num;
}
+
/* Return TRUE if there may be more frames to read */
static uint
dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished)
@@ -3744,6 +4231,9 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished)
uint8 *rxbuf;
int ifidx = 0;
uint rxcount = 0; /* Total frames read */
+ uchar reorder_info_buf[WLHOST_REORDERDATA_TOTLEN];
+ uint reorder_info_len;
+ uint pkt_count;
#if defined(DHD_DEBUG) || defined(SDTEST)
bool sdtest = FALSE; /* To limit message spew from test mode */
@@ -3751,6 +4241,11 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished)
DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+ if (!KSO_ENAB(bus)) {
+ DHD_ERROR(("%s: KSO off\n", __FUNCTION__));
+ return 0;
+ }
+
ASSERT(maxframes);
#ifdef SDTEST
@@ -4278,7 +4773,8 @@ deliver:
PKTFREE(bus->dhd->osh, pkt, FALSE);
dhd_os_sdunlock_rxq(bus->dhd);
continue;
- } else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pkt) != 0) {
+ } else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pkt, reorder_info_buf,
+ &reorder_info_len) != 0) {
DHD_ERROR(("%s: rx protocol error\n", __FUNCTION__));
dhd_os_sdlock_rxq(bus->dhd);
PKTFREE(bus->dhd->osh, pkt, FALSE);
@@ -4286,11 +4782,20 @@ deliver:
bus->dhd->rx_errors++;
continue;
}
+ if (reorder_info_len) {
+ /* Reordering info from the firmware */
+ dhd_process_pkt_reorder_info(bus->dhd, reorder_info_buf, reorder_info_len,
+ &pkt, &pkt_count);
+ if (pkt_count == 0)
+ continue;
+ }
+ else
+ pkt_count = 1;
/* Unlock during rx call */
dhd_os_sdunlock(bus->dhd);
- dhd_rx_frame(bus->dhd, ifidx, pkt, 1, chan);
+ dhd_rx_frame(bus->dhd, ifidx, pkt, pkt_count, chan);
dhd_os_sdlock(bus->dhd);
}
rxcount = maxframes - rxleft;
@@ -4434,8 +4939,13 @@ dhdsdio_dpc(dhd_bus_t *bus)
dhd_os_sdlock(bus->dhd);
+ if (!SLPAUTO_ENAB(bus) && !KSO_ENAB(bus)) {
+ DHD_ERROR(("%s: Device asleep\n", __FUNCTION__));
+ goto exit;
+ }
+
/* If waiting for HTAVAIL, check status */
- if (bus->clkstate == CLK_PENDING) {
+ if (!SLPAUTO_ENAB(bus) && (bus->clkstate == CLK_PENDING)) {
int err;
uint8 clkctl, devctl = 0;
@@ -4594,8 +5104,9 @@ clkwait:
(uint8 *)bus->ctrl_frame_buf, (uint32)bus->ctrl_frame_len,
NULL, NULL, NULL);
ASSERT(ret != BCME_PENDING);
-
- if (ret < 0) {
+ if (ret == BCME_NODEVICE) {
+ DHD_ERROR(("%s: Device asleep already\n", __FUNCTION__));
+ } else if (ret < 0) {
/* On failure, abort the command and terminate the frame */
DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n",
__FUNCTION__, ret));
@@ -4639,10 +5150,17 @@ clkwait:
/* Resched if events or tx frames are pending, else await next interrupt */
/* On failed register access, all bets are off: no resched or interrupts */
if ((bus->dhd->busstate == DHD_BUS_DOWN) || bcmsdh_regfail(sdh)) {
- DHD_ERROR(("%s: failed backplane access over SDIO, halting operation %d \n",
- __FUNCTION__, bcmsdh_regfail(sdh)));
- bus->dhd->busstate = DHD_BUS_DOWN;
- bus->intstatus = 0;
+ if ((bus->sih->buscorerev >= 12) && !(dhdsdio_sleepcsr_get(bus) &
+ SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) {
+ /* Bus failed because of KSO */
+ DHD_ERROR(("%s: Bus failed due to KSO\n", __FUNCTION__));
+ bus->kso = FALSE;
+ } else {
+ DHD_ERROR(("%s: failed backplane access over SDIO, halting operation\n",
+ __FUNCTION__));
+ bus->dhd->busstate = DHD_BUS_DOWN;
+ bus->intstatus = 0;
+ }
} else if (bus->clkstate == CLK_PENDING) {
/* Awaiting I_CHIPACTIVE; don't resched */
} else if (bus->intstatus || bus->ipend ||
@@ -4659,6 +5177,7 @@ clkwait:
dhdsdio_clkctl(bus, CLK_NONE, FALSE);
}
+exit:
dhd_os_sdunlock(bus->dhd);
return resched;
}
@@ -4701,9 +5220,13 @@ dhdsdio_isr(void *arg)
bus->ipend = TRUE;
/* Shouldn't get this interrupt if we're sleeping? */
- if (bus->sleeping) {
- DHD_ERROR(("INTERRUPT WHILE SLEEPING??\n"));
- return;
+ if (!SLPAUTO_ENAB(bus)) {
+ if (bus->sleeping) {
+ DHD_ERROR(("INTERRUPT WHILE SLEEPING??\n"));
+ return;
+ } else if (!KSO_ENAB(bus)) {
+ DHD_ERROR(("ISR in devsleep 1\n"));
+ }
}
/* Disable additional interrupts (is this needed now)? */
@@ -4982,8 +5505,8 @@ dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq)
if (bus->pktgen_total &&
(bus->pktgen_rcvd_rcvsession >= bus->pktgen_total)) {
bus->pktgen_count = 0;
- DHD_ERROR(("Pktgen:rcv test complete!\n"));
- bus->pktgen_rcv_state = PKTGEN_RCV_IDLE;
+ DHD_ERROR(("Pktgen:rcv test complete!\n"));
+ bus->pktgen_rcv_state = PKTGEN_RCV_IDLE;
dhdsdio_sdtest_set(bus, FALSE);
bus->pktgen_rcvd_rcvsession = 0;
}
@@ -5013,14 +5536,14 @@ dhd_bus_watchdog(dhd_pub_t *dhdp)
return FALSE;
/* Ignore the timer if simulating bus down */
- if (bus->sleeping)
+ if (!SLPAUTO_ENAB(bus) && bus->sleeping)
return FALSE;
if (dhdp->busstate == DHD_BUS_DOWN)
return FALSE;
/* Poll period: check device if appropriate. */
- if (bus->poll && (++bus->polltick >= bus->pollrate)) {
+ if (!SLPAUTO_ENAB(bus) && (bus->poll && (++bus->polltick >= bus->pollrate))) {
uint32 intstatus = 0;
/* Reset poll tick */
@@ -5060,6 +5583,9 @@ dhd_bus_watchdog(dhd_pub_t *dhdp)
if (bus->console.count >= dhd_console_ms) {
bus->console.count -= dhd_console_ms;
/* Make sure backplane clock is on */
+ if (SLPAUTO_ENAB(bus))
+ dhdsdio_bussleep(bus, FALSE);
+ else
dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
if (dhdsdio_readconsole(bus) < 0)
dhd_console_ms = 0; /* On error, stop trying */
@@ -5083,7 +5609,10 @@ dhd_bus_watchdog(dhd_pub_t *dhdp)
bus->idlecount = 0;
if (bus->activity) {
bus->activity = FALSE;
- dhdsdio_clkctl(bus, CLK_NONE, FALSE);
+ if (SLPAUTO_ENAB(bus))
+ dhdsdio_bussleep(bus, TRUE);
+ else
+ dhdsdio_clkctl(bus, CLK_NONE, FALSE);
}
}
}
@@ -5194,17 +5723,22 @@ dhdsdio_chipmatch(uint16 chipid)
return TRUE;
if (chipid == BCM4319_CHIP_ID)
return TRUE;
- if (chipid == BCM4330_CHIP_ID)
- return TRUE;
- if (chipid == BCM43239_CHIP_ID)
- return TRUE;
if (chipid == BCM4336_CHIP_ID)
return TRUE;
+ if (chipid == BCM4330_CHIP_ID)
+ return TRUE;
if (chipid == BCM43237_CHIP_ID)
return TRUE;
if (chipid == BCM43362_CHIP_ID)
return TRUE;
-
+ if (chipid == BCM4314_CHIP_ID)
+ return TRUE;
+ if (chipid == BCM4334_CHIP_ID)
+ return TRUE;
+ if (chipid == BCM43239_CHIP_ID)
+ return TRUE;
+ if (chipid == BCM4324_CHIP_ID)
+ return TRUE;
return FALSE;
}
@@ -5240,7 +5774,6 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot,
forcealign = TRUE;
-
DHD_TRACE(("%s: Enter\n", __FUNCTION__));
DHD_INFO(("%s: venid 0x%04x devid 0x%04x\n", __FUNCTION__, venid, devid));
@@ -5380,8 +5913,7 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot,
DHD_ERROR(("%s: dhd_bus_start failed\n", __FUNCTION__));
if (ret == BCME_NOTUP)
goto fail;
- }
-
+ }
/* Ok, have the per-port tell the stack we're open for business */
if (dhd_net_attach(bus->dhd, 0) != 0) {
DHD_ERROR(("%s: Net attach failed!!\n", __FUNCTION__));
@@ -5434,7 +5966,6 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva,
goto fail;
}
-
#ifdef DHD_DEBUG
if (DHD_INFO_ON()) {
uint fn, numfn;
@@ -5496,6 +6027,13 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva,
goto fail;
}
+ if (bus->sih->buscorerev >= 12)
+ dhdsdio_clk_kso_init(bus);
+ else
+ bus->kso = TRUE;
+
+ if (CST4330_CHIPMODE_SDIOD(bus->sih->chipst)) {
+ }
si_sdiod_drive_strength_init(bus->sih, osh, dhd_sdiod_drive_strength);
@@ -5513,6 +6051,8 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva,
DHD_ERROR(("%s: failed to find SOCRAM memory!\n", __FUNCTION__));
goto fail;
}
+
+
bus->ramsize = bus->orig_ramsize;
if (dhd_dongle_memsize)
dhd_dongle_setmemsize(bus, dhd_dongle_memsize);
@@ -5558,10 +6098,8 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva,
return TRUE;
fail:
- if (bus->sih != NULL) {
+ if (bus->sih != NULL)
si_detach(bus->sih);
- bus->sih = NULL;
- }
return FALSE;
}
@@ -5619,7 +6157,6 @@ dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh)
bus->rxflow = FALSE;
bus->prev_rxlim_hit = 0;
-
/* Done with backplane-dependent accesses, can drop clock... */
bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
@@ -5692,13 +6229,25 @@ dhdsdio_download_firmware(struct dhd_bus *bus, osl_t *osh, void *sdh)
{
bool ret;
- /* Download the firmware */
DHD_OS_WAKE_LOCK(bus->dhd);
- dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+
+ if (!SLPAUTO_ENAB(bus)) {
+ /* Download the firmware */
+ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+ } else {
+ int err = 0;
+ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, SBSDIO_ALP_AVAIL_REQ, &err);
+ }
ret = _dhdsdio_download_firmware(bus) == 0;
- dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
+ /* For SLPAUTO, keep ALP_REQ otherwise device can go into deep-sleep
+ * without host request
+ */
+ if (!SLPAUTO_ENAB(bus))
+ dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
+
DHD_OS_WAKE_UNLOCK(bus->dhd);
return ret;
}
@@ -5787,14 +6336,13 @@ dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, bool dongle_isolation, bool r
dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
}
#if !defined(BCMLXSDMMC)
- if (dongle_isolation == FALSE)
+ if (KSO_ENAB(bus) && (dongle_isolation == FALSE))
si_watchdog(bus->sih, 4);
#endif /* !defined(BCMLXSDMMC) */
if (bus->dhd) {
dhdsdio_clkctl(bus, CLK_NONE, FALSE);
}
si_detach(bus->sih);
- bus->sih = NULL;
if (bus->vars && bus->varsz)
MFREE(osh, bus->vars, bus->varsz);
bus->vars = NULL;
@@ -5845,6 +6393,17 @@ dhd_bus_unregister(void)
bcmsdh_unregister();
}
+/* Register a dummy SDIO client driver in order to be notified of new SDIO device */
+int dhd_bus_reg_sdio_notify(void* semaphore)
+{
+ return bcmsdh_reg_sdio_notify(semaphore);
+}
+
+void dhd_bus_unreg_sdio_notify(void)
+{
+ bcmsdh_unreg_sdio_notify();
+}
+
#ifdef BCMEMBEDIMAGE
static int
dhdsdio_download_code_array(struct dhd_bus *bus)
@@ -6108,6 +6667,8 @@ _dhdsdio_download_firmware(struct dhd_bus *bus)
dlok = TRUE;
}
}
+#else
+ BCM_REFERENCE(embed);
#endif
if (!dlok) {
DHD_ERROR(("%s: dongle image download failed\n", __FUNCTION__));
@@ -6142,6 +6703,11 @@ dhd_bcmsdh_recv_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf
{
int status;
+ if (!KSO_ENAB(bus)) {
+ DHD_ERROR(("%s: Device asleep\n", __FUNCTION__));
+ return BCME_NODEVICE;
+ }
+
status = bcmsdh_recv_buf(bus->sdh, addr, fn, flags, buf, nbytes, pkt, complete, handle);
return status;
@@ -6151,6 +6717,11 @@ static int
dhd_bcmsdh_send_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf, uint nbytes,
void *pkt, bcmsdh_cmplt_fn_t complete, void *handle)
{
+ if (!KSO_ENAB(bus)) {
+ DHD_ERROR(("%s: Device asleep\n", __FUNCTION__));
+ return BCME_NODEVICE;
+ }
+
return (bcmsdh_send_buf(bus->sdh, addr, fn, flags, buf, nbytes, pkt, complete, handle));
}
@@ -6265,7 +6836,7 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag)
bcmerror = BCME_SDIO_ERROR;
#ifdef DHDTHREAD
- dhd_os_sdunlock(dhdp);
+ dhd_os_sdunlock(dhdp);
#endif /* DHDTHREAD */
} else {
bcmerror = BCME_SDIO_ERROR;