/* Device console log buffer state */ #define CONSOLE_BUFFER_MAX 2024
struct rte_log_le {
__le32 buf; /* Can't be pointer on (64-bit) hosts */
__le32 buf_size;
__le32 idx; char *_buf_compat; /* Redundant pointer for backward compat. */
};
struct rte_console { /* Virtual UART * When there is no UART (e.g. Quickturn), * the host should write a complete * input line directly into cbuf and then write * the length into vcons_in. * This may also be used when there is a real UART * (at risk of conflicting with * the real UART). vcons_out is currently unused.
*/
uint vcons_in;
uint vcons_out;
/* Output (logging) buffer * Console output is written to a ring buffer log_buf at index log_idx. * The host may read the output when it sees log_idx advance. * Output will be lost if the output wraps around faster than the host * polls.
*/ struct rte_log_le log_le;
/* Console input line buffer * Characters are read one at a time into cbuf * until <CR> is received, then * the buffer is processed as a command line. * Also used for virtual UART.
*/
uint cbuf_idx; char cbuf[CBUF_LEN];
};
#define TXQLEN 2048 /* bulk tx queue length */ #define TXHI (TXQLEN - 256) /* turn on flow control above TXHI */ #define TXLOW (TXHI - 256) /* turn off flow control below TXLOW */ #define PRIOMASK 7
#define TXRETRIES 2 /* # of retries for tx frames */
#define BRCMF_RXBOUND 50 /* Default for max rx frames in
one scheduling */
#define BRCMF_TXBOUND 20 /* Default for max tx frames in
one scheduling */
#define BRCMF_TXMINMAX 1 /* Max tx frames if rx still pending */
#define MEMBLOCK 2048 /* Block size used for downloading
of dongle image */ #define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold
biggest possible glom */
#define BRCMF_FIRSTREAD (1 << 6)
/* SBSDIO_DEVICE_CTL */
/* 1: device will assert busy signal when receiving CMD53 */ #define SBSDIO_DEVCTL_SETBUSY 0x01 /* 1: assertion of sdio interrupt is synchronous to the sdio clock */ #define SBSDIO_DEVCTL_SPI_INTR_SYNC 0x02 /* 1: mask all interrupts to host except the chipActive (rev 8) */ #define SBSDIO_DEVCTL_CA_INT_ONLY 0x04 /* 1: isolate internal sdio signals, put external pads in tri-state; requires
* sdio bus power cycle to clear (rev 9) */ #define SBSDIO_DEVCTL_PADS_ISO 0x08 /* 1: enable F2 Watermark */ #define SBSDIO_DEVCTL_F2WM_ENAB 0x10 /* Force SD->SB reset mapping (rev 11) */ #define SBSDIO_DEVCTL_SB_RST_CTL 0x30 /* Determined by CoreControl bit */ #define SBSDIO_DEVCTL_RST_CORECTL 0x00 /* Force backplane reset */ #define SBSDIO_DEVCTL_RST_BPRESET 0x10 /* Force no backplane reset */ #define SBSDIO_DEVCTL_RST_NOBPRESET 0x20
/* direct(mapped) cis space */
/* MAPPED common CIS address */ #define SBSDIO_CIS_BASE_COMMON 0x1000 /* maximum bytes in one CIS */ #define SBSDIO_CIS_SIZE_LIMIT 0x200 /* cis offset addr is < 17 bits */ #define SBSDIO_CIS_OFT_ADDR_MASK 0x1FFFF
/* manfid tuple length, include tuple, link bytes */ #define SBSDIO_CIS_MANFID_TUPLE_LEN 6
/* Current protocol version */ #define SDPCM_PROT_VERSION 4
/* * Shared structure between dongle and the host. * The structure contains pointers to trap or assert information.
*/ #define SDPCM_SHARED_VERSION 0x0003 #define SDPCM_SHARED_VERSION_MASK 0x00FF #define SDPCM_SHARED_ASSERT_BUILT 0x0100 #define SDPCM_SHARED_ASSERT 0x0200 #define SDPCM_SHARED_TRAP 0x0400
/* Space for header read, limit for data packets */ #define MAX_HDR_READ (1 << 6) #define MAX_RX_DATASZ 2048
/* 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).
*/ #undef PMU_MAX_TRANSITION_DLY #define PMU_MAX_TRANSITION_DLY 1000000
/* Value for ChipClockCSR during initial setup */ #define BRCMF_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | \
SBSDIO_ALP_AVAIL_REQ)
/* dongle SDIO bus specific header info */ struct brcmf_sdio_hdrinfo {
u8 seq_num;
u8 channel;
u16 len;
u16 len_left;
u16 len_nxtfrm;
u8 dat_offset; bool lastfrm;
u16 tail_pad;
};
/* * hold counter variables
*/ struct brcmf_sdio_count {
uint intrcount; /* Count of device interrupt callbacks */
uint lastintrs; /* Count as of last watchdog timer */
uint pollcnt; /* Count of active polls */
uint regfails; /* Count of R_REG failures */
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 */
uint tickcnt; /* Number of watchdog been schedule */
ulong tx_ctlerrs; /* Err of sending ctrl frames */
ulong tx_ctlpkts; /* Ctrl frames sent to dongle */
ulong rx_ctlerrs; /* Err of processing rx ctrl frames */
ulong rx_ctlpkts; /* Ctrl frames processed from dongle */
ulong rx_readahead_cnt; /* packets where header read-ahead was used */
};
/* misc chip info needed by some of the routines */ /* Private data for SDIO bus interaction */ struct brcmf_sdio { struct brcmf_sdio_dev *sdiodev; /* sdio device handler */ struct brcmf_chip *ci; /* Chip info struct */ struct brcmf_core *sdio_core; /* sdio core info struct */
u32 hostintmask; /* Copy of Host Interrupt Mask */
atomic_t intstatus; /* Intstatus bits (events) pending */
atomic_t fcstate; /* State of dongle flow-control */
uint blocksize; /* Block size of SDIO transfers */
uint roundup; /* Max roundup limit */
struct pktq txq; /* Queue length used for flow-control */
u8 flowcontrol; /* per prio flow control bitmask */
u8 tx_seq; /* Transmit sequence number (next) */
u8 tx_max; /* Maximum transmit sequence allowed */
u8 *hdrbuf; /* buffer for handling rx frame */
u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */
u8 rx_seq; /* Receive sequence number (expected) */ struct brcmf_sdio_hdrinfo cur_read; /* info of current read frame */ bool rxskip; /* Skip receive (awaiting NAK ACK) */ bool rxpending; /* Data frame pending in dongle */
uint rxbound; /* Rx frames to read before resched */
uint txbound; /* Tx frames to send before resched */
uint txminmax;
struct sk_buff *glomd; /* Packet containing glomming descriptor */ struct sk_buff_head glom; /* Packet list for glommed superframe */
u8 *rxbuf; /* Buffer for receiving control packets */
uint rxblen; /* Allocated length of rxbuf */
u8 *rxctl; /* Aligned pointer into rxbuf */
u8 *rxctl_orig; /* pointer for freeing rxctl */
uint rxlen; /* Length of valid data in buffer */
spinlock_t rxctl_lock; /* protection lock for ctrl frame resources */
u8 sdpcm_ver; /* Bus protocol reported by dongle */
bool intr; /* Use interrupts */ bool poll; /* Use polling */
atomic_t ipend; /* Device interrupt is pending */
uint spurious; /* Count of spurious interrupts */
uint pollrate; /* Ticks between device polls */
uint polltick; /* Tick counter */
uint clkstate; /* State of sd and backplane clock(s) */
s32 idletime; /* Control for activity timeout */
s32 idlecount; /* Activity timeout counter */
s32 idleclock; /* How to set bus driver when idle */ bool rxflow_mode; /* Rx flow control mode */ bool rxflow; /* Is rx flow control on */ bool alp_only; /* Don't use HT clock (ALP only) */
u8 *ctrl_frame_buf;
u16 ctrl_frame_len; bool ctrl_frame_stat; int ctrl_frame_err;
/* SDIO Pad drive strength to select value mappings */ struct sdiod_drive_str {
u8 strength; /* Pad Drive Strength in mA */
u8 sel; /* Chip-specific select value */
};
/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */ staticconststruct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = {
{32, 0x6},
{26, 0x7},
{22, 0x4},
{16, 0x5},
{12, 0x2},
{8, 0x3},
{4, 0x0},
{0, 0x1}
};
/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */ staticconststruct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = {
{6, 0x7},
{5, 0x6},
{4, 0x5},
{3, 0x4},
{2, 0x2},
{1, 0x1},
{0, 0x0}
};
/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */ staticconststruct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = {
{3, 0x3},
{2, 0x2},
{1, 0x1},
{0, 0x0} };
/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */ staticconststruct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = {
{16, 0x7},
{12, 0x5},
{8, 0x3},
{4, 0x1}
};
staticint
brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
{
u8 wr_val = 0, rd_val, cmp_val, bmask; int err = 0; int err_cnt = 0; int try_cnt = 0;
brcmf_dbg(TRACE, "Enter: on=%d\n", on);
sdio_retune_crc_disable(bus->sdiodev->func1);
/* Cannot re-tune if device is asleep; defer till we're awake */ if (on)
sdio_retune_hold_now(bus->sdiodev->func1);
wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); /* 1st KSO write goes to AOS wake up core if device is asleep */
brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err);
/* In case of 43012 chip, the chip could go down immediately after * KSO bit is cleared. So the further reads of KSO register could * fail. Thereby just bailing out immediately after clearing KSO * bit, to avoid polling of KSO bit.
*/ if (!on && bus->ci->chip == CY_CC_43012_CHIP_ID) return err;
if (on) { /* device WAKEUP through KSO: * write bit 0 & read back until * both bits 0 (kso bit) & 1 (dev on status) are set
*/
cmp_val = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK |
SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK;
bmask = cmp_val;
usleep_range(2000, 3000);
} else { /* Put device to sleep, turn off KSO */
cmp_val = 0; /* only check for bit0, bit1(dev on status) may not * get cleared right away
*/
bmask = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK;
}
do { /* reliable KSO bit set/clr: * the sdiod sleep write access is synced to PMU 32khz clk * just one write attempt may fail, * read it back until it matches written value
*/
rd_val = brcmf_sdiod_readb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
&err); if (!err) { if ((rd_val & bmask) == cmp_val) break;
err_cnt = 0;
} /* bail out upon subsequent access errors */ if (err && (err_cnt++ > BRCMF_SDIO_MAX_ACCESS_ERRORS)) break;
/* Turn backplane clock on or off */ staticint brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
{ int err;
u8 clkctl, clkreq, devctl; unsignedlong timeout;
brcmf_dbg(SDIO, "Enter\n");
clkctl = 0;
if (bus->sr_enabled) {
bus->clkstate = (on ? CLK_AVAIL : CLK_SDONLY); return 0;
}
/* Early exit if we're already there */ if (bus->clkstate == target) return 0;
switch (target) { case CLK_AVAIL: /* Make sure SD clock is available */ if (bus->clkstate == CLK_NONE)
brcmf_sdio_sdclk(bus, true); /* Now request HT Avail on the backplane */
brcmf_sdio_htclk(bus, true, pendok); break;
case CLK_SDONLY: /* Remove HT request, or bring up SD clock */ if (bus->clkstate == CLK_NONE)
brcmf_sdio_sdclk(bus, true); elseif (bus->clkstate == CLK_AVAIL)
brcmf_sdio_htclk(bus, false, false); else
brcmf_err("request for %d -> %d\n",
bus->clkstate, target); break;
case CLK_NONE: /* Make sure to remove HT request */ if (bus->clkstate == CLK_AVAIL)
brcmf_sdio_htclk(bus, false, false); /* Now remove the SD clock */
brcmf_sdio_sdclk(bus, false); break;
} #ifdef DEBUG
brcmf_dbg(SDIO, "%d -> %d\n", oldstate, bus->clkstate); #endif/* DEBUG */
/* If SR is enabled control bus state with KSO */ if (bus->sr_enabled) { /* Done if we're already in the requested state */ if (sleep == bus->sleeping) goto end;
/* Going to sleep */ if (sleep) {
clkcsr = brcmf_sdiod_readb(bus->sdiodev,
SBSDIO_FUNC1_CHIPCLKCSR,
&err); if ((clkcsr & SBSDIO_CSR_MASK) == 0) {
brcmf_dbg(SDIO, "no clock, set ALP\n");
brcmf_sdiod_writeb(bus->sdiodev,
SBSDIO_FUNC1_CHIPCLKCSR,
SBSDIO_ALP_AVAIL_REQ, &err);
}
err = brcmf_sdio_kso_control(bus, false);
} else {
err = brcmf_sdio_kso_control(bus, true);
} if (err) {
brcmf_err("error while changing bus sleep state %d\n",
err); goto done;
}
}
end: /* control clocks */ if (sleep) { if (!bus->sr_enabled)
brcmf_sdio_clkctl(bus, CLK_NONE, pendok);
} else {
brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok);
brcmf_sdio_wd_timer(bus, true);
}
bus->sleeping = sleep;
brcmf_dbg(SDIO, "new state %s\n",
(sleep ? "SLEEP" : "WAKE"));
done:
brcmf_dbg(SDIO, "Exit: err=%d\n", err); return err;
/* * Read last word in socram to determine * address of sdpcm_shared structure
*/
shaddr = bus->ci->rambase + bus->ci->ramsize - 4; if (!bus->ci->rambase && brcmf_chip_sr_capable(bus->ci))
shaddr -= bus->ci->srsize;
rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr,
(u8 *)&addr_le, 4); if (rv < 0) goto fail;
/* * Check if addr is valid. * NVRAM length at the end of memory should have been overwritten.
*/
addr = le32_to_cpu(addr_le); if (!brcmf_sdio_valid_shared_address(addr)) {
brcmf_err("invalid sdpcm_shared address 0x%08X\n", addr);
rv = -EINVAL; goto fail;
}
/* * DEVREADY does not occur with gSPI.
*/ if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) {
bus->sdpcm_ver =
(hmb_data & HMB_DATA_VERSION_MASK) >>
HMB_DATA_VERSION_SHIFT; if (bus->sdpcm_ver != SDPCM_PROT_VERSION)
brcmf_err("Version mismatch, dongle reports %d, " "expecting %d\n",
bus->sdpcm_ver, SDPCM_PROT_VERSION); else
brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n",
bus->sdpcm_ver);
/* * Retrieve console state address now that firmware should have * updated it.
*/
brcmf_sdio_get_console_addr(bus);
}
/* * Flow Control has been moved into the RX headers and this out of band * method isn't used any more. * remaining backward compatible with older dongles.
*/ if (hmb_data & HMB_DATA_FC) {
fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >>
HMB_DATA_FCDATA_SHIFT;
if (fcbits & ~bus->flowcontrol)
bus->sdcnt.fc_xoff++;
if (bus->flowcontrol & ~fcbits)
bus->sdcnt.fc_xon++;
/* Wait until the packet has been flushed (device/FIFO stable) */ for (lastrbc = retries = 0xffff; retries > 0; retries--) {
hi = brcmf_sdiod_readb(bus->sdiodev, SBSDIO_FUNC1_RFRAMEBCHI,
&err);
lo = brcmf_sdiod_readb(bus->sdiodev, SBSDIO_FUNC1_RFRAMEBCLO,
&err);
bus->sdcnt.f1regdata += 2;
if ((hi == 0) && (lo == 0)) break;
if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) {
brcmf_err("count growing: last 0x%04x now 0x%04x\n",
lastrbc, (hi << 8) + lo);
}
lastrbc = (hi << 8) + lo;
}
if (!retries)
brcmf_err("count never zeroed: last 0x%04x\n", lastrbc); else
brcmf_dbg(SDIO, "flush took %d iterations\n", 0xffff - retries);
if (rtx) {
bus->sdcnt.rxrtx++;
brcmf_sdiod_writel(sdiod, core->base + SD_REG(tosbmailbox),
SMB_NAK, &err);
bus->sdcnt.f1regdata++; if (err == 0)
bus->rxskip = true;
}
/* Clear partial in any case */
bus->cur_read.len = 0;
}
for (i = 0; i < 3; i++) {
hi = brcmf_sdiod_readb(sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL);
lo = brcmf_sdiod_readb(sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL);
bus->sdcnt.f1regdata += 2; if ((hi == 0) && (lo == 0)) break;
}
}
/* return total length of buffer chain */ static uint brcmf_sdio_glom_len(struct brcmf_sdio *bus)
{ struct sk_buff *p;
uint total;
total = 0;
skb_queue_walk(&bus->glom, p)
total += p->len; return total;
}
/* If there's a descriptor, generate the packet chain */ if (bus->glomd) {
pfirst = pnext = NULL;
dlen = (u16) (bus->glomd->len);
dptr = bus->glomd->data; if (!dlen || (dlen & 1)) {
brcmf_err("bad glomd len(%d), ignore descriptor\n",
dlen);
dlen = 0;
}
for (totlen = num = 0; dlen; num++) { /* Get (and move past) next length */
sublen = get_unaligned_le16(dptr);
dlen -= sizeof(u16);
dptr += sizeof(u16); if ((sublen < SDPCM_HDRLEN) ||
((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) {
brcmf_err("descriptor len %d bad: %d\n",
num, sublen);
pnext = NULL; break;
} if (sublen % bus->sgentry_align) {
brcmf_err("sublen %d not multiple of %d\n",
sublen, bus->sgentry_align);
}
totlen += sublen;
/* For last frame, adjust read len so total
is a block multiple */ if (!dlen) {
sublen +=
(roundup(totlen, bus->blocksize) - totlen);
totlen = roundup(totlen, bus->blocksize);
}
/* Allocate/chain packet for next subframe */
pnext = brcmu_pkt_buf_get_skb(sublen + bus->sgentry_align); if (pnext == NULL) {
brcmf_err("bcm_pkt_buf_get_skb failed, num %d len %d\n",
num, sublen); break;
}
skb_queue_tail(&bus->glom, pnext);
/* Ok -- either we just generated a packet chain,
or had one from before */ if (!skb_queue_empty(&bus->glom)) { if (BRCMF_GLOM_ON()) {
brcmf_dbg(GLOM, "try superframe read, packet chain:\n");
skb_queue_walk(&bus->glom, pnext) {
brcmf_dbg(GLOM, " %p: %p len 0x%04x (%d)\n",
pnext, (u8 *) (pnext->data),
pnext->len, pnext->len);
}
}
/* Do an SDIO read for the superframe. Configurable iovar to * read directly into the chained packet, or allocate a large * packet and copy into the chain.
*/
sdio_claim_host(bus->sdiodev->func1);
errcode = brcmf_sdiod_recv_chain(bus->sdiodev,
&bus->glom, dlen);
sdio_release_host(bus->sdiodev->func1);
bus->sdcnt.f2rxdata++;
/* On failure, kill the superframe */ if (errcode < 0) {
brcmf_err("glom read of %d bytes failed: %d\n",
dlen, errcode);
brcmf_dbg(SDIO, "Enter\n"); if (bus->rxblen)
buf = vzalloc(bus->rxblen); if (!buf) goto done;
rbuf = bus->rxbuf;
pad = ((unsignedlong)rbuf % bus->head_align); if (pad)
rbuf += (bus->head_align - pad);
/* Copy the already-read portion over */
memcpy(buf, hdr, BRCMF_FIRSTREAD); if (len <= BRCMF_FIRSTREAD) goto gotpkt;
/* Raise rdlen to next SDIO block to avoid tail command */
rdlen = len - BRCMF_FIRSTREAD; if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) {
pad = bus->blocksize - (rdlen % bus->blocksize); if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
((len + pad) < bus->sdiodev->bus_if->maxctl))
rdlen += pad;
} elseif (rdlen % bus->head_align) {
rdlen += bus->head_align - (rdlen % bus->head_align);
}
/* Drop if the read is too big or it exceeds our maximum */ if ((rdlen + BRCMF_FIRSTREAD) > bus->sdiodev->bus_if->maxctl) {
brcmf_err("%d-byte control read exceeds %d-byte buffer\n",
rdlen, bus->sdiodev->bus_if->maxctl);
brcmf_sdio_rxfail(bus, false, false); goto done;
}
/* Point to valid data and indicate its length */
spin_lock_bh(&bus->rxctl_lock); if (bus->rxctl) {
brcmf_err("last control frame is being processed.\n");
spin_unlock_bh(&bus->rxctl_lock);
vfree(buf); goto done;
}
bus->rxctl = buf + doff;
bus->rxctl_orig = buf;
bus->rxlen = len - doff;
spin_unlock_bh(&bus->rxctl_lock);
done: /* Awake any waiters */
brcmf_sdio_dcmd_resp_wake(bus);
}
/* prepare the descriptor for the next read */
rd->len = rd->len_nxtfrm << 4;
rd->len_nxtfrm = 0; /* treat all packet as event if we don't know */
rd->channel = SDPCM_EVENT_CHANNEL;
}
rxcount = maxframes - rxleft; /* Message if we hit the limit */ if (!rxleft)
brcmf_dbg(DATA, "hit rx limit of %d frames\n", maxframes); else
brcmf_dbg(DATA, "processed %d frames\n", rxcount); /* Back off rxseq if awaiting rtx, update rx_seq */ if (bus->rxskip)
rd->seq_num--;
bus->rx_seq = rd->seq_num;
/* * struct brcmf_skbuff_cb reserves first two bytes in sk_buff::cb for * bus layer usage.
*/ /* flag marking a dummy skb added for DMA alignment requirement */ #define ALIGN_SKB_FLAG 0x8000 /* bit mask of data length chopped from the previous packet */ #define ALIGN_SKB_CHOP_LEN_MASK 0x7fff
/** * brcmf_sdio_txpkt_prep - packet preparation for transmit * @bus: brcmf_sdio structure pointer * @pktq: packet list pointer * @chan: virtual channel to transmit the packet * * Processes to be applied to the packet * - Align data buffer pointer * - Align data buffer length * - Prepare header * Return: negative value if there is error
*/ staticint
brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
uint chan)
{
u16 head_pad, total_len; struct sk_buff *pkt_next;
u8 txseq; int ret; struct brcmf_sdio_hdrinfo hd_info = {0};
txseq = bus->tx_seq;
total_len = 0;
skb_queue_walk(pktq, pkt_next) { /* alignment packet inserted in previous * loop cycle can be skipped as it is * already properly aligned and does not * need an sdpcm header.
*/ if (*(u16 *)(pkt_next->cb) & ALIGN_SKB_FLAG) continue;
/* align packet data pointer */
ret = brcmf_sdio_txpkt_hdalign(bus, pkt_next); if (ret < 0) return ret;
head_pad = (u16)ret; if (head_pad)
memset(pkt_next->data + bus->tx_hdrlen, 0, head_pad);
/* Now fill the header */
brcmf_sdio_hdpack(bus, pkt_next->data, &hd_info);
if (BRCMF_BYTES_ON() &&
((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) ||
(BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)))
brcmf_dbg_hex_dump(true, pkt_next->data, hd_info.len, "Tx Frame:\n"); elseif (BRCMF_HDRS_ON())
brcmf_dbg_hex_dump(true, pkt_next->data,
head_pad + bus->tx_hdrlen, "Tx Header:\n");
} /* Hardware length tag of the first packet should be total * length of the chain (including padding)
*/ if (bus->txglom)
brcmf_sdio_update_hwhdr(__skb_peek(pktq)->data, total_len); return 0;
}
/** * brcmf_sdio_txpkt_postp - packet post processing for transmit * @bus: brcmf_sdio structure pointer * @pktq: packet list pointer * * Processes to be applied to the packet * - Remove head padding * - Remove tail padding
*/ staticvoid
brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq)
{
u8 *hdr;
u32 dat_offset;
u16 tail_pad;
u16 dummy_flags, chop_len; struct sk_buff *pkt_next, *tmp, *pkt_prev;
/* Send frames until the limit or some other event */ for (cnt = 0; (cnt < maxframes) && data_ok(bus);) {
pkt_num = 1; if (bus->txglom)
pkt_num = min_t(u8, bus->tx_max - bus->tx_seq,
bus->sdiodev->txglomsz);
pkt_num = min_t(u32, pkt_num,
brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol));
__skb_queue_head_init(&pktq);
spin_lock_bh(&bus->txq_lock); for (i = 0; i < pkt_num; i++) {
pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map,
&prec_out); if (pkt == NULL) break;
__skb_queue_tail(&pktq, pkt);
}
spin_unlock_bh(&bus->txq_lock); if (i == 0) break;
ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL);
cnt += i;
/* In poll mode, need to check for other events */ if (!bus->intr) { /* Check device status, signal pending interrupt */
sdio_claim_host(bus->sdiodev->func1);
intstatus = brcmf_sdiod_readl(bus->sdiodev,
intstat_addr, &ret);
sdio_release_host(bus->sdiodev->func1);
bus->sdcnt.f2txdata++; if (ret != 0) break; if (intstatus & bus->hostintmask)
atomic_set(&bus->ipend, 1);
}
}
/* Force backplane clocks to assure F2 interrupt propagates */
saveclk = brcmf_sdiod_readb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
&err); if (!err) {
bpreq = saveclk;
bpreq |= brcmf_chip_is_ulp(bus->ci) ?
SBSDIO_HT_AVAIL_REQ : SBSDIO_FORCE_HT;
brcmf_sdiod_writeb(sdiodev,
SBSDIO_FUNC1_CHIPCLKCSR,
bpreq, &err);
} if (err)
brcmf_err("Failed to force clock for F2: err %d\n",
err);
/* Turn off the bus (F2), free any pending packets */
brcmf_dbg(INTR, "disable SDIO interrupts\n");
sdio_disable_func(sdiodev->func2);
/* Clear any pending interrupts now that F2 is disabled */
brcmf_sdiod_writel(sdiodev, core->base + SD_REG(intstatus),
local_hostintmask, NULL);
sdio_release_host(sdiodev->func1);
} /* Clear the data packet queues */
brcmu_pktq_flush(&bus->txq, true, NULL, NULL);
/* Clear any held glomming stuff */
brcmu_pkt_buf_free_skb(bus->glomd);
brcmf_sdio_free_glom(bus);
/* Clear rx control and wake any waiters */
spin_lock_bh(&bus->rxctl_lock);
bus->rxlen = 0;
spin_unlock_bh(&bus->rxctl_lock);
brcmf_sdio_dcmd_resp_wake(bus);
/* Reset some F2 state stuff */
bus->rxskip = false;
bus->tx_seq = bus->rx_seq = 0;
}
/* Make sure backplane clock is on */
brcmf_sdio_bus_sleep(bus, false, true);
/* Pending interrupt indicates new device status */ if (atomic_read(&bus->ipend) > 0) {
atomic_set(&bus->ipend, 0);
err = brcmf_sdio_intr_rstatus(bus);
}
/* Start with leftover status bits */
intstatus = atomic_xchg(&bus->intstatus, 0);
/* Handle flow-control change: read new state in case our ack * crossed another change interrupt. If change still set, assume * FC ON for safety, let next loop through do the debounce.
*/ if (intstatus & I_HMB_FC_CHANGE) {
intstatus &= ~I_HMB_FC_CHANGE;
brcmf_sdiod_writel(sdiod, intstat_addr, I_HMB_FC_CHANGE, &err);
/* Generally don't ask for these, can get CRC errors... */ if (intstatus & I_WR_OOSYNC) {
brcmf_err("Dongle reports WR_OOSYNC\n");
intstatus &= ~I_WR_OOSYNC;
}
/* Would be active due to wake-wlan in gSPI */ if (intstatus & I_CHIPACTIVE) {
brcmf_dbg(SDIO, "Dongle reports CHIPACTIVE\n");
intstatus &= ~I_CHIPACTIVE;
}
/* Ignore frame indications if rxskip is set */ if (bus->rxskip)
intstatus &= ~I_HMB_FRAME_IND;
/* On frame indication, read available frames */ if ((intstatus & I_HMB_FRAME_IND) && (bus->clkstate == CLK_AVAIL)) {
brcmf_sdio_readframes(bus, bus->rxbound); if (!bus->rxpending)
intstatus &= ~I_HMB_FRAME_IND;
}
/* Keep still-pending events for next scheduling */ if (intstatus)
atomic_or(intstatus, &bus->intstatus);
brcmf_sdio_clrintr(bus);
if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) &&
txctl_ok(bus)) {
sdio_claim_host(bus->sdiodev->func1); if (bus->ctrl_frame_stat) {
err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf,
bus->ctrl_frame_len);
bus->ctrl_frame_err = err;
wmb();
bus->ctrl_frame_stat = false; if (err)
brcmf_err("sdio ctrlframe tx failed err=%d\n",
err);
}
sdio_release_host(bus->sdiodev->func1);
brcmf_sdio_wait_event_wakeup(bus);
} /* Send queued frames (limit 1 if rx may still be pending) */ if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) &&
brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit &&
data_ok(bus)) {
framecnt = bus->rxpending ? min(txlimit, bus->txminmax) :
txlimit;
brcmf_sdio_sendfromq(bus, framecnt);
}
staticbool brcmf_sdio_prec_enq(struct pktq *q, struct sk_buff *pkt, int prec)
{ struct sk_buff *p; int eprec = -1; /* precedence to evict from */
/* Fast case, precedence queue is not full and we are also not * exceeding total queue length
*/ if (!pktq_pfull(q, prec) && !pktq_full(q)) {
brcmu_pktq_penq(q, prec, pkt); returntrue;
}
/* Determine precedence from which to evict packet, if any */ if (pktq_pfull(q, prec)) {
eprec = prec;
} elseif (pktq_full(q)) {
p = brcmu_pktq_peek_tail(q, &eprec); if (eprec > prec) returnfalse;
}
/* Evict if needed */ if (eprec >= 0) { /* Detect queueing to unconfigured precedence */ if (eprec == prec) returnfalse; /* refuse newer (incoming) packet */ /* Evict packet according to discard policy */
p = brcmu_pktq_pdeq_tail(q, eprec); if (p == NULL)
brcmf_err("brcmu_pktq_pdeq_tail() failed\n");
brcmu_pkt_buf_free_skb(p);
}
/* Enqueue */
p = brcmu_pktq_penq(q, prec, pkt); if (p == NULL)
brcmf_err("brcmu_pktq_penq() failed\n");
brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len); if (sdiodev->state != BRCMF_SDIOD_DATA) return -EIO;
/* Add space for the header */
skb_push(pkt, bus->tx_hdrlen); /* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */
/* In WLAN, priority is always set by the AP using WMM parameters * and this need not always follow the standard 802.1d priority. * Based on AP WMM config, map from 802.1d priority to corresponding * precedence level.
*/
prec = brcmf_map_prio_to_prec(bus_if->drvr->config,
(pkt->priority & PRIOMASK));
/* Check for existing queue, current flow-control,
pending event, or pending clock */
brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq));
bus->sdcnt.fcqueued++;
/* Priority based enq */
spin_lock_bh(&bus->txq_lock); /* reset bus_flags in packet cb */
*(u16 *)(pkt->cb) = 0; if (!brcmf_sdio_prec_enq(&bus->txq, pkt, prec)) {
skb_pull(pkt, bus->tx_hdrlen);
brcmf_err("out of bus->txq !!!\n");
ret = -ENOSR;
} else {
ret = 0;
}
while (c->last != idx) { for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) { if (c->last == idx) { /* This would output a partial line. * Instead, back up * the buffer pointer and output this * line next time around.
*/ if (c->last >= n)
c->last -= n; else
c->last = c->bufsize - n; goto break2;
}
ch = c->buf[c->last];
c->last = (c->last + 1) % c->bufsize; if (ch == '\n') break;
line[n] = ch;
}
staticint brcmf_sdio_checkdied(struct brcmf_sdio *bus)
{ int error; struct sdpcm_shared sh;
error = brcmf_sdio_readshared(bus, &sh);
if (error < 0) return error;
if ((sh.flags & SDPCM_SHARED_ASSERT_BUILT) == 0)
brcmf_dbg(INFO, "firmware not built with -assert\n"); elseif (sh.flags & SDPCM_SHARED_ASSERT)
brcmf_err("assertion in dongle\n");
if (sh.flags & SDPCM_SHARED_TRAP) {
brcmf_err("firmware trap in dongle\n");
brcmf_sdio_trap_info(NULL, bus, &sh);
}
if (rxlen)
bus->sdcnt.rx_ctlpkts++; else
bus->sdcnt.rx_ctlerrs++;
return rxlen ? (int)rxlen : -ETIMEDOUT;
}
#ifdef DEBUG staticbool
brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr,
u8 *ram_data, uint ram_sz)
{ char *ram_cmp; int err; bool ret = true; int address; int offset; int len;
/* read back and verify */
brcmf_dbg(INFO, "Compare RAM dl & ul at 0x%08x; size=%d\n", ram_addr,
ram_sz);
ram_cmp = kmalloc(MEMBLOCK, GFP_KERNEL); /* do not proceed while no memory but */ if (!ram_cmp) returntrue;
address = ram_addr;
offset = 0; while (offset < ram_sz) {
len = ((offset + MEMBLOCK) < ram_sz) ? MEMBLOCK :
ram_sz - offset;
err = brcmf_sdiod_ramrw(sdiodev, false, address, ram_cmp, len); if (err) {
brcmf_err("error %d on reading %d membytes at 0x%08x\n",
err, len, address);
ret = false; break;
} elseif (memcmp(ram_cmp, &ram_data[offset], len)) {
brcmf_err("Downloaded RAM image is corrupted, block offset is %d, len is %d\n",
offset, len);
ret = false; break;
}
offset += len;
address += len;
}
/* Take arm out of reset */ if (!brcmf_chip_set_active(bus->ci, rstvec)) {
brcmf_err("error getting out of ARM core reset\n");
bcmerror = -EIO; goto err;
}
/* maxctl provided by common layer */ if (WARN_ON(!bus_if->maxctl)) return -EINVAL;
/* Allocate control receive buffer */
bus_if->maxctl += bus->roundup;
value = roundup((bus_if->maxctl + SDPCM_HDRLEN), ALIGNMENT);
value += bus->head_align;
bus->rxbuf = kmalloc(value, GFP_ATOMIC); if (bus->rxbuf)
bus->rxblen = value;
/* the commands below use the terms tx and rx from * a device perspective, ie. bus:txglom affects the * bus transfers from device to host.
*/ if (core->rev < 12) { /* for sdio core rev < 12, disable txgloming */
iovar = 0;
err = brcmf_iovar_data_set(dev, "bus:txglom", &iovar, sizeof(iovar));
} else { /* otherwise, set txglomalign */
value = sdiodev->settings->bus.sdio.sd_sgentry_align; /* SDIO ADMA requires at least 32 bit alignment */
iovar = cpu_to_le32(max_t(u32, value, ALIGNMENT));
err = brcmf_iovar_data_set(dev, "bus:txglomalign", &iovar, sizeof(iovar));
}
/* Try forcing SDIO core to do ALPAvail request only */
clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); if (err) {
brcmf_err("error writing for HT off\n"); return err;
}
/* If register supported, wait for ALPAvail and then force ALP */ /* This may take up to 15 milliseconds */
clkval = brcmf_sdiod_readb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, NULL);
if ((clkval & ~SBSDIO_AVBITS) != clkset) {
brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
clkset, clkval); return -EACCES;
}
/* * this is a bit of special handling if reading the chipcommon chipid * register. The 4339 is a next-gen of the 4335. It uses the same * SDIO device id as 4335 and the chipid register returns 4335 as well. * It can be identified as 4339 by looking at the chip revision. It * is corrected here so the chip.c module has the right info.
*/ if (addr == CORE_CC_REG(SI_ENUM_BASE_DEFAULT, chipid) &&
(sdiodev->func1->device == SDIO_DEVICE_ID_BROADCOM_4339 ||
sdiodev->func1->device == SDIO_DEVICE_ID_BROADCOM_4335_4339)) {
rev = (val & CID_REV_MASK) >> CID_REV_SHIFT; if (rev >= 2) {
val &= ~CID_ID_MASK;
val |= BRCM_CC_4339_CHIP_ID;
}
}
/* Pick up the SDIO core info struct from chip.c */
bus->sdio_core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); if (!bus->sdio_core) goto fail;
/* Pick up the CHIPCOMMON core info struct, for bulk IO in bcmsdh.c */
sdiodev->cc_core = brcmf_chip_get_core(bus->ci, BCMA_CORE_CHIPCOMMON); if (!sdiodev->cc_core) goto fail;
sdiodev->settings = brcmf_get_module_param(sdiodev->dev,
BRCMF_BUSTYPE_SDIO,
bus->ci->chip,
bus->ci->chiprev); if (IS_ERR_OR_NULL(sdiodev->settings)) {
brcmf_err("Failed to get device parameters\n");
ret = PTR_ERR_OR_ZERO(sdiodev->settings); goto fail;
} /* platform specific configuration: * alignments must be at least 4 bytes for ADMA
*/
bus->head_align = ALIGNMENT;
bus->sgentry_align = ALIGNMENT; if (sdiodev->settings->bus.sdio.sd_head_align > ALIGNMENT)
bus->head_align = sdiodev->settings->bus.sdio.sd_head_align; if (sdiodev->settings->bus.sdio.sd_sgentry_align > ALIGNMENT)
bus->sgentry_align =
sdiodev->settings->bus.sdio.sd_sgentry_align;
/* allocate scatter-gather table. sg support * will be disabled upon allocation failure.
*/
brcmf_sdiod_sgtable_alloc(sdiodev);
/* wowl can be supported when KEEP_POWER is true and (WAKE_SDIO_IRQ * is true or when platform data OOB irq is true).
*/ if (IS_ENABLED(CONFIG_PM_SLEEP) &&
(sdio_get_host_pm_caps(sdiodev->func1) & MMC_PM_KEEP_POWER) &&
((sdio_get_host_pm_caps(sdiodev->func1) & MMC_PM_WAKE_SDIO_IRQ) ||
(sdiodev->settings->bus.sdio.oob_irq_supported)))
sdiodev->bus_if->wowl_supported = true;
if (brcmf_sdio_kso_init(bus)) {
brcmf_err("error enabling KSO\n"); goto fail;
}
/* Set card control so an SDIO card reset does a WLAN backplane reset */
reg_val = brcmf_sdiod_func0_rb(sdiodev, SDIO_CCCR_BRCM_CARDCTRL, &err); if (err) goto fail;
reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET;
brcmf_sdiod_func0_wb(sdiodev, SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err); if (err) goto fail;
/* set PMUControl so a backplane reset does PMU state reload */
reg_addr = CORE_CC_REG(brcmf_chip_get_pmu(bus->ci)->base, pmucontrol);
reg_val = brcmf_sdiod_readl(sdiodev, reg_addr, &err); if (err) goto fail;
allow_signal(SIGTERM); /* Run until signal received */
brcmf_sdiod_freezer_count(bus->sdiodev); while (1) { if (kthread_should_stop()) break;
brcmf_sdiod_freezer_uncount(bus->sdiodev);
wait = wait_for_completion_interruptible(&bus->watchdog_wait);
brcmf_sdiod_freezer_count(bus->sdiodev);
brcmf_sdiod_try_freeze(bus->sdiodev); if (!wait) {
brcmf_sdio_bus_watchdog(bus); /* Count the tick for reference */
bus->sdcnt.tickcnt++;
reinit_completion(&bus->watchdog_wait);
} else break;
} return 0;
}
/* Make sure backplane clock is on, needed to generate F2 interrupt */
brcmf_sdio_clkctl(bus, CLK_AVAIL, false); if (bus->clkstate != CLK_AVAIL) goto release;
/* Force clocks on backplane to be sure F2 interrupt propagates */
saveclk = brcmf_sdiod_readb(sdiod, SBSDIO_FUNC1_CHIPCLKCSR, &err); if (!err) {
bpreq = saveclk;
bpreq |= brcmf_chip_is_ulp(bus->ci) ?
SBSDIO_HT_AVAIL_REQ : SBSDIO_FORCE_HT;
brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_CHIPCLKCSR,
bpreq, &err);
} if (err) {
brcmf_err("Failed to force clock for F2: err %d\n", err); goto release;
}
/* If F2 successfully enabled, set core and enable interrupts */ if (!err) { /* Set up the interrupt mask and enable interrupts */
bus->hostintmask = HOSTINTMASK;
brcmf_sdiod_writel(sdiod, core->base + SD_REG(hostintmask),
bus->hostintmask, NULL);
cancel_work_sync(&bus->datawork); if (bus->brcmf_wq)
destroy_workqueue(bus->brcmf_wq);
if (bus->ci) { if (bus->sdiodev->state != BRCMF_SDIOD_NOMEDIUM) {
sdio_claim_host(bus->sdiodev->func1);
brcmf_sdio_wd_timer(bus, false);
brcmf_sdio_clkctl(bus, CLK_AVAIL, false); /* Leave the device in state where it is * 'passive'. This is done by resetting all * necessary cores.
*/
msleep(20);
brcmf_chip_set_passive(bus->ci);
brcmf_sdio_clkctl(bus, CLK_NONE, false);
sdio_release_host(bus->sdiodev->func1);
}
brcmf_chip_detach(bus->ci);
} if (bus->sdiodev->settings)
brcmf_release_module_param(bus->sdiodev->settings);
/* don't start the wd until fw is loaded */ if (bus->sdiodev->state != BRCMF_SDIOD_DATA) return;
if (active) { if (!bus->wd_active) { /* Create timer again when watchdog period is dynamically changed or in the first instance
*/
bus->timer.expires = jiffies + BRCMF_WD_POLL;
add_timer(&bus->timer);
bus->wd_active = true;
} else { /* Re arm the timer, at last watchdog period */
mod_timer(&bus->timer, jiffies + BRCMF_WD_POLL);
}
}
}
int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep)
{ int ret;
sdio_claim_host(bus->sdiodev->func1);
ret = brcmf_sdio_bus_sleep(bus, sleep, false);
sdio_release_host(bus->sdiodev->func1);
return ret;
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.83 Sekunden
(vorverarbeitet am 2026-04-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.