drivers: base: opp: add devres support and devm_* functions
authorLukasz Luba <l.luba@partner.samsung.com>
Wed, 23 Jan 2019 17:36:00 +0000 (18:36 +0100)
committerJunghoon Kim <jhoon20.kim@samsung.com>
Thu, 14 Feb 2019 05:58:21 +0000 (14:58 +0900)
The patch adds basic support for resource tracking in OPP subsystem.
From now, adding a new OPP might be done using devm_pm_opp_add() which
is safe in case of deffer probe.
When the deffer probe is used and the driver does not remove explicitly
the OPP table, it causes warnings for the next try.
With devm_pm_opp_add() the resources are freed and the next deffer probe
is safe.
There is also simple wrapper for dev_pm_opp_remove().

Change-Id: I0b8282e9faf59b34e958853a451f2e7c9a13c18b
Signed-off-by: Lukasz Luba <l.luba@partner.samsung.com>
drivers/base/power/opp/Makefile
drivers/base/power/opp/devres.c [new file with mode: 0644]

index e70ceb4..4419548 100644 (file)
@@ -1,4 +1,4 @@
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
-obj-y                          += core.o cpu.o
+obj-y                          += core.o cpu.o devres.o
 obj-$(CONFIG_OF)               += of.o
 obj-$(CONFIG_DEBUG_FS)         += debugfs.o
diff --git a/drivers/base/power/opp/devres.c b/drivers/base/power/opp/devres.c
new file mode 100644 (file)
index 0000000..7f342dd
--- /dev/null
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019, Samsung
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/device.h>
+
+int dev_pm_opp_add(struct device *dev, unsigned long freq,
+                  unsigned long u_volt);
+
+struct opp_table *_find_opp_table(struct device *dev);
+struct opp_table *allocate_opp_table(struct device *dev);
+int _opp_add_v1(struct opp_table *opp_table, struct device *dev,
+               unsigned long freq, long u_volt, bool dynamic);
+void dev_pm_opp_put_opp_table(struct opp_table *opp_table);
+void _dev_pm_opp_remove_table(struct opp_table *opp_table, struct device *dev,
+                             bool remove_all);
+void dev_pm_opp_remove(struct device *dev, unsigned long freq);
+
+static void _devm_pm_remove_opp_table(struct device *dev, void *res)
+{
+       struct opp_table *opp_table;
+
+       dev_dbg(dev, "OPP_DEVRES removing opp table\n");
+       opp_table = _find_opp_table(dev);
+       /* Check if someone did not remove the opp_table, i.e. by calling
+        * dev_pm_opp_remove() for each OPP. */
+       if (IS_ERR(opp_table))
+               return;
+
+       _dev_pm_opp_remove_table(opp_table, dev, true);
+
+       dev_pm_opp_put_opp_table(opp_table);
+}
+
+
+static struct opp_table *_devm_pm_get_opp_table(struct device *dev)
+{
+       struct opp_table **ptr, *opp_table;
+
+       ptr = devres_alloc(_devm_pm_remove_opp_table, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       opp_table = _find_opp_table(dev);
+       if (IS_ERR(opp_table)) {
+               dev_dbg(dev, "OPP_DEVRES allocating opp table\n");
+               opp_table = allocate_opp_table(dev);
+               if (!opp_table) {
+                       devres_free(ptr);
+                       return opp_table;
+               } else {
+                       *ptr = opp_table;
+                       devres_add(dev, ptr);
+               }
+       }
+
+       dev_dbg(dev, "OPP_DEVRES get opp table\n");
+       return opp_table;
+}
+
+int devm_pm_opp_add(struct device *dev, unsigned long freq,
+                   unsigned long u_volt)
+{
+       int res = 0;
+       struct opp_table *opp_table;
+       int ret;
+
+       dev_dbg(dev, "OPP_DEVRES adding OPP\n");
+
+       opp_table = _devm_pm_get_opp_table(dev);
+       if (!opp_table)
+               return -ENOMEM;
+
+       ret = _opp_add_v1(opp_table, dev, freq, u_volt, true);
+
+       dev_pm_opp_put_opp_table(opp_table);
+
+       return res;
+}
+EXPORT_SYMBOL_GPL(devm_pm_opp_add);
+
+void devm_pm_opp_remove(struct device *dev, unsigned long freq)
+{
+       dev_pm_opp_remove(dev, freq);
+}
+EXPORT_SYMBOL_GPL(devm_pm_opp_remove);