From patchwork Sun May 23 22:33:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sander Vanheule X-Patchwork-Id: 446613 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.7 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 B3486C47082 for ; Sun, 23 May 2021 22:34:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 93ABF600EF for ; Sun, 23 May 2021 22:34:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232064AbhEWWf6 (ORCPT ); Sun, 23 May 2021 18:35:58 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45480 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232068AbhEWWfz (ORCPT ); Sun, 23 May 2021 18:35:55 -0400 Received: from polaris.svanheule.net (polaris.svanheule.net [IPv6:2a00:c98:2060:a004:1::200]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 36DF2C06138D for ; Sun, 23 May 2021 15:34:28 -0700 (PDT) Received: from terra.local.svanheule.net (unknown [IPv6:2a02:a03f:eafb:ee01:bd37:7535:eb00:6fa]) (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 8CB68202A54; Mon, 24 May 2021 00:34:26 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=svanheule.net; s=mail1707; t=1621809266; 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=2X9pHkF5aWPdiW7BSlrKumYHJyz+s9wXhroqdIN+ocs=; b=tMS0KhEeMuk9/kBXf8DmZK8AatGSiDaVH2yZQeBLmTN7sb1M4GtXU7Fkvnu4cdUrVIJWt3 fnKBWsqlfQn/Pq36mIRaPtiLLZb36e7w0aOs16fkHn8goHK2eFpLTZb33vVdtp8mK/i2D+ ytCyHbVjDQeI7xvJHEhxObV/QpPuzsswYlKAKKUms3Rrc7O3e+FDkkimm5TXPHk3k23RNi LX7wsNDqJjqp2PoZI/OtKi3iPVmzCx5OsM1yncB39C2LZBPD0L5RYQL/G3L9tseHFqSXm/ Z1jZscv/PUOzBHvTth8x+3gS4yH8IexzwQ/hxnxJY0opTmyWJx0zTTaQs+0TCQ== 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 v3 1/6] gpio: regmap: Add quirk for output data register Date: Mon, 24 May 2021 00:33:59 +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-gpio@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 GPIO_REGMAP_QUIRK_SET_DIRECTION_FIRST flag in regmap_config.quirks. Signed-off-by: Sander Vanheule Reviewed-by: Michael Walle Acked-by: Bartosz Golaszewski --- drivers/gpio/gpio-regmap.c | 15 +++++++++++++-- include/linux/gpio/regmap.h | 13 +++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index 134cedf151a7..95553734e169 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -15,6 +15,7 @@ struct gpio_regmap { struct device *parent; struct regmap *regmap; struct gpio_chip gpio_chip; + unsigned int quirks; int reg_stride; int ngpio_per_reg; @@ -173,9 +174,18 @@ static int gpio_regmap_direction_input(struct gpio_chip *chip, static int gpio_regmap_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { - gpio_regmap_set(chip, offset, value); + struct gpio_regmap *gpio = gpiochip_get_data(chip); + int ret; + + if (gpio->quirks & GPIO_REGMAP_QUIRK_SET_DIRECTION_FIRST) { + ret = gpio_regmap_set_direction(chip, offset, true); + gpio_regmap_set(chip, offset, value); + } else { + gpio_regmap_set(chip, offset, value); + ret = gpio_regmap_set_direction(chip, offset, true); + } - return gpio_regmap_set_direction(chip, offset, true); + return ret; } void gpio_regmap_set_drvdata(struct gpio_regmap *gpio, void *data) @@ -227,6 +237,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config gpio->parent = config->parent; gpio->regmap = config->regmap; + gpio->quirks = config->quirks; gpio->ngpio_per_reg = config->ngpio_per_reg; gpio->reg_stride = config->reg_stride; gpio->reg_mask_xlate = config->reg_mask_xlate; diff --git a/include/linux/gpio/regmap.h b/include/linux/gpio/regmap.h index 334dd928042b..cb609489903e 100644 --- a/include/linux/gpio/regmap.h +++ b/include/linux/gpio/regmap.h @@ -12,6 +12,17 @@ struct regmap; #define GPIO_REGMAP_ADDR_ZERO ((unsigned int)(-1)) #define GPIO_REGMAP_ADDR(addr) ((addr) ? : GPIO_REGMAP_ADDR_ZERO) +enum gpio_regmap_quirk { + /* + * For hardware where the pin output value cannot be set while the pin + * is configured as an input. Resolve by setting the direction to + * output first, and the new value second. Because the previous output + * value is used immediately after the direction change, this may result + * in glitches. + */ + GPIO_REGMAP_QUIRK_SET_DIRECTION_FIRST = BIT(0), +}; + /** * struct gpio_regmap_config - Description of a generic regmap gpio_chip. * @parent: The parent device @@ -31,6 +42,7 @@ struct regmap; * @reg_stride: (Optional) May be set if the registers (of the * same type, dat, set, etc) are not consecutive. * @ngpio_per_reg: Number of GPIOs per register + * @quirks: Flags indicating GPIO chip hardware issues * @irq_domain: (Optional) IRQ domain if the controller is * interrupt-capable * @reg_mask_xlate: (Optional) Translates base address and GPIO @@ -73,6 +85,7 @@ struct gpio_regmap_config { unsigned int reg_dir_out_base; int reg_stride; int ngpio_per_reg; + unsigned int quirks; struct irq_domain *irq_domain; int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base, From patchwork Sun May 23 22:34:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Sander Vanheule X-Patchwork-Id: 446311 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.7 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 DDF92C47084 for ; Sun, 23 May 2021 22:34:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BD68B61285 for ; Sun, 23 May 2021 22:34:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232068AbhEWWf7 (ORCPT ); Sun, 23 May 2021 18:35:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45486 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232063AbhEWWf5 (ORCPT ); Sun, 23 May 2021 18:35: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 22E24C06138E for ; Sun, 23 May 2021 15:34:29 -0700 (PDT) Received: from terra.local.svanheule.net (unknown [IPv6:2a02:a03f:eafb:ee01:bd37:7535:eb00:6fa]) (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 4C431202A55; Mon, 24 May 2021 00:34:28 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=svanheule.net; s=mail1707; t=1621809268; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=vGdZ43LpkiQo5K5F5OyjmRza+0PXAyFm+PCgTkbbYrg=; b=Zy9bQIrRDJ+oZVJjmxfNJ7uYafF7sd9Z1bHB/8huJbymxgibyKSXM/IrVM/PnfmzDGi2MZ zt+rfJbqQghhR+EsAwjtMECmqz7G1cqXGwRvdWxnxsEogSSuHzyb1zvxfCJugLzjZb1QKM GDTPdbJdGmVkf4IhASSJ+OvcxdzcMxSVvc0r7uWAotoNlmvwVY+N28Um6vfvNjFTbTs6KE r/PY+LuB0R+RiK4Q0L0Bd9YEKuAsH5r+my1HLHGbAIviV1dbo6++yFlNPWkmHbnvleEBdA MWikTiGtyjSFSICqyWsFZ2QNrF96kJtNUZNCRAz4mhA0orsbxCkKDq1gWDbu7w== 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 v3 2/6] dt-bindings: leds: Binding for RTL8231 scan matrix Date: Mon, 24 May 2021 00:34:00 +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-gpio@vger.kernel.org Add a binding description for the Realtek RTL8231's LED support, which consists of up to 88 LEDs arranged in a number of scanning matrices. Signed-off-by: Sander Vanheule --- .../bindings/leds/realtek,rtl8231-leds.yaml | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml diff --git a/Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml b/Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml new file mode 100644 index 000000000000..560249cd7e8c --- /dev/null +++ b/Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml @@ -0,0 +1,166 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/realtek,rtl8231-leds.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Realtek RTL8231 LED scan matrix. + +maintainers: + - Sander Vanheule + +description: | + The RTL8231 has support for driving a number of LED matrices, by scanning + over the LEDs pins, alternatingly lighting different columns and/or rows. + + This functionality is available on an RTL8231, when it is configured for use + as an MDIO device, or SMI device. + + In single color scan mode, 88 LEDs are supported. These are grouped into + three output matrices: + - Group A of 6×6 single color LEDs. Rows and columns are driven by GPIO + pins 0-11. + L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6] + | | | | | | + P0/P6 --<--------<--------<--------<--------<--------< (3) + | | | | | | + P1/P7 --<--------<--------<--------<--------<--------< (4) + | | | | | | + P2/P8 --<--------<--------<--------<--------<--------< (5) + | | | | | | + P3/P9 --<--------<--------<--------<--------<--------< (6) + | | | | | | + P4/P10 --<--------<--------<--------<--------<--------< (7) + | | | | | | + P5/P11 --<--------<--------<--------<--------<--------< (8) + (0) (1) (2) (9) (10) (11) + - Group B of 6×6 single color LEDs. Rows and columns are driven by GPIO + pins 12-23. + L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6] + | | | | | | + P12/P18 --<--------<--------<--------<--------<--------< (15) + | | | | | | + P13/P19 --<--------<--------<--------<--------<--------< (16) + | | | | | | + P14/P20 --<--------<--------<--------<--------<--------< (17) + | | | | | | + P15/P21 --<--------<--------<--------<--------<--------< (18) + | | | | | | + P16/P22 --<--------<--------<--------<--------<--------< (19) + | | | | | | + P17/P23 --<--------<--------<--------<--------<--------< (20) + (12) (13) (14) (21) (22) (23) + - Group C of 8 pairs of anti-parallel (or bi-color) LEDs. LED selection is + provided by GPIO pins 24-27 and 29-32, polarity selection by GPIO 28. + P24 P25 ... P30 P31 + | | | | + LED POL --X-------X---/\/---X-------X (28) + (24) (25) ... (31) (32) + + In bi-color scan mode, 72 LEDs are supported. These are grouped into four + output matrices: + - Group A of 12 pairs of anti-parallel LEDs. LED selection is provided + by GPIO pins 0-11, polarity selection by GPIO 12. + - Group B of 6 pairs of anti-parallel LEDs. LED selection is provided + by GPIO pins 23-28, polarity selection by GPIO 21. + - Group C of 6 pairs of anti-parallel LEDs. LED selection is provided + by GPIO pins 29-34, polarity selection by GPIO 22. + - Group of 4×6 single color LEDs. Rows are driven by GPIO pins 15-20, + columns by GPIO pins 13-14 and 21-22 (shared with groups B and C). + L2[n] L2[n+6] L2[n+12] L2[n+18] + | | | | + +0 --<--------<---------<---------< (15) + | | | | + +1 --<--------<---------<---------< (16) + | | | | + +2 --<--------<---------<---------< (17) + | | | | + +3 --<--------<---------<---------< (18) + | | | | + +4 --<--------<---------<---------< (19) + | | | | + +6 --<--------<---------<---------< (20) + (13) (14) (21) (22) + + This node must always be a child of a 'realtek,rtl8231' node. + +properties: + $nodename: + const: led-controller + + compatible: + const: realtek,rtl8231-leds + + "#address-cells": + const: 2 + + "#size-cells": + const: 0 + + realtek,led-scan-mode: + $ref: /schemas/types.yaml#/definitions/string + description: | + Specify the scanning mode the chip should run in. See general description + for how the scanning matrices are wired up. + enum: ["single-color", "bi-color"] + +patternProperties: + "^led@": + description: | + LEDs are addressed by their port index and led index. Ports 0-23 always + support three LEDs. Additionally, but only when used in single color scan + mode, ports 24-31 support two LEDs. + type: object + + properties: + reg: + items: + - description: port index + maximum: 31 + - description: led index + maximum: 2 + + allOf: + - $ref: ../leds/common.yaml# + + required: + - reg + +required: + - compatible + - "#address-cells" + - "#size-cells" + - realtek,led-scan-mode + +additionalProperties: false + +examples: + - | + #include + led-controller { + 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@0,2 { + reg = <0 2>; + color = ; + function = LED_FUNCTION_STATUS; + }; + }; From patchwork Sun May 23 22:34:01 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sander Vanheule X-Patchwork-Id: 446612 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.7 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 1AC0DC47086 for ; Sun, 23 May 2021 22:34:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E59AF6117A for ; Sun, 23 May 2021 22:34:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232063AbhEWWgC (ORCPT ); Sun, 23 May 2021 18:36:02 -0400 Received: from polaris.svanheule.net ([84.16.241.116]:44768 "EHLO polaris.svanheule.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232080AbhEWWf6 (ORCPT ); Sun, 23 May 2021 18:35:58 -0400 Received: from terra.local.svanheule.net (unknown [IPv6:2a02:a03f:eafb:ee01:bd37:7535:eb00:6fa]) (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 E2FF9202A56; Mon, 24 May 2021 00:34:29 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=svanheule.net; s=mail1707; t=1621809270; 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=NDjmeUYm4ktr9jhuh5TpCjptsLJ0yr15QKyCnRuywZM=; b=AdWtpz5qP8irtuYxwS+2mPW0XgzA/YbcjMNtzz9zRqBLGYQ1iRhPXoD8uHXIXOywGs/HhF 4vBbGo8d5nX6ixi3rt8bgvijjHvdYLjWgiYnvgqnI/hs/8GL2Dmh+mds6hcp8hqqjCV7JG ABSsIBM8Zh24ttgLOBadjJ+Rh4/rDcqUPNhXjG/hS7QoPsBtlF5Zr7kD2gYD7vGGSpauav /zLeC9mPbaji240KCPerXXEI34IVpY1uZGfXnMHxfS7s6/rtchzKaD8j2Rv+9VxVq36mZt 0XGvgqqXginC+vnHBqddvFV1K5cFwr2jhMR9xy/jce1Aa+PU3AlzFv9bBvht/A== 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 v3 3/6] dt-bindings: mfd: Binding for RTL8231 Date: Mon, 24 May 2021 00:34:01 +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-gpio@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 Reviewed-by: Rob Herring --- .../bindings/mfd/realtek,rtl8231.yaml | 190 ++++++++++++++++++ 1 file changed, 190 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..4e2326401560 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml @@ -0,0 +1,190 @@ +# 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, PWM, and pin control are part of the main node. LED support is + configured as a sub-node. + +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 + drive-strength: + 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 + led-controller: + $ref: ../leds/realtek,rtl8231-leds.yaml# + + # PWM output + "#pwm-cells": + description: | + Twos cells with PWM index (must be 0) and PWM frequency in Hz. 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. + const: 2 + +patternProperties: + "-pins$": + type: object + $ref: ../pinctrl/pinmux-node.yaml# + + properties: + pins: + items: + 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>; + + #pwm-cells = <2>; + + 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"; + }; + + led-controller { + 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 Sun May 23 22:34:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sander Vanheule X-Patchwork-Id: 446310 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.7 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 49458C4708B for ; Sun, 23 May 2021 22:34:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 2DBD06138C for ; Sun, 23 May 2021 22:34:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232110AbhEWWgD (ORCPT ); Sun, 23 May 2021 18:36:03 -0400 Received: from polaris.svanheule.net ([84.16.241.116]:44808 "EHLO polaris.svanheule.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232091AbhEWWgA (ORCPT ); Sun, 23 May 2021 18:36:00 -0400 Received: from terra.local.svanheule.net (unknown [IPv6:2a02:a03f:eafb:ee01:bd37:7535:eb00:6fa]) (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 7E1E9202A58; Mon, 24 May 2021 00:34:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=svanheule.net; s=mail1707; t=1621809271; 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=ZRfiygicpeF22Dq/dR7HJ3wM2Y7ZWAScni3DBGZKc34=; b=c0nfF2GMW3Zf86F2gyj0b/UnnAq5mFe/WkW285KIaGtu527/IDCTlch2fSlKmi2tk80MXL QLq7dlzClpIvJ0WC7edHFdLl6RBXpmDAETHHO/oep/6+ZyY1AacTeBMbXVrQcUl228CVgI uoPSlOrp3ybDXYfq8HmyLqBgddYYKkFEuwZWaJFTOv1uFuDS+RQoCxrxF5uOt64x2Rg2xY qF3ttBij9lu2avkNzuRLwT1POjZ+30poFhbC8LgOmJjZnNkaAorHNjFosL+07RBXi2LU/2 mIuw0j8xAjifpRmPAEyLizPDn9MGR19QRwOw83GkhfU0tauc/3YHLBk1j1YtVg== 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 , kernel test robot Subject: [PATCH v3 4/6] mfd: Add RTL8231 core device Date: Mon, 24 May 2021 00:34:02 +0200 Message-Id: <6d14b72bc545a818675d99c8e91e99c96cc3e286.1621809029.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-gpio@vger.kernel.org The RTL8231 is implemented as an MDIO device, and provides a regmap interface for register access by the core and child devices. The chip can also be a device on an SMI bus, an I2C-like bus by Realtek. Since kernel support for SMI is limited, and no real-world SMI implementations have been encountered for this device, this is currently unimplemented. The use of the regmap interface should make any future support relatively straightforward. After reset, all pins are muxed to GPIO inputs before the pin drivers are enabled. This is done to prevent accidental system resets, when a pin is connected to the parent SoC's reset line. [missing MDIO_BUS dependency, provided via REGMAP_MDIO] Reported-by: kernel test robot Signed-off-by: Sander Vanheule --- drivers/mfd/Kconfig | 9 +++ drivers/mfd/Makefile | 1 + drivers/mfd/rtl8231.c | 143 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/rtl8231.h | 57 ++++++++++++++ 4 files changed, 210 insertions(+) create mode 100644 drivers/mfd/rtl8231.c create mode 100644 include/linux/mfd/rtl8231.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 5c7f2b100191..68f28a335b8c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1076,6 +1076,15 @@ config MFD_RDC321X southbridge which provides access to GPIOs and Watchdog using the southbridge PCI device configuration space. +config MFD_RTL8231 + tristate "Realtek RTL8231 GPIO and LED expander" + select MFD_CORE + select REGMAP_MDIO + help + Support for the Realtek RTL8231 GPIO and LED expander. + Provides up to 37 GPIOs, 88 LEDs, and one PWM output. + When built as a module, this module will be named rtl8231. + config MFD_RT5033 tristate "Richtek RT5033 Power Management IC" depends on I2C diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 4f6d2b8a5f76..4b27c2486ccc 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -234,6 +234,7 @@ obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o obj-$(CONFIG_MFD_HI655X_PMIC) += hi655x-pmic.o obj-$(CONFIG_MFD_DLN2) += dln2.o +obj-$(CONFIG_MFD_RTL8231) += rtl8231.o obj-$(CONFIG_MFD_RT5033) += rt5033.o obj-$(CONFIG_MFD_SKY81452) += sky81452.o diff --git a/drivers/mfd/rtl8231.c b/drivers/mfd/rtl8231.c new file mode 100644 index 000000000000..559afc44a0c7 --- /dev/null +++ b/drivers/mfd/rtl8231.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static const struct reg_field RTL8231_FIELD_LED_START = REG_FIELD(RTL8231_REG_FUNC0, 1, 1); + +static const struct mfd_cell rtl8231_cells[] = { + { + .name = "rtl8231-pinctrl", + }, + { + .name = "rtl8231-leds", + .of_compatible = "realtek,rtl8231-leds", + }, +}; + +static int rtl8231_init(struct device *dev, struct regmap *map) +{ + unsigned int val; + int err; + + err = regmap_read(map, RTL8231_REG_FUNC1, &val); + if (err) { + dev_err(dev, "failed to read READY_CODE\n"); + return err; + } + + val = FIELD_GET(RTL8231_FUNC1_READY_CODE_MASK, val); + if (val != RTL8231_FUNC1_READY_CODE_VALUE) { + dev_err(dev, "RTL8231 not present or ready 0x%x != 0x%x\n", + val, RTL8231_FUNC1_READY_CODE_VALUE); + return -ENODEV; + } + + /* SOFT_RESET bit self-clears when done */ + regmap_update_bits(map, RTL8231_REG_PIN_HI_CFG, + RTL8231_PIN_HI_CFG_SOFT_RESET, RTL8231_PIN_HI_CFG_SOFT_RESET); + usleep_range(1000, 10000); + + /* + * Chip reset results in a pin configuration that is a mix of LED and GPIO outputs. + * Select GPI functionality for all pins before enabling pin outputs. + */ + regmap_write(map, RTL8231_REG_PIN_MODE0, 0xffff); + regmap_write(map, RTL8231_REG_GPIO_DIR0, 0xffff); + regmap_write(map, RTL8231_REG_PIN_MODE1, 0xffff); + regmap_write(map, RTL8231_REG_GPIO_DIR1, 0xffff); + regmap_write(map, RTL8231_REG_PIN_HI_CFG, + RTL8231_PIN_HI_CFG_MODE_MASK | RTL8231_PIN_HI_CFG_DIR_MASK); + + return 0; +} + +static const struct regmap_config rtl8231_mdio_regmap_config = { + .val_bits = RTL8231_BITS_VAL, + .reg_bits = 5, + .max_register = RTL8231_REG_COUNT - 1, + .use_single_read = true, + .use_single_write = true, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, +}; + +static int rtl8231_mdio_probe(struct mdio_device *mdiodev) +{ + struct device *dev = &mdiodev->dev; + struct regmap_field *led_start; + struct regmap *map; + int err; + + map = devm_regmap_init_mdio(mdiodev, &rtl8231_mdio_regmap_config); + if (IS_ERR(map)) { + dev_err(dev, "failed to init regmap\n"); + return PTR_ERR(map); + } + + led_start = devm_regmap_field_alloc(dev, map, RTL8231_FIELD_LED_START); + if (IS_ERR(led_start)) + return PTR_ERR(led_start); + + dev_set_drvdata(dev, led_start); + + mdiodev->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + device_property_read_u32(dev, "reset-assert-delay", &mdiodev->reset_assert_delay); + device_property_read_u32(dev, "reset-deassert-delay", &mdiodev->reset_deassert_delay); + + err = rtl8231_init(dev, map); + if (err) + return err; + + /* LED_START enables power to output pins, and starts the LED engine */ + regmap_field_write(led_start, 1); + + return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, rtl8231_cells, + ARRAY_SIZE(rtl8231_cells), NULL, 0, NULL); +} + +__maybe_unused static int rtl8231_suspend(struct device *dev) +{ + struct regmap_field *led_start = dev_get_drvdata(dev); + + return regmap_field_write(led_start, 0); +} + +__maybe_unused static int rtl8231_resume(struct device *dev) +{ + struct regmap_field *led_start = dev_get_drvdata(dev); + + return regmap_field_write(led_start, 1); +} + +static SIMPLE_DEV_PM_OPS(rtl8231_pm_ops, rtl8231_suspend, rtl8231_resume); + +static const struct of_device_id rtl8231_of_match[] = { + { .compatible = "realtek,rtl8231" }, + {} +}; +MODULE_DEVICE_TABLE(of, rtl8231_of_match); + +static struct mdio_driver rtl8231_mdio_driver = { + .mdiodrv.driver = { + .name = "rtl8231-expander", + .of_match_table = rtl8231_of_match, + .pm = pm_ptr(&rtl8231_pm_ops), + }, + .probe = rtl8231_mdio_probe, +}; +mdio_module_driver(rtl8231_mdio_driver); + +MODULE_AUTHOR("Sander Vanheule "); +MODULE_DESCRIPTION("Realtek RTL8231 GPIO and LED expander"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/rtl8231.h b/include/linux/mfd/rtl8231.h new file mode 100644 index 000000000000..7f1df92a9d36 --- /dev/null +++ b/include/linux/mfd/rtl8231.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Register definitions the RTL8231 GPIO and LED expander chip + */ + +#ifndef __LINUX_MFD_RTL8231_H +#define __LINUX_MFD_RTL8231_H + +#include + +#define RTL8231_BITS_VAL 16 + +/* Chip control */ +#define RTL8231_REG_FUNC0 0x00 +#define RTL8231_FUNC0_SCAN_MODE BIT(0) +#define RTL8231_FUNC0_SCAN_SINGLE 0 +#define RTL8231_FUNC0_SCAN_BICOLOR BIT(0) + +#define RTL8231_REG_FUNC1 0x01 +#define RTL8231_FUNC1_READY_CODE_VALUE 0x37 +#define RTL8231_FUNC1_READY_CODE_MASK GENMASK(9, 4) + +/* Pin control */ +#define RTL8231_REG_PIN_MODE0 0x02 +#define RTL8231_REG_PIN_MODE1 0x03 + +#define RTL8231_PIN_MODE_LED 0 +#define RTL8231_PIN_MODE_GPIO 1 + +/* Pin high config: pin and GPIO control for pins 32-26 */ +#define RTL8231_REG_PIN_HI_CFG 0x04 +#define RTL8231_PIN_HI_CFG_MODE_MASK GENMASK(4, 0) +#define RTL8231_PIN_HI_CFG_DIR_MASK GENMASK(9, 5) +#define RTL8231_PIN_HI_CFG_SOFT_RESET BIT(15) + +/* GPIO control registers */ +#define RTL8231_REG_GPIO_DIR0 0x05 +#define RTL8231_REG_GPIO_DIR1 0x06 +#define RTL8231_REG_GPIO_INVERT0 0x07 +#define RTL8231_REG_GPIO_INVERT1 0x08 + +#define RTL8231_GPIO_DIR_IN 1 +#define RTL8231_GPIO_DIR_OUT 0 + +/* GPIO data registers */ +#define RTL8231_REG_GPIO_DATA0 0x1c +#define RTL8231_REG_GPIO_DATA1 0x1d +#define RTL8231_REG_GPIO_DATA2 0x1e + +/* LED control base registers */ +#define RTL8231_REG_LED0_BASE 0x09 +#define RTL8231_REG_LED1_BASE 0x10 +#define RTL8231_REG_LED2_BASE 0x17 + +#define RTL8231_REG_COUNT 0x1f + +#endif /* __LINUX_MFD_RTL8231_H */ From patchwork Sun May 23 22:34:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sander Vanheule X-Patchwork-Id: 446611 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.7 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 97022C47084 for ; Sun, 23 May 2021 22:34:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7728C600EF for ; Sun, 23 May 2021 22:34:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232101AbhEWWgF (ORCPT ); Sun, 23 May 2021 18:36:05 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45532 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232113AbhEWWgD (ORCPT ); Sun, 23 May 2021 18:36:03 -0400 Received: from polaris.svanheule.net (polaris.svanheule.net [IPv6:2a00:c98:2060:a004:1::200]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A31D1C061574 for ; Sun, 23 May 2021 15:34:36 -0700 (PDT) Received: from terra.local.svanheule.net (unknown [IPv6:2a02:a03f:eafb:ee01:bd37:7535:eb00:6fa]) (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 09EC3202A59; Mon, 24 May 2021 00:34:35 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=svanheule.net; s=mail1707; t=1621809275; 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=h1aZMxGm1wRNWmXxIIKD3LnsqxSFWyAhf737d0dL3S4=; b=ygPOaZ+vZw9TcucnwUp2d5pFtCXfkPM2XJcfGaAPSaDQHfi2TQquw/0hOYC8J/uZyC4h7h PDb3Npl86C1YNaa0EjXtuxa/azPkrMhknc88DETqgrj06Cp/v0q5fB8G2jRNmS0YPY1jxG L4JYTr6hGnre2rXVRsofZu8MLF1U6P6QNQTcBx3QSO356ffvL9kNyEy62LimXJnVVZkl87 g9VXe3lO2upR+Aho5t7a5C4Kyng6ZQMPxfeBGM00u/f4zV8IIDVTEwMQD69ST11b5/2mV5 vUnmGghMEFkqlEk3eW1IuwujjYK/3ZDm8O14QTFVEnLe5CgX2BtvtmpBD0v+cA== 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 v3 5/6] pinctrl: Add RTL8231 pin control and GPIO support Date: Mon, 24 May 2021 00:34:03 +0200 Message-Id: <185e8c61893502575c542750c8f27b09029e3078.1621809029.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-gpio@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 | 398 ++++++++++++++++++++++++++++++ 3 files changed, 410 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-rtl8231.c diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index c2c7e7963ed0..e47d6f3550df 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 pinctrl-rtl8231. + 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..d0eea42a4bd3 --- /dev/null +++ b/drivers/pinctrl/pinctrl-rtl8231.c @@ -0,0 +1,398 @@ +// 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 { + const enum rtl8231_pin_function functions; + const u8 reg; + const u8 offset; + const u8 gpio_function_value; +}; + +#define RTL8231_PIN_DESC(_num, _func, _reg, _fld, _val) \ + [_num] = { \ + .functions = RTL8231_PIN_FUNCTION_GPIO | _func, \ + .reg = _reg, \ + .offset = _fld, \ + .gpio_function_value = _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 logic can stay generic. + */ +static struct rtl8231_pin_desc rtl8231_pin_data[RTL8231_NUM_GPIOS] = { + 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), +}; + +#define RTL8231_PIN(_num) \ + { \ + .number = _num, \ + .name = "gpio" #_num, \ + .drv_data = &rtl8231_pin_data[_num] \ + } + +static const struct pinctrl_pin_desc rtl8231_pins[RTL8231_NUM_GPIOS] = { + 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, 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)) + return -EINVAL; + + *pins = &rtl8231_pins[selector].number; + *num_pins = 1; + + return 0; +} + +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) +{ + const struct rtl8231_pin_desc *desc = + (struct rtl8231_pin_desc *) &rtl8231_pins[group_selector].drv_data; + struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev); + unsigned int func_flag = BIT(func_selector); + unsigned int function_mask; + unsigned int gpio_function; + + if (!(desc->functions & func_flag)) + return -EINVAL; + + function_mask = BIT(desc->offset); + gpio_function = desc->gpio_function_value << desc->offset; + + if (func_flag == RTL8231_PIN_FUNCTION_GPIO) + return regmap_update_bits(ctrl->map, desc->reg, function_mask, gpio_function); + else + return regmap_update_bits(ctrl->map, desc->reg, function_mask, ~gpio_function); +} + +static int rtl8231_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, unsigned int offset) +{ + struct rtl8231_pin_desc *desc = + (struct rtl8231_pin_desc *) &rtl8231_pins[offset].drv_data; + struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev); + 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 (!ctrl->functions) { + dev_err(dev, "failed to allocate pin function descriptors\n"); + return -ENOMEM; + } + + 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_pin_data[pin].functions & BIT(f_idx)) + function->ngroups++; + + function->groups = devm_kcalloc(dev, function->ngroups, + sizeof(*function->groups), GFP_KERNEL); + if (!function->groups) + return -ENOMEM; + + group_name = function->groups; + for (pin = 0; pin < ctrl->pctl_desc.npins; pin++) + if (rtl8231_pin_data[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; + int err; + + 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); + ctrl->pctl_desc.pins = rtl8231_pins; + + 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 (!ctrl->map) + return -ENODEV; + + if (IS_ERR(ctrl->map)) + 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.quirks = GPIO_REGMAP_QUIRK_SET_DIRECTION_FIRST; + + 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"); From patchwork Sun May 23 22:34: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: 446309 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.7 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 5C941C47080 for ; Sun, 23 May 2021 22:34:49 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3CEC06135A for ; Sun, 23 May 2021 22:34:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232124AbhEWWgO (ORCPT ); Sun, 23 May 2021 18:36:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45522 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232128AbhEWWgH (ORCPT ); Sun, 23 May 2021 18:36:07 -0400 Received: from polaris.svanheule.net (polaris.svanheule.net [IPv6:2a00:c98:2060:a004:1::200]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 728AAC061343 for ; Sun, 23 May 2021 15:34:40 -0700 (PDT) Received: from terra.local.svanheule.net (unknown [IPv6:2a02:a03f:eafb:ee01:bd37:7535:eb00:6fa]) (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 B8CBC202A5A; Mon, 24 May 2021 00:34:38 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=svanheule.net; s=mail1707; t=1621809279; 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=raI0z/GbTTtORXRTdmtYTUROcqFo03qJcayedUYn3dY=; b=ndQzisWagpCQk26Q8U755ZcjJ08GBeb+6WKw53sPkAtLetpSIkU9L7NfyC3Qwypz50/ukL xqU6gNyPQ76S5SBocT+6HI5iIyoJtYd0+O15FKFlPv4Y5j440XASPyQaejBjs+dz5YDTbP n063bLlRErQfraEK4uP+wXcMV2vEJW7xqqn3HfUcKC4rRkCRqDIPpPUIFd/GLLFxQoF+rl Bs+QvSRq0MMWCrAt/qGsl6PusgeaSK3NdMrCXJu1nfBCqG9e4/7A0/Gw7pm+3GZopggJST UFTM3VMDIfBQk8Zk8/Wfe+68EOog3xrnUMtG83SVN6DrZLo/Lp3S6BKibPc2cw== 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 v3 6/6] leds: Add support for RTL8231 LED scan matrix Date: Mon, 24 May 2021 00:34:04 +0200 Message-Id: <213ab7580a1d3229d32f7aac67bf4e828612153a.1621809029.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-gpio@vger.kernel.org Both single and bi-color scanning modes are supported. The driver will verify that the addresses are valid for the current mode, before registering the LEDs. LEDs can be turned on, off, or toggled at one of six predefined rates from 40ms to 1280ms. Implements a platform device for use as a child device with RTL8231 MFD, and uses the parent regmap to access the required registers. Signed-off-by: Sander Vanheule --- drivers/leds/Kconfig | 10 ++ drivers/leds/Makefile | 1 + drivers/leds/leds-rtl8231.c | 291 ++++++++++++++++++++++++++++++++++++ 3 files changed, 302 insertions(+) create mode 100644 drivers/leds/leds-rtl8231.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 49d99cb084db..be723b6e9644 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -593,6 +593,16 @@ config LEDS_REGULATOR help This option enables support for regulator driven LEDs. +config LEDS_RTL8231 + tristate "RTL8231 LED matrix support" + depends on LEDS_CLASS + depends on MFD_RTL8231 + default MFD_RTL8231 + help + This options enables support for using the LED scanning matrix output + of the RTL8231 GPIO and LED expander chip. + When built as a module, this module will be named leds-rtl8231. + config LEDS_BD2802 tristate "LED driver for BD2802 RGB LED" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 7e604d3028c8..ce0f44a87dee 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o obj-$(CONFIG_LEDS_PWM) += leds-pwm.o obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o +obj-$(CONFIG_LEDS_RTL8231) += leds-rtl8231.o obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o obj-$(CONFIG_LEDS_SGM3140) += leds-sgm3140.o diff --git a/drivers/leds/leds-rtl8231.c b/drivers/leds/leds-rtl8231.c new file mode 100644 index 000000000000..4f3333a61a84 --- /dev/null +++ b/drivers/leds/leds-rtl8231.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * struct led_toggle_rate - description of an LED blinking mode + * @interval_ms: LED toggle rate in milliseconds + * @mode: Register field value used to activate this mode + * + * For LED hardware accelerated blinking, with equal on and off delay. + * Both delays are given by @interval, so the interval at which the LED blinks + * (i.e. turn on and off once) is double this value. + */ +struct led_toggle_rate { + u16 interval_ms; + u8 mode; +}; + +/** + * struct led_modes - description of all LED modes + * @toggle_rates: Array of led_toggle_rate values, sorted by ascending interval + * @num_toggle_rates: Number of elements in @led_toggle_rate + * @off: Register field value to turn LED off + * @on: Register field value to turn LED on + */ +struct led_modes { + const struct led_toggle_rate *toggle_rates; + unsigned int num_toggle_rates; + u8 off; + u8 on; +}; + +struct rtl8231_led { + struct led_classdev led; + const struct led_modes *modes; + struct regmap_field *reg_field; +}; +#define to_rtl8231_led(_cdev) container_of(_cdev, struct rtl8231_led, led) + +#define RTL8231_NUM_LEDS 3 +#define RTL8231_LED_PER_REG 5 +#define RTL8231_BITS_PER_LED 3 + +static const unsigned int rtl8231_led_port_counts_single[RTL8231_NUM_LEDS] = {32, 32, 24}; +static const unsigned int rtl8231_led_port_counts_bicolor[RTL8231_NUM_LEDS] = {24, 24, 24}; + +static const unsigned int rtl8231_led_base[RTL8231_NUM_LEDS] = { + RTL8231_REG_LED0_BASE, + RTL8231_REG_LED1_BASE, + RTL8231_REG_LED2_BASE, +}; + +static const struct led_toggle_rate rtl8231_toggle_rates[] = { + { 40, 1}, + { 80, 2}, + { 160, 3}, + { 320, 4}, + { 640, 5}, + {1280, 6}, +}; + +static const struct led_modes rtl8231_led_modes = { + .off = 0, + .on = 7, + .num_toggle_rates = ARRAY_SIZE(rtl8231_toggle_rates), + .toggle_rates = rtl8231_toggle_rates, +}; + +static void rtl8231_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct rtl8231_led *pled = to_rtl8231_led(led_cdev); + + if (brightness) + regmap_field_write(pled->reg_field, pled->modes->on); + else + regmap_field_write(pled->reg_field, pled->modes->off); +} + +static enum led_brightness rtl8231_led_brightness_get(struct led_classdev *led_cdev) +{ + struct rtl8231_led *pled = to_rtl8231_led(led_cdev); + u32 current_mode = pled->modes->off; + + regmap_field_read(pled->reg_field, ¤t_mode); + + if (current_mode == pled->modes->off) + return LED_OFF; + else + return LED_ON; +} + +static unsigned int rtl8231_led_current_interval(struct rtl8231_led *pled) +{ + unsigned int mode; + unsigned int i; + + if (regmap_field_read(pled->reg_field, &mode)) + return 0; + + for (i = 0; i < pled->modes->num_toggle_rates; i++) + if (mode == pled->modes->toggle_rates[i].mode) + return pled->modes->toggle_rates[i].interval_ms; + + return 0; +} + +static int rtl8231_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, + unsigned long *delay_off) +{ + struct rtl8231_led *pled = to_rtl8231_led(led_cdev); + const struct led_toggle_rate *rates = pled->modes->toggle_rates; + unsigned int num_rates = pled->modes->num_toggle_rates; + unsigned int interval_ms; + unsigned int i; + int err; + + if (*delay_on == 0 && *delay_off == 0) { + interval_ms = 500; + } else { + /* + * If the current mode is blinking, choose the delay that (likely) changed. + * Otherwise, choose the interval that would have the same total delay. + */ + interval_ms = rtl8231_led_current_interval(pled); + if (interval_ms > 0 && interval_ms == *delay_off) + interval_ms = *delay_on; + else if (interval_ms > 0 && interval_ms == *delay_on) + interval_ms = *delay_off; + else + interval_ms = (*delay_on + *delay_off) / 2; + } + + /* Find clamped toggle interval */ + for (i = 0; i < (num_rates - 1); i++) + if (interval_ms > rates[i].interval_ms) + break; + + interval_ms = rates[i].interval_ms; + + err = regmap_field_write(pled->reg_field, rates[i].mode); + if (err) + return err; + + *delay_on = interval_ms; + *delay_off = interval_ms; + + return 0; +} + +static int rtl8231_led_read_address(struct fwnode_handle *fwnode, unsigned int *addr_port, + unsigned int *addr_led) +{ + u32 addr[2]; + int ret; + + ret = fwnode_property_count_u32(fwnode, "reg"); + if (ret < 0) + return ret; + if (ret != 2) + return -ENODEV; + + ret = fwnode_property_read_u32_array(fwnode, "reg", addr, ARRAY_SIZE(addr)); + if (ret) + return ret; + + *addr_port = addr[0]; + *addr_led = addr[1]; + + return 0; +} + +static struct reg_field rtl8231_led_get_field(unsigned int port_index, unsigned int led_index) +{ + unsigned int offset, shift; + struct reg_field field; + + offset = port_index / RTL8231_LED_PER_REG; + shift = (port_index % RTL8231_LED_PER_REG) * RTL8231_BITS_PER_LED; + + field.reg = rtl8231_led_base[led_index] + offset; + field.lsb = shift; + field.msb = shift + RTL8231_BITS_PER_LED - 1; + + return field; +} + +static int rtl8231_led_probe_single(struct device *dev, struct regmap *map, + const unsigned int *port_counts, struct fwnode_handle *fwnode) +{ + struct led_init_data init_data = {}; + struct rtl8231_led *pled; + unsigned int port_index; + unsigned int led_index; + struct reg_field field; + int err; + + pled = devm_kzalloc(dev, sizeof(*pled), GFP_KERNEL); + if (!pled) + return -ENOMEM; + + err = rtl8231_led_read_address(fwnode, &port_index, &led_index); + if (err) { + dev_err(dev, "LED address invalid\n"); + return err; + } + + if (led_index >= RTL8231_NUM_LEDS || port_index >= port_counts[led_index]) { + dev_err(dev, "LED address (%d.%d) invalid\n", port_index, led_index); + return -ENODEV; + } + + field = rtl8231_led_get_field(port_index, led_index); + pled->reg_field = devm_regmap_field_alloc(dev, map, field); + if (IS_ERR(pled->reg_field)) + return PTR_ERR(pled->reg_field); + + pled->modes = &rtl8231_led_modes; + + pled->led.max_brightness = 1; + pled->led.brightness_get = rtl8231_led_brightness_get; + pled->led.brightness_set = rtl8231_led_brightness_set; + pled->led.blink_set = rtl8231_led_blink_set; + + init_data.fwnode = fwnode; + + return devm_led_classdev_register_ext(dev, &pled->led, &init_data); +} + +static int rtl8231_led_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const unsigned int *port_counts; + struct fwnode_handle *child; + struct regmap *map; + int err; + + map = dev_get_regmap(dev->parent, NULL); + if (!map) + return -ENODEV; + if (IS_ERR(map)) + return PTR_ERR(map); + + if (device_property_match_string(dev, "realtek,led-scan-mode", "single-color") >= 0) { + port_counts = rtl8231_led_port_counts_single; + regmap_update_bits(map, RTL8231_REG_FUNC0, + RTL8231_FUNC0_SCAN_MODE, RTL8231_FUNC0_SCAN_SINGLE); + } else if (device_property_match_string(dev, "realtek,led-scan-mode", "bi-color") >= 0) { + port_counts = rtl8231_led_port_counts_bicolor; + regmap_update_bits(map, RTL8231_REG_FUNC0, + RTL8231_FUNC0_SCAN_MODE, RTL8231_FUNC0_SCAN_BICOLOR); + } else { + dev_err(dev, "scan mode missing or invalid\n"); + return -EINVAL; + } + + fwnode_for_each_available_child_node(dev->fwnode, child) { + err = rtl8231_led_probe_single(dev, map, port_counts, child); + if (err) + dev_warn(dev, "failed to register LED %pfwP\n", child); + } + + return 0; +} + +static const struct of_device_id of_rtl8231_led_match[] = { + { .compatible = "realtek,rtl8231-leds" }, + {} +}; +MODULE_DEVICE_TABLE(of, of_rtl8231_led_match); + +static struct platform_driver rtl8231_led_driver = { + .driver = { + .name = "rtl8231-leds", + .of_match_table = of_rtl8231_led_match, + }, + .probe = rtl8231_led_probe, +}; +module_platform_driver(rtl8231_led_driver); + +MODULE_AUTHOR("Sander Vanheule "); +MODULE_DESCRIPTION("Realtek RTL8231 LED support"); +MODULE_LICENSE("GPL v2");