| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com> |
| * Copyright (c) 2020 Western Digital Corporation or its affiliates. |
| */ |
| #include <linux/bitfield.h> |
| #include <linux/clk.h> |
| #include <linux/io.h> |
| #include <linux/mfd/syscon.h> |
| #include <linux/of.h> |
| #include <linux/platform_device.h> |
| #include <linux/regmap.h> |
| #include <linux/seq_file.h> |
| #include <linux/slab.h> |
| |
| #include <linux/pinctrl/pinconf-generic.h> |
| #include <linux/pinctrl/pinconf.h> |
| #include <linux/pinctrl/pinctrl.h> |
| #include <linux/pinctrl/pinmux.h> |
| |
| #include <dt-bindings/pinctrl/k210-fpioa.h> |
| |
| #include "core.h" |
| #include "pinconf.h" |
| #include "pinctrl-utils.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 Output Enable */ |
| #define K210_PC_DO_OE BIT(14) /* set Data Out to Output Enable sig */ |
| #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 */ |
| /* Same semantics as OE above */ |
| #define K210_PC_IE BIT(20) /* Input Enable */ |
| #define K210_PC_IE_INV BIT(21) /* INVert 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) |
| |
| /* |
| * struct k210_fpioa: Kendryte K210 FPIOA memory mapped registers |
| * @pins: 48 32-bits IO pin registers |
| * @tie_en: 256 (one per function) input tie enable bits |
| * @tie_val: 256 (one per function) input tie value bits |
| */ |
| struct k210_fpioa { |
| u32 pins[48]; |
| u32 tie_en[8]; |
| u32 tie_val[8]; |
| }; |
| |
| struct k210_fpioa_data { |
| |
| struct device *dev; |
| struct pinctrl_dev *pctl; |
| |
| struct k210_fpioa __iomem *fpioa; |
| struct regmap *sysctl_map; |
| u32 power_offset; |
| struct clk *clk; |
| struct clk *pclk; |
| }; |
| |
| #define K210_PIN_NAME(i) ("IO_" #i) |
| #define K210_PIN(i) [(i)] = PINCTRL_PIN((i), K210_PIN_NAME(i)) |
| |
| static const struct pinctrl_pin_desc k210_pins[] = { |
| K210_PIN(0), K210_PIN(1), K210_PIN(2), |
| K210_PIN(3), K210_PIN(4), K210_PIN(5), |
| K210_PIN(6), K210_PIN(7), K210_PIN(8), |
| K210_PIN(9), K210_PIN(10), K210_PIN(11), |
| K210_PIN(12), K210_PIN(13), K210_PIN(14), |
| K210_PIN(15), K210_PIN(16), K210_PIN(17), |
| K210_PIN(18), K210_PIN(19), K210_PIN(20), |
| K210_PIN(21), K210_PIN(22), K210_PIN(23), |
| K210_PIN(24), K210_PIN(25), K210_PIN(26), |
| K210_PIN(27), K210_PIN(28), K210_PIN(29), |
| K210_PIN(30), K210_PIN(31), K210_PIN(32), |
| K210_PIN(33), K210_PIN(34), K210_PIN(35), |
| K210_PIN(36), K210_PIN(37), K210_PIN(38), |
| K210_PIN(39), K210_PIN(40), K210_PIN(41), |
| K210_PIN(42), K210_PIN(43), K210_PIN(44), |
| K210_PIN(45), K210_PIN(46), K210_PIN(47) |
| }; |
| |
| #define K210_NPINS ARRAY_SIZE(k210_pins) |
| |
| /* |
| * Pin groups: each of the 48 programmable pins is a group. |
| * To this are added 8 power domain groups, which for the purposes of |
| * the pin subsystem, contain no pins. The power domain groups only exist |
| * to set the power level. The id should never be used (since there are |
| * no pins 48-55). |
| */ |
| static const char *const k210_group_names[] = { |
| /* The first 48 groups are for pins, one each */ |
| K210_PIN_NAME(0), K210_PIN_NAME(1), K210_PIN_NAME(2), |
| K210_PIN_NAME(3), K210_PIN_NAME(4), K210_PIN_NAME(5), |
| K210_PIN_NAME(6), K210_PIN_NAME(7), K210_PIN_NAME(8), |
| K210_PIN_NAME(9), K210_PIN_NAME(10), K210_PIN_NAME(11), |
| K210_PIN_NAME(12), K210_PIN_NAME(13), K210_PIN_NAME(14), |
| K210_PIN_NAME(15), K210_PIN_NAME(16), K210_PIN_NAME(17), |
| K210_PIN_NAME(18), K210_PIN_NAME(19), K210_PIN_NAME(20), |
| K210_PIN_NAME(21), K210_PIN_NAME(22), K210_PIN_NAME(23), |
| K210_PIN_NAME(24), K210_PIN_NAME(25), K210_PIN_NAME(26), |
| K210_PIN_NAME(27), K210_PIN_NAME(28), K210_PIN_NAME(29), |
| K210_PIN_NAME(30), K210_PIN_NAME(31), K210_PIN_NAME(32), |
| K210_PIN_NAME(33), K210_PIN_NAME(34), K210_PIN_NAME(35), |
| K210_PIN_NAME(36), K210_PIN_NAME(37), K210_PIN_NAME(38), |
| K210_PIN_NAME(39), K210_PIN_NAME(40), K210_PIN_NAME(41), |
| K210_PIN_NAME(42), K210_PIN_NAME(43), K210_PIN_NAME(44), |
| K210_PIN_NAME(45), K210_PIN_NAME(46), K210_PIN_NAME(47), |
| [48] = "A0", [49] = "A1", [50] = "A2", |
| [51] = "B3", [52] = "B4", [53] = "B5", |
| [54] = "C6", [55] = "C7" |
| }; |
| |
| #define K210_NGROUPS ARRAY_SIZE(k210_group_names) |
| |
| enum k210_pinctrl_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, |
| }; |
| |
| #define K210_PC_DEFAULT(mode) \ |
| [K210_PC_DEFAULT_##mode] = K210_PC_MODE_##mode |
| |
| static const u32 k210_pinconf_mode_id_to_mode[] = { |
| [K210_PC_DEFAULT_DISABLED] = 0, |
| K210_PC_DEFAULT(IN), |
| [K210_PC_DEFAULT_IN_TIE] = K210_PC_MODE_IN, |
| K210_PC_DEFAULT(OUT), |
| K210_PC_DEFAULT(I2C), |
| K210_PC_DEFAULT(SCCB), |
| K210_PC_DEFAULT(SPI), |
| K210_PC_DEFAULT(GPIO), |
| [K210_PC_DEFAULT_INT13] = K210_PC_MODE_IN | K210_PC_PU, |
| }; |
| |
| #undef DEFAULT |
| |
| /* |
| * Pin functions configuration information. |
| */ |
| struct k210_pcf_info { |
| char name[15]; |
| u8 mode_id; |
| }; |
| |
| #define K210_FUNC(id, mode) \ |
| [K210_PCF_##id] = { \ |
| .name = #id, \ |
| .mode_id = K210_PC_DEFAULT_##mode \ |
| } |
| |
| static const struct k210_pcf_info k210_pcf_infos[] = { |
| K210_FUNC(JTAG_TCLK, IN), |
| K210_FUNC(JTAG_TDI, IN), |
| K210_FUNC(JTAG_TMS, IN), |
| K210_FUNC(JTAG_TDO, OUT), |
| K210_FUNC(SPI0_D0, SPI), |
| K210_FUNC(SPI0_D1, SPI), |
| K210_FUNC(SPI0_D2, SPI), |
| K210_FUNC(SPI0_D3, SPI), |
| K210_FUNC(SPI0_D4, SPI), |
| K210_FUNC(SPI0_D5, SPI), |
| K210_FUNC(SPI0_D6, SPI), |
| K210_FUNC(SPI0_D7, SPI), |
| K210_FUNC(SPI0_SS0, OUT), |
| K210_FUNC(SPI0_SS1, OUT), |
| K210_FUNC(SPI0_SS2, OUT), |
| K210_FUNC(SPI0_SS3, OUT), |
| K210_FUNC(SPI0_ARB, IN_TIE), |
| K210_FUNC(SPI0_SCLK, OUT), |
| K210_FUNC(UARTHS_RX, IN), |
| K210_FUNC(UARTHS_TX, OUT), |
| K210_FUNC(RESV6, IN), |
| K210_FUNC(RESV7, IN), |
| K210_FUNC(CLK_SPI1, OUT), |
| K210_FUNC(CLK_I2C1, OUT), |
| K210_FUNC(GPIOHS0, GPIO), |
| K210_FUNC(GPIOHS1, GPIO), |
| K210_FUNC(GPIOHS2, GPIO), |
| K210_FUNC(GPIOHS3, GPIO), |
| K210_FUNC(GPIOHS4, GPIO), |
| K210_FUNC(GPIOHS5, GPIO), |
| K210_FUNC(GPIOHS6, GPIO), |
| K210_FUNC(GPIOHS7, GPIO), |
| K210_FUNC(GPIOHS8, GPIO), |
| K210_FUNC(GPIOHS9, GPIO), |
| K210_FUNC(GPIOHS10, GPIO), |
| K210_FUNC(GPIOHS11, GPIO), |
| K210_FUNC(GPIOHS12, GPIO), |
| K210_FUNC(GPIOHS13, GPIO), |
| K210_FUNC(GPIOHS14, GPIO), |
| K210_FUNC(GPIOHS15, GPIO), |
| K210_FUNC(GPIOHS16, GPIO), |
| K210_FUNC(GPIOHS17, GPIO), |
| K210_FUNC(GPIOHS18, GPIO), |
| K210_FUNC(GPIOHS19, GPIO), |
| K210_FUNC(GPIOHS20, GPIO), |
| K210_FUNC(GPIOHS21, GPIO), |
| K210_FUNC(GPIOHS22, GPIO), |
| K210_FUNC(GPIOHS23, GPIO), |
| K210_FUNC(GPIOHS24, GPIO), |
| K210_FUNC(GPIOHS25, GPIO), |
| K210_FUNC(GPIOHS26, GPIO), |
| K210_FUNC(GPIOHS27, GPIO), |
| K210_FUNC(GPIOHS28, GPIO), |
| K210_FUNC(GPIOHS29, GPIO), |
| K210_FUNC(GPIOHS30, GPIO), |
| K210_FUNC(GPIOHS31, GPIO), |
| K210_FUNC(GPIO0, GPIO), |
| K210_FUNC(GPIO1, GPIO), |
| K210_FUNC(GPIO2, GPIO), |
| K210_FUNC(GPIO3, GPIO), |
| K210_FUNC(GPIO4, GPIO), |
| K210_FUNC(GPIO5, GPIO), |
| K210_FUNC(GPIO6, GPIO), |
| K210_FUNC(GPIO7, GPIO), |
| K210_FUNC(UART1_RX, IN), |
| K210_FUNC(UART1_TX, OUT), |
| K210_FUNC(UART2_RX, IN), |
| K210_FUNC(UART2_TX, OUT), |
| K210_FUNC(UART3_RX, IN), |
| K210_FUNC(UART3_TX, OUT), |
| K210_FUNC(SPI1_D0, SPI), |
| K210_FUNC(SPI1_D1, SPI), |
| K210_FUNC(SPI1_D2, SPI), |
| K210_FUNC(SPI1_D3, SPI), |
| K210_FUNC(SPI1_D4, SPI), |
| K210_FUNC(SPI1_D5, SPI), |
| K210_FUNC(SPI1_D6, SPI), |
| K210_FUNC(SPI1_D7, SPI), |
| K210_FUNC(SPI1_SS0, OUT), |
| K210_FUNC(SPI1_SS1, OUT), |
| K210_FUNC(SPI1_SS2, OUT), |
| K210_FUNC(SPI1_SS3, OUT), |
| K210_FUNC(SPI1_ARB, IN_TIE), |
| K210_FUNC(SPI1_SCLK, OUT), |
| K210_FUNC(SPI2_D0, SPI), |
| K210_FUNC(SPI2_SS, IN), |
| K210_FUNC(SPI2_SCLK, IN), |
| K210_FUNC(I2S0_MCLK, OUT), |
| K210_FUNC(I2S0_SCLK, OUT), |
| K210_FUNC(I2S0_WS, OUT), |
| K210_FUNC(I2S0_IN_D0, IN), |
| K210_FUNC(I2S0_IN_D1, IN), |
| K210_FUNC(I2S0_IN_D2, IN), |
| K210_FUNC(I2S0_IN_D3, IN), |
| K210_FUNC(I2S0_OUT_D0, OUT), |
| K210_FUNC(I2S0_OUT_D1, OUT), |
| K210_FUNC(I2S0_OUT_D2, OUT), |
| K210_FUNC(I2S0_OUT_D3, OUT), |
| K210_FUNC(I2S1_MCLK, OUT), |
| K210_FUNC(I2S1_SCLK, OUT), |
| K210_FUNC(I2S1_WS, OUT), |
| K210_FUNC(I2S1_IN_D0, IN), |
| K210_FUNC(I2S1_IN_D1, IN), |
| K210_FUNC(I2S1_IN_D2, IN), |
| K210_FUNC(I2S1_IN_D3, IN), |
| K210_FUNC(I2S1_OUT_D0, OUT), |
| K210_FUNC(I2S1_OUT_D1, OUT), |
| K210_FUNC(I2S1_OUT_D2, OUT), |
| K210_FUNC(I2S1_OUT_D3, OUT), |
| K210_FUNC(I2S2_MCLK, OUT), |
| K210_FUNC(I2S2_SCLK, OUT), |
| K210_FUNC(I2S2_WS, OUT), |
| K210_FUNC(I2S2_IN_D0, IN), |
| K210_FUNC(I2S2_IN_D1, IN), |
| K210_FUNC(I2S2_IN_D2, IN), |
| K210_FUNC(I2S2_IN_D3, IN), |
| K210_FUNC(I2S2_OUT_D0, OUT), |
| K210_FUNC(I2S2_OUT_D1, OUT), |
| K210_FUNC(I2S2_OUT_D2, OUT), |
| K210_FUNC(I2S2_OUT_D3, OUT), |
| K210_FUNC(RESV0, DISABLED), |
| K210_FUNC(RESV1, DISABLED), |
| K210_FUNC(RESV2, DISABLED), |
| K210_FUNC(RESV3, DISABLED), |
| K210_FUNC(RESV4, DISABLED), |
| K210_FUNC(RESV5, DISABLED), |
| K210_FUNC(I2C0_SCLK, I2C), |
| K210_FUNC(I2C0_SDA, I2C), |
| K210_FUNC(I2C1_SCLK, I2C), |
| K210_FUNC(I2C1_SDA, I2C), |
| K210_FUNC(I2C2_SCLK, I2C), |
| K210_FUNC(I2C2_SDA, I2C), |
| K210_FUNC(DVP_XCLK, OUT), |
| K210_FUNC(DVP_RST, OUT), |
| K210_FUNC(DVP_PWDN, OUT), |
| K210_FUNC(DVP_VSYNC, IN), |
| K210_FUNC(DVP_HSYNC, IN), |
| K210_FUNC(DVP_PCLK, IN), |
| K210_FUNC(DVP_D0, IN), |
| K210_FUNC(DVP_D1, IN), |
| K210_FUNC(DVP_D2, IN), |
| K210_FUNC(DVP_D3, IN), |
| K210_FUNC(DVP_D4, IN), |
| K210_FUNC(DVP_D5, IN), |
| K210_FUNC(DVP_D6, IN), |
| K210_FUNC(DVP_D7, IN), |
| K210_FUNC(SCCB_SCLK, SCCB), |
| K210_FUNC(SCCB_SDA, SCCB), |
| K210_FUNC(UART1_CTS, IN), |
| K210_FUNC(UART1_DSR, IN), |
| K210_FUNC(UART1_DCD, IN), |
| K210_FUNC(UART1_RI, IN), |
| K210_FUNC(UART1_SIR_IN, IN), |
| K210_FUNC(UART1_DTR, OUT), |
| K210_FUNC(UART1_RTS, OUT), |
| K210_FUNC(UART1_OUT2, OUT), |
| K210_FUNC(UART1_OUT1, OUT), |
| K210_FUNC(UART1_SIR_OUT, OUT), |
| K210_FUNC(UART1_BAUD, OUT), |
| K210_FUNC(UART1_RE, OUT), |
| K210_FUNC(UART1_DE, OUT), |
| K210_FUNC(UART1_RS485_EN, OUT), |
| K210_FUNC(UART2_CTS, IN), |
| K210_FUNC(UART2_DSR, IN), |
| K210_FUNC(UART2_DCD, IN), |
| K210_FUNC(UART2_RI, IN), |
| K210_FUNC(UART2_SIR_IN, IN), |
| K210_FUNC(UART2_DTR, OUT), |
| K210_FUNC(UART2_RTS, OUT), |
| K210_FUNC(UART2_OUT2, OUT), |
| K210_FUNC(UART2_OUT1, OUT), |
| K210_FUNC(UART2_SIR_OUT, OUT), |
| K210_FUNC(UART2_BAUD, OUT), |
| K210_FUNC(UART2_RE, OUT), |
| K210_FUNC(UART2_DE, OUT), |
| K210_FUNC(UART2_RS485_EN, OUT), |
| K210_FUNC(UART3_CTS, IN), |
| K210_FUNC(UART3_DSR, IN), |
| K210_FUNC(UART3_DCD, IN), |
| K210_FUNC(UART3_RI, IN), |
| K210_FUNC(UART3_SIR_IN, IN), |
| K210_FUNC(UART3_DTR, OUT), |
| K210_FUNC(UART3_RTS, OUT), |
| K210_FUNC(UART3_OUT2, OUT), |
| K210_FUNC(UART3_OUT1, OUT), |
| K210_FUNC(UART3_SIR_OUT, OUT), |
| K210_FUNC(UART3_BAUD, OUT), |
| K210_FUNC(UART3_RE, OUT), |
| K210_FUNC(UART3_DE, OUT), |
| K210_FUNC(UART3_RS485_EN, OUT), |
| K210_FUNC(TIMER0_TOGGLE1, OUT), |
| K210_FUNC(TIMER0_TOGGLE2, OUT), |
| K210_FUNC(TIMER0_TOGGLE3, OUT), |
| K210_FUNC(TIMER0_TOGGLE4, OUT), |
| K210_FUNC(TIMER1_TOGGLE1, OUT), |
| K210_FUNC(TIMER1_TOGGLE2, OUT), |
| K210_FUNC(TIMER1_TOGGLE3, OUT), |
| K210_FUNC(TIMER1_TOGGLE4, OUT), |
| K210_FUNC(TIMER2_TOGGLE1, OUT), |
| K210_FUNC(TIMER2_TOGGLE2, OUT), |
| K210_FUNC(TIMER2_TOGGLE3, OUT), |
| K210_FUNC(TIMER2_TOGGLE4, OUT), |
| K210_FUNC(CLK_SPI2, OUT), |
| K210_FUNC(CLK_I2C2, OUT), |
| K210_FUNC(INTERNAL0, OUT), |
| K210_FUNC(INTERNAL1, OUT), |
| K210_FUNC(INTERNAL2, OUT), |
| K210_FUNC(INTERNAL3, OUT), |
| K210_FUNC(INTERNAL4, OUT), |
| K210_FUNC(INTERNAL5, OUT), |
| K210_FUNC(INTERNAL6, OUT), |
| K210_FUNC(INTERNAL7, OUT), |
| K210_FUNC(INTERNAL8, OUT), |
| K210_FUNC(INTERNAL9, IN), |
| K210_FUNC(INTERNAL10, IN), |
| K210_FUNC(INTERNAL11, IN), |
| K210_FUNC(INTERNAL12, IN), |
| K210_FUNC(INTERNAL13, INT13), |
| K210_FUNC(INTERNAL14, I2C), |
| K210_FUNC(INTERNAL15, IN), |
| K210_FUNC(INTERNAL16, IN), |
| K210_FUNC(INTERNAL17, IN), |
| K210_FUNC(CONSTANT, DISABLED), |
| K210_FUNC(INTERNAL18, IN), |
| K210_FUNC(DEBUG0, OUT), |
| K210_FUNC(DEBUG1, OUT), |
| K210_FUNC(DEBUG2, OUT), |
| K210_FUNC(DEBUG3, OUT), |
| K210_FUNC(DEBUG4, OUT), |
| K210_FUNC(DEBUG5, OUT), |
| K210_FUNC(DEBUG6, OUT), |
| K210_FUNC(DEBUG7, OUT), |
| K210_FUNC(DEBUG8, OUT), |
| K210_FUNC(DEBUG9, OUT), |
| K210_FUNC(DEBUG10, OUT), |
| K210_FUNC(DEBUG11, OUT), |
| K210_FUNC(DEBUG12, OUT), |
| K210_FUNC(DEBUG13, OUT), |
| K210_FUNC(DEBUG14, OUT), |
| K210_FUNC(DEBUG15, OUT), |
| K210_FUNC(DEBUG16, OUT), |
| K210_FUNC(DEBUG17, OUT), |
| K210_FUNC(DEBUG18, OUT), |
| K210_FUNC(DEBUG19, OUT), |
| K210_FUNC(DEBUG20, OUT), |
| K210_FUNC(DEBUG21, OUT), |
| K210_FUNC(DEBUG22, OUT), |
| K210_FUNC(DEBUG23, OUT), |
| K210_FUNC(DEBUG24, OUT), |
| K210_FUNC(DEBUG25, OUT), |
| K210_FUNC(DEBUG26, OUT), |
| K210_FUNC(DEBUG27, OUT), |
| K210_FUNC(DEBUG28, OUT), |
| K210_FUNC(DEBUG29, OUT), |
| K210_FUNC(DEBUG30, OUT), |
| K210_FUNC(DEBUG31, OUT), |
| }; |
| |
| #define PIN_CONFIG_OUTPUT_INVERT (PIN_CONFIG_END + 1) |
| #define PIN_CONFIG_INPUT_INVERT (PIN_CONFIG_END + 2) |
| |
| static const struct pinconf_generic_params k210_pinconf_custom_params[] = { |
| { "output-polarity-invert", PIN_CONFIG_OUTPUT_INVERT, 1 }, |
| { "input-polarity-invert", PIN_CONFIG_INPUT_INVERT, 1 }, |
| }; |
| |
| /* |
| * Max drive strength in uA. |
| */ |
| static const int k210_pinconf_drive_strength[] = { |
| [0] = 11200, |
| [1] = 16800, |
| [2] = 22300, |
| [3] = 27800, |
| [4] = 33300, |
| [5] = 38700, |
| [6] = 44100, |
| [7] = 49500, |
| }; |
| |
| static int k210_pinconf_get_drive(unsigned int max_strength_ua) |
| { |
| int i; |
| |
| for (i = K210_PC_DRIVE_MAX; i >= 0; i--) { |
| if (k210_pinconf_drive_strength[i] <= max_strength_ua) |
| return i; |
| } |
| |
| return -EINVAL; |
| } |
| |
| static void k210_pinmux_set_pin_function(struct pinctrl_dev *pctldev, |
| u32 pin, u32 func) |
| { |
| struct k210_fpioa_data *pdata = pinctrl_dev_get_drvdata(pctldev); |
| const struct k210_pcf_info *info = &k210_pcf_infos[func]; |
| u32 mode = k210_pinconf_mode_id_to_mode[info->mode_id]; |
| u32 val = func | mode; |
| |
| dev_dbg(pdata->dev, "set pin %u function %s (%u) -> 0x%08x\n", |
| pin, info->name, func, val); |
| |
| writel(val, &pdata->fpioa->pins[pin]); |
| } |
| |
| static int k210_pinconf_set_param(struct pinctrl_dev *pctldev, |
| unsigned int pin, |
| unsigned int param, unsigned int arg) |
| { |
| struct k210_fpioa_data *pdata = pinctrl_dev_get_drvdata(pctldev); |
| u32 val = readl(&pdata->fpioa->pins[pin]); |
| int drive; |
| |
| dev_dbg(pdata->dev, "set pin %u param %u, arg 0x%x\n", |
| pin, param, arg); |
| |
| switch (param) { |
| case PIN_CONFIG_BIAS_DISABLE: |
| val &= ~K210_PC_BIAS_MASK; |
| break; |
| case PIN_CONFIG_BIAS_PULL_DOWN: |
| if (!arg) |
| return -EINVAL; |
| val |= K210_PC_PD; |
| break; |
| case PIN_CONFIG_BIAS_PULL_UP: |
| if (!arg) |
| return -EINVAL; |
| val |= K210_PC_PU; |
| break; |
| case PIN_CONFIG_DRIVE_STRENGTH: |
| arg *= 1000; |
| fallthrough; |
| case PIN_CONFIG_DRIVE_STRENGTH_UA: |
| drive = k210_pinconf_get_drive(arg); |
| if (drive < 0) |
| return drive; |
| val &= ~K210_PC_DRIVE_MASK; |
| val |= FIELD_PREP(K210_PC_DRIVE_MASK, drive); |
| break; |
| case PIN_CONFIG_INPUT_ENABLE: |
| if (arg) |
| val |= K210_PC_IE; |
| else |
| val &= ~K210_PC_IE; |
| break; |
| case PIN_CONFIG_INPUT_SCHMITT_ENABLE: |
| if (arg) |
| val |= K210_PC_ST; |
| else |
| val &= ~K210_PC_ST; |
| break; |
| case PIN_CONFIG_OUTPUT: |
| k210_pinmux_set_pin_function(pctldev, pin, K210_PCF_CONSTANT); |
| val = readl(&pdata->fpioa->pins[pin]); |
| val |= K210_PC_MODE_OUT; |
| if (!arg) |
| val |= K210_PC_DO_INV; |
| break; |
| case PIN_CONFIG_OUTPUT_ENABLE: |
| if (arg) |
| val |= K210_PC_OE; |
| else |
| val &= ~K210_PC_OE; |
| break; |
| case PIN_CONFIG_SLEW_RATE: |
| if (arg) |
| val |= K210_PC_SL; |
| else |
| val &= ~K210_PC_SL; |
| break; |
| case PIN_CONFIG_OUTPUT_INVERT: |
| if (arg) |
| val |= K210_PC_DO_INV; |
| else |
| val &= ~K210_PC_DO_INV; |
| break; |
| case PIN_CONFIG_INPUT_INVERT: |
| if (arg) |
| val |= K210_PC_DI_INV; |
| else |
| val &= ~K210_PC_DI_INV; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| writel(val, &pdata->fpioa->pins[pin]); |
| |
| return 0; |
| } |
| |
| static int k210_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, |
| unsigned long *configs, unsigned int num_configs) |
| { |
| unsigned int param, arg; |
| int i, ret; |
| |
| if (WARN_ON(pin >= K210_NPINS)) |
| return -EINVAL; |
| |
| for (i = 0; i < num_configs; i++) { |
| param = pinconf_to_config_param(configs[i]); |
| arg = pinconf_to_config_argument(configs[i]); |
| ret = k210_pinconf_set_param(pctldev, pin, param, arg); |
| if (ret) |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static void k210_pinconf_dbg_show(struct pinctrl_dev *pctldev, |
| struct seq_file *s, unsigned int pin) |
| { |
| struct k210_fpioa_data *pdata = pinctrl_dev_get_drvdata(pctldev); |
| |
| seq_printf(s, "%#x", readl(&pdata->fpioa->pins[pin])); |
| } |
| |
| static int k210_pinconf_group_set(struct pinctrl_dev *pctldev, |
| unsigned int selector, unsigned long *configs, |
| unsigned int num_configs) |
| { |
| struct k210_fpioa_data *pdata = pinctrl_dev_get_drvdata(pctldev); |
| unsigned int param, arg; |
| u32 bit; |
| int i; |
| |
| /* Pins should be configured with pinmux, not groups*/ |
| if (selector < K210_NPINS) |
| return -EINVAL; |
| |
| /* Otherwise it's a power domain */ |
| for (i = 0; i < num_configs; i++) { |
| param = pinconf_to_config_param(configs[i]); |
| if (param != PIN_CONFIG_POWER_SOURCE) |
| return -EINVAL; |
| |
| arg = pinconf_to_config_argument(configs[i]); |
| bit = BIT(selector - K210_NPINS); |
| regmap_update_bits(pdata->sysctl_map, |
| pdata->power_offset, |
| bit, arg ? bit : 0); |
| } |
| |
| return 0; |
| } |
| |
| static void k210_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, |
| struct seq_file *s, |
| unsigned int selector) |
| { |
| struct k210_fpioa_data *pdata = pinctrl_dev_get_drvdata(pctldev); |
| int ret; |
| u32 val; |
| |
| if (selector < K210_NPINS) |
| return k210_pinconf_dbg_show(pctldev, s, selector); |
| |
| ret = regmap_read(pdata->sysctl_map, pdata->power_offset, &val); |
| if (ret) { |
| dev_err(pdata->dev, "Failed to read power reg\n"); |
| return; |
| } |
| |
| seq_printf(s, "%s: %s V", k210_group_names[selector], |
| val & BIT(selector - K210_NPINS) ? "1.8" : "3.3"); |
| } |
| |
| static const struct pinconf_ops k210_pinconf_ops = { |
| .is_generic = true, |
| .pin_config_set = k210_pinconf_set, |
| .pin_config_group_set = k210_pinconf_group_set, |
| .pin_config_dbg_show = k210_pinconf_dbg_show, |
| .pin_config_group_dbg_show = k210_pinconf_group_dbg_show, |
| }; |
| |
| static int k210_pinmux_get_function_count(struct pinctrl_dev *pctldev) |
| { |
| return ARRAY_SIZE(k210_pcf_infos); |
| } |
| |
| static const char *k210_pinmux_get_function_name(struct pinctrl_dev *pctldev, |
| unsigned int selector) |
| { |
| return k210_pcf_infos[selector].name; |
| } |
| |
| static int k210_pinmux_get_function_groups(struct pinctrl_dev *pctldev, |
| unsigned int selector, |
| const char * const **groups, |
| unsigned int * const num_groups) |
| { |
| /* Any function can be mapped to any pin */ |
| *groups = k210_group_names; |
| *num_groups = K210_NPINS; |
| |
| return 0; |
| } |
| |
| static int k210_pinmux_set_mux(struct pinctrl_dev *pctldev, |
| unsigned int function, |
| unsigned int group) |
| { |
| /* Can't mux power domains */ |
| if (group >= K210_NPINS) |
| return -EINVAL; |
| |
| k210_pinmux_set_pin_function(pctldev, group, function); |
| |
| return 0; |
| } |
| |
| static const struct pinmux_ops k210_pinmux_ops = { |
| .get_functions_count = k210_pinmux_get_function_count, |
| .get_function_name = k210_pinmux_get_function_name, |
| .get_function_groups = k210_pinmux_get_function_groups, |
| .set_mux = k210_pinmux_set_mux, |
| .strict = true, |
| }; |
| |
| static int k210_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) |
| { |
| return K210_NGROUPS; |
| } |
| |
| static const char *k210_pinctrl_get_group_name(struct pinctrl_dev *pctldev, |
| unsigned int group) |
| { |
| return k210_group_names[group]; |
| } |
| |
| static int k210_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, |
| unsigned int group, |
| const unsigned int **pins, |
| unsigned int *npins) |
| { |
| if (group >= K210_NPINS) { |
| *pins = NULL; |
| *npins = 0; |
| return 0; |
| } |
| |
| *pins = &k210_pins[group].number; |
| *npins = 1; |
| |
| return 0; |
| } |
| |
| static void k210_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, |
| struct seq_file *s, unsigned int offset) |
| { |
| seq_printf(s, "%s", dev_name(pctldev->dev)); |
| } |
| |
| static int k210_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, |
| struct device_node *np, |
| struct pinctrl_map **map, |
| unsigned int *reserved_maps, |
| unsigned int *num_maps) |
| { |
| int ret, pinmux_groups; |
| u32 pinmux_group; |
| unsigned long *configs = NULL; |
| unsigned int num_configs = 0; |
| unsigned int reserve = 0; |
| |
| ret = of_property_count_strings(np, "groups"); |
| if (!ret) |
| return pinconf_generic_dt_subnode_to_map(pctldev, np, map, |
| reserved_maps, num_maps, |
| PIN_MAP_TYPE_CONFIGS_GROUP); |
| |
| pinmux_groups = of_property_count_u32_elems(np, "pinmux"); |
| if (pinmux_groups <= 0) { |
| /* Ignore this node */ |
| return 0; |
| } |
| |
| ret = pinconf_generic_parse_dt_config(np, pctldev, &configs, |
| &num_configs); |
| if (ret < 0) { |
| dev_err(pctldev->dev, "%pOF: could not parse node property\n", |
| np); |
| return ret; |
| } |
| |
| reserve = pinmux_groups * (1 + num_configs); |
| ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, num_maps, |
| reserve); |
| if (ret < 0) |
| goto exit; |
| |
| of_property_for_each_u32(np, "pinmux", pinmux_group) { |
| const char *group_name, *func_name; |
| u32 pin = FIELD_GET(K210_PG_PIN, pinmux_group); |
| u32 func = FIELD_GET(K210_PG_FUNC, pinmux_group); |
| |
| if (pin >= K210_NPINS) { |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| group_name = k210_group_names[pin]; |
| func_name = k210_pcf_infos[func].name; |
| |
| dev_dbg(pctldev->dev, "Pinmux %s: pin %u func %s\n", |
| np->name, pin, func_name); |
| |
| ret = pinctrl_utils_add_map_mux(pctldev, map, reserved_maps, |
| num_maps, group_name, |
| func_name); |
| if (ret < 0) { |
| dev_err(pctldev->dev, "%pOF add mux map failed %d\n", |
| np, ret); |
| goto exit; |
| } |
| |
| if (num_configs) { |
| ret = pinctrl_utils_add_map_configs(pctldev, map, |
| reserved_maps, num_maps, group_name, |
| configs, num_configs, |
| PIN_MAP_TYPE_CONFIGS_PIN); |
| if (ret < 0) { |
| dev_err(pctldev->dev, |
| "%pOF add configs map failed %d\n", |
| np, ret); |
| goto exit; |
| } |
| } |
| } |
| |
| ret = 0; |
| |
| exit: |
| kfree(configs); |
| return ret; |
| } |
| |
| static int k210_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, |
| struct device_node *np_config, |
| struct pinctrl_map **map, |
| unsigned int *num_maps) |
| { |
| unsigned int reserved_maps; |
| int ret; |
| |
| reserved_maps = 0; |
| *map = NULL; |
| *num_maps = 0; |
| |
| ret = k210_pinctrl_dt_subnode_to_map(pctldev, np_config, map, |
| &reserved_maps, num_maps); |
| if (ret < 0) |
| goto err; |
| |
| for_each_available_child_of_node_scoped(np_config, np) { |
| ret = k210_pinctrl_dt_subnode_to_map(pctldev, np, map, |
| &reserved_maps, num_maps); |
| if (ret < 0) |
| goto err; |
| } |
| return 0; |
| |
| err: |
| pinctrl_utils_free_map(pctldev, *map, *num_maps); |
| return ret; |
| } |
| |
| |
| static const struct pinctrl_ops k210_pinctrl_ops = { |
| .get_groups_count = k210_pinctrl_get_groups_count, |
| .get_group_name = k210_pinctrl_get_group_name, |
| .get_group_pins = k210_pinctrl_get_group_pins, |
| .pin_dbg_show = k210_pinctrl_pin_dbg_show, |
| .dt_node_to_map = k210_pinctrl_dt_node_to_map, |
| .dt_free_map = pinconf_generic_dt_free_map, |
| }; |
| |
| static struct pinctrl_desc k210_pinctrl_desc = { |
| .name = "k210-pinctrl", |
| .pins = k210_pins, |
| .npins = K210_NPINS, |
| .pctlops = &k210_pinctrl_ops, |
| .pmxops = &k210_pinmux_ops, |
| .confops = &k210_pinconf_ops, |
| .custom_params = k210_pinconf_custom_params, |
| .num_custom_params = ARRAY_SIZE(k210_pinconf_custom_params), |
| }; |
| |
| static void k210_fpioa_init_ties(struct k210_fpioa_data *pdata) |
| { |
| struct k210_fpioa __iomem *fpioa = pdata->fpioa; |
| u32 val; |
| int i, j; |
| |
| dev_dbg(pdata->dev, "Init pin ties\n"); |
| |
| /* Init pin functions input ties */ |
| for (i = 0; i < ARRAY_SIZE(fpioa->tie_en); i++) { |
| val = 0; |
| for (j = 0; j < 32; j++) { |
| if (k210_pcf_infos[i * 32 + j].mode_id == |
| K210_PC_DEFAULT_IN_TIE) { |
| dev_dbg(pdata->dev, |
| "tie_en function %d (%s)\n", |
| i * 32 + j, |
| k210_pcf_infos[i * 32 + j].name); |
| val |= BIT(j); |
| } |
| } |
| |
| /* Set value before enable */ |
| writel(val, &fpioa->tie_val[i]); |
| writel(val, &fpioa->tie_en[i]); |
| } |
| } |
| |
| static int k210_fpioa_probe(struct platform_device *pdev) |
| { |
| struct device *dev = &pdev->dev; |
| struct device_node *np = dev->of_node; |
| struct k210_fpioa_data *pdata; |
| int ret; |
| |
| dev_info(dev, "K210 FPIOA pin controller\n"); |
| |
| pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); |
| if (!pdata) |
| return -ENOMEM; |
| |
| pdata->dev = dev; |
| platform_set_drvdata(pdev, pdata); |
| |
| pdata->fpioa = devm_platform_ioremap_resource(pdev, 0); |
| if (IS_ERR(pdata->fpioa)) |
| return PTR_ERR(pdata->fpioa); |
| |
| pdata->clk = devm_clk_get(dev, "ref"); |
| if (IS_ERR(pdata->clk)) |
| return PTR_ERR(pdata->clk); |
| |
| ret = clk_prepare_enable(pdata->clk); |
| if (ret) |
| return ret; |
| |
| pdata->pclk = devm_clk_get_optional(dev, "pclk"); |
| if (!IS_ERR(pdata->pclk)) { |
| ret = clk_prepare_enable(pdata->pclk); |
| if (ret) |
| goto disable_clk; |
| } |
| |
| pdata->sysctl_map = |
| syscon_regmap_lookup_by_phandle_args(np, |
| "canaan,k210-sysctl-power", |
| 1, &pdata->power_offset); |
| if (IS_ERR(pdata->sysctl_map)) { |
| ret = PTR_ERR(pdata->sysctl_map); |
| goto disable_pclk; |
| } |
| |
| k210_fpioa_init_ties(pdata); |
| |
| pdata->pctl = pinctrl_register(&k210_pinctrl_desc, dev, (void *)pdata); |
| if (IS_ERR(pdata->pctl)) { |
| ret = PTR_ERR(pdata->pctl); |
| goto disable_pclk; |
| } |
| |
| return 0; |
| |
| disable_pclk: |
| clk_disable_unprepare(pdata->pclk); |
| disable_clk: |
| clk_disable_unprepare(pdata->clk); |
| |
| return ret; |
| } |
| |
| static const struct of_device_id k210_fpioa_dt_ids[] = { |
| { .compatible = "canaan,k210-fpioa" }, |
| { /* sentinel */ }, |
| }; |
| |
| static struct platform_driver k210_fpioa_driver = { |
| .probe = k210_fpioa_probe, |
| .driver = { |
| .name = "k210-fpioa", |
| .of_match_table = k210_fpioa_dt_ids, |
| }, |
| }; |
| builtin_platform_driver(k210_fpioa_driver); |