// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
 */

#include <clk.h>
#include <dm.h>
#include <dm/pinctrl.h>
#include <dt-bindings/pinctrl/k210-pinctrl.h>
#include <mapmem.h>
#include <regmap.h>
#include <syscon.h>
#include <asm/io.h>
#include <linux/err.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>

/*
 * The K210 only implements 8 drive levels, even though there is register space
 * for 16
 */
#define K210_PC_DRIVE_MASK GENMASK(11, 8)
#define K210_PC_DRIVE_SHIFT 8
#define K210_PC_DRIVE_0 (0 << K210_PC_DRIVE_SHIFT)
#define K210_PC_DRIVE_1 (1 << K210_PC_DRIVE_SHIFT)
#define K210_PC_DRIVE_2 (2 << K210_PC_DRIVE_SHIFT)
#define K210_PC_DRIVE_3 (3 << K210_PC_DRIVE_SHIFT)
#define K210_PC_DRIVE_4 (4 << K210_PC_DRIVE_SHIFT)
#define K210_PC_DRIVE_5 (5 << K210_PC_DRIVE_SHIFT)
#define K210_PC_DRIVE_6 (6 << K210_PC_DRIVE_SHIFT)
#define K210_PC_DRIVE_7 (7 << K210_PC_DRIVE_SHIFT)
#define K210_PC_DRIVE_MAX 7

#define K210_PC_MODE_MASK GENMASK(23, 12)
/*
 * output enabled == PC_OE & (PC_OE_INV ^ FUNCTION_OE) where FUNCTION_OE is a
 * physical signal from the function
 */
#define K210_PC_OE       BIT(12) /* Output Enable */
#define K210_PC_OE_INV   BIT(13) /* INVert function-controlled Output Enable */
#define K210_PC_DO_OE    BIT(14) /* set Data Out to the Output Enable signal */
#define K210_PC_DO_INV   BIT(15) /* INVert final Data Output */
#define K210_PC_PU       BIT(16) /* Pull Up */
#define K210_PC_PD       BIT(17) /* Pull Down */
/* Strong pull up not implemented on K210 */
#define K210_PC_SL       BIT(19) /* reduce SLew rate to prevent overshoot */
/* Same semantics as OE above */
#define K210_PC_IE       BIT(20) /* Input Enable */
#define K210_PC_IE_INV   BIT(21) /* INVert function-controlled Input Enable */
#define K210_PC_DI_INV   BIT(22) /* INVert Data Input */
#define K210_PC_ST       BIT(23) /* Schmitt Trigger */
#define K210_PC_DI       BIT(31) /* raw Data Input */
#define K210_PC_BIAS_MASK (K210_PC_PU & K210_PC_PD)

#define K210_PC_MODE_IN   (K210_PC_IE | K210_PC_ST)
#define K210_PC_MODE_OUT  (K210_PC_DRIVE_7 | K210_PC_OE)
#define K210_PC_MODE_I2C  (K210_PC_MODE_IN | K210_PC_SL | K210_PC_OE | \
			   K210_PC_PU)
#define K210_PC_MODE_SCCB (K210_PC_MODE_I2C | K210_PC_OE_INV | K210_PC_IE_INV)
#define K210_PC_MODE_SPI  (K210_PC_MODE_IN | K210_PC_IE_INV | \
			   K210_PC_MODE_OUT | K210_PC_OE_INV)
#define K210_PC_MODE_GPIO (K210_PC_MODE_IN | K210_PC_MODE_OUT)

#define K210_PG_FUNC GENMASK(7, 0)
#define K210_PG_DO BIT(8)
#define K210_PG_PIN GENMASK(22, 16)

#define PIN_CONFIG_OUTPUT_INVERT (PIN_CONFIG_END + 1)
#define PIN_CONFIG_INPUT_INVERT (PIN_CONFIG_END + 2)

struct k210_fpioa {
	u32 pins[48];
	u32 tie_en[8];
	u32 tie_val[8];
};

struct k210_pc_priv {
	struct clk clk;
	struct k210_fpioa __iomem *fpioa; /* FPIOA register */
	struct regmap *sysctl; /* Sysctl regmap */
	u32 power_offset; /* Power bank register offset */
};

