diff mbox

[RFC,1/9] opp-modifier: Introduce OPP Modifier Framework

Message ID 1394825135-60110-2-git-send-email-d-gerlach@ti.com (mailing list archive)
State RFC, archived
Headers show

Commit Message

Dave Gerlach March 14, 2014, 7:25 p.m. UTC
Introduce framework to allow an OPP modifier driver to selectively
determine which possible OPPs for an SoC are available based on
register values found in SoC through a common API.

Three functions are exported, a register and unregister function for
the opp modifier drivers to notify the API of their existence, and
opp_modify_dev_table which looks up the appropriate driver and
DT information to use for a device and then uses enable/disable to
change which previously loaded OPPs are available on the device.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
---
 drivers/power/Makefile       |   2 +
 drivers/power/opp/Makefile   |   1 +
 drivers/power/opp/core.c     | 126 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/opp-modifier.h |  35 ++++++++++++
 4 files changed, 164 insertions(+)
 create mode 100644 drivers/power/opp/Makefile
 create mode 100644 drivers/power/opp/core.c
 create mode 100644 include/linux/opp-modifier.h
diff mbox

Patch

diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index ee54a3e..ed379cf 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -58,3 +58,5 @@  obj-$(CONFIG_POWER_AVS)		+= avs/
 obj-$(CONFIG_CHARGER_SMB347)	+= smb347-charger.o
 obj-$(CONFIG_CHARGER_TPS65090)	+= tps65090-charger.o
 obj-$(CONFIG_POWER_RESET)	+= reset/
+
+obj-y				+= opp/
diff --git a/drivers/power/opp/Makefile b/drivers/power/opp/Makefile
new file mode 100644
index 0000000..820eb10
--- /dev/null
+++ b/drivers/power/opp/Makefile
@@ -0,0 +1 @@ 
+obj-y += core.o
diff --git a/drivers/power/opp/core.c b/drivers/power/opp/core.c
new file mode 100644
index 0000000..403013b
--- /dev/null
+++ b/drivers/power/opp/core.c
@@ -0,0 +1,126 @@ 
+/*
+ * OPP Modifier framework
+ *
+ * Copyright (C) 2013 Texas Instruments Inc.
+ * Dave Gerlach <d-gerlach@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * 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/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/opp-modifier.h>
+
+static DEFINE_MUTEX(opp_modifier_list_mutex);
+static LIST_HEAD(opp_modifier_list);
+
+static int opp_modify_dev_opp_table(struct opp_modifier_dev *opp_dev,
+				    struct device *dev)
+{
+	if (opp_dev->ops->modify)
+		return opp_dev->ops->modify(dev);
+
+	return -EINVAL;
+}
+
+static struct opp_modifier_dev *opp_modifier_get(struct device *dev)
+{
+	struct opp_modifier_dev *o, *opp_dev;
+	struct device_node *np;
+
+	if (!dev)
+		return ERR_PTR(-EINVAL);
+
+	np = of_parse_phandle(dev->of_node, "platform-opp-modifier", 0);
+
+	if (!np)
+		return ERR_PTR(-ENOSYS);
+
+	opp_dev = NULL;
+
+	mutex_lock(&opp_modifier_list_mutex);
+
+	list_for_each_entry(o, &opp_modifier_list, list) {
+		if (of_get_parent(np) == o->of_node) {
+			opp_dev = o;
+			break;
+		}
+	}
+
+	if (!opp_dev) {
+		mutex_unlock(&opp_modifier_list_mutex);
+		return ERR_PTR(-EINVAL);
+	}
+
+	of_node_put(np);
+
+	try_module_get(opp_dev->owner);
+	mutex_unlock(&opp_modifier_list_mutex);
+
+	return opp_dev;
+}
+
+static void opp_modifier_put(struct opp_modifier_dev *opp_dev)
+{
+	if (IS_ERR(opp_dev))
+		return;
+
+	module_put(opp_dev->owner);
+}
+
+int opp_modifier_register(struct opp_modifier_dev *opp_dev)
+{
+	mutex_lock(&opp_modifier_list_mutex);
+	list_add(&opp_dev->list, &opp_modifier_list);
+	mutex_unlock(&opp_modifier_list_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(opp_modifier_register);
+
+void opp_modifier_unregister(struct opp_modifier_dev *opp_dev)
+{
+	mutex_lock(&opp_modifier_list_mutex);
+	list_del(&opp_dev->list);
+	mutex_unlock(&opp_modifier_list_mutex);
+}
+EXPORT_SYMBOL_GPL(opp_modifier_unregister);
+
+int opp_modify_dev_table(struct device *dev)
+{
+	struct opp_modifier_dev *opp_dev;
+	int ret;
+
+	opp_dev = opp_modifier_get(dev);
+
+	/*
+	 * It is a valid case for a device to not implement
+	 * an OPP modifier table so return 0 if not present
+	 */
+
+	if (IS_ERR(opp_dev) && PTR_ERR(opp_dev) == -ENOSYS) {
+		dev_dbg(dev, "No platform-opp-modifier entry present\n");
+		return 0;
+	} else if (IS_ERR(opp_dev)) {
+		return PTR_ERR(opp_dev);
+	}
+
+	ret = opp_modify_dev_opp_table(opp_dev, dev);
+
+	opp_modifier_put(opp_dev);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(opp_modify_dev_table);
diff --git a/include/linux/opp-modifier.h b/include/linux/opp-modifier.h
new file mode 100644
index 0000000..8d50851
--- /dev/null
+++ b/include/linux/opp-modifier.h
@@ -0,0 +1,35 @@ 
+/*
+ * TI OPP Modifier Core API
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ * Dave Gerlach <d-gerlach@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_OPP_MODIFIER_H__
+#define __LINUX_OPP_MODIFIER_H__
+
+struct opp_modifier_dev {
+	struct opp_modifier_ops *ops;
+	struct module *owner;
+	struct list_head list;
+	struct device_node *of_node;
+};
+
+struct opp_modifier_ops {
+	int (*modify)(struct device *dev);
+};
+
+int opp_modifier_register(struct opp_modifier_dev *opp_dev);
+void opp_modifier_unregister(struct opp_modifier_dev *opp_dev);
+int opp_modify_dev_table(struct device *dev);
+
+#endif          /* __LINUX_OPP_MODIFIER_H__ */