// SPDX-License-Identifier: GPL-2.0-or-later
// (C) 2022 Pali Rohár <pali@kernel.org>

#include <config.h>
#include <dm.h>
#include <dm/devres.h>
#include <dm/lists.h>
#include <dm/pinctrl.h>
#include <dm/root.h>
#include <errno.h>
#include <asm/io.h>

struct mvebu_mpp_ctrl_setting {
	const char *name;
	const char *subname;
	u8 val;
	u8 variant;
};

struct mvebu_mpp_mode {
	const char *name;
	size_t nsettings;
	struct mvebu_mpp_ctrl_setting *settings;
};

#define MPP_MODE(_name, ...)					\
	{							\
		.name = _name,					\
		.nsettings = ARRAY_SIZE((			\
			(struct mvebu_mpp_ctrl_setting[])	\
			 { __VA_ARGS__ })),			\
		.settings = (struct mvebu_mpp_ctrl_setting[]){	\
			__VA_ARGS__ },				\
	}

#define MPP_VAR_FUNCTION(_val, _name, _subname, _mask)		\
	{							\
		.val = _val,					\
		.name = _name,					\
		.subname = _subname,				\
		.variant = _mask,				\
	}

#define MVEBU_MPPS_PER_REG	8
#define MVEBU_MPP_BITS		4
#define MVEBU_MPP_MASK		0xf

enum {
	V_88F6810 = BIT(0),
	V_88F6820 = BIT(1),
	V_88F6828 = BIT(2),
	V_88F6810_PLUS = (V_88F6810 | V_88F6820 | V_88F6828),
	V_88F6820_PLUS = (V_88F6820 | V_88F6828),
};

