Unverified Commit cf27b7d9 authored by Mark Brown's avatar Mark Brown
Browse files

regulator: Add support for TPS6287x

Merge series from Mårten Lindahl <marten.lindahl@axis.com>:

This series adds basic support for TI's TPS62870/TPS62871/TPS62872/
TPS62873 high-frequency single-channel step-down converters with an
I2C interface.

The devices can operate in power save mode for maximum efficiency, or
forced-PWM mode for best transient performance and lowest output
voltage ripple. All chip variants have four output voltage ranges and
the driver changes active range depending on the requested voltage
setting.

There are differences in the electrical characteristics and packaging
between the variants, but the register interfaces are identical.
parents f050e56d 7b0518fb
Loading
Loading
Loading
Loading
+52 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/regulator/ti,tps62870.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: TI TPS62870/TPS62871/TPS62872/TPS62873 voltage regulator

maintainers:
  - Mårten Lindahl <marten.lindahl@axis.com>

allOf:
  - $ref: regulator.yaml#

properties:
  compatible:
    enum:
      - ti,tps62870
      - ti,tps62871
      - ti,tps62872
      - ti,tps62873

  reg:
    maxItems: 1

  regulator-initial-mode:
    enum: [ 1, 2 ]
    description: 1 - Forced PWM mode, 2 - Low power mode

required:
  - compatible
  - reg

unevaluatedProperties: false

examples:
  - |
    i2c {
      #address-cells = <1>;
      #size-cells = <0>;

      regulator@41 {
        compatible = "ti,tps62873";
        reg = <0x41>;
        regulator-name = "+0.75V";
        regulator-min-microvolt = <400000>;
        regulator-max-microvolt = <1675000>;
        regulator-initial-mode = <1>;
      };
    };

...
+11 −0
Original line number Diff line number Diff line
@@ -1397,6 +1397,17 @@ config REGULATOR_TPS6286X
	  high-frequency synchronous step-down converters with an I2C
	  interface.

config REGULATOR_TPS6287X
	tristate "TI TPS6287x Power Regulator"
	depends on I2C && OF
	select REGMAP_I2C
	help
	  This driver supports TPS6287x voltage regulator chips. These are
	  pin-to-pin high-frequency synchronous step-down dc-dc converters
	  with an I2C interface.

	  If built as a module it will be called tps6287x-regulator.

config REGULATOR_TPS65023
	tristate "TI TPS65023 Power regulators"
	depends on I2C
+1 −0
Original line number Diff line number Diff line
@@ -163,6 +163,7 @@ obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o
obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o
obj-$(CONFIG_REGULATOR_TPS62360) += tps62360-regulator.o
obj-$(CONFIG_REGULATOR_TPS6286X) += tps6286x-regulator.o
obj-$(CONFIG_REGULATOR_TPS6287X) += tps6287x-regulator.o
obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o
obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o
obj-$(CONFIG_REGULATOR_TPS65086) += tps65086-regulator.o
+188 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2023 Axis Communications AB
 *
 * Driver for Texas Instruments TPS6287x PMIC.
 * Datasheet: https://www.ti.com/lit/ds/symlink/tps62873.pdf
 */

#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/driver.h>
#include <linux/bitfield.h>
#include <linux/linear_range.h>

#define TPS6287X_VSET		0x00
#define TPS6287X_CTRL1		0x01
#define TPS6287X_CTRL1_VRAMP	GENMASK(1, 0)
#define TPS6287X_CTRL1_FPWMEN	BIT(4)
#define TPS6287X_CTRL1_SWEN	BIT(5)
#define TPS6287X_CTRL2		0x02
#define TPS6287X_CTRL2_VRANGE	GENMASK(3, 2)
#define TPS6287X_CTRL3		0x03
#define TPS6287X_STATUS		0x04

static const struct regmap_config tps6287x_regmap_config = {
	.reg_bits = 8,
	.val_bits = 8,
	.max_register = TPS6287X_STATUS,
};

static const struct linear_range tps6287x_voltage_ranges[] = {
	LINEAR_RANGE(400000, 0, 0xFF, 1250),
	LINEAR_RANGE(400000, 0, 0xFF, 2500),
	LINEAR_RANGE(400000, 0, 0xFF, 5000),
	LINEAR_RANGE(800000, 0, 0xFF, 10000),
};

static const unsigned int tps6287x_voltage_range_sel[] = {
	0x0, 0x4, 0x8, 0xC
};

static const unsigned int tps6287x_ramp_table[] = {
	10000, 5000, 1250, 500
};

static int tps6287x_set_mode(struct regulator_dev *rdev, unsigned int mode)
{
	unsigned int val;

	switch (mode) {
	case REGULATOR_MODE_NORMAL:
		val = 0;
		break;
	case REGULATOR_MODE_FAST:
		val = TPS6287X_CTRL1_FPWMEN;
		break;
	default:
		return -EINVAL;
	}

	return regmap_update_bits(rdev->regmap, TPS6287X_CTRL1,
				  TPS6287X_CTRL1_FPWMEN, val);
}

