Commit 981dd162 authored by Chris Morgan's avatar Chris Morgan Committed by Lee Jones
Browse files

regulator: bq257xx: Add bq257xx boost regulator driver



Add support for the boost regulator found in the Texas Instruments
BQ25703. The boost regulator is capable of outputting between 4.48
and 20.8 volts and between 0 and 6.35 amps.

Signed-off-by: default avatarChris Morgan <macromorgan@hotmail.com>
Reviewed-by: default avatarMark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20250904160530.66178-5-macroalpha82@gmail.com


Signed-off-by: default avatarLee Jones <lee@kernel.org>
parent 1cc017b7
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -297,6 +297,14 @@ config REGULATOR_BD96801
	  This driver can also be built as a module. If so, the module
	  will be called bd96801-regulator.

config REGULATOR_BQ257XX
	tristate "TI BQ257XX regulator family"
	depends on MFD_BQ257XX
	depends on GPIOLIB || COMPILE_TEST
	help
	  Say Y to enable support for the boost regulator function of
	  the BQ257XX family of charger circuits.

config REGULATOR_CPCAP
	tristate "Motorola CPCAP regulator"
	depends on MFD_CPCAP
+1 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ obj-$(CONFIG_REGULATOR_BD71828) += bd71828-regulator.o
obj-$(CONFIG_REGULATOR_BD718XX) += bd718x7-regulator.o
obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o
obj-$(CONFIG_REGULATOR_BD957XMUF) += bd9576-regulator.o
obj-$(CONFIG_REGULATOR_BQ257XX) += bq257xx-regulator.o
obj-$(CONFIG_REGULATOR_DA903X)	+= da903x-regulator.o
obj-$(CONFIG_REGULATOR_BD96801) += bd96801-regulator.o
obj-$(CONFIG_REGULATOR_DA9052)	+= da9052-regulator.o
+186 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * BQ257XX Battery Charger Driver
 * Copyright (C) 2025 Chris Morgan <macromorgan@hotmail.com>
 */

#include <linux/bitfield.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/mfd/bq257xx.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>

struct bq257xx_reg_data {
	struct bq257xx_device *bq;
	struct regulator_dev *bq257xx_reg;
	struct gpio_desc *otg_en_gpio;
	struct regulator_desc desc;
};

static int bq25703_vbus_get_cur_limit(struct regulator_dev *rdev)
{
	struct bq257xx_reg_data *pdata = rdev_get_drvdata(rdev);
	int ret;
	unsigned int reg;

	ret = regmap_read(pdata->bq->regmap, BQ25703_OTG_CURRENT, &reg);
	if (ret)
		return ret;
	return FIELD_GET(BQ25703_OTG_CUR_MASK, reg) * BQ25703_OTG_CUR_STEP_UA;
}

/*
 * Check if the minimum current and maximum current requested are
 * sane values, then set the register accordingly.
 */
static int bq25703_vbus_set_cur_limit(struct regulator_dev *rdev,
				      int min_uA, int max_uA)
{
	struct bq257xx_reg_data *pdata = rdev_get_drvdata(rdev);
	unsigned int reg;

	if ((min_uA > BQ25703_OTG_CUR_MAX_UA) || (max_uA < 0))
		return -EINVAL;

	reg = (max_uA / BQ25703_OTG_CUR_STEP_UA);

	/* Catch rounding errors since our step is 50000uA. */
	if ((reg * BQ25703_OTG_CUR_STEP_UA) < min_uA)
		return -EINVAL;

	return regmap_write(pdata->bq->regmap, BQ25703_OTG_CURRENT,
			    FIELD_PREP(BQ25703_OTG_CUR_MASK, reg));
}

static int bq25703_vbus_enable(struct regulator_dev *rdev)
{
	struct bq257xx_reg_data *pdata = rdev_get_drvdata(rdev);

	if (pdata->otg_en_gpio)
		gpiod_set_value_cansleep(pdata->otg_en_gpio, 1);
	return regulator_enable_regmap(rdev);
}

static int bq25703_vbus_disable(struct regulator_dev *rdev)
{
	struct bq257xx_reg_data *pdata = rdev_get_drvdata(rdev);

	if (pdata->otg_en_gpio)
		gpiod_set_value_cansleep(pdata->otg_en_gpio, 0);
	return regulator_disable_regmap(rdev);
}

