* [PATCH 0/6] Maxim Integrated MAX77759 PMIC MFD-based drivers
@ 2025-02-24 10:28 André Draszik
2025-02-24 10:28 ` [PATCH 1/6] dt-bindings: mfd: add max77759 binding André Draszik
` (5 more replies)
0 siblings, 6 replies; 23+ messages in thread
From: André Draszik @ 2025-02-24 10:28 UTC (permalink / raw)
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,
linux-kernel, devicetree, linux-gpio, linux-hardening,
André Draszik
Hi,
This series improves support for the Maxim Integrated MAX77759
companion PMIC for USB Type-C applications using the MFD framework.
In particular, this PMIC includes Battery Charger, Fuel Gauge,
temperature sensors, USB Type-C Port Controller (TCPC), NVMEM, and a
GPIO expander.
This series adds support for the top-level MFD device, the gpio, and
nvmem cells. Other components are excluded for the following reasons:
While in the same package, Fuel Gauge and TCPC have separate and
independent I2C addresses, register maps, interrupt lines, and
aren't part of the top-level package interrupt hierarchy.
Furthermore, a driver for the TCPC part exists already (in
drivers/usb/typec/tcpm/tcpci_maxim_core.c).
I'm leaving out temperature sensors and charger in this submission,
because the former are not in use on Pixel 6 and I therefore can
not test them, and the latter can be added later, once we look at
the whole charging topic in more detail.
This PMIC is used on the Google Pixel 6 (oriole).
To make maintainers' work easier, I am planning to send the relevant
DTS and defconfig changes via a different series, unless everything
is expected to go via Lee's MFD tree in one series?
Cheers,
Andre'
Signed-off-by: André Draszik <andre.draszik@linaro.org>
---
André Draszik (6):
dt-bindings: mfd: add max77759 binding
dt-bindings: gpio: add max77759 binding
dt-bindings: nvmem: add max77759 binding
mfd: max77759: add Maxim MAX77759 core mfd driver
gpio: max77759: add Maxim MAX77759 gpio driver
nvmem: max77759: add Maxim MAX77759 NVMEM driver
.../bindings/gpio/maxim,max77759-gpio.yaml | 47 ++
.../devicetree/bindings/mfd/maxim,max77759.yaml | 104 +++
.../bindings/nvmem/maxim,max77759-nvmem.yaml | 50 ++
MAINTAINERS | 10 +
drivers/gpio/Kconfig | 13 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-max77759.c | 522 +++++++++++++++
drivers/mfd/Kconfig | 20 +
drivers/mfd/Makefile | 1 +
drivers/mfd/max77759.c | 739 +++++++++++++++++++++
drivers/nvmem/Kconfig | 12 +
drivers/nvmem/Makefile | 2 +
drivers/nvmem/max77759-nvmem.c | 156 +++++
include/linux/mfd/max77759.h | 74 +++
14 files changed, 1751 insertions(+)
---
base-commit: d4b0fd87ff0d4338b259dc79b2b3c6f7e70e8afa
change-id: 20250224-max77759-mfd-aaa7a3121b62
Best regards,
--
André Draszik <andre.draszik@linaro.org>
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH 1/6] dt-bindings: mfd: add max77759 binding 2025-02-24 10:28 [PATCH 0/6] Maxim Integrated MAX77759 PMIC MFD-based drivers André Draszik @ 2025-02-24 10:28 ` André Draszik 2025-02-24 12:48 ` Rob Herring (Arm) ` (2 more replies) 2025-02-24 10:28 ` [PATCH 2/6] dt-bindings: gpio: " André Draszik ` (4 subsequent siblings) 5 siblings, 3 replies; 23+ messages in thread From: André Draszik @ 2025-02-24 10:28 UTC (permalink / raw) 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, linux-kernel, devicetree, linux-gpio, linux-hardening, André Draszik Add device tree binding for the Maxim MAX77759 companion PMIC 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. This describes the core mfd device. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- .../devicetree/bindings/mfd/maxim,max77759.yaml | 104 +++++++++++++++++++++ MAINTAINERS | 6 ++ 2 files changed, 110 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml b/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml new file mode 100644 index 000000000000..1efb841289fb --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml @@ -0,0 +1,104 @@ +# 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 <andre.draszik@linaro.org> + +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 + + gpio-controller: true + + "#gpio-cells": + const: 2 + + gpio: + $ref: /schemas/gpio/maxim,max77759-gpio.yaml + + reg: + maxItems: 1 + + pmic-nvmem: + $ref: /schemas/nvmem/maxim,max77759-nvmem.yaml + +required: + - compatible + - interrupts + - reg + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + + 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>; + }; + + pmic-nvmem { + 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>; + }; + }; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index f076360ce3c6..f2c19a1b4c05 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14322,6 +14322,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 <andre.draszik@linaro.org> +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 <javier@dowhile0.org> L: linux-kernel@vger.kernel.org -- 2.48.1.658.g4767266eb4-goog ^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH 1/6] dt-bindings: mfd: add max77759 binding 2025-02-24 10:28 ` [PATCH 1/6] dt-bindings: mfd: add max77759 binding André Draszik @ 2025-02-24 12:48 ` Rob Herring (Arm) 2025-02-24 13:02 ` André Draszik 2025-02-24 13:07 ` Krzysztof Kozlowski 2025-02-24 15:37 ` Rob Herring 2 siblings, 1 reply; 23+ messages in thread From: Rob Herring (Arm) @ 2025-02-24 12:48 UTC (permalink / raw) To: André Draszik Cc: Lee Jones, Krzysztof Kozlowski, devicetree, Tudor Ambarus, Will McVicker, linux-gpio, linux-hardening, Gustavo A. R. Silva, Kees Cook, Conor Dooley, Linus Walleij, kernel-team, Peter Griffin, Srinivas Kandagatla, Bartosz Golaszewski, linux-kernel On Mon, 24 Feb 2025 10:28:49 +0000, André Draszik wrote: > Add device tree binding for the Maxim MAX77759 companion PMIC 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. > > This describes the core mfd device. > > Signed-off-by: André Draszik <andre.draszik@linaro.org> > --- > .../devicetree/bindings/mfd/maxim,max77759.yaml | 104 +++++++++++++++++++++ > MAINTAINERS | 6 ++ > 2 files changed, 110 insertions(+) > My bot found errors running 'make dt_binding_check' on your patch: yamllint warnings/errors: dtschema/dtc warnings/errors: /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/mfd/maxim,max77759.example.dtb: pmic@66: gpio: False schema does not allow {'compatible': ['maxim,max77759-gpio'], 'gpio-controller': True, '#gpio-cells': 2, 'interrupt-controller': True, '#interrupt-cells': 2} from schema $id: http://devicetree.org/schemas/mfd/maxim,max77759.yaml# /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/mfd/maxim,max77759.example.dtb: pmic@66: pmic-nvmem: False schema does not allow {'compatible': ['maxim,max77759-nvmem'], 'nvmem-layout': {'compatible': ['fixed-layout'], '#address-cells': 1, '#size-cells': 1, 'reboot-mode@0': {'reg': [[0, 4]]}, 'boot-reason@4': {'reg': [[4, 4]]}, 'shutdown-user-flag@8': {'reg': [[8, 1]]}, 'rsoc@10': {'reg': [[10, 2]]}}} from schema $id: http://devicetree.org/schemas/mfd/maxim,max77759.yaml# Documentation/devicetree/bindings/mfd/maxim,max77759.example.dtb: /example-0/i2c/pmic@66/gpio: failed to match any schema with compatible: ['maxim,max77759-gpio'] Documentation/devicetree/bindings/mfd/maxim,max77759.example.dtb: /example-0/i2c/pmic@66/pmic-nvmem: failed to match any schema with compatible: ['maxim,max77759-nvmem'] doc reference errors (make refcheckdocs): See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20250224-max77759-mfd-v1-1-2bff36f9d055@linaro.org The base for the series is generally the latest rc1. A different dependency should be noted in *this* patch. If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date: pip3 install dtschema --upgrade Please check and re-submit after running the above command yourself. Note that DT_SCHEMA_FILES can be set to your schema file to speed up checking your schema. However, it must be unset to test all examples with your schema. ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 1/6] dt-bindings: mfd: add max77759 binding 2025-02-24 12:48 ` Rob Herring (Arm) @ 2025-02-24 13:02 ` André Draszik 2025-02-24 13:05 ` Krzysztof Kozlowski 0 siblings, 1 reply; 23+ messages in thread From: André Draszik @ 2025-02-24 13:02 UTC (permalink / raw) To: Rob Herring (Arm) Cc: Lee Jones, Krzysztof Kozlowski, devicetree, Tudor Ambarus, Will McVicker, linux-gpio, linux-hardening, Gustavo A. R. Silva, Kees Cook, Conor Dooley, Linus Walleij, kernel-team, Peter Griffin, Srinivas Kandagatla, Bartosz Golaszewski, linux-kernel On Mon, 2025-02-24 at 06:48 -0600, Rob Herring (Arm) wrote: > > On Mon, 24 Feb 2025 10:28:49 +0000, André Draszik wrote: > > Add device tree binding for the Maxim MAX77759 companion PMIC 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. > > > > This describes the core mfd device. > > > > Signed-off-by: André Draszik <andre.draszik@linaro.org> > > --- > > .../devicetree/bindings/mfd/maxim,max77759.yaml | 104 +++++++++++++++++++++ > > MAINTAINERS | 6 ++ > > 2 files changed, 110 insertions(+) > > > > My bot found errors running 'make dt_binding_check' on your patch: > > yamllint warnings/errors: > > dtschema/dtc warnings/errors: > /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/mfd/maxim,max77759.example.dtb: pmic@66: gpio: False schema > does not allow {'compatible': ['maxim,max77759-gpio'], 'gpio-controller': True, '#gpio-cells': 2, 'interrupt-controller': True, > '#interrupt-cells': 2} > from schema $id: http://devicetree.org/schemas/mfd/maxim,max77759.yaml# > /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/mfd/maxim,max77759.example.dtb: pmic@66: pmic-nvmem: False > schema does not allow {'compatible': ['maxim,max77759-nvmem'], 'nvmem-layout': {'compatible': ['fixed-layout'], '#address-cells': 1, > '#size-cells': 1, 'reboot-mode@0': {'reg': [[0, 4]]}, 'boot-reason@4': {'reg': [[4, 4]]}, 'shutdown-user-flag@8': {'reg': [[8, 1]]}, > 'rsoc@10': {'reg': [[10, 2]]}}} > from schema $id: http://devicetree.org/schemas/mfd/maxim,max77759.yaml# > Documentation/devicetree/bindings/mfd/maxim,max77759.example.dtb: /example-0/i2c/pmic@66/gpio: failed to match any schema with > compatible: ['maxim,max77759-gpio'] > Documentation/devicetree/bindings/mfd/maxim,max77759.example.dtb: /example-0/i2c/pmic@66/pmic-nvmem: failed to match any schema with > compatible: ['maxim,max77759-nvmem'] The top-level example in here references the two (MFD cell) bindings added in the two follow-up patches for gpio and nvmem. When all three binding patches exist in the tree, the errors are gone. Is this acceptable, or shall I add the top-level example only after the bindings? Thanks, Andre' ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 1/6] dt-bindings: mfd: add max77759 binding 2025-02-24 13:02 ` André Draszik @ 2025-02-24 13:05 ` Krzysztof Kozlowski 0 siblings, 0 replies; 23+ messages in thread From: Krzysztof Kozlowski @ 2025-02-24 13:05 UTC (permalink / raw) To: André Draszik, Rob Herring (Arm) Cc: Lee Jones, Krzysztof Kozlowski, devicetree, Tudor Ambarus, Will McVicker, linux-gpio, linux-hardening, Gustavo A. R. Silva, Kees Cook, Conor Dooley, Linus Walleij, kernel-team, Peter Griffin, Srinivas Kandagatla, Bartosz Golaszewski, linux-kernel On 24/02/2025 14:02, André Draszik wrote: > On Mon, 2025-02-24 at 06:48 -0600, Rob Herring (Arm) wrote: >> >> On Mon, 24 Feb 2025 10:28:49 +0000, André Draszik wrote: >>> Add device tree binding for the Maxim MAX77759 companion PMIC 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. >>> >>> This describes the core mfd device. >>> >>> Signed-off-by: André Draszik <andre.draszik@linaro.org> >>> --- >>> .../devicetree/bindings/mfd/maxim,max77759.yaml | 104 +++++++++++++++++++++ >>> MAINTAINERS | 6 ++ >>> 2 files changed, 110 insertions(+) >>> >> >> My bot found errors running 'make dt_binding_check' on your patch: >> >> yamllint warnings/errors: >> >> dtschema/dtc warnings/errors: >> /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/mfd/maxim,max77759.example.dtb: pmic@66: gpio: False schema >> does not allow {'compatible': ['maxim,max77759-gpio'], 'gpio-controller': True, '#gpio-cells': 2, 'interrupt-controller': True, >> '#interrupt-cells': 2} >> from schema $id: http://devicetree.org/schemas/mfd/maxim,max77759.yaml# >> /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/mfd/maxim,max77759.example.dtb: pmic@66: pmic-nvmem: False >> schema does not allow {'compatible': ['maxim,max77759-nvmem'], 'nvmem-layout': {'compatible': ['fixed-layout'], '#address-cells': 1, >> '#size-cells': 1, 'reboot-mode@0': {'reg': [[0, 4]]}, 'boot-reason@4': {'reg': [[4, 4]]}, 'shutdown-user-flag@8': {'reg': [[8, 1]]}, >> 'rsoc@10': {'reg': [[10, 2]]}}} >> from schema $id: http://devicetree.org/schemas/mfd/maxim,max77759.yaml# >> Documentation/devicetree/bindings/mfd/maxim,max77759.example.dtb: /example-0/i2c/pmic@66/gpio: failed to match any schema with >> compatible: ['maxim,max77759-gpio'] >> Documentation/devicetree/bindings/mfd/maxim,max77759.example.dtb: /example-0/i2c/pmic@66/pmic-nvmem: failed to match any schema with >> compatible: ['maxim,max77759-nvmem'] > > The top-level example in here references the two (MFD cell) > bindings added in the two follow-up patches for gpio and > nvmem. When all three binding patches exist in the tree, > the errors are gone. > > Is this acceptable, or shall I add the top-level example only > after the bindings? Not sure if I follow - do you confirm that it is bisectable or it is not? If not, then it has to be fixed. Best regards, Krzysztof ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 1/6] dt-bindings: mfd: add max77759 binding 2025-02-24 10:28 ` [PATCH 1/6] dt-bindings: mfd: add max77759 binding André Draszik 2025-02-24 12:48 ` Rob Herring (Arm) @ 2025-02-24 13:07 ` Krzysztof Kozlowski 2025-02-24 13:14 ` André Draszik 2025-02-24 15:37 ` Rob Herring 2 siblings, 1 reply; 23+ messages in thread From: Krzysztof Kozlowski @ 2025-02-24 13:07 UTC (permalink / raw) To: André Draszik, 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, linux-kernel, devicetree, linux-gpio, linux-hardening On 24/02/2025 11:28, André Draszik wrote: > + gpio-controller: true > + > + "#gpio-cells": > + const: 2 > + > + gpio: > + $ref: /schemas/gpio/maxim,max77759-gpio.yaml There is no such file at this moment, so this is not bisectable. What's more, even if you fix it, you will have dependency which you *must* always clearly express in patch changelog or *top* (the first people see) of cover letter. Best regards, Krzysztof ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 1/6] dt-bindings: mfd: add max77759 binding 2025-02-24 13:07 ` Krzysztof Kozlowski @ 2025-02-24 13:14 ` André Draszik 0 siblings, 0 replies; 23+ messages in thread From: André Draszik @ 2025-02-24 13:14 UTC (permalink / raw) To: Krzysztof Kozlowski, 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, linux-kernel, devicetree, linux-gpio, linux-hardening On Mon, 2025-02-24 at 14:07 +0100, Krzysztof Kozlowski wrote: > On 24/02/2025 11:28, André Draszik wrote: > > + gpio-controller: true > > + > > + "#gpio-cells": > > + const: 2 > > + > > + gpio: > > + $ref: /schemas/gpio/maxim,max77759-gpio.yaml > > There is no such file at this moment, so this is not bisectable. What's > more, even if you fix it, you will have dependency which you *must* > always clearly express in patch changelog or *top* (the first people > see) of cover letter. Thanks Krzysztof. You're right of course. I'll fix the dependency issues in the next version Cheers, Andre' ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 1/6] dt-bindings: mfd: add max77759 binding 2025-02-24 10:28 ` [PATCH 1/6] dt-bindings: mfd: add max77759 binding André Draszik 2025-02-24 12:48 ` Rob Herring (Arm) 2025-02-24 13:07 ` Krzysztof Kozlowski @ 2025-02-24 15:37 ` Rob Herring 2025-02-24 16:05 ` André Draszik 2 siblings, 1 reply; 23+ messages in thread From: Rob Herring @ 2025-02-24 15:37 UTC (permalink / raw) To: André Draszik Cc: Lee Jones, Krzysztof Kozlowski, Conor Dooley, Linus Walleij, Bartosz Golaszewski, Srinivas Kandagatla, Kees Cook, Gustavo A. R. Silva, Peter Griffin, Tudor Ambarus, Will McVicker, kernel-team, linux-kernel, devicetree, linux-gpio, linux-hardening On Mon, Feb 24, 2025 at 10:28:49AM +0000, André Draszik wrote: > Add device tree binding for the Maxim MAX77759 companion PMIC 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. > > This describes the core mfd device. > > Signed-off-by: André Draszik <andre.draszik@linaro.org> > --- > .../devicetree/bindings/mfd/maxim,max77759.yaml | 104 +++++++++++++++++++++ > MAINTAINERS | 6 ++ > 2 files changed, 110 insertions(+) > > diff --git a/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml b/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml > new file mode 100644 > index 000000000000..1efb841289fb > --- /dev/null > +++ b/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml > @@ -0,0 +1,104 @@ > +# 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 <andre.draszik@linaro.org> > + > +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 > + > + gpio-controller: true > + > + "#gpio-cells": > + const: 2 > + > + gpio: > + $ref: /schemas/gpio/maxim,max77759-gpio.yaml > + > + reg: > + maxItems: 1 > + > + pmic-nvmem: Just 'nvmem' > + $ref: /schemas/nvmem/maxim,max77759-nvmem.yaml > + > +required: > + - compatible > + - interrupts > + - reg > + > +additionalProperties: false > + > +examples: > + - | > + #include <dt-bindings/interrupt-controller/irq.h> > + > + 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>; > + }; > + > + pmic-nvmem { nvmem { > + 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>; > + }; > + }; > + }; > + }; > + }; > diff --git a/MAINTAINERS b/MAINTAINERS > index f076360ce3c6..f2c19a1b4c05 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -14322,6 +14322,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 <andre.draszik@linaro.org> > +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 <javier@dowhile0.org> > L: linux-kernel@vger.kernel.org > > -- > 2.48.1.658.g4767266eb4-goog > ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 1/6] dt-bindings: mfd: add max77759 binding 2025-02-24 15:37 ` Rob Herring @ 2025-02-24 16:05 ` André Draszik 2025-02-26 17:56 ` André Draszik 0 siblings, 1 reply; 23+ messages in thread From: André Draszik @ 2025-02-24 16:05 UTC (permalink / raw) To: Rob Herring Cc: Lee Jones, Krzysztof Kozlowski, Conor Dooley, Linus Walleij, Bartosz Golaszewski, Srinivas Kandagatla, Kees Cook, Gustavo A. R. Silva, Peter Griffin, Tudor Ambarus, Will McVicker, kernel-team, linux-kernel, devicetree, linux-gpio, linux-hardening Hi Rob, Thanks for the review! On Mon, 2025-02-24 at 09:37 -0600, Rob Herring wrote: > On Mon, Feb 24, 2025 at 10:28:49AM +0000, André Draszik wrote: > > Add device tree binding for the Maxim MAX77759 companion PMIC 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. > > > > This describes the core mfd device. > > > > Signed-off-by: André Draszik <andre.draszik@linaro.org> > > --- > > .../devicetree/bindings/mfd/maxim,max77759.yaml | 104 +++++++++++++++++++++ > > MAINTAINERS | 6 ++ > > 2 files changed, 110 insertions(+) > > > > diff --git a/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml b/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml > > new file mode 100644 > > index 000000000000..1efb841289fb > > --- /dev/null > > +++ b/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml > > @@ -0,0 +1,104 @@ > > +# 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 <andre.draszik@linaro.org> > > + > > +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 > > + > > + gpio-controller: true > > + > > + "#gpio-cells": > > + const: 2 > > + > > + gpio: > > + $ref: /schemas/gpio/maxim,max77759-gpio.yaml > > + > > + reg: > > + maxItems: 1 > > + > > + pmic-nvmem: > > Just 'nvmem' TBH, I'd prefer that as well, and I had just 'nvmem' initially, but that doesn't work: .../maxim,max77759.example.dtb: pmic@66: nvmem: {'compatible': ['maxim,max77759-nvmem'], 'nvmem-layout': {'compatible': ['fixed- layout'], '#address-cells': 1, '#size-cells': 1, 'reboot-mode@0': {'reg': [[0, 4]]}, 'boot-reason@4': {'reg': [[4, 4]]}, 'shutdown-user-flag@8': {'reg': [[8, 1]]}, 'rsoc@10': {'reg': [[10, 2]]}}} is not of type 'array' from schema $id: http://devicetree.org/schemas/nvmem/nvmem-consumer.yaml# I don't know if this can be made to work, i.e. can you have both in yaml? Can a type be declared as a oneOf or something like that? > > > + $ref: /schemas/nvmem/maxim,max77759-nvmem.yaml > > + > > +required: > > + - compatible > > + - interrupts > > + - reg > > + > > +additionalProperties: false > > + > > +examples: > > + - | > > + #include <dt-bindings/interrupt-controller/irq.h> > > + > > + 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>; > > + }; > > + > > + pmic-nvmem { > > nvmem { dito Cheers, Andre' > > + 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>; > > + }; > > + }; > > + }; > > + }; > > + }; > > diff --git a/MAINTAINERS b/MAINTAINERS > > index f076360ce3c6..f2c19a1b4c05 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -14322,6 +14322,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 <andre.draszik@linaro.org> > > +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 <javier@dowhile0.org> > > L: linux-kernel@vger.kernel.org > > > > -- > > 2.48.1.658.g4767266eb4-goog > > ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 1/6] dt-bindings: mfd: add max77759 binding 2025-02-24 16:05 ` André Draszik @ 2025-02-26 17:56 ` André Draszik 0 siblings, 0 replies; 23+ messages in thread From: André Draszik @ 2025-02-26 17:56 UTC (permalink / raw) To: Rob Herring Cc: Lee Jones, Krzysztof Kozlowski, Conor Dooley, Linus Walleij, Bartosz Golaszewski, Srinivas Kandagatla, Kees Cook, Gustavo A. R. Silva, Peter Griffin, Tudor Ambarus, Will McVicker, kernel-team, linux-kernel, devicetree, linux-gpio, linux-hardening On Mon, 2025-02-24 at 16:05 +0000, André Draszik wrote: > Hi Rob, > > Thanks for the review! > > On Mon, 2025-02-24 at 09:37 -0600, Rob Herring wrote: > > On Mon, Feb 24, 2025 at 10:28:49AM +0000, André Draszik wrote: > > > Add device tree binding for the Maxim MAX77759 companion PMIC 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. > > > > > > This describes the core mfd device. > > > > > > Signed-off-by: André Draszik <andre.draszik@linaro.org> > > > --- > > > .../devicetree/bindings/mfd/maxim,max77759.yaml | 104 +++++++++++++++++++++ > > > MAINTAINERS | 6 ++ > > > 2 files changed, 110 insertions(+) > > > > > > diff --git a/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml > > > b/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml > > > new file mode 100644 > > > index 000000000000..1efb841289fb > > > --- /dev/null > > > +++ b/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml > > > @@ -0,0 +1,104 @@ > > > +# 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 <andre.draszik@linaro.org> > > > + > > > +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 > > > + > > > + gpio-controller: true > > > + > > > + "#gpio-cells": > > > + const: 2 > > > + > > > + gpio: > > > + $ref: /schemas/gpio/maxim,max77759-gpio.yaml > > > + > > > + reg: > > > + maxItems: 1 > > > + > > > + pmic-nvmem: > > > > Just 'nvmem' > > TBH, I'd prefer that as well, and I had just 'nvmem' initially, > but that doesn't work: > > .../maxim,max77759.example.dtb: pmic@66: nvmem: {'compatible': ['maxim,max77759-nvmem'], 'nvmem-layout': {'compatible': ['fixed- > layout'], '#address-cells': 1, '#size-cells': 1, 'reboot-mode@0': {'reg': [[0, 4]]}, 'boot-reason@4': {'reg': [[4, 4]]}, > 'shutdown-user-flag@8': {'reg': [[8, 1]]}, 'rsoc@10': {'reg': [[10, 2]]}}} is not of type 'array' > from schema $id: http://devicetree.org/schemas/nvmem/nvmem-consumer.yaml# > > I don't know if this can be made to work, i.e. can you have both > in yaml? Can a type be declared as a oneOf or something like that? I wasn't able to get this to work with a node name of just 'nvmem'. If anybody has any suggestions, I'll gladly try them. I've renamed the node from pmic-nvmem to nvmem-0 in v2, though. https://lore.kernel.org/all/20250226-max77759-mfd-v2-3-a65ebe2bc0a9@linaro.org/ Cheers, Andre ^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 2/6] dt-bindings: gpio: add max77759 binding 2025-02-24 10:28 [PATCH 0/6] Maxim Integrated MAX77759 PMIC MFD-based drivers André Draszik 2025-02-24 10:28 ` [PATCH 1/6] dt-bindings: mfd: add max77759 binding André Draszik @ 2025-02-24 10:28 ` André Draszik 2025-02-24 15:38 ` Rob Herring 2025-02-24 10:28 ` [PATCH 3/6] dt-bindings: nvmem: " André Draszik ` (3 subsequent siblings) 5 siblings, 1 reply; 23+ messages in thread From: André Draszik @ 2025-02-24 10:28 UTC (permalink / raw) 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, linux-kernel, devicetree, linux-gpio, linux-hardening, André Draszik Add the DT binding document for the GPIO module of the Maxim MAX77759. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- .../bindings/gpio/maxim,max77759-gpio.yaml | 47 ++++++++++++++++++++++ 1 file changed, 47 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 000000000000..9bafb16ad688 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/maxim,max77759-gpio.yaml @@ -0,0 +1,47 @@ +# 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 <andre.draszik@linaro.org> + +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 + + "#interrupt-cells": + const: 2 + + interrupt-controller: true + + interrupts: + maxItems: 2 + + "#gpio-cells": + const: 2 + + gpio-controller: true + + gpio-line-names: + minItems: 1 + maxItems: 2 + +required: + - compatible + - "#gpio-cells" + - gpio-controller + - "#interrupt-cells" + - interrupt-controller + +additionalProperties: false -- 2.48.1.658.g4767266eb4-goog ^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH 2/6] dt-bindings: gpio: add max77759 binding 2025-02-24 10:28 ` [PATCH 2/6] dt-bindings: gpio: " André Draszik @ 2025-02-24 15:38 ` Rob Herring 2025-02-24 21:52 ` André Draszik 0 siblings, 1 reply; 23+ messages in thread From: Rob Herring @ 2025-02-24 15:38 UTC (permalink / raw) To: André Draszik Cc: Lee Jones, Krzysztof Kozlowski, Conor Dooley, Linus Walleij, Bartosz Golaszewski, Srinivas Kandagatla, Kees Cook, Gustavo A. R. Silva, Peter Griffin, Tudor Ambarus, Will McVicker, kernel-team, linux-kernel, devicetree, linux-gpio, linux-hardening On Mon, Feb 24, 2025 at 10:28:50AM +0000, André Draszik wrote: > Add the DT binding document for the GPIO module of the Maxim MAX77759. > > Signed-off-by: André Draszik <andre.draszik@linaro.org> > --- > .../bindings/gpio/maxim,max77759-gpio.yaml | 47 ++++++++++++++++++++++ > 1 file changed, 47 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 000000000000..9bafb16ad688 > --- /dev/null > +++ b/Documentation/devicetree/bindings/gpio/maxim,max77759-gpio.yaml > @@ -0,0 +1,47 @@ > +# 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 <andre.draszik@linaro.org> > + > +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 > + > + "#interrupt-cells": > + const: 2 > + > + interrupt-controller: true > + > + interrupts: > + maxItems: 2 You need to define what each interrupt is. > + > + "#gpio-cells": > + const: 2 > + > + gpio-controller: true > + > + gpio-line-names: > + minItems: 1 > + maxItems: 2 > + > +required: > + - compatible > + - "#gpio-cells" > + - gpio-controller > + - "#interrupt-cells" > + - interrupt-controller > + > +additionalProperties: false > > -- > 2.48.1.658.g4767266eb4-goog > ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 2/6] dt-bindings: gpio: add max77759 binding 2025-02-24 15:38 ` Rob Herring @ 2025-02-24 21:52 ` André Draszik 0 siblings, 0 replies; 23+ messages in thread From: André Draszik @ 2025-02-24 21:52 UTC (permalink / raw) To: Rob Herring Cc: Lee Jones, Krzysztof Kozlowski, Conor Dooley, Linus Walleij, Bartosz Golaszewski, Srinivas Kandagatla, Kees Cook, Gustavo A. R. Silva, Peter Griffin, Tudor Ambarus, Will McVicker, kernel-team, linux-kernel, devicetree, linux-gpio, linux-hardening Hi Rob, On Mon, 2025-02-24 at 09:38 -0600, Rob Herring wrote: > On Mon, Feb 24, 2025 at 10:28:50AM +0000, André Draszik wrote: > > Add the DT binding document for the GPIO module of the Maxim MAX77759. > > > > Signed-off-by: André Draszik <andre.draszik@linaro.org> > > --- > > .../bindings/gpio/maxim,max77759-gpio.yaml | 47 ++++++++++++++++++++++ > > 1 file changed, 47 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 000000000000..9bafb16ad688 > > --- /dev/null > > +++ b/Documentation/devicetree/bindings/gpio/maxim,max77759-gpio.yaml > > @@ -0,0 +1,47 @@ > > +# 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 <andre.draszik@linaro.org> > > + > > +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 > > + > > + "#interrupt-cells": > > + const: 2 > > + > > + interrupt-controller: true > > + > > + interrupts: > > + maxItems: 2 > > You need to define what each interrupt is. I think maybe the interrupts property is not needed after all: The PMIC has one external interrupt line (described by the top-level device), and the two interrupts here are just a representation of the PMIC's internal status register (i.e. top level interrupt is raised when status register for one of the gpio lines changes, amongst other things). The intention is for a gpio driver to just treat and model them as cascaded interrupts, but they don't need to be configured in any way via device tree, as everything happens internally inside the PMIC, there is no board-dependent routing or trigger type possible. (Of course, there can be drivers subscribing to one (or both) of the two cascaded interrupts here, but that shouldn't matter I believe). Does that make sense? I added the property because Documentation/devicetree/bindings/interrupt-controller/interrupts.txt says it's a requirement to have an interrupts property, but I believe it doesn't actually apply in this case as there's nothing than can be configured. Am I missing something? Cheers, Andre' > > > + > > + "#gpio-cells": > > + const: 2 > > + > > + gpio-controller: true > > + > > + gpio-line-names: > > + minItems: 1 > > + maxItems: 2 > > + > > +required: > > + - compatible > > + - "#gpio-cells" > > + - gpio-controller > > + - "#interrupt-cells" > > + - interrupt-controller > > + > > +additionalProperties: false > > > > -- > > 2.48.1.658.g4767266eb4-goog > > ^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 3/6] dt-bindings: nvmem: add max77759 binding 2025-02-24 10:28 [PATCH 0/6] Maxim Integrated MAX77759 PMIC MFD-based drivers André Draszik 2025-02-24 10:28 ` [PATCH 1/6] dt-bindings: mfd: add max77759 binding André Draszik 2025-02-24 10:28 ` [PATCH 2/6] dt-bindings: gpio: " André Draszik @ 2025-02-24 10:28 ` André Draszik 2025-02-24 15:38 ` Rob Herring 2025-02-24 10:28 ` [PATCH 4/6] mfd: max77759: add Maxim MAX77759 core mfd driver André Draszik ` (2 subsequent siblings) 5 siblings, 1 reply; 23+ messages in thread From: André Draszik @ 2025-02-24 10:28 UTC (permalink / raw) 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, linux-kernel, devicetree, linux-gpio, linux-hardening, André Draszik Add the DT binding document for the NVMEM module of the Maxim MAX77759. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- .../bindings/nvmem/maxim,max77759-nvmem.yaml | 50 ++++++++++++++++++++++ 1 file changed, 50 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 000000000000..d3b7430ef551 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/maxim,max77759-nvmem.yaml @@ -0,0 +1,50 @@ +# 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 <andre.draszik@linaro.org> + +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 + +examples: + - | + nvmem { + compatible = "maxim,max77759-nvmem"; + #address-cells = <1>; + #size-cells = <1>; + + nvmem-layout { + compatible = "fixed-layout"; + #address-cells = <1>; + #size-cells = <1>; + + rsoc@a { + reg = <0xa 2>; + }; + }; + }; -- 2.48.1.658.g4767266eb4-goog ^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH 3/6] dt-bindings: nvmem: add max77759 binding 2025-02-24 10:28 ` [PATCH 3/6] dt-bindings: nvmem: " André Draszik @ 2025-02-24 15:38 ` Rob Herring 2025-02-24 16:06 ` André Draszik 0 siblings, 1 reply; 23+ messages in thread From: Rob Herring @ 2025-02-24 15:38 UTC (permalink / raw) To: André Draszik Cc: Lee Jones, Krzysztof Kozlowski, Conor Dooley, Linus Walleij, Bartosz Golaszewski, Srinivas Kandagatla, Kees Cook, Gustavo A. R. Silva, Peter Griffin, Tudor Ambarus, Will McVicker, kernel-team, linux-kernel, devicetree, linux-gpio, linux-hardening On Mon, Feb 24, 2025 at 10:28:51AM +0000, André Draszik wrote: > Add the DT binding document for the NVMEM module of the Maxim MAX77759. > > Signed-off-by: André Draszik <andre.draszik@linaro.org> > --- > .../bindings/nvmem/maxim,max77759-nvmem.yaml | 50 ++++++++++++++++++++++ > 1 file changed, 50 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 000000000000..d3b7430ef551 > --- /dev/null > +++ b/Documentation/devicetree/bindings/nvmem/maxim,max77759-nvmem.yaml > @@ -0,0 +1,50 @@ > +# 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 <andre.draszik@linaro.org> > + > +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 > + > +examples: > + - | > + nvmem { Drop the example here. The MFD binding has a complete one. > + compatible = "maxim,max77759-nvmem"; > + #address-cells = <1>; > + #size-cells = <1>; > + > + nvmem-layout { > + compatible = "fixed-layout"; > + #address-cells = <1>; > + #size-cells = <1>; > + > + rsoc@a { > + reg = <0xa 2>; > + }; > + }; > + }; > > -- > 2.48.1.658.g4767266eb4-goog > ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 3/6] dt-bindings: nvmem: add max77759 binding 2025-02-24 15:38 ` Rob Herring @ 2025-02-24 16:06 ` André Draszik 0 siblings, 0 replies; 23+ messages in thread From: André Draszik @ 2025-02-24 16:06 UTC (permalink / raw) To: Rob Herring Cc: Lee Jones, Krzysztof Kozlowski, Conor Dooley, Linus Walleij, Bartosz Golaszewski, Srinivas Kandagatla, Kees Cook, Gustavo A. R. Silva, Peter Griffin, Tudor Ambarus, Will McVicker, kernel-team, linux-kernel, devicetree, linux-gpio, linux-hardening On Mon, 2025-02-24 at 09:38 -0600, Rob Herring wrote: > On Mon, Feb 24, 2025 at 10:28:51AM +0000, André Draszik wrote: > > Add the DT binding document for the NVMEM module of the Maxim MAX77759. > > > > Signed-off-by: André Draszik <andre.draszik@linaro.org> > > --- > > .../bindings/nvmem/maxim,max77759-nvmem.yaml | 50 ++++++++++++++++++++++ > > 1 file changed, 50 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 000000000000..d3b7430ef551 > > --- /dev/null > > +++ b/Documentation/devicetree/bindings/nvmem/maxim,max77759-nvmem.yaml > > @@ -0,0 +1,50 @@ > > +# 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 <andre.draszik@linaro.org> > > + > > +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 > > + > > +examples: > > + - | > > + nvmem { > > Drop the example here. The MFD binding has a complete one. Will do Thanks Rob! ^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 4/6] mfd: max77759: add Maxim MAX77759 core mfd driver 2025-02-24 10:28 [PATCH 0/6] Maxim Integrated MAX77759 PMIC MFD-based drivers André Draszik ` (2 preceding siblings ...) 2025-02-24 10:28 ` [PATCH 3/6] dt-bindings: nvmem: " André Draszik @ 2025-02-24 10:28 ` André Draszik 2025-02-24 20:20 ` Christophe JAILLET 2025-02-24 10:28 ` [PATCH 5/6] gpio: max77759: add Maxim MAX77759 gpio driver André Draszik 2025-02-24 10:28 ` [PATCH 6/6] nvmem: max77759: add Maxim MAX77759 NVMEM driver André Draszik 5 siblings, 1 reply; 23+ messages in thread From: André Draszik @ 2025-02-24 10:28 UTC (permalink / raw) 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, linux-kernel, devicetree, linux-gpio, linux-hardening, André Draszik The Maxim MAX77759 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. 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 <andre.draszik@linaro.org> --- MAINTAINERS | 2 + drivers/mfd/Kconfig | 20 ++ drivers/mfd/Makefile | 1 + drivers/mfd/max77759.c | 739 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/max77759.h | 74 +++++ 5 files changed, 836 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index f2c19a1b4c05..b1bbe7165e0d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14327,6 +14327,8 @@ M: André Draszik <andre.draszik@linaro.org> 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 <javier@dowhile0.org> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index d44c69bb3dfd..1d72bf086401 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 9220eaf7cf12..cc9362afd8f0 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 000000000000..57e9a1e95df5 --- /dev/null +++ b/drivers/mfd/max77759.c @@ -0,0 +1,739 @@ +// 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 <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/completion.h> +#include <linux/dev_printk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/jiffies.h> +#include <linux/mfd/core.h> +#include <linux/mfd/max77759.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/overflow.h> +#include <linux/regmap.h> + +/* 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; + 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(regmap_get_device(max77759_mfd->regmap_maxq), + "write data failed: %d\n", ret); + if (ret) + return ret; + + /* wait for response from MaxQ */ + if (!wait_for_completion_timeout(&max77759_mfd->cmd_done, + usecs_to_jiffies(timeout_ms))) { + dev_err(regmap_get_device(max77759_mfd->regmap_maxq), + "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(regmap_get_device(max77759_mfd->regmap_maxq), + "read data failed: %d\n", ret); + return ret; + } + + /* + * As per the protocol, the first byte of the reply will match the + * request. + */ + if (rsp->rsp[0] != cmd->cmd[0]) { + dev_warn(regmap_get_device(max77759_mfd->regmap_maxq), + "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; + devm_mutex_init(&client->dev, &max77759_mfd->maxq_lock); + + 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 <andre.draszik@linaro.org>"); +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 000000000000..f901bcf502cb --- /dev/null +++ b/include/linux/mfd/max77759.h @@ -0,0 +1,74 @@ +/* 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 { + u8 length; + u8 cmd[] __counted_by(length); +}; + +struct max77759_maxq_response { + u8 length; + u8 rsp[] __counted_by(length); +}; + +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 */ -- 2.48.1.658.g4767266eb4-goog ^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH 4/6] mfd: max77759: add Maxim MAX77759 core mfd driver 2025-02-24 10:28 ` [PATCH 4/6] mfd: max77759: add Maxim MAX77759 core mfd driver André Draszik @ 2025-02-24 20:20 ` Christophe JAILLET 2025-02-24 21:32 ` André Draszik 0 siblings, 1 reply; 23+ messages in thread From: Christophe JAILLET @ 2025-02-24 20:20 UTC (permalink / raw) To: André Draszik Cc: brgl, conor+dt, devicetree, gustavoars, kees, kernel-team, krzk+dt, lee, linus.walleij, linux-gpio, linux-hardening, linux-kernel, peter.griffin, robh, srinivas.kandagatla, tudor.ambarus, willmcvicker Le 24/02/2025 à 11:28, André Draszik a écrit : > The Maxim MAX77759 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. > > 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. Hi, ... > +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; > + 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(regmap_get_device(max77759_mfd->regmap_maxq), Maybe regmap_get_device(max77759_mfd->regmap_maxq) could be assigned to a variable to simplify its usage? > + "write data failed: %d\n", ret); > + if (ret) > + return ret; Merge with the if (ret) just above? (as done a few lines below) > + > + /* wait for response from MaxQ */ > + if (!wait_for_completion_timeout(&max77759_mfd->cmd_done, > + usecs_to_jiffies(timeout_ms))) { > + dev_err(regmap_get_device(max77759_mfd->regmap_maxq), > + "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(regmap_get_device(max77759_mfd->regmap_maxq), > + "read data failed: %d\n", ret); > + return ret; > + } > + > + /* > + * As per the protocol, the first byte of the reply will match the > + * request. > + */ > + if (rsp->rsp[0] != cmd->cmd[0]) { > + dev_warn(regmap_get_device(max77759_mfd->regmap_maxq), > + "unexpected opcode response for %#.2x: %*ph\n", > + cmd->cmd[0], (int)rsp->length, rsp->rsp); > + return -EIO; > + } > + > + 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; > + devm_mutex_init(&client->dev, &max77759_mfd->maxq_lock); Error handling? > + > + i2c_set_clientdata(client, max77759_mfd); Harmless, but is it needed? (there is no i2c_get_clientdata() in the flile) > + > + for (int i = 0; i < ARRAY_SIZE(max77759_i2c_subdevs); ++i) { Unusual. Maybe declare 'i' at the beginning of the function? > + 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", }, > + {}, Unneeded trailing comma after a terminator. Maybe { } to match the style used in max77759_i2c_id? > +}; > +MODULE_DEVICE_TABLE(of, max77759_of_id); ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 4/6] mfd: max77759: add Maxim MAX77759 core mfd driver 2025-02-24 20:20 ` Christophe JAILLET @ 2025-02-24 21:32 ` André Draszik 0 siblings, 0 replies; 23+ messages in thread From: André Draszik @ 2025-02-24 21:32 UTC (permalink / raw) To: Christophe JAILLET Cc: brgl, conor+dt, devicetree, gustavoars, kees, kernel-team, krzk+dt, lee, linus.walleij, linux-gpio, linux-hardening, linux-kernel, peter.griffin, robh, srinivas.kandagatla, tudor.ambarus, willmcvicker Hi Christophe, Thanks for your review! On Mon, 2025-02-24 at 21:20 +0100, Christophe JAILLET wrote: > Le 24/02/2025 à 11:28, André Draszik a écrit : > > The Maxim MAX77759 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. > > > > 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. > > Hi, > > ... > > > +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; > > + 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(regmap_get_device(max77759_mfd->regmap_maxq), > > Maybe regmap_get_device(max77759_mfd->regmap_maxq) could be assigned to > a variable to simplify its usage? Sure, can do. > > > + "write data failed: %d\n", ret); > > + if (ret) > > + return ret; > > Merge with the if (ret) just above? (as done a few lines below) Definitely, that was an oversight somehow :-( > > > + > > + /* wait for response from MaxQ */ > > + if (!wait_for_completion_timeout(&max77759_mfd->cmd_done, > > + usecs_to_jiffies(timeout_ms))) { > > + dev_err(regmap_get_device(max77759_mfd->regmap_maxq), > > + "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(regmap_get_device(max77759_mfd->regmap_maxq), > > + "read data failed: %d\n", ret); > > + return ret; > > + } > > + > > + /* > > + * As per the protocol, the first byte of the reply will match the > > + * request. > > + */ > > + if (rsp->rsp[0] != cmd->cmd[0]) { > > + dev_warn(regmap_get_device(max77759_mfd->regmap_maxq), > > + "unexpected opcode response for %#.2x: %*ph\n", > > + cmd->cmd[0], (int)rsp->length, rsp->rsp); > > + return -EIO; > > + } > > + > > + 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; > > + devm_mutex_init(&client->dev, &max77759_mfd->maxq_lock); > > Error handling? Thanks! There's a similar one in the gpio driver which also needs fixing. > > > + > > + i2c_set_clientdata(client, max77759_mfd); > > Harmless, but is it needed? (there is no i2c_get_clientdata() in the flile) Yes, this is needed. The leaf drivers need to access it, see dev_get_drvdata(pdev->dev.parent) in those. > > > + > > + for (int i = 0; i < ARRAY_SIZE(max77759_i2c_subdevs); ++i) { > > Unusual. Maybe declare 'i' at the beginning of the function? A naive grep returned > 1000 statements like this in the kernel tree. It doesn't look like it's that unusual these days anymore. > > > + 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", }, > > + {}, > > Unneeded trailing comma after a terminator. > Maybe { } to match the style used in max77759_i2c_id? OK. I'll also fix the similar leaf drivers. Cheers, Andre' > > > +}; > > +MODULE_DEVICE_TABLE(of, max77759_of_id); > ^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 5/6] gpio: max77759: add Maxim MAX77759 gpio driver 2025-02-24 10:28 [PATCH 0/6] Maxim Integrated MAX77759 PMIC MFD-based drivers André Draszik ` (3 preceding siblings ...) 2025-02-24 10:28 ` [PATCH 4/6] mfd: max77759: add Maxim MAX77759 core mfd driver André Draszik @ 2025-02-24 10:28 ` André Draszik 2025-02-24 10:46 ` André Draszik 2025-02-24 10:28 ` [PATCH 6/6] nvmem: max77759: add Maxim MAX77759 NVMEM driver André Draszik 5 siblings, 1 reply; 23+ messages in thread From: André Draszik @ 2025-02-24 10:28 UTC (permalink / raw) 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, linux-kernel, devicetree, linux-gpio, linux-hardening, André Draszik The Maxim MAX77759 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 supports the GPIO functions using the platform device registered by the core MFD driver. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- MAINTAINERS | 1 + drivers/gpio/Kconfig | 13 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-max77759.c | 522 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 537 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index b1bbe7165e0d..d119ed1ff279 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14327,6 +14327,7 @@ M: André Draszik <andre.draszik@linaro.org> 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 f518f5458446..486b22438c76 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1462,6 +1462,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 af3ba4d81b58..d7a904047657 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -102,6 +102,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 000000000000..9f21a6cf4567 --- /dev/null +++ b/drivers/gpio/gpio-max77759.c @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright 2020 Google Inc +// Copyright 2025 Linaro Ltd. +// +// GPIO driver for Maxim MAX77759 + +#include <linux/dev_printk.h> +#include <linux/device.h> +#include <linux/device/driver.h> +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqreturn.h> +#include <linux/lockdep.h> +#include <linux/mfd/max77759.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/overflow.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/seq_file.h> + +#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) +{ + return (((ctrl & MAX77759_GPIOx_DIR_MASK(offset)) + == 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); + devm_mutex_init(&pdev->dev, &chip->maxq_lock); + devm_mutex_init(&pdev->dev, &chip->irq_lock); + + 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 <andre.draszik@linaro.org>"); +MODULE_DESCRIPTION("GPIO driver for Maxim MAX77759"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:max77759-gpio"); -- 2.48.1.658.g4767266eb4-goog ^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH 5/6] gpio: max77759: add Maxim MAX77759 gpio driver 2025-02-24 10:28 ` [PATCH 5/6] gpio: max77759: add Maxim MAX77759 gpio driver André Draszik @ 2025-02-24 10:46 ` André Draszik 0 siblings, 0 replies; 23+ messages in thread From: André Draszik @ 2025-02-24 10:46 UTC (permalink / raw) 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, linux-kernel, devicetree, linux-gpio, linux-hardening On Mon, 2025-02-24 at 10:28 +0000, André Draszik wrote: > [...] > > +#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_direction_from_control(int ctrl, unsigned int offset) > +{ > + return (((ctrl & MAX77759_GPIOx_DIR_MASK(offset)) > + == MAX77759_GPIO_DIR_OUT) > + ? GPIO_LINE_DIRECTION_OUT > + : GPIO_LINE_DIRECTION_IN); Eek, I made a last minute change that I shouldn't have :-( This should be something more like: 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); } I'll fix that for v2. A. ^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 6/6] nvmem: max77759: add Maxim MAX77759 NVMEM driver 2025-02-24 10:28 [PATCH 0/6] Maxim Integrated MAX77759 PMIC MFD-based drivers André Draszik ` (4 preceding siblings ...) 2025-02-24 10:28 ` [PATCH 5/6] gpio: max77759: add Maxim MAX77759 gpio driver André Draszik @ 2025-02-24 10:28 ` André Draszik 2025-03-12 9:29 ` Srinivas Kandagatla 5 siblings, 1 reply; 23+ messages in thread From: André Draszik @ 2025-02-24 10:28 UTC (permalink / raw) 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, linux-kernel, devicetree, linux-gpio, linux-hardening, André Draszik The Maxim MAX77759 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 exposes the non volatile memory using the platform device registered by the core MFD driver. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- 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 d119ed1ff279..bbc92dee7af4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14329,6 +14329,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 8671b7c974b9..3de07ef52490 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 5b77bbb6488b..a9d03cfbbd27 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 000000000000..4c455b242542 --- /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 <linux/dev_printk.h> +#include <linux/device.h> +#include <linux/device/driver.h> +#include <linux/err.h> +#include <linux/mfd/max77759.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/nvmem-provider.h> +#include <linux/overflow.h> +#include <linux/platform_device.h> +#include <linux/string.h> + +#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 <andre.draszik@linaro.org>"); +MODULE_DESCRIPTION("NVMEM driver for Maxim MAX77759"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:max77759-nvmem"); -- 2.48.1.658.g4767266eb4-goog ^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH 6/6] nvmem: max77759: add Maxim MAX77759 NVMEM driver 2025-02-24 10:28 ` [PATCH 6/6] nvmem: max77759: add Maxim MAX77759 NVMEM driver André Draszik @ 2025-03-12 9:29 ` Srinivas Kandagatla 0 siblings, 0 replies; 23+ messages in thread From: Srinivas Kandagatla @ 2025-03-12 9:29 UTC (permalink / raw) To: André Draszik, Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Linus Walleij, Bartosz Golaszewski, Kees Cook, Gustavo A. R. Silva Cc: Peter Griffin, Tudor Ambarus, Will McVicker, kernel-team, linux-kernel, devicetree, linux-gpio, linux-hardening On 24/02/2025 10:28, André Draszik wrote: > The Maxim MAX77759 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 exposes the non volatile memory using the platform device > registered by the core MFD driver. > > Signed-off-by: André Draszik <andre.draszik@linaro.org> > --- LGTM, Reviewed-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> > 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 d119ed1ff279..bbc92dee7af4 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -14329,6 +14329,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 8671b7c974b9..3de07ef52490 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 5b77bbb6488b..a9d03cfbbd27 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 000000000000..4c455b242542 > --- /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 <linux/dev_printk.h> > +#include <linux/device.h> > +#include <linux/device/driver.h> > +#include <linux/err.h> > +#include <linux/mfd/max77759.h> > +#include <linux/mod_devicetable.h> > +#include <linux/module.h> > +#include <linux/nvmem-provider.h> > +#include <linux/overflow.h> > +#include <linux/platform_device.h> > +#include <linux/string.h> > + > +#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 <andre.draszik@linaro.org>"); > +MODULE_DESCRIPTION("NVMEM driver for Maxim MAX77759"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("platform:max77759-nvmem"); > ^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2025-03-12 9:29 UTC | newest] Thread overview: 23+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-02-24 10:28 [PATCH 0/6] Maxim Integrated MAX77759 PMIC MFD-based drivers André Draszik 2025-02-24 10:28 ` [PATCH 1/6] dt-bindings: mfd: add max77759 binding André Draszik 2025-02-24 12:48 ` Rob Herring (Arm) 2025-02-24 13:02 ` André Draszik 2025-02-24 13:05 ` Krzysztof Kozlowski 2025-02-24 13:07 ` Krzysztof Kozlowski 2025-02-24 13:14 ` André Draszik 2025-02-24 15:37 ` Rob Herring 2025-02-24 16:05 ` André Draszik 2025-02-26 17:56 ` André Draszik 2025-02-24 10:28 ` [PATCH 2/6] dt-bindings: gpio: " André Draszik 2025-02-24 15:38 ` Rob Herring 2025-02-24 21:52 ` André Draszik 2025-02-24 10:28 ` [PATCH 3/6] dt-bindings: nvmem: " André Draszik 2025-02-24 15:38 ` Rob Herring 2025-02-24 16:06 ` André Draszik 2025-02-24 10:28 ` [PATCH 4/6] mfd: max77759: add Maxim MAX77759 core mfd driver André Draszik 2025-02-24 20:20 ` Christophe JAILLET 2025-02-24 21:32 ` André Draszik 2025-02-24 10:28 ` [PATCH 5/6] gpio: max77759: add Maxim MAX77759 gpio driver André Draszik 2025-02-24 10:46 ` André Draszik 2025-02-24 10:28 ` [PATCH 6/6] nvmem: max77759: add Maxim MAX77759 NVMEM driver André Draszik 2025-03-12 9:29 ` Srinivas Kandagatla
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).