/drivers/net/wireless/bcmdhd/dhd_sdio.c
https://bitbucket.org/cyanogenmod/android_kernel_asus_tf300t · C · 6359 lines · 4798 code · 999 blank · 562 comment · 1138 complexity · 3c52bd19fa50b30dca420548ef79f3e5 MD5 · raw file
Large files are truncated click here to view the full file
- /*
- * DHD Bus Module for SDIO
- *
- * Copyright (C) 1999-2011, Broadcom Corporation
- *
- * 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
- * following added to such license:
- *
- * As a special exception, the copyright holders of this software give you
- * permission to link this software with independent modules, and to copy and
- * distribute the resulting executable under terms of your choice, provided that
- * you also meet, for each linked independent module, the terms and conditions of
- * the license of that module. An independent module is a module which is not
- * derived from this software. The special exception does not apply to any
- * modifications of the software.
- *
- * Notwithstanding the above, under no circumstances may you combine this
- * 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 326662 2012-04-10 06:38:08Z $
- */
- #include <typedefs.h>
- #include <osl.h>
- #include <bcmsdh.h>
- #ifdef BCMEMBEDIMAGE
- #include BCMEMBEDIMAGE
- #endif /* BCMEMBEDIMAGE */
- #include <bcmdefs.h>
- #include <bcmutils.h>
- #include <bcmendian.h>
- #include <bcmdevs.h>
- #include <siutils.h>
- #include <hndpmu.h>
- #include <hndsoc.h>
- #include <bcmsdpcm.h>
- #if defined(DHD_DEBUG)
- #include <hndrte_armtrap.h>
- #include <hndrte_cons.h>
- #endif /* defined(DHD_DEBUG) */
- #include <sbchipc.h>
- #include <sbhnddma.h>
- #include <sdio.h>
- #include <sbsdio.h>
- #include <sbsdpcmdev.h>
- #include <bcmsdpcm.h>
- #include <bcmsdbus.h>
- #include <proto/ethernet.h>
- #include <proto/802.1d.h>
- #include <proto/802.11.h>
- #include <dngl_stats.h>
- #include <dhd.h>
- #include <dhd_bus.h>
- #include <dhd_proto.h>
- #include <dhd_dbg.h>
- #include <dhdioctl.h>
- #include <sdiovar.h>
- #ifndef DHDSDIO_MEM_DUMP_FNAME
- #define DHDSDIO_MEM_DUMP_FNAME "mem_dump"
- #endif
- #define QLEN 256 /* bulk rx and tx queue lengths */
- #define FCHI (QLEN - 10)
- #define FCLOW (FCHI / 2)
- #define PRIOMASK 7
- #define TXRETRIES 2 /* # of retries for tx frames */
- #define DHD_RXBOUND 50 /* Default for max rx frames in one scheduling */
- #define DHD_TXBOUND 20 /* Default for max tx frames in one scheduling */
- #define DHD_TXMINMAX 1 /* Max tx frames if rx still pending */
- #define MEMBLOCK 2048 /* Block size used for downloading of dongle image */
- #define MAX_NVRAMBUF_SIZE 4096 /* max nvram buf size */
- #define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold biggest possible glom */
- #ifndef DHD_FIRSTREAD
- #define DHD_FIRSTREAD 32
- #endif
- #if !ISPOWEROF2(DHD_FIRSTREAD)
- #error DHD_FIRSTREAD is not a power of 2!
- #endif
- /* Total length of frame header for dongle protocol */
- #define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN)
- #ifdef SDTEST
- #define SDPCM_RESERVE (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN)
- #else
- #define SDPCM_RESERVE (SDPCM_HDRLEN + DHD_SDALIGN)
- #endif
- /* Space for header read, limit for data packets */
- #ifndef MAX_HDR_READ
- #define MAX_HDR_READ 32
- #endif
- #if !ISPOWEROF2(MAX_HDR_READ)
- #error MAX_HDR_READ is not a power of 2!
- #endif
- #define MAX_RX_DATASZ 2048
- /* Maximum milliseconds to wait for F2 to come up */
- #define DHD_WAIT_F2RDY 3000
- /* Bump up limit on waiting for HT to account for first startup;
- * if the image is doing a CRC calculation before programming the PMU
- * for HT availability, it could take a couple hundred ms more, so
- * max out at a 1 second (1000000us).
- */
- #if (PMU_MAX_TRANSITION_DLY <= 1000000)
- #undef PMU_MAX_TRANSITION_DLY
- #define PMU_MAX_TRANSITION_DLY 1000000
- #endif
- /* Value for ChipClockCSR during initial setup */
- #define DHD_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ)
- #define DHD_INIT_CLKCTL2 (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP)
- /* Flags for SDH calls */
- #define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED)
- /* Packet free applicable unconditionally for sdio and sdspi. Conditional if
- * bufpool was present for gspi bus.
- */
- #define PKTFREE2() if ((bus->bus != SPI_BUS) || bus->usebufpool) \
- PKTFREE(bus->dhd->osh, pkt, FALSE);
- DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep);
- #if defined(OOB_INTR_ONLY)
- extern void bcmsdh_set_irq(int flag);
- #endif /* defined(OOB_INTR_ONLY) */
- #ifdef PROP_TXSTATUS
- extern void dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success);
- #endif
- #ifdef DHD_DEBUG
- /* Device console log buffer state */
- #define CONSOLE_LINE_MAX 192
- #define CONSOLE_BUFFER_MAX 2024
- typedef struct dhd_console {
- uint count; /* Poll interval msec counter */
- uint log_addr; /* Log struct address (fixed) */
- hndrte_log_t log; /* Log struct (host copy) */
- uint bufsize; /* Size of log buffer */
- uint8 *buf; /* Log buffer (host copy) */
- uint last; /* Last buffer read index */
- } dhd_console_t;
- #endif /* DHD_DEBUG */
- /* Private data for SDIO bus interaction */
- typedef struct dhd_bus {
- dhd_pub_t *dhd;
- bcmsdh_info_t *sdh; /* Handle for BCMSDH calls */
- si_t *sih; /* Handle for SI calls */
- char *vars; /* Variables (from CIS and/or other) */
- uint varsz; /* Size of variables buffer */
- uint32 sbaddr; /* Current SB window pointer (-1, invalid) */
- sdpcmd_regs_t *regs; /* Registers for SDIO core */
- uint sdpcmrev; /* SDIO core revision */
- uint armrev; /* CPU core revision */
- uint ramrev; /* SOCRAM core revision */
- uint32 ramsize; /* Size of RAM in SOCRAM (bytes) */
- uint32 orig_ramsize; /* Size of RAM in SOCRAM (bytes) */
- uint32 bus; /* gSPI or SDIO bus */
- uint32 hostintmask; /* Copy of Host Interrupt Mask */
- uint32 intstatus; /* Intstatus bits (events) pending */
- bool dpc_sched; /* Indicates DPC schedule (intrpt rcvd) */
- 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 */
- const char *nvram_params; /* user specified nvram params. */
- uint blocksize; /* Block size of SDIO transfers */
- uint roundup; /* Max roundup limit */
- struct pktq txq; /* Queue length used for flow-control */
- uint8 flowcontrol; /* per prio flow control bitmask */
- uint8 tx_seq; /* Transmit sequence number (next) */
- uint8 tx_max; /* Maximum transmit sequence allowed */
- uint8 hdrbuf[MAX_HDR_READ + DHD_SDALIGN];
- uint8 *rxhdr; /* Header of current rx frame (in hdrbuf) */
- uint16 nextlen; /* Next Read Len from last header */
- uint8 rx_seq; /* Receive sequence number (expected) */
- bool rxskip; /* Skip receive (awaiting NAK ACK) */
- void *glomd; /* Packet containing glomming descriptor */
- void *glom; /* Packet chain for glommed superframe */
- uint glomerr; /* Glom packet read errors */
- uint8 *rxbuf; /* Buffer for receiving control packets */
- uint rxblen; /* Allocated length of rxbuf */
- uint8 *rxctl; /* Aligned pointer into rxbuf */
- uint8 *databuf; /* Buffer for receiving big glom packet */
- uint8 *dataptr; /* Aligned pointer into databuf */
- uint rxlen; /* Length of valid data in buffer */
- uint8 sdpcm_ver; /* Bus protocol reported by dongle */
- bool intr; /* Use interrupts */
- bool poll; /* Use polling */
- bool ipend; /* Device interrupt is pending */
- bool intdis; /* Interrupts disabled by isr */
- uint intrcount; /* Count of device interrupt callbacks */
- uint lastintrs; /* Count as of last watchdog timer */
- uint spurious; /* Count of spurious interrupts */
- uint pollrate; /* Ticks between device polls */
- uint polltick; /* Tick counter */
- uint pollcnt; /* Count of active polls */
- #ifdef DHD_DEBUG
- dhd_console_t console; /* Console output polling support */
- uint console_addr; /* Console address from shared struct */
- #endif /* DHD_DEBUG */
- uint regfails; /* Count of R_REG/W_REG failures */
- uint clkstate; /* State of sd and backplane clock(s) */
- bool activity; /* Activity flag for clock down */
- int32 idletime; /* Control for activity timeout */
- int32 idlecount; /* Activity timeout counter */
- int32 idleclock; /* How to set bus driver when idle */
- int32 sd_divisor; /* Speed control to bus driver */
- int32 sd_mode; /* Mode control to bus driver */
- 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 */
- 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) */
- /* Field to decide if rx of control frames happen in rxbuf or lb-pool */
- bool usebufpool;
- #ifdef SDTEST
- /* external loopback */
- bool ext_loop;
- uint8 loopid;
- /* pktgen configuration */
- uint pktgen_freq; /* Ticks between bursts */
- uint pktgen_count; /* Packets to send each burst */
- uint pktgen_print; /* Bursts between count displays */
- uint pktgen_total; /* Stop after this many */
- uint pktgen_minlen; /* Minimum packet data len */
- uint pktgen_maxlen; /* Maximum packet data len */
- uint pktgen_mode; /* Configured mode: tx, rx, or echo */
- uint pktgen_stop; /* Number of tx failures causing stop */
- /* active pktgen fields */
- uint pktgen_tick; /* Tick counter for bursts */
- uint pktgen_ptick; /* Burst counter for printing */
- uint pktgen_sent; /* Number of test packets generated */
- uint pktgen_rcvd; /* Number of test packets received */
- uint pktgen_fail; /* Number of failed send attempts */
- uint16 pktgen_len; /* Length of next packet to send */
- #define PKTGEN_RCV_IDLE (0)
- #define PKTGEN_RCV_ONGOING (1)
- uint16 pktgen_rcv_state; /* receive state */
- uint pktgen_rcvd_rcvsession; /* test pkts rcvd per rcv session. */
- #endif /* SDTEST */
- /* Some additional counters */
- uint tx_sderrs; /* Count of tx attempts with sd errors */
- uint fcqueued; /* Tx packets that got queued */
- uint rxrtx; /* Count of rtx requests (NAK to dongle) */
- uint rx_toolong; /* Receive frames too long to receive */
- uint rxc_errors; /* SDIO errors when reading control frames */
- uint rx_hdrfail; /* SDIO errors on header reads */
- uint rx_badhdr; /* Bad received headers (roosync?) */
- uint rx_badseq; /* Mismatched rx sequence number */
- uint fc_rcvd; /* Number of flow-control events received */
- uint fc_xoff; /* Number which turned on flow-control */
- uint fc_xon; /* Number which turned off flow-control */
- uint rxglomfail; /* Failed deglom attempts */
- uint rxglomframes; /* Number of glom frames (superframes) */
- uint rxglompkts; /* Number of packets from glom frames */
- uint f2rxhdrs; /* Number of header reads */
- uint f2rxdata; /* Number of frame data reads */
- uint f2txdata; /* Number of f2 frame writes */
- uint f1regdata; /* Number of f1 register accesses */
- uint8 *ctrl_frame_buf;
- uint32 ctrl_frame_len;
- bool ctrl_frame_stat;
- uint32 rxint_mode; /* rx interrupt mode */
- } dhd_bus_t;
- /* clkstate */
- #define CLK_NONE 0
- #define CLK_SDONLY 1
- #define CLK_PENDING 2 /* Not used yet */
- #define CLK_AVAIL 3
- #define DHD_NOPMU(dhd) (FALSE)
- #ifdef DHD_DEBUG
- static int qcount[NUMPRIO];
- static int tx_packets[NUMPRIO];
- #endif /* DHD_DEBUG */
- /* Deferred transmit */
- const uint dhd_deferred_tx = 1;
- extern uint dhd_watchdog_ms;
- extern void dhd_os_wd_timer(void *bus, uint wdtick);
- /* Tx/Rx bounds */
- uint dhd_txbound;
- uint dhd_rxbound;
- uint dhd_txminmax = DHD_TXMINMAX;
- /* override the RAM size if possible */
- #define DONGLE_MIN_MEMSIZE (128 *1024)
- int dhd_dongle_memsize;
- static bool dhd_doflow;
- static bool dhd_alignctl;
- static bool sd1idle;
- static bool retrydata;
- #define RETRYCHAN(chan) (((chan) == SDPCM_EVENT_CHANNEL) || retrydata)
- static const uint watermark = 8;
- static const uint firstread = DHD_FIRSTREAD;
- #define HDATLEN (firstread - (SDPCM_HDRLEN))
- /* Retry count for register access failures */
- static const uint retry_limit = 20;
- /* Force even SD lengths (some host controllers mess up on odd bytes) */
- static bool forcealign;
- /* Flag to indicate if we should download firmware on driver load */
- uint dhd_download_fw_on_driverload = TRUE;
- #define ALIGNMENT 4
- #if defined(OOB_INTR_ONLY) && defined(HW_OOB)
- extern void bcmsdh_enable_hw_oob_intr(void *sdh, bool enable);
- #endif
- #if defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD)
- #error OOB_INTR_ONLY is NOT working with SDIO_ISR_THREAD
- #endif /* defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD) */
- #define PKTALIGN(osh, p, len, align) \
- do { \
- uint datalign; \
- datalign = (uintptr)PKTDATA((osh), (p)); \
- datalign = ROUNDUP(datalign, (align)) - datalign; \
- ASSERT(datalign < (align)); \
- ASSERT(PKTLEN((osh), (p)) >= ((len) + datalign)); \
- if (datalign) \
- PKTPULL((osh), (p), datalign); \
- PKTSETLEN((osh), (p), (len)); \
- } while (0)
- /* Limit on rounding up frames */
- 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) && \
- (((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0))
- /* To check if there's window offered for ctrl frame */
- #define TXCTLOK(bus) \
- (((uint8)(bus->tx_max - bus->tx_seq) != 0) && \
- (((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0))
- /* Macros to get register read/write status */
- /* NOTE: these assume a local dhdsdio_bus_t *bus! */
- #define R_SDREG(regvar, regaddr, retryvar) \
- do { \
- retryvar = 0; \
- do { \
- regvar = R_REG(bus->dhd->osh, regaddr); \
- } while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \
- if(retryvar > 1) \
- DHD_ERROR(("%s: regvar[ %d ], retryvar[ %d ], regfails[ %d ], bcmsdh_regfail[ %d ] \n",__FUNCTION__,regvar, retryvar ,bus->regfails, bcmsdh_regfail(bus->sdh))); \
- if (retryvar) { \
- bus->regfails += (retryvar-1); \
- if (retryvar > retry_limit) { \
- DHD_ERROR(("%s: FAILED" #regvar "READ, LINE %d\n", \
- __FUNCTION__, __LINE__)); \
- regvar = 0; \
- } \
- } \
- } while (0)
- #define W_SDREG(regval, regaddr, retryvar) \
- do { \
- retryvar = 0; \
- do { \
- W_REG(bus->dhd->osh, regaddr, regval); \
- } while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \
- if (retryvar) { \
- bus->regfails += (retryvar-1); \
- if (retryvar > retry_limit) \
- DHD_ERROR(("%s: FAILED REGISTER WRITE, LINE %d\n", \
- __FUNCTION__, __LINE__)); \
- } \
- } while (0)
- #define BUS_WAKE(bus) \
- do { \
- if ((bus)->sleeping) \
- dhdsdio_bussleep((bus), FALSE); \
- } while (0);
- /*
- * pktavail interrupts from dongle to host can be managed in 3 different ways
- * whenever there is a packet available in dongle to transmit to host.
- *
- * Mode 0: Dongle writes the software host mailbox and host is interrupted.
- * Mode 1: (sdiod core rev >= 4)
- * Device sets a new bit in the intstatus whenever there is a packet
- * available in fifo. Host can't clear this specific status bit until all the
- * packets are read from the FIFO. No need to ack dongle intstatus.
- * Mode 2: (sdiod core rev >= 4)
- * Device sets a bit in the intstatus, and host acks this by writing
- * one to this bit. Dongle won't generate anymore packet interrupts
- * until host reads all the packets from the dongle and reads a zero to
- * figure that there are no more packets. No need to disable host ints.
- * Need to ack the intstatus.
- */
- #define SDIO_DEVICE_HMB_RXINT 0 /* default old way */
- #define SDIO_DEVICE_RXDATAINT_MODE_0 1 /* from sdiod rev 4 */
- #define SDIO_DEVICE_RXDATAINT_MODE_1 2 /* from sdiod rev 4 */
- #define FRAME_AVAIL_MASK(bus) \
- ((bus->rxint_mode == SDIO_DEVICE_HMB_RXINT) ? I_HMB_FRAME_IND : I_XMTDATA_AVAIL)
- #define DHD_BUS SDIO_BUS
- #define PKT_AVAILABLE(bus, intstatus) ((intstatus) & (FRAME_AVAIL_MASK(bus)))
- #define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE)
- #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);
- #endif
- #ifdef DHD_DEBUG
- 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_download_state(dhd_bus_t *bus, bool enter);
- static void dhdsdio_release(dhd_bus_t *bus, osl_t *osh);
- static void dhdsdio_release_malloc(dhd_bus_t *bus, osl_t *osh);
- static void dhdsdio_disconnect(void *ptr);
- static bool dhdsdio_chipmatch(uint16 chipid);
- static bool dhdsdio_probe_attach(dhd_bus_t *bus, osl_t *osh, void *sdh,
- void * regsva, uint16 devid);
- static bool dhdsdio_probe_malloc(dhd_bus_t *bus, osl_t *osh, void *sdh);
- static bool dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh);
- static void dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, bool dongle_isolation,
- bool reset_flag);
- static void dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size);
- static int dhd_bcmsdh_recv_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags,
- uint8 *buf, uint nbytes,
- void *pkt, bcmsdh_cmplt_fn_t complete, void *handle);
- 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);
- static bool dhdsdio_download_firmware(dhd_bus_t *bus, osl_t *osh, void *sdh);
- static int _dhdsdio_download_firmware(dhd_bus_t *bus);
- static int dhdsdio_download_code_file(dhd_bus_t *bus, char *image_path);
- static int dhdsdio_download_nvram(dhd_bus_t *bus);
- #ifdef BCMEMBEDIMAGE
- static int dhdsdio_download_code_array(dhd_bus_t *bus);
- #endif
- #ifdef WLMEDIA_HTSF
- #include <htsf.h>
- extern uint32 dhd_get_htsf(void *dhd, int ifidx);
- #endif /* WLMEDIA_HTSF */
- extern int chip_is_b1;
- static void
- dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size)
- {
- int32 min_size = DONGLE_MIN_MEMSIZE;
- /* Restrict the memsize to user specified limit */
- DHD_ERROR(("user: Restrict the dongle ram size to %d, min accepted %d\n",
- dhd_dongle_memsize, min_size));
- if ((dhd_dongle_memsize > min_size) &&
- (dhd_dongle_memsize < (int32)bus->orig_ramsize))
- bus->ramsize = dhd_dongle_memsize;
- }
- static int
- dhdsdio_set_siaddr_window(dhd_bus_t *bus, uint32 address)
- {
- int err = 0;
- bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW,
- (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err);
- if (!err)
- bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID,
- (address >> 16) & SBSDIO_SBADDRMID_MASK, &err);
- if (!err)
- bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH,
- (address >> 24) & SBSDIO_SBADDRHIGH_MASK, &err);
- return err;
- }
- /* Turn backplane clock on or off */
- static int
- dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok)
- {
- int err;
- uint8 clkctl, clkreq, devctl;
- bcmsdh_info_t *sdh;
- DHD_TRACE(("%s: Enter\n", __FUNCTION__));
- #if defined(OOB_INTR_ONLY)
- pendok = FALSE;
- #endif
- clkctl = 0;
- sdh = bus->sdh;
- 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));
- return BCME_ERROR;
- }
- if (pendok &&
- ((bus->sih->buscoretype == PCMCIA_CORE_ID) && (bus->sih->buscorerev == 9))) {
- uint32 dummy, retries;
- R_SDREG(dummy, &bus->regs->clockctlstatus, retries);
- }
- /* Check current status */
- clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err);
- if (err) {
- DHD_ERROR(("%s: HT Avail read error: %d\n", __FUNCTION__, err));
- return BCME_ERROR;
- }
- /* Go to pending and await interrupt if appropriate */
- if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) {
- /* Allow only clock-available interrupt */
- devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err);
- if (err) {
- DHD_ERROR(("%s: Devctl access error setting CA: %d\n",
- __FUNCTION__, err));
- return BCME_ERROR;
- }
- devctl |= SBSDIO_DEVCTL_CA_INT_ONLY;
- bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err);
- DHD_INFO(("CLKCTL: set PENDING\n"));
- bus->clkstate = CLK_PENDING;
- return BCME_OK;
- } else if (bus->clkstate == CLK_PENDING) {
- /* Cancel CA-only interrupt filter */
- devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err);
- devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
- bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err);
- }
- /* Otherwise, wait here (polling) for HT Avail */
- if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
- SPINWAIT_SLEEP(sdioh_spinwait_sleep,
- ((clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
- SBSDIO_FUNC1_CHIPCLKCSR, &err)),
- !SBSDIO_CLKAV(clkctl, bus->alp_only)), PMU_MAX_TRANSITION_DLY);
- }
- if (err) {
- DHD_ERROR(("%s: HT Avail request error: %d\n", __FUNCTION__, err));
- return BCME_ERROR;
- }
- if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
- DHD_ERROR(("%s: HT Avail timeout (%d): clkctl 0x%02x\n",
- __FUNCTION__, PMU_MAX_TRANSITION_DLY, clkctl));
- return BCME_ERROR;
- }
- /* Mark clock available */
- bus->clkstate = CLK_AVAIL;
- DHD_INFO(("CLKCTL: turned ON\n"));
- #if defined(DHD_DEBUG)
- if (bus->alp_only == TRUE) {
- #if !defined(BCMLXSDMMC)
- if (!SBSDIO_ALPONLY(clkctl)) {
- DHD_ERROR(("%s: HT Clock, when ALP Only\n", __FUNCTION__));
- }
- #endif /* !defined(BCMLXSDMMC) */
- } else {
- if (SBSDIO_ALPONLY(clkctl)) {
- DHD_ERROR(("%s: HT Clock should be on.\n", __FUNCTION__));
- }
- }
- #endif /* defined (DHD_DEBUG) */
- 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);
- devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
- bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err);
- }
- bus->clkstate = CLK_SDONLY;
- bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err);
- DHD_INFO(("CLKCTL: turned OFF\n"));
- if (err) {
- DHD_ERROR(("%s: Failed access turning clock off: %d\n",
- __FUNCTION__, err));
- return BCME_ERROR;
- }
- }
- return BCME_OK;
- }
- /* Change idle/active SD state */
- static int
- dhdsdio_sdclk(dhd_bus_t *bus, bool on)
- {
- int err;
- int32 iovalue;
- DHD_TRACE(("%s: Enter\n", __FUNCTION__));
- if (on) {
- if (bus->idleclock == DHD_IDLE_STOP) {
- /* Turn on clock and restore mode */
- iovalue = 1;
- err = bcmsdh_iovar_op(bus->sdh, "sd_clock", NULL, 0,
- &iovalue, sizeof(iovalue), TRUE);
- if (err) {
- DHD_ERROR(("%s: error enabling sd_clock: %d\n",
- __FUNCTION__, err));
- return BCME_ERROR;
- }
- iovalue = bus->sd_mode;
- err = bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0,
- &iovalue, sizeof(iovalue), TRUE);
- if (err) {
- DHD_ERROR(("%s: error changing sd_mode: %d\n",
- __FUNCTION__, err));
- return BCME_ERROR;
- }
- } else if (bus->idleclock != DHD_IDLE_ACTIVE) {
- /* Restore clock speed */
- iovalue = bus->sd_divisor;
- err = bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0,
- &iovalue, sizeof(iovalue), TRUE);
- if (err) {
- DHD_ERROR(("%s: error restoring sd_divisor: %d\n",
- __FUNCTION__, err));
- return BCME_ERROR;
- }
- }
- bus->clkstate = CLK_SDONLY;
- } else {
- /* Stop or slow the SD clock itself */
- if ((bus->sd_divisor == -1) || (bus->sd_mode == -1)) {
- DHD_TRACE(("%s: can't idle clock, divisor %d mode %d\n",
- __FUNCTION__, bus->sd_divisor, bus->sd_mode));
- return BCME_ERROR;
- }
- if (bus->idleclock == DHD_IDLE_STOP) {
- if (sd1idle) {
- /* Change to SD1 mode and turn off clock */
- iovalue = 1;
- err = bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0,
- &iovalue, sizeof(iovalue), TRUE);
- if (err) {
- DHD_ERROR(("%s: error changing sd_clock: %d\n",
- __FUNCTION__, err));
- return BCME_ERROR;
- }
- }
- iovalue = 0;
- err = bcmsdh_iovar_op(bus->sdh, "sd_clock", NULL, 0,
- &iovalue, sizeof(iovalue), TRUE);
- if (err) {
- DHD_ERROR(("%s: error disabling sd_clock: %d\n",
- __FUNCTION__, err));
- return BCME_ERROR;
- }
- } else if (bus->idleclock != DHD_IDLE_ACTIVE) {
- /* Set divisor to idle value */
- iovalue = bus->idleclock;
- err = bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0,
- &iovalue, sizeof(iovalue), TRUE);
- if (err) {
- DHD_ERROR(("%s: error changing sd_divisor: %d\n",
- __FUNCTION__, err));
- return BCME_ERROR;
- }
- }
- bus->clkstate = CLK_NONE;
- }
- return BCME_OK;
- }
- /* Transition SD and backplane clock readiness */
- static int
- dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok)
- {
- int ret = BCME_OK;
- #ifdef DHD_DEBUG
- uint oldstate = bus->clkstate;
- #endif /* DHD_DEBUG */
- DHD_TRACE(("%s: Enter\n", __FUNCTION__));
- /* Early exit if we're already there */
- if (bus->clkstate == target) {
- if (target == CLK_AVAIL) {
- dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
- bus->activity = TRUE;
- }
- return ret;
- }
- switch (target) {
- case CLK_AVAIL:
- /* Make sure SD clock is available */
- if (bus->clkstate == CLK_NONE)
- dhdsdio_sdclk(bus, TRUE);
- /* Now request HT Avail on the backplane */
- ret = dhdsdio_htclk(bus, TRUE, pendok);
- if (ret == BCME_OK) {
- dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
- bus->activity = TRUE;
- }
- break;
- case CLK_SDONLY:
- /* Remove HT request, or bring up SD clock */
- if (bus->clkstate == CLK_NONE)
- ret = dhdsdio_sdclk(bus, TRUE);
- else if (bus->clkstate == CLK_AVAIL)
- ret = dhdsdio_htclk(bus, FALSE, FALSE);
- else
- DHD_ERROR(("dhdsdio_clkctl: request for %d -> %d\n",
- bus->clkstate, target));
- if (ret == BCME_OK) {
- dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
- }
- break;
- case CLK_NONE:
- /* Make sure to remove HT request */
- if (bus->clkstate == CLK_AVAIL)
- ret = dhdsdio_htclk(bus, FALSE, FALSE);
- /* Now remove the SD clock */
- ret = dhdsdio_sdclk(bus, FALSE);
- #ifdef DHD_DEBUG
- if (dhd_console_ms == 0)
- #endif /* DHD_DEBUG */
- if (bus->poll == 0)
- dhd_os_wd_timer(bus->dhd, 0);
- break;
- }
- #ifdef DHD_DEBUG
- DHD_INFO(("dhdsdio_clkctl: %d -> %d\n", oldstate, bus->clkstate));
- #endif /* DHD_DEBUG */
- return ret;
- }
- static int
- dhdsdio_bussleep(dhd_bus_t *bus, bool sleep)
- {
- bcmsdh_info_t *sdh = bus->sdh;
- sdpcmd_regs_t *regs = bus->regs;
- uint retries = 0;
- DHD_INFO(("dhdsdio_bussleep: request %s (currently %s)\n",
- (sleep ? "SLEEP" : "WAKE"),
- (bus->sleeping ? "SLEEP" : "WAKE")));
- /* Done if we're already in the requested state */
- if (sleep == bus->sleeping)
- return BCME_OK;
- /* Going to sleep: set the alarm and turn off the lights... */
- if (sleep) {
- /* Don't sleep if something is pending */
- if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq))
- return BCME_BUSY;
- /* 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);
- /* Tell device to start using OOB wakeup */
- W_SDREG(SMB_USE_OOB, ®s->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);
- 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);
- }
- /* Change state */
- bus->sleeping = TRUE;
- } else {
- /* Waking up: bus power up is ok, set local state */
- bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
- 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);
- /* Send misc interrupt to indicate OOB not needed */
- W_SDREG(0, ®s->tosbmailboxdata, retries);
- if (retries <= retry_limit)
- W_SDREG(SMB_DEV_INT, ®s->tosbmailbox, retries);
- 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);
- /* 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);
- }
- }
- return BCME_OK;
- }
- #if defined(OOB_INTR_ONLY)
- void
- dhd_enable_oob_intr(struct dhd_bus *bus, bool enable)
- {
- #if defined(HW_OOB)
- bcmsdh_enable_hw_oob_intr(bus->sdh, enable);
- #else
- sdpcmd_regs_t *regs = bus->regs;
- uint retries = 0;
- dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
- if (enable == TRUE) {
- /* Tell device to start using OOB wakeup */
- W_SDREG(SMB_USE_OOB, ®s->tosbmailbox, retries);
- if (retries > retry_limit)
- DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n"));
- } else {
- /* Send misc interrupt to indicate OOB not needed */
- W_SDREG(0, ®s->tosbmailboxdata, retries);
- if (retries <= retry_limit)
- W_SDREG(SMB_DEV_INT, ®s->tosbmailbox, retries);
- }
- /* Turn off our contribution to the HT clock request */
- dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
- #endif /* !defined(HW_OOB) */
- }
- #endif /* defined(OOB_INTR_ONLY) */
- /* Writes a HW/SW header into the packet and sends it. */
- /* Assumes: (a) header space already there, (b) caller holds lock */
- static int
- dhdsdio_txpkt(dhd_bus_t *bus, void *pkt, uint chan, bool free_pkt)
- {
- int ret;
- osl_t *osh;
- uint8 *frame;
- uint16 len, pad1 = 0;
- uint32 swheader;
- uint retries = 0;
- bcmsdh_info_t *sdh;
- void *new;
- int i;
- #ifdef WLMEDIA_HTSF
- char *p;
- htsfts_t *htsf_ts;
- #endif
- DHD_TRACE(("%s: Enter\n", __FUNCTION__));
- sdh = bus->sdh;
- osh = bus->dhd->osh;
- if (bus->dhd->dongle_reset) {
- ret = BCME_NOTREADY;
- goto done;
- }
- frame = (uint8*)PKTDATA(osh, pkt);
- #ifdef WLMEDIA_HTSF
- if (PKTLEN(osh, pkt) >= 100) {
- p = PKTDATA(osh, pkt);
- htsf_ts = (htsfts_t*) (p + HTSF_HOSTOFFSET + 12);
- if (htsf_ts->magic == HTSFMAGIC) {
- htsf_ts->c20 = get_cycles();
- htsf_ts->t20 = dhd_get_htsf(bus->dhd->info, 0);
- }
- }
- #endif /* WLMEDIA_HTSF */
- /* Add alignment padding, allocate new packet if needed */
- if ((pad1 = ((uintptr)frame % DHD_SDALIGN))) {
- if (PKTHEADROOM(osh, pkt) < pad1) {
- DHD_INFO(("%s: insufficient headroom %d for %d pad1\n",
- __FUNCTION__, (int)PKTHEADROOM(osh, pkt), pad1));
- bus->dhd->tx_realloc++;
- new = PKTGET(osh, (PKTLEN(osh, pkt) + DHD_SDALIGN), TRUE);
- if (!new) {
- DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n",
- __FUNCTION__, PKTLEN(osh, pkt) + DHD_SDALIGN));
- ret = BCME_NOMEM;
- goto done;
- }
- PKTALIGN(osh, new, PKTLEN(osh, pkt), DHD_SDALIGN);
- bcopy(PKTDATA(osh, pkt), PKTDATA(osh, new), PKTLEN(osh, pkt));
- if (free_pkt)
- PKTFREE(osh, pkt, TRUE);
- /* free the pkt if canned one is not used */
- free_pkt = TRUE;
- pkt = new;
- frame = (uint8*)PKTDATA(osh, pkt);
- ASSERT(((uintptr)frame % DHD_SDALIGN) == 0);
- pad1 = 0;
- } else {
- PKTPUSH(osh, pkt, pad1);
- frame = (uint8*)PKTDATA(osh, pkt);
- ASSERT((pad1 + SDPCM_HDRLEN) <= (int) PKTLEN(osh, pkt));
- bzero(frame, pad1 + SDPCM_HDRLEN);
- }
- }
- ASSERT(pad1 < DHD_SDALIGN);
- /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
- len = (uint16)PKTLEN(osh, pkt);
- *(uint16*)frame = htol16(len);
- *(((uint16*)frame) + 1) = htol16(~len);
- /* Software tag: channel, sequence number, data offset */
- swheader = ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq |
- (((pad1 + SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK);
- htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN);
- htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
- #ifdef DHD_DEBUG
- if (PKTPRIO(pkt) < ARRAYSIZE(tx_packets)) {
- tx_packets[PKTPRIO(pkt)]++;
- }
- if (DHD_BYTES_ON() &&
- (((DHD_CTL_ON() && (chan == SDPCM_CONTROL_CHANNEL)) ||
- (DHD_DATA_ON() && (chan != SDPCM_CONTROL_CHANNEL))))) {
- prhex("Tx Frame", frame, len);
- } else if (DHD_HDRS_ON()) {
- prhex("TxHdr", frame, MIN(len, 16));
- }
- #endif
- /* Raise len to next SDIO block to eliminate tail command */
- if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
- uint16 pad2 = bus->blocksize - (len % bus->blocksize);
- if ((pad2 <= bus->roundup) && (pad2 < bus->blocksize))
- #ifdef NOTUSED
- if (pad2 <= PKTTAILROOM(osh, pkt))
- #endif /* NOTUSED */
- len += pad2;
- } else if (len % DHD_SDALIGN) {
- len += DHD_SDALIGN - (len % DHD_SDALIGN);
- }
- /* Some controllers have trouble with odd bytes -- round to even */
- if (forcealign && (len & (ALIGNMENT - 1))) {
- #ifdef NOTUSED
- if (PKTTAILROOM(osh, pkt))
- #endif
- len = ROUNDUP(len, ALIGNMENT);
- #ifdef NOTUSED
- else
- DHD_ERROR(("%s: sending unrounded %d-byte packet\n", __FUNCTION__, len));
- #endif
- }
- do {
- ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC,
- frame, len, pkt, NULL, NULL);
- bus->f2txdata++;
- ASSERT(ret != BCME_PENDING);
- 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));
- bus->tx_sderrs++;
- bcmsdh_abort(sdh, SDIO_FUNC_2);
- bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL,
- SFC_WF_TERM, NULL);
- bus->f1regdata++;
- for (i = 0; i < 3; i++) {
- uint8 hi, lo;
- hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
- SBSDIO_FUNC1_WFRAMEBCHI, NULL);
- lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
- SBSDIO_FUNC1_WFRAMEBCLO, NULL);
- bus->f1regdata += 2;
- if ((hi == 0) && (lo == 0))
- break;
- }
- }
- if (ret == 0) {
- bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
- }
- } while ((ret < 0) && retrydata && retries++ < TXRETRIES);
- done:
- /* restore pkt buffer pointer before calling tx complete routine */
- PKTPULL(osh, pkt, SDPCM_HDRLEN + pad1);
- #ifdef PROP_TXSTATUS
- if (bus->dhd->wlfc_state) {
- dhd_os_sdunlock(bus->dhd);
- dhd_wlfc_txcomplete(bus->dhd, pkt, ret == 0);
- dhd_os_sdlock(bus->dhd);
- } else {
- #endif /* PROP_TXSTATUS */
- dhd_txcomplete(bus->dhd, pkt, ret != 0);
- if (free_pkt)
- PKTFREE(osh, pkt, TRUE);
- #ifdef PROP_TXSTATUS
- }
- #endif
- return ret;
- }
- int
- dhd_bus_txdata(struct dhd_bus *bus, void *pkt)
- {
- int ret = BCME_ERROR;
- osl_t *osh;
- uint datalen, prec;
- DHD_TRACE(("%s: Enter\n", __FUNCTION__));
- osh = bus->dhd->osh;
- datalen = PKTLEN(osh, pkt);
- #ifdef SDTEST
- /* Push the test header if doing loopback */
- if (bus->ext_loop) {
- uint8* data;
- PKTPUSH(osh, pkt, SDPCM_TEST_HDRLEN);
- data = PKTDATA(osh, pkt);
- *data++ = SDPCM_TEST_ECHOREQ;
- *data++ = (uint8)bus->loopid++;
- *data++ = (datalen >> 0);
- *data++ = (datalen >> 8);
- datalen += SDPCM_TEST_HDRLEN;
- }
- #endif /* SDTEST */
- /* Add space for the header */
- PKTPUSH(osh, pkt, SDPCM_HDRLEN);
- ASSERT(ISALIGNED((uintptr)PKTDATA(osh, pkt), 2));
- prec = PRIO2PREC((PKTPRIO(pkt) & PRIOMASK));
- #ifndef DHDTHREAD
- /* Lock: we're about to use shared data/code (and SDIO) */
- dhd_os_sdlock(bus->dhd);
- #endif /* DHDTHREAD */
- /* Check for existing queue, current flow-control, pending event, or pending clock */
- if (dhd_deferred_tx || bus->fcstate || pktq_len(&bus->txq) || bus->dpc_sched ||
- (!DATAOK(bus)) || (bus->flowcontrol & NBITVAL(prec)) ||
- (bus->clkstate != CLK_AVAIL)) {
- DHD_TRACE(("%s: deferring pktq len %d\n", __FUNCTION__,
- pktq_len(&bus->txq)));
- bus->fcqueued++;
- /* Priority based enq */
- dhd_os_sdlock_txq(bus->dhd);
- if (dhd_prec_enq(bus->dhd, &bus->txq, pkt, prec) == FALSE) {
- PKTPULL(osh, pkt, SDPCM_HDRLEN);
- #ifndef DHDTHREAD
- /* Need to also release txqlock before releasing sdlock.
- * This thread still has txqlock and releases sdlock.
- * Deadlock happens when dpc() grabs sdlock first then
- * attempts to grab txqlock.
- */
- dhd_os_sdunlock_txq(bus->dhd);
- dhd_os_sdunlock(bus->dhd);
- #endif
- #ifdef PROP_TXSTATUS
- if (bus->dhd->wlfc_state)
- dhd_wlfc_txcomplete(bus->dhd, pkt, FALSE);
- else
- #endif
- dhd_txcomplete(bus->dhd, pkt, FALSE);
- #ifndef DHDTHREAD
- dhd_os_sdlock(bus->dhd);
- dhd_os_sdlock_txq(bus->dhd);
- #endif
- #ifdef PROP_TXSTATUS
- /* let the caller decide whether to free the packet */
- if (!bus->dhd->wlfc_state)
- #endif
- PKTFREE(osh, pkt, TRUE);
- ret = BCME_NORESOURCE;
- }
- else
- ret = BCME_OK;
- dhd_os_sdunlock_txq(bus->dhd);
- if ((pktq_len(&bus->txq) >= FCHI) && dhd_doflow)
- dhd_txflowcontrol(bus->dhd, ALL_INTERFACES, ON);
- #ifdef DHD_DEBUG
- if (pktq_plen(&bus->txq, prec) > qcount[prec])
- qcount[prec] = pktq_plen(&bus->txq, prec);
- #endif
- /* Schedule DPC if needed to send queued packet(s) */
- if (dhd_deferred_tx && !bus->dpc_sched) {
- bus->dpc_sched = TRUE;
- dhd_sched_dpc(bus->dhd);
- }
- } else {
- #ifdef DHDTHREAD
- /* Lock: we're about to use shared data/code (and SDIO) */
- dhd_os_sdlock(bus->dhd);
- #endif /* DHDTHREAD */
- /* Otherwise, send it now */
- BUS_WAKE(bus);
- /* Make sure back plane ht clk is on, no pending allowed */
- dhdsdio_clkctl(bus, CLK_AVAIL, TRUE);
- #ifndef SDTEST
- ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, TRUE);
- #else
- ret = dhdsdio_txpkt(bus, pkt,
- (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), TRUE);
- #endif
- if (ret)
- bus->dhd->tx_errors++;
- else
- bus->dhd->dstats.tx_bytes += datalen;
- if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
- bus->activity = FALSE;
- dhdsdio_clkctl(bus, CLK_NONE, TRUE);
- }
- #ifdef DHDTHREAD
- dhd_os_sdunlock(bus->dhd);
- #endif /* DHDTHREAD */
- }
- #ifndef DHDTHREAD
- dhd_os_sdunlock(bus->dhd);
- #endif /* DHDTHREAD */
- return ret;
- }
- static uint
- dhdsdio_sendfromq(dhd_bus_t *bus, uint maxframes)
- {
- void *pkt;
- uint32 intstatus = 0;
- uint retries = 0;
- int ret = 0, prec_out;
- uint cnt = 0;
- uint datalen;
- uint8 tx_prec_map;
- dhd_pub_t *dhd = bus->dhd;
- sdpcmd_regs_t *regs = bus->regs;
- DHD_TRACE(("%s: Enter\n", __FUNCTION__));
- tx_prec_map = ~bus->flowcontrol;
- /* Send frames until the limit or some other event */
- for (cnt = 0; (cnt < maxframes) && DATAOK(bus); cnt++) {
- dhd_os_sdlock_txq(bus->dhd);
- if ((pkt = pktq_mdeq(&bus->txq, tx_prec_map, &prec_out)) == NULL) {
- dhd_os_sdunlock_txq(bus->dhd);
- break;
- }
- dhd_os_sdunlock_txq(bus->dhd);
- datalen = PKTLEN(bus->dhd->osh, pkt) - SDPCM_HDRLEN;
- #ifndef SDTEST
- ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, TRUE);
- #else
- ret = dhdsdio_txpkt(bus, pkt,
- (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), TRUE);
- #endif
- if (ret)
- bus->dhd->tx_errors++;
- else
- bus->dhd->dstats.tx_bytes += datalen;
- /* In poll mode, need to check for other events */
- if (!bus->intr && cnt)
- {
- /* Check device status, signal pending interrupt */
- R_SDREG(intstatus, ®s->intstatus, retries);
- bus->f2txdata++;
- if (bcmsdh_regfail(bus->sdh))
- break;
- if (intstatus & bus->hostintmask)
- bus->ipend = TRUE;
- }
- }
- /* Deflow-control stack if needed */
- if (dhd_doflow && dhd->up && (dhd->busstate == DHD_BUS_DATA) &&
- dhd->txoff && (pktq_len(&bus->txq) < FCLOW))
- dhd_txflowcontrol(dhd, ALL_INTERFACES, OFF);
- return cnt;
- }
- int
- dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen)
- {
- uint8 *frame;
- uint16 len;
- uint32 swheader;
- uint retries = 0;
- bcmsdh_info_t *sdh = bus->sdh;
- uint8 doff = 0;
- int ret = -1;
- int i;
- DHD_TRACE(("%s: Enter\n", __FUNCTION__));
- if (bus->dhd->dongle_reset)
- return -EIO;
- /* Back the pointer to make a room for bus header */
- frame = msg - SDPCM_HDRLEN;
- len = (msglen += SDPCM_HDRLEN);
- /* Add alignment padding (optional for ctl frames) */
- if (dhd_alignctl) {
- if ((doff = ((uintptr)frame % DHD_SDALIGN))) {
- frame -= doff;
- len += doff;
- msglen += doff;
- bzero(frame, doff + SDPCM_HDRLEN);
- }
- ASSERT(doff < DHD_SDALIGN);
- }
- doff += SDPCM_HDRLEN;
- /* Round send length to next SDIO block */
- if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
- uint16 pad = bus->blocksize - (len % bus->blocksize);
- if ((pad <= bus->roundup) && (pad < bus->blocksize))
- len += pad;
- } else if (len % DHD_SDALIGN) {
- len += DHD_SDALIGN - (len % DHD_SDALIGN);
- }
- /* Satisfy length-alignment requirements */
- if (forcealign && (len & (ALIGNMENT - 1)))
- len = ROUNDUP(len, ALIGNMENT);
- ASSERT(ISALIGNED((uintptr)frame, 2));
- /* Need to lock here to protect txseq and SDIO tx calls */
- dhd_os_sdlock(bus->dhd);
- BUS_WAKE(bus);
- /* Make sure backplane clock is on */
- dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
- /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
- *(uint16*)frame = htol16((uint16)msglen);
- *(((uint16*)frame) + 1) = htol16(~msglen);
- /* Software tag: channel, sequence number, data offset */
- swheader = ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK)
- | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK);
- htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN);
- htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
- if (!TXCTLOK(bus)) {
- DHD_INFO(("%s: No bus credit bus->tx_max %d, bus->tx_seq %d\n",
- __FUNCTION__, bus->tx_max, bus->tx_seq));
- bus->ctrl_frame_stat = TRUE;
- /* 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;
- } else {
- bus->dhd->txcnt_timeout++;
- if (!bus->dhd->hang_was_sent)
- DHD_ERROR(("%s: ctrl_frame_stat == TRUE txcnt_timeout=%d\n",
- __FUNCTION__, bus->dhd->txcnt_timeout));
- ret = -1;
- bus->ctrl_frame_stat = FALSE;
- goto done;
- }
- }
- bus->dhd->txcnt_timeout = 0;
- if (ret == -1) {
- #ifdef DHD_DEBUG
- if (DHD_BYTES_ON() && DHD_CTL_ON()) {
- prhex("Tx Frame", frame, len);
- } else if (DHD_HDRS_ON()) {
- prhex("TxHdr", frame, MIN(len, 16));
- }
- #endif
- do {
- ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC,
- frame, len, NULL, NULL, NULL);
- ASSERT(ret != BCME_PENDING);
- 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));
- bus->tx_sderrs++;
- bcmsdh_abort(sdh, SDIO_FUNC_2);
- bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL,
- SFC_WF_TERM, NULL);
- bus->f1regdata++;
- for (i = 0; i < 3; i++) {
- uint8 hi, lo;
- hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
- SBSDIO_FUNC1_WFRAMEBCHI, NULL);
- lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
- SBSDIO_FUNC1_WFRAMEBCLO, NULL);
- bus->f1regdata += 2;
- if ((hi == 0) && (lo == 0))
- break;
- }
- }
- if (ret == 0) {
- bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
- }
- } while ((ret < 0) && retries++ < TXRETRIES);
- }
- done:
- if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
- bus->activity = FALSE;
- dhdsdio_clkctl(bus, CLK_NONE, TRUE);
- }
- dhd_os_sdunlock(bus->dhd);
- if (ret)
- bus->dhd->tx_ctlerrs++;
- else
- bus->dhd->tx_ctlpkts++;
- if (bus->dhd->txcnt_timeout >= MAX_CNTL_TIMEOUT)
- return -ETIMEDOUT;
- return ret ? -EIO : 0;
- }
- int
- dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen)
- {
- int timeleft;
- uint rxlen = 0;
- bool pending = FALSE;
- DHD_TRACE(("%s: Enter\n", __FUNCTION__));
- if (bus->dhd->dongle_reset)
- return -EIO;
- /* Wait until control frame is available */
- timeleft = dhd_os_ioctl_resp_wait(bus->dhd, &bus->rxlen, &pending);
- dhd_os_sdlock(bus->dhd);
- rxlen = bus->rxlen;
- bcopy(bus->rxctl, msg, MIN(msglen, rxlen));
- bus->rxlen = 0;
- dhd_os_sdunlock(bus->dhd);
- if (rxlen) {
- DHD_CTL(("%s: resumed on rxctl frame, got %d expected %d\n",
- __FUNCTION__, rxlen, 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);
- #endif /* DHD_DEBUG */
- } else if (pending == TRUE) {
- /* signal pending */
- DHD_ERROR(("%s: signal pending\n", __FUNCTION__));
- return -EINTR;
- } else {
- DHD_CTL(("%s: resumed for unknown reason?\n", __FUNCTION__));
- #ifdef DHD_DEBUG
- dhd_os_sdlock(bus->dhd);
- dhdsdio_checkdied(bus, NULL, 0);
- dhd_os_sdunlock(bus->dhd);
- #endif /* DHD_DEBUG */
- }
- if (timeleft == 0) {
- bus->dhd->rxcnt_timeout++;
- DHD_ERROR(("%s: rxcnt_timeout=%d\n", __FUNCTION__, bus->dhd->rxcnt_timeout));
- }
- else
- bus->dhd->rxcnt_timeout = 0;
- if (rxlen)
- bus->dhd->rx_ctlpkts++;
- else
- bus->dhd->rx_ctlerrs++;
- if (bus->dhd->rxcnt_timeout >= MAX_CNTL_TIMEOUT)
- return -ETIMEDOUT;
- return rxlen ? (int)rxlen : -EIO;
- }
- /* IOVar table */
- enum {
- IOV_INTR = 1,
- IOV_POLLRATE,
- IOV_SDREG,
- IOV_SBREG,
- IOV_SDCIS,
- IOV_MEMBYTES,
- IOV_MEMSIZE,
- #ifdef DHD_DEBUG
- IOV_CHECKDIED,
- IOV_SERIALCONS,
- #endif /* DHD_DEBUG */
- IOV_DOWNLOAD,
- IOV_SOCRAM_STATE,
- IOV_FORCEEVEN,
- IOV_SDIOD_DRIVE,
- IOV_READAHEAD,
- IOV_SDRXCHAIN,
- IOV_ALIGNCTL,
- IOV_SDALIGN,
- IOV_DEVRESET,
- IOV_CPU,
- #ifdef SDTEST
- IOV_PKTGEN,
- IOV_EXTLOOP,
- #endif /* SDTEST */
- IOV_SPROM,
- IOV_TXBOUND,
- IOV_RXBOUND,
- IOV_TXMINMAX,
- IOV_IDLETIME,
- IOV_IDLECLOCK,
- IOV_SD1IDLE,
- IOV_SLEEP,
- IOV_DONGLEISOLATION,
- IOV_VARS,
- #ifdef SOFTAP
- IOV_FWPATH
- #endif
- };
- const bcm_iovar_t dhdsdio_iovars[] = {
- {"intr", IOV_INTR, 0, IOVT_BOOL, 0 },
- {"sleep", IOV_SLEEP, 0, IOVT_BOOL, 0 },
- {"pollrate", IOV_POLLRATE, 0, IOVT_UINT32, 0 },
- {"idletime", IOV_IDLETIME, 0, IOVT_INT32, 0 },
- {"idleclock", IOV_IDLECLOCK, 0, IOVT_INT32, 0 },
- {"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 },
- {"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 },
- {"readahead", IOV_READAHEAD, 0, IOVT_BOOL, 0 },
- {"sdrxchain", IOV_SDRXCHAIN, 0, IOVT_BOOL, 0 },
- {"alignctl", IOV_ALIGNCTL, 0, IOVT_BOOL, 0 },
- {"sdalign", IOV_SDALIGN, 0, IOVT_BOOL, 0 },
- {"devreset", IOV_DEVRESET, 0, IOVT_BOOL, 0 },
- #ifdef DHD_DEBUG
- {"sdreg", IOV_SDREG, 0, IOVT_BUFFER, sizeof(sdreg_t) },
- {"sbreg", IOV_SBREG, 0, IOVT_BUFFER, sizeof(sdreg_t) },
- {"sd_cis", IOV_SDCIS, 0, IOVT_BUFFER, DHD_IOCTL_MAXLEN },
- {"forcealign", IOV_FORCEEVEN, 0, IOVT_BOOL, 0 },
- {"txbound", IOV_TXBOUND, 0, IOVT_UINT32, 0 },
- {"rxbound", IOV_RXBOUND, 0, IOVT_UINT32, 0 },
- {"txminmax", IOV_TXMINMAX, 0, IOVT_UINT32, 0 },
- {"cpu", IOV_CPU, 0, IOVT_BOOL, 0 },
- #ifdef DHD_DEBUG
- {"checkdied", IOV_CHECKDIED, 0, IOVT_BUFFER, 0 },
- {"serial", IOV_SERIALCONS, 0, IOVT_UINT32, 0 },
- #endif /* DHD_DEBUG */
- #endif /* DHD_DEBUG */
- #ifdef SDTEST
- {"extloop", IOV_EXTLOOP, 0, IOVT_BOOL, 0 },
- {"pktgen", IOV_PKTGEN, 0, IOVT_BUFFER, sizeof(dhd_pktgen_t) },
- #endif /* SDTEST */
- {"dngl_isolation", IOV_DONGLEISOLATION, 0, IOVT_UINT32, 0 },
- #ifdef SOFTAP
- {"fwpath", IOV_FWPATH, 0, IOVT_BUFFER, 0 },
- #endif
- {NULL, 0, 0, 0, 0 }
- };
- static void
- dhd_dump_pct(struct bcmstrbuf *strbuf, char *desc, uint num, uint div)
- {
- uint q1, q2;
- if (!div) {
- bcm_bprintf(strbuf, "%s N/A", desc);
- } else {
- q1 = num / div;
- q2 = (100 * (num - (q1 * div))) / div;
- bcm_bprintf(strbuf, "%s %d.%02d", desc, q1, q2);
- }
- }
- void
- dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
- {
- dhd_bus_t *bus = dhdp->bus;
- bcm_bprintf(strbuf, "Bus SDIO structure:\n");
- bcm_bprintf(strbuf, "hostintmask 0x%08x intstatus 0x%08x sdpcm_ver %d\n",
- bus->hostintmask, bus->intstatus, bus->sdpcm_ver);
- bcm_bprintf(strbuf, "fcstate %d qlen %d tx_seq %d, max %d, rxskip %d rxlen %d rx_seq %d\n",
- bus->fcstate, pktq_len(&bus->txq), bus->tx_seq, bus->tx_max, bus->rxskip,
- bus->rxlen, bus->rx_seq);
- bcm_bprintf(strbuf, "intr %d intrcount %d lastintrs %d spurious %d\n",
- bus->intr, bus->intrcount, bus->lastintrs, bus->spurious);
- bcm_bprintf(strbuf, "pollrate %d pollcnt %d regfails %d\n",
- bus->pollrate, bus->pollcnt, bus->regfails);
- bcm_bprintf(strbuf, "\nAdditional counters:\n");
- bcm_bprintf(strbuf, "tx_sderrs %d fcqueued %d rxrtx %d rx_toolong %d rxc_errors %d\n",
- bus->tx_sderrs, bus->fcqueued, bus->rxrtx, bus->rx_toolong,
- bus->rxc_errors);
- bcm_bprintf(strbuf, "rx_hdrfail %d badhdr %d badseq %d\n",
- bus->rx_hdrfail, bus->rx_badhdr, bus->rx_badseq);
- bcm_bprintf(strbuf, "fc_rcvd %d, fc_xoff %d, fc_xon %d\n",
- bus->fc_rcvd, bus->fc_xoff, bus->fc_xon);
- bcm_bprintf(strbuf, "rxglomfail %d, rxglomframes %d, rxglompkts %d\n",
- bus->rxglomfail, bus->rxglomframes, bus->rxglompkts);
- bcm_bprintf(strbuf, "f2rx (hdrs/data) %d (%d/%d), f2tx %d f1regs %d\n",
- (bus->f2rxhdrs + bus->f2rxdata), bus->f2rxhdrs, bus->f2rxdata,
- bus->f2txdata, bus->f1regdata);
- {
- dhd_dump_pct(strbuf, "\nRx: pkts/f2rd", bus->dhd->rx_packets,
- (bus->f2rxhdrs + bus->f2rxdata));
- dhd_dump_pct(strbuf, ", pkts/f1sd", bus->dhd->rx_packets, bus->f1regdata);
- dhd_dump_pct(strbuf, ", pkts/sd", bus->dhd->rx_packets,
- (bus->f2rxhdrs + bus->f2rxdata + bus->f1regdata));
- dhd_dump_pct(strbuf, ", pkts/int", bus->dhd->rx_packets, bus->intrcount);
- bcm_bprintf(strbuf, "\n");
- dhd_dump_pct(strbuf, "Rx: glom pct", (100…