regulator: Use container_of_const() when all types are

Merge series from Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>:

Use container_of_const(), which is preferred over container_of(), when
the argument 'ptr' and returned pointer are already const, for better
code safety and readability.

Some drivers already have const everywhere, so container_of_const can be
directly used. In few other drivers, the final pointer can be constified
that way.
This commit is contained in:
Mark Brown 2025-11-26 21:21:57 +00:00
commit c67bb84434
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
27 changed files with 2168 additions and 35 deletions

View File

@ -0,0 +1,161 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/mfd/nxp,pf1550.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP PF1550 Power Management IC
maintainers:
- Samuel Kayode <samuel.kayode@savoirfairelinux.com>
description:
PF1550 PMIC provides battery charging and power supply for low power IoT and
wearable applications. This device consists of an i2c controlled MFD that
includes regulators, battery charging and an onkey/power button.
$ref: /schemas/power/supply/power-supply.yaml
properties:
compatible:
const: nxp,pf1550
reg:
maxItems: 1
interrupts:
maxItems: 1
wakeup-source: true
regulators:
type: object
additionalProperties: false
patternProperties:
"^(ldo[1-3]|sw[1-3]|vrefddr)$":
type: object
$ref: /schemas/regulator/regulator.yaml
description:
regulator configuration for ldo1-3, buck converters(sw1-3)
and DDR termination reference voltage (vrefddr)
unevaluatedProperties: false
monitored-battery:
description: |
A phandle to a monitored battery node that contains a valid value
for:
constant-charge-voltage-max-microvolt.
nxp,thermal-regulation-celsius:
description:
Temperature threshold for thermal regulation of charger in celsius.
enum: [ 80, 95, 110, 125 ]
nxp,min-system-microvolt:
description:
System specific lower limit voltage.
enum: [ 3500000, 3700000, 4300000 ]
nxp,disable-key-power:
type: boolean
description:
Disable power-down using a long key-press. The onkey driver will remove
support for the KEY_POWER key press when triggered using a long press of
the onkey.
required:
- compatible
- reg
- interrupts
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/input/linux-event-codes.h>
battery: battery-cell {
compatible = "simple-battery";
constant-charge-voltage-max-microvolt = <4400000>;
};
i2c {
#address-cells = <1>;
#size-cells = <0>;
pmic@8 {
compatible = "nxp,pf1550";
reg = <0x8>;
interrupt-parent = <&gpio1>;
interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
wakeup-source;
monitored-battery = <&battery>;
nxp,min-system-microvolt = <4300000>;
nxp,thermal-regulation-celsius = <80>;
regulators {
sw1_reg: sw1 {
regulator-name = "sw1";
regulator-min-microvolt = <600000>;
regulator-max-microvolt = <1387500>;
regulator-always-on;
regulator-ramp-delay = <6250>;
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-min-microvolt = <1270000>;
};
};
sw2_reg: sw2 {
regulator-name = "sw2";
regulator-min-microvolt = <600000>;
regulator-max-microvolt = <1387500>;
regulator-always-on;
regulator-state-mem {
regulator-on-in-suspend;
};
};
sw3_reg: sw3 {
regulator-name = "sw3";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
regulator-state-mem {
regulator-on-in-suspend;
};
};
vldo1_reg: ldo1 {
regulator-name = "ldo1";
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
regulator-state-mem {
regulator-off-in-suspend;
};
};
vldo2_reg: ldo2 {
regulator-name = "ldo2";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
vldo3_reg: ldo3 {
regulator-name = "ldo3";
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
};
};
};

View File

@ -18636,6 +18636,17 @@ S: Maintained
F: Documentation/devicetree/bindings/regulator/nxp,pf5300.yaml F: Documentation/devicetree/bindings/regulator/nxp,pf5300.yaml
F: drivers/regulator/pf530x-regulator.c F: drivers/regulator/pf530x-regulator.c
NXP PF1550 PMIC MFD DRIVER
M: Samuel Kayode <samuel.kayode@savoirfairelinux.com>
L: imx@lists.linux.dev
S: Maintained
F: Documentation/devicetree/bindings/mfd/nxp,pf1550.yaml
F: drivers/input/misc/pf1550-onkey.c
F: drivers/mfd/pf1550.c
F: drivers/power/supply/pf1550-charger.c
F: drivers/regulator/pf1550-regulator.c
F: include/linux/mfd/pfd1550.h
NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER
M: Jagan Teki <jagan@amarulasolutions.com> M: Jagan Teki <jagan@amarulasolutions.com>
S: Maintained S: Maintained

View File

@ -190,6 +190,17 @@ config INPUT_PCSPKR
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called pcspkr. module will be called pcspkr.
config INPUT_PF1550_ONKEY
tristate "NXP PF1550 Onkey support"
depends on MFD_PF1550
help
Say Y here if you want support for PF1550 PMIC. Onkey can trigger
release and 1s(push hold), 2s, 3s, 4s, 8s interrupt for long press
detect.
To compile this driver as a module, choose M here. The module will be
called pf1550-onkey.
config INPUT_PM8941_PWRKEY config INPUT_PM8941_PWRKEY
tristate "Qualcomm PM8941 power key support" tristate "Qualcomm PM8941 power key support"
depends on MFD_SPMI_PMIC depends on MFD_SPMI_PMIC

View File

@ -63,6 +63,7 @@ obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o
obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
obj-$(CONFIG_INPUT_PF1550_ONKEY) += pf1550-onkey.o
obj-$(CONFIG_INPUT_PM8941_PWRKEY) += pm8941-pwrkey.o obj-$(CONFIG_INPUT_PM8941_PWRKEY) += pm8941-pwrkey.o
obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR) += pm8xxx-vibrator.o obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR) += pm8xxx-vibrator.o
obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o

View File

