[PATCH/RFC v4 1/2] reset: Add support for dedicated reset controls
From: Geert Uytterhoeven
Date: Mon Sep 17 2018 - 12:40:10 EST
In some SoCs multiple hardware blocks may share a reset control.
The existing reset control API for shared resets will only assert such a
reset when the drivers for all hardware blocks agree.
The existing exclusive reset control API still allows to assert such a
reset, but that impacts all other hardware blocks sharing the reset.
Sometimes a driver needs to reset a specific hardware block, and be 100%
sure it has no impact on other hardware blocks. This is e.g. the case
for virtualization with device pass-through, where the host wants to
reset any exported device before and after exporting it for use by the
guest, for isolation.
Hence a new flag for dedicated resets is added to the internal methods,
with a new public reset_control_get_dedicated() method, to obtain an
exclusive handle to a reset that is dedicated to one specific hardware
block.
This supports both DT-based and lookup-based reset controls.
Signed-off-by: Geert Uytterhoeven <geert+renesas@xxxxxxxxx>
---
v4:
- New.
Notes:
- Dedicated lookup-based reset controls were not tested,
- Several internal functions now take 3 boolean flags, and should
probably be converted to take a bitmask instead,
- I think __device_reset() should call __reset_control_get() with
dedicated=true. However, that will impact existing users,
- Should a different error than -EINVAL be returned on failure?
---
drivers/reset/core.c | 76 ++++++++++++++++++++++++++++++++++++++-----
include/linux/reset.h | 60 ++++++++++++++++++++++------------
2 files changed, 107 insertions(+), 29 deletions(-)
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 225e34c56b94a2e3..5bc4eeca70c0fcc2 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -459,9 +459,38 @@ static void __reset_control_put_internal(struct reset_control *rstc)
kref_put(&rstc->refcnt, __reset_control_release);
}
+static bool __of_reset_is_dedicated(const struct device_node *node,
+ const struct of_phandle_args args)
+{
+ struct of_phandle_args args2;
+ struct device_node *node2;
+ int index, ret;
+
+ for_each_node_with_property(node2, "resets") {
+ if (node == node2)
+ continue;
+
+ for (index = 0; ; index++) {
+ ret = of_parse_phandle_with_args(node2, "resets",
+ "#reset-cells", index,
+ &args2);
+ if (ret)
+ break;
+
+ if (args2.np == args.np &&
+ args2.args_count == args.args_count &&
+ !memcmp(args2.args, args.args,
+ args.args_count * sizeof(args.args[0])))
+ return false;
+ }
+ }
+
+ return true;
+}
+
struct reset_control *__of_reset_control_get(struct device_node *node,
const char *id, int index, bool shared,
- bool optional)
+ bool optional, bool dedicated)
{
struct reset_control *rstc;
struct reset_controller_dev *r, *rcdev;
@@ -514,6 +543,11 @@ struct reset_control *__of_reset_control_get(struct device_node *node,
return ERR_PTR(rstc_id);
}
+ if (dedicated && !__of_reset_is_dedicated(node, args)) {
+ mutex_unlock(&reset_list_mutex);
+ return ERR_PTR(-EINVAL);
+ }
+
/* reset_list_mutex also protects the rcdev's reset_control list */
rstc = __reset_control_get_internal(rcdev, rstc_id, shared);
@@ -541,9 +575,25 @@ __reset_controller_by_name(const char *name)
return NULL;
}
+static bool __reset_is_dedicated(const struct reset_control_lookup *lookup)
+{
+ const struct reset_control_lookup *lookup2;
+
+ list_for_each_entry(lookup, &reset_lookup_list, list) {
+ if (lookup2 == lookup)
+ continue;
+
+ if (lookup2->provider == lookup->provider &&
+ lookup2->index == lookup->index)
+ return false;
+ }
+
+ return true;
+}
+
static struct reset_control *
__reset_control_get_from_lookup(struct device *dev, const char *con_id,
- bool shared, bool optional)
+ bool shared, bool optional, bool dedicated)
{
const struct reset_control_lookup *lookup;
struct reset_controller_dev *rcdev;
@@ -562,6 +612,11 @@ __reset_control_get_from_lookup(struct device *dev, const char *con_id,
if ((!con_id && !lookup->con_id) ||
((con_id && lookup->con_id) &&
!strcmp(con_id, lookup->con_id))) {
+ if (dedicated && !__reset_is_dedicated(lookup)) {
+ mutex_unlock(&reset_lookup_mutex);
+ return ERR_PTR(-EINVAL);
+ }
+
mutex_lock(&reset_list_mutex);
rcdev = __reset_controller_by_name(lookup->provider);
if (!rcdev) {
@@ -588,13 +643,15 @@ __reset_control_get_from_lookup(struct device *dev, const char *con_id,
}
struct reset_control *__reset_control_get(struct device *dev, const char *id,
- int index, bool shared, bool optional)
+ int index, bool shared,
+ bool optional, bool dedicated)
{
if (dev->of_node)
return __of_reset_control_get(dev->of_node, id, index, shared,
- optional);
+ optional, dedicated);
- return __reset_control_get_from_lookup(dev, id, shared, optional);
+ return __reset_control_get_from_lookup(dev, id, shared, optional,
+ dedicated);
}
EXPORT_SYMBOL_GPL(__reset_control_get);
@@ -635,7 +692,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, bool shared,
- bool optional)
+ bool optional, bool dedicated)
{
struct reset_control **ptr, *rstc;
@@ -644,7 +701,7 @@ struct reset_control *__devm_reset_control_get(struct device *dev,
if (!ptr)
return ERR_PTR(-ENOMEM);
- rstc = __reset_control_get(dev, id, index, shared, optional);
+ rstc = __reset_control_get(dev, id, index, shared, optional, dedicated);
if (!IS_ERR(rstc)) {
*ptr = rstc;
devres_add(dev, ptr);
@@ -671,7 +728,7 @@ int __device_reset(struct device *dev, bool optional)
struct reset_control *rstc;
int ret;
- rstc = __reset_control_get(dev, NULL, 0, 0, optional);
+ rstc = __reset_control_get(dev, NULL, 0, false, optional, false);
if (IS_ERR(rstc))
return PTR_ERR(rstc);
@@ -735,7 +792,8 @@ of_reset_control_array_get(struct device_node *np, bool shared, bool optional)
return ERR_PTR(-ENOMEM);
for (i = 0; i < num; i++) {
- rstc = __of_reset_control_get(np, NULL, i, shared, optional);
+ rstc = __of_reset_control_get(np, NULL, i, shared, optional,
+ false);
if (IS_ERR(rstc))
goto err_rst;
resets->rstc[i] = rstc;
diff --git a/include/linux/reset.h b/include/linux/reset.h
index 09732c36f3515a1e..6ca6e108b612f923 100644
--- a/include/linux/reset.h
+++ b/include/linux/reset.h
@@ -17,15 +17,15 @@ int reset_control_status(struct reset_control *rstc);
struct reset_control *__of_reset_control_get(struct device_node *node,
const char *id, int index, bool shared,
- bool optional);
+ bool optional, bool dedicated);
struct reset_control *__reset_control_get(struct device *dev, const char *id,
int index, bool shared,
- bool optional);
+ bool optional, bool dedicated);
void reset_control_put(struct reset_control *rstc);
int __device_reset(struct device *dev, bool optional);
struct reset_control *__devm_reset_control_get(struct device *dev,
const char *id, int index, bool shared,
- bool optional);
+ bool optional, bool dedicated);
struct reset_control *devm_reset_control_array_get(struct device *dev,
bool shared, bool optional);
@@ -66,21 +66,23 @@ static inline int __device_reset(struct device *dev, bool optional)
static inline struct reset_control *__of_reset_control_get(
struct device_node *node,
const char *id, int index, bool shared,
- bool optional)
+ bool optional, bool dedicated)
{
return optional ? NULL : ERR_PTR(-ENOTSUPP);
}
static inline struct reset_control *__reset_control_get(
struct device *dev, const char *id,
- int index, bool shared, bool optional)
+ int index, bool shared, bool optional,
+ bool dedicated)
{
return optional ? NULL : ERR_PTR(-ENOTSUPP);
}
static inline struct reset_control *__devm_reset_control_get(
struct device *dev, const char *id,
- int index, bool shared, bool optional)
+ int index, bool shared, bool optional,
+ bool dedicated)
{
return optional ? NULL : ERR_PTR(-ENOTSUPP);
}
@@ -127,7 +129,25 @@ static inline int device_reset_optional(struct device *dev)
static inline struct reset_control *
__must_check reset_control_get_exclusive(struct device *dev, const char *id)
{
- return __reset_control_get(dev, id, 0, false, false);
+ return __reset_control_get(dev, id, 0, false, false, false);
+}
+
+/**
+ * reset_control_get_dedicated - Lookup and obtain an exclusive reference
+ * to a dedicated 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 than once for the same reset_control it will
+ * return -EBUSY.
+ *
+ * Use of id names is optional.
+ */
+static inline struct reset_control *
+__must_check reset_control_get_dedicated(struct device *dev, const char *id)
+{
+ return __reset_control_get(dev, id, 0, false, false, true);
}
/**
@@ -155,19 +175,19 @@ __must_check reset_control_get_exclusive(struct device *dev, const char *id)
static inline struct reset_control *reset_control_get_shared(
struct device *dev, const char *id)
{
- return __reset_control_get(dev, id, 0, true, false);
+ return __reset_control_get(dev, id, 0, true, false, false);
}
static inline struct reset_control *reset_control_get_optional_exclusive(
struct device *dev, const char *id)
{
- return __reset_control_get(dev, id, 0, false, true);
+ return __reset_control_get(dev, id, 0, false, true, false);
}
static inline struct reset_control *reset_control_get_optional_shared(
struct device *dev, const char *id)
{
- return __reset_control_get(dev, id, 0, true, true);
+ return __reset_control_get(dev, id, 0, true, true, false);
}
/**
@@ -183,7 +203,7 @@ static inline struct reset_control *reset_control_get_optional_shared(
static inline struct reset_control *of_reset_control_get_exclusive(
struct device_node *node, const char *id)
{
- return __of_reset_control_get(node, id, 0, false, false);
+ return __of_reset_control_get(node, id, 0, false, false, false);
}
/**
@@ -208,7 +228,7 @@ static inline struct reset_control *of_reset_control_get_exclusive(
static inline struct reset_control *of_reset_control_get_shared(
struct device_node *node, const char *id)
{
- return __of_reset_control_get(node, id, 0, true, false);
+ return __of_reset_control_get(node, id, 0, true, false, false);
}
/**
@@ -225,7 +245,7 @@ static inline struct reset_control *of_reset_control_get_shared(
static inline struct reset_control *of_reset_control_get_exclusive_by_index(
struct device_node *node, int index)
{
- return __of_reset_control_get(node, NULL, index, false, false);
+ return __of_reset_control_get(node, NULL, index, false, false, false);
}
/**
@@ -253,7 +273,7 @@ static inline struct reset_control *of_reset_control_get_exclusive_by_index(
static inline struct reset_control *of_reset_control_get_shared_by_index(
struct device_node *node, int index)
{
- return __of_reset_control_get(node, NULL, index, true, false);
+ return __of_reset_control_get(node, NULL, index, true, false, false);
}
/**
@@ -272,7 +292,7 @@ static inline struct reset_control *
__must_check devm_reset_control_get_exclusive(struct device *dev,
const char *id)
{
- return __devm_reset_control_get(dev, id, 0, false, false);
+ return __devm_reset_control_get(dev, id, 0, false, false, false);
}
/**
@@ -287,19 +307,19 @@ __must_check devm_reset_control_get_exclusive(struct device *dev,
static inline struct reset_control *devm_reset_control_get_shared(
struct device *dev, const char *id)
{
- return __devm_reset_control_get(dev, id, 0, true, false);
+ return __devm_reset_control_get(dev, id, 0, true, false, false);
}
static inline struct reset_control *devm_reset_control_get_optional_exclusive(
struct device *dev, const char *id)
{
- return __devm_reset_control_get(dev, id, 0, false, true);
+ return __devm_reset_control_get(dev, id, 0, false, true, false);
}
static inline struct reset_control *devm_reset_control_get_optional_shared(
struct device *dev, const char *id)
{
- return __devm_reset_control_get(dev, id, 0, true, true);
+ return __devm_reset_control_get(dev, id, 0, true, true, false);
}
/**
@@ -317,7 +337,7 @@ static inline struct reset_control *devm_reset_control_get_optional_shared(
static inline struct reset_control *
devm_reset_control_get_exclusive_by_index(struct device *dev, int index)
{
- return __devm_reset_control_get(dev, NULL, index, false, false);
+ return __devm_reset_control_get(dev, NULL, index, false, false, false);
}
/**
@@ -333,7 +353,7 @@ devm_reset_control_get_exclusive_by_index(struct device *dev, int index)
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, true, false);
+ return __devm_reset_control_get(dev, NULL, index, true, false, false);
}
/*
--
2.17.1