Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Dmitry Baryshkov and committed by
Bartosz Golaszewski
0eb85f46 a5fae429

+125 -5
+125 -5
drivers/power/sequencing/pwrseq-qcom-wcn.c
··· 24 24 unsigned int pwup_delay_ms; 25 25 unsigned int gpio_enable_delay_ms; 26 26 const struct pwrseq_target_data **targets; 27 + bool has_vddio; /* separate VDD IO regulator */ 28 + int (*match)(struct pwrseq_device *pwrseq, struct device *dev); 27 29 }; 28 30 29 31 struct pwrseq_qcom_wcn_ctx { ··· 33 31 struct device_node *of_node; 34 32 const struct pwrseq_qcom_wcn_pdata *pdata; 35 33 struct regulator_bulk_data *regs; 34 + struct regulator *vddio; 36 35 struct gpio_desc *bt_gpio; 37 36 struct gpio_desc *wlan_gpio; 38 37 struct gpio_desc *xo_clk_gpio; ··· 55 52 if (diff_msecs < ctx->pdata->gpio_enable_delay_ms) 56 53 msleep(ctx->pdata->gpio_enable_delay_ms - diff_msecs); 57 54 } 55 + 56 + static int pwrseq_qcom_wcn_vddio_enable(struct pwrseq_device *pwrseq) 57 + { 58 + struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 59 + 60 + return regulator_enable(ctx->vddio); 61 + } 62 + 63 + static int pwrseq_qcom_wcn_vddio_disable(struct pwrseq_device *pwrseq) 64 + { 65 + struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 66 + 67 + return regulator_disable(ctx->vddio); 68 + } 69 + 70 + static const struct pwrseq_unit_data pwrseq_qcom_wcn_vddio_unit_data = { 71 + .name = "vddio-enable", 72 + .enable = pwrseq_qcom_wcn_vddio_enable, 73 + .disable = pwrseq_qcom_wcn_vddio_disable, 74 + }; 58 75 59 76 static int pwrseq_qcom_wcn_vregs_enable(struct pwrseq_device *pwrseq) 60 77 { ··· 114 91 115 92 static const struct pwrseq_unit_data pwrseq_qcom_wcn_clk_unit_data = { 116 93 .name = "clock-enable", 94 + .enable = pwrseq_qcom_wcn_clk_enable, 95 + .disable = pwrseq_qcom_wcn_clk_disable, 96 + }; 97 + 98 + static const struct pwrseq_unit_data *pwrseq_qcom_wcn3990_unit_deps[] = { 99 + &pwrseq_qcom_wcn_vddio_unit_data, 100 + &pwrseq_qcom_wcn_vregs_unit_data, 101 + NULL, 102 + }; 103 + 104 + static const struct pwrseq_unit_data pwrseq_qcom_wcn3990_unit_data = { 105 + .name = "clock-enable", 106 + .deps = pwrseq_qcom_wcn3990_unit_deps, 117 107 .enable = pwrseq_qcom_wcn_clk_enable, 118 108 .disable = pwrseq_qcom_wcn_clk_disable, 119 109 }; ··· 266 230 .post_enable = pwrseq_qcom_wcn_pwup_delay, 267 231 }; 268 232 233 + /* There are no separate BT and WLAN enablement pins */ 234 + static const struct pwrseq_target_data pwrseq_qcom_wcn3990_bt_target_data = { 235 + .name = "bluetooth", 236 + .unit = &pwrseq_qcom_wcn3990_unit_data, 237 + }; 238 + 239 + static const struct pwrseq_target_data pwrseq_qcom_wcn3990_wlan_target_data = { 240 + .name = "wlan", 241 + .unit = &pwrseq_qcom_wcn3990_unit_data, 242 + }; 243 + 269 244 static const struct pwrseq_target_data pwrseq_qcom_wcn6855_bt_target_data = { 270 245 .name = "bluetooth", 271 246 .unit = &pwrseq_qcom_wcn6855_bt_unit_data, ··· 292 245 static const struct pwrseq_target_data *pwrseq_qcom_wcn_targets[] = { 293 246 &pwrseq_qcom_wcn_bt_target_data, 294 247 &pwrseq_qcom_wcn_wlan_target_data, 248 + NULL 249 + }; 250 + 251 + static const struct pwrseq_target_data *pwrseq_qcom_wcn3990_targets[] = { 252 + &pwrseq_qcom_wcn3990_bt_target_data, 253 + &pwrseq_qcom_wcn3990_wlan_target_data, 295 254 NULL 296 255 }; 297 256 ··· 324 271 .pwup_delay_ms = 60, 325 272 .gpio_enable_delay_ms = 100, 326 273 .targets = pwrseq_qcom_wcn_targets, 274 + }; 275 + 276 + static const char *const pwrseq_wcn3990_vregs[] = { 277 + /* vddio is handled separately */ 278 + "vddxo", 279 + "vddrf", 280 + "vddch0", 281 + "vddch1", 282 + }; 283 + 284 + static int pwrseq_qcom_wcn3990_match(struct pwrseq_device *pwrseq, 285 + struct device *dev); 286 + 287 + static const struct pwrseq_qcom_wcn_pdata pwrseq_wcn3990_of_data = { 288 + .vregs = pwrseq_wcn3990_vregs, 289 + .num_vregs = ARRAY_SIZE(pwrseq_wcn3990_vregs), 290 + .pwup_delay_ms = 50, 291 + .targets = pwrseq_qcom_wcn3990_targets, 292 + .has_vddio = true, 293 + .match = pwrseq_qcom_wcn3990_match, 327 294 }; 328 295 329 296 static const char *const pwrseq_wcn6750_vregs[] = { ··· 402 329 .targets = pwrseq_qcom_wcn_targets, 403 330 }; 404 331 405 - static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq, 406 - struct device *dev) 332 + static int pwrseq_qcom_wcn_match_regulator(struct pwrseq_device *pwrseq, 333 + struct device *dev, 334 + const char *name) 407 335 { 408 336 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 409 337 struct device_node *dev_node = dev->of_node; ··· 415 341 * 'vddaon-supply' property and whether it leads us to the right 416 342 * device. 417 343 */ 418 - if (!of_property_present(dev_node, "vddaon-supply")) 344 + if (!of_property_present(dev_node, name)) 419 345 return PWRSEQ_NO_MATCH; 420 346 421 347 struct device_node *reg_node __free(device_node) = 422 - of_parse_phandle(dev_node, "vddaon-supply", 0); 348 + of_parse_phandle(dev_node, name, 0); 423 349 if (!reg_node) 424 350 return PWRSEQ_NO_MATCH; 425 351 ··· 433 359 return PWRSEQ_NO_MATCH; 434 360 435 361 return PWRSEQ_MATCH_OK; 362 + } 363 + 364 + static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq, 365 + struct device *dev) 366 + { 367 + return pwrseq_qcom_wcn_match_regulator(pwrseq, dev, "vddaon-supply"); 368 + } 369 + 370 + static int pwrseq_qcom_wcn3990_match(struct pwrseq_device *pwrseq, 371 + struct device *dev) 372 + { 373 + int ret; 374 + 375 + /* BT device */ 376 + ret = pwrseq_qcom_wcn_match_regulator(pwrseq, dev, "vddio-supply"); 377 + if (ret == PWRSEQ_MATCH_OK) 378 + return ret; 379 + 380 + /* WiFi device match */ 381 + return pwrseq_qcom_wcn_match_regulator(pwrseq, dev, "vdd-1.8-xo-supply"); 436 382 } 437 383 438 384 static int pwrseq_qcom_wcn_probe(struct platform_device *pdev) ··· 485 391 if (ret < 0) 486 392 return dev_err_probe(dev, ret, 487 393 "Failed to get all regulators\n"); 394 + 395 + if (ctx->pdata->has_vddio) { 396 + ctx->vddio = devm_regulator_get(dev, "vddio"); 397 + if (IS_ERR(ctx->vddio)) 398 + return dev_err_probe(dev, ret, "Failed to get VDDIO\n"); 399 + } 488 400 489 401 ctx->bt_gpio = devm_gpiod_get_optional(dev, "bt-enable", GPIOD_OUT_LOW); 490 402 if (IS_ERR(ctx->bt_gpio)) ··· 533 433 config.parent = dev; 534 434 config.owner = THIS_MODULE; 535 435 config.drvdata = ctx; 536 - config.match = pwrseq_qcom_wcn_match; 436 + config.match = ctx->pdata->match ? : pwrseq_qcom_wcn_match; 537 437 config.targets = ctx->pdata->targets; 538 438 539 439 ctx->pwrseq = devm_pwrseq_device_register(dev, &config); ··· 545 445 } 546 446 547 447 static const struct of_device_id pwrseq_qcom_wcn_of_match[] = { 448 + { 449 + .compatible = "qcom,wcn3950-pmu", 450 + .data = &pwrseq_wcn3990_of_data, 451 + }, 452 + { 453 + .compatible = "qcom,wcn3988-pmu", 454 + .data = &pwrseq_wcn3990_of_data, 455 + }, 456 + { 457 + .compatible = "qcom,wcn3990-pmu", 458 + .data = &pwrseq_wcn3990_of_data, 459 + }, 460 + { 461 + .compatible = "qcom,wcn3991-pmu", 462 + .data = &pwrseq_wcn3990_of_data, 463 + }, 464 + { 465 + .compatible = "qcom,wcn3998-pmu", 466 + .data = &pwrseq_wcn3990_of_data, 467 + }, 548 468 { 549 469 .compatible = "qcom,qca6390-pmu", 550 470 .data = &pwrseq_qca6390_of_data,