Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 62aa58dc authored by Houston Hoffman's avatar Houston Hoffman Committed by Akash Patel
Browse files

qcacld-3.0: Add runtime pm initialization

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

Change-Id: Ib057feaf36a04bb525a731e236187c134449c5f2
CRs-Fixed: 935300
parent 47e387ba
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -247,6 +247,7 @@ static int wlan_hdd_probe(struct device *dev, void *bdev, const hif_bus_id *bid,
	if (ret)
		goto err_hif_close;

	hif_enable_power_management(hif_ctx);

	if (reinit) {
		cds_set_recovery_in_progress(false);
+1 −0
Original line number Diff line number Diff line
@@ -642,6 +642,7 @@ CDF_STATUS hif_enable(void *hif_ctx, struct device *dev, void *bdev,
	enum hif_enable_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_management(void *hif_ctx);
int hif_bus_resume(void);
int hif_bus_suspend(void);
void hif_vote_link_down(void);
+325 −0
Original line number Diff line number Diff line
@@ -43,6 +43,8 @@
#include "bmi_msg.h"            /* TARGET_TYPE_ */
#include "regtable.h"
#include "ol_fw.h"
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <osapi_linux.h>
#include "cds_api.h"
#include "cdf_status.h"
@@ -804,6 +806,326 @@ static void wlan_tasklet(unsigned long data)
	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
/**
 * 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;
	sc->ol_sc = ol_sc;
	ol_sc->bus_type = bus_type;
	hif_pm_runtime_open(sc);

	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;
	if (sc == NULL)
		return;

	hif_pm_runtime_close(sc);
	cdf_mem_free(sc);
	ol_sc->hif_sc = NULL;
}
+62 −1
Original line number Diff line number Diff line
/*
 * 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.
 *
@@ -50,6 +50,54 @@ struct hif_tasklet_entry {
	uint8_t id;        /* 0 - 9: maps to CE, 10: fw */
	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 {
	void __iomem *mem;      /* PCI address. */
	/* For efficiency, should be first in struct */
@@ -69,6 +117,19 @@ struct hif_pci_softc {
	cdf_dma_addr_t soc_pcie_bar0;
	struct hif_tasklet_entry tasklet_entries[HIF_MAX_TASKLET_NUM];
	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);