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:
Lee Jones 2025-05-22 15:49:34 +01:00
34 changed files with 4523 additions and 512 deletions

View file

@ -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

View file

@ -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>;
};
...
};
};

View 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>;
};
};
};
};

View 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>;
};
};
};
};
};

View file

@ -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

View 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>;
};
};
};
};

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View 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");

View file

@ -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

View file

@ -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

View file

@ -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
View 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");

View file

@ -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 = &regulator_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 = &regulator_res[num_intb + i];
struct resource *res = &regulator_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");

View file

@ -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;

View file

@ -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

View file

@ -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

View 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");

View file

@ -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

View file

@ -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);

View file

@ -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 */

View 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 */

View file

@ -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 */

View 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

View file

@ -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
};

View file

@ -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