From 05dfeb2d0ccf87a7b92cd149a393b8423a26a04e Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Wed, 17 Jun 2026 23:08:43 +0530 Subject: [PATCH 1/3] regulator: qcom-refgen: correct the regulator type to CURRENT As per the REFGEN IP team, this block supplies the reference current to the PHYs in the SoC. So, correct the regulator type to REGULATOR_CURRENT to match with the HW behavior. Fixes: 7cbfbe237960 ("regulator: Introduce Qualcomm REFGEN regulator driver") Cc: stable@vger.kernel.org Reviewed-by: Konrad Dybcio Signed-off-by: Kathiravan Thirumoorthy Link: https://patch.msgid.link/20260617-ipq9650_refgen-v4-1-c505ea6c6661@oss.qualcomm.com Signed-off-by: Mark Brown --- drivers/regulator/qcom-refgen-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/qcom-refgen-regulator.c b/drivers/regulator/qcom-refgen-regulator.c index 299ac3c8c3bc..6a3795469927 100644 --- a/drivers/regulator/qcom-refgen-regulator.c +++ b/drivers/regulator/qcom-refgen-regulator.c @@ -66,7 +66,7 @@ static const struct regulator_desc sdm845_refgen_desc = { .enable_time = 5, .name = "refgen", .owner = THIS_MODULE, - .type = REGULATOR_VOLTAGE, + .type = REGULATOR_CURRENT, .ops = &(const struct regulator_ops) { .enable = qcom_sdm845_refgen_enable, .disable = qcom_sdm845_refgen_disable, @@ -82,7 +82,7 @@ static const struct regulator_desc sm8250_refgen_desc = { .enable_time = 5, .name = "refgen", .owner = THIS_MODULE, - .type = REGULATOR_VOLTAGE, + .type = REGULATOR_CURRENT, .ops = &(const struct regulator_ops) { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, From 7b5cd466e48818e5cf75cbf4aad14c9ed5d08fe2 Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Wed, 17 Jun 2026 23:08:44 +0530 Subject: [PATCH 2/3] regulator: dt-bindings: qcom,sdm845-refgen-regulator: Document IPQ9650 IPQ9650 has two REFGEN blocks which provide reference current to the PCIe, USB and UNIPHY PHYs. Unlike other supported platforms, IPQ9650 requires the REFGEN clocks to be enabled explicitly. Document the IPQ9650 compatible and the required clocks for it. While at it, move the allOf block after the 'required' property section. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Kathiravan Thirumoorthy Link: https://patch.msgid.link/20260617-ipq9650_refgen-v4-2-c505ea6c6661@oss.qualcomm.com Signed-off-by: Mark Brown --- .../qcom,sdm845-refgen-regulator.yaml | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/qcom,sdm845-refgen-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,sdm845-refgen-regulator.yaml index 40f9223d4c27..0bbf7c806fbc 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,sdm845-refgen-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom,sdm845-refgen-regulator.yaml @@ -13,9 +13,6 @@ description: The REFGEN (reference voltage generator) regulator provides reference voltage for on-chip IPs (like PHYs) on some Qualcomm SoCs. -allOf: - - $ref: regulator.yaml# - properties: compatible: oneOf: @@ -39,16 +36,44 @@ properties: - const: qcom,sm8250-refgen-regulator - enum: + - qcom,ipq9650-refgen-regulator - qcom,sdm845-refgen-regulator - qcom,sm8250-refgen-regulator reg: maxItems: 1 + clocks: + items: + - description: Core reference clock + - description: AHB interface clock + + clock-names: + items: + - const: core + - const: hclk + required: - compatible - reg +allOf: + - $ref: regulator.yaml# + + - if: + properties: + compatible: + contains: + const: qcom,ipq9650-refgen-regulator + then: + required: + - clocks + - clock-names + else: + properties: + clocks: false + clock-names: false + unevaluatedProperties: false examples: From ca5c1a0ca229fa7e035735e07f1b8959059d03bf Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Wed, 17 Jun 2026 23:08:45 +0530 Subject: [PATCH 3/3] regulator: qcom-refgen: add support for the IPQ9650 SoC IPQ9650 SoC has 2 REFGEN blocks providing the reference current to the PCIe and USB, UNIPHY PHYs. For the other SoCs, clock for this block is enabled on power up but that's not the case for IPQ9650 and we have to enable those clocks explicitly to bring up the PHYs properly. Also, add the get_status() callback to report the regulator status to the userspace. Signed-off-by: Kathiravan Thirumoorthy Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260617-ipq9650_refgen-v4-3-c505ea6c6661@oss.qualcomm.com Signed-off-by: Mark Brown --- drivers/regulator/qcom-refgen-regulator.c | 109 +++++++++++++++++++++- 1 file changed, 105 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/qcom-refgen-regulator.c b/drivers/regulator/qcom-refgen-regulator.c index 6a3795469927..cc72e5a43ba6 100644 --- a/drivers/regulator/qcom-refgen-regulator.c +++ b/drivers/regulator/qcom-refgen-regulator.c @@ -3,6 +3,7 @@ // Copyright (c) 2023, Linaro Limited #include +#include #include #include #include @@ -16,6 +17,9 @@ #define REFGEN_BIAS_EN_ENABLE 0x7 #define REFGEN_BIAS_EN_DISABLE 0x6 +#define REFGEN_REG_REFGEN_STATUS 0xc +#define REFGEN_STATUS_OUT_MASK BIT(3) + #define REFGEN_REG_BG_CTRL 0x14 #define REFGEN_BG_CTRL_MASK GENMASK(2, 1) #define REFGEN_BG_CTRL_ENABLE 0x3 @@ -25,6 +29,17 @@ #define REFGEN_PWRDWN_CTRL5_MASK BIT(0) #define REFGEN_PWRDWN_CTRL5_ENABLE 0x1 +struct qcom_refgen_regulator_data { + const struct regulator_desc *rdesc; + bool has_clocks; +}; + +struct qcom_refgen_drvdata { + struct clk_bulk_data *clks; + int num_clks; + bool is_enabled; +}; + static int qcom_sdm845_refgen_enable(struct regulator_dev *rdev) { regmap_update_bits(rdev->regmap, REFGEN_REG_BG_CTRL, REFGEN_BG_CTRL_MASK, @@ -62,6 +77,62 @@ static int qcom_sdm845_refgen_is_enabled(struct regulator_dev *rdev) return 1; } +static int qcom_ipq9650_refgen_enable(struct regulator_dev *rdev) +{ + struct qcom_refgen_drvdata *drvdata = rdev_get_drvdata(rdev); + int ret; + + ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks); + if (ret) + return ret; + + drvdata->is_enabled = true; + + return 0; +} + +static int qcom_ipq9650_refgen_disable(struct regulator_dev *rdev) +{ + struct qcom_refgen_drvdata *drvdata = rdev_get_drvdata(rdev); + + clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks); + + drvdata->is_enabled = false; + + return 0; +} + +static int qcom_ipq9650_refgen_is_enabled(struct regulator_dev *rdev) +{ + struct qcom_refgen_drvdata *drvdata = rdev_get_drvdata(rdev); + + return drvdata->is_enabled; +} + +static int qcom_ipq9650_refgen_get_status(struct regulator_dev *rdev) +{ + u32 val; + + regmap_read(rdev->regmap, REFGEN_REG_REFGEN_STATUS, &val); + if (FIELD_GET(REFGEN_STATUS_OUT_MASK, val)) + return REGULATOR_STATUS_ON; + + return REGULATOR_STATUS_OFF; +} + +static const struct regulator_desc ipq9650_refgen_desc = { + .enable_time = 5, + .name = "refgen", + .owner = THIS_MODULE, + .type = REGULATOR_CURRENT, + .ops = &(const struct regulator_ops) { + .enable = qcom_ipq9650_refgen_enable, + .disable = qcom_ipq9650_refgen_disable, + .is_enabled = qcom_ipq9650_refgen_is_enabled, + .get_status = qcom_ipq9650_refgen_get_status, + }, +}; + static const struct regulator_desc sdm845_refgen_desc = { .enable_time = 5, .name = "refgen", @@ -90,6 +161,19 @@ static const struct regulator_desc sm8250_refgen_desc = { }, }; +static const struct qcom_refgen_regulator_data ipq9650_data = { + .rdesc = &ipq9650_refgen_desc, + .has_clocks = true, +}; + +static const struct qcom_refgen_regulator_data sdm845_data = { + .rdesc = &sdm845_refgen_desc, +}; + +static const struct qcom_refgen_regulator_data sm8250_data = { + .rdesc = &sm8250_refgen_desc, +}; + static const struct regmap_config qcom_refgen_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -98,6 +182,8 @@ static const struct regmap_config qcom_refgen_regmap_config = { static int qcom_refgen_probe(struct platform_device *pdev) { + const struct qcom_refgen_regulator_data *data; + struct qcom_refgen_drvdata *drvdata = NULL; struct regulator_init_data *init_data; struct regulator_config config = {}; const struct regulator_desc *rdesc; @@ -106,10 +192,23 @@ static int qcom_refgen_probe(struct platform_device *pdev) struct regmap *regmap; void __iomem *base; - rdesc = of_device_get_match_data(dev); - if (!rdesc) + data = of_device_get_match_data(dev); + if (!data) return -ENODATA; + if (data->has_clocks) { + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->num_clks = devm_clk_bulk_get_all(dev, &drvdata->clks); + if (drvdata->num_clks < 0) + return dev_err_probe(dev, drvdata->num_clks, + "failed to get clocks\n"); + } + + rdesc = data->rdesc; + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); @@ -126,6 +225,7 @@ static int qcom_refgen_probe(struct platform_device *pdev) config.init_data = init_data; config.of_node = dev->of_node; config.regmap = regmap; + config.driver_data = drvdata; rdev = devm_regulator_register(dev, rdesc, &config); if (IS_ERR(rdev)) @@ -135,8 +235,9 @@ static int qcom_refgen_probe(struct platform_device *pdev) } static const struct of_device_id qcom_refgen_match_table[] = { - { .compatible = "qcom,sdm845-refgen-regulator", .data = &sdm845_refgen_desc }, - { .compatible = "qcom,sm8250-refgen-regulator", .data = &sm8250_refgen_desc }, + { .compatible = "qcom,ipq9650-refgen-regulator", .data = &ipq9650_data }, + { .compatible = "qcom,sdm845-refgen-regulator", .data = &sdm845_data }, + { .compatible = "qcom,sm8250-refgen-regulator", .data = &sm8250_data }, { } }; MODULE_DEVICE_TABLE(of, qcom_refgen_match_table);