@ -0,0 +1,197 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for the PF1550 ONKEY
* Copyright (C) 2016 Freescale Semiconductor, Inc. All Rights Reserved.
*
* Portions Copyright (c) 2025 Savoir-faire Linux Inc.
* Samuel Kayode <samuel.kayode@savoirfairelinux.com>
*/
#include <linux/err.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mfd/pf1550.h>
#include <linux/platform_device.h>
#define PF1550_ONKEY_IRQ_NR 6
struct onkey_drv_data {
struct device *dev;
const struct pf1550_ddata *pf1550;
bool wakeup;
struct input_dev *input;
};
static irqreturn_t pf1550_onkey_irq_handler(int irq, void *data)
{
struct onkey_drv_data *onkey = data;
struct platform_device *pdev = to_platform_device(onkey->dev);
int i, state, irq_type = -1;
for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++)
if (irq == platform_get_irq(pdev, i))
irq_type = i;
switch (irq_type) {
case PF1550_ONKEY_IRQ_PUSHI:
state = 0;
break;
case PF1550_ONKEY_IRQ_1SI:
case PF1550_ONKEY_IRQ_2SI:
case PF1550_ONKEY_IRQ_3SI:
case PF1550_ONKEY_IRQ_4SI:
case PF1550_ONKEY_IRQ_8SI:
state = 1;
break;
default:
dev_err(onkey->dev, "onkey interrupt: irq %d occurred\n",
irq_type);
return IRQ_HANDLED;
}
input_event(onkey->input, EV_KEY, KEY_POWER, state);
input_sync(onkey->input);
return IRQ_HANDLED;
}
static int pf1550_onkey_probe(struct platform_device *pdev)
{
struct onkey_drv_data *onkey;
struct input_dev *input;
bool key_power = false;
int i, irq, error;
onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL);
if (!onkey)
return -ENOMEM;
onkey->dev = &pdev->dev;
onkey->pf1550 = dev_get_drvdata(pdev->dev.parent);
if (!onkey->pf1550->regmap)
return dev_err_probe(&pdev->dev, -ENODEV,
"failed to get regmap\n");
onkey->wakeup = device_property_read_bool(pdev->dev.parent,
"wakeup-source");
if (device_property_read_bool(pdev->dev.parent,
"nxp,disable-key-power")) {
error = regmap_clear_bits(onkey->pf1550->regmap,
PF1550_PMIC_REG_PWRCTRL1,
PF1550_ONKEY_RST_EN);
if (error)
return dev_err_probe(&pdev->dev, error,
"failed: disable turn system off");
} else {
key_power = true;
}
input = devm_input_allocate_device(&pdev->dev);
if (!input)
return dev_err_probe(&pdev->dev, -ENOMEM,
"failed to allocate the input device\n");
input->name = pdev->name;
input->phys = "pf1550-onkey/input0";
input->id.bustype = BUS_HOST;
if (key_power)
input_set_capability(input, EV_KEY, KEY_POWER);
onkey->input = input;
platform_set_drvdata(pdev, onkey);
for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++) {
irq = platform_get_irq(pdev, i);
if (irq < 0)
return irq;
error = devm_request_threaded_irq(&pdev->dev, irq, NULL,
pf1550_onkey_irq_handler,
IRQF_NO_SUSPEND,
"pf1550-onkey", onkey);
if (error)
return dev_err_probe(&pdev->dev, error,
"failed: irq request (IRQ: %d)\n",
i);
}
error = input_register_device(input);
if (error)
return dev_err_probe(&pdev->dev, error,
"failed to register input device\n");
device_init_wakeup(&pdev->dev, onkey->wakeup);
return 0;
}
static int pf1550_onkey_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct onkey_drv_data *onkey = platform_get_drvdata(pdev);
int i, irq;
if (!device_may_wakeup(&pdev->dev))
regmap_write(onkey->pf1550->regmap,
PF1550_PMIC_REG_ONKEY_INT_MASK0,
ONKEY_IRQ_PUSHI | ONKEY_IRQ_1SI | ONKEY_IRQ_2SI |
ONKEY_IRQ_3SI | ONKEY_IRQ_4SI | ONKEY_IRQ_8SI);
else
for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++) {
irq = platform_get_irq(pdev, i);
if (irq > 0)
enable_irq_wake(irq);
}
return 0;
}
static int pf1550_onkey_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct onkey_drv_data *onkey = platform_get_drvdata(pdev);
int i, irq;
if (!device_may_wakeup(&pdev->dev))
regmap_write(onkey->pf1550->regmap,
PF1550_PMIC_REG_ONKEY_INT_MASK0,
~((u8)(ONKEY_IRQ_PUSHI | ONKEY_IRQ_1SI |
ONKEY_IRQ_2SI | ONKEY_IRQ_3SI | ONKEY_IRQ_4SI |
ONKEY_IRQ_8SI)));
else
for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++) {
irq = platform_get_irq(pdev, i);
if (irq > 0)
disable_irq_wake(irq);
}
return 0;
}
static SIMPLE_DEV_PM_OPS(pf1550_onkey_pm_ops, pf1550_onkey_suspend,
pf1550_onkey_resume);
static const struct platform_device_id pf1550_onkey_id[] = {
{ "pf1550-onkey", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, pf1550_onkey_id);
static struct platform_driver pf1550_onkey_driver = {
.driver = {
.name = "pf1550-onkey",
.pm = pm_sleep_ptr(&pf1550_onkey_pm_ops),
},
.probe = pf1550_onkey_probe,
.id_table = pf1550_onkey_id,
};
module_platform_driver(pf1550_onkey_driver);
MODULE_AUTHOR("Freescale Semiconductor");
MODULE_DESCRIPTION("PF1550 onkey Driver");
MODULE_LICENSE("GPL");

View File

@ -605,6 +605,22 @@ config MFD_MX25_TSADC
i.MX25 processors. They consist of a conversion queue for general i.MX25 processors. They consist of a conversion queue for general
purpose ADC and a queue for Touchscreens. purpose ADC and a queue for Touchscreens.
config MFD_PF1550
tristate "NXP PF1550 PMIC Support"
depends on I2C=y && OF
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
help
Say yes here to add support for NXP PF1550. This is a companion Power
Management IC with regulators, onkey, and charger control on chip.
This driver provides common support for accessing the device;
additional drivers must be enabled in order to use the functionality
of the device.
This driver can also be built as a module and if so will be called
pf1550.
config MFD_HI6421_PMIC config MFD_HI6421_PMIC
tristate "HiSilicon Hi6421 PMU/Codec IC" tristate "HiSilicon Hi6421 PMU/Codec IC"
depends on OF depends on OF

View File

@ -122,6 +122,8 @@ obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o
obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o
obj-$(CONFIG_MFD_PF1550) += pf1550.o
obj-$(CONFIG_MFD_NCT6694) += nct6694.o obj-$(CONFIG_MFD_NCT6694) += nct6694.o
obj-$(CONFIG_MFD_CORE) += mfd-core.o obj-$(CONFIG_MFD_CORE) += mfd-core.o

367
drivers/mfd/pf1550.c Normal file
View File

@ -0,0 +1,367 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Core driver for the PF1550
*
* Copyright (C) 2016 Freescale Semiconductor, Inc.
* Robin Gong <yibin.gong@freescale.com>
*
* Portions Copyright (c) 2025 Savoir-faire Linux Inc.
* Samuel Kayode <samuel.kayode@savoirfairelinux.com>
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
#include <linux/mfd/pf1550.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
static const struct regmap_config pf1550_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = PF1550_PMIC_REG_END,
};
static const struct regmap_irq pf1550_irqs[] = {
REGMAP_IRQ_REG(PF1550_IRQ_CHG, 0, IRQ_CHG),
REGMAP_IRQ_REG(PF1550_IRQ_REGULATOR, 0, IRQ_REGULATOR),
REGMAP_IRQ_REG(PF1550_IRQ_ONKEY, 0, IRQ_ONKEY),
};
static const struct regmap_irq_chip pf1550_irq_chip = {
.name = "pf1550",
.status_base = PF1550_PMIC_REG_INT_CATEGORY,
.init_ack_masked = 1,
.num_regs = 1,
.irqs = pf1550_irqs,
.num_irqs = ARRAY_SIZE(pf1550_irqs),
};
static const struct regmap_irq pf1550_regulator_irqs[] = {
REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW1_LS, 0, PMIC_IRQ_SW1_LS),
REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW2_LS, 0, PMIC_IRQ_SW2_LS),
REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW3_LS, 0, PMIC_IRQ_SW3_LS),
REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW1_HS, 3, PMIC_IRQ_SW1_HS),
REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW2_HS, 3, PMIC_IRQ_SW2_HS),
REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW3_HS, 3, PMIC_IRQ_SW3_HS),
REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO1_FAULT, 16, PMIC_IRQ_LDO1_FAULT),
REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO2_FAULT, 16, PMIC_IRQ_LDO2_FAULT),
REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO3_FAULT, 16, PMIC_IRQ_LDO3_FAULT),
REGMAP_IRQ_REG(PF1550_PMIC_IRQ_TEMP_110, 24, PMIC_IRQ_TEMP_110),
REGMAP_IRQ_REG(PF1550_PMIC_IRQ_TEMP_125, 24, PMIC_IRQ_TEMP_125),
};
static const struct regmap_irq_chip pf1550_regulator_irq_chip = {
.name = "pf1550-regulator",
.status_base = PF1550_PMIC_REG_SW_INT_STAT0,
.ack_base = PF1550_PMIC_REG_SW_INT_STAT0,
.mask_base = PF1550_PMIC_REG_SW_INT_MASK0,
.use_ack = 1,
.init_ack_masked = 1,
.num_regs = 25,
.irqs = pf1550_regulator_irqs,
.num_irqs = ARRAY_SIZE(pf1550_regulator_irqs),
};
static const struct resource regulator_resources[] = {
DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW1_LS),
DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW2_LS),
DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW3_LS),
DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW1_HS),
DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW2_HS),
DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW3_HS),
DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO1_FAULT),
DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO2_FAULT),
DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO3_FAULT),
DEFINE_RES_IRQ(PF1550_PMIC_IRQ_TEMP_110),
DEFINE_RES_IRQ(PF1550_PMIC_IRQ_TEMP_125),
};
static const struct regmap_irq pf1550_onkey_irqs[] = {
REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_PUSHI, 0, ONKEY_IRQ_PUSHI),
REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_1SI, 0, ONKEY_IRQ_1SI),
REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_2SI, 0, ONKEY_IRQ_2SI),
REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_3SI, 0, ONKEY_IRQ_3SI),
REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_4SI, 0, ONKEY_IRQ_4SI),
REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_8SI, 0, ONKEY_IRQ_8SI),
};
static const struct regmap_irq_chip pf1550_onkey_irq_chip = {
.name = "pf1550-onkey",
.status_base = PF1550_PMIC_REG_ONKEY_INT_STAT0,
.ack_base = PF1550_PMIC_REG_ONKEY_INT_STAT0,
.mask_base = PF1550_PMIC_REG_ONKEY_INT_MASK0,
.use_ack = 1,
.init_ack_masked = 1,
.num_regs = 1,
.irqs = pf1550_onkey_irqs,
.num_irqs = ARRAY_SIZE(pf1550_onkey_irqs),
};
static const struct resource onkey_resources[] = {
DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_PUSHI),
DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_1SI),
DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_2SI),
DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_3SI),
DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_4SI),
DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_8SI),
};
static const struct regmap_irq pf1550_charger_irqs[] = {
REGMAP_IRQ_REG(PF1550_CHARG_IRQ_BAT2SOCI, 0, CHARG_IRQ_BAT2SOCI),
REGMAP_IRQ_REG(PF1550_CHARG_IRQ_BATI, 0, CHARG_IRQ_BATI),
REGMAP_IRQ_REG(PF1550_CHARG_IRQ_CHGI, 0, CHARG_IRQ_CHGI),
REGMAP_IRQ_REG(PF1550_CHARG_IRQ_VBUSI, 0, CHARG_IRQ_VBUSI),
REGMAP_IRQ_REG(PF1550_CHARG_IRQ_THMI, 0, CHARG_IRQ_THMI),
};
static const struct regmap_irq_chip pf1550_charger_irq_chip = {
.name = "pf1550-charger",
.status_base = PF1550_CHARG_REG_CHG_INT,
.ack_base = PF1550_CHARG_REG_CHG_INT,
.mask_base = PF1550_CHARG_REG_CHG_INT_MASK,
.use_ack = 1,
.init_ack_masked = 1,
.num_regs = 1,
.irqs = pf1550_charger_irqs,
.num_irqs = ARRAY_SIZE(pf1550_charger_irqs),
};
static const struct resource charger_resources[] = {
DEFINE_RES_IRQ(PF1550_CHARG_IRQ_BAT2SOCI),
DEFINE_RES_IRQ(PF1550_CHARG_IRQ_BATI),
DEFINE_RES_IRQ(PF1550_CHARG_IRQ_CHGI),
DEFINE_RES_IRQ(PF1550_CHARG_IRQ_VBUSI),
DEFINE_RES_IRQ(PF1550_CHARG_IRQ_THMI),
};
static const struct mfd_cell pf1550_regulator_cell = {
.name = "pf1550-regulator",
.num_resources = ARRAY_SIZE(regulator_resources),
.resources = regulator_resources,
};
static const struct mfd_cell pf1550_onkey_cell = {
.name = "pf1550-onkey",
.num_resources = ARRAY_SIZE(onkey_resources),
.resources = onkey_resources,
};
static const struct mfd_cell pf1550_charger_cell = {
.name = "pf1550-charger",
.num_resources = ARRAY_SIZE(charger_resources),
.resources = charger_resources,
};
/*
* The PF1550 is shipped in variants of A0, A1,...A9. Each variant defines a
* configuration of the PMIC in a One-Time Programmable (OTP) memory.
* This memory is accessed indirectly by writing valid keys to specific
* registers of the PMIC. To read the OTP memory after writing the valid keys,
* the OTP register address to be read is written to pf1550 register 0xc4 and
* its value read from pf1550 register 0xc5.
*/
static int pf1550_read_otp(const struct pf1550_ddata *pf1550, unsigned int index,
unsigned int *val)
{
int ret = 0;
ret = regmap_write(pf1550->regmap, PF1550_PMIC_REG_KEY, PF1550_OTP_PMIC_KEY);
if (ret)
goto read_err;
ret = regmap_write(pf1550->regmap, PF1550_CHARG_REG_CHGR_KEY2, PF1550_OTP_CHGR_KEY);
if (ret)
goto read_err;
ret = regmap_write(pf1550->regmap, PF1550_TEST_REG_KEY3, PF1550_OTP_TEST_KEY);
if (ret)
goto read_err;
ret = regmap_write(pf1550->regmap, PF1550_TEST_REG_FMRADDR, index);
if (ret)
goto read_err;
ret = regmap_read(pf1550->regmap, PF1550_TEST_REG_FMRDATA, val);
if (ret)
goto read_err;
return 0;
read_err:
return dev_err_probe(pf1550->dev, ret, "OTP reg %x not found!\n", index);
}
static int pf1550_i2c_probe(struct i2c_client *i2c)
{
const struct mfd_cell *regulator = &pf1550_regulator_cell;
const struct mfd_cell *charger = &pf1550_charger_cell;
const struct mfd_cell *onkey = &pf1550_onkey_cell;
unsigned int reg_data = 0, otp_data = 0;
struct pf1550_ddata *pf1550;
struct irq_domain *domain;
int irq, ret = 0;
pf1550 = devm_kzalloc(&i2c->dev, sizeof(*pf1550), GFP_KERNEL);
if (!pf1550)
return -ENOMEM;
i2c_set_clientdata(i2c, pf1550);
pf1550->dev = &i2c->dev;
pf1550->irq = i2c->irq;
pf1550->regmap = devm_regmap_init_i2c(i2c, &pf1550_regmap_config);
if (IS_ERR(pf1550->regmap))
return dev_err_probe(pf1550->dev, PTR_ERR(pf1550->regmap),
"failed to allocate register map\n");
ret = regmap_read(pf1550->regmap, PF1550_PMIC_REG_DEVICE_ID, &reg_data);
if (ret < 0)
return dev_err_probe(pf1550->dev, ret, "cannot read chip ID\n");
if (reg_data != PF1550_DEVICE_ID)
return dev_err_probe(pf1550->dev, -ENODEV, "invalid device ID: 0x%02x\n", reg_data);
/* Regulator DVS for SW2 */
ret = pf1550_read_otp(pf1550, PF1550_OTP_SW2_SW3, &otp_data);
if (ret)
return ret;
/* When clear, DVS should be enabled */
if (!(otp_data & OTP_SW2_DVS_ENB))
pf1550->dvs2_enable = true;
/* Regulator DVS for SW1 */
ret = pf1550_read_otp(pf1550, PF1550_OTP_SW1_SW2, &otp_data);
if (ret)
return ret;
if (!(otp_data & OTP_SW1_DVS_ENB))
pf1550->dvs1_enable = true;
/* Add top level interrupts */
ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, pf1550->irq,
IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_FALLING,
0, &pf1550_irq_chip,
&pf1550->irq_data);
if (ret)
return ret;
/* Add regulator */
irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_REGULATOR);
if (irq < 0)
return dev_err_probe(pf1550->dev, irq,
"Failed to get parent vIRQ(%d) for chip %s\n",
PF1550_IRQ_REGULATOR, pf1550_irq_chip.name);
ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq,
IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_FALLING, 0,
&pf1550_regulator_irq_chip,
&pf1550->irq_data_regulator);
if (ret)
return dev_err_probe(pf1550->dev, ret, "Failed to add %s IRQ chip\n",
pf1550_regulator_irq_chip.name);
domain = regmap_irq_get_domain(pf1550->irq_data_regulator);
ret = devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, regulator, 1, NULL, 0, domain);
if (ret)
return ret;
/* Add onkey */
irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_ONKEY);
if (irq < 0)
return dev_err_probe(pf1550->dev, irq,
"Failed to get parent vIRQ(%d) for chip %s\n",
PF1550_IRQ_ONKEY, pf1550_irq_chip.name);
ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq,
IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_FALLING, 0,
&pf1550_onkey_irq_chip,
&pf1550->irq_data_onkey);
if (ret)
return dev_err_probe(pf1550->dev, ret, "Failed to add %s IRQ chip\n",
pf1550_onkey_irq_chip.name);
domain = regmap_irq_get_domain(pf1550->irq_data_onkey);
ret = devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, onkey, 1, NULL, 0, domain);
if (ret)
return ret;
/* Add battery charger */
irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_CHG);
if (irq < 0)
return dev_err_probe(pf1550->dev, irq,
"Failed to get parent vIRQ(%d) for chip %s\n",
PF1550_IRQ_CHG, pf1550_irq_chip.name);
ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq,
IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_FALLING, 0,
&pf1550_charger_irq_chip,
&pf1550->irq_data_charger);
if (ret)
return dev_err_probe(pf1550->dev, ret, "Failed to add %s IRQ chip\n",
pf1550_charger_irq_chip.name);
domain = regmap_irq_get_domain(pf1550->irq_data_charger);
return devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, charger, 1, NULL, 0, domain);
}
static int pf1550_suspend(struct device *dev)
{
struct pf1550_ddata *pf1550 = dev_get_drvdata(dev);
if (device_may_wakeup(dev)) {
enable_irq_wake(pf1550->irq);
disable_irq(pf1550->irq);
}
return 0;
}
static int pf1550_resume(struct device *dev)
{
struct pf1550_ddata *pf1550 = dev_get_drvdata(dev);
if (device_may_wakeup(dev)) {
disable_irq_wake(pf1550->irq);
enable_irq(pf1550->irq);
}
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(pf1550_pm, pf1550_suspend, pf1550_resume);
static const struct i2c_device_id pf1550_i2c_id[] = {
{ "pf1550" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, pf1550_i2c_id);
static const struct of_device_id pf1550_dt_match[] = {
{ .compatible = "nxp,pf1550" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, pf1550_dt_match);
static struct i2c_driver pf1550_i2c_driver = {
.driver = {
.name = "pf1550",
.pm = pm_sleep_ptr(&pf1550_pm),
.of_match_table = pf1550_dt_match,
},
.probe = pf1550_i2c_probe,
.id_table = pf1550_i2c_id,
};
module_i2c_driver(pf1550_i2c_driver);
MODULE_DESCRIPTION("NXP PF1550 core driver");
MODULE_AUTHOR("Robin Gong <yibin.gong@freescale.com>");
MODULE_LICENSE("GPL");

View File

@ -486,6 +486,17 @@ config CHARGER_88PM860X
help help
Say Y here to enable charger for Marvell 88PM860x chip. Say Y here to enable charger for Marvell 88PM860x chip.
config CHARGER_PF1550
tristate "NXP PF1550 battery charger driver"
depends on MFD_PF1550
help
Say Y to enable support for the NXP PF1550 battery charger.
The device is a single cell Li-Ion/Li-Polymer battery charger for
portable application.
This driver can also be built as a module. If so, the module will be
called pf1550-charger.
config BATTERY_RX51 config BATTERY_RX51
tristate "Nokia RX-51 (N900) battery driver" tristate "Nokia RX-51 (N900) battery driver"
depends on TWL4030_MADC depends on TWL4030_MADC

View File

@ -66,6 +66,7 @@ obj-$(CONFIG_CHARGER_RT9467) += rt9467-charger.o
obj-$(CONFIG_CHARGER_RT9471) += rt9471.o obj-$(CONFIG_CHARGER_RT9471) += rt9471.o
obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o
obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o
obj-$(CONFIG_CHARGER_PF1550) += pf1550-charger.o
obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o
obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o ab8500_chargalg.o obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o ab8500_chargalg.o
obj-$(CONFIG_CHARGER_CPCAP) += cpcap-charger.o obj-$(CONFIG_CHARGER_CPCAP) += cpcap-charger.o

View File

@ -0,0 +1,641 @@
// SPDX-License-Identifier: GPL-2.0
/*
* charger driver for the PF1550
*
* Copyright (C) 2016 Freescale Semiconductor, Inc.
* Robin Gong <yibin.gong@freescale.com>
*
* Portions Copyright (c) 2025 Savoir-faire Linux Inc.
* Samuel Kayode <samuel.kayode@savoirfairelinux.com>
*/
#include <linux/devm-helpers.h>
#include <linux/interrupt.h>
#include <linux/mfd/pf1550.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#define PF1550_DEFAULT_CONSTANT_VOLT 4200000
#define PF1550_DEFAULT_MIN_SYSTEM_VOLT 3500000
#define PF1550_DEFAULT_THERMAL_TEMP 95
#define PF1550_CHARGER_IRQ_NR 5
struct pf1550_charger {
struct device *dev;
const struct pf1550_ddata *pf1550;
struct power_supply *charger;
struct power_supply *battery;
struct delayed_work vbus_sense_work;
struct delayed_work chg_sense_work;
struct delayed_work bat_sense_work;
int virqs[PF1550_CHARGER_IRQ_NR];
u32 constant_volt;
u32 min_system_volt;
u32 thermal_regulation_temp;
};
static int pf1550_get_charger_state(struct regmap *regmap, int *val)
{
unsigned int data;
int ret;
ret = regmap_read(regmap, PF1550_CHARG_REG_CHG_SNS, &data);
if (ret < 0)
return ret;
data &= PF1550_CHG_SNS_MASK;
switch (data) {
case PF1550_CHG_PRECHARGE:
case PF1550_CHG_CONSTANT_CURRENT:
case PF1550_CHG_CONSTANT_VOL:
case PF1550_CHG_EOC:
*val = POWER_SUPPLY_STATUS_CHARGING;
break;
case PF1550_CHG_DONE:
*val = POWER_SUPPLY_STATUS_FULL;
break;
case PF1550_CHG_TIMER_FAULT:
case PF1550_CHG_SUSPEND:
*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
case PF1550_CHG_OFF_INV:
case PF1550_CHG_OFF_TEMP:
case PF1550_CHG_LINEAR_ONLY:
*val = POWER_SUPPLY_STATUS_DISCHARGING;
break;
default:
*val = POWER_SUPPLY_STATUS_UNKNOWN;
}
return 0;
}
static int pf1550_get_charge_type(struct regmap *regmap, int *val)
{
unsigned int data;
int ret;
ret = regmap_read(regmap, PF1550_CHARG_REG_CHG_SNS, &data);
if (ret < 0)
return ret;
data &= PF1550_CHG_SNS_MASK;
switch (data) {
case PF1550_CHG_SNS_MASK:
*val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
break;
case PF1550_CHG_CONSTANT_CURRENT:
case PF1550_CHG_CONSTANT_VOL:
case PF1550_CHG_EOC:
*val = POWER_SUPPLY_CHARGE_TYPE_FAST;
break;
case PF1550_CHG_DONE:
case PF1550_CHG_TIMER_FAULT:
case PF1550_CHG_SUSPEND:
case PF1550_CHG_OFF_INV:
case PF1550_CHG_BAT_OVER:
case PF1550_CHG_OFF_TEMP:
case PF1550_CHG_LINEAR_ONLY:
*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
break;
default:
*val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
}
return 0;
}
/*
* Supported health statuses:
* - POWER_SUPPLY_HEALTH_DEAD
* - POWER_SUPPLY_HEALTH_GOOD
* - POWER_SUPPLY_HEALTH_OVERVOLTAGE
* - POWER_SUPPLY_HEALTH_UNKNOWN
*/
static int pf1550_get_battery_health(struct regmap *regmap, int *val)
{
unsigned int data;
int ret;
ret = regmap_read(regmap, PF1550_CHARG_REG_BATT_SNS, &data);
if (ret < 0)
return ret;
data &= PF1550_BAT_SNS_MASK;
switch (data) {
case PF1550_BAT_NO_DETECT:
*val = POWER_SUPPLY_HEALTH_NO_BATTERY;
break;
case PF1550_BAT_NO_VBUS:
case PF1550_BAT_LOW_THAN_PRECHARG:
case PF1550_BAT_CHARG_FAIL:
case PF1550_BAT_HIGH_THAN_PRECHARG:
*val = POWER_SUPPLY_HEALTH_GOOD;
break;
case PF1550_BAT_OVER_VOL:
*val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
break;
default:
*val = POWER_SUPPLY_HEALTH_UNKNOWN;
break;
}
return 0;
}
static int pf1550_get_present(struct regmap *regmap, int *val)
{
unsigned int data;
int ret;
ret = regmap_read(regmap, PF1550_CHARG_REG_BATT_SNS, &data);
if (ret < 0)
return ret;
data &= PF1550_BAT_SNS_MASK;
*val = (data == PF1550_BAT_NO_DETECT) ? 0 : 1;
return 0;
}
static int pf1550_get_online(struct regmap *regmap, int *val)
{
unsigned int data;
int ret;
ret = regmap_read(regmap, PF1550_CHARG_REG_VBUS_SNS, &data);
if (ret < 0)
return ret;
*val = (data & PF1550_VBUS_VALID) ? 1 : 0;
return 0;
}
static void pf1550_chg_bat_work(struct work_struct *work)
{
struct pf1550_charger *chg = container_of(to_delayed_work(work),
struct pf1550_charger,
bat_sense_work);
unsigned int data;
if (regmap_read(chg->pf1550->regmap, PF1550_CHARG_REG_BATT_SNS, &data)) {
dev_err(chg->dev, "Read BATT_SNS error.\n");
return;
}
switch (data & PF1550_BAT_SNS_MASK) {
case PF1550_BAT_NO_VBUS:
dev_dbg(chg->dev, "No valid VBUS input.\n");
break;
case PF1550_BAT_LOW_THAN_PRECHARG:
dev_dbg(chg->dev, "VBAT < VPRECHG.LB.\n");
break;
case PF1550_BAT_CHARG_FAIL:
dev_dbg(chg->dev, "Battery charging failed.\n");
break;
case PF1550_BAT_HIGH_THAN_PRECHARG:
dev_dbg(chg->dev, "VBAT > VPRECHG.LB.\n");
break;
case PF1550_BAT_OVER_VOL:
dev_dbg(chg->dev, "VBAT > VBATOV.\n");
break;
case PF1550_BAT_NO_DETECT:
dev_dbg(chg->dev, "Battery not detected.\n");
break;
default:
dev_err(chg->dev, "Unknown value read:%x\n",
data & PF1550_CHG_SNS_MASK);
}
}
static void pf1550_chg_chg_work(struct work_struct *work)
{
struct pf1550_charger *chg = container_of(to_delayed_work(work),
struct pf1550_charger,
chg_sense_work);
unsigned int data;
if (regmap_read(chg->pf1550->regmap, PF1550_CHARG_REG_CHG_SNS, &data)) {
dev_err(chg->dev, "Read CHG_SNS error.\n");
return;
}
switch (data & PF1550_CHG_SNS_MASK) {
case PF1550_CHG_PRECHARGE:
dev_dbg(chg->dev, "In pre-charger mode.\n");
break;
case PF1550_CHG_CONSTANT_CURRENT:
dev_dbg(chg->dev, "In fast-charge constant current mode.\n");
break;
case PF1550_CHG_CONSTANT_VOL:
dev_dbg(chg->dev, "In fast-charge constant voltage mode.\n");
break;
case PF1550_CHG_EOC:
dev_dbg(chg->dev, "In EOC mode.\n");
break;
case PF1550_CHG_DONE:
dev_dbg(chg->dev, "In DONE mode.\n");
break;
case PF1550_CHG_TIMER_FAULT:
dev_info(chg->dev, "In timer fault mode.\n");
break;
case PF1550_CHG_SUSPEND:
dev_info(chg->dev, "In thermistor suspend mode.\n");
break;
case PF1550_CHG_OFF_INV:
dev_info(chg->dev, "Input invalid, charger off.\n");
break;
case PF1550_CHG_BAT_OVER:
dev_warn(chg->dev, "Battery over-voltage.\n");
break;
case PF1550_CHG_OFF_TEMP:
dev_info(chg->dev, "Temp high, charger off.\n");
break;
case PF1550_CHG_LINEAR_ONLY:
dev_dbg(chg->dev, "In Linear mode, not charging.\n");
break;
default:
dev_err(chg->dev, "Unknown value read:%x\n",
data & PF1550_CHG_SNS_MASK);
}
}
static void pf1550_chg_vbus_work(struct work_struct *work)
{
struct pf1550_charger *chg = container_of(to_delayed_work(work),
struct pf1550_charger,
vbus_sense_work);
unsigned int data;
if (regmap_read(chg->pf1550->regmap, PF1550_CHARG_REG_VBUS_SNS, &data)) {
dev_err(chg->dev, "Read VBUS_SNS error.\n");
return;
}
if (data & PF1550_VBUS_UVLO) {
dev_dbg(chg->dev, "VBUS detached.\n");
power_supply_changed(chg->battery);
}
if (data & PF1550_VBUS_IN2SYS)
dev_dbg(chg->dev, "VBUS_IN2SYS_SNS.\n");
if (data & PF1550_VBUS_OVLO)
dev_dbg(chg->dev, "VBUS_OVLO_SNS.\n");
if (data & PF1550_VBUS_VALID) {
dev_dbg(chg->dev, "VBUS attached.\n");
power_supply_changed(chg->charger);
}
}
static irqreturn_t pf1550_charger_irq_handler(int irq, void *data)
{
struct pf1550_charger *chg = data;
struct device *dev = chg->dev;
int i, irq_type = -1;
for (i = 0; i < PF1550_CHARGER_IRQ_NR; i++)
if (irq == chg->virqs[i])
irq_type = i;
switch (irq_type) {
case PF1550_CHARG_IRQ_BAT2SOCI:
dev_info(dev, "BAT to SYS Overcurrent interrupt.\n");
break;
case PF1550_CHARG_IRQ_BATI:
schedule_delayed_work(&chg->bat_sense_work,
msecs_to_jiffies(10));
break;
case PF1550_CHARG_IRQ_CHGI:
schedule_delayed_work(&chg->chg_sense_work,
msecs_to_jiffies(10));
break;
case PF1550_CHARG_IRQ_VBUSI:
schedule_delayed_work(&chg->vbus_sense_work,
msecs_to_jiffies(10));
break;
case PF1550_CHARG_IRQ_THMI:
dev_info(dev, "Thermal interrupt.\n");
break;
default:
dev_err(dev, "unknown interrupt occurred.\n");
}
return IRQ_HANDLED;
}
static enum power_supply_property pf1550_charger_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
};
static enum power_supply_property pf1550_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
};
static int pf1550_charger_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct pf1550_charger *chg = power_supply_get_drvdata(psy);
struct regmap *regmap = chg->pf1550->regmap;
int ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
ret = pf1550_get_charger_state(regmap, &val->intval);
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
ret = pf1550_get_charge_type(regmap, &val->intval);
break;
case POWER_SUPPLY_PROP_HEALTH:
ret = pf1550_get_battery_health(regmap, &val->intval);
break;
case POWER_SUPPLY_PROP_PRESENT:
ret = pf1550_get_present(regmap, &val->intval);
break;
case POWER_SUPPLY_PROP_ONLINE:
ret = pf1550_get_online(regmap, &val->intval);
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = "PF1550";
break;
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = "NXP";
break;
default:
return -EINVAL;
}
return ret;
}
static const struct power_supply_desc pf1550_charger_desc = {
.name = "pf1550-charger",
.type = POWER_SUPPLY_TYPE_MAINS,
.properties = pf1550_charger_props,
.num_properties = ARRAY_SIZE(pf1550_charger_props),
.get_property = pf1550_charger_get_property,
};
static const struct power_supply_desc pf1550_battery_desc = {
.name = "pf1550-battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = pf1550_battery_props,
.num_properties = ARRAY_SIZE(pf1550_battery_props),
.get_property = pf1550_charger_get_property,
};
static int pf1550_set_constant_volt(struct pf1550_charger *chg,
unsigned int uvolt)
{
unsigned int data;
if (uvolt >= 3500000 && uvolt <= 4440000)
data = 8 + (uvolt - 3500000) / 20000;
else
return dev_err_probe(chg->dev, -EINVAL,
"Wrong value for constant voltage\n");
dev_dbg(chg->dev, "Charging constant voltage: %u (0x%x)\n", uvolt,
data);
return regmap_update_bits(chg->pf1550->regmap,
PF1550_CHARG_REG_BATT_REG,
PF1550_CHARG_REG_BATT_REG_CHGCV_MASK, data);
}
static int pf1550_set_min_system_volt(struct pf1550_charger *chg,
unsigned int uvolt)
{
unsigned int data;
switch (uvolt) {
case 3500000:
data = 0x0;
break;
case 3700000:
data = 0x1;
break;
case 4300000:
data = 0x2;
break;
default:
return dev_err_probe(chg->dev, -EINVAL,
"Wrong value for minimum system voltage\n");
}
data <<= PF1550_CHARG_REG_BATT_REG_VMINSYS_SHIFT;
dev_dbg(chg->dev, "Minimum system regulation voltage: %u (0x%x)\n",
uvolt, data);
return regmap_update_bits(chg->pf1550->regmap,
PF1550_CHARG_REG_BATT_REG,
PF1550_CHARG_REG_BATT_REG_VMINSYS_MASK, data);
}
static int pf1550_set_thermal_regulation_temp(struct pf1550_charger *chg,
unsigned int cells)
{
unsigned int data;
switch (cells) {
case 80:
data = 0x0;
break;
case 95:
data = 0x1;
break;
case 110:
data = 0x2;
break;
case 125:
data = 0x3;
break;
default:
return dev_err_probe(chg->dev, -EINVAL,
"Wrong value for thermal temperature\n");
}
data <<= PF1550_CHARG_REG_THM_REG_CNFG_REGTEMP_SHIFT;
dev_dbg(chg->dev, "Thermal regulation loop temperature: %u (0x%x)\n",
cells, data);
return regmap_update_bits(chg->pf1550->regmap,
PF1550_CHARG_REG_THM_REG_CNFG,
PF1550_CHARG_REG_THM_REG_CNFG_REGTEMP_MASK,
data);
}
/*
* Sets charger registers to proper and safe default values.
*/
static int pf1550_reg_init(struct pf1550_charger *chg)
{
struct power_supply_battery_info *info;
struct device *dev = chg->dev;
int ret;
/* Unmask charger interrupt, mask DPMI and reserved bit */
ret = regmap_write(chg->pf1550->regmap, PF1550_CHARG_REG_CHG_INT_MASK,
PF1550_CHG_INT_MASK);
if (ret)
return dev_err_probe(dev, ret,
"Error unmask charger interrupt\n");
ret = pf1550_set_constant_volt(chg, chg->constant_volt);
if (ret)
return ret;
ret = pf1550_set_min_system_volt(chg, chg->min_system_volt);
if (ret)
return ret;
ret = pf1550_set_thermal_regulation_temp(chg,
chg->thermal_regulation_temp);
if (ret)
return ret;
/*
* The PF1550 charger has 3 modes of operation. By default, the charger
* is in mode 1; it remains off. Appropriate for applications not using
* a battery. The other supported mode is mode 2, the charger is turned
* on to charge a battery when present.
*/
if (power_supply_get_battery_info(chg->charger, &info)) {
ret = regmap_write(chg->pf1550->regmap,
PF1550_CHARG_REG_CHG_OPER,
PF1550_CHG_BAT_ON);
if (ret)
return dev_err_probe(dev, ret,
"Error turn on charger\n");
}
return 0;
}
static void pf1550_dt_parse_dev_info(struct pf1550_charger *chg)
{
struct power_supply_battery_info *info;
struct device *dev = chg->dev;
if (device_property_read_u32(dev->parent, "nxp,min-system-microvolt",
&chg->min_system_volt))
chg->min_system_volt = PF1550_DEFAULT_MIN_SYSTEM_VOLT;
if (device_property_read_u32(dev->parent,
"nxp,thermal-regulation-celsius",
&chg->thermal_regulation_temp))
chg->thermal_regulation_temp = PF1550_DEFAULT_THERMAL_TEMP;
if (power_supply_get_battery_info(chg->charger, &info))
chg->constant_volt = PF1550_DEFAULT_CONSTANT_VOLT;
else
chg->constant_volt = info->constant_charge_voltage_max_uv;
}
static int pf1550_charger_probe(struct platform_device *pdev)
{
const struct pf1550_ddata *pf1550 = dev_get_drvdata(pdev->dev.parent);
struct power_supply_config psy_cfg = {};
struct pf1550_charger *chg;
int i, irq, ret;
chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
if (!chg)
return -ENOMEM;
chg->dev = &pdev->dev;
chg->pf1550 = pf1550;
if (!chg->pf1550->regmap)
return dev_err_probe(&pdev->dev, -ENODEV,
"failed to get regmap\n");
platform_set_drvdata(pdev, chg);
ret = devm_delayed_work_autocancel(chg->dev, &chg->vbus_sense_work,
pf1550_chg_vbus_work);
if (ret)
return dev_err_probe(chg->dev, ret,
"failed to add vbus sense work\n");
ret = devm_delayed_work_autocancel(chg->dev, &chg->chg_sense_work,
pf1550_chg_chg_work);
if (ret)
return dev_err_probe(chg->dev, ret,
"failed to add charger sense work\n");
ret = devm_delayed_work_autocancel(chg->dev, &chg->bat_sense_work,
pf1550_chg_bat_work);
if (ret)
return dev_err_probe(chg->dev, ret,
"failed to add battery sense work\n");
for (i = 0; i < PF1550_CHARGER_IRQ_NR; i++) {
irq = platform_get_irq(pdev, i);
if (irq < 0)
return irq;
chg->virqs[i] = irq;
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
pf1550_charger_irq_handler,
IRQF_NO_SUSPEND,
"pf1550-charger", chg);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"failed irq request\n");
}
psy_cfg.drv_data = chg;
chg->charger = devm_power_supply_register(&pdev->dev,
&pf1550_charger_desc,
&psy_cfg);
if (IS_ERR(chg->charger))
return dev_err_probe(&pdev->dev, PTR_ERR(chg->charger),
"failed: power supply register\n");
chg->battery = devm_power_supply_register(&pdev->dev,
&pf1550_battery_desc,
&psy_cfg);
if (IS_ERR(chg->battery))
return dev_err_probe(&pdev->dev, PTR_ERR(chg->battery),
"failed: power supply register\n");
pf1550_dt_parse_dev_info(chg);
return pf1550_reg_init(chg);
}
static const struct platform_device_id pf1550_charger_id[] = {
{ "pf1550-charger", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, pf1550_charger_id);
static struct platform_driver pf1550_charger_driver = {
.driver = {
.name = "pf1550-charger",
},
.probe = pf1550_charger_probe,
.id_table = pf1550_charger_id,
};
module_platform_driver(pf1550_charger_driver);
MODULE_AUTHOR("Robin Gong <yibin.gong@freescale.com>");
MODULE_DESCRIPTION("PF1550 charger driver");
MODULE_LICENSE("GPL");

View File

@ -1116,6 +1116,15 @@ config REGULATOR_PV88090
Say y here to support the voltage regulators and convertors Say y here to support the voltage regulators and convertors
on PV88090 on PV88090
config REGULATOR_PF1550
tristate "NXP PF1550 regulator"
depends on MFD_PF1550
help
Say y here to select this option to enable the regulators on
the PF1550 PMICs.
This driver controls the PF1550 regulators via I2C bus.
The regulators include three bucks and three ldos.
config REGULATOR_PWM config REGULATOR_PWM
tristate "PWM voltage regulator" tristate "PWM voltage regulator"
depends on PWM depends on PWM

View File

@ -131,6 +131,7 @@ obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
obj-$(CONFIG_REGULATOR_PCA9450) += pca9450-regulator.o obj-$(CONFIG_REGULATOR_PCA9450) += pca9450-regulator.o
obj-$(CONFIG_REGULATOR_PF0900) += pf0900-regulator.o obj-$(CONFIG_REGULATOR_PF0900) += pf0900-regulator.o
obj-$(CONFIG_REGULATOR_PF9453) += pf9453-regulator.o obj-$(CONFIG_REGULATOR_PF9453) += pf9453-regulator.o
obj-$(CONFIG_REGULATOR_PF1550) += pf1550-regulator.o
obj-$(CONFIG_REGULATOR_PF530X) += pf530x-regulator.o obj-$(CONFIG_REGULATOR_PF530X) += pf530x-regulator.o
obj-$(CONFIG_REGULATOR_PF8X00) += pf8x00-regulator.o obj-$(CONFIG_REGULATOR_PF8X00) += pf8x00-regulator.o
obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o

View File

@ -173,9 +173,9 @@ static int set_hw_dvs_levels(struct device_node *np,
const struct regulator_desc *desc, const struct regulator_desc *desc,
struct regulator_config *cfg) struct regulator_config *cfg)
{ {
struct bd71815_regulator *data; const struct bd71815_regulator *data;
data = container_of(desc, struct bd71815_regulator, desc); data = container_of_const(desc, struct bd71815_regulator, desc);
return rohm_regulator_set_dvs_levels(data->dvs, np, desc, cfg->regmap); return rohm_regulator_set_dvs_levels(data->dvs, np, desc, cfg->regmap);
} }
@ -195,10 +195,10 @@ static int buck12_set_hw_dvs_levels(struct device_node *np,
const struct regulator_desc *desc, const struct regulator_desc *desc,
struct regulator_config *cfg) struct regulator_config *cfg)
{ {
struct bd71815_regulator *data; const struct bd71815_regulator *data;
int ret = 0, val; int ret = 0, val;
data = container_of(desc, struct bd71815_regulator, desc); data = container_of_const(desc, struct bd71815_regulator, desc);
if (of_property_present(np, "rohm,dvs-run-voltage") || if (of_property_present(np, "rohm,dvs-run-voltage") ||
of_property_present(np, "rohm,dvs-suspend-voltage") || of_property_present(np, "rohm,dvs-suspend-voltage") ||

View File

@ -95,9 +95,9 @@ static int buck_set_hw_dvs_levels(struct device_node *np,
const struct regulator_desc *desc, const struct regulator_desc *desc,
struct regulator_config *cfg) struct regulator_config *cfg)
{ {
struct bd71828_regulator_data *data; const struct bd71828_regulator_data *data;
data = container_of(desc, struct bd71828_regulator_data, desc); data = container_of_const(desc, struct bd71828_regulator_data, desc);
return rohm_regulator_set_dvs_levels(&data->dvs, np, desc, cfg->regmap); return rohm_regulator_set_dvs_levels(&data->dvs, np, desc, cfg->regmap);
} }

View File

@ -698,9 +698,9 @@ static int buck_set_hw_dvs_levels(struct device_node *np,
const struct regulator_desc *desc, const struct regulator_desc *desc,
struct regulator_config *cfg) struct regulator_config *cfg)
{ {
struct bd718xx_regulator_data *data; const struct bd718xx_regulator_data *data;
data = container_of(desc, struct bd718xx_regulator_data, desc); data = container_of_const(desc, struct bd718xx_regulator_data, desc);
return rohm_regulator_set_dvs_levels(&data->dvs, np, desc, cfg->regmap); return rohm_regulator_set_dvs_levels(&data->dvs, np, desc, cfg->regmap);
} }

View File

@ -337,12 +337,12 @@ static int ldo_map_notif(int irq, struct regulator_irq_data *rid,
int i; int i;
for (i = 0; i < rid->num_states; i++) { for (i = 0; i < rid->num_states; i++) {
struct bd96801_regulator_data *rdata; const struct bd96801_regulator_data *rdata;
struct regulator_dev *rdev; struct regulator_dev *rdev;
rdev = rid->states[i].rdev; rdev = rid->states[i].rdev;
rdata = container_of(rdev->desc, struct bd96801_regulator_data, rdata = container_of_const(rdev->desc, struct bd96801_regulator_data,
desc); desc);
rid->states[i].notifs = regulator_err2notif(rdata->ldo_errs); rid->states[i].notifs = regulator_err2notif(rdata->ldo_errs);
rid->states[i].errors = rdata->ldo_errs; rid->states[i].errors = rdata->ldo_errs;
*dev_mask |= BIT(i); *dev_mask |= BIT(i);
@ -354,9 +354,9 @@ static int bd96801_list_voltage_lr(struct regulator_dev *rdev,
unsigned int selector) unsigned int selector)
{ {
int voltage; int voltage;
struct bd96801_regulator_data *data; const struct bd96801_regulator_data *data;
data = container_of(rdev->desc, struct bd96801_regulator_data, desc); data = container_of_const(rdev->desc, struct bd96801_regulator_data, desc);
/* /*
* The BD096801 has voltage setting in two registers. One giving the * The BD096801 has voltage setting in two registers. One giving the

View File

@ -387,7 +387,7 @@ static unsigned int hi6421_regulator_ldo_get_mode(struct regulator_dev *rdev)
const struct hi6421_regulator_info *info; const struct hi6421_regulator_info *info;
unsigned int reg_val; unsigned int reg_val;
info = container_of(rdev->desc, struct hi6421_regulator_info, desc); info = container_of_const(rdev->desc, struct hi6421_regulator_info, desc);
regmap_read(rdev->regmap, rdev->desc->enable_reg, &reg_val); regmap_read(rdev->regmap, rdev->desc->enable_reg, &reg_val);
if (reg_val & info->mode_mask) if (reg_val & info->mode_mask)
return REGULATOR_MODE_IDLE; return REGULATOR_MODE_IDLE;
@ -400,7 +400,7 @@ static unsigned int hi6421_regulator_buck_get_mode(struct regulator_dev *rdev)
const struct hi6421_regulator_info *info; const struct hi6421_regulator_info *info;
unsigned int reg_val; unsigned int reg_val;
info = container_of(rdev->desc, struct hi6421_regulator_info, desc); info = container_of_const(rdev->desc, struct hi6421_regulator_info, desc);
regmap_read(rdev->regmap, rdev->desc->enable_reg, &reg_val); regmap_read(rdev->regmap, rdev->desc->enable_reg, &reg_val);
if (reg_val & info->mode_mask) if (reg_val & info->mode_mask)
return REGULATOR_MODE_STANDBY; return REGULATOR_MODE_STANDBY;
@ -414,7 +414,7 @@ static int hi6421_regulator_ldo_set_mode(struct regulator_dev *rdev,
const struct hi6421_regulator_info *info; const struct hi6421_regulator_info *info;
unsigned int new_mode; unsigned int new_mode;
info = container_of(rdev->desc, struct hi6421_regulator_info, desc); info = container_of_const(rdev->desc, struct hi6421_regulator_info, desc);
switch (mode) { switch (mode) {
case REGULATOR_MODE_NORMAL: case REGULATOR_MODE_NORMAL:
new_mode = 0; new_mode = 0;
@ -439,7 +439,7 @@ static int hi6421_regulator_buck_set_mode(struct regulator_dev *rdev,
const struct hi6421_regulator_info *info; const struct hi6421_regulator_info *info;
unsigned int new_mode; unsigned int new_mode;
info = container_of(rdev->desc, struct hi6421_regulator_info, desc); info = container_of_const(rdev->desc, struct hi6421_regulator_info, desc);
switch (mode) { switch (mode) {
case REGULATOR_MODE_NORMAL: case REGULATOR_MODE_NORMAL:
new_mode = 0; new_mode = 0;
@ -464,7 +464,7 @@ hi6421_regulator_ldo_get_optimum_mode(struct regulator_dev *rdev,
{ {
const struct hi6421_regulator_info *info; const struct hi6421_regulator_info *info;
info = container_of(rdev->desc, struct hi6421_regulator_info, desc); info = container_of_const(rdev->desc, struct hi6421_regulator_info, desc);
if (load_uA > info->eco_microamp) if (load_uA > info->eco_microamp)
return REGULATOR_MODE_NORMAL; return REGULATOR_MODE_NORMAL;

View File

@ -110,7 +110,7 @@ static unsigned int hi6421v530_regulator_ldo_get_mode(
const struct hi6421v530_regulator_info *info; const struct hi6421v530_regulator_info *info;
unsigned int reg_val; unsigned int reg_val;
info = container_of(rdev->desc, struct hi6421v530_regulator_info, rdesc); info = container_of_const(rdev->desc, struct hi6421v530_regulator_info, rdesc);
regmap_read(rdev->regmap, rdev->desc->enable_reg, &reg_val); regmap_read(rdev->regmap, rdev->desc->enable_reg, &reg_val);
if (reg_val & (info->mode_mask)) if (reg_val & (info->mode_mask))
@ -125,7 +125,7 @@ static int hi6421v530_regulator_ldo_set_mode(struct regulator_dev *rdev,
const struct hi6421v530_regulator_info *info; const struct hi6421v530_regulator_info *info;
unsigned int new_mode; unsigned int new_mode;
info = container_of(rdev->desc, struct hi6421v530_regulator_info, rdesc); info = container_of_const(rdev->desc, struct hi6421v530_regulator_info, rdesc);
switch (mode) { switch (mode) {
case REGULATOR_MODE_NORMAL: case REGULATOR_MODE_NORMAL:
new_mode = 0; new_mode = 0;

View File

@ -121,7 +121,7 @@ static unsigned int hi6421_spmi_regulator_get_mode(struct regulator_dev *rdev)
const struct hi6421_spmi_reg_info *sreg; const struct hi6421_spmi_reg_info *sreg;
unsigned int reg_val; unsigned int reg_val;
sreg = container_of(rdev->desc, struct hi6421_spmi_reg_info, desc); sreg = container_of_const(rdev->desc, struct hi6421_spmi_reg_info, desc);
regmap_read(rdev->regmap, rdev->desc->enable_reg, &reg_val); regmap_read(rdev->regmap, rdev->desc->enable_reg, &reg_val);
if (reg_val & sreg->eco_mode_mask) if (reg_val & sreg->eco_mode_mask)
@ -136,7 +136,7 @@ static int hi6421_spmi_regulator_set_mode(struct regulator_dev *rdev,
const struct hi6421_spmi_reg_info *sreg; const struct hi6421_spmi_reg_info *sreg;
unsigned int val; unsigned int val;
sreg = container_of(rdev->desc, struct hi6421_spmi_reg_info, desc); sreg = container_of_const(rdev->desc, struct hi6421_spmi_reg_info, desc);
switch (mode) { switch (mode) {
case REGULATOR_MODE_NORMAL: case REGULATOR_MODE_NORMAL:
val = 0; val = 0;
@ -162,7 +162,7 @@ hi6421_spmi_regulator_get_optimum_mode(struct regulator_dev *rdev,
{ {
const struct hi6421_spmi_reg_info *sreg; const struct hi6421_spmi_reg_info *sreg;
sreg = container_of(rdev->desc, struct hi6421_spmi_reg_info, desc); sreg = container_of_const(rdev->desc, struct hi6421_spmi_reg_info, desc);
if (!sreg->eco_uA || ((unsigned int)load_uA > sreg->eco_uA)) if (!sreg->eco_uA || ((unsigned int)load_uA > sreg->eco_uA))
return REGULATOR_MODE_NORMAL; return REGULATOR_MODE_NORMAL;

View File

@ -68,7 +68,7 @@ static int max77650_regulator_is_enabled(struct regulator_dev *rdev)
struct regmap *map; struct regmap *map;
int val, rv, en; int val, rv, en;
rdesc = container_of(rdev->desc, struct max77650_regulator_desc, desc); rdesc = container_of_const(rdev->desc, struct max77650_regulator_desc, desc);
map = rdev_get_regmap(rdev); map = rdev_get_regmap(rdev);
rv = regmap_read(map, rdesc->regB, &val); rv = regmap_read(map, rdesc->regB, &val);
@ -85,7 +85,7 @@ static int max77650_regulator_enable(struct regulator_dev *rdev)
const struct max77650_regulator_desc *rdesc; const struct max77650_regulator_desc *rdesc;
struct regmap *map; struct regmap *map;
rdesc = container_of(rdev->desc, struct max77650_regulator_desc, desc); rdesc = container_of_const(rdev->desc, struct max77650_regulator_desc, desc);
map = rdev_get_regmap(rdev); map = rdev_get_regmap(rdev);
return regmap_update_bits(map, rdesc->regB, return regmap_update_bits(map, rdesc->regB,
@ -98,7 +98,7 @@ static int max77650_regulator_disable(struct regulator_dev *rdev)
const struct max77650_regulator_desc *rdesc; const struct max77650_regulator_desc *rdesc;
struct regmap *map; struct regmap *map;
rdesc = container_of(rdev->desc, struct max77650_regulator_desc, desc); rdesc = container_of_const(rdev->desc, struct max77650_regulator_desc, desc);
map = rdev_get_regmap(rdev); map = rdev_get_regmap(rdev);
return regmap_update_bits(map, rdesc->regB, return regmap_update_bits(map, rdesc->regB,

View File

@ -80,7 +80,7 @@ static unsigned int mt6315_regulator_get_mode(struct regulator_dev *rdev)
int ret, regval; int ret, regval;
u32 modeset_mask; u32 modeset_mask;
info = container_of(rdev->desc, struct mt6315_regulator_info, desc); info = container_of_const(rdev->desc, struct mt6315_regulator_info, desc);
modeset_mask = init->modeset_mask[rdev_get_id(rdev)]; modeset_mask = init->modeset_mask[rdev_get_id(rdev)];
ret = regmap_read(rdev->regmap, MT6315_BUCK_TOP_4PHASE_ANA_CON42, &regval); ret = regmap_read(rdev->regmap, MT6315_BUCK_TOP_4PHASE_ANA_CON42, &regval);
if (ret != 0) { if (ret != 0) {
@ -111,7 +111,7 @@ static int mt6315_regulator_set_mode(struct regulator_dev *rdev,
int ret, val, curr_mode; int ret, val, curr_mode;
u32 modeset_mask; u32 modeset_mask;
info = container_of(rdev->desc, struct mt6315_regulator_info, desc); info = container_of_const(rdev->desc, struct mt6315_regulator_info, desc);
modeset_mask = init->modeset_mask[rdev_get_id(rdev)]; modeset_mask = init->modeset_mask[rdev_get_id(rdev)];
curr_mode = mt6315_regulator_get_mode(rdev); curr_mode = mt6315_regulator_get_mode(rdev);
switch (mode) { switch (mode) {
@ -165,7 +165,7 @@ static int mt6315_get_status(struct regulator_dev *rdev)
int ret; int ret;
u32 regval; u32 regval;
info = container_of(rdev->desc, struct mt6315_regulator_info, desc); info = container_of_const(rdev->desc, struct mt6315_regulator_info, desc);
ret = regmap_read(rdev->regmap, info->status_reg, &regval); ret = regmap_read(rdev->regmap, info->status_reg, &regval);
if (ret < 0) { if (ret < 0) {
dev_err(&rdev->dev, "Failed to get enable reg: %d\n", ret); dev_err(&rdev->dev, "Failed to get enable reg: %d\n", ret);

View File

@ -31,7 +31,7 @@ struct mt6358_regulator_info {
u32 modeset_mask; u32 modeset_mask;
}; };
#define to_regulator_info(x) container_of((x), struct mt6358_regulator_info, desc) #define to_regulator_info(x) container_of_const((x), struct mt6358_regulator_info, desc)
#define MT6358_BUCK(match, vreg, supply, min, max, step, \ #define MT6358_BUCK(match, vreg, supply, min, max, step, \
vosel_mask, _da_vsel_reg, _da_vsel_mask, \ vosel_mask, _da_vsel_reg, _da_vsel_mask, \

View File

@ -249,7 +249,7 @@ static int buck_set_dvs(const struct regulator_desc *desc,
} }
if (ret == 0) { if (ret == 0) {
struct pca9450_regulator_desc *regulator = container_of(desc, const struct pca9450_regulator_desc *regulator = container_of_const(desc,
struct pca9450_regulator_desc, desc); struct pca9450_regulator_desc, desc);
/* Enable DVS control through PMIC_STBY_REQ for this BUCK */ /* Enable DVS control through PMIC_STBY_REQ for this BUCK */
@ -263,7 +263,7 @@ static int pca9450_set_dvs_levels(struct device_node *np,
const struct regulator_desc *desc, const struct regulator_desc *desc,
struct regulator_config *cfg) struct regulator_config *cfg)
{ {
struct pca9450_regulator_desc *data = container_of(desc, const struct pca9450_regulator_desc *data = container_of_const(desc,
struct pca9450_regulator_desc, desc); struct pca9450_regulator_desc, desc);
const struct pc9450_dvs_config *dvs = &data->dvs; const struct pc9450_dvs_config *dvs = &data->dvs;
unsigned int reg, mask; unsigned int reg, mask;
@ -308,7 +308,7 @@ static inline unsigned int pca9450_map_mode(unsigned int mode)
static int pca9450_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) static int pca9450_buck_set_mode(struct regulator_dev *rdev, unsigned int mode)
{ {
struct pca9450_regulator_desc *desc = container_of(rdev->desc, const struct pca9450_regulator_desc *desc = container_of_const(rdev->desc,
struct pca9450_regulator_desc, desc); struct pca9450_regulator_desc, desc);
const struct pc9450_dvs_config *dvs = &desc->dvs; const struct pc9450_dvs_config *dvs = &desc->dvs;
int val; int val;
@ -333,7 +333,7 @@ static int pca9450_buck_set_mode(struct regulator_dev *rdev, unsigned int mode)
static unsigned int pca9450_buck_get_mode(struct regulator_dev *rdev) static unsigned int pca9450_buck_get_mode(struct regulator_dev *rdev)
{ {
struct pca9450_regulator_desc *desc = container_of(rdev->desc, const struct pca9450_regulator_desc *desc = container_of_const(rdev->desc,
struct pca9450_regulator_desc, desc); struct pca9450_regulator_desc, desc);
const struct pc9450_dvs_config *dvs = &desc->dvs; const struct pc9450_dvs_config *dvs = &desc->dvs;
int ret = 0, regval; int ret = 0, regval;

View File

@ -0,0 +1,429 @@
// SPDX-License-Identifier: GPL-2.0
//
// regulator driver for the PF1550
//
// Copyright (C) 2016 Freescale Semiconductor, Inc.
// Robin Gong <yibin.gong@freescale.com>
//
// Portions Copyright (c) 2025 Savoir-faire Linux Inc.
// Samuel Kayode <samuel.kayode@savoirfairelinux.com>
//
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/mfd/pf1550.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#define PF1550_REGULATOR_IRQ_NR 11
#define PF1550_MAX_REGULATOR 7
struct pf1550_desc {
struct regulator_desc desc;
unsigned char stby_reg;
unsigned char stby_mask;
unsigned char stby_enable_reg;
unsigned char stby_enable_mask;
};
struct pf1550_regulator_info {
struct device *dev;
const struct pf1550_ddata *pf1550;
struct pf1550_desc regulator_descs[PF1550_MAX_REGULATOR];
struct regulator_dev *rdevs[PF1550_MAX_REGULATOR];
};
static const int pf1550_sw12_volts[] = {
1100000, 1200000, 1350000, 1500000, 1800000, 2500000, 3000000, 3300000,
};
static const int pf1550_ldo13_volts[] = {
750000, 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000,
1150000, 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000,
1800000, 1900000, 2000000, 2100000, 2200000, 2300000, 2400000, 2500000,
2600000, 2700000, 2800000, 2900000, 3000000, 3100000, 3200000, 3300000,
};
static int pf1550_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
{
int id = rdev_get_id(rdev);
unsigned int ramp_bits = 0;
int ret;
if (id > PF1550_VREFDDR)
return -EACCES;
if (ramp_delay < 0 || ramp_delay > 6250)
return -EINVAL;
ramp_delay = 6250 / ramp_delay;
ramp_bits = ramp_delay >> 1;
ret = regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg + 4, 0x10,
ramp_bits << 4);
if (ret < 0)
dev_err(&rdev->dev, "ramp failed, err %d\n", ret);
return ret;
}
static int pf1550_set_suspend_enable(struct regulator_dev *rdev)
{
const struct pf1550_desc *desc = container_of_const(rdev->desc,
struct pf1550_desc,
desc);
unsigned int val = desc->stby_enable_mask;
return regmap_update_bits(rdev->regmap, desc->stby_enable_reg,
desc->stby_enable_mask, val);
}
static int pf1550_set_suspend_disable(struct regulator_dev *rdev)
{
const struct pf1550_desc *desc = container_of_const(rdev->desc,
struct pf1550_desc,
desc);
return regmap_update_bits(rdev->regmap, desc->stby_enable_reg,
desc->stby_enable_mask, 0);
}
static int pf1550_buck_set_table_suspend_voltage(struct regulator_dev *rdev,
int uV)
{
const struct pf1550_desc *desc = container_of_const(rdev->desc,
struct pf1550_desc,
desc);
int ret;
ret = regulator_map_voltage_ascend(rdev, uV, uV);
if (ret < 0) {
dev_err(rdev_get_dev(rdev), "failed to map %i uV\n", uV);
return ret;
}
return regmap_update_bits(rdev->regmap, desc->stby_reg,
desc->stby_mask, ret);
}
static int pf1550_buck_set_linear_suspend_voltage(struct regulator_dev *rdev,
int uV)
{
const struct pf1550_desc *desc = container_of_const(rdev->desc,
struct pf1550_desc,
desc);
int ret;
ret = regulator_map_voltage_linear(rdev, uV, uV);
if (ret < 0) {
dev_err(rdev_get_dev(rdev), "failed to map %i uV\n", uV);
return ret;
}
return regmap_update_bits(rdev->regmap, desc->stby_reg,
desc->stby_mask, ret);
}
static const struct regulator_ops pf1550_sw1_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.set_suspend_enable = pf1550_set_suspend_enable,
.set_suspend_disable = pf1550_set_suspend_disable,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_table,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
.set_suspend_voltage = pf1550_buck_set_table_suspend_voltage,
.map_voltage = regulator_map_voltage_ascend,
.set_ramp_delay = pf1550_set_ramp_delay,
};
static const struct regulator_ops pf1550_sw2_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.set_suspend_enable = pf1550_set_suspend_enable,
.set_suspend_disable = pf1550_set_suspend_disable,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
.set_suspend_voltage = pf1550_buck_set_linear_suspend_voltage,
.map_voltage = regulator_map_voltage_linear,
.set_ramp_delay = pf1550_set_ramp_delay,
};
static const struct regulator_ops pf1550_ldo1_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.set_suspend_enable = pf1550_set_suspend_enable,
.set_suspend_disable = pf1550_set_suspend_disable,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_table,
.map_voltage = regulator_map_voltage_ascend,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
};
static const struct regulator_ops pf1550_ldo2_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.set_suspend_enable = pf1550_set_suspend_enable,
.set_suspend_disable = pf1550_set_suspend_disable,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.map_voltage = regulator_map_voltage_linear,
};
static const struct regulator_ops pf1550_fixed_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.set_suspend_enable = pf1550_set_suspend_enable,
.set_suspend_disable = pf1550_set_suspend_disable,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
};
#define PF_VREF(_chip, match, _name, voltage) { \
.desc = { \
.name = #_name, \
.of_match = of_match_ptr(match), \
.regulators_node = of_match_ptr("regulators"), \
.n_voltages = 1, \
.ops = &pf1550_fixed_ops, \
.type = REGULATOR_VOLTAGE, \
.id = _chip ## _ ## _name, \
.owner = THIS_MODULE, \
.min_uV = (voltage), \
.enable_reg = _chip ## _PMIC_REG_ ## _name ## _CTRL, \
.enable_mask = 0x1, \
}, \
.stby_enable_reg = _chip ## _PMIC_REG_ ## _name ## _CTRL, \
.stby_enable_mask = 0x2, \
}
#define PF_SW(_chip, match, _name, min, max, mask, step) { \
.desc = { \
.name = #_name, \
.of_match = of_match_ptr(match), \
.regulators_node = of_match_ptr("regulators"), \
.n_voltages = ((max) - (min)) / (step) + 1, \
.ops = &pf1550_sw2_ops, \
.type = REGULATOR_VOLTAGE, \
.id = _chip ## _ ## _name, \
.owner = THIS_MODULE, \
.min_uV = (min), \
.uV_step = (step), \
.linear_min_sel = 0, \
.vsel_reg = _chip ## _PMIC_REG_ ## _name ## _VOLT, \
.vsel_mask = (mask), \
.enable_reg = _chip ## _PMIC_REG_ ## _name ## _CTRL, \
.enable_mask = 0x1, \
}, \
.stby_reg = _chip ## _PMIC_REG_ ## _name ## _STBY_VOLT, \
.stby_mask = (mask), \
.stby_enable_reg = _chip ## _PMIC_REG_ ## _name ## _CTRL, \
.stby_enable_mask = 0x2, \
}
#define PF_LDO1(_chip, match, _name, mask, voltages) { \
.desc = { \
.name = #_name, \
.of_match = of_match_ptr(match), \
.regulators_node = of_match_ptr("regulators"), \
.n_voltages = ARRAY_SIZE(voltages), \
.ops = &pf1550_ldo1_ops, \
.type = REGULATOR_VOLTAGE, \
.id = _chip ## _ ## _name, \
.owner = THIS_MODULE, \
.volt_table = voltages, \
.vsel_reg = _chip ## _PMIC_REG_ ## _name ## _VOLT, \
.vsel_mask = (mask), \
.enable_reg = _chip ## _PMIC_REG_ ## _name ## _CTRL, \
.enable_mask = 0x1, \
}, \
.stby_enable_reg = _chip ## _PMIC_REG_ ## _name ## _CTRL, \
.stby_enable_mask = 0x2, \
}
#define PF_LDO2(_chip, match, _name, mask, min, max, step) { \
.desc = { \
.name = #_name, \
.of_match = of_match_ptr(match), \
.regulators_node = of_match_ptr("regulators"), \
.n_voltages = ((max) - (min)) / (step) + 1, \
.ops = &pf1550_ldo2_ops, \
.type = REGULATOR_VOLTAGE, \
.id = _chip ## _ ## _name, \
.owner = THIS_MODULE, \
.min_uV = (min), \
.uV_step = (step), \
.linear_min_sel = 0, \
.vsel_reg = _chip ## _PMIC_REG_ ## _name ## _VOLT, \
.vsel_mask = (mask), \
.enable_reg = _chip ## _PMIC_REG_ ## _name ## _CTRL, \
.enable_mask = 0x1, \
}, \
.stby_enable_reg = _chip ## _PMIC_REG_ ## _name ## _CTRL, \
.stby_enable_mask = 0x2, \
}
static struct pf1550_desc pf1550_regulators[] = {
PF_SW(PF1550, "sw1", SW1, 600000, 1387500, 0x3f, 12500),
PF_SW(PF1550, "sw2", SW2, 600000, 1387500, 0x3f, 12500),
PF_SW(PF1550, "sw3", SW3, 1800000, 3300000, 0xf, 100000),
PF_VREF(PF1550, "vrefddr", VREFDDR, 1200000),
PF_LDO1(PF1550, "ldo1", LDO1, 0x1f, pf1550_ldo13_volts),
PF_LDO2(PF1550, "ldo2", LDO2, 0xf, 1800000, 3300000, 100000),
PF_LDO1(PF1550, "ldo3", LDO3, 0x1f, pf1550_ldo13_volts),
};
static irqreturn_t pf1550_regulator_irq_handler(int irq, void *data)
{
struct pf1550_regulator_info *info = data;
struct device *dev = info->dev;
struct platform_device *pdev = to_platform_device(dev);
int i, irq_type = -1;
unsigned int event;
for (i = 0; i < PF1550_REGULATOR_IRQ_NR; i++)
if (irq == platform_get_irq(pdev, i))
irq_type = i;
switch (irq_type) {
/* The _LS interrupts indicate over-current event. The _HS interrupts
* which are more accurate and can detect catastrophic faults, issue
* an error event. The current limit FAULT interrupt is similar to the
* _HS'
*/
case PF1550_PMIC_IRQ_SW1_LS:
case PF1550_PMIC_IRQ_SW2_LS:
case PF1550_PMIC_IRQ_SW3_LS:
event = REGULATOR_EVENT_OVER_CURRENT_WARN;
for (i = 0; i < PF1550_MAX_REGULATOR; i++)
if (!strcmp(rdev_get_name(info->rdevs[i]), "SW3"))
regulator_notifier_call_chain(info->rdevs[i],
event, NULL);
break;
case PF1550_PMIC_IRQ_SW1_HS:
case PF1550_PMIC_IRQ_SW2_HS:
case PF1550_PMIC_IRQ_SW3_HS:
event = REGULATOR_EVENT_OVER_CURRENT;
for (i = 0; i < PF1550_MAX_REGULATOR; i++)
if (!strcmp(rdev_get_name(info->rdevs[i]), "SW3"))
regulator_notifier_call_chain(info->rdevs[i],
event, NULL);
break;
case PF1550_PMIC_IRQ_LDO1_FAULT:
case PF1550_PMIC_IRQ_LDO2_FAULT:
case PF1550_PMIC_IRQ_LDO3_FAULT:
event = REGULATOR_EVENT_OVER_CURRENT;
for (i = 0; i < PF1550_MAX_REGULATOR; i++)
if (!strcmp(rdev_get_name(info->rdevs[i]), "LDO3"))
regulator_notifier_call_chain(info->rdevs[i],
event, NULL);
break;
case PF1550_PMIC_IRQ_TEMP_110:
case PF1550_PMIC_IRQ_TEMP_125:
event = REGULATOR_EVENT_OVER_TEMP;
for (i = 0; i < PF1550_MAX_REGULATOR; i++)
regulator_notifier_call_chain(info->rdevs[i],
event, NULL);
break;
default:
dev_err(dev, "regulator interrupt: irq %d occurred\n",
irq_type);
}
return IRQ_HANDLED;
}
static int pf1550_regulator_probe(struct platform_device *pdev)
{
const struct pf1550_ddata *pf1550 = dev_get_drvdata(pdev->dev.parent);
struct regulator_config config = { };
struct pf1550_regulator_info *info;
int i, irq = -1, ret = 0;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
config.regmap = dev_get_regmap(pf1550->dev, NULL);
if (!config.regmap)
return dev_err_probe(&pdev->dev, -ENODEV,
"failed to get parent regmap\n");
config.dev = pf1550->dev;
config.regmap = pf1550->regmap;
info->dev = &pdev->dev;
info->pf1550 = pf1550;
memcpy(info->regulator_descs, pf1550_regulators,
sizeof(info->regulator_descs));
for (i = 0; i < ARRAY_SIZE(pf1550_regulators); i++) {
struct regulator_desc *desc;
desc = &info->regulator_descs[i].desc;
if ((desc->id == PF1550_SW2 && !pf1550->dvs2_enable) ||
(desc->id == PF1550_SW1 && !pf1550->dvs1_enable)) {
/* OTP_SW2_DVS_ENB == 1? or OTP_SW1_DVS_ENB == 1? */
desc->volt_table = pf1550_sw12_volts;
desc->n_voltages = ARRAY_SIZE(pf1550_sw12_volts);
desc->ops = &pf1550_sw1_ops;
}
info->rdevs[i] = devm_regulator_register(&pdev->dev, desc,
&config);
if (IS_ERR(info->rdevs[i]))
return dev_err_probe(&pdev->dev,
PTR_ERR(info->rdevs[i]),
"failed to initialize regulator-%d\n",
i);
}
platform_set_drvdata(pdev, info);
for (i = 0; i < PF1550_REGULATOR_IRQ_NR; i++) {
irq = platform_get_irq(pdev, i);
if (irq < 0)
return irq;
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
pf1550_regulator_irq_handler,
IRQF_NO_SUSPEND,
"pf1550-regulator", info);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"failed: irq request (IRQ: %d)\n",
i);
}
return 0;
}
static const struct platform_device_id pf1550_regulator_id[] = {
{ "pf1550-regulator", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, pf1550_regulator_id);
static struct platform_driver pf1550_regulator_driver = {
.driver = {
.name = "pf1550-regulator",
},
.probe = pf1550_regulator_probe,
.id_table = pf1550_regulator_id,
};
module_platform_driver(pf1550_regulator_driver);
MODULE_DESCRIPTION("NXP PF1550 regulator driver");
MODULE_AUTHOR("Robin Gong <yibin.gong@freescale.com>");
MODULE_LICENSE("GPL");

View File

@ -538,7 +538,9 @@ static int buck_set_dvs(const struct regulator_desc *desc,
static int pf9453_set_dvs_levels(struct device_node *np, const struct regulator_desc *desc, static int pf9453_set_dvs_levels(struct device_node *np, const struct regulator_desc *desc,
struct regulator_config *cfg) struct regulator_config *cfg)
{ {
struct pf9453_regulator_desc *data = container_of(desc, struct pf9453_regulator_desc, desc); const struct pf9453_regulator_desc *data = container_of_const(desc,
struct pf9453_regulator_desc,
desc);
struct pf9453 *pf9453 = dev_get_drvdata(cfg->dev); struct pf9453 *pf9453 = dev_get_drvdata(cfg->dev);
const struct pf9453_dvs_config *dvs = &data->dvs; const struct pf9453_dvs_config *dvs = &data->dvs;
unsigned int reg, mask; unsigned int reg, mask;

273
include/linux/mfd/pf1550.h Normal file
View File

@ -0,0 +1,273 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Declarations for the PF1550 PMIC
*
* Copyright (C) 2016 Freescale Semiconductor, Inc.
* Robin Gong <yibin.gong@freescale.com>
*
* Portions Copyright (c) 2025 Savoir-faire Linux Inc.
* Samuel Kayode <samuel.kayode@savoirfairelinux.com>
*/
#ifndef __LINUX_MFD_PF1550_H
#define __LINUX_MFD_PF1550_H
#include <linux/i2c.h>
#include <linux/regmap.h>
enum pf1550_pmic_reg {
/* PMIC regulator part */
PF1550_PMIC_REG_DEVICE_ID = 0x00,
PF1550_PMIC_REG_OTP_FLAVOR = 0x01,
PF1550_PMIC_REG_SILICON_REV = 0x02,
PF1550_PMIC_REG_INT_CATEGORY = 0x06,
PF1550_PMIC_REG_SW_INT_STAT0 = 0x08,
PF1550_PMIC_REG_SW_INT_MASK0 = 0x09,
PF1550_PMIC_REG_SW_INT_SENSE0 = 0x0a,
PF1550_PMIC_REG_SW_INT_STAT1 = 0x0b,
PF1550_PMIC_REG_SW_INT_MASK1 = 0x0c,
PF1550_PMIC_REG_SW_INT_SENSE1 = 0x0d,
PF1550_PMIC_REG_SW_INT_STAT2 = 0x0e,
PF1550_PMIC_REG_SW_INT_MASK2 = 0x0f,
PF1550_PMIC_REG_SW_INT_SENSE2 = 0x10,
PF1550_PMIC_REG_LDO_INT_STAT0 = 0x18,
PF1550_PMIC_REG_LDO_INT_MASK0 = 0x19,
PF1550_PMIC_REG_LDO_INT_SENSE0 = 0x1a,
PF1550_PMIC_REG_TEMP_INT_STAT0 = 0x20,
PF1550_PMIC_REG_TEMP_INT_MASK0 = 0x21,
PF1550_PMIC_REG_TEMP_INT_SENSE0 = 0x22,
PF1550_PMIC_REG_ONKEY_INT_STAT0 = 0x24,
PF1550_PMIC_REG_ONKEY_INT_MASK0 = 0x25,
PF1550_PMIC_REG_ONKEY_INT_SENSE0 = 0x26,
PF1550_PMIC_REG_MISC_INT_STAT0 = 0x28,
PF1550_PMIC_REG_MISC_INT_MASK0 = 0x29,
PF1550_PMIC_REG_MISC_INT_SENSE0 = 0x2a,
PF1550_PMIC_REG_COINCELL_CONTROL = 0x30,
PF1550_PMIC_REG_SW1_VOLT = 0x32,
PF1550_PMIC_REG_SW1_STBY_VOLT = 0x33,
PF1550_PMIC_REG_SW1_SLP_VOLT = 0x34,
PF1550_PMIC_REG_SW1_CTRL = 0x35,
PF1550_PMIC_REG_SW1_CTRL1 = 0x36,
PF1550_PMIC_REG_SW2_VOLT = 0x38,
PF1550_PMIC_REG_SW2_STBY_VOLT = 0x39,
PF1550_PMIC_REG_SW2_SLP_VOLT = 0x3a,
PF1550_PMIC_REG_SW2_CTRL = 0x3b,
PF1550_PMIC_REG_SW2_CTRL1 = 0x3c,
PF1550_PMIC_REG_SW3_VOLT = 0x3e,
PF1550_PMIC_REG_SW3_STBY_VOLT = 0x3f,
PF1550_PMIC_REG_SW3_SLP_VOLT = 0x40,
PF1550_PMIC_REG_SW3_CTRL = 0x41,
PF1550_PMIC_REG_SW3_CTRL1 = 0x42,
PF1550_PMIC_REG_VSNVS_CTRL = 0x48,
PF1550_PMIC_REG_VREFDDR_CTRL = 0x4a,
PF1550_PMIC_REG_LDO1_VOLT = 0x4c,
PF1550_PMIC_REG_LDO1_CTRL = 0x4d,
PF1550_PMIC_REG_LDO2_VOLT = 0x4f,
PF1550_PMIC_REG_LDO2_CTRL = 0x50,
PF1550_PMIC_REG_LDO3_VOLT = 0x52,
PF1550_PMIC_REG_LDO3_CTRL = 0x53,
PF1550_PMIC_REG_PWRCTRL0 = 0x58,
PF1550_PMIC_REG_PWRCTRL1 = 0x59,
PF1550_PMIC_REG_PWRCTRL2 = 0x5a,
PF1550_PMIC_REG_PWRCTRL3 = 0x5b,
PF1550_PMIC_REG_SW1_PWRDN_SEQ = 0x5f,
PF1550_PMIC_REG_SW2_PWRDN_SEQ = 0x60,
PF1550_PMIC_REG_SW3_PWRDN_SEQ = 0x61,
PF1550_PMIC_REG_LDO1_PWRDN_SEQ = 0x62,
PF1550_PMIC_REG_LDO2_PWRDN_SEQ = 0x63,
PF1550_PMIC_REG_LDO3_PWRDN_SEQ = 0x64,
PF1550_PMIC_REG_VREFDDR_PWRDN_SEQ = 0x65,
PF1550_PMIC_REG_STATE_INFO = 0x67,
PF1550_PMIC_REG_I2C_ADDR = 0x68,
PF1550_PMIC_REG_IO_DRV0 = 0x69,
PF1550_PMIC_REG_IO_DRV1 = 0x6a,
PF1550_PMIC_REG_RC_16MHZ = 0x6b,
PF1550_PMIC_REG_KEY = 0x6f,
/* Charger part */
PF1550_CHARG_REG_CHG_INT = 0x80,
PF1550_CHARG_REG_CHG_INT_MASK = 0x82,
PF1550_CHARG_REG_CHG_INT_OK = 0x84,
PF1550_CHARG_REG_VBUS_SNS = 0x86,
PF1550_CHARG_REG_CHG_SNS = 0x87,
PF1550_CHARG_REG_BATT_SNS = 0x88,
PF1550_CHARG_REG_CHG_OPER = 0x89,
PF1550_CHARG_REG_CHG_TMR = 0x8a,
PF1550_CHARG_REG_CHG_EOC_CNFG = 0x8d,
PF1550_CHARG_REG_CHG_CURR_CNFG = 0x8e,
PF1550_CHARG_REG_BATT_REG = 0x8f,
PF1550_CHARG_REG_BATFET_CNFG = 0x91,
PF1550_CHARG_REG_THM_REG_CNFG = 0x92,
PF1550_CHARG_REG_VBUS_INLIM_CNFG = 0x94,
PF1550_CHARG_REG_VBUS_LIN_DPM = 0x95,
PF1550_CHARG_REG_USB_PHY_LDO_CNFG = 0x96,
PF1550_CHARG_REG_DBNC_DELAY_TIME = 0x98,
PF1550_CHARG_REG_CHG_INT_CNFG = 0x99,
PF1550_CHARG_REG_THM_ADJ_SETTING = 0x9a,
PF1550_CHARG_REG_VBUS2SYS_CNFG = 0x9b,
PF1550_CHARG_REG_LED_PWM = 0x9c,
PF1550_CHARG_REG_FAULT_BATFET_CNFG = 0x9d,
PF1550_CHARG_REG_LED_CNFG = 0x9e,
PF1550_CHARG_REG_CHGR_KEY2 = 0x9f,
PF1550_TEST_REG_FMRADDR = 0xc4,
PF1550_TEST_REG_FMRDATA = 0xc5,
PF1550_TEST_REG_KEY3 = 0xdf,
PF1550_PMIC_REG_END = 0xff,
};
/* One-Time Programmable(OTP) memory */
enum pf1550_otp_reg {
PF1550_OTP_SW1_SW2 = 0x1e,
PF1550_OTP_SW2_SW3 = 0x1f,
};
#define PF1550_DEVICE_ID 0x7c
/* Keys for reading OTP */
#define PF1550_OTP_PMIC_KEY 0x15
#define PF1550_OTP_CHGR_KEY 0x50
#define PF1550_OTP_TEST_KEY 0xab
/* Supported charger modes */
#define PF1550_CHG_BAT_OFF 1
#define PF1550_CHG_BAT_ON 2
#define PF1550_CHG_PRECHARGE 0
#define PF1550_CHG_CONSTANT_CURRENT 1
#define PF1550_CHG_CONSTANT_VOL 2
#define PF1550_CHG_EOC 3
#define PF1550_CHG_DONE 4
#define PF1550_CHG_TIMER_FAULT 6
#define PF1550_CHG_SUSPEND 7
#define PF1550_CHG_OFF_INV 8
#define PF1550_CHG_BAT_OVER 9
#define PF1550_CHG_OFF_TEMP 10
#define PF1550_CHG_LINEAR_ONLY 12
#define PF1550_CHG_SNS_MASK 0xf
#define PF1550_CHG_INT_MASK 0x51
#define PF1550_BAT_NO_VBUS 0
#define PF1550_BAT_LOW_THAN_PRECHARG 1
#define PF1550_BAT_CHARG_FAIL 2
#define PF1550_BAT_HIGH_THAN_PRECHARG 4
#define PF1550_BAT_OVER_VOL 5
#define PF1550_BAT_NO_DETECT 6
#define PF1550_BAT_SNS_MASK 0x7
#define PF1550_VBUS_UVLO BIT(2)
#define PF1550_VBUS_IN2SYS BIT(3)
#define PF1550_VBUS_OVLO BIT(4)
#define PF1550_VBUS_VALID BIT(5)
#define PF1550_CHARG_REG_BATT_REG_CHGCV_MASK 0x3f
#define PF1550_CHARG_REG_BATT_REG_VMINSYS_SHIFT 6
#define PF1550_CHARG_REG_BATT_REG_VMINSYS_MASK GENMASK(7, 6)
#define PF1550_CHARG_REG_THM_REG_CNFG_REGTEMP_SHIFT 2
#define PF1550_CHARG_REG_THM_REG_CNFG_REGTEMP_MASK GENMASK(3, 2)
#define PF1550_ONKEY_RST_EN BIT(7)
/* DVS enable masks */
#define OTP_SW1_DVS_ENB BIT(1)
#define OTP_SW2_DVS_ENB BIT(3)
/* Top level interrupt masks */
#define IRQ_REGULATOR (BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(6))
#define IRQ_ONKEY BIT(5)
#define IRQ_CHG BIT(0)
/* Regulator interrupt masks */
#define PMIC_IRQ_SW1_LS BIT(0)
#define PMIC_IRQ_SW2_LS BIT(1)
#define PMIC_IRQ_SW3_LS BIT(2)
#define PMIC_IRQ_SW1_HS BIT(0)
#define PMIC_IRQ_SW2_HS BIT(1)
#define PMIC_IRQ_SW3_HS BIT(2)
#define PMIC_IRQ_LDO1_FAULT BIT(0)
#define PMIC_IRQ_LDO2_FAULT BIT(1)
#define PMIC_IRQ_LDO3_FAULT BIT(2)
#define PMIC_IRQ_TEMP_110 BIT(0)
#define PMIC_IRQ_TEMP_125 BIT(1)
/* Onkey interrupt masks */
#define ONKEY_IRQ_PUSHI BIT(0)
#define ONKEY_IRQ_1SI BIT(1)
#define ONKEY_IRQ_2SI BIT(2)
#define ONKEY_IRQ_3SI BIT(3)
#define ONKEY_IRQ_4SI BIT(4)
#define ONKEY_IRQ_8SI BIT(5)
/* Charger interrupt masks */
#define CHARG_IRQ_BAT2SOCI BIT(1)
#define CHARG_IRQ_BATI BIT(2)
#define CHARG_IRQ_CHGI BIT(3)
#define CHARG_IRQ_VBUSI BIT(5)
#define CHARG_IRQ_DPMI BIT(6)
#define CHARG_IRQ_THMI BIT(7)
enum pf1550_irq {
PF1550_IRQ_CHG,
PF1550_IRQ_REGULATOR,
PF1550_IRQ_ONKEY,
};
enum pf1550_pmic_irq {
PF1550_PMIC_IRQ_SW1_LS,
PF1550_PMIC_IRQ_SW2_LS,
PF1550_PMIC_IRQ_SW3_LS,
PF1550_PMIC_IRQ_SW1_HS,
PF1550_PMIC_IRQ_SW2_HS,
PF1550_PMIC_IRQ_SW3_HS,
PF1550_PMIC_IRQ_LDO1_FAULT,
PF1550_PMIC_IRQ_LDO2_FAULT,
PF1550_PMIC_IRQ_LDO3_FAULT,
PF1550_PMIC_IRQ_TEMP_110,
PF1550_PMIC_IRQ_TEMP_125,
};
enum pf1550_onkey_irq {
PF1550_ONKEY_IRQ_PUSHI,
PF1550_ONKEY_IRQ_1SI,
PF1550_ONKEY_IRQ_2SI,
PF1550_ONKEY_IRQ_3SI,
PF1550_ONKEY_IRQ_4SI,
PF1550_ONKEY_IRQ_8SI,
};
enum pf1550_charg_irq {
PF1550_CHARG_IRQ_BAT2SOCI,
PF1550_CHARG_IRQ_BATI,
PF1550_CHARG_IRQ_CHGI,
PF1550_CHARG_IRQ_VBUSI,
PF1550_CHARG_IRQ_THMI,
};
enum pf1550_regulators {
PF1550_SW1,
PF1550_SW2,
PF1550_SW3,
PF1550_VREFDDR,
PF1550_LDO1,
PF1550_LDO2,
PF1550_LDO3,
};
struct pf1550_ddata {
struct regmap_irq_chip_data *irq_data_regulator;
struct regmap_irq_chip_data *irq_data_charger;
struct regmap_irq_chip_data *irq_data_onkey;
struct regmap_irq_chip_data *irq_data;
struct regmap *regmap;
struct device *dev;
bool dvs1_enable;
bool dvs2_enable;
int irq;
};
#endif /* __LINUX_MFD_PF1550_H */