Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/wireless/broadcom/brcm80211/brcmfmac/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 126 kB image not shown  

Quelle  sdio.c   Sprache: C

 
// SPDX-License-Identifier: ISC
/*
 * Copyright (c) 2010 Broadcom Corporation
 */


#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/printk.h>
#include <linux/pci_ids.h>
#include <linux/netdevice.h>
#include <linux/interrupt.h>
#include <linux/sched/signal.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/card.h>
#include <linux/mmc/core.h>
#include <linux/semaphore.h>
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/bcma/bcma.h>
#include <linux/debugfs.h>
#include <linux/vmalloc.h>
#include <linux/unaligned.h>
#include <defs.h>
#include <brcmu_wifi.h>
#include <brcmu_utils.h>
#include <brcm_hw_ids.h>
#include <soc.h>
#include "sdio.h"
#include "chip.h"
#include "firmware.h"
#include "core.h"
#include "common.h"
#include "bcdc.h"

#define DCMD_RESP_TIMEOUT msecs_to_jiffies(2500)
#define CTL_DONE_TIMEOUT msecs_to_jiffies(2500)

/* watermark expressed in number of words */
#define DEFAULT_F2_WATERMARK    0x8
#define CY_4373_F2_WATERMARK    0x40
#define CY_4373_F1_MESBUSYCTRL  (CY_4373_F2_WATERMARK | SBSDIO_MESBUSYCTRL_ENAB)
#define CY_43012_F2_WATERMARK    0x60
#define CY_43012_MES_WATERMARK  0x50
#define CY_43012_MESBUSYCTRL    (CY_43012_MES_WATERMARK | \
     SBSDIO_MESBUSYCTRL_ENAB)
#define CY_4339_F2_WATERMARK    48
#define CY_4339_MES_WATERMARK 80
#define CY_4339_MESBUSYCTRL (CY_4339_MES_WATERMARK | \
     SBSDIO_MESBUSYCTRL_ENAB)
#define CY_43455_F2_WATERMARK 0x60
#define CY_43455_MES_WATERMARK 0x50
#define CY_43455_MESBUSYCTRL (CY_43455_MES_WATERMARK | \
     SBSDIO_MESBUSYCTRL_ENAB)
#define CY_435X_F2_WATERMARK 0x40
#define CY_435X_F1_MESBUSYCTRL (CY_435X_F2_WATERMARK | \
     SBSDIO_MESBUSYCTRL_ENAB)

#ifdef DEBUG

#define BRCMF_TRAP_INFO_SIZE 80

#define CBUF_LEN (128)

/* 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];
};

#endif    /* DEBUG */
#include <chipcommon.h>

#include "bus.h"
#include "debug.h"
#include "tracepoint.h"

#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

#define SD_REG(field) \
  (offsetof(struct sdpcmd_regs, field))

/* SDIO function 1 register CHIPCLKCSR */
/* Force ALP request to backplane */
#define SBSDIO_FORCE_ALP  0x01
/* Force HT request to backplane */
#define SBSDIO_FORCE_HT   0x02
/* Force ILP request to backplane */
#define SBSDIO_FORCE_ILP  0x04
/* Make ALP ready (power up xtal) */
#define SBSDIO_ALP_AVAIL_REQ  0x08
/* Make HT ready (power up PLL) */
#define SBSDIO_HT_AVAIL_REQ  0x10
/* Squelch clock requests from HW */
#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20
/* Status: ALP is ready */
#define SBSDIO_ALP_AVAIL  0x40
/* Status: HT is ready */
#define SBSDIO_HT_AVAIL   0x80
#define SBSDIO_CSR_MASK   0x1F
#define SBSDIO_AVBITS  (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL)
#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS)
#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS)
#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval))
#define SBSDIO_CLKAV(regval, alponly) \
 (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval)))

/* intstatus */
#define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */
#define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */
#define I_SMB_SW2 (1 << 2) /* To SB Mail S/W interrupt 2 */
#define I_SMB_SW3 (1 << 3) /* To SB Mail S/W interrupt 3 */
#define I_SMB_SW_MASK 0x0000000f /* To SB Mail S/W interrupts mask */
#define I_SMB_SW_SHIFT 0 /* To SB Mail S/W interrupts shift */
#define I_HMB_SW0 (1 << 4) /* To Host Mail S/W interrupt 0 */
#define I_HMB_SW1 (1 << 5) /* To Host Mail S/W interrupt 1 */
#define I_HMB_SW2 (1 << 6) /* To Host Mail S/W interrupt 2 */
#define I_HMB_SW3 (1 << 7) /* To Host Mail S/W interrupt 3 */
#define I_HMB_SW_MASK 0x000000f0 /* To Host Mail S/W interrupts mask */
#define I_HMB_SW_SHIFT 4 /* To Host Mail S/W interrupts shift */
#define I_WR_OOSYNC (1 << 8) /* Write Frame Out Of Sync */
#define I_RD_OOSYNC (1 << 9) /* Read Frame Out Of Sync */
#define I_PC  (1 << 10) /* descriptor error */
#define I_PD  (1 << 11) /* data error */
#define I_DE  (1 << 12) /* Descriptor protocol Error */
#define I_RU  (1 << 13) /* Receive descriptor Underflow */
#define I_RO  (1 << 14) /* Receive fifo Overflow */
#define I_XU  (1 << 15) /* Transmit fifo Underflow */
#define I_RI  (1 << 16) /* Receive Interrupt */
#define I_BUSPWR (1 << 17) /* SDIO Bus Power Change (rev 9) */
#define I_XMTDATA_AVAIL (1 << 23) /* bits in fifo */
#define I_XI  (1 << 24) /* Transmit Interrupt */
#define I_RF_TERM (1 << 25) /* Read Frame Terminate */
#define I_WF_TERM (1 << 26) /* Write Frame Terminate */
#define I_PCMCIA_XU (1 << 27) /* PCMCIA Transmit FIFO Underflow */
#define I_SBINT  (1 << 28) /* sbintstatus Interrupt */
#define I_CHIPACTIVE (1 << 29) /* chip from doze to active state */
#define I_SRESET (1 << 30) /* CCCR RES interrupt */
#define I_IOE2  (1U << 31) /* CCCR IOE2 Bit Changed */
#define I_ERRORS (I_PC | I_PD | I_DE | I_RU | I_RO | I_XU)
#define I_DMA  (I_RI | I_XI | I_ERRORS)

/* corecontrol */
#define CC_CISRDY  (1 << 0) /* CIS Ready */
#define CC_BPRESEN  (1 << 1) /* CCCR RES signal */
#define CC_F2RDY  (1 << 2) /* set CCCR IOR2 bit */
#define CC_CLRPADSISO  (1 << 3) /* clear SDIO pads isolation */
#define CC_XMTDATAAVAIL_MODE (1 << 4)
#define CC_XMTDATAAVAIL_CTRL (1 << 5)

/* SDA_FRAMECTRL */
#define SFC_RF_TERM (1 << 0) /* Read Frame Terminate */
#define SFC_WF_TERM (1 << 1) /* Write Frame Terminate */
#define SFC_CRC4WOOS (1 << 2) /* CRC error for write out of sync */
#define SFC_ABORTALL (1 << 3) /* Abort all in-progress frames */

