summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2016-11-14 12:37:20 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2016-11-14 12:37:20 +0100
commit8eb1fb3519b6c1ae6ddab114a7cdc86bb24da296 (patch)
treea85ef526dca96e6c12df5cc33c935b854d77fb93 /drivers
parentf55e379fca456444ade761358e8cce8657ea9672 (diff)
parent764f84809fedd39fca75e2c8731ab181a421fd5b (diff)
downloadbarebox-8eb1fb3519b6c1ae6ddab114a7cdc86bb24da296.tar.gz
barebox-8eb1fb3519b6c1ae6ddab114a7cdc86bb24da296.tar.xz
Merge branch 'for-next/vybrid'
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/clk.c89
-rw-r--r--drivers/pinctrl/Kconfig5
-rw-r--r--drivers/pinctrl/Makefile1
-rw-r--r--drivers/pinctrl/pinctrl-vf610.c167
-rw-r--r--drivers/pinctrl/pinctrl.c45
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;