diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 101913a..0f900a9 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -9,7 +9,9 @@ * (at your option) any later version. */ #include +#include #include +#include #include #include #include @@ -26,6 +28,10 @@ */ struct reset_control { struct reset_controller_dev *rcdev; + + int gpio; + int gpio_active_high; + struct device_d *dev; unsigned int id; }; @@ -84,6 +90,9 @@ */ int reset_control_reset(struct reset_control *rstc) { + if (!rstc) + return 0; + if (rstc->rcdev->ops->reset) return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id); @@ -97,6 +106,12 @@ */ int reset_control_assert(struct reset_control *rstc) { + if (!rstc) + return 0; + + if (rstc->gpio >= 0) + return gpio_direction_output(rstc->gpio, rstc->gpio_active_high); + if (rstc->rcdev->ops->assert) return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); @@ -110,6 +125,12 @@ */ int reset_control_deassert(struct reset_control *rstc) { + if (!rstc) + return 0; + + if (rstc->gpio >= 0) + return gpio_direction_output(rstc->gpio, !rstc->gpio_active_high); + if (rstc->rcdev->ops->deassert) return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); @@ -136,6 +157,9 @@ int rstc_id; int ret; + if (!of_get_property(node, "resets", NULL)) + return NULL; + if (id) index = of_property_match_string(node, "reset-names", id); @@ -171,6 +195,29 @@ } EXPORT_SYMBOL_GPL(of_reset_control_get); +struct reset_control *gpio_reset_control_get(struct device_d *dev, const char *id) +{ + struct reset_control *rc; + int gpio; + enum of_gpio_flags flags; + + if (id) + return ERR_PTR(-EINVAL); + + if (!of_get_property(dev->device_node, "reset-gpios", NULL)) + return NULL; + + gpio = of_get_named_gpio_flags(dev->device_node, "reset-gpios", 0, &flags); + if (gpio < 0) + return ERR_PTR(gpio); + + rc = xzalloc(sizeof(*rc)); + rc->gpio = gpio; + rc->gpio_active_high = !(flags & OF_GPIO_ACTIVE_LOW); + + return rc; +} + /** * reset_control_get - Lookup and obtain a reference to a reset controller. * @dev: device to be reset by the controller @@ -188,8 +235,23 @@ return ERR_PTR(-EINVAL); rstc = of_reset_control_get(dev->device_node, id); - if (!IS_ERR(rstc)) - rstc->dev = dev; + if (IS_ERR(rstc)) + return ERR_CAST(rstc); + + /* + * If there is no dedicated reset controller device, check if we have + * a reset line controlled by a GPIO instead. + */ + if (!rstc) { + rstc = gpio_reset_control_get(dev, id); + if (IS_ERR(rstc)) + return ERR_CAST(rstc); + } + + if (!rstc) + return NULL; + + rstc->dev = dev; return rstc; } @@ -202,7 +264,7 @@ void reset_control_put(struct reset_control *rstc) { - if (IS_ERR(rstc)) + if (IS_ERR_OR_NULL(rstc)) return; kfree(rstc); @@ -224,8 +286,12 @@ int ret; rstc = reset_control_get(dev, NULL); - if (IS_ERR(rstc)) + if (IS_ERR(rstc)) { + if (PTR_ERR(rstc) == -ENOENT) + return 0; + return PTR_ERR(rstc); + } ret = reset_control_reset(rstc); @@ -234,3 +300,32 @@ return ret; } EXPORT_SYMBOL_GPL(device_reset); + +int device_reset_us(struct device_d *dev, int us) +{ + struct reset_control *rstc; + int ret; + + rstc = reset_control_get(dev, NULL); + if (IS_ERR(rstc)) { + if (PTR_ERR(rstc) == -ENOENT) + return 0; + + return PTR_ERR(rstc); + } + + ret = reset_control_assert(rstc); + if (ret) + return ret; + + udelay(us); + + ret = reset_control_deassert(rstc); + if (ret) + return ret; + + reset_control_put(rstc); + + return ret; +} +EXPORT_SYMBOL_GPL(device_reset_us); diff --git a/include/linux/reset.h b/include/linux/reset.h index f6c475c..be0d1bb 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -15,16 +15,7 @@ int __must_check device_reset(struct device_d *dev); -static inline int device_reset_optional(struct device_d *dev) -{ - return device_reset(dev); -} - -static inline struct reset_control *reset_control_get_optional( - struct device_d *dev, const char *id) -{ - return reset_control_get(dev, id); -} +int __must_check device_reset_us(struct device_d *dev, int us); #else @@ -51,15 +42,10 @@ WARN_ON(1); } -static inline int device_reset_optional(struct device_d *dev) +static inline int device_reset_us(struct device_d *dev, int us) { - return -ENOSYS; -} - -static inline struct reset_control *reset_control_get_optional( - struct device_d *dev, const char *id) -{ - return ERR_PTR(-ENOSYS); + WARN_ON(1); + return 0; } #endif /* CONFIG_RESET_CONTROLLER */