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

source code of linux/drivers/firmware/qcom/qcom_qseecom.c