#ifdef CONFIG_CMD_PINMUX
static const char k210_pc_pin_names[][6] = {
#define PIN(i) \
	[i] = "IO_" #i
	PIN(0),
	PIN(1),
	PIN(2),
	PIN(3),
	PIN(4),
	PIN(5),
	PIN(6),
	PIN(7),
	PIN(8),
	PIN(9),
	PIN(10),
	PIN(11),
	PIN(12),
	PIN(13),
	PIN(14),
	PIN(15),
	PIN(16),
	PIN(17),
	PIN(18),
	PIN(19),
	PIN(20),
	PIN(21),
	PIN(22),
	PIN(23),
	PIN(24),
	PIN(25),
	PIN(26),
	PIN(27),
	PIN(28),
	PIN(29),
	PIN(30),
	PIN(31),
	PIN(32),
	PIN(33),
	PIN(34),
	PIN(35),
	PIN(36),
	PIN(37),
	PIN(38),
	PIN(39),
	PIN(40),
	PIN(41),
	PIN(42),
	PIN(43),
	PIN(44),
	PIN(45),
	PIN(46),
	PIN(47),
#undef PIN
};

static int k210_pc_get_pins_count(struct udevice *dev)
{
	return ARRAY_SIZE(k210_pc_pin_names);
};

static const char *k210_pc_get_pin_name(struct udevice *dev, unsigned selector)
{
	return k210_pc_pin_names[selector];
}
#endif /* CONFIG_CMD_PINMUX */

/* These are just power domains */
static const char k210_pc_group_names[][3] = {
	[0] = "A0",
	[1] = "A1",
	[2] = "A2",
	[3] = "B3",
	[4] = "B4",
	[5] = "B5",
	[6] = "C6",
	[7] = "C7",
};

static int k210_pc_get_groups_count(struct udevice *dev)
{
	return ARRAY_SIZE(k210_pc_group_names);
}

static const char *k210_pc_get_group_name(struct udevice *dev,
					  unsigned selector)
{
	return k210_pc_group_names[selector];
}

enum k210_pc_mode_id {
	K210_PC_DEFAULT_DISABLED,
	K210_PC_DEFAULT_IN,
	K210_PC_DEFAULT_IN_TIE,
	K210_PC_DEFAULT_OUT,
	K210_PC_DEFAULT_I2C,
	K210_PC_DEFAULT_SCCB,
	K210_PC_DEFAULT_SPI,
	K210_PC_DEFAULT_GPIO,
	K210_PC_DEFAULT_INT13,
};