static struct mvebu_mpp_mode armada_38x_mpp_modes[] = {
	MPP_MODE("mpp0",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ua0",   "rxd",        V_88F6810_PLUS)),
	MPP_MODE("mpp1",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ua0",   "txd",        V_88F6810_PLUS)),
	MPP_MODE("mpp2",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "i2c0",  "sck",        V_88F6810_PLUS)),
	MPP_MODE("mpp3",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "i2c0",  "sda",        V_88F6810_PLUS)),
	MPP_MODE("mpp4",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ge",    "mdc",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ua1",   "txd",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "ua0",   "rts",        V_88F6810_PLUS)),
	MPP_MODE("mpp5",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ge",    "mdio",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ua1",   "rxd",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "ua0",   "cts",        V_88F6810_PLUS)),
	MPP_MODE("mpp6",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ge0",   "txclkout",   V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ge0",   "crs",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "cs3",        V_88F6810_PLUS)),
	MPP_MODE("mpp7",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ge0",   "txd0",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ad9",        V_88F6810_PLUS)),
	MPP_MODE("mpp8",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ge0",   "txd1",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ad10",       V_88F6810_PLUS)),
	MPP_MODE("mpp9",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ge0",   "txd2",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ad11",       V_88F6810_PLUS)),
	MPP_MODE("mpp10",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ge0",   "txd3",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ad12",       V_88F6810_PLUS)),
	MPP_MODE("mpp11",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ge0",   "txctl",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ad13",       V_88F6810_PLUS)),
	MPP_MODE("mpp12",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ge0",   "rxd0",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "pcie0", "rstout",     V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "spi0",  "cs1",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ad14",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "pcie3", "clkreq",     V_88F6810_PLUS)),
	MPP_MODE("mpp13",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ge0",   "rxd1",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "pcie0", "clkreq",     V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "pcie1", "clkreq",     V_88F6820_PLUS),
		 MPP_VAR_FUNCTION(4, "spi0",  "cs2",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ad15",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "pcie2", "clkreq",     V_88F6810_PLUS)),
	MPP_MODE("mpp14",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ge0",   "rxd2",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ptp",   "clk",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "dram",  "vttctrl",    V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "spi0",  "cs3",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "we1",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "pcie3", "clkreq",     V_88F6810_PLUS)),
	MPP_MODE("mpp15",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ge0",   "rxd3",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ge",    "mdc slave",  V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "pcie0", "rstout",     V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "spi0",  "mosi",       V_88F6810_PLUS)),
	MPP_MODE("mpp16",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ge0",   "rxctl",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ge",    "mdio slave", V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "dram",  "deccerr",    V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "spi0",  "miso",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "pcie0", "clkreq",     V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "pcie1", "clkreq",     V_88F6820_PLUS)),
	MPP_MODE("mpp17",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ge0",   "rxclk",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ptp",   "clk",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "ua1",   "rxd",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "spi0",  "sck",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "sata1", "prsnt",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "sata0", "prsnt",      V_88F6810_PLUS)),
	MPP_MODE("mpp18",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ge0",   "rxerr",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ptp",   "trig",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "ua1",   "txd",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "spi0",  "cs0",        V_88F6810_PLUS)),
	MPP_MODE("mpp19",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ge0",   "col",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ptp",   "evreq",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "ge0",   "txerr",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "sata1", "prsnt",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "ua0",   "cts",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "ua1",   "rxd",        V_88F6810_PLUS)),
	MPP_MODE("mpp20",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ge0",   "txclk",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ptp",   "clk",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "sata0", "prsnt",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "ua0",   "rts",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "ua1",   "txd",        V_88F6810_PLUS)),
	MPP_MODE("mpp21",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "spi0",  "cs1",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ge1",   "rxd0",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "sata0", "prsnt",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "sd0",   "cmd",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "bootcs",     V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "sata1", "prsnt",      V_88F6810_PLUS)),
	MPP_MODE("mpp22",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "spi0",  "mosi",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ad0",        V_88F6810_PLUS)),
	MPP_MODE("mpp23",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "spi0",  "sck",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ad2",        V_88F6810_PLUS)),
	MPP_MODE("mpp24",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "spi0",  "miso",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ua0",   "cts",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "ua1",   "rxd",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "sd0",   "d4",         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ready",      V_88F6810_PLUS)),
	MPP_MODE("mpp25",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "spi0",  "cs0",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ua0",   "rts",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "ua1",   "txd",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "sd0",   "d5",         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "cs0",        V_88F6810_PLUS)),
	MPP_MODE("mpp26",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "spi0",  "cs2",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "i2c1",  "sck",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "sd0",   "d6",         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "cs1",        V_88F6810_PLUS)),
	MPP_MODE("mpp27",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "spi0",  "cs3",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ge1",   "txclkout",   V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "i2c1",  "sda",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "sd0",   "d7",         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "cs2",        V_88F6810_PLUS)),
	MPP_MODE("mpp28",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ge1",   "txd0",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "sd0",   "clk",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ad5",        V_88F6810_PLUS)),
	MPP_MODE("mpp29",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ge1",   "txd1",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ale0",       V_88F6810_PLUS)),
	MPP_MODE("mpp30",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ge1",   "txd2",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "oe",         V_88F6810_PLUS)),
	MPP_MODE("mpp31",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ge1",   "txd3",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ale1",       V_88F6810_PLUS)),
	MPP_MODE("mpp32",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ge1",   "txctl",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "we0",        V_88F6810_PLUS)),
	MPP_MODE("mpp33",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "dram",  "deccerr",    V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ad3",        V_88F6810_PLUS)),
	MPP_MODE("mpp34",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ad1",        V_88F6810_PLUS)),
	MPP_MODE("mpp35",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ref",   "clk_out1",   V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "a1",         V_88F6810_PLUS)),
	MPP_MODE("mpp36",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ptp",   "trig",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "a0",         V_88F6810_PLUS)),
	MPP_MODE("mpp37",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ptp",   "clk",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ge1",   "rxclk",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "sd0",   "d3",         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ad8",        V_88F6810_PLUS)),
	MPP_MODE("mpp38",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ptp",   "evreq",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ge1",   "rxd1",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "ref",   "clk_out0",   V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "sd0",   "d0",         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ad4",        V_88F6810_PLUS)),
	MPP_MODE("mpp39",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "i2c1",  "sck",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ge1",   "rxd2",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "ua0",   "cts",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "sd0",   "d1",         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "a2",         V_88F6810_PLUS)),
	MPP_MODE("mpp40",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "i2c1",  "sda",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ge1",   "rxd3",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "ua0",   "rts",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "sd0",   "d2",         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ad6",        V_88F6810_PLUS)),
	MPP_MODE("mpp41",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ua1",   "rxd",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ge1",   "rxctl",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "ua0",   "cts",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "spi1",  "cs3",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "burst/last", V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "nand",  "rb0",        V_88F6810_PLUS)),
	MPP_MODE("mpp42",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ua1",   "txd",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "ua0",   "rts",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "ad7",        V_88F6810_PLUS)),
	MPP_MODE("mpp43",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "pcie0", "clkreq",     V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "dram",  "vttctrl",    V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "dram",  "deccerr",    V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "spi1",  "cs2",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dev",   "clkout",     V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "nand",  "rb1",        V_88F6810_PLUS)),
	MPP_MODE("mpp44",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "sata0", "prsnt",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "sata1", "prsnt",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "sata2", "prsnt",      V_88F6828),
		 MPP_VAR_FUNCTION(4, "sata3", "prsnt",      V_88F6828)),
	MPP_MODE("mpp45",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ref",   "clk_out0",   V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "pcie0", "rstout",     V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "ua1",   "rxd",        V_88F6810_PLUS)),
	MPP_MODE("mpp46",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ref",   "clk_out1",   V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "pcie0", "rstout",     V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "ua1",   "txd",        V_88F6810_PLUS)),
	MPP_MODE("mpp47",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "sata0", "prsnt",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "sata1", "prsnt",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "sata2", "prsnt",      V_88F6828),
		 MPP_VAR_FUNCTION(5, "sata3", "prsnt",      V_88F6828)),
	MPP_MODE("mpp48",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "sata0", "prsnt",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "dram",  "vttctrl",    V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "tdm",   "pclk",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "audio", "mclk",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "sd0",   "d4",         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "pcie0", "clkreq",     V_88F6810_PLUS)),
	MPP_MODE("mpp49",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "sata2", "prsnt",      V_88F6828),
		 MPP_VAR_FUNCTION(2, "sata3", "prsnt",      V_88F6828),
		 MPP_VAR_FUNCTION(3, "tdm",   "fsync",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "audio", "lrclk",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "sd0",   "d5",         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "pcie1", "clkreq",     V_88F6820_PLUS)),
	MPP_MODE("mpp50",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "pcie0", "rstout",     V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "tdm",   "drx",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "audio", "extclk",     V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "sd0",   "cmd",        V_88F6810_PLUS)),
	MPP_MODE("mpp51",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "tdm",   "dtx",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "audio", "sdo",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "dram",  "deccerr",    V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "ptp",   "trig",       V_88F6810_PLUS)),
	MPP_MODE("mpp52",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "pcie0", "rstout",     V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "tdm",   "int",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "audio", "sdi",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "sd0",   "d6",         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "ptp",   "clk",        V_88F6810_PLUS)),
	MPP_MODE("mpp53",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "sata1", "prsnt",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "sata0", "prsnt",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "tdm",   "rst",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "audio", "bclk",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "sd0",   "d7",         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "ptp",   "evreq",      V_88F6810_PLUS)),
	MPP_MODE("mpp54",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "sata0", "prsnt",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "sata1", "prsnt",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "pcie0", "rstout",     V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "ge0",   "txerr",      V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "sd0",   "d3",         V_88F6810_PLUS)),
	MPP_MODE("mpp55",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ua1",   "cts",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ge",    "mdio",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "pcie1", "clkreq",     V_88F6820_PLUS),
		 MPP_VAR_FUNCTION(4, "spi1",  "cs1",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "sd0",   "d0",         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "ua1",   "rxd",        V_88F6810_PLUS)),
	MPP_MODE("mpp56",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "ua1",   "rts",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "ge",    "mdc",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "dram",  "deccerr",    V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "spi1",  "mosi",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "ua1",   "txd",        V_88F6810_PLUS)),
	MPP_MODE("mpp57",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "spi1",  "sck",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "sd0",   "clk",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "ua1",   "txd",        V_88F6810_PLUS)),
	MPP_MODE("mpp58",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "pcie1", "clkreq",     V_88F6820_PLUS),
		 MPP_VAR_FUNCTION(2, "i2c1",  "sck",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(3, "pcie2", "clkreq",     V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "spi1",  "miso",       V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "sd0",   "d1",         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(6, "ua1",   "rxd",        V_88F6810_PLUS)),
	MPP_MODE("mpp59",
		 MPP_VAR_FUNCTION(0, "gpio",  NULL,         V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(1, "pcie0", "rstout",     V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(2, "i2c1",  "sda",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(4, "spi1",  "cs0",        V_88F6810_PLUS),
		 MPP_VAR_FUNCTION(5, "sd0",   "d2",         V_88F6810_PLUS)),
};

static const char * const armada_38x_mpp_function_names[] = {
	"gpio", /* make gpio always as function 0 */

	"audio",
	"dev",
	"dram",
	"ge",
	"ge0",
	"ge1",
	"i2c0",
	"i2c1",
	"nand",
	"pcie0",
	"pcie1",
	"pcie2",
	"pcie3",
	"ptp",
	"ref",
	"sata0",
	"sata1",
	"sata2",
	"sata3",
	"sd0",
	"spi0",
	"spi1",
	"tdm",
	"ua0",
	"ua1",
};

struct armada_38x_pinctrl {
	void __iomem *base;
	u8 variant;
};

static int armada_38x_pinctrl_get_pins_count(struct udevice *dev)
{
	return ARRAY_SIZE(armada_38x_mpp_modes);
}

static const char *armada_38x_pinctrl_get_pin_name(struct udevice *dev, unsigned int selector)
{
	return armada_38x_mpp_modes[selector].name;
}

static int armada_38x_pinctrl_get_functions_count(struct udevice *dev)
{
	return ARRAY_SIZE(armada_38x_mpp_function_names);
}

static const char *armada_38x_pinctrl_get_function_name(struct udevice *dev, unsigned int selector)
{
	return armada_38x_mpp_function_names[selector];
}

static int armada_38x_pinctrl_get_pin_muxing(struct udevice *dev, unsigned int selector,
					     char *buf, int size)
{
	struct armada_38x_pinctrl *info = dev_get_priv(dev);
	unsigned int off = (selector / MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS;
	unsigned int shift = (selector % MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS;
	const char *func_name = NULL;
	const char *sub_name = NULL;
	unsigned long config;
	int i;

	config = (readl(info->base + off) >> shift) & MVEBU_MPP_MASK;

	for (i = 0; i < armada_38x_mpp_modes[selector].nsettings; i++) {
		if (armada_38x_mpp_modes[selector].settings[i].val == config)
			break;
	}

	if (i < armada_38x_mpp_modes[selector].nsettings) {
		func_name = armada_38x_mpp_modes[selector].settings[i].name;
		sub_name = armada_38x_mpp_modes[selector].settings[i].subname;
	}

	snprintf(buf, size, "%s%s%s",
		 func_name ? func_name : "unknown",
		 sub_name ? "_" : "",
		 sub_name ? sub_name : "");
	return 0;
}

static int armada_38x_pinctrl_pinmux_set(struct udevice *dev, unsigned int pin_selector,
					 unsigned int func_selector)
{
	struct armada_38x_pinctrl *info = dev_get_priv(dev);
	unsigned int off = (pin_selector / MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS;
	unsigned int shift = (pin_selector % MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS;
	const char *func_name = armada_38x_mpp_function_names[func_selector];
	unsigned long config, reg;
	int i;

	for (i = 0; i < armada_38x_mpp_modes[pin_selector].nsettings; i++) {
		if (strcmp(armada_38x_mpp_modes[pin_selector].settings[i].name, func_name) == 0)
			break;
	}

	if (i >= armada_38x_mpp_modes[pin_selector].nsettings)
		return -EINVAL;

	if (!(info->variant & armada_38x_mpp_modes[pin_selector].settings[i].variant))
		return -EINVAL;

	reg = readl(info->base + off) & ~(MVEBU_MPP_MASK << shift);
	config = armada_38x_mpp_modes[pin_selector].settings[i].val;
	writel(reg | (config << shift), info->base + off);

	return 0;
}

static int armada_38x_pinctrl_gpio_request_enable(struct udevice *dev, unsigned int selector)
{
	char buf[20];

	armada_38x_pinctrl_get_pin_muxing(dev, selector, buf, sizeof(buf));
	if (strcmp(buf, "gpio") != 0)
		printf("Warning: Changing mpp%u function from %s to gpio...\n", selector, buf);

	return armada_38x_pinctrl_pinmux_set(dev, selector, 0); /* gpio is always function 0 */
}

static int armada_38x_pinctrl_gpio_disable_free(struct udevice *dev, unsigned int selector)
{
	/* nothing to do */
	return 0;
}

static int armada_38x_pinctrl_set_state(struct udevice *dev, struct udevice *config)
{
	return pinctrl_generic_set_state_prefix(dev, config, "marvell,");
}

static int armada_38x_pinctrl_probe(struct udevice *dev)
{
	struct armada_38x_pinctrl *info = dev_get_priv(dev);

	info->variant = (u8)dev_get_driver_data(dev);
	info->base = dev_read_addr_ptr(dev);

	if (!info->base)
		return -EINVAL;

	return 0;
}

struct pinctrl_ops armada_37xx_pinctrl_ops = {
	.get_pins_count = armada_38x_pinctrl_get_pins_count,
	.get_pin_name = armada_38x_pinctrl_get_pin_name,
	.get_functions_count = armada_38x_pinctrl_get_functions_count,
	.get_function_name = armada_38x_pinctrl_get_function_name,
	.get_pin_muxing = armada_38x_pinctrl_get_pin_muxing,
	.pinmux_set = armada_38x_pinctrl_pinmux_set,
	.gpio_request_enable = armada_38x_pinctrl_gpio_request_enable,
	.gpio_disable_free = armada_38x_pinctrl_gpio_disable_free,
	.set_state = armada_38x_pinctrl_set_state,
};

static const struct udevice_id armada_38x_pinctrl_of_match[] = {
	{
		.compatible = "marvell,mv88f6810-pinctrl",
		.data       = V_88F6810,
	},
	{
		.compatible = "marvell,mv88f6820-pinctrl",
		.data       = V_88F6820,
	},
	{
		.compatible = "marvell,mv88f6828-pinctrl",
		.data       = V_88F6828,
	},
	{ },
};

U_BOOT_DRIVER(armada_38x_pinctrl) = {
	.name = "armada-38x-pinctrl",
	.id = UCLASS_PINCTRL,
	.of_match = of_match_ptr(armada_38x_pinctrl_of_match),
	.probe = armada_38x_pinctrl_probe,
	.priv_auto = sizeof(struct armada_38x_pinctrl),
	.ops = &armada_37xx_pinctrl_ops,
};
