Add pinctrl driver for the T-Head TH1520 RISC-V SoC. Signed-off-by: Emil Renner Berthing <emil.renner.berthing@xxxxxxxxxxxxx> --- MAINTAINERS | 1 + drivers/pinctrl/Kconfig | 9 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-th1520.c | 796 +++++++++++++++++++++++++++++++ 4 files changed, 807 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-th1520.c diff --git a/MAINTAINERS b/MAINTAINERS index e2c6187a3ac8..7420914c2d77 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18616,6 +18616,7 @@ M: Fu Wei <wefu@xxxxxxxxxx> L: linux-riscv@xxxxxxxxxxxxxxxxxxx S: Maintained F: arch/riscv/boot/dts/thead/ +F: drivers/pinctrl/pinctrl-th1520.c RNBD BLOCK DRIVERS M: Md. Haris Iqbal <haris.iqbal@xxxxxxxxx> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 1de4e1edede0..44426fe0f848 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -469,6 +469,15 @@ config PINCTRL_TB10X depends on OF && ARC_PLAT_TB10X select GPIOLIB +config PINCTRL_TH1520 + tristate "Pinctrl driver for the T-Head TH1520 SoC" + depends on ARCH_THEAD || COMPILE_TEST + select GENERIC_PINMUX_FUNCTIONS + select GENERIC_PINCONF + select PINMUX + help + This selects the pinctrl driver for T-Head TH1520 RISC-V SoC. + config PINCTRL_ZYNQ bool "Pinctrl driver for Xilinx Zynq" depends on ARCH_ZYNQ diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 37575deb7a69..74350d667add 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o obj-$(CONFIG_PINCTRL_SX150X) += pinctrl-sx150x.o obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o +obj-$(CONFIG_PINCTRL_TH1520) += pinctrl-th1520.o obj-$(CONFIG_PINCTRL_ZYNQMP) += pinctrl-zynqmp.o obj-$(CONFIG_PINCTRL_ZYNQ) += pinctrl-zynq.o diff --git a/drivers/pinctrl/pinctrl-th1520.c b/drivers/pinctrl/pinctrl-th1520.c new file mode 100644 index 000000000000..1efb6ec268e6 --- /dev/null +++ b/drivers/pinctrl/pinctrl-th1520.c @@ -0,0 +1,796 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Pinctrl driver for the T-Head TH1520 SoC + * + * Copyright (C) 2023 Emil Renner Berthing <emil.renner.berthing@xxxxxxxxxxxxx> + */ + +#include <linux/io.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/spinlock.h> + +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> + +#include "core.h" +#include "pinmux.h" +#include "pinconf.h" + +#define TH1520_PADCFG_IE BIT(9) +#define TH1520_PADCFG_SL BIT(8) +#define TH1520_PADCFG_ST BIT(7) +#define TH1520_PADCFG_SPU BIT(6) +#define TH1520_PADCFG_PS BIT(5) +#define TH1520_PADCFG_PE BIT(4) +#define TH1520_PADCFG_DS GENMASK(3, 0) + +#define TH1520_PULL_DOWN_OHM 44000 /* typ. 44kOhm */ +#define TH1520_PULL_UP_OHM 48000 /* typ. 48kOhm */ +#define TH1520_PULL_STRONG_OHM 2100 /* typ. 2.1kOhm */ + +#define TH1520_FLAG_NO_PADCFG BIT(0) +#define TH1520_FLAG_NO_MUXCFG BIT(1) + +struct th1520_padgroup { + const char *name; + const struct pinctrl_pin_desc *pins; + unsigned int npins; +}; + +struct th1520_pinctrl { + struct pinctrl_desc desc; + struct mutex mutex; /* serialize adding functions */ + raw_spinlock_t lock; /* serialize register access */ + void __iomem *base; + struct pinctrl_dev *pctl; +}; + +static void __iomem *th1520_padcfg(struct th1520_pinctrl *thp, + unsigned int pin) +{ + return thp->base + 4 * (pin / 2); +} + +static unsigned int th1520_padcfg_shift(unsigned int pin) +{ + return 16 * (pin & 0x1U); +} + +static void __iomem *th1520_muxcfg(struct th1520_pinctrl *thp, + unsigned int pin) +{ + return thp->base + 0x400 + 4 * (pin / 8); +} + +static unsigned int th1520_muxcfg_shift(unsigned int pin) +{ + return 4 * (pin & 0x7U); +} + +static const struct pinctrl_pin_desc th1520_group1_pins[] = { + { .number = 0, .name = "OSC_CLK_IN", /* TODO: handle special pad config */ + .drv_data = (void *)(TH1520_FLAG_NO_PADCFG | TH1520_FLAG_NO_MUXCFG) }, + { .number = 1, .name = "OSC_CLK_OUT", + .drv_data = (void *)(TH1520_FLAG_NO_PADCFG | TH1520_FLAG_NO_MUXCFG) }, + { .number = 2, .name = "SYS_RST_N", + .drv_data = (void *)(TH1520_FLAG_NO_PADCFG | TH1520_FLAG_NO_MUXCFG) }, + { .number = 3, .name = "RTC_CLK_IN", /* TODO: handle special pad config */ + .drv_data = (void *)(TH1520_FLAG_NO_PADCFG | TH1520_FLAG_NO_MUXCFG) }, + { .number = 4, .name = "RTC_CLK_OUT", + .drv_data = (void *)(TH1520_FLAG_NO_PADCFG | TH1520_FLAG_NO_MUXCFG) }, + /* + * skip number 5 so we can calculate register + * offsets and shifts from the pin number + */ + { .number = 6, .name = "TEST_MODE", + .drv_data = (void *)(TH1520_FLAG_NO_PADCFG | TH1520_FLAG_NO_MUXCFG) }, + { .number = 7, .name = "DEBUG_MODE", + .drv_data = (void *)TH1520_FLAG_NO_PADCFG }, + { .number = 8, .name = "POR_SEL", + .drv_data = (void *)(TH1520_FLAG_NO_PADCFG | TH1520_FLAG_NO_MUXCFG) }, + PINCTRL_PIN(9, "I2C_AON_SCL"), + PINCTRL_PIN(10, "I2C_AON_SDA"), + PINCTRL_PIN(11, "CPU_JTG_TCLK"), + PINCTRL_PIN(12, "CPU_JTG_TMS"), + PINCTRL_PIN(13, "CPU_JTG_TDI"), + PINCTRL_PIN(14, "CPU_JTG_TDO"), + PINCTRL_PIN(15, "CPU_JTG_TRST"), + PINCTRL_PIN(16, "AOGPIO_7"), + PINCTRL_PIN(17, "AOGPIO_8"), + PINCTRL_PIN(18, "AOGPIO_9"), + PINCTRL_PIN(19, "AOGPIO_10"), + PINCTRL_PIN(20, "AOGPIO_11"), + PINCTRL_PIN(21, "AOGPIO_12"), + PINCTRL_PIN(22, "AOGPIO_13"), + PINCTRL_PIN(23, "AOGPIO_14"), + PINCTRL_PIN(24, "AOGPIO_15"), + PINCTRL_PIN(25, "AUDIO_PA0"), + PINCTRL_PIN(26, "AUDIO_PA1"), + PINCTRL_PIN(27, "AUDIO_PA2"), + PINCTRL_PIN(28, "AUDIO_PA3"), + PINCTRL_PIN(29, "AUDIO_PA4"), + PINCTRL_PIN(30, "AUDIO_PA5"), + PINCTRL_PIN(31, "AUDIO_PA6"), + PINCTRL_PIN(32, "AUDIO_PA7"), + PINCTRL_PIN(33, "AUDIO_PA8"), + PINCTRL_PIN(34, "AUDIO_PA9"), + PINCTRL_PIN(35, "AUDIO_PA10"), + PINCTRL_PIN(36, "AUDIO_PA11"), + PINCTRL_PIN(37, "AUDIO_PA12"), + PINCTRL_PIN(38, "AUDIO_PA13"), + PINCTRL_PIN(39, "AUDIO_PA14"), + PINCTRL_PIN(40, "AUDIO_PA15"), + PINCTRL_PIN(41, "AUDIO_PA16"), + PINCTRL_PIN(42, "AUDIO_PA17"), + PINCTRL_PIN(43, "AUDIO_PA27"), + PINCTRL_PIN(44, "AUDIO_PA28"), + PINCTRL_PIN(45, "AUDIO_PA29"), + PINCTRL_PIN(46, "AUDIO_PA30"), +}; + +static const struct th1520_padgroup th1520_group1 = { + .name = "th1520-group1", + .pins = th1520_group1_pins, + .npins = ARRAY_SIZE(th1520_group1_pins), +}; + +static const struct pinctrl_pin_desc th1520_group2_pins[] = { + PINCTRL_PIN(0, "QSPI1_SCLK"), + PINCTRL_PIN(1, "QSPI1_CSN0"), + PINCTRL_PIN(2, "QSPI1_D0_MOSI"), + PINCTRL_PIN(3, "QSPI1_D1_MISO"), + PINCTRL_PIN(4, "QSPI1_D2_WP"), + PINCTRL_PIN(5, "QSPI1_D3_HOLD"), + PINCTRL_PIN(6, "I2C0_SCL"), + PINCTRL_PIN(7, "I2C0_SDA"), + PINCTRL_PIN(8, "I2C1_SCL"), + PINCTRL_PIN(9, "I2C1_SDA"), + PINCTRL_PIN(10, "UART1_TXD"), + PINCTRL_PIN(11, "UART1_RXD"), + PINCTRL_PIN(12, "UART4_TXD"), + PINCTRL_PIN(13, "UART4_RXD"), + PINCTRL_PIN(14, "UART4_CTSN"), + PINCTRL_PIN(15, "UART4_RTSN"), + PINCTRL_PIN(16, "UART3_TXD"), + PINCTRL_PIN(17, "UART3_RXD"), + PINCTRL_PIN(18, "GPIO0_18"), + PINCTRL_PIN(19, "GPIO0_19"), + PINCTRL_PIN(20, "GPIO0_20"), + PINCTRL_PIN(21, "GPIO0_21"), + PINCTRL_PIN(22, "GPIO0_22"), + PINCTRL_PIN(23, "GPIO0_23"), + PINCTRL_PIN(24, "GPIO0_24"), + PINCTRL_PIN(25, "GPIO0_25"), + PINCTRL_PIN(26, "GPIO0_26"), + PINCTRL_PIN(27, "GPIO0_27"), + PINCTRL_PIN(28, "GPIO0_28"), + PINCTRL_PIN(29, "GPIO0_29"), + PINCTRL_PIN(30, "GPIO0_30"), + PINCTRL_PIN(31, "GPIO0_31"), + PINCTRL_PIN(32, "GPIO1_0"), + PINCTRL_PIN(33, "GPIO1_1"), + PINCTRL_PIN(34, "GPIO1_2"), + PINCTRL_PIN(35, "GPIO1_3"), + PINCTRL_PIN(36, "GPIO1_4"), + PINCTRL_PIN(37, "GPIO1_5"), + PINCTRL_PIN(38, "GPIO1_6"), + PINCTRL_PIN(39, "GPIO1_7"), + PINCTRL_PIN(40, "GPIO1_8"), + PINCTRL_PIN(41, "GPIO1_9"), + PINCTRL_PIN(42, "GPIO1_10"), + PINCTRL_PIN(43, "GPIO1_11"), + PINCTRL_PIN(44, "GPIO1_12"), + PINCTRL_PIN(45, "GPIO1_13"), + PINCTRL_PIN(46, "GPIO1_14"), + PINCTRL_PIN(47, "GPIO1_15"), + PINCTRL_PIN(48, "GPIO1_16"), + PINCTRL_PIN(49, "CLK_OUT_0"), + PINCTRL_PIN(50, "CLK_OUT_1"), + PINCTRL_PIN(51, "CLK_OUT_2"), + PINCTRL_PIN(52, "CLK_OUT_3"), + PINCTRL_PIN(53, "GPIO1_21"), + PINCTRL_PIN(54, "GPIO1_22"), + PINCTRL_PIN(55, "GPIO1_23"), + PINCTRL_PIN(56, "GPIO1_24"), + PINCTRL_PIN(57, "GPIO1_25"), + PINCTRL_PIN(58, "GPIO1_26"), + PINCTRL_PIN(59, "GPIO1_27"), + PINCTRL_PIN(60, "GPIO1_28"), + PINCTRL_PIN(61, "GPIO1_29"), + PINCTRL_PIN(62, "GPIO1_30"), +}; + +static const struct th1520_padgroup th1520_group2 = { + .name = "th1520-group2", + .pins = th1520_group2_pins, + .npins = ARRAY_SIZE(th1520_group2_pins), +}; + +static const struct pinctrl_pin_desc th1520_group3_pins[] = { + PINCTRL_PIN(0, "UART0_TXD"), + PINCTRL_PIN(1, "UART0_RXD"), + PINCTRL_PIN(2, "QSPI0_SCLK"), + PINCTRL_PIN(3, "QSPI0_CSN0"), + PINCTRL_PIN(4, "QSPI0_CSN1"), + PINCTRL_PIN(5, "QSPI0_D0_MOSI"), + PINCTRL_PIN(6, "QSPI0_D1_MISO"), + PINCTRL_PIN(7, "QSPI0_D2_WP"), + PINCTRL_PIN(8, "QSPI1_D3_HOLD"), + PINCTRL_PIN(9, "I2C2_SCL"), + PINCTRL_PIN(10, "I2C2_SDA"), + PINCTRL_PIN(11, "I2C3_SCL"), + PINCTRL_PIN(12, "I2C3_SDA"), + PINCTRL_PIN(13, "GPIO2_13"), + PINCTRL_PIN(14, "SPI_SCLK"), + PINCTRL_PIN(15, "SPI_CSN"), + PINCTRL_PIN(16, "SPI_MOSI"), + PINCTRL_PIN(17, "SPI_MISO"), + PINCTRL_PIN(18, "GPIO2_18"), + PINCTRL_PIN(19, "GPIO2_19"), + PINCTRL_PIN(20, "GPIO2_20"), + PINCTRL_PIN(21, "GPIO2_21"), + PINCTRL_PIN(22, "GPIO2_22"), + PINCTRL_PIN(23, "GPIO2_23"), + PINCTRL_PIN(24, "GPIO2_24"), + PINCTRL_PIN(25, "GPIO2_25"), + PINCTRL_PIN(26, "SDIO0_WPRTN"), + PINCTRL_PIN(27, "SDIO0_DETN"), + PINCTRL_PIN(28, "SDIO1_WPRTN"), + PINCTRL_PIN(29, "SDIO1_DETN"), + PINCTRL_PIN(30, "GPIO2_30"), + PINCTRL_PIN(31, "GPIO2_31"), + PINCTRL_PIN(32, "GPIO3_0"), + PINCTRL_PIN(33, "GPIO3_1"), + PINCTRL_PIN(34, "GPIO3_2"), + PINCTRL_PIN(35, "GPIO3_3"), + PINCTRL_PIN(36, "HDMI_SCL"), + PINCTRL_PIN(37, "HDMI_SDA"), + PINCTRL_PIN(38, "HDMI_CEC"), + PINCTRL_PIN(39, "GMAC0_TX_CLK"), + PINCTRL_PIN(40, "GMAC0_RX_CLK"), + PINCTRL_PIN(41, "GMAC0_TXEN"), + PINCTRL_PIN(42, "GMAC0_TXD0"), + PINCTRL_PIN(43, "GMAC0_TXD1"), + PINCTRL_PIN(44, "GMAC0_TXD2"), + PINCTRL_PIN(45, "GMAC0_TXD3"), + PINCTRL_PIN(46, "GMAC0_RXDV"), + PINCTRL_PIN(47, "GMAC0_RXD0"), + PINCTRL_PIN(48, "GMAC0_RXD1"), + PINCTRL_PIN(49, "GMAC0_RXD2"), + PINCTRL_PIN(50, "GMAC0_RXD3"), + PINCTRL_PIN(51, "GMAC0_MDC"), + PINCTRL_PIN(52, "GMAC0_MDIO"), + PINCTRL_PIN(53, "GMAC0_COL"), + PINCTRL_PIN(54, "GMAC0_CRS"), +}; + +static const struct th1520_padgroup th1520_group3 = { + .name = "th1520-group3", + .pins = th1520_group3_pins, + .npins = ARRAY_SIZE(th1520_group3_pins), +}; + +static int th1520_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + + return thp->desc.npins; +} + +static const char *th1520_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned int gsel) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + + return thp->desc.pins[gsel].name; +} + +static int th1520_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int gsel, + const unsigned int **pins, + unsigned int *npins) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + + *pins = &thp->desc.pins[gsel].number; + *npins = 1; + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static void th1520_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned int pin) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + void __iomem *padcfg = th1520_padcfg(thp, pin); + void __iomem *muxcfg = th1520_muxcfg(thp, pin); + unsigned long flags; + u32 pad; + u32 mux; + + raw_spin_lock_irqsave(&thp->lock, flags); + pad = readl_relaxed(padcfg); + mux = readl_relaxed(muxcfg); + raw_spin_unlock_irqrestore(&thp->lock, flags); + + seq_printf(s, "[PADCFG_%03u:0x%x=0x%07x MUXCFG_%03u:0x%x=0x%08x]", + 1 + pin / 2, 0x000 + 4 * (pin / 2), pad, + 1 + pin / 8, 0x400 + 4 * (pin / 8), mux); +} +#else +#define th1520_pin_dbg_show NULL +#endif + +static void th1520_pinctrl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned int nmaps) +{ + unsigned long *seen = NULL; + unsigned int i; + + for (i = 0; i < nmaps; i++) { + if (map[i].type == PIN_MAP_TYPE_CONFIGS_PIN && + map[i].data.configs.configs != seen) { + seen = map[i].data.configs.configs; + kfree(seen); + } + } + + kfree(map); +} + +static int th1520_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **maps, + unsigned int *num_maps) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + struct device_node *child; + struct pinctrl_map *map; + unsigned long *configs; + unsigned int nconfigs; + unsigned int nmaps; + int ret; + + nmaps = 0; + for_each_available_child_of_node(np, child) { + int npins = of_property_count_strings(child, "pins"); + + if (npins <= 0) { + of_node_put(child); + return dev_err_probe(thp->pctl->dev, -EINVAL, + "no pins selected for %pOFn.%pOFn\n", + np, child); + } + nmaps += npins; + if (of_property_present(child, "function")) + nmaps += npins; + } + + map = kcalloc(nmaps, sizeof(*map), GFP_KERNEL); + if (!map) + return -ENOMEM; + + nmaps = 0; + mutex_lock(&thp->mutex); + for_each_available_child_of_node(np, child) { + unsigned int rollback = nmaps; + struct property *prop; + const char *funcname; + const char **pgnames; + const char *pinname; + uintptr_t muxdata; + int npins; + + ret = pinconf_generic_parse_dt_config(child, pctldev, &configs, &nconfigs); + if (ret) { + dev_err(thp->pctl->dev, "error parsing pin config of group %pOFn.%pOFn\n", + np, child); + goto put_child; + } + + if (!of_property_read_string(child, "function", &funcname)) { + if (funcname[0] < '0' || funcname[0] > '5' || funcname[1]) { + ret = -EINVAL; + dev_err(thp->pctl->dev, "%pOFn.%pOFn: invalid function '%s'\n", + np, child, funcname); + goto free_configs; + } + + muxdata = funcname[0] - '0'; + funcname = devm_kasprintf(thp->pctl->dev, GFP_KERNEL, "%pOFn.%pOFn", + np, child); + if (!funcname) { + ret = -ENOMEM; + goto free_configs; + } + + npins = of_property_count_strings(child, "pins"); + pgnames = devm_kcalloc(thp->pctl->dev, npins, sizeof(*pgnames), GFP_KERNEL); + if (!pgnames) { + ret = -ENOMEM; + goto free_configs; + } + } else { + funcname = NULL; + } + + npins = 0; + of_property_for_each_string(child, "pins", prop, pinname) { + unsigned int i; + + for (i = 0; i < thp->desc.npins; i++) { + if (!strcmp(pinname, thp->desc.pins[i].name)) + break; + } + if (i == thp->desc.npins) { + nmaps = rollback; + dev_err(thp->pctl->dev, "%pOFn.%pOFn: unknown pin '%s'\n", + np, child, pinname); + goto free_configs; + } + + if (nconfigs) { + map[nmaps].type = PIN_MAP_TYPE_CONFIGS_PIN; + map[nmaps].data.configs.group_or_pin = thp->desc.pins[i].name; + map[nmaps].data.configs.configs = configs; + map[nmaps].data.configs.num_configs = nconfigs; + nmaps += 1; + } + if (funcname) { + pgnames[npins++] = thp->desc.pins[i].name; + map[nmaps].type = PIN_MAP_TYPE_MUX_GROUP; + map[nmaps].data.mux.function = funcname; + map[nmaps].data.mux.group = thp->desc.pins[i].name; + nmaps += 1; + } + } + + if (funcname) { + ret = pinmux_generic_add_function(pctldev, funcname, pgnames, + npins, (void *)muxdata); + if (ret < 0) { + dev_err(thp->pctl->dev, "error adding function %s\n", funcname); + goto put_child; + } + } + } + + *maps = map; + *num_maps = nmaps; + mutex_unlock(&thp->mutex); + return 0; + +free_configs: + kfree(configs); +put_child: + of_node_put(child); + th1520_pinctrl_dt_free_map(pctldev, map, nmaps); + mutex_unlock(&thp->mutex); + return ret; +} + +static const struct pinctrl_ops th1520_pinctrl_ops = { + .get_groups_count = th1520_pinctrl_get_groups_count, + .get_group_name = th1520_pinctrl_get_group_name, + .get_group_pins = th1520_pinctrl_get_group_pins, + .pin_dbg_show = th1520_pin_dbg_show, + .dt_node_to_map = th1520_pinctrl_dt_node_to_map, + .dt_free_map = th1520_pinctrl_dt_free_map, +}; + +static int th1520_pinmux_set_mux(struct pinctrl_dev *pctldev, + unsigned int fsel, unsigned int gsel) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + const struct function_desc *func = pinmux_generic_get_function(pctldev, fsel); + unsigned int pin = thp->desc.pins[gsel].number; + void __iomem *muxcfg = th1520_muxcfg(thp, pin); + unsigned int shift = th1520_muxcfg_shift(pin); + unsigned long flags; + u32 mask; + u32 value; + + if (!func || (uintptr_t)thp->desc.pins[gsel].drv_data & TH1520_FLAG_NO_MUXCFG) + return -EINVAL; + + mask = 0xfU << shift; + value = ((uintptr_t)func->data & 0xfU) << shift; + + raw_spin_lock_irqsave(&thp->lock, flags); + value |= readl_relaxed(muxcfg) & ~mask; + writel_relaxed(value, muxcfg); + raw_spin_unlock_irqrestore(&thp->lock, flags); + + return 0; +} + +static const struct pinmux_ops th1520_pinmux_ops = { + .get_functions_count = pinmux_generic_get_function_count, + .get_function_name = pinmux_generic_get_function_name, + .get_function_groups = pinmux_generic_get_function_groups, + .set_mux = th1520_pinmux_set_mux, + .strict = true, +}; + +static const u8 th1520_drive_strength_in_mA[16] = { + 1, 2, 3, 5, 7, 8, 10, 12, 13, 15, 16, 18, 20, 21, 23, 25, +}; + +static u16 th1520_drive_strength_from_mA(u32 arg) +{ + u16 v; + + for (v = 0; v < ARRAY_SIZE(th1520_drive_strength_in_mA) - 1; v++) { + if (arg <= th1520_drive_strength_in_mA[v]) + break; + } + + return v; +} + +static int th1520_padcfg_rmw(struct th1520_pinctrl *thp, unsigned int pin, + u16 _mask, u16 _value) +{ + void __iomem *padcfg = th1520_padcfg(thp, pin); + unsigned int shift = th1520_padcfg_shift(pin); + u32 mask = (u32)_mask << shift; + u32 value = (u32)_value << shift; + unsigned long flags; + + raw_spin_lock_irqsave(&thp->lock, flags); + value |= readl_relaxed(padcfg) & ~mask; + writel_relaxed(value, padcfg); + raw_spin_unlock_irqrestore(&thp->lock, flags); + return 0; +} + +#define PIN_CONFIG_THEAD_STRONG_PULL_UP (PIN_CONFIG_END + 1) +static const struct pinconf_generic_params th1520_pinconf_custom_params[] = { + { "thead,strong-pull-up", PIN_CONFIG_THEAD_STRONG_PULL_UP, 1 }, +}; + +#ifdef CONFIG_DEBUG_FS +static const struct pin_config_item th1520_pinconf_custom_conf_items[] = { + PCONFDUMP(PIN_CONFIG_THEAD_STRONG_PULL_UP, "input bias strong pull-up", "ohms", true), +}; +static_assert(ARRAY_SIZE(th1520_pinconf_custom_conf_items) == + ARRAY_SIZE(th1520_pinconf_custom_params)); +#else +#define th1520_pinconf_custom_conf_items NULL +#endif + +static int th1520_pinconf_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + const struct pin_desc *desc = pin_desc_get(pctldev, pin); + bool enabled; + int param; + u32 value; + u32 arg; + + if ((uintptr_t)desc->drv_data & TH1520_FLAG_NO_PADCFG) + return -ENOTSUPP; + + value = readl_relaxed(th1520_padcfg(thp, pin)); + value = (value >> th1520_padcfg_shift(pin)) & 0x3ffU; + + param = pinconf_to_config_param(*config); + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + enabled = !(value & TH1520_PADCFG_PE); + arg = 0; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + enabled = (value & (TH1520_PADCFG_PE | TH1520_PADCFG_PS)) == + TH1520_PADCFG_PE; + arg = enabled ? TH1520_PULL_DOWN_OHM : 0; + break; + case PIN_CONFIG_BIAS_PULL_UP: + enabled = (value & (TH1520_PADCFG_PE | TH1520_PADCFG_PS)) == + (TH1520_PADCFG_PE | TH1520_PADCFG_PS); + arg = enabled ? TH1520_PULL_UP_OHM : 0; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + enabled = true; + arg = th1520_drive_strength_in_mA[value & TH1520_PADCFG_DS]; + break; + case PIN_CONFIG_INPUT_ENABLE: + enabled = value & TH1520_PADCFG_IE; + arg = enabled; + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + enabled = value & TH1520_PADCFG_ST; + arg = enabled; + break; + case PIN_CONFIG_SLEW_RATE: + enabled = value & TH1520_PADCFG_SL; + arg = enabled; + break; + case PIN_CONFIG_THEAD_STRONG_PULL_UP: + enabled = value & TH1520_PADCFG_SPU; + arg = enabled ? TH1520_PULL_STRONG_OHM : 0; + break; + default: + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + return enabled ? 0 : -EINVAL; +} + +static int th1520_pinconf_group_get(struct pinctrl_dev *pctldev, + unsigned int gsel, unsigned long *config) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + unsigned int pin = thp->desc.pins[gsel].number; + + return th1520_pinconf_get(pctldev, pin, config); +} + +static int th1520_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int num_configs) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + unsigned int i; + u16 mask = 0; + u16 value = 0; + + for (i = 0; i < num_configs; i++) { + int param = pinconf_to_config_param(configs[i]); + u32 arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + mask |= TH1520_PADCFG_PE; + value &= ~TH1520_PADCFG_PE; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + if (arg == 0) + return -ENOTSUPP; + mask |= TH1520_PADCFG_PS; + value &= ~TH1520_PADCFG_PS; + break; + case PIN_CONFIG_BIAS_PULL_UP: + if (arg == 0) + return -ENOTSUPP; + mask |= TH1520_PADCFG_PS; + value |= TH1520_PADCFG_PS; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + mask |= TH1520_PADCFG_DS; + value = (value & ~TH1520_PADCFG_DS) | + th1520_drive_strength_from_mA(arg); + break; + case PIN_CONFIG_INPUT_ENABLE: + mask |= TH1520_PADCFG_IE; + if (arg) + value |= TH1520_PADCFG_IE; + else + value &= ~TH1520_PADCFG_IE; + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + mask |= TH1520_PADCFG_ST; + if (arg) + value |= TH1520_PADCFG_ST; + else + value &= ~TH1520_PADCFG_ST; + break; + case PIN_CONFIG_SLEW_RATE: + mask |= TH1520_PADCFG_SL; + if (arg) + value |= TH1520_PADCFG_SL; + else + value &= ~TH1520_PADCFG_SL; + break; + case PIN_CONFIG_THEAD_STRONG_PULL_UP: + mask |= TH1520_PADCFG_SPU; + if (arg) + value |= TH1520_PADCFG_SPU; + else + value &= ~TH1520_PADCFG_SPU; + break; + default: + return -ENOTSUPP; + } + } + + return th1520_padcfg_rmw(thp, pin, mask, value); +} + +static int th1520_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned int gsel, + unsigned long *configs, + unsigned int num_configs) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + unsigned int pin = thp->desc.pins[gsel].number; + + return th1520_pinconf_set(pctldev, pin, configs, num_configs); +} + +#ifdef CONFIG_DEBUG_FS +static void th1520_pinconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned int pin) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + u32 value = readl_relaxed(th1520_padcfg(thp, pin)); + + value = (value >> th1520_padcfg_shift(pin)) & 0x3ffU; + + seq_printf(s, " [0x%03x]", value); +} +#else +#define th1520_pinconf_dbg_show NULL +#endif + +static const struct pinconf_ops th1520_pinconf_ops = { + .pin_config_get = th1520_pinconf_get, + .pin_config_group_get = th1520_pinconf_group_get, + .pin_config_set = th1520_pinconf_set, + .pin_config_group_set = th1520_pinconf_group_set, + .pin_config_dbg_show = th1520_pinconf_dbg_show, + .is_generic = true, +}; + +static int th1520_pinctrl_probe(struct platform_device *pdev) +{ + const struct th1520_padgroup *group = device_get_match_data(&pdev->dev); + struct th1520_pinctrl *thp; + int ret; + + thp = devm_kzalloc(&pdev->dev, sizeof(*thp), GFP_KERNEL); + if (!thp) + return -ENOMEM; + + thp->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(thp->base)) + return PTR_ERR(thp->base); + + thp->desc.name = group->name; + thp->desc.pins = group->pins; + thp->desc.npins = group->npins; + thp->desc.pctlops = &th1520_pinctrl_ops; + thp->desc.pmxops = &th1520_pinmux_ops; + thp->desc.confops = &th1520_pinconf_ops; + thp->desc.owner = THIS_MODULE; + thp->desc.num_custom_params = ARRAY_SIZE(th1520_pinconf_custom_params); + thp->desc.custom_params = th1520_pinconf_custom_params; + thp->desc.custom_conf_items = th1520_pinconf_custom_conf_items; + mutex_init(&thp->mutex); + raw_spin_lock_init(&thp->lock); + + ret = devm_pinctrl_register_and_init(&pdev->dev, &thp->desc, thp, &thp->pctl); + if (ret) + return dev_err_probe(&pdev->dev, ret, "could not register pinctrl driver\n"); + + return pinctrl_enable(thp->pctl); +} + +static const struct of_device_id th1520_pinctrl_of_match[] = { + { .compatible = "thead,th1520-group1-pinctrl", .data = &th1520_group1 }, + { .compatible = "thead,th1520-group2-pinctrl", .data = &th1520_group2 }, + { .compatible = "thead,th1520-group3-pinctrl", .data = &th1520_group3 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, th1520_pinctrl_of_match); + +static struct platform_driver th1520_pinctrl_driver = { + .probe = th1520_pinctrl_probe, + .driver = { + .name = "pinctrl-th1520", + .of_match_table = th1520_pinctrl_of_match, + }, +}; +module_platform_driver(th1520_pinctrl_driver); + +MODULE_DESCRIPTION("Pinctrl driver for the T-Head TH1520 SoC"); +MODULE_AUTHOR("Emil Renner Berthing <emil.renner.berthing@xxxxxxxxxxxxx>"); +MODULE_LICENSE("GPL"); -- 2.40.1