From patchwork Wed Mar 12 09:25:59 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Andr=C3=A9_Draszik?= X-Patchwork-Id: 872934 Received: from mail-ej1-f49.google.com (mail-ej1-f49.google.com [209.85.218.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DE2B323AE7C for ; Wed, 12 Mar 2025 09:26:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741771578; cv=none; b=jFyDEhMJHebap3DJx7FnsTF7yvrWlQbJp3DxOU4bLPIyPD82WbJodLRZC9+4YbGduzSOAcwPJ3hkXwWbMONPAAK+TBlYwLt+3GnRW+T2Uf7k1lskyofdqFtDz5rhYZcqBWSmNPeqjWvybWskZXGrxTIZEpMQronDIEASy7FLCwk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741771578; c=relaxed/simple; bh=S9d+mPQHuwKwxhdfiMhyaYrX7SNT9BfoJz01stp/fyI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ZXwZX/eVToqIL1GjdZfO/bLUPqxZ7uQpHw/Ys4klmsX0I1VnmVE1hgplqIld5Zmk66aJHXzpwmMCemTspv7HumJwK4qEZCce3EUIdniagkYq7ID6QsBAx8a6+gmnPcS6PrzEOwkPFsiQyEHvF9enBliV6fJWWB9MsISmy9WqLD4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=K8T6sTC4; arc=none smtp.client-ip=209.85.218.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="K8T6sTC4" Received: by mail-ej1-f49.google.com with SMTP id a640c23a62f3a-ac2902f7c2aso586725466b.1 for ; Wed, 12 Mar 2025 02:26:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1741771574; x=1742376374; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=+371/vnLjhYbcobffzm7IbhNXrIuGqbRnBddVdteQIg=; b=K8T6sTC47WgDeHJh/eSrYP4hMkFEk08RGo6vQJv5QYZW0eQG12CYacL1ozFARn94uX YXtra6DJGFhrY7UOYVguPxQXSl9YR5oCDU2RDDOFym0Y4Fn0TT9V/B+y/QnFvSKaDk2G It/9x7YXJCDTg3LTfWb8l1jwz83aguzpM87civCWEbhDuHHpu/tFBF+YiO6dvB+AQUep ql937WLDqyJEjR4q3UExg4ld0uAHiWWXXqEFCCneYFzvI5JL4Bi3He19lEi0NOE89bLp TGOTJZ9ybNuLS/nuzK9XhyXWcs19lVQUs1xEOcPmDYiCyYAyQOQoho4ReKNalGGahybg K+9g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741771574; x=1742376374; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=+371/vnLjhYbcobffzm7IbhNXrIuGqbRnBddVdteQIg=; b=IBNAowP3nyXt9kg35Gq4IVTJ4tXnKObQg0xZNqstncd1TYZKhcUQWOulUMGzNpBaiQ ys12t3eESK8R46AuJwA+bh1mawl280HHfkF/6X7y0cONAMFJ95OZhOJBhEUiDbtl8uEN CvGky4mdul9SNlymnbPwmtbac9PEud05UvhZsPpQ2BaJgc7OLpyQTicwTaoQQcBs35/v 7iIJt+eO7/qaSNal3/wXi1VhqzcB2qvJXMqE3k3y463QolJ4yQS2+zobMCR1/hU8lAuo BWwvg1vAUrddRfHzBItIC37Is4wYkf/qi2gua5zzA1gt8TRmdtZ1r0/10PbHCCkoTfsI GALQ== X-Forwarded-Encrypted: i=1; AJvYcCX5NzxZ7xxAAqRLQt99atrXuSx2Eme7nBmk7LAL4Jkv83+9eunsoXFL+khPmwbQIymXyW6Q+LHcawfK@vger.kernel.org X-Gm-Message-State: AOJu0YwLrleGTPrCWlN+4P7xnYqYsZOkbuMUzCdQFou0s0ux3NDsHgBC suBoy8B1ZqwdZzIsHnqvxpiZ2ISODpR0I8HUC3e20FkKzH1W6hJi3Tt+PfJvKnM= X-Gm-Gg: ASbGncvz0F1BrIU5fr5+Sm+G1X08GxnkJWrgy/xokZPWzeNGjBEgkguJX1JzARTX3Bn 7Gt7MznMwdiqDDQu1185zhrXWhRYBlviViyHFTAHWfOqihC43xV6xcbLTcw7ruwLp9BY7VZH1R0 ieiIZj8vO/DddN4qYkl/ISwipZozFoFwjZXLjP1Ycoj8opJrz/5x4iTJrpRoK5q+2jH0Th0T5A9 6kY0Qd4deXk27GwLb484RVMu514PXMy8m6IzRngHoIFPQIgvAlLp21vm36FTEruhPf6pld9KB5X YBgs/6jgOsNXWjNHbbmuwYoTeeTRuP+nOhDs6e59BjZrL6yo6YNt9Q9zWBlCfr62B4naR2Ts0yF 1iHRTd2BfssMrFlZDNnFeJA6vRhX2 X-Google-Smtp-Source: AGHT+IE+gCLjQjQFoNvdTx7oZ/6z6vZSth34m9edgTqbZhf2/GIvkkBSr49cL/l1l5qBZrRdFw9wig== X-Received: by 2002:a17:907:9490:b0:ab7:c893:fc80 with SMTP id a640c23a62f3a-ac252b573bbmr2705420266b.24.1741771574042; Wed, 12 Mar 2025 02:26:14 -0700 (PDT) Received: from puffmais.c.googlers.com (8.239.204.35.bc.googleusercontent.com. [35.204.239.8]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ac25777c748sm894535266b.2.2025.03.12.02.26.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 12 Mar 2025 02:26:13 -0700 (PDT) From: =?utf-8?q?Andr=C3=A9_Draszik?= Date: Wed, 12 Mar 2025 09:25:59 +0000 Subject: [PATCH v4 1/6] dt-bindings: gpio: add max77759 binding Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250312-max77759-mfd-v4-1-b908d606c8cb@linaro.org> References: <20250312-max77759-mfd-v4-0-b908d606c8cb@linaro.org> In-Reply-To: <20250312-max77759-mfd-v4-0-b908d606c8cb@linaro.org> To: Lee Jones , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Linus Walleij , Bartosz Golaszewski , Srinivas Kandagatla , Kees Cook , "Gustavo A. R. Silva" Cc: Peter Griffin , Tudor Ambarus , Will McVicker , kernel-team@android.com, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-gpio@vger.kernel.org, linux-hardening@vger.kernel.org, =?utf-8?q?And?= =?utf-8?q?r=C3=A9_Draszik?= X-Mailer: b4 0.14.2 The Maxim MAX77759 is a companion PMIC for USB Type-C applications and includes Battery Charger, Fuel Gauge, temperature sensors, USB Type-C Port Controller (TCPC), NVMEM, and a GPIO expander. This describes its GPIO module. Reviewed-by: Rob Herring (Arm) Signed-off-by: André Draszik --- v2: * drop 'interrupts' property and sort properties alphabetically --- .../bindings/gpio/maxim,max77759-gpio.yaml | 44 ++++++++++++++++++++++ MAINTAINERS | 6 +++ 2 files changed, 50 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/maxim,max77759-gpio.yaml b/Documentation/devicetree/bindings/gpio/maxim,max77759-gpio.yaml new file mode 100644 index 0000000000000000000000000000000000000000..55734190d5ebdbc351e1f91675dddd8a9db80cd7 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/maxim,max77759-gpio.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/maxim,max77759-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim Integrated MAX77759 GPIO + +maintainers: + - André Draszik + +description: | + This module is part of the MAX77759 PMIC. For additional information, see + Documentation/devicetree/bindings/mfd/maxim,max77759.yaml. + + The MAX77759 is a PMIC integrating, amongst others, a GPIO controller + including interrupt support for 2 GPIO lines. + +properties: + compatible: + const: maxim,max77759-gpio + + "#gpio-cells": + const: 2 + + gpio-controller: true + + gpio-line-names: + minItems: 1 + maxItems: 2 + + "#interrupt-cells": + const: 2 + + interrupt-controller: true + +required: + - compatible + - "#gpio-cells" + - gpio-controller + - "#interrupt-cells" + - interrupt-controller + +additionalProperties: false diff --git a/MAINTAINERS b/MAINTAINERS index 86fa62154d3b23bf84ce2d53cbbdc33f3d43006e..a45d1bd64d116d14bc05a64fa3da852a41e1de7f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14347,6 +14347,12 @@ F: Documentation/devicetree/bindings/mfd/maxim,max77714.yaml F: drivers/mfd/max77714.c F: include/linux/mfd/max77714.h +MAXIM MAX77759 PMIC MFD DRIVER +M: André Draszik +L: linux-kernel@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/*/maxim,max77759*.yaml + MAXIM MAX77802 PMIC REGULATOR DEVICE DRIVER M: Javier Martinez Canillas L: linux-kernel@vger.kernel.org From patchwork Wed Mar 12 09:26:01 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Andr=C3=A9_Draszik?= X-Patchwork-Id: 872933 Received: from mail-ed1-f46.google.com (mail-ed1-f46.google.com [209.85.208.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F319923AE8D for ; Wed, 12 Mar 2025 09:26:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741771580; cv=none; b=LgJrS9u9ltuM7hI3khUr+OVutN4jqJ1RtaWTV0tGala4tdELtkhXbQYEJtRmm+nxbP5h4S3Vneylfu84BoIFuwCD0s/VT3UNwTLyXAJ4ji4OhcEznTtgfxnsEJy2Vo2vEAJdKekeJF/TdxYZBmFyTBWV9sAiNjwOjHbosZO0qJM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741771580; c=relaxed/simple; bh=kxHzYlD4Q7ouIup6NnoChAXZHHmMaS1kTRKj6jvl4Gw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=kEYRh+uh56itArRCG0CC8o29ZVB0jN1uS4T/1kIejG6BD0DEOtpUn1EQTkuYpFpFu6x2LH21k9LZmxfqANsDujHBVmCYnaAhN/5SNoHvb8tvPvY6RW1noUQYifFWR/calYgrnWLMNmSXpGEkZUF3d4/XgI+Gy/xBDRHtWNqS81c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=ik4Jjyd3; arc=none smtp.client-ip=209.85.208.46 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="ik4Jjyd3" Received: by mail-ed1-f46.google.com with SMTP id 4fb4d7f45d1cf-5e5deb6482cso1305049a12.1 for ; Wed, 12 Mar 2025 02:26:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1741771575; x=1742376375; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=NpG4SN8+SswQB9WTTY/KhrQ2hSRzm3/LAyaIh8poviQ=; b=ik4Jjyd3MjaIjHdhdqlWNF2fUvp0KT4S0KQwZZvPGnT5c0RJVyr/C4Kd2UreJgIMYh xLf3inY88BnaUi787eBVohPRXDjwW4in8VEizqhDWXYb4oG1vie51WTeKZ7hyhpjcoDU RN440RU4FPIBLlP/pUWar4rwBBy2JepgaljiuFp2JWC3kvew7tuz3bNzycYVTertz+oj K2Sb8ySI0HzfAx2b8aZ8jYWwcQYtPxTSIw5Fj2iIJLzETg3/GjZVjKadwi21xgrLr6XG WBTp1jtA9Z/SXCxBAlnibr7F8NJqGB5E10bLBoQJAO6x1kunbnRvncVqpPd4linD/xyk DJlQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741771575; x=1742376375; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=NpG4SN8+SswQB9WTTY/KhrQ2hSRzm3/LAyaIh8poviQ=; b=HcMnym3QlJjMB/auoBlJXCist4Nzz2rbLKGqjRH5f17EY7MzzkZib6YkYO8E2Ysmc9 yxn7STWHqYnYPkAtiBCxqUl/aL1pjGB3TrKh4evcET4fS8cvuHkr+h2UpU/DfB9KzEuT bGkejTtCxfW0qkjNJsVLxgHclLmtmDoAbARcBbP/+5Po4U8S8DeF13BWC2L3zFOsNqB4 r36pH7vjOcDb8feip4sL57pJayJS4RI5fhwBKRpUpkWkaMrxRB4FRoI54nGreJOnNGB6 XKJrL/23WoMziZDTy8mVXMU9U1DJ9G4kFPdr1fa6/8vXHwM/qflZg/luaEJyOeYj/o43 gz7Q== X-Forwarded-Encrypted: i=1; AJvYcCXItozZ3FRKxuUxOnNnLxFy7IQ3RsqCWDiTTSktFBPtfaUKvgdzgU0t6KB8wUxpiuOdOOlks7q16ZsQ@vger.kernel.org X-Gm-Message-State: AOJu0YzwA5Lgj49EzHb4QoNy9Iu2+/Sho41qWH1R+YSarqz0IBXWvsCz j5pAilJSB3RoivjjEf1NikMA2ErFMLkxG36cEQxUmM7iOFFUEhJOKg3d15b+HPfXM5PGByzTfMO y5Ps= X-Gm-Gg: ASbGncutIbRa5iBF9S2Jmjo6X6F3gaX64Nt4UBjine6isYCviRsB8Ga3sV2j4Opb+P1 MXKQxcfaRw00FQus7zlq4Fee7kd9JmPirRg3g7A9ljV2i/dqAO8E716IyYsIUiebnz32NfbrHwm 2h20Emns2A8Vzdh/ypNahXaJ+OuGzjiHRdcqdmxefwPUeUhXGcCivxDf1jnLlSfnbvbHwqU7Cki MQ/5dgx7SFk9KGdCLNBgxrso6Bz+aUMaIienSqcMXTRwccr0BO6BATYCE8BPIJMAUSbTn2tNfTz KY3cFoD2iZNRasIkstoir3kbOlJQD7k9YbInCw0oAKRh9qFrMxKemhnuWgJWaoC7LPpFfILY1qf uYQoHCVkUMxUEEbCUxSy6+zdck5MIJyt+TgZ3LUI= X-Google-Smtp-Source: AGHT+IGMTylQyff6vU9GdrAYd99Xs9o2ZI+8BDiTPyKAU8vPNdImCoElshfShEdRpTQwXFn9jHo+tg== X-Received: by 2002:a17:906:628a:b0:abf:27ac:2cf8 with SMTP id a640c23a62f3a-ac2ba563d91mr826133366b.21.1741771575046; Wed, 12 Mar 2025 02:26:15 -0700 (PDT) Received: from puffmais.c.googlers.com (8.239.204.35.bc.googleusercontent.com. [35.204.239.8]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ac25777c748sm894535266b.2.2025.03.12.02.26.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 12 Mar 2025 02:26:14 -0700 (PDT) From: =?utf-8?q?Andr=C3=A9_Draszik?= Date: Wed, 12 Mar 2025 09:26:01 +0000 Subject: [PATCH v4 3/6] dt-bindings: mfd: add max77759 binding Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250312-max77759-mfd-v4-3-b908d606c8cb@linaro.org> References: <20250312-max77759-mfd-v4-0-b908d606c8cb@linaro.org> In-Reply-To: <20250312-max77759-mfd-v4-0-b908d606c8cb@linaro.org> To: Lee Jones , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Linus Walleij , Bartosz Golaszewski , Srinivas Kandagatla , Kees Cook , "Gustavo A. R. Silva" Cc: Peter Griffin , Tudor Ambarus , Will McVicker , kernel-team@android.com, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-gpio@vger.kernel.org, linux-hardening@vger.kernel.org, =?utf-8?q?And?= =?utf-8?q?r=C3=A9_Draszik?= X-Mailer: b4 0.14.2 The Maxim MAX77759 is a companion PMIC for USB Type-C applications and includes Battery Charger, Fuel Gauge, temperature sensors, USB Type-C Port Controller (TCPC), NVMEM, and a GPIO expander. This describes the top-level device. Reviewed-by: Rob Herring (Arm) Signed-off-by: André Draszik --- v3: * drop gpio-controller and gpio-cells, GPIO is provided by the child (Rob) v2: * rename expected nvmem subdev nodename to 'nvmem-0' I'd have preferred just 'nvmem', but that matches nvmem-consumer.yaml and fails validation. Note: MAINTAINERS doesn't need updating, the binding update for the first leaf device (gpio) adds a wildcard matching all max77759 bindings --- .../devicetree/bindings/mfd/maxim,max77759.yaml | 99 ++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml b/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml new file mode 100644 index 0000000000000000000000000000000000000000..525de9ab3c2b7b431e48497330640857540625b1 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/maxim,max77759.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim Integrated MAX77759 PMIC for USB Type-C applications + +maintainers: + - André Draszik + +description: | + This is a part of device tree bindings for the MAX77759 companion Power + Management IC for USB Type-C applications. + + The MAX77759 includes Battery Charger, Fuel Gauge, temperature sensors, USB + Type-C Port Controller (TCPC), NVMEM, and a GPIO expander. + +properties: + compatible: + const: maxim,max77759 + + interrupts: + maxItems: 1 + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + + reg: + maxItems: 1 + + gpio: + $ref: /schemas/gpio/maxim,max77759-gpio.yaml + + nvmem-0: + $ref: /schemas/nvmem/maxim,max77759-nvmem.yaml + +required: + - compatible + - interrupts + - reg + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + pmic@66 { + compatible = "maxim,max77759"; + reg = <0x66>; + interrupts-extended = <&gpa8 3 IRQ_TYPE_LEVEL_LOW>; + + interrupt-controller; + #interrupt-cells = <2>; + + gpio { + compatible = "maxim,max77759-gpio"; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + nvmem-0 { + compatible = "maxim,max77759-nvmem"; + + nvmem-layout { + compatible = "fixed-layout"; + #address-cells = <1>; + #size-cells = <1>; + + reboot-mode@0 { + reg = <0x0 0x4>; + }; + + boot-reason@4 { + reg = <0x4 0x4>; + }; + + shutdown-user-flag@8 { + reg = <0x8 0x1>; + }; + + rsoc@10 { + reg = <0xa 0x2>; + }; + }; + }; + }; + }; From patchwork Wed Mar 12 09:26:02 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Andr=C3=A9_Draszik?= X-Patchwork-Id: 872932 Received: from mail-ed1-f51.google.com (mail-ed1-f51.google.com [209.85.208.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A8A8623BCE5 for ; Wed, 12 Mar 2025 09:26:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741771581; cv=none; b=MiTVTWwdfZ0b0ZVpl0V1BxJG62gC4/fGoj+wVoqQWtHoQ+xOTVv44ZScfxhDm9mWf9O+PRd1isurm0K+Tb7RSfCd8FuKQASRu3Mn6KuHs+DQsJcOVTDq2uNCC9djyM6HFtRmtS4oD7EEJ48BnzSQf37pPHtoJ9+ICw1c+bXllcA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741771581; c=relaxed/simple; bh=lQQG9VlvxEtRrFX1x0bp3zEv7m3yhN5FsIKIoojMzSw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=tl8R7Pdb/xc9DiRVMiwP7/b16QlVXqEtwuZ9ntjfeL19D0QQo85jjuZv2WYMFgNrrSICq3E4e1vuMWoYjOCQzLF7wwk2qELZMPiENiTTTxTp7otvmWd/6HyDeMDIq9YbUUaRFt9L9ZNqaHLUmWi4M5MeLZn7tb4SYQyetVYSxWY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=xvLlSVnW; arc=none smtp.client-ip=209.85.208.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="xvLlSVnW" Received: by mail-ed1-f51.google.com with SMTP id 4fb4d7f45d1cf-5e5dce099f4so8558007a12.1 for ; Wed, 12 Mar 2025 02:26:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1741771576; x=1742376376; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=u/6dZiVEa3h4qtkqVWHR5DX0piolZ7w+lyZ92IB24hw=; b=xvLlSVnWZMsur3pvZ6h6e7MDK/qIolkMpzN+5E8y8UaCeqlFkgz8lwA1Kw5sQaQJSx p5VGWMSjkl5/hWekVbvNRZSkHVYlMn27Kws6Nj5Sr4Vxgn87PVvuEDVDDRCJuucDkq3P RkQt6bwbV2IxijzOLzgb8jaDCbXYzgAxlTudJbAgg1AFd1lvgCCzqWvt9kezLq0zX7fj E2hV/UrBVwIvm8WV/qDKXIDrgeX0nTTnrzzDixKQlkD7/7uoZwb9YHfabolBejstCNFW 4PhMZem74xXz16a5RO8rEaocgZCQH9X3HiYdBY/2pneXlRseEjokx3lLr0vLOLrf/vpB POFQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741771576; x=1742376376; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=u/6dZiVEa3h4qtkqVWHR5DX0piolZ7w+lyZ92IB24hw=; b=Rr3SyvEi7alvvp+DZidyF65nGhgho0roFW5498ACjJaPVCFb1E9AK5fXAgfyVCmCZF YG9RZoYzzutcJZ9GKVjaV8cEJ9bxxYwerIhLfeMt2EBkzJFsgmsTVeIn4GxuVwl7hSOY 2mkRnU2pYTppZnn6jHfW8hmnNz7lFHgNzkEcP71bGdu2FmsaIt9yOJMyunTi9xf/De/U 27bckumaVWjkswXNkWYH8GImWHcOZpcVXxVW4PUSwqtT/wAK+kY5E1qT+bjLfYIlH827 XNgMzkolnGpfK6XDo9YnT8g2Mo+dO4qgCJgIrKdjyk+4Na09Na9JxAp6xDEvCN08PKXW YSFQ== X-Forwarded-Encrypted: i=1; AJvYcCUPLg5njiuPCV+lWJGNRw4+pWSRy1E82TQ0LTa7pE54fspWiEDBhzpUHjLYX0gBv21/G98Xm7z5bNf4@vger.kernel.org X-Gm-Message-State: AOJu0YyAXnBguxGQ/ZHlyKBbKwCHGEWtHqgm5FfKMZXbcuSUPtfYHCg3 QHCrKD3qr4htPjSJp7Y4aVp0ePBimitNi6hgdljwEVbH6+tSGfwLMW3yDZ6Fgus= X-Gm-Gg: ASbGnctEQ3y//K3lgxX77RR/SzrmVRVBn5Ca2ZJaamNOUakom5XJRPaxcf7WXjOsYby ewo7wo1qsj59/h8H5ERYwuZisfIFG65yOSJGnPDsIW1SV8ZWI3UhLKNmquxiXKZk9GYIRBmANvz CmmZvWXNVKmj/KNcK41jF4aX2VoA8rwLuQuG60PiYe51ptmp+VUMgOqsscV+bWLnlNQujKWn9SU DN/rzlbFGHxVMzZFxiTAhXQYfbawbQS3otbLQnYnHolB2zTcIQ7rCaBfEjNaN29sXaadjaHL/SK iHblQPlO3hE6B+IJtSC2WWjcwXL+ubs28saJmBAE6aQdfUIULN+KdKmvEKf7ibT8novOhwi1fi7 qbXqGoaH16URSjTYbCGij9LbVGD3a X-Google-Smtp-Source: AGHT+IHpl9XTBQAlgW5kljpOWbP6NzX0+6PLCYms4dWX3yzQaF9W1Bnq5FbgMxnYOn8aqwxj/ocEzg== X-Received: by 2002:a17:907:c928:b0:ac1:fa91:2b98 with SMTP id a640c23a62f3a-ac25264cd39mr1576957566b.14.1741771575629; Wed, 12 Mar 2025 02:26:15 -0700 (PDT) Received: from puffmais.c.googlers.com (8.239.204.35.bc.googleusercontent.com. [35.204.239.8]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ac25777c748sm894535266b.2.2025.03.12.02.26.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 12 Mar 2025 02:26:15 -0700 (PDT) From: =?utf-8?q?Andr=C3=A9_Draszik?= Date: Wed, 12 Mar 2025 09:26:02 +0000 Subject: [PATCH v4 4/6] mfd: max77759: add Maxim MAX77759 core mfd driver Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250312-max77759-mfd-v4-4-b908d606c8cb@linaro.org> References: <20250312-max77759-mfd-v4-0-b908d606c8cb@linaro.org> In-Reply-To: <20250312-max77759-mfd-v4-0-b908d606c8cb@linaro.org> To: Lee Jones , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Linus Walleij , Bartosz Golaszewski , Srinivas Kandagatla , Kees Cook , "Gustavo A. R. Silva" Cc: Peter Griffin , Tudor Ambarus , Will McVicker , kernel-team@android.com, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-gpio@vger.kernel.org, linux-hardening@vger.kernel.org, =?utf-8?q?And?= =?utf-8?q?r=C3=A9_Draszik?= X-Mailer: b4 0.14.2 The Maxim MAX77759 is a companion PMIC for USB Type-C applications and includes Battery Charger, Fuel Gauge, temperature sensors, USB Type-C Port Controller (TCPC), NVMEM, and a GPIO expander. Fuel Gauge and TCPC have separate and independent I2C addresses, register maps, and interrupt lines and are therefore excluded from the MFD core device driver here. The GPIO and NVMEM interfaces are accessed via specific commands to the built-in microprocessor. This driver implements an API that client drivers can use for accessing those. Signed-off-by: André Draszik --- v4: * add missing build_bug.h include * update an irq chip comment * fix a whitespace in register definitions v2: * add kernel doc for max77759_maxq_command() and related structs * fix an msec / usec typo * add missing error handling of devm_mutex_init() (Christophe) * align sentinel in max77759_of_id[] with max77759_i2c_id[] (Christophe) * some tidy-ups in max77759_maxq_command() (Christophe) --- MAINTAINERS | 2 + drivers/mfd/Kconfig | 20 ++ drivers/mfd/Makefile | 1 + drivers/mfd/max77759.c | 738 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/max77759.h | 98 ++++++ 5 files changed, 859 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index a45d1bd64d116d14bc05a64fa3da852a41e1de7f..38844ea24e464d0b58f8852b79e2b94f18d48998 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14352,6 +14352,8 @@ M: André Draszik L: linux-kernel@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/*/maxim,max77759*.yaml +F: drivers/mfd/max77759.c +F: include/linux/mfd/max77759.h MAXIM MAX77802 PMIC REGULATOR DEVICE DRIVER M: Javier Martinez Canillas diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index d44c69bb3dfd52d53fe26aa2d5e5ace346448f57..1d72bf086401064608cc41e9f9a0af044c6df68e 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -930,6 +930,26 @@ config MFD_MAX77714 drivers must be enabled in order to use each functionality of the device. +config MFD_MAX77759 + tristate "Maxim Integrated MAX77759 PMIC" + depends on I2C + depends on OF + select IRQ_DOMAIN + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + help + Say yes here to add support for Maxim Integrated MAX77759. + This is a companion Power Management IC for USB Type-C applications + with Battery Charger, Fuel Gauge, temperature sensors, USB Type-C + Port Controller (TCPC), NVMEM, and additional GPIO interfaces. + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the functionality + of the device. + + To compile this driver as a module, choose M here: the module will be + called max77759. + config MFD_MAX77843 bool "Maxim Semiconductor MAX77843 PMIC Support" depends on I2C=y diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 9220eaf7cf1255a8922430fe3e50e41771bbaa60..cc9362afd8f060d64ca0f0f028a0d7cfe9cfe512 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -169,6 +169,7 @@ obj-$(CONFIG_MFD_MAX77650) += max77650.o obj-$(CONFIG_MFD_MAX77686) += max77686.o obj-$(CONFIG_MFD_MAX77693) += max77693.o obj-$(CONFIG_MFD_MAX77714) += max77714.o +obj-$(CONFIG_MFD_MAX77759) += max77759.o obj-$(CONFIG_MFD_MAX77843) += max77843.o obj-$(CONFIG_MFD_MAX8907) += max8907.o max8925-objs := max8925-core.o max8925-i2c.o diff --git a/drivers/mfd/max77759.c b/drivers/mfd/max77759.c new file mode 100644 index 0000000000000000000000000000000000000000..a4f1616d600bde250a73b69464ada1fbf6d7e67e --- /dev/null +++ b/drivers/mfd/max77759.c @@ -0,0 +1,738 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright 2020 Google Inc +// Copyright 2025 Linaro Ltd. +// +// Core MFD driver for Maxim MAX77759 companion PMIC for USB Type-C + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* registers - registers useful to drivers are declared in the public header */ +/* PMIC / TOP */ +#define MAX77759_PMIC_REG_PMIC_ID 0x00 +#define MAX77759_PMIC_REG_PMIC_ID_MAX77759 59 + +#define MAX77759_PMIC_REG_PMIC_REVISION 0x01 +#define MAX77759_PMIC_REG_OTP_REVISION 0x02 + +#define MAX77759_PMIC_REG_INTSRC 0x22 +#define MAX77759_PMIC_REG_INTSRCMASK 0x23 +#define MAX77759_PMIC_REG_INTSRC_MAXQ BIT(3) +#define MAX77759_PMIC_REG_INTSRC_TOPSYS BIT(1) +#define MAX77759_PMIC_REG_INTSRC_CHGR BIT(0) + +#define MAX77759_PMIC_REG_TOPSYS_INT 0x24 +#define MAX77759_PMIC_REG_TOPSYS_INT_MASK 0x26 +#define MAX77759_PMIC_REG_TOPSYS_INT_TSHDN BIT(6) +#define MAX77759_PMIC_REG_TOPSYS_INT_SYSOVLO BIT(5) +#define MAX77759_PMIC_REG_TOPSYS_INT_SYSUVLO BIT(4) +#define MAX77759_PMIC_REG_TOPSYS_INT_FSHIP BIT(0) + +#define MAX77759_PMIC_REG_I2C_CNFG 0x40 +#define MAX77759_PMIC_REG_SWRESET 0x50 +#define MAX77759_PMIC_REG_CONTROL_FG 0x51 +#define MAX77759_PMIC_REG_LAST_REGISTER MAX77759_PMIC_REG_CONTROL_FG + +/* MaxQ */ +#define MAX77759_MAXQ_REG_AP_DATAOUT0 0x81 +#define MAX77759_MAXQ_REG_AP_DATAOUT32 0xa1 +#define MAX77759_MAXQ_REG_AP_MESSAGESZ_MAX (MAX77759_MAXQ_REG_AP_DATAOUT32 \ + - MAX77759_MAXQ_REG_AP_DATAOUT0 \ + + 1) +#define MAX77759_MAXQ_REG_AP_DATAIN0 0xb1 +#define MAX77759_MAXQ_REG_LAST_REGISTER 0xe0 + +/* charger */ +#define MAX77759_CHGR_REG_LAST_REGISTER 0xcc + +enum max77759_i2c_subdev_id { + MAX77759_I2C_SUBDEV_ID_MAXQ, + MAX77759_I2C_SUBDEV_ID_CHARGER, +}; + +struct max77759_mfd { + /* protecting MaxQ commands - only one can be active */ + struct mutex maxq_lock; + struct regmap *regmap_maxq; + struct completion cmd_done; + + struct regmap *regmap_top; + struct regmap *regmap_charger; +}; + +struct max77759_i2c_subdev { + enum max77759_i2c_subdev_id id; + const struct regmap_config *cfg; + u16 i2c_address; +}; + +/* TOP registers */ +static const struct regmap_range max77759_top_registers[] = { + regmap_reg_range(0x00, 0x02), + regmap_reg_range(0x22, 0x24), + regmap_reg_range(0x26, 0x26), + regmap_reg_range(0x40, 0x40), + regmap_reg_range(0x50, 0x51), +}; + +static const struct regmap_range max77759_top_ro_registers[] = { + regmap_reg_range(0x00, 0x02), + regmap_reg_range(0x22, 0x22), +}; + +static const struct regmap_range max77759_top_volatile_registers[] = { + regmap_reg_range(0x22, 0x22), + regmap_reg_range(0x24, 0x24), +}; + +static const struct regmap_access_table max77759_top_wr_table = { + .yes_ranges = max77759_top_registers, + .n_yes_ranges = ARRAY_SIZE(max77759_top_registers), + .no_ranges = max77759_top_ro_registers, + .n_no_ranges = ARRAY_SIZE(max77759_top_ro_registers), +}; + +static const struct regmap_access_table max77759_top_rd_table = { + .yes_ranges = max77759_top_registers, + .n_yes_ranges = ARRAY_SIZE(max77759_top_registers), +}; + +static const struct regmap_access_table max77759_top_volatile_table = { + .yes_ranges = max77759_top_volatile_registers, + .n_yes_ranges = ARRAY_SIZE(max77759_top_volatile_registers), +}; + +static const struct regmap_config max77759_regmap_config_top = { + .name = "top", + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX77759_PMIC_REG_LAST_REGISTER, + .wr_table = &max77759_top_wr_table, + .rd_table = &max77759_top_rd_table, + .volatile_table = &max77759_top_volatile_table, + .num_reg_defaults_raw = MAX77759_PMIC_REG_LAST_REGISTER + 1, + .cache_type = REGCACHE_MAPLE, +}; + +/* MaxQ registers */ +static const struct regmap_range max77759_maxq_registers[] = { + regmap_reg_range(0x60, 0x73), + regmap_reg_range(0x81, 0xa1), + regmap_reg_range(0xb1, 0xd1), + regmap_reg_range(0xe0, 0xe0), +}; + +static const struct regmap_range max77759_maxq_ro_registers[] = { + regmap_reg_range(0x60, 0x63), + regmap_reg_range(0x68, 0x6f), + regmap_reg_range(0xb1, 0xd1), +}; + +static const struct regmap_range max77759_maxq_volatile_registers[] = { + regmap_reg_range(0x64, 0x6f), + regmap_reg_range(0xb1, 0xd1), + regmap_reg_range(0xe0, 0xe0), +}; + +static const struct regmap_access_table max77759_maxq_wr_table = { + .yes_ranges = max77759_maxq_registers, + .n_yes_ranges = ARRAY_SIZE(max77759_maxq_registers), + .no_ranges = max77759_maxq_ro_registers, + .n_no_ranges = ARRAY_SIZE(max77759_maxq_ro_registers), +}; + +static const struct regmap_access_table max77759_maxq_rd_table = { + .yes_ranges = max77759_maxq_registers, + .n_yes_ranges = ARRAY_SIZE(max77759_maxq_registers), +}; + +static const struct regmap_access_table max77759_maxq_volatile_table = { + .yes_ranges = max77759_maxq_volatile_registers, + .n_yes_ranges = ARRAY_SIZE(max77759_maxq_volatile_registers), +}; + +static const struct regmap_config max77759_regmap_config_maxq = { + .name = "maxq", + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX77759_MAXQ_REG_LAST_REGISTER, + .wr_table = &max77759_maxq_wr_table, + .rd_table = &max77759_maxq_rd_table, + .volatile_table = &max77759_maxq_volatile_table, + .num_reg_defaults_raw = MAX77759_MAXQ_REG_LAST_REGISTER + 1, + .cache_type = REGCACHE_MAPLE, +}; + +/* charger registers */ +static const struct regmap_range max77759_charger_registers[] = { + regmap_reg_range(0xb0, 0xcc), +}; + +static const struct regmap_range max77759_charger_ro_registers[] = { + regmap_reg_range(0xb4, 0xb8), +}; + +static const struct regmap_range max77759_charger_volatile_registers[] = { + regmap_reg_range(0xb0, 0xb1), + regmap_reg_range(0xb4, 0xb8), +}; + +static const struct regmap_access_table max77759_charger_wr_table = { + .yes_ranges = max77759_charger_registers, + .n_yes_ranges = ARRAY_SIZE(max77759_charger_registers), + .no_ranges = max77759_charger_ro_registers, + .n_no_ranges = ARRAY_SIZE(max77759_charger_ro_registers), +}; + +static const struct regmap_access_table max77759_charger_rd_table = { + .yes_ranges = max77759_charger_registers, + .n_yes_ranges = ARRAY_SIZE(max77759_charger_registers), +}; + +static const struct regmap_access_table max77759_charger_volatile_table = { + .yes_ranges = max77759_charger_volatile_registers, + .n_yes_ranges = ARRAY_SIZE(max77759_charger_volatile_registers), +}; + +static const struct regmap_config max77759_regmap_config_charger = { + .name = "charger", + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX77759_CHGR_REG_LAST_REGISTER, + .wr_table = &max77759_charger_wr_table, + .rd_table = &max77759_charger_rd_table, + .volatile_table = &max77759_charger_volatile_table, + .num_reg_defaults_raw = MAX77759_CHGR_REG_LAST_REGISTER + 1, + .cache_type = REGCACHE_MAPLE, +}; + +/* + * Interrupts - with the following interrupt hierarchy: + * pmic IRQs (INTSRC) + * - MAXQ_INT: MaxQ IRQs + * - UIC_INT1 + * - APCmdResI + * - SysMsgI + * - GPIOxI + * - TOPSYS_INT: topsys + * - TOPSYS_INT + * - TSHDN_INT + * - SYSOVLO_INT + * - SYSUVLO_INT + * - FSHIP_NOT_RD + * - CHGR_INT: charger + * - CHG_INT + * - CHG_INT2 + */ +enum { + MAX77759_INT_MAXQ, + MAX77759_INT_TOPSYS, + MAX77759_INT_CHGR, +}; + +enum { + MAX77759_TOPSYS_INT_TSHDN, + MAX77759_TOPSYS_INT_SYSOVLO, + MAX77759_TOPSYS_INT_SYSUVLO, + MAX77759_TOPSYS_INT_FSHIP_NOT_RD, +}; + +enum { + MAX77759_MAXQ_INT_APCMDRESI, + MAX77759_MAXQ_INT_SYSMSGI, + MAX77759_MAXQ_INT_GPIO, + MAX77759_MAXQ_INT_UIC1, + MAX77759_MAXQ_INT_UIC2, + MAX77759_MAXQ_INT_UIC3, + MAX77759_MAXQ_INT_UIC4, +}; + +enum { + MAX77759_CHARGER_INT_1, + MAX77759_CHARGER_INT_2, +}; + +static const struct regmap_irq max77759_pmic_irqs[] = { + REGMAP_IRQ_REG(MAX77759_INT_MAXQ, 0, MAX77759_PMIC_REG_INTSRC_MAXQ), + REGMAP_IRQ_REG(MAX77759_INT_TOPSYS, 0, MAX77759_PMIC_REG_INTSRC_TOPSYS), + REGMAP_IRQ_REG(MAX77759_INT_CHGR, 0, MAX77759_PMIC_REG_INTSRC_CHGR), +}; + +static const struct regmap_irq max77759_maxq_irqs[] = { + REGMAP_IRQ_REG(MAX77759_MAXQ_INT_APCMDRESI, + 0, MAX77759_MAXQ_REG_UIC_INT1_APCMDRESI), + REGMAP_IRQ_REG(MAX77759_MAXQ_INT_SYSMSGI, + 0, MAX77759_MAXQ_REG_UIC_INT1_SYSMSGI), + REGMAP_IRQ_REG(MAX77759_MAXQ_INT_GPIO, 0, GENMASK(1, 0)), + REGMAP_IRQ_REG(MAX77759_MAXQ_INT_UIC1, 0, GENMASK(5, 2)), + REGMAP_IRQ_REG(MAX77759_MAXQ_INT_UIC2, 1, GENMASK(7, 0)), + REGMAP_IRQ_REG(MAX77759_MAXQ_INT_UIC3, 2, GENMASK(7, 0)), + REGMAP_IRQ_REG(MAX77759_MAXQ_INT_UIC4, 3, GENMASK(7, 0)), +}; + +static const struct regmap_irq max77759_topsys_irqs[] = { + REGMAP_IRQ_REG(MAX77759_TOPSYS_INT_TSHDN, + 0, MAX77759_PMIC_REG_TOPSYS_INT_TSHDN), + REGMAP_IRQ_REG(MAX77759_TOPSYS_INT_SYSOVLO, + 0, MAX77759_PMIC_REG_TOPSYS_INT_SYSOVLO), + REGMAP_IRQ_REG(MAX77759_TOPSYS_INT_SYSUVLO, + 0, MAX77759_PMIC_REG_TOPSYS_INT_SYSUVLO), + REGMAP_IRQ_REG(MAX77759_TOPSYS_INT_FSHIP_NOT_RD, + 0, MAX77759_PMIC_REG_TOPSYS_INT_FSHIP), +}; + +static const struct regmap_irq max77759_chgr_irqs[] = { + REGMAP_IRQ_REG(MAX77759_CHARGER_INT_1, 0, GENMASK(7, 0)), + REGMAP_IRQ_REG(MAX77759_CHARGER_INT_2, 1, GENMASK(7, 0)), +}; + +static const struct regmap_irq_chip max77759_pmic_irq_chip = { + .name = "max77759-pmic", + /* INTSRC is read-only and doesn't require clearing */ + .status_base = MAX77759_PMIC_REG_INTSRC, + .mask_base = MAX77759_PMIC_REG_INTSRCMASK, + .num_regs = 1, + .irqs = max77759_pmic_irqs, + .num_irqs = ARRAY_SIZE(max77759_pmic_irqs), +}; + +/* + * We can let regmap-irq auto-ack the topsys interrupt bits as required, but + * for all others the individual drivers need to know which interrupt bit + * exactly is set inside their interrupt handlers, and therefore we can not set + * .ack_base for those. + */ +static const struct regmap_irq_chip max77759_maxq_irq_chip = { + .name = "max77759-maxq", + .domain_suffix = "MAXQ", + .status_base = MAX77759_MAXQ_REG_UIC_INT1, + .mask_base = MAX77759_MAXQ_REG_UIC_INT1_M, + .num_regs = 4, + .irqs = max77759_maxq_irqs, + .num_irqs = ARRAY_SIZE(max77759_maxq_irqs), +}; + +static const struct regmap_irq_chip max77759_topsys_irq_chip = { + .name = "max77759-topsys", + .domain_suffix = "TOPSYS", + .status_base = MAX77759_PMIC_REG_TOPSYS_INT, + .mask_base = MAX77759_PMIC_REG_TOPSYS_INT_MASK, + .ack_base = MAX77759_PMIC_REG_TOPSYS_INT, + .num_regs = 1, + .irqs = max77759_topsys_irqs, + .num_irqs = ARRAY_SIZE(max77759_topsys_irqs), +}; + +static const struct regmap_irq_chip max77759_chrg_irq_chip = { + .name = "max77759-chgr", + .domain_suffix = "CHGR", + .status_base = MAX77759_CHGR_REG_CHG_INT, + .mask_base = MAX77759_CHGR_REG_CHG_INT_MASK, + .num_regs = 2, + .irqs = max77759_chgr_irqs, + .num_irqs = ARRAY_SIZE(max77759_chgr_irqs), +}; + +static const struct max77759_i2c_subdev max77759_i2c_subdevs[] = { + { + .id = MAX77759_I2C_SUBDEV_ID_MAXQ, + /* I2C address is same as top's */ + .cfg = &max77759_regmap_config_maxq, + }, + { + .id = MAX77759_I2C_SUBDEV_ID_CHARGER, + .cfg = &max77759_regmap_config_charger, + .i2c_address = 0x69, + }, +}; + +static const struct resource max77759_gpio_resources[] = { + DEFINE_RES_IRQ_NAMED(MAX77759_MAXQ_INT_GPIO, "GPI"), +}; + +static const struct resource max77759_charger_resources[] = { + DEFINE_RES_IRQ_NAMED(MAX77759_CHARGER_INT_1, "INT1"), + DEFINE_RES_IRQ_NAMED(MAX77759_CHARGER_INT_2, "INT2"), +}; + +static const struct mfd_cell max77759_cells[] = { + MFD_CELL_OF("max77759-nvmem", NULL, NULL, 0, 0, + "maxim,max77759-nvmem"), +}; + +static const struct mfd_cell max77759_maxq_cells[] = { + MFD_CELL_OF("max77759-gpio", max77759_gpio_resources, NULL, 0, 0, + "maxim,max77759-gpio"), +}; + +static const struct mfd_cell max77759_charger_cells[] = { + MFD_CELL_RES("max77759-charger", max77759_charger_resources), +}; + +int max77759_maxq_command(struct max77759_mfd *max77759_mfd, + const struct max77759_maxq_command *cmd, + struct max77759_maxq_response *rsp) +{ + DEFINE_FLEX(struct max77759_maxq_response, _rsp, rsp, length, 1); + int ret; + struct device *dev = regmap_get_device(max77759_mfd->regmap_maxq); + static const unsigned int timeout_ms = 200; + + if (cmd->length > MAX77759_MAXQ_REG_AP_MESSAGESZ_MAX) + return -EINVAL; + + /* rsp is allowed to be NULL. In that case we do need a temporary. */ + if (!rsp) + rsp = _rsp; + + BUILD_BUG_ON(MAX77759_MAXQ_OPCODE_MAXLENGTH + != MAX77759_MAXQ_REG_AP_MESSAGESZ_MAX); + if (!rsp->length || rsp->length > MAX77759_MAXQ_REG_AP_MESSAGESZ_MAX) + return -EINVAL; + + guard(mutex)(&max77759_mfd->maxq_lock); + + reinit_completion(&max77759_mfd->cmd_done); + + /* write the opcode and data */ + ret = regmap_bulk_write(max77759_mfd->regmap_maxq, + MAX77759_MAXQ_REG_AP_DATAOUT0, cmd->cmd, + cmd->length); + if (!ret && cmd->length < MAX77759_MAXQ_REG_AP_MESSAGESZ_MAX) + /* writing the last byte triggers MaxQ */ + ret = regmap_write(max77759_mfd->regmap_maxq, + MAX77759_MAXQ_REG_AP_DATAOUT32, 0); + if (ret) { + dev_warn(dev, "write data failed: %d\n", ret); + return ret; + } + + /* wait for response from MaxQ */ + if (!wait_for_completion_timeout(&max77759_mfd->cmd_done, + msecs_to_jiffies(timeout_ms))) { + dev_err(dev, "timed out waiting for data\n"); + return -ETIMEDOUT; + } + + ret = regmap_bulk_read(max77759_mfd->regmap_maxq, + MAX77759_MAXQ_REG_AP_DATAIN0, + rsp->rsp, rsp->length); + if (ret) { + dev_warn(dev, "read data failed: %d\n", ret); + return ret; + } + + /* + * As per the protocol, the first byte of the reply will match the + * request. + */ + if (cmd->cmd[0] != rsp->rsp[0]) { + dev_warn(dev, "unexpected opcode response for %#.2x: %*ph\n", + cmd->cmd[0], (int)rsp->length, rsp->rsp); + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(max77759_maxq_command); + +static irqreturn_t apcmdres_irq_handler(int irq, void *irq_data) +{ + struct max77759_mfd *max77759_mfd = irq_data; + + regmap_write(max77759_mfd->regmap_maxq, MAX77759_MAXQ_REG_UIC_INT1, + MAX77759_MAXQ_REG_UIC_INT1_APCMDRESI); + + complete(&max77759_mfd->cmd_done); + + return IRQ_HANDLED; +} + +static int max77759_create_i2c_subdev(struct i2c_client *client, + struct max77759_mfd *max77759_mfd, + const struct max77759_i2c_subdev *sd) +{ + struct i2c_client *sub; + struct regmap *regmap; + int ret; + + if (sd->i2c_address) { + sub = devm_i2c_new_dummy_device(&client->dev, + client->adapter, + sd->i2c_address); + + if (IS_ERR(sub)) + return dev_err_probe(&client->dev, PTR_ERR(sub), + "failed to claim i2c device %s\n", + sd->cfg->name); + } else { + sub = client; + } + + regmap = devm_regmap_init_i2c(sub, sd->cfg); + if (IS_ERR(regmap)) + return dev_err_probe(&sub->dev, PTR_ERR(regmap), + "regmap init failed\n"); + + ret = regmap_attach_dev(&client->dev, regmap, sd->cfg); + if (ret) + return dev_err_probe(&client->dev, ret, + "regmap attach failed\n"); + + if (sd->id == MAX77759_I2C_SUBDEV_ID_MAXQ) + max77759_mfd->regmap_maxq = regmap; + else if (sd->id == MAX77759_I2C_SUBDEV_ID_CHARGER) + max77759_mfd->regmap_charger = regmap; + + return 0; +} + +static int max77759_add_chained_irq_chip(struct device *dev, + struct regmap *regmap, + int pirq, + struct regmap_irq_chip_data *parent, + const struct regmap_irq_chip *chip, + struct regmap_irq_chip_data **data) +{ + int irq, ret; + + irq = regmap_irq_get_virq(parent, pirq); + if (irq < 0) + return dev_err_probe(dev, irq, + "Failed to get parent vIRQ(%d) for chip %s\n", + pirq, chip->name); + + ret = devm_regmap_add_irq_chip(dev, regmap, irq, + IRQF_ONESHOT | IRQF_SHARED, 0, chip, + data); + if (ret) + return dev_err_probe(dev, ret, "Failed to add %s IRQ chip\n", + chip->name); + + return 0; +} + +static int max77759_add_chained_maxq(struct i2c_client *client, + struct max77759_mfd *max77759_mfd, + struct regmap_irq_chip_data *parent) +{ + struct regmap_irq_chip_data *irq_chip_data; + int ret; + int apcmdres_irq; + + ret = max77759_add_chained_irq_chip(&client->dev, + max77759_mfd->regmap_maxq, + MAX77759_INT_MAXQ, + parent, + &max77759_maxq_irq_chip, + &irq_chip_data); + if (ret) + return ret; + + /* + * We need to register our own IRQ handler before any MFD cells, to + * ensure client drivers can use our MaxQ interface APIs without + * any race conditions. + */ + init_completion(&max77759_mfd->cmd_done); + apcmdres_irq = regmap_irq_get_virq(irq_chip_data, + MAX77759_MAXQ_INT_APCMDRESI); + + ret = devm_request_threaded_irq(&client->dev, apcmdres_irq, + NULL, apcmdres_irq_handler, + IRQF_ONESHOT | IRQF_SHARED, + dev_name(&client->dev), max77759_mfd); + if (ret) + return dev_err_probe(&client->dev, ret, + "MAX77759_MAXQ_INT_APCMDRESI failed\n"); + + ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO, + max77759_maxq_cells, + ARRAY_SIZE(max77759_maxq_cells), + NULL, 0, + regmap_irq_get_domain(irq_chip_data)); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to add MFD devices (MaxQ)\n"); + + return 0; +} + +static int max77759_add_chained_topsys(struct i2c_client *client, + struct max77759_mfd *max77759_mfd, + struct regmap_irq_chip_data *parent) +{ + struct regmap_irq_chip_data *irq_chip_data; + int ret; + + ret = max77759_add_chained_irq_chip(&client->dev, + max77759_mfd->regmap_top, + MAX77759_INT_TOPSYS, + parent, + &max77759_topsys_irq_chip, + &irq_chip_data); + if (ret) + return ret; + + return 0; +} + +static int max77759_add_chained_charger(struct i2c_client *client, + struct max77759_mfd *max77759_mfd, + struct regmap_irq_chip_data *parent) +{ + struct regmap_irq_chip_data *irq_chip_data; + int ret; + + ret = max77759_add_chained_irq_chip(&client->dev, + max77759_mfd->regmap_charger, + MAX77759_INT_CHGR, + parent, + &max77759_chrg_irq_chip, + &irq_chip_data); + if (ret) + return ret; + + ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO, + max77759_charger_cells, + ARRAY_SIZE(max77759_charger_cells), + NULL, 0, + regmap_irq_get_domain(irq_chip_data)); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to add MFD devices (charger)\n"); + + return 0; +} + +static int max77759_probe(struct i2c_client *client) +{ + struct regmap *regmap_top; + unsigned int pmic_id; + int ret; + struct irq_data *irq_data; + struct max77759_mfd *max77759_mfd; + unsigned long irq_flags; + struct regmap_irq_chip_data *irq_chip_data_pmic; + + regmap_top = devm_regmap_init_i2c(client, &max77759_regmap_config_top); + if (IS_ERR(regmap_top)) + return dev_err_probe(&client->dev, PTR_ERR(regmap_top), + "regmap init failed\n"); + + ret = regmap_read(regmap_top, MAX77759_PMIC_REG_PMIC_ID, &pmic_id); + if (ret) + return dev_err_probe(&client->dev, ret, + "Unable to read Device ID\n"); + + if (pmic_id != MAX77759_PMIC_REG_PMIC_ID_MAX77759) + return dev_err_probe(&client->dev, -ENODEV, + "Unsupported Device ID %#.2x (%d)\n", + pmic_id, pmic_id); + + irq_data = irq_get_irq_data(client->irq); + if (!irq_data) + return dev_err_probe(&client->dev, -EINVAL, + "Invalid IRQ: %d\n", client->irq); + + max77759_mfd = devm_kzalloc(&client->dev, sizeof(*max77759_mfd), + GFP_KERNEL); + if (!max77759_mfd) + return -ENOMEM; + + max77759_mfd->regmap_top = regmap_top; + ret = devm_mutex_init(&client->dev, &max77759_mfd->maxq_lock); + if (ret) + return ret; + + i2c_set_clientdata(client, max77759_mfd); + + for (int i = 0; i < ARRAY_SIZE(max77759_i2c_subdevs); ++i) { + ret = max77759_create_i2c_subdev(client, + max77759_mfd, + &max77759_i2c_subdevs[i]); + if (ret) + return ret; + } + + irq_flags = IRQF_ONESHOT | IRQF_SHARED; + irq_flags |= irqd_get_trigger_type(irq_data); + + ret = devm_regmap_add_irq_chip(&client->dev, max77759_mfd->regmap_top, + client->irq, irq_flags, 0, + &max77759_pmic_irq_chip, + &irq_chip_data_pmic); + if (ret) + return dev_err_probe(&client->dev, ret, + "Failed to add IRQ chip\n"); + + /* INTSRC - MaxQ & children */ + ret = max77759_add_chained_maxq(client, max77759_mfd, + irq_chip_data_pmic); + if (ret) + return ret; + + /* INTSRC - topsys & children */ + ret = max77759_add_chained_topsys(client, max77759_mfd, + irq_chip_data_pmic); + if (ret) + return ret; + + /* INTSRC - charger & children */ + ret = max77759_add_chained_charger(client, max77759_mfd, + irq_chip_data_pmic); + if (ret) + return ret; + + return devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO, + max77759_cells, ARRAY_SIZE(max77759_cells), + NULL, 0, + regmap_irq_get_domain(irq_chip_data_pmic)); +} + +static const struct i2c_device_id max77759_i2c_id[] = { + { "max77759", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max77759_i2c_id); + +static const struct of_device_id max77759_of_id[] = { + { .compatible = "maxim,max77759", }, + { } +}; +MODULE_DEVICE_TABLE(of, max77759_of_id); + +static struct i2c_driver max77759_i2c_driver = { + .driver = { + .name = "max77759", + .of_match_table = max77759_of_id, + }, + .probe = max77759_probe, + .id_table = max77759_i2c_id, +}; +module_i2c_driver(max77759_i2c_driver); + +MODULE_AUTHOR("André Draszik "); +MODULE_DESCRIPTION("Maxim MAX77759 multi-function core driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/max77759.h b/include/linux/mfd/max77759.h new file mode 100644 index 0000000000000000000000000000000000000000..b038b4e9b748287e23e3a7030496f09dc8bdc816 --- /dev/null +++ b/include/linux/mfd/max77759.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2020 Google Inc. + * Copyright 2025 Linaro Ltd. + * + * Client interface for Maxim MAX77759 MFD driver + */ + +#ifndef __LINUX_MFD_MAX77759_H +#define __LINUX_MFD_MAX77759_H + +/* MaxQ opcodes */ +#define MAX77759_MAXQ_OPCODE_MAXLENGTH 33 + +#define MAX77759_MAXQ_OPCODE_GPIO_TRIGGER_READ 0x21 +#define MAX77759_MAXQ_OPCODE_GPIO_TRIGGER_WRITE 0x22 +#define MAX77759_MAXQ_OPCODE_GPIO_CONTROL_READ 0x23 +#define MAX77759_MAXQ_OPCODE_GPIO_CONTROL_WRITE 0x24 +#define MAX77759_MAXQ_OPCODE_USER_SPACE_READ 0x81 +#define MAX77759_MAXQ_OPCODE_USER_SPACE_WRITE 0x82 + +/* + * register map (incomplete) - registers not useful for drivers are not + * declared here + */ +/* MaxQ */ +#define MAX77759_MAXQ_REG_UIC_INT1 0x64 +#define MAX77759_MAXQ_REG_UIC_INT1_APCMDRESI BIT(7) +#define MAX77759_MAXQ_REG_UIC_INT1_SYSMSGI BIT(6) +#define MAX77759_MAXQ_REG_UIC_INT1_GPIO6I BIT(1) +#define MAX77759_MAXQ_REG_UIC_INT1_GPIO5I BIT(0) +#define MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(offs, en) (((en) & 1) << (offs)) +#define MAX77759_MAXQ_REG_UIC_INT1_GPIOxI_MASK(offs) \ + MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(offs, ~0) + +#define MAX77759_MAXQ_REG_UIC_INT2 0x65 +#define MAX77759_MAXQ_REG_UIC_INT3 0x66 +#define MAX77759_MAXQ_REG_UIC_INT4 0x67 +#define MAX77759_MAXQ_REG_UIC_UIC_STATUS1 0x68 +#define MAX77759_MAXQ_REG_UIC_UIC_STATUS2 0x69 +#define MAX77759_MAXQ_REG_UIC_UIC_STATUS3 0x6a +#define MAX77759_MAXQ_REG_UIC_UIC_STATUS4 0x6b +#define MAX77759_MAXQ_REG_UIC_UIC_STATUS5 0x6c +#define MAX77759_MAXQ_REG_UIC_UIC_STATUS6 0x6d +#define MAX77759_MAXQ_REG_UIC_UIC_STATUS7 0x6f +#define MAX77759_MAXQ_REG_UIC_UIC_STATUS8 0x6f +#define MAX77759_MAXQ_REG_UIC_INT1_M 0x70 +#define MAX77759_MAXQ_REG_UIC_INT2_M 0x71 +#define MAX77759_MAXQ_REG_UIC_INT3_M 0x72 +#define MAX77759_MAXQ_REG_UIC_INT4_M 0x73 + +/* charger */ +#define MAX77759_CHGR_REG_CHG_INT 0xb0 +#define MAX77759_CHGR_REG_CHG_INT2 0xb1 +#define MAX77759_CHGR_REG_CHG_INT_MASK 0xb2 +#define MAX77759_CHGR_REG_CHG_INT2_MASK 0xb3 + +struct max77759_mfd; + +/** + * struct max77759_maxq_command - structure containing the MaxQ command to + * send + * + * @length: The number of bytes to send. + * @cmd: The data to send. + */ +struct max77759_maxq_command { + u8 length; + u8 cmd[] __counted_by(length); +}; + +/** + * struct max77759_maxq_response - structure containing the MaxQ response + * + * @length: The number of bytes to receive. + * @rsp: The data received. Must have at least @length bytes space. + */ +struct max77759_maxq_response { + u8 length; + u8 rsp[] __counted_by(length); +}; + +/** + * max77759_maxq_command() - issue a MaxQ command and wait for the response + * and associated data + * + * @max77759_mfd: The core max77759 mfd device handle. + * @cmd: The command to be sent. + * @rsp: Any response data associated with the command will be copied here; + * can be %NULL if the command has no response (other than ACK). + * + * Return: 0 on success, a negative error number otherwise. + */ +int max77759_maxq_command(struct max77759_mfd *max77759_mfd, + const struct max77759_maxq_command *cmd, + struct max77759_maxq_response *rsp); + +#endif /* __LINUX_MFD_MAX77759_H */