diff options
35 files changed, 1327 insertions, 171 deletions
diff --git a/Documentation/devicetree/bindings/mfd/ti,tps6594.yaml b/Documentation/devicetree/bindings/mfd/ti,tps6594.yaml index 6341b6070366..a48cb00afe43 100644 --- a/Documentation/devicetree/bindings/mfd/ti,tps6594.yaml +++ b/Documentation/devicetree/bindings/mfd/ti,tps6594.yaml @@ -22,6 +22,7 @@ properties: - ti,tps6593-q1 - ti,tps6594-q1 - ti,tps65224-q1 + - ti,tps652g1 reg: description: I2C slave address or SPI chip select number. diff --git a/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml b/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml index 704828687970..685ccf9cf4d4 100644 --- a/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml @@ -17,9 +17,11 @@ properties: compatible: enum: - mediatek,mt6873-dvfsrc-regulator + - mediatek,mt6893-dvfsrc-regulator - mediatek,mt8183-dvfsrc-regulator - mediatek,mt8192-dvfsrc-regulator - mediatek,mt8195-dvfsrc-regulator + - mediatek,mt8196-dvfsrc-regulator dvfsrc-vcore: description: DVFSRC-controlled SoC Vcore regulator diff --git a/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml b/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml index 4ffe5c3faea0..a5486c36830f 100644 --- a/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml @@ -100,6 +100,15 @@ properties: PMIC default "STANDBY" state voltage in uV. Only Buck1~3 have such dvs(dynamic voltage scaling) property. + regulator-allowed-modes: + description: | + Buck regulator operating modes allowed. Valid values below. + Users should use the macros from dt-bindings/regulator/nxp,pca9450-regulator.h + 0 (PCA9450_BUCK_MODE_AUTO): Auto PFM/PWM mode + 1 (PCA9450_BUCK_MODE_FORCE_PWM): Forced PWM mode + items: + enum: [ 0, 1 ] + unevaluatedProperties: false additionalProperties: false @@ -143,6 +152,7 @@ allOf: examples: - | #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/regulator/nxp,pca9450-regulator.h> i2c { #address-cells = <1>; @@ -179,6 +189,8 @@ examples: regulator-max-microvolt = <3400000>; regulator-boot-on; regulator-always-on; + regulator-initial-mode = <PCA9450_BUCK_MODE_FORCE_PWM>; + regulator-allowed-modes = <PCA9450_BUCK_MODE_FORCE_PWM>; }; buck5: BUCK5 { regulator-name = "BUCK5"; @@ -186,6 +198,8 @@ examples: regulator-max-microvolt = <3400000>; regulator-boot-on; regulator-always-on; + regulator-allowed-modes = <PCA9450_BUCK_MODE_AUTO + PCA9450_BUCK_MODE_FORCE_PWM>; }; buck6: BUCK6 { regulator-name = "BUCK6"; diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml index 3a5a0a6cf5cc..4c5b0629aa3e 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml @@ -40,6 +40,7 @@ description: | For PM660, smps1 - smps6, ldo1 - ldo3, ldo5 - ldo19 For PM660L, smps1 - smps3, smps5, ldo1 - ldo8, bob For PM7325, smps1 - smps8, ldo1 - ldo19 + For PM7550, smps1 - smps6, ldo1 - ldo23, bob For PM8005, smps1 - smps4 For PM8009, smps1 - smps2, ldo1 - ldo7 For PM8010, ldo1 - ldo7 @@ -53,6 +54,7 @@ description: | For PMI8998, bob For PMC8380, smps1 - smps8, ldo1 - lodo3 For PMR735A, smps1 - smps3, ldo1 - ldo7 + For PMR735B, ldo1 - ldo12 For PMX55, smps1 - smps7, ldo1 - ldo16 For PMX65, smps1 - smps8, ldo1 - ldo21 For PMX75, smps1 - smps10, ldo1 - ldo21 @@ -66,6 +68,7 @@ properties: - qcom,pm660-rpmh-regulators - qcom,pm660l-rpmh-regulators - qcom,pm7325-rpmh-regulators + - qcom,pm7550-rpmh-regulators - qcom,pm8005-rpmh-regulators - qcom,pm8009-rpmh-regulators - qcom,pm8009-1-rpmh-regulators @@ -87,6 +90,7 @@ properties: - qcom,pmm8155au-rpmh-regulators - qcom,pmm8654au-rpmh-regulators - qcom,pmr735a-rpmh-regulators + - qcom,pmr735b-rpmh-regulators - qcom,pmx55-rpmh-regulators - qcom,pmx65-rpmh-regulators - qcom,pmx75-rpmh-regulators @@ -222,6 +226,25 @@ allOf: properties: compatible: enum: + - qcom,pm7550-rpmh-regulators + then: + properties: + vdd-bob-supply: + description: BOB regulator parent supply phandle. + vdd-l2-l3-supply: true + vdd-l4-l5-supply: true + vdd-l9-l10-supply: true + vdd-l12-l14-supply: true + vdd-l13-l16-supply: true + vdd-l15-l17-l18-l19-l20-l21-l22-l23-supply: true + patternProperties: + "^vdd-l(1|[6-8]|11)-supply$": true + "^vdd-s[1-6]-supply$": true + + - if: + properties: + compatible: + enum: - qcom,pm8005-rpmh-regulators then: patternProperties: @@ -428,6 +451,18 @@ allOf: properties: compatible: enum: + - qcom,pmr735b-rpmh-regulators + then: + properties: + vdd-l1-l2-supply: true + vdd-l7-l8-supply: true + patternProperties: + "^vdd-l([3-6]|9|1[0-2])-supply$": true + + - if: + properties: + compatible: + enum: - qcom,pmx55-rpmh-regulators then: properties: diff --git a/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml b/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml index 41678400e63f..18944d39d08f 100644 --- a/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml @@ -12,14 +12,17 @@ maintainers: description: | The RaspberryPi 7" display has an ATTINY88-based regulator/backlight controller on the PCB, which is used to turn the display unit on/off - and control the backlight. + and control the backlight. The V2 supports 5" and 7" panels and also + offers PWM backlight control. allOf: - $ref: regulator.yaml# properties: compatible: - const: raspberrypi,7inch-touchscreen-panel-regulator + enum: + - raspberrypi,7inch-touchscreen-panel-regulator + - raspberrypi,touchscreen-panel-regulator-v2 reg: maxItems: 1 diff --git a/MAINTAINERS b/MAINTAINERS index 2624977a7543..0df73e91cf4c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -26582,6 +26582,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git F: Documentation/devicetree/bindings/regulator/ F: Documentation/power/regulator/ F: drivers/regulator/ +F: rust/kernel/regulator.rs F: include/dt-bindings/regulator/ F: include/linux/regulator/ K: regulator_get_optional diff --git a/drivers/mfd/tps6594-core.c b/drivers/mfd/tps6594-core.c index a7223e873cd1..c16c37e36617 100644 --- a/drivers/mfd/tps6594-core.c +++ b/drivers/mfd/tps6594-core.c @@ -1,6 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Core functions for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs + * Core functions for following TI PMICs: + * - LP8764 + * - TPS65224 + * - TPS652G1 + * - TPS6593 + * - TPS6594 * * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ */ @@ -414,6 +419,61 @@ static const unsigned int tps65224_irq_reg[] = { TPS6594_REG_INT_FSM_ERR, }; +/* TPS652G1 Resources */ + +static const struct mfd_cell tps652g1_common_cells[] = { + MFD_CELL_RES("tps6594-pfsm", tps65224_pfsm_resources), + MFD_CELL_RES("tps6594-pinctrl", tps65224_pinctrl_resources), + MFD_CELL_NAME("tps6594-regulator"), +}; + +static const struct regmap_irq tps652g1_irqs[] = { + /* INT_GPIO register */ + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO1, 2, TPS65224_BIT_GPIO1_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO2, 2, TPS65224_BIT_GPIO2_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO3, 2, TPS65224_BIT_GPIO3_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO4, 2, TPS65224_BIT_GPIO4_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO5, 2, TPS65224_BIT_GPIO5_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO6, 2, TPS65224_BIT_GPIO6_INT), + + /* INT_STARTUP register */ + REGMAP_IRQ_REG(TPS65224_IRQ_VSENSE, 3, TPS65224_BIT_VSENSE_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_ENABLE, 3, TPS6594_BIT_ENABLE_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_PB_SHORT, 3, TPS65224_BIT_PB_SHORT_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_FSD, 3, TPS6594_BIT_FSD_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_SOFT_REBOOT, 3, TPS6594_BIT_SOFT_REBOOT_INT), + + /* INT_MISC register */ + REGMAP_IRQ_REG(TPS65224_IRQ_BIST_PASS, 4, TPS6594_BIT_BIST_PASS_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_EXT_CLK, 4, TPS6594_BIT_EXT_CLK_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_REG_UNLOCK, 4, TPS65224_BIT_REG_UNLOCK_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_TWARN, 4, TPS6594_BIT_TWARN_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_PB_LONG, 4, TPS65224_BIT_PB_LONG_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_PB_FALL, 4, TPS65224_BIT_PB_FALL_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_PB_RISE, 4, TPS65224_BIT_PB_RISE_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_ADC_CONV_READY, 4, TPS65224_BIT_ADC_CONV_READY_INT), + + /* INT_MODERATE_ERR register */ + REGMAP_IRQ_REG(TPS65224_IRQ_TSD_ORD, 5, TPS6594_BIT_TSD_ORD_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_BIST_FAIL, 5, TPS6594_BIT_BIST_FAIL_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_REG_CRC_ERR, 5, TPS6594_BIT_REG_CRC_ERR_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_RECOV_CNT, 5, TPS6594_BIT_RECOV_CNT_INT), + + /* INT_SEVERE_ERR register */ + REGMAP_IRQ_REG(TPS65224_IRQ_TSD_IMM, 6, TPS6594_BIT_TSD_IMM_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_VCCA_OVP, 6, TPS6594_BIT_VCCA_OVP_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_PFSM_ERR, 6, TPS6594_BIT_PFSM_ERR_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_BG_XMON, 6, TPS65224_BIT_BG_XMON_INT), + + /* INT_FSM_ERR register */ + REGMAP_IRQ_REG(TPS65224_IRQ_IMM_SHUTDOWN, 7, TPS6594_BIT_IMM_SHUTDOWN_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_ORD_SHUTDOWN, 7, TPS6594_BIT_ORD_SHUTDOWN_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_MCU_PWR_ERR, 7, TPS6594_BIT_MCU_PWR_ERR_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_SOC_PWR_ERR, 7, TPS6594_BIT_SOC_PWR_ERR_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_COMM_ERR, 7, TPS6594_BIT_COMM_ERR_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_I2C2_ERR, 7, TPS65224_BIT_I2C2_ERR_INT), +}; + static inline unsigned int tps6594_get_irq_reg(struct regmap_irq_chip_data *data, unsigned int base, int index) { @@ -443,7 +503,7 @@ static int tps6594_handle_post_irq(void *irq_drv_data) * a new interrupt. */ if (tps->use_crc) { - if (tps->chip_id == TPS65224) { + if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) { regmap_reg = TPS6594_REG_INT_FSM_ERR; mask_val = TPS6594_BIT_COMM_ERR_INT; } else { @@ -481,6 +541,18 @@ static struct regmap_irq_chip tps65224_irq_chip = { .handle_post_irq = tps6594_handle_post_irq, }; +static struct regmap_irq_chip tps652g1_irq_chip = { + .ack_base = TPS6594_REG_INT_BUCK, + .ack_invert = 1, + .clear_ack = 1, + .init_ack_masked = 1, + .num_regs = ARRAY_SIZE(tps65224_irq_reg), + .irqs = tps652g1_irqs, + .num_irqs = ARRAY_SIZE(tps652g1_irqs), + .get_irq_reg = tps65224_get_irq_reg, + .handle_post_irq = tps6594_handle_post_irq, +}; + static const struct regmap_range tps6594_volatile_ranges[] = { regmap_reg_range(TPS6594_REG_INT_TOP, TPS6594_REG_STAT_READBACK_ERR), regmap_reg_range(TPS6594_REG_RTC_STATUS, TPS6594_REG_RTC_STATUS), @@ -507,7 +579,7 @@ static int tps6594_check_crc_mode(struct tps6594 *tps, bool primary_pmic) int ret; unsigned int regmap_reg, mask_val; - if (tps->chip_id == TPS65224) { + if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) { regmap_reg = TPS6594_REG_CONFIG_2; mask_val = TPS65224_BIT_I2C1_SPI_CRC_EN; } else { @@ -537,7 +609,7 @@ static int tps6594_set_crc_feature(struct tps6594 *tps) int ret; unsigned int regmap_reg, mask_val; - if (tps->chip_id == TPS65224) { + if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) { regmap_reg = TPS6594_REG_CONFIG_2; mask_val = TPS65224_BIT_I2C1_SPI_CRC_EN; } else { @@ -628,6 +700,10 @@ int tps6594_device_init(struct tps6594 *tps, bool enable_crc) irq_chip = &tps65224_irq_chip; n_cells = ARRAY_SIZE(tps65224_common_cells); cells = tps65224_common_cells; + } else if (tps->chip_id == TPS652G1) { + irq_chip = &tps652g1_irq_chip; + n_cells = ARRAY_SIZE(tps652g1_common_cells); + cells = tps652g1_common_cells; } else { irq_chip = &tps6594_irq_chip; n_cells = ARRAY_SIZE(tps6594_common_cells); @@ -651,8 +727,8 @@ int tps6594_device_init(struct tps6594 *tps, bool enable_crc) if (ret) return dev_err_probe(dev, ret, "Failed to add common child devices\n"); - /* No RTC for LP8764 and TPS65224 */ - if (tps->chip_id != LP8764 && tps->chip_id != TPS65224) { + /* No RTC for LP8764, TPS65224 and TPS652G1 */ + if (tps->chip_id != LP8764 && tps->chip_id != TPS65224 && tps->chip_id != TPS652G1) { ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, tps6594_rtc_cells, ARRAY_SIZE(tps6594_rtc_cells), NULL, 0, regmap_irq_get_domain(tps->irq_data)); diff --git a/drivers/mfd/tps6594-i2c.c b/drivers/mfd/tps6594-i2c.c index 4ab91c34d9fb..7ff7516286fd 100644 --- a/drivers/mfd/tps6594-i2c.c +++ b/drivers/mfd/tps6594-i2c.c @@ -1,6 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* - * I2C access driver for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs + * I2C access driver for the following TI PMICs: + * - LP8764 + * - TPS65224 + * - TPS652G1 + * - TPS6593 + * - TPS6594 * * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ */ @@ -197,6 +202,7 @@ static const struct of_device_id tps6594_i2c_of_match_table[] = { { .compatible = "ti,tps6593-q1", .data = (void *)TPS6593, }, { .compatible = "ti,lp8764-q1", .data = (void *)LP8764, }, { .compatible = "ti,tps65224-q1", .data = (void *)TPS65224, }, + { .compatible = "ti,tps652g1", .data = (void *)TPS652G1, }, {} }; MODULE_DEVICE_TABLE(of, tps6594_i2c_of_match_table); @@ -222,7 +228,7 @@ static int tps6594_i2c_probe(struct i2c_client *client) return dev_err_probe(dev, -EINVAL, "Failed to find matching chip ID\n"); tps->chip_id = (unsigned long)match->data; - if (tps->chip_id == TPS65224) + if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) tps6594_i2c_regmap_config.volatile_table = &tps65224_volatile_table; tps->regmap = devm_regmap_init(dev, NULL, client, &tps6594_i2c_regmap_config); diff --git a/drivers/mfd/tps6594-spi.c b/drivers/mfd/tps6594-spi.c index 6ebccb79f0cc..944b7313a1d9 100644 --- a/drivers/mfd/tps6594-spi.c +++ b/drivers/mfd/tps6594-spi.c @@ -1,6 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* - * SPI access driver for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs + * SPI access driver for the following TI PMICs: + * - LP8764 + * - TPS65224 + * - TPS652G1 + * - TPS6593 + * - TPS6594 * * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ */ @@ -82,6 +87,7 @@ static const struct of_device_id tps6594_spi_of_match_table[] = { { .compatible = "ti,tps6593-q1", .data = (void *)TPS6593, }, { .compatible = "ti,lp8764-q1", .data = (void *)LP8764, }, { .compatible = "ti,tps65224-q1", .data = (void *)TPS65224, }, + { .compatible = "ti,tps652g1", .data = (void *)TPS652G1, }, {} }; MODULE_DEVICE_TABLE(of, tps6594_spi_of_match_table); @@ -107,7 +113,7 @@ static int tps6594_spi_probe(struct spi_device *spi) return dev_err_probe(dev, -EINVAL, "Failed to find matching chip ID\n"); tps->chip_id = (unsigned long)match->data; - if (tps->chip_id == TPS65224) + if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) tps6594_spi_regmap_config.volatile_table = &tps65224_volatile_table; tps->regmap = devm_regmap_init(dev, NULL, spi, &tps6594_spi_regmap_config); diff --git a/drivers/misc/tps6594-pfsm.c b/drivers/misc/tps6594-pfsm.c index 6db1c9d48f8f..44fa81d6cec2 100644 --- a/drivers/misc/tps6594-pfsm.c +++ b/drivers/misc/tps6594-pfsm.c @@ -1,6 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 /* - * PFSM (Pre-configurable Finite State Machine) driver for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs + * PFSM (Pre-configurable Finite State Machine) driver for the following + * PMICs: + * - LP8764 + * - TPS65224 + * - TPS652G1 + * - TPS6594 + * - TPS6593 * * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ */ @@ -141,7 +147,7 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a switch (cmd) { case PMIC_GOTO_STANDBY: /* Disable LP mode on TPS6594 Family PMIC */ - if (pfsm->chip_id != TPS65224) { + if (pfsm->chip_id != TPS65224 && pfsm->chip_id != TPS652G1) { ret = regmap_clear_bits(pfsm->regmap, TPS6594_REG_RTC_CTRL_2, TPS6594_BIT_LP_STANDBY_SEL); @@ -154,8 +160,8 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a TPS6594_BIT_TRIGGER_I2C(0), TPS6594_BIT_TRIGGER_I2C(0)); break; case PMIC_GOTO_LP_STANDBY: - /* TPS65224 does not support LP STANDBY */ - if (pfsm->chip_id == TPS65224) + /* TPS65224/TPS652G1 does not support LP STANDBY */ + if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1) return ret; /* Enable LP mode */ @@ -179,8 +185,8 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a TPS6594_BIT_NSLEEP1B | TPS6594_BIT_NSLEEP2B); break; case PMIC_SET_MCU_ONLY_STATE: - /* TPS65224 does not support MCU_ONLY_STATE */ - if (pfsm->chip_id == TPS65224) + /* TPS65224/TPS652G1 does not support MCU_ONLY_STATE */ + if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1) return ret; if (copy_from_user(&state_opt, argp, sizeof(state_opt))) @@ -206,7 +212,7 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a return -EFAULT; /* Configure wake-up destination */ - if (pfsm->chip_id == TPS65224) { + if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1) { regmap_reg = TPS65224_REG_STARTUP_CTRL; mask = TPS65224_MASK_STARTUP_DEST; } else { @@ -230,9 +236,14 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a return ret; /* Modify NSLEEP1-2 bits */ - ret = regmap_clear_bits(pfsm->regmap, TPS6594_REG_FSM_NSLEEP_TRIGGERS, - pfsm->chip_id == TPS65224 ? - TPS6594_BIT_NSLEEP1B : TPS6594_BIT_NSLEEP2B); + if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1) + ret = regmap_clear_bits(pfsm->regmap, + TPS6594_REG_FSM_NSLEEP_TRIGGERS, + TPS6594_BIT_NSLEEP1B); + else + ret = regmap_clear_bits(pfsm->regmap, + TPS6594_REG_FSM_NSLEEP_TRIGGERS, + TPS6594_BIT_NSLEEP2B); break; } diff --git a/drivers/pinctrl/pinctrl-tps6594.c b/drivers/pinctrl/pinctrl-tps6594.c index 54cc810f79d6..6726853110d1 100644 --- a/drivers/pinctrl/pinctrl-tps6594.c +++ b/drivers/pinctrl/pinctrl-tps6594.c @@ -226,6 +226,10 @@ static const char *const tps65224_nerr_mcu_func_group_names[] = { "GPIO5", }; +static const char *const tps652g1_cs_spi_func_group_names[] = { + "GPIO1", +}; + struct tps6594_pinctrl_function { struct pinfunction pinfunction; u8 muxval; @@ -287,6 +291,18 @@ static const struct tps6594_pinctrl_function tps65224_pinctrl_functions[] = { FUNCTION(tps65224, nerr_mcu, TPS65224_PINCTRL_NERR_MCU_FUNCTION), }; +static const struct tps6594_pinctrl_function tps652g1_pinctrl_functions[] = { + FUNCTION(tps65224, gpio, TPS6594_PINCTRL_GPIO_FUNCTION), + FUNCTION(tps65224, sda_i2c2_sdo_spi, TPS65224_PINCTRL_SDA_I2C2_SDO_SPI_FUNCTION), + FUNCTION(tps65224, nsleep2, TPS65224_PINCTRL_NSLEEP2_FUNCTION), + FUNCTION(tps65224, nint, TPS65224_PINCTRL_NINT_FUNCTION), + FUNCTION(tps652g1, cs_spi, TPS65224_PINCTRL_SCL_I2C2_CS_SPI_FUNCTION), + FUNCTION(tps65224, nsleep1, TPS65224_PINCTRL_NSLEEP1_FUNCTION), + FUNCTION(tps65224, pb, TPS65224_PINCTRL_PB_FUNCTION), + FUNCTION(tps65224, wkup, TPS65224_PINCTRL_WKUP_FUNCTION), + FUNCTION(tps65224, syncclkin, TPS65224_PINCTRL_SYNCCLKIN_FUNCTION), +}; + struct tps6594_pinctrl { struct tps6594 *tps; struct gpio_regmap *gpio_regmap; @@ -300,6 +316,16 @@ struct tps6594_pinctrl { struct muxval_remap *remap; }; +static struct tps6594_pinctrl tps652g1_template_pinctrl = { + .funcs = tps652g1_pinctrl_functions, + .func_cnt = ARRAY_SIZE(tps652g1_pinctrl_functions), + .pins = tps65224_pins, + .num_pins = ARRAY_SIZE(tps65224_pins), + .mux_sel_mask = TPS65224_MASK_GPIO_SEL, + .remap = tps65224_muxval_remap, + .remap_cnt = ARRAY_SIZE(tps65224_muxval_remap), +}; + static struct tps6594_pinctrl tps65224_template_pinctrl = { .funcs = tps65224_pinctrl_functions, .func_cnt = ARRAY_SIZE(tps65224_pinctrl_functions), @@ -475,6 +501,15 @@ static int tps6594_pinctrl_probe(struct platform_device *pdev) return -ENOMEM; switch (tps->chip_id) { + case TPS652G1: + pctrl_desc->pins = tps65224_pins; + pctrl_desc->npins = ARRAY_SIZE(tps65224_pins); + + *pinctrl = tps652g1_template_pinctrl; + + config.ngpio = ARRAY_SIZE(tps65224_gpio_func_group_names); + config.ngpio_per_reg = TPS65224_NGPIO_PER_REG; + break; case TPS65224: pctrl_desc->pins = tps65224_pins; pctrl_desc->npins = ARRAY_SIZE(tps65224_pins); diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 6d8988387da4..eaa6df1c9f80 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1153,6 +1153,17 @@ config REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY touchscreen unit. The regulator is used to enable power to the TC358762, display and to control backlight. +config REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2 + tristate "Raspberry Pi 7-inch touchscreen panel V2 regulator" + depends on GPIOLIB + depends on I2C && OF + select GPIO_REGMAP + select REGMAP_I2C + help + This driver supports regulator on the V2 Raspberry Pi touchscreen + unit. The regulator is used to enable power to the display and to + control backlight PWM. + config REGULATOR_RC5T583 tristate "RICOH RC5T583 Power regulators" depends on MFD_RC5T583 @@ -1192,7 +1203,7 @@ config REGULATOR_RT4801 The device supports two regulators (DSVP/DSVN). config REGULATOR_RT4803 - tristate "Richtek RT4803 boost regualtor" + tristate "Richtek RT4803 boost regulator" depends on I2C select REGMAP_I2C help diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index c0bc7a0f4e67..be98b29d6675 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -136,6 +136,7 @@ obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o obj-$(CONFIG_REGULATOR_RAA215300) += raa215300.o obj-$(CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY) += rpi-panel-attiny-regulator.o +obj-$(CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2) += rpi-panel-v2-regulator.o obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o obj-$(CONFIG_REGULATOR_RK808) += rk808-regulator.o obj-$(CONFIG_REGULATOR_RN5T618) += rn5t618-regulator.o diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index 1bb048de3ecd..e803cc59d68a 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -134,9 +134,19 @@ static void voltage_change_done(struct regulator_dev *rdev, unsigned int sel, if (*mask) { /* - * Let's allow scheduling as we use I2C anyways. We just need to - * guarantee minimum of 1ms sleep - it shouldn't matter if we - * exceed it due to the scheduling. + * We had fault detection disabled for the duration of the + * voltage change. + * + * According to HW colleagues the maximum time it takes is + * 1000us. I assume that on systems with light load this + * might be less - and we could probably use DT to give + * system specific delay value if performance matters. + * + * Well, knowing we use I2C here and can add scheduling delays + * I don't think it is worth the hassle and I just add fixed + * 1ms sleep here (and allow scheduling). If this turns out to + * be a problem we can change it to delay and make the delay + * time configurable. */ msleep(1); @@ -173,16 +183,7 @@ static int voltage_change_prepare(struct regulator_dev *rdev, unsigned int sel, /* * If we increase LDO voltage when LDO is enabled we need to * disable the power-good detection until voltage has reached - * the new level. According to HW colleagues the maximum time - * it takes is 1000us. I assume that on systems with light load - * this might be less - and we could probably use DT to give - * system specific delay value if performance matters. - * - * Well, knowing we use I2C here and can add scheduling delays - * I don't think it is worth the hassle and I just add fixed - * 1ms sleep here (and allow scheduling). If this turns out to - * be a problem we can change it to delay and make the delay - * time configurable. + * the new level. */ if (new > now) { int tmp; diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index cbd6d53ebfb5..8ed9b96518cf 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3797,6 +3797,16 @@ static int _regulator_do_set_suspend_voltage(struct regulator_dev *rdev, return 0; } +static int regulator_get_voltage_delta(struct regulator_dev *rdev, int uV) +{ + int current_uV = regulator_get_voltage_rdev(rdev); + + if (current_uV < 0) + return current_uV; + + return abs(current_uV - uV); +} + static int regulator_set_voltage_unlocked(struct regulator *regulator, int min_uV, int max_uV, suspend_state_t state) @@ -3804,8 +3814,8 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, struct regulator_dev *rdev = regulator->rdev; struct regulator_voltage *voltage = ®ulator->voltage[state]; int ret = 0; + int current_uV, delta, new_delta; int old_min_uV, old_max_uV; - int current_uV; /* If we're setting the same range as last time the change * should be a noop (some cpufreq implementations use the same @@ -3852,6 +3862,37 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, voltage->max_uV = old_max_uV; } + if (rdev->constraints->max_uV_step > 0) { + /* For regulators with a maximum voltage step, reaching the desired + * voltage might take a few retries. + */ + ret = regulator_get_voltage_delta(rdev, min_uV); + if (ret < 0) + goto out; + + delta = ret; + + while (delta > 0) { + ret = regulator_balance_voltage(rdev, state); + if (ret < 0) + goto out; + + ret = regulator_get_voltage_delta(rdev, min_uV); + if (ret < 0) + goto out; + + new_delta = ret; + + /* check that voltage is converging quickly enough */ + if (new_delta - delta > rdev->constraints->max_uV_step) { + ret = -EWOULDBLOCK; + goto out; + } + + delta = new_delta; + } + } + out: return ret; } diff --git a/drivers/regulator/mt6370-regulator.c b/drivers/regulator/mt6370-regulator.c index 27cb32b726e0..c2cea904b0ca 100644 --- a/drivers/regulator/mt6370-regulator.c +++ b/drivers/regulator/mt6370-regulator.c @@ -320,7 +320,7 @@ static int mt6370_regulator_irq_register(struct mt6370_priv *priv) return 0; } -static int mt6370_regualtor_register(struct mt6370_priv *priv) +static int mt6370_regulator_register(struct mt6370_priv *priv) { struct regulator_dev *rdev; struct regulator_config cfg = {}; @@ -363,7 +363,7 @@ static int mt6370_regulator_probe(struct platform_device *pdev) return -ENODEV; } - ret = mt6370_regualtor_register(priv); + ret = mt6370_regulator_register(priv); if (ret) return ret; diff --git a/drivers/regulator/mtk-dvfsrc-regulator.c b/drivers/regulator/mtk-dvfsrc-regulator.c index f5662c569464..c0c9a6751c26 100644 --- a/drivers/regulator/mtk-dvfsrc-regulator.c +++ b/drivers/regulator/mtk-dvfsrc-regulator.c @@ -117,6 +117,24 @@ static const struct dvfsrc_regulator_pdata mt6873_data = { .size = ARRAY_SIZE(mt6873_regulators), }; +static const unsigned int mt6893_voltages[] = { + 575000, + 600000, + 650000, + 725000, + 750000, +}; + +static const struct regulator_desc mt6893_regulators[] = { + MTK_DVFSRC_VREG("dvfsrc-vcore", VCORE, mt6893_voltages), + MTK_DVFSRC_VREG("dvfsrc-vscp", VSCP, mt6893_voltages), +}; + +static const struct dvfsrc_regulator_pdata mt6893_data = { + .descs = mt6893_regulators, + .size = ARRAY_SIZE(mt6893_regulators), +}; + static const unsigned int mt8183_voltages[] = { 725000, 800000, @@ -148,6 +166,24 @@ static const struct dvfsrc_regulator_pdata mt8195_data = { .size = ARRAY_SIZE(mt8195_regulators), }; +static const unsigned int mt8196_voltages[] = { + 575000, + 600000, + 650000, + 725000, + 825000, + 875000, +}; + +static const struct regulator_desc mt8196_regulators[] = { + MTK_DVFSRC_VREG("dvfsrc-vcore", VCORE, mt8196_voltages), +}; + +static const struct dvfsrc_regulator_pdata mt8196_data = { + .descs = mt8196_regulators, + .size = ARRAY_SIZE(mt8196_regulators), +}; + static int dvfsrc_vcore_regulator_probe(struct platform_device *pdev) { struct regulator_config config = { .dev = &pdev->dev }; @@ -173,9 +209,11 @@ static int dvfsrc_vcore_regulator_probe(struct platform_device *pdev) static const struct of_device_id mtk_dvfsrc_regulator_match[] = { { .compatible = "mediatek,mt6873-dvfsrc-regulator", .data = &mt6873_data }, + { .compatible = "mediatek,mt6893-dvfsrc-regulator", .data = &mt6893_data }, { .compatible = "mediatek,mt8183-dvfsrc-regulator", .data = &mt8183_data }, { .compatible = "mediatek,mt8192-dvfsrc-regulator", .data = &mt6873_data }, { .compatible = "mediatek,mt8195-dvfsrc-regulator", .data = &mt8195_data }, + { .compatible = "mediatek,mt8196-dvfsrc-regulator", .data = &mt8196_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mtk_dvfsrc_regulator_match); diff --git a/drivers/regulator/pca9450-regulator.c b/drivers/regulator/pca9450-regulator.c index 14d19a6d6655..feadb21a8f30 100644 --- a/drivers/regulator/pca9450-regulator.c +++ b/drivers/regulator/pca9450-regulator.c @@ -17,12 +17,18 @@ #include <linux/regulator/machine.h> #include <linux/regulator/of_regulator.h> #include <linux/regulator/pca9450.h> +#include <dt-bindings/regulator/nxp,pca9450-regulator.h> + +static unsigned int pca9450_buck_get_mode(struct regulator_dev *rdev); +static int pca9450_buck_set_mode(struct regulator_dev *rdev, unsigned int mode); struct pc9450_dvs_config { unsigned int run_reg; /* dvs0 */ unsigned int run_mask; unsigned int standby_reg; /* dvs1 */ unsigned int standby_mask; + unsigned int mode_reg; /* ctrl */ + unsigned int mode_mask; }; struct pca9450_regulator_desc { @@ -80,6 +86,8 @@ static const struct regulator_ops pca9450_dvs_buck_regulator_ops = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, .set_ramp_delay = regulator_set_ramp_delay_regmap, + .set_mode = pca9450_buck_set_mode, + .get_mode = pca9450_buck_get_mode, }; static const struct regulator_ops pca9450_buck_regulator_ops = { @@ -90,6 +98,8 @@ static const struct regulator_ops pca9450_buck_regulator_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_mode = pca9450_buck_set_mode, + .get_mode = pca9450_buck_get_mode, }; static const struct regulator_ops pca9450_ldo_regulator_ops = { @@ -285,7 +295,64 @@ static int pca9450_set_dvs_levels(struct device_node *np, return ret; } -static const struct pca9450_regulator_desc pca9450a_regulators[] = { +static inline unsigned int pca9450_map_mode(unsigned int mode) +{ + switch (mode) { + case PCA9450_BUCK_MODE_AUTO: + return REGULATOR_MODE_NORMAL; + case PCA9450_BUCK_MODE_FORCE_PWM: + return REGULATOR_MODE_FAST; + default: + return REGULATOR_MODE_INVALID; + } +} + +static int pca9450_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct pca9450_regulator_desc *desc = container_of(rdev->desc, + struct pca9450_regulator_desc, desc); + const struct pc9450_dvs_config *dvs = &desc->dvs; + int val; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = dvs->mode_mask; + break; + case REGULATOR_MODE_NORMAL: + val = 0; + break; + default: + return -EINVAL; + } + + dev_dbg(&rdev->dev, "pca9450 buck set_mode %#x, %#x, %#x\n", + dvs->mode_reg, dvs->mode_mask, val); + + return regmap_update_bits(rdev->regmap, dvs->mode_reg, + dvs->mode_mask, val); +} + +static unsigned int pca9450_buck_get_mode(struct regulator_dev *rdev) +{ + struct pca9450_regulator_desc *desc = container_of(rdev->desc, + struct pca9450_regulator_desc, desc); + const struct pc9450_dvs_config *dvs = &desc->dvs; + int ret = 0, regval; + + ret = regmap_read(rdev->regmap, dvs->mode_reg, ®val); + if (ret != 0) { + dev_err(&rdev->dev, + "Failed to get pca9450 buck mode: %d\n", ret); + return ret; + } + + if ((regval & dvs->mode_mask) == dvs->mode_mask) + return REGULATOR_MODE_FAST; + + return REGULATOR_MODE_NORMAL; +} + +static struct pca9450_regulator_desc pca9450a_regulators[] = { { .desc = { .name = "buck1", @@ -308,12 +375,15 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK1OUT_DVS0, .run_mask = BUCK1OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK1OUT_DVS1, .standby_mask = BUCK1OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK1CTRL, + .mode_mask = BUCK1_FPWM, }, }, { @@ -338,12 +408,15 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK2OUT_DVS0, .run_mask = BUCK2OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK2OUT_DVS1, .standby_mask = BUCK2OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK2CTRL, + .mode_mask = BUCK2_FPWM, }, }, { @@ -368,12 +441,15 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK3OUT_DVS0, .run_mask = BUCK3OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK3OUT_DVS1, .standby_mask = BUCK3OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK3CTRL, + .mode_mask = BUCK3_FPWM, }, }, { @@ -393,6 +469,11 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .enable_mask = BUCK4_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK4CTRL, + .mode_mask = BUCK4_FPWM, }, }, { @@ -412,6 +493,11 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .enable_mask = BUCK5_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK5CTRL, + .mode_mask = BUCK5_FPWM, }, }, { @@ -431,6 +517,11 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .enable_mask = BUCK6_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK6CTRL, + .mode_mask = BUCK6_FPWM, }, }, { @@ -529,7 +620,7 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { * Buck3 removed on PCA9450B and connected with Buck1 internal for dual phase * on PCA9450C as no Buck3. */ -static const struct pca9450_regulator_desc pca9450bc_regulators[] = { +static struct pca9450_regulator_desc pca9450bc_regulators[] = { { .desc = { .name = "buck1", @@ -552,12 +643,15 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK1OUT_DVS0, .run_mask = BUCK1OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK1OUT_DVS1, .standby_mask = BUCK1OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK1CTRL, + .mode_mask = BUCK1_FPWM, }, }, { @@ -582,12 +676,15 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK2OUT_DVS0, .run_mask = BUCK2OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK2OUT_DVS1, .standby_mask = BUCK2OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK2CTRL, + .mode_mask = BUCK2_FPWM, }, }, { @@ -607,6 +704,11 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { .enable_mask = BUCK4_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK4CTRL, + .mode_mask = BUCK4_FPWM, }, }, { @@ -626,6 +728,11 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { .enable_mask = BUCK5_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK5CTRL, + .mode_mask = BUCK5_FPWM, }, }, { @@ -645,6 +752,11 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { .enable_mask = BUCK6_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK6CTRL, + .mode_mask = BUCK6_FPWM, }, }, { @@ -739,7 +851,7 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { }, }; -static const struct pca9450_regulator_desc pca9451a_regulators[] = { +static struct pca9450_regulator_desc pca9451a_regulators[] = { { .desc = { .name = "buck1", @@ -761,12 +873,15 @@ static const struct pca9450_regulator_desc pca9451a_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK1OUT_DVS0, .run_mask = BUCK1OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK1OUT_DVS1, .standby_mask = BUCK1OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK1CTRL, + .mode_mask = BUCK1_FPWM, }, }, { @@ -790,12 +905,15 @@ static const struct pca9450_regulator_desc pca9451a_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK2OUT_DVS0, .run_mask = BUCK2OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK2OUT_DVS1, .standby_mask = BUCK2OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK2CTRL, + .mode_mask = BUCK2_FPWM, }, }, { @@ -815,6 +933,11 @@ static const struct pca9450_regulator_desc pca9451a_regulators[] = { .enable_mask = BUCK4_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK4CTRL, + .mode_mask = BUCK4_FPWM, }, }, { @@ -834,6 +957,11 @@ static const struct pca9450_regulator_desc pca9451a_regulators[] = { .enable_mask = BUCK5_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK5CTRL, + .mode_mask = BUCK5_FPWM, }, }, { @@ -853,6 +981,11 @@ static const struct pca9450_regulator_desc pca9451a_regulators[] = { .enable_mask = BUCK6_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK6CTRL, + .mode_mask = BUCK6_FPWM, }, }, { @@ -990,7 +1123,7 @@ static int pca9450_i2c_probe(struct i2c_client *i2c) { enum pca9450_chip_type type = (unsigned int)(uintptr_t) of_device_get_match_data(&i2c->dev); - const struct pca9450_regulator_desc *regulator_desc; + const struct pca9450_regulator_desc *regulator_desc; struct regulator_config config = { }; struct regulator_dev *ldo5; struct pca9450 *pca9450; diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 7870722b6ee2..109f0aae09b1 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -1462,6 +1462,40 @@ static const struct rpmh_vreg_init_data pm7325_vreg_data[] = { {} }; +static const struct rpmh_vreg_init_data pm7550_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525, "vdd-s6"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2-l3"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l2-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo515, "vdd-l4-l5"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo515, "vdd-l4-l5"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo515, "vdd-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo515, "vdd-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo515, "vdd-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo515, "vdd-l9-l10"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo515, "vdd-l9-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_nldo515, "vdd-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo515_mv, "vdd-l12-l14"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo515_mv, "vdd-l13-l16"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_pldo, "vdd-l12-l14"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l13-l16"), + RPMH_VREG("ldo17", "ldo%s17", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo18", "ldo%s18", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo19", "ldo%s19", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo20", "ldo%s20", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo21", "ldo%s21", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo22", "ldo%s22", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo23", "ldo%s23", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("bob", "bob%s1", &pmic5_bob, "vdd-bob"), + {} +}; + static const struct rpmh_vreg_init_data pmr735a_vreg_data[] = { RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps520, "vdd-s1"), RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps520, "vdd-s2"), @@ -1476,6 +1510,22 @@ static const struct rpmh_vreg_init_data pmr735a_vreg_data[] = { {} }; +static const struct rpmh_vreg_init_data pmr735b_vreg_data[] = { + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo_lv, "vdd-l4"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l5"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo, "vdd-l7-l8"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l7-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo_lv, "vdd-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_nldo, "vdd-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo, "vdd-l12"), + {} +}; + static const struct rpmh_vreg_init_data pm660_vreg_data[] = { RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"), RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"), @@ -1664,10 +1714,18 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .data = pm7325_vreg_data, }, { + .compatible = "qcom,pm7550-rpmh-regulators", + .data = pm7550_vreg_data, + }, + { .compatible = "qcom,pmr735a-rpmh-regulators", .data = pmr735a_vreg_data, }, { + .compatible = "qcom,pmr735b-rpmh-regulators", + .data = pmr735b_vreg_data, + }, + { .compatible = "qcom,pm660-rpmh-regulators", .data = pm660_vreg_data, }, diff --git a/drivers/regulator/rpi-panel-v2-regulator.c b/drivers/regulator/rpi-panel-v2-regulator.c new file mode 100644 index 000000000000..30b78aa75ee3 --- /dev/null +++ b/drivers/regulator/rpi-panel-v2-regulator.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Raspberry Pi Ltd. + * Copyright (C) 2025 Marek Vasut + */ + +#include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/gpio/regmap.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pwm.h> +#include <linux/regmap.h> + +/* I2C registers of the microcontroller. */ +#define REG_ID 0x01 +#define REG_POWERON 0x02 +#define REG_PWM 0x03 + +/* Bits for poweron register */ +#define LCD_RESET_BIT BIT(0) +#define CTP_RESET_BIT BIT(1) + +/* Bits for the PWM register */ +#define PWM_BL_ENABLE BIT(7) +#define PWM_BL_MASK GENMASK(4, 0) + +/* Treat LCD_RESET and CTP_RESET as GPIOs */ +#define NUM_GPIO 2 + +static const struct regmap_config rpi_panel_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_PWM, + .can_sleep = true, +}; + +static int rpi_panel_v2_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct regmap *regmap = pwmchip_get_drvdata(chip); + unsigned int duty; + + if (state->polarity != PWM_POLARITY_NORMAL) + return -EINVAL; + + if (!state->enabled) + return regmap_write(regmap, REG_PWM, 0); + + duty = pwm_get_relative_duty_cycle(state, PWM_BL_MASK); + return regmap_write(regmap, REG_PWM, duty | PWM_BL_ENABLE); +} + +static const struct pwm_ops rpi_panel_v2_pwm_ops = { + .apply = rpi_panel_v2_pwm_apply, +}; + +/* + * I2C driver interface functions + */ +static int rpi_panel_v2_i2c_probe(struct i2c_client *i2c) +{ + struct gpio_regmap_config gconfig = { + .ngpio = NUM_GPIO, + .ngpio_per_reg = NUM_GPIO, + .parent = &i2c->dev, + .reg_set_base = REG_POWERON, + }; + struct regmap *regmap; + struct pwm_chip *pc; + int ret; + + pc = devm_pwmchip_alloc(&i2c->dev, 1, 0); + if (IS_ERR(pc)) + return PTR_ERR(pc); + + pc->ops = &rpi_panel_v2_pwm_ops; + + regmap = devm_regmap_init_i2c(i2c, &rpi_panel_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(&i2c->dev, PTR_ERR(regmap), "Failed to allocate regmap\n"); + + pwmchip_set_drvdata(pc, regmap); + + regmap_write(regmap, REG_POWERON, 0); + + gconfig.regmap = regmap; + ret = PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&i2c->dev, &gconfig)); + if (ret) + return dev_err_probe(&i2c->dev, ret, "Failed to create gpiochip\n"); + + i2c_set_clientdata(i2c, regmap); + + return devm_pwmchip_add(&i2c->dev, pc); +} + +static void rpi_panel_v2_i2c_shutdown(struct i2c_client *client) +{ + struct regmap *regmap = i2c_get_clientdata(client); + + regmap_write(regmap, REG_PWM, 0); + regmap_write(regmap, REG_POWERON, 0); +} + +static const struct of_device_id rpi_panel_v2_dt_ids[] = { + { .compatible = "raspberrypi,touchscreen-panel-regulator-v2" }, + { }, +}; +MODULE_DEVICE_TABLE(of, rpi_panel_v2_dt_ids); + +static struct i2c_driver rpi_panel_v2_regulator_driver = { + .driver = { + .name = "rpi_touchscreen_v2", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = rpi_panel_v2_dt_ids, + }, + .probe = rpi_panel_v2_i2c_probe, + .shutdown = rpi_panel_v2_i2c_shutdown, +}; + +module_i2c_driver(rpi_panel_v2_regulator_driver); + +MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com>"); +MODULE_DESCRIPTION("Regulator device driver for Raspberry Pi 7-inch V2 touchscreen"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/rt5739.c b/drivers/regulator/rt5739.c index 91412c905ce6..5fcddd7c2da7 100644 --- a/drivers/regulator/rt5739.c +++ b/drivers/regulator/rt5739.c @@ -24,6 +24,8 @@ #define RT5739_REG_NSEL1 0x01 #define RT5739_REG_CNTL1 0x02 #define RT5739_REG_ID1 0x03 +#define RT5739_REG_ID2 0x04 +#define RT5739_REG_MON 0x05 #define RT5739_REG_CNTL2 0x06 #define RT5739_REG_CNTL4 0x08 @@ -236,11 +238,18 @@ static void rt5739_init_regulator_desc(struct regulator_desc *desc, } } +static bool rt5739_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == RT5739_REG_MON; +} + static const struct regmap_config rt5739_regmap_config = { .name = "rt5739", .reg_bits = 8, .val_bits = 8, .max_register = RT5739_REG_CNTL4, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = rt5739_volatile_reg, }; static int rt5739_probe(struct i2c_client *i2c) diff --git a/drivers/regulator/rt6160-regulator.c b/drivers/regulator/rt6160-regulator.c index e2a0eee95c61..548ffdf537d3 100644 --- a/drivers/regulator/rt6160-regulator.c +++ b/drivers/regulator/rt6160-regulator.c @@ -31,8 +31,11 @@ #define RT6160_PGSTAT_MASK BIT(0) #define RT6160_VENDOR_ID 0xA0 +#define RT6166_VENDOR_ID 0xB0 #define RT6160_VOUT_MINUV 2025000 #define RT6160_VOUT_MAXUV 5200000 +#define RT6166_VOUT_MINUV 1800000 +#define RT6166_VOUD_MAXUV 4950000 #define RT6160_VOUT_STPUV 25000 #define RT6160_N_VOUTS ((RT6160_VOUT_MAXUV - RT6160_VOUT_MINUV) / RT6160_VOUT_STPUV + 1) @@ -43,6 +46,7 @@ struct rt6160_priv { struct gpio_desc *enable_gpio; struct regmap *regmap; bool enable_state; + uint8_t devid; }; static const unsigned int rt6160_ramp_tables[] = { @@ -260,15 +264,26 @@ static int rt6160_probe(struct i2c_client *i2c) if (ret) return ret; - if ((devid & RT6160_VID_MASK) != RT6160_VENDOR_ID) { + devid = devid & RT6160_VID_MASK; + + switch (devid) { + case RT6166_VENDOR_ID: + case RT6160_VENDOR_ID: + break; + default: dev_err(&i2c->dev, "VID not correct [0x%02x]\n", devid); return -ENODEV; } + priv->devid = devid; + priv->desc.name = "rt6160-buckboost"; priv->desc.type = REGULATOR_VOLTAGE; priv->desc.owner = THIS_MODULE; - priv->desc.min_uV = RT6160_VOUT_MINUV; + if (priv->devid == RT6166_VENDOR_ID) + priv->desc.min_uV = RT6166_VOUT_MINUV; + else + priv->desc.min_uV = RT6160_VOUT_MINUV; priv->desc.uV_step = RT6160_VOUT_STPUV; if (vsel_active_low) priv->desc.vsel_reg = RT6160_REG_VSELL; diff --git a/drivers/regulator/stm32-vrefbuf.c b/drivers/regulator/stm32-vrefbuf.c index a85ea94f0673..9e391206f09d 100644 --- a/drivers/regulator/stm32-vrefbuf.c +++ b/drivers/regulator/stm32-vrefbuf.c @@ -67,7 +67,6 @@ static int stm32_vrefbuf_enable(struct regulator_dev *rdev) writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); } - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return ret; @@ -87,7 +86,6 @@ static int stm32_vrefbuf_disable(struct regulator_dev *rdev) val &= ~STM32_ENVR; writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return 0; @@ -104,7 +102,6 @@ static int stm32_vrefbuf_is_enabled(struct regulator_dev *rdev) ret = readl_relaxed(priv->base + STM32_VREFBUF_CSR) & STM32_ENVR; - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return ret; @@ -125,7 +122,6 @@ static int stm32_vrefbuf_set_voltage_sel(struct regulator_dev *rdev, val = (val & ~STM32_VRS) | FIELD_PREP(STM32_VRS, sel); writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return 0; @@ -144,7 +140,6 @@ static int stm32_vrefbuf_get_voltage_sel(struct regulator_dev *rdev) val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); ret = FIELD_GET(STM32_VRS, val); - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return ret; @@ -218,7 +213,6 @@ static int stm32_vrefbuf_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, rdev); - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; diff --git a/drivers/regulator/sy8827n.c b/drivers/regulator/sy8827n.c index f11ff38b36c9..0b811514782f 100644 --- a/drivers/regulator/sy8827n.c +++ b/drivers/regulator/sy8827n.c @@ -140,7 +140,8 @@ static int sy8827n_i2c_probe(struct i2c_client *client) return -EINVAL; } - di->en_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); + di->en_gpio = devm_gpiod_get_optional(dev, "enable", + GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE); if (IS_ERR(di->en_gpio)) return PTR_ERR(di->en_gpio); diff --git a/drivers/regulator/tps6286x-regulator.c b/drivers/regulator/tps6286x-regulator.c index 75f441f36de7..e29aab06bf79 100644 --- a/drivers/regulator/tps6286x-regulator.c +++ b/drivers/regulator/tps6286x-regulator.c @@ -19,13 +19,22 @@ #define TPS6286X_CONTROL_FPWM BIT(4) #define TPS6286X_CONTROL_SWEN BIT(5) +#define TPS6286X_STATUS 0x05 + #define TPS6286X_MIN_MV 400 #define TPS6286X_MAX_MV 1675 #define TPS6286X_STEP_MV 5 +static bool tps6286x_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == TPS6286X_STATUS; +} + static const struct regmap_config tps6286x_regmap_config = { .reg_bits = 8, .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = tps6286x_volatile_reg, }; static int tps6286x_set_mode(struct regulator_dev *rdev, unsigned int mode) diff --git a/drivers/regulator/tps6287x-regulator.c b/drivers/regulator/tps6287x-regulator.c index c0f5f0a186a3..7b7d3ae39206 100644 --- a/drivers/regulator/tps6287x-regulator.c +++ b/drivers/regulator/tps6287x-regulator.c @@ -27,10 +27,17 @@ #define TPS6287X_CTRL3 0x03 #define TPS6287X_STATUS 0x04 +static bool tps6287x_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == TPS6287X_STATUS; +} + static const struct regmap_config tps6287x_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = TPS6287X_STATUS, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = tps6287x_volatile_reg, }; static const struct linear_range tps6287x_voltage_ranges[] = { diff --git a/drivers/regulator/tps6594-regulator.c b/drivers/regulator/tps6594-regulator.c index ac53792e3fed..ab882daec7c5 100644 --- a/drivers/regulator/tps6594-regulator.c +++ b/drivers/regulator/tps6594-regulator.c @@ -21,10 +21,6 @@ #define BUCK_NB 5 #define LDO_NB 4 #define MULTI_PHASE_NB 4 -/* TPS6593 and LP8764 supports OV, UV, SC, ILIM */ -#define REGS_INT_NB 4 -/* TPS65224 supports OV or UV */ -#define TPS65224_REGS_INT_NB 1 enum tps6594_regulator_id { /* DCDC's */ @@ -56,7 +52,7 @@ struct tps6594_regulator_irq_type { unsigned long event; }; -static struct tps6594_regulator_irq_type tps6594_ext_regulator_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_ext_regulator_irq_types[] = { { TPS6594_IRQ_NAME_VCCA_OV, "VCCA", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_VCCA_UV, "VCCA", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_VMON1_OV, "VMON1", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, @@ -69,7 +65,7 @@ static struct tps6594_regulator_irq_type tps6594_ext_regulator_irq_types[] = { REGULATOR_EVENT_OVER_VOLTAGE_WARN }, }; -static struct tps6594_regulator_irq_type tps65224_ext_regulator_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_ext_regulator_irq_types[] = { { TPS65224_IRQ_NAME_VCCA_UVOV, "VCCA", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, { TPS65224_IRQ_NAME_VMON1_UVOV, "VMON1", "voltage out of range", @@ -80,13 +76,13 @@ static struct tps6594_regulator_irq_type tps65224_ext_regulator_irq_types[] = { struct tps6594_regulator_irq_data { struct device *dev; - struct tps6594_regulator_irq_type *type; + const struct tps6594_regulator_irq_type *type; struct regulator_dev *rdev; }; struct tps6594_ext_regulator_irq_data { struct device *dev; - struct tps6594_regulator_irq_type *type; + const struct tps6594_regulator_irq_type *type; }; #define TPS6594_REGULATOR(_name, _of, _id, _type, _ops, _n, _vr, _vm, _er, \ @@ -192,7 +188,7 @@ static const struct regulator_ops tps6594_ldos_4_ops = { .map_voltage = regulator_map_voltage_linear_range, }; -static const struct regulator_desc buck_regs[] = { +static const struct regulator_desc tps6594_buck_regs[] = { TPS6594_REGULATOR("BUCK1", "buck1", TPS6594_BUCK_1, REGULATOR_VOLTAGE, tps6594_bucks_ops, TPS6594_MASK_BUCKS_VSET, TPS6594_REG_BUCKX_VOUT_1(0), @@ -262,7 +258,7 @@ static const struct regulator_desc tps65224_buck_regs[] = { 4, 0, 0, NULL, 0, 0), }; -static struct tps6594_regulator_irq_type tps6594_buck1_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_buck1_irq_types[] = { { TPS6594_IRQ_NAME_BUCK1_OV, "BUCK1", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_BUCK1_UV, "BUCK1", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_BUCK1_SC, "BUCK1", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -270,7 +266,7 @@ static struct tps6594_regulator_irq_type tps6594_buck1_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_buck2_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_buck2_irq_types[] = { { TPS6594_IRQ_NAME_BUCK2_OV, "BUCK2", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_BUCK2_UV, "BUCK2", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_BUCK2_SC, "BUCK2", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -278,7 +274,7 @@ static struct tps6594_regulator_irq_type tps6594_buck2_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_buck3_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_buck3_irq_types[] = { { TPS6594_IRQ_NAME_BUCK3_OV, "BUCK3", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_BUCK3_UV, "BUCK3", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_BUCK3_SC, "BUCK3", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -286,7 +282,7 @@ static struct tps6594_regulator_irq_type tps6594_buck3_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_buck4_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_buck4_irq_types[] = { { TPS6594_IRQ_NAME_BUCK4_OV, "BUCK4", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_BUCK4_UV, "BUCK4", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_BUCK4_SC, "BUCK4", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -294,7 +290,7 @@ static struct tps6594_regulator_irq_type tps6594_buck4_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_buck5_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_buck5_irq_types[] = { { TPS6594_IRQ_NAME_BUCK5_OV, "BUCK5", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_BUCK5_UV, "BUCK5", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_BUCK5_SC, "BUCK5", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -302,7 +298,7 @@ static struct tps6594_regulator_irq_type tps6594_buck5_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_ldo1_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_ldo1_irq_types[] = { { TPS6594_IRQ_NAME_LDO1_OV, "LDO1", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_LDO1_UV, "LDO1", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_LDO1_SC, "LDO1", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -310,7 +306,7 @@ static struct tps6594_regulator_irq_type tps6594_ldo1_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_ldo2_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_ldo2_irq_types[] = { { TPS6594_IRQ_NAME_LDO2_OV, "LDO2", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_LDO2_UV, "LDO2", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_LDO2_SC, "LDO2", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -318,7 +314,7 @@ static struct tps6594_regulator_irq_type tps6594_ldo2_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_ldo3_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_ldo3_irq_types[] = { { TPS6594_IRQ_NAME_LDO3_OV, "LDO3", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_LDO3_UV, "LDO3", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_LDO3_SC, "LDO3", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -326,7 +322,7 @@ static struct tps6594_regulator_irq_type tps6594_ldo3_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_ldo4_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_ldo4_irq_types[] = { { TPS6594_IRQ_NAME_LDO4_OV, "LDO4", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_LDO4_UV, "LDO4", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_LDO4_SC, "LDO4", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -334,42 +330,42 @@ static struct tps6594_regulator_irq_type tps6594_ldo4_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps65224_buck1_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_buck1_irq_types[] = { { TPS65224_IRQ_NAME_BUCK1_UVOV, "BUCK1", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_buck2_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_buck2_irq_types[] = { { TPS65224_IRQ_NAME_BUCK2_UVOV, "BUCK2", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_buck3_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_buck3_irq_types[] = { { TPS65224_IRQ_NAME_BUCK3_UVOV, "BUCK3", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_buck4_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_buck4_irq_types[] = { { TPS65224_IRQ_NAME_BUCK4_UVOV, "BUCK4", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_ldo1_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_ldo1_irq_types[] = { { TPS65224_IRQ_NAME_LDO1_UVOV, "LDO1", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_ldo2_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_ldo2_irq_types[] = { { TPS65224_IRQ_NAME_LDO2_UVOV, "LDO2", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_ldo3_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_ldo3_irq_types[] = { { TPS65224_IRQ_NAME_LDO3_UVOV, "LDO3", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type *tps6594_bucks_irq_types[] = { +static const struct tps6594_regulator_irq_type *tps6594_bucks_irq_types[] = { tps6594_buck1_irq_types, tps6594_buck2_irq_types, tps6594_buck3_irq_types, @@ -377,21 +373,21 @@ static struct tps6594_regulator_irq_type *tps6594_bucks_irq_types[] = { tps6594_buck5_irq_types, }; -static struct tps6594_regulator_irq_type *tps6594_ldos_irq_types[] = { +static const struct tps6594_regulator_irq_type *tps6594_ldos_irq_types[] = { tps6594_ldo1_irq_types, tps6594_ldo2_irq_types, tps6594_ldo3_irq_types, tps6594_ldo4_irq_types, }; -static struct tps6594_regulator_irq_type *tps65224_bucks_irq_types[] = { +static const struct tps6594_regulator_irq_type *tps65224_bucks_irq_types[] = { tps65224_buck1_irq_types, tps65224_buck2_irq_types, tps65224_buck3_irq_types, tps65224_buck4_irq_types, }; -static struct tps6594_regulator_irq_type *tps65224_ldos_irq_types[] = { +static const struct tps6594_regulator_irq_type *tps65224_ldos_irq_types[] = { tps65224_ldo1_irq_types, tps65224_ldo2_irq_types, tps65224_ldo3_irq_types, @@ -516,11 +512,11 @@ static irqreturn_t tps6594_regulator_irq_handler(int irq, void *data) static int tps6594_request_reg_irqs(struct platform_device *pdev, struct regulator_dev *rdev, struct tps6594_regulator_irq_data *irq_data, - struct tps6594_regulator_irq_type *regs_irq_types, + const struct tps6594_regulator_irq_type *regs_irq_types, size_t interrupt_cnt, int *irq_idx) { - struct tps6594_regulator_irq_type *irq_type; + const struct tps6594_regulator_irq_type *irq_type; struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent); size_t j; int irq; @@ -549,6 +545,70 @@ static int tps6594_request_reg_irqs(struct platform_device *pdev, return 0; } +struct tps6594_regulator_desc { + const struct regulator_desc *multi_phase_regs; + unsigned int num_multi_phase_regs; + + const struct regulator_desc *buck_regs; + int num_buck_regs; + + const struct regulator_desc *ldo_regs; + int num_ldo_regs; + + const struct tps6594_regulator_irq_type **bucks_irq_types; + const struct tps6594_regulator_irq_type **ldos_irq_types; + int num_irq_types; + + const struct tps6594_regulator_irq_type *ext_irq_types; + int num_ext_irqs; +}; + +static const struct tps6594_regulator_desc tps65224_reg_desc = { + .multi_phase_regs = tps65224_multi_regs, + .num_multi_phase_regs = ARRAY_SIZE(tps65224_multi_regs), + .buck_regs = tps65224_buck_regs, + .num_buck_regs = ARRAY_SIZE(tps65224_buck_regs), + .ldo_regs = tps65224_ldo_regs, + .num_ldo_regs = ARRAY_SIZE(tps65224_ldo_regs), + .bucks_irq_types = tps65224_bucks_irq_types, + .ldos_irq_types = tps65224_ldos_irq_types, + .num_irq_types = 1, /* OV or UV */ + .ext_irq_types = tps65224_ext_regulator_irq_types, + .num_ext_irqs = ARRAY_SIZE(tps65224_ext_regulator_irq_types), +}; + +static const struct tps6594_regulator_desc tps652g1_reg_desc = { + .ldo_regs = tps65224_ldo_regs, + .num_ldo_regs = ARRAY_SIZE(tps65224_ldo_regs), + .buck_regs = tps65224_buck_regs, + .num_buck_regs = ARRAY_SIZE(tps65224_buck_regs), +}; + +static const struct tps6594_regulator_desc tps6594_reg_desc = { + .multi_phase_regs = tps6594_multi_regs, + .num_multi_phase_regs = ARRAY_SIZE(tps6594_multi_regs), + .buck_regs = tps6594_buck_regs, + .num_buck_regs = ARRAY_SIZE(tps6594_buck_regs), + .ldo_regs = tps6594_ldo_regs, + .num_ldo_regs = ARRAY_SIZE(tps6594_ldo_regs), + .bucks_irq_types = tps6594_bucks_irq_types, + .ldos_irq_types = tps6594_ldos_irq_types, + .num_irq_types = 4, /* OV, UV, SC and ILIM */ + .ext_irq_types = tps6594_ext_regulator_irq_types, + .num_ext_irqs = 2, /* only VCCA OV and UV */ +}; + +static const struct tps6594_regulator_desc lp8764_reg_desc = { + .multi_phase_regs = tps6594_multi_regs, + .num_multi_phase_regs = ARRAY_SIZE(tps6594_multi_regs), + .buck_regs = tps6594_buck_regs, + .num_buck_regs = ARRAY_SIZE(tps6594_buck_regs), + .bucks_irq_types = tps6594_bucks_irq_types, + .num_irq_types = 4, /* OV, UV, SC and ILIM */ + .ext_irq_types = tps6594_ext_regulator_irq_types, + .num_ext_irqs = ARRAY_SIZE(tps6594_ext_regulator_irq_types), +}; + static int tps6594_regulator_probe(struct platform_device *pdev) { struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent); @@ -558,42 +618,36 @@ static int tps6594_regulator_probe(struct platform_device *pdev) struct regulator_config config = {}; struct tps6594_regulator_irq_data *irq_data; struct tps6594_ext_regulator_irq_data *irq_ext_reg_data; - struct tps6594_regulator_irq_type *irq_type; - struct tps6594_regulator_irq_type *irq_types; + const struct tps6594_regulator_irq_type *irq_type; bool buck_configured[BUCK_NB] = { false }; bool buck_multi[MULTI_PHASE_NB] = { false }; + const struct tps6594_regulator_desc *desc; + const struct regulator_desc *multi_regs; - static const char *npname; + const char *npname; int error, i, irq, multi; int irq_idx = 0; int buck_idx = 0; - int nr_ldo; - int nr_buck; - int nr_types; - unsigned int irq_count; - unsigned int multi_phase_cnt; size_t reg_irq_nb; - struct tps6594_regulator_irq_type **bucks_irq_types; - const struct regulator_desc *multi_regs; - struct tps6594_regulator_irq_type **ldos_irq_types; - const struct regulator_desc *ldo_regs; - size_t interrupt_count; - - if (tps->chip_id == TPS65224) { - bucks_irq_types = tps65224_bucks_irq_types; - interrupt_count = ARRAY_SIZE(tps65224_buck1_irq_types); - multi_regs = tps65224_multi_regs; - ldos_irq_types = tps65224_ldos_irq_types; - ldo_regs = tps65224_ldo_regs; - multi_phase_cnt = ARRAY_SIZE(tps65224_multi_regs); - } else { - bucks_irq_types = tps6594_bucks_irq_types; - interrupt_count = ARRAY_SIZE(tps6594_buck1_irq_types); - multi_regs = tps6594_multi_regs; - ldos_irq_types = tps6594_ldos_irq_types; - ldo_regs = tps6594_ldo_regs; - multi_phase_cnt = ARRAY_SIZE(tps6594_multi_regs); - } + + switch (tps->chip_id) { + case TPS65224: + desc = &tps65224_reg_desc; + break; + case TPS652G1: + desc = &tps652g1_reg_desc; + break; + case TPS6594: + case TPS6593: + desc = &tps6594_reg_desc; + break; + case LP8764: + desc = &lp8764_reg_desc; + break; + default: + dev_err(tps->dev, "unknown chip_id %lu\n", tps->chip_id); + return -EINVAL; + }; enum { MULTI_BUCK12, @@ -614,13 +668,14 @@ static int tps6594_regulator_probe(struct platform_device *pdev) * In case of Multiphase configuration, value should be defined for * buck_configured to avoid creating bucks for every buck in multiphase */ - for (multi = 0; multi < multi_phase_cnt; multi++) { - np = of_find_node_by_name(tps->dev->of_node, multi_regs[multi].supply_name); + for (multi = 0; multi < desc->num_multi_phase_regs; multi++) { + multi_regs = &desc->multi_phase_regs[multi]; + np = of_find_node_by_name(tps->dev->of_node, multi_regs->supply_name); npname = of_node_full_name(np); np_pmic_parent = of_get_parent(of_get_parent(np)); if (of_node_cmp(of_node_full_name(np_pmic_parent), tps->dev->of_node->full_name)) continue; - if (strcmp(npname, multi_regs[multi].supply_name) == 0) { + if (strcmp(npname, multi_regs->supply_name) == 0) { switch (multi) { case MULTI_BUCK12: buck_multi[0] = true; @@ -653,123 +708,106 @@ static int tps6594_regulator_probe(struct platform_device *pdev) } } - if (tps->chip_id == TPS65224) { - nr_buck = ARRAY_SIZE(tps65224_buck_regs); - nr_ldo = ARRAY_SIZE(tps65224_ldo_regs); - nr_types = TPS65224_REGS_INT_NB; - } else { - nr_buck = ARRAY_SIZE(buck_regs); - nr_ldo = (tps->chip_id == LP8764) ? 0 : ARRAY_SIZE(tps6594_ldo_regs); - nr_types = REGS_INT_NB; - } - - reg_irq_nb = nr_types * (nr_buck + nr_ldo); + reg_irq_nb = desc->num_irq_types * (desc->num_buck_regs + desc->num_ldo_regs); irq_data = devm_kmalloc_array(tps->dev, reg_irq_nb, sizeof(struct tps6594_regulator_irq_data), GFP_KERNEL); if (!irq_data) return -ENOMEM; - for (i = 0; i < multi_phase_cnt; i++) { + for (i = 0; i < desc->num_multi_phase_regs; i++) { if (!buck_multi[i]) continue; - rdev = devm_regulator_register(&pdev->dev, &multi_regs[i], &config); + rdev = devm_regulator_register(&pdev->dev, &desc->multi_phase_regs[i], + &config); if (IS_ERR(rdev)) return dev_err_probe(tps->dev, PTR_ERR(rdev), "failed to register %s regulator\n", pdev->name); + if (!desc->num_irq_types) + continue; + /* config multiphase buck12+buck34 */ if (i == MULTI_BUCK12_34) buck_idx = 2; error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - bucks_irq_types[buck_idx], - interrupt_count, &irq_idx); + desc->bucks_irq_types[buck_idx], + desc->num_irq_types, &irq_idx); if (error) return error; error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - bucks_irq_types[buck_idx + 1], - interrupt_count, &irq_idx); + desc->bucks_irq_types[buck_idx + 1], + desc->num_irq_types, &irq_idx); if (error) return error; if (i == MULTI_BUCK123 || i == MULTI_BUCK1234) { error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - tps6594_bucks_irq_types[buck_idx + 2], - interrupt_count, + desc->bucks_irq_types[buck_idx + 2], + desc->num_irq_types, &irq_idx); if (error) return error; } if (i == MULTI_BUCK1234) { error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - tps6594_bucks_irq_types[buck_idx + 3], - interrupt_count, + desc->bucks_irq_types[buck_idx + 3], + desc->num_irq_types, &irq_idx); if (error) return error; } } - for (i = 0; i < nr_buck; i++) { + for (i = 0; i < desc->num_buck_regs; i++) { if (buck_configured[i]) continue; - const struct regulator_desc *buck_cfg = (tps->chip_id == TPS65224) ? - tps65224_buck_regs : buck_regs; - - rdev = devm_regulator_register(&pdev->dev, &buck_cfg[i], &config); + rdev = devm_regulator_register(&pdev->dev, &desc->buck_regs[i], &config); if (IS_ERR(rdev)) return dev_err_probe(tps->dev, PTR_ERR(rdev), "failed to register %s regulator\n", pdev->name); + if (!desc->num_irq_types) + continue; + error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - bucks_irq_types[i], interrupt_count, &irq_idx); + desc->bucks_irq_types[i], + desc->num_irq_types, &irq_idx); if (error) return error; } - /* LP8764 doesn't have LDO */ - if (tps->chip_id != LP8764) { - for (i = 0; i < nr_ldo; i++) { - rdev = devm_regulator_register(&pdev->dev, &ldo_regs[i], &config); - if (IS_ERR(rdev)) - return dev_err_probe(tps->dev, PTR_ERR(rdev), - "failed to register %s regulator\n", - pdev->name); + for (i = 0; i < desc->num_ldo_regs; i++) { + rdev = devm_regulator_register(&pdev->dev, &desc->ldo_regs[i], &config); + if (IS_ERR(rdev)) + return dev_err_probe(tps->dev, PTR_ERR(rdev), + "failed to register %s regulator\n", + pdev->name); - error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - ldos_irq_types[i], interrupt_count, - &irq_idx); - if (error) - return error; - } - } + if (!desc->num_irq_types) + continue; - if (tps->chip_id == TPS65224) { - irq_types = tps65224_ext_regulator_irq_types; - irq_count = ARRAY_SIZE(tps65224_ext_regulator_irq_types); - } else { - irq_types = tps6594_ext_regulator_irq_types; - if (tps->chip_id == LP8764) - irq_count = ARRAY_SIZE(tps6594_ext_regulator_irq_types); - else - /* TPS6593 supports only VCCA OV and UV */ - irq_count = 2; + error = tps6594_request_reg_irqs(pdev, rdev, irq_data, + desc->ldos_irq_types[i], + desc->num_irq_types, &irq_idx); + if (error) + return error; } irq_ext_reg_data = devm_kmalloc_array(tps->dev, - irq_count, + desc->num_ext_irqs, sizeof(struct tps6594_ext_regulator_irq_data), GFP_KERNEL); if (!irq_ext_reg_data) return -ENOMEM; - for (i = 0; i < irq_count; ++i) { - irq_type = &irq_types[i]; + for (i = 0; i < desc->num_ext_irqs; ++i) { + irq_type = &desc->ext_irq_types[i]; irq = platform_get_irq_byname(pdev, irq_type->irq_name); if (irq < 0) return -EINVAL; @@ -787,6 +825,7 @@ static int tps6594_regulator_probe(struct platform_device *pdev) "failed to request %s IRQ %d\n", irq_type->irq_name, irq); } + return 0; } diff --git a/include/dt-bindings/regulator/nxp,pca9450-regulator.h b/include/dt-bindings/regulator/nxp,pca9450-regulator.h new file mode 100644 index 000000000000..08434caef429 --- /dev/null +++ b/include/dt-bindings/regulator/nxp,pca9450-regulator.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Device Tree binding constants for the NXP PCA9450A/B/C PMIC regulators + */ + +#ifndef _DT_BINDINGS_REGULATORS_NXP_PCA9450_H +#define _DT_BINDINGS_REGULATORS_NXP_PCA9450_H + +/* + * Buck mode constants which may be used in devicetree properties (eg. + * regulator-initial-mode, regulator-allowed-modes). + * See the manufacturer's datasheet for more information on these modes. + */ + +#define PCA9450_BUCK_MODE_AUTO 0 +#define PCA9450_BUCK_MODE_FORCE_PWM 1 + +#endif diff --git a/include/linux/mfd/tps6594.h b/include/linux/mfd/tps6594.h index 16543fd4d83e..021db8875963 100644 --- a/include/linux/mfd/tps6594.h +++ b/include/linux/mfd/tps6594.h @@ -19,6 +19,7 @@ enum pmic_id { TPS6593, LP8764, TPS65224, + TPS652G1, }; /* Macro to get page index from register address */ diff --git a/include/linux/regulator/coupler.h b/include/linux/regulator/coupler.h index 73291f280a23..5e314a4294fb 100644 --- a/include/linux/regulator/coupler.h +++ b/include/linux/regulator/coupler.h @@ -8,7 +8,8 @@ #ifndef __LINUX_REGULATOR_COUPLER_H_ #define __LINUX_REGULATOR_COUPLER_H_ -#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/types.h> #include <linux/suspend.h> struct regulator_coupler; diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 8cbb660e2ec2..2d51f9d05609 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -65,6 +65,7 @@ #include <linux/poll.h> #include <linux/property.h> #include <linux/refcount.h> +#include <linux/regulator/consumer.h> #include <linux/sched.h> #include <linux/security.h> #include <linux/slab.h> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index d2887e3b2826..2593c38491ca 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -34,6 +34,7 @@ #include "pid_namespace.c" #include "poll.c" #include "rbtree.c" +#include "regulator.c" #include "rcu.c" #include "refcount.c" #include "security.c" diff --git a/rust/helpers/regulator.c b/rust/helpers/regulator.c new file mode 100644 index 000000000000..cd8b7ba648ee --- /dev/null +++ b/rust/helpers/regulator.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/regulator/consumer.h> + +#ifndef CONFIG_REGULATOR + +void rust_helper_regulator_put(struct regulator *regulator) +{ + regulator_put(regulator); +} + +int rust_helper_regulator_set_voltage(struct regulator *regulator, int min_uV, + int max_uV) +{ + return regulator_set_voltage(regulator, min_uV, max_uV); +} + +int rust_helper_regulator_get_voltage(struct regulator *regulator) +{ + return regulator_get_voltage(regulator); +} + +struct regulator *rust_helper_regulator_get(struct device *dev, const char *id) +{ + return regulator_get(dev, id); +} + +int rust_helper_regulator_enable(struct regulator *regulator) +{ + return regulator_enable(regulator); +} + +int rust_helper_regulator_disable(struct regulator *regulator) +{ + return regulator_disable(regulator); +} + +int rust_helper_regulator_is_enabled(struct regulator *regulator) +{ + return regulator_is_enabled(regulator); +} + +#endif diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index e13d6ed88fa6..46d36a3e0bf1 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -103,6 +103,7 @@ pub mod platform; pub mod prelude; pub mod print; pub mod rbtree; +pub mod regulator; pub mod revocable; pub mod security; pub mod seq_file; diff --git a/rust/kernel/regulator.rs b/rust/kernel/regulator.rs new file mode 100644 index 000000000000..65f3a125348f --- /dev/null +++ b/rust/kernel/regulator.rs @@ -0,0 +1,418 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Regulator abstractions, providing a standard kernel interface to control +//! voltage and current regulators. +//! +//! The intention is to allow systems to dynamically control regulator power +//! output in order to save power and prolong battery life. This applies to both +//! voltage regulators (where voltage output is controllable) and current sinks +//! (where current limit is controllable). +//! +//! C header: [`include/linux/regulator/consumer.h`](srctree/include/linux/regulator/consumer.h) +//! +//! Regulators are modeled in Rust with a collection of states. Each state may +//! enforce a given invariant, and they may convert between each other where applicable. +//! +//! See [Voltage and current regulator API](https://docs.kernel.org/driver-api/regulator.html) +//! for more information. + +use crate::{ + bindings, + device::Device, + error::{from_err_ptr, to_result, Result}, + prelude::*, +}; + +use core::{marker::PhantomData, mem::ManuallyDrop, ptr::NonNull}; + +mod private { + pub trait Sealed {} + + impl Sealed for super::Enabled {} + impl Sealed for super::Disabled {} + impl Sealed for super::Dynamic {} +} + +/// A trait representing the different states a [`Regulator`] can be in. +pub trait RegulatorState: private::Sealed + 'static { + /// Whether the regulator should be disabled when dropped. + const DISABLE_ON_DROP: bool; +} + +/// A state where the [`Regulator`] is known to be enabled. +/// +/// The `enable` reference count held by this state is decremented when it is +/// dropped. +pub struct Enabled; + +/// A state where this [`Regulator`] handle has not specifically asked for the +/// underlying regulator to be enabled. This means that this reference does not +/// own an `enable` reference count, but the regulator may still be on. +pub struct Disabled; + +/// A state that models the C API. The [`Regulator`] can be either enabled or +/// disabled, and the user is in control of the reference count. This is also +/// the default state. +/// +/// Use [`Regulator::is_enabled`] to check the regulator's current state. +pub struct Dynamic; + +impl RegulatorState for Enabled { + const DISABLE_ON_DROP: bool = true; +} + +impl RegulatorState for Disabled { + const DISABLE_ON_DROP: bool = false; +} + +impl RegulatorState for Dynamic { + const DISABLE_ON_DROP: bool = false; +} + +/// A trait that abstracts the ability to check if a [`Regulator`] is enabled. +pub trait IsEnabled: RegulatorState {} +impl IsEnabled for Disabled {} +impl IsEnabled for Dynamic {} + +/// An error that can occur when trying to convert a [`Regulator`] between states. +pub struct Error<State: RegulatorState> { + /// The error that occurred. + pub error: kernel::error::Error, + + /// The regulator that caused the error, so that the operation may be retried. + pub regulator: Regulator<State>, +} + +/// A `struct regulator` abstraction. +/// +/// # Examples +/// +/// ## Enabling a regulator +/// +/// This example uses [`Regulator<Enabled>`], which is suitable for drivers that +/// enable a regulator at probe time and leave them on until the device is +/// removed or otherwise shutdown. +/// +/// These users can store [`Regulator<Enabled>`] directly in their driver's +/// private data struct. +/// +/// ``` +/// # use kernel::prelude::*; +/// # use kernel::c_str; +/// # use kernel::device::Device; +/// # use kernel::regulator::{Voltage, Regulator, Disabled, Enabled}; +/// fn enable(dev: &Device, min_voltage: Voltage, max_voltage: Voltage) -> Result { +/// // Obtain a reference to a (fictitious) regulator. +/// let regulator: Regulator<Disabled> = Regulator::<Disabled>::get(dev, c_str!("vcc"))?; +/// +/// // The voltage can be set before enabling the regulator if needed, e.g.: +/// regulator.set_voltage(min_voltage, max_voltage)?; +/// +/// // The same applies for `get_voltage()`, i.e.: +/// let voltage: Voltage = regulator.get_voltage()?; +/// +/// // Enables the regulator, consuming the previous value. +/// // +/// // From now on, the regulator is known to be enabled because of the type +/// // `Enabled`. +/// // +/// // If this operation fails, the `Error` will contain the regulator +/// // reference, so that the operation may be retried. +/// let regulator: Regulator<Enabled> = +/// regulator.try_into_enabled().map_err(|error| error.error)?; +/// +/// // The voltage can also be set after enabling the regulator, e.g.: +/// regulator.set_voltage(min_voltage, max_voltage)?; +/// +/// // The same applies for `get_voltage()`, i.e.: +/// let voltage: Voltage = regulator.get_voltage()?; +/// +/// // Dropping an enabled regulator will disable it. The refcount will be +/// // decremented. +/// drop(regulator); +/// +/// // ... +/// +/// Ok(()) +/// } +/// ``` +/// +/// A more concise shortcut is available for enabling a regulator. This is +/// equivalent to `regulator_get_enable()`: +/// +/// ``` +/// # use kernel::prelude::*; +/// # use kernel::c_str; +/// # use kernel::device::Device; +/// # use kernel::regulator::{Voltage, Regulator, Enabled}; +/// fn enable(dev: &Device) -> Result { +/// // Obtain a reference to a (fictitious) regulator and enable it. +/// let regulator: Regulator<Enabled> = Regulator::<Enabled>::get(dev, c_str!("vcc"))?; +/// +/// // Dropping an enabled regulator will disable it. The refcount will be +/// // decremented. +/// drop(regulator); +/// +/// // ... +/// +/// Ok(()) +/// } +/// ``` +/// +/// ## Disabling a regulator +/// +/// ``` +/// # use kernel::prelude::*; +/// # use kernel::device::Device; +/// # use kernel::regulator::{Regulator, Enabled, Disabled}; +/// fn disable(dev: &Device, regulator: Regulator<Enabled>) -> Result { +/// // We can also disable an enabled regulator without reliquinshing our +/// // refcount: +/// // +/// // If this operation fails, the `Error` will contain the regulator +/// // reference, so that the operation may be retried. +/// let regulator: Regulator<Disabled> = +/// regulator.try_into_disabled().map_err(|error| error.error)?; +/// +/// // The refcount will be decremented when `regulator` is dropped. +/// drop(regulator); +/// +/// // ... +/// +/// Ok(()) +/// } +/// ``` +/// +/// ## Using [`Regulator<Dynamic>`] +/// +/// This example mimics the behavior of the C API, where the user is in +/// control of the enabled reference count. This is useful for drivers that +/// might call enable and disable to manage the `enable` reference count at +/// runtime, perhaps as a result of `open()` and `close()` calls or whatever +/// other driver-specific or subsystem-specific hooks. +/// +/// ``` +/// # use kernel::prelude::*; +/// # use kernel::c_str; +/// # use kernel::device::Device; +/// # use kernel::regulator::{Regulator, Dynamic}; +/// struct PrivateData { +/// regulator: Regulator<Dynamic>, +/// } +/// +/// // A fictictious probe function that obtains a regulator and sets it up. +/// fn probe(dev: &Device) -> Result<PrivateData> { +/// // Obtain a reference to a (fictitious) regulator. +/// let mut regulator = Regulator::<Dynamic>::get(dev, c_str!("vcc"))?; +/// +/// Ok(PrivateData { regulator }) +/// } +/// +/// // A fictictious function that indicates that the device is going to be used. +/// fn open(dev: &Device, data: &mut PrivateData) -> Result { +/// // Increase the `enabled` reference count. +/// data.regulator.enable()?; +/// +/// Ok(()) +/// } +/// +/// fn close(dev: &Device, data: &mut PrivateData) -> Result { +/// // Decrease the `enabled` reference count. +/// data.regulator.disable()?; +/// +/// Ok(()) +/// } +/// +/// fn remove(dev: &Device, data: PrivateData) -> Result { +/// // `PrivateData` is dropped here, which will drop the +/// // `Regulator<Dynamic>` in turn. +/// // +/// // The reference that was obtained by `regulator_get()` will be +/// // released, but it is up to the user to make sure that the number of calls +/// // to `enable()` and `disabled()` are balanced before this point. +/// Ok(()) +/// } +/// ``` +/// +/// # Invariants +/// +/// - `inner` is a non-null wrapper over a pointer to a `struct +/// regulator` obtained from [`regulator_get()`]. +/// +/// [`regulator_get()`]: https://docs.kernel.org/driver-api/regulator.html#c.regulator_get +pub struct Regulator<State = Dynamic> +where + State: RegulatorState, +{ + inner: NonNull<bindings::regulator>, + _phantom: PhantomData<State>, +} + +impl<T: RegulatorState> Regulator<T> { + /// Sets the voltage for the regulator. + /// + /// This can be used to ensure that the device powers up cleanly. + pub fn set_voltage(&self, min_voltage: Voltage, max_voltage: Voltage) -> Result { + // SAFETY: Safe as per the type invariants of `Regulator`. + to_result(unsafe { + bindings::regulator_set_voltage( + self.inner.as_ptr(), + min_voltage.as_microvolts(), + max_voltage.as_microvolts(), + ) + }) + } + + /// Gets the current voltage of the regulator. + pub fn get_voltage(&self) -> Result<Voltage> { + // SAFETY: Safe as per the type invariants of `Regulator`. + let voltage = unsafe { bindings::regulator_get_voltage(self.inner.as_ptr()) }; + if voltage < 0 { + Err(kernel::error::Error::from_errno(voltage)) + } else { + Ok(Voltage::from_microvolts(voltage)) + } + } + + fn get_internal(dev: &Device, name: &CStr) -> Result<Regulator<T>> { + // SAFETY: It is safe to call `regulator_get()`, on a device pointer + // received from the C code. + let inner = from_err_ptr(unsafe { bindings::regulator_get(dev.as_raw(), name.as_ptr()) })?; + + // SAFETY: We can safely trust `inner` to be a pointer to a valid + // regulator if `ERR_PTR` was not returned. + let inner = unsafe { NonNull::new_unchecked(inner) }; + + Ok(Self { + inner, + _phantom: PhantomData, + }) + } + + fn enable_internal(&mut self) -> Result { + // SAFETY: Safe as per the type invariants of `Regulator`. + to_result(unsafe { bindings::regulator_enable(self.inner.as_ptr()) }) + } + + fn disable_internal(&mut self) -> Result { + // SAFETY: Safe as per the type invariants of `Regulator`. + to_result(unsafe { bindings::regulator_disable(self.inner.as_ptr()) }) + } +} + +impl Regulator<Disabled> { + /// Obtains a [`Regulator`] instance from the system. + pub fn get(dev: &Device, name: &CStr) -> Result<Self> { + Regulator::get_internal(dev, name) + } + + /// Attempts to convert the regulator to an enabled state. + pub fn try_into_enabled(self) -> Result<Regulator<Enabled>, Error<Disabled>> { + // We will be transferring the ownership of our `regulator_get()` count to + // `Regulator<Enabled>`. + let mut regulator = ManuallyDrop::new(self); + + regulator + .enable_internal() + .map(|()| Regulator { + inner: regulator.inner, + _phantom: PhantomData, + }) + .map_err(|error| Error { + error, + regulator: ManuallyDrop::into_inner(regulator), + }) + } +} + +impl Regulator<Enabled> { + /// Obtains a [`Regulator`] instance from the system and enables it. + /// + /// This is equivalent to calling `regulator_get_enable()` in the C API. + pub fn get(dev: &Device, name: &CStr) -> Result<Self> { + Regulator::<Disabled>::get_internal(dev, name)? + .try_into_enabled() + .map_err(|error| error.error) + } + + /// Attempts to convert the regulator to a disabled state. + pub fn try_into_disabled(self) -> Result<Regulator<Disabled>, Error<Enabled>> { + // We will be transferring the ownership of our `regulator_get()` count + // to `Regulator<Disabled>`. + let mut regulator = ManuallyDrop::new(self); + + regulator + .disable_internal() + .map(|()| Regulator { + inner: regulator.inner, + _phantom: PhantomData, + }) + .map_err(|error| Error { + error, + regulator: ManuallyDrop::into_inner(regulator), + }) + } +} + +impl Regulator<Dynamic> { + /// Obtains a [`Regulator`] instance from the system. The current state of + /// the regulator is unknown and it is up to the user to manage the enabled + /// reference count. + /// + /// This closely mimics the behavior of the C API and can be used to + /// dynamically manage the enabled reference count at runtime. + pub fn get(dev: &Device, name: &CStr) -> Result<Self> { + Regulator::get_internal(dev, name) + } + + /// Increases the `enabled` reference count. + pub fn enable(&mut self) -> Result { + self.enable_internal() + } + + /// Decreases the `enabled` reference count. + pub fn disable(&mut self) -> Result { + self.disable_internal() + } +} + +impl<T: IsEnabled> Regulator<T> { + /// Checks if the regulator is enabled. + pub fn is_enabled(&self) -> bool { + // SAFETY: Safe as per the type invariants of `Regulator`. + unsafe { bindings::regulator_is_enabled(self.inner.as_ptr()) != 0 } + } +} + +impl<T: RegulatorState> Drop for Regulator<T> { + fn drop(&mut self) { + if T::DISABLE_ON_DROP { + // SAFETY: By the type invariants, we know that `self` owns a + // reference on the enabled refcount, so it is safe to relinquish it + // now. + unsafe { bindings::regulator_disable(self.inner.as_ptr()) }; + } + // SAFETY: By the type invariants, we know that `self` owns a reference, + // so it is safe to relinquish it now. + unsafe { bindings::regulator_put(self.inner.as_ptr()) }; + } +} + +/// A voltage. +/// +/// This type represents a voltage value in microvolts. +#[repr(transparent)] +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct Voltage(i32); + +impl Voltage { + /// Creates a new `Voltage` from a value in microvolts. + pub fn from_microvolts(uv: i32) -> Self { + Self(uv) + } + + /// Returns the value of the voltage in microvolts as an [`i32`]. + pub fn as_microvolts(self) -> i32 { + self.0 + } +} |
