diff options
-rw-r--r-- | Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml | 10 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml | 3 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/regulator/regulator-output.yaml | 39 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/regulator/richtek,rt6190.yaml | 79 | ||||
-rw-r--r-- | Documentation/driver-api/driver-model/devres.rst | 1 | ||||
-rw-r--r-- | drivers/regulator/Kconfig | 11 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 1 | ||||
-rw-r--r-- | drivers/regulator/core.c | 43 | ||||
-rw-r--r-- | drivers/regulator/devres.c | 66 | ||||
-rw-r--r-- | drivers/regulator/internal.h | 2 | ||||
-rw-r--r-- | drivers/regulator/qcom-rpmh-regulator.c | 2 | ||||
-rw-r--r-- | drivers/regulator/qcom_smd-regulator.c | 33 | ||||
-rw-r--r-- | drivers/regulator/rt6190-regulator.c | 495 | ||||
-rw-r--r-- | drivers/regulator/userspace-consumer.c | 60 | ||||
-rw-r--r-- | include/linux/regulator/consumer.h | 2 | ||||
-rw-r--r-- | include/linux/regulator/userspace-consumer.h | 1 | ||||
-rw-r--r-- | include/linux/soc/qcom/smd-rpm.h | 2 | ||||
-rw-r--r-- | lib/test_linear_ranges.c | 13 |
18 files changed, 802 insertions, 61 deletions
diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml index 90c3bda31c23..7ff51b558702 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml @@ -412,9 +412,8 @@ examples: regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; - regulator-allowed-modes = - <RPMH_REGULATOR_MODE_LPM - RPMH_REGULATOR_MODE_HPM>; + regulator-allowed-modes = <RPMH_REGULATOR_MODE_LPM + RPMH_REGULATOR_MODE_HPM>; regulator-allow-set-load; }; @@ -431,9 +430,8 @@ examples: bob { regulator-min-microvolt = <3312000>; regulator-max-microvolt = <3600000>; - regulator-allowed-modes = - <RPMH_REGULATOR_MODE_AUTO - RPMH_REGULATOR_MODE_HPM>; + regulator-allowed-modes = <RPMH_REGULATOR_MODE_AUTO + RPMH_REGULATOR_MODE_HPM>; regulator-initial-mode = <RPMH_REGULATOR_MODE_AUTO>; }; }; diff --git a/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml index 961eed51912c..8c45f53212b1 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml @@ -71,6 +71,8 @@ description: For pmi8998, bob + For pmr735a, s1, s2, s3, l1, l2, l3, l4, l5, l6, l7 + For pms405, s1, s2, s3, s4, s5, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13 @@ -98,6 +100,7 @@ properties: - qcom,rpm-pma8084-regulators - qcom,rpm-pmi8994-regulators - qcom,rpm-pmi8998-regulators + - qcom,rpm-pmr735a-regulators - qcom,rpm-pms405-regulators patternProperties: diff --git a/Documentation/devicetree/bindings/regulator/regulator-output.yaml b/Documentation/devicetree/bindings/regulator/regulator-output.yaml new file mode 100644 index 000000000000..078b37a1a71a --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/regulator-output.yaml @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- + +$id: http://devicetree.org/schemas/regulator/regulator-output.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Regulator output connector + +maintainers: + - Zev Weiss <zev@bewilderbeest.net> + +description: | + This describes a power output connector supplied by a regulator, + such as a power outlet on a power distribution unit (PDU). The + connector may be standalone or merely one channel or set of pins + within a ganged physical connector carrying multiple independent + power outputs. + +properties: + compatible: + const: regulator-output + + vout-supply: + description: + Phandle of the regulator supplying the output. + +required: + - compatible + - vout-supply + +additionalProperties: false + +examples: + - | + output { + compatible = "regulator-output"; + vout-supply = <&output_reg>; + }; diff --git a/Documentation/devicetree/bindings/regulator/richtek,rt6190.yaml b/Documentation/devicetree/bindings/regulator/richtek,rt6190.yaml new file mode 100644 index 000000000000..29f7d3d5d658 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/richtek,rt6190.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/richtek,rt6190.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Richtek RT6190 4-Switch BuckBoost controller + +maintainers: + - ChiYuan Huang <cy_huang@richtek.com> + +description: | + The RT6190 is 4-Switch BuckBoost controller designed for converting input + voltage to output voltage that can be equal to, higher or lower than input + voltage. It operates with wide input voltage range from 4.5V to 36V, and + the output voltage can be set from 3V to 36V by external FB pin. It's commonly + used for the application like as BuckBoost bus supply, docking station and USB + power delivery product. + + Datasheet is available at + https://www.richtek.com/assets/product_file/RT6190/DS6190-02.pdf + +allOf: + - $ref: regulator.yaml# + +properties: + compatible: + enum: + - richtek,rt6190 + + reg: + maxItems: 1 + + enable-gpios: + maxItems: 1 + + wakeup-source: true + + interrupts: + maxItems: 1 + + regulator-allowed-modes: + description: | + buck allowed operating mode + 0: PSM mode (light load Power Saving Mode) + 1: FCCM mode (Forced-CCM mode) + maxItems: 2 + items: + enum: [0, 1] + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + regulator@2c { + compatible = "richtek,rt6190"; + reg = <0x2c>; + wakeup-source; + interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>; + enable-gpios = <&gpio26 1 GPIO_ACTIVE_HIGH>; + regulator-name = "richtek,rt6190-buckboost"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <32000000>; + regulator-min-microamp = <306000>; + regulator-max-microamp = <12114000>; + regulator-allowed-modes = <0 1>; + }; + }; diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index 687adb58048e..ff8158274fb3 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -408,6 +408,7 @@ PWM REGULATOR devm_regulator_bulk_register_supply_alias() devm_regulator_bulk_get() + devm_regulator_bulk_get_const() devm_regulator_bulk_get_enable() devm_regulator_bulk_put() devm_regulator_get() diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 070e4403c6c2..4e4614d70bde 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1124,6 +1124,17 @@ config REGULATOR_RT6160 The wide output range is from 2025mV to 5200mV and can be used on most common application scenario. +config REGULATOR_RT6190 + tristate "Richtek RT6190 4-Switch BuckBoost controller" + depends on I2C + select REGMAP_I2C + help + The RT6190 is a 4-Switch BuckBoost controller designed for converting + input voltage to output voltage that can be equal to, higher or lower + than input voltage. It operates with wide input voltage range from + 4.5V to 36V, and the output voltage can be set from 3V to 36V by + external FB pin. + config REGULATOR_RT6245 tristate "Richtek RT6245 voltage regulator" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 5962307e1130..c3b5cf6cceab 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -134,6 +134,7 @@ obj-$(CONFIG_REGULATOR_RT5120) += rt5120-regulator.o obj-$(CONFIG_REGULATOR_RT5190A) += rt5190a-regulator.o obj-$(CONFIG_REGULATOR_RT5759) += rt5759-regulator.o obj-$(CONFIG_REGULATOR_RT6160) += rt6160-regulator.o +obj-$(CONFIG_REGULATOR_RT6190) += rt6190-regulator.o obj-$(CONFIG_REGULATOR_RT6245) += rt6245-regulator.o obj-$(CONFIG_REGULATOR_RTMV20) += rtmv20-regulator.o obj-$(CONFIG_REGULATOR_RTQ2134) += rtq2134-regulator.o diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index bcccad8f7516..3f6c6185c187 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1943,6 +1943,7 @@ static struct regulator_dev *regulator_dev_lookup(struct device *dev, node = of_get_regulator(dev, supply); if (node) { r = of_find_regulator_by_node(node); + of_node_put(node); if (r) return r; @@ -4778,22 +4779,8 @@ static int _notifier_call_chain(struct regulator_dev *rdev, return blocking_notifier_call_chain(&rdev->notifier, event, data); } -/** - * regulator_bulk_get - get multiple regulator consumers - * - * @dev: Device to supply - * @num_consumers: Number of consumers to register - * @consumers: Configuration of consumers; clients are stored here. - * - * @return 0 on success, an errno on failure. - * - * This helper function allows drivers to get several regulator - * consumers in one operation. If any of the regulators cannot be - * acquired then any regulators that were allocated will be freed - * before returning to the caller. - */ -int regulator_bulk_get(struct device *dev, int num_consumers, - struct regulator_bulk_data *consumers) +int _regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers, enum regulator_get_type get_type) { int i; int ret; @@ -4802,8 +4789,8 @@ int regulator_bulk_get(struct device *dev, int num_consumers, consumers[i].consumer = NULL; for (i = 0; i < num_consumers; i++) { - consumers[i].consumer = regulator_get(dev, - consumers[i].supply); + consumers[i].consumer = _regulator_get(dev, + consumers[i].supply, get_type); if (IS_ERR(consumers[i].consumer)) { ret = dev_err_probe(dev, PTR_ERR(consumers[i].consumer), "Failed to get supply '%s'", @@ -4830,6 +4817,26 @@ err: return ret; } + +/** + * regulator_bulk_get - get multiple regulator consumers + * + * @dev: Device to supply + * @num_consumers: Number of consumers to register + * @consumers: Configuration of consumers; clients are stored here. + * + * @return 0 on success, an errno on failure. + * + * This helper function allows drivers to get several regulator + * consumers in one operation. If any of the regulators cannot be + * acquired then any regulators that were allocated will be freed + * before returning to the caller. + */ +int regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers) +{ + return _regulator_bulk_get(dev, num_consumers, consumers, NORMAL_GET); +} EXPORT_SYMBOL_GPL(regulator_bulk_get); static void regulator_bulk_enable_async(void *data, async_cookie_t cookie) diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c index 3265e75e97ab..fec0398d98b0 100644 --- a/drivers/regulator/devres.c +++ b/drivers/regulator/devres.c @@ -186,23 +186,9 @@ static void devm_regulator_bulk_release(struct device *dev, void *res) regulator_bulk_free(devres->num_consumers, devres->consumers); } -/** - * devm_regulator_bulk_get - managed get multiple regulator consumers - * - * @dev: device to supply - * @num_consumers: number of consumers to register - * @consumers: configuration of consumers; clients are stored here. - * - * @return 0 on success, an errno on failure. - * - * This helper function allows drivers to get several regulator - * consumers in one operation with management, the regulators will - * automatically be freed when the device is unbound. If any of the - * regulators cannot be acquired then any regulators that were - * allocated will be freed before returning to the caller. - */ -int devm_regulator_bulk_get(struct device *dev, int num_consumers, - struct regulator_bulk_data *consumers) +static int _devm_regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers, + enum regulator_get_type get_type) { struct regulator_bulk_devres *devres; int ret; @@ -212,7 +198,7 @@ int devm_regulator_bulk_get(struct device *dev, int num_consumers, if (!devres) return -ENOMEM; - ret = regulator_bulk_get(dev, num_consumers, consumers); + ret = _regulator_bulk_get(dev, num_consumers, consumers, get_type); if (!ret) { devres->consumers = consumers; devres->num_consumers = num_consumers; @@ -223,9 +209,53 @@ int devm_regulator_bulk_get(struct device *dev, int num_consumers, return ret; } + +/** + * devm_regulator_bulk_get - managed get multiple regulator consumers + * + * @dev: device to supply + * @num_consumers: number of consumers to register + * @consumers: configuration of consumers; clients are stored here. + * + * @return 0 on success, an errno on failure. + * + * This helper function allows drivers to get several regulator + * consumers in one operation with management, the regulators will + * automatically be freed when the device is unbound. If any of the + * regulators cannot be acquired then any regulators that were + * allocated will be freed before returning to the caller. + */ +int devm_regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers) +{ + return _devm_regulator_bulk_get(dev, num_consumers, consumers, NORMAL_GET); +} EXPORT_SYMBOL_GPL(devm_regulator_bulk_get); /** + * devm_regulator_bulk_get_exclusive - managed exclusive get of multiple + * regulator consumers + * + * @dev: device to supply + * @num_consumers: number of consumers to register + * @consumers: configuration of consumers; clients are stored here. + * + * @return 0 on success, an errno on failure. + * + * This helper function allows drivers to exclusively get several + * regulator consumers in one operation with management, the regulators + * will automatically be freed when the device is unbound. If any of + * the regulators cannot be acquired then any regulators that were + * allocated will be freed before returning to the caller. + */ +int devm_regulator_bulk_get_exclusive(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers) +{ + return _devm_regulator_bulk_get(dev, num_consumers, consumers, EXCLUSIVE_GET); +} +EXPORT_SYMBOL_GPL(devm_regulator_bulk_get_exclusive); + +/** * devm_regulator_bulk_get_const - devm_regulator_bulk_get() w/ const data * * @dev: device to supply diff --git a/drivers/regulator/internal.h b/drivers/regulator/internal.h index 1e9c71642143..fb4433068d29 100644 --- a/drivers/regulator/internal.h +++ b/drivers/regulator/internal.h @@ -122,4 +122,6 @@ enum regulator_get_type { struct regulator *_regulator_get(struct device *dev, const char *id, enum regulator_get_type get_type); +int _regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers, enum regulator_get_type get_type); #endif diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 4158ff126a67..f90bcdeecea5 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -1187,7 +1187,7 @@ static const struct rpmh_vreg_init_data pm7325_vreg_data[] = { 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"), - RPMH_VREG("smps3", "smp%s3", &pmic5_hfsmps510, "vdd-s3"), + RPMH_VREG("smps3", "smp%s3", &pmic5_hfsmps515, "vdd-s3"), 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"), diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c index f98168d58dce..9f2b58458841 100644 --- a/drivers/regulator/qcom_smd-regulator.c +++ b/drivers/regulator/qcom_smd-regulator.c @@ -677,6 +677,24 @@ static const struct regulator_desc pm6125_ftsmps = { .ops = &rpm_smps_ldo_ops, }; +static const struct regulator_desc pmic5_ftsmps520 = { + .linear_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(300000, 0, 263, 4000), + }, + .n_linear_ranges = 1, + .n_voltages = 264, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pmic5_hfsmps515 = { + .linear_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(320000, 0, 235, 16000), + }, + .n_linear_ranges = 1, + .n_voltages = 236, + .ops = &rpm_smps_ldo_ops, +}; + static const struct regulator_desc pms405_hfsmps3 = { .linear_ranges = (struct linear_range[]) { REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000), @@ -1265,6 +1283,20 @@ static const struct rpm_regulator_data rpm_pmi8998_regulators[] = { {} }; +static const struct rpm_regulator_data rpm_pmr735a_regulators[] = { + { "s1", QCOM_SMD_RPM_SMPE, 1, &pmic5_ftsmps520, "vdd_s1"}, + { "s2", QCOM_SMD_RPM_SMPE, 2, &pmic5_ftsmps520, "vdd_s2"}, + { "s3", QCOM_SMD_RPM_SMPE, 3, &pmic5_hfsmps515, "vdd_s3"}, + { "l1", QCOM_SMD_RPM_LDOE, 1, &pm660_nldo660, "vdd_l1_l2"}, + { "l2", QCOM_SMD_RPM_LDOE, 2, &pm660_nldo660, "vdd_l1_l2"}, + { "l3", QCOM_SMD_RPM_LDOE, 3, &pm660_nldo660, "vdd_l3"}, + { "l4", QCOM_SMD_RPM_LDOE, 4, &pm660_ht_lvpldo, "vdd_l4"}, + { "l5", QCOM_SMD_RPM_LDOE, 5, &pm660_nldo660, "vdd_l5_l6"}, + { "l6", QCOM_SMD_RPM_LDOE, 6, &pm660_nldo660, "vdd_l5_l6"}, + { "l7", QCOM_SMD_RPM_LDOE, 7, &pm660_pldo660, "vdd_l7_bob"}, + {} +}; + static const struct rpm_regulator_data rpm_pms405_regulators[] = { { "s1", QCOM_SMD_RPM_SMPA, 1, &pms405_hfsmps3, "vdd_s1" }, { "s2", QCOM_SMD_RPM_SMPA, 2, &pms405_hfsmps3, "vdd_s2" }, @@ -1305,6 +1337,7 @@ static const struct of_device_id rpm_of_match[] = { { .compatible = "qcom,rpm-pma8084-regulators", .data = &rpm_pma8084_regulators }, { .compatible = "qcom,rpm-pmi8994-regulators", .data = &rpm_pmi8994_regulators }, { .compatible = "qcom,rpm-pmi8998-regulators", .data = &rpm_pmi8998_regulators }, + { .compatible = "qcom,rpm-pmr735a-regulators", .data = &rpm_pmr735a_regulators }, { .compatible = "qcom,rpm-pms405-regulators", .data = &rpm_pms405_regulators }, {} }; diff --git a/drivers/regulator/rt6190-regulator.c b/drivers/regulator/rt6190-regulator.c new file mode 100644 index 000000000000..995e028abdd7 --- /dev/null +++ b/drivers/regulator/rt6190-regulator.c @@ -0,0 +1,495 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 Richtek Technology Corp. + * + * Author: ChiYuan Huang <cy_huang@richtek.com> + * + */ + +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/of_regulator.h> + +#define RT6190_REG_VID 0x00 +#define RT6190_REG_OUTV 0x01 +#define RT6190_REG_OUTC 0x03 +#define RT6190_REG_SET1 0x0D +#define RT6190_REG_SET2 0x0E +#define RT6190_REG_SET4 0x10 +#define RT6190_REG_RATIO 0x11 +#define RT6190_REG_OUT_VOLT_L 0x12 +#define RT6190_REG_TEMP_H 0x1B +#define RT6190_REG_STAT1 0x1C +#define RT6190_REG_ALERT1 0x1E +#define RT6190_REG_ALERT2 0x1F +#define RT6190_REG_MASK2 0x21 +#define RT6190_REG_OCPEN 0x28 +#define RT6190_REG_SET5 0x29 +#define RT6190_REG_VBUSC_ADC 0x32 +#define RT6190_REG_BUSC_VOLT_L 0x33 +#define RT6190_REG_BUSC_VOLT_H 0x34 +#define RT6190_REG_STAT3 0x37 +#define RT6190_REG_ALERT3 0x38 +#define RT6190_REG_MASK3 0x39 + +#define RT6190_ENPWM_MASK BIT(7) +#define RT6190_ENDCHG_MASK BIT(4) +#define RT6190_ALERT_OTPEVT BIT(6) +#define RT6190_ALERT_UVPEVT BIT(5) +#define RT6190_ALERT_OVPEVT BIT(4) +#define RT6190_ENGCP_MASK BIT(1) +#define RT6190_FCCM_MASK BIT(7) + +#define RICHTEK_VID 0x82 +#define RT6190_OUT_MIN_UV 3000000 +#define RT6190_OUT_MAX_UV 32000000 +#define RT6190_OUT_STEP_UV 20000 +#define RT6190_OUT_N_VOLT (RT6190_OUT_MAX_UV / RT6190_OUT_STEP_UV + 1) +#define RT6190_OUTV_MINSEL 150 +#define RT6190_OUT_MIN_UA 306000 +#define RT6190_OUT_MAX_UA 12114000 +#define RT6190_OUT_STEP_UA 24000 +#define RT6190_OUTC_MINSEL 19 +#define RT6190_EN_TIME_US 500 + +#define RT6190_PSM_MODE 0 +#define RT6190_FCCM_MODE 1 + +struct rt6190_data { + struct device *dev; + struct regmap *regmap; + struct gpio_desc *enable_gpio; + unsigned int cached_alert_evt; +}; + +static int rt6190_out_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + __le16 le_sel = cpu_to_le16(selector); + + return regmap_raw_write(regmap, RT6190_REG_OUTV, &le_sel, + sizeof(le_sel)); +} + +static int rt6190_out_get_voltage_sel(struct regulator_dev *rdev) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + __le16 le_sel; + int ret; + + ret = regmap_raw_read(regmap, RT6190_REG_OUTV, &le_sel, sizeof(le_sel)); + + return ret ?: le16_to_cpu(le_sel); +} + +static int rt6190_out_enable(struct regulator_dev *rdev) +{ + struct rt6190_data *data = rdev_get_drvdata(rdev); + struct regmap *regmap = rdev_get_regmap(rdev); + u8 out_cfg[4]; + int ret; + + pm_runtime_get_sync(data->dev); + + /* + * From off to on, vout config will restore to IC default. + * Read vout configs before enable, and restore them after enable + */ + ret = regmap_raw_read(regmap, RT6190_REG_OUTV, out_cfg, + sizeof(out_cfg)); + if (ret) + return ret; + + ret = regulator_enable_regmap(rdev); + if (ret) + return ret; + + ret = regmap_raw_write(regmap, RT6190_REG_OUTV, out_cfg, + sizeof(out_cfg)); + if (ret) + return ret; + + return regmap_update_bits(regmap, RT6190_REG_SET5, RT6190_ENGCP_MASK, + RT6190_ENGCP_MASK); +} + +static int rt6190_out_disable(struct regulator_dev *rdev) +{ + struct rt6190_data *data = rdev_get_drvdata(rdev); + struct regmap *regmap = rdev_get_regmap(rdev); + int ret; + + ret = regmap_update_bits(regmap, RT6190_REG_SET5, RT6190_ENGCP_MASK, 0); + if (ret) + return ret; + + ret = regulator_disable_regmap(rdev); + if (ret) + return ret; + + /* cleared cached alert event */ + data->cached_alert_evt = 0; + + pm_runtime_put(data->dev); + + return 0; +} + +static int rt6190_out_set_current_limit(struct regulator_dev *rdev, int min_uA, + int max_uA) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + int csel, clim; + __le16 le_csel; + + if (min_uA < RT6190_OUT_MIN_UA || max_uA > RT6190_OUT_MAX_UA) + return -EINVAL; + + csel = DIV_ROUND_UP(min_uA - RT6190_OUT_MIN_UA, RT6190_OUT_STEP_UA); + + clim = RT6190_OUT_MIN_UA + RT6190_OUT_STEP_UA * csel; + if (clim > max_uA) + return -EINVAL; + + csel += RT6190_OUTC_MINSEL; + le_csel = cpu_to_le16(csel); + + return regmap_raw_write(regmap, RT6190_REG_OUTC, &le_csel, + sizeof(le_csel)); +} + +static int rt6190_out_get_current_limit(struct regulator_dev *rdev) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + __le16 le_csel; + int csel, ret; + + ret = regmap_raw_read(regmap, RT6190_REG_OUTC, &le_csel, + sizeof(le_csel)); + if (ret) + return ret; + + csel = le16_to_cpu(le_csel); + csel -= RT6190_OUTC_MINSEL; + + return RT6190_OUT_MIN_UA + RT6190_OUT_STEP_UA * csel; +} + +static int rt6190_out_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + unsigned int val; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = RT6190_FCCM_MASK; + break; + case REGULATOR_MODE_NORMAL: + val = 0; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(regmap, RT6190_REG_SET1, RT6190_FCCM_MASK, + val); +} + +static unsigned int rt6190_out_get_mode(struct regulator_dev *rdev) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + unsigned int config; + int ret; + + ret = regmap_read(regmap, RT6190_REG_SET1, &config); + if (ret) + return REGULATOR_MODE_INVALID; + + if (config & RT6190_FCCM_MASK) + return REGULATOR_MODE_FAST; + + return REGULATOR_MODE_NORMAL; +} + +static int rt6190_out_get_error_flags(struct regulator_dev *rdev, + unsigned int *flags) +{ + struct rt6190_data *data = rdev_get_drvdata(rdev); + unsigned int state, rpt_flags = 0; + int ret; + + ret = regmap_read(data->regmap, RT6190_REG_STAT1, &state); + if (ret) + return ret; + + state |= data->cached_alert_evt; + + if (state & RT6190_ALERT_OTPEVT) + rpt_flags |= REGULATOR_ERROR_OVER_TEMP; + + if (state & RT6190_ALERT_UVPEVT) + rpt_flags |= REGULATOR_ERROR_UNDER_VOLTAGE; + + if (state & RT6190_ALERT_OVPEVT) + rpt_flags |= REGULATOR_ERROR_REGULATION_OUT; + + *flags = rpt_flags; + + return 0; +} + +static unsigned int rt6190_out_of_map_mode(unsigned int mode) +{ + switch (mode) { + case RT6190_PSM_MODE: + return REGULATOR_MODE_NORMAL; + case RT6190_FCCM_MODE: + return REGULATOR_MODE_FAST; + default: + return REGULATOR_MODE_INVALID; + } +} + +static const struct regulator_ops rt6190_regulator_ops = { + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = rt6190_out_set_voltage_sel, + .get_voltage_sel = rt6190_out_get_voltage_sel, + .enable = rt6190_out_enable, + .disable = rt6190_out_disable, + .is_enabled = regulator_is_enabled_regmap, + .set_current_limit = rt6190_out_set_current_limit, + .get_current_limit = rt6190_out_get_current_limit, + .set_active_discharge = regulator_set_active_discharge_regmap, + .set_mode = rt6190_out_set_mode, + .get_mode = rt6190_out_get_mode, + .get_error_flags = rt6190_out_get_error_flags, +}; + +static const struct regulator_desc rt6190_regulator_desc = { + .name = "rt6190-regulator", + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .ops = &rt6190_regulator_ops, + .min_uV = RT6190_OUT_MIN_UV, + .uV_step = RT6190_OUT_STEP_UV, + .n_voltages = RT6190_OUT_N_VOLT, + .linear_min_sel = RT6190_OUTV_MINSEL, + .enable_reg = RT6190_REG_SET2, + .enable_mask = RT6190_ENPWM_MASK, + .active_discharge_reg = RT6190_REG_SET2, + .active_discharge_mask = RT6190_ENDCHG_MASK, + .active_discharge_on = RT6190_ENDCHG_MASK, + .of_map_mode = rt6190_out_of_map_mode, +}; + +static bool rt6190_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT6190_REG_OUT_VOLT_L ... RT6190_REG_ALERT2: + case RT6190_REG_BUSC_VOLT_L ... RT6190_REG_BUSC_VOLT_H: + case RT6190_REG_STAT3 ... RT6190_REG_ALERT3: + return true; + default: + return false; + } +} + +static const struct regmap_config rt6190_regmap_config = { + .name = "rt6190", + .cache_type = REGCACHE_FLAT, + .reg_bits = 8, + .val_bits = 8, + .max_register = RT6190_REG_MASK3, + .num_reg_defaults_raw = RT6190_REG_MASK3 + 1, + .volatile_reg = rt6190_is_volatile_reg, +}; + +static irqreturn_t rt6190_irq_handler(int irq, void *devid) +{ + struct regulator_dev *rdev = devid; + struct rt6190_data *data = rdev_get_drvdata(rdev); + unsigned int alert; + int ret; + + ret = regmap_read(data->regmap, RT6190_REG_ALERT1, &alert); + if (ret) + return IRQ_NONE; + + /* Write clear alert events */ + ret = regmap_write(data->regmap, RT6190_REG_ALERT1, alert); + if (ret) + return IRQ_NONE; + + data->cached_alert_evt |= alert; + + if (alert & RT6190_ALERT_OTPEVT) + regulator_notifier_call_chain(rdev, REGULATOR_EVENT_OVER_TEMP, NULL); + + if (alert & RT6190_ALERT_UVPEVT) + regulator_notifier_call_chain(rdev, REGULATOR_EVENT_UNDER_VOLTAGE, NULL); + + if (alert & RT6190_ALERT_OVPEVT) + regulator_notifier_call_chain(rdev, REGULATOR_EVENT_REGULATION_OUT, NULL); + + return IRQ_HANDLED; +} + +static int rt6190_init_registers(struct regmap *regmap) +{ + int ret; + + /* Enable_ADC = 1 */ + ret = regmap_write(regmap, RT6190_REG_SET4, 0x82); + if (ret) + return ret; + + /* Config default VOUT ratio to be higher */ + ret = regmap_write(regmap, RT6190_REG_RATIO, 0x20); + + /* Mask unused alert */ + ret = regmap_write(regmap, RT6190_REG_MASK2, 0); + if (ret) + return ret; + + /* OCP config */ + ret = regmap_write(regmap, RT6190_REG_OCPEN, 0); + if (ret) + return ret; + + /* Enable VBUSC ADC */ + return regmap_write(regmap, RT6190_REG_VBUSC_ADC, 0x02); +} + +static int rt6190_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct rt6190_data *data; + struct gpio_desc *enable_gpio; + struct regmap *regmap; + struct regulator_dev *rdev; + struct regulator_config cfg = {}; + unsigned int vid; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(enable_gpio)) + return dev_err_probe(dev, PTR_ERR(enable_gpio), "Failed to get 'enable' gpio\n"); + else if (enable_gpio) + usleep_range(RT6190_EN_TIME_US, RT6190_EN_TIME_US * 2); + + regmap = devm_regmap_init_i2c(i2c, &rt6190_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n"); + + data->dev = dev; + data->enable_gpio = enable_gpio; + data->regmap = regmap; + i2c_set_clientdata(i2c, data); + + ret = regmap_read(regmap, RT6190_REG_VID, &vid); + if (ret) + return dev_err_probe(dev, ret, "Failed to read VID\n"); + + if (vid != RICHTEK_VID) + return dev_err_probe(dev, -ENODEV, "Incorrect VID 0x%02x\n", vid); + + ret = rt6190_init_registers(regmap); + if (ret) + return dev_err_probe(dev, ret, "Failed to init registers\n"); + + pm_runtime_set_active(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to set pm_runtime enable\n"); + + cfg.dev = dev; + cfg.of_node = dev->of_node; + cfg.driver_data = data; + cfg.init_data = of_get_regulator_init_data(dev, dev->of_node, + &rt6190_regulator_desc); + + rdev = devm_regulator_register(dev, &rt6190_regulator_desc, &cfg); + if (IS_ERR(rdev)) + return dev_err_probe(dev, PTR_ERR(rdev), "Failed to register regulator\n"); + + if (i2c->irq) { + ret = devm_request_threaded_irq(dev, i2c->irq, NULL, + rt6190_irq_handler, + IRQF_ONESHOT, dev_name(dev), + rdev); + if (ret) + return dev_err_probe(dev, ret, "Failed to register interrupt\n"); + } + + return 0; +} + +static int rt6190_runtime_suspend(struct device *dev) +{ + struct rt6190_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + + if (!data->enable_gpio) + return 0; + + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + + gpiod_set_value(data->enable_gpio, 0); + + return 0; +} + +static int rt6190_runtime_resume(struct device *dev) +{ + struct rt6190_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + + if (!data->enable_gpio) + return 0; + + gpiod_set_value(data->enable_gpio, 1); + usleep_range(RT6190_EN_TIME_US, RT6190_EN_TIME_US * 2); + + regcache_cache_only(regmap, false); + return regcache_sync(regmap); +} + +static const struct dev_pm_ops __maybe_unused rt6190_dev_pm = { + RUNTIME_PM_OPS(rt6190_runtime_suspend, rt6190_runtime_resume, NULL) +}; + +static const struct of_device_id rt6190_of_dev_table[] = { + { .compatible = "richtek,rt6190" }, + {} +}; +MODULE_DEVICE_TABLE(of, rt6190_of_dev_table); + +static struct i2c_driver rt6190_driver = { + .driver = { + .name = "rt6190", + .of_match_table = rt6190_of_dev_table, + .pm = pm_ptr(&rt6190_dev_pm), + }, + .probe_new = rt6190_probe, +}; +module_i2c_driver(rt6190_driver); + +MODULE_DESCRIPTION("Richtek RT6190 regulator driver"); +MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/userspace-consumer.c b/drivers/regulator/userspace-consumer.c index 8ca28664776e..402c8037cf39 100644 --- a/drivers/regulator/userspace-consumer.c +++ b/drivers/regulator/userspace-consumer.c @@ -14,6 +14,7 @@ #include <linux/err.h> #include <linux/mutex.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/regulator/userspace-consumer.h> @@ -24,6 +25,7 @@ struct userspace_consumer_data { struct mutex lock; bool enabled; + bool no_autoswitch; int num_supplies; struct regulator_bulk_data *supplies; @@ -96,19 +98,50 @@ static struct attribute *attributes[] = { NULL, }; +static umode_t attr_visible(struct kobject *kobj, struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct userspace_consumer_data *data = dev_get_drvdata(dev); + + /* If a name hasn't been set, don't bother with the attribute */ + if (attr == &dev_attr_name.attr && !data->name) + return 0; + + return attr->mode; +} + static const struct attribute_group attr_group = { .attrs = attributes, + .is_visible = attr_visible, }; static int regulator_userspace_consumer_probe(struct platform_device *pdev) { + struct regulator_userspace_consumer_data tmpdata; struct regulator_userspace_consumer_data *pdata; struct userspace_consumer_data *drvdata; int ret; pdata = dev_get_platdata(&pdev->dev); - if (!pdata) + if (!pdata) { + if (!pdev->dev.of_node) + return -EINVAL; + + pdata = &tmpdata; + memset(pdata, 0, sizeof(*pdata)); + + pdata->no_autoswitch = true; + pdata->num_supplies = 1; + pdata->supplies = devm_kzalloc(&pdev->dev, sizeof(*pdata->supplies), GFP_KERNEL); + if (!pdata->supplies) + return -ENOMEM; + pdata->supplies[0].supply = "vout"; + } + + if (pdata->num_supplies < 1) { + dev_err(&pdev->dev, "At least one supply required\n"); return -EINVAL; + } drvdata = devm_kzalloc(&pdev->dev, sizeof(struct userspace_consumer_data), @@ -119,21 +152,24 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev) drvdata->name = pdata->name; drvdata->num_supplies = pdata->num_supplies; drvdata->supplies = pdata->supplies; + drvdata->no_autoswitch = pdata->no_autoswitch; mutex_init(&drvdata->lock); - ret = devm_regulator_bulk_get(&pdev->dev, drvdata->num_supplies, - drvdata->supplies); + ret = devm_regulator_bulk_get_exclusive(&pdev->dev, drvdata->num_supplies, + drvdata->supplies); if (ret) { dev_err(&pdev->dev, "Failed to get supplies: %d\n", ret); return ret; } + platform_set_drvdata(pdev, drvdata); + ret = sysfs_create_group(&pdev->dev.kobj, &attr_group); if (ret != 0) return ret; - if (pdata->init_on) { + if (pdata->init_on && !pdata->no_autoswitch) { ret = regulator_bulk_enable(drvdata->num_supplies, drvdata->supplies); if (ret) { @@ -143,8 +179,12 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev) } } - drvdata->enabled = pdata->init_on; - platform_set_drvdata(pdev, drvdata); + ret = regulator_is_enabled(pdata->supplies[0].consumer); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get regulator status\n"); + goto err_enable; + } + drvdata->enabled = !!ret; return 0; @@ -160,17 +200,23 @@ static int regulator_userspace_consumer_remove(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &attr_group); - if (data->enabled) + if (data->enabled && !data->no_autoswitch) regulator_bulk_disable(data->num_supplies, data->supplies); return 0; } +static const struct of_device_id regulator_userspace_consumer_of_match[] = { + { .compatible = "regulator-output", }, + {}, +}; + static struct platform_driver regulator_userspace_consumer_driver = { .probe = regulator_userspace_consumer_probe, .remove = regulator_userspace_consumer_remove, .driver = { .name = "reg-userspace-consumer", + .of_match_table = regulator_userspace_consumer_of_match, }, }; diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 276488dad61d..39b666b40ea6 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -249,6 +249,8 @@ int __must_check of_regulator_bulk_get_all(struct device *dev, struct device_nod int __must_check devm_regulator_bulk_get(struct device *dev, int num_consumers, struct regulator_bulk_data *consumers); void devm_regulator_bulk_put(struct regulator_bulk_data *consumers); +int __must_check devm_regulator_bulk_get_exclusive(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers); int __must_check devm_regulator_bulk_get_const( struct device *dev, int num_consumers, const struct regulator_bulk_data *in_consumers, diff --git a/include/linux/regulator/userspace-consumer.h b/include/linux/regulator/userspace-consumer.h index b5dba0628951..2249ee697f8b 100644 --- a/include/linux/regulator/userspace-consumer.h +++ b/include/linux/regulator/userspace-consumer.h @@ -21,6 +21,7 @@ struct regulator_userspace_consumer_data { struct regulator_bulk_data *supplies; bool init_on; + bool no_autoswitch; }; #endif /* __REGULATOR_PLATFORM_CONSUMER_H_ */ diff --git a/include/linux/soc/qcom/smd-rpm.h b/include/linux/soc/qcom/smd-rpm.h index 3ab8c07f71c0..62de54992e49 100644 --- a/include/linux/soc/qcom/smd-rpm.h +++ b/include/linux/soc/qcom/smd-rpm.h @@ -19,6 +19,7 @@ struct qcom_smd_rpm; #define QCOM_SMD_RPM_CLK_BUF_A 0x616B6C63 #define QCOM_SMD_RPM_LDOA 0x616f646c #define QCOM_SMD_RPM_LDOB 0x626F646C +#define QCOM_SMD_RPM_LDOE 0x656f646c #define QCOM_SMD_RPM_RWCX 0x78637772 #define QCOM_SMD_RPM_RWMX 0x786d7772 #define QCOM_SMD_RPM_RWLC 0x636c7772 @@ -32,6 +33,7 @@ struct qcom_smd_rpm; #define QCOM_SMD_RPM_QUP_CLK 0x707571 #define QCOM_SMD_RPM_SMPA 0x61706d73 #define QCOM_SMD_RPM_SMPB 0x62706d73 +#define QCOM_SMD_RPM_SMPE 0x65706d73 #define QCOM_SMD_RPM_SPDM 0x63707362 #define QCOM_SMD_RPM_VSA 0x00617376 #define QCOM_SMD_RPM_MMAXI_CLK 0x69786d6d diff --git a/lib/test_linear_ranges.c b/lib/test_linear_ranges.c index 676e0b8abcdd..c18f9c0f1f25 100644 --- a/lib/test_linear_ranges.c +++ b/lib/test_linear_ranges.c @@ -107,17 +107,8 @@ static const unsigned int range2_vals[] = { RANGE2_MIN, RANGE2_MIN + #define SMALLEST_VAL RANGE1_MIN static struct linear_range testr[] = { - { - .min = RANGE1_MIN, - .min_sel = RANGE1_MIN_SEL, - .max_sel = RANGE1_MAX_SEL, - .step = RANGE1_STEP, - }, { - .min = RANGE2_MIN, - .min_sel = RANGE2_MIN_SEL, - .max_sel = RANGE2_MAX_SEL, - .step = RANGE2_STEP - }, + LINEAR_RANGE(RANGE1_MIN, RANGE1_MIN_SEL, RANGE1_MAX_SEL, RANGE1_STEP), + LINEAR_RANGE(RANGE2_MIN, RANGE2_MIN_SEL, RANGE2_MAX_SEL, RANGE2_STEP), }; static void range_test_get_value(struct kunit *test) |