/*
 * Software allocation of To SB Mailbox resources
 */


/* tosbmailbox bits corresponding to intstatus bits */
#define SMB_NAK  (1 << 0) /* Frame NAK */
#define SMB_INT_ACK (1 << 1) /* Host Interrupt ACK */
#define SMB_USE_OOB (1 << 2) /* Use OOB Wakeup */
#define SMB_DEV_INT (1 << 3) /* Miscellaneous Interrupt */

/* tosbmailboxdata */
#define SMB_DATA_VERSION_SHIFT 16 /* host protocol version */

/*
 * Software allocation of To Host Mailbox resources
 */


/* intstatus bits */
#define I_HMB_FC_STATE I_HMB_SW0 /* Flow Control State */
#define I_HMB_FC_CHANGE I_HMB_SW1 /* Flow Control State Changed */
#define I_HMB_FRAME_IND I_HMB_SW2 /* Frame Indication */
#define I_HMB_HOST_INT I_HMB_SW3 /* Miscellaneous Interrupt */

/* tohostmailboxdata */
#define HMB_DATA_NAKHANDLED 0x0001 /* retransmit NAK'd frame */
#define HMB_DATA_DEVREADY 0x0002 /* talk to host after enable */
#define HMB_DATA_FC  0x0004 /* per prio flowcontrol update flag */
#define HMB_DATA_FWREADY 0x0008 /* fw ready for protocol activity */
#define HMB_DATA_FWHALT  0x0010 /* firmware halted */

#define HMB_DATA_FCDATA_MASK 0xff000000
#define HMB_DATA_FCDATA_SHIFT 24

#define HMB_DATA_VERSION_MASK 0x00ff0000
#define HMB_DATA_VERSION_SHIFT 16

/*
 * Software-defined protocol header
 */


/* 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)

/* Flags for SDH calls */
#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED)

#define BRCMF_IDLE_ACTIVE 0 /* Do not request any SD clock change
 * when idle
 */

#define BRCMF_IDLE_INTERVAL 1

#define KSO_WAIT_US 50
#define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US)
#define BRCMF_SDIO_MAX_ACCESS_ERRORS 5

#ifdef DEBUG
/* Device console log buffer state */
struct brcmf_console {
 uint count;  /* Poll interval msec counter */
 uint log_addr;  /* Log struct address (fixed) */
 struct rte_log_le log_le; /* Log struct (host copy) */
 uint bufsize;  /* Size of log buffer */
 u8 *buf;  /* Log buffer (host copy) */
 uint last;  /* Last buffer read index */
};

struct brcmf_trap_info {
 __le32  type;
 __le32  epc;
 __le32  cpsr;
 __le32  spsr;
 __le32  r0; /* a1 */
 __le32  r1; /* a2 */
 __le32  r2; /* a3 */
 __le32  r3; /* a4 */
 __le32  r4; /* v1 */
 __le32  r5; /* v2 */
 __le32  r6; /* v3 */
 __le32  r7; /* v4 */
 __le32  r8; /* v5 */
 __le32  r9; /* sb/v6 */
 __le32  r10; /* sl/v7 */
 __le32  r11; /* fp/v8 */
 __le32  r12; /* ip */
 __le32  r13; /* sp */
 __le32  r14; /* lr */
 __le32  pc; /* r15 */
};
#endif    /* DEBUG */

struct sdpcm_shared {
 u32 flags;
 u32 trap_addr;
 u32 assert_exp_addr;
 u32 assert_file_addr;
 u32 assert_line;
 u32 console_addr; /* Address of struct rte_console */
 u32 msgtrace_addr;
 u8 tag[32];
 u32 brpt_addr;
};

struct sdpcm_shared_le {
 __le32 flags;
 __le32 trap_addr;
 __le32 assert_exp_addr;
 __le32 assert_file_addr;
 __le32 assert_line;
 __le32 console_addr; /* Address of struct rte_console */
 __le32 msgtrace_addr;
 u8 tag[32];
 __le32 brpt_addr;
};

/* 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 */

#ifdef DEBUG
 uint console_interval;
 struct brcmf_console console; /* Console output polling support */
 uint console_addr; /* Console address from shared struct */
#endif    /* DEBUG */

 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;

 spinlock_t txq_lock;  /* protect bus->txq */
 wait_queue_head_t ctrl_wait;
 wait_queue_head_t dcmd_resp_wait;

 struct timer_list timer;
 struct completion watchdog_wait;
 struct task_struct *watchdog_tsk;
 bool wd_active;

 struct workqueue_struct *brcmf_wq;
 struct work_struct datawork;
 bool dpc_triggered;
 bool dpc_running;

 bool txoff;  /* Transmit flow-controlled */
 struct brcmf_sdio_count sdcnt;
 bool sr_enabled; /* SaveRestore enabled */
 bool sleeping;

 u8 tx_hdrlen;  /* sdio bus header length for tx packet */
 bool txglom;  /* host tx glomming enable flag */
 u16 head_align;  /* buffer pointer alignment */
 u16 sgentry_align; /* scatter-gather buffer alignment */
};

/* clkstate */
#define CLK_NONE 0
#define CLK_SDONLY 1
#define CLK_PENDING 2
#define CLK_AVAIL 3

#ifdef DEBUG
static int qcount[NUMPRIO];
#endif    /* DEBUG */

#define DEFAULT_SDIO_DRIVE_STRENGTH 6 /* in milliamps */

#define RETRYCHAN(chan) ((chan) == SDPCM_EVENT_CHANNEL)

/* Limit on rounding up frames */
static const uint max_roundup = 512;

#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
#define ALIGNMENT  8
#else
#define ALIGNMENT  4
#endif

enum brcmf_sdio_frmtype {
 BRCMF_SDIO_FT_NORMAL,
 BRCMF_SDIO_FT_SUPER,
 BRCMF_SDIO_FT_SUB,
};

#define SDIOD_DRVSTR_KEY(chip, pmu)     (((unsigned int)(chip) << 16) | (pmu))

/* 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) */
static const struct 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) */
static const struct 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) */
static const struct 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) */
static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = {
 {16, 0x7},
 {12, 0x5},
 {8,  0x3},
 {4,  0x1}
};

