From d920940be699a866c6853971d5589d6249bce6a4 Mon Sep 17 00:00:00 2001
From: Ravikumar Kattekola <rk@ti.com>
Date: Sun, 11 Jan 2015 23:04:34 +0530
Subject: [PATCH] drivers: power: Introduce TI coprocessor driver

All the coprocessors may not be used in all the
system usecase scenarios, so we need a driver that can
initialize the coprocessors based on the current system usecase.

In a scenario where the user knows ahead what's the target OPP
for a usecase and which doesnt change during runtime, using devfreq
would be an overkill.

Introduce a simple driver to configure the initial Operating
frequency an dvoltage (OPP) for co processors in a SoC.

TI co-processor driver makes use of voltage domain manager
for handling the clock frequency changes and corresponding voltage
handling thus it can still work seamlessly in a ganged rail scenario
where the voltage rails of more than one coprocssor are ganged up.

Change-Id: If8c4afba833cf972e080f81c9e02f3126703b96c
Signed-off-by: Ravikumar Kattekola <rk@ti.com>
---
 .../devicetree/bindings/power/coproc/ti-coproc.txt |  35 ++++
 drivers/power/Kconfig                              |   1 +
 drivers/power/Makefile                             |   1 +
 drivers/power/coproc/Kconfig                       |   7 +
 drivers/power/coproc/Makefile                      |   2 +
 drivers/power/coproc/ti-coproc.c                   | 229 +++++++++++++++++++++
 6 files changed, 275 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/coproc/ti-coproc.txt
 create mode 100644 drivers/power/coproc/Kconfig
 create mode 100644 drivers/power/coproc/Makefile
 create mode 100644 drivers/power/coproc/ti-coproc.c

diff --git a/Documentation/devicetree/bindings/power/coproc/ti-coproc.txt b/Documentation/devicetree/bindings/power/coproc/ti-coproc.txt
new file mode 100644
index 0000000..d60c1f2
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/coproc/ti-coproc.txt
@@ -0,0 +1,35 @@
+Texas Instruments co-processor driver
+
+A co-processor device is a non-cpu processor found on an SoC.
+
+This document describes the clock and voltage configuration
+used by co-processors/devices typically found on Texas Instrument SoCs.
+
+Required properties
+- compatible: should be
+	"ti,coproc"
+- clocks: <functional clock>, <dpll clock>
+	dpll clock is optional
+- clock-names: "fclk", "dpll"
+	if you change names here, go chnage the driver too
+- clock-target-frequency: Mention your target frequency here
+- operating-points: all OPPs applicable for this device/coprocessor
+- coproc-voltdm: phandle of the Voltage domain associated with this device/coproc
+- voltage-tolerance: Specify a voltage tolerance allowed for this voltage domain
+as per datamanual limits
+
+Example:
+iva_coproc {
+	compatible = "ti,coproc";
+	clocks = <&dpll_iva_m2_ck>, <&dpll_iva_ck>;
+	clock-names = "fclk", "dpll";
+	clock-target-frequency = <532000000>;
+	operating-points = <
+		388200  1055000
+		430000	1150000
+		532000  1250000
+		>;
+	coproc-voltdm = <&voltdm_ivahd>;
+	voltage-tolerance = <1>;
+};
+
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 5c4fe16..8b07dc1 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -395,3 +395,4 @@ endif # POWER_SUPPLY
 
 source "drivers/power/avs/Kconfig"
 source "drivers/power/voltdm/Kconfig"
+source "drivers/power/coproc/Kconfig"
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 3d47072..a76fa2a 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -59,3 +59,4 @@ obj-$(CONFIG_CHARGER_SMB347)	+= smb347-charger.o
 obj-$(CONFIG_CHARGER_TPS65090)	+= tps65090-charger.o
 obj-$(CONFIG_POWER_RESET)	+= reset/
 obj-$(CONFIG_VOLTAGE_DOMAIN)	+= voltdm/
