qcacld-3.0: Add runtime pm initialization

Add runtime pm initialization, including the creation
of a debugfs entry.

Change-Id: Ib057feaf36a04bb525a731e236187c134449c5f2
CRs-Fixed: 935300
This commit is contained in:
Houston Hoffman
2015-11-02 21:14:55 -08:00
committed by Akash Patel
parent 5be9bac028
commit 8eb73ed3ad
4 changed files with 389 additions and 1 deletions

View File

@@ -247,6 +247,7 @@ static int wlan_hdd_probe(struct device *dev, void *bdev, const hif_bus_id *bid,
if (ret) if (ret)
goto err_hif_close; goto err_hif_close;
hif_enable_power_management(hif_ctx);
if (reinit) { if (reinit) {
cds_set_recovery_in_progress(false); cds_set_recovery_in_progress(false);

View File

@@ -642,6 +642,7 @@ CDF_STATUS hif_enable(void *hif_ctx, struct device *dev, void *bdev,
enum hif_enable_type type); enum hif_enable_type type);
void hif_disable(void *hif_ctx, enum hif_disable_type type); void hif_disable(void *hif_ctx, enum hif_disable_type type);
void hif_enable_power_gating(void *hif_ctx); void hif_enable_power_gating(void *hif_ctx);
void hif_enable_power_management(void *hif_ctx);
int hif_bus_resume(void); int hif_bus_resume(void);
int hif_bus_suspend(void); int hif_bus_suspend(void);
void hif_vote_link_down(void); void hif_vote_link_down(void);

View File

@@ -43,6 +43,8 @@
#include "bmi_msg.h" /* TARGET_TYPE_ */ #include "bmi_msg.h" /* TARGET_TYPE_ */
#include "regtable.h" #include "regtable.h"
#include "ol_fw.h" #include "ol_fw.h"
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <osapi_linux.h> #include <osapi_linux.h>
#include "cds_api.h" #include "cds_api.h"
#include "cdf_status.h" #include "cdf_status.h"
@@ -804,6 +806,326 @@ end:
cdf_atomic_dec(&scn->active_tasklet_cnt); cdf_atomic_dec(&scn->active_tasklet_cnt);
} }
#ifdef FEATURE_RUNTIME_PM
#define HIF_PCI_RUNTIME_PM_STATS(_s, _sc, _name) \
seq_printf(_s, "%30s: %u\n", #_name, _sc->pm_stats._name)
/**
* hif_pci_runtime_pm_warn() - Runtime PM Debugging API
* @sc: hif_pci_softc context
* @msg: log message
*
* log runtime pm stats when something seems off.
*
* Return: void
*/
void hif_pci_runtime_pm_warn(struct hif_pci_softc *sc, const char *msg)
{
struct hif_pm_runtime_lock *ctx;
HIF_ERROR("%s: usage_count: %d, pm_state: %d, prevent_suspend_cnt: %d",
msg, atomic_read(&sc->dev->power.usage_count),
atomic_read(&sc->pm_state),
sc->prevent_suspend_cnt);
HIF_ERROR("runtime_status: %d, runtime_error: %d, disable_depth: %d autosuspend_delay: %d",
sc->dev->power.runtime_status,
sc->dev->power.runtime_error,
sc->dev->power.disable_depth,
sc->dev->power.autosuspend_delay);
HIF_ERROR("runtime_get: %u, runtime_put: %u, request_resume: %u",
sc->pm_stats.runtime_get, sc->pm_stats.runtime_put,
sc->pm_stats.request_resume);
HIF_ERROR("allow_suspend: %u, prevent_suspend: %u",
sc->pm_stats.allow_suspend,
sc->pm_stats.prevent_suspend);
HIF_ERROR("prevent_suspend_timeout: %u, allow_suspend_timeout: %u",
sc->pm_stats.prevent_suspend_timeout,
sc->pm_stats.allow_suspend_timeout);
HIF_ERROR("Suspended: %u, resumed: %u count",
sc->pm_stats.suspended,
sc->pm_stats.resumed);
HIF_ERROR("suspend_err: %u, runtime_get_err: %u",
sc->pm_stats.suspend_err,
sc->pm_stats.runtime_get_err);
HIF_ERROR("Active Wakeup Sources preventing Runtime Suspend: ");
list_for_each_entry(ctx, &sc->prevent_suspend_list, list) {
HIF_ERROR("source %s; timeout %d ms", ctx->name, ctx->timeout);
}
WARN_ON(1);
}
/**
* hif_pci_pm_runtime_debugfs_show(): show debug stats for runtimepm
* @s: file to print to
* @data: unused
*
* debugging tool added to the debug fs for displaying runtimepm stats
*
* Return: 0
*/
static int hif_pci_pm_runtime_debugfs_show(struct seq_file *s, void *data)
{
struct hif_pci_softc *sc = s->private;
static const char * const autopm_state[] = {"NONE", "ON", "INPROGRESS",
"SUSPENDED"};
unsigned int msecs_age;
int pm_state = atomic_read(&sc->pm_state);
unsigned long timer_expires, flags;
struct hif_pm_runtime_lock *ctx;
seq_printf(s, "%30s: %s\n", "Runtime PM state",
autopm_state[pm_state]);
seq_printf(s, "%30s: %pf\n", "Last Resume Caller",
sc->pm_stats.last_resume_caller);
if (pm_state == HIF_PM_RUNTIME_STATE_SUSPENDED) {
msecs_age = jiffies_to_msecs(
jiffies - sc->pm_stats.suspend_jiffies);
seq_printf(s, "%30s: %d.%03ds\n", "Suspended Since",
msecs_age / 1000, msecs_age % 1000);
}
seq_printf(s, "%30s: %d\n", "PM Usage count",
atomic_read(&sc->dev->power.usage_count));
seq_printf(s, "%30s: %u\n", "prevent_suspend_cnt",
sc->prevent_suspend_cnt);
HIF_PCI_RUNTIME_PM_STATS(s, sc, suspended);
HIF_PCI_RUNTIME_PM_STATS(s, sc, suspend_err);
HIF_PCI_RUNTIME_PM_STATS(s, sc, resumed);
HIF_PCI_RUNTIME_PM_STATS(s, sc, runtime_get);
HIF_PCI_RUNTIME_PM_STATS(s, sc, runtime_put);
HIF_PCI_RUNTIME_PM_STATS(s, sc, request_resume);
HIF_PCI_RUNTIME_PM_STATS(s, sc, prevent_suspend);
HIF_PCI_RUNTIME_PM_STATS(s, sc, allow_suspend);
HIF_PCI_RUNTIME_PM_STATS(s, sc, prevent_suspend_timeout);
HIF_PCI_RUNTIME_PM_STATS(s, sc, allow_suspend_timeout);
HIF_PCI_RUNTIME_PM_STATS(s, sc, runtime_get_err);
timer_expires = sc->runtime_timer_expires;
if (timer_expires > 0) {
msecs_age = jiffies_to_msecs(timer_expires - jiffies);
seq_printf(s, "%30s: %d.%03ds\n", "Prevent suspend timeout",
msecs_age / 1000, msecs_age % 1000);
}
spin_lock_irqsave(&sc->runtime_lock, flags);
if (list_empty(&sc->prevent_suspend_list)) {
spin_unlock_irqrestore(&sc->runtime_lock, flags);
return 0;
}
seq_printf(s, "%30s: ", "Active Wakeup_Sources");
list_for_each_entry(ctx, &sc->prevent_suspend_list, list) {
seq_printf(s, "%s", ctx->name);
if (ctx->timeout)
seq_printf(s, "(%d ms)", ctx->timeout);
seq_puts(s, " ");
}
seq_puts(s, "\n");
spin_unlock_irqrestore(&sc->runtime_lock, flags);
return 0;
}
#undef HIF_PCI_RUNTIME_PM_STATS
/**
* hif_pci_autopm_open() - open a debug fs file to access the runtime pm stats
* @inode
* @file
*
* Return: linux error code of single_open.
*/
static int hif_pci_runtime_pm_open(struct inode *inode, struct file *file)
{
return single_open(file, hif_pci_pm_runtime_debugfs_show,
inode->i_private);
}
#ifdef WLAN_OPEN_SOURCE
static const struct file_operations hif_pci_runtime_pm_fops = {
.owner = THIS_MODULE,
.open = hif_pci_runtime_pm_open,
.release = single_release,
.read = seq_read,
.llseek = seq_lseek,
};
/**
* hif_runtime_pm_debugfs_create() - creates runtimepm debugfs entry
* @sc: pci context
*
* creates a debugfs entry to debug the runtime pm feature.
*/
static void hif_runtime_pm_debugfs_create(struct hif_pci_softc *sc)
{
sc->pm_dentry = debugfs_create_file("cnss_runtime_pm",
S_IRUSR, NULL, sc,
&hif_pci_runtime_pm_fops);
}
/**
* hif_runtime_pm_debugfs_remove() - removes runtimepm debugfs entry
* @sc: pci context
*
* removes the debugfs entry to debug the runtime pm feature.
*/
static void hif_runtime_pm_debugfs_remove(struct hif_pci_softc *sc)
{
debugfs_remove(sc->pm_dentry);
}
#else
static inline void hif_runtime_pm_debugfs_create(struct hif_pci_softc *sc)
{
}
static inline void hif_runtime_pm_debugfs_remove(struct hif_pci_softc *sc)
{
}
#endif
/**
* hif_pm_runtime_lock_timeout_fn() - callback the runtime lock timeout
* @data: calback data that is the pci context
*
* if runtime locks are aquired with a timeout, this function releases
* the locks when the latest runtime lock expires.
*
* dummy implementation until lock acquisition is implemented.
*/
void hif_pm_runtime_lock_timeout_fn(unsigned long data) {}
/**
* hif_pm_runtime_start(): start the runtime pm
* @sc: pci context
*
* After this call, runtime pm will be active.
*/
static void hif_pm_runtime_start(struct hif_pci_softc *sc)
{
struct ol_softc *ol_sc;
ol_sc = sc->ol_sc;
if (!ol_sc->enable_runtime_pm) {
HIF_INFO("%s: RUNTIME PM is disabled in ini\n", __func__);
return;
}
if (cds_get_conparam() == CDF_FTM_MODE ||
WLAN_IS_EPPING_ENABLED(cds_get_conparam())) {
HIF_INFO("%s: RUNTIME PM is disabled for FTM/EPPING mode\n",
__func__);
return;
}
setup_timer(&sc->runtime_timer, hif_pm_runtime_lock_timeout_fn,
(unsigned long)sc);
HIF_INFO("%s: Enabling RUNTIME PM, Delay: %d ms", __func__,
ol_sc->runtime_pm_delay);
cnss_runtime_init(sc->dev, ol_sc->runtime_pm_delay);
cdf_atomic_set(&sc->pm_state, HIF_PM_RUNTIME_STATE_ON);
hif_runtime_pm_debugfs_create(sc);
}
/**
* hif_pm_runtime_stop(): stop runtime pm
* @sc: pci context
*
* Turns off runtime pm and frees corresponding resources
* that were acquired by hif_runtime_pm_start().
*/
static void hif_pm_runtime_stop(struct hif_pci_softc *sc)
{
struct ol_softc *ol_sc = sc->ol_sc;
if (!ol_sc->enable_runtime_pm)
return;
if (cds_get_conparam() == CDF_FTM_MODE ||
WLAN_IS_EPPING_ENABLED(cds_get_conparam()))
return;
cnss_runtime_exit(sc->dev);
cnss_pm_runtime_request(sc->dev, CNSS_PM_RUNTIME_RESUME);
cdf_atomic_set(&sc->pm_state, HIF_PM_RUNTIME_STATE_NONE);
hif_runtime_pm_debugfs_remove(sc);
del_timer_sync(&sc->runtime_timer);
/* doesn't wait for penting trafic unlike cld-2.0 */
}
/**
* hif_pm_runtime_open(): initialize runtime pm
* @sc: pci data structure
*
* Early initialization
*/
static void hif_pm_runtime_open(struct hif_pci_softc *sc)
{
spin_lock_init(&sc->runtime_lock);
cdf_atomic_init(&sc->pm_state);
cdf_atomic_set(&sc->pm_state, HIF_PM_RUNTIME_STATE_NONE);
INIT_LIST_HEAD(&sc->prevent_suspend_list);
}
/**
* hif_pm_runtime_close(): close runtime pm
* @sc: pci bus handle
*
* ensure runtime_pm is stopped before closing the driver
*/
static void hif_pm_runtime_close(struct hif_pci_softc *sc)
{
if (cdf_atomic_read(&sc->pm_state) == HIF_PM_RUNTIME_STATE_NONE)
return;
else
hif_pm_runtime_stop(sc);
}
#else
static void hif_pm_runtime_close(struct hif_pci_softc *sc) {}
static void hif_pm_runtime_open(struct hif_pci_softc *sc) {}
static void hif_pm_runtime_start(struct hif_pci_softc *sc) {}
#endif
/**
* hif_enable_power_management(): enable power management
* @hif_ctx: hif context
*
* Currently only does runtime pm. Eventually this function could
* consolidate other power state features such as only letting
* the soc sleep after the driver finishes loading and re-enabling
* aspm (hif_enable_power_gating).
*/
void hif_enable_power_management(void *hif_ctx)
{
struct hif_pci_softc *pci_ctx;
if (hif_ctx == NULL) {
HIF_ERROR("%s, hif_ctx null", __func__);
return;
}
pci_ctx = ((struct ol_softc *)hif_ctx)->hif_sc;
hif_pm_runtime_start(pci_ctx);
}
#define ATH_PCI_PROBE_RETRY_MAX 3 #define ATH_PCI_PROBE_RETRY_MAX 3
/** /**
* hif_bus_open(): hif_bus_open * hif_bus_open(): hif_bus_open
@@ -824,6 +1146,7 @@ CDF_STATUS hif_bus_open(struct ol_softc *ol_sc, enum ath_hal_bus_type bus_type)
ol_sc->hif_sc = (void *)sc; ol_sc->hif_sc = (void *)sc;
sc->ol_sc = ol_sc; sc->ol_sc = ol_sc;
ol_sc->bus_type = bus_type; ol_sc->bus_type = bus_type;
hif_pm_runtime_open(sc);
cdf_spinlock_init(&ol_sc->irq_lock); cdf_spinlock_init(&ol_sc->irq_lock);
@@ -846,6 +1169,8 @@ void hif_bus_close(struct ol_softc *ol_sc)
sc = ol_sc->hif_sc; sc = ol_sc->hif_sc;
if (sc == NULL) if (sc == NULL)
return; return;
hif_pm_runtime_close(sc);
cdf_mem_free(sc); cdf_mem_free(sc);
ol_sc->hif_sc = NULL; ol_sc->hif_sc = NULL;
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2015 The Linux Foundation. All rights reserved. * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
* *
* Previously licensed under the ISC license by Qualcomm Atheros, Inc. * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
* *
@@ -50,6 +50,54 @@ struct hif_tasklet_entry {
uint8_t id; /* 0 - 9: maps to CE, 10: fw */ uint8_t id; /* 0 - 9: maps to CE, 10: fw */
void *hif_handler; /* struct hif_pci_softc */ void *hif_handler; /* struct hif_pci_softc */
}; };
/**
* enum hif_pm_runtime_state - Driver States for Runtime Power Management
* HIF_PM_RUNTIME_STATE_NONE: runtime pm is off
* HIF_PM_RUNTIME_STATE_ON: runtime pm is active and link is active
* HIF_PM_RUNTIME_STATE_INPROGRESS: a runtime suspend or resume is in progress
* HIF_PM_RUNTIME_STATE_SUSPENDED: the driver is runtime suspended
*/
enum hif_pm_runtime_state {
HIF_PM_RUNTIME_STATE_NONE,
HIF_PM_RUNTIME_STATE_ON,
HIF_PM_RUNTIME_STATE_INPROGRESS,
HIF_PM_RUNTIME_STATE_SUSPENDED,
};
#ifdef FEATURE_RUNTIME_PM
/**
* struct hif_pm_runtime_lock - data structure for preventing runtime suspend
* @list - global list of runtime locks
* @active - true if this lock is preventing suspend
* @name - character string for tracking this lock
*/
struct hif_pm_runtime_lock {
struct list_head list;
bool active;
uint32_t timeout;
const char *name;
};
/* Debugging stats for Runtime PM */
struct hif_pci_pm_stats {
u32 suspended;
u32 suspend_err;
u32 resumed;
u32 runtime_get;
u32 runtime_put;
u32 request_resume;
u32 allow_suspend;
u32 prevent_suspend;
u32 prevent_suspend_timeout;
u32 allow_suspend_timeout;
u32 runtime_get_err;
void *last_resume_caller;
unsigned long suspend_jiffies;
};
#endif
struct hif_pci_softc { struct hif_pci_softc {
void __iomem *mem; /* PCI address. */ void __iomem *mem; /* PCI address. */
/* For efficiency, should be first in struct */ /* For efficiency, should be first in struct */
@@ -69,6 +117,19 @@ struct hif_pci_softc {
cdf_dma_addr_t soc_pcie_bar0; cdf_dma_addr_t soc_pcie_bar0;
struct hif_tasklet_entry tasklet_entries[HIF_MAX_TASKLET_NUM]; struct hif_tasklet_entry tasklet_entries[HIF_MAX_TASKLET_NUM];
bool pci_enabled; bool pci_enabled;
#ifdef FEATURE_RUNTIME_PM
atomic_t pm_state;
uint32_t prevent_suspend_cnt;
struct hif_pci_pm_stats pm_stats;
struct work_struct pm_work;
spinlock_t runtime_lock;
struct timer_list runtime_timer;
struct list_head prevent_suspend_list;
unsigned long runtime_timer_expires;
#ifdef WLAN_OPEN_SOURCE
struct dentry *pm_dentry;
#endif
#endif
}; };
bool hif_pci_targ_is_present(struct ol_softc *scn, void *__iomem *mem); bool hif_pci_targ_is_present(struct ol_softc *scn, void *__iomem *mem);