BRCMF_FW_DEF(43143, "brcmfmac43143-sdio");
BRCMF_FW_DEF(43241B0, "brcmfmac43241b0-sdio");
BRCMF_FW_DEF(43241B4, "brcmfmac43241b4-sdio");
BRCMF_FW_DEF(43241B5, "brcmfmac43241b5-sdio");
BRCMF_FW_DEF(4329, "brcmfmac4329-sdio");
BRCMF_FW_DEF(4330, "brcmfmac4330-sdio");
BRCMF_FW_DEF(4334, "brcmfmac4334-sdio");
BRCMF_FW_DEF(43340, "brcmfmac43340-sdio");
BRCMF_FW_DEF(4335, "brcmfmac4335-sdio");
BRCMF_FW_DEF(43362, "brcmfmac43362-sdio");
BRCMF_FW_DEF(4339, "brcmfmac4339-sdio");
BRCMF_FW_DEF(43430A0, "brcmfmac43430a0-sdio");
/* Note the names are not postfixed with a1 for backward compatibility */
BRCMF_FW_CLM_DEF(43430A1, "brcmfmac43430-sdio");
BRCMF_FW_DEF(43430B0, "brcmfmac43430b0-sdio");
BRCMF_FW_CLM_DEF(43439, "brcmfmac43439-sdio");
BRCMF_FW_CLM_DEF(43455, "brcmfmac43455-sdio");
BRCMF_FW_DEF(43456, "brcmfmac43456-sdio");
BRCMF_FW_CLM_DEF(4354, "brcmfmac4354-sdio");
BRCMF_FW_CLM_DEF(4356, "brcmfmac4356-sdio");
BRCMF_FW_DEF(4359, "brcmfmac4359-sdio");
BRCMF_FW_CLM_DEF(4373, "brcmfmac4373-sdio");
BRCMF_FW_CLM_DEF(43012, "brcmfmac43012-sdio");
BRCMF_FW_CLM_DEF(43752, "brcmfmac43752-sdio");

/* firmware config files */
MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-sdio.*.txt");

/* per-board firmware binaries */
MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-sdio.*.bin");

static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = {
 BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143),
 BRCMF_FW_ENTRY(BRCM_CC_43241_CHIP_ID, 0x0000001F, 43241B0),
 BRCMF_FW_ENTRY(BRCM_CC_43241_CHIP_ID, 0x00000020, 43241B4),
 BRCMF_FW_ENTRY(BRCM_CC_43241_CHIP_ID, 0xFFFFFFC0, 43241B5),
 BRCMF_FW_ENTRY(BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, 4329),
 BRCMF_FW_ENTRY(BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, 4330),
 BRCMF_FW_ENTRY(BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, 4334),
 BRCMF_FW_ENTRY(BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, 43340),
 BRCMF_FW_ENTRY(BRCM_CC_43341_CHIP_ID, 0xFFFFFFFF, 43340),
 BRCMF_FW_ENTRY(BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, 4335),
 BRCMF_FW_ENTRY(BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, 43362),
 BRCMF_FW_ENTRY(BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, 4339),
 BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0x00000001, 43430A0),
 BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0x00000002, 43430A1),
 BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFFC, 43430B0),
 BRCMF_FW_ENTRY(BRCM_CC_4345_CHIP_ID, 0x00000200, 43456),
 BRCMF_FW_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFDC0, 43455),
 BRCMF_FW_ENTRY(BRCM_CC_43454_CHIP_ID, 0x00000040, 43455),
 BRCMF_FW_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354),
 BRCMF_FW_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356),
 BRCMF_FW_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359),
 BRCMF_FW_ENTRY(BRCM_CC_43751_CHIP_ID, 0xFFFFFFFF, 43752),
 BRCMF_FW_ENTRY(BRCM_CC_43752_CHIP_ID, 0xFFFFFFFF, 43752),
 BRCMF_FW_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373),
 BRCMF_FW_ENTRY(CY_CC_43012_CHIP_ID, 0xFFFFFFFF, 43012),
 BRCMF_FW_ENTRY(CY_CC_43439_CHIP_ID, 0xFFFFFFFF, 43439),
};

#define TXCTL_CREDITS 2

static void pkt_align(struct sk_buff *p, int len, int align)
{
 uint datalign;
 datalign = (unsigned long)(p->data);
 datalign = roundup(datalign, (align)) - datalign;
 if (datalign)
  skb_pull(p, datalign);
 __skb_trim(p, len);
}

/* To check if there's window offered */
static bool data_ok(struct brcmf_sdio *bus)
{
 u8 tx_rsv = 0;

 /* Reserve TXCTL_CREDITS credits for txctl when it is ready to send */
 if (bus->ctrl_frame_stat)
  tx_rsv = TXCTL_CREDITS;

 return (bus->tx_max - bus->tx_seq - tx_rsv) != 0 &&
        ((bus->tx_max - bus->tx_seq - tx_rsv) & 0x80) == 0;

}

/* To check if there's window offered */
static bool txctl_ok(struct brcmf_sdio *bus)
{
 return (bus->tx_max - bus->tx_seq) != 0 &&
        ((bus->tx_max - bus->tx_seq) & 0x80) == 0;
}

static int
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;

  udelay(KSO_WAIT_US);
  brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, wr_val,
       &err);

 } while (try_cnt++ < MAX_KSO_ATTEMPTS);

 if (try_cnt > 2)
  brcmf_dbg(SDIO, "try_cnt=%d rd_val=0x%x err=%d\n", try_cnt,
     rd_val, err);

 if (try_cnt > MAX_KSO_ATTEMPTS)
  brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err);

 if (on)
  sdio_retune_release(bus->sdiodev->func1);

 sdio_retune_crc_enable(bus->sdiodev->func1);

 return err;
}

#define HOSTINTMASK  (I_HMB_SW_MASK | I_CHIPACTIVE)

/* Turn backplane clock on or off */
static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
{
 int err;
 u8 clkctl, clkreq, devctl;
 unsigned long timeout;

 brcmf_dbg(SDIO, "Enter\n");

 clkctl = 0;

 if (bus->sr_enabled) {
  bus->clkstate = (on ? CLK_AVAIL : CLK_SDONLY);
  return 0;
 }

 if (on) {
  /* Request HT Avail */
  clkreq =
      bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;

  brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
       clkreq, &err);
  if (err) {
   brcmf_err("HT Avail request error: %d\n", err);
   return -EBADE;
  }

  /* Check current status */
  clkctl = brcmf_sdiod_readb(bus->sdiodev,
        SBSDIO_FUNC1_CHIPCLKCSR, &err);
  if (err) {
   brcmf_err("HT Avail read error: %d\n", err);
   return -EBADE;
  }

  /* Go to pending and await interrupt if appropriate */
  if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) {
   /* Allow only clock-available interrupt */
   devctl = brcmf_sdiod_readb(bus->sdiodev,
         SBSDIO_DEVICE_CTL, &err);
   if (err) {
    brcmf_err("Devctl error setting CA: %d\n", err);
    return -EBADE;
   }

   devctl |= SBSDIO_DEVCTL_CA_INT_ONLY;
   brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_DEVICE_CTL,
        devctl, &err);
   brcmf_dbg(SDIO, "CLKCTL: set PENDING\n");
   bus->clkstate = CLK_PENDING;

   return 0;
  } else if (bus->clkstate == CLK_PENDING) {
   /* Cancel CA-only interrupt filter */
   devctl = brcmf_sdiod_readb(bus->sdiodev,
         SBSDIO_DEVICE_CTL, &err);
   devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
   brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_DEVICE_CTL,
        devctl, &err);
  }

  /* Otherwise, wait here (polling) for HT Avail */
  timeout = jiffies +
     msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000);
  while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
   clkctl = brcmf_sdiod_readb(bus->sdiodev,
         SBSDIO_FUNC1_CHIPCLKCSR,
         &err);
   if (time_after(jiffies, timeout))
    break;
   else
    usleep_range(5000, 10000);
  }
  if (err) {
   brcmf_err("HT Avail request error: %d\n", err);
   return -EBADE;
  }
  if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
   brcmf_err("HT Avail timeout (%d): clkctl 0x%02x\n",
      PMU_MAX_TRANSITION_DLY, clkctl);
   return -EBADE;
  }

  /* Mark clock available */
  bus->clkstate = CLK_AVAIL;
  brcmf_dbg(SDIO, "CLKCTL: turned ON\n");

