[PATCH 1/2] regulator: lt8722: Add driver for LT8722

Ramon Cristopher M. Calam posted 2 patches 1 year, 1 month ago
[PATCH 1/2] regulator: lt8722: Add driver for LT8722
Posted by Ramon Cristopher M. Calam 1 year, 1 month ago
Add ADI LT8722 full bridge DC/DC converter driver.

Signed-off-by: Ramon Cristopher M. Calam <ramoncristopher.calam@analog.com>
---
 drivers/regulator/Kconfig            |  10 +
 drivers/regulator/Makefile           |   1 +
 drivers/regulator/lt8722-regulator.c | 701 +++++++++++++++++++++++++++
 3 files changed, 712 insertions(+)
 create mode 100644 drivers/regulator/lt8722-regulator.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 39297f7d8177..d88b050ce166 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -550,6 +550,16 @@ config REGULATOR_LP8788
 	help
 	  This driver supports LP8788 voltage regulator chip.
 
+config REGULATOR_LT8722
+	tristate "LT8722 ultracompact full bridge driver with SPI"
+	depends on SPI && OF
+	help
+	  This driver controls an Analog Devices LT8722 ultracompact 4A, 15V,
+	  full bridge driver with SPI interface.
+
+	  Say M here if you want to include support for the regulator as a
+	  module.
+
 config REGULATOR_LTC3589
 	tristate "LTC3589 8-output voltage regulator"
 	depends on I2C
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 3d5a803dce8a..8f9e5aec74d2 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_REGULATOR_LP87565) += lp87565-regulator.o
 obj-$(CONFIG_REGULATOR_LP8788) += lp8788-buck.o
 obj-$(CONFIG_REGULATOR_LP8788) += lp8788-ldo.o
 obj-$(CONFIG_REGULATOR_LP8755) += lp8755.o
+obj-$(CONFIG_REGULATOR_LT8722) += lt8722-regulator.o
 obj-$(CONFIG_REGULATOR_LTC3589) += ltc3589.o
 obj-$(CONFIG_REGULATOR_LTC3676) += ltc3676.o
 obj-$(CONFIG_REGULATOR_MAX14577) += max14577-regulator.o
