1 // SPDX-License-Identifier: GPL-2.0-only
3 * Qualcomm ADSP/SLPI Peripheral Image Loader for MSM8974 and MSM8996
5 * Copyright (C) 2016 Linaro Ltd
6 * Copyright (C) 2014 Sony Mobile Communications AB
7 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
10 #include <linux/clk.h>
11 #include <linux/firmware.h>
12 #include <linux/interrupt.h>
13 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include <linux/of_address.h>
16 #include <linux/of_device.h>
17 #include <linux/platform_device.h>
18 #include <linux/pm_domain.h>
19 #include <linux/pm_runtime.h>
20 #include <linux/qcom_scm.h>
21 #include <linux/regulator/consumer.h>
22 #include <linux/remoteproc.h>
23 #include <linux/soc/qcom/mdt_loader.h>
24 #include <linux/soc/qcom/smem.h>
25 #include <linux/soc/qcom/smem_state.h>
27 #include "qcom_common.h"
28 #include "qcom_pil_info.h"
29 #include "qcom_q6v5.h"
30 #include "remoteproc_internal.h"
33 int crash_reason_smem
;
34 const char *firmware_name
;
36 unsigned int minidump_id
;
40 char **active_pd_names
;
41 char **proxy_pd_names
;
44 const char *sysmon_name
;
52 struct qcom_q6v5 q6v5
;
55 struct clk
*aggre2_clk
;
57 struct regulator
*cx_supply
;
58 struct regulator
*px_supply
;
60 struct device
*active_pds
[1];
61 struct device
*proxy_pds
[3];
67 unsigned int minidump_id
;
68 int crash_reason_smem
;
70 const char *info_name
;
72 struct completion start_done
;
73 struct completion stop_done
;
76 phys_addr_t mem_reloc
;
80 struct qcom_rproc_glink glink_subdev
;
81 struct qcom_rproc_subdev smd_subdev
;
82 struct qcom_rproc_ssr ssr_subdev
;
83 struct qcom_sysmon
*sysmon
;
86 static void adsp_minidump(struct rproc
*rproc
)
88 struct qcom_adsp
*adsp
= rproc
->priv
;
90 qcom_minidump(rproc
, adsp
->minidump_id
);
93 static int adsp_pds_enable(struct qcom_adsp
*adsp
, struct device
**pds
,
99 for (i
= 0; i
< pd_count
; i
++) {
100 dev_pm_genpd_set_performance_state(pds
[i
], INT_MAX
);
101 ret
= pm_runtime_get_sync(pds
[i
]);
103 pm_runtime_put_noidle(pds
[i
]);
104 dev_pm_genpd_set_performance_state(pds
[i
], 0);
105 goto unroll_pd_votes
;
112 for (i
--; i
>= 0; i
--) {
113 dev_pm_genpd_set_performance_state(pds
[i
], 0);
114 pm_runtime_put(pds
[i
]);
120 static void adsp_pds_disable(struct qcom_adsp
*adsp
, struct device
**pds
,
125 for (i
= 0; i
< pd_count
; i
++) {
126 dev_pm_genpd_set_performance_state(pds
[i
], 0);
127 pm_runtime_put(pds
[i
]);
131 static int adsp_load(struct rproc
*rproc
, const struct firmware
*fw
)
133 struct qcom_adsp
*adsp
= (struct qcom_adsp
*)rproc
->priv
;
136 ret
= qcom_mdt_load(adsp
->dev
, fw
, rproc
->firmware
, adsp
->pas_id
,
137 adsp
->mem_region
, adsp
->mem_phys
, adsp
->mem_size
,
142 qcom_pil_info_store(adsp
->info_name
, adsp
->mem_phys
, adsp
->mem_size
);
147 static int adsp_start(struct rproc
*rproc
)
149 struct qcom_adsp
*adsp
= (struct qcom_adsp
*)rproc
->priv
;
152 qcom_q6v5_prepare(&adsp
->q6v5
);
154 ret
= adsp_pds_enable(adsp
, adsp
->active_pds
, adsp
->active_pd_count
);
158 ret
= adsp_pds_enable(adsp
, adsp
->proxy_pds
, adsp
->proxy_pd_count
);
160 goto disable_active_pds
;
162 ret
= clk_prepare_enable(adsp
->xo
);
164 goto disable_proxy_pds
;
166 ret
= clk_prepare_enable(adsp
->aggre2_clk
);
170 ret
= regulator_enable(adsp
->cx_supply
);
172 goto disable_aggre2_clk
;
174 ret
= regulator_enable(adsp
->px_supply
);
176 goto disable_cx_supply
;
178 ret
= qcom_scm_pas_auth_and_reset(adsp
->pas_id
);
181 "failed to authenticate image and release reset\n");
182 goto disable_px_supply
;
185 ret
= qcom_q6v5_wait_for_start(&adsp
->q6v5
, msecs_to_jiffies(5000));
186 if (ret
== -ETIMEDOUT
) {
187 dev_err(adsp
->dev
, "start timed out\n");
188 qcom_scm_pas_shutdown(adsp
->pas_id
);
189 goto disable_px_supply
;
195 regulator_disable(adsp
->px_supply
);
197 regulator_disable(adsp
->cx_supply
);
199 clk_disable_unprepare(adsp
->aggre2_clk
);
201 clk_disable_unprepare(adsp
->xo
);
203 adsp_pds_disable(adsp
, adsp
->proxy_pds
, adsp
->proxy_pd_count
);
205 adsp_pds_disable(adsp
, adsp
->active_pds
, adsp
->active_pd_count
);
207 qcom_q6v5_unprepare(&adsp
->q6v5
);
212 static void qcom_pas_handover(struct qcom_q6v5
*q6v5
)
214 struct qcom_adsp
*adsp
= container_of(q6v5
, struct qcom_adsp
, q6v5
);
216 regulator_disable(adsp
->px_supply
);
217 regulator_disable(adsp
->cx_supply
);
218 clk_disable_unprepare(adsp
->aggre2_clk
);
219 clk_disable_unprepare(adsp
->xo
);
220 adsp_pds_disable(adsp
, adsp
->proxy_pds
, adsp
->proxy_pd_count
);
223 static int adsp_stop(struct rproc
*rproc
)
225 struct qcom_adsp
*adsp
= (struct qcom_adsp
*)rproc
->priv
;
229 ret
= qcom_q6v5_request_stop(&adsp
->q6v5
, adsp
->sysmon
);
230 if (ret
== -ETIMEDOUT
)
231 dev_err(adsp
->dev
, "timed out on wait\n");
233 ret
= qcom_scm_pas_shutdown(adsp
->pas_id
);
235 dev_err(adsp
->dev
, "failed to shutdown: %d\n", ret
);
237 adsp_pds_disable(adsp
, adsp
->active_pds
, adsp
->active_pd_count
);
238 handover
= qcom_q6v5_unprepare(&adsp
->q6v5
);
240 qcom_pas_handover(&adsp
->q6v5
);
245 static void *adsp_da_to_va(struct rproc
*rproc
, u64 da
, size_t len
)
247 struct qcom_adsp
*adsp
= (struct qcom_adsp
*)rproc
->priv
;
250 offset
= da
- adsp
->mem_reloc
;
251 if (offset
< 0 || offset
+ len
> adsp
->mem_size
)
254 return adsp
->mem_region
+ offset
;
257 static unsigned long adsp_panic(struct rproc
*rproc
)
259 struct qcom_adsp
*adsp
= (struct qcom_adsp
*)rproc
->priv
;
261 return qcom_q6v5_panic(&adsp
->q6v5
);
264 static const struct rproc_ops adsp_ops
= {
267 .da_to_va
= adsp_da_to_va
,
268 .parse_fw
= qcom_register_dump_segments
,
273 static const struct rproc_ops adsp_minidump_ops
= {
276 .da_to_va
= adsp_da_to_va
,
279 .coredump
= adsp_minidump
,
282 static int adsp_init_clock(struct qcom_adsp
*adsp
)
286 adsp
->xo
= devm_clk_get(adsp
->dev
, "xo");
287 if (IS_ERR(adsp
->xo
)) {
288 ret
= PTR_ERR(adsp
->xo
);
289 if (ret
!= -EPROBE_DEFER
)
290 dev_err(adsp
->dev
, "failed to get xo clock");
294 if (adsp
->has_aggre2_clk
) {
295 adsp
->aggre2_clk
= devm_clk_get(adsp
->dev
, "aggre2");
296 if (IS_ERR(adsp
->aggre2_clk
)) {
297 ret
= PTR_ERR(adsp
->aggre2_clk
);
298 if (ret
!= -EPROBE_DEFER
)
300 "failed to get aggre2 clock");
308 static int adsp_init_regulator(struct qcom_adsp
*adsp
)
310 adsp
->cx_supply
= devm_regulator_get(adsp
->dev
, "cx");
311 if (IS_ERR(adsp
->cx_supply
))
312 return PTR_ERR(adsp
->cx_supply
);
314 regulator_set_load(adsp
->cx_supply
, 100000);
316 adsp
->px_supply
= devm_regulator_get(adsp
->dev
, "px");
317 return PTR_ERR_OR_ZERO(adsp
->px_supply
);
320 static int adsp_pds_attach(struct device
*dev
, struct device
**devs
,
330 /* Handle single power domain */
331 if (dev
->pm_domain
) {
333 pm_runtime_enable(dev
);
337 while (pd_names
[num_pds
])
340 for (i
= 0; i
< num_pds
; i
++) {
341 devs
[i
] = dev_pm_domain_attach_by_name(dev
, pd_names
[i
]);
342 if (IS_ERR_OR_NULL(devs
[i
])) {
343 ret
= PTR_ERR(devs
[i
]) ? : -ENODATA
;
351 for (i
--; i
>= 0; i
--)
352 dev_pm_domain_detach(devs
[i
], false);
357 static void adsp_pds_detach(struct qcom_adsp
*adsp
, struct device
**pds
,
360 struct device
*dev
= adsp
->dev
;
363 /* Handle single power domain */
364 if (dev
->pm_domain
&& pd_count
) {
365 pm_runtime_disable(dev
);
369 for (i
= 0; i
< pd_count
; i
++)
370 dev_pm_domain_detach(pds
[i
], false);
373 static int adsp_alloc_memory_region(struct qcom_adsp
*adsp
)
375 struct device_node
*node
;
379 node
= of_parse_phandle(adsp
->dev
->of_node
, "memory-region", 0);
381 dev_err(adsp
->dev
, "no memory-region specified\n");
385 ret
= of_address_to_resource(node
, 0, &r
);
389 adsp
->mem_phys
= adsp
->mem_reloc
= r
.start
;
390 adsp
->mem_size
= resource_size(&r
);
391 adsp
->mem_region
= devm_ioremap_wc(adsp
->dev
, adsp
->mem_phys
, adsp
->mem_size
);
392 if (!adsp
->mem_region
) {
393 dev_err(adsp
->dev
, "unable to map memory region: %pa+%zx\n",
394 &r
.start
, adsp
->mem_size
);
401 static int adsp_probe(struct platform_device
*pdev
)
403 const struct adsp_data
*desc
;
404 struct qcom_adsp
*adsp
;
407 const struct rproc_ops
*ops
= &adsp_ops
;
410 desc
= of_device_get_match_data(&pdev
->dev
);
414 if (!qcom_scm_is_available())
415 return -EPROBE_DEFER
;
417 fw_name
= desc
->firmware_name
;
418 ret
= of_property_read_string(pdev
->dev
.of_node
, "firmware-name",
420 if (ret
< 0 && ret
!= -EINVAL
)
423 if (desc
->minidump_id
)
424 ops
= &adsp_minidump_ops
;
426 rproc
= rproc_alloc(&pdev
->dev
, pdev
->name
, ops
, fw_name
, sizeof(*adsp
));
429 dev_err(&pdev
->dev
, "unable to allocate remoteproc\n");
433 rproc
->auto_boot
= desc
->auto_boot
;
434 rproc_coredump_set_elf_info(rproc
, ELFCLASS32
, EM_NONE
);
436 adsp
= (struct qcom_adsp
*)rproc
->priv
;
437 adsp
->dev
= &pdev
->dev
;
439 adsp
->minidump_id
= desc
->minidump_id
;
440 adsp
->pas_id
= desc
->pas_id
;
441 adsp
->has_aggre2_clk
= desc
->has_aggre2_clk
;
442 adsp
->info_name
= desc
->sysmon_name
;
443 platform_set_drvdata(pdev
, adsp
);
445 device_wakeup_enable(adsp
->dev
);
447 ret
= adsp_alloc_memory_region(adsp
);
451 ret
= adsp_init_clock(adsp
);
455 ret
= adsp_init_regulator(adsp
);
459 ret
= adsp_pds_attach(&pdev
->dev
, adsp
->active_pds
,
460 desc
->active_pd_names
);
463 adsp
->active_pd_count
= ret
;
465 ret
= adsp_pds_attach(&pdev
->dev
, adsp
->proxy_pds
,
466 desc
->proxy_pd_names
);
468 goto detach_active_pds
;
469 adsp
->proxy_pd_count
= ret
;
471 ret
= qcom_q6v5_init(&adsp
->q6v5
, pdev
, rproc
, desc
->crash_reason_smem
,
474 goto detach_proxy_pds
;
476 qcom_add_glink_subdev(rproc
, &adsp
->glink_subdev
, desc
->ssr_name
);
477 qcom_add_smd_subdev(rproc
, &adsp
->smd_subdev
);
478 qcom_add_ssr_subdev(rproc
, &adsp
->ssr_subdev
, desc
->ssr_name
);
479 adsp
->sysmon
= qcom_add_sysmon_subdev(rproc
,
482 if (IS_ERR(adsp
->sysmon
)) {
483 ret
= PTR_ERR(adsp
->sysmon
);
484 goto detach_proxy_pds
;
487 ret
= rproc_add(rproc
);
489 goto detach_proxy_pds
;
494 adsp_pds_detach(adsp
, adsp
->proxy_pds
, adsp
->proxy_pd_count
);
496 adsp_pds_detach(adsp
, adsp
->active_pds
, adsp
->active_pd_count
);
503 static int adsp_remove(struct platform_device
*pdev
)
505 struct qcom_adsp
*adsp
= platform_get_drvdata(pdev
);
507 rproc_del(adsp
->rproc
);
509 qcom_remove_glink_subdev(adsp
->rproc
, &adsp
->glink_subdev
);
510 qcom_remove_sysmon_subdev(adsp
->sysmon
);
511 qcom_remove_smd_subdev(adsp
->rproc
, &adsp
->smd_subdev
);
512 qcom_remove_ssr_subdev(adsp
->rproc
, &adsp
->ssr_subdev
);
513 rproc_free(adsp
->rproc
);
518 static const struct adsp_data adsp_resource_init
= {
519 .crash_reason_smem
= 423,
520 .firmware_name
= "adsp.mdt",
522 .has_aggre2_clk
= false,
525 .sysmon_name
= "adsp",
529 static const struct adsp_data sm8150_adsp_resource
= {
530 .crash_reason_smem
= 423,
531 .firmware_name
= "adsp.mdt",
533 .has_aggre2_clk
= false,
535 .active_pd_names
= (char*[]){
539 .proxy_pd_names
= (char*[]){
544 .sysmon_name
= "adsp",
548 static const struct adsp_data sm8250_adsp_resource
= {
549 .crash_reason_smem
= 423,
550 .firmware_name
= "adsp.mdt",
552 .has_aggre2_clk
= false,
554 .active_pd_names
= (char*[]){
558 .proxy_pd_names
= (char*[]){
564 .sysmon_name
= "adsp",
568 static const struct adsp_data msm8998_adsp_resource
= {
569 .crash_reason_smem
= 423,
570 .firmware_name
= "adsp.mdt",
572 .has_aggre2_clk
= false,
574 .proxy_pd_names
= (char*[]){
579 .sysmon_name
= "adsp",
583 static const struct adsp_data cdsp_resource_init
= {
584 .crash_reason_smem
= 601,
585 .firmware_name
= "cdsp.mdt",
587 .has_aggre2_clk
= false,
590 .sysmon_name
= "cdsp",
594 static const struct adsp_data sm8150_cdsp_resource
= {
595 .crash_reason_smem
= 601,
596 .firmware_name
= "cdsp.mdt",
598 .has_aggre2_clk
= false,
600 .active_pd_names
= (char*[]){
604 .proxy_pd_names
= (char*[]){
609 .sysmon_name
= "cdsp",
613 static const struct adsp_data sm8250_cdsp_resource
= {
614 .crash_reason_smem
= 601,
615 .firmware_name
= "cdsp.mdt",
617 .has_aggre2_clk
= false,
619 .active_pd_names
= (char*[]){
623 .proxy_pd_names
= (char*[]){
628 .sysmon_name
= "cdsp",
632 static const struct adsp_data mpss_resource_init
= {
633 .crash_reason_smem
= 421,
634 .firmware_name
= "modem.mdt",
637 .has_aggre2_clk
= false,
639 .active_pd_names
= (char*[]){
643 .proxy_pd_names
= (char*[]){
649 .sysmon_name
= "modem",
653 static const struct adsp_data slpi_resource_init
= {
654 .crash_reason_smem
= 424,
655 .firmware_name
= "slpi.mdt",
657 .has_aggre2_clk
= true,
660 .sysmon_name
= "slpi",
664 static const struct adsp_data sm8150_slpi_resource
= {
665 .crash_reason_smem
= 424,
666 .firmware_name
= "slpi.mdt",
668 .has_aggre2_clk
= false,
670 .active_pd_names
= (char*[]){
674 .proxy_pd_names
= (char*[]){
680 .sysmon_name
= "slpi",
684 static const struct adsp_data sm8250_slpi_resource
= {
685 .crash_reason_smem
= 424,
686 .firmware_name
= "slpi.mdt",
688 .has_aggre2_clk
= false,
690 .active_pd_names
= (char*[]){
694 .proxy_pd_names
= (char*[]){
700 .sysmon_name
= "slpi",
704 static const struct adsp_data msm8998_slpi_resource
= {
705 .crash_reason_smem
= 424,
706 .firmware_name
= "slpi.mdt",
708 .has_aggre2_clk
= true,
710 .proxy_pd_names
= (char*[]){
715 .sysmon_name
= "slpi",
719 static const struct adsp_data wcss_resource_init
= {
720 .crash_reason_smem
= 421,
721 .firmware_name
= "wcnss.mdt",
725 .sysmon_name
= "wcnss",
729 static const struct of_device_id adsp_of_match
[] = {
730 { .compatible
= "qcom,msm8974-adsp-pil", .data
= &adsp_resource_init
},
731 { .compatible
= "qcom,msm8996-adsp-pil", .data
= &adsp_resource_init
},
732 { .compatible
= "qcom,msm8996-slpi-pil", .data
= &slpi_resource_init
},
733 { .compatible
= "qcom,msm8998-adsp-pas", .data
= &msm8998_adsp_resource
},
734 { .compatible
= "qcom,msm8998-slpi-pas", .data
= &msm8998_slpi_resource
},
735 { .compatible
= "qcom,qcs404-adsp-pas", .data
= &adsp_resource_init
},
736 { .compatible
= "qcom,qcs404-cdsp-pas", .data
= &cdsp_resource_init
},
737 { .compatible
= "qcom,qcs404-wcss-pas", .data
= &wcss_resource_init
},
738 { .compatible
= "qcom,sc7180-mpss-pas", .data
= &mpss_resource_init
},
739 { .compatible
= "qcom,sdm845-adsp-pas", .data
= &adsp_resource_init
},
740 { .compatible
= "qcom,sdm845-cdsp-pas", .data
= &cdsp_resource_init
},
741 { .compatible
= "qcom,sm8150-adsp-pas", .data
= &sm8150_adsp_resource
},
742 { .compatible
= "qcom,sm8150-cdsp-pas", .data
= &sm8150_cdsp_resource
},
743 { .compatible
= "qcom,sm8150-mpss-pas", .data
= &mpss_resource_init
},
744 { .compatible
= "qcom,sm8150-slpi-pas", .data
= &sm8150_slpi_resource
},
745 { .compatible
= "qcom,sm8250-adsp-pas", .data
= &sm8250_adsp_resource
},
746 { .compatible
= "qcom,sm8250-cdsp-pas", .data
= &sm8250_cdsp_resource
},
747 { .compatible
= "qcom,sm8250-slpi-pas", .data
= &sm8250_slpi_resource
},
750 MODULE_DEVICE_TABLE(of
, adsp_of_match
);
752 static struct platform_driver adsp_driver
= {
754 .remove
= adsp_remove
,
756 .name
= "qcom_q6v5_pas",
757 .of_match_table
= adsp_of_match
,
761 module_platform_driver(adsp_driver
);
762 MODULE_DESCRIPTION("Qualcomm Hexagon v5 Peripheral Authentication Service driver");
763 MODULE_LICENSE("GPL v2");