#if defined(DEBUG)
  if (!bus->alp_only) {
   if (SBSDIO_ALPONLY(clkctl))
    brcmf_err("HT Clock should be on\n");
  }
#endif    /* defined (DEBUG) */

 } else {
  clkreq = 0;

  if (bus->clkstate == CLK_PENDING) {
   /* Cancel CA-only interrupt filter */
   devctl = brcmf_sdiod_readb(bus->sdiodev,
         SBSDIO_DEVICE_CTL, &err);
   devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
   brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_DEVICE_CTL,
        devctl, &err);
  }

  bus->clkstate = CLK_SDONLY;
  brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
       clkreq, &err);
  brcmf_dbg(SDIO, "CLKCTL: turned OFF\n");
  if (err) {
   brcmf_err("Failed access turning clock off: %d\n",
      err);
   return -EBADE;
  }
 }
 return 0;
}

/* Change idle/active SD state */
static int brcmf_sdio_sdclk(struct brcmf_sdio *bus, bool on)
{
 brcmf_dbg(SDIO, "Enter\n");

 if (on)
  bus->clkstate = CLK_SDONLY;
 else
  bus->clkstate = CLK_NONE;

 return 0;
}

/* Transition SD and backplane clock readiness */
static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)
{
#ifdef DEBUG
 uint oldstate = bus->clkstate;
#endif    /* DEBUG */

 brcmf_dbg(SDIO, "Enter\n");

 /* 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);
  else if (bus->clkstate == CLK_AVAIL)
   brcmf_sdio_htclk(bus, falsefalse);
  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, falsefalse);
  /* Now remove the SD clock */
  brcmf_sdio_sdclk(bus, false);
  break;
 }
#ifdef DEBUG
 brcmf_dbg(SDIO, "%d -> %d\n", oldstate, bus->clkstate);
#endif    /* DEBUG */

 return 0;
}

static int
brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
{
 int err = 0;
 u8 clkcsr;

 brcmf_dbg(SDIO, "Enter: request %s currently %s\n",
    (sleep ? "SLEEP" : "WAKE"),
    (bus->sleeping ? "SLEEP" : "WAKE"));

 /* 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;

}

#ifdef DEBUG
static inline bool brcmf_sdio_valid_shared_address(u32 addr)
{
 return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff));
}

static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
     struct sdpcm_shared *sh)
{
 u32 addr = 0;
 int rv;
 u32 shaddr = 0;
 struct sdpcm_shared_le sh_le;
 __le32 addr_le;

 sdio_claim_host(bus->sdiodev->func1);
 brcmf_sdio_bus_sleep(bus, falsefalse);

 /*
 * 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;
 }

 brcmf_dbg(INFO, "sdpcm_shared address 0x%08X\n", addr);

 /* Read hndrte_shared structure */
 rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le,
          sizeof(struct sdpcm_shared_le));
 if (rv < 0)
  goto fail;

 sdio_release_host(bus->sdiodev->func1);

 /* Endianness */
 sh->flags = le32_to_cpu(sh_le.flags);
 sh->trap_addr = le32_to_cpu(sh_le.trap_addr);
 sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr);
 sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr);
 sh->assert_line = le32_to_cpu(sh_le.assert_line);
 sh->console_addr = le32_to_cpu(sh_le.console_addr);
 sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr);

 if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) {
  brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n",
     SDPCM_SHARED_VERSION,
     sh->flags & SDPCM_SHARED_VERSION_MASK);
  return -EPROTO;
 }
 return 0;

fail:
 brcmf_err("unable to obtain sdpcm_shared info: rv=%d (addr=0x%x)\n",
    rv, addr);
 sdio_release_host(bus->sdiodev->func1);
 return rv;
}

static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus)
{
 struct sdpcm_shared sh;

 if (brcmf_sdio_readshared(bus, &sh) == 0)
  bus->console_addr = sh.console_addr;
}
#else
static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus)
{
}
#endif /* DEBUG */

static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus)
{
 struct brcmf_sdio_dev *sdiod = bus->sdiodev;
 struct brcmf_core *core = bus->sdio_core;
 u32 intstatus = 0;
 u32 hmb_data;
 u8 fcbits;
 int ret;

 brcmf_dbg(SDIO, "Enter\n");

 /* Read mailbox data and ack that we did so */
 hmb_data = brcmf_sdiod_readl(sdiod,
         core->base + SD_REG(tohostmailboxdata),
         &ret);

 if (!ret)
  brcmf_sdiod_writel(sdiod, core->base + SD_REG(tosbmailbox),
       SMB_INT_ACK, &ret);

 bus->sdcnt.f1regdata += 2;

 /* dongle indicates the firmware has halted/crashed */
 if (hmb_data & HMB_DATA_FWHALT) {
  brcmf_dbg(SDIO, "mailbox indicates firmware halted\n");
  brcmf_fw_crashed(&sdiod->func1->dev);
 }

 /* Dongle recomposed rx frames, accept them again */
 if (hmb_data & HMB_DATA_NAKHANDLED) {
  brcmf_dbg(SDIO, "Dongle reports NAK handled, expect rtx of %d\n",
     bus->rx_seq);
  if (!bus->rxskip)
   brcmf_err("unexpected NAKHANDLED!\n");

  bus->rxskip = false;
  intstatus |= I_HMB_FRAME_IND;
 }

 /*
 * 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++;

  bus->sdcnt.fc_rcvd++;
  bus->flowcontrol = fcbits;
 }

 /* Shouldn't be any others */
 if (hmb_data & ~(HMB_DATA_DEVREADY |
    HMB_DATA_NAKHANDLED |
    HMB_DATA_FC |
    HMB_DATA_FWREADY |
    HMB_DATA_FWHALT |
    HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK))
  brcmf_err("Unknown mailbox data content: 0x%02x\n",
     hmb_data);

 return intstatus;
}

static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
{
 struct brcmf_sdio_dev *sdiod = bus->sdiodev;
 struct brcmf_core *core = bus->sdio_core;
 uint retries = 0;
 u16 lastrbc;
 u8 hi, lo;
 int err;

 brcmf_err("%sterminate frame%s\n",
    abort ? "abort command, " : "",
    rtx ? ", send NAK" : "");

 if (abort)
  brcmf_sdiod_abort(bus->sdiodev, bus->sdiodev->func2);

 brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_RF_TERM,
      &err);
 bus->sdcnt.f1regdata++;

 /* 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;
}

static void brcmf_sdio_txfail(struct brcmf_sdio *bus)
{
 struct brcmf_sdio_dev *sdiodev = bus->sdiodev;
 u8 i, hi, lo;

 /* On failure, abort the command and terminate the frame */
 brcmf_err("sdio error, abort command and terminate frame\n");
 bus->sdcnt.tx_sderrs++;

 brcmf_sdiod_abort(sdiodev, sdiodev->func2);
 brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL);
 bus->sdcnt.f1regdata++;

 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;
}

