]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
reset: Share struct reset_control between reset_control_get calls
authorHans de Goede <hdegoede@redhat.com>
Tue, 23 Feb 2016 17:46:25 +0000 (18:46 +0100)
committerPhilipp Zabel <p.zabel@pengutronix.de>
Wed, 30 Mar 2016 13:42:03 +0000 (15:42 +0200)
Now that struct reset_control no longer stores the device pointer for
the device calling reset_control_get we can share a single struct
reset_control when multiple calls to reset_control_get are made for
the same reset line (same id / index).

This is a preparation patch for adding support for shared reset lines.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
drivers/reset/core.c
include/linux/reset-controller.h

index bdf1763da87a6767478df072303c63aa5b3f31e4..957750600ff3588f1ba21e16da1373058033738b 100644 (file)
 #include <linux/reset-controller.h>
 #include <linux/slab.h>
 
-static DEFINE_MUTEX(reset_controller_list_mutex);
+static DEFINE_MUTEX(reset_list_mutex);
 static LIST_HEAD(reset_controller_list);
 
 /**
  * struct reset_control - a reset control
  * @rcdev: a pointer to the reset controller device
  *         this reset control belongs to
+ * @list: list entry for the rcdev's reset controller list
  * @id: ID of the reset controller in the reset
  *      controller device
+ * @refcnt: Number of gets of this reset_control
  */
 struct reset_control {
        struct reset_controller_dev *rcdev;
+       struct list_head list;
        unsigned int id;
+       unsigned int refcnt;
 };
 
 /**
@@ -62,9 +66,11 @@ int reset_controller_register(struct reset_controller_dev *rcdev)
                rcdev->of_xlate = of_reset_simple_xlate;
        }
 
-       mutex_lock(&reset_controller_list_mutex);
+       INIT_LIST_HEAD(&rcdev->reset_control_head);
+
+       mutex_lock(&reset_list_mutex);
        list_add(&rcdev->list, &reset_controller_list);
-       mutex_unlock(&reset_controller_list_mutex);
+       mutex_unlock(&reset_list_mutex);
 
        return 0;
 }
@@ -76,9 +82,9 @@ EXPORT_SYMBOL_GPL(reset_controller_register);
  */
 void reset_controller_unregister(struct reset_controller_dev *rcdev)
 {
-       mutex_lock(&reset_controller_list_mutex);
+       mutex_lock(&reset_list_mutex);
        list_del(&rcdev->list);
-       mutex_unlock(&reset_controller_list_mutex);
+       mutex_unlock(&reset_list_mutex);
 }
 EXPORT_SYMBOL_GPL(reset_controller_unregister);
 
@@ -136,6 +142,48 @@ int reset_control_status(struct reset_control *rstc)
 }
 EXPORT_SYMBOL_GPL(reset_control_status);
 
+static struct reset_control *__reset_control_get(
+                               struct reset_controller_dev *rcdev,
+                               unsigned int index)
+{
+       struct reset_control *rstc;
+
+       lockdep_assert_held(&reset_list_mutex);
+
+       list_for_each_entry(rstc, &rcdev->reset_control_head, list) {
+               if (rstc->id == index) {
+                       rstc->refcnt++;
+                       return rstc;
+               }
+       }
+
+       rstc = kzalloc(sizeof(*rstc), GFP_KERNEL);
+       if (!rstc)
+               return ERR_PTR(-ENOMEM);
+
+       try_module_get(rcdev->owner);
+
+       rstc->rcdev = rcdev;
+       list_add(&rstc->list, &rcdev->reset_control_head);
+       rstc->id = index;
+       rstc->refcnt = 1;
+
+       return rstc;
+}
+
+static void __reset_control_put(struct reset_control *rstc)
+{
+       lockdep_assert_held(&reset_list_mutex);
+
+       if (--rstc->refcnt)
+               return;
+
+       module_put(rstc->rcdev->owner);
+
+       list_del(&rstc->list);
+       kfree(rstc);
+}
+
 struct reset_control *__of_reset_control_get(struct device_node *node,
                                             const char *id, int index)
 {
@@ -160,7 +208,7 @@ struct reset_control *__of_reset_control_get(struct device_node *node,
        if (ret)
                return ERR_PTR(ret);
 
-       mutex_lock(&reset_controller_list_mutex);
+       mutex_lock(&reset_list_mutex);
        rcdev = NULL;
        list_for_each_entry(r, &reset_controller_list, list) {
                if (args.np == r->of_node) {
@@ -171,32 +219,25 @@ struct reset_control *__of_reset_control_get(struct device_node *node,
        of_node_put(args.np);
 
        if (!rcdev) {
-               mutex_unlock(&reset_controller_list_mutex);
+               mutex_unlock(&reset_list_mutex);
                return ERR_PTR(-EPROBE_DEFER);
        }
 
        if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) {
-               mutex_unlock(&reset_controller_list_mutex);
+               mutex_unlock(&reset_list_mutex);
                return ERR_PTR(-EINVAL);
        }
 
        rstc_id = rcdev->of_xlate(rcdev, &args);
        if (rstc_id < 0) {
-               mutex_unlock(&reset_controller_list_mutex);
+               mutex_unlock(&reset_list_mutex);
                return ERR_PTR(rstc_id);
        }
 
-       try_module_get(rcdev->owner);
-       mutex_unlock(&reset_controller_list_mutex);
-
-       rstc = kzalloc(sizeof(*rstc), GFP_KERNEL);
-       if (!rstc) {
-               module_put(rcdev->owner);
-               return ERR_PTR(-ENOMEM);
-       }
+       /* reset_list_mutex also protects the rcdev's reset_control list */
+       rstc = __reset_control_get(rcdev, rstc_id);
 
-       rstc->rcdev = rcdev;
-       rstc->id = rstc_id;
+       mutex_unlock(&reset_list_mutex);
 
        return rstc;
 }
@@ -212,8 +253,9 @@ void reset_control_put(struct reset_control *rstc)
        if (IS_ERR(rstc))
                return;
 
-       module_put(rstc->rcdev->owner);
-       kfree(rstc);
+       mutex_lock(&reset_list_mutex);
+       __reset_control_put(rstc);
+       mutex_unlock(&reset_list_mutex);
 }
 EXPORT_SYMBOL_GPL(reset_control_put);
 
index a3a5bcdb1d02e6c3ffb8868f5ebb49c6f14ae636..b91ba932bbd4fcca8d5a332363bf8d65eea180a5 100644 (file)
@@ -31,6 +31,7 @@ struct of_phandle_args;
  * @ops: a pointer to device specific struct reset_control_ops
  * @owner: kernel module of the reset controller driver
  * @list: internal list of reset controller devices
+ * @reset_control_head: head of internal list of requested reset controls
  * @of_node: corresponding device tree node as phandle target
  * @of_reset_n_cells: number of cells in reset line specifiers
  * @of_xlate: translation function to translate from specifier as found in the
@@ -41,6 +42,7 @@ struct reset_controller_dev {
        const struct reset_control_ops *ops;
        struct module *owner;
        struct list_head list;
+       struct list_head reset_control_head;
        struct device_node *of_node;
        int of_reset_n_cells;
        int (*of_xlate)(struct reset_controller_dev *rcdev,