arm64: mvebu: pinctrl: Add pin control driver for A8K family
Add a DM port of Marvell pin control driver.
The A8K SoC family contains several silicone dies interconnected
in a single package. Every die is normally equipped with its own
pin controller unit.
There are 2 pin controllers in A70x0 SoC and 3 in A80x0 SoC.
Signed-off-by: Konstantin Porotchkin <kostap@marvell.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Cc: Simon Glass <sjg@chromium.org>
Cc: Stefan Roese <sr@denx.de>
Cc: Nadav Haklai <nadavh@marvell.com>
Cc: Neta Zur Hershkovits <neta@marvell.com>
Cc: Omri Itach <omrii@marvell.com>
Cc: Igal Liberman <igall@marvell.com>
Cc: Haim Boot <hayim@marvell.com>
Cc: Hanna Hawa <hannah@marvell.com>
Signed-off-by: Stefan Roese <sr@denx.de>
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 12be3cf..efcb4c0 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -181,5 +181,6 @@
source "drivers/pinctrl/nxp/Kconfig"
source "drivers/pinctrl/uniphier/Kconfig"
source "drivers/pinctrl/exynos/Kconfig"
+source "drivers/pinctrl/mvebu/Kconfig"
endmenu
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index f28b5c1..512112a 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -15,3 +15,4 @@
obj-$(CONFIG_PIC32_PINCTRL) += pinctrl_pic32.o
obj-$(CONFIG_PINCTRL_EXYNOS) += exynos/
obj-$(CONFIG_PINCTRL_MESON) += meson/
+obj-$(CONFIG_PINCTRL_MVEBU) += mvebu/
diff --git a/drivers/pinctrl/mvebu/Kconfig b/drivers/pinctrl/mvebu/Kconfig
new file mode 100644
index 0000000..cf9c299
--- /dev/null
+++ b/drivers/pinctrl/mvebu/Kconfig
@@ -0,0 +1,7 @@
+config PINCTRL_MVEBU
+ depends on ARCH_MVEBU
+ bool
+ default y
+ help
+ Support pin multiplexing and pin configuration control on
+ Marvell's Armada-8K SoC.
diff --git a/drivers/pinctrl/mvebu/Makefile b/drivers/pinctrl/mvebu/Makefile
new file mode 100644
index 0000000..f4f7864
--- /dev/null
+++ b/drivers/pinctrl/mvebu/Makefile
@@ -0,0 +1,7 @@
+#
+# Copyright (C) 2016 Marvell International Ltd.
+#
+# SPDX-License-Identifier: GPL-2.0
+# https://spdx.org/licenses
+
+obj-$(CONFIG_PINCTRL_MVEBU) += pinctrl-mvebu.o
diff --git a/drivers/pinctrl/mvebu/pinctrl-mvebu.c b/drivers/pinctrl/mvebu/pinctrl-mvebu.c
new file mode 100644
index 0000000..b077639
--- /dev/null
+++ b/drivers/pinctrl/mvebu/pinctrl-mvebu.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2016 Marvell International Ltd.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ * https://spdx.org/licenses
+ */
+
+#include <common.h>
+#include <config.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <dm/root.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/arch-armada8k/soc-info.h>
+#include "pinctrl-mvebu.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * mvebu_pinctrl_set_state: configure pin functions.
+ * @dev: the pinctrl device to be configured.
+ * @config: the state to be configured.
+ * @return: 0 in success
+ */
+int mvebu_pinctrl_set_state(struct udevice *dev, struct udevice *config)
+{
+ const void *blob = gd->fdt_blob;
+ int node = config->of_offset;
+ struct mvebu_pinctrl_priv *priv;
+ u32 pin_arr[MVEBU_MAX_PINS_PER_BANK];
+ u32 function;
+ int i, pin_count;
+
+ priv = dev_get_priv(dev);
+
+ pin_count = fdtdec_get_int_array_count(blob, node,
+ "marvell,pins",
+ pin_arr,
+ MVEBU_MAX_PINS_PER_BANK);
+ if (pin_count <= 0) {
+ debug("Failed reading pins array for pinconfig %s (%d)\n",
+ config->name, pin_count);
+ return -EINVAL;
+ }
+
+ function = fdtdec_get_int(blob, node, "marvell,function", 0xff);
+
+ for (i = 0; i < pin_count; i++) {
+ int reg_offset;
+ int field_offset;
+ int pin = pin_arr[i];
+
+ if (function > priv->max_func) {
+ debug("Illegal function %d for pinconfig %s\n",
+ function, config->name);
+ return -EINVAL;
+ }
+
+ /* Calculate register address and bit in register */
+ reg_offset = priv->reg_direction * 4 *
+ (pin >> (PIN_REG_SHIFT));
+ field_offset = (BITS_PER_PIN) * (pin & PIN_FIELD_MASK);
+
+ clrsetbits_le32(priv->base_reg + reg_offset,
+ PIN_FUNC_MASK << field_offset,
+ (function & PIN_FUNC_MASK) << field_offset);
+ }
+
+ return 0;
+}
+
+/*
+ * mvebu_pinctrl_set_state_all: configure the entire bank pin functions.
+ * @dev: the pinctrl device to be configured.
+ * @config: the state to be configured.
+ * @return: 0 in success
+ */
+static int mvebu_pinctrl_set_state_all(struct udevice *dev,
+ struct udevice *config)
+{
+ const void *blob = gd->fdt_blob;
+ int node = config->of_offset;
+ struct mvebu_pinctrl_priv *priv;
+ u32 func_arr[MVEBU_MAX_PINS_PER_BANK];
+ int pin, err;
+
+ priv = dev_get_priv(dev);
+
+ err = fdtdec_get_int_array(blob, node, "pin-func",
+ func_arr, priv->pin_cnt);
+ if (err) {
+ debug("Failed reading pin functions for bank %s\n",
+ priv->bank_name);
+ return -EINVAL;
+ }
+
+ for (pin = 0; pin < priv->pin_cnt; pin++) {
+ int reg_offset;
+ int field_offset;
+ u32 func = func_arr[pin];
+
+ /* Bypass pins with function 0xFF */
+ if (func == 0xff) {
+ debug("Warning: pin %d value is not modified ", pin);
+ debug("(kept as default)\n");
+ continue;
+ } else if (func > priv->max_func) {
+ debug("Illegal function %d for pin %d\n", func, pin);
+ return -EINVAL;
+ }
+
+ /* Calculate register address and bit in register */
+ reg_offset = priv->reg_direction * 4 *
+ (pin >> (PIN_REG_SHIFT));
+ field_offset = (BITS_PER_PIN) * (pin & PIN_FIELD_MASK);
+
+ clrsetbits_le32(priv->base_reg + reg_offset,
+ PIN_FUNC_MASK << field_offset,
+ (func & PIN_FUNC_MASK) << field_offset);
+ }
+
+ return 0;
+}
+
+int mvebu_pinctl_probe(struct udevice *dev)
+{
+ const void *blob = gd->fdt_blob;
+ int node = dev->of_offset;
+ struct mvebu_pinctrl_priv *priv;
+
+ priv = dev_get_priv(dev);
+ if (!priv) {
+ debug("%s: Failed to get private\n", __func__);
+ return -EINVAL;
+ }
+
+ priv->base_reg = dev_get_addr_ptr(dev);
+ if (priv->base_reg == (void *)FDT_ADDR_T_NONE) {
+ debug("%s: Failed to get base address\n", __func__);
+ return -EINVAL;
+ }
+
+ priv->pin_cnt = fdtdec_get_int(blob, node, "pin-count",
+ MVEBU_MAX_PINS_PER_BANK);
+ priv->max_func = fdtdec_get_int(blob, node, "max-func",
+ MVEBU_MAX_FUNC);
+ priv->bank_name = fdt_getprop(blob, node, "bank-name", NULL);
+
+ priv->reg_direction = 1;
+ if (fdtdec_get_bool(blob, node, "reverse-reg"))
+ priv->reg_direction = -1;
+
+ return mvebu_pinctrl_set_state_all(dev, dev);
+}
+
+static struct pinctrl_ops mvebu_pinctrl_ops = {
+ .set_state = mvebu_pinctrl_set_state
+};
+
+static const struct udevice_id mvebu_pinctrl_ids[] = {
+ { .compatible = "marvell,mvebu-pinctrl" },
+ { .compatible = "marvell,armada-ap806-pinctrl" },
+ { .compatible = "marvell,a70x0-pinctrl" },
+ { .compatible = "marvell,a80x0-cp0-pinctrl" },
+ { .compatible = "marvell,a80x0-cp1-pinctrl" },
+ { }
+};
+
+U_BOOT_DRIVER(pinctrl_mvebu) = {
+ .name = "mvebu_pinctrl",
+ .id = UCLASS_PINCTRL,
+ .of_match = mvebu_pinctrl_ids,
+ .priv_auto_alloc_size = sizeof(struct mvebu_pinctrl_priv),
+ .ops = &mvebu_pinctrl_ops,
+ .probe = mvebu_pinctl_probe
+};
diff --git a/drivers/pinctrl/mvebu/pinctrl-mvebu.h b/drivers/pinctrl/mvebu/pinctrl-mvebu.h
new file mode 100644
index 0000000..1a1d3ef
--- /dev/null
+++ b/drivers/pinctrl/mvebu/pinctrl-mvebu.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 Marvell International Ltd.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ * https://spdx.org/licenses
+ */
+
+ #ifndef __PINCTRL_MVEBU_H_
+ #define __PINCTRL_MVEBU_H_
+
+ #define MVEBU_MAX_PINCTL_BANKS 4
+ #define MVEBU_MAX_PINS_PER_BANK 100
+ #define MVEBU_MAX_FUNC 0xF
+
+/*
+ * struct mvebu_pin_bank_data: mvebu-pinctrl bank data
+ * @base_reg: controller base address for this bank
+ * @pin_cnt: number of pins included in this bank
+ * @max_func: maximum configurable function value for pins in this bank
+ * @reg_direction:
+ * @bank_name: the pin's bank name
+ */
+struct mvebu_pinctrl_priv {
+ void *base_reg;
+ uint pin_cnt;
+ uint max_func;
+ int reg_direction;
+ const char *bank_name;
+};
+
+#endif /* __PINCTRL_MVEBU_H_ */