Commit 04f38933 authored by Lee Jones's avatar Lee Jones

Merge branch 'ib-mfd-regulator-pm8008-6.11' into ibs-for-mfd-merged

parents 5ffe70d8 11d861d2
......@@ -19,110 +19,136 @@ properties:
const: qcom,pm8008
reg:
description:
I2C slave address.
maxItems: 1
interrupts:
maxItems: 1
description: Parent interrupt.
reset-gpios:
maxItems: 1
vdd-l1-l2-supply: true
vdd-l3-l4-supply: true
vdd-l5-supply: true
vdd-l6-supply: true
vdd-l7-supply: true
"#interrupt-cells":
gpio-controller: true
"#gpio-cells":
const: 2
description: |
The first cell is the IRQ number, the second cell is the IRQ trigger
flag. All interrupts are listed in include/dt-bindings/mfd/qcom-pm8008.h.
gpio-ranges:
maxItems: 1
interrupt-controller: true
"#address-cells":
const: 1
"#interrupt-cells":
const: 2
"#size-cells":
"#thermal-sensor-cells":
const: 0
patternProperties:
"^gpio@[0-9a-f]+$":
pinctrl:
type: object
additionalProperties: false
patternProperties:
"-state$":
type: object
description: |
The GPIO peripheral. This node may be specified twice, one for each GPIO.
properties:
compatible:
items:
- const: qcom,pm8008-gpio
- const: qcom,spmi-gpio
reg:
description: Peripheral address of one of the two GPIO peripherals.
maxItems: 1
gpio-controller: true
gpio-ranges:
maxItems: 1
allOf:
- $ref: /schemas/pinctrl/pinmux-node.yaml
- $ref: /schemas/pinctrl/pincfg-node.yaml
interrupt-controller: true
properties:
pins:
items:
pattern: "^gpio[12]$"
"#interrupt-cells":
const: 2
function:
items:
- enum:
- normal
"#gpio-cells":
const: 2
required:
- pins
- function
required:
- compatible
- reg
- gpio-controller
- interrupt-controller
- "#gpio-cells"
- gpio-ranges
- "#interrupt-cells"
additionalProperties: false
regulators:
type: object
additionalProperties: false
patternProperties:
"^ldo[1-7]$":
type: object
$ref: /schemas/regulator/regulator.yaml#
unevaluatedProperties: false
required:
- compatible
- reg
- interrupts
- "#address-cells"
- "#size-cells"
- vdd-l1-l2-supply
- vdd-l3-l4-supply
- vdd-l5-supply
- vdd-l6-supply
- vdd-l7-supply
- gpio-controller
- "#gpio-cells"
- gpio-ranges
- interrupt-controller
- "#interrupt-cells"
- "#thermal-sensor-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/mfd/qcom-pm8008.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
pmic@8 {
pm8008: pmic@8 {
compatible = "qcom,pm8008";
reg = <0x8>;
#address-cells = <1>;
#size-cells = <0>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&tlmm>;
interrupts = <32 IRQ_TYPE_EDGE_RISING>;
pm8008_gpios: gpio@c000 {
compatible = "qcom,pm8008-gpio", "qcom,spmi-gpio";
reg = <0xc000>;
gpio-controller;
gpio-ranges = <&pm8008_gpios 0 0 2>;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
reset-gpios = <&tlmm 42 GPIO_ACTIVE_LOW>;
vdd-l1-l2-supply = <&vreg_s8b_1p2>;
vdd-l3-l4-supply = <&vreg_s1b_1p8>;
vdd-l5-supply = <&vreg_bob>;
vdd-l6-supply = <&vreg_bob>;
vdd-l7-supply = <&vreg_bob>;
gpio-controller;
#gpio-cells = <2>;
gpio-ranges = <&pm8008 0 0 2>;
interrupt-controller;
#interrupt-cells = <2>;
#thermal-sensor-cells = <0>;
pinctrl {
gpio-keys-state {
pins = "gpio1";
function = "normal";
};
};
regulators {
ldo1 {
regulator-name = "vreg_l1";
regulator-min-microvolt = <950000>;
regulator-max-microvolt = <1300000>;
};
};
};
};
......
......@@ -2220,6 +2220,7 @@ config MFD_ACER_A500_EC
config MFD_QCOM_PM8008
tristate "QCOM PM8008 Power Management IC"
depends on I2C && OF
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
help
......
......@@ -4,10 +4,13 @@
*/
#include <linux/bitops.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
......@@ -15,8 +18,6 @@
#include <linux/regmap.h>
#include <linux/slab.h>
#include <dt-bindings/mfd/qcom-pm8008.h>
#define I2C_INTR_STATUS_BASE 0x0550
#define INT_RT_STS_OFFSET 0x10
#define INT_SET_TYPE_OFFSET 0x11
......@@ -37,34 +38,54 @@ enum {
#define PM8008_PERIPH_0_BASE 0x900
#define PM8008_PERIPH_1_BASE 0x2400
#define PM8008_PERIPH_2_BASE 0xC000
#define PM8008_PERIPH_3_BASE 0xC100
#define PM8008_PERIPH_2_BASE 0xc000
#define PM8008_PERIPH_3_BASE 0xc100
#define PM8008_TEMP_ALARM_ADDR PM8008_PERIPH_1_BASE
#define PM8008_GPIO1_ADDR PM8008_PERIPH_2_BASE
#define PM8008_GPIO2_ADDR PM8008_PERIPH_3_BASE
/* PM8008 IRQ numbers */
#define PM8008_IRQ_MISC_UVLO 0
#define PM8008_IRQ_MISC_OVLO 1
#define PM8008_IRQ_MISC_OTST2 2
#define PM8008_IRQ_MISC_OTST3 3
#define PM8008_IRQ_MISC_LDO_OCP 4
#define PM8008_IRQ_TEMP_ALARM 5
#define PM8008_IRQ_GPIO1 6
#define PM8008_IRQ_GPIO2 7
enum {
SET_TYPE_INDEX,
POLARITY_HI_INDEX,
POLARITY_LO_INDEX,
};
static unsigned int pm8008_config_regs[] = {
static const unsigned int pm8008_config_regs[] = {
INT_SET_TYPE_OFFSET,
INT_POL_HIGH_OFFSET,
INT_POL_LOW_OFFSET,
};
static struct regmap_irq pm8008_irqs[] = {
REGMAP_IRQ_REG(PM8008_IRQ_MISC_UVLO, PM8008_MISC, BIT(0)),
REGMAP_IRQ_REG(PM8008_IRQ_MISC_OVLO, PM8008_MISC, BIT(1)),
REGMAP_IRQ_REG(PM8008_IRQ_MISC_OTST2, PM8008_MISC, BIT(2)),
REGMAP_IRQ_REG(PM8008_IRQ_MISC_OTST3, PM8008_MISC, BIT(3)),
REGMAP_IRQ_REG(PM8008_IRQ_MISC_LDO_OCP, PM8008_MISC, BIT(4)),
REGMAP_IRQ_REG(PM8008_IRQ_TEMP_ALARM, PM8008_TEMP_ALARM, BIT(0)),
REGMAP_IRQ_REG(PM8008_IRQ_GPIO1, PM8008_GPIO1, BIT(0)),
REGMAP_IRQ_REG(PM8008_IRQ_GPIO2, PM8008_GPIO2, BIT(0)),
#define _IRQ(_irq, _off, _mask, _types) \
[_irq] = { \
.reg_offset = (_off), \
.mask = (_mask), \
.type = { \
.type_reg_offset = (_off), \
.types_supported = (_types), \
}, \
}
static const struct regmap_irq pm8008_irqs[] = {
_IRQ(PM8008_IRQ_MISC_UVLO, PM8008_MISC, BIT(0), IRQ_TYPE_EDGE_RISING),
_IRQ(PM8008_IRQ_MISC_OVLO, PM8008_MISC, BIT(1), IRQ_TYPE_EDGE_RISING),
_IRQ(PM8008_IRQ_MISC_OTST2, PM8008_MISC, BIT(2), IRQ_TYPE_EDGE_RISING),
_IRQ(PM8008_IRQ_MISC_OTST3, PM8008_MISC, BIT(3), IRQ_TYPE_EDGE_RISING),
_IRQ(PM8008_IRQ_MISC_LDO_OCP, PM8008_MISC, BIT(4), IRQ_TYPE_EDGE_RISING),
_IRQ(PM8008_IRQ_TEMP_ALARM, PM8008_TEMP_ALARM,BIT(0), IRQ_TYPE_SENSE_MASK),
_IRQ(PM8008_IRQ_GPIO1, PM8008_GPIO1, BIT(0), IRQ_TYPE_SENSE_MASK),
_IRQ(PM8008_IRQ_GPIO2, PM8008_GPIO2, BIT(0), IRQ_TYPE_SENSE_MASK),
};
static const unsigned int pm8008_periph_base[] = {
......@@ -118,8 +139,8 @@ static int pm8008_set_type_config(unsigned int **buf, unsigned int type,
return 0;
}
static struct regmap_irq_chip pm8008_irq_chip = {
.name = "pm8008_irq",
static const struct regmap_irq_chip pm8008_irq_chip = {
.name = "pm8008",
.main_status = I2C_INTR_STATUS_BASE,
.num_main_regs = 1,
.irqs = pm8008_irqs,
......@@ -137,62 +158,106 @@ static struct regmap_irq_chip pm8008_irq_chip = {
.get_irq_reg = pm8008_get_irq_reg,
};
static struct regmap_config qcom_mfd_regmap_cfg = {
static const struct regmap_config qcom_mfd_regmap_cfg = {
.name = "primary",
.reg_bits = 16,
.val_bits = 8,
.max_register = 0xFFFF,
.max_register = 0xffff,
};
static int pm8008_probe_irq_peripherals(struct device *dev,
struct regmap *regmap,
int client_irq)
{
int rc, i;
struct regmap_irq_type *type;
struct regmap_irq_chip_data *irq_data;
for (i = 0; i < ARRAY_SIZE(pm8008_irqs); i++) {
type = &pm8008_irqs[i].type;
static const struct regmap_config pm8008_regmap_cfg_2 = {
.name = "secondary",
.reg_bits = 16,
.val_bits = 8,
.max_register = 0xffff,
};
type->type_reg_offset = pm8008_irqs[i].reg_offset;
static const struct resource pm8008_temp_res[] = {
DEFINE_RES_MEM(PM8008_TEMP_ALARM_ADDR, 0x100),
DEFINE_RES_IRQ(PM8008_IRQ_TEMP_ALARM),
};
if (type->type_reg_offset == PM8008_MISC)
type->types_supported = IRQ_TYPE_EDGE_RISING;
else
type->types_supported = (IRQ_TYPE_EDGE_BOTH |
IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW);
}
static const struct mfd_cell pm8008_cells[] = {
MFD_CELL_NAME("pm8008-regulator"),
MFD_CELL_RES("qpnp-temp-alarm", pm8008_temp_res),
MFD_CELL_NAME("pm8008-gpio"),
};
rc = devm_regmap_add_irq_chip(dev, regmap, client_irq,
IRQF_SHARED, 0, &pm8008_irq_chip, &irq_data);
if (rc) {
dev_err(dev, "Failed to add IRQ chip: %d\n", rc);
return rc;
}
static void devm_irq_domain_fwnode_release(void *data)
{
struct fwnode_handle *fwnode = data;
return 0;
irq_domain_free_fwnode(fwnode);
}
static int pm8008_probe(struct i2c_client *client)
{
int rc;
struct device *dev;
struct regmap *regmap;
struct regmap_irq_chip_data *irq_data;
struct device *dev = &client->dev;
struct regmap *regmap, *regmap2;
struct fwnode_handle *fwnode;
struct i2c_client *dummy;
struct gpio_desc *reset;
char *name;
int ret;
dummy = devm_i2c_new_dummy_device(dev, client->adapter, client->addr + 1);
if (IS_ERR(dummy)) {
ret = PTR_ERR(dummy);
dev_err(dev, "failed to claim second address: %d\n", ret);
return ret;
}
regmap2 = devm_regmap_init_i2c(dummy, &qcom_mfd_regmap_cfg);
if (IS_ERR(regmap2))
return PTR_ERR(regmap2);
ret = regmap_attach_dev(dev, regmap2, &pm8008_regmap_cfg_2);
if (ret)
return ret;
dev = &client->dev;
/* Default regmap must be attached last. */
regmap = devm_regmap_init_i2c(client, &qcom_mfd_regmap_cfg);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
i2c_set_clientdata(client, regmap);
reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(reset))
return PTR_ERR(reset);
if (of_property_read_bool(dev->of_node, "interrupt-controller")) {
rc = pm8008_probe_irq_peripherals(dev, regmap, client->irq);
if (rc)
dev_err(dev, "Failed to probe irq periphs: %d\n", rc);
/*
* The PMIC does not appear to require a post-reset delay, but wait
* for a millisecond for now anyway.
*/
usleep_range(1000, 2000);
name = devm_kasprintf(dev, GFP_KERNEL, "%pOF-internal", dev->of_node);
if (!name)
return -ENOMEM;
name = strreplace(name, '/', ':');
fwnode = irq_domain_alloc_named_fwnode(name);
if (!fwnode)
return -ENOMEM;
ret = devm_add_action_or_reset(dev, devm_irq_domain_fwnode_release, fwnode);
if (ret)
return ret;
ret = devm_regmap_add_irq_chip_fwnode(dev, fwnode, regmap, client->irq,
IRQF_SHARED, 0, &pm8008_irq_chip, &irq_data);
if (ret) {
dev_err(dev, "failed to add IRQ chip: %d\n", ret);
return ret;
}
return devm_of_platform_populate(dev);
/* Needed by GPIO driver. */
dev_set_drvdata(dev, regmap_irq_get_domain(irq_data));
return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, pm8008_cells,
ARRAY_SIZE(pm8008_cells), NULL, 0,
regmap_irq_get_domain(irq_data));
}
static const struct of_device_id pm8008_match[] = {
......
......@@ -1033,6 +1033,13 @@ config REGULATOR_PWM
This driver supports PWM controlled voltage regulators. PWM
duty cycle can increase or decrease the voltage.
config REGULATOR_QCOM_PM8008
tristate "Qualcomm PM8008 PMIC regulators"
depends on MFD_QCOM_PM8008
help
Select this option to enable support for the voltage regulators in
Qualcomm PM8008 PMICs.
config REGULATOR_QCOM_REFGEN
tristate "Qualcomm REFGEN regulator driver"
depends on ARCH_QCOM || COMPILE_TEST
......
......@@ -113,6 +113,7 @@ obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o
obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
obj-$(CONFIG_REGULATOR_MTK_DVFSRC) += mtk-dvfsrc-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_LABIBB) += qcom-labibb-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_PM8008) += qcom-pm8008-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_REFGEN) += qcom-refgen-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2024 Linaro Limited
*/
#include <linux/array_size.h>
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/math.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <asm/byteorder.h>
#define DEFAULT_VOLTAGE_STEPPER_RATE 38400
#define LDO_STEPPER_CTL_REG 0x3b
#define STEP_RATE_MASK GENMASK(1, 0)
#define LDO_VSET_LB_REG 0x40
#define LDO_ENABLE_REG 0x46
#define ENABLE_BIT BIT(7)
struct pm8008_regulator {
struct regmap *regmap;
struct regulator_desc desc;
unsigned int base;
};
struct pm8008_regulator_data {
const char *name;
const char *supply_name;
unsigned int base;
int min_dropout_uV;
const struct linear_range *voltage_range;
};
static const struct linear_range nldo_ranges[] = {
REGULATOR_LINEAR_RANGE(528000, 0, 122, 8000),
};
static const struct linear_range pldo_ranges[] = {
REGULATOR_LINEAR_RANGE(1504000, 0, 237, 8000),
};
static const struct pm8008_regulator_data pm8008_reg_data[] = {
{ "ldo1", "vdd-l1-l2", 0x4000, 225000, nldo_ranges, },
{ "ldo2", "vdd-l1-l2", 0x4100, 225000, nldo_ranges, },
{ "ldo3", "vdd-l3-l4", 0x4200, 300000, pldo_ranges, },
{ "ldo4", "vdd-l3-l4", 0x4300, 300000, pldo_ranges, },
{ "ldo5", "vdd-l5", 0x4400, 200000, pldo_ranges, },
{ "ldo6", "vdd-l6", 0x4500, 200000, pldo_ranges, },
{ "ldo7", "vdd-l7", 0x4600, 200000, pldo_ranges, },
};
static int pm8008_regulator_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel)
{
struct pm8008_regulator *preg = rdev_get_drvdata(rdev);
unsigned int mV;
__le16 val;
int ret;
ret = regulator_list_voltage_linear_range(rdev, sel);
if (ret < 0)
return ret;
mV = DIV_ROUND_UP(ret, 1000);
val = cpu_to_le16(mV);
ret = regmap_bulk_write(preg->regmap, preg->base + LDO_VSET_LB_REG,
&val, sizeof(val));
if (ret < 0)
return ret;
return 0;
}
static int pm8008_regulator_get_voltage_sel(struct regulator_dev *rdev)
{
struct pm8008_regulator *preg = rdev_get_drvdata(rdev);
unsigned int uV;
__le16 val;
int ret;
ret = regmap_bulk_read(preg->regmap, preg->base + LDO_VSET_LB_REG,
&val, sizeof(val));
if (ret < 0)
return ret;
uV = le16_to_cpu(val) * 1000;
return (uV - preg->desc.min_uV) / preg->desc.uV_step;
}
static const struct regulator_ops pm8008_regulator_ops = {
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = pm8008_regulator_set_voltage_sel,
.get_voltage_sel = pm8008_regulator_get_voltage_sel,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
};
static int pm8008_regulator_probe(struct platform_device *pdev)
{
const struct pm8008_regulator_data *data;
struct regulator_config config = {};
struct device *dev = &pdev->dev;
struct pm8008_regulator *preg;
struct regulator_desc *desc;
struct regulator_dev *rdev;
struct regmap *regmap;
unsigned int val;
int ret, i;
regmap = dev_get_regmap(dev->parent, "secondary");
if (!regmap)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(pm8008_reg_data); i++) {
data = &pm8008_reg_data[i];
preg = devm_kzalloc(dev, sizeof(*preg), GFP_KERNEL);
if (!preg)
return -ENOMEM;
preg->regmap = regmap;
preg->base = data->base;
desc = &preg->desc;
desc->name = data->name;
desc->supply_name = data->supply_name;
desc->of_match = data->name;
desc->regulators_node = of_match_ptr("regulators");
desc->ops = &pm8008_regulator_ops;
desc->type = REGULATOR_VOLTAGE;
desc->owner = THIS_MODULE;
desc->linear_ranges = data->voltage_range;
desc->n_linear_ranges = 1;
desc->uV_step = desc->linear_ranges[0].step;
desc->min_uV = desc->linear_ranges[0].min;
desc->n_voltages = linear_range_values_in_range(&desc->linear_ranges[0]);
ret = regmap_read(regmap, preg->base + LDO_STEPPER_CTL_REG, &val);
if (ret < 0) {
dev_err(dev, "failed to read step rate: %d\n", ret);
return ret;
}
val &= STEP_RATE_MASK;
desc->ramp_delay = DEFAULT_VOLTAGE_STEPPER_RATE >> val;
desc->min_dropout_uV = data->min_dropout_uV;
desc->enable_reg = preg->base + LDO_ENABLE_REG;
desc->enable_mask = ENABLE_BIT;
config.dev = dev->parent;
config.driver_data = preg;
config.regmap = regmap;
rdev = devm_regulator_register(dev, desc, &config);
if (IS_ERR(rdev)) {
ret = PTR_ERR(rdev);
dev_err(dev, "failed to register regulator %s: %d\n",
desc->name, ret);
return ret;
}
}
return 0;
}
static const struct platform_device_id pm8008_regulator_id_table[] = {
{ "pm8008-regulator" },
{ }
};
MODULE_DEVICE_TABLE(platform, pm8008_regulator_id_table);
static struct platform_driver pm8008_regulator_driver = {
.driver = {
.name = "qcom-pm8008-regulator",
},
.probe = pm8008_regulator_probe,
.id_table = pm8008_regulator_id_table,
};
module_platform_driver(pm8008_regulator_driver);
MODULE_DESCRIPTION("Qualcomm PM8008 PMIC regulator driver");
MODULE_LICENSE("GPL");
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021 The Linux Foundation. All rights reserved.
*/
#ifndef __DT_BINDINGS_MFD_QCOM_PM8008_H
#define __DT_BINDINGS_MFD_QCOM_PM8008_H
/* PM8008 IRQ numbers */
#define PM8008_IRQ_MISC_UVLO 0
#define PM8008_IRQ_MISC_OVLO 1
#define PM8008_IRQ_MISC_OTST2 2
#define PM8008_IRQ_MISC_OTST3 3
#define PM8008_IRQ_MISC_LDO_OCP 4
#define PM8008_IRQ_TEMP_ALARM 5
#define PM8008_IRQ_GPIO1 6
#define PM8008_IRQ_GPIO2 7
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment