From nobody Mon Feb 9 07:20:26 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6B596357A28; Mon, 19 Jan 2026 11:04:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768820678; cv=none; b=qhgWNAuttKFaSHY84YCnJs9MW/a0Vg0wY+E5VdtvpdyazXr7qbh4dmk9yil4gAM5u4pK8oAMxT43rOxjqJ00Xo2IG3YYcyRA0GIu+QnZpvm//S0kBNZe6mfY8GLtGgUfDvkHHjNlULDm57Ud3GpSb6vbtxGAtyS/fPO0rdv7rvI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768820678; c=relaxed/simple; bh=EXXJNVSyddJ8THv25lw3tM2hNTWk2Yhb7QVTj2leCoY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jwGeL6ugAfcNahiUgWx8pdmydBjImbk8BfWEkPPbgsBo14dqIyOerVSTNelJwM9/kHqzGk9XNNfuLU+dXq1AdSEOq0djrNjAIrOt5gcogceHF2oD0aTXiXMgblVVlt78WxXe07rewWI1KM96Ayh67esmjvgm4xtxM4oQYWYft9c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FBF1HyO+; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="FBF1HyO+" Received: by smtp.kernel.org (Postfix) with ESMTPSA id EF3B3C116C6; Mon, 19 Jan 2026 11:04:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768820676; bh=EXXJNVSyddJ8THv25lw3tM2hNTWk2Yhb7QVTj2leCoY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FBF1HyO+zPrFUZFaZLTPhnq2g33doBGx25oe7HhvsqYL2QkXw6s7Kfurd4bn3SBep 11vjTA4fYVMSQaEwzx8k2JsgyxHn9WLr6iouCRrwULnABzTLWFABRZ5cT0QFz70+mN r5PlVFgVk9cQYA1nqe3IoQQzMVmhb4uh/w4bLPj2tn1UUUaXilLp1FmUGEz0TCDffS 62neQwLdqpwavdmalCZau293BIgoJsELp48dC7Jt6prUWzF4ITYvj8FR8bZkvePFgr ApVLlFi3T2ips8vhV1c3kvsENog3iL9Z20fd9LbWZp5c2bLhkP2vsHkrQCdADCJyyR ECF3JbusVy44A== From: Conor Dooley To: linusw@kernel.org Cc: conor@kernel.org, Conor Dooley , Linus Walleij , Rob Herring , Krzysztof Kozlowski , linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, devicetree@vger.kernel.org, Valentina.FernandezAlanis@microchip.com Subject: [PATCH v3 4/6] pinctrl: add polarfire soc mssio pinctrl driver Date: Mon, 19 Jan 2026 11:03:55 +0000 Message-ID: <20260119-ivory-mutiny-44ef537e53b7@spud> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260119-rearrange-germproof-3e3096cc0da4@spud> References: <20260119-rearrange-germproof-3e3096cc0da4@spud> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=22121; i=conor.dooley@microchip.com; h=from:subject:message-id; bh=iu/gIKZLOr1VNdIyD4zpGYRJApgyBM6NULoKXv0gmgY=; b=owGbwMvMwCVWscWwfUFT0iXG02pJDJl5/Bs5F6t/n8b+Q/dwYGjHpCO6KvxzrE1Ws7ws49qx4 cxqqdaojlIWBjEuBlkxRZbE230tUuv/uOxw7nkLM4eVCWQIAxenAEzkvyEjw60v9T9tON0170/K iDQvWXY5kD+xIJbnf16ug7nfax7mL4wM50NKZyWlsqodVfS4WCHxpHbWhWaDvfHus48+WVLhvOY /CwA= X-Developer-Key: i=conor.dooley@microchip.com; a=openpgp; fpr=F9ECA03CF54F12CD01F1655722E2C55B37CF380C Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Conor Dooley On Polarfire SoC, the Bank 2 and Bank 4 IOs connected to the Multiprocessor Subsystem (MSS) are controlled by IOMUX_CRs 1 through 6, which determine what function in routed to them, and MSSIO_BANK#_IO_CFG_CRs, which determine the configuration of each pin. Add a driver for this pin controller, including several custom properties that reflect aspects of the MSS's configuration. Reuse the Kconfig option for iomux0, since controlling MSSIOs without iomux0 routing a function to the MSSIOs in question is pointless, and routing a function to the MSSIOs is equally unhelpful if none of them are configured to make use of that function. Signed-off-by: Conor Dooley --- drivers/pinctrl/microchip/Kconfig | 6 +- drivers/pinctrl/microchip/Makefile | 1 + .../pinctrl/microchip/pinctrl-mpfs-mssio.c | 737 ++++++++++++++++++ 3 files changed, 741 insertions(+), 3 deletions(-) create mode 100644 drivers/pinctrl/microchip/pinctrl-mpfs-mssio.c diff --git a/drivers/pinctrl/microchip/Kconfig b/drivers/pinctrl/microchip/= Kconfig index f7536d94a8cf..b8e454ddf2b2 100644 --- a/drivers/pinctrl/microchip/Kconfig +++ b/drivers/pinctrl/microchip/Kconfig @@ -10,10 +10,10 @@ config PINCTRL_PIC64GX This selects the pinctrl driver for gpio2 on pic64gx. =20 config PINCTRL_POLARFIRE_SOC - bool "Polarfire SoC pinctrl driver" + bool "Polarfire SoC pinctrl drivers" depends on ARCH_MICROCHIP || COMPILE_TEST depends on OF - select GENERIC_PINCONF + select GENERIC_PINCTRL default y help - This selects the pinctrl driver for Microchip Polarfire SoC. + This selects the pinctrl drivers for Microchip Polarfire SoC. diff --git a/drivers/pinctrl/microchip/Makefile b/drivers/pinctrl/microchip= /Makefile index 584d48e7be3b..ab0575cd2ed1 100644 --- a/drivers/pinctrl/microchip/Makefile +++ b/drivers/pinctrl/microchip/Makefile @@ -2,3 +2,4 @@ =20 obj-$(CONFIG_PINCTRL_PIC64GX) +=3D pinctrl-pic64gx-gpio2.o obj-$(CONFIG_PINCTRL_POLARFIRE_SOC) +=3D pinctrl-mpfs-iomux0.o +obj-$(CONFIG_PINCTRL_POLARFIRE_SOC) +=3D pinctrl-mpfs-mssio.o diff --git a/drivers/pinctrl/microchip/pinctrl-mpfs-mssio.c b/drivers/pinct= rl/microchip/pinctrl-mpfs-mssio.c new file mode 100644 index 000000000000..3d5ffd6cb14b --- /dev/null +++ b/drivers/pinctrl/microchip/pinctrl-mpfs-mssio.c @@ -0,0 +1,737 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../core.h" +#include "../pinctrl-utils.h" +#include "../pinconf.h" +#include "../pinmux.h" + +#define MPFS_PINCTRL_PAD_MUX_MASK GENMASK(3, 0) + +#define MPFS_PINCTRL_IOCFG_MASK GENMASK(14, 0) +#define MPFS_PINCTRL_IBUFMD_MASK GENMASK(2, 0) +#define MPFS_PINCTRL_DRV_MASK GENMASK(6, 3) +#define MPFS_PINCTRL_CLAMP BIT(7) +#define MPFS_PINCTRL_ENHYST BIT(8) +#define MPFS_PINCTRL_LOCKDN BIT(9) +#define MPFS_PINCTRL_WPD BIT(10) +#define MPFS_PINCTRL_WPU BIT(11) +#define MPFS_PINCTRL_PULL_MASK GENMASK(11, 10) +#define MPFS_PINCTRL_LP_PERSIST_EN BIT(12) +#define MPFS_PINCTRL_LP_BYPASS_EN BIT(13) + +#define MPFS_PINCTRL_MSSIO_BANK2_CFG_CR 0x1c4 +#define MPFS_PINCTRL_MSSIO_BANK4_CFG_CR 0x1c8 +#define MPFS_PINCTRL_BANK_VOLTAGE_MASK GENMASK(19, 16) + +#define MPFS_PINCTRL_IOCFG01_REG 0x234 + +#define MPFS_PINCTRL_INTER_BANK_GAP 0x4 + +#define MPFS_PINCTRL_BANK2_START 14 + +#define MPFS_PINCTRL_LOCKDOWN (PIN_CONFIG_END + 1) +#define MPFS_PINCTRL_CLAMP_DIODE (PIN_CONFIG_END + 2) +#define MPFS_PINCTRL_IBUFMD (PIN_CONFIG_END + 3) + +struct mpfs_pinctrl_mux_config { + u8 pin; + u8 function; +}; + +struct mpfs_pinctrl { + struct pinctrl_dev *pctrl; + struct device *dev; + struct regmap *regmap; + struct regmap *sysreg_regmap; + struct mutex mutex; + struct pinctrl_desc desc; +}; + +struct mpfs_pinctrl_drive_strength { + u8 ma; + u8 val; +}; + +struct mpfs_pinctrl_bank_voltage { + u32 uv; + u8 val; +}; + +static struct mpfs_pinctrl_drive_strength mpfs_pinctrl_drive_strengths[8] = =3D { + { .ma =3D 2, .val =3D 2 }, + { .ma =3D 4, .val =3D 3 }, + { .ma =3D 6, .val =3D 4 }, + { .ma =3D 8, .val =3D 5 }, + { .ma =3D 10, .val =3D 6 }, + { .ma =3D 12, .val =3D 7 }, + { .ma =3D 16, .val =3D 10 }, + { .ma =3D 20, .val =3D 12 }, +}; +static struct mpfs_pinctrl_bank_voltage mpfs_pinctrl_bank_voltages[8] =3D { + { .uv =3D 1200000, .val =3D 0 }, + { .uv =3D 1500000, .val =3D 2 }, + { .uv =3D 1800000, .val =3D 4 }, + { .uv =3D 2500000, .val =3D 6 }, + { .uv =3D 3300000, .val =3D 8 }, + { .uv =3D 0, .val =3D 0x3f }, // pin unused +}; + +static int mpfs_pinctrl_get_drive_strength_ma(u32 drive_strength) +{ + size_t num =3D ARRAY_SIZE(mpfs_pinctrl_drive_strengths); + + for (int i =3D 0; i < num; i++) + if (drive_strength =3D=3D mpfs_pinctrl_drive_strengths[i].val) + return mpfs_pinctrl_drive_strengths[i].ma; + + return -EINVAL; +} + +static int mpfs_pinctrl_get_drive_strength_val(u32 drive_strength_ma) +{ + size_t num =3D ARRAY_SIZE(mpfs_pinctrl_drive_strengths); + + if (!drive_strength_ma) + return -EINVAL; + + for (int i =3D 0; i < num; i++) + if (drive_strength_ma <=3D mpfs_pinctrl_drive_strengths[i].ma) + return mpfs_pinctrl_drive_strengths[i].val; + + return mpfs_pinctrl_drive_strengths[num - 1].val; +} + +static int mpfs_pinctrl_get_bank_voltage_uv(u32 bank_voltage) +{ + size_t num =3D ARRAY_SIZE(mpfs_pinctrl_bank_voltages); + + for (int i =3D 0; i < num; i++) + if (bank_voltage =3D=3D mpfs_pinctrl_bank_voltages[i].val) + return mpfs_pinctrl_bank_voltages[i].uv; + + return -EINVAL; +} + +static int mpfs_pinctrl_get_bank_voltage_val(u32 bank_voltage_uv) +{ + size_t num =3D ARRAY_SIZE(mpfs_pinctrl_bank_voltages); + + for (int i =3D 0; i < num; i++) + if (bank_voltage_uv <=3D mpfs_pinctrl_bank_voltages[i].uv) + return mpfs_pinctrl_bank_voltages[i].val; + + return -EINVAL; +} + +static u32 mpfs_pinctrl_pin_to_bank_voltage(struct mpfs_pinctrl *pctrl, un= signed int pin) +{ + u32 bank_voltage, val; + + if (pin < MPFS_PINCTRL_BANK2_START) + regmap_read(pctrl->sysreg_regmap, MPFS_PINCTRL_MSSIO_BANK4_CFG_CR, &val); + else + regmap_read(pctrl->sysreg_regmap, MPFS_PINCTRL_MSSIO_BANK2_CFG_CR, &val); + + bank_voltage =3D FIELD_GET(MPFS_PINCTRL_BANK_VOLTAGE_MASK, val); + + return mpfs_pinctrl_get_bank_voltage_uv(bank_voltage); +} + +static void mpfs_pinctrl_set_bank_voltage(struct mpfs_pinctrl *pctrl, unsi= gned int pin, + u32 bank_voltage) +{ + u32 val =3D FIELD_PREP(MPFS_PINCTRL_BANK_VOLTAGE_MASK, bank_voltage); + + if (pin < MPFS_PINCTRL_BANK2_START) + regmap_assign_bits(pctrl->sysreg_regmap, MPFS_PINCTRL_MSSIO_BANK4_CFG_CR, + MPFS_PINCTRL_BANK_VOLTAGE_MASK, val); + else + regmap_assign_bits(pctrl->sysreg_regmap, MPFS_PINCTRL_MSSIO_BANK2_CFG_CR, + MPFS_PINCTRL_BANK_VOLTAGE_MASK, val); +} + +static char *mpfs_pinctrl_function_names[] =3D { + "sd", + "emmc", + "qspi", + "spi", + "usb", + "uart", + "i2c", + "can", + "mdio", + "misc", + "reserved", + "gpio", + "fabric test", + "tied-low", + "tied-high", + "tristate" +}; + +static int mpfs_pinctrl_function_map(const char *function) +{ + size_t num =3D ARRAY_SIZE(mpfs_pinctrl_function_names); + + for (int i =3D 0; i < num; i++) + if (!strcmp(function, mpfs_pinctrl_function_names[i])) + return i; + + return -EINVAL; +} + +static const struct pinconf_generic_params mpfs_pinctrl_custom_bindings[] = =3D { + { "microchip,clamp-diode", MPFS_PINCTRL_CLAMP_DIODE, 1 }, + { "microchip,ibufmd", MPFS_PINCTRL_IBUFMD, 0x0 }, +}; + +static int mpfs_pinctrl_pin_to_iomux_offset(unsigned int pin) +{ + int offset; + + switch (pin) { + case 0 ... 7: + offset =3D pin * 4; + break; + case 8 ... 13: + offset =3D (pin - 8) * 4; + break; + case 14 ... 21: + offset =3D (pin - 14) * 4; + break; + case 22 ... 29: + offset =3D (pin - 22) * 4; + break; + case 30 ... 37: + offset =3D (pin - 30) * 4; + break; + default: + offset =3D -EINVAL; + } + + return offset; +} + +static int mpfs_pinctrl_pin_to_iomux_reg(unsigned int pin) +{ + int reg; + + switch (pin) { + case 0 ... 7: + reg =3D 0x204; + break; + case 8 ... 13: + reg =3D 0x208; + break; + case 14 ... 21: + reg =3D 0x20c; + break; + case 22 ... 29: + reg =3D 0x210; + break; + case 30 ... 37: + reg =3D 0x214; + break; + default: + reg =3D -EINVAL; + } + + return reg; +} + +static int mpfs_pinctrl_pin_to_iocfg_reg(unsigned int pin) +{ + u32 reg =3D MPFS_PINCTRL_IOCFG01_REG; + + if (pin >=3D MPFS_PINCTRL_BANK2_START) + reg +=3D MPFS_PINCTRL_INTER_BANK_GAP; + + // 2 pins per 32-bit register + reg +=3D (pin / 2) * 0x4; + + return reg; +} + +static int mpfs_pinctrl_pin_to_iocfg_offset(unsigned int pin) +{ + return 16 * (pin % 2); +} + +static void mpfs_pinctrl_dbg_show(struct pinctrl_dev *pctrl_dev, struct se= q_file *seq, + unsigned int pin) +{ + struct mpfs_pinctrl *pctrl =3D pinctrl_dev_get_drvdata(pctrl_dev); + u32 func; + int reg, offset; + + reg =3D mpfs_pinctrl_pin_to_iomux_reg(pin); + offset =3D mpfs_pinctrl_pin_to_iomux_offset(pin); + + seq_printf(seq, "reg: %x, offset: %u ", reg, offset); + seq_printf(seq, "pin: %u ", pin); + + if (reg < 0 || offset < 0) + return; + + regmap_read(pctrl->regmap, reg, &func); + func =3D (func >> offset) & MPFS_PINCTRL_PAD_MUX_MASK; + seq_printf(seq, "func: %s (%x)\n", mpfs_pinctrl_function_names[func], fun= c); +} + +static const struct pinctrl_ops mpfs_pinctrl_ops =3D { + .get_groups_count =3D pinctrl_generic_get_group_count, + .get_group_name =3D pinctrl_generic_get_group_name, + .get_group_pins =3D pinctrl_generic_get_group_pins, + .pin_dbg_show =3D mpfs_pinctrl_dbg_show, + .dt_node_to_map =3D pinctrl_generic_pins_function_dt_node_to_map, + .dt_free_map =3D pinctrl_utils_free_map, +}; + +static int mpfs_pinctrl_set_pin_func(struct mpfs_pinctrl *pctrl, u8 pin, u= 8 function) +{ + struct device *dev =3D pctrl->dev; + int reg, offset; + u32 func, mask; + + reg =3D mpfs_pinctrl_pin_to_iomux_reg(pin); + offset =3D mpfs_pinctrl_pin_to_iomux_offset(pin); + + func =3D function << offset; + mask =3D MPFS_PINCTRL_PAD_MUX_MASK << offset; + + dev_dbg(dev, "Setting pin %u. reg: %x offset %u func %x\n", pin, reg, off= set, func); + + if (reg < 0 || offset < 0) + return -EINVAL; + + regmap_update_bits(pctrl->regmap, reg, mask, func); + + return 0; +} + +static int mpfs_pinctrl_set_mux(struct pinctrl_dev *pctrl_dev, unsigned in= t fsel, + unsigned int gsel) +{ + struct mpfs_pinctrl *pctrl =3D pinctrl_dev_get_drvdata(pctrl_dev); + const struct group_desc *group; + const char **functions; + + group =3D pinctrl_generic_get_group(pctrl_dev, gsel); + if (!group) + return -EINVAL; + + functions =3D group->data; + + for (int i =3D 0; i < group->grp.npins; i++) { + int function; + + function =3D mpfs_pinctrl_function_map(functions[i]); + if (function < 0) { + dev_err(pctrl->dev, "invalid function %s\n", functions[i]); + return function; + } + + mpfs_pinctrl_set_pin_func(pctrl, group->grp.pins[i], function); + } + + return 0; +} + +static const struct pinmux_ops mpfs_pinctrl_pinmux_ops =3D { + .get_functions_count =3D pinmux_generic_get_function_count, + .get_function_name =3D pinmux_generic_get_function_name, + .get_function_groups =3D pinmux_generic_get_function_groups, + .set_mux =3D mpfs_pinctrl_set_mux, +}; + +static int mpfs_pinctrl_pinconf_get(struct pinctrl_dev *pctrl_dev, unsigne= d int pin, + unsigned long *config) +{ + struct mpfs_pinctrl *pctrl =3D pinctrl_dev_get_drvdata(pctrl_dev); + int param =3D pinconf_to_config_param(*config); + int reg =3D mpfs_pinctrl_pin_to_iocfg_reg(pin); + int val; + u32 arg; + u8 str; + + regmap_read(pctrl->regmap, reg, &val); + + val =3D val >> mpfs_pinctrl_pin_to_iocfg_offset(pin); + val =3D val & MPFS_PINCTRL_IOCFG_MASK; + + switch (param) { + case PIN_CONFIG_BIAS_BUS_HOLD: + if (!(val & MPFS_PINCTRL_WPD)) + return -EINVAL; + + if (!(val & MPFS_PINCTRL_WPU)) + return -EINVAL; + + arg =3D 1; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + if (!(val & MPFS_PINCTRL_WPD)) + return -EINVAL; + + if (val & MPFS_PINCTRL_WPU) + return -EINVAL; + + arg =3D 1; + break; + case PIN_CONFIG_BIAS_PULL_UP: + if (!(val & MPFS_PINCTRL_WPU)) + return -EINVAL; + + if (val & MPFS_PINCTRL_WPD) + return -EINVAL; + + arg =3D 1; + break; + case PIN_CONFIG_BIAS_DISABLE: + if (val & MPFS_PINCTRL_PULL_MASK) + return -EINVAL; + + arg =3D 1; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + str =3D FIELD_GET(MPFS_PINCTRL_DRV_MASK, val); + if (!str) + return -EINVAL; + + arg =3D mpfs_pinctrl_get_drive_strength_ma(str); + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + if (!FIELD_GET(MPFS_PINCTRL_ENHYST, val)) + return -EINVAL; + + arg =3D 1; + break; + case PIN_CONFIG_PERSIST_STATE: + if (!FIELD_GET(MPFS_PINCTRL_LP_PERSIST_EN, val)) + return -EINVAL; + + arg =3D 1; + break; + case PIN_CONFIG_MODE_LOW_POWER: + if (!FIELD_GET(MPFS_PINCTRL_LP_BYPASS_EN, val)) + return -EINVAL; + + arg =3D 1; + break; + case PIN_CONFIG_POWER_SOURCE: + arg =3D mpfs_pinctrl_pin_to_bank_voltage(pctrl, pin); + break; + case MPFS_PINCTRL_CLAMP_DIODE: + if (!FIELD_GET(MPFS_PINCTRL_CLAMP, val)) + return -EINVAL; + + arg =3D 1; + break; + case MPFS_PINCTRL_LOCKDOWN: + /* + * Lockdown is a read-only configuration, it'll get set if the + * tamper unit triggers global lockdown and lockdown has been + * set in the MSS Configurator for the bank a pin belongs to. + */ + if (!FIELD_GET(MPFS_PINCTRL_LOCKDN, val)) + return -EINVAL; + + arg =3D 1; + break; + case MPFS_PINCTRL_IBUFMD: + arg =3D FIELD_GET(MPFS_PINCTRL_IBUFMD_MASK, val); + break; + default: + return -ENOTSUPP; + } + + *config =3D pinconf_to_config_packed(param, arg); + + return 0; +} + +static int mpfs_pinctrl_pinconf_generate_config(struct mpfs_pinctrl *pctrl= , unsigned int pin, + unsigned long *configs, unsigned int num_configs, + u32 *value, u32 *bank_voltage) +{ + u32 val =3D 0; + + for (int i =3D 0; i < num_configs; i++) { + int param, tmp; + u32 arg; + + param =3D pinconf_to_config_param(configs[i]); + arg =3D pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_BUS_HOLD: + val |=3D MPFS_PINCTRL_PULL_MASK; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + val &=3D ~MPFS_PINCTRL_PULL_MASK; + val |=3D MPFS_PINCTRL_WPD; + break; + case PIN_CONFIG_BIAS_PULL_UP: + val &=3D ~MPFS_PINCTRL_PULL_MASK; + val |=3D MPFS_PINCTRL_WPU; + break; + case PIN_CONFIG_BIAS_DISABLE: + val &=3D ~MPFS_PINCTRL_PULL_MASK; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + tmp =3D mpfs_pinctrl_get_drive_strength_val(arg); + if (tmp < 0) + return tmp; + + val |=3D FIELD_PREP(MPFS_PINCTRL_DRV_MASK, tmp); + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + if (!arg) + break; + val |=3D MPFS_PINCTRL_ENHYST; + break; + case PIN_CONFIG_PERSIST_STATE: + val |=3D MPFS_PINCTRL_LP_PERSIST_EN; + break; + case PIN_CONFIG_MODE_LOW_POWER: + if (arg) + val |=3D MPFS_PINCTRL_LP_BYPASS_EN; + break; + case PIN_CONFIG_POWER_SOURCE: + tmp =3D mpfs_pinctrl_get_bank_voltage_val(arg); + if (tmp < 0) + return tmp; + + *bank_voltage =3D tmp; + break; + case MPFS_PINCTRL_CLAMP_DIODE: + val |=3D MPFS_PINCTRL_CLAMP; + break; + case MPFS_PINCTRL_IBUFMD: + val |=3D FIELD_PREP(MPFS_PINCTRL_IBUFMD_MASK, arg); + break; + default: + dev_err(pctrl->dev, "config %u not supported\n", param); + return -ENOTSUPP; + } + } + + *value =3D val; + return 0; +} + +static int mpfs_pinctrl_pin_set_config(struct mpfs_pinctrl *pctrl, unsigne= d int pin, u32 config) +{ + int reg =3D mpfs_pinctrl_pin_to_iocfg_reg(pin); + int offset =3D mpfs_pinctrl_pin_to_iocfg_offset(pin); + u32 val, mask; + + mask =3D MPFS_PINCTRL_IOCFG_MASK << offset; + val =3D config << offset; + + regmap_update_bits(pctrl->regmap, reg, mask, val); + + return 0; +} + +static int mpfs_pinctrl_pinconf_set(struct pinctrl_dev *pctrl_dev, unsigne= d int pin, + unsigned long *configs, unsigned int num_configs) +{ + struct mpfs_pinctrl *pctrl =3D pinctrl_dev_get_drvdata(pctrl_dev); + u32 val, bank_voltage =3D 0; + int ret; + + ret =3D mpfs_pinctrl_pinconf_generate_config(pctrl, pin, configs, num_con= figs, &val, + &bank_voltage); + if (ret) + return ret; + + ret =3D mpfs_pinctrl_pin_set_config(pctrl, pin, val); + if (ret) + return ret; + + if (bank_voltage) + mpfs_pinctrl_set_bank_voltage(pctrl, pin, bank_voltage); + + return 0; +} + +static int mpfs_pinctrl_pinconf_group_set(struct pinctrl_dev *pctrl_dev, u= nsigned int gsel, + unsigned long *configs, unsigned int num_configs) +{ + struct mpfs_pinctrl *pctrl =3D pinctrl_dev_get_drvdata(pctrl_dev); + const struct group_desc *group; + unsigned int pin; + u32 val, bank_voltage =3D 0; + int ret; + + group =3D pinctrl_generic_get_group(pctrl_dev, gsel); + if (!group) + return -EINVAL; + + /* + * Assume that the first pin in a group is representative, as the mss + * configurator doesn't allow splitting a function between two banks. + */ + pin =3D group->grp.pins[0]; + + ret =3D mpfs_pinctrl_pinconf_generate_config(pctrl, pin, configs, num_con= figs, &val, + &bank_voltage); + if (ret) + return ret; + + for (int i =3D 0; i < group->grp.npins; i++) + mpfs_pinctrl_pin_set_config(pctrl, group->grp.pins[i], val); + + if (bank_voltage) + mpfs_pinctrl_set_bank_voltage(pctrl, group->grp.pins[0], bank_voltage); + + return 0; +} + +static void mpfs_pinctrl_pinconf_dbg_show(struct pinctrl_dev *pctrl_dev, s= truct seq_file *seq, + unsigned int pin) +{ + struct mpfs_pinctrl *pctrl =3D pinctrl_dev_get_drvdata(pctrl_dev); + u32 val; + int reg, offset; + + reg =3D mpfs_pinctrl_pin_to_iocfg_reg(pin); + offset =3D mpfs_pinctrl_pin_to_iocfg_offset(pin); + + seq_printf(seq, "pin: %u ", pin); + seq_printf(seq, "reg: %x offset: %u ", reg, offset); + + if (reg < 0 || offset < 0) + return; + + regmap_read(pctrl->regmap, reg, &val); + val =3D (val & (MPFS_PINCTRL_IOCFG_MASK << offset)) >> offset; + seq_printf(seq, "val: %x\n", val); +} + +static const struct pinconf_ops mpfs_pinctrl_pinconf_ops =3D { + .pin_config_get =3D mpfs_pinctrl_pinconf_get, + .pin_config_set =3D mpfs_pinctrl_pinconf_set, + .pin_config_group_set =3D mpfs_pinctrl_pinconf_group_set, + .pin_config_dbg_show =3D mpfs_pinctrl_pinconf_dbg_show, + .is_generic =3D true, +}; + +static const struct pinctrl_pin_desc mpfs_pinctrl_pins[] =3D { + PINCTRL_PIN(0, "bank 4 0"), + PINCTRL_PIN(1, "bank 4 1"), + PINCTRL_PIN(2, "bank 4 2"), + PINCTRL_PIN(3, "bank 4 3"), + PINCTRL_PIN(4, "bank 4 4"), + PINCTRL_PIN(5, "bank 4 5"), + PINCTRL_PIN(6, "bank 4 6"), + PINCTRL_PIN(7, "bank 4 7"), + PINCTRL_PIN(8, "bank 4 8"), + PINCTRL_PIN(9, "bank 4 9"), + PINCTRL_PIN(10, "bank 4 10"), + PINCTRL_PIN(11, "bank 4 11"), + PINCTRL_PIN(12, "bank 4 12"), + PINCTRL_PIN(13, "bank 4 13"), + + PINCTRL_PIN(14, "bank 2 0"), + PINCTRL_PIN(15, "bank 2 1"), + PINCTRL_PIN(16, "bank 2 2"), + PINCTRL_PIN(17, "bank 2 3"), + PINCTRL_PIN(18, "bank 2 4"), + PINCTRL_PIN(19, "bank 2 5"), + PINCTRL_PIN(20, "bank 2 6"), + PINCTRL_PIN(21, "bank 2 7"), + PINCTRL_PIN(22, "bank 2 8"), + PINCTRL_PIN(23, "bank 2 9"), + PINCTRL_PIN(24, "bank 2 10"), + PINCTRL_PIN(25, "bank 2 11"), + PINCTRL_PIN(26, "bank 2 12"), + PINCTRL_PIN(27, "bank 2 13"), + PINCTRL_PIN(28, "bank 2 14"), + PINCTRL_PIN(29, "bank 2 15"), + PINCTRL_PIN(30, "bank 2 16"), + PINCTRL_PIN(31, "bank 2 17"), + PINCTRL_PIN(32, "bank 2 18"), + PINCTRL_PIN(33, "bank 2 19"), + PINCTRL_PIN(34, "bank 2 20"), + PINCTRL_PIN(35, "bank 2 21"), + PINCTRL_PIN(36, "bank 2 22"), + PINCTRL_PIN(37, "bank 2 23"), +}; + +static int mpfs_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct mpfs_pinctrl *pctrl; + int ret; + + pctrl =3D devm_kzalloc(dev, sizeof(*pctrl), GFP_KERNEL); + if (!pctrl) + return -ENOMEM; + + pctrl->regmap =3D device_node_to_regmap(pdev->dev.parent->of_node); + if (IS_ERR(pctrl->regmap)) + dev_err_probe(dev, PTR_ERR(pctrl->regmap), "Failed to find syscon regmap= \n"); + + pctrl->sysreg_regmap =3D syscon_regmap_lookup_by_compatible("microchip,mp= fs-sysreg-scb"); + if (IS_ERR(pctrl->sysreg_regmap)) + return PTR_ERR(pctrl->sysreg_regmap); + + pctrl->desc.name =3D dev_name(dev); + pctrl->desc.pins =3D mpfs_pinctrl_pins; + pctrl->desc.npins =3D ARRAY_SIZE(mpfs_pinctrl_pins); + pctrl->desc.pctlops =3D &mpfs_pinctrl_ops; + pctrl->desc.pmxops =3D &mpfs_pinctrl_pinmux_ops; + pctrl->desc.confops =3D &mpfs_pinctrl_pinconf_ops; + pctrl->desc.owner =3D THIS_MODULE; + pctrl->desc.num_custom_params =3D ARRAY_SIZE(mpfs_pinctrl_custom_bindings= ); + pctrl->desc.custom_params =3D mpfs_pinctrl_custom_bindings; + + pctrl->dev =3D dev; + + ret =3D devm_mutex_init(dev, &pctrl->mutex); + if (ret) + return ret; + + platform_set_drvdata(pdev, pctrl); + + pctrl->pctrl =3D devm_pinctrl_register(&pdev->dev, &pctrl->desc, pctrl); + if (IS_ERR(pctrl->pctrl)) + return PTR_ERR(pctrl->pctrl); + + return 0; +} + +static const struct of_device_id mpfs_pinctrl_of_match[] =3D { + { .compatible =3D "microchip,mpfs-pinctrl-mssio" }, + { } +}; +MODULE_DEVICE_TABLE(of, mpfs_pinctrl_of_match); + +static struct platform_driver mpfs_pinctrl_driver =3D { + .driver =3D { + .name =3D "mpfs-pinctrl", + .of_match_table =3D mpfs_pinctrl_of_match, + }, + .probe =3D mpfs_pinctrl_probe, +}; +module_platform_driver(mpfs_pinctrl_driver); + +MODULE_AUTHOR("Conor Dooley "); +MODULE_DESCRIPTION("Polarfire SoC mssio pinctrl driver"); +MODULE_LICENSE("GPL"); --=20 2.51.0