xref: /wlan-driver/qca-wifi-host-cmn/hif/src/hif_runtime_pm.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1*5113495bSYour Name /*
2*5113495bSYour Name  * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved.
3*5113495bSYour Name  * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
4*5113495bSYour Name  *
5*5113495bSYour Name  * Permission to use, copy, modify, and/or distribute this software for any
6*5113495bSYour Name  * purpose with or without fee is hereby granted, provided that the above
7*5113495bSYour Name  * copyright notice and this permission notice appear in all copies.
8*5113495bSYour Name 
9*5113495bSYour Name  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10*5113495bSYour Name  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11*5113495bSYour Name  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12*5113495bSYour Name  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13*5113495bSYour Name  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14*5113495bSYour Name  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15*5113495bSYour Name  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*5113495bSYour Name  */
17*5113495bSYour Name 
18*5113495bSYour Name #include <linux/slab.h>
19*5113495bSYour Name #include <linux/interrupt.h>
20*5113495bSYour Name #include <linux/if_arp.h>
21*5113495bSYour Name #include "hif_io32.h"
22*5113495bSYour Name #include "hif_runtime_pm.h"
23*5113495bSYour Name #include "hif.h"
24*5113495bSYour Name #include "target_type.h"
25*5113495bSYour Name #include "hif_main.h"
26*5113495bSYour Name #include "ce_main.h"
27*5113495bSYour Name #include "ce_api.h"
28*5113495bSYour Name #include "ce_internal.h"
29*5113495bSYour Name #include "ce_reg.h"
30*5113495bSYour Name #include "ce_bmi.h"
31*5113495bSYour Name #include "regtable.h"
32*5113495bSYour Name #include "hif_hw_version.h"
33*5113495bSYour Name #include <linux/debugfs.h>
34*5113495bSYour Name #include <linux/seq_file.h>
35*5113495bSYour Name #include "qdf_status.h"
36*5113495bSYour Name #include "qdf_atomic.h"
37*5113495bSYour Name #include "pld_common.h"
38*5113495bSYour Name #include "mp_dev.h"
39*5113495bSYour Name #include "hif_debug.h"
40*5113495bSYour Name 
41*5113495bSYour Name #include "ce_tasklet.h"
42*5113495bSYour Name #include "targaddrs.h"
43*5113495bSYour Name #include "hif_exec.h"
44*5113495bSYour Name 
45*5113495bSYour Name #define CNSS_RUNTIME_FILE "cnss_runtime_pm"
46*5113495bSYour Name #define CNSS_RUNTIME_FILE_PERM QDF_FILE_USR_READ
47*5113495bSYour Name 
48*5113495bSYour Name #ifdef FEATURE_RUNTIME_PM
49*5113495bSYour Name 
50*5113495bSYour Name static struct hif_rtpm_ctx g_hif_rtpm_ctx;
51*5113495bSYour Name static struct hif_rtpm_ctx *gp_hif_rtpm_ctx;
52*5113495bSYour Name 
53*5113495bSYour Name /**
54*5113495bSYour Name  * hif_rtpm_id_to_string() - Convert dbgid to respective string
55*5113495bSYour Name  * @id: debug id
56*5113495bSYour Name  *
57*5113495bSYour Name  * Debug support function to convert  dbgid to string.
58*5113495bSYour Name  * Please note to add new string in the array at index equal to
59*5113495bSYour Name  * its enum value in wlan_rtpm_dbgid.
60*5113495bSYour Name  *
61*5113495bSYour Name  * Return: String of ID
62*5113495bSYour Name  */
hif_rtpm_id_to_string(enum hif_rtpm_client_id id)63*5113495bSYour Name static const char *hif_rtpm_id_to_string(enum hif_rtpm_client_id id)
64*5113495bSYour Name {
65*5113495bSYour Name 	static const char * const strings[] = {
66*5113495bSYour Name 					"HIF_RTPM_ID_RESERVED",
67*5113495bSYour Name 					"HIF_RTPM_HAL_REO_CMD",
68*5113495bSYour Name 					"HIF_RTPM_WMI",
69*5113495bSYour Name 					"HIF_RTPM_HTT",
70*5113495bSYour Name 					"HIF_RTPM_DP",
71*5113495bSYour Name 					"HIF_RTPM_RING_STATS",
72*5113495bSYour Name 					"HIF_RTPM_CE",
73*5113495bSYour Name 					"HIF_RTPM_FORCE_WAKE",
74*5113495bSYour Name 					"HIF_RTPM_ID_PM_QOS_NOTIFY",
75*5113495bSYour Name 					"HIF_RTPM_ID_WIPHY_SUSPEND",
76*5113495bSYour Name 					"HIF_RTPM_ID_MAX"
77*5113495bSYour Name 	};
78*5113495bSYour Name 
79*5113495bSYour Name 	return strings[id];
80*5113495bSYour Name }
81*5113495bSYour Name 
82*5113495bSYour Name /**
83*5113495bSYour Name  * hif_rtpm_read_usage_count() - Read device usage count
84*5113495bSYour Name  *
85*5113495bSYour Name  * Return: current usage count
86*5113495bSYour Name  */
hif_rtpm_read_usage_count(void)87*5113495bSYour Name static inline int hif_rtpm_read_usage_count(void)
88*5113495bSYour Name {
89*5113495bSYour Name 	return qdf_atomic_read(&gp_hif_rtpm_ctx->dev->power.usage_count);
90*5113495bSYour Name }
91*5113495bSYour Name 
92*5113495bSYour Name /**
93*5113495bSYour Name  * hif_rtpm_print(): print stats for runtimepm
94*5113495bSYour Name  * @type: type of caller
95*5113495bSYour Name  * @index: pointer to index to keep track of print position
96*5113495bSYour Name  * @buf: pointer of buffer to print to
97*5113495bSYour Name  * @fmt: format string
98*5113495bSYour Name  *
99*5113495bSYour Name  * debugging tool added to allow for unified API for debug/sys fs rtpm printing
100*5113495bSYour Name  */
101*5113495bSYour Name static void
hif_rtpm_print(enum hif_rtpm_fill_type type,int * index,void * buf,char * fmt,...)102*5113495bSYour Name hif_rtpm_print(enum hif_rtpm_fill_type type, int *index, void *buf,
103*5113495bSYour Name 	       char *fmt, ...)
104*5113495bSYour Name {
105*5113495bSYour Name 	va_list args;
106*5113495bSYour Name 
107*5113495bSYour Name 	va_start(args, fmt);
108*5113495bSYour Name 	if (type == HIF_RTPM_FILL_TYPE_SYSFS) {
109*5113495bSYour Name 		if (index)
110*5113495bSYour Name 			*index += vscnprintf((char *)buf + *index, PAGE_SIZE,
111*5113495bSYour Name 					     fmt, args);
112*5113495bSYour Name 	} else if (type == HIF_RTPM_FILL_TYPE_DEBUGFS) {
113*5113495bSYour Name 		seq_vprintf((struct seq_file *)buf, fmt, args);
114*5113495bSYour Name 	}
115*5113495bSYour Name 
116*5113495bSYour Name 	va_end(args);
117*5113495bSYour Name }
118*5113495bSYour Name 
119*5113495bSYour Name #define HIF_RTPM_STATS(_type, _index,  _s, _rtpm_ctx, _name) \
120*5113495bSYour Name 	hif_rtpm_print(_type, _index,  _s, "%30s: %u\n", #_name, \
121*5113495bSYour Name 		       (_rtpm_ctx)->stats._name)
122*5113495bSYour Name 
hif_rtpm_log_debug_stats(void * s,enum hif_rtpm_fill_type type)123*5113495bSYour Name int hif_rtpm_log_debug_stats(void *s, enum hif_rtpm_fill_type type)
124*5113495bSYour Name {
125*5113495bSYour Name 	int index = 0;
126*5113495bSYour Name 	struct hif_rtpm_client *client = NULL;
127*5113495bSYour Name 	struct hif_pm_runtime_lock *ctx;
128*5113495bSYour Name 	static const char * const autopm_state[] = {"NONE", "ON", "RESUMING",
129*5113495bSYour Name 			"RESUMING_LINKUP", "SUSPENDING", "SUSPENDED"};
130*5113495bSYour Name 	int pm_state = qdf_atomic_read(&gp_hif_rtpm_ctx->pm_state);
131*5113495bSYour Name 	int i;
132*5113495bSYour Name 
133*5113495bSYour Name 	hif_rtpm_print(type, &index, s, "%30s: %llu\n", "Current timestamp",
134*5113495bSYour Name 		       qdf_get_log_timestamp());
135*5113495bSYour Name 
136*5113495bSYour Name 	hif_rtpm_print(type, &index, s, "%30s: %s\n", "Runtime PM state",
137*5113495bSYour Name 		       autopm_state[pm_state]);
138*5113495bSYour Name 
139*5113495bSYour Name 	hif_rtpm_print(type, &index, s, "%30s: %llu\n", "Last Busy timestamp",
140*5113495bSYour Name 		       gp_hif_rtpm_ctx->stats.last_busy_ts);
141*5113495bSYour Name 
142*5113495bSYour Name 	hif_rtpm_print(type, &index, s, "%30s: %llu\n", "Last resume request timestamp",
143*5113495bSYour Name 		       gp_hif_rtpm_ctx->stats.request_resume_ts);
144*5113495bSYour Name 
145*5113495bSYour Name 	hif_rtpm_print(type, &index, s, "%30s: %llu\n",
146*5113495bSYour Name 		       "Last resume request by",
147*5113495bSYour Name 		       gp_hif_rtpm_ctx->stats.request_resume_id);
148*5113495bSYour Name 
149*5113495bSYour Name 	hif_rtpm_print(type, &index, s, "%30s: %ps\n", "Last Busy Marker",
150*5113495bSYour Name 		       gp_hif_rtpm_ctx->stats.last_busy_marker);
151*5113495bSYour Name 
152*5113495bSYour Name 	hif_rtpm_print(type, &index, s, "Rx busy marker counts:\n");
153*5113495bSYour Name 	hif_rtpm_print(type, &index, s, "%30s: %u %llu\n",
154*5113495bSYour Name 		       hif_rtpm_id_to_string(HIF_RTPM_ID_DP),
155*5113495bSYour Name 		       gp_hif_rtpm_ctx->clients[HIF_RTPM_ID_DP]->last_busy_cnt,
156*5113495bSYour Name 		       gp_hif_rtpm_ctx->clients[HIF_RTPM_ID_DP]->last_busy_ts);
157*5113495bSYour Name 
158*5113495bSYour Name 	hif_rtpm_print(type, &index, s, "%30s: %u %llu\n",
159*5113495bSYour Name 		       hif_rtpm_id_to_string(HIF_RTPM_ID_CE),
160*5113495bSYour Name 		       gp_hif_rtpm_ctx->clients[HIF_RTPM_ID_CE]->last_busy_cnt,
161*5113495bSYour Name 		       gp_hif_rtpm_ctx->clients[HIF_RTPM_ID_CE]->last_busy_ts);
162*5113495bSYour Name 
163*5113495bSYour Name 	HIF_RTPM_STATS(type, &index, s, gp_hif_rtpm_ctx, last_busy_id);
164*5113495bSYour Name 
165*5113495bSYour Name 	if (pm_state == HIF_RTPM_STATE_SUSPENDED) {
166*5113495bSYour Name 		hif_rtpm_print(type, &index, s, "%30s: %llx us\n",
167*5113495bSYour Name 			       "Suspended Since",
168*5113495bSYour Name 			       gp_hif_rtpm_ctx->stats.suspend_ts);
169*5113495bSYour Name 	}
170*5113495bSYour Name 
171*5113495bSYour Name 	HIF_RTPM_STATS(type, &index, s, gp_hif_rtpm_ctx, resume_count);
172*5113495bSYour Name 	HIF_RTPM_STATS(type, &index, s, gp_hif_rtpm_ctx, suspend_count);
173*5113495bSYour Name 	HIF_RTPM_STATS(type, &index, s, gp_hif_rtpm_ctx, suspend_err_count);
174*5113495bSYour Name 
175*5113495bSYour Name 	hif_rtpm_print(type, &index, s, "%30s: %d\n", "PM Usage count",
176*5113495bSYour Name 		       hif_rtpm_read_usage_count());
177*5113495bSYour Name 
178*5113495bSYour Name 	hif_rtpm_print(type, &index, s,
179*5113495bSYour Name 		       "get  put  get-timestamp put-timestamp :DBGID_NAME\n");
180*5113495bSYour Name 	for (i = 0; i < HIF_RTPM_ID_MAX; i++) {
181*5113495bSYour Name 		client = gp_hif_rtpm_ctx->clients[i];
182*5113495bSYour Name 		if (!client)
183*5113495bSYour Name 			continue;
184*5113495bSYour Name 		hif_rtpm_print(type, &index, s, "%-10d ",
185*5113495bSYour Name 			       qdf_atomic_read(&client->get_count));
186*5113495bSYour Name 		hif_rtpm_print(type, &index, s, "%-10d ",
187*5113495bSYour Name 			       qdf_atomic_read(&client->put_count));
188*5113495bSYour Name 		hif_rtpm_print(type, &index, s, "0x%-10llx ", client->get_ts);
189*5113495bSYour Name 		hif_rtpm_print(type, &index, s, "0x%-10llx ", client->put_ts);
190*5113495bSYour Name 		hif_rtpm_print(type, &index, s, ":%-2d %-30s\n", i,
191*5113495bSYour Name 			       hif_rtpm_id_to_string(i));
192*5113495bSYour Name 	}
193*5113495bSYour Name 	hif_rtpm_print(type, &index, s, "\n");
194*5113495bSYour Name 
195*5113495bSYour Name 	qdf_spin_lock_bh(&gp_hif_rtpm_ctx->prevent_list_lock);
196*5113495bSYour Name 	if (list_empty(&gp_hif_rtpm_ctx->prevent_list)) {
197*5113495bSYour Name 		qdf_spin_unlock_bh(&gp_hif_rtpm_ctx->prevent_list_lock);
198*5113495bSYour Name 		return index;
199*5113495bSYour Name 	}
200*5113495bSYour Name 
201*5113495bSYour Name 	hif_rtpm_print(type, &index, s, "%30s: ", "Active Wakeup_Sources");
202*5113495bSYour Name 	list_for_each_entry(ctx, &gp_hif_rtpm_ctx->prevent_list, list) {
203*5113495bSYour Name 		hif_rtpm_print(type, &index, s, "%s", ctx->name);
204*5113495bSYour Name 		hif_rtpm_print(type, &index, s, " ");
205*5113495bSYour Name 	}
206*5113495bSYour Name 	qdf_spin_unlock_bh(&gp_hif_rtpm_ctx->prevent_list_lock);
207*5113495bSYour Name 	hif_rtpm_print(type, &index, s, "\n");
208*5113495bSYour Name 
209*5113495bSYour Name 	return index;
210*5113495bSYour Name }
211*5113495bSYour Name 
212*5113495bSYour Name /**
213*5113495bSYour Name  * hif_rtpm_debugfs_show(): show debug stats for runtimepm
214*5113495bSYour Name  * @s: file to print to
215*5113495bSYour Name  * @data: unused
216*5113495bSYour Name  *
217*5113495bSYour Name  * debugging tool added to the debug fs for displaying runtimepm stats
218*5113495bSYour Name  *
219*5113495bSYour Name  * Return: 0
220*5113495bSYour Name  */
221*5113495bSYour Name 
hif_rtpm_debugfs_show(struct seq_file * s,void * data)222*5113495bSYour Name static int hif_rtpm_debugfs_show(struct seq_file *s, void *data)
223*5113495bSYour Name {
224*5113495bSYour Name 	return hif_rtpm_log_debug_stats((void *)s, HIF_RTPM_FILL_TYPE_DEBUGFS);
225*5113495bSYour Name }
226*5113495bSYour Name 
227*5113495bSYour Name #undef HIF_RTPM_STATS
228*5113495bSYour Name 
229*5113495bSYour Name /**
230*5113495bSYour Name  * hif_rtpm_debugfs_open() - open a debug fs file to access the runtime pm stats
231*5113495bSYour Name  * @inode:
232*5113495bSYour Name  * @file:
233*5113495bSYour Name  *
234*5113495bSYour Name  * Return: linux error code of single_open.
235*5113495bSYour Name  */
hif_rtpm_debugfs_open(struct inode * inode,struct file * file)236*5113495bSYour Name static int hif_rtpm_debugfs_open(struct inode *inode, struct file *file)
237*5113495bSYour Name {
238*5113495bSYour Name 	return single_open(file, hif_rtpm_debugfs_show,
239*5113495bSYour Name 			inode->i_private);
240*5113495bSYour Name }
241*5113495bSYour Name 
242*5113495bSYour Name static const struct file_operations hif_rtpm_fops = {
243*5113495bSYour Name 	.owner          = THIS_MODULE,
244*5113495bSYour Name 	.open           = hif_rtpm_debugfs_open,
245*5113495bSYour Name 	.release        = single_release,
246*5113495bSYour Name 	.read           = seq_read,
247*5113495bSYour Name 	.llseek         = seq_lseek,
248*5113495bSYour Name };
249*5113495bSYour Name 
250*5113495bSYour Name /**
251*5113495bSYour Name  * hif_rtpm_debugfs_create() - creates runtimepm debugfs entry
252*5113495bSYour Name  *
253*5113495bSYour Name  * creates a debugfs entry to debug the runtime pm feature.
254*5113495bSYour Name  */
hif_rtpm_debugfs_create(void)255*5113495bSYour Name static void hif_rtpm_debugfs_create(void)
256*5113495bSYour Name {
257*5113495bSYour Name 	gp_hif_rtpm_ctx->pm_dentry = qdf_debugfs_create_entry(CNSS_RUNTIME_FILE,
258*5113495bSYour Name 							CNSS_RUNTIME_FILE_PERM,
259*5113495bSYour Name 							NULL,
260*5113495bSYour Name 							NULL,
261*5113495bSYour Name 							&hif_rtpm_fops);
262*5113495bSYour Name }
263*5113495bSYour Name 
264*5113495bSYour Name /**
265*5113495bSYour Name  * hif_rtpm_debugfs_remove() - removes runtimepm debugfs entry
266*5113495bSYour Name  *
267*5113495bSYour Name  * removes the debugfs entry to debug the runtime pm feature.
268*5113495bSYour Name  */
hif_rtpm_debugfs_remove(void)269*5113495bSYour Name static void hif_rtpm_debugfs_remove(void)
270*5113495bSYour Name {
271*5113495bSYour Name 	qdf_debugfs_remove_file(gp_hif_rtpm_ctx->pm_dentry);
272*5113495bSYour Name }
273*5113495bSYour Name 
274*5113495bSYour Name /**
275*5113495bSYour Name  * hif_rtpm_init() - Initialize Runtime PM
276*5113495bSYour Name  * @dev: device structure
277*5113495bSYour Name  * @delay: delay to be configured for auto suspend
278*5113495bSYour Name  *
279*5113495bSYour Name  * This function will init all the Runtime PM config.
280*5113495bSYour Name  *
281*5113495bSYour Name  * Return: void
282*5113495bSYour Name  */
hif_rtpm_init(struct device * dev,int delay)283*5113495bSYour Name static void hif_rtpm_init(struct device *dev, int delay)
284*5113495bSYour Name {
285*5113495bSYour Name 	pm_runtime_set_autosuspend_delay(dev, delay);
286*5113495bSYour Name 	pm_runtime_use_autosuspend(dev);
287*5113495bSYour Name 	pm_runtime_allow(dev);
288*5113495bSYour Name 	pm_runtime_mark_last_busy(dev);
289*5113495bSYour Name 	pm_runtime_put_noidle(dev);
290*5113495bSYour Name 	pm_suspend_ignore_children(dev, true);
291*5113495bSYour Name }
292*5113495bSYour Name 
293*5113495bSYour Name /**
294*5113495bSYour Name  * hif_rtpm_exit() - Deinit/Exit Runtime PM
295*5113495bSYour Name  * @dev: device structure
296*5113495bSYour Name  *
297*5113495bSYour Name  * This function will deinit all the Runtime PM config.
298*5113495bSYour Name  *
299*5113495bSYour Name  * Return: void
300*5113495bSYour Name  */
hif_rtpm_exit(struct device * dev)301*5113495bSYour Name static void hif_rtpm_exit(struct device *dev)
302*5113495bSYour Name {
303*5113495bSYour Name 	pm_runtime_get_noresume(dev);
304*5113495bSYour Name 	pm_runtime_set_active(dev);
305*5113495bSYour Name 	pm_runtime_forbid(dev);
306*5113495bSYour Name }
307*5113495bSYour Name 
hif_rtpm_alloc_last_busy_hist(void)308*5113495bSYour Name static void hif_rtpm_alloc_last_busy_hist(void)
309*5113495bSYour Name {
310*5113495bSYour Name 	int i;
311*5113495bSYour Name 
312*5113495bSYour Name 	for (i = 0; i < CE_COUNT_MAX; i++) {
313*5113495bSYour Name 		if (i != CE_ID_1 && i != CE_ID_2 && i != CE_ID_7) {
314*5113495bSYour Name 			gp_hif_rtpm_ctx->busy_hist[i] = NULL;
315*5113495bSYour Name 			continue;
316*5113495bSYour Name 		}
317*5113495bSYour Name 
318*5113495bSYour Name 		gp_hif_rtpm_ctx->busy_hist[i] =
319*5113495bSYour Name 			qdf_mem_malloc(sizeof(struct hif_rtpm_last_busy_hist));
320*5113495bSYour Name 		if (!gp_hif_rtpm_ctx->busy_hist[i])
321*5113495bSYour Name 			return;
322*5113495bSYour Name 	}
323*5113495bSYour Name }
324*5113495bSYour Name 
hif_rtpm_free_last_busy_hist(void)325*5113495bSYour Name static void hif_rtpm_free_last_busy_hist(void)
326*5113495bSYour Name {
327*5113495bSYour Name 	int i;
328*5113495bSYour Name 
329*5113495bSYour Name 	for (i = 0; i < CE_COUNT_MAX; i++) {
330*5113495bSYour Name 		if (i != CE_ID_1 && i != CE_ID_2 && i != CE_ID_7)
331*5113495bSYour Name 			continue;
332*5113495bSYour Name 
333*5113495bSYour Name 		qdf_mem_free(gp_hif_rtpm_ctx->busy_hist[i]);
334*5113495bSYour Name 	}
335*5113495bSYour Name }
336*5113495bSYour Name 
hif_rtpm_open(struct hif_softc * scn)337*5113495bSYour Name void hif_rtpm_open(struct hif_softc *scn)
338*5113495bSYour Name {
339*5113495bSYour Name 	gp_hif_rtpm_ctx = &g_hif_rtpm_ctx;
340*5113495bSYour Name 	gp_hif_rtpm_ctx->dev = scn->qdf_dev->dev;
341*5113495bSYour Name 	qdf_spinlock_create(&gp_hif_rtpm_ctx->runtime_lock);
342*5113495bSYour Name 	qdf_spinlock_create(&gp_hif_rtpm_ctx->runtime_suspend_lock);
343*5113495bSYour Name 	qdf_spinlock_create(&gp_hif_rtpm_ctx->prevent_list_lock);
344*5113495bSYour Name 	qdf_atomic_init(&gp_hif_rtpm_ctx->pm_state);
345*5113495bSYour Name 	qdf_atomic_set(&gp_hif_rtpm_ctx->pm_state, HIF_RTPM_STATE_NONE);
346*5113495bSYour Name 	qdf_atomic_init(&gp_hif_rtpm_ctx->monitor_wake_intr);
347*5113495bSYour Name 	INIT_LIST_HEAD(&gp_hif_rtpm_ctx->prevent_list);
348*5113495bSYour Name 	gp_hif_rtpm_ctx->client_count = 0;
349*5113495bSYour Name 	gp_hif_rtpm_ctx->pending_job = 0;
350*5113495bSYour Name 	hif_rtpm_register(HIF_RTPM_ID_CE, NULL);
351*5113495bSYour Name 	hif_rtpm_register(HIF_RTPM_ID_FORCE_WAKE, NULL);
352*5113495bSYour Name 	hif_rtpm_alloc_last_busy_hist();
353*5113495bSYour Name 	hif_info_high("Runtime PM attached");
354*5113495bSYour Name }
355*5113495bSYour Name 
356*5113495bSYour Name static int __hif_pm_runtime_allow_suspend(struct hif_pm_runtime_lock *lock);
357*5113495bSYour Name 
358*5113495bSYour Name /**
359*5113495bSYour Name  * hif_rtpm_sanitize_exit(): sanitize runtime PM gets/puts from driver
360*5113495bSYour Name  *
361*5113495bSYour Name  * Ensure all gets/puts are in sync before exiting runtime PM feature.
362*5113495bSYour Name  * Also make sure all runtime PM locks are deinitialized properly.
363*5113495bSYour Name  *
364*5113495bSYour Name  * Return: void
365*5113495bSYour Name  */
hif_rtpm_sanitize_exit(void)366*5113495bSYour Name static void hif_rtpm_sanitize_exit(void)
367*5113495bSYour Name {
368*5113495bSYour Name 	struct hif_pm_runtime_lock *ctx, *tmp;
369*5113495bSYour Name 	struct hif_rtpm_client *client;
370*5113495bSYour Name 	int i, active_count;
371*5113495bSYour Name 
372*5113495bSYour Name 	qdf_spin_lock_bh(&gp_hif_rtpm_ctx->prevent_list_lock);
373*5113495bSYour Name 	list_for_each_entry_safe(ctx, tmp,
374*5113495bSYour Name 				 &gp_hif_rtpm_ctx->prevent_list, list) {
375*5113495bSYour Name 		hif_runtime_lock_deinit(ctx);
376*5113495bSYour Name 	}
377*5113495bSYour Name 	qdf_spin_unlock_bh(&gp_hif_rtpm_ctx->prevent_list_lock);
378*5113495bSYour Name 
379*5113495bSYour Name 	/* check if get and put out of sync for all clients */
380*5113495bSYour Name 	for (i = 0; i < HIF_RTPM_ID_MAX; i++) {
381*5113495bSYour Name 		client = gp_hif_rtpm_ctx->clients[i];
382*5113495bSYour Name 		if (client) {
383*5113495bSYour Name 			if (qdf_atomic_read(&client->active_count)) {
384*5113495bSYour Name 				active_count =
385*5113495bSYour Name 					qdf_atomic_read(&client->active_count);
386*5113495bSYour Name 				hif_err("Client active: %u- %s", i,
387*5113495bSYour Name 					hif_rtpm_id_to_string(i));
388*5113495bSYour Name 				QDF_DEBUG_PANIC("Client active on exit!");
389*5113495bSYour Name 				while (active_count--)
390*5113495bSYour Name 					__hif_rtpm_put_noidle(
391*5113495bSYour Name 							gp_hif_rtpm_ctx->dev);
392*5113495bSYour Name 			}
393*5113495bSYour Name 			QDF_DEBUG_PANIC("Client not deinitialized");
394*5113495bSYour Name 			qdf_mem_free(client);
395*5113495bSYour Name 			gp_hif_rtpm_ctx->clients[i] = NULL;
396*5113495bSYour Name 		}
397*5113495bSYour Name 	}
398*5113495bSYour Name }
399*5113495bSYour Name 
400*5113495bSYour Name /**
401*5113495bSYour Name  * hif_rtpm_sanitize_ssr_exit() - Empty the suspend list on SSR
402*5113495bSYour Name  *
403*5113495bSYour Name  * API is used to empty the runtime pm prevent suspend list.
404*5113495bSYour Name  *
405*5113495bSYour Name  * Return: void
406*5113495bSYour Name  */
hif_rtpm_sanitize_ssr_exit(void)407*5113495bSYour Name static void hif_rtpm_sanitize_ssr_exit(void)
408*5113495bSYour Name {
409*5113495bSYour Name 	struct hif_pm_runtime_lock *ctx, *tmp;
410*5113495bSYour Name 
411*5113495bSYour Name 	qdf_spin_lock_bh(&gp_hif_rtpm_ctx->prevent_list_lock);
412*5113495bSYour Name 	list_for_each_entry_safe(ctx, tmp,
413*5113495bSYour Name 				 &gp_hif_rtpm_ctx->prevent_list, list) {
414*5113495bSYour Name 		__hif_pm_runtime_allow_suspend(ctx);
415*5113495bSYour Name 	}
416*5113495bSYour Name 	qdf_spin_unlock_bh(&gp_hif_rtpm_ctx->prevent_list_lock);
417*5113495bSYour Name }
418*5113495bSYour Name 
hif_rtpm_close(struct hif_softc * scn)419*5113495bSYour Name void hif_rtpm_close(struct hif_softc *scn)
420*5113495bSYour Name {
421*5113495bSYour Name 	hif_rtpm_free_last_busy_hist();
422*5113495bSYour Name 	hif_rtpm_deregister(HIF_RTPM_ID_CE);
423*5113495bSYour Name 	hif_rtpm_deregister(HIF_RTPM_ID_FORCE_WAKE);
424*5113495bSYour Name 
425*5113495bSYour Name 	hif_is_recovery_in_progress(scn) ?
426*5113495bSYour Name 		hif_rtpm_sanitize_ssr_exit() :
427*5113495bSYour Name 		hif_rtpm_sanitize_exit();
428*5113495bSYour Name 
429*5113495bSYour Name 	qdf_mem_set(gp_hif_rtpm_ctx, sizeof(*gp_hif_rtpm_ctx), 0);
430*5113495bSYour Name 	gp_hif_rtpm_ctx = NULL;
431*5113495bSYour Name 	hif_info_high("Runtime PM context detached");
432*5113495bSYour Name }
433*5113495bSYour Name 
hif_set_enable_rpm(struct hif_opaque_softc * hif_hdl)434*5113495bSYour Name void hif_set_enable_rpm(struct hif_opaque_softc *hif_hdl)
435*5113495bSYour Name {
436*5113495bSYour Name 	struct hif_softc *scn = HIF_GET_SOFTC(hif_hdl);
437*5113495bSYour Name 
438*5113495bSYour Name 	gp_hif_rtpm_ctx->enable_rpm = scn->hif_config.enable_runtime_pm;
439*5113495bSYour Name }
440*5113495bSYour Name 
hif_rtpm_start(struct hif_softc * scn)441*5113495bSYour Name void hif_rtpm_start(struct hif_softc *scn)
442*5113495bSYour Name {
443*5113495bSYour Name 	uint32_t mode = hif_get_conparam(scn);
444*5113495bSYour Name 
445*5113495bSYour Name 	if (!gp_hif_rtpm_ctx->enable_rpm) {
446*5113495bSYour Name 		hif_info_high("RUNTIME PM is disabled in ini");
447*5113495bSYour Name 		return;
448*5113495bSYour Name 	}
449*5113495bSYour Name 
450*5113495bSYour Name 	if (mode == QDF_GLOBAL_FTM_MODE || QDF_IS_EPPING_ENABLED(mode) ||
451*5113495bSYour Name 	    mode == QDF_GLOBAL_MONITOR_MODE) {
452*5113495bSYour Name 		hif_info("RUNTIME PM is disabled for FTM/EPPING/MONITOR mode");
453*5113495bSYour Name 		return;
454*5113495bSYour Name 	}
455*5113495bSYour Name 
456*5113495bSYour Name 	hif_info_high("Enabling RUNTIME PM, Delay: %d ms",
457*5113495bSYour Name 		      scn->hif_config.runtime_pm_delay);
458*5113495bSYour Name 
459*5113495bSYour Name 	qdf_atomic_set(&gp_hif_rtpm_ctx->pm_state, HIF_RTPM_STATE_ON);
460*5113495bSYour Name 	hif_rtpm_init(gp_hif_rtpm_ctx->dev, scn->hif_config.runtime_pm_delay);
461*5113495bSYour Name 	gp_hif_rtpm_ctx->cfg_delay = scn->hif_config.runtime_pm_delay;
462*5113495bSYour Name 	gp_hif_rtpm_ctx->delay = gp_hif_rtpm_ctx->cfg_delay;
463*5113495bSYour Name 	hif_rtpm_debugfs_create();
464*5113495bSYour Name }
465*5113495bSYour Name 
hif_rtpm_stop(struct hif_softc * scn)466*5113495bSYour Name void hif_rtpm_stop(struct hif_softc *scn)
467*5113495bSYour Name {
468*5113495bSYour Name 	uint32_t mode = hif_get_conparam(scn);
469*5113495bSYour Name 
470*5113495bSYour Name 	if (!gp_hif_rtpm_ctx->enable_rpm)
471*5113495bSYour Name 		return;
472*5113495bSYour Name 
473*5113495bSYour Name 	if (mode == QDF_GLOBAL_FTM_MODE || QDF_IS_EPPING_ENABLED(mode) ||
474*5113495bSYour Name 	    mode == QDF_GLOBAL_MONITOR_MODE)
475*5113495bSYour Name 		return;
476*5113495bSYour Name 
477*5113495bSYour Name 	hif_rtpm_exit(gp_hif_rtpm_ctx->dev);
478*5113495bSYour Name 
479*5113495bSYour Name 	hif_rtpm_sync_resume();
480*5113495bSYour Name 
481*5113495bSYour Name 	qdf_atomic_set(&gp_hif_rtpm_ctx->pm_state, HIF_RTPM_STATE_NONE);
482*5113495bSYour Name 	hif_rtpm_debugfs_remove();
483*5113495bSYour Name }
484*5113495bSYour Name 
hif_rtpm_register(uint32_t id,void (* hif_rtpm_cbk)(void))485*5113495bSYour Name QDF_STATUS hif_rtpm_register(uint32_t id, void (*hif_rtpm_cbk)(void))
486*5113495bSYour Name {
487*5113495bSYour Name 	struct hif_rtpm_client *client;
488*5113495bSYour Name 
489*5113495bSYour Name 	if (qdf_unlikely(!gp_hif_rtpm_ctx)) {
490*5113495bSYour Name 		hif_err("Runtime PM context NULL");
491*5113495bSYour Name 		return QDF_STATUS_E_FAILURE;
492*5113495bSYour Name 	}
493*5113495bSYour Name 
494*5113495bSYour Name 	if (id >= HIF_RTPM_ID_MAX || gp_hif_rtpm_ctx->clients[id]) {
495*5113495bSYour Name 		hif_err("Invalid client %d", id);
496*5113495bSYour Name 		return QDF_STATUS_E_INVAL;
497*5113495bSYour Name 	}
498*5113495bSYour Name 
499*5113495bSYour Name 	client = qdf_mem_malloc(sizeof(struct hif_rtpm_client));
500*5113495bSYour Name 	if (!client)
501*5113495bSYour Name 		return QDF_STATUS_E_NOMEM;
502*5113495bSYour Name 
503*5113495bSYour Name 	client->hif_rtpm_cbk = hif_rtpm_cbk;
504*5113495bSYour Name 	qdf_atomic_init(&client->active_count);
505*5113495bSYour Name 	qdf_atomic_init(&client->get_count);
506*5113495bSYour Name 	qdf_atomic_init(&client->put_count);
507*5113495bSYour Name 
508*5113495bSYour Name 	gp_hif_rtpm_ctx->clients[id] = client;
509*5113495bSYour Name 	gp_hif_rtpm_ctx->client_count++;
510*5113495bSYour Name 
511*5113495bSYour Name 	return QDF_STATUS_SUCCESS;
512*5113495bSYour Name }
513*5113495bSYour Name 
hif_rtpm_deregister(uint32_t id)514*5113495bSYour Name QDF_STATUS hif_rtpm_deregister(uint32_t id)
515*5113495bSYour Name {
516*5113495bSYour Name 	struct hif_rtpm_client *client;
517*5113495bSYour Name 	int active_count;
518*5113495bSYour Name 
519*5113495bSYour Name 	if (qdf_unlikely(!gp_hif_rtpm_ctx)) {
520*5113495bSYour Name 		hif_err("Runtime PM context NULL");
521*5113495bSYour Name 		return QDF_STATUS_E_FAILURE;
522*5113495bSYour Name 	}
523*5113495bSYour Name 
524*5113495bSYour Name 	if (id >= HIF_RTPM_ID_MAX || !gp_hif_rtpm_ctx->clients[id]) {
525*5113495bSYour Name 		hif_err("invalid client, id: %u", id);
526*5113495bSYour Name 		return QDF_STATUS_E_INVAL;
527*5113495bSYour Name 	}
528*5113495bSYour Name 
529*5113495bSYour Name 	client = gp_hif_rtpm_ctx->clients[id];
530*5113495bSYour Name 	if (qdf_atomic_read(&client->active_count)) {
531*5113495bSYour Name 		active_count = qdf_atomic_read(&client->active_count);
532*5113495bSYour Name 		hif_err("Client: %u-%s Runtime PM active",
533*5113495bSYour Name 			id, hif_rtpm_id_to_string(id));
534*5113495bSYour Name 		hif_err("last get called: 0x%llx, get count: %d, put count: %d",
535*5113495bSYour Name 			client->get_ts, qdf_atomic_read(&client->get_count),
536*5113495bSYour Name 			qdf_atomic_read(&client->put_count));
537*5113495bSYour Name 		QDF_DEBUG_PANIC("Get and PUT call out of sync!");
538*5113495bSYour Name 		while (active_count--)
539*5113495bSYour Name 			__hif_rtpm_put_noidle(gp_hif_rtpm_ctx->dev);
540*5113495bSYour Name 	}
541*5113495bSYour Name 
542*5113495bSYour Name 	qdf_mem_free(client);
543*5113495bSYour Name 	gp_hif_rtpm_ctx->clients[id] = NULL;
544*5113495bSYour Name 
545*5113495bSYour Name 	return QDF_STATUS_SUCCESS;
546*5113495bSYour Name }
547*5113495bSYour Name 
hif_rtpm_set_autosuspend_delay(int delay)548*5113495bSYour Name QDF_STATUS hif_rtpm_set_autosuspend_delay(int delay)
549*5113495bSYour Name {
550*5113495bSYour Name 	if (delay < HIF_RTPM_DELAY_MIN || delay > HIF_RTPM_DELAY_MAX) {
551*5113495bSYour Name 		hif_err("Invalid delay value %d ms", delay);
552*5113495bSYour Name 		return QDF_STATUS_E_INVAL;
553*5113495bSYour Name 	}
554*5113495bSYour Name 
555*5113495bSYour Name 	__hif_rtpm_set_autosuspend_delay(gp_hif_rtpm_ctx->dev, delay);
556*5113495bSYour Name 	gp_hif_rtpm_ctx->delay = delay;
557*5113495bSYour Name 	hif_info_high("RTPM delay set: %d ms", delay);
558*5113495bSYour Name 
559*5113495bSYour Name 	return QDF_STATUS_SUCCESS;
560*5113495bSYour Name }
561*5113495bSYour Name 
hif_rtpm_restore_autosuspend_delay(void)562*5113495bSYour Name QDF_STATUS hif_rtpm_restore_autosuspend_delay(void)
563*5113495bSYour Name {
564*5113495bSYour Name 	if (gp_hif_rtpm_ctx->delay == gp_hif_rtpm_ctx->cfg_delay) {
565*5113495bSYour Name 		hif_info_rl("RTPM delay already default: %d",
566*5113495bSYour Name 			    gp_hif_rtpm_ctx->delay);
567*5113495bSYour Name 		return QDF_STATUS_E_ALREADY;
568*5113495bSYour Name 	}
569*5113495bSYour Name 
570*5113495bSYour Name 	__hif_rtpm_set_autosuspend_delay(gp_hif_rtpm_ctx->dev,
571*5113495bSYour Name 					 gp_hif_rtpm_ctx->cfg_delay);
572*5113495bSYour Name 	gp_hif_rtpm_ctx->delay = gp_hif_rtpm_ctx->cfg_delay;
573*5113495bSYour Name 	hif_info_rl("RTPM delay set: %d ms", gp_hif_rtpm_ctx->delay);
574*5113495bSYour Name 
575*5113495bSYour Name 	return QDF_STATUS_SUCCESS;
576*5113495bSYour Name }
577*5113495bSYour Name 
hif_rtpm_get_autosuspend_delay(void)578*5113495bSYour Name int hif_rtpm_get_autosuspend_delay(void)
579*5113495bSYour Name {
580*5113495bSYour Name 	return gp_hif_rtpm_ctx->delay;
581*5113495bSYour Name }
582*5113495bSYour Name 
hif_runtime_lock_init(qdf_runtime_lock_t * lock,const char * name)583*5113495bSYour Name int hif_runtime_lock_init(qdf_runtime_lock_t *lock, const char *name)
584*5113495bSYour Name {
585*5113495bSYour Name 	struct hif_pm_runtime_lock *context;
586*5113495bSYour Name 
587*5113495bSYour Name 	if (qdf_unlikely(!gp_hif_rtpm_ctx)) {
588*5113495bSYour Name 		hif_err("Runtime PM context NULL");
589*5113495bSYour Name 		return QDF_STATUS_E_FAILURE;
590*5113495bSYour Name 	}
591*5113495bSYour Name 
592*5113495bSYour Name 	hif_debug("Initializing Runtime PM wakelock %s", name);
593*5113495bSYour Name 
594*5113495bSYour Name 	context = qdf_mem_malloc(sizeof(*context));
595*5113495bSYour Name 	if (!context)
596*5113495bSYour Name 		return -ENOMEM;
597*5113495bSYour Name 
598*5113495bSYour Name 	context->name = name ? name : "Default";
599*5113495bSYour Name 	lock->lock = context;
600*5113495bSYour Name 
601*5113495bSYour Name 	return 0;
602*5113495bSYour Name }
603*5113495bSYour Name 
hif_runtime_lock_deinit(struct hif_pm_runtime_lock * lock)604*5113495bSYour Name void hif_runtime_lock_deinit(struct hif_pm_runtime_lock *lock)
605*5113495bSYour Name {
606*5113495bSYour Name 	if (!lock) {
607*5113495bSYour Name 		hif_err("Runtime PM lock already freed");
608*5113495bSYour Name 		return;
609*5113495bSYour Name 	}
610*5113495bSYour Name 
611*5113495bSYour Name 	hif_debug("Deinitializing Runtime PM wakelock %s", lock->name);
612*5113495bSYour Name 
613*5113495bSYour Name 	if (gp_hif_rtpm_ctx) {
614*5113495bSYour Name 		qdf_spin_lock_bh(&gp_hif_rtpm_ctx->prevent_list_lock);
615*5113495bSYour Name 		__hif_pm_runtime_allow_suspend(lock);
616*5113495bSYour Name 		qdf_spin_unlock_bh(&gp_hif_rtpm_ctx->prevent_list_lock);
617*5113495bSYour Name 	}
618*5113495bSYour Name 
619*5113495bSYour Name 	qdf_mem_free(lock);
620*5113495bSYour Name }
621*5113495bSYour Name 
622*5113495bSYour Name /**
623*5113495bSYour Name  * hif_rtpm_enabled() - To check if Runtime PM is enabled
624*5113495bSYour Name  *
625*5113495bSYour Name  * This function will check if Runtime PM is enabled or not.
626*5113495bSYour Name  *
627*5113495bSYour Name  * Return: void
628*5113495bSYour Name  */
hif_rtpm_enabled(void)629*5113495bSYour Name static bool hif_rtpm_enabled(void)
630*5113495bSYour Name {
631*5113495bSYour Name 	if (qdf_unlikely(!gp_hif_rtpm_ctx))
632*5113495bSYour Name 		return false;
633*5113495bSYour Name 
634*5113495bSYour Name 	if (gp_hif_rtpm_ctx->enable_rpm)
635*5113495bSYour Name 		return true;
636*5113495bSYour Name 
637*5113495bSYour Name 	return __hif_rtpm_enabled(gp_hif_rtpm_ctx->dev);
638*5113495bSYour Name }
639*5113495bSYour Name 
hif_rtpm_get(uint8_t type,uint32_t id)640*5113495bSYour Name QDF_STATUS hif_rtpm_get(uint8_t type, uint32_t id)
641*5113495bSYour Name {
642*5113495bSYour Name 	struct hif_rtpm_client *client = NULL;
643*5113495bSYour Name 	int ret = QDF_STATUS_E_FAILURE;
644*5113495bSYour Name 	int pm_state;
645*5113495bSYour Name 
646*5113495bSYour Name 	if (!hif_rtpm_enabled())
647*5113495bSYour Name 		return QDF_STATUS_SUCCESS;
648*5113495bSYour Name 
649*5113495bSYour Name 	if (id >= HIF_RTPM_ID_MAX || !gp_hif_rtpm_ctx->clients[id]) {
650*5113495bSYour Name 		QDF_DEBUG_PANIC("Invalid client, id: %u", id);
651*5113495bSYour Name 		return -QDF_STATUS_E_INVAL;
652*5113495bSYour Name 	}
653*5113495bSYour Name 
654*5113495bSYour Name 	client = gp_hif_rtpm_ctx->clients[id];
655*5113495bSYour Name 
656*5113495bSYour Name 	if (type != HIF_RTPM_GET_ASYNC) {
657*5113495bSYour Name 		switch (type) {
658*5113495bSYour Name 		case HIF_RTPM_GET_FORCE:
659*5113495bSYour Name 			ret = __hif_rtpm_get(gp_hif_rtpm_ctx->dev);
660*5113495bSYour Name 			break;
661*5113495bSYour Name 		case HIF_RTPM_GET_SYNC:
662*5113495bSYour Name 			ret = __hif_rtpm_get_sync(gp_hif_rtpm_ctx->dev);
663*5113495bSYour Name 			break;
664*5113495bSYour Name 		case HIF_RTPM_GET_NORESUME:
665*5113495bSYour Name 			__hif_rtpm_get_noresume(gp_hif_rtpm_ctx->dev);
666*5113495bSYour Name 			ret = 0;
667*5113495bSYour Name 			break;
668*5113495bSYour Name 		default:
669*5113495bSYour Name 			QDF_DEBUG_PANIC("Invalid call type");
670*5113495bSYour Name 			return QDF_STATUS_E_BADMSG;
671*5113495bSYour Name 		}
672*5113495bSYour Name 
673*5113495bSYour Name 		if (ret < 0 && ret != -EINPROGRESS) {
674*5113495bSYour Name 			hif_err("pm_state: %d ret: %d",
675*5113495bSYour Name 				qdf_atomic_read(&gp_hif_rtpm_ctx->pm_state),
676*5113495bSYour Name 				ret);
677*5113495bSYour Name 			__hif_rtpm_put_noidle(gp_hif_rtpm_ctx->dev);
678*5113495bSYour Name 		} else {
679*5113495bSYour Name 			ret = QDF_STATUS_SUCCESS;
680*5113495bSYour Name 		}
681*5113495bSYour Name 		goto out;
682*5113495bSYour Name 	}
683*5113495bSYour Name 
684*5113495bSYour Name 	pm_state = qdf_atomic_read(&gp_hif_rtpm_ctx->pm_state);
685*5113495bSYour Name 	if (pm_state <= HIF_RTPM_STATE_RESUMING_LINKUP) {
686*5113495bSYour Name 		ret = __hif_rtpm_get(gp_hif_rtpm_ctx->dev);
687*5113495bSYour Name 		/* Get will return 1 if the device is already active,
688*5113495bSYour Name 		 * just return success in that case
689*5113495bSYour Name 		 */
690*5113495bSYour Name 		if (ret > 0) {
691*5113495bSYour Name 			ret = QDF_STATUS_SUCCESS;
692*5113495bSYour Name 		} else if (ret == 0 || ret == -EINPROGRESS) {
693*5113495bSYour Name 			qdf_spin_lock_bh(&gp_hif_rtpm_ctx->runtime_lock);
694*5113495bSYour Name 			pm_state = qdf_atomic_read(&gp_hif_rtpm_ctx->pm_state);
695*5113495bSYour Name 			if (pm_state >= HIF_RTPM_STATE_RESUMING) {
696*5113495bSYour Name 				__hif_rtpm_put_noidle(gp_hif_rtpm_ctx->dev);
697*5113495bSYour Name 				gp_hif_rtpm_ctx->stats.request_resume_ts =
698*5113495bSYour Name 							qdf_get_log_timestamp();
699*5113495bSYour Name 				gp_hif_rtpm_ctx->stats.request_resume_id = id;
700*5113495bSYour Name 				ret = QDF_STATUS_E_FAILURE;
701*5113495bSYour Name 			} else {
702*5113495bSYour Name 				ret = QDF_STATUS_SUCCESS;
703*5113495bSYour Name 			}
704*5113495bSYour Name 			qdf_spin_unlock_bh(&gp_hif_rtpm_ctx->runtime_lock);
705*5113495bSYour Name 		} else if (ret < 0) {
706*5113495bSYour Name 			hif_err("pm_state: %d ret: %d",
707*5113495bSYour Name 				qdf_atomic_read(&gp_hif_rtpm_ctx->pm_state),
708*5113495bSYour Name 				ret);
709*5113495bSYour Name 			__hif_rtpm_put_noidle(gp_hif_rtpm_ctx->dev);
710*5113495bSYour Name 		}
711*5113495bSYour Name 	} else if (pm_state >= HIF_RTPM_STATE_RESUMING) {
712*5113495bSYour Name 		/* Do not log in performance path */
713*5113495bSYour Name 		if (id != HIF_RTPM_ID_DP)
714*5113495bSYour Name 			hif_info_high("request RTPM resume by %d- %s",
715*5113495bSYour Name 				      id, hif_rtpm_id_to_string(id));
716*5113495bSYour Name 		__hif_rtpm_request_resume(gp_hif_rtpm_ctx->dev);
717*5113495bSYour Name 		gp_hif_rtpm_ctx->stats.request_resume_ts =
718*5113495bSYour Name 						qdf_get_log_timestamp();
719*5113495bSYour Name 		gp_hif_rtpm_ctx->stats.request_resume_id = id;
720*5113495bSYour Name 		return QDF_STATUS_E_FAILURE;
721*5113495bSYour Name 	}
722*5113495bSYour Name 
723*5113495bSYour Name out:
724*5113495bSYour Name 	if (QDF_IS_STATUS_SUCCESS(ret)) {
725*5113495bSYour Name 		qdf_atomic_inc(&client->active_count);
726*5113495bSYour Name 		qdf_atomic_inc(&client->get_count);
727*5113495bSYour Name 		client->get_ts = qdf_get_log_timestamp();
728*5113495bSYour Name 	}
729*5113495bSYour Name 
730*5113495bSYour Name 	return ret;
731*5113495bSYour Name }
732*5113495bSYour Name 
hif_rtpm_put(uint8_t type,uint32_t id)733*5113495bSYour Name QDF_STATUS hif_rtpm_put(uint8_t type, uint32_t id)
734*5113495bSYour Name {
735*5113495bSYour Name 	struct hif_rtpm_client *client;
736*5113495bSYour Name 	int usage_count;
737*5113495bSYour Name 
738*5113495bSYour Name 	if (!hif_rtpm_enabled())
739*5113495bSYour Name 		return QDF_STATUS_SUCCESS;
740*5113495bSYour Name 
741*5113495bSYour Name 	if (id >= HIF_RTPM_ID_MAX || !gp_hif_rtpm_ctx->clients[id]) {
742*5113495bSYour Name 		hif_err("Invalid client, id: %u", id);
743*5113495bSYour Name 		return QDF_STATUS_E_INVAL;
744*5113495bSYour Name 	}
745*5113495bSYour Name 
746*5113495bSYour Name 	client = gp_hif_rtpm_ctx->clients[id];
747*5113495bSYour Name 
748*5113495bSYour Name 	usage_count = hif_rtpm_read_usage_count();
749*5113495bSYour Name 	if (usage_count == 2 && !gp_hif_rtpm_ctx->enable_rpm) {
750*5113495bSYour Name 		hif_err("Unexpected PUT when runtime PM is disabled");
751*5113495bSYour Name 		QDF_BUG(0);
752*5113495bSYour Name 		return QDF_STATUS_E_CANCELED;
753*5113495bSYour Name 	} else if (!usage_count || !qdf_atomic_read(&client->active_count)) {
754*5113495bSYour Name 		hif_info_high("Put without a Get operation, %u-%s",
755*5113495bSYour Name 			      id, hif_rtpm_id_to_string(id));
756*5113495bSYour Name 		return QDF_STATUS_E_CANCELED;
757*5113495bSYour Name 	}
758*5113495bSYour Name 
759*5113495bSYour Name 	switch (type) {
760*5113495bSYour Name 	case HIF_RTPM_PUT_ASYNC:
761*5113495bSYour Name 		__hif_rtpm_put_auto(gp_hif_rtpm_ctx->dev);
762*5113495bSYour Name 		break;
763*5113495bSYour Name 	case HIF_RTPM_PUT_NOIDLE:
764*5113495bSYour Name 		__hif_rtpm_put_noidle(gp_hif_rtpm_ctx->dev);
765*5113495bSYour Name 		break;
766*5113495bSYour Name 	case HIF_RTPM_PUT_SYNC_SUSPEND:
767*5113495bSYour Name 		__hif_rtpm_put_sync_suspend(gp_hif_rtpm_ctx->dev);
768*5113495bSYour Name 		break;
769*5113495bSYour Name 	default:
770*5113495bSYour Name 		QDF_DEBUG_PANIC("Invalid call type");
771*5113495bSYour Name 		return QDF_STATUS_E_BADMSG;
772*5113495bSYour Name 	}
773*5113495bSYour Name 
774*5113495bSYour Name 	__hif_rtpm_mark_last_busy(gp_hif_rtpm_ctx->dev);
775*5113495bSYour Name 	qdf_atomic_dec(&client->active_count);
776*5113495bSYour Name 	qdf_atomic_inc(&client->put_count);
777*5113495bSYour Name 	client->put_ts = qdf_get_log_timestamp();
778*5113495bSYour Name 	gp_hif_rtpm_ctx->stats.last_busy_ts = client->put_ts;
779*5113495bSYour Name 
780*5113495bSYour Name 	return QDF_STATUS_SUCCESS;
781*5113495bSYour Name }
782*5113495bSYour Name 
783*5113495bSYour Name /**
784*5113495bSYour Name  * __hif_pm_runtime_prevent_suspend() - prevent runtime suspend for a protocol
785*5113495bSYour Name  *                                      reason
786*5113495bSYour Name  * @lock: runtime_pm lock being acquired
787*5113495bSYour Name  *
788*5113495bSYour Name  * Return: 0 if successful.
789*5113495bSYour Name  */
__hif_pm_runtime_prevent_suspend(struct hif_pm_runtime_lock * lock)790*5113495bSYour Name static int __hif_pm_runtime_prevent_suspend(struct hif_pm_runtime_lock *lock)
791*5113495bSYour Name {
792*5113495bSYour Name 	int ret = 0;
793*5113495bSYour Name 
794*5113495bSYour Name 	if (lock->active)
795*5113495bSYour Name 		return 0;
796*5113495bSYour Name 
797*5113495bSYour Name 	ret = __hif_rtpm_get(gp_hif_rtpm_ctx->dev);
798*5113495bSYour Name 
799*5113495bSYour Name 	/**
800*5113495bSYour Name 	 * The ret can be -EINPROGRESS, if Runtime status is RPM_RESUMING or
801*5113495bSYour Name 	 * RPM_SUSPENDING. Any other negative value is an error.
802*5113495bSYour Name 	 * We shouldn't do runtime_put here as in later point allow
803*5113495bSYour Name 	 * suspend gets called with the context and there the usage count
804*5113495bSYour Name 	 * is decremented, so suspend will be prevented.
805*5113495bSYour Name 	 */
806*5113495bSYour Name 	if (ret < 0 && ret != -EINPROGRESS) {
807*5113495bSYour Name 		gp_hif_rtpm_ctx->stats.runtime_get_err++;
808*5113495bSYour Name 		hif_err("pm_state: %d ret: %d",
809*5113495bSYour Name 			qdf_atomic_read(&gp_hif_rtpm_ctx->pm_state),
810*5113495bSYour Name 			ret);
811*5113495bSYour Name 	}
812*5113495bSYour Name 
813*5113495bSYour Name 	list_add_tail(&lock->list, &gp_hif_rtpm_ctx->prevent_list);
814*5113495bSYour Name 	lock->active = true;
815*5113495bSYour Name 	gp_hif_rtpm_ctx->prevent_cnt++;
816*5113495bSYour Name 	gp_hif_rtpm_ctx->stats.prevent_suspend++;
817*5113495bSYour Name 	return ret;
818*5113495bSYour Name }
819*5113495bSYour Name 
820*5113495bSYour Name /**
821*5113495bSYour Name  * __hif_pm_runtime_allow_suspend() - Allow Runtime suspend
822*5113495bSYour Name  * @lock: runtime pm lock
823*5113495bSYour Name  *
824*5113495bSYour Name  * This function will allow runtime suspend, by decrementing
825*5113495bSYour Name  * device's usage count.
826*5113495bSYour Name  *
827*5113495bSYour Name  * Return: status
828*5113495bSYour Name  */
__hif_pm_runtime_allow_suspend(struct hif_pm_runtime_lock * lock)829*5113495bSYour Name static int __hif_pm_runtime_allow_suspend(struct hif_pm_runtime_lock *lock)
830*5113495bSYour Name {
831*5113495bSYour Name 	int ret = 0;
832*5113495bSYour Name 	int usage_count;
833*5113495bSYour Name 
834*5113495bSYour Name 	if (gp_hif_rtpm_ctx->prevent_cnt == 0 || !lock->active)
835*5113495bSYour Name 		return ret;
836*5113495bSYour Name 
837*5113495bSYour Name 	usage_count = hif_rtpm_read_usage_count();
838*5113495bSYour Name 	/*
839*5113495bSYour Name 	 * For runtime PM enabled case, the usage count should never be 0
840*5113495bSYour Name 	 * at this point. For runtime PM disabled case, it should never be
841*5113495bSYour Name 	 * 2 at this point. Catch unexpected PUT without GET here.
842*5113495bSYour Name 	 */
843*5113495bSYour Name 	if (usage_count == 2 && !gp_hif_rtpm_ctx->enable_rpm) {
844*5113495bSYour Name 		hif_err("Unexpected PUT when runtime PM is disabled");
845*5113495bSYour Name 		QDF_BUG(0);
846*5113495bSYour Name 		return QDF_STATUS_E_CANCELED;
847*5113495bSYour Name 	} else if (!usage_count) {
848*5113495bSYour Name 		hif_info_high("Put without a Get operation, %s", lock->name);
849*5113495bSYour Name 		return QDF_STATUS_E_CANCELED;
850*5113495bSYour Name 	}
851*5113495bSYour Name 
852*5113495bSYour Name 	hif_rtpm_mark_last_busy(HIF_RTPM_ID_RESERVED);
853*5113495bSYour Name 	ret = __hif_rtpm_put_auto(gp_hif_rtpm_ctx->dev);
854*5113495bSYour Name 
855*5113495bSYour Name 	list_del(&lock->list);
856*5113495bSYour Name 	lock->active = false;
857*5113495bSYour Name 	gp_hif_rtpm_ctx->prevent_cnt--;
858*5113495bSYour Name 	gp_hif_rtpm_ctx->stats.allow_suspend++;
859*5113495bSYour Name 	return ret;
860*5113495bSYour Name }
861*5113495bSYour Name 
hif_pm_runtime_prevent_suspend(struct hif_pm_runtime_lock * lock)862*5113495bSYour Name int hif_pm_runtime_prevent_suspend(struct hif_pm_runtime_lock *lock)
863*5113495bSYour Name {
864*5113495bSYour Name 	if (!hif_rtpm_enabled() || !lock)
865*5113495bSYour Name 		return -EINVAL;
866*5113495bSYour Name 
867*5113495bSYour Name 	if (in_irq())
868*5113495bSYour Name 		WARN_ON(1);
869*5113495bSYour Name 
870*5113495bSYour Name 	qdf_spin_lock_bh(&gp_hif_rtpm_ctx->prevent_list_lock);
871*5113495bSYour Name 	__hif_pm_runtime_prevent_suspend(lock);
872*5113495bSYour Name 	qdf_spin_unlock_bh(&gp_hif_rtpm_ctx->prevent_list_lock);
873*5113495bSYour Name 
874*5113495bSYour Name 	if (qdf_atomic_read(&gp_hif_rtpm_ctx->pm_state) >=
875*5113495bSYour Name 		HIF_RTPM_STATE_SUSPENDING)
876*5113495bSYour Name 		hif_info_high("request RTPM resume by %s",
877*5113495bSYour Name 			      lock->name);
878*5113495bSYour Name 
879*5113495bSYour Name 	return 0;
880*5113495bSYour Name }
881*5113495bSYour Name 
882*5113495bSYour Name /**
883*5113495bSYour Name  * __hif_pm_runtime_prevent_suspend_sync() - synchronized prevent runtime
884*5113495bSYour Name  *  suspend for a protocol reason
885*5113495bSYour Name  * @lock: runtime_pm lock being acquired
886*5113495bSYour Name  *
887*5113495bSYour Name  * Return: 0 if successful.
888*5113495bSYour Name  */
889*5113495bSYour Name static
__hif_pm_runtime_prevent_suspend_sync(struct hif_pm_runtime_lock * lock)890*5113495bSYour Name int __hif_pm_runtime_prevent_suspend_sync(struct hif_pm_runtime_lock *lock)
891*5113495bSYour Name {
892*5113495bSYour Name 	int ret = 0;
893*5113495bSYour Name 
894*5113495bSYour Name 	if (lock->active)
895*5113495bSYour Name 		return 0;
896*5113495bSYour Name 
897*5113495bSYour Name 	ret = __hif_rtpm_get_sync(gp_hif_rtpm_ctx->dev);
898*5113495bSYour Name 
899*5113495bSYour Name 	/**
900*5113495bSYour Name 	 * The ret can be -EINPROGRESS, if Runtime status is RPM_RESUMING or
901*5113495bSYour Name 	 * RPM_SUSPENDING. Any other negative value is an error.
902*5113495bSYour Name 	 * We shouldn't do runtime_put here as in later point allow
903*5113495bSYour Name 	 * suspend gets called with the context and there the usage count
904*5113495bSYour Name 	 * is decremented, so suspend will be prevented.
905*5113495bSYour Name 	 */
906*5113495bSYour Name 	if (ret < 0 && ret != -EINPROGRESS) {
907*5113495bSYour Name 		gp_hif_rtpm_ctx->stats.runtime_get_err++;
908*5113495bSYour Name 		hif_err("pm_state: %d ret: %d",
909*5113495bSYour Name 			qdf_atomic_read(&gp_hif_rtpm_ctx->pm_state),
910*5113495bSYour Name 			ret);
911*5113495bSYour Name 	}
912*5113495bSYour Name 
913*5113495bSYour Name 	qdf_spin_lock_bh(&gp_hif_rtpm_ctx->prevent_list_lock);
914*5113495bSYour Name 	list_add_tail(&lock->list, &gp_hif_rtpm_ctx->prevent_list);
915*5113495bSYour Name 	lock->active = true;
916*5113495bSYour Name 	gp_hif_rtpm_ctx->prevent_cnt++;
917*5113495bSYour Name 	gp_hif_rtpm_ctx->stats.prevent_suspend++;
918*5113495bSYour Name 	qdf_spin_unlock_bh(&gp_hif_rtpm_ctx->prevent_list_lock);
919*5113495bSYour Name 
920*5113495bSYour Name 	return ret;
921*5113495bSYour Name }
922*5113495bSYour Name 
hif_pm_runtime_prevent_suspend_sync(struct hif_pm_runtime_lock * lock)923*5113495bSYour Name int hif_pm_runtime_prevent_suspend_sync(struct hif_pm_runtime_lock *lock)
924*5113495bSYour Name {
925*5113495bSYour Name 	if (!hif_rtpm_enabled())
926*5113495bSYour Name 		return 0;
927*5113495bSYour Name 
928*5113495bSYour Name 	if (!lock)
929*5113495bSYour Name 		return -EINVAL;
930*5113495bSYour Name 
931*5113495bSYour Name 	if (in_irq())
932*5113495bSYour Name 		WARN_ON(1);
933*5113495bSYour Name 
934*5113495bSYour Name 	__hif_pm_runtime_prevent_suspend_sync(lock);
935*5113495bSYour Name 
936*5113495bSYour Name 	if (qdf_atomic_read(&gp_hif_rtpm_ctx->pm_state) >=
937*5113495bSYour Name 		HIF_RTPM_STATE_SUSPENDING)
938*5113495bSYour Name 		hif_info_high("request RTPM resume by %s",
939*5113495bSYour Name 			      lock->name);
940*5113495bSYour Name 
941*5113495bSYour Name 	return 0;
942*5113495bSYour Name }
943*5113495bSYour Name 
hif_pm_runtime_allow_suspend(struct hif_pm_runtime_lock * lock)944*5113495bSYour Name int hif_pm_runtime_allow_suspend(struct hif_pm_runtime_lock *lock)
945*5113495bSYour Name {
946*5113495bSYour Name 	if (!hif_rtpm_enabled())
947*5113495bSYour Name 		return 0;
948*5113495bSYour Name 
949*5113495bSYour Name 	if (!lock)
950*5113495bSYour Name 		return -EINVAL;
951*5113495bSYour Name 
952*5113495bSYour Name 	if (in_irq())
953*5113495bSYour Name 		WARN_ON(1);
954*5113495bSYour Name 
955*5113495bSYour Name 	qdf_spin_lock_bh(&gp_hif_rtpm_ctx->prevent_list_lock);
956*5113495bSYour Name 	__hif_pm_runtime_allow_suspend(lock);
957*5113495bSYour Name 	qdf_spin_unlock_bh(&gp_hif_rtpm_ctx->prevent_list_lock);
958*5113495bSYour Name 
959*5113495bSYour Name 	return 0;
960*5113495bSYour Name }
961*5113495bSYour Name 
hif_rtpm_sync_resume(void)962*5113495bSYour Name QDF_STATUS hif_rtpm_sync_resume(void)
963*5113495bSYour Name {
964*5113495bSYour Name 	struct device *dev;
965*5113495bSYour Name 	int pm_state;
966*5113495bSYour Name 	int ret;
967*5113495bSYour Name 
968*5113495bSYour Name 	if (!hif_rtpm_enabled())
969*5113495bSYour Name 		return 0;
970*5113495bSYour Name 
971*5113495bSYour Name 	dev = gp_hif_rtpm_ctx->dev;
972*5113495bSYour Name 	pm_state = qdf_atomic_read(&gp_hif_rtpm_ctx->pm_state);
973*5113495bSYour Name 
974*5113495bSYour Name 	ret = __hif_rtpm_resume(dev);
975*5113495bSYour Name 	__hif_rtpm_mark_last_busy(dev);
976*5113495bSYour Name 
977*5113495bSYour Name 	if (ret >= 0) {
978*5113495bSYour Name 		gp_hif_rtpm_ctx->stats.resume_count++;
979*5113495bSYour Name 		gp_hif_rtpm_ctx->stats.resume_ts = qdf_get_log_timestamp();
980*5113495bSYour Name 		gp_hif_rtpm_ctx->stats.last_busy_ts =
981*5113495bSYour Name 					gp_hif_rtpm_ctx->stats.resume_ts;
982*5113495bSYour Name 		return QDF_STATUS_SUCCESS;
983*5113495bSYour Name 	}
984*5113495bSYour Name 
985*5113495bSYour Name 	hif_err("pm_state: %d, err: %d", pm_state, ret);
986*5113495bSYour Name 	return QDF_STATUS_E_FAILURE;
987*5113495bSYour Name }
988*5113495bSYour Name 
hif_rtpm_request_resume(void)989*5113495bSYour Name void hif_rtpm_request_resume(void)
990*5113495bSYour Name {
991*5113495bSYour Name 	__hif_rtpm_request_resume(gp_hif_rtpm_ctx->dev);
992*5113495bSYour Name 	hif_info_high("request RTPM resume %s", (char *)_RET_IP_);
993*5113495bSYour Name }
994*5113495bSYour Name 
hif_rtpm_check_and_request_resume(bool suspend_in_progress)995*5113495bSYour Name void hif_rtpm_check_and_request_resume(bool suspend_in_progress)
996*5113495bSYour Name {
997*5113495bSYour Name 	enum hif_rtpm_state state;
998*5113495bSYour Name 
999*5113495bSYour Name 	hif_rtpm_suspend_lock();
1000*5113495bSYour Name 	state = qdf_atomic_read(&gp_hif_rtpm_ctx->pm_state);
1001*5113495bSYour Name 	hif_rtpm_suspend_unlock();
1002*5113495bSYour Name 
1003*5113495bSYour Name 	if ((state == HIF_RTPM_STATE_SUSPENDED) ||
1004*5113495bSYour Name 	    (suspend_in_progress && (state == HIF_RTPM_STATE_SUSPENDING))) {
1005*5113495bSYour Name 		__hif_rtpm_request_resume(gp_hif_rtpm_ctx->dev);
1006*5113495bSYour Name 		gp_hif_rtpm_ctx->stats.request_resume_ts =
1007*5113495bSYour Name 						qdf_get_log_timestamp();
1008*5113495bSYour Name 		gp_hif_rtpm_ctx->stats.request_resume_id = HIF_RTPM_ID_RESERVED;
1009*5113495bSYour Name 	}
1010*5113495bSYour Name }
1011*5113495bSYour Name 
hif_rtpm_get_monitor_wake_intr(void)1012*5113495bSYour Name int hif_rtpm_get_monitor_wake_intr(void)
1013*5113495bSYour Name {
1014*5113495bSYour Name 	return qdf_atomic_read(&gp_hif_rtpm_ctx->monitor_wake_intr);
1015*5113495bSYour Name }
1016*5113495bSYour Name 
hif_rtpm_set_monitor_wake_intr(int val)1017*5113495bSYour Name void hif_rtpm_set_monitor_wake_intr(int val)
1018*5113495bSYour Name {
1019*5113495bSYour Name 	qdf_atomic_set(&gp_hif_rtpm_ctx->monitor_wake_intr, val);
1020*5113495bSYour Name }
1021*5113495bSYour Name 
hif_rtpm_display_last_busy_hist(struct hif_opaque_softc * hif_ctx)1022*5113495bSYour Name void hif_rtpm_display_last_busy_hist(struct hif_opaque_softc *hif_ctx)
1023*5113495bSYour Name {
1024*5113495bSYour Name 	struct hif_softc *scn;
1025*5113495bSYour Name 	struct hif_rtpm_ctx *rtpm_ctx = gp_hif_rtpm_ctx;
1026*5113495bSYour Name 	struct hif_rtpm_last_busy_hist *hist;
1027*5113495bSYour Name 	unsigned long cur_idx;
1028*5113495bSYour Name 	int i;
1029*5113495bSYour Name 
1030*5113495bSYour Name 	scn = HIF_GET_SOFTC(hif_ctx);
1031*5113495bSYour Name 	if (!scn)
1032*5113495bSYour Name 		return;
1033*5113495bSYour Name 
1034*5113495bSYour Name 	hif_info_high("RTPM last busy ts:%llu client:%s from:%ps",
1035*5113495bSYour Name 		      rtpm_ctx->stats.last_busy_ts,
1036*5113495bSYour Name 		      hif_rtpm_id_to_string(rtpm_ctx->stats.last_busy_id),
1037*5113495bSYour Name 		      rtpm_ctx->stats.last_busy_marker);
1038*5113495bSYour Name 
1039*5113495bSYour Name 	/*Display CE and DP clients RTPM stats*/
1040*5113495bSYour Name 	for (i = 0; i < HIF_RTPM_ID_MAX; i++) {
1041*5113495bSYour Name 		if (!rtpm_ctx->clients[i] ||
1042*5113495bSYour Name 		    (i != HIF_RTPM_ID_CE && i != HIF_RTPM_ID_DP))
1043*5113495bSYour Name 			continue;
1044*5113495bSYour Name 		hif_info_high("RTPM client:%s busy_ts:%llu get_ts:%llu put_ts:%llu get_cnt:%d put_cnt:%d",
1045*5113495bSYour Name 			      hif_rtpm_id_to_string(i),
1046*5113495bSYour Name 			      rtpm_ctx->clients[i]->last_busy_ts,
1047*5113495bSYour Name 			      rtpm_ctx->clients[i]->get_ts,
1048*5113495bSYour Name 			      rtpm_ctx->clients[i]->put_ts,
1049*5113495bSYour Name 			      qdf_atomic_read(&rtpm_ctx->clients[i]->get_count),
1050*5113495bSYour Name 			      qdf_atomic_read(&rtpm_ctx->clients[i]->put_count));
1051*5113495bSYour Name 	}
1052*5113495bSYour Name 
1053*5113495bSYour Name 	for (i = 0; i < CE_COUNT_MAX; i++) {
1054*5113495bSYour Name 		hist = gp_hif_rtpm_ctx->busy_hist[i];
1055*5113495bSYour Name 		if (!hist)
1056*5113495bSYour Name 			continue;
1057*5113495bSYour Name 		cur_idx = hist->last_busy_idx;
1058*5113495bSYour Name 
1059*5113495bSYour Name 		hif_info_high("RTPM CE-%u last busy_cnt:%lu cur_idx:%lu ts1:%llu ts2:%llu ts3:%llu ts4:%llu",
1060*5113495bSYour Name 			      i, hist->last_busy_cnt, cur_idx,
1061*5113495bSYour Name 			      hist->last_busy_ts[cur_idx & HIF_RTPM_BUSY_HIST_MASK],
1062*5113495bSYour Name 			      hist->last_busy_ts[(cur_idx + 4) & HIF_RTPM_BUSY_HIST_MASK],
1063*5113495bSYour Name 			      hist->last_busy_ts[(cur_idx + 8) & HIF_RTPM_BUSY_HIST_MASK],
1064*5113495bSYour Name 			      hist->last_busy_ts[(cur_idx + 12) & HIF_RTPM_BUSY_HIST_MASK]);
1065*5113495bSYour Name 	}
1066*5113495bSYour Name }
1067*5113495bSYour Name 
hif_rtpm_record_ce_last_busy_evt(struct hif_softc * scn,unsigned long ce_id)1068*5113495bSYour Name void hif_rtpm_record_ce_last_busy_evt(struct hif_softc *scn,
1069*5113495bSYour Name 				      unsigned long ce_id)
1070*5113495bSYour Name {
1071*5113495bSYour Name 	struct hif_rtpm_last_busy_hist *hist;
1072*5113495bSYour Name 	unsigned long idx;
1073*5113495bSYour Name 
1074*5113495bSYour Name 	if (!scn || !gp_hif_rtpm_ctx->busy_hist[ce_id])
1075*5113495bSYour Name 		return;
1076*5113495bSYour Name 
1077*5113495bSYour Name 	hist = gp_hif_rtpm_ctx->busy_hist[ce_id];
1078*5113495bSYour Name 	hist->last_busy_cnt++;
1079*5113495bSYour Name 	hist->last_busy_idx++;
1080*5113495bSYour Name 	idx = hist->last_busy_idx & HIF_RTPM_BUSY_HIST_MASK;
1081*5113495bSYour Name 	hist->last_busy_ts[idx] = qdf_get_log_timestamp();
1082*5113495bSYour Name }
1083*5113495bSYour Name 
hif_rtpm_mark_last_busy(uint32_t id)1084*5113495bSYour Name void hif_rtpm_mark_last_busy(uint32_t id)
1085*5113495bSYour Name {
1086*5113495bSYour Name 	__hif_rtpm_mark_last_busy(gp_hif_rtpm_ctx->dev);
1087*5113495bSYour Name 	gp_hif_rtpm_ctx->stats.last_busy_ts = qdf_get_log_timestamp();
1088*5113495bSYour Name 	gp_hif_rtpm_ctx->stats.last_busy_id = id;
1089*5113495bSYour Name 	gp_hif_rtpm_ctx->stats.last_busy_marker = (void *)_RET_IP_;
1090*5113495bSYour Name 	if (gp_hif_rtpm_ctx->clients[id]) {
1091*5113495bSYour Name 		gp_hif_rtpm_ctx->clients[id]->last_busy_cnt++;
1092*5113495bSYour Name 		gp_hif_rtpm_ctx->clients[id]->last_busy_ts =
1093*5113495bSYour Name 					gp_hif_rtpm_ctx->stats.last_busy_ts;
1094*5113495bSYour Name 	}
1095*5113495bSYour Name }
1096*5113495bSYour Name 
hif_rtpm_set_client_job(uint32_t client_id)1097*5113495bSYour Name void hif_rtpm_set_client_job(uint32_t client_id)
1098*5113495bSYour Name {
1099*5113495bSYour Name 	int pm_state;
1100*5113495bSYour Name 
1101*5113495bSYour Name 	if (!gp_hif_rtpm_ctx->clients[client_id])
1102*5113495bSYour Name 		return;
1103*5113495bSYour Name 
1104*5113495bSYour Name 	qdf_spin_lock_bh(&gp_hif_rtpm_ctx->runtime_lock);
1105*5113495bSYour Name 	pm_state = qdf_atomic_read(&gp_hif_rtpm_ctx->pm_state);
1106*5113495bSYour Name 	if (pm_state <= HIF_RTPM_STATE_RESUMING_LINKUP &&
1107*5113495bSYour Name 	    gp_hif_rtpm_ctx->clients[client_id]->hif_rtpm_cbk)
1108*5113495bSYour Name 		gp_hif_rtpm_ctx->clients[client_id]->hif_rtpm_cbk();
1109*5113495bSYour Name 	else
1110*5113495bSYour Name 		qdf_set_bit(client_id, &gp_hif_rtpm_ctx->pending_job);
1111*5113495bSYour Name 	qdf_spin_unlock_bh(&gp_hif_rtpm_ctx->runtime_lock);
1112*5113495bSYour Name }
1113*5113495bSYour Name 
1114*5113495bSYour Name /**
1115*5113495bSYour Name  * hif_rtpm_pending_job() - continue jobs when bus resumed
1116*5113495bSYour Name  *
1117*5113495bSYour Name  * Return: Void
1118*5113495bSYour Name  */
hif_rtpm_pending_job(void)1119*5113495bSYour Name static void hif_rtpm_pending_job(void)
1120*5113495bSYour Name {
1121*5113495bSYour Name 	int i;
1122*5113495bSYour Name 
1123*5113495bSYour Name 	for (i = 0; i < gp_hif_rtpm_ctx->client_count; i++) {
1124*5113495bSYour Name 		if (qdf_test_and_clear_bit(i, &gp_hif_rtpm_ctx->pending_job)) {
1125*5113495bSYour Name 			qdf_spin_unlock_bh(&gp_hif_rtpm_ctx->runtime_lock);
1126*5113495bSYour Name 			if (gp_hif_rtpm_ctx->clients[i]->hif_rtpm_cbk)
1127*5113495bSYour Name 				gp_hif_rtpm_ctx->clients[i]->hif_rtpm_cbk();
1128*5113495bSYour Name 			qdf_spin_lock_bh(&gp_hif_rtpm_ctx->runtime_lock);
1129*5113495bSYour Name 		}
1130*5113495bSYour Name 	}
1131*5113495bSYour Name }
1132*5113495bSYour Name 
1133*5113495bSYour Name #define PREVENT_LIST_STRING_LEN 200
1134*5113495bSYour Name 
hif_rtpm_print_prevent_list(void)1135*5113495bSYour Name void hif_rtpm_print_prevent_list(void)
1136*5113495bSYour Name {
1137*5113495bSYour Name 	struct hif_rtpm_client *client;
1138*5113495bSYour Name 	struct hif_pm_runtime_lock *ctx;
1139*5113495bSYour Name 	char *str_buf;
1140*5113495bSYour Name 	int i, prevent_list_count, len = 0;
1141*5113495bSYour Name 
1142*5113495bSYour Name 	str_buf = qdf_mem_malloc(PREVENT_LIST_STRING_LEN);
1143*5113495bSYour Name 	if (!str_buf)
1144*5113495bSYour Name 		return;
1145*5113495bSYour Name 
1146*5113495bSYour Name 	qdf_spin_lock(&gp_hif_rtpm_ctx->prevent_list_lock);
1147*5113495bSYour Name 	prevent_list_count = gp_hif_rtpm_ctx->prevent_cnt;
1148*5113495bSYour Name 	if (prevent_list_count) {
1149*5113495bSYour Name 		list_for_each_entry(ctx, &gp_hif_rtpm_ctx->prevent_list, list)
1150*5113495bSYour Name 			len += qdf_scnprintf(str_buf + len,
1151*5113495bSYour Name 				PREVENT_LIST_STRING_LEN - len,
1152*5113495bSYour Name 				"%s ", ctx->name);
1153*5113495bSYour Name 	}
1154*5113495bSYour Name 	qdf_spin_unlock(&gp_hif_rtpm_ctx->prevent_list_lock);
1155*5113495bSYour Name 
1156*5113495bSYour Name 	if (prevent_list_count)
1157*5113495bSYour Name 		hif_info_high("prevent_suspend_cnt %u, prevent_list: %s",
1158*5113495bSYour Name 			      prevent_list_count, str_buf);
1159*5113495bSYour Name 
1160*5113495bSYour Name 	qdf_mem_free(str_buf);
1161*5113495bSYour Name 
1162*5113495bSYour Name 	for (i = 0; i < HIF_RTPM_ID_MAX; i++) {
1163*5113495bSYour Name 		client = gp_hif_rtpm_ctx->clients[i];
1164*5113495bSYour Name 		if (client && qdf_atomic_read(&client->active_count))
1165*5113495bSYour Name 			hif_info_high("client: %d: %s- active count: %d", i,
1166*5113495bSYour Name 				      hif_rtpm_id_to_string(i),
1167*5113495bSYour Name 				      qdf_atomic_read(&client->active_count));
1168*5113495bSYour Name 	}
1169*5113495bSYour Name }
1170*5113495bSYour Name 
1171*5113495bSYour Name /**
1172*5113495bSYour Name  * hif_rtpm_is_suspend_allowed() - Reject suspend if client is active
1173*5113495bSYour Name  *
1174*5113495bSYour Name  * Return: True if no clients are active
1175*5113495bSYour Name  */
hif_rtpm_is_suspend_allowed(void)1176*5113495bSYour Name static bool hif_rtpm_is_suspend_allowed(void)
1177*5113495bSYour Name {
1178*5113495bSYour Name 	if (!gp_hif_rtpm_ctx || !gp_hif_rtpm_ctx->enable_rpm)
1179*5113495bSYour Name 		return false;
1180*5113495bSYour Name 
1181*5113495bSYour Name 	if (!hif_rtpm_read_usage_count())
1182*5113495bSYour Name 		return true;
1183*5113495bSYour Name 
1184*5113495bSYour Name 	return false;
1185*5113495bSYour Name }
1186*5113495bSYour Name 
hif_rtpm_suspend_lock(void)1187*5113495bSYour Name void hif_rtpm_suspend_lock(void)
1188*5113495bSYour Name {
1189*5113495bSYour Name 	qdf_spin_lock_irqsave(&gp_hif_rtpm_ctx->runtime_suspend_lock);
1190*5113495bSYour Name }
1191*5113495bSYour Name 
hif_rtpm_suspend_unlock(void)1192*5113495bSYour Name void hif_rtpm_suspend_unlock(void)
1193*5113495bSYour Name {
1194*5113495bSYour Name 	qdf_spin_unlock_irqrestore(&gp_hif_rtpm_ctx->runtime_suspend_lock);
1195*5113495bSYour Name }
1196*5113495bSYour Name 
1197*5113495bSYour Name /**
1198*5113495bSYour Name  * hif_rtpm_set_state(): utility function
1199*5113495bSYour Name  * @state: state to set
1200*5113495bSYour Name  *
1201*5113495bSYour Name  * Return: Void
1202*5113495bSYour Name  */
1203*5113495bSYour Name static inline
hif_rtpm_set_state(enum hif_rtpm_state state)1204*5113495bSYour Name void hif_rtpm_set_state(enum hif_rtpm_state state)
1205*5113495bSYour Name {
1206*5113495bSYour Name 	qdf_atomic_set(&gp_hif_rtpm_ctx->pm_state, state);
1207*5113495bSYour Name }
1208*5113495bSYour Name 
hif_rtpm_get_state(void)1209*5113495bSYour Name int hif_rtpm_get_state(void)
1210*5113495bSYour Name {
1211*5113495bSYour Name 	return qdf_atomic_read(&gp_hif_rtpm_ctx->pm_state);
1212*5113495bSYour Name }
1213*5113495bSYour Name 
hif_pre_runtime_suspend(struct hif_opaque_softc * hif_ctx)1214*5113495bSYour Name int hif_pre_runtime_suspend(struct hif_opaque_softc *hif_ctx)
1215*5113495bSYour Name {
1216*5113495bSYour Name 	if (!hif_can_suspend_link(hif_ctx)) {
1217*5113495bSYour Name 		hif_err("Runtime PM not supported for link up suspend");
1218*5113495bSYour Name 		return -EINVAL;
1219*5113495bSYour Name 	}
1220*5113495bSYour Name 
1221*5113495bSYour Name 	qdf_spin_lock_bh(&gp_hif_rtpm_ctx->runtime_lock);
1222*5113495bSYour Name 	hif_rtpm_set_state(HIF_RTPM_STATE_SUSPENDING);
1223*5113495bSYour Name 
1224*5113495bSYour Name 	/* keep this after set suspending */
1225*5113495bSYour Name 	if (!hif_rtpm_is_suspend_allowed()) {
1226*5113495bSYour Name 		qdf_spin_unlock_bh(&gp_hif_rtpm_ctx->runtime_lock);
1227*5113495bSYour Name 		hif_rtpm_print_prevent_list();
1228*5113495bSYour Name 		gp_hif_rtpm_ctx->stats.suspend_err_count++;
1229*5113495bSYour Name 		gp_hif_rtpm_ctx->stats.suspend_err_ts = qdf_get_log_timestamp();
1230*5113495bSYour Name 		hif_info_high("Runtime PM not allowed now");
1231*5113495bSYour Name 		return -EINVAL;
1232*5113495bSYour Name 	}
1233*5113495bSYour Name 
1234*5113495bSYour Name 	qdf_spin_unlock_bh(&gp_hif_rtpm_ctx->runtime_lock);
1235*5113495bSYour Name 
1236*5113495bSYour Name 	return QDF_STATUS_SUCCESS;
1237*5113495bSYour Name }
1238*5113495bSYour Name 
hif_process_runtime_suspend_success(void)1239*5113495bSYour Name void hif_process_runtime_suspend_success(void)
1240*5113495bSYour Name {
1241*5113495bSYour Name 	hif_rtpm_set_state(HIF_RTPM_STATE_SUSPENDED);
1242*5113495bSYour Name 	gp_hif_rtpm_ctx->stats.suspend_count++;
1243*5113495bSYour Name 	gp_hif_rtpm_ctx->stats.suspend_ts = qdf_get_log_timestamp();
1244*5113495bSYour Name }
1245*5113495bSYour Name 
hif_process_runtime_suspend_failure(void)1246*5113495bSYour Name void hif_process_runtime_suspend_failure(void)
1247*5113495bSYour Name {
1248*5113495bSYour Name 	qdf_spin_lock_bh(&gp_hif_rtpm_ctx->runtime_lock);
1249*5113495bSYour Name 	hif_rtpm_set_state(HIF_RTPM_STATE_ON);
1250*5113495bSYour Name 	hif_rtpm_pending_job();
1251*5113495bSYour Name 	qdf_spin_unlock_bh(&gp_hif_rtpm_ctx->runtime_lock);
1252*5113495bSYour Name 
1253*5113495bSYour Name 	gp_hif_rtpm_ctx->stats.suspend_err_count++;
1254*5113495bSYour Name 	gp_hif_rtpm_ctx->stats.suspend_err_ts = qdf_get_log_timestamp();
1255*5113495bSYour Name 	gp_hif_rtpm_ctx->stats.last_busy_ts = qdf_get_log_timestamp();
1256*5113495bSYour Name 	hif_rtpm_mark_last_busy(HIF_RTPM_ID_RESERVED);
1257*5113495bSYour Name }
1258*5113495bSYour Name 
hif_pre_runtime_resume(void)1259*5113495bSYour Name void hif_pre_runtime_resume(void)
1260*5113495bSYour Name {
1261*5113495bSYour Name 	qdf_spin_lock_bh(&gp_hif_rtpm_ctx->runtime_lock);
1262*5113495bSYour Name 	hif_rtpm_set_monitor_wake_intr(0);
1263*5113495bSYour Name 	hif_rtpm_set_state(HIF_RTPM_STATE_RESUMING);
1264*5113495bSYour Name 	qdf_spin_unlock_bh(&gp_hif_rtpm_ctx->runtime_lock);
1265*5113495bSYour Name }
1266*5113495bSYour Name 
hif_process_runtime_resume_linkup(void)1267*5113495bSYour Name void hif_process_runtime_resume_linkup(void)
1268*5113495bSYour Name {
1269*5113495bSYour Name 	qdf_spin_lock_bh(&gp_hif_rtpm_ctx->runtime_lock);
1270*5113495bSYour Name 	hif_rtpm_set_state(HIF_RTPM_STATE_RESUMING_LINKUP);
1271*5113495bSYour Name 	hif_rtpm_pending_job();
1272*5113495bSYour Name 	qdf_spin_unlock_bh(&gp_hif_rtpm_ctx->runtime_lock);
1273*5113495bSYour Name }
1274*5113495bSYour Name 
hif_process_runtime_resume_success(void)1275*5113495bSYour Name void hif_process_runtime_resume_success(void)
1276*5113495bSYour Name {
1277*5113495bSYour Name 	hif_rtpm_set_state(HIF_RTPM_STATE_ON);
1278*5113495bSYour Name 	gp_hif_rtpm_ctx->stats.resume_count++;
1279*5113495bSYour Name 	gp_hif_rtpm_ctx->stats.resume_ts = qdf_get_log_timestamp();
1280*5113495bSYour Name 	gp_hif_rtpm_ctx->stats.last_busy_ts = gp_hif_rtpm_ctx->stats.resume_ts;
1281*5113495bSYour Name 	hif_rtpm_mark_last_busy(HIF_RTPM_ID_RESERVED);
1282*5113495bSYour Name }
1283*5113495bSYour Name 
hif_runtime_suspend(struct hif_opaque_softc * hif_ctx)1284*5113495bSYour Name int hif_runtime_suspend(struct hif_opaque_softc *hif_ctx)
1285*5113495bSYour Name {
1286*5113495bSYour Name 	int errno;
1287*5113495bSYour Name 
1288*5113495bSYour Name 	errno = hif_bus_suspend(hif_ctx);
1289*5113495bSYour Name 	if (errno) {
1290*5113495bSYour Name 		hif_err("Failed bus suspend: %d", errno);
1291*5113495bSYour Name 		return errno;
1292*5113495bSYour Name 	}
1293*5113495bSYour Name 
1294*5113495bSYour Name 	hif_rtpm_set_monitor_wake_intr(1);
1295*5113495bSYour Name 
1296*5113495bSYour Name 	errno = hif_bus_suspend_noirq(hif_ctx);
1297*5113495bSYour Name 	if (errno) {
1298*5113495bSYour Name 		hif_err("Failed bus suspend noirq: %d", errno);
1299*5113495bSYour Name 		hif_rtpm_set_monitor_wake_intr(0);
1300*5113495bSYour Name 		goto bus_resume;
1301*5113495bSYour Name 	}
1302*5113495bSYour Name 
1303*5113495bSYour Name 	return 0;
1304*5113495bSYour Name 
1305*5113495bSYour Name bus_resume:
1306*5113495bSYour Name 	QDF_BUG(!hif_bus_resume(hif_ctx));
1307*5113495bSYour Name 
1308*5113495bSYour Name 	return errno;
1309*5113495bSYour Name }
1310*5113495bSYour Name 
hif_runtime_resume(struct hif_opaque_softc * hif_ctx)1311*5113495bSYour Name int hif_runtime_resume(struct hif_opaque_softc *hif_ctx)
1312*5113495bSYour Name {
1313*5113495bSYour Name 	int errno;
1314*5113495bSYour Name 
1315*5113495bSYour Name 	QDF_BUG(!hif_bus_resume_noirq(hif_ctx));
1316*5113495bSYour Name 	errno = hif_bus_resume(hif_ctx);
1317*5113495bSYour Name 	if (errno)
1318*5113495bSYour Name 		hif_err("Failed runtime resume: %d", errno);
1319*5113495bSYour Name 
1320*5113495bSYour Name 	return errno;
1321*5113495bSYour Name }
1322*5113495bSYour Name 
hif_fastpath_resume(struct hif_opaque_softc * hif_ctx)1323*5113495bSYour Name void hif_fastpath_resume(struct hif_opaque_softc *hif_ctx)
1324*5113495bSYour Name {
1325*5113495bSYour Name 	struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx);
1326*5113495bSYour Name 	struct CE_state *ce_state;
1327*5113495bSYour Name 
1328*5113495bSYour Name 	if (!scn)
1329*5113495bSYour Name 		return;
1330*5113495bSYour Name 
1331*5113495bSYour Name 	if (scn->fastpath_mode_on) {
1332*5113495bSYour Name 		if (Q_TARGET_ACCESS_BEGIN(scn) < 0)
1333*5113495bSYour Name 			return;
1334*5113495bSYour Name 
1335*5113495bSYour Name 		ce_state = scn->ce_id_to_state[CE_HTT_H2T_MSG];
1336*5113495bSYour Name 		qdf_spin_lock_bh(&ce_state->ce_index_lock);
1337*5113495bSYour Name 
1338*5113495bSYour Name 		/*war_ce_src_ring_write_idx_set */
1339*5113495bSYour Name 		CE_SRC_RING_WRITE_IDX_SET(scn, ce_state->ctrl_addr,
1340*5113495bSYour Name 					  ce_state->src_ring->write_index);
1341*5113495bSYour Name 		qdf_spin_unlock_bh(&ce_state->ce_index_lock);
1342*5113495bSYour Name 		Q_TARGET_ACCESS_END(scn);
1343*5113495bSYour Name 	}
1344*5113495bSYour Name }
1345*5113495bSYour Name #endif /* FEATURE_RUNTIME_PM */
1346