From 93ce941adf0f74e6704335c994629db072da3045 Mon Sep 17 00:00:00 2001 From: ashish kumar Date: Fri, 10 Mar 2023 04:51:34 -0600 Subject: [PATCH 210/296] brcmfmac: Support idleclock feature for 43012/43022 chip Modified and taken from: http://gerrit-iot.aus.cypress.com:8081/#/c/14941 This supports switching OFF SD clock during Sleep state to avoid back powering of chip for VDDIO current measurement Wakeup - sleep Sequence: Set SD CLOCK EN bit 2 in CLOCK CONTROL Register 0x2C in HOST Controller Set KSO Bit in CSR Register Set 4 bit mode in BUS INTERFACE CONTROL and HOST Control register Process the interrupt or packet Set 1 bit mode in BUS INTERFACE CONTROL and HOST Control register KSO CLEAR Clear SD CLOCK EN bit 2 in CLOCK CONTROL Register 0x2C in HOST Controller NOTE: 1 bit mode is set during sleep for supporting interrupt processing without SD clock Old Jira ref: Current Jira: Usage: insmod brcmfmac.ko sdio_idleclk_disable=1 ==> Enables this feature insmod brcmfmac.ko ==> disables this feature by default. Related mmc: https://gitlab.intra.infineon.com/linux/brcm80211/-/merge_requests/258/ Signed-off-by: Ashish Kumar Signed-off-by: Shelley Yang --- .../broadcom/brcm80211/brcmfmac/common.c | 7 ++ .../broadcom/brcm80211/brcmfmac/common.h | 18 ++++ .../broadcom/brcm80211/brcmfmac/sdio.c | 86 +++++++++++++++++++ 3 files changed, 111 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index 98fc6500c048..7225978788f3 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -132,6 +132,10 @@ static int brcmf_bt_over_sdio; module_param_named(bt_over_sdio, brcmf_bt_over_sdio, int, 0); MODULE_PARM_DESC(bt_over_sdio, "Enable BT over SDIO"); +static int brcmf_sdio_idleclk_disable = BRCMFMAC_AUTO; +module_param_named(sdio_idleclk_disable, brcmf_sdio_idleclk_disable, int, 0644); +MODULE_PARM_DESC(sdio_idleclk_disable, "Disable SDIO idle clock"); + static struct brcmfmac_platform_data *brcmfmac_pdata; struct brcmf_mp_global_t brcmf_mp_global; @@ -818,6 +822,9 @@ struct brcmf_mp_device *brcmf_get_module_param(struct device *dev, settings->bt_over_sdio = !!brcmf_bt_over_sdio; brcmf_dbg(INFO, "bt_over_sdio: %d\n", settings->bt_over_sdio); + settings->idleclk_disable = brcmf_sdio_idleclk_disable; + brcmf_dbg(INFO, "idleclk_disable: %d\n", settings->idleclk_disable); + if (bus_type == BRCMF_BUSTYPE_SDIO) { settings->bus.sdio.txglomsz = brcmf_sdiod_txglomsz; brcmf_dbg(INFO, "txglomsz: %d\n", settings->bus.sdio.txglomsz); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h index c67cf760175f..eab720625ef8 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h @@ -11,6 +11,22 @@ #define BRCMF_FW_ALTPATH_LEN 256 +#define BRCMFMAC_DISABLE 0 +#define BRCMFMAC_ENABLE 1 +#define BRCMFMAC_AUTO 2 + +/* Keeping these macro definition here because these are defined in mmc drivers. + * So for 3rd party mmc, fmac build should not fail due to build error. + */ + +/* SDIO IDLECLOCK Support - reusing pm_caps */ +#ifndef SDIO_IDLECLOCK_DIS +#define SDIO_IDLECLOCK_DIS BIT(2) /* Start SDClock */ +#define SDIO_IDLECLOCK_EN BIT(3) /* Stop SDClock */ +#define SDIO_SDMODE_1BIT BIT(4) /* Set 1-bit Bus mode */ +#define SDIO_SDMODE_4BIT BIT(5) /* Set 4-bit Bus mode */ +#endif /* !SDIO_IDLECLOCK_DIS */ + /* Definitions for the module global and device specific settings are defined * here. Two structs are used for them. brcmf_mp_global_t and brcmf_mp_device. * The mp_global is instantiated once in a global struct and gets initialized @@ -49,6 +65,7 @@ extern struct brcmf_mp_global_t brcmf_mp_global; * @country_codes: If available, pointer to struct for translating country codes * @bus: Bus specific platform data. Only SDIO at the mmoment. * @pkt_prio: Support customer dscp to WMM up mapping. + * @idleclk_disable: SDIO bus clock output disable when bus is idle. */ struct brcmf_mp_device { bool p2p_enable; @@ -77,6 +94,7 @@ struct brcmf_mp_device { struct brcmfmac_sdio_pd sdio; } bus; bool pkt_prio; + int idleclk_disable; }; /** diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index e84ae4e8c378..fcaf8eadecce 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -340,6 +341,7 @@ struct rte_console { #define BRCMF_IDLE_ACTIVE 0 /* Do not request any SD clock change * when idle */ +#define BRCMF_IDLE_STOP (-1) /* Request SD clock be stopped */ #define BRCMF_IDLE_INTERVAL 1 #define KSO_WAIT_US 50 @@ -986,11 +988,64 @@ static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok) return 0; } +/** + * brcmf_sdio_set_sdbus_clk_width - set SD clock enable/disable and sd_mode + * @func: SDIO function attached to host + * @flags: reusing existing mmc->pm_flags to pass idle clk disable/enable or + * change sdbus width through mmc. + */ +int brcmf_sdio_set_sdbus_clk_width(struct brcmf_sdio *bus, unsigned int flags) +{ + struct mmc_host *host; + u8 ctrl; + int ret = 0; + + if (WARN_ON(!bus)) + return -EINVAL; + + host = bus->sdiodev->func1->card->host; + + brcmf_dbg(SDIO, "Enter\n"); + + if (flags == SDIO_IDLECLOCK_DIS || flags == SDIO_IDLECLOCK_EN) { + /* Switch OFF/ON SD CLOCK in sdio Host Controller */ + host->pm_caps |= flags; + /* Call SDHCI interface function from ops */ + host->ops->set_ios(host, &host->ios); + } else if (flags == SDIO_SDMODE_1BIT || flags == SDIO_SDMODE_4BIT) { + ctrl = brcmf_sdiod_func0_rb(bus->sdiodev, SDIO_CCCR_IF, &ret); + /* Check for Error */ + if (ret) + return ret; + + /* Clear first two bits + * 00 - 1 bit wide + * 10 - 4 bit wide + */ + ctrl &= ~SDIO_BUS_WIDTH_MASK; + /* set as 4-bit bus width */ + if (flags == SDIO_SDMODE_4BIT) + ctrl |= SDIO_BUS_WIDTH_4BIT; + + brcmf_sdiod_func0_wb(bus->sdiodev, SDIO_CCCR_IF, ctrl, &ret); + /* Update HOST CTRL register with 1 bit or 4 bit mode */ + host->pm_caps |= flags; + /* Call SDHCI interface function from ops */ + host->ops->set_ios(host, &host->ios); + } + return ret; +} + /* Change idle/active SD state */ static int brcmf_sdio_sdclk(struct brcmf_sdio *bus, bool on) { brcmf_dbg(SDIO, "Enter\n"); + if (bus->idleclock == BRCMF_IDLE_STOP) + brcmf_sdio_set_sdbus_clk_width(bus, (on ? + SDIO_IDLECLOCK_DIS : + SDIO_IDLECLOCK_EN)); + if (on) bus->clkstate = CLK_SDONLY; else @@ -1124,9 +1179,24 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) SBSDIO_FUNC1_CHIPCLKCSR, SBSDIO_ALP_AVAIL_REQ, &err); } + + if (bus->idleclock == BRCMF_IDLE_STOP) + brcmf_sdio_set_sdbus_clk_width(bus, + SDIO_SDMODE_1BIT); + err = brcmf_sdio_kso_control(bus, false); + + if (bus->idleclock == BRCMF_IDLE_STOP) + brcmf_sdio_sdclk(bus, false); } else { + if (bus->idleclock == BRCMF_IDLE_STOP && bus->clkstate == CLK_NONE) + brcmf_sdio_clkctl(bus, CLK_SDONLY, false); + err = brcmf_sdio_kso_control(bus, true); + + if (bus->idleclock == BRCMF_IDLE_STOP) + brcmf_sdio_set_sdbus_clk_width(bus, + SDIO_SDMODE_4BIT); } if (err) { brcmf_err("error while changing bus sleep state %d\n", @@ -5191,6 +5261,22 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, bus->sdiodev->fmac_ulp.ulp_state = FMAC_ULP_IDLE; } + if (sdiod->settings->idleclk_disable == BRCMFMAC_DISABLE) { + bus->idleclock = BRCMF_IDLE_ACTIVE; + } else if (sdiod->settings->idleclk_disable == BRCMFMAC_ENABLE) { + bus->idleclock = BRCMF_IDLE_STOP; + } else if (sdiod->settings->idleclk_disable == BRCMFMAC_AUTO) { + if (sdiod->func1->device == SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012 || + sdiod->func1->device == SDIO_DEVICE_ID_BROADCOM_CYPRESS_43022 || + sdiod->func1->device == SDIO_DEVICE_ID_CYPRESS_43022) { + bus->idleclock = BRCMF_IDLE_STOP; + } else { + bus->idleclock = BRCMF_IDLE_ACTIVE; + } + } else { + brcmf_err("unexpected idleclk_disable%d\n", sdiod->settings->idleclk_disable); + } + /* ready */ return; -- 2.25.1