1*bdac188eSBartosz Golaszewski // SPDX-License-Identifier: GPL-2.0-or-later
2*bdac188eSBartosz Golaszewski /*
3*bdac188eSBartosz Golaszewski * Driver for Qualcomm Secure Execution Environment (SEE) interface (QSEECOM).
4*bdac188eSBartosz Golaszewski * Responsible for setting up and managing QSEECOM client devices.
5*bdac188eSBartosz Golaszewski *
6*bdac188eSBartosz Golaszewski * Copyright (C) 2023 Maximilian Luz <luzmaximilian@gmail.com>
7*bdac188eSBartosz Golaszewski */
8*bdac188eSBartosz Golaszewski #include <linux/auxiliary_bus.h>
9*bdac188eSBartosz Golaszewski #include <linux/module.h>
10*bdac188eSBartosz Golaszewski #include <linux/platform_device.h>
11*bdac188eSBartosz Golaszewski #include <linux/slab.h>
12*bdac188eSBartosz Golaszewski #include <linux/types.h>
13*bdac188eSBartosz Golaszewski
14*bdac188eSBartosz Golaszewski #include <linux/firmware/qcom/qcom_qseecom.h>
15*bdac188eSBartosz Golaszewski #include <linux/firmware/qcom/qcom_scm.h>
16*bdac188eSBartosz Golaszewski
17*bdac188eSBartosz Golaszewski struct qseecom_app_desc {
18*bdac188eSBartosz Golaszewski const char *app_name;
19*bdac188eSBartosz Golaszewski const char *dev_name;
20*bdac188eSBartosz Golaszewski };
21*bdac188eSBartosz Golaszewski
qseecom_client_release(struct device * dev)22*bdac188eSBartosz Golaszewski static void qseecom_client_release(struct device *dev)
23*bdac188eSBartosz Golaszewski {
24*bdac188eSBartosz Golaszewski struct qseecom_client *client;
25*bdac188eSBartosz Golaszewski
26*bdac188eSBartosz Golaszewski client = container_of(dev, struct qseecom_client, aux_dev.dev);
27*bdac188eSBartosz Golaszewski kfree(client);
28*bdac188eSBartosz Golaszewski }
29*bdac188eSBartosz Golaszewski
qseecom_client_remove(void * data)30*bdac188eSBartosz Golaszewski static void qseecom_client_remove(void *data)
31*bdac188eSBartosz Golaszewski {
32*bdac188eSBartosz Golaszewski struct qseecom_client *client = data;
33*bdac188eSBartosz Golaszewski
34*bdac188eSBartosz Golaszewski auxiliary_device_delete(&client->aux_dev);
35*bdac188eSBartosz Golaszewski auxiliary_device_uninit(&client->aux_dev);
36*bdac188eSBartosz Golaszewski }
37*bdac188eSBartosz Golaszewski
qseecom_client_register(struct platform_device * qseecom_dev,const struct qseecom_app_desc * desc)38*bdac188eSBartosz Golaszewski static int qseecom_client_register(struct platform_device *qseecom_dev,
39*bdac188eSBartosz Golaszewski const struct qseecom_app_desc *desc)
40*bdac188eSBartosz Golaszewski {
41*bdac188eSBartosz Golaszewski struct qseecom_client *client;
42*bdac188eSBartosz Golaszewski u32 app_id;
43*bdac188eSBartosz Golaszewski int ret;
44*bdac188eSBartosz Golaszewski
45*bdac188eSBartosz Golaszewski /* Try to find the app ID, skip device if not found */
46*bdac188eSBartosz Golaszewski ret = qcom_scm_qseecom_app_get_id(desc->app_name, &app_id);
47*bdac188eSBartosz Golaszewski if (ret)
48*bdac188eSBartosz Golaszewski return ret == -ENOENT ? 0 : ret;
49*bdac188eSBartosz Golaszewski
50*bdac188eSBartosz Golaszewski dev_info(&qseecom_dev->dev, "setting up client for %s\n", desc->app_name);
51*bdac188eSBartosz Golaszewski
52*bdac188eSBartosz Golaszewski /* Allocate and set-up the client device */
53*bdac188eSBartosz Golaszewski client = kzalloc(sizeof(*client), GFP_KERNEL);
54*bdac188eSBartosz Golaszewski if (!client)
55*bdac188eSBartosz Golaszewski return -ENOMEM;
56*bdac188eSBartosz Golaszewski
57*bdac188eSBartosz Golaszewski client->aux_dev.name = desc->dev_name;
58*bdac188eSBartosz Golaszewski client->aux_dev.dev.parent = &qseecom_dev->dev;
59*bdac188eSBartosz Golaszewski client->aux_dev.dev.release = qseecom_client_release;
60*bdac188eSBartosz Golaszewski client->app_id = app_id;
61*bdac188eSBartosz Golaszewski
62*bdac188eSBartosz Golaszewski ret = auxiliary_device_init(&client->aux_dev);
63*bdac188eSBartosz Golaszewski if (ret) {
64*bdac188eSBartosz Golaszewski kfree(client);
65*bdac188eSBartosz Golaszewski return ret;
66*bdac188eSBartosz Golaszewski }
67*bdac188eSBartosz Golaszewski
68*bdac188eSBartosz Golaszewski ret = auxiliary_device_add(&client->aux_dev);
69*bdac188eSBartosz Golaszewski if (ret) {
70*bdac188eSBartosz Golaszewski auxiliary_device_uninit(&client->aux_dev);
71*bdac188eSBartosz Golaszewski return ret;
72*bdac188eSBartosz Golaszewski }
73*bdac188eSBartosz Golaszewski
74*bdac188eSBartosz Golaszewski ret = devm_add_action_or_reset(&qseecom_dev->dev, qseecom_client_remove, client);
75*bdac188eSBartosz Golaszewski if (ret)
76*bdac188eSBartosz Golaszewski return ret;
77*bdac188eSBartosz Golaszewski
78*bdac188eSBartosz Golaszewski return 0;
79*bdac188eSBartosz Golaszewski }
80*bdac188eSBartosz Golaszewski
81*bdac188eSBartosz Golaszewski /*
82*bdac188eSBartosz Golaszewski * List of supported applications. One client device will be created per entry,
83*bdac188eSBartosz Golaszewski * assuming the app has already been loaded (usually by firmware bootloaders)
84*bdac188eSBartosz Golaszewski * and its ID can be queried successfully.
85*bdac188eSBartosz Golaszewski */
86*bdac188eSBartosz Golaszewski static const struct qseecom_app_desc qcom_qseecom_apps[] = {
87*bdac188eSBartosz Golaszewski { "qcom.tz.uefisecapp", "uefisecapp" },
88*bdac188eSBartosz Golaszewski };
89*bdac188eSBartosz Golaszewski
qcom_qseecom_probe(struct platform_device * qseecom_dev)90*bdac188eSBartosz Golaszewski static int qcom_qseecom_probe(struct platform_device *qseecom_dev)
91*bdac188eSBartosz Golaszewski {
92*bdac188eSBartosz Golaszewski int ret;
93*bdac188eSBartosz Golaszewski int i;
94*bdac188eSBartosz Golaszewski
95*bdac188eSBartosz Golaszewski /* Set up client devices for each base application */
96*bdac188eSBartosz Golaszewski for (i = 0; i < ARRAY_SIZE(qcom_qseecom_apps); i++) {
97*bdac188eSBartosz Golaszewski ret = qseecom_client_register(qseecom_dev, &qcom_qseecom_apps[i]);
98*bdac188eSBartosz Golaszewski if (ret)
99*bdac188eSBartosz Golaszewski return ret;
100*bdac188eSBartosz Golaszewski }
101*bdac188eSBartosz Golaszewski
102*bdac188eSBartosz Golaszewski return 0;
103*bdac188eSBartosz Golaszewski }
104*bdac188eSBartosz Golaszewski
105*bdac188eSBartosz Golaszewski static struct platform_driver qcom_qseecom_driver = {
106*bdac188eSBartosz Golaszewski .driver = {
107*bdac188eSBartosz Golaszewski .name = "qcom_qseecom",
108*bdac188eSBartosz Golaszewski },
109*bdac188eSBartosz Golaszewski .probe = qcom_qseecom_probe,
110*bdac188eSBartosz Golaszewski };
111*bdac188eSBartosz Golaszewski
qcom_qseecom_init(void)112*bdac188eSBartosz Golaszewski static int __init qcom_qseecom_init(void)
113*bdac188eSBartosz Golaszewski {
114*bdac188eSBartosz Golaszewski return platform_driver_register(&qcom_qseecom_driver);
115*bdac188eSBartosz Golaszewski }
116*bdac188eSBartosz Golaszewski subsys_initcall(qcom_qseecom_init);
117*bdac188eSBartosz Golaszewski
118*bdac188eSBartosz Golaszewski MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
119*bdac188eSBartosz Golaszewski MODULE_DESCRIPTION("Driver for the Qualcomm SEE (QSEECOM) interface");
120*bdac188eSBartosz Golaszewski MODULE_LICENSE("GPL");
121