arm64: dts: exynos: Update Exynos5433 CHIPID node
[platform/kernel/linux-exynos.git] / drivers / reset / core.c
index 0090784..da4292e 100644 (file)
@@ -43,11 +43,24 @@ struct reset_control {
        unsigned int id;
        struct kref refcnt;
        bool shared;
+       bool array;
        atomic_t deassert_count;
        atomic_t triggered_count;
 };
 
 /**
+ * struct reset_control_array - an array of reset controls
+ * @base: reset control for compatibility with reset control API functions
+ * @num_rstcs: number of reset controls
+ * @rstc: array of reset controls
+ */
+struct reset_control_array {
+       struct reset_control base;
+       unsigned int num_rstcs;
+       struct reset_control *rstc[];
+};
+
+/**
  * of_reset_simple_xlate - translate reset_spec to the reset line number
  * @rcdev: a pointer to the reset controller device
  * @reset_spec: reset line specifier as found in the device tree
@@ -135,6 +148,65 @@ int devm_reset_controller_register(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(devm_reset_controller_register);
 
+static inline struct reset_control_array *
+rstc_to_array(struct reset_control *rstc) {
+       return container_of(rstc, struct reset_control_array, base);
+}
+
+static int reset_control_array_reset(struct reset_control_array *resets)
+{
+       int ret, i;
+
+       for (i = 0; i < resets->num_rstcs; i++) {
+               ret = reset_control_reset(resets->rstc[i]);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int reset_control_array_assert(struct reset_control_array *resets)
+{
+       int ret, i;
+
+       for (i = 0; i < resets->num_rstcs; i++) {
+               ret = reset_control_assert(resets->rstc[i]);
+               if (ret)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       while (i--)
+               reset_control_deassert(resets->rstc[i]);
+       return ret;
+}
+
+static int reset_control_array_deassert(struct reset_control_array *resets)
+{
+       int ret, i;
+
+       for (i = 0; i < resets->num_rstcs; i++) {
+               ret = reset_control_deassert(resets->rstc[i]);
+               if (ret)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       while (i--)
+               reset_control_assert(resets->rstc[i]);
+       return ret;
+}
+
+static inline bool reset_control_is_array(struct reset_control *rstc)
+{
+       return rstc->array;
+}
+
 /**
  * reset_control_reset - reset the controlled device
  * @rstc: reset controller
@@ -158,6 +230,9 @@ int reset_control_reset(struct reset_control *rstc)
        if (WARN_ON(IS_ERR(rstc)))
                return -EINVAL;
 
+       if (reset_control_is_array(rstc))
+               return reset_control_array_reset(rstc_to_array(rstc));
+
        if (!rstc->rcdev->ops->reset)
                return -ENOTSUPP;
 
@@ -202,8 +277,8 @@ int reset_control_assert(struct reset_control *rstc)
        if (WARN_ON(IS_ERR(rstc)))
                return -EINVAL;
 
-       if (!rstc->rcdev->ops->assert)
-               return -ENOTSUPP;
+       if (reset_control_is_array(rstc))
+               return reset_control_array_assert(rstc_to_array(rstc));
 
        if (rstc->shared) {
                if (WARN_ON(atomic_read(&rstc->triggered_count) != 0))
@@ -214,6 +289,21 @@ int reset_control_assert(struct reset_control *rstc)
 
                if (atomic_dec_return(&rstc->deassert_count) != 0)
                        return 0;
+
+               /*
+                * Shared reset controls allow the reset line to be in any state
+                * after this call, so doing nothing is a valid option.
+                */
+               if (!rstc->rcdev->ops->assert)
+                       return 0;
+       } else {
+               /*
+                * If the reset controller does not implement .assert(), there
+                * is no way to guarantee that the reset line is asserted after
+                * this call.
+                */
+               if (!rstc->rcdev->ops->assert)
+                       return -ENOTSUPP;
        }
 
        return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id);
@@ -240,8 +330,8 @@ int reset_control_deassert(struct reset_control *rstc)
        if (WARN_ON(IS_ERR(rstc)))
                return -EINVAL;
 
-       if (!rstc->rcdev->ops->deassert)
-               return -ENOTSUPP;
+       if (reset_control_is_array(rstc))
+               return reset_control_array_deassert(rstc_to_array(rstc));
 
        if (rstc->shared) {
                if (WARN_ON(atomic_read(&rstc->triggered_count) != 0))
@@ -251,6 +341,16 @@ int reset_control_deassert(struct reset_control *rstc)
                        return 0;
        }
 
+       /*
+        * If the reset controller does not implement .deassert(), we assume
+        * that it handles self-deasserting reset lines via .reset(). In that
+        * case, the reset lines are deasserted by default. If that is not the
+        * case, the reset controller driver should implement .deassert() and
+        * return -ENOTSUPP.
+        */
+       if (!rstc->rcdev->ops->deassert)
+               return 0;
+
        return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id);
 }
 EXPORT_SYMBOL_GPL(reset_control_deassert);
@@ -266,7 +366,7 @@ int reset_control_status(struct reset_control *rstc)
        if (!rstc)
                return 0;
 
-       if (WARN_ON(IS_ERR(rstc)))
+       if (WARN_ON(IS_ERR(rstc)) || reset_control_is_array(rstc))
                return -EINVAL;
 
        if (rstc->rcdev->ops->status)
