From patchwork Wed Feb 26 17:51:21 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: 868629 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 C33EB22A1F1 for ; Wed, 26 Feb 2025 17:51:26 +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=1740592288; cv=none; b=ln5PEbQQsar7M9vpDqi7bGX9hGY2CnfFAuMyf2Y5HN0MyrIuqBJR7EgOoGwIpydwfkQ+L8FLH96Ard9+KdPRCh7HR3qrgTkPYne4JBCbXy7FTrN1UwXtdQ2tNu9B2JmNUa/DOH6RSLRk4l8BHZ5nkpxZidKqcVPfdiEq26Ha1EY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740592288; c=relaxed/simple; bh=I4feaQK2NeL1iMZsywRHTjEz/SueHsximMXGo9uKgLE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NwrwQSCp+numfb5Eq8TpvS0Y98D1offVr1s+Gww2flZyW8GvyPMjawX3W8V9adxmAUOpyaZptCOSMAL1VVrAbvRFl3g6ZHr2wCVi5Dunw6UdaBXWDEGh8vAAbtN9UGqGVaXeqyjkdopI9dUUUhAwyX1K4sd59lKUyACTtgyEoeQ= 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=km5KCfub; 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="km5KCfub" Received: by mail-ed1-f46.google.com with SMTP id 4fb4d7f45d1cf-5e058ca6806so12527036a12.3 for ; Wed, 26 Feb 2025 09:51:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1740592285; x=1741197085; 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=A9YomS+c0wThxhIl04CUTErMlrQA8JmWzVl1nv3uerI=; b=km5KCfuboEwYf3Lk8LTH7jJmnQvgCD+Dc8ClSyE8K3EK2mwV9/emetFM6v+S7X7Hlu 4uK9rloDtfjz47LEtO9xjUAS9Oj89VIw4I3Py/QU+qc6r8zA39pTHTvSvq/V8+bgBiZY hj5VZSTd0RnEm9E3xpJx32rUEzk3Nrf3/Ze6LTAnx0OR0x4paJ5o9GLhCYhwOG/+Bxmb 05DHgc8rvMxqaTa+VuQyBDwUiVbBEPcYOTO1Zf1pVtoq1c/JHNW0hyVMxDy1vTRWohTf 6h9LXVt1sg0IlQogJ2yHxaPYzhO/n6JStcDD2mwliNsVUV9qGJB9iGWYaemL3Fh8pwc3 egIg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740592285; x=1741197085; 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=A9YomS+c0wThxhIl04CUTErMlrQA8JmWzVl1nv3uerI=; b=FfqZaiLPVFJKkqFBGdxhbyibuo7JN7wgQKqSEq12FV6AjKNDVG+HPGNDldm8VhjJXB pBYg3J19REBVomE8mv2Jdjp0WVrwkpr9joC7icWvFN4zQeKX6isgjtpQj9DpLSloshsi IvFpSLTwwmhTtUGgGozcB1jRWlWrz0gCyEoGHQ/Afr0AARbq5Eja7Pr33YPQKKeDm6Jx bgbfB1u7LYGwUI7vUgjawWnf0D+H0SxCxJ7w+7IPqmfzRNGbSjvZxBQ+NGeaJafWUeIC 1HA3nz+qyp7aj+QMlafQxFWj1v9YD4AiSYk77ZGgd1H21Fg/XZb1PdKobfGZ0WF73tzY jQoQ== X-Forwarded-Encrypted: i=1; AJvYcCWMhy4iEPusWUfiH8HQT450p2euDZ+JB1mhe9AYh43zUrE/XLvwvlW7Bo9dlVC4Jf/mtym/m46Kjg1N@vger.kernel.org X-Gm-Message-State: AOJu0Yzj3+bTQf3X5G102IsGX/HHjqtoQcXxXMHhhlk8kLy2K4vCrIvM BHKyOISqW9mF9cYTMVhxXZwQ+q5apOai7ej33f6nHtkGS66O5ZkcFa8T/Er27ZU= X-Gm-Gg: ASbGnctWX25X+BQrQ+X3NCc7IzXPsHchp1cwrwppZuqwA2nTvdRGvn7KbYCQT3XQIbl UYiTK1+G28FyEX0/mBFVaQAwQo7L9iunlsI/5Hr7mdwDl4fiRw4COOXHpU582nM7Om+y70r3uGn GscJ6jAxmfAP1aMqAjZLIfctv80u2iYebGg6OoWefYjIdpH2JJnyuI/QBo8jy6ejxv1dC60vqM6 klHlEk8HCeKhsbJlaf50Vp30vFuOf8c+Xc6r3+0u+Wsj8rSHXn6gTl1T6w/kt/yH+t5WTa2Fk3V 5pcnidqLHBQPRbNlXQ84biqeYYiNc/JwbXDSo2oT2eXoYCUfCPwP2aK21Yb3o7qiniAgy56YuOk n4rMEZAh7kQ== X-Google-Smtp-Source: AGHT+IF7S3Byg/2EA8G/VaFXwC0WijN7Hyq+Y45Wz3xtOjrJyqaLL3aB8h/6a9ePi5nZG42xtp7hZw== X-Received: by 2002:a17:906:6a22:b0:aba:6204:1c03 with SMTP id a640c23a62f3a-abc0df5d89fmr2632175766b.57.1740592284983; Wed, 26 Feb 2025 09:51:24 -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-abed1cdb131sm361889866b.7.2025.02.26.09.51.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 26 Feb 2025 09:51:24 -0800 (PST) From: =?utf-8?q?Andr=C3=A9_Draszik?= Date: Wed, 26 Feb 2025 17:51:21 +0000 Subject: [PATCH v2 2/6] dt-bindings: nvmem: 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: <20250226-max77759-mfd-v2-2-a65ebe2bc0a9@linaro.org> References: <20250226-max77759-mfd-v2-0-a65ebe2bc0a9@linaro.org> In-Reply-To: <20250226-max77759-mfd-v2-0-a65ebe2bc0a9@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). 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 Wed Feb 26 17:51:24 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: 868628 Received: from mail-ej1-f47.google.com (mail-ej1-f47.google.com [209.85.218.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 96B1022A4E3 for ; Wed, 26 Feb 2025 17:51:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740592292; cv=none; b=Ot7PH85t5QFe+Xrhj8thh8Qe5rHnEczCitFGzIeEHZIqlc57Risz+80rIQiY+cWby0HMO5dyZiE9q7znvccOLv9I7XAyL/cCS3tl69uEohjQYpNWe6xYqBCh7AU7HSrfNSmwgQNAG32YJ61c6/5vSFQrPzaZWl/Kv2BFMx2VocY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740592292; c=relaxed/simple; bh=uCvQQwUccg2G8B6tI9/UFfZak5bmvwusCNMPeFJ9HEg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ki9Zue/F2MosVu+seEpAwxR9CzTZQL3Ww6km6W8LGS4NOtyNZfIdw37YhF2tsazW5IFoLbcdPmFvEEeTx0epeplk3WEsntle9fuWalvhAjRNbyI1U0muZ6wBgLKRUvcMVfvf27mnTSiFCSkkqKxIcwFwy2nKBwGtWnlJ+vjVWBA= 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=qX7eoCZ5; arc=none smtp.client-ip=209.85.218.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="qX7eoCZ5" Received: by mail-ej1-f47.google.com with SMTP id a640c23a62f3a-abb8045c3f3so1273166b.2 for ; Wed, 26 Feb 2025 09:51:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1740592287; x=1741197087; 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=HxvDAmqbkNweSUFrnpsax7vPo1H+x6FC4LoXoO35Cis=; b=qX7eoCZ5WLiV9pFzud45SdtN3TUFQNXvRXsmdXpEYOop8JkLBVxwiBBUFaMhytjjhG PPhzTF09fJo/2xajxzdg6DtDI6Vbx85FnrLNpqBkgNPusiJont0zqx/B8RWSffxq4ksp PkKWlx6RMv0msVFyh2hlIKKzijECjvf9XaIYOT/I3A8z/XsKcp2170bsJ6X69Lcja7CR DurlKPWzy27u8RLlG4QWwUUUpqCUk5iuxuL96y5wbh9nSHnwNAohTT5WpQrh8ASXv2pJ CIvkZ8BqMUGf//VB7z/AeAIfqywzpkpUHaTqbPk3KsJPF6myjCzI8qTck64yOC/KORUp fCvQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740592287; x=1741197087; 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=HxvDAmqbkNweSUFrnpsax7vPo1H+x6FC4LoXoO35Cis=; b=tZ6Idx5+S+qr4MzOaZ/2rJIMsSo9ZoKHYp0fVCixw7nInb/j5A/6FKhv5w213pk/N/ Lsi4qCmJgdfR3uBesF9aq7fOsBBoFrRAatXqJ+dz5I5sazbsJ3yci8fXv5RIKoCwWrhv Fd0g3PmBjklsMLnhvnzBorbduckTNNF6+ivFj+PrpLYXDCxoCif0tJ8B8UcF/tO7RqNM vLMBPsYTRk+JWtifmB8xWTpx//F5236ra/A6K3FS3aJWjNJz4/d4WAYLjCPQFaqOSSZe gW7yiiSvcyDfMGS4e+w9YqcMQe4lx6x4JvxKY1KqB5O0fPb177d9Vgh2+qwmuse/1JDl EpGA== X-Forwarded-Encrypted: i=1; AJvYcCXH5FuPDJWE3EYvVyB2GrfXyNuzk/swPxi6PSYp6TAf0zROsa966jA4TLeO2o4Awqy5Ra9ALBt0T8x9@vger.kernel.org X-Gm-Message-State: AOJu0YxypTFWtB8BnPIrBe+IshJ9CDsMcApBUH2W5jovwe/AuUuWMV9C WbQJlqjn8ANhSNXVqtxe24o5a9SLA9qOaOVGz/dHIbvyHFhAtl/zB+GPwCCYqRE= X-Gm-Gg: ASbGnct2E4jG8xfLUt0r85i/SmktTKgZziYIPuS6ezLPp2lHEwHjZRrtlq5JzYZXlwE XJzauMJ2JVLjrzOs1vtxKMgYWdN2FZ7fOFkLh0AGmIRB3dGEM048DAoDKrIUwPghzokOeZ2JUd+ zqaYZLjINPAteS9JFbFcX2rZcfEN4v/uozdvbubhG4bWs++fCNKdgcmbgoeUL1nm0Pz3NYorYtA wyY5xcUtsiwcfYJFX210RRZoNx1055WdqbMNz55J6BkwTkgJwygSot7R3kw5ceUMxMBtgboLqO6 GH0F15w26ghTuZAvEZVEpi+c2ktJ5XUIbRZdDpuDwa+GxzMA9PMTXqk7QbsbRxwTWyu7yHM6apK fL26UxJ34Sg== X-Google-Smtp-Source: AGHT+IGKGVdFv8V6dD9D5pS/YVQJ2/CHrr05rUPGVLvnQLDL3wgAfQVeSJg1USlhmT2Xk47fB7600A== X-Received: by 2002:a17:906:30d4:b0:ab7:e3cb:ca81 with SMTP id a640c23a62f3a-abeeee9faefmr521838666b.30.1740592286713; Wed, 26 Feb 2025 09:51:26 -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-abed1cdb131sm361889866b.7.2025.02.26.09.51.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 26 Feb 2025 09:51:26 -0800 (PST) From: =?utf-8?q?Andr=C3=A9_Draszik?= Date: Wed, 26 Feb 2025 17:51:24 +0000 Subject: [PATCH v2 5/6] gpio: max77759: add Maxim MAX77759 gpio driver Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250226-max77759-mfd-v2-5-a65ebe2bc0a9@linaro.org> References: <20250226-max77759-mfd-v2-0-a65ebe2bc0a9@linaro.org> In-Reply-To: <20250226-max77759-mfd-v2-0-a65ebe2bc0a9@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 --- 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 | 528 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 543 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..b304c533dc9d6bf792d179c6c36b467c2c134e56 --- /dev/null +++ b/drivers/gpio/gpio-max77759.c @@ -0,0 +1,528 @@ +// 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; + int handled = 0; + + /* iterate until no interrupt is pending */ + handled = 0; + 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 == 0, 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) { + unsigned int virq; + + /* + * 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)); + + virq = irq_find_mapping(gc->irq.domain, offset); + handle_nested_irq(virq); + + handled = 1; + } + } + + 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.owner = THIS_MODULE; + 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 Wed Feb 26 17:51:25 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: 868627 Received: from mail-ej1-f52.google.com (mail-ej1-f52.google.com [209.85.218.52]) (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 39DAD22A4F4 for ; Wed, 26 Feb 2025 17:51:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740592293; cv=none; b=FTRnGnTcyzouLorf9gRWZQJ8P5ZysCpminrIWtNYSJBGVo75V7JIJLbJaclV2Q6uBZZDb//4oPp0KJZlAaLRFhuL/VrAGemWORc6EXCiLiorI3C6epHdZNaKJo5AMYrGmgDgzwmYMZv+iLkCj3/jR8InrlsgDDnYMy6iQnYDARk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740592293; c=relaxed/simple; bh=UqQ4D2zqbUqZqP16QvU/8ioZrSbOXhjX9gezexgfYN8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=uDKyv7H7t8nIHr+1QWTMTpdy56PVule2h0NL2EksgpE+cLGS9AzpvqiL4GwDMej73a/0Cqis0g2RQEds0Sh2qj2cnHJz+rWGNtmnk/oNdgsAhlv/gSFmSmmY8ODi6+XDxAqenASkZGXStYEUJRLNs0wRMjGVXfAJyxewtHob+iQ= 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=nqFTsVTO; arc=none smtp.client-ip=209.85.218.52 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="nqFTsVTO" Received: by mail-ej1-f52.google.com with SMTP id a640c23a62f3a-abb75200275so1885566b.3 for ; Wed, 26 Feb 2025 09:51:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1740592287; x=1741197087; 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=qR0lzQBLBFN6egr/8EFBOjjr5I/gEiXiq1v9lNopb6A=; b=nqFTsVTOXm7UnBPuhvyg5wQgEqXAwfeIwfAStnkFJE0INC+XevnLjfXYTW0qMxdmUQ i8jvC0hw+63j6JNtlzjLxYX0eiH37A4UPbbQP0amzE6RXkxgCut2BlCVfxjpxrYT4+lk EsawWbJjYMVqiDaOSn99OXC3xPs77Qs7uY71a0n+f3zv4FrN9B29ZW/kByHSW9CYwQ1h iUs3iwz5S9ScV0UDW79llJHIjb8+gVT7ePEWtp/vZ0OE6TDIWmYJqtFICAacVx90CC+h 0MfwfDKxx1iKJlMFIw4/Ms0nSKPglqH8bWFzgAq4lQ+mR55BwO4tApGOKN5EkREjumdP j/Sg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740592287; x=1741197087; 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=qR0lzQBLBFN6egr/8EFBOjjr5I/gEiXiq1v9lNopb6A=; b=O+shXb4JNMcyPd1IxhZnRqQsj9CIlHNKx11Ho02CRCBtphl2/rzFOBQ7m+nZ/J3AeT ZU0kPeIP7n/Li4bmyZyqmgc+NWkyXvuIzlF1JJYgsoP8y8GW1/BHnp1oIbLkDdsOe+wz fInyOUq8V4A2ci67NOJB9TQYm5S+5VYQqq2f0OZ1VmK7HrGPS8LLsoXWnOvsMBWV1Lhf veNKPxAKcu+LOvMKxI8z+o3SFnfyPEnL7qVkm0UprLm/jLm5AxzDDewh+GCus9v4CjY3 /7Y4Acg01VbGsCdfh4pmNgl4EN0cgVfU395R8wqZaWwKOt7aBeDuSkzsNgjIx3k0feHx kaag== X-Forwarded-Encrypted: i=1; AJvYcCUIyiCzApdi9JAM/94wv/BSLGFlJW/VfP57aN7gQPgylb3sijJI5RIs2SNLxm1OsAgwqPNhkIdyC/l9@vger.kernel.org X-Gm-Message-State: AOJu0YwYZnAcNTqPTCryni2vNtvpQU9woVlSuXvNTE0xfYHUsLAkuIB+ GESQilqJZLPfkaIgTRzqv0NqPaOHc7qcmKFhfWG1kL5/Cd5swo4PzrHyHUdSgDE= X-Gm-Gg: ASbGncsAsryBsS2ELCl6z1RnekXtPjB2kkWUe/0VeqjYfRRw/wPwfcWxpAjiTQPtxVn x6P1BxSpIsidabm5ALGhBZMB5rbJmSi8dJZdjcSqpwvRpKVS8tiE2QhfamLOS+6uBubUUXB0EBH vKN5LulRt+YVB5dwOOs31jUlpVCWlB3Qzlo0OTfCkZP//OZp8p3ZLMKa7PCYT0IX2oIgTTGZ4mi mjEtX65A8OizBlFa4gs04VrPjk/5gRqcdXSsFO97c0xysmNW0+7up6P8NXbVtT0ffCgLghYOD2E 1AlucT7b5jjGrmbC6hDRSaq6VSDmiMmVbU9BSIiBzBaySZWDDULssb+E1jEKOuu9x66q0XPhvBX XqpoithVBUw== X-Google-Smtp-Source: AGHT+IG0IeO5SUAyrOgZTQXJwk2KUNpsD8thnJT4walwJNGemDcdZeiA+s5C0pgPEaVkniVO7R8h9g== X-Received: by 2002:a17:907:7754:b0:ab7:cccb:ae2b with SMTP id a640c23a62f3a-abc099b7f67mr2225689366b.4.1740592287374; Wed, 26 Feb 2025 09:51:27 -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-abed1cdb131sm361889866b.7.2025.02.26.09.51.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 26 Feb 2025 09:51:26 -0800 (PST) From: =?utf-8?q?Andr=C3=A9_Draszik?= Date: Wed, 26 Feb 2025 17:51:25 +0000 Subject: [PATCH v2 6/6] nvmem: max77759: add Maxim MAX77759 NVMEM driver Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250226-max77759-mfd-v2-6-a65ebe2bc0a9@linaro.org> References: <20250226-max77759-mfd-v2-0-a65ebe2bc0a9@linaro.org> In-Reply-To: <20250226-max77759-mfd-v2-0-a65ebe2bc0a9@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");