1 // SPDX-License-Identifier: GPL-2.0-only
3 * Qualcomm Protection Domain mapper
5 * Copyright (c) 2023 Linaro Ltd.
8 #include <linux/auxiliary_bus.h>
9 #include <linux/kernel.h>
10 #include <linux/mod_devicetable.h>
11 #include <linux/module.h>
13 #include <linux/refcount.h>
14 #include <linux/slab.h>
15 #include <linux/soc/qcom/qmi.h>
17 #include "pdr_internal.h"
19 #define SERVREG_QMI_VERSION 0x101
20 #define SERVREG_QMI_INSTANCE 0
22 #define TMS_SERVREG_SERVICE "tms/servreg"
24 struct qcom_pdm_domain_data
{
27 /* NULL-terminated array */
28 const char * services
[];
31 struct qcom_pdm_domain
{
32 struct list_head list
;
37 struct qcom_pdm_service
{
38 struct list_head list
;
39 struct list_head domains
;
43 struct qcom_pdm_data
{
45 struct qmi_handle handle
;
46 struct list_head services
;
49 static DEFINE_MUTEX(qcom_pdm_mutex
); /* protects __qcom_pdm_data */
50 static struct qcom_pdm_data
*__qcom_pdm_data
;
52 static struct qcom_pdm_service
*qcom_pdm_find(struct qcom_pdm_data
*data
,
55 struct qcom_pdm_service
*service
;
57 list_for_each_entry(service
, &data
->services
, list
) {
58 if (!strcmp(service
->name
, name
))
65 static int qcom_pdm_add_service_domain(struct qcom_pdm_data
*data
,
66 const char *service_name
,
67 const char *domain_name
,
70 struct qcom_pdm_service
*service
;
71 struct qcom_pdm_domain
*domain
;
73 service
= qcom_pdm_find(data
, service_name
);
75 list_for_each_entry(domain
, &service
->domains
, list
) {
76 if (!strcmp(domain
->name
, domain_name
))
80 service
= kzalloc(sizeof(*service
), GFP_KERNEL
);
84 INIT_LIST_HEAD(&service
->domains
);
85 service
->name
= service_name
;
87 list_add_tail(&service
->list
, &data
->services
);
90 domain
= kzalloc(sizeof(*domain
), GFP_KERNEL
);
92 if (list_empty(&service
->domains
)) {
93 list_del(&service
->list
);
100 domain
->name
= domain_name
;
101 domain
->instance_id
= instance_id
;
102 list_add_tail(&domain
->list
, &service
->domains
);
107 static int qcom_pdm_add_domain(struct qcom_pdm_data
*data
,
108 const struct qcom_pdm_domain_data
*domain
)
113 ret
= qcom_pdm_add_service_domain(data
,
116 domain
->instance_id
);
120 for (i
= 0; domain
->services
[i
]; i
++) {
121 ret
= qcom_pdm_add_service_domain(data
,
124 domain
->instance_id
);
133 static void qcom_pdm_free_domains(struct qcom_pdm_data
*data
)
135 struct qcom_pdm_service
*service
, *tservice
;
136 struct qcom_pdm_domain
*domain
, *tdomain
;
138 list_for_each_entry_safe(service
, tservice
, &data
->services
, list
) {
139 list_for_each_entry_safe(domain
, tdomain
, &service
->domains
, list
) {
140 list_del(&domain
->list
);
144 list_del(&service
->list
);
149 static void qcom_pdm_get_domain_list(struct qmi_handle
*qmi
,
150 struct sockaddr_qrtr
*sq
,
154 struct qcom_pdm_data
*data
= container_of(qmi
, struct qcom_pdm_data
, handle
);
155 const struct servreg_get_domain_list_req
*req
= decoded
;
156 struct servreg_get_domain_list_resp
*rsp
;
157 struct qcom_pdm_service
*service
;
161 rsp
= kzalloc(sizeof(*rsp
), GFP_KERNEL
);
165 offset
= req
->domain_offset_valid
? req
->domain_offset
: 0;
167 rsp
->resp
.result
= QMI_RESULT_SUCCESS_V01
;
168 rsp
->resp
.error
= QMI_ERR_NONE_V01
;
170 rsp
->db_rev_count_valid
= true;
171 rsp
->db_rev_count
= 1;
173 rsp
->total_domains_valid
= true;
174 rsp
->total_domains
= 0;
176 mutex_lock(&qcom_pdm_mutex
);
178 service
= qcom_pdm_find(data
, req
->service_name
);
180 struct qcom_pdm_domain
*domain
;
182 rsp
->domain_list_valid
= true;
183 rsp
->domain_list_len
= 0;
185 list_for_each_entry(domain
, &service
->domains
, list
) {
186 u32 i
= rsp
->total_domains
++;
188 if (i
>= offset
&& i
< SERVREG_DOMAIN_LIST_LENGTH
) {
189 u32 j
= rsp
->domain_list_len
++;
191 strscpy(rsp
->domain_list
[j
].name
, domain
->name
,
192 sizeof(rsp
->domain_list
[i
].name
));
193 rsp
->domain_list
[j
].instance
= domain
->instance_id
;
195 pr_debug("PDM: found %s / %d\n", domain
->name
,
196 domain
->instance_id
);
201 pr_debug("PDM: service '%s' offset %d returning %d domains (of %d)\n", req
->service_name
,
202 req
->domain_offset_valid
? req
->domain_offset
: -1, rsp
->domain_list_len
, rsp
->total_domains
);
204 ret
= qmi_send_response(qmi
, sq
, txn
, SERVREG_GET_DOMAIN_LIST_REQ
,
205 SERVREG_GET_DOMAIN_LIST_RESP_MAX_LEN
,
206 servreg_get_domain_list_resp_ei
, rsp
);
208 pr_err("Error sending servreg response: %d\n", ret
);
210 mutex_unlock(&qcom_pdm_mutex
);
215 static void qcom_pdm_pfr(struct qmi_handle
*qmi
,
216 struct sockaddr_qrtr
*sq
,
220 const struct servreg_loc_pfr_req
*req
= decoded
;
221 struct servreg_loc_pfr_resp rsp
= {};
224 pr_warn_ratelimited("PDM: service '%s' crash: '%s'\n", req
->service
, req
->reason
);
226 rsp
.rsp
.result
= QMI_RESULT_SUCCESS_V01
;
227 rsp
.rsp
.error
= QMI_ERR_NONE_V01
;
229 ret
= qmi_send_response(qmi
, sq
, txn
, SERVREG_LOC_PFR_REQ
,
230 SERVREG_LOC_PFR_RESP_MAX_LEN
,
231 servreg_loc_pfr_resp_ei
, &rsp
);
233 pr_err("Error sending servreg response: %d\n", ret
);
236 static const struct qmi_msg_handler qcom_pdm_msg_handlers
[] = {
239 .msg_id
= SERVREG_GET_DOMAIN_LIST_REQ
,
240 .ei
= servreg_get_domain_list_req_ei
,
241 .decoded_size
= sizeof(struct servreg_get_domain_list_req
),
242 .fn
= qcom_pdm_get_domain_list
,
246 .msg_id
= SERVREG_LOC_PFR_REQ
,
247 .ei
= servreg_loc_pfr_req_ei
,
248 .decoded_size
= sizeof(struct servreg_loc_pfr_req
),
254 static const struct qcom_pdm_domain_data adsp_audio_pd
= {
255 .domain
= "msm/adsp/audio_pd",
263 static const struct qcom_pdm_domain_data adsp_charger_pd
= {
264 .domain
= "msm/adsp/charger_pd",
266 .services
= { NULL
},
269 static const struct qcom_pdm_domain_data adsp_root_pd
= {
270 .domain
= "msm/adsp/root_pd",
272 .services
= { NULL
},
275 static const struct qcom_pdm_domain_data adsp_root_pd_pdr
= {
276 .domain
= "msm/adsp/root_pd",
284 static const struct qcom_pdm_domain_data adsp_sensor_pd
= {
285 .domain
= "msm/adsp/sensor_pd",
287 .services
= { NULL
},
290 static const struct qcom_pdm_domain_data msm8996_adsp_audio_pd
= {
291 .domain
= "msm/adsp/audio_pd",
293 .services
= { NULL
},
296 static const struct qcom_pdm_domain_data msm8996_adsp_root_pd
= {
297 .domain
= "msm/adsp/root_pd",
299 .services
= { NULL
},
302 static const struct qcom_pdm_domain_data cdsp_root_pd
= {
303 .domain
= "msm/cdsp/root_pd",
305 .services
= { NULL
},
308 static const struct qcom_pdm_domain_data slpi_root_pd
= {
309 .domain
= "msm/slpi/root_pd",
311 .services
= { NULL
},
314 static const struct qcom_pdm_domain_data slpi_sensor_pd
= {
315 .domain
= "msm/slpi/sensor_pd",
317 .services
= { NULL
},
320 static const struct qcom_pdm_domain_data mpss_root_pd
= {
321 .domain
= "msm/modem/root_pd",
328 static const struct qcom_pdm_domain_data mpss_root_pd_gps
= {
329 .domain
= "msm/modem/root_pd",
337 static const struct qcom_pdm_domain_data mpss_root_pd_gps_pdr
= {
338 .domain
= "msm/modem/root_pd",
347 static const struct qcom_pdm_domain_data msm8996_mpss_root_pd
= {
348 .domain
= "msm/modem/root_pd",
350 .services
= { NULL
},
353 static const struct qcom_pdm_domain_data mpss_wlan_pd
= {
354 .domain
= "msm/modem/wlan_pd",
363 static const struct qcom_pdm_domain_data
*msm8996_domains
[] = {
364 &msm8996_adsp_audio_pd
,
365 &msm8996_adsp_root_pd
,
366 &msm8996_mpss_root_pd
,
370 static const struct qcom_pdm_domain_data
*msm8998_domains
[] = {
376 static const struct qcom_pdm_domain_data
*qcm2290_domains
[] = {
385 static const struct qcom_pdm_domain_data
*qcs404_domains
[] = {
395 static const struct qcom_pdm_domain_data
*sc7180_domains
[] = {
399 &mpss_root_pd_gps_pdr
,
404 static const struct qcom_pdm_domain_data
*sc7280_domains
[] = {
410 &mpss_root_pd_gps_pdr
,
414 static const struct qcom_pdm_domain_data
*sc8180x_domains
[] = {
424 static const struct qcom_pdm_domain_data
*sc8280xp_domains
[] = {
432 static const struct qcom_pdm_domain_data
*sdm660_domains
[] = {
442 static const struct qcom_pdm_domain_data
*sdm670_domains
[] = {
451 static const struct qcom_pdm_domain_data
*sdm845_domains
[] = {
462 static const struct qcom_pdm_domain_data
*sm6115_domains
[] = {
472 static const struct qcom_pdm_domain_data
*sm6350_domains
[] = {
481 static const struct qcom_pdm_domain_data
*sm8150_domains
[] = {
490 static const struct qcom_pdm_domain_data
*sm8250_domains
[] = {
499 static const struct qcom_pdm_domain_data
*sm8350_domains
[] = {
510 static const struct qcom_pdm_domain_data
*sm8550_domains
[] = {
520 static const struct qcom_pdm_domain_data
*x1e80100_domains
[] = {
529 static const struct of_device_id qcom_pdm_domains
[] __maybe_unused
= {
530 { .compatible
= "qcom,apq8016", .data
= NULL
, },
531 { .compatible
= "qcom,apq8064", .data
= NULL
, },
532 { .compatible
= "qcom,apq8074", .data
= NULL
, },
533 { .compatible
= "qcom,apq8084", .data
= NULL
, },
534 { .compatible
= "qcom,apq8096", .data
= msm8996_domains
, },
535 { .compatible
= "qcom,msm8226", .data
= NULL
, },
536 { .compatible
= "qcom,msm8909", .data
= NULL
, },
537 { .compatible
= "qcom,msm8916", .data
= NULL
, },
538 { .compatible
= "qcom,msm8939", .data
= NULL
, },
539 { .compatible
= "qcom,msm8974", .data
= NULL
, },
540 { .compatible
= "qcom,msm8996", .data
= msm8996_domains
, },
541 { .compatible
= "qcom,msm8998", .data
= msm8998_domains
, },
542 { .compatible
= "qcom,qcm2290", .data
= qcm2290_domains
, },
543 { .compatible
= "qcom,qcm6490", .data
= sc7280_domains
, },
544 { .compatible
= "qcom,qcs404", .data
= qcs404_domains
, },
545 { .compatible
= "qcom,sc7180", .data
= sc7180_domains
, },
546 { .compatible
= "qcom,sc7280", .data
= sc7280_domains
, },
547 { .compatible
= "qcom,sc8180x", .data
= sc8180x_domains
, },
548 { .compatible
= "qcom,sc8280xp", .data
= sc8280xp_domains
, },
549 { .compatible
= "qcom,sda660", .data
= sdm660_domains
, },
550 { .compatible
= "qcom,sdm660", .data
= sdm660_domains
, },
551 { .compatible
= "qcom,sdm670", .data
= sdm670_domains
, },
552 { .compatible
= "qcom,sdm845", .data
= sdm845_domains
, },
553 { .compatible
= "qcom,sm4250", .data
= sm6115_domains
, },
554 { .compatible
= "qcom,sm6115", .data
= sm6115_domains
, },
555 { .compatible
= "qcom,sm6350", .data
= sm6350_domains
, },
556 { .compatible
= "qcom,sm7325", .data
= sc7280_domains
, },
557 { .compatible
= "qcom,sm8150", .data
= sm8150_domains
, },
558 { .compatible
= "qcom,sm8250", .data
= sm8250_domains
, },
559 { .compatible
= "qcom,sm8350", .data
= sm8350_domains
, },
560 { .compatible
= "qcom,sm8450", .data
= sm8350_domains
, },
561 { .compatible
= "qcom,sm8550", .data
= sm8550_domains
, },
562 { .compatible
= "qcom,sm8650", .data
= sm8550_domains
, },
563 { .compatible
= "qcom,x1e80100", .data
= x1e80100_domains
, },
567 static void qcom_pdm_stop(struct qcom_pdm_data
*data
)
569 qcom_pdm_free_domains(data
);
571 /* The server is removed automatically */
572 qmi_handle_release(&data
->handle
);
577 static struct qcom_pdm_data
*qcom_pdm_start(void)
579 const struct qcom_pdm_domain_data
* const *domains
;
580 const struct of_device_id
*match
;
581 struct qcom_pdm_data
*data
;
582 struct device_node
*root
;
585 root
= of_find_node_by_path("/");
587 return ERR_PTR(-ENODEV
);
589 match
= of_match_node(qcom_pdm_domains
, root
);
592 pr_notice("PDM: no support for the platform, userspace daemon might be required.\n");
593 return ERR_PTR(-ENODEV
);
596 domains
= match
->data
;
598 pr_debug("PDM: no domains\n");
599 return ERR_PTR(-ENODEV
);
602 data
= kzalloc(sizeof(*data
), GFP_KERNEL
);
604 return ERR_PTR(-ENOMEM
);
606 INIT_LIST_HEAD(&data
->services
);
608 ret
= qmi_handle_init(&data
->handle
, SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN
,
609 NULL
, qcom_pdm_msg_handlers
);
615 refcount_set(&data
->refcnt
, 1);
617 for (i
= 0; domains
[i
]; i
++) {
618 ret
= qcom_pdm_add_domain(data
, domains
[i
]);
623 ret
= qmi_add_server(&data
->handle
, SERVREG_LOCATOR_SERVICE
,
624 SERVREG_QMI_VERSION
, SERVREG_QMI_INSTANCE
);
626 pr_err("PDM: error adding server %d\n", ret
);
638 static int qcom_pdm_probe(struct auxiliary_device
*auxdev
,
639 const struct auxiliary_device_id
*id
)
642 struct qcom_pdm_data
*data
;
645 mutex_lock(&qcom_pdm_mutex
);
647 if (!__qcom_pdm_data
) {
648 data
= qcom_pdm_start();
653 __qcom_pdm_data
= data
;
655 refcount_inc(&__qcom_pdm_data
->refcnt
);
658 auxiliary_set_drvdata(auxdev
, __qcom_pdm_data
);
660 mutex_unlock(&qcom_pdm_mutex
);
665 static void qcom_pdm_remove(struct auxiliary_device
*auxdev
)
667 struct qcom_pdm_data
*data
;
669 data
= auxiliary_get_drvdata(auxdev
);
673 if (refcount_dec_and_mutex_lock(&data
->refcnt
, &qcom_pdm_mutex
)) {
674 __qcom_pdm_data
= NULL
;
676 mutex_unlock(&qcom_pdm_mutex
);
680 static const struct auxiliary_device_id qcom_pdm_table
[] = {
681 { .name
= "qcom_common.pd-mapper" },
684 MODULE_DEVICE_TABLE(auxiliary
, qcom_pdm_table
);
686 static struct auxiliary_driver qcom_pdm_drv
= {
687 .name
= "qcom-pdm-mapper",
688 .id_table
= qcom_pdm_table
,
689 .probe
= qcom_pdm_probe
,
690 .remove
= qcom_pdm_remove
,
692 module_auxiliary_driver(qcom_pdm_drv
);
694 MODULE_DESCRIPTION("Qualcomm Protection Domain Mapper");
695 MODULE_LICENSE("GPL");