From patchwork Fri Feb 28 14:25:15 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: 13996500 Received: from mail-ed1-f47.google.com (mail-ed1-f47.google.com [209.85.208.47]) (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 D978E26E62F for ; Fri, 28 Feb 2025 14:25:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740752724; cv=none; b=mTGCfDmvWZ6GfGVEHF/LqVWb6Au4WOgb013ZNLhrRfAqGRZopU4cQE+5xDfnpRnPL3AAUgkvnZ5iguD0TLO3I8f7VVZCpzH9VMrRgoFCfeHZ+Kh9dSBRW/gf4orN6zbNnhx0fy5NDSvPDetKhsr1/qubJZqRLFYsDKfwaJpzJyY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740752724; c=relaxed/simple; bh=q8yvSV3OfR1cMeYWUwudsYOgSeKbzLSKCy1iuJ6/+EQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=YPvUln7Yng4aRqsoRptGreZSFusv2BnUcjK2YaaJRc7KPxYTFi610POA5Y6IeiJylVp5CvkMjKG9D/dujO9Z4wrcJuks0io5fbjsD6JU5MlY3jeQzqZO88b4LSCFNiLrnUbif1RkUhmz/qQrwCTiFUwjRQBasbkj4K3CVYjmUlI= 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=sNruckEa; arc=none smtp.client-ip=209.85.208.47 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="sNruckEa" Received: by mail-ed1-f47.google.com with SMTP id 4fb4d7f45d1cf-5e4c0c12bccso3837458a12.1 for ; Fri, 28 Feb 2025 06:25:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1740752720; x=1741357520; 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=Pv4wwRg4MWsotmAwTgSc9LO+fNoS+BdIZnuXjMZYvGI=; b=sNruckEamZmIgRbxuKsDuxtQ2qhK0FsXxlNgxFoEGvnqgQAvpE4XZGWpyDeuCIJx3a zZ7qnE6eVY75VGT9RSahq/loP+KUwrOV/xq21lCuDXF1cCD0Orsr3TWLn/X/+k5MwN45 yvTX4YvhedbAbwK8/jMrnMUi6jLWw6c4V68efQcq63+pG37+yYWlSRHno5Izz9pleDW8 S+WaTeSCY0iC17+uP7jY2tP6pvqmAsdGNI0SsoFByMoGADPLWS5ntNfzVUf5ksPR3a3V VNQMprq8QBVlbxukdhR4hnnyIaVjOzv3h0pq+c0U7THK7rozvXPAa4MIQ14dy7LPBofX M+vA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740752720; x=1741357520; 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=Pv4wwRg4MWsotmAwTgSc9LO+fNoS+BdIZnuXjMZYvGI=; b=FkwR62D67c21DbLopF/55cbESBt4WYFPQTpoM5vvUMRscj8X0/6qWrAwE7UwIMV6LV /bxDPvdMxcUhT4OmMQISKZi+v2HtsAIMyJdjUqsC9FdegA9gGUtbg8E3zwbJ24NDI8zS ULtngHa7C9HfizVvHmAh15k2SlvFdj/g++D5asTUc3I/VHlDkUbqpC6WLGRQQoqAh8c1 GlQg96vDa9+IpLAwi8pjLdhyLi/pGf5Xz9Rf3gpr2NN2mg1sM543vtRGGwjA2Mb7HKgL pLBiih64yDcBqzDZgJnsGxRgRzG6CFvDZrzAe+5O47E1M/L5EeJ78jplRSGcX+aOCsz/ OC3A== X-Forwarded-Encrypted: i=1; AJvYcCWydaCha9A+Nn8ijF7uGF7lRuABnE1FVrsepWwjhJ6RzwfXHBUNZndxs+qBsCJbvXpue8qYhZO6c7wAc35YGQg=@vger.kernel.org X-Gm-Message-State: AOJu0Yw7+17pfbdYzr+VkI7zkYE1Dvf6LhnojsMFUP/dX6TvBV99m5VW 4kKhF4HhXhxk+JnjlfP8RTWybPM2Wc5dh1dgfRYBljC6rzOvg6Vt11uBeoBQPkUWAvrvTm6bXCE ZFuQ= X-Gm-Gg: ASbGncsJcsEqo7LbD4nVsAGXMZ7UkCckoobmIxAGNFlGRYAbHGao+qjQH4PCZxVej02 xdm//uooHDNumscFaS1xNS3bNi6PElwnOXkLGWdyzIEEP5lchDs5wAWxkqdaRz012NeMcLQjKDr wwMvh2bWX1sUx3Dqe1tVbSsKwpovr8oBfqD77jBQSfNdd+XI7PxAd4ShRGhhRxHpZnWBom93U/b jiGrpig4FW8bPg3Jpjfj9wfh//kodDxo05vbvQfMpNRe8ClkfQQdeHRndy3zZfywRJ1A04PspbW 84hwdcn+uOuFx427x9vsIAvhc8fTIAOtDj7kL4hL/+DkNXvrszoaAs6oRFbrZRgpv9xKmM7U+ts dnPKAL+n+dw== X-Google-Smtp-Source: AGHT+IE/1tdmhyjPnwbfzddWeK3bk95/GNSAF7R8hQFGpqHDHMj7vR+nD7yX+QHUckF9GBrqLY5hFw== X-Received: by 2002:a17:907:7f27:b0:abe:fa17:12e0 with SMTP id a640c23a62f3a-abf25f8dd1dmr349589766b.11.1740752720119; Fri, 28 Feb 2025 06:25:20 -0800 (PST) Received: from puffmais.c.googlers.com (30.171.91.34.bc.googleusercontent.com. [34.91.171.30]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-abf0c75bffcsm298754466b.143.2025.02.28.06.25.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2025 06:25:19 -0800 (PST) From: =?utf-8?q?Andr=C3=A9_Draszik?= Date: Fri, 28 Feb 2025 14:25:15 +0000 Subject: [PATCH v3 1/6] dt-bindings: gpio: add max77759 binding Precedence: bulk X-Mailing-List: linux-hardening@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250228-max77759-mfd-v3-1-0c3627d42526@linaro.org> References: <20250228-max77759-mfd-v3-0-0c3627d42526@linaro.org> In-Reply-To: <20250228-max77759-mfd-v3-0-0c3627d42526@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 Fri Feb 28 14:25:16 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: 13996501 Received: from mail-ej1-f48.google.com (mail-ej1-f48.google.com [209.85.218.48]) (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 6348326E636 for ; Fri, 28 Feb 2025 14:25:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740752724; cv=none; b=nHX/jpMwJCSzzoZLdQfqAD9kBgTEHY6bVdkEAYXxkGr0RDv+RKgpUBHXBpJJdRiyLWxxX4HN2wPG4tNSyTXE+MC6QgFIpBVAl7ACDsgmFM7VbrFTtsEDWsYxyvXwHjV9gG7hUd32pvPiodVuk9f1pJ8qRMKn89ou9jUyOvOEhEk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740752724; c=relaxed/simple; bh=OIzHAEtZLRdehCT8XTvoyMMkGvyxwP0N0aEn13IQuXs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=IPPRheeGyUj3RuVL842zwj+unkTqMscaRazcQUxJhEwEgtGnBwc+qRvwkMuU9u459JnN95LJrbWRGqMVkrLbvB+JQHhbSrNyzVce/w+QWoUUinXryFFit4upSffCMnXBtp0RJEmjNDKXA6B819ARK/SPtOM3Tm7r3wC387IG3uI= 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=YD/Gv684; arc=none smtp.client-ip=209.85.218.48 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="YD/Gv684" Received: by mail-ej1-f48.google.com with SMTP id a640c23a62f3a-abb81285d33so415417666b.0 for ; Fri, 28 Feb 2025 06:25:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1740752721; x=1741357521; 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=j95NBh5qzJcx7cZbH3fh5wk/7zjXRZ1HkMfWcLmgpJU=; b=YD/Gv684NgBtRmqnEuxv3ZkTs8lpd7IQ/kRJw2IH69AGEhsuwCjyGIrbbnc6xVsqil vpcHL45K9SrE2RSI+6cogyigSU04wK14oiP6QTBa186Hz539aRYfvpPOBaejT3ZHmwbs Q0w2ILqh91FiCpORBKDYPTasE6hQiXFVBbwDufJMNfYY6kKkaB94yFNvuLGGxLlLInlW HA35N+Nexa1pVKWuFQcWotaQuSFow2XY1jRvl97oMFR96jbrBOMaZXCxAFsfajpoSEWx gP8tmxAKCc7e49OA+u9wUWWQiICOsTsoRuElQbDbqe8B7kdYDx/z2SDYNg3FN/5npdlZ Q1gw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740752721; x=1741357521; 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=j95NBh5qzJcx7cZbH3fh5wk/7zjXRZ1HkMfWcLmgpJU=; b=P7xrMJ8wTl70g5a+xZuueZBb+w5fRrX8L6seyZyVdPnYcm6VKxJ9FktUjhXhw2wM2C GAL/6pXT+UKMpmlk3Oo5GLSZCpqZtcNeaftXFcyx8h6yNK+5d07VYp0FenDZCj+cz2XD pr0LRptxQaaNnbG4nm4YI6ZWlm9zM2S3l53gYs4OD9mX0s9Lyehro4r/jTq2zgaM7v38 B86UzFiqVmJJWzeSImqdW3jC0TlGo9ENE5L+TRNDAB/Qsa6XtNzV4hS4Ye8faMm9qS4m wjs5gvR98miEmzIVDQlcFLJA6+xgvOojBjaFrkPnIhEguOoT1/oyCFnEnggSsGJbVbOK XTnA== X-Forwarded-Encrypted: i=1; AJvYcCVYp5jEljPezoZXJ/LJOKf3D5TJmrGlNrWviuG95R2lP3LQDbK0OJk00+0T4PdiSvkJ84GQKH7rowbi6W/5boI=@vger.kernel.org X-Gm-Message-State: AOJu0YyRY0WY4vU0WXaeawrfcDzHw5LgGqEVeqlhOPXZKerSHM7jPAUM e5W/v9xMRlByNUAVvNWGgBRe0wstxV4G8VnTpb62RGt6euRNBAXOaKY1p3ftZnL7SFac8XpNmbj 9L2s= X-Gm-Gg: ASbGnctwJ9cUhqRC5yhrlMuMk9OOjbJtxRNFssF5LyZSTohHR7C4rUD2lWTJD0I70lI 2ZO9c5ZUYuidtVOFlLvdsMi5hrOc9L/d8jJ8Trz0drePRyu3O8T2DnRET1oJmiKXHuZf/Tpy2zx lE/LejPueLVNb50xSCeBulNdqzXLrDzqKwY4Xckc+6i7PbzBC9TvGT39qOB37ldMce7R1EjlUmr in7/BHRDjkiKSCLbQ6A4XRfRV9/warnQt467wtYskZN1vdmI0vHc34oV1UgLwtYhHzzcbCJzvjr EDDvxeK5M8dX28QOLjUZd06205/MNEcd13DVZagCRXhSUSOtv8d9ceT77bPK4lTNrVrjzt8Jdpv UwDtzoP2LYA== X-Google-Smtp-Source: AGHT+IH7P3KcRrpCODNCujFnOOLmi8XeVbBnZbd6Z7IVNjqTTc15B6SHZjXQ6PZV2vMMvoftmGaSyw== X-Received: by 2002:a17:907:7fa3:b0:ab7:d361:11b4 with SMTP id a640c23a62f3a-abf25f9053bmr352779866b.7.1740752720580; Fri, 28 Feb 2025 06:25:20 -0800 (PST) Received: from puffmais.c.googlers.com (30.171.91.34.bc.googleusercontent.com. [34.91.171.30]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-abf0c75bffcsm298754466b.143.2025.02.28.06.25.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2025 06:25:20 -0800 (PST) From: =?utf-8?q?Andr=C3=A9_Draszik?= Date: Fri, 28 Feb 2025 14:25:16 +0000 Subject: [PATCH v3 2/6] dt-bindings: nvmem: add max77759 binding Precedence: bulk X-Mailing-List: linux-hardening@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250228-max77759-mfd-v3-2-0c3627d42526@linaro.org> References: <20250228-max77759-mfd-v3-0-0c3627d42526@linaro.org> In-Reply-To: <20250228-max77759-mfd-v3-0-0c3627d42526@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 storage module (NVMEM). Reviewed-by: Rob Herring (Arm) Signed-off-by: André Draszik --- v2: * drop example as the MFD binding has a complete one (Rob) Note: MAINTAINERS doesn't need updating, the binding update for the first leaf device (gpio) adds a wildcard matching all max77759 bindings --- .../bindings/nvmem/maxim,max77759-nvmem.yaml | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Documentation/devicetree/bindings/nvmem/maxim,max77759-nvmem.yaml b/Documentation/devicetree/bindings/nvmem/maxim,max77759-nvmem.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1e3bd4433007341a11040f513bf444866b9e38a8 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/maxim,max77759-nvmem.yaml @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/maxim,max77759-nvmem.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim Integrated MAX77759 Non Volatile Memory + +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, Non Volatile Memory + (NVMEM) with 30 bytes of storage which can be used by software to store + information or communicate with a boot loader. + +properties: + compatible: + const: maxim,max77759-nvmem + + wp-gpios: false + +required: + - compatible + +allOf: + - $ref: nvmem.yaml# + +unevaluatedProperties: false From patchwork Fri Feb 28 14:25:17 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: 13996502 Received: from mail-ed1-f43.google.com (mail-ed1-f43.google.com [209.85.208.43]) (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 04A2E26E952 for ; Fri, 28 Feb 2025 14:25:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740752725; cv=none; b=Xb+zHNTXa+5TEEiWzmknTniuGvaCpHnlJZsgpS+ISG31mJHYva2prSh0kUpH6Ifw4omEZ520YXj+CdkvzZaiRa3NTp/YwTzGjgXetJgA/1DwD6+AQzOXRUUt3mnKVog/4CvPQZRvSE0oyA2ZsLx94+l3VUXxTK3O1EvbXr0nPsY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740752725; c=relaxed/simple; bh=A1c7oPj+mHgomHNOeZfQ5imtH0eO01+ucNLtjE3IxZY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=cETAH1NyOJNNXfazn0dnaiN0pbheoen9xiF/AvesF52XUL49YSu6JUjavgeg5l1qxKdaowyJITrTvRc8H0Hnf8N4vf0+nISTdp5vq5d/GA/Tr/ugKyQK26qjIOqR31SR0qIVrJS+R4qKAoy1L3ArDn0xrGpSNKSzLSoDg3oUKzU= 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=CiMC9zrE; arc=none smtp.client-ip=209.85.208.43 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="CiMC9zrE" Received: by mail-ed1-f43.google.com with SMTP id 4fb4d7f45d1cf-5e0573a84fcso3118743a12.2 for ; Fri, 28 Feb 2025 06:25:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1740752721; x=1741357521; 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=vbGkP6lgrmkK8Sb6Eo6E5YJrFog34o66alqe4OQ4cJM=; b=CiMC9zrEe91nKl7iq6Eehit1qnI6rCRsg+Do1L3Jr0ywfn3LdAiOuZlnLzXXT3UBJa prFUhykB0AyAnGQ5lnAz0rsKYs93N/m/y8PpoNw3tD1CTOeq881ncWWTSkSqUdMF27Sr dlzsEtqRk0HfSsABMDPIqrL0mAT5hWywQIliEqZcv1SnHDQLXqBtiECEUQct0A96+cco vwNKUahIwZBqqWByWCu51olH8IBOT5hKaZXl17K3m1rEvw5N7L1wlDvNLy9qp5aB6kcs 4cyUbPtQ7Ws4DaLnNi0HNKRUlohoJFWHRIIEkubCtBhv2cGqB5tuiM70xShzLwfcLSFy al6A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740752721; x=1741357521; 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=vbGkP6lgrmkK8Sb6Eo6E5YJrFog34o66alqe4OQ4cJM=; b=DYAv4GT9C1wrIWBED3fopTwdbBLWWrKOft1oQqiAZhnz/l1Q9UELxyGg71fvhnET2S dtwbI0fJdLo3EGcHrt5sra9Xk16MAXPqRDIi8slxQCEwqyA0VL6LYzVCKOWIesqwECWD 13npwrK+nPp+rTnC++fx1p+aPjsNp/Mx9PImioarUlS9wl3ciqOcBMMoy+5gjrkyb5pH N31gnzKVk3fN/a04n+hN0eAOsbz8v+5uot1xLxJviBcEVYT7jAZXf/D3t16yf0eeNduL ijAhQO9sE445IR6OPVhukXdnKVBp0QexKChizJncwi1KYmrZWMRHDRB4Ue8w7gaxqlm5 fJTQ== X-Forwarded-Encrypted: i=1; AJvYcCXFdokNTthV2AqTWYuTJlgiKTbG+QP0JxsiyyBORmSW01qlMW7cky+5+LYZeT50QEmPPAjeylYKsZmivplbLXA=@vger.kernel.org X-Gm-Message-State: AOJu0YzmNwZcznkL2CuJR+dkaOyi5cVAA+wNNSHyGgxd1TVpwnXb+LU6 en7fFZvUcYe1ND9fdR6AYfLb8sUUN2/7HrSjnKUN+6x17hvqSc2/ZsfhFKkgsNy9iu/0yS8hXke 10W4= X-Gm-Gg: ASbGncvnyWvnanrOKrtePOgb2Ne06chRQkeotUstYKDPm5T+ydNVNpNhrifFaKX8O2L mIKpd5DmeafWBCZ6hqgDBKrhQl1mVDA/MBmnIFHwC9KuvWq/KRezfXVX5diu7NP1yZIQjULa1vt uSwzDMBEhHnDqhGbwl0wYaLsuR+nfjZiuOTbSBvRKvCrCS/CIDZgTMavgPQwWdY7I/2aPKljy44 R1LMGkH9LQNsrkjM7mFWQXi7NSy1x9lszi70F8gGq2GFMEd9FI/9Up3eATiUGBKeVyTyqi63dBq gNwl8lDZGbZov1yIQQYK0Xvft7b5YfuTv6SjxvnLIw6QpS7+9kWLMeNCGSoZgwOqPTp++YQCURn h+uIq7xKW1A== X-Google-Smtp-Source: AGHT+IGqhQFvrzNMYQjiKO9Vc60qd948jBI9yWaEAq0UnRjcu0b6mkDidj6LMsQD1AYOi6xvC5fWXQ== X-Received: by 2002:a17:907:2d28:b0:abf:1379:d67a with SMTP id a640c23a62f3a-abf261f9661mr404014266b.8.1740752721106; Fri, 28 Feb 2025 06:25:21 -0800 (PST) Received: from puffmais.c.googlers.com (30.171.91.34.bc.googleusercontent.com. [34.91.171.30]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-abf0c75bffcsm298754466b.143.2025.02.28.06.25.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2025 06:25:20 -0800 (PST) From: =?utf-8?q?Andr=C3=A9_Draszik?= Date: Fri, 28 Feb 2025 14:25:17 +0000 Subject: [PATCH v3 3/6] dt-bindings: mfd: add max77759 binding Precedence: bulk X-Mailing-List: linux-hardening@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250228-max77759-mfd-v3-3-0c3627d42526@linaro.org> References: <20250228-max77759-mfd-v3-0-0c3627d42526@linaro.org> In-Reply-To: <20250228-max77759-mfd-v3-0-0c3627d42526@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. Signed-off-by: André Draszik Reviewed-by: Rob Herring (Arm) --- 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 Fri Feb 28 14:25:18 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: 13996504 Received: from mail-ej1-f54.google.com (mail-ej1-f54.google.com [209.85.218.54]) (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 A059A26F478 for ; Fri, 28 Feb 2025 14:25:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740752727; cv=none; b=fZrkB8ApLL8dZaSjqqKSbo+oqwAhgJUzf2wm6WVs4LFnNK90EV1BRmU4FHN+QW6PSmAsH30bq9fAGpjcYNNLRVg1D0p6dfJq7TNoadKcM0TeKzmkT17Ik4epvCFlK6/k/1OAMME9QBk37P7bvTV81FaGTd5BtiXbKi+rd87yc+s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740752727; c=relaxed/simple; bh=qPcIgjgd2QFMCKqvHGFtBOFIIQkX+e2PpOKNB7mN6lU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=HWu9OQCGcuDLocWjFhG9VDUbSz4x5GS7wiuHJEXeOMqmT5YCYpmmIkP9hUOo6/HXEshEzFyIDBtAbYJmUKmYX6NOam8vG7xUCp9UcrGrJXefVR6e+p7fqoumQG3xB7RmgO5Eq5R252rB/hJh/xtndni1XbcPjNdiXhK1zFEi2gQ= 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=aW1CQBFW; arc=none smtp.client-ip=209.85.218.54 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="aW1CQBFW" Received: by mail-ej1-f54.google.com with SMTP id a640c23a62f3a-aaec61d0f65so449506466b.1 for ; Fri, 28 Feb 2025 06:25:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1740752722; x=1741357522; 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=1/ph3EOq2gKWidt/zHINU8TKMgRMQdchGkU69xBG2V8=; b=aW1CQBFWu2tsdXhFlaRVtisVaN3Mg3PsRvnNEGzCyXtYLrULy1ti7cxkXcc877S5K0 UmRoZgKC+WQYBkheDVOdeAuDkqsdak8zR69IbKBVvuGobQMKd/rgEt5GqLQZI50WwmCZ o1j2n4bRk96zqc6QAVTda9o6a0tF+Ed1yNIPMhlOwbU15kLj8vuXNqStqnE4ZNhztyMM hlO9q7kEE/aFvTOk2INOXQ+c7pK1jAreOz2buPeWqY1wNLwK5kqGI3OGsvwbDpbfnO/4 q71Nxt9CAqwJ+fYZo91BwSjwRlQI/zsLsgtHmeSl7eOWZJJMYlNcslX0hmjGrOa9IuQf BUfg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740752722; x=1741357522; 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=1/ph3EOq2gKWidt/zHINU8TKMgRMQdchGkU69xBG2V8=; b=mh/ebEzt4vyX3HPWc8qn6pam4y1aPbpzOeVZNAAOHOmX4osAU+aQ/kkgNKau9G5oCW 1dAIrBqD0v9Z6vOJe0wNBOVMmYAlypkFaF1x/brB3DbTD1oaiUkGbWjYzpzyq1c61Eaf zseAWFTs+dsWu13fRMawMSp18IVfLqSRVpTqOcwM/cQGsc5WQN+F7HmANa5HA6cn9y4F TzuG8VwHdWTUvk7tVNjF+pvy1+0zE3+j9FGQb+RUorqxyZHjdPwVmjj0mDIByhO9RL5S JBUlA73eH+S/Ela7Y1IhlJmxJY2haDRCOv1vqmhGyWxT5O7YRZmanmRqnlofHF4iIhM6 QfnA== X-Forwarded-Encrypted: i=1; AJvYcCVxDVCHtNURfu+Pfpa57hoNfu7+dkkyU8LBlc3YKvYEYafHb/i787oyNdLIQu72sWgbr9pLZ5pzlmHVwPcF7hg=@vger.kernel.org X-Gm-Message-State: AOJu0Yzj14/uMfpsnH0nzMKn7Td6ksu7QIP6sVr3I2LCT3bV+NHV1WrH LrDcfWM6mEFPbP+S8LA4s61/vHJ6VUfoNenxwKbJvHPu8UorcQq6asUvgaPYPtQjMX7Qo9+NPbp l4ws= X-Gm-Gg: ASbGncvkM5oTWt6aZ4PXd83gWGZ69ON+aQwSCRj56U9d7orjtdtx11NR5I0WcsdLuh0 sZ3hdNaLg8EeQts/V+BeIPAioNGgJOg5qqzjqheIwONccHZlCaLOTZ6DXbDuOIwa14UlxZHZQvG k7gNZVIMvUxKW0C3e0vhEx3wSMzYUhUM+ROGXulJo1C0cXp/X4oVxe5yF/6kBNCr3iI1NPoFTWS ydZHddOcI2gjvO/lLsRf5q/04WtPnnVfiNNgKJ+yJskvWTlIJxZph1tYQKMvY+xqujdR5D2W1DW Le3jmDGkjXQ2GoAzozFPaw1GmLK9OqA8O+MQ1u2sL9XM/GKLn/HL+4TfXq/a2b2kyJzf2Pa/KZ1 1jeIV91e+CQ== X-Google-Smtp-Source: AGHT+IHjfbNauRcOROf8yie7bvb/SxesMvV29hj7x1gsOxypucrENE2P8fqk0xPrHmBSrjFAkTS2zw== X-Received: by 2002:a17:906:6a16:b0:ab7:caa9:330a with SMTP id a640c23a62f3a-abf25fc4936mr393193266b.23.1740752721698; Fri, 28 Feb 2025 06:25:21 -0800 (PST) Received: from puffmais.c.googlers.com (30.171.91.34.bc.googleusercontent.com. [34.91.171.30]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-abf0c75bffcsm298754466b.143.2025.02.28.06.25.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2025 06:25:21 -0800 (PST) From: =?utf-8?q?Andr=C3=A9_Draszik?= Date: Fri, 28 Feb 2025 14:25:18 +0000 Subject: [PATCH v3 4/6] mfd: max77759: add Maxim MAX77759 core mfd driver Precedence: bulk X-Mailing-List: linux-hardening@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250228-max77759-mfd-v3-4-0c3627d42526@linaro.org> References: <20250228-max77759-mfd-v3-0-0c3627d42526@linaro.org> In-Reply-To: <20250228-max77759-mfd-v3-0-0c3627d42526@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 --- 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 | 737 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/max77759.h | 98 ++++++ 5 files changed, 858 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..7391591129e3ccc7427839e2297b5fc33c1810b6 --- /dev/null +++ b/drivers/mfd/max77759.c @@ -0,0 +1,737 @@ +// 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 + +/* 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", + .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. + * MAX77759_MAXQ_REG_UIC_INT1 is read-only and doesn't require clearing. + */ +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 */ From patchwork Fri Feb 28 14:25:19 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: 13996505 Received: from mail-ed1-f49.google.com (mail-ed1-f49.google.com [209.85.208.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 09D7A26FD9A for ; Fri, 28 Feb 2025 14:25:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740752727; cv=none; b=DN0CzDEOSH1c6b1mWI9WS37m6hqNi2U1XebfDv1VB5V2YvD8Pb9c+NcmQ5KdvdKGJQFKwyoeQoSR9/OqwIgf9mYYsjQr1DDfKYKVYt/ZrO8WDoKtziL4NnnFvGs35qIKs+SOYsy/IKnobLGAn+e1AWkGgany+novutyLhAFWF24= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740752727; c=relaxed/simple; bh=HMxFBEzOnomeRVJT/8ZThga+OBgqpCCPNaBisIQC1mk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Q8CPAQjDWhBja6xLy2ExQE/6mNaMBUmbVtbY2enp+3jhMJurr9t23X6S5UHq0yIYyU/8CohRniDY/oPMILuJVcMomjNKiKXGU84+HxIQqBYSn2jXt+9BJcTjYsHixdt1v/dMIQ3KrMRzofoxLubUNg+m2Hs+lFstR6o2zHRn+MY= 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=Vb581QyL; arc=none smtp.client-ip=209.85.208.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="Vb581QyL" Received: by mail-ed1-f49.google.com with SMTP id 4fb4d7f45d1cf-5e5050d19e9so1107289a12.3 for ; Fri, 28 Feb 2025 06:25:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1740752722; x=1741357522; 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=7rSWKaYf+IMBJS/G0Ht1cqLQyJJFkBa4DfLdZByVqYI=; b=Vb581QyLgZFi/pFtyD/iUD/a6MrXoqtzhv7j72rBuBRZV+mbwKzs1pqJtj/SO8KTgf 1ifa0/DnUlxNf4Ibpe+tu92JkUSwxpdkwN/zxFITzVFHtqd/fVll1YWBcHFEpD7IRqVK oQvUdGGzRAMPQSMFebIxPjDKh8wt2JyVR0MO5iCxlrWWw8YssypoG8bb/7Q4t+MT7B55 KF8O6xcmXnsaUph4W00Iy2BvXQciBzg1Xu1y8egO2rzTlilepatAYXVAeyQr1weinVux 2VRnJZg4Obn0tJ2jRL/ptWkZGS8ZNmnCiPt55DPVTRa+I4RjuA+eI7TUFwyvu+VtxuXj pWHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740752722; x=1741357522; 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=7rSWKaYf+IMBJS/G0Ht1cqLQyJJFkBa4DfLdZByVqYI=; b=UsjAyKHg03rWJ+Ej94dQxea5l2XrK6V3SGjSCZBKR/vYVYmoTaHCYqsCnjqecvm5QD dkr1gZKGkKv0XzRAG+9/NWgEEhuE4geLpINY+4EYuFUJwroQp07bdykvJNPaXcdGpAi4 3w7q2KGdLO/2YRqv9oyHlUgedKd9HFwYTPxIklLrjCFoKfZ9irCKzkaXnKV/t+G27tQg K5MOQrBXHLR+efcS3j3wVClwg1pKmNYCPxPr48capjeh+mnsxahm6q3ARufR+XW3OCm7 hFmCqRfEdKGQ10M61VNtTdaD7Immn7Aw524BdF4DkWLJOgQobsxjyuoUzDRdpjFUYPl4 jJ1A== X-Forwarded-Encrypted: i=1; AJvYcCUkq0xwIdujqrqGAoKXfpgKxkftQfnRdURs8zqwxQIvbATyXBnAderA/VGCOHEr2I0cALhkikuyhWvWQo+ngBI=@vger.kernel.org X-Gm-Message-State: AOJu0Yz4zBqJVqoUz6XBgAkfH8OUk3jueQpjT1PgEq3u+eZqF5n6rmOB GFapXlwKpGlPofRV1fKsa10DS7KUA+wiG9lDYfVR+cxFuoCaSZ8jUmXUpQG8ZUJAiZ+YEwvNywv RKME= X-Gm-Gg: ASbGncuK4egVN++YjOqeCyGXm5khNcM7x2fLfHCUcg7z50wwMCAQmgLCj9tC8Kyb6BQ WcFRgwp7ZpwxF7fZRrfE1+xnuFJVZXdi+z3EDUHBldwHTgskV+CvH6Hnzrh+Jg7D8bPVwS+F6ke Y9eJAbXHCVrTRyL8GjP285w1ivmURUQURELMfxEaPOWY50N5HA6RJUoR5FOxi+RTYFtHlx9e654 1gImKa/CS7zrcX0t71MdpAOZgBzO2fsJ1i/+KmZRbkDhtT0WHOpWZjK+qbYOJ8/AyAbC29KXsDf OcZGo/nKlwgvLXrlsvephrnSD4yqOK7j3hg59yAAJq7vItkcSLjWxHEkMibUlxR996c8d7i3iLd GqD1dO6kfxA== X-Google-Smtp-Source: AGHT+IHaaf9zQCGa4o6zkdz2Y6qnZSpNbT3cS0Rc+fn571sjOKjt1jMmoUThrELrIZfTXX6XDuOtgw== X-Received: by 2002:a17:906:6a1d:b0:ab7:e41d:34b6 with SMTP id a640c23a62f3a-abf261cd5a6mr338559066b.28.1740752722215; Fri, 28 Feb 2025 06:25:22 -0800 (PST) Received: from puffmais.c.googlers.com (30.171.91.34.bc.googleusercontent.com. [34.91.171.30]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-abf0c75bffcsm298754466b.143.2025.02.28.06.25.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2025 06:25:21 -0800 (PST) From: =?utf-8?q?Andr=C3=A9_Draszik?= Date: Fri, 28 Feb 2025 14:25:19 +0000 Subject: [PATCH v3 5/6] gpio: max77759: add Maxim MAX77759 gpio driver Precedence: bulk X-Mailing-List: linux-hardening@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250228-max77759-mfd-v3-5-0c3627d42526@linaro.org> References: <20250228-max77759-mfd-v3-0-0c3627d42526@linaro.org> In-Reply-To: <20250228-max77759-mfd-v3-0-0c3627d42526@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 driver supports the GPIO functions using the platform device registered by the core MFD driver. Signed-off-by: André Draszik --- v3: * drop duplicate init of 'handled' variable in irq handler * use boolean with IRQ_RETVAL() (Linus) * drop 'virq' variable inside irq handler to avoid confusion (Linus) * drop assignment of struct gpio_chip::owner (Linus) v2: * fix max77759_gpio_direction_from_control() * add missing error handling of devm_mutex_init() (Christophe) * align sentinel in max77759_gpio_of_id[] with other max77759 drivers (Christophe) --- MAINTAINERS | 1 + drivers/gpio/Kconfig | 13 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-max77759.c | 524 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 539 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 38844ea24e464d0b58f8852b79e2b94f18d48998..ef3aadcf86ce35d8807733c94f790cde0f7255af 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14352,6 +14352,7 @@ M: André Draszik L: linux-kernel@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/*/maxim,max77759*.yaml +F: drivers/gpio/gpio-max77759.c F: drivers/mfd/max77759.c F: include/linux/mfd/max77759.h diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3e9b174fee84142a8b50cdca5b967671eeda6dd3..216e80580fa443160c20da3e0916979fd08df99a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1463,6 +1463,19 @@ config GPIO_MAX77650 GPIO driver for MAX77650/77651 PMIC from Maxim Semiconductor. These chips have a single pin that can be configured as GPIO. +config GPIO_MAX77759 + tristate "Maxim Integrated MAX77759 GPIO support" + depends on MFD_MAX77759 + default MFD_MAX77759 + select GPIOLIB_IRQCHIP + help + GPIO driver for MAX77759 PMIC from Maxim Integrated. + There are two GPIOs available on these chips in total, both of + which can also generate interrupts. + + This driver can also be built as a module. If so, the module will be + called gpio-max77759. + config GPIO_PALMAS bool "TI PALMAS series PMICs GPIO" depends on MFD_PALMAS diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index af130882ffeeef8b1d518867bfe1493ec4f21b5f..3b9a55f9f79eda33c135cfedb230ef7775b51bee 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -105,6 +105,7 @@ obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o obj-$(CONFIG_GPIO_MAX77620) += gpio-max77620.o obj-$(CONFIG_GPIO_MAX77650) += gpio-max77650.o +obj-$(CONFIG_GPIO_MAX77759) += gpio-max77759.o obj-$(CONFIG_GPIO_MB86S7X) += gpio-mb86s7x.o obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o obj-$(CONFIG_GPIO_MENZ127) += gpio-menz127.o diff --git a/drivers/gpio/gpio-max77759.c b/drivers/gpio/gpio-max77759.c new file mode 100644 index 0000000000000000000000000000000000000000..b0f5d839a347e472cbe0b8624b534ecf8f21f64c --- /dev/null +++ b/drivers/gpio/gpio-max77759.c @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright 2020 Google Inc +// Copyright 2025 Linaro Ltd. +// +// GPIO driver for Maxim MAX77759 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX77759_N_GPIOS ARRAY_SIZE(max77759_gpio_line_names) +static const char * const max77759_gpio_line_names[] = { "GPIO5", "GPIO6" }; + +struct max77759_gpio_chip { + struct regmap *map; + struct max77759_mfd *max77759_mfd; + struct gpio_chip gc; + struct mutex maxq_lock; /* protect MaxQ r/m/w operations */ + + struct mutex irq_lock; /* protect irq bus */ + int irq_mask; + int irq_mask_changed; + int irq_trig; + int irq_trig_changed; +}; + +#define MAX77759_GPIOx_TRIGGER(offs, val) (((val) & 1) << (offs)) +#define MAX77759_GPIOx_TRIGGER_MASK(offs) MAX77759_GPIOx_TRIGGER(offs, ~0) +enum max77759_trigger_gpio_type { + MAX77759_GPIO_TRIGGER_RISING = 0, + MAX77759_GPIO_TRIGGER_FALLING = 1 +}; + +#define MAX77759_GPIOx_DIR(offs, dir) (((dir) & 1) << (2 + (3 * (offs)))) +#define MAX77759_GPIOx_DIR_MASK(offs) MAX77759_GPIOx_DIR(offs, ~0) +enum max77759_control_gpio_dir { + MAX77759_GPIO_DIR_IN = 0, + MAX77759_GPIO_DIR_OUT = 1 +}; + +#define MAX77759_GPIOx_OUTVAL(offs, val) (((val) & 1) << (3 + (3 * (offs)))) +#define MAX77759_GPIOx_OUTVAL_MASK(offs) MAX77759_GPIOx_OUTVAL(offs, ~0) + +#define MAX77759_GPIOx_INVAL_MASK(offs) (BIT(4) << (3 * (offs))) + +static int max77759_gpio_maxq_gpio_trigger_read(struct max77759_gpio_chip *chip) +{ + DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, 1); + DEFINE_FLEX(struct max77759_maxq_response, rsp, rsp, length, 2); + int ret; + + cmd->cmd[0] = MAX77759_MAXQ_OPCODE_GPIO_TRIGGER_READ; + + ret = max77759_maxq_command(chip->max77759_mfd, cmd, rsp); + if (ret < 0) + return ret; + + return rsp->rsp[1]; +} + +static int max77759_gpio_maxq_gpio_trigger_write(struct max77759_gpio_chip *chip, + u8 trigger) +{ + DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, 2); + + cmd->cmd[0] = MAX77759_MAXQ_OPCODE_GPIO_TRIGGER_WRITE; + cmd->cmd[1] = trigger; + + return max77759_maxq_command(chip->max77759_mfd, cmd, NULL); +} + +static int max77759_gpio_maxq_gpio_control_read(struct max77759_gpio_chip *chip) +{ + DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, 1); + DEFINE_FLEX(struct max77759_maxq_response, rsp, rsp, length, 2); + int ret; + + cmd->cmd[0] = MAX77759_MAXQ_OPCODE_GPIO_CONTROL_READ; + + ret = max77759_maxq_command(chip->max77759_mfd, cmd, rsp); + if (ret < 0) + return ret; + + return rsp->rsp[1]; +} + +static int max77759_gpio_maxq_gpio_control_write(struct max77759_gpio_chip *chip, + u8 ctrl) +{ + DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, 2); + + cmd->cmd[0] = MAX77759_MAXQ_OPCODE_GPIO_CONTROL_WRITE; + cmd->cmd[1] = ctrl; + + return max77759_maxq_command(chip->max77759_mfd, cmd, NULL); +} + +static int +max77759_gpio_direction_from_control(int ctrl, unsigned int offset) +{ + enum max77759_control_gpio_dir dir; + + dir = !!(ctrl & MAX77759_GPIOx_DIR_MASK(offset)); + return ((dir == MAX77759_GPIO_DIR_OUT) + ? GPIO_LINE_DIRECTION_OUT + : GPIO_LINE_DIRECTION_IN); +} + +static int max77759_gpio_get_direction(struct gpio_chip *gc, + unsigned int offset) +{ + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + int ctrl; + + ctrl = max77759_gpio_maxq_gpio_control_read(chip); + if (ctrl < 0) + return ctrl; + + return max77759_gpio_direction_from_control(ctrl, offset); +} + +static int max77759_gpio_direction_helper(struct gpio_chip *gc, + unsigned int offset, + enum max77759_control_gpio_dir dir, + int value) +{ + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + int ctrl, new_ctrl; + + guard(mutex)(&chip->maxq_lock); + + ctrl = max77759_gpio_maxq_gpio_control_read(chip); + if (ctrl < 0) + return ctrl; + + new_ctrl = ctrl & ~MAX77759_GPIOx_DIR_MASK(offset); + new_ctrl |= MAX77759_GPIOx_DIR(offset, dir); + + if (dir == MAX77759_GPIO_DIR_OUT) { + new_ctrl &= ~MAX77759_GPIOx_OUTVAL_MASK(offset); + new_ctrl |= MAX77759_GPIOx_OUTVAL(offset, value); + } + + if (new_ctrl == ctrl) + return 0; + + return max77759_gpio_maxq_gpio_control_write(chip, new_ctrl); +} + +static int max77759_gpio_direction_input(struct gpio_chip *gc, + unsigned int offset) +{ + return max77759_gpio_direction_helper(gc, offset, + MAX77759_GPIO_DIR_IN, -1); +} + +static int max77759_gpio_direction_output(struct gpio_chip *gc, + unsigned int offset, int value) +{ + return max77759_gpio_direction_helper(gc, offset, + MAX77759_GPIO_DIR_OUT, value); +} + +static int max77759_gpio_get_value(struct gpio_chip *gc, unsigned int offset) +{ + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + int ctrl, mask; + + ctrl = max77759_gpio_maxq_gpio_control_read(chip); + if (ctrl < 0) + return ctrl; + + /* + * The input status bit doesn't reflect the pin state when the GPIO is + * configured as an output. Check the direction, and inspect the input + * or output bit accordingly. + */ + mask = ((max77759_gpio_direction_from_control(ctrl, offset) + == GPIO_LINE_DIRECTION_IN) + ? MAX77759_GPIOx_INVAL_MASK(offset) + : MAX77759_GPIOx_OUTVAL_MASK(offset)); + + return !!(ctrl & mask); +} + +static void max77759_gpio_set_value(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + int ctrl, new_ctrl; + + guard(mutex)(&chip->maxq_lock); + + ctrl = max77759_gpio_maxq_gpio_control_read(chip); + if (ctrl < 0) + return; + + new_ctrl = ctrl & ~MAX77759_GPIOx_OUTVAL_MASK(offset); + new_ctrl |= MAX77759_GPIOx_OUTVAL(offset, value); + + if (new_ctrl == ctrl) + return; + + max77759_gpio_maxq_gpio_control_write(chip, new_ctrl); +} + +static void max77759_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + chip->irq_mask &= ~MAX77759_MAXQ_REG_UIC_INT1_GPIOxI_MASK(hwirq); + chip->irq_mask |= MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 1); + chip->irq_mask_changed |= MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 1); + + gpiochip_disable_irq(gc, hwirq); +} + +static void max77759_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + gpiochip_enable_irq(gc, hwirq); + + chip->irq_mask &= ~MAX77759_MAXQ_REG_UIC_INT1_GPIOxI_MASK(hwirq); + chip->irq_mask |= MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 0); + chip->irq_mask_changed |= MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 1); +} + +static int max77759_gpio_set_irq_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + chip->irq_trig &= ~MAX77759_GPIOx_TRIGGER_MASK(hwirq); + switch (type) { + case IRQ_TYPE_EDGE_RISING: + chip->irq_trig |= MAX77759_GPIOx_TRIGGER(hwirq, + MAX77759_GPIO_TRIGGER_RISING); + break; + + case IRQ_TYPE_EDGE_FALLING: + chip->irq_trig |= MAX77759_GPIOx_TRIGGER(hwirq, + MAX77759_GPIO_TRIGGER_FALLING); + break; + + default: + return -EINVAL; + } + + chip->irq_trig_changed |= MAX77759_GPIOx_TRIGGER(hwirq, 1); + + return 0; +} + +static void max77759_gpio_bus_lock(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + + mutex_lock(&chip->irq_lock); +} + +static int max77759_gpio_bus_sync_unlock_helper(struct gpio_chip *gc, + struct max77759_gpio_chip *chip) + __must_hold(&chip->maxq_lock) +{ + int ctrl, trigger, new_trigger, new_ctrl; + unsigned long irq_trig_changed; + int offset; + int ret; + + lockdep_assert_held(&chip->maxq_lock); + + ctrl = max77759_gpio_maxq_gpio_control_read(chip); + trigger = max77759_gpio_maxq_gpio_trigger_read(chip); + if (ctrl < 0 || trigger < 0) { + dev_err(gc->parent, "failed to read current state: %d / %d\n", + ctrl, trigger); + return (ctrl < 0) ? ctrl : trigger; + } + + new_trigger = trigger & ~chip->irq_trig_changed; + new_trigger |= (chip->irq_trig & chip->irq_trig_changed); + + /* change GPIO direction if required */ + new_ctrl = ctrl; + irq_trig_changed = chip->irq_trig_changed; + for_each_set_bit(offset, &irq_trig_changed, MAX77759_N_GPIOS) { + new_ctrl &= ~MAX77759_GPIOx_DIR_MASK(offset); + new_ctrl |= MAX77759_GPIOx_DIR(offset, MAX77759_GPIO_DIR_IN); + } + + if (new_trigger != trigger) { + ret = max77759_gpio_maxq_gpio_trigger_write(chip, new_trigger); + if (ret) { + dev_err(gc->parent, + "failed to write new trigger: %d\n", ret); + return ret; + } + } + + if (new_ctrl != ctrl) { + ret = max77759_gpio_maxq_gpio_control_write(chip, new_ctrl); + if (ret) { + dev_err(gc->parent, + "failed to write new control: %d\n", ret); + return ret; + } + } + + chip->irq_trig_changed = 0; + + return 0; +} + +static void max77759_gpio_bus_sync_unlock(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + int ret; + + scoped_guard(mutex, &chip->maxq_lock) { + ret = max77759_gpio_bus_sync_unlock_helper(gc, chip); + if (ret) + goto out_unlock; + } + + ret = regmap_update_bits(chip->map, + MAX77759_MAXQ_REG_UIC_INT1_M, + chip->irq_mask_changed, chip->irq_mask); + if (ret) { + dev_err(gc->parent, + "failed to update UIC_INT1 irq mask: %d\n", ret); + goto out_unlock; + } + + chip->irq_mask_changed = 0; + +out_unlock: + mutex_unlock(&chip->irq_lock); +} + +static void max77759_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + + seq_puts(p, dev_name(gc->parent)); +} + +static const struct irq_chip max77759_gpio_irq_chip = { + .irq_mask = max77759_gpio_irq_mask, + .irq_unmask = max77759_gpio_irq_unmask, + .irq_set_type = max77759_gpio_set_irq_type, + .irq_bus_lock = max77759_gpio_bus_lock, + .irq_bus_sync_unlock = max77759_gpio_bus_sync_unlock, + .irq_print_chip = max77759_gpio_irq_print_chip, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static irqreturn_t max77759_gpio_irqhandler(int irq, void *data) +{ + struct max77759_gpio_chip *chip = data; + struct gpio_chip *gc = &chip->gc; + bool handled = false; + + /* iterate until no interrupt is pending */ + while (true) { + unsigned int uic_int1; + int ret; + unsigned long pending; + int offset; + + ret = regmap_read(chip->map, MAX77759_MAXQ_REG_UIC_INT1, + &uic_int1); + if (ret < 0) { + dev_err_ratelimited(gc->parent, + "failed to read IRQ status: %d\n", + ret); + /* + * If !handled, we have looped not even once, which + * means we should return IRQ_NONE in that case (and + * of course IRQ_HANDLED otherwise). + */ + return IRQ_RETVAL(handled); + } + + pending = uic_int1; + pending &= (MAX77759_MAXQ_REG_UIC_INT1_GPIO6I + | MAX77759_MAXQ_REG_UIC_INT1_GPIO5I); + if (!pending) + break; + + for_each_set_bit(offset, &pending, MAX77759_N_GPIOS) { + /* + * ACK interrupt by writing 1 to bit 'offset', all + * others need to be written as 0. This needs to be + * done unconditionally hence regmap_set_bits() is + * inappropriate here. + */ + regmap_write(chip->map, MAX77759_MAXQ_REG_UIC_INT1, + BIT(offset)); + + handle_nested_irq(irq_find_mapping(gc->irq.domain, + offset)); + + handled = true; + } + } + + return IRQ_RETVAL(handled); +} + +static int max77759_gpio_probe(struct platform_device *pdev) +{ + struct max77759_gpio_chip *chip; + int irq; + struct gpio_irq_chip *girq; + int ret; + unsigned long irq_flags; + struct irq_data *irqd; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->map = dev_get_regmap(pdev->dev.parent, "maxq"); + if (!chip->map) + return dev_err_probe(&pdev->dev, -ENODEV, "Missing regmap\n"); + + irq = platform_get_irq_byname(pdev, "GPI"); + if (irq < 0) + return dev_err_probe(&pdev->dev, irq, "Failed to get IRQ\n"); + + chip->max77759_mfd = dev_get_drvdata(pdev->dev.parent); + ret = devm_mutex_init(&pdev->dev, &chip->maxq_lock); + if (ret) + return ret; + ret = devm_mutex_init(&pdev->dev, &chip->irq_lock); + if (ret) + return ret; + + chip->gc.base = -1; + chip->gc.label = dev_name(&pdev->dev); + chip->gc.parent = &pdev->dev; + chip->gc.can_sleep = true; + + chip->gc.names = max77759_gpio_line_names; + chip->gc.ngpio = MAX77759_N_GPIOS; + chip->gc.get_direction = max77759_gpio_get_direction; + chip->gc.direction_input = max77759_gpio_direction_input; + chip->gc.direction_output = max77759_gpio_direction_output; + chip->gc.get = max77759_gpio_get_value; + chip->gc.set = max77759_gpio_set_value; + + girq = &chip->gc.irq; + gpio_irq_chip_set_chip(girq, &max77759_gpio_irq_chip); + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + girq->threaded = true; + + ret = devm_gpiochip_add_data(&pdev->dev, &chip->gc, chip); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to add GPIO chip\n"); + + irq_flags = IRQF_ONESHOT | IRQF_SHARED; + irqd = irq_get_irq_data(irq); + if (irqd) + irq_flags |= irqd_get_trigger_type(irqd); + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + max77759_gpio_irqhandler, irq_flags, + dev_name(&pdev->dev), chip); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to request IRQ\n"); + + return ret; +} + +static const struct of_device_id max77759_gpio_of_id[] = { + { .compatible = "maxim,max77759-gpio", }, + { } +}; +MODULE_DEVICE_TABLE(of, max77759_gpio_of_id); + +static struct platform_driver max77759_gpio_driver = { + .driver = { + .name = "max77759-gpio", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = max77759_gpio_of_id, + }, + .probe = max77759_gpio_probe, +}; + +module_platform_driver(max77759_gpio_driver); + +MODULE_AUTHOR("André Draszik "); +MODULE_DESCRIPTION("GPIO driver for Maxim MAX77759"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:max77759-gpio"); From patchwork Fri Feb 28 14:25:20 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: 13996503 Received: from mail-ej1-f42.google.com (mail-ej1-f42.google.com [209.85.218.42]) (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 B718826FDA7 for ; Fri, 28 Feb 2025 14:25:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740752727; cv=none; b=i+dVjEHR4KZasxWZU+AP7zNbdbdO+Hu7Tozn+TIC6bJE1lRIqFT390U61fmc0xWGfgHwqBet857YdnnRu4M9D9sWUtUZWCHN/+A85AWar9Po1VdxpujnT/VmJFz/Xy2fqoV3awlDO4psStqWUu/S9OqCWY+tXJpRWNRFkTRVEh8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740752727; c=relaxed/simple; bh=Fv860Al3txKzzpQ0W1HeoGu6xme4o+Rh2jZ3Zrcd7g4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=P88nF1rsPfSlOGPHX/6Hs25BOyvr3MLN9JAR+NoG39Xd9zVfNVfZbzijGch3+TOeyZSdklfXY1tiD2SrwshUT3ixnIg9qwXElmzd7ExVmBsQi8KEynbIwWY7Qcvz0N3FPOWAMIB8ho3EZ580DbmJFNDgsVec+slI+MXI8cZJP+g= 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=xe+Q9GyR; arc=none smtp.client-ip=209.85.218.42 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="xe+Q9GyR" Received: by mail-ej1-f42.google.com with SMTP id a640c23a62f3a-abb7f539c35so404219066b.1 for ; Fri, 28 Feb 2025 06:25:24 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1740752723; x=1741357523; 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=8LIzYk799OwiRGJ+U43eGq7mGeHnDZAFhWnQAaeMfaA=; b=xe+Q9GyRP38cKsmhFa6jvRmi40WhOsMohSEzcueX7WdWALczV7Lnygx8GQkp62GYA8 x7Ry3PT99aFrgkW+btA01dDk1iPEmnbEpqTJKb7LSLPl18kWo942CKHes4gcVx9qpsKW JNVz4/uPPehzjzNXuvQTD4rzW3JoKPKG/Vc93AXaqx8MFYalTL3m3gTDYFPcdHv7qNF1 Vl5mxGxhUHiFSskHG/rConLoEXNtbcDiEzMWJ41b5oQKrtot6UEIs+4wH1SLa9slZo+H LrbGQ61BL7/1pL0If4ignv41g7DMpNiOOkVEtrOJfR4bvkqlCP3wvqeiReKH6d8XzPKI BEoQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740752723; x=1741357523; 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=8LIzYk799OwiRGJ+U43eGq7mGeHnDZAFhWnQAaeMfaA=; b=MG7bru64Xc2l0imq9713xN//RAVNfxRgcWL6HHAiK3VQ9uOLZxg079ma+L1IH+2JyU CCfvl1lGype/Vb6NZzf71zdpP/yVmeKOHviAOaELB+JpwASF8uAvWmKKtuRxyPZxfoVj Xt13HmNTGEQ1ZJTnceW4Lw3qMMUSqwFcGVprBoP+9i6MZCOsy73Ska7f4iq2NNatxzIO SjRgj1YiXE+R1gtD4O1XlTN/hlbF1XEYUHDuw1E6X9gA29M/NnVeYJEz/rwe2aHew79O 2xszrEc4rsV3o+6PwhiWcM0whYF94PkvfZ0vdEm6h6cfHf3wWzudbEnb8WC77/QcGuWv 3gsg== X-Forwarded-Encrypted: i=1; AJvYcCXJwCRtPLjCxHhsOfFjBN18I4YTKEPz3yOV72yPMW497Css6HcVZqZhaZ7GV+aO64FQFbxIBSBCI4F1LRPTKUs=@vger.kernel.org X-Gm-Message-State: AOJu0Yy5f3EpAWvX2TV/aed9Sw9JnI1q4uQ0JIm1497l4ZhhkKWaoVsZ uk/q+q0QOkJAp7pJjQXwShxfqIPpt/PyIs8LLopK5fTuAr9RBxvoD9V+3O6G79/IFWnN+MeHLnv Udmc= X-Gm-Gg: ASbGnctDA6LUshbyBfUW5nM49MewR6f2CZj5cY90m81g7/TjvJzV3MZitrhCdIYKYAl nATowNV3sE5E5TEzgcmHmjLuBDkO61yJCOEe5zB7v6l15RdTSdGQUpy7ley62odKtMnbjTQUnSO UQy8fjmZ41ixiTmX6gCvTgcrj1drqCalwaLPctjHbYAGWJA/M6oY4Qy1Xog+PjYX5gIBa3hiFTn Jhy6nTtzOUqAwBebWfHd9gNcLbhPUdNzKfEKZwOfn4kBqsOqAa/YcvFU8yuwVy+d2HuKyZDfQa/ BrFODbSPFcdNh/t+J6r9IqGzRnLgag1lW7HH6Y1oNHTpTDjksIED8BjqePoKHDLyn2lhErNQ1uL uD+jfRUrCkw== X-Google-Smtp-Source: AGHT+IEpoA52+trT4ge4qUp4b2kA9Vtxbi9NdPY8mvecWdEGjATUZNAbcSVG8gkYPO68c7Dv+KQCHQ== X-Received: by 2002:a17:907:2d91:b0:ab6:d575:9540 with SMTP id a640c23a62f3a-abf26822d6cmr376294966b.50.1740752722682; Fri, 28 Feb 2025 06:25:22 -0800 (PST) Received: from puffmais.c.googlers.com (30.171.91.34.bc.googleusercontent.com. [34.91.171.30]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-abf0c75bffcsm298754466b.143.2025.02.28.06.25.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2025 06:25:22 -0800 (PST) From: =?utf-8?q?Andr=C3=A9_Draszik?= Date: Fri, 28 Feb 2025 14:25:20 +0000 Subject: [PATCH v3 6/6] nvmem: max77759: add Maxim MAX77759 NVMEM driver Precedence: bulk X-Mailing-List: linux-hardening@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250228-max77759-mfd-v3-6-0c3627d42526@linaro.org> References: <20250228-max77759-mfd-v3-0-0c3627d42526@linaro.org> In-Reply-To: <20250228-max77759-mfd-v3-0-0c3627d42526@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 driver exposes the non volatile memory using the platform device registered by the core MFD driver. Signed-off-by: André Draszik --- v2: * align sentinel in max77759_nvmem_of_id[] with other max77759 drivers (Christophe) --- MAINTAINERS | 1 + drivers/nvmem/Kconfig | 12 ++++ drivers/nvmem/Makefile | 2 + drivers/nvmem/max77759-nvmem.c | 156 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 171 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index ef3aadcf86ce35d8807733c94f790cde0f7255af..88c53e3fabe1760abf7914290c8729330739b0b9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14354,6 +14354,7 @@ S: Maintained F: Documentation/devicetree/bindings/*/maxim,max77759*.yaml F: drivers/gpio/gpio-max77759.c F: drivers/mfd/max77759.c +F: drivers/nvmem/max77759-nvmem.c F: include/linux/mfd/max77759.h MAXIM MAX77802 PMIC REGULATOR DEVICE DRIVER diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 8671b7c974b933e147154bb40b5d41b5730518d2..3de07ef524906ad24a89e58abdfe93529a83c80f 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -154,6 +154,18 @@ config NVMEM_LPC18XX_OTP To compile this driver as a module, choose M here: the module will be called nvmem_lpc18xx_otp. +config NVMEM_MAX77759 + tristate "Maxim Integrated MAX77759 NVMEM Support" + depends on MFD_MAX77759 + default MFD_MAX77759 + help + Say Y here to include support for the user-accessible storage found + in Maxim Integrated MAX77759 PMICs. This IC provides space for 30 + bytes of storage. + + This driver can also be built as a module. If so, the module + will be called nvmem-max77759. + config NVMEM_MESON_EFUSE tristate "Amlogic Meson GX eFuse Support" depends on (ARCH_MESON || COMPILE_TEST) && MESON_SM diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 5b77bbb6488bf89bfb305750a1cbf4a6731a0a58..a9d03cfbbd27e68d40f8c330e72e20378b12a481 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -34,6 +34,8 @@ obj-$(CONFIG_NVMEM_LPC18XX_EEPROM) += nvmem_lpc18xx_eeprom.o nvmem_lpc18xx_eeprom-y := lpc18xx_eeprom.o obj-$(CONFIG_NVMEM_LPC18XX_OTP) += nvmem_lpc18xx_otp.o nvmem_lpc18xx_otp-y := lpc18xx_otp.o +obj-$(CONFIG_NVMEM_MAX77759) += nvmem-max77759.o +nvmem-max77759-y := max77759-nvmem.o obj-$(CONFIG_NVMEM_MESON_EFUSE) += nvmem_meson_efuse.o nvmem_meson_efuse-y := meson-efuse.o obj-$(CONFIG_NVMEM_MESON_MX_EFUSE) += nvmem_meson_mx_efuse.o diff --git a/drivers/nvmem/max77759-nvmem.c b/drivers/nvmem/max77759-nvmem.c new file mode 100644 index 0000000000000000000000000000000000000000..bc535a73daaaf2caeb772cd17da61f8a030b6a6f --- /dev/null +++ b/drivers/nvmem/max77759-nvmem.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright 2020 Google Inc +// Copyright 2025 Linaro Ltd. +// +// NVMEM driver for Maxim MAX77759 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX77759_NVMEM_OPCODE_HEADER_LEN 3 +/* + * NVMEM commands have a three byte header (which becomes part of the command), + * so we need to subtract that. + */ +#define MAX77759_NVMEM_SIZE (MAX77759_MAXQ_OPCODE_MAXLENGTH \ + - MAX77759_NVMEM_OPCODE_HEADER_LEN) + +struct max77759_nvmem { + struct device *dev; + struct max77759_mfd *max77759_mfd; +}; + +static bool max77759_nvmem_is_valid(unsigned int offset, size_t bytes) +{ + return (offset + bytes - 1 <= MAX77759_NVMEM_SIZE); +} + +static int max77759_nvmem_reg_read(void *priv, unsigned int offset, + void *val, size_t bytes) +{ + struct max77759_nvmem *nvmem = priv; + DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, + MAX77759_NVMEM_OPCODE_HEADER_LEN); + DEFINE_FLEX(struct max77759_maxq_response, rsp, rsp, length, + MAX77759_MAXQ_OPCODE_MAXLENGTH); + int ret; + + if (!max77759_nvmem_is_valid(offset, bytes)) { + dev_err(nvmem->dev, "outside NVMEM area: %u / %zu\n", + offset, bytes); + return -EINVAL; + } + + cmd->cmd[0] = MAX77759_MAXQ_OPCODE_USER_SPACE_READ; + cmd->cmd[1] = offset; + cmd->cmd[2] = bytes; + rsp->length = bytes + MAX77759_NVMEM_OPCODE_HEADER_LEN; + + ret = max77759_maxq_command(nvmem->max77759_mfd, cmd, rsp); + if (ret < 0) + return ret; + + if (memcmp(cmd->cmd, rsp->rsp, MAX77759_NVMEM_OPCODE_HEADER_LEN)) { + dev_warn(nvmem->dev, "protocol error (read)\n"); + return -EIO; + } + + memcpy(val, &rsp->rsp[MAX77759_NVMEM_OPCODE_HEADER_LEN], bytes); + + return 0; +} + +static int max77759_nvmem_reg_write(void *priv, unsigned int offset, + void *val, size_t bytes) +{ + struct max77759_nvmem *nvmem = priv; + DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, + MAX77759_MAXQ_OPCODE_MAXLENGTH); + DEFINE_FLEX(struct max77759_maxq_response, rsp, rsp, length, + MAX77759_MAXQ_OPCODE_MAXLENGTH); + int ret; + + if (!max77759_nvmem_is_valid(offset, bytes)) { + dev_err(nvmem->dev, "outside NVMEM area: %u / %zu\n", + offset, bytes); + return -EINVAL; + } + + cmd->cmd[0] = MAX77759_MAXQ_OPCODE_USER_SPACE_WRITE; + cmd->cmd[1] = offset; + cmd->cmd[2] = bytes; + memcpy(&cmd->cmd[MAX77759_NVMEM_OPCODE_HEADER_LEN], val, bytes); + cmd->length = bytes + MAX77759_NVMEM_OPCODE_HEADER_LEN; + rsp->length = cmd->length; + + ret = max77759_maxq_command(nvmem->max77759_mfd, cmd, rsp); + if (ret < 0) + return ret; + + if (memcmp(cmd->cmd, rsp->rsp, cmd->length)) { + dev_warn(nvmem->dev, "protocol error (write)\n"); + return -EIO; + } + + return 0; +} + +static int max77759_nvmem_probe(struct platform_device *pdev) +{ + struct nvmem_config config = { + .dev = &pdev->dev, + .name = dev_name(&pdev->dev), + .id = NVMEM_DEVID_NONE, + .type = NVMEM_TYPE_EEPROM, + .ignore_wp = true, + .size = MAX77759_NVMEM_SIZE, + .word_size = sizeof(u8), + .stride = sizeof(u8), + .reg_read = max77759_nvmem_reg_read, + .reg_write = max77759_nvmem_reg_write, + }; + struct max77759_nvmem *nvmem; + + nvmem = devm_kzalloc(&pdev->dev, sizeof(*nvmem), GFP_KERNEL); + if (!nvmem) + return -ENOMEM; + + nvmem->dev = &pdev->dev; + nvmem->max77759_mfd = dev_get_drvdata(pdev->dev.parent); + + config.priv = nvmem; + + return PTR_ERR_OR_ZERO(devm_nvmem_register(config.dev, &config)); +} + +static const struct of_device_id max77759_nvmem_of_id[] = { + { .compatible = "maxim,max77759-nvmem", }, + { } +}; +MODULE_DEVICE_TABLE(of, max77759_nvmem_of_id); + +static struct platform_driver max77759_nvmem_driver = { + .driver = { + .name = "max77759-nvmem", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = max77759_nvmem_of_id, + }, + .probe = max77759_nvmem_probe, +}; + +module_platform_driver(max77759_nvmem_driver); + +MODULE_AUTHOR("André Draszik "); +MODULE_DESCRIPTION("NVMEM driver for Maxim MAX77759"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:max77759-nvmem");