Skip to content

Commit

Permalink
brcmfmac: clear status for in-band interrupt in brcmf_sdbrcm_isr
Browse files Browse the repository at this point in the history
SDIO in-band interrupt is level sensitive according to SDIO standard.
When the register interrupt handler gets called by SDIO stack it is
running in non interrupt context and expected to clear the interrupt
from the dongle. Therefore in-band and out-of-band interrupt need to
be handled differently.

Cc: Wei Ni <wni@nvidia.com>
Cc: Stephen Warren <swarren@wwwdotorg.org>

Reported-by: Wei Ni <wni@nvidia.com>
Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Franky Lin <frankyl@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Tested-by: Stephen Warren <swarren@wwwdotorg.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Franky Lin authored and John W. Linville committed Sep 24, 2012
1 parent 7057fd0 commit 4531603
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 33 deletions.
2 changes: 1 addition & 1 deletion drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address)
return err;
}

static int
int
brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
void *data, bool write)
{
Expand Down
97 changes: 65 additions & 32 deletions drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -494,8 +494,8 @@ struct brcmf_sdio {
u32 ramsize; /* Size of RAM in SOCRAM (bytes) */

u32 hostintmask; /* Copy of Host Interrupt Mask */
u32 intstatus; /* Intstatus bits (events) pending */
bool fcstate; /* State of dongle flow-control */
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 */
Expand Down Expand Up @@ -2271,20 +2271,53 @@ static inline void brcmf_sdbrcm_adddpctsk(struct brcmf_sdio *bus)
spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
}

static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
{
u8 idx;
u32 addr;
unsigned long val;
int n, ret;

idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV);
addr = bus->ci->c_inf[idx].base +
offsetof(struct sdpcmd_regs, intstatus);

ret = brcmf_sdio_regrw_helper(bus->sdiodev, addr, &val, false);
bus->sdcnt.f1regdata++;
if (ret != 0)
val = 0;

val &= bus->hostintmask;
atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE));

/* Clear interrupts */
if (val) {
ret = brcmf_sdio_regrw_helper(bus->sdiodev, addr, &val, true);
bus->sdcnt.f1regdata++;
}

if (ret) {
atomic_set(&bus->intstatus, 0);
} else if (val) {
for_each_set_bit(n, &val, 32)
set_bit(n, (unsigned long *)&bus->intstatus.counter);
}

return ret;
}

static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
{
u32 intstatus, newstatus = 0;
u32 newstatus = 0;
unsigned long intstatus;
uint rxlimit = bus->rxbound; /* Rx frames to read before resched */
uint txlimit = bus->txbound; /* Tx frames to send before resched */
uint framecnt = 0; /* Temporary counter of tx/rx frames */
bool rxdone = true; /* Flag for no more read data */
int err = 0;
int err = 0, n;

brcmf_dbg(TRACE, "Enter\n");

/* Start with leftover status bits */
intstatus = bus->intstatus;

down(&bus->sdsem);

/* If waiting for HTAVAIL, check status */
Expand Down Expand Up @@ -2339,24 +2372,13 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
/* Pending interrupt indicates new device status */
if (atomic_read(&bus->ipend) > 0) {
atomic_set(&bus->ipend, 0);
err = r_sdreg32(bus, &newstatus,
offsetof(struct sdpcmd_regs, intstatus));
bus->sdcnt.f1regdata++;
if (err != 0)
newstatus = 0;
newstatus &= bus->hostintmask;
bus->fcstate = !!(newstatus & I_HMB_FC_STATE);
if (newstatus) {
err = w_sdreg32(bus, newstatus,
offsetof(struct sdpcmd_regs,
intstatus));
bus->sdcnt.f1regdata++;
}
sdio_claim_host(bus->sdiodev->func[1]);
err = brcmf_sdio_intr_rstatus(bus);
sdio_release_host(bus->sdiodev->func[1]);
}

/* Merge new bits with previous */
intstatus |= newstatus;
bus->intstatus = 0;
/* 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
Expand All @@ -2370,8 +2392,8 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
err = r_sdreg32(bus, &newstatus,
offsetof(struct sdpcmd_regs, intstatus));
bus->sdcnt.f1regdata += 2;
bus->fcstate =
!!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE));
atomic_set(&bus->fcstate,
!!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE)));
intstatus |= (newstatus & bus->hostintmask);
}

Expand Down Expand Up @@ -2416,7 +2438,10 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
}

/* Keep still-pending events for next scheduling */
bus->intstatus = intstatus;
if (intstatus) {
for_each_set_bit(n, &intstatus, 32)
set_bit(n, (unsigned long *)&bus->intstatus.counter);
}

brcmf_sdbrcm_clrintr(bus);

Expand Down Expand Up @@ -2461,7 +2486,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
brcmf_sdbrcm_wait_event_wakeup(bus);
}
/* Send queued frames (limit 1 if rx may still be pending) */
else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate &&
else if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) &&
brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit
&& data_ok(bus)) {
framecnt = rxdone ? txlimit : min(txlimit, bus->txminmax);
Expand All @@ -2472,10 +2497,12 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
if ((bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) || (err != 0)) {
brcmf_dbg(ERROR, "failed backplane access over SDIO, halting operation\n");
bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
bus->intstatus = 0;
} else if (bus->intstatus || atomic_read(&bus->ipend) > 0 ||
(!bus->fcstate && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)
&& data_ok(bus)) || PKT_AVAILABLE()) {
atomic_set(&bus->intstatus, 0);
} else if (atomic_read(&bus->intstatus) ||
atomic_read(&bus->ipend) > 0 ||
(!atomic_read(&bus->fcstate) &&
brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
data_ok(bus)) || PKT_AVAILABLE()) {
brcmf_sdbrcm_adddpctsk(bus);
}

Expand Down Expand Up @@ -3640,7 +3667,13 @@ void brcmf_sdbrcm_isr(void *arg)
}
/* Count the interrupt call */
bus->sdcnt.intrcount++;
atomic_set(&bus->ipend, 1);
if (in_interrupt())
atomic_set(&bus->ipend, 1);
else
if (brcmf_sdio_intr_rstatus(bus)) {
brcmf_dbg(ERROR, "failed backplane access\n");
bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
}

/* Disable additional interrupts (is this needed now)? */
if (!bus->intr)
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ extern void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr,
u8 data, int *ret);
extern void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
u32 data, int *ret);
extern int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
void *data, bool write);

/* Buffer transfer to/from device (client) core via cmd53.
* fn: function number
Expand Down

0 comments on commit 4531603

Please sign in to comment.