blob: 4c0531f2bbeb6eda0bff63915bef72a1b37ae5a5 [file] [log] [blame]
Lior David639ebed2017-02-28 14:23:55 +02001/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/of.h>
16#include <linux/of_gpio.h>
17#include <linux/msm_pcie.h>
18#include <asm/dma-iommu.h>
19#include <linux/msm-bus.h>
20#include <linux/iommu.h>
21#include <linux/version.h>
22#include <linux/delay.h>
23#include <soc/qcom/subsystem_restart.h>
24#include <soc/qcom/subsystem_notif.h>
25#include <soc/qcom/ramdump.h>
26#include <soc/qcom/memory_dump.h>
27#include <linux/regulator/consumer.h>
28#include <linux/clk.h>
Lior Daviddb711962017-02-15 21:04:55 +020029#include <linux/interrupt.h>
30#include <linux/cpumask.h>
31#include <linux/cpufreq.h>
32#include <linux/sched/core_ctl.h>
Lior David639ebed2017-02-28 14:23:55 +020033#include "wil_platform.h"
34#include "msm_11ad.h"
35
Maya Erez34c251e2017-03-27 09:31:39 +030036#define SMMU_BASE 0x20000000 /* Device address range base */
Lior David639ebed2017-02-28 14:23:55 +020037#define SMMU_SIZE ((SZ_1G * 4ULL) - SMMU_BASE)
38
39#define WIGIG_ENABLE_DELAY 50
Lior David639ebed2017-02-28 14:23:55 +020040
41#define WIGIG_SUBSYS_NAME "WIGIG"
Alexei Avshalom Lazar52864112017-12-07 12:49:56 +020042#define WIGIG_RAMDUMP_SIZE_SPARROW 0x200000 /* maximum ramdump size */
43#define WIGIG_RAMDUMP_SIZE_TALYN 0x400000 /* maximum ramdump size */
Lior David639ebed2017-02-28 14:23:55 +020044#define WIGIG_DUMP_FORMAT_VER 0x1
45#define WIGIG_DUMP_MAGIC_VER_V1 0x57474947
46#define VDD_MIN_UV 1028000
47#define VDD_MAX_UV 1028000
48#define VDD_MAX_UA 575000
49#define VDDIO_MIN_UV 1950000
50#define VDDIO_MAX_UV 2040000
51#define VDDIO_MAX_UA 70300
52
Maya Erez17ef6c32017-03-27 08:37:07 +030053#define PCIE20_CAP_LINKCTRLSTATUS 0x80
54
Maya Erez3ef3e362017-03-27 08:47:02 +030055#define WIGIG_MIN_CPU_BOOST_KBPS 150000
56
Lior David639ebed2017-02-28 14:23:55 +020057struct device;
58
59static const char * const gpio_en_name = "qcom,wigig-en";
60static const char * const sleep_clk_en_name = "qcom,sleep-clk-en";
61
Alexei Avshalom Lazar52864112017-12-07 12:49:56 +020062struct wigig_pci {
63 struct pci_device_id pci_dev;
64 u32 ramdump_sz;
65};
66
67static const struct wigig_pci wigig_pci_tbl[] = {
68 { .pci_dev = { PCI_DEVICE(0x1ae9, 0x0310) },
69 .ramdump_sz = WIGIG_RAMDUMP_SIZE_SPARROW},
70 { .pci_dev = { PCI_DEVICE(0x17cb, 0x1201) },
71 .ramdump_sz = WIGIG_RAMDUMP_SIZE_TALYN},
72};
73
Lior David639ebed2017-02-28 14:23:55 +020074struct msm11ad_vreg {
75 const char *name;
76 struct regulator *reg;
77 int max_uA;
78 int min_uV;
79 int max_uV;
80 bool enabled;
81};
82
83struct msm11ad_clk {
84 const char *name;
85 struct clk *clk;
86 bool enabled;
87};
88
89struct msm11ad_ctx {
90 struct list_head list;
91 struct device *dev; /* for platform device */
92 int gpio_en; /* card enable */
93 int sleep_clk_en; /* sleep clock enable for low PM management */
94
95 /* pci device */
96 u32 rc_index; /* PCIE root complex index */
97 struct pci_dev *pcidev;
98 struct pci_saved_state *pristine_state;
Maya Erez3ef3e362017-03-27 08:47:02 +030099 bool l1_enabled_in_enum;
Lior David639ebed2017-02-28 14:23:55 +0200100
101 /* SMMU */
102 bool use_smmu; /* have SMMU enabled? */
Maya Erez34c251e2017-03-27 09:31:39 +0300103 int smmu_s1_en;
Lior David639ebed2017-02-28 14:23:55 +0200104 int smmu_fast_map;
Maya Erez34c251e2017-03-27 09:31:39 +0300105 int smmu_coherent;
Lior David639ebed2017-02-28 14:23:55 +0200106 struct dma_iommu_mapping *mapping;
Maya Erez34c251e2017-03-27 09:31:39 +0300107 u32 smmu_base;
108 u32 smmu_size;
Lior David639ebed2017-02-28 14:23:55 +0200109
110 /* bus frequency scaling */
111 struct msm_bus_scale_pdata *bus_scale;
112 u32 msm_bus_handle;
113
114 /* subsystem restart */
115 struct wil_platform_rops rops;
116 void *wil_handle;
117 struct subsys_desc subsysdesc;
118 struct subsys_device *subsys;
119 void *subsys_handle;
120 bool recovery_in_progress;
121
122 /* ramdump */
123 void *ramdump_addr;
124 struct msm_dump_data dump_data;
125 struct ramdump_device *ramdump_dev;
Alexei Avshalom Lazar52864112017-12-07 12:49:56 +0200126 u32 ramdump_size;
Lior David639ebed2017-02-28 14:23:55 +0200127
128 /* external vregs and clocks */
129 struct msm11ad_vreg vdd;
130 struct msm11ad_vreg vddio;
131 struct msm11ad_clk rf_clk3;
132 struct msm11ad_clk rf_clk3_pin;
Lior Daviddb711962017-02-15 21:04:55 +0200133
134 /* cpu boost support */
135 bool use_cpu_boost;
136 bool is_cpu_boosted;
137 struct cpumask boost_cpu;
Maya Erez692e94c2017-05-14 16:40:19 +0300138
139 bool keep_radio_on_during_sleep;
Maya Erez308f9fe2017-11-08 21:35:31 +0200140 int features;
Lior David639ebed2017-02-28 14:23:55 +0200141};
142
143static LIST_HEAD(dev_list);
144
145static struct msm11ad_ctx *pcidev2ctx(struct pci_dev *pcidev)
146{
147 struct msm11ad_ctx *ctx;
148
149 list_for_each_entry(ctx, &dev_list, list) {
150 if (ctx->pcidev == pcidev)
151 return ctx;
152 }
153 return NULL;
154}
155
156static int msm_11ad_init_vreg(struct device *dev,
157 struct msm11ad_vreg *vreg, const char *name)
158{
159 int rc = 0;
160
161 if (!vreg)
162 return 0;
163
164 vreg->name = kstrdup(name, GFP_KERNEL);
165 if (!vreg->name)
166 return -ENOMEM;
167
168 vreg->reg = devm_regulator_get(dev, name);
169 if (IS_ERR_OR_NULL(vreg->reg)) {
170 rc = PTR_ERR(vreg->reg);
171 dev_err(dev, "%s: failed to get %s, rc=%d\n",
172 __func__, name, rc);
173 kfree(vreg->name);
174 vreg->reg = NULL;
175 goto out;
176 }
177
178 dev_info(dev, "%s: %s initialized successfully\n", __func__, name);
179
180out:
181 return rc;
182}
183
184static int msm_11ad_release_vreg(struct device *dev, struct msm11ad_vreg *vreg)
185{
186 if (!vreg || !vreg->reg)
187 return 0;
188
189 dev_info(dev, "%s: %s released\n", __func__, vreg->name);
190
191 devm_regulator_put(vreg->reg);
192 vreg->reg = NULL;
193 kfree(vreg->name);
194
195 return 0;
196}
197
198static int msm_11ad_init_clk(struct device *dev, struct msm11ad_clk *clk,
199 const char *name)
200{
201 int rc = 0;
202
203 clk->name = kstrdup(name, GFP_KERNEL);
204 if (!clk->name)
205 return -ENOMEM;
206
207 clk->clk = devm_clk_get(dev, name);
208 if (IS_ERR(clk->clk)) {
209 rc = PTR_ERR(clk->clk);
210 if (rc == -ENOENT)
211 rc = -EPROBE_DEFER;
212 dev_err(dev, "%s: failed to get %s rc %d",
213 __func__, name, rc);
214 kfree(clk->name);
215 clk->clk = NULL;
216 goto out;
217 }
218
219 dev_info(dev, "%s: %s initialized successfully\n", __func__, name);
220
221out:
222 return rc;
223}
224
225static int msm_11ad_release_clk(struct device *dev, struct msm11ad_clk *clk)
226{
227 if (!clk || !clk->clk)
228 return 0;
229
230 dev_info(dev, "%s: %s released\n", __func__, clk->name);
231
232 devm_clk_put(dev, clk->clk);
233 clk->clk = NULL;
234
235 kfree(clk->name);
236
237 return 0;
238}
239
240static int msm_11ad_init_vregs(struct msm11ad_ctx *ctx)
241{
242 int rc;
243 struct device *dev = ctx->dev;
244
245 if (!of_property_read_bool(dev->of_node, "qcom,use-ext-supply"))
246 return 0;
247
248 rc = msm_11ad_init_vreg(dev, &ctx->vdd, "vdd");
249 if (rc)
250 goto out;
251
252 ctx->vdd.max_uV = VDD_MAX_UV;
253 ctx->vdd.min_uV = VDD_MIN_UV;
254 ctx->vdd.max_uA = VDD_MAX_UA;
255
256 rc = msm_11ad_init_vreg(dev, &ctx->vddio, "vddio");
257 if (rc)
258 goto vddio_fail;
259
260 ctx->vddio.max_uV = VDDIO_MAX_UV;
261 ctx->vddio.min_uV = VDDIO_MIN_UV;
262 ctx->vddio.max_uA = VDDIO_MAX_UA;
263
264 return rc;
265
266vddio_fail:
267 msm_11ad_release_vreg(dev, &ctx->vdd);
268out:
269 return rc;
270}
271
272static void msm_11ad_release_vregs(struct msm11ad_ctx *ctx)
273{
274 msm_11ad_release_vreg(ctx->dev, &ctx->vdd);
275 msm_11ad_release_vreg(ctx->dev, &ctx->vddio);
276}
277
278static int msm_11ad_cfg_vreg(struct device *dev,
279 struct msm11ad_vreg *vreg, bool on)
280{
281 int rc = 0;
282 int min_uV;
283 int uA_load;
284
285 if (!vreg || !vreg->reg)
286 goto out;
287
288 if (regulator_count_voltages(vreg->reg) > 0) {
289 min_uV = on ? vreg->min_uV : 0;
290 rc = regulator_set_voltage(vreg->reg, min_uV, vreg->max_uV);
291 if (rc) {
292 dev_err(dev, "%s: %s set voltage failed, err=%d\n",
293 __func__, vreg->name, rc);
294 goto out;
295 }
296 uA_load = on ? vreg->max_uA : 0;
297 rc = regulator_set_load(vreg->reg, uA_load);
298 if (rc >= 0) {
299 /*
300 * regulator_set_load() returns new regulator
301 * mode upon success.
302 */
303 dev_dbg(dev,
304 "%s: %s regulator_set_load rc(%d)\n",
305 __func__, vreg->name, rc);
306 rc = 0;
307 } else {
308 dev_err(dev,
309 "%s: %s set load(uA_load=%d) failed, rc=%d\n",
310 __func__, vreg->name, uA_load, rc);
311 goto out;
312 }
313 }
314
315out:
316 return rc;
317}
318
319static int msm_11ad_enable_vreg(struct msm11ad_ctx *ctx,
320 struct msm11ad_vreg *vreg)
321{
322 struct device *dev = ctx->dev;
323 int rc = 0;
324
325 if (!vreg || !vreg->reg || vreg->enabled)
326 goto out;
327
328 rc = msm_11ad_cfg_vreg(dev, vreg, true);
329 if (rc)
330 goto out;
331
332 rc = regulator_enable(vreg->reg);
333 if (rc) {
334 dev_err(dev, "%s: %s enable failed, rc=%d\n",
335 __func__, vreg->name, rc);
336 goto enable_fail;
337 }
338
339 vreg->enabled = true;
340
341 dev_info(dev, "%s: %s enabled\n", __func__, vreg->name);
342
343 return rc;
344
345enable_fail:
346 msm_11ad_cfg_vreg(dev, vreg, false);
347out:
348 return rc;
349}
350
351static int msm_11ad_disable_vreg(struct msm11ad_ctx *ctx,
352 struct msm11ad_vreg *vreg)
353{
354 struct device *dev = ctx->dev;
355 int rc = 0;
356
357 if (!vreg || !vreg->reg || !vreg->enabled)
358 goto out;
359
360 rc = regulator_disable(vreg->reg);
361 if (rc) {
362 dev_err(dev, "%s: %s disable failed, rc=%d\n",
363 __func__, vreg->name, rc);
364 goto out;
365 }
366
367 /* ignore errors on applying disable config */
368 msm_11ad_cfg_vreg(dev, vreg, false);
369 vreg->enabled = false;
370
371 dev_info(dev, "%s: %s disabled\n", __func__, vreg->name);
372
373out:
374 return rc;
375}
376
377static int msm_11ad_enable_vregs(struct msm11ad_ctx *ctx)
378{
379 int rc = 0;
380
381 rc = msm_11ad_enable_vreg(ctx, &ctx->vdd);
382 if (rc)
383 goto out;
384
385 rc = msm_11ad_enable_vreg(ctx, &ctx->vddio);
386 if (rc)
387 goto vddio_fail;
388
389 return rc;
390
391vddio_fail:
392 msm_11ad_disable_vreg(ctx, &ctx->vdd);
393out:
394 return rc;
395}
396
397static int msm_11ad_disable_vregs(struct msm11ad_ctx *ctx)
398{
399 if (!ctx->vdd.reg && !ctx->vddio.reg)
400 goto out;
401
402 /* ignore errors on disable vreg */
403 msm_11ad_disable_vreg(ctx, &ctx->vdd);
404 msm_11ad_disable_vreg(ctx, &ctx->vddio);
405
406out:
407 return 0;
408}
409
410static int msm_11ad_enable_clk(struct msm11ad_ctx *ctx,
411 struct msm11ad_clk *clk)
412{
413 struct device *dev = ctx->dev;
414 int rc = 0;
415
416 if (!clk || !clk->clk || clk->enabled)
417 goto out;
418
419 rc = clk_prepare_enable(clk->clk);
420 if (rc) {
421 dev_err(dev, "%s: failed to enable %s, rc(%d)\n",
422 __func__, clk->name, rc);
423 goto out;
424 }
425 clk->enabled = true;
426
427 dev_dbg(dev, "%s: %s enabled\n", __func__, clk->name);
428
429out:
430 return rc;
431}
432
433static void msm_11ad_disable_clk(struct msm11ad_ctx *ctx,
434 struct msm11ad_clk *clk)
435{
436 struct device *dev = ctx->dev;
437
438 if (!clk || !clk->clk || !clk->enabled)
439 goto out;
440
441 clk_disable_unprepare(clk->clk);
442 clk->enabled = false;
443
444 dev_dbg(dev, "%s: %s disabled\n", __func__, clk->name);
445
446out:
447 return;
448}
449
450static int msm_11ad_enable_clocks(struct msm11ad_ctx *ctx)
451{
452 int rc;
453
454 rc = msm_11ad_enable_clk(ctx, &ctx->rf_clk3);
455 if (rc)
456 return rc;
457
458 rc = msm_11ad_enable_clk(ctx, &ctx->rf_clk3_pin);
459 if (rc)
460 msm_11ad_disable_clk(ctx, &ctx->rf_clk3);
461
462 return rc;
463}
464
465static int msm_11ad_init_clocks(struct msm11ad_ctx *ctx)
466{
467 int rc;
468 struct device *dev = ctx->dev;
469
470 if (!of_property_read_bool(dev->of_node, "qcom,use-ext-clocks"))
471 return 0;
472
473 rc = msm_11ad_init_clk(dev, &ctx->rf_clk3, "rf_clk3_clk");
474 if (rc)
475 return rc;
476
477 rc = msm_11ad_init_clk(dev, &ctx->rf_clk3_pin, "rf_clk3_pin_clk");
478 if (rc)
479 msm_11ad_release_clk(ctx->dev, &ctx->rf_clk3);
480
481 return rc;
482}
483
484static void msm_11ad_release_clocks(struct msm11ad_ctx *ctx)
485{
486 msm_11ad_release_clk(ctx->dev, &ctx->rf_clk3_pin);
487 msm_11ad_release_clk(ctx->dev, &ctx->rf_clk3);
488}
489
490static void msm_11ad_disable_clocks(struct msm11ad_ctx *ctx)
491{
492 msm_11ad_disable_clk(ctx, &ctx->rf_clk3_pin);
493 msm_11ad_disable_clk(ctx, &ctx->rf_clk3);
494}
495
Maya Erez3ef3e362017-03-27 08:47:02 +0300496int msm_11ad_ctrl_aspm_l1(struct msm11ad_ctx *ctx, bool enable)
497{
498 int rc;
499 u32 val;
500 struct pci_dev *pdev = ctx->pcidev;
501 bool l1_enabled;
502
503 /* Read current state */
504 rc = pci_read_config_dword(pdev,
505 PCIE20_CAP_LINKCTRLSTATUS, &val);
506 if (rc) {
507 dev_err(ctx->dev,
508 "reading PCIE20_CAP_LINKCTRLSTATUS failed:%d\n", rc);
509 return rc;
510 }
511 dev_dbg(ctx->dev, "PCIE20_CAP_LINKCTRLSTATUS read returns 0x%x\n", val);
512
513 l1_enabled = val & PCI_EXP_LNKCTL_ASPM_L1;
514 if (l1_enabled == enable) {
515 dev_dbg(ctx->dev, "ASPM_L1 is already %s\n",
516 l1_enabled ? "enabled" : "disabled");
517 return 0;
518 }
519
520 if (enable)
521 val |= PCI_EXP_LNKCTL_ASPM_L1; /* enable bit 1 */
522 else
523 val &= ~PCI_EXP_LNKCTL_ASPM_L1; /* disable bit 1 */
524
525 dev_dbg(ctx->dev, "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x)\n",
526 val);
527 rc = pci_write_config_dword(pdev,
528 PCIE20_CAP_LINKCTRLSTATUS, val);
529 if (rc)
530 dev_err(ctx->dev,
531 "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x) failed:%d\n",
532 val, rc);
533
534 return rc;
535}
536
Maya Erez692e94c2017-05-14 16:40:19 +0300537static int msm_11ad_turn_device_power_off(struct msm11ad_ctx *ctx)
Lior David639ebed2017-02-28 14:23:55 +0200538{
Lior David639ebed2017-02-28 14:23:55 +0200539 if (ctx->gpio_en >= 0)
540 gpio_direction_output(ctx->gpio_en, 0);
541
542 if (ctx->sleep_clk_en >= 0)
543 gpio_direction_output(ctx->sleep_clk_en, 0);
544
545 msm_11ad_disable_clocks(ctx);
546
547 msm_11ad_disable_vregs(ctx);
548
Maya Erez692e94c2017-05-14 16:40:19 +0300549 return 0;
Lior David639ebed2017-02-28 14:23:55 +0200550}
551
Maya Erez692e94c2017-05-14 16:40:19 +0300552static int msm_11ad_turn_device_power_on(struct msm11ad_ctx *ctx)
Lior David639ebed2017-02-28 14:23:55 +0200553{
554 int rc;
Lior David639ebed2017-02-28 14:23:55 +0200555
556 rc = msm_11ad_enable_vregs(ctx);
557 if (rc) {
558 dev_err(ctx->dev, "msm_11ad_enable_vregs failed :%d\n",
559 rc);
560 return rc;
561 }
562
563 rc = msm_11ad_enable_clocks(ctx);
564 if (rc) {
565 dev_err(ctx->dev, "msm_11ad_enable_clocks failed :%d\n", rc);
566 goto err_disable_vregs;
567 }
568
569 if (ctx->sleep_clk_en >= 0)
570 gpio_direction_output(ctx->sleep_clk_en, 1);
571
Lior David639ebed2017-02-28 14:23:55 +0200572 if (ctx->gpio_en >= 0) {
573 gpio_direction_output(ctx->gpio_en, 1);
574 msleep(WIGIG_ENABLE_DELAY);
575 }
576
Maya Erez692e94c2017-05-14 16:40:19 +0300577 return 0;
578
579err_disable_vregs:
580 msm_11ad_disable_vregs(ctx);
581 return rc;
582}
583
584static int msm_11ad_suspend_power_off(void *handle)
585{
586 int rc;
587 struct msm11ad_ctx *ctx = handle;
588 struct pci_dev *pcidev;
589
590 pr_debug("%s\n", __func__);
591
592 if (!ctx) {
593 pr_err("%s: No context\n", __func__);
594 return -ENODEV;
595 }
596
597 pcidev = ctx->pcidev;
598
599 msm_pcie_shadow_control(ctx->pcidev, 0);
600
601 rc = pci_save_state(pcidev);
602 if (rc) {
603 dev_err(ctx->dev, "pci_save_state failed :%d\n", rc);
604 goto out;
605 }
606 ctx->pristine_state = pci_store_saved_state(pcidev);
607
608 rc = msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcidev->bus->number,
609 pcidev, NULL, 0);
610 if (rc) {
611 dev_err(ctx->dev, "msm_pcie_pm_control(SUSPEND) failed :%d\n",
612 rc);
613 goto out;
614 }
615
616 rc = msm_11ad_turn_device_power_off(ctx);
617
618out:
619 return rc;
620}
621
622static int ops_suspend(void *handle, bool keep_device_power)
623{
624 struct msm11ad_ctx *ctx = handle;
625 struct pci_dev *pcidev;
626 int rc;
627
628 pr_debug("11ad suspend: %s\n", __func__);
629 if (!ctx) {
630 pr_err("11ad suspend: No context\n");
631 return -ENODEV;
632 }
633
634 if (!keep_device_power)
635 return msm_11ad_suspend_power_off(handle);
636
637 pcidev = ctx->pcidev;
638
639 msm_pcie_shadow_control(pcidev, 0);
640
641 dev_dbg(ctx->dev, "disable device and save config\n");
642 pci_disable_device(pcidev);
643 pci_save_state(pcidev);
644 ctx->pristine_state = pci_store_saved_state(pcidev);
645 dev_dbg(ctx->dev, "moving to D3\n");
646 pci_set_power_state(pcidev, PCI_D3hot);
647
648 rc = msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcidev->bus->number,
649 pcidev, NULL, 0);
650 if (rc)
651 dev_err(ctx->dev, "msm_pcie_pm_control(SUSPEND) failed :%d\n",
652 rc);
653
654 return rc;
655}
656
657static int msm_11ad_resume_power_on(void *handle)
658{
659 int rc;
660 struct msm11ad_ctx *ctx = handle;
661 struct pci_dev *pcidev;
662
663 pr_debug("%s\n", __func__);
664
665 if (!ctx) {
666 pr_err("%s: No context\n", __func__);
667 return -ENODEV;
668 }
669 pcidev = ctx->pcidev;
670
671 rc = msm_11ad_turn_device_power_on(ctx);
672 if (rc)
673 return rc;
674
Lior David639ebed2017-02-28 14:23:55 +0200675 rc = msm_pcie_pm_control(MSM_PCIE_RESUME, pcidev->bus->number,
Maya Erez692e94c2017-05-14 16:40:19 +0300676 pcidev, NULL, 0);
Lior David639ebed2017-02-28 14:23:55 +0200677 if (rc) {
678 dev_err(ctx->dev, "msm_pcie_pm_control(RESUME) failed :%d\n",
679 rc);
680 goto err_disable_power;
681 }
Maya Erez692e94c2017-05-14 16:40:19 +0300682
683 pci_set_power_state(pcidev, PCI_D0);
684
685 if (ctx->pristine_state)
686 pci_load_saved_state(ctx->pcidev, ctx->pristine_state);
687 pci_restore_state(ctx->pcidev);
688
689 msm_pcie_shadow_control(ctx->pcidev, 1);
Lior David639ebed2017-02-28 14:23:55 +0200690
Maya Erez3ef3e362017-03-27 08:47:02 +0300691 /* Disable L1, in case it is enabled */
692 if (ctx->l1_enabled_in_enum) {
693 rc = msm_11ad_ctrl_aspm_l1(ctx, false);
694 if (rc) {
695 dev_err(ctx->dev,
696 "failed to disable L1, rc %d\n", rc);
697 goto err_suspend_rc;
698 }
Maya Erez17ef6c32017-03-27 08:37:07 +0300699 }
700
Lior David639ebed2017-02-28 14:23:55 +0200701 return 0;
702
703err_suspend_rc:
704 msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcidev->bus->number,
Maya Erez692e94c2017-05-14 16:40:19 +0300705 pcidev, NULL, 0);
Lior David639ebed2017-02-28 14:23:55 +0200706err_disable_power:
Maya Erez692e94c2017-05-14 16:40:19 +0300707 msm_11ad_turn_device_power_off(ctx);
708 return rc;
709}
Lior David639ebed2017-02-28 14:23:55 +0200710
Maya Erez692e94c2017-05-14 16:40:19 +0300711static int ops_resume(void *handle, bool device_powered_on)
712{
713 struct msm11ad_ctx *ctx = handle;
714 struct pci_dev *pcidev;
715 int rc;
Lior David639ebed2017-02-28 14:23:55 +0200716
Maya Erez692e94c2017-05-14 16:40:19 +0300717 pr_debug("11ad resume: %s\n", __func__);
718 if (!ctx) {
719 pr_err("11ad resume: No context\n");
720 return -ENODEV;
721 }
Lior David639ebed2017-02-28 14:23:55 +0200722
Maya Erez692e94c2017-05-14 16:40:19 +0300723 pcidev = ctx->pcidev;
724
725 if (!device_powered_on)
726 return msm_11ad_resume_power_on(handle);
727
728 rc = msm_pcie_pm_control(MSM_PCIE_RESUME, pcidev->bus->number,
729 pcidev, NULL, 0);
730 if (rc) {
731 dev_err(ctx->dev, "msm_pcie_pm_control(RESUME) failed :%d\n",
732 rc);
733 return rc;
734 }
735 pci_set_power_state(pcidev, PCI_D0);
736
737 dev_dbg(ctx->dev, "restore state and enable device\n");
738 pci_load_saved_state(pcidev, ctx->pristine_state);
739 pci_restore_state(pcidev);
740
741 rc = pci_enable_device(pcidev);
742 if (rc) {
743 dev_err(ctx->dev, "pci_enable_device failed (%d)\n", rc);
744 goto out;
745 }
746
747 msm_pcie_shadow_control(pcidev, 1);
748
749 dev_dbg(ctx->dev, "pci set master\n");
750 pci_set_master(pcidev);
751
752out:
Lior David639ebed2017-02-28 14:23:55 +0200753 return rc;
754}
755
756static int msm_11ad_smmu_init(struct msm11ad_ctx *ctx)
757{
758 int atomic_ctx = 1;
759 int rc;
Maya Erez34c251e2017-03-27 09:31:39 +0300760 int force_pt_coherent = 1;
761 int smmu_bypass = !ctx->smmu_s1_en;
Lior David639ebed2017-02-28 14:23:55 +0200762
763 if (!ctx->use_smmu)
764 return 0;
765
Maya Erez34c251e2017-03-27 09:31:39 +0300766 dev_info(ctx->dev, "Initialize SMMU, bypass=%d, fastmap=%d, coherent=%d\n",
767 smmu_bypass, ctx->smmu_fast_map, ctx->smmu_coherent);
Lior David639ebed2017-02-28 14:23:55 +0200768
769 ctx->mapping = arm_iommu_create_mapping(&platform_bus_type,
Maya Erez34c251e2017-03-27 09:31:39 +0300770 ctx->smmu_base, ctx->smmu_size);
Lior David639ebed2017-02-28 14:23:55 +0200771 if (IS_ERR_OR_NULL(ctx->mapping)) {
772 rc = PTR_ERR(ctx->mapping) ?: -ENODEV;
773 dev_err(ctx->dev, "Failed to create IOMMU mapping (%d)\n", rc);
774 return rc;
775 }
776
777 rc = iommu_domain_set_attr(ctx->mapping->domain,
778 DOMAIN_ATTR_ATOMIC,
779 &atomic_ctx);
780 if (rc) {
781 dev_err(ctx->dev, "Set atomic attribute to SMMU failed (%d)\n",
782 rc);
783 goto release_mapping;
784 }
785
Maya Erez34c251e2017-03-27 09:31:39 +0300786 if (smmu_bypass) {
Lior David639ebed2017-02-28 14:23:55 +0200787 rc = iommu_domain_set_attr(ctx->mapping->domain,
788 DOMAIN_ATTR_S1_BYPASS,
Maya Erez34c251e2017-03-27 09:31:39 +0300789 &smmu_bypass);
Lior David639ebed2017-02-28 14:23:55 +0200790 if (rc) {
791 dev_err(ctx->dev, "Set bypass attribute to SMMU failed (%d)\n",
792 rc);
793 goto release_mapping;
794 }
Maya Erez34c251e2017-03-27 09:31:39 +0300795 } else {
796 /* Set dma-coherent and page table coherency */
797 if (ctx->smmu_coherent) {
798 arch_setup_dma_ops(&ctx->pcidev->dev, 0, 0, NULL, true);
799 rc = iommu_domain_set_attr(ctx->mapping->domain,
800 DOMAIN_ATTR_PAGE_TABLE_FORCE_COHERENT,
801 &force_pt_coherent);
802 if (rc) {
803 dev_err(ctx->dev,
804 "Set SMMU PAGE_TABLE_FORCE_COHERENT attr failed (%d)\n",
805 rc);
806 goto release_mapping;
807 }
808 }
809
810 if (ctx->smmu_fast_map) {
811 rc = iommu_domain_set_attr(ctx->mapping->domain,
812 DOMAIN_ATTR_FAST,
813 &ctx->smmu_fast_map);
814 if (rc) {
815 dev_err(ctx->dev, "Set fast attribute to SMMU failed (%d)\n",
816 rc);
817 goto release_mapping;
818 }
Lior David639ebed2017-02-28 14:23:55 +0200819 }
820 }
821
822 rc = arm_iommu_attach_device(&ctx->pcidev->dev, ctx->mapping);
823 if (rc) {
824 dev_err(ctx->dev, "arm_iommu_attach_device failed (%d)\n", rc);
825 goto release_mapping;
826 }
827 dev_info(ctx->dev, "attached to IOMMU\n");
828
829 return 0;
830release_mapping:
831 arm_iommu_release_mapping(ctx->mapping);
832 ctx->mapping = NULL;
833 return rc;
834}
835
836static int msm_11ad_ssr_shutdown(const struct subsys_desc *subsys,
837 bool force_stop)
838{
839 pr_info("%s(%p,%d)\n", __func__, subsys, force_stop);
840 /* nothing is done in shutdown. We do full recovery in powerup */
841 return 0;
842}
843
844static int msm_11ad_ssr_powerup(const struct subsys_desc *subsys)
845{
846 int rc = 0;
847 struct platform_device *pdev;
848 struct msm11ad_ctx *ctx;
849
850 pr_info("%s(%p)\n", __func__, subsys);
851
852 pdev = to_platform_device(subsys->dev);
853 ctx = platform_get_drvdata(pdev);
854
855 if (!ctx)
856 return -ENODEV;
857
858 if (ctx->recovery_in_progress) {
859 if (ctx->rops.fw_recovery && ctx->wil_handle) {
860 dev_info(ctx->dev, "requesting FW recovery\n");
861 rc = ctx->rops.fw_recovery(ctx->wil_handle);
862 }
863 ctx->recovery_in_progress = false;
864 }
865
866 return rc;
867}
868
Lior David88865332017-03-30 17:22:57 +0300869static int msm_11ad_ssr_copy_ramdump(struct msm11ad_ctx *ctx)
870{
871 if (ctx->rops.ramdump && ctx->wil_handle) {
872 int rc = ctx->rops.ramdump(ctx->wil_handle, ctx->ramdump_addr,
Alexei Avshalom Lazar52864112017-12-07 12:49:56 +0200873 ctx->ramdump_size);
Lior David88865332017-03-30 17:22:57 +0300874 if (rc) {
875 dev_err(ctx->dev, "ramdump failed : %d\n", rc);
876 return -EINVAL;
877 }
878 }
879
880 ctx->dump_data.version = WIGIG_DUMP_FORMAT_VER;
881 strlcpy(ctx->dump_data.name, WIGIG_SUBSYS_NAME,
882 sizeof(ctx->dump_data.name));
883
884 ctx->dump_data.magic = WIGIG_DUMP_MAGIC_VER_V1;
885 return 0;
886}
887
Lior David639ebed2017-02-28 14:23:55 +0200888static int msm_11ad_ssr_ramdump(int enable, const struct subsys_desc *subsys)
889{
890 int rc;
891 struct ramdump_segment segment;
892 struct platform_device *pdev;
893 struct msm11ad_ctx *ctx;
894
895 pdev = to_platform_device(subsys->dev);
896 ctx = platform_get_drvdata(pdev);
897
898 if (!ctx)
899 return -ENODEV;
900
901 if (!enable)
902 return 0;
903
Lior David88865332017-03-30 17:22:57 +0300904 if (!ctx->recovery_in_progress) {
905 rc = msm_11ad_ssr_copy_ramdump(ctx);
906 if (rc)
907 return rc;
Lior David639ebed2017-02-28 14:23:55 +0200908 }
909
910 memset(&segment, 0, sizeof(segment));
911 segment.v_address = ctx->ramdump_addr;
Alexei Avshalom Lazar52864112017-12-07 12:49:56 +0200912 segment.size = ctx->ramdump_size;
Lior David639ebed2017-02-28 14:23:55 +0200913
914 return do_ramdump(ctx->ramdump_dev, &segment, 1);
915}
916
917static void msm_11ad_ssr_crash_shutdown(const struct subsys_desc *subsys)
918{
Lior David639ebed2017-02-28 14:23:55 +0200919 struct platform_device *pdev;
920 struct msm11ad_ctx *ctx;
921
922 pdev = to_platform_device(subsys->dev);
923 ctx = platform_get_drvdata(pdev);
924
925 if (!ctx) {
926 pr_err("%s: no context\n", __func__);
927 return;
928 }
929
Lior David88865332017-03-30 17:22:57 +0300930 if (!ctx->recovery_in_progress)
931 (void)msm_11ad_ssr_copy_ramdump(ctx);
Lior David639ebed2017-02-28 14:23:55 +0200932}
933
934static void msm_11ad_ssr_deinit(struct msm11ad_ctx *ctx)
935{
936 if (ctx->ramdump_dev) {
937 destroy_ramdump_device(ctx->ramdump_dev);
938 ctx->ramdump_dev = NULL;
939 }
940
941 kfree(ctx->ramdump_addr);
942 ctx->ramdump_addr = NULL;
943
944 if (ctx->subsys_handle) {
945 subsystem_put(ctx->subsys_handle);
946 ctx->subsys_handle = NULL;
947 }
948
949 if (ctx->subsys) {
950 subsys_unregister(ctx->subsys);
951 ctx->subsys = NULL;
952 }
953}
954
955static int msm_11ad_ssr_init(struct msm11ad_ctx *ctx)
956{
957 int rc;
958 struct msm_dump_entry dump_entry;
959
960 ctx->subsysdesc.name = "WIGIG";
961 ctx->subsysdesc.owner = THIS_MODULE;
962 ctx->subsysdesc.shutdown = msm_11ad_ssr_shutdown;
963 ctx->subsysdesc.powerup = msm_11ad_ssr_powerup;
964 ctx->subsysdesc.ramdump = msm_11ad_ssr_ramdump;
965 ctx->subsysdesc.crash_shutdown = msm_11ad_ssr_crash_shutdown;
966 ctx->subsysdesc.dev = ctx->dev;
967 ctx->subsys = subsys_register(&ctx->subsysdesc);
968 if (IS_ERR(ctx->subsys)) {
969 rc = PTR_ERR(ctx->subsys);
970 dev_err(ctx->dev, "subsys_register failed :%d\n", rc);
971 goto out_rc;
972 }
973
974 /* register ramdump area */
Alexei Avshalom Lazar52864112017-12-07 12:49:56 +0200975 ctx->ramdump_addr = kmalloc(ctx->ramdump_size, GFP_KERNEL);
Lior David639ebed2017-02-28 14:23:55 +0200976 if (!ctx->ramdump_addr) {
977 rc = -ENOMEM;
978 goto out_rc;
979 }
980
981 ctx->dump_data.addr = virt_to_phys(ctx->ramdump_addr);
Alexei Avshalom Lazar52864112017-12-07 12:49:56 +0200982 ctx->dump_data.len = ctx->ramdump_size;
Lior David639ebed2017-02-28 14:23:55 +0200983 dump_entry.id = MSM_DUMP_DATA_WIGIG;
984 dump_entry.addr = virt_to_phys(&ctx->dump_data);
985
986 rc = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry);
987 if (rc) {
988 dev_err(ctx->dev, "Dump table setup failed: %d\n", rc);
989 goto out_rc;
990 }
991
992 ctx->ramdump_dev = create_ramdump_device(ctx->subsysdesc.name,
993 ctx->subsysdesc.dev);
994 if (!ctx->ramdump_dev) {
995 dev_err(ctx->dev, "Create ramdump device failed: %d\n", rc);
996 rc = -ENOMEM;
997 goto out_rc;
998 }
999
1000 return 0;
1001
1002out_rc:
1003 msm_11ad_ssr_deinit(ctx);
1004 return rc;
1005}
1006
Lior Daviddb711962017-02-15 21:04:55 +02001007static void msm_11ad_init_cpu_boost(struct msm11ad_ctx *ctx)
1008{
1009 unsigned int minfreq = 0, maxfreq = 0, freq;
Hamad Kadmany6e03e622017-05-09 13:35:31 +03001010 int i, boost_cpu = 0;
Lior Daviddb711962017-02-15 21:04:55 +02001011
1012 for_each_possible_cpu(i) {
1013 freq = cpufreq_quick_get_max(i);
1014 if (freq > maxfreq) {
1015 maxfreq = freq;
1016 boost_cpu = i;
1017 }
1018 if (!minfreq || freq < minfreq)
1019 minfreq = freq;
1020 }
1021
1022 if (minfreq != maxfreq) {
1023 /*
1024 * use first big core for boost, to be compatible with WLAN
1025 * which assigns big cores from the last index
1026 */
1027 ctx->use_cpu_boost = true;
1028 cpumask_clear(&ctx->boost_cpu);
1029 cpumask_set_cpu(boost_cpu, &ctx->boost_cpu);
1030 dev_info(ctx->dev, "CPU boost: will use core %d\n", boost_cpu);
1031 } else {
1032 ctx->use_cpu_boost = false;
1033 dev_info(ctx->dev, "CPU boost disabled, uniform topology\n");
1034 }
1035}
1036
Lior David639ebed2017-02-28 14:23:55 +02001037static int msm_11ad_probe(struct platform_device *pdev)
1038{
1039 struct msm11ad_ctx *ctx;
1040 struct device *dev = &pdev->dev;
1041 struct device_node *of_node = dev->of_node;
1042 struct device_node *rc_node;
1043 struct pci_dev *pcidev = NULL;
Maya Erez34c251e2017-03-27 09:31:39 +03001044 u32 smmu_mapping[2];
Alexei Avshalom Lazar52864112017-12-07 12:49:56 +02001045 int rc, i;
Maya Erez17ef6c32017-03-27 08:37:07 +03001046 u32 val;
Alexei Avshalom Lazar52864112017-12-07 12:49:56 +02001047 bool pcidev_found = false;
Lior David639ebed2017-02-28 14:23:55 +02001048
1049 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
1050 if (!ctx)
1051 return -ENOMEM;
1052
1053 ctx->dev = dev;
1054
1055 /*== parse ==*/
1056
1057 /* Information pieces:
1058 * - of_node stands for "wil6210":
1059 * wil6210: qcom,wil6210 {
1060 * compatible = "qcom,wil6210";
1061 * qcom,pcie-parent = <&pcie1>;
1062 * qcom,wigig-en = <&tlmm 94 0>; (ctx->gpio_en)
1063 * qcom,sleep-clk-en = <&pm8994_gpios 18 0>; (ctx->sleep_clk_en)
1064 * qcom,msm-bus,name = "wil6210";
1065 * qcom,msm-bus,num-cases = <2>;
1066 * qcom,msm-bus,num-paths = <1>;
1067 * qcom,msm-bus,vectors-KBps =
1068 * <100 512 0 0>,
1069 * <100 512 600000 800000>;
1070 * qcom,smmu-support;
1071 *};
1072 * rc_node stands for "qcom,pcie", selected entries:
1073 * cell-index = <1>; (ctx->rc_index)
1074 * iommus = <&anoc0_smmu>;
1075 * qcom,smmu-exist;
1076 */
1077
1078 /* wigig-en is optional property */
1079 ctx->gpio_en = of_get_named_gpio(of_node, gpio_en_name, 0);
1080 if (ctx->gpio_en < 0)
1081 dev_warn(ctx->dev, "GPIO <%s> not found, enable GPIO not used\n",
1082 gpio_en_name);
1083 ctx->sleep_clk_en = of_get_named_gpio(of_node, sleep_clk_en_name, 0);
1084 if (ctx->sleep_clk_en < 0)
1085 dev_warn(ctx->dev, "GPIO <%s> not found, sleep clock not used\n",
1086 sleep_clk_en_name);
1087 rc_node = of_parse_phandle(of_node, "qcom,pcie-parent", 0);
1088 if (!rc_node) {
1089 dev_err(ctx->dev, "Parent PCIE device not found\n");
1090 return -EINVAL;
1091 }
1092 rc = of_property_read_u32(rc_node, "cell-index", &ctx->rc_index);
1093 if (rc < 0) {
1094 dev_err(ctx->dev, "Parent PCIE device index not found\n");
1095 return -EINVAL;
1096 }
1097 ctx->use_smmu = of_property_read_bool(of_node, "qcom,smmu-support");
Maya Erez692e94c2017-05-14 16:40:19 +03001098 ctx->keep_radio_on_during_sleep = of_property_read_bool(of_node,
Hamad Kadmany290544c2017-05-22 13:33:32 +03001099 "qcom,keep-radio-on-during-sleep");
Lior David639ebed2017-02-28 14:23:55 +02001100 ctx->bus_scale = msm_bus_cl_get_pdata(pdev);
Alexei Avshalom Lazar4b3303f2017-11-23 10:59:04 +02001101 if (!ctx->bus_scale) {
1102 dev_err(ctx->dev, "Unable to read bus-scaling from DT\n");
1103 return -EINVAL;
1104 }
Lior David639ebed2017-02-28 14:23:55 +02001105
Maya Erez34c251e2017-03-27 09:31:39 +03001106 ctx->smmu_s1_en = of_property_read_bool(of_node, "qcom,smmu-s1-en");
1107 if (ctx->smmu_s1_en) {
1108 ctx->smmu_fast_map = of_property_read_bool(
1109 of_node, "qcom,smmu-fast-map");
1110 ctx->smmu_coherent = of_property_read_bool(
1111 of_node, "qcom,smmu-coherent");
1112 }
1113 rc = of_property_read_u32_array(dev->of_node, "qcom,smmu-mapping",
1114 smmu_mapping, 2);
1115 if (rc) {
1116 dev_err(ctx->dev,
1117 "Failed to read base/size smmu addresses %d, fallback to default\n",
1118 rc);
1119 ctx->smmu_base = SMMU_BASE;
1120 ctx->smmu_size = SMMU_SIZE;
1121 } else {
1122 ctx->smmu_base = smmu_mapping[0];
1123 ctx->smmu_size = smmu_mapping[1];
1124 }
1125 dev_dbg(ctx->dev, "smmu_base=0x%x smmu_sise=0x%x\n",
1126 ctx->smmu_base, ctx->smmu_size);
Lior David639ebed2017-02-28 14:23:55 +02001127
1128 /*== execute ==*/
1129 /* turn device on */
1130 rc = msm_11ad_init_vregs(ctx);
1131 if (rc) {
1132 dev_err(ctx->dev, "msm_11ad_init_vregs failed: %d\n", rc);
Hamad Kadmanyc3bc8b22017-12-05 07:59:24 +02001133 return rc;
Lior David639ebed2017-02-28 14:23:55 +02001134 }
1135 rc = msm_11ad_enable_vregs(ctx);
1136 if (rc) {
1137 dev_err(ctx->dev, "msm_11ad_enable_vregs failed: %d\n", rc);
1138 goto out_vreg_clk;
1139 }
1140
1141 rc = msm_11ad_init_clocks(ctx);
1142 if (rc) {
1143 dev_err(ctx->dev, "msm_11ad_init_clocks failed: %d\n", rc);
1144 goto out_vreg_clk;
1145 }
1146
1147 rc = msm_11ad_enable_clocks(ctx);
1148 if (rc) {
1149 dev_err(ctx->dev, "msm_11ad_enable_clocks failed: %d\n", rc);
1150 goto out_vreg_clk;
1151 }
1152
1153 if (ctx->gpio_en >= 0) {
1154 rc = gpio_request(ctx->gpio_en, gpio_en_name);
1155 if (rc < 0) {
1156 dev_err(ctx->dev, "failed to request GPIO %d <%s>\n",
1157 ctx->gpio_en, gpio_en_name);
1158 goto out_req;
1159 }
1160 rc = gpio_direction_output(ctx->gpio_en, 1);
1161 if (rc < 0) {
1162 dev_err(ctx->dev, "failed to set GPIO %d <%s>\n",
1163 ctx->gpio_en, gpio_en_name);
1164 goto out_set;
1165 }
1166 msleep(WIGIG_ENABLE_DELAY);
1167 }
1168
1169 /* enumerate it on PCIE */
1170 rc = msm_pcie_enumerate(ctx->rc_index);
1171 if (rc < 0) {
1172 dev_err(ctx->dev, "Parent PCIE enumeration failed\n");
1173 goto out_rc;
1174 }
1175 /* search for PCIE device in our domain */
Alexei Avshalom Lazar52864112017-12-07 12:49:56 +02001176 for (i = 0; i < ARRAY_SIZE(wigig_pci_tbl); ++i) {
1177 do {
1178 pcidev = pci_get_device(wigig_pci_tbl[i].pci_dev.vendor,
1179 wigig_pci_tbl[i].pci_dev.device,
1180 pcidev);
1181 if (!pcidev)
1182 break;
Lior David639ebed2017-02-28 14:23:55 +02001183
Alexei Avshalom Lazar52864112017-12-07 12:49:56 +02001184 if (pci_domain_nr(pcidev->bus) == ctx->rc_index) {
1185 ctx->ramdump_size = wigig_pci_tbl[i].ramdump_sz;
1186 pcidev_found = true;
1187 break;
1188 }
1189 } while (true);
1190
1191 if (pcidev_found)
Lior David639ebed2017-02-28 14:23:55 +02001192 break;
Alexei Avshalom Lazar52864112017-12-07 12:49:56 +02001193 }
1194 if (!pcidev_found) {
Lior David639ebed2017-02-28 14:23:55 +02001195 rc = -ENODEV;
Alexei Avshalom Lazar52864112017-12-07 12:49:56 +02001196 dev_err(ctx->dev, "Wigig device not found\n");
Lior David639ebed2017-02-28 14:23:55 +02001197 goto out_rc;
1198 }
1199 ctx->pcidev = pcidev;
Alexei Avshalom Lazar52864112017-12-07 12:49:56 +02001200 dev_dbg(ctx->dev, "Wigig device %4x:%4x found\n",
1201 ctx->pcidev->vendor, ctx->pcidev->device);
Maya Erez17ef6c32017-03-27 08:37:07 +03001202
Alexei Avshalom Lazar4b3303f2017-11-23 10:59:04 +02001203 rc = msm_pcie_pm_control(MSM_PCIE_RESUME, pcidev->bus->number,
1204 pcidev, NULL, 0);
1205 if (rc) {
1206 dev_err(ctx->dev, "msm_pcie_pm_control(RESUME) failed:%d\n",
1207 rc);
1208 goto out_rc;
1209 }
1210
1211 pci_set_power_state(pcidev, PCI_D0);
1212
1213 pci_restore_state(ctx->pcidev);
1214
Maya Erez3ef3e362017-03-27 08:47:02 +03001215 /* Read current state */
1216 rc = pci_read_config_dword(pcidev,
Maya Erez17ef6c32017-03-27 08:37:07 +03001217 PCIE20_CAP_LINKCTRLSTATUS, &val);
1218 if (rc) {
1219 dev_err(ctx->dev,
1220 "reading PCIE20_CAP_LINKCTRLSTATUS failed:%d\n",
1221 rc);
Alexei Avshalom Lazar4b3303f2017-11-23 10:59:04 +02001222 goto out_suspend;
Maya Erez17ef6c32017-03-27 08:37:07 +03001223 }
Maya Erez3ef3e362017-03-27 08:47:02 +03001224
1225 ctx->l1_enabled_in_enum = val & PCI_EXP_LNKCTL_ASPM_L1;
1226 dev_dbg(ctx->dev, "L1 is %s in enumeration\n",
1227 ctx->l1_enabled_in_enum ? "enabled" : "disabled");
1228
1229 /* Disable L1, in case it is enabled */
1230 if (ctx->l1_enabled_in_enum) {
1231 rc = msm_11ad_ctrl_aspm_l1(ctx, false);
1232 if (rc) {
1233 dev_err(ctx->dev,
1234 "failed to disable L1, rc %d\n", rc);
Alexei Avshalom Lazar4b3303f2017-11-23 10:59:04 +02001235 goto out_suspend;
Maya Erez3ef3e362017-03-27 08:47:02 +03001236 }
Maya Erez17ef6c32017-03-27 08:37:07 +03001237 }
1238
Lior David639ebed2017-02-28 14:23:55 +02001239 if (ctx->sleep_clk_en >= 0) {
1240 rc = gpio_request(ctx->sleep_clk_en, "msm_11ad");
1241 if (rc < 0) {
1242 dev_err(ctx->dev,
1243 "failed to request GPIO %d <%s>, sleep clock disabled\n",
1244 ctx->sleep_clk_en, sleep_clk_en_name);
1245 ctx->sleep_clk_en = -EINVAL;
1246 } else {
1247 gpio_direction_output(ctx->sleep_clk_en, 0);
1248 }
1249 }
1250
1251 /* register for subsystem restart */
1252 rc = msm_11ad_ssr_init(ctx);
1253 if (rc) {
1254 dev_err(ctx->dev, "msm_11ad_ssr_init failed: %d\n", rc);
Alexei Avshalom Lazar4b3303f2017-11-23 10:59:04 +02001255 goto out_suspend;
Lior David639ebed2017-02-28 14:23:55 +02001256 }
1257
Lior Daviddb711962017-02-15 21:04:55 +02001258 msm_11ad_init_cpu_boost(ctx);
1259
Lior David639ebed2017-02-28 14:23:55 +02001260 /* report */
1261 dev_info(ctx->dev, "msm_11ad discovered. %p {\n"
1262 " gpio_en = %d\n"
1263 " sleep_clk_en = %d\n"
1264 " rc_index = %d\n"
1265 " use_smmu = %d\n"
1266 " pcidev = %p\n"
1267 "}\n", ctx, ctx->gpio_en, ctx->sleep_clk_en, ctx->rc_index,
1268 ctx->use_smmu, ctx->pcidev);
1269
1270 platform_set_drvdata(pdev, ctx);
1271 device_disable_async_suspend(&pcidev->dev);
1272
1273 list_add_tail(&ctx->list, &dev_list);
Maya Erez692e94c2017-05-14 16:40:19 +03001274 msm_11ad_suspend_power_off(ctx);
Lior David639ebed2017-02-28 14:23:55 +02001275
1276 return 0;
Alexei Avshalom Lazar4b3303f2017-11-23 10:59:04 +02001277out_suspend:
1278 msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcidev->bus->number,
1279 pcidev, NULL, 0);
Lior David639ebed2017-02-28 14:23:55 +02001280out_rc:
1281 if (ctx->gpio_en >= 0)
1282 gpio_direction_output(ctx->gpio_en, 0);
1283out_set:
1284 if (ctx->gpio_en >= 0)
1285 gpio_free(ctx->gpio_en);
1286out_req:
1287 ctx->gpio_en = -EINVAL;
1288out_vreg_clk:
1289 msm_11ad_disable_clocks(ctx);
1290 msm_11ad_release_clocks(ctx);
1291 msm_11ad_disable_vregs(ctx);
1292 msm_11ad_release_vregs(ctx);
1293
1294 return rc;
1295}
1296
1297static int msm_11ad_remove(struct platform_device *pdev)
1298{
1299 struct msm11ad_ctx *ctx = platform_get_drvdata(pdev);
1300
1301 msm_11ad_ssr_deinit(ctx);
1302 list_del(&ctx->list);
1303 dev_info(ctx->dev, "%s: pdev %p pcidev %p\n", __func__, pdev,
1304 ctx->pcidev);
1305 kfree(ctx->pristine_state);
1306
Lior David639ebed2017-02-28 14:23:55 +02001307 pci_dev_put(ctx->pcidev);
1308 if (ctx->gpio_en >= 0) {
1309 gpio_direction_output(ctx->gpio_en, 0);
1310 gpio_free(ctx->gpio_en);
1311 }
1312 if (ctx->sleep_clk_en >= 0)
1313 gpio_free(ctx->sleep_clk_en);
1314
1315 msm_11ad_disable_clocks(ctx);
1316 msm_11ad_release_clocks(ctx);
1317 msm_11ad_disable_vregs(ctx);
1318 msm_11ad_release_vregs(ctx);
1319
1320 return 0;
1321}
1322
1323static const struct of_device_id msm_11ad_of_match[] = {
1324 { .compatible = "qcom,wil6210", },
1325 {},
1326};
1327
1328static struct platform_driver msm_11ad_driver = {
1329 .driver = {
1330 .name = "msm_11ad",
1331 .of_match_table = msm_11ad_of_match,
1332 },
1333 .probe = msm_11ad_probe,
1334 .remove = msm_11ad_remove,
1335};
1336module_platform_driver(msm_11ad_driver);
1337
Lior Daviddb711962017-02-15 21:04:55 +02001338static void msm_11ad_set_boost_affinity(struct msm11ad_ctx *ctx)
1339{
1340 /*
1341 * There is a very small window where user space can change the
1342 * affinity after we changed it here and before setting the
1343 * NO_BALANCING flag. Retry this several times as a workaround.
1344 */
1345 int retries = 5, rc;
1346 struct irq_desc *desc;
1347
1348 while (retries > 0) {
1349 irq_modify_status(ctx->pcidev->irq, IRQ_NO_BALANCING, 0);
1350 rc = irq_set_affinity_hint(ctx->pcidev->irq, &ctx->boost_cpu);
1351 if (rc)
1352 dev_warn(ctx->dev,
1353 "Failed set affinity, rc=%d\n", rc);
1354 irq_modify_status(ctx->pcidev->irq, 0, IRQ_NO_BALANCING);
1355 desc = irq_to_desc(ctx->pcidev->irq);
1356 if (cpumask_equal(desc->irq_common_data.affinity,
1357 &ctx->boost_cpu))
1358 break;
1359 retries--;
1360 }
1361
1362 if (!retries)
1363 dev_warn(ctx->dev, "failed to set CPU boost affinity\n");
1364}
1365
Dedy Lanskyf91b9152017-05-16 15:18:02 +03001366static void msm_11ad_clear_boost_affinity(struct msm11ad_ctx *ctx)
1367{
1368 int rc;
1369
1370 irq_modify_status(ctx->pcidev->irq, IRQ_NO_BALANCING, 0);
1371 rc = irq_set_affinity_hint(ctx->pcidev->irq, NULL);
1372 if (rc)
1373 dev_warn(ctx->dev,
1374 "Failed clear affinity, rc=%d\n", rc);
1375}
1376
Lior David639ebed2017-02-28 14:23:55 +02001377/* hooks for the wil6210 driver */
1378static int ops_bus_request(void *handle, u32 kbps /* KBytes/Sec */)
1379{
1380 struct msm11ad_ctx *ctx = (struct msm11ad_ctx *)handle;
1381 int rc, i;
1382 int vote = 0; /* vote 0 in case requested kbps cannot be satisfied */
1383 struct msm_bus_paths *usecase;
1384 u32 usecase_kbps;
1385 u32 min_kbps = ~0;
1386
1387 /* find the lowest usecase that is bigger than requested kbps */
1388 for (i = 0; i < ctx->bus_scale->num_usecases; i++) {
1389 usecase = &ctx->bus_scale->usecase[i];
1390 /*
1391 * assume we have single path (vectors[0]). If we ever
1392 * have multiple paths, need to define the behavior
1393 */
1394 usecase_kbps = div64_u64(usecase->vectors[0].ib, 1000);
1395 if (usecase_kbps >= kbps && usecase_kbps < min_kbps) {
1396 min_kbps = usecase_kbps;
1397 vote = i;
1398 }
1399 }
1400
1401 rc = msm_bus_scale_client_update_request(ctx->msm_bus_handle, vote);
1402 if (rc)
1403 dev_err(ctx->dev,
1404 "Failed msm_bus voting. kbps=%d vote=%d, rc=%d\n",
1405 kbps, vote, rc);
1406
Lior Daviddb711962017-02-15 21:04:55 +02001407 if (ctx->use_cpu_boost) {
1408 bool was_boosted = ctx->is_cpu_boosted;
1409 bool needs_boost = (kbps >= WIGIG_MIN_CPU_BOOST_KBPS);
1410
1411 if (was_boosted != needs_boost) {
1412 if (needs_boost) {
1413 rc = core_ctl_set_boost(true);
1414 if (rc) {
1415 dev_err(ctx->dev,
1416 "Failed enable boost rc=%d\n",
1417 rc);
1418 goto out;
1419 }
1420 msm_11ad_set_boost_affinity(ctx);
1421 dev_dbg(ctx->dev, "CPU boost enabled\n");
1422 } else {
1423 rc = core_ctl_set_boost(false);
1424 if (rc)
1425 dev_err(ctx->dev,
1426 "Failed disable boost rc=%d\n",
1427 rc);
Dedy Lanskyf91b9152017-05-16 15:18:02 +03001428 msm_11ad_clear_boost_affinity(ctx);
Lior Daviddb711962017-02-15 21:04:55 +02001429 dev_dbg(ctx->dev, "CPU boost disabled\n");
1430 }
1431 ctx->is_cpu_boosted = needs_boost;
1432 }
1433 }
1434out:
Lior David639ebed2017-02-28 14:23:55 +02001435 return rc;
1436}
1437
1438static void ops_uninit(void *handle)
1439{
1440 struct msm11ad_ctx *ctx = (struct msm11ad_ctx *)handle;
1441
1442 if (ctx->msm_bus_handle) {
1443 msm_bus_scale_unregister_client(ctx->msm_bus_handle);
1444 ctx->msm_bus_handle = 0;
1445 }
1446
1447 if (ctx->use_smmu) {
1448 arm_iommu_detach_device(&ctx->pcidev->dev);
1449 arm_iommu_release_mapping(ctx->mapping);
1450 ctx->mapping = NULL;
1451 }
1452
1453 memset(&ctx->rops, 0, sizeof(ctx->rops));
1454 ctx->wil_handle = NULL;
1455
Maya Erez692e94c2017-05-14 16:40:19 +03001456 msm_11ad_suspend_power_off(ctx);
Lior David639ebed2017-02-28 14:23:55 +02001457}
1458
1459static int msm_11ad_notify_crash(struct msm11ad_ctx *ctx)
1460{
1461 int rc;
1462
1463 if (ctx->subsys) {
1464 dev_info(ctx->dev, "SSR requested\n");
Lior David88865332017-03-30 17:22:57 +03001465 (void)msm_11ad_ssr_copy_ramdump(ctx);
Lior David639ebed2017-02-28 14:23:55 +02001466 ctx->recovery_in_progress = true;
Alexei Avshalom Lazar5f439732017-11-15 10:55:49 +02001467 subsys_set_crash_status(ctx->subsys, CRASH_STATUS_ERR_FATAL);
Lior David639ebed2017-02-28 14:23:55 +02001468 rc = subsystem_restart_dev(ctx->subsys);
1469 if (rc) {
1470 dev_err(ctx->dev,
1471 "subsystem_restart_dev fail: %d\n", rc);
1472 ctx->recovery_in_progress = false;
1473 }
1474 }
1475
1476 return 0;
1477}
1478
1479static int ops_notify(void *handle, enum wil_platform_event evt)
1480{
1481 struct msm11ad_ctx *ctx = (struct msm11ad_ctx *)handle;
1482 int rc = 0;
1483
1484 switch (evt) {
1485 case WIL_PLATFORM_EVT_FW_CRASH:
1486 rc = msm_11ad_notify_crash(ctx);
1487 break;
1488 case WIL_PLATFORM_EVT_PRE_RESET:
1489 /*
Maya Erez308f9fe2017-11-08 21:35:31 +02001490 * Enable rf_clk3 clock before resetting the device to ensure
1491 * stable ref clock during the device reset
Lior David639ebed2017-02-28 14:23:55 +02001492 */
Maya Erez308f9fe2017-11-08 21:35:31 +02001493 if (ctx->features &
1494 BIT(WIL_PLATFORM_FEATURE_FW_EXT_CLK_CONTROL)) {
1495 rc = msm_11ad_enable_clk(ctx, &ctx->rf_clk3);
1496 if (rc) {
1497 dev_err(ctx->dev,
1498 "failed to enable clk, rc %d\n", rc);
1499 break;
1500 }
1501 }
1502
Maya Erez3ef3e362017-03-27 08:47:02 +03001503 /* Re-enable L1 in case it was enabled in enumeration */
1504 if (ctx->l1_enabled_in_enum) {
1505 rc = msm_11ad_ctrl_aspm_l1(ctx, true);
1506 if (rc)
1507 dev_err(ctx->dev,
1508 "failed to enable L1, rc %d\n", rc);
1509 }
Lior David639ebed2017-02-28 14:23:55 +02001510 break;
1511 case WIL_PLATFORM_EVT_FW_RDY:
1512 /*
Maya Erez308f9fe2017-11-08 21:35:31 +02001513 * Disable rf_clk3 clock after the device is up to allow
Lior David639ebed2017-02-28 14:23:55 +02001514 * the device to control it via its GPIO for power saving
1515 */
Maya Erez308f9fe2017-11-08 21:35:31 +02001516 if (ctx->features &
1517 BIT(WIL_PLATFORM_FEATURE_FW_EXT_CLK_CONTROL))
1518 msm_11ad_disable_clk(ctx, &ctx->rf_clk3);
Lior David639ebed2017-02-28 14:23:55 +02001519 break;
1520 default:
1521 pr_debug("%s: Unhandled event %d\n", __func__, evt);
1522 break;
1523 }
1524
1525 return rc;
1526}
1527
Maya Ereza3a52882017-10-22 11:12:59 +03001528static int ops_get_capa(void *handle)
Maya Erez692e94c2017-05-14 16:40:19 +03001529{
1530 struct msm11ad_ctx *ctx = (struct msm11ad_ctx *)handle;
Maya Ereza3a52882017-10-22 11:12:59 +03001531 int capa;
Maya Erez692e94c2017-05-14 16:40:19 +03001532
1533 pr_debug("%s: keep radio on during sleep is %s\n", __func__,
1534 ctx->keep_radio_on_during_sleep ? "allowed" : "not allowed");
1535
Maya Ereza3a52882017-10-22 11:12:59 +03001536 capa = (ctx->keep_radio_on_during_sleep ?
1537 BIT(WIL_PLATFORM_CAPA_RADIO_ON_IN_SUSPEND) : 0) |
1538 BIT(WIL_PLATFORM_CAPA_T_PWR_ON_0) |
1539 BIT(WIL_PLATFORM_CAPA_EXT_CLK);
1540
1541 return capa;
Maya Erez692e94c2017-05-14 16:40:19 +03001542}
1543
Maya Erez308f9fe2017-11-08 21:35:31 +02001544static void ops_set_features(void *handle, int features)
1545{
1546 struct msm11ad_ctx *ctx = (struct msm11ad_ctx *)handle;
1547
1548 pr_debug("%s: features 0x%x\n", __func__, features);
1549 ctx->features = features;
1550}
1551
Lior David639ebed2017-02-28 14:23:55 +02001552void *msm_11ad_dev_init(struct device *dev, struct wil_platform_ops *ops,
1553 const struct wil_platform_rops *rops, void *wil_handle)
1554{
1555 struct pci_dev *pcidev = to_pci_dev(dev);
1556 struct msm11ad_ctx *ctx = pcidev2ctx(pcidev);
1557
1558 if (!ctx) {
1559 pr_err("Context not found for pcidev %p\n", pcidev);
1560 return NULL;
1561 }
1562
1563 /* bus scale */
1564 ctx->msm_bus_handle =
1565 msm_bus_scale_register_client(ctx->bus_scale);
1566 if (!ctx->msm_bus_handle) {
1567 dev_err(ctx->dev, "Failed msm_bus registration\n");
1568 return NULL;
1569 }
1570 dev_info(ctx->dev, "msm_bus handle 0x%x\n", ctx->msm_bus_handle);
1571 /* smmu */
1572 if (msm_11ad_smmu_init(ctx)) {
1573 msm_bus_scale_unregister_client(ctx->msm_bus_handle);
1574 ctx->msm_bus_handle = 0;
1575 return NULL;
1576 }
1577
1578 /* subsystem restart */
1579 if (rops) {
1580 ctx->rops = *rops;
1581 ctx->wil_handle = wil_handle;
1582 }
1583
1584 /* fill ops */
1585 memset(ops, 0, sizeof(*ops));
1586 ops->bus_request = ops_bus_request;
1587 ops->suspend = ops_suspend;
1588 ops->resume = ops_resume;
1589 ops->uninit = ops_uninit;
1590 ops->notify = ops_notify;
Maya Ereza3a52882017-10-22 11:12:59 +03001591 ops->get_capa = ops_get_capa;
Maya Erez308f9fe2017-11-08 21:35:31 +02001592 ops->set_features = ops_set_features;
Lior David639ebed2017-02-28 14:23:55 +02001593
1594 return ctx;
1595}
1596EXPORT_SYMBOL(msm_11ad_dev_init);
1597
1598int msm_11ad_modinit(void)
1599{
1600 struct msm11ad_ctx *ctx = list_first_entry_or_null(&dev_list,
1601 struct msm11ad_ctx,
1602 list);
1603
1604 if (!ctx) {
1605 pr_err("Context not found\n");
1606 return -EINVAL;
1607 }
1608
Lior David639ebed2017-02-28 14:23:55 +02001609 ctx->subsys_handle = subsystem_get(ctx->subsysdesc.name);
1610
Maya Erez692e94c2017-05-14 16:40:19 +03001611 return msm_11ad_resume_power_on(ctx);
Lior David639ebed2017-02-28 14:23:55 +02001612}
1613EXPORT_SYMBOL(msm_11ad_modinit);
1614
1615void msm_11ad_modexit(void)
1616{
1617 struct msm11ad_ctx *ctx = list_first_entry_or_null(&dev_list,
1618 struct msm11ad_ctx,
1619 list);
1620
1621 if (!ctx) {
1622 pr_err("Context not found\n");
1623 return;
1624 }
1625
1626 if (ctx->subsys_handle) {
1627 subsystem_put(ctx->subsys_handle);
1628 ctx->subsys_handle = NULL;
1629 }
1630}
1631EXPORT_SYMBOL(msm_11ad_modexit);
1632
1633MODULE_LICENSE("GPL v2");
1634MODULE_DESCRIPTION("Platform driver for Qualcomm Technologies, Inc. 11ad card");
1635