msm: 8064: hsic: Add msm_bus vote for hsic controller driver

Drivers need to specify their bandwidth requirements to
bus-scaling driver to get guaranteed bandwidth on fabrics.

HSIC USB performance depends upon system fabric frequency
as HSIC USB controller has to support high bi-directional
data transfers. Hence, request for high bus bandwidth as
long as HSIC is active.

Also add debugfs entry to enable/disable the bus voting:-
echo enable > /sys/kernel/debug/ehci_hsic_msm_dbg/bus_voting
echo disable > /sys/kernel/debug/ehci_hsic_msm_dbg/bus_voting

CRs-Fixed: 342032
Signed-off-by: Hemant Kumar <hemantk@codeaurora.org>
Change-Id: I7e208d67ef1c0168cd7621aeae4e8b123adef255
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index df20d60..d01af99 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -479,9 +479,59 @@
 
 }
 #ifdef CONFIG_USB_EHCI_MSM_HSIC
+/* Bandwidth requests (zero) if no vote placed */
+static struct msm_bus_vectors hsic_init_vectors[] = {
+	{
+		.src = MSM_BUS_MASTER_SPS,
+		.dst = MSM_BUS_SLAVE_EBI_CH0,
+		.ab = 0,
+		.ib = 0,
+	},
+	{
+		.src = MSM_BUS_MASTER_SPS,
+		.dst = MSM_BUS_SLAVE_SPS,
+		.ab = 0,
+		.ib = 0,
+	},
+};
+
+/* Bus bandwidth requests in Bytes/sec */
+static struct msm_bus_vectors hsic_max_vectors[] = {
+	{
+		.src = MSM_BUS_MASTER_SPS,
+		.dst = MSM_BUS_SLAVE_EBI_CH0,
+		.ab = 60000000,		/* At least 480Mbps on bus. */
+		.ib = 960000000,	/* MAX bursts rate */
+	},
+	{
+		.src = MSM_BUS_MASTER_SPS,
+		.dst = MSM_BUS_SLAVE_SPS,
+		.ab = 0,
+		.ib = 512000000, /*vote for 64Mhz dfab clk rate*/
+	},
+};
+
+static struct msm_bus_paths hsic_bus_scale_usecases[] = {
+	{
+		ARRAY_SIZE(hsic_init_vectors),
+		hsic_init_vectors,
+	},
+	{
+		ARRAY_SIZE(hsic_max_vectors),
+		hsic_max_vectors,
+	},
+};
+
+static struct msm_bus_scale_pdata hsic_bus_scale_pdata = {
+	hsic_bus_scale_usecases,
+	ARRAY_SIZE(hsic_bus_scale_usecases),
+	.name = "hsic",
+};
+
 static struct msm_hsic_host_platform_data msm_hsic_pdata = {
-	.strobe		= 88,
-	.data		= 89,
+	.strobe			= 88,
+	.data			= 89,
+	.bus_scale_table	= &hsic_bus_scale_pdata,
 };
 #else
 static struct msm_hsic_host_platform_data msm_hsic_pdata;
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index 347711b..dbbfad4 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -25,9 +25,12 @@
 #include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
 #include <linux/wakelock.h>
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
+#include <mach/msm_bus.h>
 
 #include <linux/usb/msm_hsusb_hw.h>
 #include <linux/usb/msm_hsusb.h>
@@ -55,8 +58,10 @@
 	int			peripheral_status_irq;
 	int			wakeup_irq;
 	bool			wakeup_irq_enabled;
+	uint32_t		bus_perf_client;
 };
 
+static bool debug_bus_voting_enabled = true;
 static inline struct msm_hsic_hcd *hcd_to_hsic(struct usb_hcd *hcd)
 {
 	return (struct msm_hsic_hcd *) (hcd->hcd_priv);
@@ -464,6 +469,14 @@
 	if (ret < 0)
 		dev_err(mehci->dev, "unable to set vddcx voltage: min:0.5v max:1.3v\n");
 
+	if (mehci->bus_perf_client && debug_bus_voting_enabled) {
+		ret = msm_bus_scale_client_update_request(
+				mehci->bus_perf_client, 0);
+		if (ret)
+			dev_err(mehci->dev, "%s: Failed to dvote for "
+				   "bus bandwidth %d\n", __func__, ret);
+	}
+
 	atomic_set(&mehci->in_lpm, 1);
 	enable_irq(hcd->irq);
 	wake_unlock(&mehci->wlock);
@@ -487,6 +500,14 @@
 
 	wake_lock(&mehci->wlock);
 
+	if (mehci->bus_perf_client && debug_bus_voting_enabled) {
+		ret = msm_bus_scale_client_update_request(
+				mehci->bus_perf_client, 1);
+		if (ret)
+			dev_err(mehci->dev, "%s: Failed to vote for "
+				   "bus bandwidth %d\n", __func__, ret);
+	}
+
 	ret = regulator_set_voltage(mehci->hsic_vddcx,
 				USB_PHY_VDD_DIG_VOL_MIN,
 				USB_PHY_VDD_DIG_VOL_MAX);
@@ -754,6 +775,88 @@
 	return IRQ_HANDLED;
 }
 
