diff options
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.patch | 271 |
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); |