diff options
-rw-r--r-- | Documentation/devicetree/bindings/regulator/adi,adp5055-regulator.yaml | 157 | ||||
-rw-r--r-- | MAINTAINERS | 7 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-of.c | 9 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.c | 14 | ||||
-rw-r--r-- | drivers/regulator/Kconfig | 11 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 1 | ||||
-rw-r--r-- | drivers/regulator/adp5055-regulator.c | 424 | ||||
-rw-r--r-- | drivers/regulator/core.c | 2 | ||||
-rw-r--r-- | drivers/regulator/pf9453-regulator.c | 1 | ||||
-rw-r--r-- | drivers/regulator/rpi-panel-attiny-regulator.c | 83 | ||||
-rw-r--r-- | drivers/regulator/s5m8767.c | 146 | ||||
-rw-r--r-- | include/linux/gpio/consumer.h | 9 |
12 files changed, 701 insertions, 163 deletions
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 000000000000..9c4ead4c9fd1 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/adi,adp5055-regulator.yaml @@ -0,0 +1,157 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%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 <alexisczezar.torreno@analog.com> + +description: | + The ADP5055 combines three high performance buck regulator. 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 + +properties: + compatible: + enum: + - adi,adp5055 + + reg: + enum: + - 0x70 + - 0x71 + + adi,tset-us: + description: + Setting time used by the device. This is changed via soldering specific + resistor values on the CFG2 pin. + enum: [2600, 20800] + default: 2600 + + adi,ocp-blanking: + description: + If present, overcurrent protection (OCP) blanking for all regulator is on. + type: boolean + + adi,delay-power-good: + description: + Configures delay timer of the power good (PWRGD) pin. Delay is based on + Tset which can be 2.6 ms or 20.8 ms. + type: boolean + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + '^buck[0-2]$': + type: object + $ref: regulator.yaml# + unevaluatedProperties: false + + properties: + enable-gpios: + maxItems: 1 + description: + GPIO specifier to enable the GPIO control for each regulator. The + driver supports two modes of enable, hardware only (GPIOs) or software + only (Registers). Pure hardware enabling requires each regulator to + contain this property. If at least one regulator does not have this, + the driver automatically switches to software only mode. + + adi,dvs-limit-upper-microvolt: + description: + Configure the allowable upper side limit of the voltage output of each + regulator in microvolt. Relative to the default Vref trimming value. + Vref = 600 mV. Voltages are in 12 mV steps, value is autoadjusted. + Vout_high = Vref_trim + dvs-limit-upper. + minimum: 12000 + maximum: 192000 + default: 192000 + + adi,dvs-limit-lower-microvolt: + description: + Configure the allowable lower side limit of the voltage output of each + regulator in microvolt. Relative to the default Vref trimming value. + Vref = 600 mV. Voltages are in 12 mV steps, value is autoadjusted. + Vout_low = Vref_trim + dvs-limit-lower. + minimum: -190500 + maximum: -10500 + default: -190500 + + adi,fast-transient: + description: + Configures the fast transient sensitivity for each regulator. + "none" - No fast transient. + "3G_1.5%" - 1.5% window with 3*350uA/V + "5G_1.5%" - 1.5% window with 5*350uA/V + "5G_2.5%" - 2.5% window with 5*350uA/V + enum: [none, 3G_1.5%, 5G_1.5%, 5G_2.5%] + default: 5G_2.5% + + adi,mask-power-good: + description: + If present, masks individual regulators PWRGD signal to the external + PWRGD hardware pin. + type: boolean + + required: + - regulator-name + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + regulator@70 { + compatible = "adi,adp5055"; + reg = <0x70>; + #address-cells = <1>; + #size-cells = <0>; + + adi,tset-us = <2600>; + adi,ocp-blanking; + adi,delay-power-good; + + buck0 { + regulator-name = "buck0"; + enable-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>; + adi,dvs-limit-upper-microvolt = <192000>; + adi,dvs-limit-lower-microvolt = <(-190500)>; + adi,fast-transient = "5G_2.5%"; + adi,mask-power-good; + }; + + buck1 { + regulator-name = "buck1"; + enable-gpios = <&gpio 18 GPIO_ACTIVE_HIGH>; + adi,dvs-limit-upper-microvolt = <192000>; + adi,dvs-limit-lower-microvolt = <(-190500)>; + adi,fast-transient = "5G_2.5%"; + adi,mask-power-good; + }; + + buck2 { + regulator-name = "buck2"; + enable-gpios = <&gpio 19 GPIO_ACTIVE_HIGH>; + adi,dvs-limit-upper-microvolt = <192000>; + adi,dvs-limit-lower-microvolt = <(-190500)>; + adi,fast-transient = "5G_2.5%"; + adi,mask-power-good; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index c59316109e3f..a669ad08c35b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1567,6 +1567,13 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/filter/adi,admv8818.yaml F: drivers/iio/filter/admv8818.c +ANALOG DEVICES INC ADP5055 DRIVER +M: Alexis Czezar Torreno <alexisczezar.torreno@analog.com> +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/regulator/adi,adp5055-regulator.yaml +F: drivers/regulator/adp5055-regulator.c + ANALOG DEVICES INC ADP5061 DRIVER M: Michael Hennerich <Michael.Hennerich@analog.com> L: linux-pm@vger.kernel.org diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 65f6a7177b78..17802d97492f 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -224,6 +224,15 @@ static void of_gpio_try_fixup_polarity(const struct device_node *np, */ { "lantiq,pci-xway", "gpio-reset", false }, #endif +#if IS_ENABLED(CONFIG_REGULATOR_S5M8767) + /* + * According to S5M8767, the DVS and DS pin are + * active-high signals. However, exynos5250-spring.dts use + * active-low setting. + */ + { "samsung,s5m8767-pmic", "s5m8767,pmic-buck-dvs-gpios", true }, + { "samsung,s5m8767-pmic", "s5m8767,pmic-buck-ds-gpios", true }, +#endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_TSC2005) /* * DTS for Nokia N900 incorrectly specified "active high" diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index b8197502a5ac..2e5b6982e76d 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -266,6 +266,20 @@ struct gpio_device *gpiod_to_gpio_device(struct gpio_desc *desc) EXPORT_SYMBOL_GPL(gpiod_to_gpio_device); /** + * gpiod_is_equal() - Check if two GPIO descriptors refer to the same pin. + * @desc: Descriptor to compare. + * @other: The second descriptor to compare against. + * + * Returns: + * True if the descriptors refer to the same physical pin. False otherwise. + */ +bool gpiod_is_equal(struct gpio_desc *desc, struct gpio_desc *other) +{ + return desc == other; +} +EXPORT_SYMBOL_GPL(gpiod_is_equal); + +/** * gpio_device_get_base() - Get the base GPIO number allocated by this device * @gdev: GPIO device * diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 05e32d764028..ad37f274dd8e 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -122,6 +122,17 @@ 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 + select REGMAP_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 524e026c0273..c0bc7a0f4e67 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -22,6 +22,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 000000000000..4b004a6b2f84 --- /dev/null +++ b/drivers/regulator/adp5055-regulator.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Regulator driver for Analog Devices ADP5055 +// +// Copyright (C) 2025 Analog Devices, Inc. + +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> + +// 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_EN_MODE BIT(0) +#define ADP5055_MASK_OCP_BLANKING BIT(7) +#define ADP5055_MASK_PSM BIT(4) +#define ADP5055_MASK_DIS2 BIT(2) +#define ADP5055_MASK_DIS1 BIT(1) +#define ADP5055_MASK_DIS0 BIT(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_TRANSIENT2 GENMASK(5, 4) +#define ADP5055_MASK_FAST_TRANSIENT1 GENMASK(3, 2) +#define ADP5055_MASK_FAST_TRANSIENT0 GENMASK(1, 0) +#define ADP5055_MASK_DLY_PWRGD BIT(4) +#define ADP5055_MASK_PWRGD2 BIT(2) +#define ADP5055_MASK_PWRGD1 BIT(1) +#define ADP5055_MASK_PWRGD0 BIT(0) + +#define ADP5055_MIN_VOUT 408000 +#define ADP5055_NUM_CH 3 + +struct adp5055 { + struct device *dev; + struct regmap *regmap; + u32 tset; + struct gpio_desc *en_gpiod[ADP5055_NUM_CH]; + bool en_mode_software; + int dvs_limit_upper[ADP5055_NUM_CH]; + int dvs_limit_lower[ADP5055_NUM_CH]; + u32 fast_transient[ADP5055_NUM_CH]; + bool mask_power_good[ADP5055_NUM_CH]; +}; + +static const unsigned int adp5055_tset_vals[] = { + 2600, + 20800, +}; + +static const unsigned int adp5055_enable_delay_vals_2_6[] = { + 0, + 2600, + 5200, + 7800, + 10400, + 13000, + 15600, + 18200, +}; + +static const unsigned int adp5055_enable_delay_vals_20_8[] = { + 0, + 20800, + 41600, + 62400, + 83200, + 104000, + 124800, + 145600, +}; + +static const char * const adp5055_fast_transient_vals[] = { + "none", + "3G_1.5%", + "5G_1.5%", + "5G_2.5%", +}; + +static int adp5055_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) + return i; + + return -EINVAL; +} + +static const struct regmap_range adp5055_reg_ranges[] = { + regmap_reg_range(0xD1, 0xE0), +}; + +static const struct regmap_access_table adp5055_access_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 = 0xE0, + .wr_table = &adp5055_access_ranges_table, + .rd_table = &adp5055_access_ranges_table, +}; + +static const struct linear_range adp5055_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(ADP5055_MIN_VOUT, 0, 255, 1500), +}; + +static int adp5055_parse_fw(struct device *dev, struct adp5055 *adp5055) +{ + int i, ret; + struct regmap *regmap = adp5055->regmap; + int val; + bool ocp_blanking; + bool delay_power_good; + + ret = device_property_read_u32(dev, "adi,tset-us", &adp5055->tset); + if (!ret) { + ret = adp5055_get_prop_index(adp5055_tset_vals, + ARRAY_SIZE(adp5055_tset_vals), adp5055->tset); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to initialize tset."); + adp5055->tset = adp5055_tset_vals[ret]; + } + + ocp_blanking = device_property_read_bool(dev, "adi,ocp-blanking"); + + delay_power_good = device_property_read_bool(dev, + "adi,delay-power-good"); + + for (i = 0; i < ADP5055_NUM_CH; i++) { + val = FIELD_PREP(ADP5055_MASK_DVS_LIM_UPPER, + DIV_ROUND_CLOSEST_ULL(192000 - adp5055->dvs_limit_upper[i], 12000)); + val |= FIELD_PREP(ADP5055_MASK_DVS_LIM_LOWER, + DIV_ROUND_CLOSEST_ULL(adp5055->dvs_limit_lower[i] + 190500, 12000)); + ret = regmap_write(regmap, ADP5055_DVS_LIM0 + i, val); + if (ret) + return ret; + } + + val = FIELD_PREP(ADP5055_MASK_EN_MODE, adp5055->en_mode_software); + ret = regmap_write(regmap, ADP5055_CTRL_MODE1, val); + if (ret) + return ret; + + val = FIELD_PREP(ADP5055_MASK_OCP_BLANKING, ocp_blanking); + ret = regmap_update_bits(regmap, ADP5055_CTRL_MODE2, + ADP5055_MASK_OCP_BLANKING, val); + if (ret) + return ret; + + val = FIELD_PREP(ADP5055_MASK_FAST_TRANSIENT2, adp5055->fast_transient[2]); + val |= FIELD_PREP(ADP5055_MASK_FAST_TRANSIENT1, adp5055->fast_transient[1]); + val |= FIELD_PREP(ADP5055_MASK_FAST_TRANSIENT0, adp5055->fast_transient[0]); + ret = regmap_write(regmap, ADP5055_FT_CFG, val); + if (ret) + return ret; + + val = FIELD_PREP(ADP5055_MASK_DLY_PWRGD, delay_power_good); + val |= FIELD_PREP(ADP5055_MASK_PWRGD2, adp5055->mask_power_good[2]); + val |= FIELD_PREP(ADP5055_MASK_PWRGD1, adp5055->mask_power_good[1]); + val |= FIELD_PREP(ADP5055_MASK_PWRGD0, adp5055->mask_power_good[0]); + ret = regmap_write(regmap, ADP5055_PG_CFG, val); + if (ret) + return ret; + + return 0; +} + +static int adp5055_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct adp5055 *adp5055 = config->driver_data; + int id, ret, pval, i; + + id = desc->id; + + if (of_property_read_bool(np, "enable-gpios")) { + adp5055->en_gpiod[id] = devm_fwnode_gpiod_get(config->dev, + of_fwnode_handle(np), "enable", + GPIOD_OUT_LOW, "enable"); + if (IS_ERR(adp5055->en_gpiod[id])) + return dev_err_probe(config->dev, PTR_ERR(adp5055->en_gpiod[id]), + "Failed to get enable GPIO\n"); + + config->ena_gpiod = adp5055->en_gpiod[id]; + } else { + adp5055->en_mode_software = true; + } + + ret = of_property_read_u32(np, "adi,dvs-limit-upper-microvolt", &pval); + if (ret) + adp5055->dvs_limit_upper[id] = 192000; + else + adp5055->dvs_limit_upper[id] = pval; + + if (adp5055->dvs_limit_upper[id] > 192000 || adp5055->dvs_limit_upper[id] < 12000) + return dev_err_probe(config->dev, adp5055->dvs_limit_upper[id], + "Out of range - dvs-limit-upper-microvolt value."); + + ret = of_property_read_u32(np, "adi,dvs-limit-lower-microvolt", &pval); + if (ret) + adp5055->dvs_limit_lower[id] = -190500; + else + adp5055->dvs_limit_lower[id] = pval; + + if (adp5055->dvs_limit_lower[id] > -10500 || adp5055->dvs_limit_lower[id] < -190500) + return dev_err_probe(config->dev, adp5055->dvs_limit_lower[id], + "Out of range - dvs-limit-lower-microvolt value."); + + for (i = 0; i < 4; i++) { + ret = of_property_match_string(np, "adi,fast-transient", + adp5055_fast_transient_vals[i]); + if (!ret) + break; + } + + if (ret < 0) + adp5055->fast_transient[id] = 3; + else + adp5055->fast_transient[id] = i; + + adp5055->mask_power_good[id] = of_property_read_bool(np, "adi,mask-power-good"); + + return 0; +} + +static int adp5055_set_mode(struct regulator_dev *rdev, u32 mode) +{ + struct adp5055 *adp5055 = rdev_get_drvdata(rdev); + int id, ret; + + id = rdev_get_id(rdev); + + switch (mode) { + case REGULATOR_MODE_NORMAL: + ret = regmap_update_bits(adp5055->regmap, ADP5055_CTRL_MODE2, + ADP5055_MASK_PSM << id, 0); + break; + case REGULATOR_MODE_IDLE: + ret = regmap_update_bits(adp5055->regmap, ADP5055_CTRL_MODE2, + ADP5055_MASK_PSM << id, ADP5055_MASK_PSM << id); + break; + default: + return dev_err_probe(&rdev->dev, -EINVAL, + "Unsupported mode: %d\n", mode); + } + + return ret; +} + +static unsigned int adp5055_get_mode(struct regulator_dev *rdev) +{ + struct adp5055 *adp5055 = rdev_get_drvdata(rdev); + int id, ret, regval; + + id = rdev_get_id(rdev); + + ret = regmap_read(adp5055->regmap, ADP5055_CTRL_MODE2, ®val); + if (ret) + return ret; + + if (regval & (ADP5055_MASK_PSM << id)) + return REGULATOR_MODE_IDLE; + else + return REGULATOR_MODE_NORMAL; +} + +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, + .set_active_discharge = regulator_set_active_discharge_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_mode = adp5055_set_mode, + .get_mode = adp5055_get_mode, + .set_ramp_delay = regulator_set_ramp_delay_regmap, +}; + +#define ADP5055_REG_(_name, _id, _ch, _ops) \ + [_id] = { \ + .name = _name, \ + .of_match = of_match_ptr(_name), \ + .of_parse_cb = adp5055_of_parse_cb, \ + .id = _id, \ + .ops = _ops, \ + .linear_ranges = adp5055_voltage_ranges, \ + .n_linear_ranges = ARRAY_SIZE(adp5055_voltage_ranges), \ + .vsel_reg = ADP5055_VID##_ch, \ + .vsel_mask = GENMASK(7, 0), \ + .enable_reg = ADP5055_CTRL123, \ + .enable_mask = BIT(_ch), \ + .active_discharge_on = ADP5055_MASK_DIS##_id, \ + .active_discharge_off = 0, \ + .active_discharge_mask = ADP5055_MASK_DIS##_id, \ + .active_discharge_reg = ADP5055_CTRL_MODE2, \ + .ramp_reg = ADP5055_DLY##_ch, \ + .ramp_mask = ADP5055_MASK_EN_DLY, \ + .n_ramp_values = ARRAY_SIZE(adp5055_enable_delay_vals_2_6), \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + } + +#define ADP5055_REG(_name, _id, _ch) \ + ADP5055_REG_(_name, _id, _ch, &adp5055_ops) + +static struct regulator_desc adp5055_regulators[] = { + ADP5055_REG("buck0", 0, 0), + ADP5055_REG("buck1", 1, 1), + ADP5055_REG("buck2", 2, 2), +}; + +static int adp5055_probe(struct i2c_client *client) +{ + struct regulator_init_data *init_data; + struct device *dev = &client->dev; + struct adp5055 *adp5055; + int i, ret; + + 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->tset = 2600; + adp5055->en_mode_software = false; + + adp5055->regmap = devm_regmap_init_i2c(client, &adp5055_regmap_config); + if (IS_ERR(adp5055->regmap)) + return dev_err_probe(dev, PTR_ERR(adp5055->regmap), "Failed to allocate reg map"); + + for (i = 0; i < ADP5055_NUM_CH; i++) { + const struct regulator_desc *desc; + struct regulator_config config = { }; + struct regulator_dev *rdev; + + if (adp5055->tset == 2600) + adp5055_regulators[i].ramp_delay_table = adp5055_enable_delay_vals_2_6; + else + adp5055_regulators[i].ramp_delay_table = adp5055_enable_delay_vals_20_8; + + desc = &adp5055_regulators[i]; + + 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)) { + return dev_err_probe(dev, PTR_ERR(rdev), + "Failed to register %s\n", desc->name); + } + } + + ret = adp5055_parse_fw(dev, adp5055); + if (ret < 0) + return ret; + + return 0; +} + +static const struct of_device_id adp5055_of_match[] = { + { .compatible = "adi,adp5055", }, + { } +}; +MODULE_DEVICE_TABLE(of, adp5055_of_match); + +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", + .of_match_table = adp5055_of_match, + }, + .probe = adp5055_probe, + .id_table = adp5055_ids, +}; +module_i2c_driver(adp5055_driver); + +MODULE_DESCRIPTION("ADP5055 Voltage Regulator Driver"); +MODULE_AUTHOR("Alexis Czezar Torreno <alexisczezar.torreno@analog.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 90629a756693..7a248dc8d2e2 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2617,7 +2617,7 @@ static int regulator_ena_gpio_request(struct regulator_dev *rdev, mutex_lock(®ulator_list_mutex); list_for_each_entry(pin, ®ulator_ena_gpio_list, list) { - if (pin->gpiod == gpiod) { + if (gpiod_is_equal(pin->gpiod, gpiod)) { rdev_dbg(rdev, "GPIO is already used\n"); goto update_ena_gpio_to_rdev; } diff --git a/drivers/regulator/pf9453-regulator.c b/drivers/regulator/pf9453-regulator.c index ed6bf0f6c4fe..b58c9c0a5022 100644 --- a/drivers/regulator/pf9453-regulator.c +++ b/drivers/regulator/pf9453-regulator.c @@ -412,6 +412,7 @@ static int find_closest_bigger(unsigned int target, const unsigned int *table, * pf9453_regulator_set_ramp_delay_regmap * * @rdev: regulator to operate on + * @ramp_delay: desired ramp delay value in microseconds * * Regulators that use regmap for their register I/O can set the ramp_reg * and ramp_mask fields in their descriptor and then use this as their diff --git a/drivers/regulator/rpi-panel-attiny-regulator.c b/drivers/regulator/rpi-panel-attiny-regulator.c index 6c3b6bfac961..58dbf8bffa5d 100644 --- a/drivers/regulator/rpi-panel-attiny-regulator.c +++ b/drivers/regulator/rpi-panel-attiny-regulator.c @@ -6,12 +6,14 @@ */ #include <linux/backlight.h> +#include <linux/cleanup.h> #include <linux/err.h> #include <linux/gpio/driver.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/regmap.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> @@ -93,7 +95,7 @@ static int attiny_lcd_power_enable(struct regulator_dev *rdev) { struct attiny_lcd *state = rdev_get_drvdata(rdev); - mutex_lock(&state->lock); + guard(mutex)(&state->lock); /* Ensure bridge, and tp stay in reset */ attiny_set_port_state(state, REG_PORTC, 0); @@ -114,8 +116,6 @@ static int attiny_lcd_power_enable(struct regulator_dev *rdev) msleep(80); - mutex_unlock(&state->lock); - return 0; } @@ -123,7 +123,7 @@ static int attiny_lcd_power_disable(struct regulator_dev *rdev) { struct attiny_lcd *state = rdev_get_drvdata(rdev); - mutex_lock(&state->lock); + guard(mutex)(&state->lock); regmap_write(rdev->regmap, REG_PWM, 0); usleep_range(5000, 10000); @@ -135,8 +135,6 @@ static int attiny_lcd_power_disable(struct regulator_dev *rdev) attiny_set_port_state(state, REG_PORTC, 0); msleep(30); - mutex_unlock(&state->lock); - return 0; } @@ -144,19 +142,17 @@ static int attiny_lcd_power_is_enabled(struct regulator_dev *rdev) { struct attiny_lcd *state = rdev_get_drvdata(rdev); unsigned int data; - int ret, i; - - mutex_lock(&state->lock); - - for (i = 0; i < 10; i++) { - ret = regmap_read(rdev->regmap, REG_PORTC, &data); - if (!ret) - break; - usleep_range(10000, 12000); + int ret = 0, i; + + scoped_guard(mutex, &state->lock) { + for (i = 0; i < 10; i++) { + ret = regmap_read(rdev->regmap, REG_PORTC, &data); + if (!ret) + break; + usleep_range(10000, 12000); + } } - mutex_unlock(&state->lock); - if (ret < 0) return ret; @@ -189,7 +185,7 @@ static int attiny_update_status(struct backlight_device *bl) int brightness = backlight_get_brightness(bl); int ret, i; - mutex_lock(&state->lock); + guard(mutex)(&state->lock); for (i = 0; i < 10; i++) { ret = regmap_write(regmap, REG_PWM, brightness); @@ -197,8 +193,6 @@ static int attiny_update_status(struct backlight_device *bl) break; } - mutex_unlock(&state->lock); - return ret; } @@ -211,15 +205,12 @@ static int attiny_gpio_get_direction(struct gpio_chip *gc, unsigned int off) return GPIO_LINE_DIRECTION_OUT; } -static void attiny_gpio_set(struct gpio_chip *gc, unsigned int off, int val) +static int attiny_gpio_set(struct gpio_chip *gc, unsigned int off, int val) { struct attiny_lcd *state = gpiochip_get_data(gc); u8 last_val; - if (off >= NUM_GPIO) - return; - - mutex_lock(&state->lock); + guard(mutex)(&state->lock); last_val = attiny_get_port_state(state, mappings[off].reg); if (val) @@ -242,7 +233,7 @@ static void attiny_gpio_set(struct gpio_chip *gc, unsigned int off, int val) msleep(100); } - mutex_unlock(&state->lock); + return 0; } static int attiny_i2c_read(struct i2c_client *client, u8 reg, unsigned int *buf) @@ -296,7 +287,10 @@ static int attiny_i2c_probe(struct i2c_client *i2c) if (!state) return -ENOMEM; - mutex_init(&state->lock); + ret = devm_mutex_init(&i2c->dev, &state->lock); + if (ret) + return ret; + i2c_set_clientdata(i2c, state); regmap = devm_regmap_init_i2c(i2c, &attiny_regmap_config); @@ -304,13 +298,13 @@ static int attiny_i2c_probe(struct i2c_client *i2c) ret = PTR_ERR(regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret); - goto error; + return ret; } ret = attiny_i2c_read(i2c, REG_ID, &data); if (ret < 0) { dev_err(&i2c->dev, "Failed to read REG_ID reg: %d\n", ret); - goto error; + return ret; } switch (data) { @@ -319,8 +313,7 @@ static int attiny_i2c_probe(struct i2c_client *i2c) break; default: dev_err(&i2c->dev, "Unknown Atmel firmware revision: 0x%02x\n", data); - ret = -ENODEV; - goto error; + return -ENODEV; } regmap_write(regmap, REG_POWERON, 0); @@ -336,8 +329,7 @@ static int attiny_i2c_probe(struct i2c_client *i2c) rdev = devm_regulator_register(&i2c->dev, &attiny_regulator, &config); if (IS_ERR(rdev)) { dev_err(&i2c->dev, "Failed to register ATTINY regulator\n"); - ret = PTR_ERR(rdev); - goto error; + return PTR_ERR(rdev); } props.type = BACKLIGHT_RAW; @@ -348,10 +340,8 @@ static int attiny_i2c_probe(struct i2c_client *i2c) bl = devm_backlight_device_register(&i2c->dev, dev_name(&i2c->dev), &i2c->dev, state, &attiny_bl, &props); - if (IS_ERR(bl)) { - ret = PTR_ERR(bl); - goto error; - } + if (IS_ERR(bl)) + return PTR_ERR(bl); bl->props.brightness = 0xff; @@ -361,31 +351,17 @@ static int attiny_i2c_probe(struct i2c_client *i2c) state->gc.base = -1; state->gc.ngpio = NUM_GPIO; - state->gc.set = attiny_gpio_set; + state->gc.set_rv = attiny_gpio_set; state->gc.get_direction = attiny_gpio_get_direction; state->gc.can_sleep = true; ret = devm_gpiochip_add_data(&i2c->dev, &state->gc, state); - if (ret) { + if (ret) dev_err(&i2c->dev, "Failed to create gpiochip: %d\n", ret); - goto error; - } - - return 0; - -error: - mutex_destroy(&state->lock); return ret; } -static void attiny_i2c_remove(struct i2c_client *client) -{ - struct attiny_lcd *state = i2c_get_clientdata(client); - - mutex_destroy(&state->lock); -} - static const struct of_device_id attiny_dt_ids[] = { { .compatible = "raspberrypi,7inch-touchscreen-panel-regulator" }, {}, @@ -399,7 +375,6 @@ static struct i2c_driver attiny_regulator_driver = { .of_match_table = attiny_dt_ids, }, .probe = attiny_i2c_probe, - .remove = attiny_i2c_remove, }; module_i2c_driver(attiny_regulator_driver); diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index d25cd81e3f36..fe2631378ccd 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -5,7 +5,7 @@ #include <linux/cleanup.h> #include <linux/err.h> -#include <linux/of_gpio.h> +#include <linux/of.h> #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -35,8 +35,8 @@ struct s5m8767_info { u8 buck2_vol[8]; u8 buck3_vol[8]; u8 buck4_vol[8]; - int buck_gpios[3]; - int buck_ds[3]; + struct gpio_desc *buck_gpios[3]; + struct gpio_desc *buck_ds[3]; int buck_gpioindex; }; @@ -272,9 +272,9 @@ static inline int s5m8767_set_high(struct s5m8767_info *s5m8767) { int temp_index = s5m8767->buck_gpioindex; - gpio_set_value(s5m8767->buck_gpios[0], (temp_index >> 2) & 0x1); - gpio_set_value(s5m8767->buck_gpios[1], (temp_index >> 1) & 0x1); - gpio_set_value(s5m8767->buck_gpios[2], temp_index & 0x1); + gpiod_set_value(s5m8767->buck_gpios[0], !!(temp_index & BIT(2))); + gpiod_set_value(s5m8767->buck_gpios[1], !!(temp_index & BIT(1))); + gpiod_set_value(s5m8767->buck_gpios[2], !!(temp_index & BIT(0))); return 0; } @@ -283,9 +283,9 @@ static inline int s5m8767_set_low(struct s5m8767_info *s5m8767) { int temp_index = s5m8767->buck_gpioindex; - gpio_set_value(s5m8767->buck_gpios[2], temp_index & 0x1); - gpio_set_value(s5m8767->buck_gpios[1], (temp_index >> 1) & 0x1); - gpio_set_value(s5m8767->buck_gpios[0], (temp_index >> 2) & 0x1); + gpiod_set_value(s5m8767->buck_gpios[2], !!(temp_index & BIT(0))); + gpiod_set_value(s5m8767->buck_gpios[1], !!(temp_index & BIT(1))); + gpiod_set_value(s5m8767->buck_gpios[0], !!(temp_index & BIT(2))); return 0; } @@ -482,42 +482,6 @@ static int s5m8767_enable_ext_control(struct s5m8767_info *s5m8767, #ifdef CONFIG_OF -static int s5m8767_pmic_dt_parse_dvs_gpio(struct sec_pmic_dev *iodev, - struct sec_platform_data *pdata, - struct device_node *pmic_np) -{ - int i, gpio; - - for (i = 0; i < 3; i++) { - gpio = of_get_named_gpio(pmic_np, - "s5m8767,pmic-buck-dvs-gpios", i); - if (!gpio_is_valid(gpio)) { - dev_err(iodev->dev, "invalid gpio[%d]: %d\n", i, gpio); - return -EINVAL; - } - pdata->buck_gpios[i] = gpio; - } - return 0; -} - -static int s5m8767_pmic_dt_parse_ds_gpio(struct sec_pmic_dev *iodev, - struct sec_platform_data *pdata, - struct device_node *pmic_np) -{ - int i, gpio; - - for (i = 0; i < 3; i++) { - gpio = of_get_named_gpio(pmic_np, - "s5m8767,pmic-buck-ds-gpios", i); - if (!gpio_is_valid(gpio)) { - dev_err(iodev->dev, "invalid gpio[%d]: %d\n", i, gpio); - return -EINVAL; - } - pdata->buck_ds[i] = gpio; - } - return 0; -} - static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, struct sec_platform_data *pdata) { @@ -525,7 +489,7 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, struct device_node *pmic_np, *reg_np; struct sec_regulator_data *rdata; struct sec_opmode_data *rmode; - unsigned int i, dvs_voltage_nr = 8, ret; + unsigned int i, dvs_voltage_nr = 8; pmic_np = iodev->dev->of_node; if (!pmic_np) { @@ -635,10 +599,6 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs || pdata->buck4_gpiodvs) { - ret = s5m8767_pmic_dt_parse_dvs_gpio(iodev, pdata, pmic_np); - if (ret) - return -EINVAL; - if (of_property_read_u32(pmic_np, "s5m8767,pmic-buck-default-dvs-idx", &pdata->buck_default_idx)) { @@ -652,10 +612,6 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, } } - ret = s5m8767_pmic_dt_parse_ds_gpio(iodev, pdata, pmic_np); - if (ret) - return -EINVAL; - pdata->buck2_ramp_enable = of_property_read_bool(pmic_np, "s5m8767,pmic-buck2-ramp-enable"); pdata->buck3_ramp_enable = of_property_read_bool(pmic_np, "s5m8767,pmic-buck3-ramp-enable"); pdata->buck4_ramp_enable = of_property_read_bool(pmic_np, "s5m8767,pmic-buck4-ramp-enable"); @@ -684,6 +640,8 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) struct regulator_config config = { }; struct s5m8767_info *s5m8767; int i, ret, buck_init; + const char *gpiods_names[3] = { "S5M8767 DS2", "S5M8767 DS3", "S5M8767 DS4" }; + const char *gpiodvs_names[3] = { "S5M8767 SET1", "S5M8767 SET2", "S5M8767 SET3" }; if (!pdata) { dev_err(pdev->dev.parent, "Platform data not supplied\n"); @@ -731,12 +689,6 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) s5m8767->buck2_gpiodvs = pdata->buck2_gpiodvs; s5m8767->buck3_gpiodvs = pdata->buck3_gpiodvs; s5m8767->buck4_gpiodvs = pdata->buck4_gpiodvs; - s5m8767->buck_gpios[0] = pdata->buck_gpios[0]; - s5m8767->buck_gpios[1] = pdata->buck_gpios[1]; - s5m8767->buck_gpios[2] = pdata->buck_gpios[2]; - s5m8767->buck_ds[0] = pdata->buck_ds[0]; - s5m8767->buck_ds[1] = pdata->buck_ds[1]; - s5m8767->buck_ds[2] = pdata->buck_ds[2]; s5m8767->ramp_delay = pdata->buck_ramp_delay; s5m8767->buck2_ramp = pdata->buck2_ramp_enable; @@ -787,58 +739,36 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs || pdata->buck4_gpiodvs) { + for (i = 0; i < 3; i++) { + enum gpiod_flags flags; - if (!gpio_is_valid(pdata->buck_gpios[0]) || - !gpio_is_valid(pdata->buck_gpios[1]) || - !gpio_is_valid(pdata->buck_gpios[2])) { - dev_err(&pdev->dev, "GPIO NOT VALID\n"); - return -EINVAL; - } - - ret = devm_gpio_request(&pdev->dev, pdata->buck_gpios[0], - "S5M8767 SET1"); - if (ret) - return ret; - - ret = devm_gpio_request(&pdev->dev, pdata->buck_gpios[1], - "S5M8767 SET2"); - if (ret) - return ret; - - ret = devm_gpio_request(&pdev->dev, pdata->buck_gpios[2], - "S5M8767 SET3"); - if (ret) - return ret; + if (s5m8767->buck_gpioindex & BIT(2 - i)) + flags = GPIOD_OUT_HIGH; + else + flags = GPIOD_OUT_LOW; + + s5m8767->buck_gpios[i] = devm_gpiod_get_index(iodev->dev, + "s5m8767,pmic-buck-dvs", i, + flags); + if (IS_ERR(s5m8767->buck_gpios[i])) { + return dev_err_probe(iodev->dev, PTR_ERR(s5m8767->buck_gpios[i]), + "invalid gpio[%d]\n", i); + } - /* SET1 GPIO */ - gpio_direction_output(pdata->buck_gpios[0], - (s5m8767->buck_gpioindex >> 2) & 0x1); - /* SET2 GPIO */ - gpio_direction_output(pdata->buck_gpios[1], - (s5m8767->buck_gpioindex >> 1) & 0x1); - /* SET3 GPIO */ - gpio_direction_output(pdata->buck_gpios[2], - (s5m8767->buck_gpioindex >> 0) & 0x1); + gpiod_set_consumer_name(s5m8767->buck_gpios[i], gpiodvs_names[i]); + } } - ret = devm_gpio_request(&pdev->dev, pdata->buck_ds[0], "S5M8767 DS2"); - if (ret) - return ret; - - ret = devm_gpio_request(&pdev->dev, pdata->buck_ds[1], "S5M8767 DS3"); - if (ret) - return ret; - - ret = devm_gpio_request(&pdev->dev, pdata->buck_ds[2], "S5M8767 DS4"); - if (ret) - return ret; - - /* DS2 GPIO */ - gpio_direction_output(pdata->buck_ds[0], 0x0); - /* DS3 GPIO */ - gpio_direction_output(pdata->buck_ds[1], 0x0); - /* DS4 GPIO */ - gpio_direction_output(pdata->buck_ds[2], 0x0); + for (i = 0; i < 3; i++) { + s5m8767->buck_ds[i] = devm_gpiod_get_index(iodev->dev, + "s5m8767,pmic-buck-ds", i, + GPIOD_OUT_LOW); + if (IS_ERR(s5m8767->buck_ds[i])) { + return dev_err_probe(iodev->dev, PTR_ERR(s5m8767->buck_ds[i]), + "can't get GPIO %d\n", i); + } + gpiod_set_consumer_name(s5m8767->buck_ds[i], gpiods_names[i]); + } regmap_update_bits(s5m8767->iodev->regmap_pmic, S5M8767_REG_BUCK2CTRL, 1 << 1, diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 8adc8e9cb4a7..12c59999d15b 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -181,6 +181,8 @@ struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev, enum gpiod_flags flags, const char *label); +bool gpiod_is_equal(struct gpio_desc *desc, struct gpio_desc *other); + #else /* CONFIG_GPIOLIB */ #include <linux/bug.h> @@ -548,6 +550,13 @@ struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev, return ERR_PTR(-ENOSYS); } +static inline bool +gpiod_is_equal(struct gpio_desc *desc, struct gpio_desc *other) +{ + WARN_ON(desc || other); + return false; +} + #endif /* CONFIG_GPIOLIB */ #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_HTE) |