static void brcmf_sdio_free_glom(struct brcmf_sdio *bus)
{
 struct sk_buff *cur, *next;

 skb_queue_walk_safe(&bus->glom, cur, next) {
  skb_unlink(cur, &bus->glom);
  brcmu_pkt_buf_free_skb(cur);
 }
}

/*
 * brcmfmac sdio bus specific header
 * This is the lowest layer header wrapped on the packets transmitted between
 * host and WiFi dongle which contains information needed for SDIO core and
 * firmware
 *
 * It consists of 3 parts: hardware header, hardware extension header and
 * software header
 * hardware header (frame tag) - 4 bytes
 * Byte 0~1: Frame length
 * Byte 2~3: Checksum, bit-wise inverse of frame length
 * hardware extension header - 8 bytes
 * Tx glom mode only, N/A for Rx or normal Tx
 * Byte 0~1: Packet length excluding hw frame tag
 * Byte 2: Reserved
 * Byte 3: Frame flags, bit 0: last frame indication
 * Byte 4~5: Reserved
 * Byte 6~7: Tail padding length
 * software header - 8 bytes
 * Byte 0: Rx/Tx sequence number
 * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag
 * Byte 2: Length of next data frame, reserved for Tx
 * Byte 3: Data offset
 * Byte 4: Flow control bits, reserved for Tx
 * Byte 5: Maximum Sequence number allowed by firmware for Tx, N/A for Tx packet
 * Byte 6~7: Reserved
 */

#define SDPCM_HWHDR_LEN   4
#define SDPCM_HWEXT_LEN   8
#define SDPCM_SWHDR_LEN   8
#define SDPCM_HDRLEN   (SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN)
/* software header */
#define SDPCM_SEQ_MASK   0x000000ff
#define SDPCM_SEQ_WRAP   256
#define SDPCM_CHANNEL_MASK  0x00000f00
#define SDPCM_CHANNEL_SHIFT  8
#define SDPCM_CONTROL_CHANNEL  0 /* Control */
#define SDPCM_EVENT_CHANNEL  1 /* Asyc Event Indication */
#define SDPCM_DATA_CHANNEL  2 /* Data Xmit/Recv */
#define SDPCM_GLOM_CHANNEL  3 /* Coalesced packets */
#define SDPCM_TEST_CHANNEL  15 /* Test/debug packets */
#define SDPCM_GLOMDESC(p)  (((u8 *)p)[1] & 0x80)
#define SDPCM_NEXTLEN_MASK  0x00ff0000
#define SDPCM_NEXTLEN_SHIFT  16
#define SDPCM_DOFFSET_MASK  0xff000000
#define SDPCM_DOFFSET_SHIFT  24
#define SDPCM_FCMASK_MASK  0x000000ff
#define SDPCM_WINDOW_MASK  0x0000ff00
#define SDPCM_WINDOW_SHIFT  8

static inline u8 brcmf_sdio_getdatoffset(u8 *swheader)
{
 u32 hdrvalue;
 hdrvalue = le32_to_cpu(*(__le32 *)swheader);
 return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT);
}

static inline bool brcmf_sdio_fromevntchan(u8 *swheader)
{
 u32 hdrvalue;
 u8 ret;

 hdrvalue = le32_to_cpu(*(__le32 *)swheader);
 ret = (u8)((hdrvalue & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT);

 return (ret == SDPCM_EVENT_CHANNEL);
}

static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,
         struct brcmf_sdio_hdrinfo *rd,
         enum brcmf_sdio_frmtype type)
{
 u16 len, checksum;
 u8 rx_seq, fc, tx_seq_max;
 u32 swheader;

 trace_brcmf_sdpcm_hdr(SDPCM_RX, header);

 /* hw header */
 len = get_unaligned_le16(header);
 checksum = get_unaligned_le16(header + sizeof(u16));
 /* All zero means no more to read */
 if (!(len | checksum)) {
  bus->rxpending = false;
  return -ENODATA;
 }
 if ((u16)(~(len ^ checksum))) {
  brcmf_err("HW header checksum error\n");
  bus->sdcnt.rx_badhdr++;
  brcmf_sdio_rxfail(bus, falsefalse);
  return -EIO;
 }
 if (len < SDPCM_HDRLEN) {
  brcmf_err("HW header length error\n");
  return -EPROTO;
 }
 if (type == BRCMF_SDIO_FT_SUPER &&
     (roundup(len, bus->blocksize) != rd->len)) {
  brcmf_err("HW superframe header length error\n");
  return -EPROTO;
 }
 if (type == BRCMF_SDIO_FT_SUB && len > rd->len) {
  brcmf_err("HW subframe header length error\n");
  return -EPROTO;
 }
 rd->len = len;

 /* software header */
 header += SDPCM_HWHDR_LEN;
 swheader = le32_to_cpu(*(__le32 *)header);
 if (type == BRCMF_SDIO_FT_SUPER && SDPCM_GLOMDESC(header)) {
  brcmf_err("Glom descriptor found in superframe head\n");
  rd->len = 0;
  return -EINVAL;
 }
 rx_seq = (u8)(swheader & SDPCM_SEQ_MASK);
 rd->channel = (swheader & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT;
 if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL &&
     type != BRCMF_SDIO_FT_SUPER) {
  brcmf_err("HW header length too long\n");
  bus->sdcnt.rx_toolong++;
  brcmf_sdio_rxfail(bus, falsefalse);
  rd->len = 0;
  return -EPROTO;
 }
 if (type == BRCMF_SDIO_FT_SUPER && rd->channel != SDPCM_GLOM_CHANNEL) {
  brcmf_err("Wrong channel for superframe\n");
  rd->len = 0;
  return -EINVAL;
 }
 if (type == BRCMF_SDIO_FT_SUB && rd->channel != SDPCM_DATA_CHANNEL &&
     rd->channel != SDPCM_EVENT_CHANNEL) {
  brcmf_err("Wrong channel for subframe\n");
  rd->len = 0;
  return -EINVAL;
 }
 rd->dat_offset = brcmf_sdio_getdatoffset(header);
 if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) {
  brcmf_err("seq %d: bad data offset\n", rx_seq);
  bus->sdcnt.rx_badhdr++;
  brcmf_sdio_rxfail(bus, falsefalse);
  rd->len = 0;
  return -ENXIO;
 }
 if (rd->seq_num != rx_seq) {
  brcmf_dbg(SDIO, "seq %d, expected %d\n", rx_seq, rd->seq_num);
  bus->sdcnt.rx_badseq++;
  rd->seq_num = rx_seq;
 }
 /* no need to check the reset for subframe */
 if (type == BRCMF_SDIO_FT_SUB)
  return 0;
 rd->len_nxtfrm = (swheader & SDPCM_NEXTLEN_MASK) >> SDPCM_NEXTLEN_SHIFT;
 if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) {
  /* only warm for NON glom packet */
  if (rd->channel != SDPCM_GLOM_CHANNEL)
   brcmf_err("seq %d: next length error\n", rx_seq);
  rd->len_nxtfrm = 0;
 }
 swheader = le32_to_cpu(*(__le32 *)(header + 4));
 fc = swheader & SDPCM_FCMASK_MASK;
 if (bus->flowcontrol != fc) {
  if (~bus->flowcontrol & fc)
   bus->sdcnt.fc_xoff++;
  if (bus->flowcontrol & ~fc)
   bus->sdcnt.fc_xon++;
  bus->sdcnt.fc_rcvd++;
  bus->flowcontrol = fc;
 }
 tx_seq_max = (swheader & SDPCM_WINDOW_MASK) >> SDPCM_WINDOW_SHIFT;
 if ((u8)(tx_seq_max - bus->tx_seq) > 0x40) {
  brcmf_err("seq %d: max tx seq number error\n", rx_seq);
  tx_seq_max = bus->tx_seq + 2;
 }
 bus->tx_max = tx_seq_max;

 return 0;
}

