1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2024 Linaro Ltd. 4 */ 5 6 #include <linux/clk.h> 7 #include <linux/delay.h> 8 #include <linux/device.h> 9 #include <linux/gpio/consumer.h> 10 #include <linux/jiffies.h> 11 #include <linux/mod_devicetable.h> 12 #include <linux/module.h> 13 #include <linux/of.h> 14 #include <linux/platform_device.h> 15 #include <linux/regulator/consumer.h> 16 #include <linux/pwrseq/provider.h> 17 #include <linux/string.h> 18 #include <linux/types.h> 19 20 struct pwrseq_qcom_wcn_pdata { 21 const char *const *vregs; 22 size_t num_vregs; 23 unsigned int pwup_delay_ms; 24 unsigned int gpio_enable_delay_ms; 25 const struct pwrseq_target_data **targets; 26 }; 27 28 struct pwrseq_qcom_wcn_ctx { 29 struct pwrseq_device *pwrseq; 30 struct device_node *of_node; 31 const struct pwrseq_qcom_wcn_pdata *pdata; 32 struct regulator_bulk_data *regs; 33 struct gpio_desc *bt_gpio; 34 struct gpio_desc *wlan_gpio; 35 struct gpio_desc *xo_clk_gpio; 36 struct clk *clk; 37 unsigned long last_gpio_enable_jf; 38 }; 39 40 static void pwrseq_qcom_wcn_ensure_gpio_delay(struct pwrseq_qcom_wcn_ctx *ctx) 41 { 42 unsigned long diff_jiffies; 43 unsigned int diff_msecs; 44 45 if (!ctx->pdata->gpio_enable_delay_ms) 46 return; 47 48 diff_jiffies = jiffies - ctx->last_gpio_enable_jf; 49 diff_msecs = jiffies_to_msecs(diff_jiffies); 50 51 if (diff_msecs < ctx->pdata->gpio_enable_delay_ms) 52 msleep(ctx->pdata->gpio_enable_delay_ms - diff_msecs); 53 } 54 55 static int pwrseq_qcom_wcn_vregs_enable(struct pwrseq_device *pwrseq) 56 { 57 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 58 59 return regulator_bulk_enable(ctx->pdata->num_vregs, ctx->regs); 60 } 61 62 static int pwrseq_qcom_wcn_vregs_disable(struct pwrseq_device *pwrseq) 63 { 64 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 65 66 return regulator_bulk_disable(ctx->pdata->num_vregs, ctx->regs); 67 } 68 69 static const struct pwrseq_unit_data pwrseq_qcom_wcn_vregs_unit_data = { 70 .name = "regulators-enable", 71 .enable = pwrseq_qcom_wcn_vregs_enable, 72 .disable = pwrseq_qcom_wcn_vregs_disable, 73 }; 74 75 static int pwrseq_qcom_wcn_clk_enable(struct pwrseq_device *pwrseq) 76 { 77 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 78 79 return clk_prepare_enable(ctx->clk); 80 } 81 82 static int pwrseq_qcom_wcn_clk_disable(struct pwrseq_device *pwrseq) 83 { 84 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 85 86 clk_disable_unprepare(ctx->clk); 87 88 return 0; 89 } 90 91 static const struct pwrseq_unit_data pwrseq_qcom_wcn_clk_unit_data = { 92 .name = "clock-enable", 93 .enable = pwrseq_qcom_wcn_clk_enable, 94 .disable = pwrseq_qcom_wcn_clk_disable, 95 }; 96 97 static const struct pwrseq_unit_data *pwrseq_qcom_wcn_unit_deps[] = { 98 &pwrseq_qcom_wcn_vregs_unit_data, 99 &pwrseq_qcom_wcn_clk_unit_data, 100 NULL 101 }; 102 103 static int pwrseq_qcom_wcn6855_clk_assert(struct pwrseq_device *pwrseq) 104 { 105 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 106 107 if (!ctx->xo_clk_gpio) 108 return 0; 109 110 msleep(1); 111 112 gpiod_set_value_cansleep(ctx->xo_clk_gpio, 1); 113 usleep_range(100, 200); 114 115 return 0; 116 } 117 118 static const struct pwrseq_unit_data pwrseq_qcom_wcn6855_xo_clk_assert = { 119 .name = "xo-clk-assert", 120 .enable = pwrseq_qcom_wcn6855_clk_assert, 121 }; 122 123 static const struct pwrseq_unit_data *pwrseq_qcom_wcn6855_unit_deps[] = { 124 &pwrseq_qcom_wcn_vregs_unit_data, 125 &pwrseq_qcom_wcn_clk_unit_data, 126 &pwrseq_qcom_wcn6855_xo_clk_assert, 127 NULL 128 }; 129 130 static int pwrseq_qcom_wcn_bt_enable(struct pwrseq_device *pwrseq) 131 { 132 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 133 134 pwrseq_qcom_wcn_ensure_gpio_delay(ctx); 135 gpiod_set_value_cansleep(ctx->bt_gpio, 1); 136 ctx->last_gpio_enable_jf = jiffies; 137 138 return 0; 139 } 140 141 static int pwrseq_qcom_wcn_bt_disable(struct pwrseq_device *pwrseq) 142 { 143 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 144 145 gpiod_set_value_cansleep(ctx->bt_gpio, 0); 146 147 return 0; 148 } 149 150 static const struct pwrseq_unit_data pwrseq_qcom_wcn_bt_unit_data = { 151 .name = "bluetooth-enable", 152 .deps = pwrseq_qcom_wcn_unit_deps, 153 .enable = pwrseq_qcom_wcn_bt_enable, 154 .disable = pwrseq_qcom_wcn_bt_disable, 155 }; 156 157 static const struct pwrseq_unit_data pwrseq_qcom_wcn6855_bt_unit_data = { 158 .name = "wlan-enable", 159 .deps = pwrseq_qcom_wcn6855_unit_deps, 160 .enable = pwrseq_qcom_wcn_bt_enable, 161 .disable = pwrseq_qcom_wcn_bt_disable, 162 }; 163 164 static int pwrseq_qcom_wcn_wlan_enable(struct pwrseq_device *pwrseq) 165 { 166 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 167 168 pwrseq_qcom_wcn_ensure_gpio_delay(ctx); 169 gpiod_set_value_cansleep(ctx->wlan_gpio, 1); 170 ctx->last_gpio_enable_jf = jiffies; 171 172 return 0; 173 } 174 175 static int pwrseq_qcom_wcn_wlan_disable(struct pwrseq_device *pwrseq) 176 { 177 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 178 179 gpiod_set_value_cansleep(ctx->wlan_gpio, 0); 180 181 return 0; 182 } 183 184 static const struct pwrseq_unit_data pwrseq_qcom_wcn_wlan_unit_data = { 185 .name = "wlan-enable", 186 .deps = pwrseq_qcom_wcn_unit_deps, 187 .enable = pwrseq_qcom_wcn_wlan_enable, 188 .disable = pwrseq_qcom_wcn_wlan_disable, 189 }; 190 191 static const struct pwrseq_unit_data pwrseq_qcom_wcn6855_wlan_unit_data = { 192 .name = "wlan-enable", 193 .deps = pwrseq_qcom_wcn6855_unit_deps, 194 .enable = pwrseq_qcom_wcn_wlan_enable, 195 .disable = pwrseq_qcom_wcn_wlan_disable, 196 }; 197 198 static int pwrseq_qcom_wcn_pwup_delay(struct pwrseq_device *pwrseq) 199 { 200 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 201 202 if (ctx->pdata->pwup_delay_ms) 203 msleep(ctx->pdata->pwup_delay_ms); 204 205 return 0; 206 } 207 208 static int pwrseq_qcom_wcn6855_xo_clk_deassert(struct pwrseq_device *pwrseq) 209 { 210 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 211 212 if (ctx->xo_clk_gpio) { 213 usleep_range(2000, 5000); 214 gpiod_set_value_cansleep(ctx->xo_clk_gpio, 0); 215 } 216 217 return pwrseq_qcom_wcn_pwup_delay(pwrseq); 218 } 219 220 static const struct pwrseq_target_data pwrseq_qcom_wcn_bt_target_data = { 221 .name = "bluetooth", 222 .unit = &pwrseq_qcom_wcn_bt_unit_data, 223 .post_enable = pwrseq_qcom_wcn_pwup_delay, 224 }; 225 226 static const struct pwrseq_target_data pwrseq_qcom_wcn_wlan_target_data = { 227 .name = "wlan", 228 .unit = &pwrseq_qcom_wcn_wlan_unit_data, 229 .post_enable = pwrseq_qcom_wcn_pwup_delay, 230 }; 231 232 static const struct pwrseq_target_data pwrseq_qcom_wcn6855_bt_target_data = { 233 .name = "bluetooth", 234 .unit = &pwrseq_qcom_wcn6855_bt_unit_data, 235 .post_enable = pwrseq_qcom_wcn6855_xo_clk_deassert, 236 }; 237 238 static const struct pwrseq_target_data pwrseq_qcom_wcn6855_wlan_target_data = { 239 .name = "wlan", 240 .unit = &pwrseq_qcom_wcn6855_wlan_unit_data, 241 .post_enable = pwrseq_qcom_wcn6855_xo_clk_deassert, 242 }; 243 244 static const struct pwrseq_target_data *pwrseq_qcom_wcn_targets[] = { 245 &pwrseq_qcom_wcn_bt_target_data, 246 &pwrseq_qcom_wcn_wlan_target_data, 247 NULL 248 }; 249 250 static const struct pwrseq_target_data *pwrseq_qcom_wcn6855_targets[] = { 251 &pwrseq_qcom_wcn6855_bt_target_data, 252 &pwrseq_qcom_wcn6855_wlan_target_data, 253 NULL 254 }; 255 256 static const char *const pwrseq_qca6390_vregs[] = { 257 "vddio", 258 "vddaon", 259 "vddpmu", 260 "vddrfa0p95", 261 "vddrfa1p3", 262 "vddrfa1p9", 263 "vddpcie1p3", 264 "vddpcie1p9", 265 }; 266 267 static const struct pwrseq_qcom_wcn_pdata pwrseq_qca6390_of_data = { 268 .vregs = pwrseq_qca6390_vregs, 269 .num_vregs = ARRAY_SIZE(pwrseq_qca6390_vregs), 270 .pwup_delay_ms = 60, 271 .gpio_enable_delay_ms = 100, 272 .targets = pwrseq_qcom_wcn_targets, 273 }; 274 275 static const char *const pwrseq_wcn6750_vregs[] = { 276 "vddaon", 277 "vddasd", 278 "vddpmu", 279 "vddrfa0p8", 280 "vddrfa1p2", 281 "vddrfa1p7", 282 "vddrfa2p2", 283 }; 284 285 static const struct pwrseq_qcom_wcn_pdata pwrseq_wcn6750_of_data = { 286 .vregs = pwrseq_wcn6750_vregs, 287 .num_vregs = ARRAY_SIZE(pwrseq_wcn6750_vregs), 288 .pwup_delay_ms = 50, 289 .gpio_enable_delay_ms = 5, 290 .targets = pwrseq_qcom_wcn_targets, 291 }; 292 293 static const char *const pwrseq_wcn6855_vregs[] = { 294 "vddio", 295 "vddaon", 296 "vddpmu", 297 "vddpmumx", 298 "vddpmucx", 299 "vddrfa0p95", 300 "vddrfa1p3", 301 "vddrfa1p9", 302 "vddpcie1p3", 303 "vddpcie1p9", 304 }; 305 306 static const struct pwrseq_qcom_wcn_pdata pwrseq_wcn6855_of_data = { 307 .vregs = pwrseq_wcn6855_vregs, 308 .num_vregs = ARRAY_SIZE(pwrseq_wcn6855_vregs), 309 .pwup_delay_ms = 50, 310 .gpio_enable_delay_ms = 5, 311 .targets = pwrseq_qcom_wcn6855_targets, 312 }; 313 314 static const char *const pwrseq_wcn7850_vregs[] = { 315 "vdd", 316 "vddio", 317 "vddio1p2", 318 "vddaon", 319 "vdddig", 320 "vddrfa1p2", 321 "vddrfa1p8", 322 }; 323 324 static const struct pwrseq_qcom_wcn_pdata pwrseq_wcn7850_of_data = { 325 .vregs = pwrseq_wcn7850_vregs, 326 .num_vregs = ARRAY_SIZE(pwrseq_wcn7850_vregs), 327 .pwup_delay_ms = 50, 328 .targets = pwrseq_qcom_wcn_targets, 329 }; 330 331 static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq, 332 struct device *dev) 333 { 334 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 335 struct device_node *dev_node = dev->of_node; 336 337 /* 338 * The PMU supplies power to the Bluetooth and WLAN modules. both 339 * consume the PMU AON output so check the presence of the 340 * 'vddaon-supply' property and whether it leads us to the right 341 * device. 342 */ 343 if (!of_property_present(dev_node, "vddaon-supply")) 344 return 0; 345 346 struct device_node *reg_node __free(device_node) = 347 of_parse_phandle(dev_node, "vddaon-supply", 0); 348 if (!reg_node) 349 return 0; 350 351 /* 352 * `reg_node` is the PMU AON regulator, its parent is the `regulators` 353 * node and finally its grandparent is the PMU device node that we're 354 * looking for. 355 */ 356 if (!reg_node->parent || !reg_node->parent->parent || 357 reg_node->parent->parent != ctx->of_node) 358 return 0; 359 360 return 1; 361 } 362 363 static int pwrseq_qcom_wcn_probe(struct platform_device *pdev) 364 { 365 struct device *dev = &pdev->dev; 366 struct pwrseq_qcom_wcn_ctx *ctx; 367 struct pwrseq_config config; 368 int i, ret; 369 370 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 371 if (!ctx) 372 return -ENOMEM; 373 374 ctx->of_node = dev->of_node; 375 376 ctx->pdata = of_device_get_match_data(dev); 377 if (!ctx->pdata) 378 return dev_err_probe(dev, -ENODEV, 379 "Failed to obtain platform data\n"); 380 381 ctx->regs = devm_kcalloc(dev, ctx->pdata->num_vregs, 382 sizeof(*ctx->regs), GFP_KERNEL); 383 if (!ctx->regs) 384 return -ENOMEM; 385 386 for (i = 0; i < ctx->pdata->num_vregs; i++) 387 ctx->regs[i].supply = ctx->pdata->vregs[i]; 388 389 ret = devm_regulator_bulk_get(dev, ctx->pdata->num_vregs, ctx->regs); 390 if (ret < 0) 391 return dev_err_probe(dev, ret, 392 "Failed to get all regulators\n"); 393 394 ctx->bt_gpio = devm_gpiod_get_optional(dev, "bt-enable", GPIOD_OUT_LOW); 395 if (IS_ERR(ctx->bt_gpio)) 396 return dev_err_probe(dev, PTR_ERR(ctx->bt_gpio), 397 "Failed to get the Bluetooth enable GPIO\n"); 398 399 /* 400 * FIXME: This should actually be GPIOD_OUT_LOW, but doing so would 401 * cause the WLAN power to be toggled, resulting in PCIe link down. 402 * Since the PCIe controller driver is not handling link down currently, 403 * the device becomes unusable. So we need to keep this workaround until 404 * the link down handling is implemented in the controller driver. 405 */ 406 ctx->wlan_gpio = devm_gpiod_get_optional(dev, "wlan-enable", 407 GPIOD_ASIS); 408 if (IS_ERR(ctx->wlan_gpio)) 409 return dev_err_probe(dev, PTR_ERR(ctx->wlan_gpio), 410 "Failed to get the WLAN enable GPIO\n"); 411 412 ctx->xo_clk_gpio = devm_gpiod_get_optional(dev, "xo-clk", 413 GPIOD_OUT_LOW); 414 if (IS_ERR(ctx->xo_clk_gpio)) 415 return dev_err_probe(dev, PTR_ERR(ctx->xo_clk_gpio), 416 "Failed to get the XO_CLK GPIO\n"); 417 418 /* 419 * Set direction to output but keep the current value in order to not 420 * disable the WLAN module accidentally if it's already powered on. 421 */ 422 gpiod_direction_output(ctx->wlan_gpio, 423 gpiod_get_value_cansleep(ctx->wlan_gpio)); 424 425 ctx->clk = devm_clk_get_optional(dev, NULL); 426 if (IS_ERR(ctx->clk)) 427 return dev_err_probe(dev, PTR_ERR(ctx->clk), 428 "Failed to get the reference clock\n"); 429 430 memset(&config, 0, sizeof(config)); 431 432 config.parent = dev; 433 config.owner = THIS_MODULE; 434 config.drvdata = ctx; 435 config.match = pwrseq_qcom_wcn_match; 436 config.targets = ctx->pdata->targets; 437 438 ctx->pwrseq = devm_pwrseq_device_register(dev, &config); 439 if (IS_ERR(ctx->pwrseq)) 440 return dev_err_probe(dev, PTR_ERR(ctx->pwrseq), 441 "Failed to register the power sequencer\n"); 442 443 return 0; 444 } 445 446 static const struct of_device_id pwrseq_qcom_wcn_of_match[] = { 447 { 448 .compatible = "qcom,qca6390-pmu", 449 .data = &pwrseq_qca6390_of_data, 450 }, 451 { 452 .compatible = "qcom,wcn6855-pmu", 453 .data = &pwrseq_wcn6855_of_data, 454 }, 455 { 456 .compatible = "qcom,wcn7850-pmu", 457 .data = &pwrseq_wcn7850_of_data, 458 }, 459 { 460 .compatible = "qcom,wcn6750-pmu", 461 .data = &pwrseq_wcn6750_of_data, 462 }, 463 { } 464 }; 465 MODULE_DEVICE_TABLE(of, pwrseq_qcom_wcn_of_match); 466 467 static struct platform_driver pwrseq_qcom_wcn_driver = { 468 .driver = { 469 .name = "pwrseq-qcom_wcn", 470 .of_match_table = pwrseq_qcom_wcn_of_match, 471 }, 472 .probe = pwrseq_qcom_wcn_probe, 473 }; 474 module_platform_driver(pwrseq_qcom_wcn_driver); 475 476 MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>"); 477 MODULE_DESCRIPTION("Qualcomm WCN PMU power sequencing driver"); 478 MODULE_LICENSE("GPL"); 479