+obj-$(CONFIG_TI_COPROC)		+= coproc/
diff --git a/drivers/power/coproc/Kconfig b/drivers/power/coproc/Kconfig
new file mode 100644
index 0000000..ec0e307
--- /dev/null
+++ b/drivers/power/coproc/Kconfig
@@ -0,0 +1,7 @@
+config TI_COPROC
+	tristate "TI Co-processor driversupport"
+	depends on VOLTAGE_DOMAIN_OMAP
+	help
+		TI Coprocessor driver helps initialize a coprocessor to
+		a particular Operating performance point.
+
diff --git a/drivers/power/coproc/Makefile b/drivers/power/coproc/Makefile
new file mode 100644
index 0000000..5e393a5
--- /dev/null
+++ b/drivers/power/coproc/Makefile
@@ -0,0 +1,2 @@
+#Texas Instruments co-processor init driver
+obj-${CONFIG_TI_COPROC}	+= ti-coproc.o
diff --git a/drivers/power/coproc/ti-coproc.c b/drivers/power/coproc/ti-coproc.c
new file mode 100644
index 0000000..3795d36
--- /dev/null
+++ b/drivers/power/coproc/ti-coproc.c
@@ -0,0 +1,229 @@
+/*
+ * OMAP SoC COPROC  driver
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
+ *		Carlos Hernandez <ceh@ti.com>
+ *		Nishanth Menon <nm@ti.com>
+ *		Ravikumar Kattekola <rk@ti.com>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/suspend.h>
+#include <linux/pm_opp.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/pm_voltage_domain.h>
+
+/**
+ * struct coproc_data - Co processor private data
+ * @profile:   profile specific for this device
+ * @dev:  device pointer
+ * @:   for this device
+ * @stat: my current statistics
+ * @dev_clk: my device clk
+ * @dpll_clk: my dpll clk
+ * @nb:	my notifier block
+ */
+struct coproc_data {
+	struct device *dev;
+	struct clk *dev_clk;
+	struct clk *dpll_clk;
+	struct notifier_block *clk_nb;
+};
+
+/**
+ * DOC:
+ * clock-names should be defined in dts file, e.g.
+ * coproc {
+ *
+ *  compatible = "ti,coproc";
+ *  clocks = <&dpll_iva_m2_ck>, <&dpll_iva_ck>;
+ *  clock-names = "fclk", "dpll";
+ *  clock-target-frequency = <532000000>;
+ *  operating-points = <
+ *    388200  1055000
+ *    500000  1150000
+ *    532000  1250000
+ *  >;
+ *  coproc-voltdm = <&voltdm_ivahd>;
+ *  voltage-tolerance = <1>;
+ *
+ * };
+ */
+#define DEV_CLK_NAME "fclk"
+#define DPLL_CLK_NAME "dpll"
+
+static int coproc_probe(struct platform_device *pdev)
+{
+	struct coproc_data *d;
+	unsigned int voltage_latency;
+	u32 target_freq = 0;
+	int err = 0;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = of_node_get(dev->of_node);
+	bool noset_dpll_as_rate;
+
+	dev_info(dev, "probe\n");
+
+	of_property_read_u32(np, "clock-target-frequency", &target_freq);
+	if (!target_freq) {
+		dev_err(dev, "%s: Invalid/no target frequency found in dt.\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	noset_dpll_as_rate = of_property_read_bool(np, "noset-dpll-rate");
+
+	d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL);
+	if (d == NULL) {
+		dev_err(dev, "%s: Cannot allocate memory.\n", __func__);
+		err = -ENOMEM;
+		goto out;
+	}
+	platform_set_drvdata(pdev, d);
+
+	d->dpll_clk = devm_clk_get(dev, DPLL_CLK_NAME);
+	if (IS_ERR(d->dpll_clk)) {
+		err = PTR_ERR(d->dpll_clk);
+		dev_err(dev, "%s: Cannot get dpll clk(%d).\n", __func__, err);
+		d->dpll_clk = NULL;
+	}
+
+	d->dev_clk = devm_clk_get(dev, DEV_CLK_NAME);
+	if (IS_ERR(d->dev_clk)) {
+		err = PTR_ERR(d->dpll_clk);
+		dev_err(dev, "%s: Cannot get func clk(%d).\n", __func__, err);
+		goto out;
+	}
+
+	if (noset_dpll_as_rate)
+		d->dpll_clk = NULL;
+
+	err = of_init_opp_table(dev);
+	if (err) {
+		dev_err(dev, "%s: Cannot initialize opp table(%d).\n", __func__,
+			err);
+		goto out;
+	}
+	d->dev = dev;
+
+	/* Register voltage domain notifier */
+	d->clk_nb = of_pm_voltdm_notifier_register(dev, np, d->dev_clk,
+						   "coproc",
+						   &voltage_latency);
+	if (IS_ERR(d->clk_nb)) {
+		err = PTR_ERR(d->clk_nb);
+		/* defer probe if regulator is not yet registered */
+		if (err == -EPROBE_DEFER) {
+			dev_err(dev,
+				"coproc clock notifier not ready, retry\n");
+		} else {
+			dev_err(dev,
+				"Failed to register coproc clk notifier: %d\n",
+				err);
+		}
+		goto out;
+	}
+
+	if (target_freq) {
+		if (d->dpll_clk) {
+			err = clk_set_rate(d->dpll_clk, target_freq);
+			if (err) {
+				dev_err(dev, "%s: Cannot set dpll clock rate(%d).\n",
+					__func__, err);
+				goto out;
+			}
+		}
+
+		err = clk_set_rate(d->dev_clk, target_freq);
+		if (err) {
+			dev_err(dev, "%s: Cannot set func clock rate(%d).\n",
+				__func__, err);
+			goto out;
+		}
+	}
+
+	/* All good.. */
+	goto out;
+
+out:
+	dev_info(dev, "%s result=%d", __func__, err);
+	return err;
+}
+
+static int coproc_remove(struct platform_device *pdev)
+{
+	struct coproc_data *d = platform_get_drvdata(pdev);
+	struct device *dev = &pdev->dev;
+
+	dev_info(dev, "remove\n");
+
+	of_pm_voltdm_notifier_unregister(d->clk_nb);
+
+	dev_info(dev, "%s Removed\n", __func__);
+	return 0;
+}
+
+/**
+ * coproc_suspend() - dummy hook for suspend
+ * @dev: device pointer
+ *
+ * Return: 0
+ */
+static int coproc_suspend(struct device *dev)
+{
+	dev_info(dev, "suspend\n");
+	return 0;
+}
+
+/**
+ * coproc_resume() - dummy hook for resume
+ * @dev: device pointer
+ *
+ * Return: 0
+ */
+static int coproc_resume(struct device *dev)
+{
+	dev_info(dev, "resume\n");
+	return 0;
+}
+
+/* Device power management hooks */
+static SIMPLE_DEV_PM_OPS(coproc_pm,
+			 coproc_suspend,
+			 coproc_resume);
+
+static struct of_device_id of_coproc_match[] = {
+	{.compatible = "ti,coproc"},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, of_coproc_match);
+
+static struct platform_driver coproc_driver = {
+	.probe = coproc_probe,
+	.remove = coproc_remove,
+	.driver = {
+		   .name = "coproc",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(of_coproc_match),
+		   .pm = &coproc_pm,
+		   },
+};
+module_platform_driver(coproc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("OMAP Co-processor Driver");
+MODULE_AUTHOR("Texas Instruments Inc.");
-- 
1.9.1