diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2015-12-08 08:28:49 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2015-12-08 08:28:49 +0100 |
commit | 3c40bc8c26beac31ada80efa99b60d49b27da64a (patch) | |
tree | 584107042d615e3261f92206202b5fba3890324f /drivers | |
parent | 9fdf56c8d47d1084fef065f2a234a9b42c694279 (diff) | |
parent | d9ba9c92b72b1c795da524becd41859450aafc1a (diff) | |
download | barebox-3c40bc8c26beac31ada80efa99b60d49b27da64a.tar.gz barebox-3c40bc8c26beac31ada80efa99b60d49b27da64a.tar.xz |
Merge branch 'for-next/reset'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/reset/core.c | 103 |
1 files changed, 99 insertions, 4 deletions
diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 101913aefe..0f900a92d0 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -9,7 +9,9 @@ * (at your option) any later version. */ #include <common.h> +#include <gpio.h> #include <malloc.h> +#include <of_gpio.h> #include <linux/err.h> #include <linux/kernel.h> #include <linux/reset.h> @@ -26,6 +28,10 @@ static LIST_HEAD(reset_controller_list); */ 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 @@ EXPORT_SYMBOL_GPL(reset_controller_unregister); */ 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 @@ EXPORT_SYMBOL_GPL(reset_control_reset); */ 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 @@ EXPORT_SYMBOL_GPL(reset_control_assert); */ 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 @@ struct reset_control *of_reset_control_get(struct device_node *node, 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 @@ struct reset_control *of_reset_control_get(struct device_node *node, } 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 @@ struct reset_control *reset_control_get(struct device_d *dev, const char *id) 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 @@ EXPORT_SYMBOL_GPL(reset_control_get); 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 device_reset(struct device_d *dev) 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 @@ int device_reset(struct device_d *dev) 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); |