1/*
2 **************************************************************************
3 * Copyright (c) 2014,2015, The Linux Foundation. All rights reserved.
4 * Permission to use, copy, modify, and/or distribute this software for
5 * any purpose with or without fee is hereby granted, provided that the
6 * above copyright notice and this permission notice appear in all copies.
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
13 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 **************************************************************************
15 */
16
17/*
18 * nss_pm.c
19 *    NSS Power Management APIs
20 *
21 */
22#include <linux/debugfs.h>
23#include <linux/module.h>
24#include <linux/netdevice.h>
25#include <nss_hal.h>
26#include <nss_api_if.h>
27
28#if (NSS_PM_SUPPORT == 1)
29#include "nss_pm.h"
30
31/*
32 * Global NSS PM structure
33 */
34struct nss_pm_global_ctx ctx;
35
36/*
37 * Bus vector table for GMAC driver
38 */
39static struct msm_bus_paths nss_gmac_bw_level_tbl[NSS_PM_PERF_MAX_LEVELS] = {
40		[NSS_PM_PERF_LEVEL_SUSPEND] =  GMAC_BW_MBPS(0, 0),
41				/* 0 MHz to DDR, 0 MHz to TCM */
42		[NSS_PM_PERF_LEVEL_IDLE] =  GMAC_BW_MBPS(133, 5),
43				/* 133 MHz to DDR, 5 MHz to TCM */
44		[NSS_PM_PERF_LEVEL_NOMINAL] =  GMAC_BW_MBPS(200, 400),
45				/* 200 MHz to DDR, 10 MHz to TCM */
46		[NSS_PM_PERF_LEVEL_TURBO] = GMAC_BW_MBPS(266, 533),
47				/* 266 MHz to DDR, 20 MHz to TCM  */
48};
49
50/*
51 * Bus vector table for Crypto driver
52 */
53static struct msm_bus_paths nss_crypto_bw_level_tbl[NSS_PM_PERF_MAX_LEVELS] = {
54		[NSS_PM_PERF_LEVEL_SUSPEND] =  CRYPTO_BW_MBPS(0, 0),
55				/* 0 MHz to DDR, 0 MHz to TCM */
56		[NSS_PM_PERF_LEVEL_IDLE] =  CRYPTO_BW_MBPS(133, 5),
57				/* 133 MHz to DDR, 5 MHz to TCM */
58		[NSS_PM_PERF_LEVEL_NOMINAL] =  CRYPTO_BW_MBPS(200, 400),
59				/* 200 MHz to DDR, 10 MHz to TCM */
60		[NSS_PM_PERF_LEVEL_TURBO] = CRYPTO_BW_MBPS(266, 533),
61				/* 266 MHz to DDR, 20 MHz to TCM  */
62};
63
64#ifdef  NSS_PM_NETAP_GMAC_SCALING
65
66/*
67 * Bus vector table for NSS HLOS driver
68 * This requests bw for both NSS Fab0 and Fab1 on behalf of GMAC and NSS Drivers
69 */
70static struct msm_bus_paths nss_netap_bw_level_tbl[NSS_PM_PERF_MAX_LEVELS] = {
71		[NSS_PM_PERF_LEVEL_SUSPEND] =  GMAC_BW_MBPS(0, 0),
72			/* 0 MHz to DDR, 0 MHz to TCM */
73		[NSS_PM_PERF_LEVEL_IDLE] =  GMAC_BW_MBPS(133, 5),
74			/* 133 MHz to DDR, 5 MHz to TCM */
75		[NSS_PM_PERF_LEVEL_NOMINAL] =  GMAC_BW_MBPS(200, 400),
76			/* 200 MHz to DDR, 10 MHz to TCM */
77		[NSS_PM_PERF_LEVEL_TURBO] = GMAC_BW_MBPS(266, 533),
78			/* 266 MHz to DDR, 20 MHz to TCM  */
79};
80
81#else
82
83/*
84 * Bus vector table for NSS HLOS driver
85 */
86static struct msm_bus_paths nss_netap_bw_level_tbl[NSS_PM_PERF_MAX_LEVELS] = {
87		[NSS_PM_PERF_LEVEL_SUSPEND] =  NETAP_BW_MBPS(0, 0),
88			/* 0 MHz to DDR, 0 MHz to TCM */
89		[NSS_PM_PERF_LEVEL_IDLE] =  NETAP_BW_MBPS(133, 133),
90			/* 133 MHz to DDR and TCM */
91		[NSS_PM_PERF_LEVEL_NOMINAL] =  NETAP_BW_MBPS(400, 400),
92			/* 400 MHz to DDR and TCM */
93		[NSS_PM_PERF_LEVEL_TURBO] = NETAP_BW_MBPS(533, 533),
94			/* 533 MHz to DDR and TCM  */
95};
96
97#endif
98
99/*
100 * Bus Driver Platform data for GMAC, Crypto and Netap clients
101 */
102static struct msm_bus_scale_pdata nss_bus_scale[] = {
103	[NSS_PM_CLIENT_GMAC] = {
104		.usecase = nss_gmac_bw_level_tbl,
105		.num_usecases = ARRAY_SIZE(nss_gmac_bw_level_tbl),
106		.active_only = 1,
107		.name = "qca-nss-gmac",
108	},
109
110	[NSS_PM_CLIENT_CRYPTO] = {
111		.usecase = nss_crypto_bw_level_tbl,
112		.num_usecases = ARRAY_SIZE(nss_crypto_bw_level_tbl),
113		.active_only = 1,
114		.name = "qca-nss-crypto",
115	},
116
117	[NSS_PM_CLIENT_NETAP] = {
118		.usecase = nss_netap_bw_level_tbl,
119		.num_usecases = ARRAY_SIZE(nss_netap_bw_level_tbl),
120		.active_only = 1,
121		.name = "qca-nss-drv",
122	},
123};
124
125/*
126 * nss_pm_dbg_perf_level_get
127 *    debugfs hook to get the current performance level
128 */
129static int nss_pm_dbg_perf_level_get(void *data, u64 *val)
130{
131	nss_pm_client_data_t *pm_client;
132
133	pm_client = (nss_pm_client_data_t *)data;
134	*val = pm_client->current_perf_lvl;
135
136	return NSS_PM_API_SUCCESS;
137}
138
139/*
140 * nss_pm_dbg_autoscale_get
141 *    debugfs hook to get the current autoscale setting
142 */
143static int nss_pm_dbg_autoscale_get(void *data, u64 *val)
144{
145	nss_pm_client_data_t *pm_client;
146
147	pm_client = (nss_pm_client_data_t *)data;
148	*val = pm_client->auto_scale;
149
150	return NSS_PM_API_SUCCESS;
151}
152
153/*
154 * nss_pm_dbg_perf_level_set
155 *    debugfs hook to set perf level for a client
156 */
157static int nss_pm_dbg_perf_level_set(void *data, u64 val)
158{
159	uint32_t perf_level;
160
161	perf_level = (uint32_t) val;
162
163	if (perf_level >= NSS_PM_PERF_MAX_LEVELS ||
164			perf_level < NSS_PM_PERF_LEVEL_IDLE) {
165		nss_pm_warning("unsupported performance level %d \n", perf_level);
166		return NSS_PM_API_FAILED;
167	}
168
169	nss_pm_set_perf_level(data, perf_level);
170	return NSS_PM_API_SUCCESS;
171}
172
173/*
174 * nss_pm_dbg_autoscale_set
175 *    debugfs hook to enable auto scaling for a client
176 */
177static int nss_pm_dbg_autoscale_set(void *data, u64 val)
178{
179	nss_pm_client_data_t *pm_client;
180
181	if (val > 1) {
182		nss_pm_warning(" Invalid set value, valid values are 0/1 \n");
183		return NSS_PM_API_FAILED;
184	}
185
186	pm_client->auto_scale = (uint32_t)val;
187	return NSS_PM_API_SUCCESS;
188}
189
190DEFINE_SIMPLE_ATTRIBUTE(perf_level_fops, nss_pm_dbg_perf_level_get, nss_pm_dbg_perf_level_set, "%llu\n");
191
192DEFINE_SIMPLE_ATTRIBUTE(autoscale_fops, nss_pm_dbg_autoscale_get, nss_pm_dbg_autoscale_set, "%llu\n");
193#endif /** (NSS_PM_SUPPORT == 1) */
194
195/*
196 * nss_pm_client_register
197 *    Initialize GMAC specific PM parameters
198 *
199 * Creates debugfs hooks for user-space control of NSS Client PM
200 * Initializes Bus BW to Idle Perf level
201 * Returns PM handle to the caller.
202 *
203 */
204void *nss_pm_client_register(nss_pm_client_t client_id)
205{
206#if (NSS_PM_SUPPORT == 1)
207	int ret;
208	struct dentry *pm_dentry;
209	nss_pm_client_data_t *pm_client;
210
211	if (unlikely(client_id >= NSS_PM_MAX_CLIENTS))  {
212		nss_pm_warning("nss_pm_client_register invalid client id %d \n", client_id);
213		goto error;
214	}
215
216	pm_client  =  &ctx.nss_pm_client[client_id];
217
218	pm_client->bus_perf_client = msm_bus_scale_register_client(&nss_bus_scale[client_id]);
219	if (!pm_client->bus_perf_client) {
220		nss_pm_warning("unable to register bus client \n");
221		goto error;
222	}
223
224	ret = msm_bus_scale_client_update_request(pm_client->bus_perf_client, NSS_PM_PERF_LEVEL_IDLE);
225	if (ret) {
226		nss_pm_warning("initial bandwidth req failed (%d)\n", ret);
227		msm_bus_scale_unregister_client((uint32_t) pm_client->bus_perf_client);
228		goto error;
229	}
230
231	pm_client->current_perf_lvl = NSS_PM_PERF_LEVEL_IDLE;
232
233	switch (client_id) {
234		case NSS_PM_CLIENT_GMAC:
235			pm_dentry = debugfs_create_dir("gmac" , ctx.pm_dentry);
236			break;
237
238		case NSS_PM_CLIENT_CRYPTO:
239			pm_dentry = debugfs_create_dir("crypto" , ctx.pm_dentry);
240			break;
241
242		case NSS_PM_CLIENT_NETAP:
243			pm_dentry = debugfs_create_dir("netap" , ctx.pm_dentry);
244			break;
245
246		default:
247			nss_pm_warning("debugfs create failed invalid client id %d \n", client_id);
248			msm_bus_scale_unregister_client((uint32_t) pm_client->bus_perf_client);
249			goto error;
250
251	}
252
253	if (unlikely(pm_dentry == NULL)) {
254		nss_pm_info("debugfs not created for %d client pm \n", client_id);
255		goto out;
256	}
257
258	pm_client->dentry = pm_dentry;
259	pm_client->client_id = client_id;
260
261	if (!debugfs_create_file("perf_level", S_IRUGO | S_IWUSR, pm_dentry, pm_client, &perf_level_fops)) {
262		nss_pm_info("debugfs perf_level file not created for %d client pm \n", client_id);
263	}
264
265	if (!debugfs_create_file("auto-scale", S_IRUGO | S_IWUSR, pm_dentry, pm_client, &autoscale_fops)) {
266		nss_pm_info("debugfs auto-scale file not created for %d client pm \n", client_id);
267	}
268
269out:
270	return (void *)pm_client;
271error:
272#endif
273	return NULL;
274}
275EXPORT_SYMBOL(nss_pm_client_register);
276
277/*
278 * nss_pm_client_unregister
279 *    Unregister the client for any PM operations
280 */
281int nss_pm_client_unregister(nss_pm_client_t client_id)
282{
283#if (NSS_PM_SUPPORT == 1)
284	nss_pm_client_data_t *pm_client;
285
286	if (unlikely(client_id >= NSS_PM_MAX_CLIENTS))  {
287		nss_pm_warning("nss_pm_client_unregister invalid client id %d \n", client_id);
288		goto error;
289	}
290
291	pm_client  =  &ctx.nss_pm_client[client_id];
292
293	if (unlikely(pm_client == NULL))  {
294		nss_pm_warning("nss_pm_client_unregister client not registered %d \n", client_id);
295		goto error;
296	}
297
298	if (pm_client->bus_perf_client) {
299		msm_bus_scale_unregister_client((uint32_t) pm_client->bus_perf_client);
300	} else {
301		nss_pm_info("nss_pm_client_unregister: client not registered \n");
302	}
303
304	if (likely(pm_client->dentry != NULL)) {
305		debugfs_remove_recursive(pm_client->dentry);
306	}
307
308	return NSS_PM_API_SUCCESS;
309
310error:
311#endif
312	return NSS_PM_API_FAILED;
313}
314
315/*
316 * nss_pm_set_perf_level()
317 *    Sets the performance level of client specific Fabrics and Clocks to requested level
318 */
319nss_pm_interface_status_t nss_pm_set_perf_level(void *handle, nss_pm_perf_level_t lvl)
320{
321#if (NSS_PM_SUPPORT == 1)
322	int ret = 0;
323	nss_pm_client_data_t *pm_client;
324
325	pm_client = (nss_pm_client_data_t *) handle;
326	if (pm_client->current_perf_lvl == lvl) {
327		nss_pm_trace("Already at perf level %d , ignoring request \n", lvl);
328		return NSS_PM_API_SUCCESS;
329	}
330
331	if (!pm_client->bus_perf_client) {
332		nss_pm_warning("Bus driver client not registered.request failed \n");
333		return NSS_PM_API_FAILED;
334	}
335
336	/*
337	 * Do client specific operations here
338	 */
339	if (pm_client->client_id == NSS_PM_CLIENT_NETAP) {
340		if ((lvl == NSS_PM_PERF_LEVEL_TURBO) && (ctx.turbo_support == true)) {
341			/*
342			 * For turbo perf level, switch TCM source to
343			 * SRC1 to set TCM clock = 400 MHz
344			 * SRC0 and SRC1 are set to 266 and 400 MHz resp.
345			 * in nss_hal/ipq806x/nss_hal_pvt.c
346			 */
347			writel(0x3, NSSTCM_CLK_SRC_CTL);
348		} else {
349			/*
350			 * For Nominal and Idle perf level, switch to SRC0 to
351			 * set TCM clock = 266 MHz
352			 */
353			writel(0x2, NSSTCM_CLK_SRC_CTL);
354
355			if (lvl == NSS_PM_PERF_LEVEL_TURBO) {
356				lvl = NSS_PM_PERF_LEVEL_NOMINAL;
357			}
358		}
359	}
360
361	if (pm_client->client_id == NSS_PM_CLIENT_CRYPTO) {
362		if ((lvl == NSS_PM_PERF_LEVEL_TURBO) && (ctx.turbo_support == true))  {
363			/*
364			 * For Turbo mode, set Crypto core and
365			 * Fabric  port clocks to 213 MHz
366			 */
367			writel(0x23, CE5_ACLK_SRC0_NS);
368			writel(0x23, CE5_HCLK_SRC0_NS);
369			writel(0x23, CE5_CORE_CLK_SRC0_NS);
370
371			writel(0x2, CE5_ACLK_SRC_CTL);
372			writel(0x2, CE5_HCLK_SRC_CTL);
373			writel(0x2, CE5_CORE_CLK_SRC_CTL);
374		} else {
375			lvl = NSS_PM_PERF_LEVEL_NOMINAL;
376		}
377	}
378
379	/* Update bandwidth if request has changed. This may sleep. */
380	ret = msm_bus_scale_client_update_request(pm_client->bus_perf_client, lvl);
381	if (ret) {
382		nss_pm_warning("bandwidth request failed (%d)\n", ret);
383		return NSS_PM_API_FAILED;
384	}
385
386	nss_pm_info("perf level request, current: %d new: %d \n", pm_client->current_perf_lvl, lvl);
387	pm_client->current_perf_lvl = lvl;
388#endif
389	return NSS_PM_API_SUCCESS;
390}
391EXPORT_SYMBOL(nss_pm_set_perf_level);
392
393#if (NSS_PM_SUPPORT == 1)
394/*
395 * nss_pm_set_turbo()
396 *   Sets the turbo support flag globally for all clients
397 */
398void nss_pm_set_turbo() {
399
400	nss_pm_info("NSS Bus PM - Platform supports Turbo Mode \n");
401	ctx.turbo_support = true;
402}
403
404/*
405 * nss_pm_init()
406 *    Initialize NSS PM top level structures
407 */
408void nss_pm_init(void) {
409
410	nss_pm_info("NSS Bus PM (platform - IPQ806x, build - %s:%s)\n", __DATE__, __TIME__);
411
412	ctx.pm_dentry = debugfs_create_dir("qca-nss-pm", NULL);
413
414	/* Default turbo support is set to off */
415	ctx.turbo_support = false;
416
417	if (unlikely(ctx.pm_dentry == NULL)) {
418		nss_pm_warning("Failed to create qca-nss-drv directory in debugfs");
419	}
420}
421#endif
422