reset: Share struct reset_control between reset_control_get calls

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>
This commit is contained in:
Hans de Goede 2016-02-23 18:46:25 +01:00 committed by Philipp Zabel
parent 6c96f05c8b
commit c15ddec2ca
2 changed files with 65 additions and 21 deletions

View File

@ -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); /* reset_list_mutex also protects the rcdev's reset_control list */
mutex_unlock(&reset_controller_list_mutex); rstc = __reset_control_get(rcdev, rstc_id);
rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); mutex_unlock(&reset_list_mutex);
if (!rstc) {
module_put(rcdev->owner);
return ERR_PTR(-ENOMEM);
}
rstc->rcdev = rcdev;
rstc->id = rstc_id;
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); mutex_lock(&reset_list_mutex);
kfree(rstc); __reset_control_put(rstc);
mutex_unlock(&reset_list_mutex);
} }
EXPORT_SYMBOL_GPL(reset_control_put); EXPORT_SYMBOL_GPL(reset_control_put);

View File

@ -31,6 +31,7 @@ struct of_phandle_args;
* @ops: a pointer to device specific struct reset_control_ops * @ops: a pointer to device specific struct reset_control_ops
* @owner: kernel module of the reset controller driver * @owner: kernel module of the reset controller driver
* @list: internal list of reset controller devices * @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_node: corresponding device tree node as phandle target
* @of_reset_n_cells: number of cells in reset line specifiers * @of_reset_n_cells: number of cells in reset line specifiers
* @of_xlate: translation function to translate from specifier as found in the * @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; const struct reset_control_ops *ops;
struct module *owner; struct module *owner;
struct list_head list; struct list_head list;
struct list_head reset_control_head;
struct device_node *of_node; struct device_node *of_node;
int of_reset_n_cells; int of_reset_n_cells;
int (*of_xlate)(struct reset_controller_dev *rcdev, int (*of_xlate)(struct reset_controller_dev *rcdev,