mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:44:45 +01:00
Merge branches 'ib-firmware-mfd-6.16', 'ib-mfd-clocksource-pwm-6.16', 'ib-mfd-gpio-nvmem-6.16', 'ib-mfd-regulator-6.16' and 'ib-mfd-regulator-6.16-1' into ibs-for-mfd-merged
This commit is contained in:
commit
18e6c1d2a7
34 changed files with 4523 additions and 512 deletions
|
|
@ -0,0 +1,44 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/gpio/maxim,max77759-gpio.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Maxim Integrated MAX77759 GPIO
|
||||
|
||||
maintainers:
|
||||
- André Draszik <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
|
||||
|
||||
"#gpio-cells":
|
||||
const: 2
|
||||
|
||||
gpio-controller: true
|
||||
|
||||
gpio-line-names:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
"#interrupt-cells":
|
||||
const: 2
|
||||
|
||||
interrupt-controller: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- "#gpio-cells"
|
||||
- gpio-controller
|
||||
- "#interrupt-cells"
|
||||
- interrupt-controller
|
||||
|
||||
additionalProperties: false
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
-------------------------------
|
||||
BCM590xx Power Management Units
|
||||
-------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible: "brcm,bcm59056"
|
||||
- reg: I2C slave address
|
||||
- interrupts: interrupt for the PMU. Generic interrupt client node bindings
|
||||
are described in interrupt-controller/interrupts.txt
|
||||
|
||||
------------------
|
||||
Voltage Regulators
|
||||
------------------
|
||||
|
||||
Optional child nodes:
|
||||
- regulators: container node for regulators following the generic
|
||||
regulator binding in regulator/regulator.txt
|
||||
|
||||
The valid regulator node names for BCM59056 are:
|
||||
rfldo, camldo1, camldo2, simldo1, simldo2, sdldo, sdxldo,
|
||||
mmcldo1, mmcldo2, audldo, micldo, usbldo, vibldo,
|
||||
csr, iosr1, iosr2, msr, sdsr1, sdsr2, vsr,
|
||||
gpldo1, gpldo2, gpldo3, gpldo4, gpldo5, gpldo6,
|
||||
vbus
|
||||
|
||||
Example:
|
||||
pmu: bcm59056@8 {
|
||||
compatible = "brcm,bcm59056";
|
||||
reg = <0x08>;
|
||||
interrupts = <GIC_SPI 215 IRQ_TYPE_LEVEL_HIGH>;
|
||||
regulators {
|
||||
rfldo_reg: rfldo {
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
};
|
||||
|
||||
...
|
||||
};
|
||||
};
|
||||
76
Documentation/devicetree/bindings/mfd/brcm,bcm59056.yaml
Normal file
76
Documentation/devicetree/bindings/mfd/brcm,bcm59056.yaml
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mfd/brcm,bcm59056.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Broadcom BCM590xx Power Management Units
|
||||
|
||||
maintainers:
|
||||
- Artur Weber <aweber.kernel@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- brcm,bcm59054
|
||||
- brcm,bcm59056
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
regulators:
|
||||
type: object
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: brcm,bcm59054
|
||||
then:
|
||||
properties:
|
||||
regulators:
|
||||
$ref: /schemas/regulator/brcm,bcm59054.yaml#
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: brcm,bcm59056
|
||||
then:
|
||||
properties:
|
||||
regulators:
|
||||
$ref: /schemas/regulator/brcm,bcm59056.yaml#
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
pmic@8 {
|
||||
compatible = "brcm,bcm59056";
|
||||
reg = <0x08>;
|
||||
interrupts = <GIC_SPI 215 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
regulators {
|
||||
rfldo {
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
99
Documentation/devicetree/bindings/mfd/maxim,max77759.yaml
Normal file
99
Documentation/devicetree/bindings/mfd/maxim,max77759.yaml
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mfd/maxim,max77759.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Maxim Integrated MAX77759 PMIC for USB Type-C applications
|
||||
|
||||
maintainers:
|
||||
- André Draszik <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
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
gpio:
|
||||
$ref: /schemas/gpio/maxim,max77759-gpio.yaml
|
||||
|
||||
nvmem-0:
|
||||
$ref: /schemas/nvmem/maxim,max77759-nvmem.yaml
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- interrupts
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <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>;
|
||||
};
|
||||
|
||||
nvmem-0 {
|
||||
compatible = "maxim,max77759-nvmem";
|
||||
|
||||
nvmem-layout {
|
||||
compatible = "fixed-layout";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
reboot-mode@0 {
|
||||
reg = <0x0 0x4>;
|
||||
};
|
||||
|
||||
boot-reason@4 {
|
||||
reg = <0x4 0x4>;
|
||||
};
|
||||
|
||||
shutdown-user-flag@8 {
|
||||
reg = <0x8 0x1>;
|
||||
};
|
||||
|
||||
rsoc@10 {
|
||||
reg = <0xa 0x2>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -4,19 +4,21 @@
|
|||
$id: http://devicetree.org/schemas/mfd/rohm,bd96801-pmic.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ROHM BD96801 Scalable Power Management Integrated Circuit
|
||||
title: ROHM BD96801/BD96805 Scalable Power Management Integrated Circuit
|
||||
|
||||
maintainers:
|
||||
- Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
|
||||
|
||||
description:
|
||||
BD96801 is an automotive grade single-chip power management IC.
|
||||
It integrates 4 buck converters and 3 LDOs with safety features like
|
||||
BD96801 and BD96805 are automotive grade, single-chip power management ICs.
|
||||
They both integrate 4 buck converters and 3 LDOs with safety features like
|
||||
over-/under voltage and over current detection and a watchdog.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: rohm,bd96801
|
||||
enum:
|
||||
- rohm,bd96801
|
||||
- rohm,bd96805
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
|||
101
Documentation/devicetree/bindings/mfd/rohm,bd96802-pmic.yaml
Normal file
101
Documentation/devicetree/bindings/mfd/rohm,bd96802-pmic.yaml
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mfd/rohm,bd96802-pmic.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ROHM BD96802 / BD96806 Scalable Power Management Integrated Circuit
|
||||
|
||||
maintainers:
|
||||
- Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
|
||||
|
||||
description: |
|
||||
BD96802Qxx-C and BD96806 are automotive grade configurable Power Management
|
||||
Integrated Circuits supporting Functional Safety features for application
|
||||
processors, SoCs and FPGAs
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- rohm,bd96802
|
||||
- rohm,bd96806
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
description:
|
||||
The PMIC provides intb and errb IRQ lines. The errb IRQ line is used
|
||||
for fatal IRQs which will cause the PMIC to shut down power outputs.
|
||||
In many systems this will shut down the SoC contolling the PMIC and
|
||||
connecting/handling the errb can be omitted. However, there are cases
|
||||
where the SoC is not powered by the PMIC or has a short time backup
|
||||
energy to handle shutdown of critical hardware. In that case it may be
|
||||
useful to connect the errb and handle errb events.
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- enum: [intb, errb]
|
||||
- const: errb
|
||||
|
||||
regulators:
|
||||
$ref: ../regulator/rohm,bd96802-regulator.yaml
|
||||
description:
|
||||
List of child nodes that specify the regulators.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- regulators
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
pmic: pmic@62 {
|
||||
reg = <0x62>;
|
||||
compatible = "rohm,bd96802";
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <29 IRQ_TYPE_LEVEL_LOW>, <6 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupt-names = "intb", "errb";
|
||||
|
||||
regulators {
|
||||
buck1 {
|
||||
regulator-name = "buck1";
|
||||
regulator-ramp-delay = <1250>;
|
||||
/* 0.5V min INITIAL - 150 mV tune */
|
||||
regulator-min-microvolt = <350000>;
|
||||
/* 3.3V + 150mV tune */
|
||||
regulator-max-microvolt = <3450000>;
|
||||
|
||||
/* These can be set only when PMIC is in STBY */
|
||||
rohm,initial-voltage-microvolt = <500000>;
|
||||
regulator-ov-error-microvolt = <230000>;
|
||||
regulator-uv-error-microvolt = <230000>;
|
||||
regulator-temp-protection-kelvin = <1>;
|
||||
regulator-temp-warn-kelvin = <0>;
|
||||
};
|
||||
buck2 {
|
||||
regulator-name = "buck2";
|
||||
regulator-min-microvolt = <350000>;
|
||||
regulator-max-microvolt = <3450000>;
|
||||
|
||||
rohm,initial-voltage-microvolt = <3000000>;
|
||||
regulator-ov-error-microvolt = <18000>;
|
||||
regulator-uv-error-microvolt = <18000>;
|
||||
regulator-temp-protection-kelvin = <1>;
|
||||
regulator-temp-warn-kelvin = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -21,7 +21,12 @@ maintainers:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
const: st,stm32-lptimer
|
||||
oneOf:
|
||||
- items:
|
||||
- const: st,stm32mp25-lptimer
|
||||
- const: st,stm32-lptimer
|
||||
- items:
|
||||
- const: st,stm32-lptimer
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
@ -48,13 +53,21 @@ properties:
|
|||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
pwm:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: st,stm32-pwm-lp
|
||||
oneOf:
|
||||
- items:
|
||||
- const: st,stm32mp25-pwm-lp
|
||||
- const: st,stm32-pwm-lp
|
||||
- items:
|
||||
- const: st,stm32-pwm-lp
|
||||
|
||||
"#pwm-cells":
|
||||
const: 3
|
||||
|
|
@ -69,7 +82,12 @@ properties:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
const: st,stm32-lptimer-counter
|
||||
oneOf:
|
||||
- items:
|
||||
- const: st,stm32mp25-lptimer-counter
|
||||
- const: st,stm32-lptimer-counter
|
||||
- items:
|
||||
- const: st,stm32-lptimer-counter
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
|
@ -80,7 +98,12 @@ properties:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
const: st,stm32-lptimer-timer
|
||||
oneOf:
|
||||
- items:
|
||||
- const: st,stm32mp25-lptimer-timer
|
||||
- const: st,stm32-lptimer-timer
|
||||
- items:
|
||||
- const: st,stm32-lptimer-timer
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
|
@ -92,13 +115,18 @@ patternProperties:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
const: st,stm32-lptimer-trigger
|
||||
oneOf:
|
||||
- items:
|
||||
- const: st,stm32mp25-lptimer-trigger
|
||||
- const: st,stm32-lptimer-trigger
|
||||
- items:
|
||||
- const: st,stm32-lptimer-trigger
|
||||
|
||||
reg:
|
||||
description: Identify trigger hardware block.
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 2
|
||||
maximum: 4
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/nvmem/maxim,max77759-nvmem.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Maxim Integrated MAX77759 Non Volatile Memory
|
||||
|
||||
maintainers:
|
||||
- André Draszik <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
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/regulator/brcm,bcm59054.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Broadcom BCM59054 Power Management Unit regulators
|
||||
|
||||
description: |
|
||||
This is a part of device tree bindings for the BCM59054 power
|
||||
management unit.
|
||||
|
||||
See Documentation/devicetree/bindings/mfd/brcm,bcm59056.yaml for
|
||||
additional information and example.
|
||||
|
||||
maintainers:
|
||||
- Artur Weber <aweber.kernel@gmail.com>
|
||||
|
||||
patternProperties:
|
||||
"^(cam|sim|mmc)ldo[1-2]$":
|
||||
type: object
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
"^(rf|sd|sdx|aud|mic|usb|vib|tcx)ldo$":
|
||||
type: object
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
"^(c|mm|v)sr$":
|
||||
type: object
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
"^(io|sd)sr[1-2]$":
|
||||
type: object
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
"^gpldo[1-3]$":
|
||||
type: object
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
"^lvldo[1-2]$":
|
||||
type: object
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
vbus:
|
||||
type: object
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
additionalProperties: false
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/regulator/brcm,bcm59056.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Broadcom BCM59056 Power Management Unit regulators
|
||||
|
||||
description: |
|
||||
This is a part of device tree bindings for the BCM59056 power
|
||||
management unit.
|
||||
|
||||
See Documentation/devicetree/bindings/mfd/brcm,bcm59056.yaml for
|
||||
additional information and example.
|
||||
|
||||
maintainers:
|
||||
- Artur Weber <aweber.kernel@gmail.com>
|
||||
|
||||
patternProperties:
|
||||
"^(cam|sim|mmc)ldo[1-2]$":
|
||||
type: object
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
"^(rf|sd|sdx|aud|mic|usb|vib)ldo$":
|
||||
type: object
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
"^(c|m|v)sr$":
|
||||
type: object
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
"^(io|sd)sr[1-2]$":
|
||||
type: object
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
"^gpldo[1-6]$":
|
||||
type: object
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
vbus:
|
||||
type: object
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
additionalProperties: false
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/regulator/rohm,bd96802-regulator.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ROHM BD96802 Power Management Integrated Circuit regulators
|
||||
|
||||
maintainers:
|
||||
- Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
|
||||
|
||||
description:
|
||||
This module is part of the ROHM BD96802 MFD device. For more details
|
||||
see Documentation/devicetree/bindings/mfd/rohm,bd96802-pmic.yaml.
|
||||
|
||||
The regulator controller is represented as a sub-node of the PMIC node
|
||||
on the device tree.
|
||||
|
||||
Regulator nodes should be named to buck1 and buck2.
|
||||
|
||||
patternProperties:
|
||||
"^buck[1-2]$":
|
||||
type: object
|
||||
description:
|
||||
Properties for single BUCK regulator.
|
||||
$ref: regulator.yaml#
|
||||
|
||||
properties:
|
||||
rohm,initial-voltage-microvolt:
|
||||
description:
|
||||
Initial voltage for regulator. Voltage can be tuned +/-150 mV from
|
||||
this value. NOTE, This can be modified via I2C only when PMIC is in
|
||||
STBY state.
|
||||
minimum: 500000
|
||||
maximum: 3300000
|
||||
|
||||
rohm,keep-on-stby:
|
||||
description:
|
||||
Keep the regulator powered when PMIC transitions to STBY state.
|
||||
type: boolean
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
additionalProperties: false
|
||||
11
MAINTAINERS
11
MAINTAINERS
|
|
@ -14575,6 +14575,16 @@ 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
|
||||
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
|
||||
M: Javier Martinez Canillas <javier@dowhile0.org>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
|
|
@ -21000,6 +21010,7 @@ F: include/linux/mfd/rohm-bd71828.h
|
|||
F: include/linux/mfd/rohm-bd718x7.h
|
||||
F: include/linux/mfd/rohm-bd957x.h
|
||||
F: include/linux/mfd/rohm-bd96801.h
|
||||
F: include/linux/mfd/rohm-bd96802.h
|
||||
F: include/linux/mfd/rohm-generic.h
|
||||
F: include/linux/mfd/rohm-shared.h
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
* Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
|
@ -27,6 +28,7 @@ struct stm32_lp_private {
|
|||
u32 psc;
|
||||
struct device *dev;
|
||||
struct clk *clk;
|
||||
u32 version;
|
||||
};
|
||||
|
||||
static struct stm32_lp_private*
|
||||
|
|
@ -47,12 +49,46 @@ static int stm32_clkevent_lp_shutdown(struct clock_event_device *clkevt)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_clkevent_lp_set_timer(unsigned long evt,
|
||||
struct clock_event_device *clkevt,
|
||||
int is_periodic)
|
||||
static int stm32mp25_clkevent_lp_set_evt(struct stm32_lp_private *priv, unsigned long evt)
|
||||
{
|
||||
struct stm32_lp_private *priv = to_priv(clkevt);
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
regmap_read(priv->reg, STM32_LPTIM_CR, &val);
|
||||
if (!FIELD_GET(STM32_LPTIM_ENABLE, val)) {
|
||||
/* Enable LPTIMER to be able to write into IER and ARR registers */
|
||||
regmap_write(priv->reg, STM32_LPTIM_CR, STM32_LPTIM_ENABLE);
|
||||
/*
|
||||
* After setting the ENABLE bit, a delay of two counter clock cycles is needed
|
||||
* before the LPTIM is actually enabled. For 32KHz rate, this makes approximately
|
||||
* 62.5 micro-seconds, round it up.
|
||||
*/
|
||||
udelay(63);
|
||||
}
|
||||
/* set next event counter */
|
||||
regmap_write(priv->reg, STM32_LPTIM_ARR, evt);
|
||||
/* enable ARR interrupt */
|
||||
regmap_write(priv->reg, STM32_LPTIM_IER, STM32_LPTIM_ARRMIE);
|
||||
|
||||
/* Poll DIEROK and ARROK to ensure register access has completed */
|
||||
ret = regmap_read_poll_timeout_atomic(priv->reg, STM32_LPTIM_ISR, val,
|
||||
(val & STM32_LPTIM_DIEROK_ARROK) ==
|
||||
STM32_LPTIM_DIEROK_ARROK,
|
||||
10, 500);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "access to LPTIM timed out\n");
|
||||
/* Disable LPTIMER */
|
||||
regmap_write(priv->reg, STM32_LPTIM_CR, 0);
|
||||
return ret;
|
||||
}
|
||||
/* Clear DIEROK and ARROK flags */
|
||||
regmap_write(priv->reg, STM32_LPTIM_ICR, STM32_LPTIM_DIEROKCF_ARROKCF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32_clkevent_lp_set_evt(struct stm32_lp_private *priv, unsigned long evt)
|
||||
{
|
||||
/* disable LPTIMER to be able to write into IER register*/
|
||||
regmap_write(priv->reg, STM32_LPTIM_CR, 0);
|
||||
/* enable ARR interrupt */
|
||||
|
|
@ -61,6 +97,22 @@ static int stm32_clkevent_lp_set_timer(unsigned long evt,
|
|||
regmap_write(priv->reg, STM32_LPTIM_CR, STM32_LPTIM_ENABLE);
|
||||
/* set next event counter */
|
||||
regmap_write(priv->reg, STM32_LPTIM_ARR, evt);
|
||||
}
|
||||
|
||||
static int stm32_clkevent_lp_set_timer(unsigned long evt,
|
||||
struct clock_event_device *clkevt,
|
||||
int is_periodic)
|
||||
{
|
||||
struct stm32_lp_private *priv = to_priv(clkevt);
|
||||
int ret;
|
||||
|
||||
if (priv->version == STM32_LPTIM_VERR_23) {
|
||||
ret = stm32mp25_clkevent_lp_set_evt(priv, evt);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
stm32_clkevent_lp_set_evt(priv, evt);
|
||||
}
|
||||
|
||||
/* start counter */
|
||||
if (is_periodic)
|
||||
|
|
@ -176,6 +228,7 @@ static int stm32_clkevent_lp_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
priv->reg = ddata->regmap;
|
||||
priv->version = ddata->version;
|
||||
priv->clk = ddata->clk;
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret)
|
||||
|
|
|
|||
|
|
@ -1463,6 +1463,19 @@ config GPIO_MAX77650
|
|||
GPIO driver for MAX77650/77651 PMIC from Maxim Semiconductor.
|
||||
These chips have a single pin that can be configured as GPIO.
|
||||
|
||||
config GPIO_MAX77759
|
||||
tristate "Maxim Integrated MAX77759 GPIO support"
|
||||
depends on MFD_MAX77759
|
||||
default MFD_MAX77759
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
GPIO driver for MAX77759 PMIC from Maxim Integrated.
|
||||
There are two GPIOs available on these chips in total, both of
|
||||
which can also generate interrupts.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called gpio-max77759.
|
||||
|
||||
config GPIO_PALMAS
|
||||
bool "TI PALMAS series PMICs GPIO"
|
||||
depends on MFD_PALMAS
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o
|
|||
obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o
|
||||
obj-$(CONFIG_GPIO_MAX77620) += gpio-max77620.o
|
||||
obj-$(CONFIG_GPIO_MAX77650) += gpio-max77650.o
|
||||
obj-$(CONFIG_GPIO_MAX77759) += gpio-max77759.o
|
||||
obj-$(CONFIG_GPIO_MB86S7X) += gpio-mb86s7x.o
|
||||
obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o
|
||||
obj-$(CONFIG_GPIO_MENZ127) += gpio-menz127.o
|
||||
|
|
|
|||
530
drivers/gpio/gpio-max77759.c
Normal file
530
drivers/gpio/gpio-max77759.c
Normal file
|
|
@ -0,0 +1,530 @@
|
|||
// 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 *max77759;
|
||||
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, 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, 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, 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, cmd, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
max77759_gpio_direction_from_control(int ctrl, unsigned int offset)
|
||||
{
|
||||
enum max77759_control_gpio_dir dir;
|
||||
|
||||
dir = !!(ctrl & MAX77759_GPIOx_DIR_MASK(offset));
|
||||
return ((dir == MAX77759_GPIO_DIR_OUT)
|
||||
? GPIO_LINE_DIRECTION_OUT
|
||||
: GPIO_LINE_DIRECTION_IN);
|
||||
}
|
||||
|
||||
static int max77759_gpio_get_direction(struct gpio_chip *gc,
|
||||
unsigned int offset)
|
||||
{
|
||||
struct max77759_gpio_chip *chip = gpiochip_get_data(gc);
|
||||
int ctrl;
|
||||
|
||||
ctrl = max77759_gpio_maxq_gpio_control_read(chip);
|
||||
if (ctrl < 0)
|
||||
return ctrl;
|
||||
|
||||
return max77759_gpio_direction_from_control(ctrl, offset);
|
||||
}
|
||||
|
||||
static int max77759_gpio_direction_helper(struct gpio_chip *gc,
|
||||
unsigned int offset,
|
||||
enum max77759_control_gpio_dir dir,
|
||||
int value)
|
||||
{
|
||||
struct max77759_gpio_chip *chip = gpiochip_get_data(gc);
|
||||
int ctrl, new_ctrl;
|
||||
|
||||
guard(mutex)(&chip->maxq_lock);
|
||||
|
||||
ctrl = max77759_gpio_maxq_gpio_control_read(chip);
|
||||
if (ctrl < 0)
|
||||
return ctrl;
|
||||
|
||||
new_ctrl = ctrl & ~MAX77759_GPIOx_DIR_MASK(offset);
|
||||
new_ctrl |= MAX77759_GPIOx_DIR(offset, dir);
|
||||
|
||||
if (dir == MAX77759_GPIO_DIR_OUT) {
|
||||
new_ctrl &= ~MAX77759_GPIOx_OUTVAL_MASK(offset);
|
||||
new_ctrl |= MAX77759_GPIOx_OUTVAL(offset, value);
|
||||
}
|
||||
|
||||
if (new_ctrl == ctrl)
|
||||
return 0;
|
||||
|
||||
return max77759_gpio_maxq_gpio_control_write(chip, new_ctrl);
|
||||
}
|
||||
|
||||
static int max77759_gpio_direction_input(struct gpio_chip *gc,
|
||||
unsigned int offset)
|
||||
{
|
||||
return max77759_gpio_direction_helper(gc, offset,
|
||||
MAX77759_GPIO_DIR_IN, -1);
|
||||
}
|
||||
|
||||
static int max77759_gpio_direction_output(struct gpio_chip *gc,
|
||||
unsigned int offset, int value)
|
||||
{
|
||||
return max77759_gpio_direction_helper(gc, offset,
|
||||
MAX77759_GPIO_DIR_OUT, value);
|
||||
}
|
||||
|
||||
static int max77759_gpio_get_value(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
struct max77759_gpio_chip *chip = gpiochip_get_data(gc);
|
||||
int ctrl, mask;
|
||||
|
||||
ctrl = max77759_gpio_maxq_gpio_control_read(chip);
|
||||
if (ctrl < 0)
|
||||
return ctrl;
|
||||
|
||||
/*
|
||||
* The input status bit doesn't reflect the pin state when the GPIO is
|
||||
* configured as an output. Check the direction, and inspect the input
|
||||
* or output bit accordingly.
|
||||
*/
|
||||
mask = ((max77759_gpio_direction_from_control(ctrl, offset)
|
||||
== GPIO_LINE_DIRECTION_IN)
|
||||
? MAX77759_GPIOx_INVAL_MASK(offset)
|
||||
: MAX77759_GPIOx_OUTVAL_MASK(offset));
|
||||
|
||||
return !!(ctrl & mask);
|
||||
}
|
||||
|
||||
static int 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 ctrl;
|
||||
|
||||
new_ctrl = 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 void max77759_gpio_irq_mask(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct max77759_gpio_chip *chip = gpiochip_get_data(gc);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
|
||||
chip->irq_mask &= ~MAX77759_MAXQ_REG_UIC_INT1_GPIOxI_MASK(hwirq);
|
||||
chip->irq_mask |= MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 1);
|
||||
chip->irq_mask_changed |= MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 1);
|
||||
|
||||
gpiochip_disable_irq(gc, hwirq);
|
||||
}
|
||||
|
||||
static void max77759_gpio_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct max77759_gpio_chip *chip = gpiochip_get_data(gc);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
|
||||
gpiochip_enable_irq(gc, hwirq);
|
||||
|
||||
chip->irq_mask &= ~MAX77759_MAXQ_REG_UIC_INT1_GPIOxI_MASK(hwirq);
|
||||
chip->irq_mask |= MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 0);
|
||||
chip->irq_mask_changed |= MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 1);
|
||||
}
|
||||
|
||||
static int max77759_gpio_set_irq_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct max77759_gpio_chip *chip = gpiochip_get_data(gc);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
|
||||
chip->irq_trig &= ~MAX77759_GPIOx_TRIGGER_MASK(hwirq);
|
||||
switch (type) {
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
chip->irq_trig |= MAX77759_GPIOx_TRIGGER(hwirq,
|
||||
MAX77759_GPIO_TRIGGER_RISING);
|
||||
break;
|
||||
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
chip->irq_trig |= MAX77759_GPIOx_TRIGGER(hwirq,
|
||||
MAX77759_GPIO_TRIGGER_FALLING);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip->irq_trig_changed |= MAX77759_GPIOx_TRIGGER(hwirq, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void max77759_gpio_bus_lock(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct max77759_gpio_chip *chip = gpiochip_get_data(gc);
|
||||
|
||||
mutex_lock(&chip->irq_lock);
|
||||
}
|
||||
|
||||
static int max77759_gpio_bus_sync_unlock_helper(struct gpio_chip *gc,
|
||||
struct max77759_gpio_chip *chip)
|
||||
__must_hold(&chip->maxq_lock)
|
||||
{
|
||||
int ctrl, trigger, new_trigger, new_ctrl;
|
||||
unsigned long irq_trig_changed;
|
||||
int offset;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&chip->maxq_lock);
|
||||
|
||||
ctrl = max77759_gpio_maxq_gpio_control_read(chip);
|
||||
trigger = max77759_gpio_maxq_gpio_trigger_read(chip);
|
||||
if (ctrl < 0 || trigger < 0) {
|
||||
dev_err(gc->parent, "failed to read current state: %d / %d\n",
|
||||
ctrl, trigger);
|
||||
return (ctrl < 0) ? ctrl : trigger;
|
||||
}
|
||||
|
||||
new_trigger = trigger & ~chip->irq_trig_changed;
|
||||
new_trigger |= (chip->irq_trig & chip->irq_trig_changed);
|
||||
|
||||
/* change GPIO direction if required */
|
||||
new_ctrl = ctrl;
|
||||
irq_trig_changed = chip->irq_trig_changed;
|
||||
for_each_set_bit(offset, &irq_trig_changed, MAX77759_N_GPIOS) {
|
||||
new_ctrl &= ~MAX77759_GPIOx_DIR_MASK(offset);
|
||||
new_ctrl |= MAX77759_GPIOx_DIR(offset, MAX77759_GPIO_DIR_IN);
|
||||
}
|
||||
|
||||
if (new_trigger != trigger) {
|
||||
ret = max77759_gpio_maxq_gpio_trigger_write(chip, new_trigger);
|
||||
if (ret) {
|
||||
dev_err(gc->parent,
|
||||
"failed to write new trigger: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_ctrl != ctrl) {
|
||||
ret = max77759_gpio_maxq_gpio_control_write(chip, new_ctrl);
|
||||
if (ret) {
|
||||
dev_err(gc->parent,
|
||||
"failed to write new control: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
chip->irq_trig_changed = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void max77759_gpio_bus_sync_unlock(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct max77759_gpio_chip *chip = gpiochip_get_data(gc);
|
||||
int ret;
|
||||
|
||||
scoped_guard(mutex, &chip->maxq_lock) {
|
||||
ret = max77759_gpio_bus_sync_unlock_helper(gc, chip);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(chip->map,
|
||||
MAX77759_MAXQ_REG_UIC_INT1_M,
|
||||
chip->irq_mask_changed, chip->irq_mask);
|
||||
if (ret) {
|
||||
dev_err(gc->parent,
|
||||
"failed to update UIC_INT1 irq mask: %d\n", ret);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
chip->irq_mask_changed = 0;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&chip->irq_lock);
|
||||
}
|
||||
|
||||
static void max77759_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
|
||||
seq_puts(p, dev_name(gc->parent));
|
||||
}
|
||||
|
||||
static const struct irq_chip max77759_gpio_irq_chip = {
|
||||
.irq_mask = max77759_gpio_irq_mask,
|
||||
.irq_unmask = max77759_gpio_irq_unmask,
|
||||
.irq_set_type = max77759_gpio_set_irq_type,
|
||||
.irq_bus_lock = max77759_gpio_bus_lock,
|
||||
.irq_bus_sync_unlock = max77759_gpio_bus_sync_unlock,
|
||||
.irq_print_chip = max77759_gpio_irq_print_chip,
|
||||
.flags = IRQCHIP_IMMUTABLE,
|
||||
GPIOCHIP_IRQ_RESOURCE_HELPERS,
|
||||
};
|
||||
|
||||
static irqreturn_t max77759_gpio_irqhandler(int irq, void *data)
|
||||
{
|
||||
struct max77759_gpio_chip *chip = data;
|
||||
struct gpio_chip *gc = &chip->gc;
|
||||
bool handled = false;
|
||||
|
||||
/* iterate until no interrupt is pending */
|
||||
while (true) {
|
||||
unsigned int uic_int1;
|
||||
int ret;
|
||||
unsigned long pending;
|
||||
int offset;
|
||||
|
||||
ret = regmap_read(chip->map, MAX77759_MAXQ_REG_UIC_INT1,
|
||||
&uic_int1);
|
||||
if (ret < 0) {
|
||||
dev_err_ratelimited(gc->parent,
|
||||
"failed to read IRQ status: %d\n",
|
||||
ret);
|
||||
/*
|
||||
* If !handled, we have looped not even once, which
|
||||
* means we should return IRQ_NONE in that case (and
|
||||
* of course IRQ_HANDLED otherwise).
|
||||
*/
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
pending = uic_int1;
|
||||
pending &= (MAX77759_MAXQ_REG_UIC_INT1_GPIO6I
|
||||
| MAX77759_MAXQ_REG_UIC_INT1_GPIO5I);
|
||||
if (!pending)
|
||||
break;
|
||||
|
||||
for_each_set_bit(offset, &pending, MAX77759_N_GPIOS) {
|
||||
/*
|
||||
* ACK interrupt by writing 1 to bit 'offset', all
|
||||
* others need to be written as 0. This needs to be
|
||||
* done unconditionally hence regmap_set_bits() is
|
||||
* inappropriate here.
|
||||
*/
|
||||
regmap_write(chip->map, MAX77759_MAXQ_REG_UIC_INT1,
|
||||
BIT(offset));
|
||||
|
||||
handle_nested_irq(irq_find_mapping(gc->irq.domain,
|
||||
offset));
|
||||
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
static int max77759_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct max77759_gpio_chip *chip;
|
||||
int irq;
|
||||
struct gpio_irq_chip *girq;
|
||||
int ret;
|
||||
unsigned long irq_flags;
|
||||
struct irq_data *irqd;
|
||||
|
||||
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->map = dev_get_regmap(pdev->dev.parent, "maxq");
|
||||
if (!chip->map)
|
||||
return dev_err_probe(&pdev->dev, -ENODEV, "Missing regmap\n");
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "GPI");
|
||||
if (irq < 0)
|
||||
return dev_err_probe(&pdev->dev, irq, "Failed to get IRQ\n");
|
||||
|
||||
chip->max77759 = dev_get_drvdata(pdev->dev.parent);
|
||||
ret = devm_mutex_init(&pdev->dev, &chip->maxq_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = devm_mutex_init(&pdev->dev, &chip->irq_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
chip->gc.base = -1;
|
||||
chip->gc.label = dev_name(&pdev->dev);
|
||||
chip->gc.parent = &pdev->dev;
|
||||
chip->gc.can_sleep = true;
|
||||
|
||||
chip->gc.names = max77759_gpio_line_names;
|
||||
chip->gc.ngpio = MAX77759_N_GPIOS;
|
||||
chip->gc.get_direction = max77759_gpio_get_direction;
|
||||
chip->gc.direction_input = max77759_gpio_direction_input;
|
||||
chip->gc.direction_output = max77759_gpio_direction_output;
|
||||
chip->gc.get = max77759_gpio_get_value;
|
||||
chip->gc.set_rv = 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 const struct platform_device_id max77759_gpio_platform_id[] = {
|
||||
{ "max77759-gpio", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, max77759_gpio_platform_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,
|
||||
.id_table = max77759_gpio_platform_id,
|
||||
};
|
||||
|
||||
module_platform_driver(max77759_gpio_driver);
|
||||
|
||||
MODULE_AUTHOR("André Draszik <andre.draszik@linaro.org>");
|
||||
MODULE_DESCRIPTION("GPIO driver for Maxim MAX77759");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -943,6 +943,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
|
||||
|
|
|
|||
|
|
@ -169,6 +169,7 @@ obj-$(CONFIG_MFD_MAX77686) += max77686.o
|
|||
obj-$(CONFIG_MFD_MAX77693) += max77693.o
|
||||
obj-$(CONFIG_MFD_MAX77705) += max77705.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
|
||||
|
|
|
|||
|
|
@ -17,6 +17,15 @@
|
|||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* Under primary I2C address: */
|
||||
#define BCM590XX_REG_PMUID 0x1e
|
||||
|
||||
#define BCM590XX_REG_PMUREV 0x1f
|
||||
#define BCM590XX_PMUREV_DIG_MASK 0xF
|
||||
#define BCM590XX_PMUREV_DIG_SHIFT 0
|
||||
#define BCM590XX_PMUREV_ANA_MASK 0xF0
|
||||
#define BCM590XX_PMUREV_ANA_SHIFT 4
|
||||
|
||||
static const struct mfd_cell bcm590xx_devs[] = {
|
||||
{
|
||||
.name = "bcm590xx-vregs",
|
||||
|
|
@ -37,6 +46,47 @@ static const struct regmap_config bcm590xx_regmap_config_sec = {
|
|||
.cache_type = REGCACHE_MAPLE,
|
||||
};
|
||||
|
||||
/* Map PMU ID value to model name string */
|
||||
static const char * const bcm590xx_names[] = {
|
||||
[BCM590XX_PMUID_BCM59054] = "BCM59054",
|
||||
[BCM590XX_PMUID_BCM59056] = "BCM59056",
|
||||
};
|
||||
|
||||
static int bcm590xx_parse_version(struct bcm590xx *bcm590xx)
|
||||
{
|
||||
unsigned int id, rev;
|
||||
int ret;
|
||||
|
||||
/* Get PMU ID and verify that it matches compatible */
|
||||
ret = regmap_read(bcm590xx->regmap_pri, BCM590XX_REG_PMUID, &id);
|
||||
if (ret) {
|
||||
dev_err(bcm590xx->dev, "failed to read PMU ID: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (id != bcm590xx->pmu_id) {
|
||||
dev_err(bcm590xx->dev, "Incorrect ID for %s: expected %x, got %x.\n",
|
||||
bcm590xx_names[bcm590xx->pmu_id], bcm590xx->pmu_id, id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Get PMU revision and store it in the info struct */
|
||||
ret = regmap_read(bcm590xx->regmap_pri, BCM590XX_REG_PMUREV, &rev);
|
||||
if (ret) {
|
||||
dev_err(bcm590xx->dev, "failed to read PMU revision: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bcm590xx->rev_digital = (rev & BCM590XX_PMUREV_DIG_MASK) >> BCM590XX_PMUREV_DIG_SHIFT;
|
||||
|
||||
bcm590xx->rev_analog = (rev & BCM590XX_PMUREV_ANA_MASK) >> BCM590XX_PMUREV_ANA_SHIFT;
|
||||
|
||||
dev_dbg(bcm590xx->dev, "PMU ID 0x%x (%s), revision: digital %d, analog %d",
|
||||
id, bcm590xx_names[id], bcm590xx->rev_digital, bcm590xx->rev_analog);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm590xx_i2c_probe(struct i2c_client *i2c_pri)
|
||||
{
|
||||
struct bcm590xx *bcm590xx;
|
||||
|
|
@ -50,6 +100,8 @@ static int bcm590xx_i2c_probe(struct i2c_client *i2c_pri)
|
|||
bcm590xx->dev = &i2c_pri->dev;
|
||||
bcm590xx->i2c_pri = i2c_pri;
|
||||
|
||||
bcm590xx->pmu_id = (uintptr_t) of_device_get_match_data(bcm590xx->dev);
|
||||
|
||||
bcm590xx->regmap_pri = devm_regmap_init_i2c(i2c_pri,
|
||||
&bcm590xx_regmap_config_pri);
|
||||
if (IS_ERR(bcm590xx->regmap_pri)) {
|
||||
|
|
@ -76,6 +128,10 @@ static int bcm590xx_i2c_probe(struct i2c_client *i2c_pri)
|
|||
goto err;
|
||||
}
|
||||
|
||||
ret = bcm590xx_parse_version(bcm590xx);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = devm_mfd_add_devices(&i2c_pri->dev, -1, bcm590xx_devs,
|
||||
ARRAY_SIZE(bcm590xx_devs), NULL, 0, NULL);
|
||||
if (ret < 0) {
|
||||
|
|
@ -91,12 +147,20 @@ err:
|
|||
}
|
||||
|
||||
static const struct of_device_id bcm590xx_of_match[] = {
|
||||
{ .compatible = "brcm,bcm59056" },
|
||||
{
|
||||
.compatible = "brcm,bcm59054",
|
||||
.data = (void *)BCM590XX_PMUID_BCM59054,
|
||||
},
|
||||
{
|
||||
.compatible = "brcm,bcm59056",
|
||||
.data = (void *)BCM590XX_PMUID_BCM59056,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bcm590xx_of_match);
|
||||
|
||||
static const struct i2c_device_id bcm590xx_i2c_id[] = {
|
||||
{ "bcm59054" },
|
||||
{ "bcm59056" },
|
||||
{ }
|
||||
};
|
||||
|
|
|
|||
690
drivers/mfd/max77759.c
Normal file
690
drivers/mfd/max77759.c
Normal file
|
|
@ -0,0 +1,690 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright 2020 Google Inc
|
||||
* Copyright 2025 Linaro Ltd.
|
||||
*
|
||||
* Core 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>
|
||||
|
||||
/* Chip ID as per MAX77759_PMIC_REG_PMIC_ID */
|
||||
enum {
|
||||
MAX77759_CHIP_ID = 59,
|
||||
};
|
||||
|
||||
enum max77759_i2c_subdev_id {
|
||||
/*
|
||||
* These are arbitrary and simply used to match struct
|
||||
* max77759_i2c_subdev entries to the regmap pointers in struct
|
||||
* max77759 during probe().
|
||||
*/
|
||||
MAX77759_I2C_SUBDEV_ID_MAXQ,
|
||||
MAX77759_I2C_SUBDEV_ID_CHARGER,
|
||||
};
|
||||
|
||||
struct max77759_i2c_subdev {
|
||||
enum max77759_i2c_subdev_id id;
|
||||
const struct regmap_config *cfg;
|
||||
u16 i2c_address;
|
||||
};
|
||||
|
||||
static const struct regmap_range max77759_top_registers[] = {
|
||||
regmap_reg_range(0x00, 0x02), /* PMIC_ID / PMIC_REVISION / OTP_REVISION */
|
||||
regmap_reg_range(0x22, 0x24), /* INTSRC / INTSRCMASK / TOPSYS_INT */
|
||||
regmap_reg_range(0x26, 0x26), /* TOPSYS_INT_MASK */
|
||||
regmap_reg_range(0x40, 0x40), /* I2C_CNFG */
|
||||
regmap_reg_range(0x50, 0x51), /* SWRESET / CONTROL_FG */
|
||||
};
|
||||
|
||||
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_CONTROL_FG,
|
||||
.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_CONTROL_FG + 1,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static const struct regmap_range max77759_maxq_registers[] = {
|
||||
regmap_reg_range(0x60, 0x73), /* Device ID, Rev, INTx, STATUSx, MASKx */
|
||||
regmap_reg_range(0x81, 0xa1), /* AP_DATAOUTx */
|
||||
regmap_reg_range(0xb1, 0xd1), /* AP_DATAINx */
|
||||
regmap_reg_range(0xe0, 0xe0), /* UIC_SWRST */
|
||||
};
|
||||
|
||||
static const struct regmap_range max77759_maxq_ro_registers[] = {
|
||||
regmap_reg_range(0x60, 0x63), /* Device ID, Rev */
|
||||
regmap_reg_range(0x68, 0x6f), /* STATUSx */
|
||||
regmap_reg_range(0xb1, 0xd1),
|
||||
};
|
||||
|
||||
static const struct regmap_range max77759_maxq_volatile_registers[] = {
|
||||
regmap_reg_range(0x64, 0x6f), /* INTx, STATUSx */
|
||||
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_UIC_SWRST,
|
||||
.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_UIC_SWRST + 1,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
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), /* INT_OK, DETAILS_0x */
|
||||
};
|
||||
|
||||
static const struct regmap_range max77759_charger_volatile_registers[] = {
|
||||
regmap_reg_range(0xb0, 0xb1), /* INTx */
|
||||
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_CHG_CNFG_19,
|
||||
.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_CHG_CNFG_19 + 1,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
/*
|
||||
* Interrupts - with the following interrupt hierarchy:
|
||||
* pmic IRQs (INTSRC)
|
||||
* - MAXQ_INT: MaxQ IRQs
|
||||
* - UIC_INT1
|
||||
* - APCmdResI
|
||||
* - SysMsgI
|
||||
* - GPIOxI
|
||||
* - TOPSYS_INT: topsys
|
||||
* - TOPSYS_INT
|
||||
* - TSHDN_INT
|
||||
* - SYSOVLO_INT
|
||||
* - SYSUVLO_INT
|
||||
* - FSHIP_NOT_RD
|
||||
* - CHGR_INT: charger
|
||||
* - CHG_INT
|
||||
* - CHG_INT2
|
||||
*/
|
||||
enum {
|
||||
MAX77759_INT_MAXQ,
|
||||
MAX77759_INT_TOPSYS,
|
||||
MAX77759_INT_CHGR,
|
||||
};
|
||||
|
||||
enum {
|
||||
MAX77759_TOPSYS_INT_TSHDN,
|
||||
MAX77759_TOPSYS_INT_SYSOVLO,
|
||||
MAX77759_TOPSYS_INT_SYSUVLO,
|
||||
MAX77759_TOPSYS_INT_FSHIP_NOT_RD,
|
||||
};
|
||||
|
||||
enum {
|
||||
MAX77759_MAXQ_INT_APCMDRESI,
|
||||
MAX77759_MAXQ_INT_SYSMSGI,
|
||||
MAX77759_MAXQ_INT_GPIO,
|
||||
MAX77759_MAXQ_INT_UIC1,
|
||||
MAX77759_MAXQ_INT_UIC2,
|
||||
MAX77759_MAXQ_INT_UIC3,
|
||||
MAX77759_MAXQ_INT_UIC4,
|
||||
};
|
||||
|
||||
enum {
|
||||
MAX77759_CHARGER_INT_1,
|
||||
MAX77759_CHARGER_INT_2,
|
||||
};
|
||||
|
||||
static const struct regmap_irq max77759_pmic_irqs[] = {
|
||||
REGMAP_IRQ_REG(MAX77759_INT_MAXQ, 0, MAX77759_PMIC_REG_INTSRC_MAXQ),
|
||||
REGMAP_IRQ_REG(MAX77759_INT_TOPSYS, 0, MAX77759_PMIC_REG_INTSRC_TOPSYS),
|
||||
REGMAP_IRQ_REG(MAX77759_INT_CHGR, 0, MAX77759_PMIC_REG_INTSRC_CHGR),
|
||||
};
|
||||
|
||||
static const struct regmap_irq max77759_maxq_irqs[] = {
|
||||
REGMAP_IRQ_REG(MAX77759_MAXQ_INT_APCMDRESI, 0, MAX77759_MAXQ_REG_UIC_INT1_APCMDRESI),
|
||||
REGMAP_IRQ_REG(MAX77759_MAXQ_INT_SYSMSGI, 0, MAX77759_MAXQ_REG_UIC_INT1_SYSMSGI),
|
||||
REGMAP_IRQ_REG(MAX77759_MAXQ_INT_GPIO, 0, GENMASK(1, 0)),
|
||||
REGMAP_IRQ_REG(MAX77759_MAXQ_INT_UIC1, 0, GENMASK(5, 2)),
|
||||
REGMAP_IRQ_REG(MAX77759_MAXQ_INT_UIC2, 1, GENMASK(7, 0)),
|
||||
REGMAP_IRQ_REG(MAX77759_MAXQ_INT_UIC3, 2, GENMASK(7, 0)),
|
||||
REGMAP_IRQ_REG(MAX77759_MAXQ_INT_UIC4, 3, GENMASK(7, 0)),
|
||||
};
|
||||
|
||||
static const struct regmap_irq max77759_topsys_irqs[] = {
|
||||
REGMAP_IRQ_REG(MAX77759_TOPSYS_INT_TSHDN, 0, MAX77759_PMIC_REG_TOPSYS_INT_TSHDN),
|
||||
REGMAP_IRQ_REG(MAX77759_TOPSYS_INT_SYSOVLO, 0, MAX77759_PMIC_REG_TOPSYS_INT_SYSOVLO),
|
||||
REGMAP_IRQ_REG(MAX77759_TOPSYS_INT_SYSUVLO, 0, MAX77759_PMIC_REG_TOPSYS_INT_SYSUVLO),
|
||||
REGMAP_IRQ_REG(MAX77759_TOPSYS_INT_FSHIP_NOT_RD, 0, MAX77759_PMIC_REG_TOPSYS_INT_FSHIP),
|
||||
};
|
||||
|
||||
static const struct regmap_irq max77759_chgr_irqs[] = {
|
||||
REGMAP_IRQ_REG(MAX77759_CHARGER_INT_1, 0, GENMASK(7, 0)),
|
||||
REGMAP_IRQ_REG(MAX77759_CHARGER_INT_2, 1, GENMASK(7, 0)),
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip max77759_pmic_irq_chip = {
|
||||
.name = "max77759-pmic",
|
||||
/* INTSRC is read-only and doesn't require clearing */
|
||||
.status_base = MAX77759_PMIC_REG_INTSRC,
|
||||
.mask_base = MAX77759_PMIC_REG_INTSRCMASK,
|
||||
.num_regs = 1,
|
||||
.irqs = max77759_pmic_irqs,
|
||||
.num_irqs = ARRAY_SIZE(max77759_pmic_irqs),
|
||||
};
|
||||
|
||||
/*
|
||||
* We can let regmap-irq auto-ack the topsys interrupt bits as required, but
|
||||
* for all others the individual drivers need to know which interrupt bit
|
||||
* exactly is set inside their interrupt handlers, and therefore we can not set
|
||||
* .ack_base for those.
|
||||
*/
|
||||
static const struct regmap_irq_chip max77759_maxq_irq_chip = {
|
||||
.name = "max77759-maxq",
|
||||
.domain_suffix = "MAXQ",
|
||||
.status_base = MAX77759_MAXQ_REG_UIC_INT1,
|
||||
.mask_base = MAX77759_MAXQ_REG_UIC_INT1_M,
|
||||
.num_regs = 4,
|
||||
.irqs = max77759_maxq_irqs,
|
||||
.num_irqs = ARRAY_SIZE(max77759_maxq_irqs),
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip max77759_topsys_irq_chip = {
|
||||
.name = "max77759-topsys",
|
||||
.domain_suffix = "TOPSYS",
|
||||
.status_base = MAX77759_PMIC_REG_TOPSYS_INT,
|
||||
.mask_base = MAX77759_PMIC_REG_TOPSYS_INT_MASK,
|
||||
.ack_base = MAX77759_PMIC_REG_TOPSYS_INT,
|
||||
.num_regs = 1,
|
||||
.irqs = max77759_topsys_irqs,
|
||||
.num_irqs = ARRAY_SIZE(max77759_topsys_irqs),
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip max77759_chrg_irq_chip = {
|
||||
.name = "max77759-chgr",
|
||||
.domain_suffix = "CHGR",
|
||||
.status_base = MAX77759_CHGR_REG_CHG_INT,
|
||||
.mask_base = MAX77759_CHGR_REG_CHG_INT_MASK,
|
||||
.num_regs = 2,
|
||||
.irqs = max77759_chgr_irqs,
|
||||
.num_irqs = ARRAY_SIZE(max77759_chgr_irqs),
|
||||
};
|
||||
|
||||
static const struct max77759_i2c_subdev max77759_i2c_subdevs[] = {
|
||||
{
|
||||
.id = MAX77759_I2C_SUBDEV_ID_MAXQ,
|
||||
.cfg = &max77759_regmap_config_maxq,
|
||||
/* I2C address is same as for sub-block 'top' */
|
||||
},
|
||||
{
|
||||
.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 *max77759,
|
||||
const struct max77759_maxq_command *cmd,
|
||||
struct max77759_maxq_response *rsp)
|
||||
{
|
||||
DEFINE_FLEX(struct max77759_maxq_response, _rsp, rsp, length, 1);
|
||||
struct device *dev = regmap_get_device(max77759->regmap_maxq);
|
||||
static const unsigned int timeout_ms = 200;
|
||||
int ret;
|
||||
|
||||
if (cmd->length > MAX77759_MAXQ_OPCODE_MAXLENGTH)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* As a convenience for API users when issuing simple commands, rsp is
|
||||
* allowed to be NULL. In that case we need a temporary here to write
|
||||
* the response to, as we need to verify that the command was indeed
|
||||
* completed correctly.
|
||||
*/
|
||||
if (!rsp)
|
||||
rsp = _rsp;
|
||||
|
||||
if (!rsp->length || rsp->length > MAX77759_MAXQ_OPCODE_MAXLENGTH)
|
||||
return -EINVAL;
|
||||
|
||||
guard(mutex)(&max77759->maxq_lock);
|
||||
|
||||
reinit_completion(&max77759->cmd_done);
|
||||
|
||||
/*
|
||||
* MaxQ latches the message when the DATAOUT32 register is written. If
|
||||
* cmd->length is shorter we still need to write 0 to it.
|
||||
*/
|
||||
ret = regmap_bulk_write(max77759->regmap_maxq,
|
||||
MAX77759_MAXQ_REG_AP_DATAOUT0, cmd->cmd,
|
||||
cmd->length);
|
||||
if (!ret && cmd->length < MAX77759_MAXQ_OPCODE_MAXLENGTH)
|
||||
ret = regmap_write(max77759->regmap_maxq,
|
||||
MAX77759_MAXQ_REG_AP_DATAOUT32, 0);
|
||||
if (ret) {
|
||||
dev_err(dev, "writing command failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Wait for response from MaxQ */
|
||||
if (!wait_for_completion_timeout(&max77759->cmd_done,
|
||||
msecs_to_jiffies(timeout_ms))) {
|
||||
dev_err(dev, "timed out waiting for response\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
ret = regmap_bulk_read(max77759->regmap_maxq,
|
||||
MAX77759_MAXQ_REG_AP_DATAIN0,
|
||||
rsp->rsp, rsp->length);
|
||||
if (ret) {
|
||||
dev_err(dev, "reading response failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* As per the protocol, the first byte of the reply will match the
|
||||
* request.
|
||||
*/
|
||||
if (cmd->cmd[0] != rsp->rsp[0]) {
|
||||
dev_err(dev, "unexpected opcode response for %#.2x: %*ph\n",
|
||||
cmd->cmd[0], (int)rsp->length, rsp->rsp);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(max77759_maxq_command);
|
||||
|
||||
static irqreturn_t apcmdres_irq_handler(int irq, void *irq_data)
|
||||
{
|
||||
struct max77759 *max77759 = irq_data;
|
||||
|
||||
regmap_write(max77759->regmap_maxq, MAX77759_MAXQ_REG_UIC_INT1,
|
||||
MAX77759_MAXQ_REG_UIC_INT1_APCMDRESI);
|
||||
|
||||
complete(&max77759->cmd_done);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int max77759_create_i2c_subdev(struct i2c_client *client,
|
||||
struct max77759 *max77759,
|
||||
const struct max77759_i2c_subdev *sd)
|
||||
{
|
||||
struct i2c_client *sub;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If 'sd' has an I2C address, 'sub' will be assigned a new 'dummy'
|
||||
* device, otherwise use it as-is.
|
||||
*/
|
||||
sub = client;
|
||||
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);
|
||||
}
|
||||
|
||||
regmap = devm_regmap_init_i2c(sub, sd->cfg);
|
||||
if (IS_ERR(regmap))
|
||||
return dev_err_probe(&sub->dev, PTR_ERR(regmap),
|
||||
"regmap init for '%s' failed\n",
|
||||
sd->cfg->name);
|
||||
|
||||
ret = regmap_attach_dev(&client->dev, regmap, sd->cfg);
|
||||
if (ret)
|
||||
return dev_err_probe(&client->dev, ret,
|
||||
"regmap attach of '%s' failed\n",
|
||||
sd->cfg->name);
|
||||
|
||||
if (sd->id == MAX77759_I2C_SUBDEV_ID_MAXQ)
|
||||
max77759->regmap_maxq = regmap;
|
||||
else if (sd->id == MAX77759_I2C_SUBDEV_ID_CHARGER)
|
||||
max77759->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 *max77759,
|
||||
struct regmap_irq_chip_data *parent)
|
||||
{
|
||||
struct regmap_irq_chip_data *irq_chip_data;
|
||||
int apcmdres_irq;
|
||||
int ret;
|
||||
|
||||
ret = max77759_add_chained_irq_chip(&client->dev,
|
||||
max77759->regmap_maxq,
|
||||
MAX77759_INT_MAXQ,
|
||||
parent,
|
||||
&max77759_maxq_irq_chip,
|
||||
&irq_chip_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
init_completion(&max77759->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);
|
||||
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 child devices (MaxQ)\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77759_add_chained_topsys(struct i2c_client *client,
|
||||
struct max77759 *max77759,
|
||||
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->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 *max77759,
|
||||
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->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 child devices (charger)\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77759_probe(struct i2c_client *client)
|
||||
{
|
||||
struct regmap_irq_chip_data *irq_chip_data_pmic;
|
||||
struct irq_data *irq_data;
|
||||
struct max77759 *max77759;
|
||||
unsigned long irq_flags;
|
||||
unsigned int pmic_id;
|
||||
int ret;
|
||||
|
||||
max77759 = devm_kzalloc(&client->dev, sizeof(*max77759), GFP_KERNEL);
|
||||
if (!max77759)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, max77759);
|
||||
|
||||
max77759->regmap_top = devm_regmap_init_i2c(client,
|
||||
&max77759_regmap_config_top);
|
||||
if (IS_ERR(max77759->regmap_top))
|
||||
return dev_err_probe(&client->dev, PTR_ERR(max77759->regmap_top),
|
||||
"regmap init for '%s' failed\n",
|
||||
max77759_regmap_config_top.name);
|
||||
|
||||
ret = regmap_read(max77759->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_CHIP_ID)
|
||||
return dev_err_probe(&client->dev, -ENODEV,
|
||||
"unsupported device ID %#.2x (%d)\n",
|
||||
pmic_id, pmic_id);
|
||||
|
||||
ret = devm_mutex_init(&client->dev, &max77759->maxq_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(max77759_i2c_subdevs); i++) {
|
||||
ret = max77759_create_i2c_subdev(client, max77759,
|
||||
&max77759_i2c_subdevs[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
irq_data = irq_get_irq_data(client->irq);
|
||||
if (!irq_data)
|
||||
return dev_err_probe(&client->dev, -EINVAL,
|
||||
"invalid IRQ: %d\n", client->irq);
|
||||
|
||||
irq_flags = IRQF_ONESHOT | IRQF_SHARED;
|
||||
irq_flags |= irqd_get_trigger_type(irq_data);
|
||||
|
||||
ret = devm_regmap_add_irq_chip(&client->dev, max77759->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 '%s'\n",
|
||||
max77759_pmic_irq_chip.name);
|
||||
|
||||
ret = max77759_add_chained_maxq(client, max77759, irq_chip_data_pmic);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max77759_add_chained_topsys(client, max77759, irq_chip_data_pmic);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max77759_add_chained_charger(client, max77759, 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" },
|
||||
{ }
|
||||
};
|
||||
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 core driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -38,108 +38,172 @@
|
|||
#include <linux/types.h>
|
||||
|
||||
#include <linux/mfd/rohm-bd96801.h>
|
||||
#include <linux/mfd/rohm-bd96802.h>
|
||||
#include <linux/mfd/rohm-generic.h>
|
||||
|
||||
static const struct resource regulator_errb_irqs[] = {
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_OTP_ERR_STAT, "bd96801-otp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_DBIST_ERR_STAT, "bd96801-dbist-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_EEP_ERR_STAT, "bd96801-eep-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_ABIST_ERR_STAT, "bd96801-abist-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_PRSTB_ERR_STAT, "bd96801-prstb-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_DRMOS1_ERR_STAT, "bd96801-drmoserr1"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_DRMOS2_ERR_STAT, "bd96801-drmoserr2"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_SLAVE_ERR_STAT, "bd96801-slave-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_VREF_ERR_STAT, "bd96801-vref-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_TSD_ERR_STAT, "bd96801-tsd"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_UVLO_ERR_STAT, "bd96801-uvlo-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_OVLO_ERR_STAT, "bd96801-ovlo-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_OSC_ERR_STAT, "bd96801-osc-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_PON_ERR_STAT, "bd96801-pon-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_POFF_ERR_STAT, "bd96801-poff-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_CMD_SHDN_ERR_STAT, "bd96801-cmd-shdn-err"),
|
||||
struct bd968xx {
|
||||
const struct resource *errb_irqs;
|
||||
const struct resource *intb_irqs;
|
||||
int num_errb_irqs;
|
||||
int num_intb_irqs;
|
||||
const struct regmap_irq_chip *errb_irq_chip;
|
||||
const struct regmap_irq_chip *intb_irq_chip;
|
||||
const struct regmap_config *regmap_config;
|
||||
struct mfd_cell *cells;
|
||||
int num_cells;
|
||||
int unlock_reg;
|
||||
int unlock_val;
|
||||
};
|
||||
|
||||
static const struct resource bd96801_reg_errb_irqs[] = {
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_OTP_ERR_STAT, "otp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_DBIST_ERR_STAT, "dbist-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_EEP_ERR_STAT, "eep-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_ABIST_ERR_STAT, "abist-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_PRSTB_ERR_STAT, "prstb-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_DRMOS1_ERR_STAT, "drmoserr1"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_DRMOS2_ERR_STAT, "drmoserr2"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_SLAVE_ERR_STAT, "slave-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_VREF_ERR_STAT, "vref-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_TSD_ERR_STAT, "tsd"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_UVLO_ERR_STAT, "uvlo-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_OVLO_ERR_STAT, "ovlo-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_OSC_ERR_STAT, "osc-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_PON_ERR_STAT, "pon-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_POFF_ERR_STAT, "poff-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_CMD_SHDN_ERR_STAT, "cmd-shdn-err"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_INT_PRSTB_WDT_ERR, "bd96801-prstb-wdt-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_INT_CHIP_IF_ERR, "bd96801-chip-if-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_INT_SHDN_ERR_STAT, "bd96801-int-shdn-err"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_PVIN_ERR_STAT, "bd96801-buck1-pvin-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OVP_ERR_STAT, "bd96801-buck1-ovp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_UVP_ERR_STAT, "bd96801-buck1-uvp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_SHDN_ERR_STAT, "bd96801-buck1-shdn-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_INT_SHDN_ERR_STAT, "int-shdn-err"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_PVIN_ERR_STAT, "bd96801-buck2-pvin-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OVP_ERR_STAT, "bd96801-buck2-ovp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_UVP_ERR_STAT, "bd96801-buck2-uvp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_SHDN_ERR_STAT, "bd96801-buck2-shdn-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_PVIN_ERR_STAT, "buck1-pvin-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OVP_ERR_STAT, "buck1-ovp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_UVP_ERR_STAT, "buck1-uvp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_SHDN_ERR_STAT, "buck1-shdn-err"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_PVIN_ERR_STAT, "bd96801-buck3-pvin-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OVP_ERR_STAT, "bd96801-buck3-ovp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_UVP_ERR_STAT, "bd96801-buck3-uvp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_SHDN_ERR_STAT, "bd96801-buck3-shdn-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_PVIN_ERR_STAT, "buck2-pvin-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OVP_ERR_STAT, "buck2-ovp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_UVP_ERR_STAT, "buck2-uvp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_SHDN_ERR_STAT, "buck2-shdn-err"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_PVIN_ERR_STAT, "bd96801-buck4-pvin-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OVP_ERR_STAT, "bd96801-buck4-ovp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_UVP_ERR_STAT, "bd96801-buck4-uvp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_SHDN_ERR_STAT, "bd96801-buck4-shdn-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_PVIN_ERR_STAT, "buck3-pvin-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OVP_ERR_STAT, "buck3-ovp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_UVP_ERR_STAT, "buck3-uvp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_SHDN_ERR_STAT, "buck3-shdn-err"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO5_PVIN_ERR_STAT, "bd96801-ldo5-pvin-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO5_OVP_ERR_STAT, "bd96801-ldo5-ovp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO5_UVP_ERR_STAT, "bd96801-ldo5-uvp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO5_SHDN_ERR_STAT, "bd96801-ldo5-shdn-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_PVIN_ERR_STAT, "buck4-pvin-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OVP_ERR_STAT, "buck4-ovp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_UVP_ERR_STAT, "buck4-uvp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_SHDN_ERR_STAT, "buck4-shdn-err"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO6_PVIN_ERR_STAT, "bd96801-ldo6-pvin-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO6_OVP_ERR_STAT, "bd96801-ldo6-ovp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO6_UVP_ERR_STAT, "bd96801-ldo6-uvp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO6_SHDN_ERR_STAT, "bd96801-ldo6-shdn-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO5_PVIN_ERR_STAT, "ldo5-pvin-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO5_OVP_ERR_STAT, "ldo5-ovp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO5_UVP_ERR_STAT, "ldo5-uvp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO5_SHDN_ERR_STAT, "ldo5-shdn-err"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO7_PVIN_ERR_STAT, "bd96801-ldo7-pvin-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO7_OVP_ERR_STAT, "bd96801-ldo7-ovp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO7_UVP_ERR_STAT, "bd96801-ldo7-uvp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO7_SHDN_ERR_STAT, "bd96801-ldo7-shdn-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO6_PVIN_ERR_STAT, "ldo6-pvin-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO6_OVP_ERR_STAT, "ldo6-ovp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO6_UVP_ERR_STAT, "ldo6-uvp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO6_SHDN_ERR_STAT, "ldo6-shdn-err"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO7_PVIN_ERR_STAT, "ldo7-pvin-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO7_OVP_ERR_STAT, "ldo7-ovp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO7_UVP_ERR_STAT, "ldo7-uvp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO7_SHDN_ERR_STAT, "ldo7-shdn-err"),
|
||||
};
|
||||
|
||||
static const struct resource regulator_intb_irqs[] = {
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_TW_STAT, "bd96801-core-thermal"),
|
||||
static const struct resource bd96802_reg_errb_irqs[] = {
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_OTP_ERR_STAT, "otp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_DBIST_ERR_STAT, "dbist-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_EEP_ERR_STAT, "eep-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_ABIST_ERR_STAT, "abist-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_PRSTB_ERR_STAT, "prstb-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_DRMOS1_ERR_STAT, "drmoserr1"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_DRMOS1_ERR_STAT, "drmoserr2"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_SLAVE_ERR_STAT, "slave-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_VREF_ERR_STAT, "vref-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_TSD_ERR_STAT, "tsd"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_UVLO_ERR_STAT, "uvlo-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_OVLO_ERR_STAT, "ovlo-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_OSC_ERR_STAT, "osc-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_PON_ERR_STAT, "pon-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_POFF_ERR_STAT, "poff-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_CMD_SHDN_ERR_STAT, "cmd-shdn-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_INT_SHDN_ERR_STAT, "int-shdn-err"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OCPH_STAT, "bd96801-buck1-overcurr-h"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OCPL_STAT, "bd96801-buck1-overcurr-l"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OCPN_STAT, "bd96801-buck1-overcurr-n"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OVD_STAT, "bd96801-buck1-overvolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_UVD_STAT, "bd96801-buck1-undervolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_TW_CH_STAT, "bd96801-buck1-thermal"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_PVIN_ERR_STAT, "buck1-pvin-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_OVP_ERR_STAT, "buck1-ovp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_UVP_ERR_STAT, "buck1-uvp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_SHDN_ERR_STAT, "buck1-shdn-err"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OCPH_STAT, "bd96801-buck2-overcurr-h"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OCPL_STAT, "bd96801-buck2-overcurr-l"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OCPN_STAT, "bd96801-buck2-overcurr-n"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OVD_STAT, "bd96801-buck2-overvolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_UVD_STAT, "bd96801-buck2-undervolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_TW_CH_STAT, "bd96801-buck2-thermal"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_PVIN_ERR_STAT, "buck2-pvin-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_OVP_ERR_STAT, "buck2-ovp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_UVP_ERR_STAT, "buck2-uvp-err"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_SHDN_ERR_STAT, "buck2-shdn-err"),
|
||||
};
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OCPH_STAT, "bd96801-buck3-overcurr-h"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OCPL_STAT, "bd96801-buck3-overcurr-l"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OCPN_STAT, "bd96801-buck3-overcurr-n"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OVD_STAT, "bd96801-buck3-overvolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_UVD_STAT, "bd96801-buck3-undervolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_TW_CH_STAT, "bd96801-buck3-thermal"),
|
||||
static const struct resource bd96801_reg_intb_irqs[] = {
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_TW_STAT, "core-thermal"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OCPH_STAT, "bd96801-buck4-overcurr-h"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OCPL_STAT, "bd96801-buck4-overcurr-l"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OCPN_STAT, "bd96801-buck4-overcurr-n"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OVD_STAT, "bd96801-buck4-overvolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_UVD_STAT, "bd96801-buck4-undervolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_TW_CH_STAT, "bd96801-buck4-thermal"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OCPH_STAT, "buck1-overcurr-h"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OCPL_STAT, "buck1-overcurr-l"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OCPN_STAT, "buck1-overcurr-n"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OVD_STAT, "buck1-overvolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_UVD_STAT, "buck1-undervolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_TW_CH_STAT, "buck1-thermal"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO5_OCPH_STAT, "bd96801-ldo5-overcurr"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO5_OVD_STAT, "bd96801-ldo5-overvolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO5_UVD_STAT, "bd96801-ldo5-undervolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OCPH_STAT, "buck2-overcurr-h"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OCPL_STAT, "buck2-overcurr-l"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OCPN_STAT, "buck2-overcurr-n"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OVD_STAT, "buck2-overvolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_UVD_STAT, "buck2-undervolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_TW_CH_STAT, "buck2-thermal"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO6_OCPH_STAT, "bd96801-ldo6-overcurr"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO6_OVD_STAT, "bd96801-ldo6-overvolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO6_UVD_STAT, "bd96801-ldo6-undervolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OCPH_STAT, "buck3-overcurr-h"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OCPL_STAT, "buck3-overcurr-l"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OCPN_STAT, "buck3-overcurr-n"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OVD_STAT, "buck3-overvolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_UVD_STAT, "buck3-undervolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_TW_CH_STAT, "buck3-thermal"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO7_OCPH_STAT, "bd96801-ldo7-overcurr"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO7_OVD_STAT, "bd96801-ldo7-overvolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO7_UVD_STAT, "bd96801-ldo7-undervolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OCPH_STAT, "buck4-overcurr-h"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OCPL_STAT, "buck4-overcurr-l"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OCPN_STAT, "buck4-overcurr-n"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OVD_STAT, "buck4-overvolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_UVD_STAT, "buck4-undervolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_TW_CH_STAT, "buck4-thermal"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO5_OCPH_STAT, "ldo5-overcurr"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO5_OVD_STAT, "ldo5-overvolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO5_UVD_STAT, "ldo5-undervolt"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO6_OCPH_STAT, "ldo6-overcurr"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO6_OVD_STAT, "ldo6-overvolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO6_UVD_STAT, "ldo6-undervolt"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO7_OCPH_STAT, "ldo7-overcurr"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO7_OVD_STAT, "ldo7-overvolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96801_LDO7_UVD_STAT, "ldo7-undervolt"),
|
||||
};
|
||||
|
||||
static const struct resource bd96802_reg_intb_irqs[] = {
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_TW_STAT, "core-thermal"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_OCPH_STAT, "buck1-overcurr-h"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_OCPL_STAT, "buck1-overcurr-l"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_OCPN_STAT, "buck1-overcurr-n"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_OVD_STAT, "buck1-overvolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_UVD_STAT, "buck1-undervolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_TW_CH_STAT, "buck1-thermal"),
|
||||
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_OCPH_STAT, "buck2-overcurr-h"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_OCPL_STAT, "buck2-overcurr-l"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_OCPN_STAT, "buck2-overcurr-n"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_OVD_STAT, "buck2-overvolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_UVD_STAT, "buck2-undervolt"),
|
||||
DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_TW_CH_STAT, "buck2-thermal"),
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
@ -152,6 +216,20 @@ static struct mfd_cell bd96801_cells[] = {
|
|||
[REGULATOR_CELL] = { .name = "bd96801-regulator", },
|
||||
};
|
||||
|
||||
static struct mfd_cell bd96802_cells[] = {
|
||||
[WDG_CELL] = { .name = "bd96801-wdt", },
|
||||
[REGULATOR_CELL] = { .name = "bd96802-regulator", },
|
||||
};
|
||||
static struct mfd_cell bd96805_cells[] = {
|
||||
[WDG_CELL] = { .name = "bd96801-wdt", },
|
||||
[REGULATOR_CELL] = { .name = "bd96805-regulator", },
|
||||
};
|
||||
|
||||
static struct mfd_cell bd96806_cells[] = {
|
||||
[WDG_CELL] = { .name = "bd96806-wdt", },
|
||||
[REGULATOR_CELL] = { .name = "bd96806-regulator", },
|
||||
};
|
||||
|
||||
static const struct regmap_range bd96801_volatile_ranges[] = {
|
||||
/* Status registers */
|
||||
regmap_reg_range(BD96801_REG_WD_FEED, BD96801_REG_WD_FAILCOUNT),
|
||||
|
|
@ -169,11 +247,28 @@ static const struct regmap_range bd96801_volatile_ranges[] = {
|
|||
regmap_reg_range(BD96801_LDO5_VOL_LVL_REG, BD96801_LDO7_VOL_LVL_REG),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table volatile_regs = {
|
||||
static const struct regmap_range bd96802_volatile_ranges[] = {
|
||||
/* Status regs */
|
||||
regmap_reg_range(BD96801_REG_WD_FEED, BD96801_REG_WD_FAILCOUNT),
|
||||
regmap_reg_range(BD96801_REG_WD_ASK, BD96801_REG_WD_ASK),
|
||||
regmap_reg_range(BD96801_REG_WD_STATUS, BD96801_REG_WD_STATUS),
|
||||
regmap_reg_range(BD96801_REG_PMIC_STATE, BD96801_REG_INT_BUCK2_ERRB),
|
||||
regmap_reg_range(BD96801_REG_INT_SYS_INTB, BD96801_REG_INT_BUCK2_INTB),
|
||||
/* Registers which do not update value unless PMIC is in STBY */
|
||||
regmap_reg_range(BD96801_REG_SSCG_CTRL, BD96801_REG_SHD_INTB),
|
||||
regmap_reg_range(BD96801_REG_BUCK_OVP, BD96801_REG_BOOT_OVERTIME),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table bd96801_volatile_regs = {
|
||||
.yes_ranges = bd96801_volatile_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(bd96801_volatile_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table bd96802_volatile_regs = {
|
||||
.yes_ranges = bd96802_volatile_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(bd96802_volatile_ranges),
|
||||
};
|
||||
|
||||
/*
|
||||
* For ERRB we need main register bit mapping as bit(0) indicates active IRQ
|
||||
* in one of the first 3 sub IRQ registers, For INTB we can use default 1 to 1
|
||||
|
|
@ -188,7 +283,7 @@ static unsigned int bit5_offsets[] = {7}; /* LDO 5 stat */
|
|||
static unsigned int bit6_offsets[] = {8}; /* LDO 6 stat */
|
||||
static unsigned int bit7_offsets[] = {9}; /* LDO 7 stat */
|
||||
|
||||
static const struct regmap_irq_sub_irq_map errb_sub_irq_offsets[] = {
|
||||
static const struct regmap_irq_sub_irq_map bd96801_errb_sub_irq_offsets[] = {
|
||||
REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets),
|
||||
REGMAP_IRQ_MAIN_REG_OFFSET(bit1_offsets),
|
||||
REGMAP_IRQ_MAIN_REG_OFFSET(bit2_offsets),
|
||||
|
|
@ -199,6 +294,12 @@ static const struct regmap_irq_sub_irq_map errb_sub_irq_offsets[] = {
|
|||
REGMAP_IRQ_MAIN_REG_OFFSET(bit7_offsets),
|
||||
};
|
||||
|
||||
static const struct regmap_irq_sub_irq_map bd96802_errb_sub_irq_offsets[] = {
|
||||
REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets),
|
||||
REGMAP_IRQ_MAIN_REG_OFFSET(bit1_offsets),
|
||||
REGMAP_IRQ_MAIN_REG_OFFSET(bit2_offsets),
|
||||
};
|
||||
|
||||
static const struct regmap_irq bd96801_errb_irqs[] = {
|
||||
/* Reg 0x52 Fatal ERRB1 */
|
||||
REGMAP_IRQ_REG(BD96801_OTP_ERR_STAT, 0, BD96801_OTP_ERR_MASK),
|
||||
|
|
@ -259,6 +360,39 @@ static const struct regmap_irq bd96801_errb_irqs[] = {
|
|||
REGMAP_IRQ_REG(BD96801_LDO7_SHDN_ERR_STAT, 9, BD96801_OUT_SHDN_ERR_MASK),
|
||||
};
|
||||
|
||||
static const struct regmap_irq bd96802_errb_irqs[] = {
|
||||
/* Reg 0x52 Fatal ERRB1 */
|
||||
REGMAP_IRQ_REG(BD96802_OTP_ERR_STAT, 0, BD96801_OTP_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_DBIST_ERR_STAT, 0, BD96801_DBIST_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_EEP_ERR_STAT, 0, BD96801_EEP_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_ABIST_ERR_STAT, 0, BD96801_ABIST_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_PRSTB_ERR_STAT, 0, BD96801_PRSTB_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_DRMOS1_ERR_STAT, 0, BD96801_DRMOS1_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_DRMOS2_ERR_STAT, 0, BD96801_DRMOS2_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_SLAVE_ERR_STAT, 0, BD96801_SLAVE_ERR_MASK),
|
||||
/* 0x53 Fatal ERRB2 */
|
||||
REGMAP_IRQ_REG(BD96802_VREF_ERR_STAT, 1, BD96801_VREF_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_TSD_ERR_STAT, 1, BD96801_TSD_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_UVLO_ERR_STAT, 1, BD96801_UVLO_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_OVLO_ERR_STAT, 1, BD96801_OVLO_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_OSC_ERR_STAT, 1, BD96801_OSC_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_PON_ERR_STAT, 1, BD96801_PON_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_POFF_ERR_STAT, 1, BD96801_POFF_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_CMD_SHDN_ERR_STAT, 1, BD96801_CMD_SHDN_ERR_MASK),
|
||||
/* 0x54 Fatal INTB shadowed to ERRB */
|
||||
REGMAP_IRQ_REG(BD96802_INT_SHDN_ERR_STAT, 2, BD96801_INT_SHDN_ERR_MASK),
|
||||
/* Reg 0x55 BUCK1 ERR IRQs */
|
||||
REGMAP_IRQ_REG(BD96802_BUCK1_PVIN_ERR_STAT, 3, BD96801_OUT_PVIN_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_BUCK1_OVP_ERR_STAT, 3, BD96801_OUT_OVP_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_BUCK1_UVP_ERR_STAT, 3, BD96801_OUT_UVP_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_BUCK1_SHDN_ERR_STAT, 3, BD96801_OUT_SHDN_ERR_MASK),
|
||||
/* Reg 0x56 BUCK2 ERR IRQs */
|
||||
REGMAP_IRQ_REG(BD96802_BUCK2_PVIN_ERR_STAT, 4, BD96801_OUT_PVIN_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_BUCK2_OVP_ERR_STAT, 4, BD96801_OUT_OVP_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_BUCK2_UVP_ERR_STAT, 4, BD96801_OUT_UVP_ERR_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_BUCK2_SHDN_ERR_STAT, 4, BD96801_OUT_SHDN_ERR_MASK),
|
||||
};
|
||||
|
||||
static const struct regmap_irq bd96801_intb_irqs[] = {
|
||||
/* STATUS SYSTEM INTB */
|
||||
REGMAP_IRQ_REG(BD96801_TW_STAT, 0, BD96801_TW_STAT_MASK),
|
||||
|
|
@ -307,6 +441,69 @@ static const struct regmap_irq bd96801_intb_irqs[] = {
|
|||
REGMAP_IRQ_REG(BD96801_LDO7_UVD_STAT, 7, BD96801_LDO_UVD_STAT_MASK),
|
||||
};
|
||||
|
||||
static const struct regmap_irq bd96802_intb_irqs[] = {
|
||||
/* STATUS SYSTEM INTB */
|
||||
REGMAP_IRQ_REG(BD96802_TW_STAT, 0, BD96801_TW_STAT_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_WDT_ERR_STAT, 0, BD96801_WDT_ERR_STAT_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_I2C_ERR_STAT, 0, BD96801_I2C_ERR_STAT_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_CHIP_IF_ERR_STAT, 0, BD96801_CHIP_IF_ERR_STAT_MASK),
|
||||
/* STATUS BUCK1 INTB */
|
||||
REGMAP_IRQ_REG(BD96802_BUCK1_OCPH_STAT, 1, BD96801_BUCK_OCPH_STAT_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_BUCK1_OCPL_STAT, 1, BD96801_BUCK_OCPL_STAT_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_BUCK1_OCPN_STAT, 1, BD96801_BUCK_OCPN_STAT_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_BUCK1_OVD_STAT, 1, BD96801_BUCK_OVD_STAT_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_BUCK1_UVD_STAT, 1, BD96801_BUCK_UVD_STAT_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_BUCK1_TW_CH_STAT, 1, BD96801_BUCK_TW_CH_STAT_MASK),
|
||||
/* BUCK 2 INTB */
|
||||
REGMAP_IRQ_REG(BD96802_BUCK2_OCPH_STAT, 2, BD96801_BUCK_OCPH_STAT_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_BUCK2_OCPL_STAT, 2, BD96801_BUCK_OCPL_STAT_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_BUCK2_OCPN_STAT, 2, BD96801_BUCK_OCPN_STAT_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_BUCK2_OVD_STAT, 2, BD96801_BUCK_OVD_STAT_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_BUCK2_UVD_STAT, 2, BD96801_BUCK_UVD_STAT_MASK),
|
||||
REGMAP_IRQ_REG(BD96802_BUCK2_TW_CH_STAT, 2, BD96801_BUCK_TW_CH_STAT_MASK),
|
||||
};
|
||||
|
||||
/*
|
||||
* The IRQ stuff is a bit hairy. The BD96801 / BD96802 provide two physical
|
||||
* IRQ lines called INTB and ERRB. They share the same main status register.
|
||||
*
|
||||
* For ERRB, mapping from main status to sub-status is such that the
|
||||
* 'global' faults are mapped to first 3 sub-status registers - and indicated
|
||||
* by the first bit[0] in main status reg.
|
||||
*
|
||||
* Rest of the status registers are for indicating stuff for individual
|
||||
* regulators, 1 sub register / regulator and 1 main status register bit /
|
||||
* regulator, starting from bit[1].
|
||||
*
|
||||
* Eg, regulator specific stuff has 1 to 1 mapping from main-status to sub
|
||||
* registers but 'global' ERRB IRQs require mapping from main status bit[0] to
|
||||
* 3 status registers.
|
||||
*
|
||||
* Furthermore, the BD96801 has 7 regulators where the BD96802 has only 2.
|
||||
*
|
||||
* INTB has only 1 sub status register for 'global' events and then own sub
|
||||
* status register for each of the regulators. So, for INTB we have direct
|
||||
* 1 to 1 mapping - BD96801 just having 5 register and 5 main status bits
|
||||
* more than the BD96802.
|
||||
*
|
||||
* Sharing the main status bits could be a problem if we had both INTB and
|
||||
* ERRB IRQs asserted but for different sub-status offsets. This might lead
|
||||
* IRQ controller code to go read a sub status register which indicates no
|
||||
* active IRQs. I assume this occurring repeteadly might lead the IRQ to be
|
||||
* disabled by core as a result of repeteadly returned IRQ_NONEs.
|
||||
*
|
||||
* I don't consider this as a fatal problem for now because:
|
||||
* a) Having ERRB asserted leads to PMIC fault state which will kill
|
||||
* the SoC powered by the PMIC. (So, relevant only for potential
|
||||
* case of not powering the processor with this PMIC).
|
||||
* b) Having ERRB set without having respective INTB is unlikely
|
||||
* (haven't actually verified this).
|
||||
*
|
||||
* So, let's proceed with main status enabled for both INTB and ERRB. We can
|
||||
* later disable main-status usage on systems where this ever proves to be
|
||||
* a problem.
|
||||
*/
|
||||
|
||||
static const struct regmap_irq_chip bd96801_irq_chip_errb = {
|
||||
.name = "bd96801-irq-errb",
|
||||
.domain_suffix = "errb",
|
||||
|
|
@ -320,7 +517,23 @@ static const struct regmap_irq_chip bd96801_irq_chip_errb = {
|
|||
.init_ack_masked = true,
|
||||
.num_regs = 10,
|
||||
.irq_reg_stride = 1,
|
||||
.sub_reg_offsets = &errb_sub_irq_offsets[0],
|
||||
.sub_reg_offsets = &bd96801_errb_sub_irq_offsets[0],
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip bd96802_irq_chip_errb = {
|
||||
.name = "bd96802-irq-errb",
|
||||
.domain_suffix = "errb",
|
||||
.main_status = BD96801_REG_INT_MAIN,
|
||||
.num_main_regs = 1,
|
||||
.irqs = &bd96802_errb_irqs[0],
|
||||
.num_irqs = ARRAY_SIZE(bd96802_errb_irqs),
|
||||
.status_base = BD96801_REG_INT_SYS_ERRB1,
|
||||
.mask_base = BD96801_REG_MASK_SYS_ERRB,
|
||||
.ack_base = BD96801_REG_INT_SYS_ERRB1,
|
||||
.init_ack_masked = true,
|
||||
.num_regs = 5,
|
||||
.irq_reg_stride = 1,
|
||||
.sub_reg_offsets = &bd96802_errb_sub_irq_offsets[0],
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip bd96801_irq_chip_intb = {
|
||||
|
|
@ -338,25 +551,124 @@ static const struct regmap_irq_chip bd96801_irq_chip_intb = {
|
|||
.irq_reg_stride = 1,
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip bd96802_irq_chip_intb = {
|
||||
.name = "bd96802-irq-intb",
|
||||
.domain_suffix = "intb",
|
||||
.main_status = BD96801_REG_INT_MAIN,
|
||||
.num_main_regs = 1,
|
||||
.irqs = &bd96802_intb_irqs[0],
|
||||
.num_irqs = ARRAY_SIZE(bd96802_intb_irqs),
|
||||
.status_base = BD96801_REG_INT_SYS_INTB,
|
||||
.mask_base = BD96801_REG_MASK_SYS_INTB,
|
||||
.ack_base = BD96801_REG_INT_SYS_INTB,
|
||||
.init_ack_masked = true,
|
||||
.num_regs = 3,
|
||||
.irq_reg_stride = 1,
|
||||
};
|
||||
|
||||
static const struct regmap_config bd96801_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.volatile_table = &volatile_regs,
|
||||
.volatile_table = &bd96801_volatile_regs,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
};
|
||||
|
||||
static const struct regmap_config bd96802_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.volatile_table = &bd96802_volatile_regs,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
};
|
||||
|
||||
static const struct bd968xx bd96801_data = {
|
||||
.errb_irqs = bd96801_reg_errb_irqs,
|
||||
.intb_irqs = bd96801_reg_intb_irqs,
|
||||
.num_errb_irqs = ARRAY_SIZE(bd96801_reg_errb_irqs),
|
||||
.num_intb_irqs = ARRAY_SIZE(bd96801_reg_intb_irqs),
|
||||
.errb_irq_chip = &bd96801_irq_chip_errb,
|
||||
.intb_irq_chip = &bd96801_irq_chip_intb,
|
||||
.regmap_config = &bd96801_regmap_config,
|
||||
.cells = bd96801_cells,
|
||||
.num_cells = ARRAY_SIZE(bd96801_cells),
|
||||
.unlock_reg = BD96801_LOCK_REG,
|
||||
.unlock_val = BD96801_UNLOCK,
|
||||
};
|
||||
|
||||
static const struct bd968xx bd96802_data = {
|
||||
.errb_irqs = bd96802_reg_errb_irqs,
|
||||
.intb_irqs = bd96802_reg_intb_irqs,
|
||||
.num_errb_irqs = ARRAY_SIZE(bd96802_reg_errb_irqs),
|
||||
.num_intb_irqs = ARRAY_SIZE(bd96802_reg_intb_irqs),
|
||||
.errb_irq_chip = &bd96802_irq_chip_errb,
|
||||
.intb_irq_chip = &bd96802_irq_chip_intb,
|
||||
.regmap_config = &bd96802_regmap_config,
|
||||
.cells = bd96802_cells,
|
||||
.num_cells = ARRAY_SIZE(bd96802_cells),
|
||||
.unlock_reg = BD96801_LOCK_REG,
|
||||
.unlock_val = BD96801_UNLOCK,
|
||||
};
|
||||
|
||||
static const struct bd968xx bd96805_data = {
|
||||
.errb_irqs = bd96801_reg_errb_irqs,
|
||||
.intb_irqs = bd96801_reg_intb_irqs,
|
||||
.num_errb_irqs = ARRAY_SIZE(bd96801_reg_errb_irqs),
|
||||
.num_intb_irqs = ARRAY_SIZE(bd96801_reg_intb_irqs),
|
||||
.errb_irq_chip = &bd96801_irq_chip_errb,
|
||||
.intb_irq_chip = &bd96801_irq_chip_intb,
|
||||
.regmap_config = &bd96801_regmap_config,
|
||||
.cells = bd96805_cells,
|
||||
.num_cells = ARRAY_SIZE(bd96805_cells),
|
||||
.unlock_reg = BD96801_LOCK_REG,
|
||||
.unlock_val = BD96801_UNLOCK,
|
||||
};
|
||||
|
||||
static struct bd968xx bd96806_data = {
|
||||
.errb_irqs = bd96802_reg_errb_irqs,
|
||||
.intb_irqs = bd96802_reg_intb_irqs,
|
||||
.num_errb_irqs = ARRAY_SIZE(bd96802_reg_errb_irqs),
|
||||
.num_intb_irqs = ARRAY_SIZE(bd96802_reg_intb_irqs),
|
||||
.errb_irq_chip = &bd96802_irq_chip_errb,
|
||||
.intb_irq_chip = &bd96802_irq_chip_intb,
|
||||
.regmap_config = &bd96802_regmap_config,
|
||||
.cells = bd96806_cells,
|
||||
.num_cells = ARRAY_SIZE(bd96806_cells),
|
||||
.unlock_reg = BD96801_LOCK_REG,
|
||||
.unlock_val = BD96801_UNLOCK,
|
||||
};
|
||||
|
||||
static int bd96801_i2c_probe(struct i2c_client *i2c)
|
||||
{
|
||||
struct regmap_irq_chip_data *intb_irq_data, *errb_irq_data;
|
||||
struct irq_domain *intb_domain, *errb_domain;
|
||||
const struct bd968xx *ddata;
|
||||
const struct fwnode_handle *fwnode;
|
||||
struct resource *regulator_res;
|
||||
struct resource wdg_irq;
|
||||
struct regmap *regmap;
|
||||
int intb_irq, errb_irq, num_intb, num_errb = 0;
|
||||
int intb_irq, errb_irq, num_errb = 0;
|
||||
int num_regu_irqs, wdg_irq_no;
|
||||
unsigned int chip_type;
|
||||
int i, ret;
|
||||
|
||||
chip_type = (unsigned int)(uintptr_t)device_get_match_data(&i2c->dev);
|
||||
switch (chip_type) {
|
||||
case ROHM_CHIP_TYPE_BD96801:
|
||||
ddata = &bd96801_data;
|
||||
break;
|
||||
case ROHM_CHIP_TYPE_BD96802:
|
||||
ddata = &bd96802_data;
|
||||
break;
|
||||
case ROHM_CHIP_TYPE_BD96805:
|
||||
ddata = &bd96805_data;
|
||||
break;
|
||||
case ROHM_CHIP_TYPE_BD96806:
|
||||
ddata = &bd96806_data;
|
||||
break;
|
||||
default:
|
||||
dev_err(&i2c->dev, "Unknown IC\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fwnode = dev_fwnode(&i2c->dev);
|
||||
if (!fwnode)
|
||||
return dev_err_probe(&i2c->dev, -EINVAL, "Failed to find fwnode\n");
|
||||
|
|
@ -365,34 +677,32 @@ static int bd96801_i2c_probe(struct i2c_client *i2c)
|
|||
if (intb_irq < 0)
|
||||
return dev_err_probe(&i2c->dev, intb_irq, "INTB IRQ not configured\n");
|
||||
|
||||
num_intb = ARRAY_SIZE(regulator_intb_irqs);
|
||||
|
||||
/* ERRB may be omitted if processor is powered by the PMIC */
|
||||
errb_irq = fwnode_irq_get_byname(fwnode, "errb");
|
||||
if (errb_irq < 0)
|
||||
errb_irq = 0;
|
||||
if (errb_irq == -EPROBE_DEFER)
|
||||
return errb_irq;
|
||||
|
||||
if (errb_irq)
|
||||
num_errb = ARRAY_SIZE(regulator_errb_irqs);
|
||||
if (errb_irq > 0)
|
||||
num_errb = ddata->num_errb_irqs;
|
||||
|
||||
num_regu_irqs = num_intb + num_errb;
|
||||
num_regu_irqs = ddata->num_intb_irqs + num_errb;
|
||||
|
||||
regulator_res = devm_kcalloc(&i2c->dev, num_regu_irqs,
|
||||
sizeof(*regulator_res), GFP_KERNEL);
|
||||
if (!regulator_res)
|
||||
return -ENOMEM;
|
||||
|
||||
regmap = devm_regmap_init_i2c(i2c, &bd96801_regmap_config);
|
||||
regmap = devm_regmap_init_i2c(i2c, ddata->regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return dev_err_probe(&i2c->dev, PTR_ERR(regmap),
|
||||
"Regmap initialization failed\n");
|
||||
|
||||
ret = regmap_write(regmap, BD96801_LOCK_REG, BD96801_UNLOCK);
|
||||
ret = regmap_write(regmap, ddata->unlock_reg, ddata->unlock_val);
|
||||
if (ret)
|
||||
return dev_err_probe(&i2c->dev, ret, "Failed to unlock PMIC\n");
|
||||
|
||||
ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, intb_irq,
|
||||
IRQF_ONESHOT, 0, &bd96801_irq_chip_intb,
|
||||
IRQF_ONESHOT, 0, ddata->intb_irq_chip,
|
||||
&intb_irq_data);
|
||||
if (ret)
|
||||
return dev_err_probe(&i2c->dev, ret, "Failed to add INTB IRQ chip\n");
|
||||
|
|
@ -404,24 +714,25 @@ static int bd96801_i2c_probe(struct i2c_client *i2c)
|
|||
* has two domains so we do IRQ mapping here and provide the
|
||||
* already mapped IRQ numbers to sub-devices.
|
||||
*/
|
||||
for (i = 0; i < num_intb; i++) {
|
||||
for (i = 0; i < ddata->num_intb_irqs; i++) {
|
||||
struct resource *res = ®ulator_res[i];
|
||||
|
||||
*res = regulator_intb_irqs[i];
|
||||
*res = ddata->intb_irqs[i];
|
||||
res->start = res->end = irq_create_mapping(intb_domain,
|
||||
res->start);
|
||||
}
|
||||
|
||||
wdg_irq_no = irq_create_mapping(intb_domain, BD96801_WDT_ERR_STAT);
|
||||
wdg_irq = DEFINE_RES_IRQ_NAMED(wdg_irq_no, "bd96801-wdg");
|
||||
bd96801_cells[WDG_CELL].resources = &wdg_irq;
|
||||
bd96801_cells[WDG_CELL].num_resources = 1;
|
||||
|
||||
ddata->cells[WDG_CELL].resources = &wdg_irq;
|
||||
ddata->cells[WDG_CELL].num_resources = 1;
|
||||
|
||||
if (!num_errb)
|
||||
goto skip_errb;
|
||||
|
||||
ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, errb_irq, IRQF_ONESHOT,
|
||||
0, &bd96801_irq_chip_errb, &errb_irq_data);
|
||||
0, ddata->errb_irq_chip, &errb_irq_data);
|
||||
if (ret)
|
||||
return dev_err_probe(&i2c->dev, ret,
|
||||
"Failed to add ERRB IRQ chip\n");
|
||||
|
|
@ -429,18 +740,17 @@ static int bd96801_i2c_probe(struct i2c_client *i2c)
|
|||
errb_domain = regmap_irq_get_domain(errb_irq_data);
|
||||
|
||||
for (i = 0; i < num_errb; i++) {
|
||||
struct resource *res = ®ulator_res[num_intb + i];
|
||||
struct resource *res = ®ulator_res[ddata->num_intb_irqs + i];
|
||||
|
||||
*res = regulator_errb_irqs[i];
|
||||
*res = ddata->errb_irqs[i];
|
||||
res->start = res->end = irq_create_mapping(errb_domain, res->start);
|
||||
}
|
||||
|
||||
skip_errb:
|
||||
bd96801_cells[REGULATOR_CELL].resources = regulator_res;
|
||||
bd96801_cells[REGULATOR_CELL].num_resources = num_regu_irqs;
|
||||
|
||||
ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, bd96801_cells,
|
||||
ARRAY_SIZE(bd96801_cells), NULL, 0, NULL);
|
||||
ddata->cells[REGULATOR_CELL].resources = regulator_res;
|
||||
ddata->cells[REGULATOR_CELL].num_resources = num_regu_irqs;
|
||||
ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, ddata->cells,
|
||||
ddata->num_cells, NULL, 0, NULL);
|
||||
if (ret)
|
||||
dev_err_probe(&i2c->dev, ret, "Failed to create subdevices\n");
|
||||
|
||||
|
|
@ -448,7 +758,10 @@ skip_errb:
|
|||
}
|
||||
|
||||
static const struct of_device_id bd96801_of_match[] = {
|
||||
{ .compatible = "rohm,bd96801", },
|
||||
{ .compatible = "rohm,bd96801", .data = (void *)ROHM_CHIP_TYPE_BD96801 },
|
||||
{ .compatible = "rohm,bd96802", .data = (void *)ROHM_CHIP_TYPE_BD96802 },
|
||||
{ .compatible = "rohm,bd96805", .data = (void *)ROHM_CHIP_TYPE_BD96805 },
|
||||
{ .compatible = "rohm,bd96806", .data = (void *)ROHM_CHIP_TYPE_BD96806 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bd96801_of_match);
|
||||
|
|
@ -476,5 +789,5 @@ static void __exit bd96801_i2c_exit(void)
|
|||
module_exit(bd96801_i2c_exit);
|
||||
|
||||
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
|
||||
MODULE_DESCRIPTION("ROHM BD96801 Power Management IC driver");
|
||||
MODULE_DESCRIPTION("ROHM BD9680X Power Management IC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
* Inspired by Benjamin Gaignard's stm32-timers driver
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/mfd/stm32-lptimer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
|
@ -49,6 +50,36 @@ static int stm32_lptimer_detect_encoder(struct stm32_lptimer *ddata)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_lptimer_detect_hwcfgr(struct stm32_lptimer *ddata)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(ddata->regmap, STM32_LPTIM_VERR, &ddata->version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Try to guess parameters from HWCFGR: e.g. encoder mode (STM32MP15) */
|
||||
ret = regmap_read(ddata->regmap, STM32_LPTIM_HWCFGR1, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Fallback to legacy init if HWCFGR isn't present */
|
||||
if (!val)
|
||||
return stm32_lptimer_detect_encoder(ddata);
|
||||
|
||||
ddata->has_encoder = FIELD_GET(STM32_LPTIM_HWCFGR1_ENCODER, val);
|
||||
|
||||
ret = regmap_read(ddata->regmap, STM32_LPTIM_HWCFGR2, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Number of capture/compare channels */
|
||||
ddata->num_cc_chans = FIELD_GET(STM32_LPTIM_HWCFGR2_CHAN_NUM, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_lptimer_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
|
@ -73,7 +104,7 @@ static int stm32_lptimer_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(ddata->clk))
|
||||
return PTR_ERR(ddata->clk);
|
||||
|
||||
ret = stm32_lptimer_detect_encoder(ddata);
|
||||
ret = stm32_lptimer_detect_hwcfgr(ddata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
145
drivers/nvmem/max77759-nvmem.c
Normal file
145
drivers/nvmem/max77759-nvmem.c
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
// 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 *max77759;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
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, 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;
|
||||
|
||||
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, 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 = 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 const struct platform_device_id max77759_nvmem_platform_id[] = {
|
||||
{ "max77759-nvmem", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, max77759_nvmem_platform_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,
|
||||
.id_table = max77759_nvmem_platform_id,
|
||||
};
|
||||
|
||||
module_platform_driver(max77759_nvmem_driver);
|
||||
|
||||
MODULE_AUTHOR("André Draszik <andre.draszik@linaro.org>");
|
||||
MODULE_DESCRIPTION("NVMEM driver for Maxim MAX77759");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
struct stm32_pwm_lp {
|
||||
struct clk *clk;
|
||||
struct regmap *regmap;
|
||||
unsigned int num_cc_chans;
|
||||
};
|
||||
|
||||
static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
|
||||
|
|
@ -30,13 +31,101 @@ static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
|
|||
/* STM32 Low-Power Timer is preceded by a configurable power-of-2 prescaler */
|
||||
#define STM32_LPTIM_MAX_PRESCALER 128
|
||||
|
||||
static int stm32_pwm_lp_update_allowed(struct stm32_pwm_lp *priv, int channel)
|
||||
{
|
||||
int ret;
|
||||
u32 ccmr1;
|
||||
unsigned long ccmr;
|
||||
|
||||
/* Only one PWM on this LPTIMER: enable, prescaler and reload value can be changed */
|
||||
if (!priv->num_cc_chans)
|
||||
return true;
|
||||
|
||||
ret = regmap_read(priv->regmap, STM32_LPTIM_CCMR1, &ccmr1);
|
||||
if (ret)
|
||||
return ret;
|
||||
ccmr = ccmr1 & (STM32_LPTIM_CC1E | STM32_LPTIM_CC2E);
|
||||
|
||||
/* More than one channel enabled: enable, prescaler or ARR value can't be changed */
|
||||
if (bitmap_weight(&ccmr, sizeof(u32) * BITS_PER_BYTE) > 1)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Only one channel is enabled (or none): check status on the other channel, to
|
||||
* report if enable, prescaler or ARR value can be changed.
|
||||
*/
|
||||
if (channel)
|
||||
return !(ccmr1 & STM32_LPTIM_CC1E);
|
||||
else
|
||||
return !(ccmr1 & STM32_LPTIM_CC2E);
|
||||
}
|
||||
|
||||
static int stm32_pwm_lp_compare_channel_apply(struct stm32_pwm_lp *priv, int channel,
|
||||
bool enable, enum pwm_polarity polarity)
|
||||
{
|
||||
u32 ccmr1, val, mask;
|
||||
bool reenable;
|
||||
int ret;
|
||||
|
||||
/* No dedicated CC channel: nothing to do */
|
||||
if (!priv->num_cc_chans)
|
||||
return 0;
|
||||
|
||||
ret = regmap_read(priv->regmap, STM32_LPTIM_CCMR1, &ccmr1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (channel) {
|
||||
/* Must disable CC channel (CCxE) to modify polarity (CCxP), then re-enable */
|
||||
reenable = (enable && FIELD_GET(STM32_LPTIM_CC2E, ccmr1)) &&
|
||||
(polarity != FIELD_GET(STM32_LPTIM_CC2P, ccmr1));
|
||||
|
||||
mask = STM32_LPTIM_CC2SEL | STM32_LPTIM_CC2E | STM32_LPTIM_CC2P;
|
||||
val = FIELD_PREP(STM32_LPTIM_CC2P, polarity);
|
||||
val |= FIELD_PREP(STM32_LPTIM_CC2E, enable);
|
||||
} else {
|
||||
reenable = (enable && FIELD_GET(STM32_LPTIM_CC1E, ccmr1)) &&
|
||||
(polarity != FIELD_GET(STM32_LPTIM_CC1P, ccmr1));
|
||||
|
||||
mask = STM32_LPTIM_CC1SEL | STM32_LPTIM_CC1E | STM32_LPTIM_CC1P;
|
||||
val = FIELD_PREP(STM32_LPTIM_CC1P, polarity);
|
||||
val |= FIELD_PREP(STM32_LPTIM_CC1E, enable);
|
||||
}
|
||||
|
||||
if (reenable) {
|
||||
u32 cfgr, presc;
|
||||
unsigned long rate;
|
||||
unsigned int delay_us;
|
||||
|
||||
ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CCMR1,
|
||||
channel ? STM32_LPTIM_CC2E : STM32_LPTIM_CC1E, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* After a write to the LPTIM_CCMRx register, a new write operation can only be
|
||||
* performed after a delay of at least (PRESC × 3) clock cycles
|
||||
*/
|
||||
ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr);
|
||||
if (ret)
|
||||
return ret;
|
||||
presc = FIELD_GET(STM32_LPTIM_PRESC, cfgr);
|
||||
rate = clk_get_rate(priv->clk) >> presc;
|
||||
if (!rate)
|
||||
return -EINVAL;
|
||||
delay_us = 3 * DIV_ROUND_UP(USEC_PER_SEC, rate);
|
||||
usleep_range(delay_us, delay_us * 2);
|
||||
}
|
||||
|
||||
return regmap_update_bits(priv->regmap, STM32_LPTIM_CCMR1, mask, val);
|
||||
}
|
||||
|
||||
static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
|
||||
unsigned long long prd, div, dty;
|
||||
struct pwm_state cstate;
|
||||
u32 val, mask, cfgr, presc = 0;
|
||||
u32 arr, val, mask, cfgr, presc = 0;
|
||||
bool reenable;
|
||||
int ret;
|
||||
|
||||
|
|
@ -45,10 +134,28 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
|
||||
if (!state->enabled) {
|
||||
if (cstate.enabled) {
|
||||
/* Disable LP timer */
|
||||
ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
|
||||
/* Disable CC channel if any */
|
||||
ret = stm32_pwm_lp_compare_channel_apply(priv, pwm->hwpwm, false,
|
||||
state->polarity);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(priv->regmap, pwm->hwpwm ?
|
||||
STM32_LPTIM_CCR2 : STM32_LPTIM_CMP, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Check if the timer can be disabled */
|
||||
ret = stm32_pwm_lp_update_allowed(priv, pwm->hwpwm);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret) {
|
||||
/* Disable LP timer */
|
||||
ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* disable clock to PWM counter */
|
||||
clk_disable(priv->clk);
|
||||
}
|
||||
|
|
@ -79,6 +186,23 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
dty = prd * state->duty_cycle;
|
||||
do_div(dty, state->period);
|
||||
|
||||
ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* When there are several channels, they share the same prescaler and reload value.
|
||||
* Check if this can be changed, or the values are the same for all channels.
|
||||
*/
|
||||
if (!stm32_pwm_lp_update_allowed(priv, pwm->hwpwm)) {
|
||||
ret = regmap_read(priv->regmap, STM32_LPTIM_ARR, &arr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if ((FIELD_GET(STM32_LPTIM_PRESC, cfgr) != presc) || (arr != prd - 1))
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (!cstate.enabled) {
|
||||
/* enable clock to drive PWM counter */
|
||||
ret = clk_enable(priv->clk);
|
||||
|
|
@ -86,15 +210,20 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if ((FIELD_GET(STM32_LPTIM_PRESC, cfgr) != presc) ||
|
||||
(FIELD_GET(STM32_LPTIM_WAVPOL, cfgr) != state->polarity)) {
|
||||
((FIELD_GET(STM32_LPTIM_WAVPOL, cfgr) != state->polarity) && !priv->num_cc_chans)) {
|
||||
val = FIELD_PREP(STM32_LPTIM_PRESC, presc);
|
||||
val |= FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
|
||||
mask = STM32_LPTIM_PRESC | STM32_LPTIM_WAVPOL;
|
||||
mask = STM32_LPTIM_PRESC;
|
||||
|
||||
if (!priv->num_cc_chans) {
|
||||
/*
|
||||
* WAVPOL bit is only available when no capature compare channel is used,
|
||||
* e.g. on LPTIMER instances that have only one output channel. CCMR1 is
|
||||
* used otherwise.
|
||||
*/
|
||||
val |= FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
|
||||
mask |= STM32_LPTIM_WAVPOL;
|
||||
}
|
||||
|
||||
/* Must disable LP timer to modify CFGR */
|
||||
reenable = true;
|
||||
|
|
@ -120,20 +249,27 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, prd - (1 + dty));
|
||||
/* Write CMP/CCRx register and ensure it's been properly written */
|
||||
ret = regmap_write(priv->regmap, pwm->hwpwm ? STM32_LPTIM_CCR2 : STM32_LPTIM_CMP,
|
||||
prd - (1 + dty));
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* ensure CMP & ARR registers are properly written */
|
||||
ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
|
||||
/* ensure ARR and CMP/CCRx registers are properly written */
|
||||
ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val, pwm->hwpwm ?
|
||||
(val & STM32_LPTIM_CMP2_ARROK) == STM32_LPTIM_CMP2_ARROK :
|
||||
(val & STM32_LPTIM_CMPOK_ARROK) == STM32_LPTIM_CMPOK_ARROK,
|
||||
100, 1000);
|
||||
if (ret) {
|
||||
dev_err(pwmchip_parent(chip), "ARR/CMP registers write issue\n");
|
||||
goto err;
|
||||
}
|
||||
ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
|
||||
STM32_LPTIM_CMPOKCF_ARROKCF);
|
||||
ret = regmap_write(priv->regmap, STM32_LPTIM_ICR, pwm->hwpwm ?
|
||||
STM32_LPTIM_CMP2OKCF_ARROKCF : STM32_LPTIM_CMPOKCF_ARROKCF);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = stm32_pwm_lp_compare_channel_apply(priv, pwm->hwpwm, true, state->polarity);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
|
@ -161,11 +297,22 @@ static int stm32_pwm_lp_get_state(struct pwm_chip *chip,
|
|||
{
|
||||
struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
|
||||
unsigned long rate = clk_get_rate(priv->clk);
|
||||
u32 val, presc, prd;
|
||||
u32 val, presc, prd, ccmr1;
|
||||
bool enabled;
|
||||
u64 tmp;
|
||||
|
||||
regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
|
||||
state->enabled = !!FIELD_GET(STM32_LPTIM_ENABLE, val);
|
||||
enabled = !!FIELD_GET(STM32_LPTIM_ENABLE, val);
|
||||
if (priv->num_cc_chans) {
|
||||
/* There's a CC chan, need to also check if it's enabled */
|
||||
regmap_read(priv->regmap, STM32_LPTIM_CCMR1, &ccmr1);
|
||||
if (pwm->hwpwm)
|
||||
enabled &= !!FIELD_GET(STM32_LPTIM_CC2E, ccmr1);
|
||||
else
|
||||
enabled &= !!FIELD_GET(STM32_LPTIM_CC1E, ccmr1);
|
||||
}
|
||||
state->enabled = enabled;
|
||||
|
||||
/* Keep PWM counter clock refcount in sync with PWM initial state */
|
||||
if (state->enabled) {
|
||||
int ret = clk_enable(priv->clk);
|
||||
|
|
@ -176,14 +323,21 @@ static int stm32_pwm_lp_get_state(struct pwm_chip *chip,
|
|||
|
||||
regmap_read(priv->regmap, STM32_LPTIM_CFGR, &val);
|
||||
presc = FIELD_GET(STM32_LPTIM_PRESC, val);
|
||||
state->polarity = FIELD_GET(STM32_LPTIM_WAVPOL, val);
|
||||
if (priv->num_cc_chans) {
|
||||
if (pwm->hwpwm)
|
||||
state->polarity = FIELD_GET(STM32_LPTIM_CC2P, ccmr1);
|
||||
else
|
||||
state->polarity = FIELD_GET(STM32_LPTIM_CC1P, ccmr1);
|
||||
} else {
|
||||
state->polarity = FIELD_GET(STM32_LPTIM_WAVPOL, val);
|
||||
}
|
||||
|
||||
regmap_read(priv->regmap, STM32_LPTIM_ARR, &prd);
|
||||
tmp = prd + 1;
|
||||
tmp = (tmp << presc) * NSEC_PER_SEC;
|
||||
state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);
|
||||
|
||||
regmap_read(priv->regmap, STM32_LPTIM_CMP, &val);
|
||||
regmap_read(priv->regmap, pwm->hwpwm ? STM32_LPTIM_CCR2 : STM32_LPTIM_CMP, &val);
|
||||
tmp = prd - val;
|
||||
tmp = (tmp << presc) * NSEC_PER_SEC;
|
||||
state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate);
|
||||
|
|
@ -201,15 +355,25 @@ static int stm32_pwm_lp_probe(struct platform_device *pdev)
|
|||
struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
|
||||
struct stm32_pwm_lp *priv;
|
||||
struct pwm_chip *chip;
|
||||
unsigned int npwm;
|
||||
int ret;
|
||||
|
||||
chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*priv));
|
||||
if (!ddata->num_cc_chans) {
|
||||
/* No dedicated CC channel, so there's only one PWM channel */
|
||||
npwm = 1;
|
||||
} else {
|
||||
/* There are dedicated CC channels, each with one PWM output */
|
||||
npwm = ddata->num_cc_chans;
|
||||
}
|
||||
|
||||
chip = devm_pwmchip_alloc(&pdev->dev, npwm, sizeof(*priv));
|
||||
if (IS_ERR(chip))
|
||||
return PTR_ERR(chip);
|
||||
priv = to_stm32_pwm_lp(chip);
|
||||
|
||||
priv->regmap = ddata->regmap;
|
||||
priv->clk = ddata->clk;
|
||||
priv->num_cc_chans = ddata->num_cc_chans;
|
||||
chip->ops = &stm32_pwm_lp_ops;
|
||||
|
||||
ret = devm_pwmchip_add(&pdev->dev, chip);
|
||||
|
|
@ -225,12 +389,15 @@ static int stm32_pwm_lp_suspend(struct device *dev)
|
|||
{
|
||||
struct pwm_chip *chip = dev_get_drvdata(dev);
|
||||
struct pwm_state state;
|
||||
unsigned int i;
|
||||
|
||||
pwm_get_state(&chip->pwms[0], &state);
|
||||
if (state.enabled) {
|
||||
dev_err(dev, "The consumer didn't stop us (%s)\n",
|
||||
chip->pwms[0].label);
|
||||
return -EBUSY;
|
||||
for (i = 0; i < chip->npwm; i++) {
|
||||
pwm_get_state(&chip->pwms[i], &state);
|
||||
if (state.enabled) {
|
||||
dev_err(dev, "The consumer didn't stop us (%s)\n",
|
||||
chip->pwms[i].label);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
return pinctrl_pm_select_sleep_state(dev);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -83,6 +83,7 @@ enum {
|
|||
#define BD96801_LDO6_VSEL_REG 0x26
|
||||
#define BD96801_LDO7_VSEL_REG 0x27
|
||||
#define BD96801_BUCK_VSEL_MASK 0x1F
|
||||
#define BD96805_BUCK_VSEL_MASK 0x3f
|
||||
#define BD96801_LDO_VSEL_MASK 0xff
|
||||
|
||||
#define BD96801_MASK_RAMP_DELAY 0xc0
|
||||
|
|
@ -90,6 +91,7 @@ enum {
|
|||
#define BD96801_BUCK_INT_VOUT_MASK 0xff
|
||||
|
||||
#define BD96801_BUCK_VOLTS 256
|
||||
#define BD96805_BUCK_VOLTS 64
|
||||
#define BD96801_LDO_VOLTS 256
|
||||
|
||||
#define BD96801_OVP_MASK 0x03
|
||||
|
|
@ -160,6 +162,30 @@ static const struct linear_range bd96801_buck_init_volts[] = {
|
|||
REGULATOR_LINEAR_RANGE(3300000 - 150000, 0xed, 0xff, 0),
|
||||
};
|
||||
|
||||
/* BD96802 uses same voltage ranges for bucks as BD96801 */
|
||||
#define bd96802_tune_volts bd96801_tune_volts
|
||||
#define bd96802_buck_init_volts bd96801_buck_init_volts
|
||||
|
||||
/*
|
||||
* On BD96805 we have similar "negative tuning range" as on BD96801, except
|
||||
* that the max tuning is -310 ... +310 mV (instead of the 150mV). We use same
|
||||
* approach as with the BD96801 ranges.
|
||||
*/
|
||||
static const struct linear_range bd96805_tune_volts[] = {
|
||||
REGULATOR_LINEAR_RANGE(310000, 0x00, 0x1F, 10000),
|
||||
REGULATOR_LINEAR_RANGE(0, 0x20, 0x3F, 10000),
|
||||
};
|
||||
|
||||
static const struct linear_range bd96805_buck_init_volts[] = {
|
||||
REGULATOR_LINEAR_RANGE(500000 - 310000, 0x00, 0xc8, 5000),
|
||||
REGULATOR_LINEAR_RANGE(1550000 - 310000, 0xc9, 0xec, 50000),
|
||||
REGULATOR_LINEAR_RANGE(3300000 - 310000, 0xed, 0xff, 0),
|
||||
};
|
||||
|
||||
/* BD96806 uses same voltage ranges for bucks as BD96805 */
|
||||
#define bd96806_tune_volts bd96805_tune_volts
|
||||
#define bd96806_buck_init_volts bd96805_buck_init_volts
|
||||
|
||||
static const struct linear_range bd96801_ldo_int_volts[] = {
|
||||
REGULATOR_LINEAR_RANGE(300000, 0x00, 0x78, 25000),
|
||||
REGULATOR_LINEAR_RANGE(3300000, 0x79, 0xff, 0),
|
||||
|
|
@ -198,89 +224,89 @@ struct bd96801_irqinfo {
|
|||
|
||||
static const struct bd96801_irqinfo buck1_irqinfo[] = {
|
||||
BD96801_IRQINFO(BD96801_PROT_OCP, "buck1-over-curr-h", 500,
|
||||
"bd96801-buck1-overcurr-h"),
|
||||
"buck1-overcurr-h"),
|
||||
BD96801_IRQINFO(BD96801_PROT_OCP, "buck1-over-curr-l", 500,
|
||||
"bd96801-buck1-overcurr-l"),
|
||||
"buck1-overcurr-l"),
|
||||
BD96801_IRQINFO(BD96801_PROT_OCP, "buck1-over-curr-n", 500,
|
||||
"bd96801-buck1-overcurr-n"),
|
||||
"buck1-overcurr-n"),
|
||||
BD96801_IRQINFO(BD96801_PROT_OVP, "buck1-over-voltage", 500,
|
||||
"bd96801-buck1-overvolt"),
|
||||
"buck1-overvolt"),
|
||||
BD96801_IRQINFO(BD96801_PROT_UVP, "buck1-under-voltage", 500,
|
||||
"bd96801-buck1-undervolt"),
|
||||
"buck1-undervolt"),
|
||||
BD96801_IRQINFO(BD96801_PROT_TEMP, "buck1-over-temp", 500,
|
||||
"bd96801-buck1-thermal")
|
||||
"buck1-thermal")
|
||||
};
|
||||
|
||||
static const struct bd96801_irqinfo buck2_irqinfo[] = {
|
||||
BD96801_IRQINFO(BD96801_PROT_OCP, "buck2-over-curr-h", 500,
|
||||
"bd96801-buck2-overcurr-h"),
|
||||
"buck2-overcurr-h"),
|
||||
BD96801_IRQINFO(BD96801_PROT_OCP, "buck2-over-curr-l", 500,
|
||||
"bd96801-buck2-overcurr-l"),
|
||||
"buck2-overcurr-l"),
|
||||
BD96801_IRQINFO(BD96801_PROT_OCP, "buck2-over-curr-n", 500,
|
||||
"bd96801-buck2-overcurr-n"),
|
||||
"buck2-overcurr-n"),
|
||||
BD96801_IRQINFO(BD96801_PROT_OVP, "buck2-over-voltage", 500,
|
||||
"bd96801-buck2-overvolt"),
|
||||
"buck2-overvolt"),
|
||||
BD96801_IRQINFO(BD96801_PROT_UVP, "buck2-under-voltage", 500,
|
||||
"bd96801-buck2-undervolt"),
|
||||
"buck2-undervolt"),
|
||||
BD96801_IRQINFO(BD96801_PROT_TEMP, "buck2-over-temp", 500,
|
||||
"bd96801-buck2-thermal")
|
||||
"buck2-thermal")
|
||||
};
|
||||
|
||||
static const struct bd96801_irqinfo buck3_irqinfo[] = {
|
||||
BD96801_IRQINFO(BD96801_PROT_OCP, "buck3-over-curr-h", 500,
|
||||
"bd96801-buck3-overcurr-h"),
|
||||
"buck3-overcurr-h"),
|
||||
BD96801_IRQINFO(BD96801_PROT_OCP, "buck3-over-curr-l", 500,
|
||||
"bd96801-buck3-overcurr-l"),
|
||||
"buck3-overcurr-l"),
|
||||
BD96801_IRQINFO(BD96801_PROT_OCP, "buck3-over-curr-n", 500,
|
||||
"bd96801-buck3-overcurr-n"),
|
||||
"buck3-overcurr-n"),
|
||||
BD96801_IRQINFO(BD96801_PROT_OVP, "buck3-over-voltage", 500,
|
||||
"bd96801-buck3-overvolt"),
|
||||
"buck3-overvolt"),
|
||||
BD96801_IRQINFO(BD96801_PROT_UVP, "buck3-under-voltage", 500,
|
||||
"bd96801-buck3-undervolt"),
|
||||
"buck3-undervolt"),
|
||||
BD96801_IRQINFO(BD96801_PROT_TEMP, "buck3-over-temp", 500,
|
||||
"bd96801-buck3-thermal")
|
||||
"buck3-thermal")
|
||||
};
|
||||
|
||||
static const struct bd96801_irqinfo buck4_irqinfo[] = {
|
||||
BD96801_IRQINFO(BD96801_PROT_OCP, "buck4-over-curr-h", 500,
|
||||
"bd96801-buck4-overcurr-h"),
|
||||
"buck4-overcurr-h"),
|
||||
BD96801_IRQINFO(BD96801_PROT_OCP, "buck4-over-curr-l", 500,
|
||||
"bd96801-buck4-overcurr-l"),
|
||||
"buck4-overcurr-l"),
|
||||
BD96801_IRQINFO(BD96801_PROT_OCP, "buck4-over-curr-n", 500,
|
||||
"bd96801-buck4-overcurr-n"),
|
||||
"buck4-overcurr-n"),
|
||||
BD96801_IRQINFO(BD96801_PROT_OVP, "buck4-over-voltage", 500,
|
||||
"bd96801-buck4-overvolt"),
|
||||
"buck4-overvolt"),
|
||||
BD96801_IRQINFO(BD96801_PROT_UVP, "buck4-under-voltage", 500,
|
||||
"bd96801-buck4-undervolt"),
|
||||
"buck4-undervolt"),
|
||||
BD96801_IRQINFO(BD96801_PROT_TEMP, "buck4-over-temp", 500,
|
||||
"bd96801-buck4-thermal")
|
||||
"buck4-thermal")
|
||||
};
|
||||
|
||||
static const struct bd96801_irqinfo ldo5_irqinfo[] = {
|
||||
BD96801_IRQINFO(BD96801_PROT_OCP, "ldo5-overcurr", 500,
|
||||
"bd96801-ldo5-overcurr"),
|
||||
"ldo5-overcurr"),
|
||||
BD96801_IRQINFO(BD96801_PROT_OVP, "ldo5-over-voltage", 500,
|
||||
"bd96801-ldo5-overvolt"),
|
||||
"ldo5-overvolt"),
|
||||
BD96801_IRQINFO(BD96801_PROT_UVP, "ldo5-under-voltage", 500,
|
||||
"bd96801-ldo5-undervolt"),
|
||||
"ldo5-undervolt"),
|
||||
};
|
||||
|
||||
static const struct bd96801_irqinfo ldo6_irqinfo[] = {
|
||||
BD96801_IRQINFO(BD96801_PROT_OCP, "ldo6-overcurr", 500,
|
||||
"bd96801-ldo6-overcurr"),
|
||||
"ldo6-overcurr"),
|
||||
BD96801_IRQINFO(BD96801_PROT_OVP, "ldo6-over-voltage", 500,
|
||||
"bd96801-ldo6-overvolt"),
|
||||
"ldo6-overvolt"),
|
||||
BD96801_IRQINFO(BD96801_PROT_UVP, "ldo6-under-voltage", 500,
|
||||
"bd96801-ldo6-undervolt"),
|
||||
"ldo6-undervolt"),
|
||||
};
|
||||
|
||||
static const struct bd96801_irqinfo ldo7_irqinfo[] = {
|
||||
BD96801_IRQINFO(BD96801_PROT_OCP, "ldo7-overcurr", 500,
|
||||
"bd96801-ldo7-overcurr"),
|
||||
"ldo7-overcurr"),
|
||||
BD96801_IRQINFO(BD96801_PROT_OVP, "ldo7-over-voltage", 500,
|
||||
"bd96801-ldo7-overvolt"),
|
||||
"ldo7-overvolt"),
|
||||
BD96801_IRQINFO(BD96801_PROT_UVP, "ldo7-under-voltage", 500,
|
||||
"bd96801-ldo7-undervolt"),
|
||||
"ldo7-undervolt"),
|
||||
};
|
||||
|
||||
struct bd96801_irq_desc {
|
||||
|
|
@ -302,6 +328,7 @@ struct bd96801_pmic_data {
|
|||
struct bd96801_regulator_data regulator_data[BD96801_NUM_REGULATORS];
|
||||
struct regmap *regmap;
|
||||
int fatal_ind;
|
||||
int num_regulators;
|
||||
};
|
||||
|
||||
static int ldo_map_notif(int irq, struct regulator_irq_data *rid,
|
||||
|
|
@ -503,6 +530,70 @@ static int bd96801_walk_regulator_dt(struct device *dev, struct regmap *regmap,
|
|||
* case later. What we can easly do for preparing is to not use static global
|
||||
* data for regulators though.
|
||||
*/
|
||||
static const struct bd96801_pmic_data bd96802_data = {
|
||||
.regulator_data = {
|
||||
{
|
||||
.desc = {
|
||||
.name = "buck1",
|
||||
.of_match = of_match_ptr("buck1"),
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.id = BD96801_BUCK1,
|
||||
.ops = &bd96801_buck_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.linear_ranges = bd96802_tune_volts,
|
||||
.n_linear_ranges = ARRAY_SIZE(bd96802_tune_volts),
|
||||
.n_voltages = BD96801_BUCK_VOLTS,
|
||||
.enable_reg = BD96801_REG_ENABLE,
|
||||
.enable_mask = BD96801_BUCK1_EN_MASK,
|
||||
.enable_is_inverted = true,
|
||||
.vsel_reg = BD96801_BUCK1_VSEL_REG,
|
||||
.vsel_mask = BD96801_BUCK_VSEL_MASK,
|
||||
.ramp_reg = BD96801_BUCK1_VSEL_REG,
|
||||
.ramp_mask = BD96801_MASK_RAMP_DELAY,
|
||||
.ramp_delay_table = &buck_ramp_table[0],
|
||||
.n_ramp_values = ARRAY_SIZE(buck_ramp_table),
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.init_ranges = bd96802_buck_init_volts,
|
||||
.num_ranges = ARRAY_SIZE(bd96802_buck_init_volts),
|
||||
.irq_desc = {
|
||||
.irqinfo = (struct bd96801_irqinfo *)&buck1_irqinfo[0],
|
||||
.num_irqs = ARRAY_SIZE(buck1_irqinfo),
|
||||
},
|
||||
},
|
||||
{
|
||||
.desc = {
|
||||
.name = "buck2",
|
||||
.of_match = of_match_ptr("buck2"),
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.id = BD96801_BUCK2,
|
||||
.ops = &bd96801_buck_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.linear_ranges = bd96802_tune_volts,
|
||||
.n_linear_ranges = ARRAY_SIZE(bd96802_tune_volts),
|
||||
.n_voltages = BD96801_BUCK_VOLTS,
|
||||
.enable_reg = BD96801_REG_ENABLE,
|
||||
.enable_mask = BD96801_BUCK2_EN_MASK,
|
||||
.enable_is_inverted = true,
|
||||
.vsel_reg = BD96801_BUCK2_VSEL_REG,
|
||||
.vsel_mask = BD96801_BUCK_VSEL_MASK,
|
||||
.ramp_reg = BD96801_BUCK2_VSEL_REG,
|
||||
.ramp_mask = BD96801_MASK_RAMP_DELAY,
|
||||
.ramp_delay_table = &buck_ramp_table[0],
|
||||
.n_ramp_values = ARRAY_SIZE(buck_ramp_table),
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.irq_desc = {
|
||||
.irqinfo = (struct bd96801_irqinfo *)&buck2_irqinfo[0],
|
||||
.num_irqs = ARRAY_SIZE(buck2_irqinfo),
|
||||
},
|
||||
.init_ranges = bd96802_buck_init_volts,
|
||||
.num_ranges = ARRAY_SIZE(bd96802_buck_init_volts),
|
||||
},
|
||||
},
|
||||
.num_regulators = 2,
|
||||
};
|
||||
|
||||
static const struct bd96801_pmic_data bd96801_data = {
|
||||
.regulator_data = {
|
||||
{
|
||||
|
|
@ -688,11 +779,265 @@ static const struct bd96801_pmic_data bd96801_data = {
|
|||
.ldo_vol_lvl = BD96801_LDO7_VOL_LVL_REG,
|
||||
},
|
||||
},
|
||||
.num_regulators = 7,
|
||||
};
|
||||
|
||||
static int initialize_pmic_data(struct device *dev,
|
||||
static const struct bd96801_pmic_data bd96805_data = {
|
||||
.regulator_data = {
|
||||
{
|
||||
.desc = {
|
||||
.name = "buck1",
|
||||
.of_match = of_match_ptr("buck1"),
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.id = BD96801_BUCK1,
|
||||
.ops = &bd96801_buck_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.linear_ranges = bd96805_tune_volts,
|
||||
.n_linear_ranges = ARRAY_SIZE(bd96805_tune_volts),
|
||||
.n_voltages = BD96805_BUCK_VOLTS,
|
||||
.enable_reg = BD96801_REG_ENABLE,
|
||||
.enable_mask = BD96801_BUCK1_EN_MASK,
|
||||
.enable_is_inverted = true,
|
||||
.vsel_reg = BD96801_BUCK1_VSEL_REG,
|
||||
.vsel_mask = BD96805_BUCK_VSEL_MASK,
|
||||
.ramp_reg = BD96801_BUCK1_VSEL_REG,
|
||||
.ramp_mask = BD96801_MASK_RAMP_DELAY,
|
||||
.ramp_delay_table = &buck_ramp_table[0],
|
||||
.n_ramp_values = ARRAY_SIZE(buck_ramp_table),
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.init_ranges = bd96805_buck_init_volts,
|
||||
.num_ranges = ARRAY_SIZE(bd96805_buck_init_volts),
|
||||
.irq_desc = {
|
||||
.irqinfo = (struct bd96801_irqinfo *)&buck1_irqinfo[0],
|
||||
.num_irqs = ARRAY_SIZE(buck1_irqinfo),
|
||||
},
|
||||
}, {
|
||||
.desc = {
|
||||
.name = "buck2",
|
||||
.of_match = of_match_ptr("buck2"),
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.id = BD96801_BUCK2,
|
||||
.ops = &bd96801_buck_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.linear_ranges = bd96805_tune_volts,
|
||||
.n_linear_ranges = ARRAY_SIZE(bd96805_tune_volts),
|
||||
.n_voltages = BD96805_BUCK_VOLTS,
|
||||
.enable_reg = BD96801_REG_ENABLE,
|
||||
.enable_mask = BD96801_BUCK2_EN_MASK,
|
||||
.enable_is_inverted = true,
|
||||
.vsel_reg = BD96801_BUCK2_VSEL_REG,
|
||||
.vsel_mask = BD96805_BUCK_VSEL_MASK,
|
||||
.ramp_reg = BD96801_BUCK2_VSEL_REG,
|
||||
.ramp_mask = BD96801_MASK_RAMP_DELAY,
|
||||
.ramp_delay_table = &buck_ramp_table[0],
|
||||
.n_ramp_values = ARRAY_SIZE(buck_ramp_table),
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.irq_desc = {
|
||||
.irqinfo = (struct bd96801_irqinfo *)&buck2_irqinfo[0],
|
||||
.num_irqs = ARRAY_SIZE(buck2_irqinfo),
|
||||
},
|
||||
.init_ranges = bd96805_buck_init_volts,
|
||||
.num_ranges = ARRAY_SIZE(bd96805_buck_init_volts),
|
||||
}, {
|
||||
.desc = {
|
||||
.name = "buck3",
|
||||
.of_match = of_match_ptr("buck3"),
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.id = BD96801_BUCK3,
|
||||
.ops = &bd96801_buck_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.linear_ranges = bd96805_tune_volts,
|
||||
.n_linear_ranges = ARRAY_SIZE(bd96805_tune_volts),
|
||||
.n_voltages = BD96805_BUCK_VOLTS,
|
||||
.enable_reg = BD96801_REG_ENABLE,
|
||||
.enable_mask = BD96801_BUCK3_EN_MASK,
|
||||
.enable_is_inverted = true,
|
||||
.vsel_reg = BD96801_BUCK3_VSEL_REG,
|
||||
.vsel_mask = BD96805_BUCK_VSEL_MASK,
|
||||
.ramp_reg = BD96801_BUCK3_VSEL_REG,
|
||||
.ramp_mask = BD96801_MASK_RAMP_DELAY,
|
||||
.ramp_delay_table = &buck_ramp_table[0],
|
||||
.n_ramp_values = ARRAY_SIZE(buck_ramp_table),
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.irq_desc = {
|
||||
.irqinfo = (struct bd96801_irqinfo *)&buck3_irqinfo[0],
|
||||
.num_irqs = ARRAY_SIZE(buck3_irqinfo),
|
||||
},
|
||||
.init_ranges = bd96805_buck_init_volts,
|
||||
.num_ranges = ARRAY_SIZE(bd96805_buck_init_volts),
|
||||
}, {
|
||||
.desc = {
|
||||
.name = "buck4",
|
||||
.of_match = of_match_ptr("buck4"),
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.id = BD96801_BUCK4,
|
||||
.ops = &bd96801_buck_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.linear_ranges = bd96805_tune_volts,
|
||||
.n_linear_ranges = ARRAY_SIZE(bd96805_tune_volts),
|
||||
.n_voltages = BD96805_BUCK_VOLTS,
|
||||
.enable_reg = BD96801_REG_ENABLE,
|
||||
.enable_mask = BD96801_BUCK4_EN_MASK,
|
||||
.enable_is_inverted = true,
|
||||
.vsel_reg = BD96801_BUCK4_VSEL_REG,
|
||||
.vsel_mask = BD96805_BUCK_VSEL_MASK,
|
||||
.ramp_reg = BD96801_BUCK4_VSEL_REG,
|
||||
.ramp_mask = BD96801_MASK_RAMP_DELAY,
|
||||
.ramp_delay_table = &buck_ramp_table[0],
|
||||
.n_ramp_values = ARRAY_SIZE(buck_ramp_table),
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.irq_desc = {
|
||||
.irqinfo = (struct bd96801_irqinfo *)&buck4_irqinfo[0],
|
||||
.num_irqs = ARRAY_SIZE(buck4_irqinfo),
|
||||
},
|
||||
.init_ranges = bd96805_buck_init_volts,
|
||||
.num_ranges = ARRAY_SIZE(bd96805_buck_init_volts),
|
||||
}, {
|
||||
.desc = {
|
||||
.name = "ldo5",
|
||||
.of_match = of_match_ptr("ldo5"),
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.id = BD96801_LDO5,
|
||||
.ops = &bd96801_ldo_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.linear_ranges = bd96801_ldo_int_volts,
|
||||
.n_linear_ranges = ARRAY_SIZE(bd96801_ldo_int_volts),
|
||||
.n_voltages = BD96801_LDO_VOLTS,
|
||||
.enable_reg = BD96801_REG_ENABLE,
|
||||
.enable_mask = BD96801_LDO5_EN_MASK,
|
||||
.enable_is_inverted = true,
|
||||
.vsel_reg = BD96801_LDO5_VSEL_REG,
|
||||
.vsel_mask = BD96801_LDO_VSEL_MASK,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.irq_desc = {
|
||||
.irqinfo = (struct bd96801_irqinfo *)&ldo5_irqinfo[0],
|
||||
.num_irqs = ARRAY_SIZE(ldo5_irqinfo),
|
||||
},
|
||||
.ldo_vol_lvl = BD96801_LDO5_VOL_LVL_REG,
|
||||
}, {
|
||||
.desc = {
|
||||
.name = "ldo6",
|
||||
.of_match = of_match_ptr("ldo6"),
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.id = BD96801_LDO6,
|
||||
.ops = &bd96801_ldo_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.linear_ranges = bd96801_ldo_int_volts,
|
||||
.n_linear_ranges = ARRAY_SIZE(bd96801_ldo_int_volts),
|
||||
.n_voltages = BD96801_LDO_VOLTS,
|
||||
.enable_reg = BD96801_REG_ENABLE,
|
||||
.enable_mask = BD96801_LDO6_EN_MASK,
|
||||
.enable_is_inverted = true,
|
||||
.vsel_reg = BD96801_LDO6_VSEL_REG,
|
||||
.vsel_mask = BD96801_LDO_VSEL_MASK,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.irq_desc = {
|
||||
.irqinfo = (struct bd96801_irqinfo *)&ldo6_irqinfo[0],
|
||||
.num_irqs = ARRAY_SIZE(ldo6_irqinfo),
|
||||
},
|
||||
.ldo_vol_lvl = BD96801_LDO6_VOL_LVL_REG,
|
||||
}, {
|
||||
.desc = {
|
||||
.name = "ldo7",
|
||||
.of_match = of_match_ptr("ldo7"),
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.id = BD96801_LDO7,
|
||||
.ops = &bd96801_ldo_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.linear_ranges = bd96801_ldo_int_volts,
|
||||
.n_linear_ranges = ARRAY_SIZE(bd96801_ldo_int_volts),
|
||||
.n_voltages = BD96801_LDO_VOLTS,
|
||||
.enable_reg = BD96801_REG_ENABLE,
|
||||
.enable_mask = BD96801_LDO7_EN_MASK,
|
||||
.enable_is_inverted = true,
|
||||
.vsel_reg = BD96801_LDO7_VSEL_REG,
|
||||
.vsel_mask = BD96801_LDO_VSEL_MASK,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.irq_desc = {
|
||||
.irqinfo = (struct bd96801_irqinfo *)&ldo7_irqinfo[0],
|
||||
.num_irqs = ARRAY_SIZE(ldo7_irqinfo),
|
||||
},
|
||||
.ldo_vol_lvl = BD96801_LDO7_VOL_LVL_REG,
|
||||
},
|
||||
},
|
||||
.num_regulators = 7,
|
||||
};
|
||||
|
||||
static const struct bd96801_pmic_data bd96806_data = {
|
||||
.regulator_data = {
|
||||
{
|
||||
.desc = {
|
||||
.name = "buck1",
|
||||
.of_match = of_match_ptr("buck1"),
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.id = BD96801_BUCK1,
|
||||
.ops = &bd96801_buck_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.linear_ranges = bd96806_tune_volts,
|
||||
.n_linear_ranges = ARRAY_SIZE(bd96806_tune_volts),
|
||||
.n_voltages = BD96805_BUCK_VOLTS,
|
||||
.enable_reg = BD96801_REG_ENABLE,
|
||||
.enable_mask = BD96801_BUCK1_EN_MASK,
|
||||
.enable_is_inverted = true,
|
||||
.vsel_reg = BD96801_BUCK1_VSEL_REG,
|
||||
.vsel_mask = BD96805_BUCK_VSEL_MASK,
|
||||
.ramp_reg = BD96801_BUCK1_VSEL_REG,
|
||||
.ramp_mask = BD96801_MASK_RAMP_DELAY,
|
||||
.ramp_delay_table = &buck_ramp_table[0],
|
||||
.n_ramp_values = ARRAY_SIZE(buck_ramp_table),
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.init_ranges = bd96806_buck_init_volts,
|
||||
.num_ranges = ARRAY_SIZE(bd96806_buck_init_volts),
|
||||
.irq_desc = {
|
||||
.irqinfo = (struct bd96801_irqinfo *)&buck1_irqinfo[0],
|
||||
.num_irqs = ARRAY_SIZE(buck1_irqinfo),
|
||||
},
|
||||
},
|
||||
{
|
||||
.desc = {
|
||||
.name = "buck2",
|
||||
.of_match = of_match_ptr("buck2"),
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.id = BD96801_BUCK2,
|
||||
.ops = &bd96801_buck_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.linear_ranges = bd96806_tune_volts,
|
||||
.n_linear_ranges = ARRAY_SIZE(bd96806_tune_volts),
|
||||
.n_voltages = BD96805_BUCK_VOLTS,
|
||||
.enable_reg = BD96801_REG_ENABLE,
|
||||
.enable_mask = BD96801_BUCK2_EN_MASK,
|
||||
.enable_is_inverted = true,
|
||||
.vsel_reg = BD96801_BUCK2_VSEL_REG,
|
||||
.vsel_mask = BD96805_BUCK_VSEL_MASK,
|
||||
.ramp_reg = BD96801_BUCK2_VSEL_REG,
|
||||
.ramp_mask = BD96801_MASK_RAMP_DELAY,
|
||||
.ramp_delay_table = &buck_ramp_table[0],
|
||||
.n_ramp_values = ARRAY_SIZE(buck_ramp_table),
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.irq_desc = {
|
||||
.irqinfo = (struct bd96801_irqinfo *)&buck2_irqinfo[0],
|
||||
.num_irqs = ARRAY_SIZE(buck2_irqinfo),
|
||||
},
|
||||
.init_ranges = bd96806_buck_init_volts,
|
||||
.num_ranges = ARRAY_SIZE(bd96806_buck_init_volts),
|
||||
},
|
||||
},
|
||||
.num_regulators = 2,
|
||||
};
|
||||
|
||||
static int initialize_pmic_data(struct platform_device *pdev,
|
||||
struct bd96801_pmic_data *pdata)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int r, i;
|
||||
|
||||
/*
|
||||
|
|
@ -700,7 +1045,7 @@ static int initialize_pmic_data(struct device *dev,
|
|||
* wish to modify IRQ information independently for each driver
|
||||
* instance.
|
||||
*/
|
||||
for (r = 0; r < BD96801_NUM_REGULATORS; r++) {
|
||||
for (r = 0; r < pdata->num_regulators; r++) {
|
||||
const struct bd96801_irqinfo *template;
|
||||
struct bd96801_irqinfo *new;
|
||||
int num_infos;
|
||||
|
|
@ -741,8 +1086,7 @@ static int bd96801_rdev_errb_irqs(struct platform_device *pdev,
|
|||
int i;
|
||||
void *retp;
|
||||
static const char * const single_out_errb_irqs[] = {
|
||||
"bd96801-%s-pvin-err", "bd96801-%s-ovp-err",
|
||||
"bd96801-%s-uvp-err", "bd96801-%s-shdn-err",
|
||||
"%s-pvin-err", "%s-ovp-err", "%s-uvp-err", "%s-shdn-err",
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(single_out_errb_irqs); i++) {
|
||||
|
|
@ -779,12 +1123,10 @@ static int bd96801_global_errb_irqs(struct platform_device *pdev,
|
|||
int i, num_irqs;
|
||||
void *retp;
|
||||
static const char * const global_errb_irqs[] = {
|
||||
"bd96801-otp-err", "bd96801-dbist-err", "bd96801-eep-err",
|
||||
"bd96801-abist-err", "bd96801-prstb-err", "bd96801-drmoserr1",
|
||||
"bd96801-drmoserr2", "bd96801-slave-err", "bd96801-vref-err",
|
||||
"bd96801-tsd", "bd96801-uvlo-err", "bd96801-ovlo-err",
|
||||
"bd96801-osc-err", "bd96801-pon-err", "bd96801-poff-err",
|
||||
"bd96801-cmd-shdn-err", "bd96801-int-shdn-err"
|
||||
"otp-err", "dbist-err", "eep-err", "abist-err", "prstb-err",
|
||||
"drmoserr1", "drmoserr2", "slave-err", "vref-err", "tsd",
|
||||
"uvlo-err", "ovlo-err", "osc-err", "pon-err", "poff-err",
|
||||
"cmd-shdn-err", "int-shdn-err"
|
||||
};
|
||||
|
||||
num_irqs = ARRAY_SIZE(global_errb_irqs);
|
||||
|
|
@ -869,6 +1211,7 @@ static int bd96801_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct regulator_dev *ldo_errs_rdev_arr[BD96801_NUM_LDOS];
|
||||
struct regulator_dev *all_rdevs[BD96801_NUM_REGULATORS];
|
||||
struct bd96801_pmic_data *pdata_template;
|
||||
struct bd96801_regulator_data *rdesc;
|
||||
struct regulator_config config = {};
|
||||
int ldo_errs_arr[BD96801_NUM_LDOS];
|
||||
|
|
@ -881,12 +1224,16 @@ static int bd96801_probe(struct platform_device *pdev)
|
|||
|
||||
parent = pdev->dev.parent;
|
||||
|
||||
pdata = devm_kmemdup(&pdev->dev, &bd96801_data, sizeof(bd96801_data),
|
||||
pdata_template = (struct bd96801_pmic_data *)platform_get_device_id(pdev)->driver_data;
|
||||
if (!pdata_template)
|
||||
return -ENODEV;
|
||||
|
||||
pdata = devm_kmemdup(&pdev->dev, pdata_template, sizeof(bd96801_data),
|
||||
GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
if (initialize_pmic_data(&pdev->dev, pdata))
|
||||
if (initialize_pmic_data(pdev, pdata))
|
||||
return -ENOMEM;
|
||||
|
||||
pdata->regmap = dev_get_regmap(parent, NULL);
|
||||
|
|
@ -909,11 +1256,11 @@ static int bd96801_probe(struct platform_device *pdev)
|
|||
use_errb = true;
|
||||
|
||||
ret = bd96801_walk_regulator_dt(&pdev->dev, pdata->regmap, rdesc,
|
||||
BD96801_NUM_REGULATORS);
|
||||
pdata->num_regulators);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pdata->regulator_data); i++) {
|
||||
for (i = 0; i < pdata->num_regulators; i++) {
|
||||
struct regulator_dev *rdev;
|
||||
struct bd96801_irq_desc *idesc = &rdesc[i].irq_desc;
|
||||
int j;
|
||||
|
|
@ -926,6 +1273,7 @@ static int bd96801_probe(struct platform_device *pdev)
|
|||
rdesc[i].desc.name);
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
|
||||
all_rdevs[i] = rdev;
|
||||
/*
|
||||
* LDOs don't have own temperature monitoring. If temperature
|
||||
|
|
@ -956,12 +1304,12 @@ static int bd96801_probe(struct platform_device *pdev)
|
|||
if (temp_notif_ldos) {
|
||||
int irq;
|
||||
struct regulator_irq_desc tw_desc = {
|
||||
.name = "bd96801-core-thermal",
|
||||
.name = "core-thermal",
|
||||
.irq_off_ms = 500,
|
||||
.map_event = ldo_map_notif,
|
||||
};
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "bd96801-core-thermal");
|
||||
irq = platform_get_irq_byname(pdev, "core-thermal");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
|
|
@ -975,14 +1323,17 @@ static int bd96801_probe(struct platform_device *pdev)
|
|||
|
||||
if (use_errb)
|
||||
return bd96801_global_errb_irqs(pdev, all_rdevs,
|
||||
ARRAY_SIZE(all_rdevs));
|
||||
pdata->num_regulators);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id bd96801_pmic_id[] = {
|
||||
{ "bd96801-regulator", },
|
||||
{ }
|
||||
{ "bd96801-regulator", (kernel_ulong_t)&bd96801_data },
|
||||
{ "bd96802-regulator", (kernel_ulong_t)&bd96802_data },
|
||||
{ "bd96805-regulator", (kernel_ulong_t)&bd96805_data },
|
||||
{ "bd96806-regulator", (kernel_ulong_t)&bd96806_data },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, bd96801_pmic_id);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,26 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* PMU ID register values; also used as device type */
|
||||
#define BCM590XX_PMUID_BCM59054 0x54
|
||||
#define BCM590XX_PMUID_BCM59056 0x56
|
||||
|
||||
/* Known chip revision IDs */
|
||||
#define BCM59054_REV_DIGITAL_A1 1
|
||||
#define BCM59054_REV_ANALOG_A1 2
|
||||
|
||||
#define BCM59056_REV_DIGITAL_A0 1
|
||||
#define BCM59056_REV_ANALOG_A0 1
|
||||
|
||||
#define BCM59056_REV_DIGITAL_B0 2
|
||||
#define BCM59056_REV_ANALOG_B0 2
|
||||
|
||||
/* regmap types */
|
||||
enum bcm590xx_regmap_type {
|
||||
BCM590XX_REGMAP_PRI,
|
||||
BCM590XX_REGMAP_SEC,
|
||||
};
|
||||
|
||||
/* max register address */
|
||||
#define BCM590XX_MAX_REGISTER_PRI 0xe7
|
||||
#define BCM590XX_MAX_REGISTER_SEC 0xf0
|
||||
|
|
@ -24,6 +44,13 @@ struct bcm590xx {
|
|||
struct regmap *regmap_pri;
|
||||
struct regmap *regmap_sec;
|
||||
unsigned int id;
|
||||
|
||||
/* PMU ID value; also used as device type */
|
||||
u8 pmu_id;
|
||||
|
||||
/* Chip revision, read from PMUREV reg */
|
||||
u8 rev_digital;
|
||||
u8 rev_analog;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_MFD_BCM590XX_H */
|
||||
|
|
|
|||
165
include/linux/mfd/max77759.h
Normal file
165
include/linux/mfd/max77759.h
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright 2020 Google Inc.
|
||||
* Copyright 2025 Linaro Ltd.
|
||||
*
|
||||
* Maxim MAX77759 core driver
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_MAX77759_H
|
||||
#define __LINUX_MFD_MAX77759_H
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define MAX77759_PMIC_REG_PMIC_ID 0x00
|
||||
#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_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
|
||||
#define MAX77759_MAXQ_REG_AP_DATAOUT0 0x81
|
||||
#define MAX77759_MAXQ_REG_AP_DATAOUT32 0xa1
|
||||
#define MAX77759_MAXQ_REG_AP_DATAIN0 0xb1
|
||||
#define MAX77759_MAXQ_REG_UIC_SWRST 0xe0
|
||||
|
||||
#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
|
||||
#define MAX77759_CHGR_REG_CHG_INT_OK 0xb4
|
||||
#define MAX77759_CHGR_REG_CHG_DETAILS_00 0xb5
|
||||
#define MAX77759_CHGR_REG_CHG_DETAILS_01 0xb6
|
||||
#define MAX77759_CHGR_REG_CHG_DETAILS_02 0xb7
|
||||
#define MAX77759_CHGR_REG_CHG_DETAILS_03 0xb8
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_00 0xb9
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_01 0xba
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_02 0xbb
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_03 0xbc
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_04 0xbd
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_05 0xbe
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_06 0xbf
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_07 0xc0
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_08 0xc1
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_09 0xc2
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_10 0xc3
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_11 0xc4
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_12 0xc5
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_13 0xc6
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_14 0xc7
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_15 0xc8
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_16 0xc9
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_17 0xca
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_18 0xcb
|
||||
#define MAX77759_CHGR_REG_CHG_CNFG_19 0xcc
|
||||
|
||||
/* MaxQ opcodes for max77759_maxq_command() */
|
||||
#define MAX77759_MAXQ_OPCODE_MAXLENGTH (MAX77759_MAXQ_REG_AP_DATAOUT32 - \
|
||||
MAX77759_MAXQ_REG_AP_DATAOUT0 + \
|
||||
1)
|
||||
|
||||
#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
|
||||
|
||||
/**
|
||||
* struct max77759 - core max77759 internal data structure
|
||||
*
|
||||
* @regmap_top: Regmap for accessing TOP registers
|
||||
* @maxq_lock: Lock for serializing access to MaxQ
|
||||
* @regmap_maxq: Regmap for accessing MaxQ registers
|
||||
* @cmd_done: Used to signal completion of a MaxQ command
|
||||
* @regmap_charger: Regmap for accessing charger registers
|
||||
*
|
||||
* The MAX77759 comprises several sub-blocks, namely TOP, MaxQ, Charger,
|
||||
* Fuel Gauge, and TCPCI.
|
||||
*/
|
||||
struct max77759 {
|
||||
struct regmap *regmap_top;
|
||||
|
||||
/* This protects MaxQ commands - only one can be active */
|
||||
struct mutex maxq_lock;
|
||||
struct regmap *regmap_maxq;
|
||||
struct completion cmd_done;
|
||||
|
||||
struct regmap *regmap_charger;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct max77759_maxq_command - structure containing the MaxQ command to
|
||||
* send
|
||||
*
|
||||
* @length: The number of bytes to send.
|
||||
* @cmd: The data to send.
|
||||
*/
|
||||
struct max77759_maxq_command {
|
||||
u8 length;
|
||||
u8 cmd[] __counted_by(length);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct max77759_maxq_response - structure containing the MaxQ response
|
||||
*
|
||||
* @length: The number of bytes to receive.
|
||||
* @rsp: The data received. Must have at least @length bytes space.
|
||||
*/
|
||||
struct max77759_maxq_response {
|
||||
u8 length;
|
||||
u8 rsp[] __counted_by(length);
|
||||
};
|
||||
|
||||
/**
|
||||
* max77759_maxq_command() - issue a MaxQ command and wait for the response
|
||||
* and associated data
|
||||
*
|
||||
* @max77759: The core max77759 device handle.
|
||||
* @cmd: The command to be sent.
|
||||
* @rsp: Any response data associated with the command will be copied here;
|
||||
* can be %NULL if the command has no response (other than ACK).
|
||||
*
|
||||
* Return: 0 on success, a negative error number otherwise.
|
||||
*/
|
||||
int max77759_maxq_command(struct max77759 *max77759,
|
||||
const struct max77759_maxq_command *cmd,
|
||||
struct max77759_maxq_response *rsp);
|
||||
|
||||
#endif /* __LINUX_MFD_MAX77759_H */
|
||||
|
|
@ -40,7 +40,9 @@
|
|||
* INTB status registers are at range 0x5c ... 0x63
|
||||
*/
|
||||
#define BD96801_REG_INT_SYS_ERRB1 0x52
|
||||
#define BD96801_REG_INT_BUCK2_ERRB 0x56
|
||||
#define BD96801_REG_INT_SYS_INTB 0x5c
|
||||
#define BD96801_REG_INT_BUCK2_INTB 0x5e
|
||||
#define BD96801_REG_INT_LDO7_INTB 0x63
|
||||
|
||||
/* MASK registers */
|
||||
|
|
|
|||
74
include/linux/mfd/rohm-bd96802.h
Normal file
74
include/linux/mfd/rohm-bd96802.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2025 ROHM Semiconductors
|
||||
*
|
||||
* The digital interface of trhe BD96802 PMIC is a reduced version of the
|
||||
* BD96801. Hence the BD96801 definitions are used for registers and masks
|
||||
* while this header only holds the IRQ definitions - mainly to avoid gaps in
|
||||
* IRQ numbers caused by the lack of some BUCKs / LDOs and their respective
|
||||
* IRQs.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_BD96802_H__
|
||||
#define __LINUX_MFD_BD96802_H__
|
||||
|
||||
/* ERRB IRQs */
|
||||
enum {
|
||||
/* Reg 0x52, 0x53, 0x54 - ERRB system IRQs */
|
||||
BD96802_OTP_ERR_STAT,
|
||||
BD96802_DBIST_ERR_STAT,
|
||||
BD96802_EEP_ERR_STAT,
|
||||
BD96802_ABIST_ERR_STAT,
|
||||
BD96802_PRSTB_ERR_STAT,
|
||||
BD96802_DRMOS1_ERR_STAT,
|
||||
BD96802_DRMOS2_ERR_STAT,
|
||||
BD96802_SLAVE_ERR_STAT,
|
||||
BD96802_VREF_ERR_STAT,
|
||||
BD96802_TSD_ERR_STAT,
|
||||
BD96802_UVLO_ERR_STAT,
|
||||
BD96802_OVLO_ERR_STAT,
|
||||
BD96802_OSC_ERR_STAT,
|
||||
BD96802_PON_ERR_STAT,
|
||||
BD96802_POFF_ERR_STAT,
|
||||
BD96802_CMD_SHDN_ERR_STAT,
|
||||
BD96802_INT_SHDN_ERR_STAT,
|
||||
|
||||
/* Reg 0x55 BUCK1 ERR IRQs */
|
||||
BD96802_BUCK1_PVIN_ERR_STAT,
|
||||
BD96802_BUCK1_OVP_ERR_STAT,
|
||||
BD96802_BUCK1_UVP_ERR_STAT,
|
||||
BD96802_BUCK1_SHDN_ERR_STAT,
|
||||
|
||||
/* Reg 0x56 BUCK2 ERR IRQs */
|
||||
BD96802_BUCK2_PVIN_ERR_STAT,
|
||||
BD96802_BUCK2_OVP_ERR_STAT,
|
||||
BD96802_BUCK2_UVP_ERR_STAT,
|
||||
BD96802_BUCK2_SHDN_ERR_STAT,
|
||||
};
|
||||
|
||||
/* INTB IRQs */
|
||||
enum {
|
||||
/* Reg 0x5c (System INTB) */
|
||||
BD96802_TW_STAT,
|
||||
BD96802_WDT_ERR_STAT,
|
||||
BD96802_I2C_ERR_STAT,
|
||||
BD96802_CHIP_IF_ERR_STAT,
|
||||
|
||||
/* Reg 0x5d (BUCK1 INTB) */
|
||||
BD96802_BUCK1_OCPH_STAT,
|
||||
BD96802_BUCK1_OCPL_STAT,
|
||||
BD96802_BUCK1_OCPN_STAT,
|
||||
BD96802_BUCK1_OVD_STAT,
|
||||
BD96802_BUCK1_UVD_STAT,
|
||||
BD96802_BUCK1_TW_CH_STAT,
|
||||
|
||||
/* Reg 0x5e (BUCK2 INTB) */
|
||||
BD96802_BUCK2_OCPH_STAT,
|
||||
BD96802_BUCK2_OCPL_STAT,
|
||||
BD96802_BUCK2_OCPN_STAT,
|
||||
BD96802_BUCK2_OVD_STAT,
|
||||
BD96802_BUCK2_UVD_STAT,
|
||||
BD96802_BUCK2_TW_CH_STAT,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -17,6 +17,9 @@ enum rohm_chip_type {
|
|||
ROHM_CHIP_TYPE_BD71837,
|
||||
ROHM_CHIP_TYPE_BD71847,
|
||||
ROHM_CHIP_TYPE_BD96801,
|
||||
ROHM_CHIP_TYPE_BD96802,
|
||||
ROHM_CHIP_TYPE_BD96805,
|
||||
ROHM_CHIP_TYPE_BD96806,
|
||||
ROHM_CHIP_TYPE_AMOUNT
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -17,20 +17,30 @@
|
|||
#define STM32_LPTIM_IER 0x08 /* Interrupt Enable Reg */
|
||||
#define STM32_LPTIM_CFGR 0x0C /* Configuration Reg */
|
||||
#define STM32_LPTIM_CR 0x10 /* Control Reg */
|
||||
#define STM32_LPTIM_CMP 0x14 /* Compare Reg */
|
||||
#define STM32_LPTIM_CMP 0x14 /* Compare Reg (MP25 CCR1) */
|
||||
#define STM32_LPTIM_ARR 0x18 /* Autoreload Reg */
|
||||
#define STM32_LPTIM_CNT 0x1C /* Counter Reg */
|
||||
#define STM32_LPTIM_CCMR1 0x2C /* Capture/Compare Mode MP25 */
|
||||
#define STM32_LPTIM_CCR2 0x34 /* Compare Reg2 MP25 */
|
||||
|
||||
#define STM32_LPTIM_HWCFGR2 0x3EC /* Hardware configuration register 2 - MP25 */
|
||||
#define STM32_LPTIM_HWCFGR1 0x3F0 /* Hardware configuration register 1 - MP15 */
|
||||
#define STM32_LPTIM_VERR 0x3F4 /* Version identification register - MP15 */
|
||||
|
||||
/* STM32_LPTIM_ISR - bit fields */
|
||||
#define STM32_LPTIM_DIEROK_ARROK (BIT(24) | BIT(4)) /* MP25 */
|
||||
#define STM32_LPTIM_CMP2_ARROK (BIT(19) | BIT(4))
|
||||
#define STM32_LPTIM_CMPOK_ARROK GENMASK(4, 3)
|
||||
#define STM32_LPTIM_ARROK BIT(4)
|
||||
#define STM32_LPTIM_CMPOK BIT(3)
|
||||
|
||||
/* STM32_LPTIM_ICR - bit fields */
|
||||
#define STM32_LPTIM_ARRMCF BIT(1)
|
||||
#define STM32_LPTIM_DIEROKCF_ARROKCF (BIT(24) | BIT(4)) /* MP25 */
|
||||
#define STM32_LPTIM_CMP2OKCF_ARROKCF (BIT(19) | BIT(4))
|
||||
#define STM32_LPTIM_CMPOKCF_ARROKCF GENMASK(4, 3)
|
||||
#define STM32_LPTIM_ARRMCF BIT(1)
|
||||
|
||||
/* STM32_LPTIM_IER - bit flieds */
|
||||
/* STM32_LPTIM_IER - bit fields */
|
||||
#define STM32_LPTIM_ARRMIE BIT(1)
|
||||
|
||||
/* STM32_LPTIM_CR - bit fields */
|
||||
|
|
@ -53,16 +63,37 @@
|
|||
/* STM32_LPTIM_ARR */
|
||||
#define STM32_LPTIM_MAX_ARR 0xFFFF
|
||||
|
||||
/* STM32_LPTIM_CCMR1 */
|
||||
#define STM32_LPTIM_CC2P GENMASK(19, 18)
|
||||
#define STM32_LPTIM_CC2E BIT(17)
|
||||
#define STM32_LPTIM_CC2SEL BIT(16)
|
||||
#define STM32_LPTIM_CC1P GENMASK(3, 2)
|
||||
#define STM32_LPTIM_CC1E BIT(1)
|
||||
#define STM32_LPTIM_CC1SEL BIT(0)
|
||||
|
||||
/* STM32_LPTIM_HWCFGR1 */
|
||||
#define STM32_LPTIM_HWCFGR1_ENCODER BIT(16)
|
||||
|
||||
/* STM32_LPTIM_HWCFGR2 */
|
||||
#define STM32_LPTIM_HWCFGR2_CHAN_NUM GENMASK(3, 0)
|
||||
|
||||
/* STM32_LPTIM_VERR */
|
||||
#define STM32_LPTIM_VERR_23 0x23 /* STM32MP25 */
|
||||
|
||||
/**
|
||||
* struct stm32_lptimer - STM32 Low-Power Timer data assigned by parent device
|
||||
* @clk: clock reference for this instance
|
||||
* @regmap: register map reference for this instance
|
||||
* @has_encoder: indicates this Low-Power Timer supports encoder mode
|
||||
* @num_cc_chans: indicates the number of capture/compare channels
|
||||
* @version: indicates the major and minor revision of the controller
|
||||
*/
|
||||
struct stm32_lptimer {
|
||||
struct clk *clk;
|
||||
struct regmap *regmap;
|
||||
bool has_encoder;
|
||||
unsigned int num_cc_chans;
|
||||
u32 version;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue