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/module.h>
12 #include <linux/of.h>
13 #include <linux/platform_device.h>
14 #include <linux/property.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 bool has_vddio; /* separate VDD IO regulator */
27 int (*match)(struct pwrseq_device *pwrseq, struct device *dev);
28 };
29
30 struct pwrseq_qcom_wcn_ctx {
31 struct pwrseq_device *pwrseq;
32 struct device_node *of_node;
33 const struct pwrseq_qcom_wcn_pdata *pdata;
34 struct regulator_bulk_data *regs;
35 struct regulator *vddio;
36 struct gpio_desc *bt_gpio;
37 struct gpio_desc *wlan_gpio;
38 struct gpio_desc *xo_clk_gpio;
39 struct clk *clk;
40 unsigned long last_gpio_enable_jf;
41 };
42
pwrseq_qcom_wcn_ensure_gpio_delay(struct pwrseq_qcom_wcn_ctx * ctx)43 static void pwrseq_qcom_wcn_ensure_gpio_delay(struct pwrseq_qcom_wcn_ctx *ctx)
44 {
45 unsigned long diff_jiffies;
46 unsigned int diff_msecs;
47
48 if (!ctx->pdata->gpio_enable_delay_ms)
49 return;
50
51 diff_jiffies = jiffies - ctx->last_gpio_enable_jf;
52 diff_msecs = jiffies_to_msecs(diff_jiffies);
53
54 if (diff_msecs < ctx->pdata->gpio_enable_delay_ms)
55 msleep(ctx->pdata->gpio_enable_delay_ms - diff_msecs);
56 }
57
pwrseq_qcom_wcn_vddio_enable(struct pwrseq_device * pwrseq)58 static int pwrseq_qcom_wcn_vddio_enable(struct pwrseq_device *pwrseq)
59 {
60 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
61
62 return regulator_enable(ctx->vddio);
63 }
64
pwrseq_qcom_wcn_vddio_disable(struct pwrseq_device * pwrseq)65 static int pwrseq_qcom_wcn_vddio_disable(struct pwrseq_device *pwrseq)
66 {
67 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
68
69 return regulator_disable(ctx->vddio);
70 }
71
72 static const struct pwrseq_unit_data pwrseq_qcom_wcn_vddio_unit_data = {
73 .name = "vddio-enable",
74 .enable = pwrseq_qcom_wcn_vddio_enable,
75 .disable = pwrseq_qcom_wcn_vddio_disable,
76 };
77
pwrseq_qcom_wcn_vregs_enable(struct pwrseq_device * pwrseq)78 static int pwrseq_qcom_wcn_vregs_enable(struct pwrseq_device *pwrseq)
79 {
80 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
81
82 return regulator_bulk_enable(ctx->pdata->num_vregs, ctx->regs);
83 }
84
pwrseq_qcom_wcn_vregs_disable(struct pwrseq_device * pwrseq)85 static int pwrseq_qcom_wcn_vregs_disable(struct pwrseq_device *pwrseq)
86 {
87 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
88
89 return regulator_bulk_disable(ctx->pdata->num_vregs, ctx->regs);
90 }
91
92 static const struct pwrseq_unit_data pwrseq_qcom_wcn_vregs_unit_data = {
93 .name = "regulators-enable",
94 .enable = pwrseq_qcom_wcn_vregs_enable,
95 .disable = pwrseq_qcom_wcn_vregs_disable,
96 };
97
pwrseq_qcom_wcn_clk_enable(struct pwrseq_device * pwrseq)98 static int pwrseq_qcom_wcn_clk_enable(struct pwrseq_device *pwrseq)
99 {
100 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
101
102 return clk_prepare_enable(ctx->clk);
103 }
104
pwrseq_qcom_wcn_clk_disable(struct pwrseq_device * pwrseq)105 static int pwrseq_qcom_wcn_clk_disable(struct pwrseq_device *pwrseq)
106 {
107 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
108
109 clk_disable_unprepare(ctx->clk);
110
111 return 0;
112 }
113
114 static const struct pwrseq_unit_data pwrseq_qcom_wcn_clk_unit_data = {
115 .name = "clock-enable",
116 .enable = pwrseq_qcom_wcn_clk_enable,
117 .disable = pwrseq_qcom_wcn_clk_disable,
118 };
119
120 static const struct pwrseq_unit_data *pwrseq_qcom_wcn3990_unit_deps[] = {
121 &pwrseq_qcom_wcn_vddio_unit_data,
122 &pwrseq_qcom_wcn_vregs_unit_data,
123 NULL,
124 };
125
126 static const struct pwrseq_unit_data pwrseq_qcom_wcn3990_unit_data = {
127 .name = "clock-enable",
128 .deps = pwrseq_qcom_wcn3990_unit_deps,
129 .enable = pwrseq_qcom_wcn_clk_enable,
130 .disable = pwrseq_qcom_wcn_clk_disable,
131 };
132
133 static const struct pwrseq_unit_data *pwrseq_qcom_wcn_unit_deps[] = {
134 &pwrseq_qcom_wcn_vregs_unit_data,
135 &pwrseq_qcom_wcn_clk_unit_data,
136 NULL
137 };
138
pwrseq_qcom_wcn6855_clk_assert(struct pwrseq_device * pwrseq)139 static int pwrseq_qcom_wcn6855_clk_assert(struct pwrseq_device *pwrseq)
140 {
141 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
142
143 if (!ctx->xo_clk_gpio)
144 return 0;
145
146 msleep(1);
147
148 gpiod_set_value_cansleep(ctx->xo_clk_gpio, 1);
149 usleep_range(100, 200);
150
151 return 0;
152 }
153
154 static const struct pwrseq_unit_data pwrseq_qcom_wcn6855_xo_clk_assert = {
155 .name = "xo-clk-assert",
156 .enable = pwrseq_qcom_wcn6855_clk_assert,
157 };
158
159 static const struct pwrseq_unit_data *pwrseq_qcom_wcn6855_unit_deps[] = {
160 &pwrseq_qcom_wcn_vregs_unit_data,
161 &pwrseq_qcom_wcn_clk_unit_data,
162 &pwrseq_qcom_wcn6855_xo_clk_assert,
163 NULL
164 };
165
pwrseq_qcom_wcn_bt_enable(struct pwrseq_device * pwrseq)166 static int pwrseq_qcom_wcn_bt_enable(struct pwrseq_device *pwrseq)
167 {
168 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
169
170 pwrseq_qcom_wcn_ensure_gpio_delay(ctx);
171 gpiod_set_value_cansleep(ctx->bt_gpio, 1);
172 ctx->last_gpio_enable_jf = jiffies;
173
174 return 0;
175 }
176
pwrseq_qcom_wcn_bt_disable(struct pwrseq_device * pwrseq)177 static int pwrseq_qcom_wcn_bt_disable(struct pwrseq_device *pwrseq)
178 {
179 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
180
181 gpiod_set_value_cansleep(ctx->bt_gpio, 0);
182
183 return 0;
184 }
185
186 static const struct pwrseq_unit_data pwrseq_qcom_wcn_bt_unit_data = {
187 .name = "bluetooth-enable",
188 .deps = pwrseq_qcom_wcn_unit_deps,
189 .enable = pwrseq_qcom_wcn_bt_enable,
190 .disable = pwrseq_qcom_wcn_bt_disable,
191 };
192
193 static const struct pwrseq_unit_data pwrseq_qcom_wcn6855_bt_unit_data = {
194 .name = "bluetooth-enable",
195 .deps = pwrseq_qcom_wcn6855_unit_deps,
196 .enable = pwrseq_qcom_wcn_bt_enable,
197 .disable = pwrseq_qcom_wcn_bt_disable,
198 };
199
pwrseq_qcom_wcn_wlan_enable(struct pwrseq_device * pwrseq)200 static int pwrseq_qcom_wcn_wlan_enable(struct pwrseq_device *pwrseq)
201 {
202 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
203
204 pwrseq_qcom_wcn_ensure_gpio_delay(ctx);
205 gpiod_set_value_cansleep(ctx->wlan_gpio, 1);
206 ctx->last_gpio_enable_jf = jiffies;
207
208 return 0;
209 }
210
pwrseq_qcom_wcn_wlan_disable(struct pwrseq_device * pwrseq)211 static int pwrseq_qcom_wcn_wlan_disable(struct pwrseq_device *pwrseq)
212 {
213 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
214
215 gpiod_set_value_cansleep(ctx->wlan_gpio, 0);
216
217 return 0;
218 }
219
220 static const struct pwrseq_unit_data pwrseq_qcom_wcn_wlan_unit_data = {
221 .name = "wlan-enable",
222 .deps = pwrseq_qcom_wcn_unit_deps,
223 .enable = pwrseq_qcom_wcn_wlan_enable,
224 .disable = pwrseq_qcom_wcn_wlan_disable,
225 };
226
227 static const struct pwrseq_unit_data pwrseq_qcom_wcn6855_wlan_unit_data = {
228 .name = "wlan-enable",
229 .deps = pwrseq_qcom_wcn6855_unit_deps,
230 .enable = pwrseq_qcom_wcn_wlan_enable,
231 .disable = pwrseq_qcom_wcn_wlan_disable,
232 };
233
pwrseq_qcom_wcn_pwup_delay(struct pwrseq_device * pwrseq)234 static int pwrseq_qcom_wcn_pwup_delay(struct pwrseq_device *pwrseq)
235 {
236 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
237
238 if (ctx->pdata->pwup_delay_ms)
239 msleep(ctx->pdata->pwup_delay_ms);
240
241 return 0;
242 }
243
pwrseq_qcom_wcn6855_xo_clk_deassert(struct pwrseq_device * pwrseq)244 static int pwrseq_qcom_wcn6855_xo_clk_deassert(struct pwrseq_device *pwrseq)
245 {
246 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
247
248 if (ctx->xo_clk_gpio) {
249 usleep_range(2000, 5000);
250 gpiod_set_value_cansleep(ctx->xo_clk_gpio, 0);
251 }
252
253 return pwrseq_qcom_wcn_pwup_delay(pwrseq);
254 }
255
256 static const struct pwrseq_target_data pwrseq_qcom_wcn_bt_target_data = {
257 .name = "bluetooth",
258 .unit = &pwrseq_qcom_wcn_bt_unit_data,
259 .post_enable = pwrseq_qcom_wcn_pwup_delay,
260 };
261
262 static const struct pwrseq_target_data pwrseq_qcom_wcn_wlan_target_data = {
263 .name = "wlan",
264 .unit = &pwrseq_qcom_wcn_wlan_unit_data,
265 .post_enable = pwrseq_qcom_wcn_pwup_delay,
266 };
267
268 /* There are no separate BT and WLAN enablement pins */
269 static const struct pwrseq_target_data pwrseq_qcom_wcn3990_bt_target_data = {
270 .name = "bluetooth",
271 .unit = &pwrseq_qcom_wcn3990_unit_data,
272 };
273
274 static const struct pwrseq_target_data pwrseq_qcom_wcn3990_wlan_target_data = {
275 .name = "wlan",
276 .unit = &pwrseq_qcom_wcn3990_unit_data,
277 };
278
279 static const struct pwrseq_target_data pwrseq_qcom_wcn6855_bt_target_data = {
280 .name = "bluetooth",
281 .unit = &pwrseq_qcom_wcn6855_bt_unit_data,
282 .post_enable = pwrseq_qcom_wcn6855_xo_clk_deassert,
283 };
284
285 static const struct pwrseq_target_data pwrseq_qcom_wcn6855_wlan_target_data = {
286 .name = "wlan",
287 .unit = &pwrseq_qcom_wcn6855_wlan_unit_data,
288 .post_enable = pwrseq_qcom_wcn6855_xo_clk_deassert,
289 };
290
291 static const struct pwrseq_target_data *pwrseq_qcom_wcn_targets[] = {
292 &pwrseq_qcom_wcn_bt_target_data,
293 &pwrseq_qcom_wcn_wlan_target_data,
294 NULL
295 };
296
297 static const struct pwrseq_target_data *pwrseq_qcom_wcn3990_targets[] = {
298 &pwrseq_qcom_wcn3990_bt_target_data,
299 &pwrseq_qcom_wcn3990_wlan_target_data,
300 NULL
301 };
302
303 static const struct pwrseq_target_data *pwrseq_qcom_wcn6855_targets[] = {
304 &pwrseq_qcom_wcn6855_bt_target_data,
305 &pwrseq_qcom_wcn6855_wlan_target_data,
306 NULL
307 };
308
309 static const char *const pwrseq_qca6390_vregs[] = {
310 "vddio",
311 "vddaon",
312 "vddpmu",
313 "vddrfa0p95",
314 "vddrfa1p3",
315 "vddrfa1p9",
316 "vddpcie1p3",
317 "vddpcie1p9",
318 };
319
320 static const struct pwrseq_qcom_wcn_pdata pwrseq_qca6390_of_data = {
321 .vregs = pwrseq_qca6390_vregs,
322 .num_vregs = ARRAY_SIZE(pwrseq_qca6390_vregs),
323 .pwup_delay_ms = 60,
324 .gpio_enable_delay_ms = 100,
325 .targets = pwrseq_qcom_wcn_targets,
326 };
327
328 static const char *const pwrseq_wcn3990_vregs[] = {
329 /* vddio is handled separately */
330 "vddxo",
331 "vddrf",
332 "vddch0",
333 "vddch1",
334 };
335
336 static int pwrseq_qcom_wcn3990_match(struct pwrseq_device *pwrseq,
337 struct device *dev);
338
339 static const struct pwrseq_qcom_wcn_pdata pwrseq_wcn3990_of_data = {
340 .vregs = pwrseq_wcn3990_vregs,
341 .num_vregs = ARRAY_SIZE(pwrseq_wcn3990_vregs),
342 .pwup_delay_ms = 50,
343 .targets = pwrseq_qcom_wcn3990_targets,
344 .has_vddio = true,
345 .match = pwrseq_qcom_wcn3990_match,
346 };
347
348 static const char *const pwrseq_wcn6750_vregs[] = {
349 "vddaon",
350 "vddasd",
351 "vddpmu",
352 "vddrfa0p8",
353 "vddrfa1p2",
354 "vddrfa1p7",
355 "vddrfa2p2",
356 };
357
358 static const struct pwrseq_qcom_wcn_pdata pwrseq_wcn6750_of_data = {
359 .vregs = pwrseq_wcn6750_vregs,
360 .num_vregs = ARRAY_SIZE(pwrseq_wcn6750_vregs),
361 .pwup_delay_ms = 50,
362 .gpio_enable_delay_ms = 5,
363 .targets = pwrseq_qcom_wcn_targets,
364 };
365
366 static const char *const pwrseq_wcn6855_vregs[] = {
367 "vddio",
368 "vddaon",
369 "vddpmu",
370 "vddpmumx",
371 "vddpmucx",
372 "vddrfa0p95",
373 "vddrfa1p3",
374 "vddrfa1p9",
375 "vddpcie1p3",
376 "vddpcie1p9",
377 };
378
379 static const struct pwrseq_qcom_wcn_pdata pwrseq_wcn6855_of_data = {
380 .vregs = pwrseq_wcn6855_vregs,
381 .num_vregs = ARRAY_SIZE(pwrseq_wcn6855_vregs),
382 .pwup_delay_ms = 50,
383 .gpio_enable_delay_ms = 5,
384 .targets = pwrseq_qcom_wcn6855_targets,
385 };
386
387 static const char *const pwrseq_wcn7850_vregs[] = {
388 "vdd",
389 "vddio",
390 "vddio1p2",
391 "vddaon",
392 "vdddig",
393 "vddrfa1p2",
394 "vddrfa1p8",
395 };
396
397 static const struct pwrseq_qcom_wcn_pdata pwrseq_wcn7850_of_data = {
398 .vregs = pwrseq_wcn7850_vregs,
399 .num_vregs = ARRAY_SIZE(pwrseq_wcn7850_vregs),
400 .pwup_delay_ms = 50,
401 .targets = pwrseq_qcom_wcn_targets,
402 };
403
pwrseq_qcom_wcn_match_regulator(struct pwrseq_device * pwrseq,struct device * dev,const char * name)404 static int pwrseq_qcom_wcn_match_regulator(struct pwrseq_device *pwrseq,
405 struct device *dev,
406 const char *name)
407 {
408 struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
409 struct device_node *dev_node = dev->of_node;
410
411 /*
412 * The PMU supplies power to the Bluetooth and WLAN modules. both
413 * consume the PMU AON output so check the presence of the
414 * 'vddaon-supply' property and whether it leads us to the right
415 * device.
416 */
417 if (!of_property_present(dev_node, name))
418 return PWRSEQ_NO_MATCH;
419
420 struct device_node *reg_node __free(device_node) =
421 of_parse_phandle(dev_node, name, 0);
422 if (!reg_node)
423 return PWRSEQ_NO_MATCH;
424
425 /*
426 * `reg_node` is the PMU AON regulator, its parent is the `regulators`
427 * node and finally its grandparent is the PMU device node that we're
428 * looking for.
429 */
430 if (!reg_node->parent || !reg_node->parent->parent ||
431 reg_node->parent->parent != ctx->of_node)
432 return PWRSEQ_NO_MATCH;
433
434 return PWRSEQ_MATCH_OK;
435 }
436
pwrseq_qcom_wcn_match(struct pwrseq_device * pwrseq,struct device * dev)437 static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq,
438 struct device *dev)
439 {
440 return pwrseq_qcom_wcn_match_regulator(pwrseq, dev, "vddaon-supply");
441 }
442
pwrseq_qcom_wcn3990_match(struct pwrseq_device * pwrseq,struct device * dev)443 static int pwrseq_qcom_wcn3990_match(struct pwrseq_device *pwrseq,
444 struct device *dev)
445 {
446 int ret;
447
448 /* BT device */
449 ret = pwrseq_qcom_wcn_match_regulator(pwrseq, dev, "vddio-supply");
450 if (ret == PWRSEQ_MATCH_OK)
451 return ret;
452
453 /* WiFi device match */
454 return pwrseq_qcom_wcn_match_regulator(pwrseq, dev, "vdd-1.8-xo-supply");
455 }
456
pwrseq_qcom_wcn_probe(struct platform_device * pdev)457 static int pwrseq_qcom_wcn_probe(struct platform_device *pdev)
458 {
459 struct device *dev = &pdev->dev;
460 struct pwrseq_qcom_wcn_ctx *ctx;
461 struct pwrseq_config config;
462 int i, ret;
463
464 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
465 if (!ctx)
466 return -ENOMEM;
467
468 ctx->of_node = dev->of_node;
469
470 ctx->pdata = device_get_match_data(dev);
471 if (!ctx->pdata)
472 return dev_err_probe(dev, -ENODEV,
473 "Failed to obtain platform data\n");
474
475 ctx->regs = devm_kcalloc(dev, ctx->pdata->num_vregs,
476 sizeof(*ctx->regs), GFP_KERNEL);
477 if (!ctx->regs)
478 return -ENOMEM;
479
480 for (i = 0; i < ctx->pdata->num_vregs; i++)
481 ctx->regs[i].supply = ctx->pdata->vregs[i];
482
483 ret = devm_regulator_bulk_get(dev, ctx->pdata->num_vregs, ctx->regs);
484 if (ret < 0)
485 return dev_err_probe(dev, ret,
486 "Failed to get all regulators\n");
487
488 if (ctx->pdata->has_vddio) {
489 ctx->vddio = devm_regulator_get(dev, "vddio");
490 if (IS_ERR(ctx->vddio))
491 return dev_err_probe(dev, PTR_ERR(ctx->vddio), "Failed to get VDDIO\n");
492 }
493
494 ctx->bt_gpio = devm_gpiod_get_optional(dev, "bt-enable", GPIOD_OUT_LOW);
495 if (IS_ERR(ctx->bt_gpio))
496 return dev_err_probe(dev, PTR_ERR(ctx->bt_gpio),
497 "Failed to get the Bluetooth enable GPIO\n");
498
499 /*
500 * FIXME: This should actually be GPIOD_OUT_LOW, but doing so would
501 * cause the WLAN power to be toggled, resulting in PCIe link down.
502 * Since the PCIe controller driver is not handling link down currently,
503 * the device becomes unusable. So we need to keep this workaround until
504 * the link down handling is implemented in the controller driver.
505 */
506 ctx->wlan_gpio = devm_gpiod_get_optional(dev, "wlan-enable",
507 GPIOD_ASIS);
508 if (IS_ERR(ctx->wlan_gpio))
509 return dev_err_probe(dev, PTR_ERR(ctx->wlan_gpio),
510 "Failed to get the WLAN enable GPIO\n");
511
512 ctx->xo_clk_gpio = devm_gpiod_get_optional(dev, "xo-clk",
513 GPIOD_OUT_LOW);
514 if (IS_ERR(ctx->xo_clk_gpio))
515 return dev_err_probe(dev, PTR_ERR(ctx->xo_clk_gpio),
516 "Failed to get the XO_CLK GPIO\n");
517
518 /*
519 * Set direction to output but keep the current value in order to not
520 * disable the WLAN module accidentally if it's already powered on.
521 */
522 gpiod_direction_output(ctx->wlan_gpio,
523 gpiod_get_value_cansleep(ctx->wlan_gpio));
524
525 ctx->clk = devm_clk_get_optional(dev, NULL);
526 if (IS_ERR(ctx->clk))
527 return dev_err_probe(dev, PTR_ERR(ctx->clk),
528 "Failed to get the reference clock\n");
529
530 memset(&config, 0, sizeof(config));
531
532 config.parent = dev;
533 config.owner = THIS_MODULE;
534 config.drvdata = ctx;
535 config.match = ctx->pdata->match ? : pwrseq_qcom_wcn_match;
536 config.targets = ctx->pdata->targets;
537
538 ctx->pwrseq = devm_pwrseq_device_register(dev, &config);
539 if (IS_ERR(ctx->pwrseq))
540 return dev_err_probe(dev, PTR_ERR(ctx->pwrseq),
541 "Failed to register the power sequencer\n");
542
543 return 0;
544 }
545
546 static const struct of_device_id pwrseq_qcom_wcn_of_match[] = {
547 {
548 .compatible = "qcom,wcn3950-pmu",
549 .data = &pwrseq_wcn3990_of_data,
550 },
551 {
552 .compatible = "qcom,wcn3988-pmu",
553 .data = &pwrseq_wcn3990_of_data,
554 },
555 {
556 .compatible = "qcom,wcn3990-pmu",
557 .data = &pwrseq_wcn3990_of_data,
558 },
559 {
560 .compatible = "qcom,wcn3991-pmu",
561 .data = &pwrseq_wcn3990_of_data,
562 },
563 {
564 .compatible = "qcom,wcn3998-pmu",
565 .data = &pwrseq_wcn3990_of_data,
566 },
567 {
568 .compatible = "qcom,qca6390-pmu",
569 .data = &pwrseq_qca6390_of_data,
570 },
571 {
572 .compatible = "qcom,wcn6855-pmu",
573 .data = &pwrseq_wcn6855_of_data,
574 },
575 {
576 .compatible = "qcom,wcn7850-pmu",
577 .data = &pwrseq_wcn7850_of_data,
578 },
579 {
580 .compatible = "qcom,wcn6750-pmu",
581 .data = &pwrseq_wcn6750_of_data,
582 },
583 { }
584 };
585 MODULE_DEVICE_TABLE(of, pwrseq_qcom_wcn_of_match);
586
587 static struct platform_driver pwrseq_qcom_wcn_driver = {
588 .driver = {
589 .name = "pwrseq-qcom_wcn",
590 .of_match_table = pwrseq_qcom_wcn_of_match,
591 },
592 .probe = pwrseq_qcom_wcn_probe,
593 };
594 module_platform_driver(pwrseq_qcom_wcn_driver);
595
596 MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");
597 MODULE_DESCRIPTION("Qualcomm WCN PMU power sequencing driver");
598 MODULE_LICENSE("GPL");
599