From 6c96f05c8bb8bc4177613ef3c23a56b455e75887 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 23 Feb 2016 18:46:24 +0100 Subject: reset: Make [of_]reset_control_get[_foo] functions wrappers With both the regular, _by_index and _optional variants we already have quite a few variants of [of_]reset_control_get[_foo], the upcoming addition of shared reset lines support makes this worse. This commit changes all the variants into wrappers around common core functions. For completeness sake this commit also adds a new devm_get_reset_control_by_index wrapper. Signed-off-by: Hans de Goede Signed-off-by: Philipp Zabel --- include/linux/reset.h | 126 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 90 insertions(+), 36 deletions(-) (limited to 'include/linux/reset.h') diff --git a/include/linux/reset.h b/include/linux/reset.h index c4c097de0ba9..1bb69a29d6db 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -1,8 +1,8 @@ #ifndef _LINUX_RESET_H_ #define _LINUX_RESET_H_ -struct device; -struct device_node; +#include + struct reset_control; #ifdef CONFIG_RESET_CONTROLLER @@ -12,9 +12,11 @@ int reset_control_assert(struct reset_control *rstc); int reset_control_deassert(struct reset_control *rstc); int reset_control_status(struct reset_control *rstc); -struct reset_control *reset_control_get(struct device *dev, const char *id); +struct reset_control *__of_reset_control_get(struct device_node *node, + const char *id, int index); void reset_control_put(struct reset_control *rstc); -struct reset_control *devm_reset_control_get(struct device *dev, const char *id); +struct reset_control *__devm_reset_control_get(struct device *dev, + const char *id, int index); int __must_check device_reset(struct device *dev); @@ -23,24 +25,6 @@ static inline int device_reset_optional(struct device *dev) return device_reset(dev); } -static inline struct reset_control *reset_control_get_optional( - struct device *dev, const char *id) -{ - return reset_control_get(dev, id); -} - -static inline struct reset_control *devm_reset_control_get_optional( - struct device *dev, const char *id) -{ - return devm_reset_control_get(dev, id); -} - -struct reset_control *of_reset_control_get(struct device_node *node, - const char *id); - -struct reset_control *of_reset_control_get_by_index( - struct device_node *node, int index); - #else static inline int reset_control_reset(struct reset_control *rstc) @@ -77,44 +61,114 @@ static inline int device_reset_optional(struct device *dev) return -ENOTSUPP; } -static inline struct reset_control *__must_check reset_control_get( - struct device *dev, const char *id) +static inline struct reset_control *__of_reset_control_get( + struct device_node *node, + const char *id, int index) { - WARN_ON(1); return ERR_PTR(-EINVAL); } -static inline struct reset_control *__must_check devm_reset_control_get( - struct device *dev, const char *id) +static inline struct reset_control *__devm_reset_control_get( + struct device *dev, + const char *id, int index) { - WARN_ON(1); return ERR_PTR(-EINVAL); } -static inline struct reset_control *reset_control_get_optional( +#endif /* CONFIG_RESET_CONTROLLER */ + +/** + * reset_control_get - Lookup and obtain a reference to a reset controller. + * @dev: device to be reset by the controller + * @id: reset line name + * + * Returns a struct reset_control or IS_ERR() condition containing errno. + * + * Use of id names is optional. + */ +static inline struct reset_control *__must_check reset_control_get( struct device *dev, const char *id) { - return ERR_PTR(-ENOTSUPP); +#ifndef CONFIG_RESET_CONTROLLER + WARN_ON(1); +#endif + return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0); } -static inline struct reset_control *devm_reset_control_get_optional( +static inline struct reset_control *reset_control_get_optional( struct device *dev, const char *id) { - return ERR_PTR(-ENOTSUPP); + return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0); } +/** + * of_reset_control_get - Lookup and obtain a reference to a reset controller. + * @node: device to be reset by the controller + * @id: reset line name + * + * Returns a struct reset_control or IS_ERR() condition containing errno. + * + * Use of id names is optional. + */ static inline struct reset_control *of_reset_control_get( struct device_node *node, const char *id) { - return ERR_PTR(-ENOTSUPP); + return __of_reset_control_get(node, id, 0); } +/** + * of_reset_control_get_by_index - Lookup and obtain a reference to a reset + * controller by index. + * @node: device to be reset by the controller + * @index: index of the reset controller + * + * This is to be used to perform a list of resets for a device or power domain + * in whatever order. Returns a struct reset_control or IS_ERR() condition + * containing errno. + */ static inline struct reset_control *of_reset_control_get_by_index( - struct device_node *node, int index) + struct device_node *node, int index) { - return ERR_PTR(-ENOTSUPP); + return __of_reset_control_get(node, NULL, index); } -#endif /* CONFIG_RESET_CONTROLLER */ +/** + * devm_reset_control_get - resource managed reset_control_get() + * @dev: device to be reset by the controller + * @id: reset line name + * + * Managed reset_control_get(). For reset controllers returned from this + * function, reset_control_put() is called automatically on driver detach. + * See reset_control_get() for more information. + */ +static inline struct reset_control *__must_check devm_reset_control_get( + struct device *dev, const char *id) +{ +#ifndef CONFIG_RESET_CONTROLLER + WARN_ON(1); +#endif + return __devm_reset_control_get(dev, id, 0); +} + +static inline struct reset_control *devm_reset_control_get_optional( + struct device *dev, const char *id) +{ + return __devm_reset_control_get(dev, id, 0); +} + +/** + * devm_reset_control_get_by_index - resource managed reset_control_get + * @dev: device to be reset by the controller + * @index: index of the reset controller + * + * Managed reset_control_get(). For reset controllers returned from this + * function, reset_control_put() is called automatically on driver detach. + * See reset_control_get() for more information. + */ +static inline struct reset_control *devm_reset_control_get_by_index( + struct device *dev, int index) +{ + return __devm_reset_control_get(dev, NULL, index); +} #endif -- cgit v1.2.3-71-gd317 From 0b52297f2288ca239e598afe6c92db83d8d2bfcd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 23 Feb 2016 18:46:26 +0100 Subject: reset: Add support for shared reset controls In some SoCs some hw-blocks share a reset control. Add support for this setup by adding new: reset_control_get_shared() devm_reset_control_get_shared() devm_reset_control_get_shared_by_index() methods to get a reset_control. Note that this patch omits adding of_ variants, if these are needed later they can be easily added. This patch also changes the behavior of the existing exclusive reset_control_get() variants, if these are now called more then once for the same reset_control they will return -EBUSY. To catch existing drivers triggering this error (there should not be any) a WARN_ON(1) is added in this path. When a reset_control is shared, the behavior of reset_control_assert / deassert is changed, for shared reset_controls these will work like the clock-enable/disable and regulator-on/off functions. They will keep a deassert_count, and only (re-)assert the reset after reset_control_assert has been called as many times as reset_control_deassert was called. Calling reset_control_assert without first calling reset_control_deassert is not allowed on a shared reset control. Calling reset_control_reset is also not allowed on a shared reset control. Signed-off-by: Hans de Goede Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 59 +++++++++++++++++++++++++------ include/linux/reset.h | 96 +++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 129 insertions(+), 26 deletions(-) (limited to 'include/linux/reset.h') diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 957750600ff3..72b32bd15549 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -8,6 +8,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ +#include #include #include #include @@ -29,12 +30,16 @@ static LIST_HEAD(reset_controller_list); * @id: ID of the reset controller in the reset * controller device * @refcnt: Number of gets of this reset_control + * @shared: Is this a shared (1), or an exclusive (0) reset_control? + * @deassert_cnt: Number of times this reset line has been deasserted */ struct reset_control { struct reset_controller_dev *rcdev; struct list_head list; unsigned int id; unsigned int refcnt; + int shared; + atomic_t deassert_count; }; /** @@ -91,9 +96,14 @@ EXPORT_SYMBOL_GPL(reset_controller_unregister); /** * reset_control_reset - reset the controlled device * @rstc: reset controller + * + * Calling this on a shared reset controller is an error. */ int reset_control_reset(struct reset_control *rstc) { + if (WARN_ON(rstc->shared)) + return -EINVAL; + if (rstc->rcdev->ops->reset) return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id); @@ -104,26 +114,48 @@ EXPORT_SYMBOL_GPL(reset_control_reset); /** * reset_control_assert - asserts the reset line * @rstc: reset controller + * + * Calling this on an exclusive reset controller guarantees that the reset + * will be asserted. When called on a shared reset controller the line may + * still be deasserted, as long as other users keep it so. + * + * For shared reset controls a driver cannot expect the hw's registers and + * internal state to be reset, but must be prepared for this to happen. */ int reset_control_assert(struct reset_control *rstc) { - if (rstc->rcdev->ops->assert) - return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); + if (!rstc->rcdev->ops->assert) + return -ENOTSUPP; - return -ENOTSUPP; + if (rstc->shared) { + if (WARN_ON(atomic_read(&rstc->deassert_count) == 0)) + return -EINVAL; + + if (atomic_dec_return(&rstc->deassert_count) != 0) + return 0; + } + + return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); } EXPORT_SYMBOL_GPL(reset_control_assert); /** * reset_control_deassert - deasserts the reset line * @rstc: reset controller + * + * After calling this function, the reset is guaranteed to be deasserted. */ int reset_control_deassert(struct reset_control *rstc) { - if (rstc->rcdev->ops->deassert) - return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); + if (!rstc->rcdev->ops->deassert) + return -ENOTSUPP; - return -ENOTSUPP; + if (rstc->shared) { + if (atomic_inc_return(&rstc->deassert_count) != 1) + return 0; + } + + return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); } EXPORT_SYMBOL_GPL(reset_control_deassert); @@ -144,7 +176,7 @@ EXPORT_SYMBOL_GPL(reset_control_status); static struct reset_control *__reset_control_get( struct reset_controller_dev *rcdev, - unsigned int index) + unsigned int index, int shared) { struct reset_control *rstc; @@ -152,6 +184,9 @@ static struct reset_control *__reset_control_get( list_for_each_entry(rstc, &rcdev->reset_control_head, list) { if (rstc->id == index) { + if (WARN_ON(!rstc->shared || !shared)) + return ERR_PTR(-EBUSY); + rstc->refcnt++; return rstc; } @@ -167,6 +202,7 @@ static struct reset_control *__reset_control_get( list_add(&rstc->list, &rcdev->reset_control_head); rstc->id = index; rstc->refcnt = 1; + rstc->shared = shared; return rstc; } @@ -185,7 +221,7 @@ static void __reset_control_put(struct reset_control *rstc) } struct reset_control *__of_reset_control_get(struct device_node *node, - const char *id, int index) + const char *id, int index, int shared) { struct reset_control *rstc; struct reset_controller_dev *r, *rcdev; @@ -235,7 +271,7 @@ struct reset_control *__of_reset_control_get(struct device_node *node, } /* reset_list_mutex also protects the rcdev's reset_control list */ - rstc = __reset_control_get(rcdev, rstc_id); + rstc = __reset_control_get(rcdev, rstc_id, shared); mutex_unlock(&reset_list_mutex); @@ -265,7 +301,7 @@ static void devm_reset_control_release(struct device *dev, void *res) } struct reset_control *__devm_reset_control_get(struct device *dev, - const char *id, int index) + const char *id, int index, int shared) { struct reset_control **ptr, *rstc; @@ -274,7 +310,8 @@ struct reset_control *__devm_reset_control_get(struct device *dev, if (!ptr) return ERR_PTR(-ENOMEM); - rstc = __of_reset_control_get(dev ? dev->of_node : NULL, id, index); + rstc = __of_reset_control_get(dev ? dev->of_node : NULL, + id, index, shared); if (!IS_ERR(rstc)) { *ptr = rstc; devres_add(dev, ptr); diff --git a/include/linux/reset.h b/include/linux/reset.h index 1bb69a29d6db..a552134a209e 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -13,10 +13,10 @@ int reset_control_deassert(struct reset_control *rstc); int reset_control_status(struct reset_control *rstc); struct reset_control *__of_reset_control_get(struct device_node *node, - const char *id, int index); + const char *id, int index, int shared); void reset_control_put(struct reset_control *rstc); struct reset_control *__devm_reset_control_get(struct device *dev, - const char *id, int index); + const char *id, int index, int shared); int __must_check device_reset(struct device *dev); @@ -63,14 +63,14 @@ static inline int device_reset_optional(struct device *dev) static inline struct reset_control *__of_reset_control_get( struct device_node *node, - const char *id, int index) + const char *id, int index, int shared) { return ERR_PTR(-EINVAL); } static inline struct reset_control *__devm_reset_control_get( struct device *dev, - const char *id, int index) + const char *id, int index, int shared) { return ERR_PTR(-EINVAL); } @@ -78,11 +78,17 @@ static inline struct reset_control *__devm_reset_control_get( #endif /* CONFIG_RESET_CONTROLLER */ /** - * reset_control_get - Lookup and obtain a reference to a reset controller. + * reset_control_get - Lookup and obtain an exclusive reference to a + * reset controller. * @dev: device to be reset by the controller * @id: reset line name * * Returns a struct reset_control or IS_ERR() condition containing errno. + * If this function is called more then once for the same reset_control it will + * return -EBUSY. + * + * See reset_control_get_shared for details on shared references to + * reset-controls. * * Use of id names is optional. */ @@ -92,17 +98,46 @@ static inline struct reset_control *__must_check reset_control_get( #ifndef CONFIG_RESET_CONTROLLER WARN_ON(1); #endif - return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0); + return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0, 0); } static inline struct reset_control *reset_control_get_optional( struct device *dev, const char *id) { - return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0); + return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0, 0); } /** - * of_reset_control_get - Lookup and obtain a reference to a reset controller. + * reset_control_get_shared - Lookup and obtain a shared reference to a + * reset controller. + * @dev: device to be reset by the controller + * @id: reset line name + * + * Returns a struct reset_control or IS_ERR() condition containing errno. + * This function is intended for use with reset-controls which are shared + * between hardware-blocks. + * + * When a reset-control is shared, the behavior of reset_control_assert / + * deassert is changed, the reset-core will keep track of a deassert_count + * and only (re-)assert the reset after reset_control_assert has been called + * as many times as reset_control_deassert was called. Also see the remark + * about shared reset-controls in the reset_control_assert docs. + * + * Calling reset_control_assert without first calling reset_control_deassert + * is not allowed on a shared reset control. Calling reset_control_reset is + * also not allowed on a shared reset control. + * + * Use of id names is optional. + */ +static inline struct reset_control *reset_control_get_shared( + struct device *dev, const char *id) +{ + return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0, 1); +} + +/** + * of_reset_control_get - Lookup and obtain an exclusive reference to a + * reset controller. * @node: device to be reset by the controller * @id: reset line name * @@ -113,12 +148,12 @@ static inline struct reset_control *reset_control_get_optional( static inline struct reset_control *of_reset_control_get( struct device_node *node, const char *id) { - return __of_reset_control_get(node, id, 0); + return __of_reset_control_get(node, id, 0, 0); } /** - * of_reset_control_get_by_index - Lookup and obtain a reference to a reset - * controller by index. + * of_reset_control_get_by_index - Lookup and obtain an exclusive reference to + * a reset controller by index. * @node: device to be reset by the controller * @index: index of the reset controller * @@ -129,7 +164,7 @@ static inline struct reset_control *of_reset_control_get( static inline struct reset_control *of_reset_control_get_by_index( struct device_node *node, int index) { - return __of_reset_control_get(node, NULL, index); + return __of_reset_control_get(node, NULL, index, 0); } /** @@ -147,13 +182,13 @@ static inline struct reset_control *__must_check devm_reset_control_get( #ifndef CONFIG_RESET_CONTROLLER WARN_ON(1); #endif - return __devm_reset_control_get(dev, id, 0); + return __devm_reset_control_get(dev, id, 0, 0); } static inline struct reset_control *devm_reset_control_get_optional( struct device *dev, const char *id) { - return __devm_reset_control_get(dev, id, 0); + return __devm_reset_control_get(dev, id, 0, 0); } /** @@ -168,7 +203,38 @@ static inline struct reset_control *devm_reset_control_get_optional( static inline struct reset_control *devm_reset_control_get_by_index( struct device *dev, int index) { - return __devm_reset_control_get(dev, NULL, index); + return __devm_reset_control_get(dev, NULL, index, 0); +} + +/** + * devm_reset_control_get_shared - resource managed reset_control_get_shared() + * @dev: device to be reset by the controller + * @id: reset line name + * + * Managed reset_control_get_shared(). For reset controllers returned from + * this function, reset_control_put() is called automatically on driver detach. + * See reset_control_get_shared() for more information. + */ +static inline struct reset_control *devm_reset_control_get_shared( + struct device *dev, const char *id) +{ + return __devm_reset_control_get(dev, id, 0, 1); +} + +/** + * devm_reset_control_get_shared_by_index - resource managed + * reset_control_get_shared + * @dev: device to be reset by the controller + * @index: index of the reset controller + * + * Managed reset_control_get_shared(). For reset controllers returned from + * this function, reset_control_put() is called automatically on driver detach. + * See reset_control_get_shared() for more information. + */ +static inline struct reset_control *devm_reset_control_get_shared_by_index( + struct device *dev, int index) +{ + return __devm_reset_control_get(dev, NULL, index, 1); } #endif -- cgit v1.2.3-71-gd317 From 4113652252fad972e0c191b1e536dc74a6faebdc Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 1 Apr 2016 21:38:16 +0200 Subject: reset: Add missing function stub for device_reset The Mediatek's thermal driver fails to compile when the RESET_CONTROLLER option is not set. Logically, as the driver depends on this option to compile, the Kconfig should select it but actually that is not correct because the Kconfig provides also the COMPILE_TEST to increase the compile test coverage. By providing the missing 'device_reset' stub for the driver in reset.h, that let the kernel to compile on different platforms with the Mediatek thermal driver enabled with the COMPILE_TEST option. Signed-off-by: Daniel Lezcano Signed-off-by: Philipp Zabel --- include/linux/reset.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include/linux/reset.h') diff --git a/include/linux/reset.h b/include/linux/reset.h index a552134a209e..ec0306ce7b92 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -56,6 +56,12 @@ static inline void reset_control_put(struct reset_control *rstc) WARN_ON(1); } +static inline int __must_check device_reset(struct device *dev) +{ + WARN_ON(1); + return -ENOTSUPP; +} + static inline int device_reset_optional(struct device *dev) { return -ENOTSUPP; -- cgit v1.2.3-71-gd317