|
@@ -18,19 +18,23 @@
|
|
#include <linux/reset-controller.h>
|
|
#include <linux/reset-controller.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/slab.h>
|
|
|
|
|
|
-static DEFINE_MUTEX(reset_controller_list_mutex);
|
|
|
|
|
|
+static DEFINE_MUTEX(reset_list_mutex);
|
|
static LIST_HEAD(reset_controller_list);
|
|
static LIST_HEAD(reset_controller_list);
|
|
|
|
|
|
/**
|
|
/**
|
|
* struct reset_control - a reset control
|
|
* struct reset_control - a reset control
|
|
* @rcdev: a pointer to the reset controller device
|
|
* @rcdev: a pointer to the reset controller device
|
|
* this reset control belongs to
|
|
* this reset control belongs to
|
|
|
|
+ * @list: list entry for the rcdev's reset controller list
|
|
* @id: ID of the reset controller in the reset
|
|
* @id: ID of the reset controller in the reset
|
|
* controller device
|
|
* controller device
|
|
|
|
+ * @refcnt: Number of gets of this reset_control
|
|
*/
|
|
*/
|
|
struct reset_control {
|
|
struct reset_control {
|
|
struct reset_controller_dev *rcdev;
|
|
struct reset_controller_dev *rcdev;
|
|
|
|
+ struct list_head list;
|
|
unsigned int id;
|
|
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;
|
|
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);
|
|
list_add(&rcdev->list, &reset_controller_list);
|
|
- mutex_unlock(&reset_controller_list_mutex);
|
|
|
|
|
|
+ mutex_unlock(&reset_list_mutex);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -76,9 +82,9 @@ EXPORT_SYMBOL_GPL(reset_controller_register);
|
|
*/
|
|
*/
|
|
void reset_controller_unregister(struct reset_controller_dev *rcdev)
|
|
void reset_controller_unregister(struct reset_controller_dev *rcdev)
|
|
{
|
|
{
|
|
- mutex_lock(&reset_controller_list_mutex);
|
|
|
|
|
|
+ mutex_lock(&reset_list_mutex);
|
|
list_del(&rcdev->list);
|
|
list_del(&rcdev->list);
|
|
- mutex_unlock(&reset_controller_list_mutex);
|
|
|
|
|
|
+ mutex_unlock(&reset_list_mutex);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(reset_controller_unregister);
|
|
EXPORT_SYMBOL_GPL(reset_controller_unregister);
|
|
|
|
|
|
@@ -136,6 +142,48 @@ int reset_control_status(struct reset_control *rstc)
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(reset_control_status);
|
|
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,
|
|
struct reset_control *__of_reset_control_get(struct device_node *node,
|
|
const char *id, int index)
|
|
const char *id, int index)
|
|
{
|
|
{
|
|
@@ -160,7 +208,7 @@ struct reset_control *__of_reset_control_get(struct device_node *node,
|
|
if (ret)
|
|
if (ret)
|
|
return ERR_PTR(ret);
|
|
return ERR_PTR(ret);
|
|
|
|
|
|
- mutex_lock(&reset_controller_list_mutex);
|
|
|
|
|
|
+ mutex_lock(&reset_list_mutex);
|
|
rcdev = NULL;
|
|
rcdev = NULL;
|
|
list_for_each_entry(r, &reset_controller_list, list) {
|
|
list_for_each_entry(r, &reset_controller_list, list) {
|
|
if (args.np == r->of_node) {
|
|
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);
|
|
of_node_put(args.np);
|
|
|
|
|
|
if (!rcdev) {
|
|
if (!rcdev) {
|
|
- mutex_unlock(&reset_controller_list_mutex);
|
|
|
|
|
|
+ mutex_unlock(&reset_list_mutex);
|
|
return ERR_PTR(-EPROBE_DEFER);
|
|
return ERR_PTR(-EPROBE_DEFER);
|
|
}
|
|
}
|
|
|
|
|
|
if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) {
|
|
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);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
}
|
|
|
|
|
|
rstc_id = rcdev->of_xlate(rcdev, &args);
|
|
rstc_id = rcdev->of_xlate(rcdev, &args);
|
|
if (rstc_id < 0) {
|
|
if (rstc_id < 0) {
|
|
- mutex_unlock(&reset_controller_list_mutex);
|
|
|
|
|
|
+ mutex_unlock(&reset_list_mutex);
|
|
return ERR_PTR(rstc_id);
|
|
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;
|
|
return rstc;
|
|
}
|
|
}
|
|
@@ -212,8 +253,9 @@ void reset_control_put(struct reset_control *rstc)
|
|
if (IS_ERR(rstc))
|
|
if (IS_ERR(rstc))
|
|
return;
|
|
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);
|
|
EXPORT_SYMBOL_GPL(reset_control_put);
|
|
|
|
|