From patchwork Sun Oct 25 00:59:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luka Kovacic X-Patchwork-Id: 294286 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=-12.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,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 63DBEC2D0A3 for ; Sun, 25 Oct 2020 00:59:57 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 2F71D2098B for ; Sun, 25 Oct 2020 00:59:57 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=sartura-hr.20150623.gappssmtp.com header.i=@sartura-hr.20150623.gappssmtp.com header.b="xn4SrHuf" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1766546AbgJYA74 (ORCPT ); Sat, 24 Oct 2020 20:59:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37760 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1765976AbgJYA7g (ORCPT ); Sat, 24 Oct 2020 20:59:36 -0400 Received: from mail-ej1-x642.google.com (mail-ej1-x642.google.com [IPv6:2a00:1450:4864:20::642]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D360BC0613CE for ; Sat, 24 Oct 2020 17:59:34 -0700 (PDT) Received: by mail-ej1-x642.google.com with SMTP id z5so8181978ejw.7 for ; Sat, 24 Oct 2020 17:59:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sartura-hr.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=aH5lWPXQedbYF5LSN3GmPneR24giitxJlZaOPlHn8X4=; b=xn4SrHufyIdk3ZW8fhVbEwY/Cfexu7XrDPDOsJs0k8hA+mzZMMAh1p9OlrTcqIKpuc HyikUU4gKtRsD3NFH5Cs1/C5Q2f5839USzyoESq3Eu+srs/r37rAOFy/u2rQIO2/bUMY aGikyb+iYKIiuzQv5PpB+rfPaqIqVDYcHdEaS3KxV0cBcoVD5VkFEgQOD/hmUmRHxd1F mP1pZdrPMrdPEQtOhT0H8gXLYn0PR/6TL7mLml+KPsH4N5eABgfuTjivGo+B2Ff2IrIc 2bMk7lwjntIgu+maPYKOVcbEeNm8GqPKcjM2LrDlAQgLxD8i4ZOPBh+mzwlaCeuN4gd+ h9qg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=aH5lWPXQedbYF5LSN3GmPneR24giitxJlZaOPlHn8X4=; b=twsVMkZ0WhfeIrD507QMMC6vqXNm7lyYRkTTsCQHSTKuKB4u/C0WsKaIGkIPoN3iZa ziSt0tpkUtUFUsoxGX0vEQ5rRhdRvBmGXO14d4KSR50gVXthRZBblmGXzDrchSmMGxXD GR78y7b0GI3AS8kBs+Ls+DsJQmj5IxSv3ZHM9zcvkpVmuTGOV6z8xAHlptYz6GO31T3n bjGda3HzBUdK9UqBwIonNR15O3ZjWpdB1yzFY7HsGMo3c6YcdMLZF//Y0BZ/8SAvptgU G1LWB1hB1HA9DdkDYu1HsDxWJQvhxktpOZRZ3QROPrreXlgZPo2Y08Smv+hwbZYQixM+ 0tWw== X-Gm-Message-State: AOAM531RUG1GVHJkjGQE39dNJknOiFi6lNjusba1KEBtlB1d55CHVr3l qO8W+1NJ+WqgIPxYxIpbWkEJJA== X-Google-Smtp-Source: ABdhPJylLKFGX7abLq0mKkvDiVjAO12knb97BYuUnm7XN8ZSdPRY3x1e9gk2+GW1ieunzok3TbhOEw== X-Received: by 2002:a17:906:3bd7:: with SMTP id v23mr9414507ejf.100.1603587572043; Sat, 24 Oct 2020 17:59:32 -0700 (PDT) Received: from localhost.localdomain ([2a00:ee2:4b0d:3001:fbc5:498b:ed6d:cfac]) by smtp.gmail.com with ESMTPSA id q5sm2797274edt.79.2020.10.24.17.59.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 24 Oct 2020 17:59:31 -0700 (PDT) From: Luka Kovacic To: linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-leds@vger.kernel.org, devicetree@vger.kernel.org Cc: lee.jones@linaro.org, pavel@ucw.cz, dmurphy@ti.com, robh+dt@kernel.org, jdelvare@suse.com, linux@roeck-us.net, marek.behun@nic.cz, luka.perkov@sartura.hr, andy.shevchenko@gmail.com, robert.marko@sartura.hr, Luka Kovacic Subject: [PATCH v7 1/6] dt-bindings: Add IEI vendor prefix and IEI WT61P803 PUZZLE driver bindings Date: Sun, 25 Oct 2020 02:59:11 +0200 Message-Id: <20201025005916.64747-2-luka.kovacic@sartura.hr> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201025005916.64747-1-luka.kovacic@sartura.hr> References: <20201025005916.64747-1-luka.kovacic@sartura.hr> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Add the IEI WT61P803 PUZZLE Device Tree bindings for MFD, HWMON and LED drivers. A new vendor prefix is also added accordingly for IEI Integration Corp. Signed-off-by: Luka Kovacic Cc: Luka Perkov Cc: Robert Marko --- .../hwmon/iei,wt61p803-puzzle-hwmon.yaml | 53 ++++++++++++ .../leds/iei,wt61p803-puzzle-leds.yaml | 45 ++++++++++ .../bindings/mfd/iei,wt61p803-puzzle.yaml | 83 +++++++++++++++++++ .../devicetree/bindings/vendor-prefixes.yaml | 2 + 4 files changed, 183 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/iei,wt61p803-puzzle-hwmon.yaml create mode 100644 Documentation/devicetree/bindings/leds/iei,wt61p803-puzzle-leds.yaml create mode 100644 Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml diff --git a/Documentation/devicetree/bindings/hwmon/iei,wt61p803-puzzle-hwmon.yaml b/Documentation/devicetree/bindings/hwmon/iei,wt61p803-puzzle-hwmon.yaml new file mode 100644 index 000000000000..c24a24e90495 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/iei,wt61p803-puzzle-hwmon.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/iei,wt61p803-puzzle-hwmon.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: IEI WT61P803 PUZZLE MCU HWMON module from IEI Integration Corp. + +maintainers: + - Luka Kovacic + +description: | + This module is a part of the IEI WT61P803 PUZZLE MFD device. For more details + see Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml. + + The HWMON module is a sub-node of the MCU node in the Device Tree. + +properties: + compatible: + const: iei,wt61p803-puzzle-hwmon + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^fan-group@[0-1]$": + type: object + properties: + reg: + minimum: 0 + maximum: 1 + description: + Fan group ID + + cooling-levels: + minItems: 1 + maxItems: 255 + description: + Cooling levels for the fans (PWM value mapping) + description: | + Properties for each fan group. + required: + - reg + +required: + - compatible + - "#address-cells" + - "#size-cells" + +additionalProperties: false diff --git a/Documentation/devicetree/bindings/leds/iei,wt61p803-puzzle-leds.yaml b/Documentation/devicetree/bindings/leds/iei,wt61p803-puzzle-leds.yaml new file mode 100644 index 000000000000..bbf264c13189 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/iei,wt61p803-puzzle-leds.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/iei,wt61p803-puzzle-leds.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: IEI WT61P803 PUZZLE MCU LED module from IEI Integration Corp. + +maintainers: + - Luka Kovacic + +description: | + This module is a part of the IEI WT61P803 PUZZLE MFD device. For more details + see Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml. + + The LED module is a sub-node of the MCU node in the Device Tree. + +properties: + compatible: + const: iei,wt61p803-puzzle-leds + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + "led@0": + type: object + $ref: common.yaml + description: | + Properties for a single LED. + properties: + reg: + description: + Index of the LED. Only one LED is supported at the moment. + minimum: 0 + maximum: 0 + +required: + - compatible + - "#address-cells" + - "#size-cells" + +additionalProperties: false diff --git a/Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml b/Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml new file mode 100644 index 000000000000..64264c664c48 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/iei,wt61p803-puzzle.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: IEI WT61P803 PUZZLE MCU from IEI Integration Corp. + +maintainers: + - Luka Kovacic + +description: | + IEI WT61P803 PUZZLE MCU is embedded in some IEI Puzzle series boards. + It's used for controlling system power states, fans, LEDs and temperature + sensors. + + For Device Tree bindings of other sub-modules (HWMON, LEDs) refer to the + binding documents under the respective subsystem directories. + +properties: + compatible: + const: iei,wt61p803-puzzle + + current-speed: + description: + Serial bus speed in bps + maxItems: 1 + + enable-beep: true + + hwmon: + $ref: ../hwmon/iei,wt61p803-puzzle-hwmon.yaml + + leds: + $ref: ../leds/iei,wt61p803-puzzle-leds.yaml + +required: + - compatible + - current-speed + +additionalProperties: false + +examples: + - | + #include + serial { + status = "okay"; + mcu { + compatible = "iei,wt61p803-puzzle"; + current-speed = <115200>; + enable-beep; + + leds { + compatible = "iei,wt61p803-puzzle-leds"; + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + function = LED_FUNCTION_POWER; + color = ; + }; + }; + + hwmon { + compatible = "iei,wt61p803-puzzle-hwmon"; + #address-cells = <1>; + #size-cells = <0>; + + fan-group@0 { + #cooling-cells = <2>; + reg = <0x00>; + cooling-levels = <64 102 170 230 250>; + }; + + fan-group@1 { + #cooling-cells = <2>; + reg = <0x01>; + cooling-levels = <64 102 170 230 250>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 63996ab03521..5f2595f0b2ad 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -467,6 +467,8 @@ patternProperties: description: IC Plus Corp. "^idt,.*": description: Integrated Device Technologies, Inc. + "^iei,.*": + description: IEI Integration Corp. "^ifi,.*": description: Ingenieurburo Fur Ic-Technologie (I/F/I) "^ilitek,.*": From patchwork Sun Oct 25 00:59:12 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luka Kovacic X-Patchwork-Id: 294288 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=-12.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, 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 A86B5C55179 for ; Sun, 25 Oct 2020 00:59:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5A07D2098B for ; Sun, 25 Oct 2020 00:59:40 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=sartura-hr.20150623.gappssmtp.com header.i=@sartura-hr.20150623.gappssmtp.com header.b="UOcH7t7Z" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1765989AbgJYA7i (ORCPT ); Sat, 24 Oct 2020 20:59:38 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37774 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1765987AbgJYA7h (ORCPT ); Sat, 24 Oct 2020 20:59:37 -0400 Received: from mail-ed1-x542.google.com (mail-ed1-x542.google.com [IPv6:2a00:1450:4864:20::542]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C2010C0613CE for ; Sat, 24 Oct 2020 17:59:36 -0700 (PDT) Received: by mail-ed1-x542.google.com with SMTP id bc23so5680331edb.5 for ; Sat, 24 Oct 2020 17:59:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sartura-hr.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=m30QPTp8vBjlUKaCFUA6qeh3iz05csQtqBIAn5RwjNI=; b=UOcH7t7Z5fCJ5XSDTe5FbxgZ0e1q7ZO0CkdOf1+XodWJJS4PwvrM/ME7TvbmVEJXaJ 7qsTDRBXqkxYmL6lcJNimW9fBLtZeln9G8UgsghzN5rneVtdlRsvrnwBArin+5w8jbl+ QCpp9b4Cjgqxa1IRWuye0I0rNA2FrEJIWGsH9kGtjJYLOErhtaPtkzPGI/2mgI0WkPp8 lf/43LcGRQiHsAI0sfSPYFlxA8SOLA15U3PK8Y2GIfI0SzrAG2az8k+mdLjbTOsxHFgO 288LAP3zFSNhqKlsPqPv8Wsa2wf4ry4jmNBlmhVNUUmDFlDjvCaupVS3n1UszmLuIioc L75g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=m30QPTp8vBjlUKaCFUA6qeh3iz05csQtqBIAn5RwjNI=; b=Kofm73rM+FDOY9Bcic9KFPt+NAUMSeC3Q897+JZFg/fLrmOPcEX4EC5+Jt/ADuT4V9 edadcD7jxN1Yo6UObB4mGTYMn5Ft2loJ+/k8ocuG+0K9lhHLy7L/vHe/aW6gOpmDGi2e 4URpl2bQNNQcCbTxvMTZKyHxXVST3L1NT0wGR0chXEd5nWIdHuQYAnmh0TGmad4jmcC5 TymoKmc32RFfvw4DaK5J3oJyqpdaW+sd7KgK4BhOGPcXc4ZwXGlcFsKW4JKMenDOIlsm HraaR2nb9bscUD6FSZhdyBL3LQ9ZdZd7Lzsa9Kbg9qIml2FXXnn3MSPM7FPCc6iiAv2L OtcQ== X-Gm-Message-State: AOAM533rKKRSK+JvVZXqif5JVKbTfb4Aevu98pK48JkIi2T27RmWf5ha uY4cuYoQ5dMBdgB/NsTCdvXsTw== X-Google-Smtp-Source: ABdhPJw8882gMl/xLmpoepEspJeaZQuXSArLYKYVFWPyKPifPzkcumk2W7IFJpqrMzSY5OgACzeSOA== X-Received: by 2002:a50:d88c:: with SMTP id p12mr6774617edj.352.1603587573567; Sat, 24 Oct 2020 17:59:33 -0700 (PDT) Received: from localhost.localdomain ([2a00:ee2:4b0d:3001:fbc5:498b:ed6d:cfac]) by smtp.gmail.com with ESMTPSA id q5sm2797274edt.79.2020.10.24.17.59.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 24 Oct 2020 17:59:32 -0700 (PDT) From: Luka Kovacic To: linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-leds@vger.kernel.org, devicetree@vger.kernel.org Cc: lee.jones@linaro.org, pavel@ucw.cz, dmurphy@ti.com, robh+dt@kernel.org, jdelvare@suse.com, linux@roeck-us.net, marek.behun@nic.cz, luka.perkov@sartura.hr, andy.shevchenko@gmail.com, robert.marko@sartura.hr, Luka Kovacic Subject: [PATCH v7 2/6] drivers: mfd: Add a driver for IEI WT61P803 PUZZLE MCU Date: Sun, 25 Oct 2020 02:59:12 +0200 Message-Id: <20201025005916.64747-3-luka.kovacic@sartura.hr> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201025005916.64747-1-luka.kovacic@sartura.hr> References: <20201025005916.64747-1-luka.kovacic@sartura.hr> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Add a driver for the IEI WT61P803 PUZZLE microcontroller, used in some IEI Puzzle series devices. The microcontroller controls system power, temperature sensors, fans and LEDs. This driver implements the core functionality for device communication over the system serial (serdev bus). It handles MCU messages and the internal MCU properties. Some properties can be managed over sysfs. Signed-off-by: Luka Kovacic Cc: Luka Perkov Cc: Robert Marko --- drivers/mfd/Kconfig | 8 + drivers/mfd/Makefile | 1 + drivers/mfd/iei-wt61p803-puzzle.c | 1039 +++++++++++++++++++++++ include/linux/mfd/iei-wt61p803-puzzle.h | 66 ++ 4 files changed, 1114 insertions(+) create mode 100644 drivers/mfd/iei-wt61p803-puzzle.c create mode 100644 include/linux/mfd/iei-wt61p803-puzzle.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 33df0837ab41..77afdb633466 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2118,5 +2118,13 @@ config SGI_MFD_IOC3 If you have an SGI Origin, Octane, or a PCI IOC3 card, then say Y. Otherwise say N. +config MFD_IEI_WT61P803_PUZZLE + tristate "IEI WT61P803 PUZZLE MCU driver" + depends on SERIAL_DEV_BUS + help + IEI WT61P803 PUZZLE is a system power management microcontroller + used for fan control, temperature sensor reading, LED control + and system identification. + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index a60e5f835283..33b88023a68d 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -236,6 +236,7 @@ obj-$(CONFIG_MFD_HI655X_PMIC) += hi655x-pmic.o obj-$(CONFIG_MFD_DLN2) += dln2.o obj-$(CONFIG_MFD_RT5033) += rt5033.o obj-$(CONFIG_MFD_SKY81452) += sky81452.o +obj-$(CONFIG_MFD_IEI_WT61P803_PUZZLE) += iei-wt61p803-puzzle.o intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o diff --git a/drivers/mfd/iei-wt61p803-puzzle.c b/drivers/mfd/iei-wt61p803-puzzle.c new file mode 100644 index 000000000000..1bb456ff02ef --- /dev/null +++ b/drivers/mfd/iei-wt61p803-puzzle.c @@ -0,0 +1,1039 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* IEI WT61P803 PUZZLE MCU Driver + * System management microcontroller for fan control, temperature sensor reading, + * LED control and system identification on IEI Puzzle series ARM-based appliances. + * + * Copyright (C) 2020 Sartura Ltd. + * Author: Luka Kovacic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IEI_WT61P803_PUZZLE_MAX_COMMAND_LENGTH (20 + 2) +#define IEI_WT61P803_PUZZLE_RESP_BUF_SIZE 512 + +#define IEI_WT61P803_PUZZLE_VERSION_MAC_LENGTH 17 +#define IEI_WT61P803_PUZZLE_VERSION_SN_LENGTH 36 +#define IEI_WT61P803_PUZZLE_VERSION_VERSION_LENGTH 6 +#define IEI_WT61P803_PUZZLE_VERSION_BUILD_INFO_LENGTH 16 +#define IEI_WT61P803_PUZZLE_VERSION_PROTOCOL_VERSION_LENGTH 8 + +/* Use HZ as a timeout value throughout the driver */ +#define IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT HZ + +/** + * struct iei_wt61p803_puzzle_mcu_status - MCU flags state + * @ac_recovery_status_flag: AC Recovery Status Flag + * @power_loss_recovery: System recovery after power loss + * @power_status: System Power-on Method + */ +struct iei_wt61p803_puzzle_mcu_status { + u8 ac_recovery_status_flag; + u8 power_loss_recovery; + u8 power_status; +}; + +/** + * enum iei_wt61p803_puzzle_reply_state - State of the reply + * @FRAME_OK: The frame was completely processed/received + * @FRAME_PROCESSING: First bytes were received, but the frame isn't complete + * @FRAME_STRUCT_EMPTY: The frame struct is empty, no data was received + * @FRAME_TIMEOUT: The frame processing timed out, communication failed + * + * Describes the general state of the frame that is currently being received. + */ +enum iei_wt61p803_puzzle_reply_state { + FRAME_OK = 0x00, + FRAME_PROCESSING = 0x01, + FRAME_STRUCT_EMPTY = 0xFF, + FRAME_TIMEOUT = 0xFE +}; + +/** + * struct iei_wt61p803_puzzle_reply - MCU reply + * @size: Size of the MCU reply + * @data: Full MCU reply buffer + * @state: Current state of the packet + * @received: Was the response fullfilled + */ +struct iei_wt61p803_puzzle_reply { + size_t size; + unsigned char *data; + u8 state; + struct completion received; +}; + +/** + * struct iei_wt61p803_puzzle_mcu_version - MCU version status + * @version: Primary firmware version + * @build_info: Build date and time + * @bootloader_mode: Status of the MCU operation + * @protocol_version: MCU communication protocol version + * @serial_number: Device factory serial number + * @mac_address: Device factory MAC addresses + */ +struct iei_wt61p803_puzzle_mcu_version { + char version[IEI_WT61P803_PUZZLE_VERSION_VERSION_LENGTH + 1]; + char build_info[IEI_WT61P803_PUZZLE_VERSION_BUILD_INFO_LENGTH + 1]; + bool bootloader_mode; + char protocol_version[IEI_WT61P803_PUZZLE_VERSION_PROTOCOL_VERSION_LENGTH + 1]; + char serial_number[IEI_WT61P803_PUZZLE_VERSION_SN_LENGTH + 1]; + char mac_address[8][IEI_WT61P803_PUZZLE_VERSION_MAC_LENGTH + 1]; +}; + +/** + * struct iei_wt61p803_puzzle - IEI WT61P803 PUZZLE MCU Driver + * @serdev: Pointer to underlying serdev device + * @kobj: Pointer to kobject (sysfs) + * @reply_lock: Reply mutex lock + * @bus_lock: Bus mutex lock + * @reply: Pointer to the iei_wt61p803_puzzle_reply struct + * @version: MCU version related data + * @status: MCU status related data + * @response_buffer Command response buffer allocation + * @lock General member mutex lock + */ +struct iei_wt61p803_puzzle { + struct serdev_device *serdev; + struct kobject *kobj; + struct mutex reply_lock; + struct mutex bus_lock; + struct iei_wt61p803_puzzle_reply *reply; + struct iei_wt61p803_puzzle_mcu_version version; + struct iei_wt61p803_puzzle_mcu_status status; + unsigned char *response_buffer; + struct mutex lock; +}; + +static unsigned char iei_wt61p803_puzzle_checksum(unsigned char *buf, size_t len) +{ + unsigned char checksum = 0; + unsigned int i; + + for (i = 0; i < len; i++) + checksum ^= buf[i]; + + return checksum; +} + +static int iei_wt61p803_puzzle_process_resp(struct iei_wt61p803_puzzle *mcu, + unsigned char *raw_resp_data, size_t size) +{ + struct device *dev = &mcu->serdev->dev; + unsigned char checksum; + + mutex_lock(&mcu->reply_lock); + + /* Check the incoming frame header */ + if (!(raw_resp_data[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START || + raw_resp_data[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER || + (raw_resp_data[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM && + raw_resp_data[1] == IEI_WT61P803_PUZZLE_CMD_EEPROM_READ))) { + + /* Frame header is not correct, check whether to append */ + if (mcu->reply->state != FRAME_PROCESSING) { + dev_err(dev, "Invalid frame header and state (0x%x)", mcu->reply->state); + mutex_unlock(&mcu->reply_lock); + return -EIO; + } + + /* Append the frame to existing data */ + memcpy(mcu->reply->data + mcu->reply->size, raw_resp_data, size); + mcu->reply->size += size; + } else { + /* Start processing a new frame */ + memcpy(mcu->reply->data, raw_resp_data, size); + mcu->reply->size = size; + mcu->reply->state = FRAME_PROCESSING; + } + + checksum = iei_wt61p803_puzzle_checksum(mcu->reply->data, mcu->reply->size - 1); + + if (checksum != mcu->reply->data[mcu->reply->size - 1]) { + /* The checksum isn't matched yet, wait for new frames */ + mutex_unlock(&mcu->reply_lock); + return (int)size; + } + + /* Received all the data */ + mcu->reply->state = FRAME_OK; + complete(&mcu->reply->received); + + mutex_unlock(&mcu->reply_lock); + + return (int)size; +} + +static int iei_wt61p803_puzzle_recv_buf(struct serdev_device *serdev, + const unsigned char *data, size_t size) +{ + struct iei_wt61p803_puzzle *mcu = serdev_device_get_drvdata(serdev); + int ret; + + ret = iei_wt61p803_puzzle_process_resp(mcu, (unsigned char *)data, size); + + /* Return the number of processed bytes if function returns error */ + if (ret < 0) + return (int)size; + + return ret; +} + +static const struct serdev_device_ops iei_wt61p803_puzzle_serdev_device_ops = { + .receive_buf = iei_wt61p803_puzzle_recv_buf, + .write_wakeup = serdev_device_write_wakeup, +}; + +/** + * iei_wt61p803_puzzle_write_command_watchdog() - Watchdog of the normal cmd + * @mcu: Pointer to the iei_wt61p803_puzzle core MFD struct + * @cmd: Pointer to the char array to send (size should be content + 1 (xor)) + * @size: Size of the cmd char array + * @reply_data: Pointer to the reply/response data array (should be allocated) + * @reply_size: Pointer to size_t (size of reply_data) + * @retry_count: Number of times to retry sending the command to the MCU + */ +int iei_wt61p803_puzzle_write_command_watchdog(struct iei_wt61p803_puzzle *mcu, + unsigned char *cmd, size_t size, + unsigned char *reply_data, + size_t *reply_size, int retry_count) +{ + struct device *dev = &mcu->serdev->dev; + int ret, i; + + for (i = 0; i < retry_count; i++) { + ret = iei_wt61p803_puzzle_write_command(mcu, cmd, size, + reply_data, reply_size); + + if (ret != -ETIMEDOUT) + return ret; + } + + dev_err(dev, "%s: Command response timed out. Retries: %d", __func__, retry_count); + + return -ETIMEDOUT; +} +EXPORT_SYMBOL_GPL(iei_wt61p803_puzzle_write_command_watchdog); + +/** + * iei_wt61p803_puzzle_write_command() - Send a structured command to the MCU + * @mcu: Pointer to the iei_wt61p803_puzzle core MFD struct + * @cmd: Pointer to the char array to send (size should be content + 1 (xor)) + * @size: Size of the cmd char array + * @reply_data: Pointer to the reply/response data array (should be allocated) + * + * Sends a structured command to the MCU. + */ +int iei_wt61p803_puzzle_write_command(struct iei_wt61p803_puzzle *mcu, + unsigned char *cmd, size_t size, + unsigned char *reply_data, + size_t *reply_size) +{ + struct device *dev = &mcu->serdev->dev; + int ret; + int len = (int)size; + + if (size > IEI_WT61P803_PUZZLE_MAX_COMMAND_LENGTH) + return -EINVAL; + + cmd[len - 1] = iei_wt61p803_puzzle_checksum(cmd, size - 1); + + mutex_lock(&mcu->bus_lock); + mutex_lock(&mcu->reply_lock); + + if (!mcu->reply) { + ret = -EFAULT; + goto exit; + } + + /* Initialize reply struct */ + reinit_completion(&mcu->reply->received); + mcu->reply->state = FRAME_STRUCT_EMPTY; + mcu->reply->size = 0; + mutex_unlock(&mcu->reply_lock); + + ret = serdev_device_write(mcu->serdev, cmd, len, IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT); + + if (ret < 0) { + mutex_unlock(&mcu->bus_lock); + return ret; + } + + if (!wait_for_completion_timeout(&mcu->reply->received, + IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT)) { + dev_err(dev, "Command reply receive timeout\n"); + mutex_lock(&mcu->reply_lock); + reinit_completion(&mcu->reply->received); + mcu->reply->state = FRAME_TIMEOUT; + + ret = -ETIMEDOUT; + goto exit; + } + + mutex_lock(&mcu->reply_lock); + + if (!mcu->reply) { + ret = -EFAULT; + goto exit; + } + + *reply_size = mcu->reply->size; + /* Copy the received data, as it will not be available after a new frame is received */ + memcpy(reply_data, mcu->reply->data, mcu->reply->size); + + ret = 0; +exit: + mutex_unlock(&mcu->reply_lock); + mutex_unlock(&mcu->bus_lock); + return ret; +} +EXPORT_SYMBOL_GPL(iei_wt61p803_puzzle_write_command); + +int iei_wt61p803_puzzle_buzzer(struct iei_wt61p803_puzzle *mcu, bool long_beep) +{ + unsigned char *resp_buf = mcu->response_buffer; + unsigned char buzzer_cmd[4] = {}; + size_t reply_size = 0; + int ret; + + buzzer_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START; + buzzer_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FUNCTION_SINGLE; + buzzer_cmd[2] = long_beep ? '3' : '2'; /* Buzzer 1.5 / 0.5 second beep */ + + mutex_lock(&mcu->lock); + ret = iei_wt61p803_puzzle_write_command(mcu, buzzer_cmd, sizeof(buzzer_cmd), + resp_buf, &reply_size); + if (ret) + goto exit; + + if (reply_size != 3) { + ret = -EIO; + goto exit; + } + + if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START && + resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK && + resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) { + ret = -EPROTO; + goto exit; + } +exit: + mutex_unlock(&mcu->lock); + return ret; +} + +static int iei_wt61p803_puzzle_get_version(struct iei_wt61p803_puzzle *mcu) +{ + static unsigned char version_cmd[3] = { + IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER, + IEI_WT61P803_PUZZLE_CMD_OTHER_VERSION, + }; + static unsigned char build_info_cmd[3] = { + IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER, + IEI_WT61P803_PUZZLE_CMD_OTHER_BUILD, + }; + static unsigned char bootloader_mode_cmd[3] = { + IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER, + IEI_WT61P803_PUZZLE_CMD_OTHER_BOOTLOADER_MODE, + }; + static unsigned char protocol_version_cmd[3] = { + IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER, + IEI_WT61P803_PUZZLE_CMD_OTHER_PROTOCOL_VERSION, + }; + unsigned char *rb = mcu->response_buffer; + size_t reply_size = 0; + int ret; + + mutex_lock(&mcu->lock); + + ret = iei_wt61p803_puzzle_write_command(mcu, version_cmd, sizeof(version_cmd), + rb, &reply_size); + if (ret) + goto err; + if (reply_size < 7) { + ret = -EIO; + goto err; + } + sprintf(mcu->version.version, "v%c.%c%c%c", rb[2], rb[3], rb[4], rb[5]); + + ret = iei_wt61p803_puzzle_write_command(mcu, build_info_cmd, + sizeof(build_info_cmd), rb, + &reply_size); + if (ret) + goto err; + if (reply_size < 15) { + ret = -EIO; + goto err; + } + sprintf(mcu->version.build_info, "%c%c/%c%c/%c%c%c%c %c%c:%c%c", + rb[8], rb[9], rb[6], rb[7], rb[2], + rb[3], rb[4], rb[5], rb[10], rb[11], + rb[12], rb[13]); + + ret = iei_wt61p803_puzzle_write_command(mcu, bootloader_mode_cmd, + sizeof(bootloader_mode_cmd), rb, + &reply_size); + if (ret) + goto err; + if (reply_size < 4) { + ret = -EIO; + goto err; + } + if (rb[2] == IEI_WT61P803_PUZZLE_CMD_OTHER_MODE_APPS) + mcu->version.bootloader_mode = false; + else if (rb[2] == IEI_WT61P803_PUZZLE_CMD_OTHER_MODE_BOOTLOADER) + mcu->version.bootloader_mode = true; + + ret = iei_wt61p803_puzzle_write_command(mcu, protocol_version_cmd, + sizeof(protocol_version_cmd), rb, + &reply_size); + if (ret) + goto err; + if (reply_size < 9) { + ret = -EIO; + goto err; + } + sprintf(mcu->version.protocol_version, "v%c.%c%c%c%c%c", + rb[7], rb[6], rb[5], rb[4], rb[3], rb[2]); +err: + mutex_unlock(&mcu->lock); + return ret; +} + +static int iei_wt61p803_puzzle_get_mcu_status(struct iei_wt61p803_puzzle *mcu) +{ + static unsigned char mcu_status_cmd[5] = { + IEI_WT61P803_PUZZLE_CMD_HEADER_START, + IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER, + IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS, + IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS, + }; + unsigned char *resp_buf = mcu->response_buffer; + size_t reply_size = 0; + int ret; + + mutex_lock(&mcu->lock); + ret = iei_wt61p803_puzzle_write_command(mcu, mcu_status_cmd, sizeof(mcu_status_cmd), + resp_buf, &reply_size); + if (ret) + goto exit; + if (reply_size < 20) { + ret = -EIO; + goto exit; + } + + /* Response format: + * (IDX RESPONSE) + * 0 @ + * 1 O + * 2 S + * 3 S + * ... + * 5 AC Recovery Status Flag + * ... + * 10 Power Loss Recovery + * ... + * 19 Power Status (system power on method) + * 20 XOR checksum + */ + if (resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START && + resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER && + resp_buf[2] == IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS && + resp_buf[3] == IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS) { + mcu->status.ac_recovery_status_flag = resp_buf[5]; + mcu->status.power_loss_recovery = resp_buf[10]; + mcu->status.power_status = resp_buf[19]; + } +exit: + mutex_unlock(&mcu->lock); + return ret; +} + +static int iei_wt61p803_puzzle_get_serial_number(struct iei_wt61p803_puzzle *mcu) +{ + unsigned char *resp_buf = mcu->response_buffer; + static unsigned char serial_number_cmd[5] = { + IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM, + IEI_WT61P803_PUZZLE_CMD_EEPROM_READ, + 0x00, /* EEPROM read address */ + 0x24, /* Data length */ + }; + size_t reply_size = 0; + int ret; + + mutex_lock(&mcu->lock); + ret = iei_wt61p803_puzzle_write_command(mcu, serial_number_cmd, + sizeof(serial_number_cmd), + resp_buf, &reply_size); + if (ret) + goto err; + + sprintf(mcu->version.serial_number, "%.*s", + IEI_WT61P803_PUZZLE_VERSION_SN_LENGTH, resp_buf + 4); +err: + mutex_unlock(&mcu->lock); + return ret; +} + +static int iei_wt61p803_puzzle_write_serial_number(struct iei_wt61p803_puzzle *mcu, + unsigned char serial_number[36]) +{ + unsigned char *resp_buf = mcu->response_buffer; + unsigned char serial_number_header[4] = { + IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM, + IEI_WT61P803_PUZZLE_CMD_EEPROM_WRITE, + 0x00, /* EEPROM write address */ + 0xC, /* Data length */ + }; + unsigned char serial_number_cmd[4+12+1]; /* header, serial number, XOR checksum */ + size_t reply_size = 0; + int ret, sn_counter; + + /* The MCU can only handle 22 byte messages, send the S/N in 12 byte chunks */ + mutex_lock(&mcu->lock); + for (sn_counter = 0; sn_counter < 3; sn_counter++) { + serial_number_header[2] = 0x0 + (0xC) * sn_counter; + + memcpy(serial_number_cmd, serial_number_header, 4); + memcpy(serial_number_cmd + 4, serial_number + (0xC) * sn_counter, 0xC); + + serial_number_cmd[sizeof(serial_number_cmd) - 1] = 0; + + ret = iei_wt61p803_puzzle_write_command(mcu, serial_number_cmd, + sizeof(serial_number_cmd), + resp_buf, &reply_size); + if (ret) + goto err; + if (reply_size != 3) { + ret = -EIO; + goto err; + } + if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START && + resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK && + resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) { + ret = -EPROTO; + goto err; + } + } + + sprintf(mcu->version.serial_number, "%.*s", + IEI_WT61P803_PUZZLE_VERSION_SN_LENGTH, serial_number); +err: + mutex_unlock(&mcu->lock); + return ret; +} + +static int iei_wt61p803_puzzle_get_mac_addresses(struct iei_wt61p803_puzzle *mcu) +{ + unsigned char *resp_buf = mcu->response_buffer; + unsigned char mac_address_cmd[5] = { + IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM, + IEI_WT61P803_PUZZLE_CMD_EEPROM_READ, + 0x00, /* EEPROM read address */ + 0x11, /* Data length */ + }; + size_t reply_size = 0; + int ret, mac_counter; + + mutex_lock(&mcu->lock); + for (mac_counter = 0; mac_counter < 8; mac_counter++) { + mac_address_cmd[2] = 0x24 + (0x11) * mac_counter; + + ret = iei_wt61p803_puzzle_write_command(mcu, mac_address_cmd, + sizeof(mac_address_cmd), + resp_buf, &reply_size); + if (ret) + continue; + + if (reply_size < 22) { + ret = -EIO; + goto err; + } + + sprintf(mcu->version.mac_address[mac_counter], "%.*s", + IEI_WT61P803_PUZZLE_VERSION_MAC_LENGTH, resp_buf + 4); + } +err: + mutex_unlock(&mcu->lock); + return ret; +} + +static int iei_wt61p803_puzzle_write_mac_address(struct iei_wt61p803_puzzle *mcu, + unsigned char mac_address[17], + int mac_address_idx) +{ + unsigned char *resp_buf = mcu->response_buffer; + unsigned char mac_address_header[4] = { + IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM, + IEI_WT61P803_PUZZLE_CMD_EEPROM_WRITE, + 0x00, /* EEPROM write address */ + 0x11, /* Data length */ + }; + unsigned char mac_address_cmd[4+17+1]; /* header, MAC address, XOR checksum*/ + size_t reply_size = 0; + int ret; + + if (!(mac_address_idx < 8)) + return -EINVAL; + + mac_address_header[2] = 0x24 + (0x11) * mac_address_idx; + + /* Concat mac_address_header, mac_address to mac_address_cmd */ + memcpy(mac_address_cmd, mac_address_header, 4); + memcpy(mac_address_cmd + 4, mac_address, 17); + + mac_address_cmd[sizeof(mac_address_cmd) - 1] = 0; + + mutex_lock(&mcu->lock); + ret = iei_wt61p803_puzzle_write_command(mcu, mac_address_cmd, + sizeof(mac_address_cmd), + resp_buf, &reply_size); + if (ret) + goto err; + if (reply_size != 3) { + ret = -EIO; + goto err; + } + if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START && + resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK && + resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) { + ret = -EPROTO; + goto err; + } + + sprintf(mcu->version.mac_address[mac_address_idx], "%.*s", + IEI_WT61P803_PUZZLE_VERSION_MAC_LENGTH, mac_address); +err: + mutex_unlock(&mcu->lock); + return ret; +} + +static int iei_wt61p803_puzzle_write_power_loss_recovery(struct iei_wt61p803_puzzle *mcu, + int power_loss_recovery_action) +{ + unsigned char *resp_buf = mcu->response_buffer; + unsigned char power_loss_recovery_cmd[5] = {}; + unsigned char cmd_buf[2]; + size_t reply_size = 0; + int ret; + + if (power_loss_recovery_action < 0 || power_loss_recovery_action > 4) + return -EINVAL; + + ret = snprintf(cmd_buf, sizeof(cmd_buf), "%d", power_loss_recovery_action); + if (ret < 0) + return ret; + + power_loss_recovery_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START; + power_loss_recovery_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER; + power_loss_recovery_cmd[2] = IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_POWER_LOSS; + power_loss_recovery_cmd[3] = cmd_buf[0]; + + mutex_lock(&mcu->lock); + ret = iei_wt61p803_puzzle_write_command(mcu, power_loss_recovery_cmd, + sizeof(power_loss_recovery_cmd), + resp_buf, &reply_size); + if (ret) + goto exit; + mcu->status.power_loss_recovery = power_loss_recovery_action; +exit: + mutex_unlock(&mcu->lock); + return ret; +} + +#define sysfs_container(dev) \ + (container_of((dev)->kobj.parent, struct device, kobj)) + +static ssize_t version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + return sprintf(buf, "%s\n", mcu->version.version); +} +static DEVICE_ATTR_RO(version); + +static ssize_t build_info_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + return sprintf(buf, "%s\n", mcu->version.build_info); +} +static DEVICE_ATTR_RO(build_info); + +static ssize_t bootloader_mode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + return sprintf(buf, "%d\n", mcu->version.bootloader_mode); +} +static DEVICE_ATTR_RO(bootloader_mode); + +static ssize_t protocol_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + return sprintf(buf, "%s\n", mcu->version.protocol_version); +} +static DEVICE_ATTR_RO(protocol_version); + +static ssize_t serial_number_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + int ret; + + mutex_lock(&mcu->lock); + ret = sprintf(buf, "%s\n", mcu->version.serial_number); + mutex_unlock(&mcu->lock); + + return ret; +} + +static ssize_t serial_number_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned char serial_number[IEI_WT61P803_PUZZLE_VERSION_SN_LENGTH]; + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + int ret; + + if ((int)count != IEI_WT61P803_PUZZLE_VERSION_SN_LENGTH + 1) + return -EINVAL; + + memcpy(serial_number, (unsigned char *)buf, IEI_WT61P803_PUZZLE_VERSION_SN_LENGTH); + + ret = iei_wt61p803_puzzle_write_serial_number(mcu, serial_number); + if (ret) + return ret; + + return count; +} +static DEVICE_ATTR_RW(serial_number); + +static ssize_t mac_address_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + int idx, ret; + + mutex_lock(&mcu->lock); + + if (strlen(attr->attr.name) != 13) + return -EIO; + + idx = attr->attr.name[12] - '0'; + if (idx < 0 || idx > 7) + return -EIO; + + ret = sprintf(buf, "%s\n", mcu->version.mac_address[idx]); + + mutex_unlock(&mcu->lock); + return ret; +} + +static ssize_t mac_address_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned char mac_address[IEI_WT61P803_PUZZLE_VERSION_MAC_LENGTH]; + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + int idx, ret; + + if ((int)count != 17 + 1) + return -EINVAL; + + memcpy(mac_address, (unsigned char *)buf, IEI_WT61P803_PUZZLE_VERSION_MAC_LENGTH); + + if (strlen(attr->attr.name) != 13) + return -EIO; + + idx = attr->attr.name[12] - '0'; + if (idx < 0 || idx > 7) + return -EIO; + + ret = iei_wt61p803_puzzle_write_mac_address(mcu, mac_address, idx); + if (ret) + return ret; + + return count; +} +static DEVICE_ATTR(mac_address_0, 0644, mac_address_show, mac_address_store); +static DEVICE_ATTR(mac_address_1, 0644, mac_address_show, mac_address_store); +static DEVICE_ATTR(mac_address_2, 0644, mac_address_show, mac_address_store); +static DEVICE_ATTR(mac_address_3, 0644, mac_address_show, mac_address_store); +static DEVICE_ATTR(mac_address_4, 0644, mac_address_show, mac_address_store); +static DEVICE_ATTR(mac_address_5, 0644, mac_address_show, mac_address_store); +static DEVICE_ATTR(mac_address_6, 0644, mac_address_show, mac_address_store); +static DEVICE_ATTR(mac_address_7, 0644, mac_address_show, mac_address_store); + +static ssize_t ac_recovery_status_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + int ret; + + ret = iei_wt61p803_puzzle_get_mcu_status(mcu); + if (ret) + return ret; + + mutex_lock(&mcu->lock); + ret = sprintf(buf, "%x\n", mcu->status.ac_recovery_status_flag); + mutex_unlock(&mcu->lock); + + return ret; +} +static DEVICE_ATTR_RO(ac_recovery_status); + +static ssize_t power_loss_recovery_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + int ret; + + ret = iei_wt61p803_puzzle_get_mcu_status(mcu); + if (ret) + return ret; + + mutex_lock(&mcu->lock); + ret = sprintf(buf, "%x\n", mcu->status.power_loss_recovery); + mutex_unlock(&mcu->lock); + + return ret; +} + +static ssize_t power_loss_recovery_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + int ret; + long power_loss_recovery_action = 0; + + ret = kstrtol(buf, 10, &power_loss_recovery_action); + if (ret) + return ret; + + ret = iei_wt61p803_puzzle_write_power_loss_recovery(mcu, + (int)power_loss_recovery_action); + if (ret) + return ret; + + return count; +} +static DEVICE_ATTR_RW(power_loss_recovery); + +static ssize_t power_status_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + int ret; + + ret = iei_wt61p803_puzzle_get_mcu_status(mcu); + if (ret) + return ret; + + mutex_lock(&mcu->lock); + ret = sprintf(buf, "%x\n", mcu->status.power_status); + mutex_unlock(&mcu->lock); + + return ret; +} +static DEVICE_ATTR_RO(power_status); + +static struct attribute *iei_wt61p803_puzzle_attrs[] = { + &dev_attr_version.attr, + &dev_attr_build_info.attr, + &dev_attr_bootloader_mode.attr, + &dev_attr_protocol_version.attr, + &dev_attr_serial_number.attr, + &dev_attr_mac_address_0.attr, + &dev_attr_mac_address_1.attr, + &dev_attr_mac_address_2.attr, + &dev_attr_mac_address_3.attr, + &dev_attr_mac_address_4.attr, + &dev_attr_mac_address_5.attr, + &dev_attr_mac_address_6.attr, + &dev_attr_mac_address_7.attr, + &dev_attr_ac_recovery_status.attr, + &dev_attr_power_loss_recovery.attr, + &dev_attr_power_status.attr, + NULL +}; +ATTRIBUTE_GROUPS(iei_wt61p803_puzzle); + +static int iei_wt61p803_puzzle_sysfs_create(struct device *dev, + struct iei_wt61p803_puzzle *mcu) +{ + int ret; + + mcu->kobj = kobject_create_and_add("iei_wt61p803_puzzle_core", &dev->kobj); + if (!mcu->kobj) + return -ENOMEM; + + ret = sysfs_create_groups(mcu->kobj, iei_wt61p803_puzzle_groups); + if (ret) { + kobject_del(mcu->kobj); + kobject_put(mcu->kobj); + mcu->kobj = NULL; + } + + return ret; +} + +static int iei_wt61p803_puzzle_sysfs_remove(struct device *dev, + struct iei_wt61p803_puzzle *mcu) +{ + /* Remove sysfs groups */ + sysfs_remove_groups(mcu->kobj, iei_wt61p803_puzzle_groups); + + /* Remove the kobject */ + kobject_del(mcu->kobj); + kobject_put(mcu->kobj); + mcu->kobj = NULL; + + return 0; +} + +static int iei_wt61p803_puzzle_probe(struct serdev_device *serdev) +{ + struct device *dev = &serdev->dev; + struct iei_wt61p803_puzzle *mcu; + u32 baud; + int ret; + + /* Read the baud rate from 'current-speed', because the MCU supports different rates */ + if (device_property_read_u32(dev, "current-speed", &baud)) { + dev_err(dev, + "'current-speed' is not specified in device node\n"); + return -EINVAL; + } + dev_info(dev, "Driver baud rate: %d", baud); + + /* Allocate the memory */ + mcu = devm_kzalloc(dev, sizeof(*mcu), GFP_KERNEL); + if (!mcu) + return -ENOMEM; + + mcu->reply = devm_kzalloc(dev, sizeof(*mcu->reply), GFP_KERNEL); + if (!mcu->reply) + return -ENOMEM; + + mcu->reply->data = devm_kzalloc(dev, IEI_WT61P803_PUZZLE_RESP_BUF_SIZE, GFP_KERNEL); + if (!mcu->reply->data) + return -ENOMEM; + + mcu->response_buffer = devm_kzalloc(dev, IEI_WT61P803_PUZZLE_BUF_SIZE, GFP_KERNEL); + if (!mcu->response_buffer) + return -ENOMEM; + + /* Initialize device struct data */ + mcu->serdev = serdev; + init_completion(&mcu->reply->received); + mutex_init(&mcu->reply_lock); + mutex_init(&mcu->bus_lock); + mutex_init(&mcu->lock); + + /* Setup UART interface */ + serdev_device_set_drvdata(serdev, mcu); + serdev_device_set_client_ops(serdev, &iei_wt61p803_puzzle_serdev_device_ops); + ret = devm_serdev_device_open(dev, serdev); + if (ret) + return ret; + serdev_device_set_baudrate(serdev, baud); + serdev_device_set_flow_control(serdev, false); + ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE); + if (ret) { + dev_err(dev, "Failed to set parity"); + return ret; + } + + ret = iei_wt61p803_puzzle_get_version(mcu); + if (ret) + return ret; + + ret = iei_wt61p803_puzzle_get_mac_addresses(mcu); + if (ret) + return ret; + + ret = iei_wt61p803_puzzle_get_serial_number(mcu); + if (ret) + return ret; + + dev_info(dev, "MCU version: %s", mcu->version.version); + dev_info(dev, "MCU firmware build info: %s", mcu->version.build_info); + dev_info(dev, "MCU in bootloader mode: %s", + mcu->version.bootloader_mode ? "true" : "false"); + dev_info(dev, "MCU protocol version: %s", mcu->version.protocol_version); + + if (device_property_read_bool(dev, "enable-beep")) { + ret = iei_wt61p803_puzzle_buzzer(mcu, false); + if (ret) + return ret; + } + + ret = iei_wt61p803_puzzle_sysfs_create(dev, mcu); + + return devm_of_platform_populate(dev); +} + +static void iei_wt61p803_puzzle_remove(struct serdev_device *serdev) +{ + struct device *dev = &serdev->dev; + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev); + + iei_wt61p803_puzzle_sysfs_remove(dev, mcu); +} + +static const struct of_device_id iei_wt61p803_puzzle_dt_ids[] = { + { .compatible = "iei,wt61p803-puzzle" }, + { } +}; + +MODULE_DEVICE_TABLE(of, iei_wt61p803_puzzle_dt_ids); + +static struct serdev_device_driver iei_wt61p803_puzzle_drv = { + .probe = iei_wt61p803_puzzle_probe, + .remove = iei_wt61p803_puzzle_remove, + .driver = { + .name = "iei-wt61p803-puzzle", + .of_match_table = iei_wt61p803_puzzle_dt_ids, + }, +}; + +module_serdev_device_driver(iei_wt61p803_puzzle_drv); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Luka Kovacic "); +MODULE_DESCRIPTION("IEI WT61P803 PUZZLE MCU Driver"); diff --git a/include/linux/mfd/iei-wt61p803-puzzle.h b/include/linux/mfd/iei-wt61p803-puzzle.h new file mode 100644 index 000000000000..7f2da5887dae --- /dev/null +++ b/include/linux/mfd/iei-wt61p803-puzzle.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* IEI WT61P803 PUZZLE MCU Driver + * System management microcontroller for fan control, temperature sensor reading, + * LED control and system identification on IEI Puzzle series ARM-based appliances. + * + * Copyright (C) 2020 Sartura Ltd. + * Author: Luka Kovacic + */ + +#ifndef _MFD_IEI_WT61P803_PUZZLE_H_ +#define _MFD_IEI_WT61P803_PUZZLE_H_ + +#define IEI_WT61P803_PUZZLE_BUF_SIZE 512 + +/* Command magic numbers */ +#define IEI_WT61P803_PUZZLE_CMD_HEADER_START 0x40 /* @ */ +#define IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER 0x25 /* % */ +#define IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM 0xF7 + +#define IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK 0x30 /* 0 */ +#define IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK 0x70 + +#define IEI_WT61P803_PUZZLE_CMD_EEPROM_READ 0xA1 +#define IEI_WT61P803_PUZZLE_CMD_EEPROM_WRITE 0xA0 + +#define IEI_WT61P803_PUZZLE_CMD_OTHER_VERSION 0x56 /* V */ +#define IEI_WT61P803_PUZZLE_CMD_OTHER_BUILD 0x42 /* B */ +#define IEI_WT61P803_PUZZLE_CMD_OTHER_BOOTLOADER_MODE 0x4D /* M */ +#define IEI_WT61P803_PUZZLE_CMD_OTHER_MODE_BOOTLOADER 0x30 +#define IEI_WT61P803_PUZZLE_CMD_OTHER_MODE_APPS 0x31 +#define IEI_WT61P803_PUZZLE_CMD_OTHER_PROTOCOL_VERSION 0x50 /* P */ + +#define IEI_WT61P803_PUZZLE_CMD_FUNCTION_SINGLE 0x43 /* C */ +#define IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER 0x4F /* O */ +#define IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS 0x53 /* S */ +#define IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_POWER_LOSS 0x41 /* A */ + +#define IEI_WT61P803_PUZZLE_CMD_LED 0x52 /* R */ +#define IEI_WT61P803_PUZZLE_CMD_LED_POWER 0x31 /* 1 */ + +#define IEI_WT61P803_PUZZLE_CMD_TEMP 0x54 /* T */ +#define IEI_WT61P803_PUZZLE_CMD_TEMP_ALL 0x41 /* A */ + +#define IEI_WT61P803_PUZZLE_CMD_FAN 0x46 /* F */ +#define IEI_WT61P803_PUZZLE_CMD_FAN_PWM_READ 0x5A /* Z */ +#define IEI_WT61P803_PUZZLE_CMD_FAN_PWM_WRITE 0x57 /* W */ +#define IEI_WT61P803_PUZZLE_CMD_FAN_PWM_BASE 0x30 +#define IEI_WT61P803_PUZZLE_CMD_FAN_RPM_BASE 0x41 /* A */ + +#define IEI_WT61P803_PUZZLE_CMD_FAN_PWM(x) (IEI_WT61P803_PUZZLE_CMD_FAN_PWM_BASE + (x)) /* 0 - 1 */ +#define IEI_WT61P803_PUZZLE_CMD_FAN_RPM(x) (IEI_WT61P803_PUZZLE_CMD_FAN_RPM_BASE + (x)) /* 0 - 5 */ + +struct iei_wt61p803_puzzle_mcu_version; +struct iei_wt61p803_puzzle_reply; +struct iei_wt61p803_puzzle; + +int iei_wt61p803_puzzle_write_command_watchdog(struct iei_wt61p803_puzzle *mcu, + unsigned char *cmd, size_t size, + unsigned char *reply_data, size_t *reply_size, + int retry_count); + +int iei_wt61p803_puzzle_write_command(struct iei_wt61p803_puzzle *mcu, + unsigned char *cmd, size_t size, + unsigned char *reply_data, size_t *reply_size); + +#endif /* _MFD_IEI_WT61P803_PUZZLE_H_ */ From patchwork Sun Oct 25 00:59:13 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luka Kovacic X-Patchwork-Id: 286011 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=-12.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,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 CEDACC5DF9D for ; Sun, 25 Oct 2020 00:59:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 974B422281 for ; Sun, 25 Oct 2020 00:59:40 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=sartura-hr.20150623.gappssmtp.com header.i=@sartura-hr.20150623.gappssmtp.com header.b="HDFBG5eh" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1765985AbgJYA7i (ORCPT ); Sat, 24 Oct 2020 20:59:38 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37770 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1765966AbgJYA7h (ORCPT ); Sat, 24 Oct 2020 20:59:37 -0400 Received: from mail-ed1-x541.google.com (mail-ed1-x541.google.com [IPv6:2a00:1450:4864:20::541]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 92D3CC0613D2 for ; Sat, 24 Oct 2020 17:59:36 -0700 (PDT) Received: by mail-ed1-x541.google.com with SMTP id t20so5656689edr.11 for ; Sat, 24 Oct 2020 17:59:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sartura-hr.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=WI9VwF+ufq92z8MsS1+THxz8jpbVH4hcPGiOb9osy/g=; b=HDFBG5eh378Z+ZzP24RwcUCjkr9r9mlanAXH4RN0NbIGy4r3qIS/ASOWFz3SBrgjHj eynbHL8FeHJWy2YEIF7apZuqYmlp3BSXpXfEtYsJjYYoa54GF3LHCST3O+MomxxEamDF pqChcy7o1ftWNGl3CSJ5/ewQ7CHiUmb5nvmMlKmDK8BmB8+glZAI+kHzu/Zzj3ibQeVa 3myD3pK2ftUW0SuaC7pWVFv3xoEJ8rdrHm0G5VFlqGj8sDyc8ki82fpk0SCTd4fhiyH2 FGnwjk6x222PY27IMIWON0GhiaTex3yytlj1aVmoZ/8dVxlHhAET2i4/SzVVMN3lTLZR 2KzA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=WI9VwF+ufq92z8MsS1+THxz8jpbVH4hcPGiOb9osy/g=; b=YiF6m5wjgT0KXMco6Z08MLNQu0aLn3qow2ICgab2YOyxtthtazrL3OeISZufvavRtj mcpuXsCQym5tUcgLTcMstWsn6fwOPS4ssD1z0bc5PftPAhhxe6w7l4EdKP9TUFpM+BbI g8gXSmKCnMGynPW1HFYALTLIQ2MT/43kFbooY2HELWvsopzvMpPw68UGfaZ3x1GLMyvP O1o6dYvv2oQHXviehT53v+l0s82NDlzWcg3o4PEbKcsBJ9QWgwvc1CtV86vv08SAQXcL hjDzKN1IcOAs1449PriUmOD8eQkfNUz0hxBwWrx68TBQNGp59uLG3k6tKW4bjvcCrVWY d38g== X-Gm-Message-State: AOAM5319X71brQ21k9N24TzGQ9rUMsyB1nF7fARupI2wIX7/lOcAF70Z Zk3GqsligGDhwBGQvnu5JJeEiA== X-Google-Smtp-Source: ABdhPJziTfHeGxUXgrliWuDjH7qYJwmIac1Va6PW31cdco4jcxTb2ME+7HEY9d9Ai93z2MqH3M41eg== X-Received: by 2002:a50:c04e:: with SMTP id u14mr9407621edd.134.1603587575173; Sat, 24 Oct 2020 17:59:35 -0700 (PDT) Received: from localhost.localdomain ([2a00:ee2:4b0d:3001:fbc5:498b:ed6d:cfac]) by smtp.gmail.com with ESMTPSA id q5sm2797274edt.79.2020.10.24.17.59.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 24 Oct 2020 17:59:34 -0700 (PDT) From: Luka Kovacic To: linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-leds@vger.kernel.org, devicetree@vger.kernel.org Cc: lee.jones@linaro.org, pavel@ucw.cz, dmurphy@ti.com, robh+dt@kernel.org, jdelvare@suse.com, linux@roeck-us.net, marek.behun@nic.cz, luka.perkov@sartura.hr, andy.shevchenko@gmail.com, robert.marko@sartura.hr, Luka Kovacic Subject: [PATCH v7 3/6] drivers: hwmon: Add the IEI WT61P803 PUZZLE HWMON driver Date: Sun, 25 Oct 2020 02:59:13 +0200 Message-Id: <20201025005916.64747-4-luka.kovacic@sartura.hr> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201025005916.64747-1-luka.kovacic@sartura.hr> References: <20201025005916.64747-1-luka.kovacic@sartura.hr> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Add the IEI WT61P803 PUZZLE HWMON driver, that handles the fan speed control via PWM, reading fan speed and reading on-board temperature sensors. The driver registers a HWMON device and a simple thermal cooling device to enable in-kernel fan management. This driver depends on the IEI WT61P803 PUZZLE MFD driver. Signed-off-by: Luka Kovacic Cc: Luka Perkov Cc: Robert Marko --- drivers/hwmon/Kconfig | 8 + drivers/hwmon/Makefile | 1 + drivers/hwmon/iei-wt61p803-puzzle-hwmon.c | 412 ++++++++++++++++++++++ 3 files changed, 421 insertions(+) create mode 100644 drivers/hwmon/iei-wt61p803-puzzle-hwmon.c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 8dc28b26916e..e0469384af2a 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -722,6 +722,14 @@ config SENSORS_IBMPOWERNV This driver can also be built as a module. If so, the module will be called ibmpowernv. +config SENSORS_IEI_WT61P803_PUZZLE_HWMON + tristate "IEI WT61P803 PUZZLE MFD HWMON Driver" + depends on MFD_IEI_WT61P803_PUZZLE + help + The IEI WT61P803 PUZZLE MFD HWMON Driver handles reading fan speed + and writing fan PWM values. It also supports reading on-board + temperature sensors. + config SENSORS_IIO_HWMON tristate "Hwmon driver that uses channels specified via iio maps" depends on IIO diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index a8f4b35b136b..b0afb2d6896f 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -83,6 +83,7 @@ obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o obj-$(CONFIG_SENSORS_I5500) += i5500_temp.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o +obj-$(CONFIG_SENSORS_IEI_WT61P803_PUZZLE_HWMON) += iei-wt61p803-puzzle-hwmon.o obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o diff --git a/drivers/hwmon/iei-wt61p803-puzzle-hwmon.c b/drivers/hwmon/iei-wt61p803-puzzle-hwmon.c new file mode 100644 index 000000000000..61b06c9e61df --- /dev/null +++ b/drivers/hwmon/iei-wt61p803-puzzle-hwmon.c @@ -0,0 +1,412 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* IEI WT61P803 PUZZLE MCU HWMON Driver + * + * Copyright (C) 2020 Sartura Ltd. + * Author: Luka Kovacic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IEI_WT61P803_PUZZLE_HWMON_MAX_TEMP 2 +#define IEI_WT61P803_PUZZLE_HWMON_MAX_FAN 5 +#define IEI_WT61P803_PUZZLE_HWMON_MAX_PWM 2 +#define IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL 255 + +/** + * struct iei_wt61p803_puzzle_thermal_cooling_device - Thermal cooling device instance + * @mcu_hwmon: Parent driver struct pointer + * @tcdev: Thermal cooling device pointer + * @name: Thermal cooling device name + * @pwm_channel: Controlled PWM channel (0 or 1) + * @cooling_levels: Thermal cooling device cooling levels (DT) + */ +struct iei_wt61p803_puzzle_thermal_cooling_device { + struct iei_wt61p803_puzzle_hwmon *mcu_hwmon; + struct thermal_cooling_device *tcdev; + char name[THERMAL_NAME_LENGTH]; + int pwm_channel; + u8 *cooling_levels; +}; + +/** + * struct iei_wt61p803_puzzle_hwmon - MCU HWMON Driver + * @mcu: MCU struct pointer + * @response_buffer Global MCU response buffer allocation + * @thermal_cooling_dev_present: Per-channel thermal cooling device control indicator + * @cdev: Per-channel thermal cooling device private structure + */ +struct iei_wt61p803_puzzle_hwmon { + struct iei_wt61p803_puzzle *mcu; + unsigned char *response_buffer; + bool thermal_cooling_dev_present[IEI_WT61P803_PUZZLE_HWMON_MAX_PWM]; + struct iei_wt61p803_puzzle_thermal_cooling_device + *cdev[IEI_WT61P803_PUZZLE_HWMON_MAX_PWM]; +}; + +#define raw_temp_to_milidegree_celsius(x) (((x) - 0x80) * 1000) +static int iei_wt61p803_puzzle_read_temp_sensor(struct iei_wt61p803_puzzle_hwmon *mcu_hwmon, + int channel, long *value) +{ + unsigned char *resp_buf = mcu_hwmon->response_buffer; + static unsigned char temp_sensor_ntc_cmd[4] = { + IEI_WT61P803_PUZZLE_CMD_HEADER_START, + IEI_WT61P803_PUZZLE_CMD_TEMP, + IEI_WT61P803_PUZZLE_CMD_TEMP_ALL, + }; + size_t reply_size = 0; + int ret; + + ret = iei_wt61p803_puzzle_write_command(mcu_hwmon->mcu, temp_sensor_ntc_cmd, + sizeof(temp_sensor_ntc_cmd), resp_buf, + &reply_size); + + if (ret) + return ret; + + if (reply_size != 7) + return -EIO; + + /* Check the number of NTC values */ + if (resp_buf[3] != '2') + return -EIO; + + *value = raw_temp_to_milidegree_celsius(resp_buf[4 + channel]); + + return 0; +} + +#define raw_fan_val_to_rpm(x, y) ((((x) << 8 | (y)) / 2) * 60) +static int iei_wt61p803_puzzle_read_fan_speed(struct iei_wt61p803_puzzle_hwmon *mcu_hwmon, + int channel, long *value) +{ + unsigned char *resp_buf = mcu_hwmon->response_buffer; + unsigned char fan_speed_cmd[4] = {}; + size_t reply_size = 0; + int ret; + + fan_speed_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START; + fan_speed_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FAN; + fan_speed_cmd[2] = IEI_WT61P803_PUZZLE_CMD_FAN_RPM(channel); + + ret = iei_wt61p803_puzzle_write_command(mcu_hwmon->mcu, fan_speed_cmd, + sizeof(fan_speed_cmd), resp_buf, + &reply_size); + if (ret) + return ret; + + if (reply_size != 7) + return -EIO; + + *value = raw_fan_val_to_rpm(resp_buf[3], resp_buf[4]); + + return 0; +} + +static int iei_wt61p803_puzzle_write_pwm_channel(struct iei_wt61p803_puzzle_hwmon *mcu_hwmon, + int channel, long pwm_set_val) +{ + unsigned char *resp_buf = mcu_hwmon->response_buffer; + unsigned char pwm_set_cmd[6] = {}; + size_t reply_size = 0; + int ret; + + pwm_set_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START; + pwm_set_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FAN; + pwm_set_cmd[2] = IEI_WT61P803_PUZZLE_CMD_FAN_PWM_WRITE; + pwm_set_cmd[3] = IEI_WT61P803_PUZZLE_CMD_FAN_PWM(channel); + pwm_set_cmd[4] = (unsigned char)pwm_set_val; + + ret = iei_wt61p803_puzzle_write_command(mcu_hwmon->mcu, pwm_set_cmd, + sizeof(pwm_set_cmd), resp_buf, + &reply_size); + if (ret) + return ret; + + if (reply_size != 3) + return -EIO; + + if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START && + resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK && + resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) + return -EIO; + + return 0; +} + +static int iei_wt61p803_puzzle_read_pwm_channel(struct iei_wt61p803_puzzle_hwmon *mcu_hwmon, + int channel, long *value) +{ + unsigned char *resp_buf = mcu_hwmon->response_buffer; + unsigned char pwm_get_cmd[5] = {}; + size_t reply_size = 0; + int ret; + + pwm_get_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START; + pwm_get_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FAN; + pwm_get_cmd[2] = IEI_WT61P803_PUZZLE_CMD_FAN_PWM_READ; + pwm_get_cmd[3] = IEI_WT61P803_PUZZLE_CMD_FAN_PWM(channel); + + ret = iei_wt61p803_puzzle_write_command(mcu_hwmon->mcu, pwm_get_cmd, + sizeof(pwm_get_cmd), resp_buf, + &reply_size); + if (ret) + return ret; + + if (reply_size != 5) + return -EIO; + + if (resp_buf[2] != IEI_WT61P803_PUZZLE_CMD_FAN_PWM_READ) + return -EIO; + + *value = resp_buf[3]; + + return 0; +} + +static int iei_wt61p803_puzzle_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = dev_get_drvdata(dev->parent); + + switch (type) { + case hwmon_pwm: + return iei_wt61p803_puzzle_read_pwm_channel(mcu_hwmon, channel, val); + case hwmon_fan: + return iei_wt61p803_puzzle_read_fan_speed(mcu_hwmon, channel, val); + case hwmon_temp: + return iei_wt61p803_puzzle_read_temp_sensor(mcu_hwmon, channel, val); + default: + return -EINVAL; + } +} + +static int iei_wt61p803_puzzle_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = dev_get_drvdata(dev->parent); + + if (attr != hwmon_pwm_input) + return -EINVAL; + if (mcu_hwmon->thermal_cooling_dev_present[channel]) { + /* + * The Thermal Framework has already claimed this specific PWM + * channel. + */ + return -EBUSY; + } + return iei_wt61p803_puzzle_write_pwm_channel(mcu_hwmon, channel, val); +} + +static umode_t iei_wt61p803_puzzle_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_pwm: + if (attr == hwmon_pwm_input) + return 0644; + break; + case hwmon_fan: + if (attr == hwmon_fan_input) + return 0444; + break; + case hwmon_temp: + if (attr == hwmon_temp_input) + return 0444; + break; + default: + return 0; + } + + return 0; +} + +static const struct hwmon_ops iei_wt61p803_puzzle_hwmon_ops = { + .is_visible = iei_wt61p803_puzzle_is_visible, + .read = iei_wt61p803_puzzle_read, + .write = iei_wt61p803_puzzle_write, +}; + +static const struct hwmon_channel_info *iei_wt61p803_puzzle_info[] = { + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT, + HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_chip_info iei_wt61p803_puzzle_chip_info = { + .ops = &iei_wt61p803_puzzle_hwmon_ops, + .info = iei_wt61p803_puzzle_info, +}; + +static int iei_wt61p803_puzzle_get_max_state(struct thermal_cooling_device *tcdev, + unsigned long *state) +{ + *state = IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL; + + return 0; +} + +static int iei_wt61p803_puzzle_get_cur_state(struct thermal_cooling_device *tcdev, + unsigned long *state) +{ + struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; + struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = cdev->mcu_hwmon; + long value; + int ret; + + ret = iei_wt61p803_puzzle_read_pwm_channel(mcu_hwmon, cdev->pwm_channel, &value); + if (ret) + return ret; + + *state = value; + + return 0; +} + +static int iei_wt61p803_puzzle_set_cur_state(struct thermal_cooling_device *tcdev, + unsigned long state) +{ + struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; + struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = cdev->mcu_hwmon; + + return iei_wt61p803_puzzle_write_pwm_channel(mcu_hwmon, cdev->pwm_channel, state); +} + +static const struct thermal_cooling_device_ops iei_wt61p803_puzzle_cooling_ops = { + .get_max_state = iei_wt61p803_puzzle_get_max_state, + .get_cur_state = iei_wt61p803_puzzle_get_cur_state, + .set_cur_state = iei_wt61p803_puzzle_set_cur_state, +}; + +static int iei_wt61p803_puzzle_enable_thermal_cooling_dev(struct device *dev, + struct fwnode_handle *child, + struct iei_wt61p803_puzzle_hwmon *mcu_hwmon) +{ + struct iei_wt61p803_puzzle_thermal_cooling_device *cdev; + u32 pwm_channel; + u8 num_levels; + int ret; + + ret = fwnode_property_read_u32(child, "reg", &pwm_channel); + if (ret) + return ret; + + mcu_hwmon->thermal_cooling_dev_present[pwm_channel] = true; + + num_levels = fwnode_property_count_u8(child, "cooling-levels"); + if (!num_levels) + return -EINVAL; + + cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL); + if (!cdev) + return -ENOMEM; + + cdev->cooling_levels = devm_kmalloc_array(dev, num_levels, sizeof(u8), GFP_KERNEL); + if (!cdev->cooling_levels) + return -ENOMEM; + + ret = fwnode_property_read_u8_array(child, "cooling-levels", + cdev->cooling_levels, + num_levels); + if (ret) { + dev_err(dev, "Couldn't read property 'cooling-levels'"); + return ret; + } + + snprintf(cdev->name, THERMAL_NAME_LENGTH, "iei_wt61p803_puzzle_%d", pwm_channel); + + cdev->tcdev = devm_thermal_of_cooling_device_register(dev, NULL, cdev->name, cdev, + &iei_wt61p803_puzzle_cooling_ops); + if (IS_ERR(cdev->tcdev)) + return PTR_ERR(cdev->tcdev); + + cdev->mcu_hwmon = mcu_hwmon; + cdev->pwm_channel = pwm_channel; + + mcu_hwmon->cdev[pwm_channel] = cdev; + + return 0; +} + +static int iei_wt61p803_puzzle_hwmon_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev->parent); + struct iei_wt61p803_puzzle_hwmon *mcu_hwmon; + struct fwnode_handle *child; + struct device *hwmon_dev; + int ret; + + mcu_hwmon = devm_kzalloc(dev, sizeof(*mcu_hwmon), GFP_KERNEL); + if (!mcu_hwmon) + return -ENOMEM; + + mcu_hwmon->response_buffer = devm_kzalloc(dev, IEI_WT61P803_PUZZLE_BUF_SIZE, GFP_KERNEL); + if (!mcu_hwmon->response_buffer) + return -ENOMEM; + + mcu_hwmon->mcu = mcu; + platform_set_drvdata(pdev, mcu_hwmon); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, "iei_wt61p803_puzzle", + mcu_hwmon, + &iei_wt61p803_puzzle_chip_info, + NULL); + + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + /* Control fans via PWM lines via Linux Kernel */ + if (IS_ENABLED(CONFIG_THERMAL)) { + device_for_each_child_node(dev, child) { + ret = iei_wt61p803_puzzle_enable_thermal_cooling_dev(dev, child, mcu_hwmon); + if (ret) { + dev_err(dev, "Enabling the PWM fan failed\n"); + fwnode_handle_put(child); + return ret; + } + } + } + return 0; +} + +static const struct of_device_id iei_wt61p803_puzzle_hwmon_id_table[] = { + { .compatible = "iei,wt61p803-puzzle-hwmon" }, + {} +}; +MODULE_DEVICE_TABLE(of, iei_wt61p803_puzzle_hwmon_id_table); + +static struct platform_driver iei_wt61p803_puzzle_hwmon_driver = { + .driver = { + .name = "iei-wt61p803-puzzle-hwmon", + .of_match_table = iei_wt61p803_puzzle_hwmon_id_table, + }, + .probe = iei_wt61p803_puzzle_hwmon_probe, +}; + +module_platform_driver(iei_wt61p803_puzzle_hwmon_driver); + +MODULE_DESCRIPTION("IEI WT61P803 PUZZLE MCU HWMON Driver"); +MODULE_AUTHOR("Luka Kovacic "); +MODULE_LICENSE("GPL"); From patchwork Sun Oct 25 00:59:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luka Kovacic X-Patchwork-Id: 286009 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=-12.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,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 27D57C55178 for ; Sun, 25 Oct 2020 00:59:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E081C2098B for ; Sun, 25 Oct 2020 00:59:55 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=sartura-hr.20150623.gappssmtp.com header.i=@sartura-hr.20150623.gappssmtp.com header.b="EcpONYT0" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1766005AbgJYA7z (ORCPT ); Sat, 24 Oct 2020 20:59:55 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37790 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1765988AbgJYA7k (ORCPT ); Sat, 24 Oct 2020 20:59:40 -0400 Received: from mail-ej1-x643.google.com (mail-ej1-x643.google.com [IPv6:2a00:1450:4864:20::643]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 890FEC0613D2 for ; Sat, 24 Oct 2020 17:59:39 -0700 (PDT) Received: by mail-ej1-x643.google.com with SMTP id z5so8182115ejw.7 for ; Sat, 24 Oct 2020 17:59:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sartura-hr.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=pNka07v35n6GgBVGUaqQWih7YFOHx7rnG3PDXmNysho=; b=EcpONYT0ZNR8buT9HysoZKNwlGo4hOdFTwyHgjKHQRUx//KMIUOZgwYkNYvRMAx6St SpStmG4Uru+Cxy7xuXNBHOUb5ukMc4N/t2O+sMF9D0IlRZmLL0ZDlclvT7l7BLrDWqZC kEIVSldHjKsRIaSOYLonAqbln8AmBBfHPZYVt+UnWap+hHb+dH1qvGY50/ppx6tDluJa 4uQLKJ+d58G/JA7tepNLWmEO2mBA5TxEW/eaQr8r3UQ7xpyC/8hQZLHY+xiQPQ0KMCM/ JjXhsrri4dkRcYEYAT4ZAz9zh7EoU9zDsKpiA7Ze47E4634dAnGTYBJDniKYBZIdMrSY XgUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=pNka07v35n6GgBVGUaqQWih7YFOHx7rnG3PDXmNysho=; b=DDZqi2luH5xVNB6RHWQFP9zRGIG1M/avT9JCPOZwNrtflvZBeVYXarsektqJKjFctK v82vH8NID5B2xINbypcUqTK/riatSWBd2rBmIwkxZKuGyKg4tF0BwhJUC0mnXMzGPCQO +LVhI5IIJ1JiDtHAgytrYnkVmz+UEr4uhQaFcZLkEEslMFZKDP/wfCnFSx9GyLK82Jus ZMWRhmcHJO4F43enbdlziIYmvTsToDGIsaOoCa60u6TiWrTpwFVMBu+pkVdjsfpnTcFR Y6MyFbtT7MtmaxDvWQSuGW2Ms1Tg7mBfYgzApIDDm4Rbeao0Vs6DGiupGLnoN1F9+QoM KxRw== X-Gm-Message-State: AOAM533XRIvLEnLCtIB2P1OhLlDHahqjQmLL9d39qBuFcvolAWeaQK2e DNe7hvvK1AIQCuTuqflmiRvhdQ== X-Google-Smtp-Source: ABdhPJzY+QvHOZ2QjJYNSLdieJ8BD30QoPWiBVY2MBle/4hccJm1XwsxCDi8yIvI1s+/ISzzm4QmCw== X-Received: by 2002:a17:906:158e:: with SMTP id k14mr8851965ejd.496.1603587576699; Sat, 24 Oct 2020 17:59:36 -0700 (PDT) Received: from localhost.localdomain ([2a00:ee2:4b0d:3001:fbc5:498b:ed6d:cfac]) by smtp.gmail.com with ESMTPSA id q5sm2797274edt.79.2020.10.24.17.59.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 24 Oct 2020 17:59:36 -0700 (PDT) From: Luka Kovacic To: linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-leds@vger.kernel.org, devicetree@vger.kernel.org Cc: lee.jones@linaro.org, pavel@ucw.cz, dmurphy@ti.com, robh+dt@kernel.org, jdelvare@suse.com, linux@roeck-us.net, marek.behun@nic.cz, luka.perkov@sartura.hr, andy.shevchenko@gmail.com, robert.marko@sartura.hr, Luka Kovacic Subject: [PATCH v7 4/6] drivers: leds: Add the IEI WT61P803 PUZZLE LED driver Date: Sun, 25 Oct 2020 02:59:14 +0200 Message-Id: <20201025005916.64747-5-luka.kovacic@sartura.hr> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201025005916.64747-1-luka.kovacic@sartura.hr> References: <20201025005916.64747-1-luka.kovacic@sartura.hr> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Add support for the IEI WT61P803 PUZZLE LED driver. Currently only the front panel power LED is supported, since it is the only LED on this board wired through the MCU. The LED is wired directly to the on-board MCU controller and is toggled using an MCU command. Support for more LEDs is going to be added in case more boards implement this microcontroller, as LEDs use many different GPIOs. This driver depends on the IEI WT61P803 PUZZLE MFD driver. Signed-off-by: Luka Kovacic Cc: Luka Perkov Cc: Robert Marko --- drivers/leds/Kconfig | 8 ++ drivers/leds/Makefile | 1 + drivers/leds/leds-iei-wt61p803-puzzle.c | 161 ++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 drivers/leds/leds-iei-wt61p803-puzzle.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 1c181df24eae..9028d9bb90b8 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -332,6 +332,14 @@ config LEDS_IPAQ_MICRO Choose this option if you want to use the notification LED on Compaq/HP iPAQ h3100 and h3600. +config LEDS_IEI_WT61P803_PUZZLE + tristate "LED Support for the IEI WT61P803 PUZZLE MCU" + depends on LEDS_CLASS + depends on MFD_IEI_WT61P803_PUZZLE + help + This option enables support for LEDs controlled by the IEI WT61P803 + M801 MCU. + config LEDS_HP6XX tristate "LED Support for the HP Jornada 6xx" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index c2c7d7ade0d0..cd362437fefd 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o obj-$(CONFIG_LEDS_IP30) += leds-ip30.o obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o +obj-$(CONFIG_LEDS_IEI_WT61P803_PUZZLE) += leds-iei-wt61p803-puzzle.o obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o diff --git a/drivers/leds/leds-iei-wt61p803-puzzle.c b/drivers/leds/leds-iei-wt61p803-puzzle.c new file mode 100644 index 000000000000..e52394b9d96c --- /dev/null +++ b/drivers/leds/leds-iei-wt61p803-puzzle.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* IEI WT61P803 PUZZLE MCU LED Driver + * + * Copyright (C) 2020 Sartura Ltd. + * Author: Luka Kovacic + */ + +#include +#include +#include +#include +#include +#include +#include + +enum iei_wt61p803_puzzle_led_state { + IEI_LED_OFF = 0x30, + IEI_LED_ON = 0x31, + IEI_LED_BLINK_5HZ = 0x32, + IEI_LED_BLINK_1HZ = 0x33, +}; + +/** + * struct iei_wt61p803_puzzle_led - MCU LED Driver + * @cdev: LED classdev + * @mcu: MCU struct pointer + * @response_buffer Global MCU response buffer allocation + * @lock: General mutex lock to protect simultaneous R/W access to led_power_state + * @led_power_state: State of the front panel power LED + */ +struct iei_wt61p803_puzzle_led { + struct led_classdev cdev; + struct iei_wt61p803_puzzle *mcu; + unsigned char *response_buffer; + struct mutex lock; + int led_power_state; +}; + +static inline struct iei_wt61p803_puzzle_led *cdev_to_iei_wt61p803_puzzle_led + (struct led_classdev *led_cdev) +{ + return container_of(led_cdev, struct iei_wt61p803_puzzle_led, cdev); +} + +static int iei_wt61p803_puzzle_led_brightness_set_blocking(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct iei_wt61p803_puzzle_led *priv = cdev_to_iei_wt61p803_puzzle_led(cdev); + unsigned char *resp_buf = priv->response_buffer; + unsigned char led_power_cmd[5] = {}; + size_t reply_size; + int ret; + + led_power_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START; + led_power_cmd[1] = IEI_WT61P803_PUZZLE_CMD_LED; + led_power_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_POWER; + led_power_cmd[3] = brightness == LED_OFF ? IEI_LED_OFF : IEI_LED_ON; + + ret = iei_wt61p803_puzzle_write_command(priv->mcu, led_power_cmd, + sizeof(led_power_cmd), + resp_buf, + &reply_size); + if (ret) + return ret; + + if (reply_size != 3) + return -EIO; + + if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START && + resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK && + resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) + return -EIO; + + mutex_lock(&priv->lock); + priv->led_power_state = brightness; + mutex_unlock(&priv->lock); + + return 0; +} + +static enum led_brightness iei_wt61p803_puzzle_led_brightness_get(struct led_classdev *cdev) +{ + struct iei_wt61p803_puzzle_led *priv = cdev_to_iei_wt61p803_puzzle_led(cdev); + int led_state; + + mutex_lock(&priv->lock); + led_state = priv->led_power_state; + mutex_unlock(&priv->lock); + + return led_state; +} + +static int iei_wt61p803_puzzle_led_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev->parent); + struct iei_wt61p803_puzzle_led *priv; + struct led_init_data init_data = {}; + struct fwnode_handle *child; + int ret; + u32 reg; + + if (device_get_child_node_count(dev) != 1) + return -EINVAL; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->response_buffer = devm_kzalloc(dev, IEI_WT61P803_PUZZLE_BUF_SIZE, GFP_KERNEL); + if (!priv->response_buffer) + return -ENOMEM; + + priv->mcu = mcu; + priv->led_power_state = 1; + mutex_init(&priv->lock); + dev_set_drvdata(dev, priv); + + child = device_get_next_child_node(dev, NULL); + + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret || reg > 1) { + dev_err(dev, "Could not register 'reg' (%u)\n", reg); + ret = -EINVAL; + goto err_child_node; + } + + priv->cdev.brightness_set_blocking = iei_wt61p803_puzzle_led_brightness_set_blocking; + priv->cdev.brightness_get = iei_wt61p803_puzzle_led_brightness_get; + priv->cdev.max_brightness = 1; + init_data.fwnode = child; + + ret = devm_led_classdev_register_ext(dev, &priv->cdev, &init_data); + if (ret) { + dev_err(dev, "Could not register LED\n"); + goto err_child_node; + } +err_child_node: + fwnode_handle_put(child); + return ret; +} + +static const struct of_device_id iei_wt61p803_puzzle_led_of_match[] = { + { .compatible = "iei,wt61p803-puzzle-leds" }, + { } +}; +MODULE_DEVICE_TABLE(of, iei_wt61p803_puzzle_led_of_match); + +static struct platform_driver iei_wt61p803_puzzle_led_driver = { + .driver = { + .name = "iei-wt61p803-puzzle-led", + .of_match_table = iei_wt61p803_puzzle_led_of_match, + }, + .probe = iei_wt61p803_puzzle_led_probe, +}; +module_platform_driver(iei_wt61p803_puzzle_led_driver); + +MODULE_DESCRIPTION("IEI WT61P803 PUZZLE front panel LED driver"); +MODULE_AUTHOR("Luka Kovacic "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:leds-iei-wt61p803-puzzle"); From patchwork Sun Oct 25 00:59:15 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luka Kovacic X-Patchwork-Id: 294287 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=-12.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,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 A91B4C56201 for ; Sun, 25 Oct 2020 00:59:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6D6812098B for ; Sun, 25 Oct 2020 00:59:55 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=sartura-hr.20150623.gappssmtp.com header.i=@sartura-hr.20150623.gappssmtp.com header.b="z2zvmmLy" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1766544AbgJYA7y (ORCPT ); Sat, 24 Oct 2020 20:59:54 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37806 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1766005AbgJYA7o (ORCPT ); Sat, 24 Oct 2020 20:59:44 -0400 Received: from mail-ej1-x633.google.com (mail-ej1-x633.google.com [IPv6:2a00:1450:4864:20::633]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 661C4C0613D5 for ; Sat, 24 Oct 2020 17:59:43 -0700 (PDT) Received: by mail-ej1-x633.google.com with SMTP id qh17so8189609ejb.6 for ; Sat, 24 Oct 2020 17:59:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sartura-hr.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=jsG9e64+hm1kWvWLBkpftUqDmTbdR66JgHzNao/M1zI=; b=z2zvmmLynPgR1cbJpxqtOTD+kgGz3tkVfBRK1tcVtKpke4GttpBchWQksQP5VYvTyL ZAcgK5TDZrBttv3+/L7nb+E0N/EL3YIIL73JVJyzVHawoK0RUTfOpDCZiMJEYjV6/7XU 8PnGCBimp3gyp6Ruabs+CLmLjfedrk065lS7XyathaleAXkV2SVfh/6ahWEKNzRfj0Hp jSyKOOx22en4us0Tjb3/siM+7E0jwGJzcmHD+jAMsYTfm1tehE4E74rr28Q+w4CBnrkt EGVwvmdqQWOVuI8R8Vn1DYpUnGLll64MpcG21W/GR0zFoROJ7vPAr8ITOT+xx2mrZBmJ ptQg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=jsG9e64+hm1kWvWLBkpftUqDmTbdR66JgHzNao/M1zI=; b=ttpr5vd3eHna/2N7WYsEDb2Hr9Do0g/rzIPUPYPIxw0M9mzsOZoZ5DZELf2Gz/v44J dR4WNrByjChVg+9rAObdaN/pCAjEhSHay4tfGSuv09qOFgRQhPznUUjZjq1s63df1+Ya pyn4LyWTPKCcFgZpx7JQmapU/2SA4IuIS/A1Yba3cXXJkfC7Ehn3TsPCyBv9vv6eZvEZ 5UlAGMX4EwNM6f+E0om8MfY1PxNPC5696EQn1CRkUWQJmjOjdywTHYd7tvjuzNQImBLK vXWS4eRpUiGSOoZJ33Ww/u7izIu2UzArseVDdU08ZIkNmTkBgToigAYI5Z5OCqS6Wdtz F8dw== X-Gm-Message-State: AOAM533RMnaAZucMVipJfRsaEx1/8HGVtXdKYL8s90pQ1czCjPhPcTz9 YOYuOgbm68n7TcHzrLwE1YmBmQ== X-Google-Smtp-Source: ABdhPJy2YUH+9pwxUXc+ErJ+s6/P4Fpl0mK8GJOl/Xh+cOmBWyjrESZVLgVUjULn9fBAd/G0ugvBqw== X-Received: by 2002:a17:906:edb0:: with SMTP id sa16mr9062858ejb.327.1603587578110; Sat, 24 Oct 2020 17:59:38 -0700 (PDT) Received: from localhost.localdomain ([2a00:ee2:4b0d:3001:fbc5:498b:ed6d:cfac]) by smtp.gmail.com with ESMTPSA id q5sm2797274edt.79.2020.10.24.17.59.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 24 Oct 2020 17:59:37 -0700 (PDT) From: Luka Kovacic To: linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-leds@vger.kernel.org, devicetree@vger.kernel.org Cc: lee.jones@linaro.org, pavel@ucw.cz, dmurphy@ti.com, robh+dt@kernel.org, jdelvare@suse.com, linux@roeck-us.net, marek.behun@nic.cz, luka.perkov@sartura.hr, andy.shevchenko@gmail.com, robert.marko@sartura.hr, Luka Kovacic Subject: [PATCH v7 5/6] Documentation/ABI: Add iei-wt61p803-puzzle driver sysfs interface documentation Date: Sun, 25 Oct 2020 02:59:15 +0200 Message-Id: <20201025005916.64747-6-luka.kovacic@sartura.hr> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201025005916.64747-1-luka.kovacic@sartura.hr> References: <20201025005916.64747-1-luka.kovacic@sartura.hr> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Add the iei-wt61p803-puzzle driver sysfs interface documentation to allow monitoring and control of the microcontroller from user space. Signed-off-by: Luka Kovacic Cc: Luka Perkov Cc: Robert Marko --- .../testing/sysfs-driver-iei-wt61p803-puzzle | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-iei-wt61p803-puzzle diff --git a/Documentation/ABI/testing/sysfs-driver-iei-wt61p803-puzzle b/Documentation/ABI/testing/sysfs-driver-iei-wt61p803-puzzle new file mode 100644 index 000000000000..6e71d85f3296 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-iei-wt61p803-puzzle @@ -0,0 +1,55 @@ +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/mac_address_* +Date: September 2020 +Contact: Luka Kovacic +Description: (RW) Internal factory assigned MAC address values + +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/serial_number +Date: September 2020 +Contact: Luka Kovacic +Description: (RW) Internal factory assigned serial number + +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/version +Date: September 2020 +Contact: Luka Kovacic +Description: (RO) Internal MCU firmware version + +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/protocol_version +Date: September 2020 +Contact: Luka Kovacic +Description: (RO) Internal MCU communication protocol version + +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/power_loss_recovery +Date: September 2020 +Contact: Luka Kovacic +Description: (RW) Host platform power loss recovery settings + Value mapping: 0 - Always-On, 1 - Always-Off, 2 - Always-AC, 3 - Always-WA + +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/bootloader_mode +Date: September 2020 +Contact: Luka Kovacic +Description: (RO) Internal MCU bootloader mode status + +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/power_status +Date: September 2020 +Contact: Luka Kovacic +Description: (RO) Power status indicates the host platform power on method. + Value mapping (bitwise list): + 0x80 - Null + 0x40 - Firmware flag + 0x20 - Power loss detection flag (powered off) + 0x10 - Power loss detection flag (AC mode) + 0x08 - Button power on + 0x04 - WOL power on + 0x02 - RTC alarm power on + 0x01 - AC recover power on + +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/build_info +Date: September 2020 +Contact: Luka Kovacic +Description: (RO) Internal MCU firmware build date + Format: yyyy/mm/dd hh:mm + +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/ac_recovery_status +Date: September 2020 +Contact: Luka Kovacic +Description: (RO) Host platform AC recovery status value From patchwork Sun Oct 25 00:59:16 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luka Kovacic X-Patchwork-Id: 286010 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=-12.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,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 6DBEDC56201 for ; Sun, 25 Oct 2020 00:59:45 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 38E9422281 for ; Sun, 25 Oct 2020 00:59:45 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=sartura-hr.20150623.gappssmtp.com header.i=@sartura-hr.20150623.gappssmtp.com header.b="QGH5hDI8" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1766004AbgJYA7o (ORCPT ); Sat, 24 Oct 2020 20:59:44 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37806 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1766005AbgJYA7l (ORCPT ); Sat, 24 Oct 2020 20:59:41 -0400 Received: from mail-ed1-x542.google.com (mail-ed1-x542.google.com [IPv6:2a00:1450:4864:20::542]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F073EC0613D7 for ; Sat, 24 Oct 2020 17:59:40 -0700 (PDT) Received: by mail-ed1-x542.google.com with SMTP id p13so5682763edi.7 for ; Sat, 24 Oct 2020 17:59:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sartura-hr.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=ve+g7yzha+kqfqdGqXpj/2snQKKTgSW/x5r8vVHUiK4=; b=QGH5hDI8Hd8y5MrXbJP2aNTf7VQEKvsotI6K0PQZxetjrvQh7qQKE8IQwIWEA7CNDG TSHSzD9U1rgrabpmY1YJYnSuBooGWGg8DKZGKSgCHH0x4NFae4wR3lb8H3wW+hwGKv9q /QcnB61IgnJsiGScDKgJMOwJKtwycFgTdw0nl4JxtvQMhUnc+vwB6kToi0aXAoUu3xSD rMeKVCb0wDoqV2zDcgrxWscaNfXaT7gWmSseGJeZUxp+escfiWYRK1Kv/KxDrKZKuLBr YkqN89WiotEsL4FoPgP7tQFdOVuP9K9+CUUJfzsd6Wd3v7lxMtn3N1wTvMw0qFfpyIyz HxoA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ve+g7yzha+kqfqdGqXpj/2snQKKTgSW/x5r8vVHUiK4=; b=MQ1LY9znDLcmfZYyDC4y7zxl972B1M87qv56ckhbP10F81UBnbaYAUzWkabLLQ5u+p 88exXYSLVH9iXbX5cEskFGAPRVxawV3xYcG1hdLYnAGwVPh0q4RbSthRVxWum4m9plkj ZA+MzDlhNtdnSeZ4I8Ce3lYfsz0ahgCFXy43s21ViDpuDI0Kiw6cCneaDZFYJXwwY0/r vuKynExXVltundcynaFvz3659D26Zn7VBp2chryhMUaTHfePOS4WDdeLaMTugqZVf1wc F/Q41g3qtV1pvyKZKTqQeAIxNnXltXTQRPw5isGDazh0h62mBAKdRrHSmnqxbnicQncV mL6w== X-Gm-Message-State: AOAM531eNCQ/9OpWkIq49JLJCWRca1bAcsdJMPUrTlwYS5huIo8kcoAA pNMIiPVBqXwZdTw0oCRBNIzwgBJvfGcdKQ== X-Google-Smtp-Source: ABdhPJwolPXq3q6ayIHgj2Llk3qDBqNWadvUfztpEUU6Xrq9oUb81d+t+e+WyC0U3Ysd1mlouuuQPQ== X-Received: by 2002:a50:d751:: with SMTP id i17mr9408440edj.337.1603587579517; Sat, 24 Oct 2020 17:59:39 -0700 (PDT) Received: from localhost.localdomain ([2a00:ee2:4b0d:3001:fbc5:498b:ed6d:cfac]) by smtp.gmail.com with ESMTPSA id q5sm2797274edt.79.2020.10.24.17.59.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 24 Oct 2020 17:59:38 -0700 (PDT) From: Luka Kovacic To: linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-leds@vger.kernel.org, devicetree@vger.kernel.org Cc: lee.jones@linaro.org, pavel@ucw.cz, dmurphy@ti.com, robh+dt@kernel.org, jdelvare@suse.com, linux@roeck-us.net, marek.behun@nic.cz, luka.perkov@sartura.hr, andy.shevchenko@gmail.com, robert.marko@sartura.hr, Luka Kovacic Subject: [PATCH v7 6/6] MAINTAINERS: Add an entry for the IEI WT61P803 PUZZLE driver Date: Sun, 25 Oct 2020 02:59:16 +0200 Message-Id: <20201025005916.64747-7-luka.kovacic@sartura.hr> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201025005916.64747-1-luka.kovacic@sartura.hr> References: <20201025005916.64747-1-luka.kovacic@sartura.hr> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Add an entry for the IEI WT61P803 PUZZLE driver (MFD, HWMON, LED drivers). Signed-off-by: Luka Kovacic Cc: Luka Perkov Cc: Robert Marko --- MAINTAINERS | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 867157311dc8..d56fdc300066 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8460,6 +8460,20 @@ F: include/net/nl802154.h F: net/ieee802154/ F: net/mac802154/ +IEI WT61P803 M801 MFD DRIVER +M: Luka Kovacic +M: Luka Perkov +L: linux-kernel@vger.kernel.org +S: Maintained +F: Documentation/ABI/stable/sysfs-driver-iei-wt61p803-puzzle +F: Documentation/devicetree/bindings/hwmon/iei,wt61p803-puzzle-hwmon.yaml +F: Documentation/devicetree/bindings/leds/iei,wt61p803-puzzle-leds.yaml +F: Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml +F: drivers/hwmon/iei-wt61p803-puzzle-hwmon.c +F: drivers/leds/leds-iei-wt61p803-puzzle.c +F: drivers/mfd/iei-wt61p803-puzzle.c +F: include/linux/mfd/iei-wt61p803-puzzle.h + IFE PROTOCOL M: Yotam Gigi M: Jamal Hadi Salim