Skip to content

Commit 0eb85f4

Browse files
lumagBartosz Golaszewski
authored andcommitted
power: sequencing: qcom-wcn: add support for WCN39xx
The WCN39xx family of WiFi/BT chips incorporates a simple PMU, spreading voltages over internal rails. Implement power sequencing support for this generation of WCN chips. Unlike later devices, they don't have separate enable GPIO lines, letting the chip figure out the necessary parts on its own. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> Link: https://lore.kernel.org/r/20260106-wcn3990-pwrctl-v2-5-0386204328be@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
1 parent a5fae42 commit 0eb85f4

1 file changed

Lines changed: 125 additions & 5 deletions

File tree

drivers/power/sequencing/pwrseq-qcom-wcn.c

Lines changed: 125 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,16 @@ struct pwrseq_qcom_wcn_pdata {
2424
unsigned int pwup_delay_ms;
2525
unsigned int gpio_enable_delay_ms;
2626
const struct pwrseq_target_data **targets;
27+
bool has_vddio; /* separate VDD IO regulator */
28+
int (*match)(struct pwrseq_device *pwrseq, struct device *dev);
2729
};
2830

2931
struct pwrseq_qcom_wcn_ctx {
3032
struct pwrseq_device *pwrseq;
3133
struct device_node *of_node;
3234
const struct pwrseq_qcom_wcn_pdata *pdata;
3335
struct regulator_bulk_data *regs;
36+
struct regulator *vddio;
3437
struct gpio_desc *bt_gpio;
3538
struct gpio_desc *wlan_gpio;
3639
struct gpio_desc *xo_clk_gpio;
@@ -53,6 +56,26 @@ static void pwrseq_qcom_wcn_ensure_gpio_delay(struct pwrseq_qcom_wcn_ctx *ctx)
5356
msleep(ctx->pdata->gpio_enable_delay_ms - diff_msecs);
5457
}
5558

