diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 3e26e232ced36f2b6363b2593df2932b47571b13..6d3893eafe92a18f4a416c9fb312df7b296417f3 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -1980,7 +1980,8 @@ config MSM_LPASS_8960 config MSM_WCNSS_SSR_8960 tristate "MSM 8960 WCNSS restart module" - depends on (ARCH_MSM8960) + depends on ARCH_MSM8960 + depends on WCNSS_CORE help This option enables the WCNSS restart module for MSM8960, which monitors WCNSS hardware watchdog interrupt lines and plugs WCNSS diff --git a/arch/arm/mach-msm/wcnss-ssr-8960.c b/arch/arm/mach-msm/wcnss-ssr-8960.c index 7e4bdb9827a5ec3f5191cecb2d3644ccee40943d..f569de4b44464c39141e56a3fc8ff9e5b1a2e015 100644 --- a/arch/arm/mach-msm/wcnss-ssr-8960.c +++ b/arch/arm/mach-msm/wcnss-ssr-8960.c @@ -31,12 +31,6 @@ #define MODULE_NAME "wcnss_8960" #define MAX_BUF_SIZE 0x51 -static void __iomem *msm_riva_base; -#define MSM_RIVA_PHYS 0x03204000 -#define RIVA_SSR_OUT (msm_riva_base + 0x0b4) -#define RIVA_SSR_BIT BIT(23) - - static struct delayed_work cancel_vote_work; static void *riva_ramdump_dev; static int riva_crash; @@ -147,20 +141,9 @@ static int riva_powerup(const struct subsys_desc *subsys) struct platform_device *pdev = wcnss_get_platform_device(); struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config(); int ret = -1; - u32 reg = 0; - - msm_riva_base = ioremap(MSM_RIVA_PHYS, SZ_256); - if (!msm_riva_base) { - pr_err("ioremap MSM_RIVA_PHYS failed\n"); - goto poweron; - } - reg = readl_relaxed(RIVA_SSR_OUT); - reg |= RIVA_SSR_BIT; - writel_relaxed(reg, RIVA_SSR_OUT); - iounmap(msm_riva_base); + wcnss_ssr_boot_notify(); -poweron: if (pdev && pwlanconfig) ret = wcnss_wlan_power(&pdev->dev, pwlanconfig, WCNSS_WLAN_SWITCH_ON); diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c index c0a4e0e13a56468185cb6e5e47fd1c9744c96180..48620bea487c44bda3d854ddd37c00e84a326827 100644 --- a/drivers/net/wireless/wcnss/wcnss_wlan.c +++ b/drivers/net/wireless/wcnss/wcnss_wlan.c @@ -11,6 +11,7 @@ */ #include <linux/module.h> +#include <linux/io.h> #include <linux/slab.h> #include <linux/err.h> #include <linux/platform_device.h> @@ -34,6 +35,14 @@ static int has_48mhz_xo = WCNSS_CONFIG_UNSPECIFIED; module_param(has_48mhz_xo, int, S_IWUSR | S_IRUGO); MODULE_PARM_DESC(has_48mhz_xo, "Is an external 48 MHz XO present"); +static DEFINE_SPINLOCK(reg_spinlock); + +#define MSM_RIVA_PHYS 0x03204000 + +#define RIVA_SPARE_OFFSET 0x0b4 +#define RIVA_SSR_BIT BIT(23) +#define RIVA_SUSPEND_BIT BIT(24) + static struct { struct platform_device *pdev; void *pil; @@ -50,6 +59,7 @@ static struct { struct wcnss_wlan_config wlan_config; struct delayed_work wcnss_work; struct wake_lock wcnss_wake_lock; + void __iomem *msm_wcnss_base; } *penv = NULL; static ssize_t wcnss_serial_number_show(struct device *dev, @@ -310,21 +320,99 @@ unsigned int wcnss_get_serial_number(void) } EXPORT_SYMBOL(wcnss_get_serial_number); +void wcnss_ssr_boot_notify(void) +{ + void __iomem *pmu_spare_reg; + u32 reg = 0; + unsigned long flags; + + pmu_spare_reg = penv->msm_wcnss_base + RIVA_SPARE_OFFSET; + + spin_lock_irqsave(®_spinlock, flags); + reg = readl_relaxed(pmu_spare_reg); + reg |= RIVA_SSR_BIT; + writel_relaxed(reg, pmu_spare_reg); + spin_unlock_irqrestore(®_spinlock, flags); +} +EXPORT_SYMBOL(wcnss_ssr_boot_notify); + +static int enable_wcnss_suspend_notify; + +static int enable_wcnss_suspend_notify_set(const char *val, + struct kernel_param *kp) +{ + int ret; + + ret = param_set_int(val, kp); + if (ret) + return ret; + + if (enable_wcnss_suspend_notify) + pr_info("Suspend notification activated for wcnss\n"); + + return 0; +} +module_param_call(enable_wcnss_suspend_notify, enable_wcnss_suspend_notify_set, + param_get_int, &enable_wcnss_suspend_notify, S_IRUGO | S_IWUSR); + +static void wcnss_suspend_notify(void) +{ + void __iomem *pmu_spare_reg; + u32 reg = 0; + unsigned long flags; + + /* For Riva */ + pmu_spare_reg = penv->msm_wcnss_base + RIVA_SPARE_OFFSET; + + spin_lock_irqsave(®_spinlock, flags); + reg = readl_relaxed(pmu_spare_reg); + reg |= RIVA_SUSPEND_BIT; + writel_relaxed(reg, pmu_spare_reg); + spin_unlock_irqrestore(®_spinlock, flags); +} + +static void wcnss_resume_notify(void) +{ + void __iomem *pmu_spare_reg; + u32 reg = 0; + unsigned long flags; + + /* For Riva */ + pmu_spare_reg = penv->msm_wcnss_base + RIVA_SPARE_OFFSET; + spin_lock_irqsave(®_spinlock, flags); + reg = readl_relaxed(pmu_spare_reg); + reg &= ~RIVA_SUSPEND_BIT; + writel_relaxed(reg, pmu_spare_reg); + spin_unlock_irqrestore(®_spinlock, flags); +} + static int wcnss_wlan_suspend(struct device *dev) { + int ret = 0; + if (penv && dev && (dev == &penv->pdev->dev) && penv->smd_channel_ready && - penv->pm_ops && penv->pm_ops->suspend) - return penv->pm_ops->suspend(dev); + penv->pm_ops && penv->pm_ops->suspend) { + ret = penv->pm_ops->suspend(dev); + if (ret == 0 && enable_wcnss_suspend_notify) + wcnss_suspend_notify(); + return ret; + } return 0; } static int wcnss_wlan_resume(struct device *dev) { + int ret = 0; + if (penv && dev && (dev == &penv->pdev->dev) && penv->smd_channel_ready && - penv->pm_ops && penv->pm_ops->resume) - return penv->pm_ops->resume(dev); + penv->pm_ops && penv->pm_ops->resume) { + ret = penv->pm_ops->resume(dev); + if (ret == 0 && enable_wcnss_suspend_notify) + wcnss_resume_notify(); + return ret; + } return 0; } @@ -416,8 +504,17 @@ wcnss_trigger_config(struct platform_device *pdev) wake_lock_init(&penv->wcnss_wake_lock, WAKE_LOCK_SUSPEND, "wcnss"); + penv->msm_wcnss_base = ioremap(MSM_RIVA_PHYS, SZ_256); + if (!penv->msm_wcnss_base) { + pr_err("%s: ioremap wcnss physical failed\n", __func__); + goto fail_wake; + } + return 0; +fail_wake: + wake_lock_destroy(&penv->wcnss_wake_lock); + fail_sysfs: fail_res: if (penv->pil) diff --git a/include/linux/wcnss_wlan.h b/include/linux/wcnss_wlan.h index 295be8ff8b351cda45acbb604c799d7fb42ea75c..6f29b789953ced18afc3e71e0c52b2962dbe3a6b 100644 --- a/include/linux/wcnss_wlan.h +++ b/include/linux/wcnss_wlan.h @@ -50,6 +50,7 @@ unsigned int wcnss_get_serial_number(void); void wcnss_flush_delayed_boot_votes(void); void wcnss_allow_suspend(void); void wcnss_prevent_suspend(void); +void wcnss_ssr_boot_notify(void); #define wcnss_wlan_get_drvdata(dev) dev_get_drvdata(dev) #define wcnss_wlan_set_drvdata(dev, data) dev_set_drvdata((dev), (data))