static const u32 k210_pc_mode_id_to_mode[] = {
#define DEFAULT(mode) \
	[K210_PC_DEFAULT_##mode] = K210_PC_MODE_##mode
	[K210_PC_DEFAULT_DISABLED] = 0,
	DEFAULT(IN),
	[K210_PC_DEFAULT_IN_TIE] = K210_PC_MODE_IN,
	DEFAULT(OUT),
	DEFAULT(I2C),
	DEFAULT(SCCB),
	DEFAULT(SPI),
	DEFAULT(GPIO),
	[K210_PC_DEFAULT_INT13] = K210_PC_MODE_IN | K210_PC_PU,
#undef DEFAULT
};

/* This saves around 2K vs having a pointer+mode */
struct k210_pcf_info {
#ifdef CONFIG_CMD_PINMUX
	char name[15];
#endif
	u8 mode_id;
};

static const struct k210_pcf_info k210_pcf_infos[] = {
#ifdef CONFIG_CMD_PINMUX
#define FUNC(id, mode) \
	[K210_PCF_##id] = { \
		.name = #id, \
		.mode_id = K210_PC_DEFAULT_##mode \
	}
#else
#define FUNC(id, mode) \
	[K210_PCF_##id] = { \
		.mode_id = K210_PC_DEFAULT_##mode \
	}
#endif
	FUNC(JTAG_TCLK,      IN),
	FUNC(JTAG_TDI,       IN),
	FUNC(JTAG_TMS,       IN),
	FUNC(JTAG_TDO,       OUT),
	FUNC(SPI0_D0,        SPI),
	FUNC(SPI0_D1,        SPI),
	FUNC(SPI0_D2,        SPI),
	FUNC(SPI0_D3,        SPI),
	FUNC(SPI0_D4,        SPI),
	FUNC(SPI0_D5,        SPI),
	FUNC(SPI0_D6,        SPI),
	FUNC(SPI0_D7,        SPI),
	FUNC(SPI0_SS0,       OUT),
	FUNC(SPI0_SS1,       OUT),
	FUNC(SPI0_SS2,       OUT),
	FUNC(SPI0_SS3,       OUT),
	FUNC(SPI0_ARB,       IN_TIE),
	FUNC(SPI0_SCLK,      OUT),
	FUNC(UARTHS_RX,      IN),
	FUNC(UARTHS_TX,      OUT),
	FUNC(RESV6,          IN),
	FUNC(RESV7,          IN),
	FUNC(CLK_SPI1,       OUT),
	FUNC(CLK_I2C1,       OUT),
	FUNC(GPIOHS0,        GPIO),
	FUNC(GPIOHS1,        GPIO),
	FUNC(GPIOHS2,        GPIO),
	FUNC(GPIOHS3,        GPIO),
	FUNC(GPIOHS4,        GPIO),
	FUNC(GPIOHS5,        GPIO),
	FUNC(GPIOHS6,        GPIO),
	FUNC(GPIOHS7,        GPIO),
	FUNC(GPIOHS8,        GPIO),
	FUNC(GPIOHS9,        GPIO),
	FUNC(GPIOHS10,       GPIO),
	FUNC(GPIOHS11,       GPIO),
	FUNC(GPIOHS12,       GPIO),
	FUNC(GPIOHS13,       GPIO),
	FUNC(GPIOHS14,       GPIO),
	FUNC(GPIOHS15,       GPIO),
	FUNC(GPIOHS16,       GPIO),
	FUNC(GPIOHS17,       GPIO),
	FUNC(GPIOHS18,       GPIO),
	FUNC(GPIOHS19,       GPIO),
	FUNC(GPIOHS20,       GPIO),
	FUNC(GPIOHS21,       GPIO),
	FUNC(GPIOHS22,       GPIO),
	FUNC(GPIOHS23,       GPIO),
	FUNC(GPIOHS24,       GPIO),
	FUNC(GPIOHS25,       GPIO),
	FUNC(GPIOHS26,       GPIO),
	FUNC(GPIOHS27,       GPIO),
	FUNC(GPIOHS28,       GPIO),
	FUNC(GPIOHS29,       GPIO),
	FUNC(GPIOHS30,       GPIO),
	FUNC(GPIOHS31,       GPIO),
	FUNC(GPIO0,          GPIO),
	FUNC(GPIO1,          GPIO),
	FUNC(GPIO2,          GPIO),
	FUNC(GPIO3,          GPIO),
	FUNC(GPIO4,          GPIO),
	FUNC(GPIO5,          GPIO),
	FUNC(GPIO6,          GPIO),
	FUNC(GPIO7,          GPIO),
	FUNC(UART1_RX,       IN),
	FUNC(UART1_TX,       OUT),
	FUNC(UART2_RX,       IN),
	FUNC(UART2_TX,       OUT),
	FUNC(UART3_RX,       IN),
	FUNC(UART3_TX,       OUT),
	FUNC(SPI1_D0,        SPI),
	FUNC(SPI1_D1,        SPI),
	FUNC(SPI1_D2,        SPI),
	FUNC(SPI1_D3,        SPI),
	FUNC(SPI1_D4,        SPI),
	FUNC(SPI1_D5,        SPI),
	FUNC(SPI1_D6,        SPI),
	FUNC(SPI1_D7,        SPI),
	FUNC(SPI1_SS0,       OUT),
	FUNC(SPI1_SS1,       OUT),
	FUNC(SPI1_SS2,       OUT),
	FUNC(SPI1_SS3,       OUT),
	FUNC(SPI1_ARB,       IN_TIE),
	FUNC(SPI1_SCLK,      OUT),
	FUNC(SPI2_D0,        SPI),
	FUNC(SPI2_SS,        IN),
	FUNC(SPI2_SCLK,      IN),
	FUNC(I2S0_MCLK,      OUT),
	FUNC(I2S0_SCLK,      OUT),
	FUNC(I2S0_WS,        OUT),
	FUNC(I2S0_IN_D0,     IN),
	FUNC(I2S0_IN_D1,     IN),
	FUNC(I2S0_IN_D2,     IN),
	FUNC(I2S0_IN_D3,     IN),
	FUNC(I2S0_OUT_D0,    OUT),
	FUNC(I2S0_OUT_D1,    OUT),
	FUNC(I2S0_OUT_D2,    OUT),
	FUNC(I2S0_OUT_D3,    OUT),
	FUNC(I2S1_MCLK,      OUT),
	FUNC(I2S1_SCLK,      OUT),
	FUNC(I2S1_WS,        OUT),
	FUNC(I2S1_IN_D0,     IN),
	FUNC(I2S1_IN_D1,     IN),
	FUNC(I2S1_IN_D2,     IN),
	FUNC(I2S1_IN_D3,     IN),
	FUNC(I2S1_OUT_D0,    OUT),
	FUNC(I2S1_OUT_D1,    OUT),
	FUNC(I2S1_OUT_D2,    OUT),
	FUNC(I2S1_OUT_D3,    OUT),
	FUNC(I2S2_MCLK,      OUT),
	FUNC(I2S2_SCLK,      OUT),
	FUNC(I2S2_WS,        OUT),
	FUNC(I2S2_IN_D0,     IN),
	FUNC(I2S2_IN_D1,     IN),
	FUNC(I2S2_IN_D2,     IN),
	FUNC(I2S2_IN_D3,     IN),
	FUNC(I2S2_OUT_D0,    OUT),
	FUNC(I2S2_OUT_D1,    OUT),
	FUNC(I2S2_OUT_D2,    OUT),
	FUNC(I2S2_OUT_D3,    OUT),
	FUNC(RESV0,          DISABLED),
	FUNC(RESV1,          DISABLED),
	FUNC(RESV2,          DISABLED),
	FUNC(RESV3,          DISABLED),
	FUNC(RESV4,          DISABLED),
	FUNC(RESV5,          DISABLED),
	FUNC(I2C0_SCLK,      I2C),
	FUNC(I2C0_SDA,       I2C),
	FUNC(I2C1_SCLK,      I2C),
	FUNC(I2C1_SDA,       I2C),
	FUNC(I2C2_SCLK,      I2C),
	FUNC(I2C2_SDA,       I2C),
	FUNC(DVP_XCLK,       OUT),
	FUNC(DVP_RST,        OUT),
	FUNC(DVP_PWDN,       OUT),
	FUNC(DVP_VSYNC,      IN),
	FUNC(DVP_HSYNC,      IN),
	FUNC(DVP_PCLK,       IN),
	FUNC(DVP_D0,         IN),
	FUNC(DVP_D1,         IN),
	FUNC(DVP_D2,         IN),
	FUNC(DVP_D3,         IN),
	FUNC(DVP_D4,         IN),
	FUNC(DVP_D5,         IN),
	FUNC(DVP_D6,         IN),
	FUNC(DVP_D7,         IN),
	FUNC(SCCB_SCLK,      SCCB),
	FUNC(SCCB_SDA,       SCCB),
	FUNC(UART1_CTS,      IN),
	FUNC(UART1_DSR,      IN),
	FUNC(UART1_DCD,      IN),
	FUNC(UART1_RI,       IN),
	FUNC(UART1_SIR_IN,   IN),
	FUNC(UART1_DTR,      OUT),
	FUNC(UART1_RTS,      OUT),
	FUNC(UART1_OUT2,     OUT),
	FUNC(UART1_OUT1,     OUT),
	FUNC(UART1_SIR_OUT,  OUT),
	FUNC(UART1_BAUD,     OUT),
	FUNC(UART1_RE,       OUT),
	FUNC(UART1_DE,       OUT),
	FUNC(UART1_RS485_EN, OUT),
	FUNC(UART2_CTS,      IN),
	FUNC(UART2_DSR,      IN),
	FUNC(UART2_DCD,      IN),
	FUNC(UART2_RI,       IN),
	FUNC(UART2_SIR_IN,   IN),
	FUNC(UART2_DTR,      OUT),
	FUNC(UART2_RTS,      OUT),
	FUNC(UART2_OUT2,     OUT),
	FUNC(UART2_OUT1,     OUT),
	FUNC(UART2_SIR_OUT,  OUT),
	FUNC(UART2_BAUD,     OUT),
	FUNC(UART2_RE,       OUT),
	FUNC(UART2_DE,       OUT),
	FUNC(UART2_RS485_EN, OUT),
	FUNC(UART3_CTS,      IN),
	FUNC(UART3_DSR,      IN),
	FUNC(UART3_DCD,      IN),
	FUNC(UART3_RI,       IN),
	FUNC(UART3_SIR_IN,   IN),
	FUNC(UART3_DTR,      OUT),
	FUNC(UART3_RTS,      OUT),
	FUNC(UART3_OUT2,     OUT),
	FUNC(UART3_OUT1,     OUT),
	FUNC(UART3_SIR_OUT,  OUT),
	FUNC(UART3_BAUD,     OUT),
	FUNC(UART3_RE,       OUT),
	FUNC(UART3_DE,       OUT),
	FUNC(UART3_RS485_EN, OUT),
	FUNC(TIMER0_TOGGLE1, OUT),
	FUNC(TIMER0_TOGGLE2, OUT),
	FUNC(TIMER0_TOGGLE3, OUT),
	FUNC(TIMER0_TOGGLE4, OUT),
	FUNC(TIMER1_TOGGLE1, OUT),
	FUNC(TIMER1_TOGGLE2, OUT),
	FUNC(TIMER1_TOGGLE3, OUT),
	FUNC(TIMER1_TOGGLE4, OUT),
	FUNC(TIMER2_TOGGLE1, OUT),
	FUNC(TIMER2_TOGGLE2, OUT),
	FUNC(TIMER2_TOGGLE3, OUT),
	FUNC(TIMER2_TOGGLE4, OUT),
	FUNC(CLK_SPI2,       OUT),
	FUNC(CLK_I2C2,       OUT),
	FUNC(INTERNAL0,      OUT),
	FUNC(INTERNAL1,      OUT),
	FUNC(INTERNAL2,      OUT),
	FUNC(INTERNAL3,      OUT),
	FUNC(INTERNAL4,      OUT),
	FUNC(INTERNAL5,      OUT),
	FUNC(INTERNAL6,      OUT),
	FUNC(INTERNAL7,      OUT),
	FUNC(INTERNAL8,      OUT),
	FUNC(INTERNAL9,      IN),
	FUNC(INTERNAL10,     IN),
	FUNC(INTERNAL11,     IN),
	FUNC(INTERNAL12,     IN),
	FUNC(INTERNAL13,     INT13),
	FUNC(INTERNAL14,     I2C),
	FUNC(INTERNAL15,     IN),
	FUNC(INTERNAL16,     IN),
	FUNC(INTERNAL17,     IN),
	FUNC(CONSTANT,       DISABLED),
	FUNC(INTERNAL18,     IN),
	FUNC(DEBUG0,         OUT),
	FUNC(DEBUG1,         OUT),
	FUNC(DEBUG2,         OUT),
	FUNC(DEBUG3,         OUT),
	FUNC(DEBUG4,         OUT),
	FUNC(DEBUG5,         OUT),
	FUNC(DEBUG6,         OUT),
	FUNC(DEBUG7,         OUT),
	FUNC(DEBUG8,         OUT),
	FUNC(DEBUG9,         OUT),
	FUNC(DEBUG10,        OUT),
	FUNC(DEBUG11,        OUT),
	FUNC(DEBUG12,        OUT),
	FUNC(DEBUG13,        OUT),
	FUNC(DEBUG14,        OUT),
	FUNC(DEBUG15,        OUT),
	FUNC(DEBUG16,        OUT),
	FUNC(DEBUG17,        OUT),
	FUNC(DEBUG18,        OUT),
	FUNC(DEBUG19,        OUT),
	FUNC(DEBUG20,        OUT),
	FUNC(DEBUG21,        OUT),
	FUNC(DEBUG22,        OUT),
	FUNC(DEBUG23,        OUT),
	FUNC(DEBUG24,        OUT),
	FUNC(DEBUG25,        OUT),
	FUNC(DEBUG26,        OUT),
	FUNC(DEBUG27,        OUT),
	FUNC(DEBUG28,        OUT),
	FUNC(DEBUG29,        OUT),
	FUNC(DEBUG30,        OUT),
	FUNC(DEBUG31,        OUT),
#undef FUNC
};

static int k210_pc_pinmux_set(struct udevice *dev, u32 pinmux_group)
{
	unsigned pin = FIELD_GET(K210_PG_PIN, pinmux_group);
	bool do_oe = FIELD_GET(K210_PG_DO, pinmux_group);
	unsigned func = FIELD_GET(K210_PG_FUNC, pinmux_group);
	struct k210_pc_priv *priv = dev_get_priv(dev);
	const struct k210_pcf_info *info = &k210_pcf_infos[func];
	u32 mode = k210_pc_mode_id_to_mode[info->mode_id];
	u32 val = func | mode | (do_oe ? K210_PC_DO_OE : 0);

	debug("%s(%.8x): IO_%.2u = %3u | %.8x\n", __func__, pinmux_group, pin,
	      func, mode);

	writel(val, &priv->fpioa->pins[pin]);
	return pin;
}

/* Max drive strength in uA */
static const int k210_pc_drive_strength[] = {
	[0] = 11200,
	[1] = 16800,
	[2] = 22300,
	[3] = 27800,
	[4] = 33300,
	[5] = 38700,
	[6] = 44100,
	[7] = 49500,
};

static int k210_pc_get_drive(unsigned max_strength_ua)
{
	int i;

	for (i = K210_PC_DRIVE_MAX; i >= 0; i--)
		if (k210_pc_drive_strength[i] < max_strength_ua)
			return i;

	return -EINVAL;
}

static int k210_pc_pinconf_set(struct udevice *dev, unsigned pin_selector,
			       unsigned param, unsigned argument)
{
	struct k210_pc_priv *priv = dev_get_priv(dev);
	u32 val = readl(&priv->fpioa->pins[pin_selector]);

	switch (param) {
	case PIN_CONFIG_BIAS_DISABLE:
		val &= ~K210_PC_BIAS_MASK;
		break;
	case PIN_CONFIG_BIAS_PULL_DOWN:
		if (argument)
			val |= K210_PC_PD;
		else
			return -EINVAL;
		break;
	case PIN_CONFIG_BIAS_PULL_UP:
		if (argument)
			val |= K210_PC_PU;
		else
			return -EINVAL;
		break;
	case PIN_CONFIG_DRIVE_STRENGTH:
		argument *= 1000;
	case PIN_CONFIG_DRIVE_STRENGTH_UA: {
		int drive = k210_pc_get_drive(argument);

		if (IS_ERR_VALUE(drive))
			return drive;
		val &= ~K210_PC_DRIVE_MASK;
		val |= FIELD_PREP(K210_PC_DRIVE_MASK, drive);
		break;
	}
	case PIN_CONFIG_INPUT_ENABLE:
		if (argument)
			val |= K210_PC_IE;
		else
			val &= ~K210_PC_IE;
		break;
	case PIN_CONFIG_INPUT_SCHMITT:
		argument = 1;
	case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
		if (argument)
			val |= K210_PC_ST;
		else
			val &= ~K210_PC_ST;
		break;
	case PIN_CONFIG_OUTPUT:
		k210_pc_pinmux_set(dev,
				   K210_FPIOA(pin_selector, K210_PCF_CONSTANT));
		val = readl(&priv->fpioa->pins[pin_selector]);
		val |= K210_PC_MODE_OUT;

		if (!argument)
			val |= K210_PC_DO_INV;
		break;
	case PIN_CONFIG_OUTPUT_ENABLE:
		if (argument)
			val |= K210_PC_OE;
		else
			val &= ~K210_PC_OE;
		break;
	case PIN_CONFIG_SLEW_RATE:
		if (argument)
			val |= K210_PC_SL;
		else
			val &= ~K210_PC_SL;
		break;
	case PIN_CONFIG_OUTPUT_INVERT:
		if (argument)
			val |= K210_PC_DO_INV;
		else
			val &= ~K210_PC_DO_INV;
		break;
	case PIN_CONFIG_INPUT_INVERT:
		if (argument)
			val |= K210_PC_DI_INV;
		else
			val &= ~K210_PC_DI_INV;
		break;
	default:
		return -EINVAL;
	}

	writel(val, &priv->fpioa->pins[pin_selector]);
	return 0;
}

static int k210_pc_pinconf_group_set(struct udevice *dev,
				     unsigned group_selector, unsigned param,
				     unsigned argument)
{
	struct k210_pc_priv *priv = dev_get_priv(dev);

	if (param == PIN_CONFIG_POWER_SOURCE) {
		u32 bit = BIT(group_selector);

		regmap_update_bits(priv->sysctl, priv->power_offset, bit,
				   argument ? bit : 0);
	} else {
		return -EINVAL;
	}

	return 0;
}

#ifdef CONFIG_CMD_PINMUX
static int k210_pc_get_pin_muxing(struct udevice *dev, unsigned int selector,
				  char *buf, int size)
{
	struct k210_pc_priv *priv = dev_get_priv(dev);
	u32 val = readl(&priv->fpioa->pins[selector]);
	const struct k210_pcf_info *info = &k210_pcf_infos[val & K210_PCF_MASK];

	strncpy(buf, info->name, min((size_t)size, sizeof(info->name)));
	return 0;
}
#endif

static const struct pinconf_param k210_pc_pinconf_params[] = {
	{ "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
	{ "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
	{ "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
	{ "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, U32_MAX },
	{ "drive-strength-ua", PIN_CONFIG_DRIVE_STRENGTH_UA, U32_MAX },
	{ "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 },
	{ "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 },
	{ "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 },
	{ "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 },
	{ "power-source", PIN_CONFIG_POWER_SOURCE, K210_PC_POWER_1V8 },
	{ "output-low", PIN_CONFIG_OUTPUT, 0 },
	{ "output-high", PIN_CONFIG_OUTPUT, 1 },
	{ "output-enable", PIN_CONFIG_OUTPUT_ENABLE, 1 },
	{ "output-disable", PIN_CONFIG_OUTPUT_ENABLE, 0 },
	{ "slew-rate", PIN_CONFIG_SLEW_RATE, 1 },
	{ "output-polarity-invert", PIN_CONFIG_OUTPUT_INVERT, 1},
	{ "input-polarity-invert", PIN_CONFIG_INPUT_INVERT, 1},
};

static const struct pinctrl_ops k210_pc_pinctrl_ops = {
#ifdef CONFIG_CMD_PINMUX
	.get_pins_count = k210_pc_get_pins_count,
	.get_pin_name = k210_pc_get_pin_name,
#endif
	.get_groups_count = k210_pc_get_groups_count,
	.get_group_name = k210_pc_get_group_name,
	.pinmux_property_set = k210_pc_pinmux_set,
	.pinconf_num_params = ARRAY_SIZE(k210_pc_pinconf_params),
	.pinconf_params = k210_pc_pinconf_params,
	.pinconf_set = k210_pc_pinconf_set,
	.pinconf_group_set = k210_pc_pinconf_group_set,
	.set_state = pinctrl_generic_set_state,
#ifdef CONFIG_CMD_PINMUX
	.get_pin_muxing = k210_pc_get_pin_muxing,
#endif
};

static int k210_pc_probe(struct udevice *dev)
{
	int ret, i, j;
	struct k210_pc_priv *priv = dev_get_priv(dev);
	struct ofnode_phandle_args args;

	priv->fpioa = dev_read_addr_ptr(dev);
	if (!priv->fpioa)
		return -EINVAL;

	ret = clk_get_by_index(dev, 0, &priv->clk);
	if (ret)
		return ret;

	ret = clk_enable(&priv->clk);
	if (ret && ret != -ENOSYS && ret != -ENOTSUPP)
		return ret;

	ret = dev_read_phandle_with_args(dev, "canaan,k210-sysctl-power",
					NULL, 1, 0, &args);
        if (ret)
		return ret;

	if (args.args_count != 1)
		return -EINVAL;

	priv->sysctl = syscon_node_to_regmap(args.node);
	if (IS_ERR(priv->sysctl))
		return PTR_ERR(priv->sysctl);

	priv->power_offset = args.args[0];

	debug("%s: fpioa = %p sysctl = %p power offset = %x\n", __func__,
	      priv->fpioa, (void *)priv->sysctl->ranges[0].start,
	      priv->power_offset);

	/* Init input ties */
	for (i = 0; i < ARRAY_SIZE(priv->fpioa->tie_en); i++) {
		u32 val = 0;

		for (j = 0; j < 32; j++)
			if (k210_pcf_infos[i * 32 + j].mode_id ==
			    K210_PC_DEFAULT_IN_TIE)
				val |= BIT(j);
		writel(val, &priv->fpioa->tie_en[i]);
		writel(val, &priv->fpioa->tie_val[i]);
	}

	return 0;
}

static const struct udevice_id k210_pc_ids[] = {
	{ .compatible = "canaan,k210-fpioa" },
	{ }
};

U_BOOT_DRIVER(pinctrl_k210) = {
	.name = "pinctrl_k210",
	.id = UCLASS_PINCTRL,
	.of_match = k210_pc_ids,
	.probe = k210_pc_probe,
	.priv_auto	= sizeof(struct k210_pc_priv),
	.ops = &k210_pc_pinctrl_ops,
};