static unsigned int tps6287x_get_mode(struct regulator_dev *rdev)
{
	unsigned int val;
	int ret;

	ret = regmap_read(rdev->regmap, TPS6287X_CTRL1, &val);
	if (ret < 0)
		return 0;

	return (val & TPS6287X_CTRL1_FPWMEN) ? REGULATOR_MODE_FAST :
	    REGULATOR_MODE_NORMAL;
}

static unsigned int tps6287x_of_map_mode(unsigned int mode)
{
	switch (mode) {
	case REGULATOR_MODE_NORMAL:
	case REGULATOR_MODE_FAST:
		return mode;
	default:
		return REGULATOR_MODE_INVALID;
	}
}

static const struct regulator_ops tps6287x_regulator_ops = {
	.enable = regulator_enable_regmap,
	.disable = regulator_disable_regmap,
	.set_mode = tps6287x_set_mode,
	.get_mode = tps6287x_get_mode,
	.is_enabled = regulator_is_enabled_regmap,
	.get_voltage_sel = regulator_get_voltage_sel_pickable_regmap,
	.set_voltage_sel = regulator_set_voltage_sel_pickable_regmap,
	.list_voltage = regulator_list_voltage_pickable_linear_range,
	.set_ramp_delay = regulator_set_ramp_delay_regmap,
};

static struct regulator_desc tps6287x_reg = {
	.name = "tps6287x",
	.owner = THIS_MODULE,
	.ops = &tps6287x_regulator_ops,
	.of_map_mode = tps6287x_of_map_mode,
	.type = REGULATOR_VOLTAGE,
	.enable_reg = TPS6287X_CTRL1,
	.enable_mask = TPS6287X_CTRL1_SWEN,
	.vsel_reg = TPS6287X_VSET,
	.vsel_mask = 0xFF,
	.vsel_range_reg = TPS6287X_CTRL2,
	.vsel_range_mask = TPS6287X_CTRL2_VRANGE,
	.ramp_reg = TPS6287X_CTRL1,
	.ramp_mask = TPS6287X_CTRL1_VRAMP,
	.ramp_delay_table = tps6287x_ramp_table,
	.n_ramp_values = ARRAY_SIZE(tps6287x_ramp_table),
	.linear_ranges = tps6287x_voltage_ranges,
	.n_linear_ranges = ARRAY_SIZE(tps6287x_voltage_ranges),
	.linear_range_selectors = tps6287x_voltage_range_sel,
};

static int tps6287x_i2c_probe(struct i2c_client *i2c)
{
	struct device *dev = &i2c->dev;
	struct regulator_config config = {};
	struct regulator_dev *rdev;

	config.regmap = devm_regmap_init_i2c(i2c, &tps6287x_regmap_config);
	if (IS_ERR(config.regmap)) {
		dev_err(dev, "Failed to init i2c\n");
		return PTR_ERR(config.regmap);
	}

	config.dev = dev;
	config.of_node = dev->of_node;
	config.init_data = of_get_regulator_init_data(dev, dev->of_node,
						      &tps6287x_reg);

	rdev = devm_regulator_register(dev, &tps6287x_reg, &config);
	if (IS_ERR(rdev)) {
		dev_err(dev, "Failed to register regulator\n");
		return PTR_ERR(rdev);
	}

	dev_dbg(dev, "Probed regulator\n");

	return 0;
}

static const struct of_device_id tps6287x_dt_ids[] = {
	{ .compatible = "ti,tps62870", },
	{ .compatible = "ti,tps62871", },
	{ .compatible = "ti,tps62872", },
	{ .compatible = "ti,tps62873", },
	{ }
};

MODULE_DEVICE_TABLE(of, tps6287x_dt_ids);

static const struct i2c_device_id tps6287x_i2c_id[] = {
	{ "tps62870", 0 },
	{ "tps62871", 0 },
	{ "tps62872", 0 },
	{ "tps62873", 0 },
	{},
};

MODULE_DEVICE_TABLE(i2c, tps6287x_i2c_id);

static struct i2c_driver tps6287x_regulator_driver = {
	.driver = {
		.name = "tps6287x",
		.of_match_table = tps6287x_dt_ids,
	},
	.probe_new = tps6287x_i2c_probe,
	.id_table = tps6287x_i2c_id,
};

module_i2c_driver(tps6287x_regulator_driver);

MODULE_AUTHOR("Mårten Lindahl <marten.lindahl@axis.com>");
MODULE_DESCRIPTION("Regulator driver for TI TPS6287X PMIC");
MODULE_LICENSE("GPL");