aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/sunxi/patches-4.4/143-reset-add-shared-resetcontrol-asserts.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/sunxi/patches-4.4/143-reset-add-shared-resetcontrol-asserts.patch')
-rw-r--r--target/linux/sunxi/patches-4.4/143-reset-add-shared-resetcontrol-asserts.patch271
1 files changed, 271 insertions, 0 deletions
diff --git a/target/linux/sunxi/patches-4.4/143-reset-add-shared-resetcontrol-asserts.patch b/target/linux/sunxi/patches-4.4/143-reset-add-shared-resetcontrol-asserts.patch
new file mode 100644
index 0000000000..3e962d9dab
--- /dev/null
+++ b/target/linux/sunxi/patches-4.4/143-reset-add-shared-resetcontrol-asserts.patch
@@ -0,0 +1,271 @@
+From d25cfe9b4f9663216ce4e011e3f1e7fa669ab58a Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Fri, 27 Nov 2015 21:09:05 +0100
+Subject: [PATCH] reset: Add shared reset_control_[de]assert variants
+
+Add reset_control_deassert_shared / reset_control_assert_shared
+functions which are intended for use by drivers for hw blocks which
+(may) share a reset line with another driver / hw block.
+
+Unlike the regular reset_control_[de]assert functions these functions
+keep track of how often deassert_shared / assert_shared have been called
+and keep the line deasserted as long as deassert has been called more
+times than assert.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+Changes in v2:
+-This is a new patch in v2 of this patch-set
+---
+ drivers/reset/core.c | 121 ++++++++++++++++++++++++++++++++++++---
+ include/linux/reset-controller.h | 2 +
+ include/linux/reset.h | 2 +
+ 3 files changed, 116 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/reset/core.c b/drivers/reset/core.c
+index 9ab9290..8c3436c 100644
+--- a/drivers/reset/core.c
++++ b/drivers/reset/core.c
+@@ -22,16 +22,29 @@ static DEFINE_MUTEX(reset_controller_list_mutex);
+ static LIST_HEAD(reset_controller_list);
+
+ /**
++ * struct reset_line - a reset line
++ * @list: list entry for the reset controllers reset line list
++ * @id: ID of the reset line in the reset controller device
++ * @refcnt: Number of reset_control structs referencing this device
++ * @deassert_cnt: Number of times this reset line has been deasserted
++ */
++struct reset_line {
++ struct list_head list;
++ unsigned int id;
++ unsigned int refcnt;
++ unsigned int deassert_cnt;
++};
++
++/**
+ * struct reset_control - a reset control
+ * @rcdev: a pointer to the reset controller device
+ * this reset control belongs to
+- * @id: ID of the reset controller in the reset
+- * controller device
++ * @line: reset line for this reset control
+ */
+ struct reset_control {
+ struct reset_controller_dev *rcdev;
++ struct reset_line *line;
+ struct device *dev;
+- unsigned int id;
+ };
+
+ /**
+@@ -66,6 +79,8 @@ int reset_controller_register(struct reset_controller_dev *rcdev)
+ rcdev->of_xlate = of_reset_simple_xlate;
+ }
+
++ INIT_LIST_HEAD(&rcdev->reset_line_head);
++
+ mutex_lock(&reset_controller_list_mutex);
+ list_add(&rcdev->list, &reset_controller_list);
+ mutex_unlock(&reset_controller_list_mutex);
+@@ -93,7 +108,7 @@ EXPORT_SYMBOL_GPL(reset_controller_unregister);
+ int reset_control_reset(struct reset_control *rstc)
+ {
+ if (rstc->rcdev->ops->reset)
+- return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id);
++ return rstc->rcdev->ops->reset(rstc->rcdev, rstc->line->id);
+
+ return -ENOTSUPP;
+ }
+@@ -106,7 +121,7 @@ EXPORT_SYMBOL_GPL(reset_control_reset);
+ int reset_control_assert(struct reset_control *rstc)
+ {
+ if (rstc->rcdev->ops->assert)
+- return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id);
++ return rstc->rcdev->ops->assert(rstc->rcdev, rstc->line->id);
+
+ return -ENOTSUPP;
+ }
+@@ -119,13 +134,55 @@ EXPORT_SYMBOL_GPL(reset_control_assert);
+ int reset_control_deassert(struct reset_control *rstc)
+ {
+ if (rstc->rcdev->ops->deassert)
+- return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id);
++ return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->line->id);
+
+ return -ENOTSUPP;
+ }
+ EXPORT_SYMBOL_GPL(reset_control_deassert);
+
+ /**
++ * reset_control_assert_shared - asserts a shared reset line
++ * @rstc: reset controller
++ *
++ * Assert a shared reset line, this functions decreases the deassert count
++ * of the line by one and asserts it if, and only if, the deassert count
++ * reaches 0.
++ */
++int reset_control_assert_shared(struct reset_control *rstc)
++{
++ if (!rstc->rcdev->ops->assert)
++ return -ENOTSUPP;
++
++ rstc->line->deassert_cnt--;
++ if (rstc->line->deassert_cnt)
++ return 0;
++
++ return rstc->rcdev->ops->assert(rstc->rcdev, rstc->line->id);
++}
++EXPORT_SYMBOL_GPL(reset_control_assert_shared);
++
++/**
++ * reset_control_deassert_shared - deasserts a shared reset line
++ * @rstc: reset controller
++ *
++ * Assert a shared reset line, this functions increases the deassert count
++ * of the line by one and deasserts the reset line (if it was not already
++ * deasserted).
++ */
++int reset_control_deassert_shared(struct reset_control *rstc)
++{
++ if (!rstc->rcdev->ops->deassert)
++ return -ENOTSUPP;
++
++ rstc->line->deassert_cnt++;
++ if (rstc->line->deassert_cnt != 1)
++ return 0;
++
++ return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->line->id);
++}
++EXPORT_SYMBOL_GPL(reset_control_deassert_shared);
++
++/**
+ * reset_control_status - returns a negative errno if not supported, a
+ * positive value if the reset line is asserted, or zero if the reset
+ * line is not asserted.
+@@ -134,12 +191,47 @@ EXPORT_SYMBOL_GPL(reset_control_deassert);
+ int reset_control_status(struct reset_control *rstc)
+ {
+ if (rstc->rcdev->ops->status)
+- return rstc->rcdev->ops->status(rstc->rcdev, rstc->id);
++ return rstc->rcdev->ops->status(rstc->rcdev, rstc->line->id);
+
+ return -ENOTSUPP;
+ }
+ EXPORT_SYMBOL_GPL(reset_control_status);
+
++static struct reset_line *reset_line_get(struct reset_controller_dev *rcdev,
++ unsigned int index)
++{
++ struct reset_line *line;
++
++ list_for_each_entry(line, &rcdev->reset_line_head, list) {
++ if (line->id == index) {
++ line->refcnt++;
++ return line;
++ }
++ }
++
++ line = kzalloc(sizeof(*line), GFP_KERNEL);
++ if (!line)
++ return NULL;
++
++ list_add(&line->list, &rcdev->reset_line_head);
++ line->id = index;
++ line->refcnt = 1;
++
++ return line;
++}
++
++static void reset_line_put(struct reset_line *line)
++{
++ if (!line)
++ return;
++
++ if (--line->refcnt)
++ return;
++
++ list_del(&line->list);
++ kfree(line);
++}
++
+ /**
+ * of_reset_control_get_by_index - Lookup and obtain a reference to a reset
+ * controller by index.
+@@ -155,6 +247,7 @@ struct reset_control *of_reset_control_get_by_index(struct device_node *node,
+ {
+ struct reset_control *rstc = ERR_PTR(-EPROBE_DEFER);
+ struct reset_controller_dev *r, *rcdev;
++ struct reset_line *line;
+ struct of_phandle_args args;
+ int rstc_id;
+ int ret;
+@@ -186,16 +279,22 @@ struct reset_control *of_reset_control_get_by_index(struct device_node *node,
+ }
+
+ try_module_get(rcdev->owner);
++
++ /* reset_controller_list_mutex also protects the reset_line list */
++ line = reset_line_get(rcdev, rstc_id);
++
+ mutex_unlock(&reset_controller_list_mutex);
+
+ rstc = kzalloc(sizeof(*rstc), GFP_KERNEL);
+- if (!rstc) {
++ if (!line || !rstc) {
++ kfree(rstc);
++ reset_line_put(line);
+ module_put(rcdev->owner);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ rstc->rcdev = rcdev;
+- rstc->id = rstc_id;
++ rstc->line = line;
+
+ return rstc;
+ }
+@@ -259,6 +358,10 @@ void reset_control_put(struct reset_control *rstc)
+ if (IS_ERR(rstc))
+ return;
+
++ mutex_lock(&reset_controller_list_mutex);
++ reset_line_put(rstc->line);
++ mutex_unlock(&reset_controller_list_mutex);
++
+ module_put(rstc->rcdev->owner);
+ kfree(rstc);
+ }
+diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h
+index ce6b962..7f2cbd1 100644
+--- a/include/linux/reset-controller.h
++++ b/include/linux/reset-controller.h
+@@ -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_line_head: head of internal list of reset lines
+ * @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 {
+ struct reset_control_ops *ops;
+ struct module *owner;
+ struct list_head list;
++ struct list_head reset_line_head;
+ struct device_node *of_node;
+ int of_reset_n_cells;
+ int (*of_xlate)(struct reset_controller_dev *rcdev,
+diff --git a/include/linux/reset.h b/include/linux/reset.h
+index c4c097d..1cca8ce 100644
+--- a/include/linux/reset.h
++++ b/include/linux/reset.h
+@@ -11,6 +11,8 @@ int reset_control_reset(struct reset_control *rstc);
+ int reset_control_assert(struct reset_control *rstc);
+ int reset_control_deassert(struct reset_control *rstc);
+ int reset_control_status(struct reset_control *rstc);
++int reset_control_assert_shared(struct reset_control *rstc);
++int reset_control_deassert_shared(struct reset_control *rstc);
+
+ struct reset_control *reset_control_get(struct device *dev, const char *id);
+ void reset_control_put(struct reset_control *rstc);