@@ -404,6 +504,16 @@ struct reset_control *__reset_control_get(struct device *dev, const char *id,
 }
 EXPORT_SYMBOL_GPL(__reset_control_get);
 
+static void reset_control_array_put(struct reset_control_array *resets)
+{
+       int i;
+
+       mutex_lock(&reset_list_mutex);
+       for (i = 0; i < resets->num_rstcs; i++)
+               __reset_control_put_internal(resets->rstc[i]);
+       mutex_unlock(&reset_list_mutex);
+}
+
 /**
  * reset_control_put - free the reset controller
  * @rstc: reset controller
@@ -413,6 +523,11 @@ void reset_control_put(struct reset_control *rstc)
        if (IS_ERR_OR_NULL(rstc))
                return;
 
+       if (reset_control_is_array(rstc)) {
+               reset_control_array_put(rstc_to_array(rstc));
+               return;
+       }
+
        mutex_lock(&reset_list_mutex);
        __reset_control_put_internal(rstc);
        mutex_unlock(&reset_list_mutex);
@@ -451,17 +566,18 @@ EXPORT_SYMBOL_GPL(__devm_reset_control_get);
  * device_reset - find reset controller associated with the device
  *                and perform reset
  * @dev: device to be reset by the controller
+ * @optional: whether it is optional to reset the device
  *
- * Convenience wrapper for reset_control_get() and reset_control_reset().
+ * Convenience wrapper for __reset_control_get() and reset_control_reset().
  * This is useful for the common case of devices with single, dedicated reset
  * lines.
  */
-int device_reset(struct device *dev)
+int __device_reset(struct device *dev, bool optional)
 {
        struct reset_control *rstc;
        int ret;
 
-       rstc = reset_control_get(dev, NULL);
+       rstc = __reset_control_get(dev, NULL, 0, 0, optional);
        if (IS_ERR(rstc))
                return PTR_ERR(rstc);
 
@@ -471,4 +587,117 @@ int device_reset(struct device *dev)
 
        return ret;
 }
-EXPORT_SYMBOL_GPL(device_reset);
+EXPORT_SYMBOL_GPL(__device_reset);
+
+/**
+ * APIs to manage an array of reset controls.
+ */
+/**
+ * of_reset_control_get_count - Count number of resets available with a device
+ *
+ * @node: device node that contains 'resets'.
+ *
+ * Returns positive reset count on success, or error number on failure and
+ * on count being zero.
+ */
+static int of_reset_control_get_count(struct device_node *node)
+{
+       int count;
+
+       if (!node)
+               return -EINVAL;
+
+       count = of_count_phandle_with_args(node, "resets", "#reset-cells");
+       if (count == 0)
+               count = -ENOENT;
+
+       return count;
+}
+
+/**
+ * of_reset_control_array_get - Get a list of reset controls using
+ *                             device node.
+ *
+ * @np: device node for the device that requests the reset controls array
+ * @shared: whether reset controls are shared or not
+ * @optional: whether it is optional to get the reset controls
+ *
+ * Returns pointer to allocated reset_control_array on success or
+ * error on failure
+ */
+struct reset_control *
+of_reset_control_array_get(struct device_node *np, bool shared, bool optional)
+{
+       struct reset_control_array *resets;
+       struct reset_control *rstc;
+       int num, i;
+
+       num = of_reset_control_get_count(np);
+       if (num < 0)
+               return optional ? NULL : ERR_PTR(num);
+
+       resets = kzalloc(sizeof(*resets) + sizeof(resets->rstc[0]) * num,
+                        GFP_KERNEL);
+       if (!resets)
+               return ERR_PTR(-ENOMEM);
+
+       for (i = 0; i < num; i++) {
+               rstc = __of_reset_control_get(np, NULL, i, shared, optional);
+               if (IS_ERR(rstc))
+                       goto err_rst;
+               resets->rstc[i] = rstc;
+       }
+       resets->num_rstcs = num;
+       resets->base.array = true;
+
+       return &resets->base;
+
+err_rst:
+       mutex_lock(&reset_list_mutex);
+       while (--i >= 0)
+               __reset_control_put_internal(resets->rstc[i]);
+       mutex_unlock(&reset_list_mutex);
+
+       kfree(resets);
+
+       return rstc;
+}
+EXPORT_SYMBOL_GPL(of_reset_control_array_get);
+
+/**
+ * devm_reset_control_array_get - Resource managed reset control array get
+ *
+ * @dev: device that requests the list of reset controls
+ * @shared: whether reset controls are shared or not
+ * @optional: whether it is optional to get the reset controls
+ *
+ * The reset control array APIs are intended for a list of resets
+ * that just have to be asserted or deasserted, without any
+ * requirements on the order.
+ *
+ * Returns pointer to allocated reset_control_array on success or
+ * error on failure
+ */
+struct reset_control *
+devm_reset_control_array_get(struct device *dev, bool shared, bool optional)
+{
+       struct reset_control **devres;
+       struct reset_control *rstc;
+
+       devres = devres_alloc(devm_reset_control_release, sizeof(*devres),
+                             GFP_KERNEL);
+       if (!devres)
+               return ERR_PTR(-ENOMEM);
+
+       rstc = of_reset_control_array_get(dev->of_node, shared, optional);
+       if (IS_ERR(rstc)) {
+               devres_free(devres);
+               return rstc;
+       }
+
+       *devres = rstc;
+       devres_add(dev, devres);
+
+       return rstc;
+}
+EXPORT_SYMBOL_GPL(devm_reset_control_array_get);