static inline void brcmf_sdio_update_hwhdr(u8 *header, u16 frm_length)
{
 *(__le16 *)header = cpu_to_le16(frm_length);
 *(((__le16 *)header) + 1) = cpu_to_le16(~frm_length);
}

static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header,
         struct brcmf_sdio_hdrinfo *hd_info)
{
 u32 hdrval;
 u8 hdr_offset;

 brcmf_sdio_update_hwhdr(header, hd_info->len);
 hdr_offset = SDPCM_HWHDR_LEN;

 if (bus->txglom) {
  hdrval = (hd_info->len - hdr_offset) | (hd_info->lastfrm << 24);
  *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval);
  hdrval = (u16)hd_info->tail_pad << 16;
  *(((__le32 *)(header + hdr_offset)) + 1) = cpu_to_le32(hdrval);
  hdr_offset += SDPCM_HWEXT_LEN;
 }

 hdrval = hd_info->seq_num;
 hdrval |= (hd_info->channel << SDPCM_CHANNEL_SHIFT) &
    SDPCM_CHANNEL_MASK;
 hdrval |= (hd_info->dat_offset << SDPCM_DOFFSET_SHIFT) &
    SDPCM_DOFFSET_MASK;
 *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval);
 *(((__le32 *)(header + hdr_offset)) + 1) = 0;
 trace_brcmf_sdpcm_hdr(SDPCM_TX + !!(bus->txglom), header);
}

static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq)
{
 u16 dlen, totlen;
 u8 *dptr, num = 0;
 u16 sublen;
 struct sk_buff *pfirst, *pnext;

 int errcode;
 u8 doff;

 struct brcmf_sdio_hdrinfo rd_new;

 /* If packets, issue read(s) and send up packet chain */
 /* Return sequence numbers consumed? */

 brcmf_dbg(SDIO, "start: glomd %p glom %p\n",
    bus->glomd, skb_peek(&bus->glom));

 /* 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);

   /* Adhere to start alignment requirements */
   pkt_align(pnext, sublen, bus->sgentry_align);
  }

  /* If all allocations succeeded, save packet chain
 in bus structure */

  if (pnext) {
   brcmf_dbg(GLOM, "allocated %d-byte packet chain for %d subframes\n",
      totlen, num);
   if (BRCMF_GLOM_ON() && bus->cur_read.len &&
       totlen != bus->cur_read.len) {
    brcmf_dbg(GLOM, "glomdesc mismatch: nextlen %d glomdesc %d rxseq %d\n",
       bus->cur_read.len, totlen, rxseq);
   }
   pfirst = pnext = NULL;
  } else {
   brcmf_sdio_free_glom(bus);
   num = 0;
  }

  /* Done with descriptor packet */
  brcmu_pkt_buf_free_skb(bus->glomd);
  bus->glomd = NULL;
  bus->cur_read.len = 0;
 }

 /* 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);
   }
  }

  pfirst = skb_peek(&bus->glom);
  dlen = (u16) brcmf_sdio_glom_len(bus);

  /* 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);

   sdio_claim_host(bus->sdiodev->func1);
   brcmf_sdio_rxfail(bus, truefalse);
   bus->sdcnt.rxglomfail++;
   brcmf_sdio_free_glom(bus);
   sdio_release_host(bus->sdiodev->func1);
   return 0;
  }

  brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
       pfirst->data, min_t(int, pfirst->len, 48),
       "SUPERFRAME:\n");

  rd_new.seq_num = rxseq;
  rd_new.len = dlen;
  sdio_claim_host(bus->sdiodev->func1);
  errcode = brcmf_sdio_hdparse(bus, pfirst->data, &rd_new,
          BRCMF_SDIO_FT_SUPER);
  sdio_release_host(bus->sdiodev->func1);
  bus->cur_read.len = rd_new.len_nxtfrm << 4;

  /* Remove superframe header, remember offset */
  skb_pull(pfirst, rd_new.dat_offset);
  num = 0;

  /* Validate all the subframe headers */
  skb_queue_walk(&bus->glom, pnext) {
   /* leave when invalid subframe is found */
   if (errcode)
    break;

   rd_new.len = pnext->len;
   rd_new.seq_num = rxseq++;
   sdio_claim_host(bus->sdiodev->func1);
   errcode = brcmf_sdio_hdparse(bus, pnext->data, &rd_new,
           BRCMF_SDIO_FT_SUB);
   sdio_release_host(bus->sdiodev->func1);
   brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
        pnext->data, 32, "subframe:\n");

   num++;
  }

  if (errcode) {
   /* Terminate frame on error */
   sdio_claim_host(bus->sdiodev->func1);
   brcmf_sdio_rxfail(bus, truefalse);
   bus->sdcnt.rxglomfail++;
   brcmf_sdio_free_glom(bus);
   sdio_release_host(bus->sdiodev->func1);
   bus->cur_read.len = 0;
   return 0;
  }

  /* Basic SD framing looks ok - process each packet (header) */

  skb_queue_walk_safe(&bus->glom, pfirst, pnext) {
   dptr = (u8 *) (pfirst->data);
   sublen = get_unaligned_le16(dptr);
   doff = brcmf_sdio_getdatoffset(&dptr[SDPCM_HWHDR_LEN]);

   brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
        dptr, pfirst->len,
        "Rx Subframe Data:\n");

   __skb_trim(pfirst, sublen);
   skb_pull(pfirst, doff);

   if (pfirst->len == 0) {
    skb_unlink(pfirst, &bus->glom);
    brcmu_pkt_buf_free_skb(pfirst);
    continue;
   }

   brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
        pfirst->data,
        min_t(int, pfirst->len, 32),
        "subframe %d to stack, %p (%p/%d) nxt/lnk %p/%p\n",
        bus->glom.qlen, pfirst, pfirst->data,
        pfirst->len, pfirst->next,
        pfirst->prev);
   skb_unlink(pfirst, &bus->glom);
   if (brcmf_sdio_fromevntchan(&dptr[SDPCM_HWHDR_LEN]))
    brcmf_rx_event(bus->sdiodev->dev, pfirst);
   else
    brcmf_rx_frame(bus->sdiodev->dev, pfirst,
            falsefalse);
   bus->sdcnt.rxglompkts++;
  }

  bus->sdcnt.rxglomframes++;
 }
 return num;
}

