From 0801e6c5d8224a27abb7afc920676782efdd5922 Mon Sep 17 00:00:00 2001 From: Daniel Kim Date: Wed, 29 Jan 2014 15:32:13 +0100 Subject: brcmfmac: enable firmware console logging functionality Address of rte console was not initialized and so console logging functionality didn't kick in. This patch fixes it and makes console polling interval a debugfs variable. Reviewed-by: Franky Lin Signed-off-by: Daniel Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 154 ++++++++++++--------- 1 file changed, 88 insertions(+), 66 deletions(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 3e991897d7ca..98d07846d6e5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -953,6 +953,86 @@ end: } +#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; + int rv; + u32 shaddr = 0; + struct sdpcm_shared_le sh_le; + __le32 addr_le; + + shaddr = bus->ci->rambase + bus->ramsize - 4; + + /* + * Read last word in socram to determine + * address of sdpcm_shared structure + */ + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_bus_sleep(bus, false, false); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); + sdio_release_host(bus->sdiodev->func[1]); + if (rv < 0) + return rv; + + addr = le32_to_cpu(addr_le); + + brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr); + + /* + * Check if addr is valid. + * NVRAM length at the end of memory should have been overwritten. + */ + if (!brcmf_sdio_valid_shared_address(addr)) { + brcmf_err("invalid sdpcm_shared address 0x%08X\n", + addr); + return -EINVAL; + } + + /* Read hndrte_shared structure */ + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, + sizeof(struct sdpcm_shared_le)); + if (rv < 0) + return rv; + + /* 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; +} + +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) { u32 intstatus = 0; @@ -996,6 +1076,12 @@ static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus) 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); } /* @@ -2810,72 +2896,6 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) } #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; - int rv; - u32 shaddr = 0; - struct sdpcm_shared_le sh_le; - __le32 addr_le; - - shaddr = bus->ci->rambase + bus->ramsize - 4; - - /* - * Read last word in socram to determine - * address of sdpcm_shared structure - */ - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_bus_sleep(bus, false, false); - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); - sdio_release_host(bus->sdiodev->func[1]); - if (rv < 0) - return rv; - - addr = le32_to_cpu(addr_le); - - brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr); - - /* - * Check if addr is valid. - * NVRAM length at the end of memory should have been overwritten. - */ - if (!brcmf_sdio_valid_shared_address(addr)) { - brcmf_err("invalid sdpcm_shared address 0x%08X\n", - addr); - return -EINVAL; - } - - /* Read hndrte_shared structure */ - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, - sizeof(struct sdpcm_shared_le)); - if (rv < 0) - return rv; - - /* 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; -} - static int brcmf_sdio_dump_console(struct brcmf_sdio *bus, struct sdpcm_shared *sh, char __user *data, size_t count) @@ -3105,6 +3125,8 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) debugfs_create_file("forensics", S_IRUGO, dentry, bus, &brcmf_sdio_forensic_ops); brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt); + debugfs_create_u32("console_interval", 0644, dentry, + &bus->console_interval); } #else static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) -- cgit v1.2.3 From 65d80d0b80960f9bc35d1067a1a297efc60cc014 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 29 Jan 2014 15:32:14 +0100 Subject: brcmfmac: move SDIO specific functions The chip related functions will be made agnostic of the host interface. However, some functions in the source file are rather SDIO specific so better move it to the SDIO specific source file. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 173 ++++++++++++++++++++- 1 file changed, 172 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 98d07846d6e5..090ce1875f15 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -494,6 +494,52 @@ enum brcmf_sdio_frmtype { BRCMF_SDIO_FT_SUB, }; +#define SDIOD_DRVSTR_KEY(chip, pmu) (((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} +}; + #define BCM43143_FIRMWARE_NAME "brcm/brcmfmac43143-sdio.bin" #define BCM43143_NVRAM_NAME "brcm/brcmfmac43143-sdio.txt" #define BCM43241B0_FIRMWARE_NAME "brcm/brcmfmac43241b0-sdio.bin" @@ -3744,6 +3790,131 @@ static void brcmf_sdio_dataworker(struct work_struct *work) } } +static char *brcmf_sdio_chip_name(uint chipid, char *buf, uint len) +{ + const char *fmt; + + fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; + snprintf(buf, len, fmt, chipid); + return buf; +} + +static void +brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci, u32 drivestrength) +{ + const struct sdiod_drive_str *str_tab = NULL; + u32 str_mask; + u32 str_shift; + char chn[8]; + u32 base = ci->c_inf[0].base; + u32 i; + u32 drivestrength_sel = 0; + u32 cc_data_temp; + u32 addr; + + if (!(ci->c_inf[0].caps & CC_CAP_PMU)) + return; + + switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { + case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12): + str_tab = sdiod_drvstr_tab1_1v8; + str_mask = 0x00003800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17): + str_tab = sdiod_drvstr_tab6_1v8; + str_mask = 0x00001800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17): + /* note: 43143 does not support tristate */ + i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1; + if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) { + str_tab = sdiod_drvstr_tab2_3v3; + str_mask = 0x00000007; + str_shift = 0; + } else + brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n", + brcmf_sdio_chip_name(ci->chip, chn, 8), + drivestrength); + break; + case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13): + str_tab = sdiod_drive_strength_tab5_1v8; + str_mask = 0x00003800; + str_shift = 11; + break; + default: + brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", + brcmf_sdio_chip_name(ci->chip, chn, 8), + ci->chiprev, ci->pmurev); + break; + } + + if (str_tab != NULL) { + for (i = 0; str_tab[i].strength != 0; i++) { + if (drivestrength >= str_tab[i].strength) { + drivestrength_sel = str_tab[i].sel; + break; + } + } + addr = CORE_CC_REG(base, chipcontrol_addr); + brcmf_sdiod_regwl(sdiodev, addr, 1, NULL); + cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL); + cc_data_temp &= ~str_mask; + drivestrength_sel <<= str_shift; + cc_data_temp |= drivestrength_sel; + brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL); + + brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n", + str_tab[i].strength, drivestrength, cc_data_temp); + } +} + +int brcmf_sdio_buscoreprep(struct brcmf_sdio_dev *sdiodev) +{ + int err = 0; + u8 clkval, clkset; + + /* Try forcing SDIO core to do ALPAvail request only */ + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; + brcmf_sdiod_regwb(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_regrb(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; + } + + SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, NULL)), + !SBSDIO_ALPAV(clkval)), + PMU_MAX_TRANSITION_DLY); + if (!SBSDIO_ALPAV(clkval)) { + brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n", + clkval); + return -EBUSY; + } + + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); + udelay(65); + + /* Also, disable the extra SDIO pull-ups */ + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); + + return 0; +} + static bool brcmf_sdio_probe_attach(struct brcmf_sdio *bus) { @@ -3794,7 +3965,7 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) drivestrength = bus->sdiodev->pdata->drive_strength; else drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH; - brcmf_sdio_chip_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); + brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); /* Get info on the SOCRAM cores... */ bus->ramsize = bus->ci->ramsize; -- cgit v1.2.3 From 20c9c9bc144c8a7df04259fa98fd0597b2f85cc2 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 29 Jan 2014 15:32:15 +0100 Subject: brcmfmac: rename sdio_chip.[ch] Just renaming the file. This file will contain chip related functions that are independent of the host interface type. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 090ce1875f15..d78fffb43964 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -40,7 +40,7 @@ #include #include #include "sdio_host.h" -#include "sdio_chip.h" +#include "chip.h" #include "nvram.h" #define DCMD_RESP_TIMEOUT 2000 /* In milli second */ -- cgit v1.2.3 From f9951c13349ac1553ab1e0962dc3f877e4841037 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 29 Jan 2014 15:32:17 +0100 Subject: brcmfmac: simplify sdio code download routine. brcmf_sdio_download_code_file is using a loop to send small blobs of data. This is unnecessarily complex and was simplified with this patch. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 31 ++++++---------------- 1 file changed, 8 insertions(+), 23 deletions(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index d78fffb43964..be2ec8a55e76 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -3291,32 +3291,17 @@ static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus, const struct firmware *fw) { int err; - int offset; - int address; - int len; brcmf_dbg(TRACE, "Enter\n"); - err = 0; - offset = 0; - address = bus->ci->rambase; - while (offset < fw->size) { - len = ((offset + MEMBLOCK) < fw->size) ? MEMBLOCK : - fw->size - offset; - err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, - (u8 *)&fw->data[offset], len); - if (err) { - brcmf_err("error %d on writing %d membytes at 0x%08x\n", - err, len, address); - return err; - } - offset += len; - address += len; - } - if (!err) - if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase, - (u8 *)fw->data, fw->size)) - err = -EIO; + err = brcmf_sdiod_ramrw(bus->sdiodev, true, bus->ci->rambase, + (u8 *)fw->data, fw->size); + if (err) + brcmf_err("error %d on writing %d membytes at 0x%08x\n", + err, (int)fw->size, bus->ci->rambase); + else if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase, + (u8 *)fw->data, fw->size)) + err = -EIO; return err; } -- cgit v1.2.3 From e0c180ecf181aa157df28313c82d3b7449b5df65 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 29 Jan 2014 15:32:18 +0100 Subject: brcmfmac: on sdio remove first detach bus then stop worker. Currently the function sdio_remove will first destroy the datawork workqueue and then detach the bus. This can create the situation where work gets added on non-existing work queue resulting in panic. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index be2ec8a55e76..098185442085 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -4205,14 +4205,14 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) /* De-register interrupt handler */ brcmf_sdiod_intr_unregister(bus->sdiodev); - cancel_work_sync(&bus->datawork); - if (bus->brcmf_wq) - destroy_workqueue(bus->brcmf_wq); - if (bus->sdiodev->bus_if->drvr) { brcmf_detach(bus->sdiodev->dev); } + cancel_work_sync(&bus->datawork); + if (bus->brcmf_wq) + destroy_workqueue(bus->brcmf_wq); + if (bus->ci) { if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { sdio_claim_host(bus->sdiodev->func[1]); -- cgit v1.2.3 From cb7cf7be9eba76f3cd6258906074c72084570c84 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 29 Jan 2014 15:32:19 +0100 Subject: brcmfmac: make chip related functions host interface independent This patch make several chip related functions host interface independent by defining callback interface struct brcmf_buscore_ops. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 200 ++++++++++++--------- 1 file changed, 111 insertions(+), 89 deletions(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 098185442085..3056f1173c67 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -156,6 +157,33 @@ struct rte_console { /* manfid tuple length, include tuple, link bytes */ #define SBSDIO_CIS_MANFID_TUPLE_LEN 6 +#define CORE_BUS_REG(base, field) \ + (base + 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_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 */ @@ -665,27 +693,24 @@ static bool data_ok(struct brcmf_sdio *bus) * Reads a register in the SDIO hardware block. This block occupies a series of * adresses on the 32 bit backplane bus. */ -static int -r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset) +static int r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset) { - u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); + struct brcmf_core *core; int ret; - *regvar = brcmf_sdiod_regrl(bus->sdiodev, - bus->ci->c_inf[idx].base + offset, &ret); + core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + *regvar = brcmf_sdiod_regrl(bus->sdiodev, core->base + offset, &ret); return ret; } -static int -w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset) +static int w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset) { - u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); + struct brcmf_core *core; int ret; - brcmf_sdiod_regwl(bus->sdiodev, - bus->ci->c_inf[idx].base + reg_offset, - regval, &ret); + core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + brcmf_sdiod_regwl(bus->sdiodev, core->base + reg_offset, regval, &ret); return ret; } @@ -2425,14 +2450,13 @@ static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus) static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) { - u8 idx; + struct brcmf_core *buscore; 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); + buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus); val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret); bus->sdcnt.f1regdata++; @@ -3344,7 +3368,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) brcmf_sdio_clkctl(bus, CLK_AVAIL, false); /* Keep arm in reset */ - brcmf_sdio_chip_enter_download(bus->sdiodev, bus->ci); + brcmf_chip_enter_download(bus->ci); fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_BIN); if (fw == NULL) { @@ -3376,7 +3400,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) } /* Take arm out of reset */ - if (!brcmf_sdio_chip_exit_download(bus->sdiodev, bus->ci, rstvec)) { + if (!brcmf_chip_exit_download(bus->ci, rstvec)) { brcmf_err("error getting out of ARM core reset\n"); goto err; } @@ -3391,40 +3415,6 @@ err: return bcmerror; } -static bool brcmf_sdio_sr_capable(struct brcmf_sdio *bus) -{ - u32 addr, reg, pmu_cc3_mask = ~0; - int err; - - brcmf_dbg(TRACE, "Enter\n"); - - /* old chips with PMU version less than 17 don't support save restore */ - if (bus->ci->pmurev < 17) - return false; - - switch (bus->ci->chip) { - case BCM43241_CHIP_ID: - case BCM4335_CHIP_ID: - case BCM4339_CHIP_ID: - /* read PMU chipcontrol register 3 */ - addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_addr); - brcmf_sdiod_regwl(bus->sdiodev, addr, 3, NULL); - addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_data); - reg = brcmf_sdiod_regrl(bus->sdiodev, addr, NULL); - return (reg & pmu_cc3_mask) != 0; - default: - addr = CORE_CC_REG(bus->ci->c_inf[0].base, pmucapabilities_ext); - reg = brcmf_sdiod_regrl(bus->sdiodev, addr, &err); - if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0) - return false; - - addr = CORE_CC_REG(bus->ci->c_inf[0].base, retention_ctl); - reg = brcmf_sdiod_regrl(bus->sdiodev, addr, NULL); - return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK | - PMU_RCTL_LOGIC_DISABLE_MASK)) == 0; - } -} - static void brcmf_sdio_sr_init(struct brcmf_sdio *bus) { int err = 0; @@ -3476,7 +3466,7 @@ static int brcmf_sdio_kso_init(struct brcmf_sdio *bus) brcmf_dbg(TRACE, "Enter\n"); /* KSO bit added in SDIO core rev 12 */ - if (bus->ci->c_inf[1].rev < 12) + if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) return 0; val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err); @@ -3507,15 +3497,13 @@ static int brcmf_sdio_bus_preinit(struct device *dev) struct brcmf_sdio *bus = sdiodev->bus; uint pad_size; u32 value; - u8 idx; int err; /* the commands below use the terms tx and rx from * a device perspective, ie. bus:txglom affects the * bus transfers from device to host. */ - idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); - if (bus->ci->c_inf[idx].rev < 12) { + if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) { /* for sdio core rev < 12, disable txgloming */ value = 0; err = brcmf_iovar_data_set(dev, "bus:txglom", &value, @@ -3626,7 +3614,7 @@ static int brcmf_sdio_bus_init(struct device *dev) ret = -ENODEV; } - if (brcmf_sdio_sr_capable(bus)) { + if (brcmf_chip_sr_capable(bus->ci)) { brcmf_sdio_sr_init(bus); } else { /* Restore previous clock setting */ @@ -3775,30 +3763,20 @@ static void brcmf_sdio_dataworker(struct work_struct *work) } } -static char *brcmf_sdio_chip_name(uint chipid, char *buf, uint len) -{ - const char *fmt; - - fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; - snprintf(buf, len, fmt, chipid); - return buf; -} - static void brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u32 drivestrength) + struct brcmf_chip *ci, u32 drivestrength) { const struct sdiod_drive_str *str_tab = NULL; u32 str_mask; u32 str_shift; - char chn[8]; - u32 base = ci->c_inf[0].base; + u32 base; u32 i; u32 drivestrength_sel = 0; u32 cc_data_temp; u32 addr; - if (!(ci->c_inf[0].caps & CC_CAP_PMU)) + if (!(ci->cc_caps & CC_CAP_PMU)) return; switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { @@ -3821,8 +3799,7 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, str_shift = 0; } else brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n", - brcmf_sdio_chip_name(ci->chip, chn, 8), - drivestrength); + ci->name, drivestrength); break; case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13): str_tab = sdiod_drive_strength_tab5_1v8; @@ -3831,8 +3808,7 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, break; default: brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", - brcmf_sdio_chip_name(ci->chip, chn, 8), - ci->chiprev, ci->pmurev); + ci->name, ci->chiprev, ci->pmurev); break; } @@ -3843,6 +3819,7 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, break; } } + base = brcmf_chip_get_chipcommon(ci)->base; addr = CORE_CC_REG(base, chipcontrol_addr); brcmf_sdiod_regwl(sdiodev, addr, 1, NULL); cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL); @@ -3856,8 +3833,9 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, } } -int brcmf_sdio_buscoreprep(struct brcmf_sdio_dev *sdiodev) +static int brcmf_sdio_buscoreprep(void *ctx) { + struct brcmf_sdio_dev *sdiodev = ctx; int err = 0; u8 clkval, clkset; @@ -3900,6 +3878,55 @@ int brcmf_sdio_buscoreprep(struct brcmf_sdio_dev *sdiodev) return 0; } +static void brcmf_sdio_buscore_exitdl(void *ctx, struct brcmf_chip *chip, + u32 rstvec) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + struct brcmf_core *core; + u32 reg_addr; + + /* clear all interrupts */ + core = brcmf_chip_get_core(chip, BCMA_CORE_SDIO_DEV); + reg_addr = core->base + offsetof(struct sdpcmd_regs, intstatus); + brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); + + if (rstvec) + /* Write reset vector to address 0 */ + brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec, + sizeof(rstvec)); +} + +static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + u32 val, rev; + + val = brcmf_sdiod_regrl(sdiodev, addr, NULL); + if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && + addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) { + rev = (val & CID_REV_MASK) >> CID_REV_SHIFT; + if (rev >= 2) { + val &= ~CID_ID_MASK; + val |= BCM4339_CHIP_ID; + } + } + return val; +} + +static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + + brcmf_sdiod_regwl(sdiodev, addr, val, NULL); +} + +static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = { + .prepare = brcmf_sdio_buscoreprep, + .exit_dl = brcmf_sdio_buscore_exitdl, + .read32 = brcmf_sdio_buscore_read32, + .write32 = brcmf_sdio_buscore_write32, +}; + static bool brcmf_sdio_probe_attach(struct brcmf_sdio *bus) { @@ -3915,7 +3942,7 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL)); /* - * Force PLL off until brcmf_sdio_chip_attach() + * Force PLL off until brcmf_chip_attach() * programs PLL control regs */ @@ -3936,8 +3963,10 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) */ brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_DOWN); - if (brcmf_sdio_chip_attach(bus->sdiodev, &bus->ci)) { - brcmf_err("brcmf_sdio_chip_attach failed!\n"); + bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops); + if (IS_ERR(bus->ci)) { + brcmf_err("brcmf_chip_attach failed!\n"); + bus->ci = NULL; goto fail; } @@ -3973,24 +4002,18 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) goto fail; /* set PMUControl so a backplane reset does PMU state reload */ - reg_addr = CORE_CC_REG(bus->ci->c_inf[0].base, + reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base, pmucontrol); - reg_val = brcmf_sdiod_regrl(bus->sdiodev, - reg_addr, - &err); + reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err); if (err) goto fail; reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT); - brcmf_sdiod_regwl(bus->sdiodev, - reg_addr, - reg_val, - &err); + brcmf_sdiod_regwl(bus->sdiodev, reg_addr, reg_val, &err); if (err) goto fail; - sdio_release_host(bus->sdiodev->func[1]); brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN); @@ -4223,12 +4246,11 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) * all necessary cores. */ msleep(20); - brcmf_sdio_chip_enter_download(bus->sdiodev, - bus->ci); + brcmf_chip_enter_download(bus->ci); brcmf_sdio_clkctl(bus, CLK_NONE, false); sdio_release_host(bus->sdiodev->func[1]); } - brcmf_sdio_chip_detach(&bus->ci); + brcmf_chip_detach(bus->ci); } brcmu_pkt_buf_free_skb(bus->txglom_sgpad); -- cgit v1.2.3 From 82030d6df3856896e6a7c912347fe547eaf61738 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 29 Jan 2014 15:32:20 +0100 Subject: brcmfmac: remove TRACE level debug message from brcmf_sdio_bus_sleep() The function brcmf_sdio_bus_sleep() function is called rather frequently, which fills the log when TRACE level is enabled. Reduced the level to SDIO. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 3056f1173c67..631d5dc5b6d5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -971,8 +971,8 @@ static int brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) { int err = 0; - brcmf_dbg(TRACE, "Enter\n"); - brcmf_dbg(SDIO, "request %s currently %s\n", + + brcmf_dbg(SDIO, "Enter: request %s currently %s\n", (sleep ? "SLEEP" : "WAKE"), (bus->sleeping ? "SLEEP" : "WAKE")); -- cgit v1.2.3 From 5aa9f0ea18f3d5ec329a619b0bc54e214e02bc33 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 25 Feb 2014 20:30:27 +0100 Subject: brcmfmac: fix use of skb control buffer in SDIO driver part The SDIO driver has a 16-bit field defined in the skbuff control buffer. However, it is accessed as a u32 overwriting other control info. Another issue is that the field is not initialized for networking packets, but the control buffer content is unspecified as other networking layers can use it. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 631d5dc5b6d5..fa4ec69871c7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2112,7 +2112,7 @@ static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus, memcpy(pkt_pad->data, pkt->data + pkt->len - tail_chop, tail_chop); - *(u32 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop; + *(u16 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop; skb_trim(pkt, pkt->len - tail_chop); __skb_queue_after(pktq, pkt, pkt_pad); } else { @@ -2159,7 +2159,7 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, * already properly aligned and does not * need an sdpcm header. */ - if (*(u32 *)(pkt_next->cb) & ALIGN_SKB_FLAG) + if (*(u16 *)(pkt_next->cb) & ALIGN_SKB_FLAG) continue; /* align packet data pointer */ @@ -2223,11 +2223,11 @@ brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq) u8 *hdr; u32 dat_offset; u16 tail_pad; - u32 dummy_flags, chop_len; + u16 dummy_flags, chop_len; struct sk_buff *pkt_next, *tmp, *pkt_prev; skb_queue_walk_safe(pktq, pkt_next, tmp) { - dummy_flags = *(u32 *)(pkt_next->cb); + dummy_flags = *(u16 *)(pkt_next->cb); if (dummy_flags & ALIGN_SKB_FLAG) { chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK; if (chop_len) { @@ -2709,6 +2709,8 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) /* Priority based enq */ spin_lock_irqsave(&bus->txqlock, flags); + /* reset bus_flags in packet cb */ + *(u16 *)(pkt->cb) = 0; if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) { skb_pull(pkt, bus->tx_hdrlen); brcmf_err("out of bus->txq !!!\n"); -- cgit v1.2.3 From 44ff5660e5cf5ccb739e77a19d2ecf1144cf9e36 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 25 Feb 2014 20:30:28 +0100 Subject: brcmfmac: remove unused variable data_len from brcmf_sdio_bus_txdata() The local variable data_len is assigned but never used so get rid of it. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index fa4ec69871c7..ce1ee1e2f229 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2686,15 +2686,13 @@ static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev) static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) { int ret = -EBADE; - uint datalen, prec; + uint prec; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; ulong flags; - brcmf_dbg(TRACE, "Enter\n"); - - datalen = pkt->len; + brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len); /* Add space for the header */ skb_push(pkt, bus->tx_hdrlen); -- cgit v1.2.3 From 47ab4cd893f565a1093f316ec95babfa181e722a Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 25 Feb 2014 20:30:29 +0100 Subject: brcmfmac: Correct header debug dump for sdio tx hdrs. Reviewed-by: Arend Van Spriel Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index ce1ee1e2f229..ce997324370b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2193,10 +2193,10 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, 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, hd_info.len, + 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, + brcmf_dbg_hex_dump(true, pkt_next->data, head_pad + bus->tx_hdrlen, "Tx Header:\n"); } -- cgit v1.2.3 From 71abdc00d54bb48ad2df4f9710ac661cf3aed30d Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 25 Feb 2014 20:30:31 +0100 Subject: brcmfmac: Minimize SDIO dpc scheduling. SDIO dpc scheduling is done (repeated) when counter is set. This counter gets decreased when dpc is finished. It is more efficient to set counter to 0 before the dpc is actullay run. This will minimize the frequency with which dpc is executed. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index ce997324370b..cc0f1ad8b42c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -3758,8 +3758,8 @@ static void brcmf_sdio_dataworker(struct work_struct *work) datawork); while (atomic_read(&bus->dpc_tskcnt)) { + atomic_set(&bus->dpc_tskcnt, 0); brcmf_sdio_dpc(bus); - atomic_dec(&bus->dpc_tskcnt); } } -- cgit v1.2.3 From 6f6c195b95136e0b2972b3c5213f6cd876c4fa8f Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 25 Feb 2014 20:30:32 +0100 Subject: brcmfmac: Remove immediate sleep support from SDIO. Immediate sleep support is an aggressive power saving option that has not been enabled in brcmfmac and is removed to simplify code. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index cc0f1ad8b42c..04f5e32cfb48 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -304,7 +304,6 @@ struct rte_console { /* Flags for SDH calls */ #define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED) -#define BRCMF_IDLE_IMMEDIATE (-1) /* Enter idle immediately */ #define BRCMF_IDLE_ACTIVE 0 /* Do not request any SD clock change * when idle */ @@ -2662,16 +2661,6 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) data_ok(bus)) || PKT_AVAILABLE()) { atomic_inc(&bus->dpc_tskcnt); } - - /* If we're done for now, turn off clock request. */ - if ((bus->clkstate != CLK_PENDING) - && bus->idletime == BRCMF_IDLE_IMMEDIATE) { - bus->activity = false; - brcmf_dbg(SDIO, "idle state\n"); - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_bus_sleep(bus, true, false); - sdio_release_host(bus->sdiodev->func[1]); - } } static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev) @@ -2948,15 +2937,6 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) } while (ret < 0 && retries++ < TXRETRIES); } - if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) && - atomic_read(&bus->dpc_tskcnt) == 0) { - bus->activity = false; - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_dbg(INFO, "idle\n"); - brcmf_sdio_clkctl(bus, CLK_NONE, true); - sdio_release_host(bus->sdiodev->func[1]); - } - if (ret) bus->sdcnt.tx_ctlerrs++; else -- cgit v1.2.3 From b6a8cf2cd8f1d17c850db0f591e3d349d417b40a Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 25 Feb 2014 20:30:33 +0100 Subject: brcmfmac: Small cleanup of redundant code. In time some of the code got redundant, without being noticed. This patch does not change any functionality, just removes redundant code. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 04f5e32cfb48..ac61419dc998 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -770,8 +770,6 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) return err; } -#define PKT_AVAILABLE() (intstatus & I_HMB_FRAME_IND) - #define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE) /* Turn backplane clock on or off */ @@ -870,7 +868,6 @@ static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok) } #endif /* defined (DEBUG) */ - bus->activity = true; } else { clkreq = 0; @@ -2341,7 +2338,7 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) cnt += i; /* In poll mode, need to check for other events */ - if (!bus->intr && cnt) { + if (!bus->intr) { /* Check device status, signal pending interrupt */ sdio_claim_host(bus->sdiodev->func[1]); ret = r_sdreg32(bus, &intstatus, @@ -2485,9 +2482,8 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) { 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 */ + uint framecnt; /* Temporary counter of tx/rx frames */ int err = 0, n; brcmf_dbg(TRACE, "Enter\n"); @@ -2585,11 +2581,10 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) intstatus &= ~I_HMB_FRAME_IND; /* On frame indication, read available frames */ - if (PKT_AVAILABLE() && bus->clkstate == CLK_AVAIL) { - framecnt = brcmf_sdio_readframes(bus, rxlimit); + if ((intstatus & I_HMB_FRAME_IND) && (bus->clkstate == CLK_AVAIL)) { + brcmf_sdio_readframes(bus, bus->rxbound); if (!bus->rxpending) intstatus &= ~I_HMB_FRAME_IND; - rxlimit -= min(framecnt, rxlimit); } /* Keep still-pending events for next scheduling */ @@ -2647,8 +2642,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) && data_ok(bus)) { framecnt = bus->rxpending ? min(txlimit, bus->txminmax) : txlimit; - framecnt = brcmf_sdio_sendfromq(bus, framecnt); - txlimit -= framecnt; + brcmf_sdio_sendfromq(bus, framecnt); } if (!brcmf_bus_ready(bus->sdiodev->bus_if) || (err != 0)) { @@ -2658,7 +2652,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) atomic_read(&bus->ipend) > 0 || (!atomic_read(&bus->fcstate) && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && - data_ok(bus)) || PKT_AVAILABLE()) { + data_ok(bus))) { atomic_inc(&bus->dpc_tskcnt); } } -- cgit v1.2.3 From c98db0bec72ac7ef127119c1ed962d6f56802b12 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 26 Feb 2014 13:32:32 +0100 Subject: brcmfmac: Use atomic functions for intstatus update. The intstatus in sdio code can be updated from different threads. To protect intstatus access, atomic functions are used. The loop was replaced using atomic_set_mask(). Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index ac61419dc998..0ccb7affeb04 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2449,7 +2449,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) struct brcmf_core *buscore; u32 addr; unsigned long val; - int n, ret; + int ret; buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus); @@ -2457,7 +2457,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret); bus->sdcnt.f1regdata++; if (ret != 0) - val = 0; + return ret; val &= bus->hostintmask; atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE)); @@ -2466,13 +2466,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) if (val) { brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret); 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); + atomic_set_mask(val, &bus->intstatus); } return ret; @@ -2484,7 +2478,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) unsigned long intstatus; uint txlimit = bus->txbound; /* Tx frames to send before resched */ uint framecnt; /* Temporary counter of tx/rx frames */ - int err = 0, n; + int err = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -2588,10 +2582,8 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) } /* Keep still-pending events for next scheduling */ - if (intstatus) { - for_each_set_bit(n, &intstatus, 32) - set_bit(n, (unsigned long *)&bus->intstatus.counter); - } + if (intstatus) + atomic_set_mask(intstatus, &bus->intstatus); brcmf_sdio_clrintr(bus); -- cgit v1.2.3 From 81c7883c46fddd53b7a98c3659ffae21189ae4ab Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 25 Feb 2014 20:30:35 +0100 Subject: brcmfmac: Put frame sdio tx error handling in sub function. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 112 ++++++--------------- 1 file changed, 33 insertions(+), 79 deletions(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 0ccb7affeb04..c894ee358153 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -1237,6 +1237,28 @@ static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) 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, SDIO_FUNC_2); + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL); + bus->sdcnt.f1regdata++; + + for (i = 0; i < 3; i++) { + hi = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL); + lo = brcmf_sdiod_regrb(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) { @@ -2252,7 +2274,6 @@ static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq, uint chan) { int ret; - int i; struct sk_buff *pkt_next, *tmp; brcmf_dbg(TRACE, "Enter\n"); @@ -2265,28 +2286,9 @@ static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq, ret = brcmf_sdiod_send_pkt(bus->sdiodev, pktq); bus->sdcnt.f2txdata++; - if (ret < 0) { - /* On failure, abort the command and terminate the frame */ - brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", - ret); - bus->sdcnt.tx_sderrs++; + if (ret < 0) + brcmf_sdio_txfail(bus); - brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, NULL); - bus->sdcnt.f1regdata++; - - for (i = 0; i < 3; i++) { - u8 hi, lo; - hi = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCHI, NULL); - lo = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCLO, NULL); - bus->sdcnt.f1regdata += 2; - if ((hi == 0) && (lo == 0)) - break; - } - } sdio_release_host(bus->sdiodev->func[1]); done: @@ -2588,42 +2590,17 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) brcmf_sdio_clrintr(bus); if (data_ok(bus) && bus->ctrl_frame_stat && - (bus->clkstate == CLK_AVAIL)) { - int i; + (bus->clkstate == CLK_AVAIL)) { sdio_claim_host(bus->sdiodev->func[1]); err = brcmf_sdiod_send_buf(bus->sdiodev, bus->ctrl_frame_buf, (u32)bus->ctrl_frame_len); - if (err < 0) { - /* On failure, abort the command and - terminate the frame */ - brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", - err); - bus->sdcnt.tx_sderrs++; - - brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); - - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, &err); - bus->sdcnt.f1regdata++; - - for (i = 0; i < 3; i++) { - u8 hi, lo; - hi = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCHI, - &err); - lo = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCLO, - &err); - bus->sdcnt.f1regdata += 2; - if ((hi == 0) && (lo == 0)) - break; - } - - } else { + if (err < 0) + brcmf_sdio_txfail(bus); + else bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; - } + sdio_release_host(bus->sdiodev->func[1]); bus->ctrl_frame_stat = false; brcmf_sdio_wait_event_wakeup(bus); @@ -2793,38 +2770,15 @@ break2: static int brcmf_sdio_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len) { - int i; int ret; bus->ctrl_frame_stat = false; ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len); - if (ret < 0) { - /* On failure, abort the command and terminate the frame */ - brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", - ret); - bus->sdcnt.tx_sderrs++; - - brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); - - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, NULL); - bus->sdcnt.f1regdata++; - - for (i = 0; i < 3; i++) { - u8 hi, lo; - hi = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCHI, NULL); - lo = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCLO, NULL); - bus->sdcnt.f1regdata += 2; - if (hi == 0 && lo == 0) - break; - } - return ret; - } - - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; + if (ret < 0) + brcmf_sdio_txfail(bus); + else + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; return ret; } -- cgit v1.2.3 From af1fa210f4fc6e304b859b386a3c8a266b1110ab Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 27 Feb 2014 19:25:00 +0100 Subject: brcmfmac: use pre-allocated scatter-gather table for txglomming Instead of allocating a scatter-gather table for every transmit reuse a pre-allocated table. The transmit path will be faster by taking out this allocation. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index c894ee358153..b5ded8a57cb0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -113,8 +113,6 @@ struct rte_console { #define BRCMF_TXBOUND 20 /* Default for max tx frames in one scheduling */ -#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */ - #define BRCMF_TXMINMAX 1 /* Max tx frames if rx still pending */ #define MEMBLOCK 2048 /* Block size used for downloading @@ -511,10 +509,6 @@ static const uint max_roundup = 512; #define ALIGNMENT 4 -static int brcmf_sdio_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE; -module_param_named(txglomsz, brcmf_sdio_txglomsz, int, 0); -MODULE_PARM_DESC(txglomsz, "maximum tx packet chain size [SDIO]"); - enum brcmf_sdio_frmtype { BRCMF_SDIO_FT_NORMAL, BRCMF_SDIO_FT_SUPER, @@ -2321,7 +2315,7 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) __skb_queue_head_init(&pktq); if (bus->txglom) pkt_num = min_t(u8, bus->tx_max - bus->tx_seq, - brcmf_sdio_txglomsz); + bus->sdiodev->txglomsz); pkt_num = min_t(u32, pkt_num, brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)); spin_lock_bh(&bus->txqlock); -- cgit v1.2.3 From 561e722201e41e304936b8a2aaa282c46ad4f393 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 6 Mar 2014 10:16:11 +0100 Subject: Revert "brcmfmac: Use atomic functions for intstatus update." This reverts commit c98db0bec72ac7ef127119c1ed962d6f56802b12. The function atomic_set_mask() is not architecture independent so it can not be used in the driver as is. Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 5c2706e50775..70bab5e089eb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2444,7 +2444,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) struct brcmf_core *buscore; u32 addr; unsigned long val; - int ret; + int n, ret; buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus); @@ -2452,7 +2452,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret); bus->sdcnt.f1regdata++; if (ret != 0) - return ret; + val = 0; val &= bus->hostintmask; atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE)); @@ -2461,7 +2461,13 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) if (val) { brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret); bus->sdcnt.f1regdata++; - atomic_set_mask(val, &bus->intstatus); + } + + 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; @@ -2473,7 +2479,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) unsigned long intstatus; uint txlimit = bus->txbound; /* Tx frames to send before resched */ uint framecnt; /* Temporary counter of tx/rx frames */ - int err = 0; + int err = 0, n; brcmf_dbg(TRACE, "Enter\n"); @@ -2577,8 +2583,10 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) } /* Keep still-pending events for next scheduling */ - if (intstatus) - atomic_set_mask(intstatus, &bus->intstatus); + if (intstatus) { + for_each_set_bit(n, &intstatus, 32) + set_bit(n, (unsigned long *)&bus->intstatus.counter); + } brcmf_sdio_clrintr(bus); -- cgit v1.2.3 From 5cbb9c285bdcc14ee381120dab7e23a81060f1c0 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 6 Mar 2014 10:16:12 +0100 Subject: brcmfmac: Use atomic functions for intstatus update. The intstatus in sdio code can be updated from different threads. To protect intstatus access, atomic functions are used. One of them is set_bit, but this function is not guaranteed atomic on all platforms. The loop was replaced with local created OR function. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 29 +++++++++++----------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 70bab5e089eb..a111b6fbbeba 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2439,12 +2439,21 @@ static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus) } } +static void atomic_orr(int val, atomic_t *v) +{ + int old_val; + + old_val = atomic_read(v); + while (atomic_cmpxchg(v, old_val, val | old_val) != old_val) + old_val = atomic_read(v); +} + static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) { struct brcmf_core *buscore; u32 addr; unsigned long val; - int n, ret; + int ret; buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus); @@ -2452,7 +2461,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret); bus->sdcnt.f1regdata++; if (ret != 0) - val = 0; + return ret; val &= bus->hostintmask; atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE)); @@ -2461,13 +2470,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) if (val) { brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret); 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); + atomic_orr(val, &bus->intstatus); } return ret; @@ -2479,7 +2482,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) unsigned long intstatus; uint txlimit = bus->txbound; /* Tx frames to send before resched */ uint framecnt; /* Temporary counter of tx/rx frames */ - int err = 0, n; + int err = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -2583,10 +2586,8 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) } /* Keep still-pending events for next scheduling */ - if (intstatus) { - for_each_set_bit(n, &intstatus, 32) - set_bit(n, (unsigned long *)&bus->intstatus.counter); - } + if (intstatus) + atomic_orr(intstatus, &bus->intstatus); brcmf_sdio_clrintr(bus); -- cgit v1.2.3 From a797ca1eadeef7f4fdba2ab5d143d56cc3ec5da3 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Sat, 15 Mar 2014 17:18:17 +0100 Subject: brcmfmac: add BCM4354 SDIO interface support BCM4354 is an a/b/g/n/ac 2x2 WiFi chip. This patch adds support for it through SDIO interface. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Arend Van Spriel Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index a111b6fbbeba..4aa8678590d5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -578,6 +578,8 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { #define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt" #define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin" #define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt" +#define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin" +#define BCM4354_NVRAM_NAME "brcm/brcmfmac4354-sdio.txt" MODULE_FIRMWARE(BCM43143_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43143_NVRAM_NAME); @@ -597,6 +599,8 @@ MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43362_NVRAM_NAME); MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4339_NVRAM_NAME); +MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4354_NVRAM_NAME); struct brcmf_firmware_names { u32 chipid; @@ -622,7 +626,8 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BCM4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, { BCM4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, { BCM43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, - { BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) } + { BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, + { BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } }; -- cgit v1.2.3 From fed7ec44e7ef647c1b1584164fe172963731f26d Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 15 Mar 2014 17:18:18 +0100 Subject: brcmfmac: Protect tx seq number for data and control SDIO tx uses a sequence number which is common for data and control. This requires that access to this sequence number is protected. A mutex was used to achieve this, but it also required the reordering of code for tx control. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 214 +++++++++++---------- 1 file changed, 108 insertions(+), 106 deletions(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 4aa8678590d5..bcdaf72389f3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -458,10 +458,11 @@ struct brcmf_sdio { bool alp_only; /* Don't use HT clock (ALP only) */ u8 *ctrl_frame_buf; - u32 ctrl_frame_len; + u16 ctrl_frame_len; bool ctrl_frame_stat; - spinlock_t txqlock; + spinlock_t txq_lock; /* protect bus->txq */ + struct semaphore tx_seq_lock; /* protect bus->tx_seq */ wait_queue_head_t ctrl_wait; wait_queue_head_t dcmd_resp_wait; @@ -2316,13 +2317,15 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) /* Send frames until the limit or some other event */ for (cnt = 0; (cnt < maxframes) && data_ok(bus);) { pkt_num = 1; - __skb_queue_head_init(&pktq); + if (down_interruptible(&bus->tx_seq_lock)) + return cnt; 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)); - spin_lock_bh(&bus->txqlock); + __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); @@ -2330,11 +2333,15 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) break; __skb_queue_tail(&pktq, pkt); } - spin_unlock_bh(&bus->txqlock); - if (i == 0) + spin_unlock_bh(&bus->txq_lock); + if (i == 0) { + up(&bus->tx_seq_lock); break; + } ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL); + up(&bus->tx_seq_lock); + cnt += i; /* In poll mode, need to check for other events */ @@ -2363,6 +2370,68 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) return cnt; } +static int brcmf_sdio_tx_ctrlframe(struct brcmf_sdio *bus, u8 *frame, u16 len) +{ + u8 doff; + u16 pad; + uint retries = 0; + struct brcmf_sdio_hdrinfo hd_info = {0}; + int ret; + + brcmf_dbg(TRACE, "Enter\n"); + + /* Back the pointer to make room for bus header */ + frame -= bus->tx_hdrlen; + len += bus->tx_hdrlen; + + /* Add alignment padding (optional for ctl frames) */ + doff = ((unsigned long)frame % bus->head_align); + if (doff) { + frame -= doff; + len += doff; + memset(frame + bus->tx_hdrlen, 0, doff); + } + + /* Round send length to next SDIO block */ + pad = 0; + if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { + pad = bus->blocksize - (len % bus->blocksize); + if ((pad > bus->roundup) || (pad >= bus->blocksize)) + pad = 0; + } else if (len % bus->head_align) { + pad = bus->head_align - (len % bus->head_align); + } + len += pad; + + hd_info.len = len - pad; + hd_info.channel = SDPCM_CONTROL_CHANNEL; + hd_info.dat_offset = doff + bus->tx_hdrlen; + hd_info.seq_num = bus->tx_seq; + hd_info.lastfrm = true; + hd_info.tail_pad = pad; + brcmf_sdio_hdpack(bus, frame, &hd_info); + + if (bus->txglom) + brcmf_sdio_update_hwhdr(frame, len); + + brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), + frame, len, "Tx Frame:\n"); + brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) && + BRCMF_HDRS_ON(), + frame, min_t(u16, len, 16), "TxHdr:\n"); + + do { + ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len); + + if (ret < 0) + brcmf_sdio_txfail(bus); + else + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; + } while (ret < 0 && retries++ < TXRETRIES); + + return ret; +} + static void brcmf_sdio_bus_stop(struct device *dev) { u32 local_hostintmask; @@ -2596,26 +2665,23 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) brcmf_sdio_clrintr(bus); - if (data_ok(bus) && bus->ctrl_frame_stat && - (bus->clkstate == CLK_AVAIL)) { - - sdio_claim_host(bus->sdiodev->func[1]); - err = brcmf_sdiod_send_buf(bus->sdiodev, bus->ctrl_frame_buf, - (u32)bus->ctrl_frame_len); - - if (err < 0) - brcmf_sdio_txfail(bus); - else - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; + if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) && + (down_interruptible(&bus->tx_seq_lock) == 0)) { + if (data_ok(bus)) { + sdio_claim_host(bus->sdiodev->func[1]); + err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, + bus->ctrl_frame_len); + sdio_release_host(bus->sdiodev->func[1]); - sdio_release_host(bus->sdiodev->func[1]); - bus->ctrl_frame_stat = false; - brcmf_sdio_wait_event_wakeup(bus); + bus->ctrl_frame_stat = false; + brcmf_sdio_wait_event_wakeup(bus); + } + up(&bus->tx_seq_lock); } /* Send queued frames (limit 1 if rx may still be pending) */ - else if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && - brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit - && data_ok(bus)) { + 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); @@ -2649,7 +2715,6 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; - ulong flags; brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len); @@ -2665,7 +2730,7 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) bus->sdcnt.fcqueued++; /* Priority based enq */ - spin_lock_irqsave(&bus->txqlock, flags); + spin_lock_bh(&bus->txq_lock); /* reset bus_flags in packet cb */ *(u16 *)(pkt->cb) = 0; if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) { @@ -2680,7 +2745,7 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) bus->txoff = true; brcmf_txflowblock(bus->sdiodev->dev, true); } - spin_unlock_irqrestore(&bus->txqlock, flags); + spin_unlock_bh(&bus->txq_lock); #ifdef DEBUG if (pktq_plen(&bus->txq, prec) > qcount[prec]) @@ -2775,87 +2840,27 @@ break2: } #endif /* DEBUG */ -static int brcmf_sdio_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len) -{ - int ret; - - bus->ctrl_frame_stat = false; - ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len); - - if (ret < 0) - brcmf_sdio_txfail(bus); - else - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; - - return ret; -} - static int brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) { - u8 *frame; - u16 len, pad; - uint retries = 0; - u8 doff = 0; - int ret = -1; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; - struct brcmf_sdio_hdrinfo hd_info = {0}; + int ret = -1; brcmf_dbg(TRACE, "Enter\n"); - /* Back the pointer to make a room for bus header */ - frame = msg - bus->tx_hdrlen; - len = (msglen += bus->tx_hdrlen); - - /* Add alignment padding (optional for ctl frames) */ - doff = ((unsigned long)frame % bus->head_align); - if (doff) { - frame -= doff; - len += doff; - msglen += doff; - memset(frame, 0, doff + bus->tx_hdrlen); - } - /* precondition: doff < bus->head_align */ - doff += bus->tx_hdrlen; - - /* Round send length to next SDIO block */ - pad = 0; - if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { - pad = bus->blocksize - (len % bus->blocksize); - if ((pad > bus->roundup) || (pad >= bus->blocksize)) - pad = 0; - } else if (len % bus->head_align) { - pad = bus->head_align - (len % bus->head_align); - } - len += pad; - - /* precondition: IS_ALIGNED((unsigned long)frame, 2) */ - - /* Make sure backplane clock is on */ - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_bus_sleep(bus, false, false); - sdio_release_host(bus->sdiodev->func[1]); - - hd_info.len = (u16)msglen; - hd_info.channel = SDPCM_CONTROL_CHANNEL; - hd_info.dat_offset = doff; - hd_info.seq_num = bus->tx_seq; - hd_info.lastfrm = true; - hd_info.tail_pad = pad; - brcmf_sdio_hdpack(bus, frame, &hd_info); - - if (bus->txglom) - brcmf_sdio_update_hwhdr(frame, len); + if (down_interruptible(&bus->tx_seq_lock)) + return -EINTR; if (!data_ok(bus)) { brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n", bus->tx_max, bus->tx_seq); - bus->ctrl_frame_stat = true; + up(&bus->tx_seq_lock); /* Send from dpc */ - bus->ctrl_frame_buf = frame; - bus->ctrl_frame_len = len; + bus->ctrl_frame_buf = msg; + bus->ctrl_frame_len = msglen; + bus->ctrl_frame_stat = true; wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat, @@ -2866,22 +2871,18 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) ret = 0; } else { brcmf_dbg(SDIO, "ctrl_frame_stat == true\n"); + bus->ctrl_frame_stat = false; + if (down_interruptible(&bus->tx_seq_lock)) + return -EINTR; ret = -1; } } - if (ret == -1) { - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), - frame, len, "Tx Frame:\n"); - brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) && - BRCMF_HDRS_ON(), - frame, min_t(u16, len, 16), "TxHdr:\n"); - - do { - sdio_claim_host(bus->sdiodev->func[1]); - ret = brcmf_sdio_tx_frame(bus, frame, len); - sdio_release_host(bus->sdiodev->func[1]); - } while (ret < 0 && retries++ < TXRETRIES); + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_bus_sleep(bus, false, false); + ret = brcmf_sdio_tx_ctrlframe(bus, msg, msglen); + sdio_release_host(bus->sdiodev->func[1]); + up(&bus->tx_seq_lock); } if (ret) @@ -4052,7 +4053,8 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) } spin_lock_init(&bus->rxctl_lock); - spin_lock_init(&bus->txqlock); + spin_lock_init(&bus->txq_lock); + sema_init(&bus->tx_seq_lock, 1); init_waitqueue_head(&bus->ctrl_wait); init_waitqueue_head(&bus->dcmd_resp_wait); -- cgit v1.2.3 From 8a385ba542dd6d20e3067d486f5f75ff71baa4db Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 15 Mar 2014 17:18:20 +0100 Subject: brcmfmac: assure active clock request upon entering SLEEP state When the SDIO driver goes in low power state it must assure that a clock request in ChipCLKCSR is set. Otherwise waking up the device can fail. The register is read and if necessary the ALP clock will be requested. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 39 ++++++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index bcdaf72389f3..859eddd526ef 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -175,6 +175,7 @@ struct rte_console { #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) @@ -720,16 +721,12 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) int err = 0; int try_cnt = 0; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(TRACE, "Enter: on=%d\n", on); wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); /* 1st KSO write goes to AOS wake up core if device is asleep */ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err); - if (err) { - brcmf_err("SDIO_AOS KSO write error: %d\n", err); - return err; - } if (on) { /* device WAKEUP through KSO: @@ -759,13 +756,19 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) &err); if (((rd_val & bmask) == cmp_val) && !err) break; - brcmf_dbg(SDIO, "KSO wr/rd retry:%d (max: %d) ERR:%x\n", - try_cnt, MAX_KSO_ATTEMPTS, err); + udelay(KSO_WAIT_US); brcmf_sdiod_regwb(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); + return err; } @@ -966,6 +969,7 @@ 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"), @@ -984,8 +988,20 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) atomic_read(&bus->ipend) > 0 || (!atomic_read(&bus->fcstate) && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && - data_ok(bus))) - return -EBUSY; + data_ok(bus))) { + err = -EBUSY; + goto done; + } + + clkcsr = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, + &err); + if ((clkcsr & SBSDIO_CSR_MASK) == 0) { + brcmf_dbg(SDIO, "no clock, set ALP\n"); + brcmf_sdiod_regwb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, + SBSDIO_ALP_AVAIL_REQ, &err); + } err = brcmf_sdio_kso_control(bus, false); /* disable watchdog */ if (!err) @@ -1002,7 +1018,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) } else { brcmf_err("error while changing bus sleep state %d\n", err); - return err; + goto done; } } @@ -1014,7 +1030,8 @@ end: } else { brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); } - +done: + brcmf_dbg(SDIO, "Exit: err=%d\n", err); return err; } -- cgit v1.2.3 From 58e9df462fd70a1862378beb46b312f1f6bca94f Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 20 Mar 2014 10:18:01 +0100 Subject: brcmfmac: reinit watchdog completion after handling watchdog The watchdog thread waits on completion that is set from a timer. As the completion is count based this could mean that on a busy system the watchdog is handled multiple times with a very short interval. This is not the intended behaviour. After handling the watchdog it should wait for the next timer expiry. This is accomplished by reinitializing the completion. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 859eddd526ef..47a6f3957b7f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -3994,6 +3994,7 @@ brcmf_sdio_watchdog_thread(void *data) brcmf_sdio_bus_watchdog(bus); /* Count the tick for reference */ bus->sdcnt.tickcnt++; + reinit_completion(&bus->watchdog_wait); } else break; } -- cgit v1.2.3