+static int ehci_hsic_msm_bus_show(struct seq_file *s, void *unused)
+{
+	if (debug_bus_voting_enabled)
+		seq_printf(s, "enabled\n");
+	else
+		seq_printf(s, "disabled\n");
+
+	return 0;
+}
+
+static int ehci_hsic_msm_bus_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ehci_hsic_msm_bus_show, inode->i_private);
+}
+
+static ssize_t ehci_hsic_msm_bus_write(struct file *file,
+	const char __user *ubuf, size_t count, loff_t *ppos)
+{
+	char buf[8];
+	int ret;
+	struct seq_file *s = file->private_data;
+	struct msm_hsic_hcd *mehci = s->private;
+
+	memset(buf, 0x00, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "enable", 6)) {
+		/* Do not vote here. Let hsic driver decide when to vote */
+		debug_bus_voting_enabled = true;
+	} else {
+		debug_bus_voting_enabled = false;
+		if (mehci->bus_perf_client) {
+			ret = msm_bus_scale_client_update_request(
+					mehci->bus_perf_client, 0);
+			if (ret)
+				dev_err(mehci->dev, "%s: Failed to devote "
+					   "for bus bw %d\n", __func__, ret);
+		}
+	}
+
+	return count;
+}
+
+const struct file_operations ehci_hsic_msm_bus_fops = {
+	.open = ehci_hsic_msm_bus_open,
+	.read = seq_read,
+	.write = ehci_hsic_msm_bus_write,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static struct dentry *ehci_hsic_msm_dbg_root;
+static int ehci_hsic_msm_debugfs_init(struct msm_hsic_hcd *mehci)
+{
+	struct dentry *ehci_hsic_msm_dentry;
+
+	ehci_hsic_msm_dbg_root = debugfs_create_dir("ehci_hsic_msm_dbg", NULL);
+
+	if (!ehci_hsic_msm_dbg_root || IS_ERR(ehci_hsic_msm_dbg_root))
+		return -ENODEV;
+
+	ehci_hsic_msm_dentry = debugfs_create_file("bus_voting",
+		S_IRUGO | S_IWUSR,
+		ehci_hsic_msm_dbg_root, mehci,
+		&ehci_hsic_msm_bus_fops);
+
+	if (!ehci_hsic_msm_dentry) {
+		debugfs_remove_recursive(ehci_hsic_msm_dbg_root);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void ehci_hsic_msm_debugfs_cleanup(void)
+{
+	debugfs_remove_recursive(ehci_hsic_msm_dbg_root);
+}
+
+
 
 static int __devinit ehci_hsic_msm_probe(struct platform_device *pdev)
 {
@@ -886,6 +989,27 @@
 		}
 	}
 
+	ret = ehci_hsic_msm_debugfs_init(mehci);
+	if (ret)
+		dev_dbg(&pdev->dev, "mode debugfs file is"
+			"not available\n");
+
+	if (pdata && pdata->bus_scale_table) {
+		mehci->bus_perf_client =
+		    msm_bus_scale_register_client(pdata->bus_scale_table);
+		/* Configure BUS performance parameters for MAX bandwidth */
+		if (mehci->bus_perf_client) {
+			ret = msm_bus_scale_client_update_request(
+					mehci->bus_perf_client, 1);
+			if (ret)
+				dev_err(&pdev->dev, "%s: Failed to vote for "
+					   "bus bandwidth %d\n", __func__, ret);
+		} else {
+			dev_err(&pdev->dev, "%s: Failed to register BUS "
+						"scaling client!!\n", __func__);
+		}
+	}
+
 	/*
 	 * This pdev->dev is assigned parent of root-hub by USB core,
 	 * hence, runtime framework automatically calls this driver's
@@ -930,6 +1054,10 @@
 		free_irq(mehci->wakeup_irq, mehci);
 	}
 
+	if (mehci->bus_perf_client)
+		msm_bus_scale_unregister_client(mehci->bus_perf_client);
+
+	ehci_hsic_msm_debugfs_cleanup();
 	device_init_wakeup(&pdev->dev, 0);
 	pm_runtime_set_suspended(&pdev->dev);
 
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index bb6a01d..1a6df8b 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -331,6 +331,7 @@
 	unsigned strobe;
 	unsigned data;
 	unsigned hub_reset;
+	struct msm_bus_scale_pdata *bus_scale_table;
 };
 
 struct msm_usb_host_platform_data {