static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition,
         bool *pending)
{
 DECLARE_WAITQUEUE(wait, current);
 int timeout = DCMD_RESP_TIMEOUT;

 /* Wait until control frame is available */
 add_wait_queue(&bus->dcmd_resp_wait, &wait);
 set_current_state(TASK_INTERRUPTIBLE);

 while (!(*condition) && (!signal_pending(current) && timeout))
  timeout = schedule_timeout(timeout);

 if (signal_pending(current))
  *pending = true;

 set_current_state(TASK_RUNNING);
 remove_wait_queue(&bus->dcmd_resp_wait, &wait);

 return timeout;
}

static int brcmf_sdio_dcmd_resp_wake(struct brcmf_sdio *bus)
{
 wake_up_interruptible(&bus->dcmd_resp_wait);

 return 0;
}
static void
brcmf_sdio_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
{
 uint rdlen, pad;
 u8 *buf = NULL, *rbuf;
 int sdret;

 brcmf_dbg(SDIO, "Enter\n");
 if (bus->rxblen)
  buf = vzalloc(bus->rxblen);
 if (!buf)
  goto done;

 rbuf = bus->rxbuf;
 pad = ((unsigned long)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;
 } else if (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, falsefalse);
  goto done;
 }

 if ((len - doff) > bus->sdiodev->bus_if->maxctl) {
  brcmf_err("%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n",
     len, len - doff, bus->sdiodev->bus_if->maxctl);
  bus->sdcnt.rx_toolong++;
  brcmf_sdio_rxfail(bus, falsefalse);
  goto done;
 }

 /* Read remain of frame body */
 sdret = brcmf_sdiod_recv_buf(bus->sdiodev, rbuf, rdlen);
 bus->sdcnt.f2rxdata++;

 /* Control frame failures need retransmission */
 if (sdret < 0) {
  brcmf_err("read %d control bytes failed: %d\n",
     rdlen, sdret);
  bus->sdcnt.rxc_errors++;
  brcmf_sdio_rxfail(bus, truetrue);
  goto done;
 } else
  memcpy(buf + BRCMF_FIRSTREAD, rbuf, rdlen);

gotpkt:

 brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(),
      buf, len, "RxCtrl:\n");

 /* 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);
}

/* Pad read to blocksize for efficiency */
static void brcmf_sdio_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen)
{
 if (bus->roundup && bus->blocksize && *rdlen > bus->blocksize) {
  *pad = bus->blocksize - (*rdlen % bus->blocksize);
  if (*pad <= bus->roundup && *pad < bus->blocksize &&
      *rdlen + *pad + BRCMF_FIRSTREAD < MAX_RX_DATASZ)
   *rdlen += *pad;
 } else if (*rdlen % bus->head_align) {
  *rdlen += bus->head_align - (*rdlen % bus->head_align);
 }
}

static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
{
 struct sk_buff *pkt;  /* Packet for event or data frames */
 u16 pad;  /* Number of pad bytes to read */
 uint rxleft = 0; /* Remaining number of frames allowed */
 int ret;  /* Return code from calls */
 uint rxcount = 0; /* Total frames read */
 struct brcmf_sdio_hdrinfo *rd = &bus->cur_read, rd_new;
 u8 head_read = 0;

 brcmf_dbg(SDIO, "Enter\n");

 /* Not finished unless we encounter no more frames indication */
 bus->rxpending = true;

 for (rd->seq_num = bus->rx_seq, rxleft = maxframes;
      !bus->rxskip && rxleft && bus->sdiodev->state == BRCMF_SDIOD_DATA;
      rd->seq_num++, rxleft--) {

  /* Handle glomming separately */
  if (bus->glomd || !skb_queue_empty(&bus->glom)) {
   u8 cnt;
   brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n",
      bus->glomd, skb_peek(&bus->glom));
   cnt = brcmf_sdio_rxglom(bus, rd->seq_num);
   brcmf_dbg(GLOM, "rxglom returned %d\n", cnt);
   rd->seq_num += cnt - 1;
   rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1;
   continue;
  }

  rd->len_left = rd->len;
  /* read header first for unknown frame length */
  sdio_claim_host(bus->sdiodev->func1);
  if (!rd->len) {
   ret = brcmf_sdiod_recv_buf(bus->sdiodev,
         bus->rxhdr, BRCMF_FIRSTREAD);
   bus->sdcnt.f2rxhdrs++;
   if (ret < 0) {
    brcmf_err("RXHEADER FAILED: %d\n",
       ret);
    bus->sdcnt.rx_hdrfail++;
    brcmf_sdio_rxfail(bus, truetrue);
    sdio_release_host(bus->sdiodev->func1);
    continue;
   }

   brcmf_dbg_hex_dump(BRCMF_BYTES_ON() || BRCMF_HDRS_ON(),
        bus->rxhdr, SDPCM_HDRLEN,
        "RxHdr:\n");

   if (brcmf_sdio_hdparse(bus, bus->rxhdr, rd,
            BRCMF_SDIO_FT_NORMAL)) {
    sdio_release_host(bus->sdiodev->func1);
    if (!bus->rxpending)
     break;
    else
     continue;
   }

   if (rd->channel == SDPCM_CONTROL_CHANNEL) {
    brcmf_sdio_read_control(bus, bus->rxhdr,
       rd->len,
       rd->dat_offset);
    /* 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;
    sdio_release_host(bus->sdiodev->func1);
    continue;
   }
   rd->len_left = rd->len > BRCMF_FIRSTREAD ?
           rd->len - BRCMF_FIRSTREAD : 0;
   head_read = BRCMF_FIRSTREAD;
  }

  brcmf_sdio_pad(bus, &pad, &rd->len_left);

  pkt = brcmu_pkt_buf_get_skb(rd->len_left + head_read +
         bus->head_align);
  if (!pkt) {
   /* Give up on data, request rtx of events */
   brcmf_err("brcmu_pkt_buf_get_skb failed\n");
   brcmf_sdio_rxfail(bus, false,
         RETRYCHAN(rd->channel));
   sdio_release_host(bus->sdiodev->func1);
   continue;
  }
  skb_pull(pkt, head_read);
  pkt_align(pkt, rd->len_left, bus->head_align);

  ret = brcmf_sdiod_recv_pkt(bus->sdiodev, pkt);
  bus->sdcnt.f2rxdata++;
  sdio_release_host(bus->sdiodev->func1);

  if (ret < 0) {
   brcmf_err("read %d bytes from channel %d failed: %d\n",
      rd->len, rd->channel, ret);
   brcmu_pkt_buf_free_skb(pkt);
   sdio_claim_host(bus->sdiodev->func1);
   brcmf_sdio_rxfail(bus, true,
         RETRYCHAN(rd->channel));
   sdio_release_host(bus->sdiodev->func1);
   continue;
  }

  if (head_read) {
   skb_push(pkt, head_read);
   memcpy(pkt->data, bus->rxhdr, head_read);
   head_read = 0;
  } else {
   memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN);
   rd_new.seq_num = rd->seq_num;
   sdio_claim_host(bus->sdiodev->func1);
   if (brcmf_sdio_hdparse(bus, bus->rxhdr, &rd_new,
            BRCMF_SDIO_FT_NORMAL)) {
    rd->len = 0;
    brcmf_sdio_rxfail(bus, truetrue);
    sdio_release_host(bus->sdiodev->func1);
    brcmu_pkt_buf_free_skb(pkt);
    continue;
   }
   bus->sdcnt.rx_readahead_cnt++;
   if (rd->len != roundup(rd_new.len, 16)) {
    brcmf_err("frame length mismatch:read %d, should be %d\n",
       rd->len,
       roundup(rd_new.len, 16) >> 4);
    rd->len = 0;
    brcmf_sdio_rxfail(bus, truetrue);
    sdio_release_host(bus->sdiodev->func1);
    brcmu_pkt_buf_free_skb(pkt);
    continue;
   }
   sdio_release_host(bus->sdiodev->func1);
   rd->len_nxtfrm = rd_new.len_nxtfrm;
   rd->channel = rd_new.channel;
   rd->dat_offset = rd_new.dat_offset;

   brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() &&
          BRCMF_DATA_ON()) &&
        BRCMF_HDRS_ON(),
        bus->rxhdr, SDPCM_HDRLEN,
        "RxHdr:\n");

   if (rd_new.channel == SDPCM_CONTROL_CHANNEL) {
    brcmf_err("readahead on control packet %d?\n",
       rd_new.seq_num);
    /* Force retry w/normal header read */
    rd->len = 0;
    sdio_claim_host(bus->sdiodev->func1);
    brcmf_sdio_rxfail(bus, falsetrue);
    sdio_release_host(bus->sdiodev->func1);
    brcmu_pkt_buf_free_skb(pkt);
    continue;
   }
  }

  brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
       pkt->data, rd->len, "Rx Data:\n");

  /* Save superframe descriptor and allocate packet frame */
  if (rd->channel == SDPCM_GLOM_CHANNEL) {
   if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_HWHDR_LEN])) {
    brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n",
       rd->len);
    brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
         pkt->data, rd->len,
         "Glom Data:\n");
    __skb_trim(pkt, rd->len);
    skb_pull(pkt, SDPCM_HDRLEN);
    bus->glomd = pkt;
   } else {
    brcmf_err("%s: glom superframe w/o "
       "descriptor!\n", __func__);
    sdio_claim_host(bus->sdiodev->func1);
    brcmf_sdio_rxfail(bus, falsefalse);
    sdio_release_host(bus->sdiodev->func1);
   }
   /* 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;
   continue;
  }

  /* Fill in packet len and prio, deliver upward */
  __skb_trim(pkt, rd->len);
  skb_pull(pkt, rd->dat_offset);

  if (pkt->len == 0)
   brcmu_pkt_buf_free_skb(pkt);
  else if (rd->channel == SDPCM_EVENT_CHANNEL)
   brcmf_rx_event(bus->sdiodev->dev, pkt);
  else
   brcmf_rx_frame(bus->sdiodev->dev, pkt,
           falsefalse);

  /* 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;

 return rxcount;
}

static void
brcmf_sdio_wait_event_wakeup(struct brcmf_sdio *bus)
{
 wake_up_interruptible(&bus->ctrl_wait);
 return;
}

static int brcmf_sdio_txpkt_hdalign(struct brcmf_sdio *bus, struct sk_buff *pkt)
{
 struct brcmf_bus_stats *stats;
 u16 head_pad;
 u8 *dat_buf;

 dat_buf = (u8 *)(pkt->data);

 /* Check head padding */
 head_pad = ((unsigned long)dat_buf % bus->head_align);
 if (head_pad) {
  if (skb_headroom(pkt) < head_pad) {
   stats = &bus->sdiodev->bus_if->stats;
   atomic_inc(&stats->pktcowed);
   if (skb_cow_head(pkt, head_pad)) {
    atomic_inc(&stats->pktcow_failed);
    return -ENOMEM;
   }
   head_pad = 0;
  }
  skb_push(pkt, head_pad);
  dat_buf = (u8 *)(pkt->data);
 }
 memset(dat_buf, 0, head_pad + bus->tx_hdrlen);
 return head_pad;
}

