diff options
author | Beniamino Galvani <b.galvani@gmail.com> | 2016-08-16 12:49:49 +0300 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2016-09-06 20:18:19 +0300 |
commit | 677b53580d5d86ca65737497829d581544ae71bf (patch) | |
tree | ca7ada0e79534beb892ebd037efacf881394bb50 /drivers/pinctrl/meson/pinctrl-meson.c | |
parent | dd83840e5efc297b184196f2603f537a167f85e4 (diff) | |
download | u-boot-677b53580d5d86ca65737497829d581544ae71bf.tar.xz |
pinctrl: add driver for meson-gxbb pin controller
Add a pin controller driver for Meson GXBB adapted from Linux kernel.
Signed-off-by: Beniamino Galvani <b.galvani@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'drivers/pinctrl/meson/pinctrl-meson.c')
-rw-r--r-- | drivers/pinctrl/meson/pinctrl-meson.c | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c new file mode 100644 index 0000000000..d21a3dd755 --- /dev/null +++ b/drivers/pinctrl/meson/pinctrl-meson.c @@ -0,0 +1,179 @@ +/* + * (C) Copyright 2016 - Beniamino Galvani <b.galvani@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm/device.h> +#include <dm/pinctrl.h> +#include <fdt_support.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/sizes.h> + +#include "pinctrl-meson.h" + +DECLARE_GLOBAL_DATA_PTR; + +static const char *meson_pinctrl_dummy_name = "_dummy"; + +static int meson_pinctrl_get_groups_count(struct udevice *dev) +{ + struct meson_pinctrl *priv = dev_get_priv(dev); + + return priv->data->num_groups; +} + +static const char *meson_pinctrl_get_group_name(struct udevice *dev, + unsigned selector) +{ + struct meson_pinctrl *priv = dev_get_priv(dev); + + if (!priv->data->groups[selector].name) + return meson_pinctrl_dummy_name; + + return priv->data->groups[selector].name; +} + +static int meson_pinmux_get_functions_count(struct udevice *dev) +{ + struct meson_pinctrl *priv = dev_get_priv(dev); + + return priv->data->num_funcs; +} + +static const char *meson_pinmux_get_function_name(struct udevice *dev, + unsigned selector) +{ + struct meson_pinctrl *priv = dev_get_priv(dev); + + return priv->data->funcs[selector].name; +} + +static void meson_pinmux_disable_other_groups(struct meson_pinctrl *priv, + unsigned int pin, int sel_group) +{ + struct meson_pmx_group *group; + void __iomem *addr; + int i, j; + + for (i = 0; i < priv->data->num_groups; i++) { + group = &priv->data->groups[i]; + if (group->is_gpio || i == sel_group) + continue; + + for (j = 0; j < group->num_pins; j++) { + if (group->pins[j] == pin) { + /* We have found a group using the pin */ + debug("pinmux: disabling %s\n", group->name); + addr = priv->reg_mux + group->reg * 4; + writel(readl(addr) & ~BIT(group->bit), addr); + } + } + } +} + +static int meson_pinmux_group_set(struct udevice *dev, + unsigned group_selector, + unsigned func_selector) +{ + struct meson_pinctrl *priv = dev_get_priv(dev); + const struct meson_pmx_group *group; + const struct meson_pmx_func *func; + void __iomem *addr; + int i; + + group = &priv->data->groups[group_selector]; + func = &priv->data->funcs[func_selector]; + + debug("pinmux: set group %s func %s\n", group->name, func->name); + + /* + * Disable groups using the same pins. + * The selected group is not disabled to avoid glitches. + */ + for (i = 0; i < group->num_pins; i++) { + meson_pinmux_disable_other_groups(priv, + group->pins[i], + group_selector); + } + + /* Function 0 (GPIO) doesn't need any additional setting */ + if (func_selector) { + addr = priv->reg_mux + group->reg * 4; + writel(readl(addr) | BIT(group->bit), addr); + } + + return 0; +} + +const struct pinctrl_ops meson_pinctrl_ops = { + .get_groups_count = meson_pinctrl_get_groups_count, + .get_group_name = meson_pinctrl_get_group_name, + .get_functions_count = meson_pinmux_get_functions_count, + .get_function_name = meson_pinmux_get_function_name, + .pinmux_group_set = meson_pinmux_group_set, + .set_state = pinctrl_generic_set_state, +}; + +static fdt_addr_t parse_address(int offset, const char *name, int na, int ns) +{ + int index, len = 0; + const fdt32_t *reg; + + index = fdt_find_string(gd->fdt_blob, offset, "reg-names", name); + if (index < 0) + return FDT_ADDR_T_NONE; + + reg = fdt_getprop(gd->fdt_blob, offset, "reg", &len); + if (!reg || (len <= (index * sizeof(fdt32_t) * (na + ns)))) + return FDT_ADDR_T_NONE; + + reg += index * (na + ns); + + return fdt_translate_address((void *)gd->fdt_blob, offset, reg); +} + +int meson_pinctrl_probe(struct udevice *dev) +{ + struct meson_pinctrl *priv = dev_get_priv(dev); + fdt_addr_t addr; + int node, gpio = -1, len; + int na, ns; + + na = fdt_address_cells(gd->fdt_blob, dev->parent->of_offset); + if (na < 1) { + debug("bad #address-cells\n"); + return -EINVAL; + } + + ns = fdt_size_cells(gd->fdt_blob, dev->parent->of_offset); + if (ns < 1) { + debug("bad #size-cells\n"); + return -EINVAL; + } + + fdt_for_each_subnode(gd->fdt_blob, node, dev->of_offset) { + if (fdt_getprop(gd->fdt_blob, node, "gpio-controller", &len)) { + gpio = node; + break; + } + } + + if (!gpio) { + debug("gpio node not found\n"); + return -EINVAL; + } + + addr = parse_address(gpio, "mux", na, ns); + if (addr == FDT_ADDR_T_NONE) { + debug("mux not found\n"); + return -EINVAL; + } + + priv->reg_mux = (void __iomem *)addr; + priv->data = (struct meson_pinctrl_data *)dev_get_driver_data(dev); + + return 0; +} |