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-gerl...@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 --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-gerl...@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-gerl...@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__ */
-- 
1.9.0

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to