From nobody Tue Dec 2 02:31:41 2025 Received: from polaris.svanheule.net (polaris.svanheule.net [84.16.241.116]) (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 236B933A6E3 for ; Wed, 19 Nov 2025 20:03:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=84.16.241.116 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763582613; cv=none; b=U2uu884cJFWV32BmUUZRI/Lt6dR96MyF/bYN0NBPN/WZFbJisNMeSwqp07e7WI6kC/cPFQ5xcqAceb8Ki6UmtOLz0EE5ikjVaywcVH/ViFTD0nUtHS3X5XP+pX8vu1puLicKGc+9XrnGRzccIvuZ+8WFw8FgTyM9J4W/7S2K5R4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763582613; c=relaxed/simple; bh=Xx0Jn1xyCysLiJ3kVIuxRTsoI6Vpb/gOec4NC9bOJyQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QYDA3ke5hpACO/xK8/Rf0WpG7lwgkDCahgGBDuVCBxE580f7RnmeQgGIk7pfyDtoHpBb5wTgYMHYXa9p3Qru+SF/QSUqGU5m6Bv8FzQ1FV0kFqHkHDfsD1c0O+7mbWTVITDp5CGpGOTE0hh9hV473qyOwPq0NF701+MCOU1nb/0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=svanheule.net; spf=pass smtp.mailfrom=svanheule.net; dkim=pass (2048-bit key) header.d=svanheule.net header.i=@svanheule.net header.b=ZmAyeIPH; arc=none smtp.client-ip=84.16.241.116 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=svanheule.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=svanheule.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=svanheule.net header.i=@svanheule.net header.b="ZmAyeIPH" Received: from terra.vega.svanheule.net (2a02-1812-162c-8f00-1e2d-b404-3319-eba8.ip6.access.telenet.be [IPv6:2a02:1812:162c:8f00:1e2d:b404:3319:eba8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: sander@svanheule.net) by polaris.svanheule.net (Postfix) with ESMTPSA id 944F96A1E9A; Wed, 19 Nov 2025 21:03:16 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=svanheule.net; s=mail1707; t=1763582596; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=JMxcMAsL4PvqU2CDxJD2RIDHlzUPTLJ+OfiIY7x34ig=; b=ZmAyeIPHVk7SbCL32dx7qVZxvHWooURhVU7zu92Ut18dxIZJJHgihDG4SvC2cBNQI4c31P vqimHMmx0e/7YfsUReRWUNkU/sdGvDfp0gjcT7rFrS8taympEOXj90+z47S4efloAGHl3w nqVFm6eU1VfaCB1IUO8Wo32nmahvC2YbY3894CYyJmzcGDbfQl9mP4GWMpTZp6FQq8gkCl Ib66p7tjh2Vyg75t6ldNkAUXFWFPn8j2LNzCHwXOejRaRK05xFZ+Hk3ZZsDop70kjwyVMB uGsWouYeEnwj3ySpd6B0HCllfO/7VnmYkY2eu1N7TtQ6tDX9VC1Bob9Z6xN8oQ== From: Sander Vanheule To: Lee Jones , Pavel Machek , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Linus Walleij , Michael Walle , Bartosz Golaszewski Cc: linux-leds@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, Sander Vanheule Subject: [PATCH v8 4/6] pinctrl: Add RTL8231 pin control and GPIO support Date: Wed, 19 Nov 2025 21:03:03 +0100 Message-ID: <20251119200306.60569-5-sander@svanheule.net> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251119200306.60569-1-sander@svanheule.net> References: <20251119200306.60569-1-sander@svanheule.net> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This driver implements the GPIO and pin muxing features provided by the RTL8231. The device should be instantiated as an MFD child, where the parent device has already configured the regmap used for register access. Debouncing is only available for the six highest GPIOs, and must be emulated when other pins are used for (button) inputs. Although described in the bindings, drive strength selection is currently not implemented. Signed-off-by: Sander Vanheule Reviewed-by: Linus Walleij --- Changes since v7: - Second attempt at fixing storage size of pinfunction flags - Add OF dependency for pinconf_generic_dt_node_to_map symbol - Allow building with COMPILE_TEST Changes since v6: - Use uintptr_t storage for enum rtl8231_pin_function - Simplify safe direction configuration - Add GPIOLIB dependency - Add Linus's review tag --- drivers/pinctrl/Kconfig | 12 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-rtl8231.c | 533 ++++++++++++++++++++++++++++++ 3 files changed, 546 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-rtl8231.c diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 4f8507ebbdac..f1ad8edb98ff 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -520,6 +520,18 @@ config PINCTRL_ROCKCHIP help This support pinctrl and GPIO driver for Rockchip SoCs. =20 +config PINCTRL_RTL8231 + tristate "Realtek RTL8231 GPIO expander's pin controller" + depends on OF && (MFD_RTL8231 || COMPILE_TEST) + default MFD_RTL8231 + select GENERIC_PINCONF + select GENERIC_PINMUX_FUNCTIONS + select GPIOLIB + select GPIO_REGMAP + help + Support for RTL8231 expander's GPIOs and pin controller. + When built as a module, the module will be called pinctrl-rtl8231. + config PINCTRL_SCMI tristate "Pinctrl driver using SCMI protocol interface" depends on ARM_SCMI_PROTOCOL || COMPILE_TEST diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index e0cfb9b7c99b..ded51723d452 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_PINCTRL_PISTACHIO) +=3D pinctrl-pistachio.o obj-$(CONFIG_PINCTRL_RK805) +=3D pinctrl-rk805.o obj-$(CONFIG_PINCTRL_ROCKCHIP) +=3D pinctrl-rockchip.o obj-$(CONFIG_PINCTRL_RP1) +=3D pinctrl-rp1.o +obj-$(CONFIG_PINCTRL_RTL8231) +=3D pinctrl-rtl8231.o obj-$(CONFIG_PINCTRL_SCMI) +=3D pinctrl-scmi.o obj-$(CONFIG_PINCTRL_SINGLE) +=3D pinctrl-single.o obj-$(CONFIG_PINCTRL_ST) +=3D pinctrl-st.o diff --git a/drivers/pinctrl/pinctrl-rtl8231.c b/drivers/pinctrl/pinctrl-rt= l8231.c new file mode 100644 index 000000000000..f38f0726546c --- /dev/null +++ b/drivers/pinctrl/pinctrl-rtl8231.c @@ -0,0 +1,533 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "pinmux.h" +#include + +#define RTL8231_NUM_GPIOS 37 +#define RTL8231_DEBOUNCE_USEC 100000 +#define RTL8231_DEBOUNCE_MIN_OFFSET 31 + +struct rtl8231_pin_ctrl { + struct regmap *map; +}; + +/* + * Pin controller functionality + */ +enum rtl8231_pin_function { + RTL8231_PIN_FUNCTION_GPIO =3D BIT(0), + RTL8231_PIN_FUNCTION_LED =3D BIT(1), + RTL8231_PIN_FUNCTION_PWM =3D BIT(2), +}; + +struct rtl8231_function_info { + enum rtl8231_pin_function flag; + const char *name; +}; + +#define RTL8231_FUNCTION(_name, _flag) \ +((struct rtl8231_function_info) { \ + .flag =3D (_flag), \ + .name =3D (_name), \ + }) + +static const struct rtl8231_function_info rtl8231_pin_functions[] =3D { + RTL8231_FUNCTION("gpio", RTL8231_PIN_FUNCTION_GPIO), + RTL8231_FUNCTION("led", RTL8231_PIN_FUNCTION_LED), + RTL8231_FUNCTION("pwm", RTL8231_PIN_FUNCTION_PWM), +}; + +struct rtl8231_pin_desc { + enum rtl8231_pin_function functions:8; + u8 reg; + u8 offset; + u8 gpio_function_value; +}; + +#define RTL8231_PIN_DESC(_num, _func, _reg, _fld, _val) \ + [(_num)] =3D ((struct rtl8231_pin_desc) { \ + .functions =3D RTL8231_PIN_FUNCTION_GPIO | (_func), \ + .reg =3D (_reg), \ + .offset =3D (_fld), \ + .gpio_function_value =3D (_val), \ + }) +#define RTL8231_GPIO_PIN_DESC(_num, _reg, _fld) \ + RTL8231_PIN_DESC(_num, 0, _reg, _fld, RTL8231_PIN_MODE_GPIO) +#define RTL8231_LED_PIN_DESC(_num, _reg, _fld) \ + RTL8231_PIN_DESC(_num, RTL8231_PIN_FUNCTION_LED, _reg, _fld, RTL8231_PIN_= MODE_GPIO) +#define RTL8231_PWM_PIN_DESC(_num, _reg, _fld) \ + RTL8231_PIN_DESC(_num, RTL8231_PIN_FUNCTION_PWM, _reg, _fld, 0) + +/* + * All pins have a GPIO/LED mux bit, but the bits for pins 35/36 are read-= only. Use this bit + * for the GPIO-only pin instead of a placeholder, so the rest of the logi= c can stay generic. + */ +static const struct rtl8231_pin_desc rtl8231_pin_data[RTL8231_NUM_GPIOS] = =3D { + RTL8231_LED_PIN_DESC(0, RTL8231_REG_PIN_MODE0, 0), + RTL8231_LED_PIN_DESC(1, RTL8231_REG_PIN_MODE0, 1), + RTL8231_LED_PIN_DESC(2, RTL8231_REG_PIN_MODE0, 2), + RTL8231_LED_PIN_DESC(3, RTL8231_REG_PIN_MODE0, 3), + RTL8231_LED_PIN_DESC(4, RTL8231_REG_PIN_MODE0, 4), + RTL8231_LED_PIN_DESC(5, RTL8231_REG_PIN_MODE0, 5), + RTL8231_LED_PIN_DESC(6, RTL8231_REG_PIN_MODE0, 6), + RTL8231_LED_PIN_DESC(7, RTL8231_REG_PIN_MODE0, 7), + RTL8231_LED_PIN_DESC(8, RTL8231_REG_PIN_MODE0, 8), + RTL8231_LED_PIN_DESC(9, RTL8231_REG_PIN_MODE0, 9), + RTL8231_LED_PIN_DESC(10, RTL8231_REG_PIN_MODE0, 10), + RTL8231_LED_PIN_DESC(11, RTL8231_REG_PIN_MODE0, 11), + RTL8231_LED_PIN_DESC(12, RTL8231_REG_PIN_MODE0, 12), + RTL8231_LED_PIN_DESC(13, RTL8231_REG_PIN_MODE0, 13), + RTL8231_LED_PIN_DESC(14, RTL8231_REG_PIN_MODE0, 14), + RTL8231_LED_PIN_DESC(15, RTL8231_REG_PIN_MODE0, 15), + RTL8231_LED_PIN_DESC(16, RTL8231_REG_PIN_MODE1, 0), + RTL8231_LED_PIN_DESC(17, RTL8231_REG_PIN_MODE1, 1), + RTL8231_LED_PIN_DESC(18, RTL8231_REG_PIN_MODE1, 2), + RTL8231_LED_PIN_DESC(19, RTL8231_REG_PIN_MODE1, 3), + RTL8231_LED_PIN_DESC(20, RTL8231_REG_PIN_MODE1, 4), + RTL8231_LED_PIN_DESC(21, RTL8231_REG_PIN_MODE1, 5), + RTL8231_LED_PIN_DESC(22, RTL8231_REG_PIN_MODE1, 6), + RTL8231_LED_PIN_DESC(23, RTL8231_REG_PIN_MODE1, 7), + RTL8231_LED_PIN_DESC(24, RTL8231_REG_PIN_MODE1, 8), + RTL8231_LED_PIN_DESC(25, RTL8231_REG_PIN_MODE1, 9), + RTL8231_LED_PIN_DESC(26, RTL8231_REG_PIN_MODE1, 10), + RTL8231_LED_PIN_DESC(27, RTL8231_REG_PIN_MODE1, 11), + RTL8231_LED_PIN_DESC(28, RTL8231_REG_PIN_MODE1, 12), + RTL8231_LED_PIN_DESC(29, RTL8231_REG_PIN_MODE1, 13), + RTL8231_LED_PIN_DESC(30, RTL8231_REG_PIN_MODE1, 14), + RTL8231_LED_PIN_DESC(31, RTL8231_REG_PIN_MODE1, 15), + RTL8231_LED_PIN_DESC(32, RTL8231_REG_PIN_HI_CFG, 0), + RTL8231_LED_PIN_DESC(33, RTL8231_REG_PIN_HI_CFG, 1), + RTL8231_LED_PIN_DESC(34, RTL8231_REG_PIN_HI_CFG, 2), + RTL8231_PWM_PIN_DESC(35, RTL8231_REG_FUNC1, 3), + RTL8231_GPIO_PIN_DESC(36, RTL8231_REG_PIN_HI_CFG, 4), +}; +static const unsigned int PWM_PIN =3D 35; + +#define RTL8231_PIN(_num) \ + ((struct pinctrl_pin_desc) { \ + .number =3D (_num), \ + .name =3D "gpio" #_num, \ + .drv_data =3D (void *) &rtl8231_pin_data[(_num)] \ + }) + +static const struct pinctrl_pin_desc rtl8231_pins[RTL8231_NUM_GPIOS] =3D { + RTL8231_PIN(0), + RTL8231_PIN(1), + RTL8231_PIN(2), + RTL8231_PIN(3), + RTL8231_PIN(4), + RTL8231_PIN(5), + RTL8231_PIN(6), + RTL8231_PIN(7), + RTL8231_PIN(8), + RTL8231_PIN(9), + RTL8231_PIN(10), + RTL8231_PIN(11), + RTL8231_PIN(12), + RTL8231_PIN(13), + RTL8231_PIN(14), + RTL8231_PIN(15), + RTL8231_PIN(16), + RTL8231_PIN(17), + RTL8231_PIN(18), + RTL8231_PIN(19), + RTL8231_PIN(20), + RTL8231_PIN(21), + RTL8231_PIN(22), + RTL8231_PIN(23), + RTL8231_PIN(24), + RTL8231_PIN(25), + RTL8231_PIN(26), + RTL8231_PIN(27), + RTL8231_PIN(28), + RTL8231_PIN(29), + RTL8231_PIN(30), + RTL8231_PIN(31), + RTL8231_PIN(32), + RTL8231_PIN(33), + RTL8231_PIN(34), + RTL8231_PIN(35), + RTL8231_PIN(36), +}; + +static int rtl8231_get_groups_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(rtl8231_pins); +} + +static const char *rtl8231_get_group_name(struct pinctrl_dev *pctldev, uns= igned int selector) +{ + return rtl8231_pins[selector].name; +} + +static int rtl8231_get_group_pins(struct pinctrl_dev *pctldev, unsigned in= t selector, + const unsigned int **pins, unsigned int *num_pins) +{ + if (selector >=3D ARRAY_SIZE(rtl8231_pins)) + return -EINVAL; + + *pins =3D &rtl8231_pins[selector].number; + *num_pins =3D 1; + + return 0; +} + +static const struct pinctrl_ops rtl8231_pinctrl_ops =3D { + .get_groups_count =3D rtl8231_get_groups_count, + .get_group_name =3D rtl8231_get_group_name, + .get_group_pins =3D rtl8231_get_group_pins, + .dt_node_to_map =3D pinconf_generic_dt_node_to_map_all, + .dt_free_map =3D pinconf_generic_dt_free_map, +}; + +static int rtl8231_set_mux(struct pinctrl_dev *pctldev, unsigned int func_= selector, + unsigned int group_selector) +{ + const struct function_desc *func =3D pinmux_generic_get_function(pctldev,= func_selector); + const struct rtl8231_pin_desc *desc =3D rtl8231_pins[group_selector].drv_= data; + const struct rtl8231_pin_ctrl *ctrl =3D pinctrl_dev_get_drvdata(pctldev); + enum rtl8231_pin_function func_flag =3D (uintptr_t) func->data; + unsigned int function_mask; + unsigned int gpio_function; + + if (!(desc->functions & func_flag)) + return -EINVAL; + + function_mask =3D BIT(desc->offset); + gpio_function =3D desc->gpio_function_value << desc->offset; + + if (func_flag =3D=3D RTL8231_PIN_FUNCTION_GPIO) + return regmap_update_bits(ctrl->map, desc->reg, function_mask, gpio_func= tion); + else + return regmap_update_bits(ctrl->map, desc->reg, function_mask, ~gpio_fun= ction); +} + +static int rtl8231_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, unsigned int offset) +{ + const struct rtl8231_pin_desc *desc =3D rtl8231_pins[offset].drv_data; + const struct rtl8231_pin_ctrl *ctrl =3D pinctrl_dev_get_drvdata(pctldev); + unsigned int function_mask; + unsigned int gpio_function; + + function_mask =3D BIT(desc->offset); + gpio_function =3D desc->gpio_function_value << desc->offset; + + return regmap_update_bits(ctrl->map, desc->reg, function_mask, gpio_funct= ion); +} + +static const struct pinmux_ops rtl8231_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, + .function_is_gpio =3D pinmux_generic_function_is_gpio, + .set_mux =3D rtl8231_set_mux, + .gpio_request_enable =3D rtl8231_gpio_request_enable, + .strict =3D true, +}; + +static int rtl8231_pin_config_get(struct pinctrl_dev *pctldev, unsigned in= t offset, + unsigned long *config) +{ + const struct rtl8231_pin_ctrl *ctrl =3D pinctrl_dev_get_drvdata(pctldev); + unsigned int param =3D pinconf_to_config_param(*config); + unsigned int arg; + int err; + int v; + + switch (param) { + case PIN_CONFIG_INPUT_DEBOUNCE: + if (offset < RTL8231_DEBOUNCE_MIN_OFFSET) + return -EINVAL; + + err =3D regmap_read(ctrl->map, RTL8231_REG_FUNC1, &v); + if (err) + return err; + + v =3D FIELD_GET(RTL8231_FUNC1_DEBOUNCE_MASK, v); + if (v & BIT(offset - RTL8231_DEBOUNCE_MIN_OFFSET)) + arg =3D RTL8231_DEBOUNCE_USEC; + else + arg =3D 0; + break; + default: + return -ENOTSUPP; + } + + *config =3D pinconf_to_config_packed(param, arg); + + return 0; +} + +static int rtl8231_pin_config_set(struct pinctrl_dev *pctldev, unsigned in= t offset, + unsigned long *configs, unsigned int num_configs) +{ + const struct rtl8231_pin_ctrl *ctrl =3D pinctrl_dev_get_drvdata(pctldev); + unsigned int param, arg; + unsigned int pin_mask; + int err; + int i; + + for (i =3D 0; i < num_configs; i++) { + param =3D pinconf_to_config_param(configs[i]); + arg =3D pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_INPUT_DEBOUNCE: + if (offset < RTL8231_DEBOUNCE_MIN_OFFSET) + return -EINVAL; + + pin_mask =3D FIELD_PREP(RTL8231_FUNC1_DEBOUNCE_MASK, + BIT(offset - RTL8231_DEBOUNCE_MIN_OFFSET)); + + switch (arg) { + case 0: + err =3D regmap_update_bits(ctrl->map, RTL8231_REG_FUNC1, + pin_mask, 0); + break; + case RTL8231_DEBOUNCE_USEC: + err =3D regmap_update_bits(ctrl->map, RTL8231_REG_FUNC1, + pin_mask, pin_mask); + break; + default: + return -EINVAL; + } + + break; + default: + return -ENOTSUPP; + } + } + + return err; +} + +static const struct pinconf_ops rtl8231_pinconf_ops =3D { + .is_generic =3D true, + .pin_config_get =3D rtl8231_pin_config_get, + .pin_config_set =3D rtl8231_pin_config_set, +}; + +static int rtl8231_pinctrl_init_functions(struct pinctrl_dev *pctl, + const struct pinctrl_desc *pctl_desc) +{ + enum rtl8231_pin_function flag; + struct pinfunction func; + const char **groups; + unsigned int f_idx; + const char *name; + unsigned int pin; + int num_groups; + int err; + + for (f_idx =3D 0; f_idx < ARRAY_SIZE(rtl8231_pin_functions); f_idx++) { + name =3D rtl8231_pin_functions[f_idx].name; + flag =3D rtl8231_pin_functions[f_idx].flag; + + for (pin =3D 0, num_groups =3D 0; pin < pctl_desc->npins; pin++) + if (rtl8231_pin_data[pin].functions & flag) + num_groups++; + + groups =3D devm_kcalloc(pctl->dev, num_groups, sizeof(*groups), GFP_KERN= EL); + if (!groups) + return -ENOMEM; + + for (pin =3D 0, num_groups =3D 0; pin < pctl_desc->npins; pin++) + if (rtl8231_pin_data[pin].functions & flag) + groups[num_groups++] =3D rtl8231_pins[pin].name; + + func =3D PINCTRL_PINFUNCTION(name, groups, num_groups); + if (flag =3D=3D RTL8231_PIN_FUNCTION_GPIO) + func.flags |=3D PINFUNCTION_FLAG_GPIO; + + err =3D pinmux_generic_add_pinfunction(pctl, &func, (void *) ((uintptr_t= ) flag)); + if (err < 0) + return err; + } + + return 0; +} + +struct pin_field_info { + const struct reg_field gpio_dir; + const struct reg_field mode; +}; + +static const struct pin_field_info pin_fields[] =3D { + { + .gpio_dir =3D REG_FIELD(RTL8231_REG_GPIO_DIR0, 0, 15), + .mode =3D REG_FIELD(RTL8231_REG_PIN_MODE0, 0, 15), + }, + { + .gpio_dir =3D REG_FIELD(RTL8231_REG_GPIO_DIR1, 0, 15), + .mode =3D REG_FIELD(RTL8231_REG_PIN_MODE1, 0, 15), + }, + { + .gpio_dir =3D REG_FIELD(RTL8231_REG_PIN_HI_CFG, 5, 9), + .mode =3D REG_FIELD(RTL8231_REG_PIN_HI_CFG, 0, 4), + }, +}; + +static int rtl8231_configure_safe(struct device *dev, struct regmap *map) +{ + struct regmap_field *field_mode; + struct regmap_field *field_dir; + unsigned int is_input; + unsigned int is_gpio; + int err; + + for (unsigned int i =3D 0; i < ARRAY_SIZE(pin_fields); i++) { + field_dir =3D devm_regmap_field_alloc(dev, map, pin_fields[i].gpio_dir); + if (IS_ERR(field_dir)) + return PTR_ERR(field_dir); + + field_mode =3D devm_regmap_field_alloc(dev, map, pin_fields[i].mode); + if (IS_ERR(field_mode)) + return PTR_ERR(field_mode); + + err =3D regmap_field_read(field_dir, &is_input); + if (err) + return err; + + err =3D regmap_field_read(field_mode, &is_gpio); + if (err) + return err; + + /* Enable field for PWM (on GPIO35) is in another register */ + if (pin_fields[i].mode.reg =3D=3D RTL8231_REG_PIN_HI_CFG) { + err =3D regmap_test_bits(map, rtl8231_pin_data[PWM_PIN].reg, + BIT(rtl8231_pin_data[PWM_PIN].offset)); + if (err < 0) + return err; + + if (err) + is_gpio &=3D ~BIT(PWM_PIN % RTL8231_BITS_VAL); + } + + /* + * Set every pin that is not muxed as a GPIO to gpio-in. That + * way the pin will be high impedance when it is muxed to GPIO, + * preventing unwanted glitches. + * The pin muxes are left as-is, so there are no signal changes. + */ + regmap_field_write(field_dir, is_input | ~is_gpio); + + devm_regmap_field_free(dev, field_dir); + devm_regmap_field_free(dev, field_mode); + } + + return 0; +} + +static const struct pinctrl_desc rtl8231_pctl_desc =3D { + .name =3D "rtl8231-pinctrl", + .owner =3D THIS_MODULE, + .confops =3D &rtl8231_pinconf_ops, + .pctlops =3D &rtl8231_pinctrl_ops, + .pmxops =3D &rtl8231_pinmux_ops, + .npins =3D ARRAY_SIZE(rtl8231_pins), + .pins =3D rtl8231_pins, +}; + +static int rtl8231_pinctrl_init(struct device *dev, struct rtl8231_pin_ctr= l *ctrl) +{ + struct pinctrl_dev *pctldev; + int err; + + err =3D devm_pinctrl_register_and_init(dev->parent, &rtl8231_pctl_desc, c= trl, &pctldev); + if (err) { + dev_err(dev, "failed to register pin controller\n"); + return err; + } + + err =3D rtl8231_pinctrl_init_functions(pctldev, &rtl8231_pctl_desc); + if (err) + return err; + + err =3D pinctrl_enable(pctldev); + if (err) + dev_err(dev, "failed to enable pin controller\n"); + + return err; +} + +/* + * GPIO controller functionality + */ +static int rtl8231_gpio_reg_mask_xlate(struct gpio_regmap *gpio, unsigned = int base, + unsigned int offset, unsigned int *reg, unsigned int *mask) +{ + unsigned int pin_mask =3D BIT(offset % RTL8231_BITS_VAL); + + if (base =3D=3D RTL8231_REG_GPIO_DATA0 || offset < 32) { + *reg =3D base + offset / RTL8231_BITS_VAL; + *mask =3D pin_mask; + } else if (base =3D=3D RTL8231_REG_GPIO_DIR0) { + *reg =3D RTL8231_REG_PIN_HI_CFG; + *mask =3D FIELD_PREP(RTL8231_PIN_HI_CFG_DIR_MASK, pin_mask); + } else { + return -EINVAL; + } + + return 0; +} + +static int rtl8231_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct rtl8231_pin_ctrl *ctrl; + struct gpio_regmap_config gpio_cfg =3D {}; + int err; + + ctrl =3D devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + ctrl->map =3D dev_get_regmap(dev->parent, NULL); + if (!ctrl->map) + return -ENODEV; + + err =3D rtl8231_configure_safe(dev, ctrl->map); + if (err) + return err; + + err =3D rtl8231_pinctrl_init(dev, ctrl); + if (err) + return err; + + gpio_cfg.regmap =3D ctrl->map; + gpio_cfg.parent =3D dev->parent; + gpio_cfg.ngpio =3D RTL8231_NUM_GPIOS; + gpio_cfg.ngpio_per_reg =3D RTL8231_BITS_VAL; + + gpio_cfg.reg_dat_base =3D GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DATA0); + gpio_cfg.reg_set_base =3D GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DATA0); + gpio_cfg.reg_dir_in_base =3D GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DIR0); + + gpio_cfg.reg_mask_xlate =3D rtl8231_gpio_reg_mask_xlate; + + return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_cfg)); +} + +static struct platform_driver rtl8231_pinctrl_driver =3D { + .driver =3D { + .name =3D "rtl8231-pinctrl", + }, + .probe =3D rtl8231_pinctrl_probe, +}; +module_platform_driver(rtl8231_pinctrl_driver); + +MODULE_AUTHOR("Sander Vanheule "); +MODULE_DESCRIPTION("Realtek RTL8231 pin control and GPIO support"); +MODULE_LICENSE("GPL"); --=20 2.51.1