diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2017-06-14 09:29:53 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2017-06-14 09:29:53 +0200 |
commit | 4a62a18d33e39f00d717c4aa314697dca924cf3f (patch) | |
tree | ffb4af6c18d67754af46bb2b1abe71764415c4ff /drivers | |
parent | b1eff2cc397fa99630fe8779b511b5715ec39c07 (diff) | |
parent | a762345c7c331cf0c80819ab2fc368f3cec0a4e9 (diff) | |
download | barebox-4a62a18d33e39f00d717c4aa314697dca924cf3f.tar.gz barebox-4a62a18d33e39f00d717c4aa314697dca924cf3f.tar.xz |
Merge branch 'for-next/gpio'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpio/gpio-imx.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.c | 146 | ||||
-rw-r--r-- | drivers/phy/usb-nop-xceiv.c | 45 |
3 files changed, 183 insertions, 10 deletions
diff --git a/drivers/gpio/gpio-imx.c b/drivers/gpio/gpio-imx.c index bfb0119c84..d8bcea2234 100644 --- a/drivers/gpio/gpio-imx.c +++ b/drivers/gpio/gpio-imx.c @@ -93,7 +93,7 @@ static int imx_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int void __iomem *base = imxgpio->base; u32 val; - gpio_set_value(gpio + chip->base, value); + imx_gpio_set_value(chip, gpio, value); val = readl(base + imxgpio->regs->gdir); val |= 1 << gpio; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1f57c76ec1..a3e17ada0d 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -5,6 +5,7 @@ #include <command.h> #include <complete.h> #include <gpio.h> +#include <of_gpio.h> #include <errno.h> #include <malloc.h> @@ -13,6 +14,7 @@ static LIST_HEAD(chip_list); struct gpio_info { struct gpio_chip *chip; bool requested; + bool active_low; char *label; }; @@ -45,6 +47,15 @@ static struct gpio_info *gpio_to_desc(unsigned gpio) return NULL; } +static int gpio_adjust_value(struct gpio_info *gi, + int value) +{ + if (value < 0) + return value; + + return !!value ^ gi->active_low; +} + int gpio_request(unsigned gpio, const char *label) { struct gpio_info *gi = gpio_to_desc(gpio); @@ -69,6 +80,7 @@ int gpio_request(unsigned gpio, const char *label) } gi->requested = true; + gi->active_low = false; gi->label = xstrdup(label); done: @@ -93,6 +105,7 @@ void gpio_free(unsigned gpio) gi->chip->ops->free(gi->chip, gpio - gi->chip->base); gi->requested = false; + gi->active_low = false; free(gi->label); gi->label = NULL; } @@ -111,11 +124,20 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) if (err) return err; - if (flags & GPIOF_DIR_IN) + if (flags & GPIOF_ACTIVE_LOW) { + struct gpio_info *gi = gpio_to_desc(gpio); + gi->active_low = true; + } + + if (flags & GPIOF_DIR_IN) { err = gpio_direction_input(gpio); - else + } else if (flags & GPIOF_LOGICAL) { + err = gpio_direction_active(gpio, + !!(flags & GPIOF_INIT_ACTIVE)); + } else { err = gpio_direction_output(gpio, - (flags & GPIOF_INIT_HIGH) ? 1 : 0); + !!(flags & GPIOF_INIT_HIGH)); + } if (err) goto free_gpio; @@ -178,6 +200,13 @@ void gpio_set_value(unsigned gpio, int value) } EXPORT_SYMBOL(gpio_set_value); +void gpio_set_active(unsigned gpio, bool value) +{ + struct gpio_info *gi = gpio_to_desc(gpio); + gpio_set_value(gpio, gpio_adjust_value(gi, value)); +} +EXPORT_SYMBOL(gpio_set_active); + int gpio_get_value(unsigned gpio) { struct gpio_info *gi = gpio_to_desc(gpio); @@ -196,6 +225,13 @@ int gpio_get_value(unsigned gpio) } EXPORT_SYMBOL(gpio_get_value); +int gpio_is_active(unsigned gpio) +{ + struct gpio_info *gi = gpio_to_desc(gpio); + return gpio_adjust_value(gi, gpio_get_value(gpio)); +} +EXPORT_SYMBOL(gpio_is_active); + int gpio_direction_output(unsigned gpio, int value) { struct gpio_info *gi = gpio_to_desc(gpio); @@ -215,6 +251,13 @@ int gpio_direction_output(unsigned gpio, int value) } EXPORT_SYMBOL(gpio_direction_output); +int gpio_direction_active(unsigned gpio, bool value) +{ + struct gpio_info *gi = gpio_to_desc(gpio); + return gpio_direction_output(gpio, gpio_adjust_value(gi, value)); +} +EXPORT_SYMBOL(gpio_direction_active); + int gpio_direction_input(unsigned gpio) { struct gpio_info *gi = gpio_to_desc(gpio); @@ -262,6 +305,99 @@ static int gpiochip_find_base(int start, int ngpio) return base; } +static int of_hog_gpio(struct device_node *np, struct gpio_chip *chip, + unsigned int idx) +{ + struct device_node *chip_np = chip->dev->device_node; + unsigned long flags = 0; + u32 gpio_cells, gpio_num, gpio_flags; + int ret, gpio; + const char *name = NULL; + + ret = of_property_read_u32(chip_np, "#gpio-cells", &gpio_cells); + if (ret) + return ret; + + /* + * Support for GPIOs that don't have #gpio-cells set to 2 is + * not implemented + */ + if (WARN_ON(gpio_cells != 2)) + return -ENOTSUPP; + + ret = of_property_read_u32_index(np, "gpios", idx * gpio_cells, + &gpio_num); + if (ret) + return ret; + + ret = of_property_read_u32_index(np, "gpios", idx * gpio_cells + 1, + &gpio_flags); + if (ret) + return ret; + + if (gpio_flags & OF_GPIO_ACTIVE_LOW) + flags |= GPIOF_ACTIVE_LOW; + + gpio = gpio_get_num(chip->dev, gpio_num); + if (ret == -EPROBE_DEFER) + return ret; + + if (ret < 0) { + dev_err(chip->dev, "unable to get gpio %u\n", gpio_num); + return ret; + } + + + /* + * Note that, in order to be compatible with Linux, the code + * below interprets 'output-high' as to mean 'output-active'. + * That is, when processed for active-low GPIO, it will result + * in output being asserted logically 'active', but physically + * 'low'. + * + * Conversely it means that specifying 'output-low' for + * 'active-low' GPIO would result in 'high' level observed on + * the corresponding pin + * + */ + if (of_property_read_bool(np, "input")) + flags |= GPIOF_DIR_IN; + else if (of_property_read_bool(np, "output-low")) + flags |= GPIOF_OUT_INIT_INACTIVE; + else if (of_property_read_bool(np, "output-high")) + flags |= GPIOF_OUT_INIT_ACTIVE; + else + return -EINVAL; + + of_property_read_string(np, "line-name", &name); + + return gpio_request_one(gpio, flags, name); +} + +static int of_gpiochip_scan_hogs(struct gpio_chip *chip) +{ + struct device_node *np; + int ret, i; + + for_each_available_child_of_node(chip->dev->device_node, np) { + if (!of_property_read_bool(np, "gpio-hog")) + continue; + + for (ret = 0, i = 0; + !ret; + ret = of_hog_gpio(np, chip, i), i++) + ; + /* + * We ignore -EOVERFLOW because it serves us as an + * indicator that there's no more GPIOs to handle. + */ + if (ret < 0 && ret != -EOVERFLOW) + return ret; + } + + return 0; +} + int gpiochip_add(struct gpio_chip *chip) { int base, i; @@ -280,7 +416,7 @@ int gpiochip_add(struct gpio_chip *chip) for (i = chip->base; i < chip->base + chip->ngpio; i++) gpio_desc[i].chip = chip; - return 0; + return of_gpiochip_scan_hogs(chip); } void gpiochip_remove(struct gpio_chip *chip) @@ -334,7 +470,7 @@ static int do_gpiolib(int argc, char *argv[]) printf(" GPIO %*d: %*s %*s %*s %s\n", 4, i, 3, (dir < 0) ? "unk" : ((dir == GPIOF_DIR_IN) ? "in" : "out"), 3, (val < 0) ? "unk" : ((val == 0) ? "lo" : "hi"), - 9, gi->requested ? "true" : "false", + 12, gi->requested ? (gi->active_low ? "active low" : "true") : "false", (gi->requested && gi->label) ? gi->label : ""); } diff --git a/drivers/phy/usb-nop-xceiv.c b/drivers/phy/usb-nop-xceiv.c index d403fe4d66..b124e6c0c4 100644 --- a/drivers/phy/usb-nop-xceiv.c +++ b/drivers/phy/usb-nop-xceiv.c @@ -22,12 +22,15 @@ #include <linux/phy/phy.h> #include <linux/clk.h> #include <linux/err.h> +#include <gpio.h> +#include <of_gpio.h> struct nop_usbphy { struct usb_phy usb_phy; struct phy *phy; struct phy_provider *provider; struct clk *clk; + int reset; }; static struct phy *nop_usbphy_xlate(struct device_d *dev, @@ -40,9 +43,22 @@ static struct phy *nop_usbphy_xlate(struct device_d *dev, static int nop_usbphy_init(struct phy *phy) { + int ret; struct nop_usbphy *nopphy = phy_get_drvdata(phy); - return clk_enable(nopphy->clk); + ret = clk_enable(nopphy->clk); + if (ret < 0) + return ret; + + if (gpio_is_valid(nopphy->reset)) { + /* + * Let's wait for 100 ms before deasserting reset line + */ + mdelay(100); + gpio_set_active(nopphy->reset, false); + } + + return 0; } static struct usb_phy *nop_usbphy_to_usbphy(struct phy *phy) @@ -61,6 +77,9 @@ static int nop_usbphy_probe(struct device_d *dev) { int ret; struct nop_usbphy *nopphy; + enum of_gpio_flags of_flags; + char *name = NULL; + unsigned long flags = GPIOF_OUT_INIT_ACTIVE; nopphy = xzalloc(sizeof(*nopphy)); @@ -70,6 +89,20 @@ static int nop_usbphy_probe(struct device_d *dev) if (IS_ERR(nopphy->clk)) nopphy->clk = NULL; + nopphy->reset = of_get_named_gpio_flags(dev->device_node, + "reset-gpios", 0, &of_flags); + if (gpio_is_valid(nopphy->reset)) { + /* assert reset */ + + if (of_flags & OF_GPIO_ACTIVE_LOW) + flags |= GPIOF_ACTIVE_LOW; + + name = basprintf("%s reset", dev_name(dev)); + ret = gpio_request_one(nopphy->reset, flags, name); + if (ret < 0) + goto err_free; + } + /* FIXME: Add vbus regulator support */ /* FIXME: Add vbus-detect-gpio support */ @@ -77,7 +110,7 @@ static int nop_usbphy_probe(struct device_d *dev) nopphy->phy = phy_create(dev, NULL, &nop_phy_ops, NULL); if (IS_ERR(nopphy->phy)) { ret = PTR_ERR(nopphy->phy); - goto err_free; + goto release_gpio; } phy_set_drvdata(nopphy->phy, nopphy); @@ -85,13 +118,17 @@ static int nop_usbphy_probe(struct device_d *dev) nopphy->provider = of_phy_provider_register(dev, nop_usbphy_xlate); if (IS_ERR(nopphy->provider)) { ret = PTR_ERR(nopphy->provider); - goto err_free; + goto release_gpio; } return 0; + +release_gpio: + if (gpio_is_valid(nopphy->reset)) + gpio_free(nopphy->reset); err_free: free(nopphy); - + free(name); return ret; }; |