diff --git a/drivers/regulator/lt8722-regulator.c b/drivers/regulator/lt8722-regulator.c
new file mode 100644
index 000000000000..f5d378c0b1f2
--- /dev/null
+++ b/drivers/regulator/lt8722-regulator.c
@@ -0,0 +1,701 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices LT8722 Ultracompact Full Bridge Driver with SPI driver
+ *
+ * Copyright 2024 Analog Devices Inc.
+ */
+
+#include <asm/unaligned.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/spi/spi.h>
+#include <linux/util_macros.h>
+
+/* Register map */
+#define LT8722_SPIS_COMMAND		0x00
+#define LT8722_SPIS_STATUS		0x01
+#define	LT8722_SPIS_DAC_ILIMN		0x02
+#define	LT8722_SPIS_DAC_ILIMP		0x03
+#define	LT8722_SPIS_DAC			0x04
+#define	LT8722_SPIS_OV_CLAMP		0x05
+#define	LT8722_SPIS_UV_CLAMP		0x06
+#define	LT8722_SPIS_AMUX		0x07
+
+/* Register masks */
+#define LT8722_SPIS_COMMAND_MASK	GENMASK(21, 0)
+#define LT8722_SPIS_STATUS_MASK		GENMASK(10, 0)
+#define LT8722_SPIS_DAC_ILIMN_MASK	GENMASK(8, 0)
+#define LT8722_SPIS_DAC_ILIMP_MASK	GENMASK(8, 0)
+#define LT8722_SPIS_DAC_MASK		GENMASK(31, 0)
+#define LT8722_SPIS_OV_CLAMP_MASK	GENMASK(3, 0)
+#define LT8722_SPIS_UV_CLAMP_MASK	GENMASK(3, 0)
+#define LT8722_SPIS_AMUX_MASK		GENMASK(6, 0)
+
+/* SPIS_COMMAND register bit masks */
+#define LT8722_EN_REQ_MASK		BIT(0)
+#define LT8722_SWEN_REQ_MASK		BIT(1)
+#define LT8722_SW_FRQ_SET_MASK		GENMASK(4, 2)
+#define LT8722_SW_FRQ_ADJ_MASK		GENMASK(6, 5)
+#define LT8722_SYS_DC_MASK		GENMASK(8, 7)
+#define LT8722_VCC_VREG_MASK		BIT(9)
+#define LT8722_SW_VC_IN_MASK		GENMASK(13, 11)
+#define LT8722_SPI_RST_MASK		BIT(14)
+#define LT8722_PWR_LIM_MASK		GENMASK(18, 15)
+
+#define LT8722_FAULTS_MASK		GENMASK(10, 5)
+#define LT8722_UV_OV_MASK		GENMASK(23, 20)
+#define LT8722_OC_MASK			BIT(5)
+#define LT8722_TSD_MASK			BIT(6)
+
+#define LT8722_CRC8_POLY		0x07
+#define LT8722_CRC_INIT			0x00
+
+#define LT8722_READ_CMD			0xF4
+#define LT8722_WRITE_CMD		0xF2
+#define LT8722_RW_CMD_SIZE		8
+#define LT8722_DATA_SIZE		4
+#define LT8722_DATA_POS			2
+#define LT8722_CRC_POS			6
+#define LT8722_ACK			0xA5
+#define LT8722_ACK_POS			7
+
+#define LT8722_DAC_VREF			2500000
+#define LT8722_DAC_BITS			25
+#define LT8722_ILIM_STEP		13280
+#define LT8722_RAMP_STEPS		5
+
+#define LT8722_MIN_DAC_CODE		0xFF000000
+#define LT8722_MAX_DAC_CODE		0x00FFFFFF
+#define LT8722_ILIMN_MIN_IOUT		-6786000
+#define LT8722_ILIMN_MAX_IOUT		-664640
+#define LT8722_ILIMP_MIN_IOUT		637440
+#define LT8722_ILIMP_MAX_IOUT		6800000
+#define LT8722_MIN_VOUT			-20000000
+#define LT8722_MAX_VOUT			20000000
+#define LT8722_MIN_IOUT			-6786000
+#define LT8722_MAX_IOUT			6800000
+
+DECLARE_CRC8_TABLE(lt8722_crc8_table);
+
+struct lt8722_chip_info {
+	struct spi_device *spi;
+	struct regulator_dev *rdev;
+	struct gpio_desc *en_gpio;
+	struct gpio_desc *swen_gpio;
+	int uv_clamp_uV;
+	int ov_clamp_uV;
+	int ilimn_uA;
+	int ilimp_uA;
+	int switch_freq_hz;
+	const char *switch_freq_adjust;
+	const char *duty_cycle_range;
+	int vcc_vreg_mV;
+	int peak_inductor_current_mA;
+	int power_limit_mW;
+};
+
+static const unsigned int lt8722_uv_clamp[] = {
+	-20000000,
+	-18750000,
+	-17500000,
+	-16250000,
+	-15000000,
+	-13750000,
+	-12500000,
+	-11250000,
+	-10000000,
+	-8750000,
+	-7500000,
+	-6250000,
+	-5000000,
+	-3750000,
+	-2500000,
+	-1250000,
+};
+
+static const unsigned int lt8722_ov_clamp[] = {
+	1250000,
+	2500000,
+	3750000,
+	5000000,
+	6250000,
+	7500000,
+	8750000,
+	10000000,
+	11250000,
+	12500000,
+	13750000,
+	15000000,
+	16250000,
+	17500000,
+	18750000,
+	20000000,
+};
+
+static const unsigned int lt8722_switch_freq[] = {
+	500000,
+	1000000,
+	1500000,
+	2000000,
+	2500000,
+	3000000,
+};
+
+static const char * const lt8722_switch_freq_adjust[] = {
+	"0%",
+	"15%",
+	"-15%",
+};
+
+static const char * const lt8722_duty_cycle_range[] = {
+	"20%-80%",
+	"15%-85%",
+	"10%-90%",
+};
+
+static const unsigned int lt8722_vcc_vreg[] = {
+	3100,
+	3400,
+};
+
+static const unsigned int lt8722_peak_inductor_current[] = {
+	252,
+	594,
+	936,
+	1278,
+	1620,
+	1962,
+	2304,
+	2646,
+};
+
+static const unsigned int lt8722_power_limit[] = {
+	2000,
+	0,
+	3000,
+	3500,
+};
+
+static s32 _lt8722_dac_to_uV(u32 dac_val)
+{
+	s64 tmp;
+
+	tmp = (s64)dac_val * LT8722_DAC_VREF;
+	tmp = 16 * div_s64(tmp, BIT(LT8722_DAC_BITS));
+
+	return tmp;
+}
+
+static s32 _lt8722_uV_to_dac(s32 uV)
+{
+	s64 tmp;
+
+	tmp = (s64)uV * BIT(LT8722_DAC_BITS);
+	tmp = div_s64(tmp, LT8722_DAC_VREF * 16);
+
+	return tmp;
+}
+
+static int lt8722_reg_read(struct spi_device *spi, u8 reg, u32 *val)
+{
+	int ret;
+	struct spi_transfer t;
+	u8 rx_buf[LT8722_RW_CMD_SIZE] = {0};
+	u8 tx_buf[LT8722_RW_CMD_SIZE] = {0};
+
+	tx_buf[0] = LT8722_READ_CMD;
+	tx_buf[1] = reg << 1;
+	tx_buf[2] = crc8(lt8722_crc8_table, tx_buf, 2, LT8722_CRC_INIT);
+
+	t.tx_buf = tx_buf;
+	t.rx_buf = rx_buf;
+	t.len = LT8722_RW_CMD_SIZE;
+
+	ret = spi_sync_transfer(spi, &t, 1);
+	if (ret < 0)
+		return ret;
+
+	if (rx_buf[LT8722_CRC_POS] != crc8(lt8722_crc8_table, rx_buf,
+				LT8722_CRC_POS, LT8722_CRC_INIT))
+		return -EIO;
+
+	if (rx_buf[LT8722_ACK_POS] != LT8722_ACK)
+		return -EIO;
+
+	*val = get_unaligned_be32(&rx_buf[LT8722_DATA_POS]);
+
+	return 0;
+}
+
+static int lt8722_reg_write(struct spi_device *spi, u8 reg, u32 val)
+{
+	int ret;
+	struct spi_transfer t;
+	u8 rx_buf[LT8722_RW_CMD_SIZE] = {0};
+	u8 tx_buf[LT8722_RW_CMD_SIZE] = {0};
+
+	tx_buf[0] = LT8722_WRITE_CMD;
+	tx_buf[1] = reg << 1;
+
+	put_unaligned_be32(val, &tx_buf[LT8722_DATA_POS]);
+
+	tx_buf[LT8722_CRC_POS] = crc8(lt8722_crc8_table, tx_buf, LT8722_CRC_POS,
+				      LT8722_CRC_INIT);
+
+	t.tx_buf = tx_buf;
+	t.rx_buf = rx_buf;
+	t.len = LT8722_RW_CMD_SIZE;
+
+	ret = spi_sync_transfer(spi, &t, 1);
+	if (ret < 0)
+		return ret;
+
+	if (rx_buf[LT8722_ACK_POS] != LT8722_ACK)
+		return -EIO;
+
+	return 0;
+}
+
+static int lt8722_reg_write_mask(struct spi_device *spi, u8 reg, u32 mask,
+				 u32 val)
+{
+	int ret;
+	u32 reg_val;
+
+	ret = lt8722_reg_read(spi, reg, &reg_val);
+	if (ret < 0)
+		return ret;
+
+	reg_val = (reg_val & ~mask) | (val & mask);
+
+	return lt8722_reg_write(spi, reg, reg_val);
+}
+
+static int lt8722_set_voltage(struct regulator_dev *rdev, int min_uV,
+			      int max_uV, unsigned int *selector)
+{
+	struct lt8722_chip_info *chip = rdev_get_drvdata(rdev);
+
+	if (min_uV < LT8722_MIN_VOUT || max_uV > LT8722_MAX_VOUT)
+		return -EINVAL;
+
+	*selector = _lt8722_uV_to_dac(min_uV);
+
+	return lt8722_reg_write(chip->spi, LT8722_SPIS_DAC, *selector);
+}
+
+static int lt8722_get_voltage(struct regulator_dev *rdev)
+{
+	struct lt8722_chip_info *chip = rdev_get_drvdata(rdev);
+	int ret, dac_val;
+
+	ret = lt8722_reg_read(chip->spi, LT8722_SPIS_DAC, &dac_val);
+	if (ret < 0)
+		return ret;
+
+	dac_val = FIELD_GET(LT8722_SPIS_DAC_MASK, dac_val);
+
+	return _lt8722_dac_to_uV(dac_val);
+}
+
+static int lt8722_get_prop_index(const u32 *table, size_t table_size, u32 value)
+{
+	int i;
+
+	for (i = 0; i < table_size; i++)
+		if (table[i] == value)
+			break;
+
+	if (i == table_size)
+		return -EINVAL;
+
+	return i;
+}
+
+static int lt8722_parse_fw(struct lt8722_chip_info *chip,
+			   struct regulator_init_data *init_data)
+{
+	int ret;
+
+	/* Override the min_uV constraint with the minimum output voltage */
+	init_data->constraints.min_uV = LT8722_MIN_VOUT;
+
+	ret = device_property_read_u32(&chip->spi->dev, "adi,uv-clamp-microvolt",
+				       &chip->uv_clamp_uV);
+	if (!ret) {
+		ret = lt8722_get_prop_index(lt8722_uv_clamp,
+				ARRAY_SIZE(lt8722_uv_clamp), chip->uv_clamp_uV);
+		if (ret < 0)
+			return ret;
+
+		ret = lt8722_reg_write(chip->spi, LT8722_SPIS_UV_CLAMP, ret);
+		if (ret < 0)
+			return ret;
+
+		/* Override the min_uV constraint with the UV clamp value */
+		init_data->constraints.min_uV = chip->uv_clamp_uV;
+	}
+
+	/* Override the max_uV constraint with the maximum output voltage */
+	init_data->constraints.max_uV = LT8722_MAX_VOUT;
+
+	ret = device_property_read_u32(&chip->spi->dev, "adi,ov-clamp-microvolt",
+				       &chip->ov_clamp_uV);
+	if (!ret) {
+		ret = lt8722_get_prop_index(lt8722_ov_clamp,
+				ARRAY_SIZE(lt8722_ov_clamp), chip->ov_clamp_uV);
+		if (ret < 0)
+			return ret;
+
+		ret = lt8722_reg_write(chip->spi, LT8722_SPIS_OV_CLAMP, ret);
+		if (ret < 0)
+			return ret;
+
+		/* Override the max_uV constraint with the OV clamp value */
+		init_data->constraints.max_uV = chip->ov_clamp_uV;
+	}
+
+	/* Override the min_uA constraint with the minimum output current */
+	init_data->constraints.min_uA = LT8722_MIN_IOUT;
+
+	ret = device_property_read_u32(&chip->spi->dev, "adi,ilimn-microamp",
+				       &chip->ilimn_uA);
+	if (!ret) {
+		if (chip->ilimn_uA < LT8722_ILIMN_MIN_IOUT ||
+		    chip->ilimn_uA > LT8722_ILIMN_MAX_IOUT)
+			return -EINVAL;
+
+		ret = div_s64(chip->ilimn_uA, -LT8722_ILIM_STEP);
+
+		ret = lt8722_reg_write(chip->spi, LT8722_SPIS_DAC_ILIMN, ret);
+		if (ret < 0)
+			return ret;
+
+		/* Override the min_uA constraint with the ILIMN value */
+		init_data->constraints.min_uA = chip->ilimn_uA;
+	}
+
+	/* Override the max_uA constraint with the maximum output current */
+	init_data->constraints.max_uA = LT8722_MAX_IOUT;
+
+	ret = device_property_read_u32(&chip->spi->dev, "adi,ilimp-microamp",
+				       &chip->ilimp_uA);
+	if (!ret) {
+		if (chip->ilimp_uA < LT8722_ILIMP_MIN_IOUT ||
+		    chip->ilimp_uA > LT8722_ILIMP_MAX_IOUT)
+			return -EINVAL;
+
+		ret = div_s64(LT8722_MAX_IOUT - chip->ilimp_uA, LT8722_ILIM_STEP);
+
+		ret = lt8722_reg_write(chip->spi, LT8722_SPIS_DAC_ILIMP, ret);
+		if (ret < 0)
+			return ret;
+
+		/* Override the max_uA constraint with the ILIMP value */
+		init_data->constraints.max_uA = chip->ilimp_uA;
+	}
+
+	ret = device_property_read_u32(&chip->spi->dev, "adi,switch-frequency-hz",
+				       &chip->switch_freq_hz);
+	if (!ret) {
+		ret = lt8722_get_prop_index(lt8722_switch_freq,
+					    ARRAY_SIZE(lt8722_switch_freq),
+					    chip->switch_freq_hz);
+		if (ret < 0)
+			return ret;
+
+		ret = lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
+				LT8722_SW_FRQ_SET_MASK,
+				FIELD_PREP(LT8722_SW_FRQ_SET_MASK, ret));
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = device_property_match_property_string(&chip->spi->dev,
+			"adi,switch-frequency-adjust",
+			lt8722_switch_freq_adjust,
+			ARRAY_SIZE(lt8722_switch_freq_adjust));
+	if (ret >= 0) {
+		ret = lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
+				LT8722_SW_FRQ_ADJ_MASK,
+				FIELD_PREP(LT8722_SW_FRQ_ADJ_MASK, ret));
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = device_property_match_property_string(&chip->spi->dev,
+			"adi,duty-cycle-range", lt8722_duty_cycle_range,
+			ARRAY_SIZE(lt8722_duty_cycle_range));
+	if (ret >= 0) {
+		ret = lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
+				LT8722_SYS_DC_MASK,
+				FIELD_PREP(LT8722_SYS_DC_MASK, ret));
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = device_property_read_u32(&chip->spi->dev, "adi,vcc-vreg-millivolt",
+				       &chip->vcc_vreg_mV);
+	if (!ret) {
+		ret = lt8722_get_prop_index(lt8722_vcc_vreg,
+				ARRAY_SIZE(lt8722_vcc_vreg), chip->vcc_vreg_mV);
+		if (ret < 0)
+			return ret;
+
+		ret = lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
+				LT8722_VCC_VREG_MASK,
+				FIELD_PREP(LT8722_VCC_VREG_MASK, ret));
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = device_property_read_u32(&chip->spi->dev,
+				       "adi,peak-inductor-current-milliamp",
+				       &chip->peak_inductor_current_mA);
+	if (!ret) {
+		ret = lt8722_get_prop_index(lt8722_peak_inductor_current,
+				ARRAY_SIZE(lt8722_peak_inductor_current),
+				chip->peak_inductor_current_mA);
+		if (ret < 0)
+			return ret;
+
+		ret = lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
+				LT8722_SW_VC_IN_MASK,
+				FIELD_PREP(LT8722_SW_VC_IN_MASK, ret));
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = device_property_read_u32(&chip->spi->dev, "adi,power-limit-milliwatt",
+				       &chip->power_limit_mW);
+	if (!ret) {
+		ret = lt8722_get_prop_index(lt8722_power_limit,
+					    ARRAY_SIZE(lt8722_power_limit),
+					    chip->power_limit_mW);
+		if (ret < 0)
+			return ret;
+
+		ret = lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
+				LT8722_PWR_LIM_MASK,
+				FIELD_PREP(LT8722_PWR_LIM_MASK, ret));
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int lt8722_enable(struct regulator_dev *rdev)
+{
+	struct lt8722_chip_info *chip = rdev_get_drvdata(rdev);
+
+	gpiod_set_value_cansleep(chip->en_gpio, 1);
+
+	return lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
+			LT8722_EN_REQ_MASK,
+			FIELD_PREP(LT8722_EN_REQ_MASK, 0x1));
+}
+
+static int lt8722_disable(struct regulator_dev *rdev)
+{
+	struct lt8722_chip_info *chip = rdev_get_drvdata(rdev);
+
+	gpiod_set_value_cansleep(chip->en_gpio, 0);
+
+	return lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
+			LT8722_EN_REQ_MASK,
+			FIELD_PREP(LT8722_EN_REQ_MASK, 0x0));
+}
+
+static int lt8722_is_enabled(struct regulator_dev *rdev)
+{
+	struct lt8722_chip_info *chip = rdev_get_drvdata(rdev);
+	int ret;
+	u32 reg_val;
+	bool en_req, en_pin;
+
+	ret = lt8722_reg_read(chip->spi, LT8722_SPIS_COMMAND, &reg_val);
+	if (ret < 0)
+		return ret;
+
+	en_req = FIELD_GET(LT8722_EN_REQ_MASK, reg_val);
+	en_pin = gpiod_get_value(chip->en_gpio);
+
+	return en_req && en_pin;
+}
+
+static int lt8722_get_error_flags(struct regulator_dev *rdev,
+				  unsigned int *flags)
+{
+	struct lt8722_chip_info *chip = rdev_get_drvdata(rdev);
+	int ret;
+	u32 reg_val;
+
+	ret = lt8722_reg_read(chip->spi, LT8722_SPIS_STATUS, &reg_val);
+	if (ret < 0)
+		return ret;
+
+	*flags = 0;
+
+	if (FIELD_GET(LT8722_OC_MASK, reg_val))
+		*flags |= REGULATOR_ERROR_OVER_CURRENT;
+
+	if (FIELD_GET(LT8722_TSD_MASK, reg_val))
+		*flags |= REGULATOR_ERROR_OVER_TEMP;
+
+	return 0;
+}
+
+static int lt8722_set_soft_start(struct regulator_dev *rdev)
+{
+	struct lt8722_chip_info *chip = rdev_get_drvdata(rdev);
+	s32 dac_val;
+	int ret, i;
+
+	/* Clear faults before enabled VCC LDO and other device circuitry */
+	ret = lt8722_reg_write(chip->spi, LT8722_SPIS_STATUS, 0x0);
+	if (ret < 0)
+		return ret;
+
+	ret = lt8722_enable(rdev);
+	if (ret < 0)
+		return ret;
+
+	/* Configure output voltage control DAC to 0xFF000000 */
+	ret = lt8722_reg_write(chip->spi, LT8722_SPIS_DAC, LT8722_MIN_DAC_CODE);
+	if (ret < 0)
+		return ret;
+
+	/* Write all SPIS_STATUS register bits to 0 */
+	ret = lt8722_reg_write(chip->spi, LT8722_SPIS_STATUS, 0x0);
+	if (ret < 0)
+		return ret;
+
+	fsleep(1000);
+
+	/* Ramp the output voltage control DAC from 0xFF000000 to 0x00000000 */
+	for (i = 0; i < LT8722_RAMP_STEPS; i++) {
+		dac_val = LT8722_MIN_DAC_CODE + 0x400000 * i;
+
+		ret = lt8722_reg_write(chip->spi, LT8722_SPIS_DAC, dac_val);
+		if (ret < 0)
+			return ret;
+
+		fsleep(1000);
+	}
+
+	/* Enable the PWM switching behavior */
+	gpiod_set_value_cansleep(chip->swen_gpio, 1);
+
+	ret = lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
+			LT8722_SWEN_REQ_MASK,
+			FIELD_PREP(LT8722_SWEN_REQ_MASK, 0x1));
+	if (ret < 0)
+		return ret;
+
+	fsleep(160);
+
+	return 0;
+}
+
+static const struct regulator_ops lt8722_regulator_ops = {
+	.set_voltage = lt8722_set_voltage,
+	.get_voltage = lt8722_get_voltage,
+	.enable = lt8722_enable,
+	.disable = lt8722_disable,
+	.is_enabled = lt8722_is_enabled,
+	.set_soft_start = lt8722_set_soft_start,
+	.get_error_flags = lt8722_get_error_flags,
+};
+
+static struct regulator_desc lt8722_regulator_desc = {
+	.name = "lt8722",
+	.ops = &lt8722_regulator_ops,
+	.type = REGULATOR_VOLTAGE,
+	.owner = THIS_MODULE,
+};
+
+static int lt8722_probe(struct spi_device *spi)
+{
+	struct regulator_init_data *init_data;
+	struct regulator_config config = { };
+	struct lt8722_chip_info *chip;
+	int ret;
+
+	init_data = of_get_regulator_init_data(&spi->dev, spi->dev.of_node,
+					       &lt8722_regulator_desc);
+	if (!init_data)
+		return -EINVAL;
+
+	chip = devm_kzalloc(&spi->dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	crc8_populate_msb(lt8722_crc8_table, LT8722_CRC8_POLY);
+
+	chip->spi = spi;
+
+	chip->en_gpio = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(chip->en_gpio))
+		return PTR_ERR(chip->en_gpio);
+
+	chip->swen_gpio = devm_gpiod_get(&spi->dev, "switch-enable",
+			GPIOD_OUT_LOW);
+	if (IS_ERR(chip->swen_gpio))
+		return PTR_ERR(chip->swen_gpio);
+
+	ret = lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
+			LT8722_SPI_RST_MASK,
+			FIELD_PREP(LT8722_SPI_RST_MASK, 0x1));
+	if (ret < 0)
+		return ret;
+
+	ret = lt8722_parse_fw(chip, init_data);
+	if (ret < 0)
+		return ret;
+
+	config.dev = &spi->dev;
+	config.init_data = init_data;
+	config.driver_data = chip;
+
+	chip->rdev = devm_regulator_register(&spi->dev, &lt8722_regulator_desc,
+					     &config);
+	if (IS_ERR(chip->rdev))
+		return PTR_ERR(chip->rdev);
+
+	return 0;
+}
+
+static const struct of_device_id lt8722_of_match[] = {
+	{ .compatible = "adi,lt8722", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, lt8722_of_match);
+
+static const struct spi_device_id lt8722_id[] = {
+	{ "lt8722" },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, lt8722_id);
+
+struct spi_driver lt8722_driver = {
+	.driver = {
+		.name = "lt8722",
+		.of_match_table = lt8722_of_match,
+	},
+	.probe = lt8722_probe,
+	.id_table = lt8722_id,
+};
+module_spi_driver(lt8722_driver);
+
+MODULE_AUTHOR("Ramon Cristopher Calam <ramoncristopher.calam@analog.com>");
+MODULE_DESCRIPTION("LT8722 ultracompact full bridge driver with SPI driver");
+MODULE_LICENSE("GPL");
-- 
2.47.0
Re: [PATCH 1/2] regulator: lt8722: Add driver for LT8722
Posted by anish kumar 1 year, 1 month ago
On Fri, Nov 8, 2024 at 1:40 AM Ramon Cristopher M. Calam
<ramoncristopher.calam@analog.com> wrote:
>
> Add ADI LT8722 full bridge DC/DC converter driver.
>
> Signed-off-by: Ramon Cristopher M. Calam <ramoncristopher.calam@analog.com>
> ---
>  drivers/regulator/Kconfig            |  10 +
>  drivers/regulator/Makefile           |   1 +
>  drivers/regulator/lt8722-regulator.c | 701 +++++++++++++++++++++++++++
>  3 files changed, 712 insertions(+)
>  create mode 100644 drivers/regulator/lt8722-regulator.c
>
> diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
> index 39297f7d8177..d88b050ce166 100644
> --- a/drivers/regulator/Kconfig
> +++ b/drivers/regulator/Kconfig
> @@ -550,6 +550,16 @@ config REGULATOR_LP8788
>         help
>           This driver supports LP8788 voltage regulator chip.
>
> +config REGULATOR_LT8722
> +       tristate "LT8722 ultracompact full bridge driver with SPI"
> +       depends on SPI && OF
> +       help
> +         This driver controls an Analog Devices LT8722 ultracompact 4A, 15V,
> +         full bridge driver with SPI interface.
> +
> +         Say M here if you want to include support for the regulator as a
> +         module.
> +
>  config REGULATOR_LTC3589
>         tristate "LTC3589 8-output voltage regulator"
>         depends on I2C
> diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
> index 3d5a803dce8a..8f9e5aec74d2 100644
> --- a/drivers/regulator/Makefile
> +++ b/drivers/regulator/Makefile
> @@ -67,6 +67,7 @@ obj-$(CONFIG_REGULATOR_LP87565) += lp87565-regulator.o
>  obj-$(CONFIG_REGULATOR_LP8788) += lp8788-buck.o
>  obj-$(CONFIG_REGULATOR_LP8788) += lp8788-ldo.o
>  obj-$(CONFIG_REGULATOR_LP8755) += lp8755.o
> +obj-$(CONFIG_REGULATOR_LT8722) += lt8722-regulator.o
>  obj-$(CONFIG_REGULATOR_LTC3589) += ltc3589.o
>  obj-$(CONFIG_REGULATOR_LTC3676) += ltc3676.o
>  obj-$(CONFIG_REGULATOR_MAX14577) += max14577-regulator.o
> diff --git a/drivers/regulator/lt8722-regulator.c b/drivers/regulator/lt8722-regulator.c
> new file mode 100644
> index 000000000000..f5d378c0b1f2
> --- /dev/null
> +++ b/drivers/regulator/lt8722-regulator.c
> @@ -0,0 +1,701 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Analog Devices LT8722 Ultracompact Full Bridge Driver with SPI driver
> + *
> + * Copyright 2024 Analog Devices Inc.
> + */
> +
> +#include <asm/unaligned.h>
> +#include <linux/bitfield.h>
> +#include <linux/bits.h>
> +#include <linux/crc8.h>
> +#include <linux/delay.h>
> +#include <linux/gpio.h>
> +#include <linux/math.h>
> +#include <linux/module.h>
> +#include <linux/regulator/driver.h>
> +#include <linux/regulator/machine.h>
> +#include <linux/regulator/of_regulator.h>
> +#include <linux/spi/spi.h>
> +#include <linux/util_macros.h>
> +
> +/* Register map */
> +#define LT8722_SPIS_COMMAND            0x00
> +#define LT8722_SPIS_STATUS             0x01
> +#define        LT8722_SPIS_DAC_ILIMN           0x02
> +#define        LT8722_SPIS_DAC_ILIMP           0x03
> +#define        LT8722_SPIS_DAC                 0x04
> +#define        LT8722_SPIS_OV_CLAMP            0x05
> +#define        LT8722_SPIS_UV_CLAMP            0x06
> +#define        LT8722_SPIS_AMUX                0x07
> +
> +/* Register masks */
> +#define LT8722_SPIS_COMMAND_MASK       GENMASK(21, 0)
> +#define LT8722_SPIS_STATUS_MASK                GENMASK(10, 0)
> +#define LT8722_SPIS_DAC_ILIMN_MASK     GENMASK(8, 0)
> +#define LT8722_SPIS_DAC_ILIMP_MASK     GENMASK(8, 0)
> +#define LT8722_SPIS_DAC_MASK           GENMASK(31, 0)
> +#define LT8722_SPIS_OV_CLAMP_MASK      GENMASK(3, 0)
> +#define LT8722_SPIS_UV_CLAMP_MASK      GENMASK(3, 0)
> +#define LT8722_SPIS_AMUX_MASK          GENMASK(6, 0)
> +
> +/* SPIS_COMMAND register bit masks */
> +#define LT8722_EN_REQ_MASK             BIT(0)
> +#define LT8722_SWEN_REQ_MASK           BIT(1)
> +#define LT8722_SW_FRQ_SET_MASK         GENMASK(4, 2)
> +#define LT8722_SW_FRQ_ADJ_MASK         GENMASK(6, 5)
> +#define LT8722_SYS_DC_MASK             GENMASK(8, 7)
> +#define LT8722_VCC_VREG_MASK           BIT(9)
> +#define LT8722_SW_VC_IN_MASK           GENMASK(13, 11)
> +#define LT8722_SPI_RST_MASK            BIT(14)
> +#define LT8722_PWR_LIM_MASK            GENMASK(18, 15)
> +
> +#define LT8722_FAULTS_MASK             GENMASK(10, 5)
> +#define LT8722_UV_OV_MASK              GENMASK(23, 20)
> +#define LT8722_OC_MASK                 BIT(5)
> +#define LT8722_TSD_MASK                        BIT(6)
> +
> +#define LT8722_CRC8_POLY               0x07
> +#define LT8722_CRC_INIT                        0x00
> +
> +#define LT8722_READ_CMD                        0xF4
> +#define LT8722_WRITE_CMD               0xF2
> +#define LT8722_RW_CMD_SIZE             8
> +#define LT8722_DATA_SIZE               4
> +#define LT8722_DATA_POS                        2
> +#define LT8722_CRC_POS                 6
> +#define LT8722_ACK                     0xA5
> +#define LT8722_ACK_POS                 7
> +
> +#define LT8722_DAC_VREF                        2500000
> +#define LT8722_DAC_BITS                        25
> +#define LT8722_ILIM_STEP               13280
> +#define LT8722_RAMP_STEPS              5
> +
> +#define LT8722_MIN_DAC_CODE            0xFF000000
> +#define LT8722_MAX_DAC_CODE            0x00FFFFFF
> +#define LT8722_ILIMN_MIN_IOUT          -6786000
> +#define LT8722_ILIMN_MAX_IOUT          -664640
> +#define LT8722_ILIMP_MIN_IOUT          637440
> +#define LT8722_ILIMP_MAX_IOUT          6800000
> +#define LT8722_MIN_VOUT                        -20000000
> +#define LT8722_MAX_VOUT                        20000000
> +#define LT8722_MIN_IOUT                        -6786000
> +#define LT8722_MAX_IOUT                        6800000
> +
> +DECLARE_CRC8_TABLE(lt8722_crc8_table);
> +
> +struct lt8722_chip_info {
> +       struct spi_device *spi;
> +       struct regulator_dev *rdev;
> +       struct gpio_desc *en_gpio;
> +       struct gpio_desc *swen_gpio;
> +       int uv_clamp_uV;
> +       int ov_clamp_uV;
> +       int ilimn_uA;
> +       int ilimp_uA;
> +       int switch_freq_hz;
> +       const char *switch_freq_adjust;
> +       const char *duty_cycle_range;
> +       int vcc_vreg_mV;
> +       int peak_inductor_current_mA;
> +       int power_limit_mW;
> +};
> +
> +static const unsigned int lt8722_uv_clamp[] = {
> +       -20000000,
> +       -18750000,
> +       -17500000,
> +       -16250000,
> +       -15000000,
> +       -13750000,
> +       -12500000,
> +       -11250000,
> +       -10000000,
> +       -8750000,
> +       -7500000,
> +       -6250000,
> +       -5000000,
> +       -3750000,
> +       -2500000,
> +       -1250000,
> +};
> +
> +static const unsigned int lt8722_ov_clamp[] = {
> +       1250000,
> +       2500000,
> +       3750000,
> +       5000000,
> +       6250000,
> +       7500000,
> +       8750000,
> +       10000000,
> +       11250000,
> +       12500000,
> +       13750000,
> +       15000000,
> +       16250000,
> +       17500000,
> +       18750000,
> +       20000000,
> +};
> +
> +static const unsigned int lt8722_switch_freq[] = {
> +       500000,
> +       1000000,
> +       1500000,
> +       2000000,
> +       2500000,
> +       3000000,
> +};
> +
> +static const char * const lt8722_switch_freq_adjust[] = {
> +       "0%",
> +       "15%",
> +       "-15%",
> +};
> +
> +static const char * const lt8722_duty_cycle_range[] = {
> +       "20%-80%",
> +       "15%-85%",
> +       "10%-90%",
> +};
> +
> +static const unsigned int lt8722_vcc_vreg[] = {
> +       3100,
> +       3400,
> +};
> +
> +static const unsigned int lt8722_peak_inductor_current[] = {
> +       252,
> +       594,
> +       936,
> +       1278,
> +       1620,
> +       1962,
> +       2304,
> +       2646,
> +};
> +
> +static const unsigned int lt8722_power_limit[] = {
> +       2000,
> +       0,
> +       3000,
> +       3500,
> +};
> +
> +static s32 _lt8722_dac_to_uV(u32 dac_val)
> +{
> +       s64 tmp;
> +
> +       tmp = (s64)dac_val * LT8722_DAC_VREF;
> +       tmp = 16 * div_s64(tmp, BIT(LT8722_DAC_BITS));
> +
> +       return tmp;
> +}
> +
> +static s32 _lt8722_uV_to_dac(s32 uV)
> +{
> +       s64 tmp;
> +
> +       tmp = (s64)uV * BIT(LT8722_DAC_BITS);
> +       tmp = div_s64(tmp, LT8722_DAC_VREF * 16);
> +
> +       return tmp;
> +}
> +
> +static int lt8722_reg_read(struct spi_device *spi, u8 reg, u32 *val)
> +{
> +       int ret;
> +       struct spi_transfer t;
> +       u8 rx_buf[LT8722_RW_CMD_SIZE] = {0};
> +       u8 tx_buf[LT8722_RW_CMD_SIZE] = {0};
> +
> +       tx_buf[0] = LT8722_READ_CMD;
> +       tx_buf[1] = reg << 1;
> +       tx_buf[2] = crc8(lt8722_crc8_table, tx_buf, 2, LT8722_CRC_INIT);
> +
> +       t.tx_buf = tx_buf;
> +       t.rx_buf = rx_buf;
> +       t.len = LT8722_RW_CMD_SIZE;
> +
> +       ret = spi_sync_transfer(spi, &t, 1);
> +       if (ret < 0)
> +               return ret;
> +
> +       if (rx_buf[LT8722_CRC_POS] != crc8(lt8722_crc8_table, rx_buf,
> +                               LT8722_CRC_POS, LT8722_CRC_INIT))
> +               return -EIO;
> +
> +       if (rx_buf[LT8722_ACK_POS] != LT8722_ACK)
> +               return -EIO;
> +
> +       *val = get_unaligned_be32(&rx_buf[LT8722_DATA_POS]);
> +
> +       return 0;
> +}
> +
> +static int lt8722_reg_write(struct spi_device *spi, u8 reg, u32 val)
> +{
> +       int ret;
> +       struct spi_transfer t;
> +       u8 rx_buf[LT8722_RW_CMD_SIZE] = {0};
> +       u8 tx_buf[LT8722_RW_CMD_SIZE] = {0};
> +
> +       tx_buf[0] = LT8722_WRITE_CMD;
> +       tx_buf[1] = reg << 1;
> +
> +       put_unaligned_be32(val, &tx_buf[LT8722_DATA_POS]);
> +
> +       tx_buf[LT8722_CRC_POS] = crc8(lt8722_crc8_table, tx_buf, LT8722_CRC_POS,
> +                                     LT8722_CRC_INIT);
> +
> +       t.tx_buf = tx_buf;
> +       t.rx_buf = rx_buf;
> +       t.len = LT8722_RW_CMD_SIZE;
> +
> +       ret = spi_sync_transfer(spi, &t, 1);
> +       if (ret < 0)
> +               return ret;
> +
> +       if (rx_buf[LT8722_ACK_POS] != LT8722_ACK)
> +               return -EIO;
> +
> +       return 0;
> +}
> +
> +static int lt8722_reg_write_mask(struct spi_device *spi, u8 reg, u32 mask,
> +                                u32 val)
> +{
> +       int ret;
> +       u32 reg_val;
> +
> +       ret = lt8722_reg_read(spi, reg, &reg_val);
> +       if (ret < 0)
> +               return ret;
> +
> +       reg_val = (reg_val & ~mask) | (val & mask);
> +
> +       return lt8722_reg_write(spi, reg, reg_val);
> +}
> +
> +static int lt8722_set_voltage(struct regulator_dev *rdev, int min_uV,
> +                             int max_uV, unsigned int *selector)
> +{
> +       struct lt8722_chip_info *chip = rdev_get_drvdata(rdev);
> +
> +       if (min_uV < LT8722_MIN_VOUT || max_uV > LT8722_MAX_VOUT)
> +               return -EINVAL;
> +
> +       *selector = _lt8722_uV_to_dac(min_uV);
> +
> +       return lt8722_reg_write(chip->spi, LT8722_SPIS_DAC, *selector);
> +}
> +
> +static int lt8722_get_voltage(struct regulator_dev *rdev)
> +{
> +       struct lt8722_chip_info *chip = rdev_get_drvdata(rdev);
> +       int ret, dac_val;
> +
> +       ret = lt8722_reg_read(chip->spi, LT8722_SPIS_DAC, &dac_val);
> +       if (ret < 0)
> +               return ret;
> +
> +       dac_val = FIELD_GET(LT8722_SPIS_DAC_MASK, dac_val);
> +
> +       return _lt8722_dac_to_uV(dac_val);
> +}
> +
> +static int lt8722_get_prop_index(const u32 *table, size_t table_size, u32 value)
> +{
> +       int i;
> +
> +       for (i = 0; i < table_size; i++)
> +               if (table[i] == value)
> +                       break;

Nit pick: instead of break you can return the index
instead of checking again below if it is equal to
table_size, saving one check. Once you do that
then you can get rid of below if and simply return
EINVAL.

> +
> +       if (i == table_size)
> +               return -EINVAL;
> +
> +       return i;
> +}
> +
> +static int lt8722_parse_fw(struct lt8722_chip_info *chip,
> +                          struct regulator_init_data *init_data)
> +{
> +       int ret;
> +
> +       /* Override the min_uV constraint with the minimum output voltage */
> +       init_data->constraints.min_uV = LT8722_MIN_VOUT;
> +
> +       ret = device_property_read_u32(&chip->spi->dev, "adi,uv-clamp-microvolt",
> +                                      &chip->uv_clamp_uV);
> +       if (!ret) {
> +               ret = lt8722_get_prop_index(lt8722_uv_clamp,
> +                               ARRAY_SIZE(lt8722_uv_clamp), chip->uv_clamp_uV);
> +               if (ret < 0)
> +                       return ret;
> +
> +               ret = lt8722_reg_write(chip->spi, LT8722_SPIS_UV_CLAMP, ret);
> +               if (ret < 0)
> +                       return ret;
> +
> +               /* Override the min_uV constraint with the UV clamp value */
> +               init_data->constraints.min_uV = chip->uv_clamp_uV;
> +       }
> +
> +       /* Override the max_uV constraint with the maximum output voltage */
> +       init_data->constraints.max_uV = LT8722_MAX_VOUT;
> +
> +       ret = device_property_read_u32(&chip->spi->dev, "adi,ov-clamp-microvolt",
> +                                      &chip->ov_clamp_uV);
> +       if (!ret) {
> +               ret = lt8722_get_prop_index(lt8722_ov_clamp,
> +                               ARRAY_SIZE(lt8722_ov_clamp), chip->ov_clamp_uV);
> +               if (ret < 0)
> +                       return ret;
> +
> +               ret = lt8722_reg_write(chip->spi, LT8722_SPIS_OV_CLAMP, ret);
> +               if (ret < 0)
> +                       return ret;
> +
> +               /* Override the max_uV constraint with the OV clamp value */
> +               init_data->constraints.max_uV = chip->ov_clamp_uV;
> +       }
> +
> +       /* Override the min_uA constraint with the minimum output current */
> +       init_data->constraints.min_uA = LT8722_MIN_IOUT;
> +
> +       ret = device_property_read_u32(&chip->spi->dev, "adi,ilimn-microamp",
> +                                      &chip->ilimn_uA);
> +       if (!ret) {
> +               if (chip->ilimn_uA < LT8722_ILIMN_MIN_IOUT ||
> +                   chip->ilimn_uA > LT8722_ILIMN_MAX_IOUT)
> +                       return -EINVAL;
> +
> +               ret = div_s64(chip->ilimn_uA, -LT8722_ILIM_STEP);
> +
> +               ret = lt8722_reg_write(chip->spi, LT8722_SPIS_DAC_ILIMN, ret);
> +               if (ret < 0)
> +                       return ret;
> +
> +               /* Override the min_uA constraint with the ILIMN value */
> +               init_data->constraints.min_uA = chip->ilimn_uA;
> +       }
> +
> +       /* Override the max_uA constraint with the maximum output current */
> +       init_data->constraints.max_uA = LT8722_MAX_IOUT;
> +
> +       ret = device_property_read_u32(&chip->spi->dev, "adi,ilimp-microamp",
> +                                      &chip->ilimp_uA);
> +       if (!ret) {
> +               if (chip->ilimp_uA < LT8722_ILIMP_MIN_IOUT ||
> +                   chip->ilimp_uA > LT8722_ILIMP_MAX_IOUT)
> +                       return -EINVAL;
> +
> +               ret = div_s64(LT8722_MAX_IOUT - chip->ilimp_uA, LT8722_ILIM_STEP);
> +
> +               ret = lt8722_reg_write(chip->spi, LT8722_SPIS_DAC_ILIMP, ret);
> +               if (ret < 0)
> +                       return ret;
> +
> +               /* Override the max_uA constraint with the ILIMP value */
> +               init_data->constraints.max_uA = chip->ilimp_uA;
> +       }
> +
> +       ret = device_property_read_u32(&chip->spi->dev, "adi,switch-frequency-hz",
> +                                      &chip->switch_freq_hz);
> +       if (!ret) {
> +               ret = lt8722_get_prop_index(lt8722_switch_freq,
> +                                           ARRAY_SIZE(lt8722_switch_freq),
> +                                           chip->switch_freq_hz);
> +               if (ret < 0)
> +                       return ret;
> +
> +               ret = lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
> +                               LT8722_SW_FRQ_SET_MASK,
> +                               FIELD_PREP(LT8722_SW_FRQ_SET_MASK, ret));
> +               if (ret < 0)
> +                       return ret;
> +       }
> +
> +       ret = device_property_match_property_string(&chip->spi->dev,
> +                       "adi,switch-frequency-adjust",
> +                       lt8722_switch_freq_adjust,
> +                       ARRAY_SIZE(lt8722_switch_freq_adjust));
> +       if (ret >= 0) {
> +               ret = lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
> +                               LT8722_SW_FRQ_ADJ_MASK,
> +                               FIELD_PREP(LT8722_SW_FRQ_ADJ_MASK, ret));
> +               if (ret < 0)
> +                       return ret;
> +       }
> +
> +       ret = device_property_match_property_string(&chip->spi->dev,
> +                       "adi,duty-cycle-range", lt8722_duty_cycle_range,
> +                       ARRAY_SIZE(lt8722_duty_cycle_range));
> +       if (ret >= 0) {
> +               ret = lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
> +                               LT8722_SYS_DC_MASK,
> +                               FIELD_PREP(LT8722_SYS_DC_MASK, ret));
> +               if (ret < 0)
> +                       return ret;
> +       }
> +
> +       ret = device_property_read_u32(&chip->spi->dev, "adi,vcc-vreg-millivolt",
> +                                      &chip->vcc_vreg_mV);
> +       if (!ret) {
> +               ret = lt8722_get_prop_index(lt8722_vcc_vreg,
> +                               ARRAY_SIZE(lt8722_vcc_vreg), chip->vcc_vreg_mV);
> +               if (ret < 0)
> +                       return ret;
> +
> +               ret = lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
> +                               LT8722_VCC_VREG_MASK,
> +                               FIELD_PREP(LT8722_VCC_VREG_MASK, ret));
> +               if (ret < 0)
> +                       return ret;
> +       }
> +
> +       ret = device_property_read_u32(&chip->spi->dev,
> +                                      "adi,peak-inductor-current-milliamp",
> +                                      &chip->peak_inductor_current_mA);
> +       if (!ret) {
> +               ret = lt8722_get_prop_index(lt8722_peak_inductor_current,
> +                               ARRAY_SIZE(lt8722_peak_inductor_current),
> +                               chip->peak_inductor_current_mA);
> +               if (ret < 0)
> +                       return ret;
> +
> +               ret = lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
> +                               LT8722_SW_VC_IN_MASK,
> +                               FIELD_PREP(LT8722_SW_VC_IN_MASK, ret));
> +               if (ret < 0)
> +                       return ret;
> +       }
> +
> +       ret = device_property_read_u32(&chip->spi->dev, "adi,power-limit-milliwatt",
> +                                      &chip->power_limit_mW);
> +       if (!ret) {
> +               ret = lt8722_get_prop_index(lt8722_power_limit,
> +                                           ARRAY_SIZE(lt8722_power_limit),
> +                                           chip->power_limit_mW);
> +               if (ret < 0)
> +                       return ret;
> +
> +               ret = lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
> +                               LT8722_PWR_LIM_MASK,
> +                               FIELD_PREP(LT8722_PWR_LIM_MASK, ret));
> +               if (ret < 0)
> +                       return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int lt8722_enable(struct regulator_dev *rdev)
> +{
> +       struct lt8722_chip_info *chip = rdev_get_drvdata(rdev);
> +
> +       gpiod_set_value_cansleep(chip->en_gpio, 1);
> +
> +       return lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
> +                       LT8722_EN_REQ_MASK,
> +                       FIELD_PREP(LT8722_EN_REQ_MASK, 0x1));
> +}
> +
> +static int lt8722_disable(struct regulator_dev *rdev)
> +{
> +       struct lt8722_chip_info *chip = rdev_get_drvdata(rdev);
> +
> +       gpiod_set_value_cansleep(chip->en_gpio, 0);
> +
> +       return lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
> +                       LT8722_EN_REQ_MASK,
> +                       FIELD_PREP(LT8722_EN_REQ_MASK, 0x0));
> +}
> +
> +static int lt8722_is_enabled(struct regulator_dev *rdev)
> +{
> +       struct lt8722_chip_info *chip = rdev_get_drvdata(rdev);
> +       int ret;
> +       u32 reg_val;
> +       bool en_req, en_pin;
> +
> +       ret = lt8722_reg_read(chip->spi, LT8722_SPIS_COMMAND, &reg_val);
> +       if (ret < 0)
> +               return ret;
> +
> +       en_req = FIELD_GET(LT8722_EN_REQ_MASK, reg_val);
> +       en_pin = gpiod_get_value(chip->en_gpio);
> +
> +       return en_req && en_pin;
> +}
> +
> +static int lt8722_get_error_flags(struct regulator_dev *rdev,
> +                                 unsigned int *flags)
> +{
> +       struct lt8722_chip_info *chip = rdev_get_drvdata(rdev);
> +       int ret;
> +       u32 reg_val;
> +
> +       ret = lt8722_reg_read(chip->spi, LT8722_SPIS_STATUS, &reg_val);
> +       if (ret < 0)
> +               return ret;
> +
> +       *flags = 0;
> +
> +       if (FIELD_GET(LT8722_OC_MASK, reg_val))
> +               *flags |= REGULATOR_ERROR_OVER_CURRENT;
> +
> +       if (FIELD_GET(LT8722_TSD_MASK, reg_val))
> +               *flags |= REGULATOR_ERROR_OVER_TEMP;
> +
> +       return 0;
> +}
> +
> +static int lt8722_set_soft_start(struct regulator_dev *rdev)
> +{
> +       struct lt8722_chip_info *chip = rdev_get_drvdata(rdev);
> +       s32 dac_val;
> +       int ret, i;
> +
> +       /* Clear faults before enabled VCC LDO and other device circuitry */
> +       ret = lt8722_reg_write(chip->spi, LT8722_SPIS_STATUS, 0x0);
> +       if (ret < 0)
> +               return ret;
> +
> +       ret = lt8722_enable(rdev);
> +       if (ret < 0)
> +               return ret;
> +
> +       /* Configure output voltage control DAC to 0xFF000000 */
> +       ret = lt8722_reg_write(chip->spi, LT8722_SPIS_DAC, LT8722_MIN_DAC_CODE);
> +       if (ret < 0)
> +               return ret;
> +
> +       /* Write all SPIS_STATUS register bits to 0 */
> +       ret = lt8722_reg_write(chip->spi, LT8722_SPIS_STATUS, 0x0);
> +       if (ret < 0)
> +               return ret;
> +
> +       fsleep(1000);
> +
> +       /* Ramp the output voltage control DAC from 0xFF000000 to 0x00000000 */
> +       for (i = 0; i < LT8722_RAMP_STEPS; i++) {
> +               dac_val = LT8722_MIN_DAC_CODE + 0x400000 * i;
> +
> +               ret = lt8722_reg_write(chip->spi, LT8722_SPIS_DAC, dac_val);
> +               if (ret < 0)
> +                       return ret;
> +
> +               fsleep(1000);
> +       }
> +
> +       /* Enable the PWM switching behavior */
> +       gpiod_set_value_cansleep(chip->swen_gpio, 1);
> +
> +       ret = lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
> +                       LT8722_SWEN_REQ_MASK,
> +                       FIELD_PREP(LT8722_SWEN_REQ_MASK, 0x1));
> +       if (ret < 0)
> +               return ret;
> +
> +       fsleep(160);
> +
> +       return 0;
> +}
> +
> +static const struct regulator_ops lt8722_regulator_ops = {
> +       .set_voltage = lt8722_set_voltage,
> +       .get_voltage = lt8722_get_voltage,
> +       .enable = lt8722_enable,
> +       .disable = lt8722_disable,
> +       .is_enabled = lt8722_is_enabled,
> +       .set_soft_start = lt8722_set_soft_start,
> +       .get_error_flags = lt8722_get_error_flags,
> +};
> +
> +static struct regulator_desc lt8722_regulator_desc = {
> +       .name = "lt8722",
> +       .ops = &lt8722_regulator_ops,
> +       .type = REGULATOR_VOLTAGE,
> +       .owner = THIS_MODULE,
> +};
> +
> +static int lt8722_probe(struct spi_device *spi)
> +{
> +       struct regulator_init_data *init_data;
> +       struct regulator_config config = { };
> +       struct lt8722_chip_info *chip;
> +       int ret;
> +
> +       init_data = of_get_regulator_init_data(&spi->dev, spi->dev.of_node,
> +                                              &lt8722_regulator_desc);
> +       if (!init_data)
> +               return -EINVAL;
> +
> +       chip = devm_kzalloc(&spi->dev, sizeof(*chip), GFP_KERNEL);
> +       if (!chip)
> +               return -ENOMEM;
> +
> +       crc8_populate_msb(lt8722_crc8_table, LT8722_CRC8_POLY);
> +
> +       chip->spi = spi;
> +
> +       chip->en_gpio = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW);
> +       if (IS_ERR(chip->en_gpio))
> +               return PTR_ERR(chip->en_gpio);
> +
> +       chip->swen_gpio = devm_gpiod_get(&spi->dev, "switch-enable",
> +                       GPIOD_OUT_LOW);
> +       if (IS_ERR(chip->swen_gpio))
> +               return PTR_ERR(chip->swen_gpio);
> +
> +       ret = lt8722_reg_write_mask(chip->spi, LT8722_SPIS_COMMAND,
> +                       LT8722_SPI_RST_MASK,
> +                       FIELD_PREP(LT8722_SPI_RST_MASK, 0x1));
> +       if (ret < 0)
> +               return ret;
> +
> +       ret = lt8722_parse_fw(chip, init_data);
> +       if (ret < 0)
> +               return ret;
> +
> +       config.dev = &spi->dev;
> +       config.init_data = init_data;
> +       config.driver_data = chip;
> +
> +       chip->rdev = devm_regulator_register(&spi->dev, &lt8722_regulator_desc,
> +                                            &config);
> +       if (IS_ERR(chip->rdev))
> +               return PTR_ERR(chip->rdev);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id lt8722_of_match[] = {
> +       { .compatible = "adi,lt8722", },
> +       { }
> +};
> +MODULE_DEVICE_TABLE(of, lt8722_of_match);
> +
> +static const struct spi_device_id lt8722_id[] = {
> +       { "lt8722" },
> +       { }
> +};
> +MODULE_DEVICE_TABLE(spi, lt8722_id);
> +
> +struct spi_driver lt8722_driver = {
> +       .driver = {
> +               .name = "lt8722",
> +               .of_match_table = lt8722_of_match,
> +       },
> +       .probe = lt8722_probe,
> +       .id_table = lt8722_id,
> +};
> +module_spi_driver(lt8722_driver);
> +
> +MODULE_AUTHOR("Ramon Cristopher Calam <ramoncristopher.calam@analog.com>");
> +MODULE_DESCRIPTION("LT8722 ultracompact full bridge driver with SPI driver");
> +MODULE_LICENSE("GPL");
> --
> 2.47.0
>
>
Re: [PATCH 1/2] regulator: lt8722: Add driver for LT8722
Posted by kernel test robot 1 year, 1 month ago
Hi Ramon,