static const struct regulator_ops bq25703_vbus_ops = {
	.enable = bq25703_vbus_enable,
	.disable = bq25703_vbus_disable,
	.is_enabled = regulator_is_enabled_regmap,
	.list_voltage = regulator_list_voltage_linear,
	.get_voltage_sel = regulator_get_voltage_sel_regmap,
	.set_voltage_sel = regulator_set_voltage_sel_regmap,
	.get_current_limit = bq25703_vbus_get_cur_limit,
	.set_current_limit = bq25703_vbus_set_cur_limit,
};

static const struct regulator_desc bq25703_vbus_desc = {
	.name = "vbus",
	.of_match = of_match_ptr("vbus"),
	.regulators_node = of_match_ptr("regulators"),
	.type = REGULATOR_VOLTAGE,
	.owner = THIS_MODULE,
	.ops = &bq25703_vbus_ops,
	.min_uV = BQ25703_OTG_VOLT_MIN_UV,
	.uV_step = BQ25703_OTG_VOLT_STEP_UV,
	.n_voltages = BQ25703_OTG_VOLT_NUM_VOLT,
	.enable_mask = BQ25703_EN_OTG_MASK,
	.enable_reg = BQ25703_CHARGE_OPTION_3,
	.enable_val = BQ25703_EN_OTG_MASK,
	.disable_val = 0,
	.vsel_reg = BQ25703_OTG_VOLT,
	.vsel_mask = BQ25703_OTG_VOLT_MASK,
};

/* Get optional GPIO for OTG regulator enable. */
static void bq257xx_reg_dt_parse_gpio(struct platform_device *pdev)
{
	struct device_node *child, *subchild;
	struct bq257xx_reg_data *pdata = platform_get_drvdata(pdev);

	child = of_get_child_by_name(pdev->dev.of_node,
				     pdata->desc.regulators_node);
	if (!child)
		return;

	subchild = of_get_child_by_name(child, pdata->desc.of_match);
	if (!subchild)
		return;

	of_node_put(child);

	pdata->otg_en_gpio = devm_fwnode_gpiod_get_index(&pdev->dev,
							 of_fwnode_handle(subchild),
							 "enable", 0,
							 GPIOD_OUT_LOW,
							 pdata->desc.of_match);

	of_node_put(subchild);

	if (IS_ERR(pdata->otg_en_gpio)) {
		dev_err(&pdev->dev, "Error getting enable gpio: %ld\n",
			PTR_ERR(pdata->otg_en_gpio));
		return;
	}
}

static int bq257xx_regulator_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct bq257xx_device *bq = dev_get_drvdata(pdev->dev.parent);
	struct bq257xx_reg_data *pdata;
	struct device_node *np = dev->of_node;
	struct regulator_config cfg = {};

	pdev->dev.of_node = pdev->dev.parent->of_node;
	pdev->dev.of_node_reused = true;

	pdata = devm_kzalloc(&pdev->dev, sizeof(struct bq257xx_reg_data), GFP_KERNEL);
	if (!pdata)
		return -ENOMEM;

	pdata->bq = bq;
	pdata->desc = bq25703_vbus_desc;

	platform_set_drvdata(pdev, pdata);
	bq257xx_reg_dt_parse_gpio(pdev);

	cfg.dev = &pdev->dev;
	cfg.driver_data = pdata;
	cfg.of_node = np;
	cfg.regmap = dev_get_regmap(pdev->dev.parent, NULL);
	if (!cfg.regmap)
		return -ENODEV;

	pdata->bq257xx_reg = devm_regulator_register(dev, &pdata->desc, &cfg);
	if (IS_ERR(pdata->bq257xx_reg)) {
		return dev_err_probe(&pdev->dev, PTR_ERR(pdata->bq257xx_reg),
				     "error registering bq257xx regulator");
	}

	return 0;
}

static struct platform_driver bq257xx_reg_driver = {
	.driver = {
		.name = "bq257xx-regulator",
	},
	.probe = bq257xx_regulator_probe,
};

module_platform_driver(bq257xx_reg_driver);

MODULE_DESCRIPTION("bq257xx regulator driver");
MODULE_AUTHOR("Chris Morgan <macromorgan@hotmail.com>");
MODULE_LICENSE("GPL");