/*
 * 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

static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus,
        struct sk_buff_head *pktq,
        struct sk_buff *pkt, u16 total_len)
{
 struct brcmf_sdio_dev *sdiodev;
 struct sk_buff *pkt_pad;
 u16 tail_pad, tail_chop, chain_pad;
 unsigned int blksize;
 bool lastfrm;
 int ntail, ret;

 sdiodev = bus->sdiodev;
 blksize = sdiodev->func2->cur_blksize;
 /* sg entry alignment should be a divisor of block size */
 WARN_ON(blksize % bus->sgentry_align);

 /* Check tail padding */
 lastfrm = skb_queue_is_last(pktq, pkt);
 tail_pad = 0;
 tail_chop = pkt->len % bus->sgentry_align;
 if (tail_chop)
  tail_pad = bus->sgentry_align - tail_chop;
 chain_pad = (total_len + tail_pad) % blksize;
 if (lastfrm && chain_pad)
  tail_pad += blksize - chain_pad;
 if (skb_tailroom(pkt) < tail_pad && pkt->len > blksize) {
  pkt_pad = brcmu_pkt_buf_get_skb(tail_pad + tail_chop +
      bus->head_align);
  if (pkt_pad == NULL)
   return -ENOMEM;
  ret = brcmf_sdio_txpkt_hdalign(bus, pkt_pad);
  if (unlikely(ret < 0)) {
   kfree_skb(pkt_pad);
   return ret;
  }
  memcpy(pkt_pad->data,
         pkt->data + pkt->len - tail_chop,
         tail_chop);
  *(u16 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop;
  skb_trim(pkt, pkt->len - tail_chop);
  skb_trim(pkt_pad, tail_pad + tail_chop);
  __skb_queue_after(pktq, pkt, pkt_pad);
 } else {
  ntail = pkt->data_len + tail_pad -
   (pkt->end - pkt->tail);
  if (skb_cloned(pkt) || ntail > 0)
   if (pskb_expand_head(pkt, 0, ntail, GFP_ATOMIC))
    return -ENOMEM;
  if (skb_linearize(pkt))
   return -ENOMEM;
  __skb_put(pkt, tail_pad);
 }

 return tail_pad;
}

/**
 * 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
 */

static int
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);

  total_len += pkt_next->len;

  hd_info.len = pkt_next->len;
  hd_info.lastfrm = skb_queue_is_last(pktq, pkt_next);
  if (bus->txglom && pktq->qlen > 1) {
   ret = brcmf_sdio_txpkt_prep_sg(bus, pktq,
             pkt_next, total_len);
   if (ret < 0)
    return ret;
   hd_info.tail_pad = (u16)ret;
   total_len += (u16)ret;
  }

  hd_info.channel = chan;
  hd_info.dat_offset = head_pad + bus->tx_hdrlen;
  hd_info.seq_num = txseq++;

  /* 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");
  else if (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
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=89 H=93 G=90

¤ Dauer der Verarbeitung: 0.42 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.