kernel test robot noticed the following build errors:

[auto build test ERROR on e18da71634d12a94a15138947538ef2f0ac22746]

url:    https://github.com/intel-lab-lkp/linux/commits/Ramon-Cristopher-M-Calam/regulator-lt8722-Add-driver-for-LT8722/20241108-174141
base:   e18da71634d12a94a15138947538ef2f0ac22746
patch link:    https://lore.kernel.org/r/20241108093544.9492-2-ramoncristopher.calam%40analog.com
patch subject: [PATCH 1/2] regulator: lt8722: Add driver for LT8722
config: hexagon-allmodconfig (https://download.01.org/0day-ci/archive/20241109/202411090141.pm0JwBtT-lkp@intel.com/config)
compiler: clang version 20.0.0git (https://github.com/llvm/llvm-project 592c0fe55f6d9a811028b5f3507be91458ab2713)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241109/202411090141.pm0JwBtT-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202411090141.pm0JwBtT-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/regulator/lt8722-regulator.c:8:10: fatal error: 'asm/unaligned.h' file not found
       8 | #include <asm/unaligned.h>
         |          ^~~~~~~~~~~~~~~~~
   1 error generated.

Kconfig warnings: (for reference only)
   WARNING: unmet direct dependencies detected for GET_FREE_REGION
   Depends on [n]: SPARSEMEM [=n]
   Selected by [m]:
   - RESOURCE_KUNIT_TEST [=m] && RUNTIME_TESTING_MENU [=y] && KUNIT [=m]


vim +8 drivers/regulator/lt8722-regulator.c

   > 8	#include <asm/unaligned.h>
     9	#include <linux/bitfield.h>
    10	#include <linux/bits.h>
    11	#include <linux/crc8.h>
    12	#include <linux/delay.h>
    13	#include <linux/gpio.h>
    14	#include <linux/math.h>
    15	#include <linux/module.h>
    16	#include <linux/regulator/driver.h>
    17	#include <linux/regulator/machine.h>
    18	#include <linux/regulator/of_regulator.h>
    19	#include <linux/spi/spi.h>
    20	#include <linux/util_macros.h>
    21	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH 1/2] regulator: lt8722: Add driver for LT8722
Posted by kernel test robot 1 year, 1 month ago
Hi Ramon,

kernel test robot noticed the following build errors:

[auto build test ERROR on e18da71634d12a94a15138947538ef2f0ac22746]

url:    https://github.com/intel-lab-lkp/linux/commits/Ramon-Cristopher-M-Calam/regulator-lt8722-Add-driver-for-LT8722/20241108-174141
base:   e18da71634d12a94a15138947538ef2f0ac22746
patch link:    https://lore.kernel.org/r/20241108093544.9492-2-ramoncristopher.calam%40analog.com
patch subject: [PATCH 1/2] regulator: lt8722: Add driver for LT8722
config: m68k-allmodconfig (https://download.01.org/0day-ci/archive/20241108/202411082316.hXgR3Bv2-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241108/202411082316.hXgR3Bv2-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202411082316.hXgR3Bv2-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/regulator/lt8722-regulator.c:8:10: fatal error: asm/unaligned.h: No such file or directory
       8 | #include <asm/unaligned.h>
         |          ^~~~~~~~~~~~~~~~~
   compilation terminated.

Kconfig warnings: (for reference only)
   WARNING: unmet direct dependencies detected for GET_FREE_REGION
   Depends on [n]: SPARSEMEM [=n]
   Selected by [m]:
   - RESOURCE_KUNIT_TEST [=m] && RUNTIME_TESTING_MENU [=y] && KUNIT [=m]


vim +8 drivers/regulator/lt8722-regulator.c

   > 8	#include <asm/unaligned.h>
     9	#include <linux/bitfield.h>
    10	#include <linux/bits.h>
    11	#include <linux/crc8.h>
    12	#include <linux/delay.h>
    13	#include <linux/gpio.h>
    14	#include <linux/math.h>
    15	#include <linux/module.h>
    16	#include <linux/regulator/driver.h>
    17	#include <linux/regulator/machine.h>
    18	#include <linux/regulator/of_regulator.h>
    19	#include <linux/spi/spi.h>
    20	#include <linux/util_macros.h>
    21	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH 1/2] regulator: lt8722: Add driver for LT8722
Posted by Mark Brown 1 year, 1 month ago
On Fri, Nov 08, 2024 at 05:35:43PM +0800, Ramon Cristopher M. Calam wrote:
> Add ADI LT8722 full bridge DC/DC converter driver.

> +++ b/drivers/regulator/lt8722-regulator.c
> @@ -0,0 +1,701 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Analog Devices LT8722 Ultracompact Full Bridge Driver with SPI driver
> + *

Please make the entire comment a C++ one so things look more intentional.

> +static int lt8722_reg_read(struct spi_device *spi, u8 reg, u32 *val)
> +{

> +static int lt8722_reg_write(struct spi_device *spi, u8 reg, u32 val)
> +{

You can use these as reg_read() and reg_write() operations in regmap
which will allow the driver to use all the standard helpers and vastly
reduce the size of the driver.

> +static int lt8722_parse_fw(struct lt8722_chip_info *chip,
> +			   struct regulator_init_data *init_data)
> +{
> +	int ret;
> +
> +	/* Override the min_uV constraint with the minimum output voltage */
> +	init_data->constraints.min_uV = LT8722_MIN_VOUT;

Any modification of the constraints by the driver is a bug.  Adjust the
information the driver provides about the voltages it supports if you
need to do this.

> +static int lt8722_is_enabled(struct regulator_dev *rdev)
> +{
> +	struct lt8722_chip_info *chip = rdev_get_drvdata(rdev);
> +	int ret;
> +	u32 reg_val;
> +	bool en_req, en_pin;
> +
> +	ret = lt8722_reg_read(chip->spi, LT8722_SPIS_COMMAND, &reg_val);
> +	if (ret < 0)
> +		return ret;
> +
> +	en_req = FIELD_GET(LT8722_EN_REQ_MASK, reg_val);
> +	en_pin = gpiod_get_value(chip->en_gpio);
> +
> +	return en_req && en_pin;
> +}

Always adjusting both the GPIO and register all the time like this is
pointless, it turns the GPIO into just pure overhead.  Just use the
standard support for setting enables via registrers and GPIOs.  When
there's a GPIO leave the register permanently enabld.

> +	chip->en_gpio = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW);
> +	if (IS_ERR(chip->en_gpio))
> +		return PTR_ERR(chip->en_gpio);

Presumably this is optional, it could just be tied off.