From 209fab79cf6ed7c0e1e3946f7797b1025c4aab40 Mon Sep 17 00:00:00 2001 From: Alexis Czezar Torreno Date: Fri, 6 Dec 2024 10:44:30 +0800 Subject: [PATCH 1/3] dt-bindings: regulator: add adi,adp5055-regulator.yaml Add documentation for devicetree bindings for ADP5055. Signed-off-by: Alexis Czezar Torreno --- .../regulator/adi,adp5055-regulator.yaml | 203 ++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/adi,adp5055-regulator.yaml diff --git a/Documentation/devicetree/bindings/regulator/adi,adp5055-regulator.yaml b/Documentation/devicetree/bindings/regulator/adi,adp5055-regulator.yaml new file mode 100644 index 00000000000000..43bc7f8d26bbcc --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/adi,adp5055-regulator.yaml @@ -0,0 +1,203 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright (c) 2024 Analog Devices, Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/adi,adp5055-regulator.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADP5055 Triple Buck Regulator + +maintainers: + - Alexis Czezar Torreno + +description: | + The ADP5055 combines three high performance buck regulators. + The device enables direct connection to high input voltages + up to 18 V with no preregulators. + https://www.analog.com/media/en/technical-documentation/data-sheets/adp5055.pdf + +allOf: + - $ref: regulator.yaml# + +properties: + compatible: + enum: + - adi,adp5055 + + reg: + description: + I2C address of the device. LSB depends on value of + hardware pin CFG2. + enum: + - 0x70 + - 0x71 + + adi,enable-mode-code: + description: + 2-bit value to configure channel enabling + 0 - Physical EN pin is used for enabling. + 1 - Internal Register is used for enabling. + 2 - Both register and physical pin needed. + 3 - Either register or physical pin. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 3 + default: 0 + + adi,ocp-blanking: + description: + Enables or disables over current protection + blanking (OCP) for all channels. + 0 = Disabled + 1 = Enabled + type: boolean + default: 0 + + adi,power-saving-mode-ch321-code: + description: + 3-bit value to enable power saving mode for + individual channels. BIT[2:0] = [CH3, CH2, CH1]. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + default: 0 + + adi,output-discharge-function-ch321-code: + description: + 3-bit value to enable output discharge functionality + for individual channels. BIT[2:0] = [CH3, CH2, CH1]. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + default: 7 + + adi,disable-delay-code-ch123: + description: + An array of 3-bit values to configure the disable delay + for each channel. {CH1[2:0], CH2[2:0], CH3[2:0]. + 0 - No delay. + 1 - 2 * Tset + 2 - 4 * Tset + 3 - 6 * Tset + 4 - 8 * Tset + 5 - 10 * Tset + 6 - 12 * Tset + 7 - 14 * Tset + $ref: /schemas/types.yaml#/definitions/uint32-array + maxItems: 3 + items: + minimum: 0 + maximum: 7 + default: 0, 0, 0 + + adi,enable-delay-code-ch123: + description: + An array of 3-bit values to configure the enable delay + for each channel. {CH1[2:0], CH2[2:0], CH3[2:0]. + 0 - No delay. + 1 - 1 * Tset + 2 - 2 * Tset + 3 - 3 * Tset + 4 - 4 * Tset + 5 - 5 * Tset + 6 - 6 * Tset + 7 - 7 * Tset + $ref: /schemas/types.yaml#/definitions/uint32-array + maxItems: 3 + items: + minimum: 0 + maximum: 7 + default: 0, 0, 0 + + adi,dvs-limit-upper-code-ch123: + description: + An array of 4-bit values to configure the allowable upper + side limit of the voltage output of each channel. + {CH1[3:0], CH2[3:0], CH3[3:0]. + Vout_high = Vout + 192mV - (12mV * code) + $ref: /schemas/types.yaml#/definitions/uint32-array + maxItems: 3 + items: + minimum: 0 + maximum: 15 + default: 0, 0, 0 + + adi,dvs-limit-lower-code-ch123: + description: + An array of 4-bit values to configure the allowable lower + side limit of the voltage output of each channel. + {CH1[3:0], CH2[3:0], CH3[3:0]. + Vout_low = Vout - 190.5mV + (12mV * code) + $ref: /schemas/types.yaml#/definitions/uint32-array + maxItems: 3 + items: + minimum: 0 + maximum: 15 + default: 0, 0, 0 + + adi,fast-transient-code-ch123: + description: + An array of 2-bit values to configure the fast transient + sensitivity for each channel. {CH1[1:0], CH2[1:0], CH3[1:0]. + 0 - No fast transient. + 1 - 1.5% window with 3*350uA/V + 2 - 1.5% window with 5*350uA/V + 3 - 2.5% window with 5*350uA/V + $ref: /schemas/types.yaml#/definitions/uint32-array + maxItems: 3 + items: + minimum: 0 + maximum: 3 + default: 3, 3, 3 + + adi,delay-power-good: + description: + Configures delay timer of the power good (PWRGD) pin. + 0 - No delay. + 1 - Delay timer = Tset. + type: boolean + default: 1 + + adi,mask-power-good-ch321-code: + description: + 3-bit value to masks or unmasks individual channels to the + external PWRGD hardware pin. BIT[2:0] = [CH3, CH2, CH1]. + 0 - Outputs the PWRGD signal to the external PWRGD pin. + 1 - Masks the PWRGD signal to the external PWRGD pin. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + default: 0 + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + regulator@70 { + compatible = "adi,adp5055"; + reg = <0x70>; + + adi,enable-mode-code = <0>; + adi,ocp-blanking = <0>; + adi,power-saving-mode-ch321-code = <7>; + adi,output-discharge-function-ch321-code = <0>; + adi,disable-delay-code-ch123 = <0>, <0>, <0>; + adi,enable-delay-code-ch123 = <0>, <0>, <0>; + adi,dvs-limit-upper-code-ch123 = <0>, <0>, <0>; + adi,dvs-limit-lower-code-ch123 = <0>, <0>, <0>; + adi,fast-transient-code-ch123 = <3>, <3>, <3>; + adi,delay-power-good = <1>; + adi,mask-power-good-ch321-code = <0>; + + regulator-min-microvolt = <408000>; + regulator-max-microvolt = <790500>; + }; + }; From d77a73c2853726b4c757f99aeda7efb684c6fde1 Mon Sep 17 00:00:00 2001 From: Alexis Czezar Torreno Date: Fri, 6 Dec 2024 10:50:42 +0800 Subject: [PATCH 2/3] regulator: adp5055: Add driver for adp5055 Add ADI ADP5055 triple buck regulator driver support. Signed-off-by: Alexis Czezar Torreno --- drivers/regulator/Kconfig | 10 + drivers/regulator/Makefile | 1 + drivers/regulator/adp5055-regulator.c | 325 ++++++++++++++++++++++++++ 3 files changed, 336 insertions(+) create mode 100644 drivers/regulator/adp5055-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 41291800200422..b5a5edcbbe2694 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -106,6 +106,16 @@ config REGULATOR_AD5398 This driver supports AD5398 and AD5821 current regulator chips. If building into module, its name is ad5398.ko. +config REGULATOR_ADP5055 + tristate "Analog Devices ADP5055 Triple Buck Regulator" + depends on I2C + help + This driver controls an Analog Devices ADP5055 with triple buck + regulators using an I2C interface. + + Say M here if you want to include support for the regulator as a + module. + config REGULATOR_ANATOP tristate "Freescale i.MX on-chip ANATOP LDO regulators" depends on ARCH_MXC || COMPILE_TEST diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index b2b059b5ee565a..96f4b2c9a2443e 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_REGULATOR_AB8500) += ab8500-ext.o ab8500.o obj-$(CONFIG_REGULATOR_ACT8865) += act8865-regulator.o obj-$(CONFIG_REGULATOR_ACT8945A) += act8945a-regulator.o obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o +obj-$(CONFIG_REGULATOR_ADP5055) += adp5055-regulator.o obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o obj-$(CONFIG_REGULATOR_ARIZONA_LDO1) += arizona-ldo1.o obj-$(CONFIG_REGULATOR_ARIZONA_MICSUPP) += arizona-micsupp.o diff --git a/drivers/regulator/adp5055-regulator.c b/drivers/regulator/adp5055-regulator.c new file mode 100644 index 00000000000000..5b574b5776b82f --- /dev/null +++ b/drivers/regulator/adp5055-regulator.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Regulator driver for Analog Devices ADP5055 + * + * Copyright (C) 2024 Analog Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * ADP5055 Register Map. + */ +#define ADP5055_CTRL123 0xD1 +#define ADP5055_CTRL_MODE1 0xD3 +#define ADP5055_CTRL_MODE2 0xD4 +#define ADP5055_DLY0 0xD5 +#define ADP5055_DLY1 0xD6 +#define ADP5055_DLY2 0xD7 +#define ADP5055_VID0 0xD8 +#define ADP5055_VID1 0xD9 +#define ADP5055_VID2 0xDA +#define ADP5055_DVS_LIM0 0xDC +#define ADP5055_DVS_LIM1 0xDD +#define ADP5055_DVS_LIM2 0xDE +#define ADP5055_FT_CFG 0xDF +#define ADP5055_PG_CFG 0xE0 + +/* + * ADP5055 Field Masks. + */ +#define ADP5055_MASK_EN0 BIT(0) +#define ADP5055_MASK_EN1 BIT(1) +#define ADP5055_MASK_EN2 BIT(2) +#define ADP5055_MASK_EN_MODE GENMASK(1, 0) +#define ADP5055_MASK_OCP_BLANKING BIT(7) +#define ADP5055_MASK_PSM321 GENMASK(6, 4) +#define ADP5055_MASK_DIS GENMASK(2, 0) +#define ADP5055_MASK_DIS_DLY GENMASK(6, 4) +#define ADP5055_MASK_EN_DLY GENMASK(2, 0) +#define ADP5055_MASK_DVS_LIM_UPPER GENMASK(7, 4) +#define ADP5055_MASK_DVS_LIM_LOWER GENMASK(3, 0) +#define ADP5055_MASK_FAST_TRANSIENT3 GENMASK(5, 4) +#define ADP5055_MASK_FAST_TRANSIENT2 GENMASK(3, 2) +#define ADP5055_MASK_FAST_TRANSIENT1 GENMASK(1, 0) +#define ADP5055_MASK_DLY_PWRGD BIT(4) +#define ADP5055_MASK_PWRGD321 GENMASK(2, 0) + +#define ADP5055_MAX_VOUT 790500 +#define ADP5055_MIN_VOUT 408000 + +#define ADP5055_NUM_CH 3 + +struct adp5055 { + struct regmap *regmap; + u32 enable_mode_code; + bool ocp_blanking; + u32 power_saving_mode_ch321_code; + u32 output_discharge_function_ch321_code; + u32 disable_delay_code_ch123[ADP5055_NUM_CH]; + u32 enable_delay_code_ch123[ADP5055_NUM_CH]; + u32 dvs_limit_upper_code_ch123[ADP5055_NUM_CH]; + u32 dvs_limit_lower_code_ch123[ADP5055_NUM_CH]; + u32 fast_transient_code_ch123[ADP5055_NUM_CH]; + bool delay_power_good; + u32 mask_power_good_ch321_code; +}; + +static const struct regmap_range adp5055_reg_ranges[] = { + regmap_reg_range(0xD1, 0xE0), +}; + +static const struct regmap_access_table adp5055_write_ranges_table = { + .yes_ranges = adp5055_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(adp5055_reg_ranges), +}; + +static const struct regmap_access_table adp5055_read_ranges_table = { + .yes_ranges = adp5055_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(adp5055_reg_ranges), +}; + +static const struct regmap_config adp5055_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xFF, + .wr_table = &adp5055_write_ranges_table, + .rd_table = &adp5055_read_ranges_table, +}; + +static const struct linear_range adp5055_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(408000, 0, 255, 1500), +}; + +static int adp5055_parse_fw(struct device *dev, struct adp5055 *adp5055) +{ + int i, ret; + struct regmap *regmap = adp5055->regmap; + int val1, val2, val3; + + adp5055->enable_mode_code = 0; + adp5055->ocp_blanking = 0; + adp5055->power_saving_mode_ch321_code = 0; + adp5055->output_discharge_function_ch321_code = 7; + for (i = 0; i < ADP5055_NUM_CH; i++) { + adp5055->disable_delay_code_ch123[i] = 0; + adp5055->enable_delay_code_ch123[i] = 0; + adp5055->dvs_limit_upper_code_ch123[i] = 0; + adp5055->dvs_limit_lower_code_ch123[i] = 0; + adp5055->fast_transient_code_ch123[i] = 3; + } + adp5055->delay_power_good = 1; + adp5055->mask_power_good_ch321_code = 0; + + device_property_read_u32(dev, "adi,enable-mode-code", + &adp5055->enable_mode_code); + adp5055->ocp_blanking = device_property_read_bool(dev, "adi,ocp-blanking"); + device_property_read_u32(dev, "adi,power-saving-mode-ch321-code", + &adp5055->power_saving_mode_ch321_code); + device_property_read_u32(dev, "adi,output-discharge-function-ch321-code", + &adp5055->output_discharge_function_ch321_code); + device_property_read_u32_array(dev, "adi,disable-delay-code-ch123", + &adp5055->disable_delay_code_ch123[0], ADP5055_NUM_CH); + device_property_read_u32_array(dev, "adi,enable-delay-code-ch123", + &adp5055->enable_delay_code_ch123[0], ADP5055_NUM_CH); + device_property_read_u32_array(dev, "adi,dvs-limit-upper-code-ch123", + &adp5055->dvs_limit_upper_code_ch123[0], ADP5055_NUM_CH); + device_property_read_u32_array(dev, "adi,dvs-limit-lower-code-ch123", + &adp5055->dvs_limit_lower_code_ch123[0], ADP5055_NUM_CH); + device_property_read_u32_array(dev, "adi,fast-transient-code-ch123", + &adp5055->fast_transient_code_ch123[0], ADP5055_NUM_CH); + adp5055->delay_power_good = device_property_read_bool(dev, + "adi,delay-power-good"); + device_property_read_u32(dev, "adi,mask-power-good-ch321-code", + &adp5055->mask_power_good_ch321_code); + + val1 = FIELD_PREP(ADP5055_MASK_EN_MODE, adp5055->enable_mode_code); + ret = regmap_write(regmap, ADP5055_CTRL_MODE1, val1); + if (ret) + return ret; + + val1 = FIELD_PREP(ADP5055_MASK_OCP_BLANKING, adp5055->ocp_blanking); + val2 = FIELD_PREP(ADP5055_MASK_PSM321, adp5055->power_saving_mode_ch321_code); + val3 = FIELD_PREP(ADP5055_MASK_DIS, + adp5055->output_discharge_function_ch321_code); + ret = regmap_write(regmap, ADP5055_CTRL_MODE2, val1 | val2 | val3); + if (ret) + return ret; + + val1 = FIELD_PREP(ADP5055_MASK_DIS_DLY, adp5055->disable_delay_code_ch123[0]); + val2 = FIELD_PREP(ADP5055_MASK_EN_DLY, adp5055->enable_delay_code_ch123[0]); + ret = regmap_write(regmap, ADP5055_DLY0, val1 | val2); + if (ret) + return ret; + + val1 = FIELD_PREP(ADP5055_MASK_DIS_DLY, adp5055->disable_delay_code_ch123[1]); + val2 = FIELD_PREP(ADP5055_MASK_EN_DLY, adp5055->enable_delay_code_ch123[1]); + ret = regmap_write(regmap, ADP5055_DLY1, val1 | val2); + if (ret) + return ret; + + val1 = FIELD_PREP(ADP5055_MASK_DIS_DLY, adp5055->disable_delay_code_ch123[2]); + val2 = FIELD_PREP(ADP5055_MASK_EN_DLY, adp5055->enable_delay_code_ch123[2]); + ret = regmap_write(regmap, ADP5055_DLY2, val1 | val2); + if (ret) + return ret; + + val1 = FIELD_PREP(ADP5055_MASK_DVS_LIM_UPPER, + adp5055->dvs_limit_upper_code_ch123[0]); + val2 = FIELD_PREP(ADP5055_MASK_DVS_LIM_LOWER, + adp5055->dvs_limit_lower_code_ch123[0]); + ret = regmap_write(regmap, ADP5055_DVS_LIM0, val1 | val2); + if (ret) + return ret; + + val1 = FIELD_PREP(ADP5055_MASK_DVS_LIM_UPPER, + adp5055->dvs_limit_upper_code_ch123[1]); + val2 = FIELD_PREP(ADP5055_MASK_DVS_LIM_LOWER, + adp5055->dvs_limit_lower_code_ch123[1]); + ret = regmap_write(regmap, ADP5055_DVS_LIM1, val1 | val2); + if (ret) + return ret; + + val1 = FIELD_PREP(ADP5055_MASK_DVS_LIM_UPPER, + adp5055->dvs_limit_upper_code_ch123[2]); + val2 = FIELD_PREP(ADP5055_MASK_DVS_LIM_LOWER, + adp5055->dvs_limit_lower_code_ch123[2]); + ret = regmap_write(regmap, ADP5055_DVS_LIM2, val1 | val2); + if (ret) + return ret; + + val1 = FIELD_PREP(ADP5055_MASK_FAST_TRANSIENT1, + adp5055->fast_transient_code_ch123[0]); + val2 = FIELD_PREP(ADP5055_MASK_FAST_TRANSIENT2, + adp5055->fast_transient_code_ch123[1]); + val3 = FIELD_PREP(ADP5055_MASK_FAST_TRANSIENT3, + adp5055->fast_transient_code_ch123[2]); + ret = regmap_write(regmap, ADP5055_FT_CFG, val1 | val2 | val3); + if (ret) + return ret; + + val1 = FIELD_PREP(ADP5055_MASK_DLY_PWRGD, adp5055->delay_power_good); + val2 = FIELD_PREP(ADP5055_MASK_PWRGD321, adp5055->mask_power_good_ch321_code); + ret = regmap_write(regmap, ADP5055_PG_CFG, val1 | val2); + if (ret) + return ret; + + return 0; +} + +static const struct regulator_ops adp5055_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +#define ADP5055_REG_(_name, _id, _ops) \ + [_id] = { \ + .name = _name, \ + .ops = _ops, \ + .linear_ranges = adp5055_voltage_ranges, \ + .n_linear_ranges = ARRAY_SIZE(adp5055_voltage_ranges), \ + .vsel_reg = ADP5055_VID##_id, \ + .vsel_mask = GENMASK(7, 0), \ + .enable_reg = ADP5055_CTRL123, \ + .enable_mask = ADP5055_MASK_EN##_id, \ + .owner = THIS_MODULE, \ + } + +#define ADP5055_REG(_name, _id) \ + ADP5055_REG_(_name, _id, &adp5055_ops) + +static const struct regulator_desc adp5055_regulators[] = { + ADP5055_REG("DCDC1", 0), + ADP5055_REG("DCDC2", 1), + ADP5055_REG("DCDC3", 2), +}; + +static const struct of_device_id adp5055_dt_ids[] = { + { .compatible = "adi,adp5055"}, + { } +}; +MODULE_DEVICE_TABLE(of, adp5055_dt_ids); + +static int adp5055_probe(struct i2c_client *client) +{ + struct regulator_init_data *init_data; + struct device *dev = &client->dev; + struct adp5055 *adp5055; + const struct regmap_config *regmap_config; + int i, ret; + + regmap_config = &adp5055_regmap_config; + + init_data = of_get_regulator_init_data(dev, client->dev.of_node, + &adp5055_regulators[0]); + if (!init_data) + return -EINVAL; + + adp5055 = devm_kzalloc(dev, sizeof(struct adp5055), GFP_KERNEL); + if (!adp5055) + return -ENOMEM; + + adp5055->regmap = devm_regmap_init_i2c(client, regmap_config); + if (IS_ERR(adp5055->regmap)) { + ret = PTR_ERR(adp5055->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + ret = adp5055_parse_fw(dev, adp5055); + if (ret < 0) + return ret; + + for (i = 0; i < ADP5055_NUM_CH; i++) { + const struct regulator_desc *desc = &adp5055_regulators[i]; + struct regulator_config config = { }; + struct regulator_dev *rdev; + + config.dev = dev; + config.driver_data = adp5055; + config.regmap = adp5055->regmap; + config.init_data = init_data; + + rdev = devm_regulator_register(dev, desc, &config); + if (IS_ERR(rdev)) { + dev_err(dev, "Failed to register %s\n", desc->name); + return PTR_ERR(rdev); + } + } + + i2c_set_clientdata(client, adp5055); + + return 0; +} + +static const struct i2c_device_id adp5055_ids[] = { + { .name = "adp5055"}, + { }, +}; +MODULE_DEVICE_TABLE(i2c, adp5055_ids); + +static struct i2c_driver adp5055_driver = { + .driver = { + .name = "adp5055", + }, + .probe = adp5055_probe, + .id_table = adp5055_ids, +}; + +module_i2c_driver(adp5055_driver); + +MODULE_DESCRIPTION("ADP5055 Voltage Regulator Driver"); +MODULE_AUTHOR("Alexis Czezar Torreno "); +MODULE_LICENSE("GPL"); From 03486f902293a8200097bd3a5c36013436282bce Mon Sep 17 00:00:00 2001 From: Alexis Czezar Torreno Date: Fri, 6 Dec 2024 10:51:31 +0800 Subject: [PATCH 3/3] kconfig.adi: Add ADP5055 imply REGULATOR_ADP5055. Signed-off-by: Alexis Czezar Torreno --- Kconfig.adi | 1 + 1 file changed, 1 insertion(+) diff --git a/Kconfig.adi b/Kconfig.adi index 66eb89473a6cd2..2a3d4eecd7339d 100644 --- a/Kconfig.adi +++ b/Kconfig.adi @@ -95,6 +95,7 @@ config KERNEL_ALL_ADI_DRIVERS imply REGULATOR_MAX77857 imply REGULATOR_MAX77541 imply MFD_MAX77541 + imply REGULATOR_ADP5055 source "drivers/clk/Kconfig.adi" source "drivers/hwmon/Kconfig.adi"