[ipq806x] Bus scaling APIs for all NSS drivers - HLOS, GMAC and Crypto

CRs-Fixed: 572301

Change-Id: Ibe0985f7c468c4e80478d019ac4b9e9abc0992ca
Signed-off-by: Pamidipati, Vijay <vpamidip@codeaurora.org>
diff --git a/Makefile b/Makefile
index cdc94c7..bc2cd5c 100755
--- a/Makefile
+++ b/Makefile
@@ -17,7 +17,7 @@
 # ###################################################
 
 obj-m += qca-nss-drv.o
-qca-nss-drv-objs := nss_init.o nss_core.o nss_tx_rx.o nss_stats.o
+qca-nss-drv-objs := nss_init.o nss_core.o nss_tx_rx.o nss_stats.o nss_pm.o
 
 obj-m += qca-nss-connmgr-ipv4.o
 obj-m += qca-nss-connmgr-ipv6.o
@@ -35,6 +35,7 @@
 ccflags-y += -I$(obj)/nss_hal/include -DNSS_DEBUG_LEVEL=0 -DNSS_EMPTY_BUFFER_SIZE=1792 -DNSS_PKT_STATS_ENABLED=0
 ccflags-y += -DNSS_CONNMGR_DEBUG_LEVEL=0
 ccflags-y += -DNSS_TUNIPIP6_DEBUG_LEVEL=0
+ccflags-y += -DNSS_PM_DEBUG_LEVEL=0
 
 obj ?= .
 
diff --git a/nss_api_if.h b/nss_api_if.h
index 5a17259..1d9f33f 100755
--- a/nss_api_if.h
+++ b/nss_api_if.h
@@ -94,6 +94,28 @@
 	uint32_t event;			/* NSS_LAG_{RELEASE/ENSLAVE} */
 };
 