59+
static int pwrseq_qcom_wcn_vddio_enable(struct pwrseq_device *pwrseq)
60+
{
61+
struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
62+
63+
return regulator_enable(ctx->vddio);
64+
}
65+
66+
static int pwrseq_qcom_wcn_vddio_disable(struct pwrseq_device *pwrseq)
67+
{
68+
struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
69+
70+
return regulator_disable(ctx->vddio);
71+
}
72+
73+
static const struct pwrseq_unit_data pwrseq_qcom_wcn_vddio_unit_data = {
74+
.name = "vddio-enable",
75+
.enable = pwrseq_qcom_wcn_vddio_enable,
76+
.disable = pwrseq_qcom_wcn_vddio_disable,
77+
};
78+
5679
static int pwrseq_qcom_wcn_vregs_enable(struct pwrseq_device *pwrseq)
5780
{
5881
struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
@@ -95,6 +118,19 @@ static const struct pwrseq_unit_data pwrseq_qcom_wcn_clk_unit_data = {
95118
.disable = pwrseq_qcom_wcn_clk_disable,
96119
};
97120

121+
static const struct pwrseq_unit_data *pwrseq_qcom_wcn3990_unit_deps[] = {
122+
&pwrseq_qcom_wcn_vddio_unit_data,
123+
&pwrseq_qcom_wcn_vregs_unit_data,
124+
NULL,
125+
};
126+
127+
static const struct pwrseq_unit_data pwrseq_qcom_wcn3990_unit_data = {
128+
.name = "clock-enable",
129+
.deps = pwrseq_qcom_wcn3990_unit_deps,
130+
.enable = pwrseq_qcom_wcn_clk_enable,
131+
.disable = pwrseq_qcom_wcn_clk_disable,
132+
};
133+
98134
static const struct pwrseq_unit_data *pwrseq_qcom_wcn_unit_deps[] = {
99135
&pwrseq_qcom_wcn_vregs_unit_data,
100136
&pwrseq_qcom_wcn_clk_unit_data,
@@ -230,6 +266,17 @@ static const struct pwrseq_target_data pwrseq_qcom_wcn_wlan_target_data = {
230266
.post_enable = pwrseq_qcom_wcn_pwup_delay,
231267
};
232268

269+
/* There are no separate BT and WLAN enablement pins */
270+
static const struct pwrseq_target_data pwrseq_qcom_wcn3990_bt_target_data = {
271+
.name = "bluetooth",
272+
.unit = &pwrseq_qcom_wcn3990_unit_data,
273+
};
274+
275+
static const struct pwrseq_target_data pwrseq_qcom_wcn3990_wlan_target_data = {
276+
.name = "wlan",
277+
.unit = &pwrseq_qcom_wcn3990_unit_data,
278+
};
279+
233280
static const struct pwrseq_target_data pwrseq_qcom_wcn6855_bt_target_data = {
234281
.name = "bluetooth",
235282
.unit = &pwrseq_qcom_wcn6855_bt_unit_data,
@@ -248,6 +295,12 @@ static const struct pwrseq_target_data *pwrseq_qcom_wcn_targets[] = {
248295
NULL
249296
};
250297

298+
static const struct pwrseq_target_data *pwrseq_qcom_wcn3990_targets[] = {
299+
&pwrseq_qcom_wcn3990_bt_target_data,
300+
&pwrseq_qcom_wcn3990_wlan_target_data,
301+
NULL
302+
};
303+
251304
static const struct pwrseq_target_data *pwrseq_qcom_wcn6855_targets[] = {
252305
&pwrseq_qcom_wcn6855_bt_target_data,
253306
&pwrseq_qcom_wcn6855_wlan_target_data,
@@ -273,6 +326,26 @@ static const struct pwrseq_qcom_wcn_pdata pwrseq_qca6390_of_data = {
273326
.targets = pwrseq_qcom_wcn_targets,
274327
};
275328

329+
static const char *const pwrseq_wcn3990_vregs[] = {
330+
/* vddio is handled separately */
331+
"vddxo",
332+
"vddrf",
333+
"vddch0",
334+
"vddch1",
335+
};
336+
337+
static int pwrseq_qcom_wcn3990_match(struct pwrseq_device *pwrseq,
338+
struct device *dev);
339+
340+
static const struct pwrseq_qcom_wcn_pdata pwrseq_wcn3990_of_data = {
341+
.vregs = pwrseq_wcn3990_vregs,
342+
.num_vregs = ARRAY_SIZE(pwrseq_wcn3990_vregs),
343+
.pwup_delay_ms = 50,
344+
.targets = pwrseq_qcom_wcn3990_targets,
345+
.has_vddio = true,
346+
.match = pwrseq_qcom_wcn3990_match,
347+
};
348+
276349
static const char *const pwrseq_wcn6750_vregs[] = {
277350
"vddaon",
278351
"vddasd",
@@ -329,8 +402,9 @@ static const struct pwrseq_qcom_wcn_pdata pwrseq_wcn7850_of_data = {
329402
.targets = pwrseq_qcom_wcn_targets,
330403
};
331404

332-
static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq,
333-
struct device *dev)
405+
static int pwrseq_qcom_wcn_match_regulator(struct pwrseq_device *pwrseq,
406+
struct device *dev,
407+
const char *name)
334408
{
335409
struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
336410
struct device_node *dev_node = dev->of_node;
@@ -341,11 +415,11 @@ static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq,
341415
* 'vddaon-supply' property and whether it leads us to the right
342416
* device.
343417
*/
344-
if (!of_property_present(dev_node, "vddaon-supply"))
418+
if (!of_property_present(dev_node, name))
345419
return PWRSEQ_NO_MATCH;
346420

347421
struct device_node *reg_node __free(device_node) =
348-
of_parse_phandle(dev_node, "vddaon-supply", 0);
422+
of_parse_phandle(dev_node, name, 0);
349423
if (!reg_node)
350424
return PWRSEQ_NO_MATCH;
351425

@@ -361,6 +435,26 @@ static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq,
361435
return PWRSEQ_MATCH_OK;
362436
}
363437

438+
static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq,
439+
struct device *dev)
440+
{
441+
return pwrseq_qcom_wcn_match_regulator(pwrseq, dev, "vddaon-supply");
442+
}
443+
444+
static int pwrseq_qcom_wcn3990_match(struct pwrseq_device *pwrseq,
445+
struct device *dev)
446+
{
447+
int ret;
448+
449+
/* BT device */
450+
ret = pwrseq_qcom_wcn_match_regulator(pwrseq, dev, "vddio-supply");
451+
if (ret == PWRSEQ_MATCH_OK)
452+
return ret;
453+
454+
/* WiFi device match */
455+
return pwrseq_qcom_wcn_match_regulator(pwrseq, dev, "vdd-1.8-xo-supply");
456+
}
457+
364458
static int pwrseq_qcom_wcn_probe(struct platform_device *pdev)
365459
{
366460
struct device *dev = &pdev->dev;
@@ -392,6 +486,12 @@ static int pwrseq_qcom_wcn_probe(struct platform_device *pdev)
392486
return dev_err_probe(dev, ret,
393487
"Failed to get all regulators\n");
394488

489+
if (ctx->pdata->has_vddio) {
490+
ctx->vddio = devm_regulator_get(dev, "vddio");
491+
if (IS_ERR(ctx->vddio))
492+
return dev_err_probe(dev, ret, "Failed to get VDDIO\n");
493+
}
494+
395495
ctx->bt_gpio = devm_gpiod_get_optional(dev, "bt-enable", GPIOD_OUT_LOW);
396496
if (IS_ERR(ctx->bt_gpio))
397497
return dev_err_probe(dev, PTR_ERR(ctx->bt_gpio),
@@ -433,7 +533,7 @@ static int pwrseq_qcom_wcn_probe(struct platform_device *pdev)
433533
config.parent = dev;
434534
config.owner = THIS_MODULE;
435535
config.drvdata = ctx;
436-
config.match = pwrseq_qcom_wcn_match;
536+
config.match = ctx->pdata->match ? : pwrseq_qcom_wcn_match;
437537
config.targets = ctx->pdata->targets;
438538

439539
ctx->pwrseq = devm_pwrseq_device_register(dev, &config);
@@ -445,6 +545,26 @@ static int pwrseq_qcom_wcn_probe(struct platform_device *pdev)
445545
}
446546

447547
static const struct of_device_id pwrseq_qcom_wcn_of_match[] = {
548+
{
549+
.compatible = "qcom,wcn3950-pmu",
550+
.data = &pwrseq_wcn3990_of_data,
551+
},
552+
{
553+
.compatible = "qcom,wcn3988-pmu",
554+
.data = &pwrseq_wcn3990_of_data,
555+
},
556+
{
557+
.compatible = "qcom,wcn3990-pmu",
558+
.data = &pwrseq_wcn3990_of_data,
559+
},
560+
{
561+
.compatible = "qcom,wcn3991-pmu",
562+
.data = &pwrseq_wcn3990_of_data,
563+
},
564+
{
565+
.compatible = "qcom,wcn3998-pmu",
566+
.data = &pwrseq_wcn3990_of_data,
567+
},
448568
{
449569
.compatible = "qcom,qca6390-pmu",
450570
.data = &pwrseq_qca6390_of_data,

0 commit comments

Comments
 (0)