From patchwork Mon May 17 19:28:04 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sander Vanheule X-Patchwork-Id: 440362 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 83F9DC433ED for ; Mon, 17 May 2021 19:28:45 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 68571611B0 for ; Mon, 17 May 2021 19:28:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233748AbhEQT37 (ORCPT ); Mon, 17 May 2021 15:29:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51750 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232773AbhEQT35 (ORCPT ); Mon, 17 May 2021 15:29:57 -0400 Received: from polaris.svanheule.net (polaris.svanheule.net [IPv6:2a00:c98:2060:a004:1::200]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AD241C061573 for ; Mon, 17 May 2021 12:28:39 -0700 (PDT) Received: from terra.local.svanheule.net (unknown [IPv6:2a02:a03f:eafb:ee01:404a:340a:91cb:c07b]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: sander@svanheule.net) by polaris.svanheule.net (Postfix) with ESMTPSA id 16B581FFBF8; Mon, 17 May 2021 21:28:38 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=svanheule.net; s=mail1707; t=1621279718; 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=3q6JoyXSrkPLBoFuPy3yaThUjOXJKmAlTPeZBosKKkc=; b=LOwGCCu/IYFbqsbTPoCSb6E3ENeTVgct/Tp56oZZsi4aw3g2F2GfVsTOvZRdkH9I0hmKHe gl+3TCr1Gkgm4EtEss/qK+deBP77YIWotFro6JAJz5cyK4SEnZ+n1vJ2QTivkFlC8qqlgW 2TjiMNXD2Aip7vvnPzDnPoTCAHGJgPhV+cAmGIAto/FR1wtR5PMU9zdHV4vyMB3dczsmHH 6PXBXkdm3EynCku/qOZrFwSqg4AHS/unJQNXy7d6eu4hi+PYtKWrALkj5cyjtYGWVMKFiQ 2fGkh44iFv+Bt+6X7p2DWw4mOj3m40NKLS64QUJC6glwjecb62OK9GBTgFPHOw== From: Sander Vanheule To: Pavel Machek , Rob Herring , Lee Jones , Mark Brown , Greg Kroah-Hartman , "Rafael J . Wysocki" , Michael Walle , Linus Walleij , Bartosz Golaszewski , linux-leds@vger.kernel.org, devicetree@vger.kernel.org, linux-gpio@vger.kernel.org Cc: Andrew Lunn , Andy Shevchenko , linux-kernel@vger.kernel.org, Sander Vanheule Subject: [PATCH v2 2/7] gpio: regmap: Add configurable dir/value order Date: Mon, 17 May 2021 21:28:04 +0200 Message-Id: X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org GPIO chips may not support setting the output value when a pin is configured as an input, although the current implementation assumes this is always possible. Add support for setting pin direction before value. The order defaults to setting the value first, but this can be reversed by setting the regmap_config.no_set_on_input flag, similar to the corresponding flag in the gpio-mmio driver. Signed-off-by: Sander Vanheule --- drivers/gpio/gpio-regmap.c | 20 +++++++++++++++++--- include/linux/gpio/regmap.h | 3 +++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index 134cedf151a7..1cdb20f8f8b4 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -170,14 +170,25 @@ static int gpio_regmap_direction_input(struct gpio_chip *chip, return gpio_regmap_set_direction(chip, offset, false); } -static int gpio_regmap_direction_output(struct gpio_chip *chip, - unsigned int offset, int value) +static int gpio_regmap_dir_out_val_first(struct gpio_chip *chip, + unsigned int offset, int value) { gpio_regmap_set(chip, offset, value); return gpio_regmap_set_direction(chip, offset, true); } +static int gpio_regmap_dir_out_dir_first(struct gpio_chip *chip, + unsigned int offset, int value) +{ + int err; + + err = gpio_regmap_set_direction(chip, offset, true); + gpio_regmap_set(chip, offset, value); + + return err; +} + void gpio_regmap_set_drvdata(struct gpio_regmap *gpio, void *data) { gpio->driver_data = data; @@ -277,7 +288,10 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config if (gpio->reg_dir_in_base || gpio->reg_dir_out_base) { chip->get_direction = gpio_regmap_get_direction; chip->direction_input = gpio_regmap_direction_input; - chip->direction_output = gpio_regmap_direction_output; + if (config->no_set_on_input) + chip->direction_output = gpio_regmap_dir_out_dir_first; + else + chip->direction_output = gpio_regmap_dir_out_val_first; } ret = gpiochip_add_data(chip, gpio); diff --git a/include/linux/gpio/regmap.h b/include/linux/gpio/regmap.h index 334dd928042b..2a732f8f23be 100644 --- a/include/linux/gpio/regmap.h +++ b/include/linux/gpio/regmap.h @@ -30,6 +30,8 @@ struct regmap; * @reg_dir_out_base: (Optional) out setting register base address * @reg_stride: (Optional) May be set if the registers (of the * same type, dat, set, etc) are not consecutive. + * @no_set_on_input: Set if output value can only be set when the direction + * is configured as output. * @ngpio_per_reg: Number of GPIOs per register * @irq_domain: (Optional) IRQ domain if the controller is * interrupt-capable @@ -73,6 +75,7 @@ struct gpio_regmap_config { unsigned int reg_dir_out_base; int reg_stride; int ngpio_per_reg; + bool no_set_on_input; struct irq_domain *irq_domain; int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base, From patchwork Mon May 17 19:28:06 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sander Vanheule X-Patchwork-Id: 440361 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7363CC43461 for ; Mon, 17 May 2021 19:28:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 59AC961261 for ; Mon, 17 May 2021 19:28:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234119AbhEQTaH (ORCPT ); Mon, 17 May 2021 15:30:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51788 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233695AbhEQTaC (ORCPT ); Mon, 17 May 2021 15:30:02 -0400 Received: from polaris.svanheule.net (polaris.svanheule.net [IPv6:2a00:c98:2060:a004:1::200]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 25332C061763 for ; Mon, 17 May 2021 12:28:44 -0700 (PDT) Received: from terra.local.svanheule.net (unknown [IPv6:2a02:a03f:eafb:ee01:404a:340a:91cb:c07b]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: sander@svanheule.net) by polaris.svanheule.net (Postfix) with ESMTPSA id ABA421FFBFA; Mon, 17 May 2021 21:28:42 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=svanheule.net; s=mail1707; t=1621279723; 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=UYjdfSw7ndqIS5WR6CeYrp/D/E+mLdRADgJD7MCncgI=; b=VPhcRk1CYZmz7Wq/HHkBPB8LqTwCAl+VwiSsivuOJ7G4ve5jDcE4pU1aWrp3o0ylfFrMJe aoj+5WmzQarhxUdnqLzOoaNGVqtUoanBTRRTyOXLeK3agXdeGve/aO6VoSWOVW5DQkMSki vopxVm3SFuCbU31T1LTkn9wekp3DsTf2oWyXIqz0Aq1Ve2IFYZjNF1GRiShxjBI6SbBH/7 Z3BH9AUaNwCnKUvkgocVFfB/MjDucgWSBMFXWUx4smeXRNxx9SgnQGf+Q9edcOf5VGjyGC pRjZIi3IG4hITPxPcFJsRMokoBNpOPv3liPTfTtEoOk1AExCzmyUe3sEBeMDUA== From: Sander Vanheule To: Pavel Machek , Rob Herring , Lee Jones , Mark Brown , Greg Kroah-Hartman , "Rafael J . Wysocki" , Michael Walle , Linus Walleij , Bartosz Golaszewski , linux-leds@vger.kernel.org, devicetree@vger.kernel.org, linux-gpio@vger.kernel.org Cc: Andrew Lunn , Andy Shevchenko , linux-kernel@vger.kernel.org, Sander Vanheule Subject: [PATCH v2 4/7] dt-bindings: mfd: Binding for RTL8231 Date: Mon, 17 May 2021 21:28:06 +0200 Message-Id: X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Add a binding description for the Realtek RTL8231, a GPIO and LED expander chip commonly used in ethernet switches based on a Realtek switch SoC. These chips can be addressed via an MDIO or SMI bus, or used as a plain 36-bit shift register. This binding only describes the feature set provided by the MDIO/SMI configuration, and covers the GPIO, PWM, and pin control properties. The LED properties are defined in a separate binding. Signed-off-by: Sander Vanheule Reviewed-by: Linus Walleij --- .../bindings/mfd/realtek,rtl8231.yaml | 202 ++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml diff --git a/Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml b/Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml new file mode 100644 index 000000000000..24ab7344c0c4 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml @@ -0,0 +1,202 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/realtek,rtl8231.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Realtek RTL8231 GPIO and LED expander. + +maintainers: + - Sander Vanheule + +description: | + The RTL8231 is a GPIO and LED expander chip, providing up to 37 GPIOs, up to + 88 LEDs, and up to one PWM output. This device is frequently used alongside + Realtek switch SoCs, to provide additional I/O capabilities. + + To manage the RTL8231's features, its strapping pins can be used to configure + it in one of three modes: shift register, MDIO device, or SMI device. The + shift register mode does not need special support. In MDIO or SMI mode, most + pins can be configured as a GPIO output, LED matrix scan line/column, or as a + PWM output. + + The GPIO and pin control are part of the main node. PWM and LED support are + configured as sub-nodes. + +properties: + compatible: + const: realtek,rtl8231 + + reg: + description: MDIO or SMI device address. + maxItems: 1 + + # GPIO support + gpio-controller: true + + "#gpio-cells": + const: 2 + description: | + The first cell is the pin number and the second cell is used to specify + the gpio active state. + + gpio-ranges: + description: | + Must reference itself, and provide a zero-based mapping for 37 pins. + maxItems: 1 + + # Pin muxing and configuration + realtek,drive-strength: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Common drive strength used for all GPIO output pins, must be 4mA or 8mA. + On reset, this value will default to 8mA. + enum: [4, 8] + + # LED scanning matrix + leds: + $ref: ../leds/realtek,rtl8231-leds.yaml# + + # PWM output + pwm: + type: object + description: | + Subnode describing the PWM peripheral. To use the PWM output, gpio35 must + be muxed to its 'pwm' function. Valid frequency values for consumers are + 1200, 1600, 2000, 2400, 2800, 3200, 4000, and 4800. + + properties: + "#pwm-cells": + description: | + Twos cells with PWM index (must be 0) and PWM frequency in Hz. + const: 2 + + required: + - "#pwm-cells" + +patternProperties: + "-pins$": + type: object + $ref: ../pinctrl/pinmux-node.yaml# + + properties: + pins: + items: + oneOf: + - enum: ["gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", + "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", + "gpio14", "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", + "gpio21", "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", + "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", + "gpio35", "gpio36"] + minItems: 1 + maxItems: 37 + function: + description: | + Select which function to use. "gpio" is supported for all pins, "led" is supported + for pins 0-34, "pwm" is supported for pin 35. + enum: ["gpio", "led", "pwm"] + + required: + - pins + - function + +required: + - compatible + - reg + - gpio-controller + - "#gpio-cells" + - gpio-ranges + +additionalProperties: false + +examples: + - | + // Minimal example + mdio { + #address-cells = <1>; + #size-cells = <0>; + + expander0: expander@0 { + compatible = "realtek,rtl8231"; + reg = <0>; + + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&expander0 0 0 37>; + }; + }; + - | + // All bells and whistles included + #include + mdio { + #address-cells = <1>; + #size-cells = <0>; + + expander1: expander@1 { + compatible = "realtek,rtl8231"; + reg = <1>; + + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&expander1 0 0 37>; + + realtek,drive-strength = <4>; + + button-pins { + pins = "gpio36"; + function = "gpio"; + input-debounce = "100000"; + }; + + pwm-pins { + pins = "gpio35"; + function = "pwm"; + }; + + led-pins { + pins = "gpio0", "gpio1", "gpio3", "gpio4"; + function = "led"; + }; + + pwm { + #pwm-cells = <2>; + }; + + leds { + compatible = "realtek,rtl8231-leds"; + #address-cells = <2>; + #size-cells = <0>; + + realtek,led-scan-mode = "single-color"; + + led@0,0 { + reg = <0 0>; + color = ; + function = LED_FUNCTION_LAN; + function-enumerator = <0>; + }; + + led@0,1 { + reg = <0 1>; + color = ; + function = LED_FUNCTION_LAN; + function-enumerator = <0>; + }; + + led@1,0 { + reg = <1 0>; + color = ; + function = LED_FUNCTION_LAN; + function-enumerator = <1>; + }; + + led@1,1 { + reg = <1 1>; + color = ; + function = LED_FUNCTION_LAN; + function-enumerator = <1>; + }; + }; + }; + }; From patchwork Mon May 17 19:28:08 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sander Vanheule X-Patchwork-Id: 440360 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3CE46C43461 for ; Mon, 17 May 2021 19:28:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 174CB611B0 for ; Mon, 17 May 2021 19:28:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234328AbhEQTaO (ORCPT ); Mon, 17 May 2021 15:30:14 -0400 Received: from polaris.svanheule.net ([84.16.241.116]:57522 "EHLO polaris.svanheule.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233990AbhEQTaE (ORCPT ); Mon, 17 May 2021 15:30:04 -0400 Received: from terra.local.svanheule.net (unknown [IPv6:2a02:a03f:eafb:ee01:404a:340a:91cb:c07b]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: sander@svanheule.net) by polaris.svanheule.net (Postfix) with ESMTPSA id AA0F21FFBFC; Mon, 17 May 2021 21:28:46 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=svanheule.net; s=mail1707; t=1621279727; 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=ifWs/Wlm7tVHLpu2tgxONBVrNkwSFg9g2R3M0Tho0Rk=; b=tWL/yPzxu3FA5J6vEduvTu+kxGJ3etVSPJp3g2bUn5KW9sV9yNRovEzi9gxiHehH7E94qF 2yshNmDZmH7cBJvZWlJFZ/bh/HL0h7NyyxX1nL8G5dVHUuZ/FOihrIi7IJJ36z07YDfueU aLp2wUP6QQXxYhlhryH+CP9TRst5Z7lIRuUkSlZYrjt81ZmssWREXlmkeT9P7jAvIlxtv9 EZ1Mb3hXmmsGecptiiU9qbtLXh6JF04Z/InnW3jT5g8YWVlhyu9VjxgIL5Hzb48dk6M4tc 1TkDXZIVewPm+C9e4QVG9yiLH0Ig11tk1P/G7/7ASn6coSmjtmPhm8NTTo/d4Q== From: Sander Vanheule To: Pavel Machek , Rob Herring , Lee Jones , Mark Brown , Greg Kroah-Hartman , "Rafael J . Wysocki" , Michael Walle , Linus Walleij , Bartosz Golaszewski , linux-leds@vger.kernel.org, devicetree@vger.kernel.org, linux-gpio@vger.kernel.org Cc: Andrew Lunn , Andy Shevchenko , linux-kernel@vger.kernel.org, Sander Vanheule Subject: [PATCH v2 6/7] pinctrl: Add RTL8231 pin control and GPIO support Date: Mon, 17 May 2021 21:28:08 +0200 Message-Id: <041077d195f1cc81bf6363388cb4adfb06cff4ef.1621279162.git.sander@svanheule.net> X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org 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. Although described in the bindings, pin debouncing and drive strength selection are currently not implemented. Debouncing is only available for the six highest GPIOs, and must be emulated when other pins are used for (button) inputs anyway. Signed-off-by: Sander Vanheule --- drivers/pinctrl/Kconfig | 11 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-rtl8231.c | 377 ++++++++++++++++++++++++++++++ 3 files changed, 389 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-rtl8231.c diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index c2c7e7963ed0..462df82c5133 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -221,6 +221,17 @@ config PINCTRL_ROCKCHIP help This support pinctrl and gpio driver for Rockchip SoCs. +config PINCTRL_RTL8231 + tristate "Realtek RTL8231 GPIO expander's pin controller" + depends on MFD_RTL8231 + default MFD_RTL8231 + select GENERIC_PINCONF + select GPIO_REGMAP + select PINMUX + help + Support for RTL8231 expander's GPIOs and pin controller. + When built as a module, the module will be called rtl8231_pinctrl. + config PINCTRL_SINGLE tristate "One-register-per-pin type device tree based pinctrl driver" depends on OF diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 5ef5334a797f..239603efb317 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_PINCTRL_PALMAS) += pinctrl-palmas.o obj-$(CONFIG_PINCTRL_PIC32) += pinctrl-pic32.o obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o +obj-$(CONFIG_PINCTRL_RTL8231) += pinctrl-rtl8231.o obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o obj-$(CONFIG_PINCTRL_SX150X) += pinctrl-sx150x.o obj-$(CONFIG_ARCH_TEGRA) += tegra/ diff --git a/drivers/pinctrl/pinctrl-rtl8231.c b/drivers/pinctrl/pinctrl-rtl8231.c new file mode 100644 index 000000000000..44f08ba39f14 --- /dev/null +++ b/drivers/pinctrl/pinctrl-rtl8231.c @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define RTL8231_NUM_GPIOS 37 + +struct rtl8231_function { + const char *name; + unsigned int ngroups; + const char **groups; +}; + +struct rtl8231_pin_ctrl { + struct pinctrl_desc pctl_desc; + unsigned int nfunctions; + struct rtl8231_function *functions; + struct regmap *map; +}; + +/* + * Pin controller functionality + */ +static const char * const rtl8231_pin_function_names[] = { + "gpio", + "led", + "pwm", +}; + +enum rtl8231_pin_function { + RTL8231_PIN_FUNCTION_GPIO = BIT(0), + RTL8231_PIN_FUNCTION_LED = BIT(1), + RTL8231_PIN_FUNCTION_PWM = BIT(2), +}; + +struct rtl8231_pin_desc { + unsigned int number; + const char *name; + enum rtl8231_pin_function functions; + u8 reg; + u8 offset; + u8 gpio_function_value; +}; + +#define RTL8231_PIN(_num, _func, _reg, _fld, _val) \ + { \ + .number = _num, \ + .name = "gpio" #_num, \ + .functions = RTL8231_PIN_FUNCTION_GPIO | _func, \ + .reg = _reg, \ + .offset = _fld, \ + .gpio_function_value = _val, \ + } +#define RTL8231_GPIO_PIN(_num) \ + RTL8231_PIN(_num, 0, 0, 0, 0) +#define RTL8231_LED_PIN(_num, _reg, _fld) \ + RTL8231_PIN(_num, RTL8231_PIN_FUNCTION_LED, _reg, _fld, RTL8231_PIN_MODE_GPIO) +#define RTL8231_PWM_PIN(_num, _reg, _fld) \ + RTL8231_PIN(_num, RTL8231_PIN_FUNCTION_PWM, _reg, _fld, 0) + +/* Pins always support GPIO, and may support one alternate function */ +static const struct rtl8231_pin_desc rtl8231_pins[RTL8231_NUM_GPIOS] = { + RTL8231_LED_PIN(0, RTL8231_REG_PIN_MODE0, 0), + RTL8231_LED_PIN(1, RTL8231_REG_PIN_MODE0, 1), + RTL8231_LED_PIN(2, RTL8231_REG_PIN_MODE0, 2), + RTL8231_LED_PIN(3, RTL8231_REG_PIN_MODE0, 3), + RTL8231_LED_PIN(4, RTL8231_REG_PIN_MODE0, 4), + RTL8231_LED_PIN(5, RTL8231_REG_PIN_MODE0, 5), + RTL8231_LED_PIN(6, RTL8231_REG_PIN_MODE0, 6), + RTL8231_LED_PIN(7, RTL8231_REG_PIN_MODE0, 7), + RTL8231_LED_PIN(8, RTL8231_REG_PIN_MODE0, 8), + RTL8231_LED_PIN(9, RTL8231_REG_PIN_MODE0, 9), + RTL8231_LED_PIN(10, RTL8231_REG_PIN_MODE0, 10), + RTL8231_LED_PIN(11, RTL8231_REG_PIN_MODE0, 11), + RTL8231_LED_PIN(12, RTL8231_REG_PIN_MODE0, 12), + RTL8231_LED_PIN(13, RTL8231_REG_PIN_MODE0, 13), + RTL8231_LED_PIN(14, RTL8231_REG_PIN_MODE0, 14), + RTL8231_LED_PIN(15, RTL8231_REG_PIN_MODE0, 15), + RTL8231_LED_PIN(16, RTL8231_REG_PIN_MODE1, 0), + RTL8231_LED_PIN(17, RTL8231_REG_PIN_MODE1, 1), + RTL8231_LED_PIN(18, RTL8231_REG_PIN_MODE1, 2), + RTL8231_LED_PIN(19, RTL8231_REG_PIN_MODE1, 3), + RTL8231_LED_PIN(20, RTL8231_REG_PIN_MODE1, 4), + RTL8231_LED_PIN(21, RTL8231_REG_PIN_MODE1, 5), + RTL8231_LED_PIN(22, RTL8231_REG_PIN_MODE1, 6), + RTL8231_LED_PIN(23, RTL8231_REG_PIN_MODE1, 7), + RTL8231_LED_PIN(24, RTL8231_REG_PIN_MODE1, 8), + RTL8231_LED_PIN(25, RTL8231_REG_PIN_MODE1, 9), + RTL8231_LED_PIN(26, RTL8231_REG_PIN_MODE1, 10), + RTL8231_LED_PIN(27, RTL8231_REG_PIN_MODE1, 11), + RTL8231_LED_PIN(28, RTL8231_REG_PIN_MODE1, 12), + RTL8231_LED_PIN(29, RTL8231_REG_PIN_MODE1, 13), + RTL8231_LED_PIN(30, RTL8231_REG_PIN_MODE1, 14), + RTL8231_LED_PIN(31, RTL8231_REG_PIN_MODE1, 15), + RTL8231_LED_PIN(32, RTL8231_REG_PIN_HI_CFG, 0), + RTL8231_LED_PIN(33, RTL8231_REG_PIN_HI_CFG, 1), + RTL8231_LED_PIN(34, RTL8231_REG_PIN_HI_CFG, 2), + RTL8231_PWM_PIN(35, RTL8231_REG_FUNC1, 3), + RTL8231_GPIO_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, unsigned int selector) +{ + return rtl8231_pins[selector].name; +} + +static int rtl8231_get_group_pins(struct pinctrl_dev *pctldev, unsigned int selector, + const unsigned int **pins, unsigned int *num_pins) +{ + if (selector < ARRAY_SIZE(rtl8231_pins)) { + *pins = &rtl8231_pins[selector].number; + *num_pins = 1; + return 0; + } + + return -EINVAL; +} + +static const struct pinctrl_ops rtl8231_pinctrl_ops = { + .get_groups_count = rtl8231_get_groups_count, + .get_group_name = rtl8231_get_group_name, + .get_group_pins = rtl8231_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map_all, + .dt_free_map = pinconf_generic_dt_free_map, +}; + +static int rtl8231_get_functions_count(struct pinctrl_dev *pctldev) +{ + struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev); + + return ctrl->nfunctions; +} + +static const char *rtl8231_get_function_name(struct pinctrl_dev *pctldev, unsigned int selector) +{ + struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev); + + return ctrl->functions[selector].name; +} + +static int rtl8231_get_function_groups(struct pinctrl_dev *pctldev, unsigned int selector, + const char * const **groups, unsigned int *num_groups) +{ + struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev); + + *groups = ctrl->functions[selector].groups; + *num_groups = ctrl->functions[selector].ngroups; + return 0; +} + +static int rtl8231_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector, + unsigned int group_selector) +{ + struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev); + const struct rtl8231_pin_desc *desc = &rtl8231_pins[group_selector]; + unsigned int func_flag = BIT(func_selector); + unsigned int function_mask; + unsigned int gpio_function; + int err = 0; + + if (!(desc->functions & func_flag)) + return -EINVAL; + + function_mask = BIT(desc->offset); + gpio_function = desc->gpio_function_value << desc->offset; + + switch (func_flag) { + case RTL8231_PIN_FUNCTION_LED: + case RTL8231_PIN_FUNCTION_PWM: + err = regmap_update_bits(ctrl->map, desc->reg, function_mask, ~gpio_function); + break; + case RTL8231_PIN_FUNCTION_GPIO: + err = regmap_update_bits(ctrl->map, desc->reg, function_mask, gpio_function); + break; + default: + return -EINVAL; + } + + return err; +} + +static int rtl8231_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, unsigned int offset) +{ + struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev); + const struct rtl8231_pin_desc *desc = &rtl8231_pins[offset]; + unsigned int function_mask; + unsigned int gpio_function; + + function_mask = BIT(desc->offset); + gpio_function = desc->gpio_function_value << desc->offset; + + return regmap_update_bits(ctrl->map, desc->reg, function_mask, gpio_function); +} + +static const struct pinmux_ops rtl8231_pinmux_ops = { + .set_mux = rtl8231_set_mux, + .get_functions_count = rtl8231_get_functions_count, + .get_function_name = rtl8231_get_function_name, + .get_function_groups = rtl8231_get_function_groups, + .gpio_request_enable = rtl8231_gpio_request_enable, + .strict = true +}; + + +static int rtl8231_pinctrl_init_functions(struct device *dev, struct rtl8231_pin_ctrl *ctrl) +{ + struct rtl8231_function *function; + const char **group_name; + unsigned int f_idx; + unsigned int pin; + + ctrl->nfunctions = ARRAY_SIZE(rtl8231_pin_function_names); + ctrl->functions = devm_kcalloc(dev, ctrl->nfunctions, sizeof(*ctrl->functions), GFP_KERNEL); + if (IS_ERR(ctrl->functions)) { + dev_err(dev, "failed to allocate pin function descriptors\n"); + return PTR_ERR(ctrl->functions); + } + + for (f_idx = 0; f_idx < ctrl->nfunctions; f_idx++) { + function = &ctrl->functions[f_idx]; + function->name = rtl8231_pin_function_names[f_idx]; + + for (pin = 0; pin < ctrl->pctl_desc.npins; pin++) + if (rtl8231_pins[pin].functions & BIT(f_idx)) + function->ngroups++; + + function->groups = devm_kcalloc(dev, function->ngroups, + sizeof(*function->groups), GFP_KERNEL); + if (IS_ERR(function->groups)) { + dev_err(dev, "failed to allocate pin function group names\n"); + return PTR_ERR(function->groups); + } + + group_name = function->groups; + for (pin = 0; pin < ctrl->pctl_desc.npins; pin++) + if (rtl8231_pins[pin].functions & BIT(f_idx)) + *group_name++ = rtl8231_pins[pin].name; + } + + return 0; +} + +static int rtl8231_pinctrl_init(struct device *dev, struct rtl8231_pin_ctrl *ctrl) +{ + struct pinctrl_dev *pctl; + struct pinctrl_pin_desc *pins; + unsigned int pin; + int err = 0; + + ctrl->pctl_desc.name = "rtl8231-pinctrl", + ctrl->pctl_desc.owner = THIS_MODULE, + ctrl->pctl_desc.pctlops = &rtl8231_pinctrl_ops, + ctrl->pctl_desc.pmxops = &rtl8231_pinmux_ops, + + ctrl->pctl_desc.npins = ARRAY_SIZE(rtl8231_pins); + pins = devm_kcalloc(dev, ctrl->pctl_desc.npins, sizeof(*pins), GFP_KERNEL); + if (IS_ERR(pins)) { + dev_err(dev, "failed to allocate pin descriptors\n"); + return PTR_ERR(pins); + } + ctrl->pctl_desc.pins = pins; + + for (pin = 0; pin < ctrl->pctl_desc.npins; pin++) { + pins[pin].number = rtl8231_pins[pin].number; + pins[pin].name = rtl8231_pins[pin].name; + } + + err = rtl8231_pinctrl_init_functions(dev, ctrl); + if (err) + return err; + + err = devm_pinctrl_register_and_init(dev->parent, &ctrl->pctl_desc, ctrl, &pctl); + if (err) { + dev_err(dev, "failed to register pin controller\n"); + return err; + } + + err = pinctrl_enable(pctl); + 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 = BIT(offset % RTL8231_BITS_VAL); + + if (base == RTL8231_REG_GPIO_DATA0 || offset < 32) { + *reg = base + offset / RTL8231_BITS_VAL; + *mask = pin_mask; + } else if (base == RTL8231_REG_GPIO_DIR0) { + *reg = RTL8231_REG_PIN_HI_CFG; + *mask = 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 = &pdev->dev; + struct rtl8231_pin_ctrl *ctrl; + struct gpio_regmap_config gpio_cfg = {}; + struct gpio_regmap *gr; + int err; + + ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + ctrl->map = dev_get_regmap(dev->parent, NULL); + if (IS_ERR_OR_NULL(ctrl->map)) { + dev_err(dev, "failed to retrieve regmap\n"); + if (!ctrl->map) + return -ENODEV; + else + return PTR_ERR(ctrl->map); + } + + err = rtl8231_pinctrl_init(dev, ctrl); + if (err) + return err; + + gpio_cfg.regmap = ctrl->map; + gpio_cfg.parent = dev->parent; + gpio_cfg.ngpio = RTL8231_NUM_GPIOS; + gpio_cfg.ngpio_per_reg = RTL8231_BITS_VAL; + + gpio_cfg.reg_dat_base = GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DATA0); + gpio_cfg.reg_set_base = GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DATA0); + gpio_cfg.reg_dir_in_base = GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DIR0); + gpio_cfg.no_set_on_input = true; + + gpio_cfg.reg_mask_xlate = rtl8231_gpio_reg_mask_xlate; + + gr = devm_gpio_regmap_register(dev, &gpio_cfg); + if (IS_ERR(gr)) { + dev_err(dev, "failed to register gpio controller\n"); + return PTR_ERR(gr); + } + + return 0; +} + +static struct platform_driver rtl8231_pinctrl_driver = { + .driver = { + .name = "rtl8231-pinctrl", + }, + .probe = 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 v2");