diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2016-11-14 12:37:20 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2016-11-14 12:37:20 +0100 |
commit | 8eb1fb3519b6c1ae6ddab114a7cdc86bb24da296 (patch) | |
tree | a85ef526dca96e6c12df5cc33c935b854d77fb93 /drivers | |
parent | f55e379fca456444ade761358e8cce8657ea9672 (diff) | |
parent | 764f84809fedd39fca75e2c8731ab181a421fd5b (diff) | |
download | barebox-8eb1fb3519b6c1ae6ddab114a7cdc86bb24da296.tar.gz barebox-8eb1fb3519b6c1ae6ddab114a7cdc86bb24da296.tar.xz |
Merge branch 'for-next/vybrid'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/clk.c | 89 | ||||
-rw-r--r-- | drivers/pinctrl/Kconfig | 5 | ||||
-rw-r--r-- | drivers/pinctrl/Makefile | 1 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl-vf610.c | 167 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl.c | 45 |
5 files changed, 300 insertions, 7 deletions
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 15e424db1b..19377b893c 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -406,7 +406,7 @@ EXPORT_SYMBOL_GPL(of_clk_del_provider); struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec) { struct of_clk_provider *provider; - struct clk *clk = ERR_PTR(-ENOENT); + struct clk *clk = ERR_PTR(-EPROBE_DEFER); /* Check if we have such a provider in our array */ list_for_each_entry(provider, &of_clk_providers, link) { @@ -439,6 +439,47 @@ char *of_clk_get_parent_name(struct device_node *np, unsigned int index) } EXPORT_SYMBOL_GPL(of_clk_get_parent_name); +struct clock_provider { + of_clk_init_cb_t clk_init_cb; + struct device_node *np; + struct list_head node; +}; + +/* + * This function looks for a parent clock. If there is one, then it + * checks that the provider for this parent clock was initialized, in + * this case the parent clock will be ready. + */ +static int parent_ready(struct device_node *np) +{ + int i = 0; + + while (true) { + struct clk *clk = of_clk_get(np, i); + + /* this parent is ready we can check the next one */ + if (!IS_ERR(clk)) { + clk_put(clk); + i++; + continue; + } + + /* at least one parent is not ready, we exit now */ + if (PTR_ERR(clk) == -EPROBE_DEFER) + return 0; + + /* + * Here we make assumption that the device tree is + * written correctly. So an error means that there is + * no more parent. As we didn't exit yet, then the + * previous parent are ready. If there is no clock + * parent, no need to wait for them, then we can + * consider their absence as being ready + */ + return 1; + } +} + /** * of_clk_init() - Scan and init clock providers from the DT * @root: parent of the first level to probe or NULL for the root of the tree @@ -451,8 +492,11 @@ EXPORT_SYMBOL_GPL(of_clk_get_parent_name); */ int of_clk_init(struct device_node *root, const struct of_device_id *matches) { + struct clock_provider *clk_provider, *next; + bool is_init_done; + bool force = false; + LIST_HEAD(clk_provider_list); const struct of_device_id *match; - int rc; if (!root) root = of_find_node_by_path("/"); @@ -461,12 +505,43 @@ int of_clk_init(struct device_node *root, const struct of_device_id *matches) if (!matches) matches = __clk_of_table_start; + /* First prepare the list of the clocks providers */ for_each_matching_node_and_match(root, matches, &match) { - of_clk_init_cb_t clk_init_cb = (of_clk_init_cb_t)match->data; - rc = clk_init_cb(root); - if (rc) - pr_err("%s: failed to init clock for %s: %d\n", - __func__, root->full_name, rc); + struct clock_provider *parent; + + if (!of_device_is_available(root)) + continue; + + parent = xzalloc(sizeof(*parent)); + + parent->clk_init_cb = match->data; + parent->np = root; + list_add_tail(&parent->node, &clk_provider_list); + } + + while (!list_empty(&clk_provider_list)) { + is_init_done = false; + list_for_each_entry_safe(clk_provider, next, + &clk_provider_list, node) { + + if (force || parent_ready(clk_provider->np)) { + + clk_provider->clk_init_cb(clk_provider->np); + + list_del(&clk_provider->node); + free(clk_provider); + is_init_done = true; + } + } + + /* + * We didn't manage to initialize any of the + * remaining providers during the last loop, so now we + * initialize all the remaining ones unconditionally + * in case the clock parent was not mandatory + */ + if (!is_init_done) + force = true; } return 0; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 5c69928e75..12fff4f010 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -77,4 +77,9 @@ config PINCTRL_TEGRA_XUSB source drivers/pinctrl/mvebu/Kconfig +config PINCTRL_VF610 + bool + default y if ARCH_VF610 + help + Pinmux controller found on Vybrid VF610 family of SoCs endif diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index af9b30df0f..9450dbbdf5 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -9,5 +9,6 @@ obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o +obj-$(CONFIG_PINCTRL_VF610) += pinctrl-vf610.o obj-$(CONFIG_ARCH_MVEBU) += mvebu/ diff --git a/drivers/pinctrl/pinctrl-vf610.c b/drivers/pinctrl/pinctrl-vf610.c new file mode 100644 index 0000000000..b479bf20e6 --- /dev/null +++ b/drivers/pinctrl/pinctrl-vf610.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2016 Zodiac Inflight Innovation + * Author: Andrey Smirnov <andrew.smirnov@gmail.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <common.h> +#include <init.h> +#include <io.h> +#include <of.h> +#include <pinctrl.h> +#include <malloc.h> +#include <gpio.h> + +enum { + PINCTRL_VF610_MUX_LINE_SIZE = 20, + PINCTRL_VF610_MUX_SHIFT = 20, + + PINCTRL_VF610_IBE = 1 << 0, + PINCTRL_VF610_OBE = 1 << 1, + PINCTRL_VF610_xBE = 0b11, +}; + +struct pinctrl_vf610 { + void __iomem *base; + struct pinctrl_device pinctrl; +}; + +static int pinctrl_vf610_set_state(struct pinctrl_device *pdev, + struct device_node *np) +{ + const __be32 *list; + int npins, size, i; + + struct pinctrl_vf610 *iomux = + container_of(pdev, struct pinctrl_vf610, pinctrl); + + list = of_get_property(np, "fsl,pins", &size); + if (!list) + return -EINVAL; + + if (!size || size % PINCTRL_VF610_MUX_LINE_SIZE) { + dev_err(pdev->dev, "Invalid fsl,pins property in %s\n", + np->full_name); + return -EINVAL; + } + + npins = size / PINCTRL_VF610_MUX_LINE_SIZE; + + for (i = 0; i < npins; i++) { + u32 mux_reg = be32_to_cpu(*list++); + u32 input_reg = be32_to_cpu(*list++); + u32 mux_val = be32_to_cpu(*list++); + u32 input_val = be32_to_cpu(*list++); + u32 conf_val = be32_to_cpu(*list++); + + writel(mux_val << PINCTRL_VF610_MUX_SHIFT | conf_val, + iomux->base + mux_reg); + + if (input_reg) + writel(input_val, iomux->base + input_reg); + } + + return 0; +} + +static int pinctrl_vf610_set_direction(struct pinctrl_device *pdev, + unsigned int pin, bool input) +{ + u32 pad_cr; + const u32 off = pin * sizeof(u32); + struct pinctrl_vf610 *iomux = + container_of(pdev, struct pinctrl_vf610, pinctrl); + + pad_cr = readl(iomux->base + off); + + if (input) { + pad_cr |= PINCTRL_VF610_IBE; + pad_cr &= ~PINCTRL_VF610_OBE; + } else { + pad_cr &= ~PINCTRL_VF610_IBE; + pad_cr |= PINCTRL_VF610_OBE; + } + + writel(pad_cr, iomux->base + off); + + return 0; +} + +static int pinctrl_vf610_get_direction(struct pinctrl_device *pdev, + unsigned int pin) +{ + const u32 off = pin * sizeof(u32); + struct pinctrl_vf610 *iomux = + container_of(pdev, struct pinctrl_vf610, pinctrl); + + const u32 pad_cr = readl(iomux->base + off); + + switch (pad_cr & PINCTRL_VF610_xBE) { + case PINCTRL_VF610_IBE: + return GPIOF_DIR_IN; + case PINCTRL_VF610_OBE: + return GPIOF_DIR_OUT; + default: + return -EINVAL; + } +} + +static struct pinctrl_ops pinctrl_vf610_ops = { + .set_state = pinctrl_vf610_set_state, + .set_direction = pinctrl_vf610_set_direction, + .get_direction = pinctrl_vf610_get_direction, +}; + +static int pinctrl_vf610_probe(struct device_d *dev) +{ + int ret; + struct resource *io; + struct pinctrl_vf610 *iomux; + + iomux = xzalloc(sizeof(*iomux)); + + io = dev_request_mem_resource(dev, 0); + if (IS_ERR(io)) + return PTR_ERR(io); + + iomux->base = IOMEM(io->start); + iomux->pinctrl.dev = dev; + iomux->pinctrl.ops = &pinctrl_vf610_ops; + iomux->pinctrl.base = 0; + iomux->pinctrl.npins = ARCH_NR_GPIOS; + + ret = pinctrl_register(&iomux->pinctrl); + if (ret) + free(iomux); + + return ret; +} + +static __maybe_unused struct of_device_id pinctrl_vf610_dt_ids[] = { + { .compatible = "fsl,vf610-iomuxc", }, + { /* sentinel */ } +}; + +static struct driver_d pinctrl_vf610_driver = { + .name = "vf610-pinctrl", + .probe = pinctrl_vf610_probe, + .of_compatible = DRV_OF_COMPAT(pinctrl_vf610_dt_ids), +}; + +static int pinctrl_vf610_init(void) +{ + return platform_driver_register(&pinctrl_vf610_driver); +} +postcore_initcall(pinctrl_vf610_init); diff --git a/drivers/pinctrl/pinctrl.c b/drivers/pinctrl/pinctrl.c index e7b08a2bfe..bef4fcdba1 100644 --- a/drivers/pinctrl/pinctrl.c +++ b/drivers/pinctrl/pinctrl.c @@ -24,6 +24,51 @@ static LIST_HEAD(pinctrl_list); +static struct pinctrl_device *pin_to_pinctrl(unsigned int pin) +{ + struct pinctrl_device *pinctrl; + + list_for_each_entry(pinctrl, &pinctrl_list, list) + if (pin > pinctrl->base && + pin < pinctrl->base + pinctrl->npins) + return pinctrl; + return NULL; +} + +static int pinctrl_gpio_direction(unsigned pin, bool input) +{ + struct pinctrl_device *pinctrl = pin_to_pinctrl(pin); + + if (!pinctrl) + return -EINVAL; + + BUG_ON(!pinctrl->ops->set_direction); + + return pinctrl->ops->set_direction(pinctrl, pin, input); +} + +int pinctrl_gpio_direction_input(unsigned int pin) +{ + return pinctrl_gpio_direction(pin, true); +} + +int pinctrl_gpio_direction_output(unsigned int pin) +{ + return pinctrl_gpio_direction(pin, false); +} + +int pinctrl_gpio_get_direction(unsigned pin) +{ + struct pinctrl_device *pinctrl = pin_to_pinctrl(pin); + + if (!pinctrl) + return -EINVAL; + + BUG_ON(!pinctrl->ops->get_direction); + + return pinctrl->ops->get_direction(pinctrl, pin); +} + static struct pinctrl_device *find_pinctrl(struct device_node *node) { struct pinctrl_device *pdev; |