| // 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, | |
| }; |