+/*
+ * @brief NSS PM Clients
+ * NSS clients that can request for Bus/Clock performance levels
+ **/
+typedef enum nss_pm_client {
+	NSS_PM_CLIENT_GMAC,
+	NSS_PM_CLIENT_CRYPTO,
+	NSS_PM_CLIENT_NETAP,
+	NSS_PM_MAX_CLIENTS,
+} nss_pm_client_t;
+
+/**
+ * @brief NSS Performance Levels
+ * This is passed as parameter to NSS PM perf level requests
+ */
+typedef enum nss_pm_perf_level {
+	NSS_PM_PERF_LEVEL_SUSPEND = 0,
+	NSS_PM_PERF_LEVEL_IDLE,
+	NSS_PM_PERF_LEVEL_NOMINAL,
+	NSS_PM_PERF_LEVEL_TURBO,
+	NSS_PM_PERF_MAX_LEVELS,
+} nss_pm_perf_level_t;
 
 /**
  * IPv4 rule sync reasons.
@@ -511,6 +533,14 @@
 } nss_cb_unregister_status_t;
 
 /**
+ * PM Client interface status
+ */
+typedef enum {
+	NSS_PM_API_SUCCESS = 0,
+	NSS_PM_API_FAILED,
+} nss_pm_interface_status_t;
+
+/**
  * NSS GMAC event type
  */
 typedef enum {
@@ -1047,5 +1077,31 @@
  */
 nss_tx_status_t nss_freq_change(void *ctx, uint32_t eng, uint32_t start_or_end);
 
+/**
+ * @brief Register PM Driver Client
+ *
+ * @param client_id Identifies the Client driver registering with PM driver
+ *
+ * @return
+ */
+extern void *nss_pm_client_register(nss_pm_client_t client_id);
+
+/**
+ * @brief Unregister PM Driver Client
+ *
+ * @param client_id Identifies the Client driver registering with PM driver
+ *
+ * @return
+ */
+int nss_pm_client_unregister(nss_pm_client_t client_id);
+
+/**
+ * @brief Update Bus Bandwidth level for a client
+ *
+ * @param handle - Client Handle
+ * @param lvl - Perf Level
+ */
+extern nss_pm_interface_status_t nss_pm_set_perf_level(void *handle, nss_pm_perf_level_t lvl);
+
 /**@}*/
 #endif /** __NSS_API_IF_H */
diff --git a/nss_init.c b/nss_init.c
index 6547bd5..ccb2f07 100755
--- a/nss_init.c
+++ b/nss_init.c
@@ -19,8 +19,9 @@
  *	NSS init APIs
  *
  */
-
 #include "nss_core.h"
+#include "nss_pm.h"
+
 #include <nss_hal.h>
 #include <nss_clocks.h>
 
@@ -693,6 +694,11 @@
 	nss_wq = create_workqueue("nss_freq_queue");
 
 	/*
+	 * Initialize NSS Bus PM module
+	 */
+	nss_pm_init();
+
+	/*
 	 * Register platform_driver
 	 */
 	return platform_driver_register(&nss_driver);
diff --git a/nss_pm.c b/nss_pm.c
new file mode 100644
index 0000000..01bbb80
--- /dev/null
+++ b/nss_pm.c
@@ -0,0 +1,332 @@
+
+/*
+ **************************************************************************
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+/*
+ * nss_pm.c
+ *    NSS Power Management APIs
+ *
+ */
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+
+#include "nss_pm.h"
+
+/*
+ * Global NSS PM structure
+ */
+struct nss_pm_global_ctx ctx;
+
+/*
+ * Bus vector table for GMAC driver
+ */
+static struct msm_bus_paths nss_gmac_bw_level_tbl[NSS_PM_PERF_MAX_LEVELS] = {
+		[NSS_PM_PERF_LEVEL_SUSPEND] =  GMAC_BW_MBPS(0, 0),
+				/* 0 MHz to DDR, 0 MHz to TCM */
+		[NSS_PM_PERF_LEVEL_IDLE] =  GMAC_BW_MBPS(133, 5),
+				/* 133 MHz to DDR, 5 MHz to TCM */
+		[NSS_PM_PERF_LEVEL_NOMINAL] =  GMAC_BW_MBPS(200, 400),
+				/* 200 MHz to DDR, 10 MHz to TCM */
+		[NSS_PM_PERF_LEVEL_TURBO] = GMAC_BW_MBPS(266, 533),
+				/* 266 MHz to DDR, 20 MHz to TCM  */
+};
+
+/*
+ * Bus vector table for Crypto driver
+ */
+static struct msm_bus_paths nss_crypto_bw_level_tbl[NSS_PM_PERF_MAX_LEVELS] = {
+		[NSS_PM_PERF_LEVEL_SUSPEND] =  CRYPTO_BW_MBPS(0, 0),
+				/* 0 MHz to DDR, 0 MHz to TCM */
+		[NSS_PM_PERF_LEVEL_IDLE] =  CRYPTO_BW_MBPS(133, 5),
+				/* 133 MHz to DDR, 5 MHz to TCM */
+		[NSS_PM_PERF_LEVEL_NOMINAL] =  CRYPTO_BW_MBPS(200, 400),
+				/* 200 MHz to DDR, 10 MHz to TCM */
+		[NSS_PM_PERF_LEVEL_TURBO] = CRYPTO_BW_MBPS(266, 533),
+				/* 266 MHz to DDR, 20 MHz to TCM  */
+};
+
+/*
+ * Bus vector table for NSS HLOS driver
+ */
+static struct msm_bus_paths nss_netap_bw_level_tbl[NSS_PM_PERF_MAX_LEVELS] = {
+		[NSS_PM_PERF_LEVEL_SUSPEND] =  NETAP_BW_MBPS(0, 0),
+				/* 0 MHz to DDR, 0 MHz to TCM */
+		[NSS_PM_PERF_LEVEL_IDLE] =  NETAP_BW_MBPS(133, 133),
+				/* 133 MHz to DDR and TCM */
+		[NSS_PM_PERF_LEVEL_NOMINAL] =  NETAP_BW_MBPS(400, 400),
+				/* 400 MHz to DDR and TCM */
+		[NSS_PM_PERF_LEVEL_TURBO] = NETAP_BW_MBPS(533, 533),
+				/* 533 MHz to DDR and TCM  */
+};
+
+/*
+ * Bus Driver Platform data for GMAC, Crypto and Netap clients
+ */
+static struct msm_bus_scale_pdata nss_bus_scale[] = {
+	[NSS_PM_CLIENT_GMAC] = {
+		.usecase = nss_gmac_bw_level_tbl,
+		.num_usecases = ARRAY_SIZE(nss_gmac_bw_level_tbl),
+		.active_only = 1,
+		.name = "qca-nss-gmac",
+	},
+
+	[NSS_PM_CLIENT_CRYPTO] = {
+		.usecase = nss_crypto_bw_level_tbl,
+		.num_usecases = ARRAY_SIZE(nss_crypto_bw_level_tbl),
+		.active_only = 1,
+		.name = "qca-nss-crypto",
+	},
+
+	[NSS_PM_CLIENT_NETAP] = {
+		.usecase = nss_netap_bw_level_tbl,
+		.num_usecases = ARRAY_SIZE(nss_netap_bw_level_tbl),
+		.active_only = 1,
+		.name = "qca-nss-drv",
+	},
+};
+
+/*
+ * nss_pm_dbg_perf_level_get
+ *    debugfs hook to get the current performance level
+ */
+static int nss_pm_dbg_perf_level_get(void *data, u64 *val)
+{
+	nss_pm_client_data_t *pm_client;
+
+	pm_client = (nss_pm_client_data_t *)data;
+	*val = pm_client->current_perf_lvl;
+
+	return NSS_PM_API_SUCCESS;
+}
+
+/*
+ * nss_pm_dbg_autoscale_get
+ *    debugfs hook to get the current autoscale setting
+ */
+static int nss_pm_dbg_autoscale_get(void *data, u64 *val)
+{
+	nss_pm_client_data_t *pm_client;
+
+	pm_client = (nss_pm_client_data_t *)data;
+	*val = pm_client->auto_scale;
+
+	return NSS_PM_API_SUCCESS;
+}
+
+/*
+ * nss_pm_dbg_perf_level_set
+ *    debugfs hook to set perf level for a client
+ */
+static int nss_pm_dbg_perf_level_set(void *data, u64 val)
+{
+	uint32_t perf_level;
+
+	perf_level = (uint32_t) val;
+
+	if (perf_level >= NSS_PM_PERF_MAX_LEVELS ||
+			perf_level < NSS_PM_PERF_LEVEL_IDLE) {
+		nss_pm_warning("unsupported performance level %d \n", perf_level);
+		return NSS_PM_API_FAILED;
+	}
+
+	nss_pm_set_perf_level(data, perf_level);
+	return NSS_PM_API_SUCCESS;
+}
+
+/*
+ * nss_pm_dbg_autoscale_set
+ *    debugfs hook to enable auto scaling for a client
+ */
+static int nss_pm_dbg_autoscale_set(void *data, u64 val)
+{
+	nss_pm_client_data_t *pm_client;
+
+	if (val > 1) {
+		nss_pm_warning(" Invalid set value, valid values are 0/1 \n");
+		return NSS_PM_API_FAILED;
+	}
+
+	pm_client->auto_scale = (uint32_t)val;
+	return NSS_PM_API_SUCCESS;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(perf_level_fops, nss_pm_dbg_perf_level_get, nss_pm_dbg_perf_level_set, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(autoscale_fops, nss_pm_dbg_autoscale_get, nss_pm_dbg_autoscale_set, "%llu\n");
+
+/*
+ * nss_pm_client_register
+ *    Initialize GMAC specific PM parameters
+ *
+ * Creates debugfs hooks for user-space control of NSS Client PM
+ * Initializes Bus BW to Idle Perf level
+ * Returns PM handle to the caller.
+ *
+ */
+void *nss_pm_client_register(nss_pm_client_t client_id)
+{
+	int ret;
+	struct dentry *pm_dentry;
+	nss_pm_client_data_t *pm_client;
+
+	if (unlikely(client_id >= NSS_PM_MAX_CLIENTS))  {
+		nss_pm_warning("nss_pm_client_register invalid client id %d \n", client_id);
+		goto error;
+	}
+
+	pm_client  =  &ctx.nss_pm_client[client_id];
+
+	pm_client->bus_perf_client = msm_bus_scale_register_client(&nss_bus_scale[client_id]);
+	if (!pm_client->bus_perf_client) {
+		nss_pm_warning("unable to register bus client \n");
+		goto error;
+	}
+
+	ret = msm_bus_scale_client_update_request(pm_client->bus_perf_client, NSS_PM_PERF_LEVEL_IDLE);
+	if (ret) {
+		nss_pm_warning("initial bandwidth req failed (%d)\n", ret);
+		msm_bus_scale_unregister_client((uint32_t) pm_client->bus_perf_client);
+		goto error;
+	}
+
+	pm_client->current_perf_lvl = NSS_PM_PERF_LEVEL_IDLE;
+
+	switch (client_id) {
+		case NSS_PM_CLIENT_GMAC:
+			pm_dentry = debugfs_create_dir("gmac" , ctx.pm_dentry);
+			break;
+
+		case NSS_PM_CLIENT_CRYPTO:
+			pm_dentry = debugfs_create_dir("crypto" , ctx.pm_dentry);
+			break;
+
+		case NSS_PM_CLIENT_NETAP:
+			pm_dentry = debugfs_create_dir("netap" , ctx.pm_dentry);
+			break;
+
+		default:
+			nss_pm_warning("debugfs create failed invalid client id %d \n", client_id);
+			msm_bus_scale_unregister_client((uint32_t) pm_client->bus_perf_client);
+			goto error;
+
+	}
+
+	if (unlikely(pm_dentry == NULL)) {
+		nss_pm_info("debugfs not created for %d client pm \n", client_id);
+		goto out;
+	}
+
+	pm_client->dentry = pm_dentry;
+
+	if (!debugfs_create_file("perf_level", S_IRUGO | S_IWUSR, pm_dentry, pm_client, &perf_level_fops)) {
+		nss_pm_info("debugfs perf_level file not created for %d client pm \n", client_id);
+	}
+
+	if (!debugfs_create_file("auto-scale", S_IRUGO | S_IWUSR, pm_dentry, pm_client, &autoscale_fops)) {
+		nss_pm_info("debugfs auto-scale file not created for %d client pm \n", client_id);
+	}
+
+out:
+	return (void *)pm_client;
+error:
+	return NULL;
+}
+EXPORT_SYMBOL(nss_pm_client_register);
+
+/*
+ * nss_pm_client_unregister
+ *    Unregister the client for any PM operations
+ */
+int nss_pm_client_unregister(nss_pm_client_t client_id)
+{
+	nss_pm_client_data_t *pm_client;
+
+	if (unlikely(client_id >= NSS_PM_MAX_CLIENTS))  {
+		nss_pm_warning("nss_pm_client_unregister invalid client id %d \n", client_id);
+		goto error;
+	}
+
+	pm_client  =  &ctx.nss_pm_client[client_id];
+
+	if (unlikely(pm_client == NULL))  {
+		nss_pm_warning("nss_pm_client_unregister client not registered %d \n", client_id);
+		goto error;
+	}
+
+	if (pm_client->bus_perf_client) {
+		msm_bus_scale_unregister_client((uint32_t) pm_client->bus_perf_client);
+	} else {
+		nss_pm_info("nss_pm_client_unregister: client not registered \n");
+	}
+
+	if (likely(pm_client->dentry != NULL)) {
+		debugfs_remove_recursive(pm_client->dentry);
+	}
+
+	return NSS_PM_API_SUCCESS;
+
+error:
+	return NSS_PM_API_FAILED;
+}
+
+/*
+ * nss_pm_set_perf_level()
+ *    Sets the performance level of client specific Fabrics and Clocks to requested level
+ */
+nss_pm_interface_status_t nss_pm_set_perf_level(void *handle, nss_pm_perf_level_t lvl)
+{
+	int ret = 0;
+	nss_pm_client_data_t *pm_client;
+
+	pm_client = (nss_pm_client_data_t *) handle;
+	if (pm_client->current_perf_lvl == lvl) {
+		nss_pm_trace("Already at perf level %d , ignoring request \n", lvl);
+		return NSS_PM_API_SUCCESS;
+	}
+
+	if (!pm_client->bus_perf_client) {
+		nss_pm_warning("Bus driver client not registered.request failed \n");
+		return NSS_PM_API_FAILED;
+	}
+
+	/* Update bandwidth if request has changed. This may sleep. */
+	ret = msm_bus_scale_client_update_request(pm_client->bus_perf_client, lvl);
+	if (ret) {
+		nss_pm_warning("bandwidth request failed (%d)\n", ret);
+		return NSS_PM_API_FAILED;
+	}
+
+	nss_pm_info("perf level request, current: %d new: %d \n", pm_client->current_perf_lvl, lvl);
+	pm_client->current_perf_lvl = lvl;
+
+	return NSS_PM_API_SUCCESS;
+}
+EXPORT_SYMBOL(nss_pm_set_perf_level);
+
+/*
+ * nss_pm_init()
+ *    Initialize NSS PM top level structures
+ */
+void nss_pm_init(void) {
+
+	nss_pm_info("NSS Bus PM (platform - IPQ806x, build - %s:%s)\n", __DATE__, __TIME__);
+
+	ctx.pm_dentry = debugfs_create_dir("qca-nss-pm", NULL);
+
+	if (unlikely(ctx.pm_dentry == NULL)) {
+		nss_pm_warning("Failed to create qca-nss-drv directory in debugfs");
+	}
+}
diff --git a/nss_pm.h b/nss_pm.h
new file mode 100644
index 0000000..df728dd
--- /dev/null
+++ b/nss_pm.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2013, Qualcomm Atheros Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * nss_pm.h
+ *    NSS PM Driver header file
+ */
+
+#ifndef __NSS_PM_H
+#define __NSS_PM_H
+
+#include <mach/msm_nss_gmac.h>
+#include <mach/msm_nss_crypto.h>
+#include <mach/msm_bus_board.h>
+#include <mach/msm_bus.h>
+#include "nss_api_if.h"
+
+/*
+ * NSS PM debug macros
+ */
+#if (NSS_PM_DEBUG_LEVEL < 1)
+#define nss_pm_assert(fmt, args...)
+#else
+#define nss_pm_assert(c) if (!(c)) { BUG_ON(!(c)); }
+#endif
+
+#if (NSS_PM_DEBUG_LEVEL < 2)
+#define nss_pm_warning(fmt, args...)
+#else
+#define nss_pm_warning(fmt, args...) printk(KERN_WARNING "nss_pm:"fmt, ##args)
+#endif
+
+#if (NSS_PM_DEBUG_LEVEL < 3)
+#define nss_pm_info(fmt, args...)
+#else
+#define nss_pm_info(fmt, args...) printk(KERN_INFO "nss_pm:"fmt, ##args)
+#endif
+
+#if (NSS_PM_DEBUG_LEVEL < 4)
+#define nss_pm_trace(fmt, args...)
+#else
+#define nss_pm_trace(fmt, args...) printk(KERN_DEBUG "nss_pm:"fmt, ##args)
+#endif
+
+
+/*
+ * PM Client data structure
+ */
+typedef struct {
+	uint32_t bus_perf_client;
+	uint32_t clk_handle;
+	uint32_t current_perf_lvl;
+	uint32_t auto_scale;
+	struct dentry *dentry;
+} nss_pm_client_data_t;
+
+/*
+ * NSS PM driver context
+ */
+struct nss_pm_global_ctx {
+	struct dentry *pm_dentry;
+	nss_pm_client_data_t nss_pm_client[NSS_PM_MAX_CLIENTS];
+};
+
+/*
+ * Macro defining Bus vector for GMAC driver
+ */
+#define GMAC_BW_MBPS(_data_bw, _desc_bw) \
+{ \
+	.vectors = (struct msm_bus_vectors[]){ \
+		{\
+			.src = MSM_BUS_MASTER_NSS_GMAC_0, \
+			.dst = MSM_BUS_SLAVE_EBI_CH0, \
+			.ab = (_data_bw) * 16 * 1000000ULL, \
+			.ib = (_data_bw) * 16 * 1000000ULL, \
+		}, \
+		{ \
+			.src =  MSM_BUS_MASTER_NSS_GMAC_0, \
+			.dst = MSM_BUS_SLAVE_NSS_TCM, \
+			.ab = (_desc_bw) * 8 * 1000000ULL, \
+			.ib = (_desc_bw) * 8 * 1000000ULL, \
+		}, \
+	}, \
+	.num_paths = 2, \
+}
+
+/*
+ *  Macro defining Bus vector for NSS crypto driver
+ */
+#define CRYPTO_BW_MBPS(_data_bw, _desc_bw) \
+{ \
+	.vectors = (struct msm_bus_vectors[]){ \
+		{\
+			.src = MSM_BUS_MASTER_NSS_CRYPTO5_0, \
+			.dst = MSM_BUS_SLAVE_EBI_CH0, \
+			.ab = (_data_bw) * 16 * 1000000ULL, \
+			.ib = (_data_bw) * 16 * 1000000ULL, \
+		}, \
+		{ \
+			.src =  MSM_BUS_MASTER_NSS_CRYPTO5_0, \
+			.dst = MSM_BUS_SLAVE_NSS_TCM, \
+			.ab = (_desc_bw) * 8 * 1000000ULL, \
+			.ib = (_desc_bw) * 8 * 1000000ULL, \
+		}, \
+	}, \
+	.num_paths = 2, \
+}
+
+/*
+ * Macro defining Bus vector for NSS driver
+ *
+ */
+#define NETAP_BW_MBPS(_data_bw, _desc_bw) \
+{ \
+	.vectors = (struct msm_bus_vectors[]){ \
+		{\
+			.src = MSM_BUS_MASTER_UBI32_0, \
+			.dst = MSM_BUS_SLAVE_EBI_CH0, \
+			.ab = (_data_bw) * 16 * 1000000ULL, \
+			.ib = (_data_bw) * 16 * 1000000ULL, \
+		}, \
+		{ \
+			.src = MSM_BUS_MASTER_UBI32_0, \
+			.dst = MSM_BUS_SLAVE_NSS_TCM, \
+			.ab = (_desc_bw) * 8 * 1000000ULL, \
+			.ib = (_desc_bw) * 8 * 1000000ULL, \
+		}, \
+	}, \
+	.num_paths = 2, \
+}
+
+/*
+ * Initialize NSS PM top level structures
+ */
+void nss_pm_init(void);
+
+#endif  /** __NSS_PM_H */