aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2018-09-26 11:18:42 -0400
committerArnd Bergmann <arnd@arndb.de>2018-09-26 11:18:53 -0400
commitba61ab1a232d75a8bf92bbfedc10162a6e7e95c7 (patch)
treeb0dc602d23a3925bcbcd1826436501a676d53140
parent1e25ee6d8083bb78e164d497989dc3cafda70fb8 (diff)
parente60f02ddb4d2e29b0eb30dbe55475822c4bf3818 (diff)
Merge tag 'zynqmp-soc-for-v4.20-v2' of https://github.com/Xilinx/linux-xlnx into next/drivers
arm64: zynqmp: SoC changes for v4.20 - Adding firmware API for SoC with debugfs interface Firmware driver communicates to Platform Management Unit (PMU) by using SMC instructions routed to Arm Trusted Firmware (ATF). Initial version adds support for base firmware driver with query and clock APIs. EEMI spec is available here: https://www.xilinx.com/support/documentation/user_guides/ug1200-eemi-api.pdf * tag 'zynqmp-soc-for-v4.20-v2' of https://github.com/Xilinx/linux-xlnx: firmware: xilinx: Add debugfs for query data API firmware: xilinx: Add debugfs interface firmware: xilinx: Add clock APIs firmware: xilinx: Add query data API firmware: xilinx: Add Zynqmp firmware driver dt-bindings: firmware: Add bindings for ZynqMP firmware Signed-off-by: Arnd Bergmann <arnd@arndb.de>
-rw-r--r--Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt29
-rw-r--r--arch/arm64/Kconfig.platforms1
-rw-r--r--drivers/firmware/Kconfig1
-rw-r--r--drivers/firmware/Makefile1
-rw-r--r--drivers/firmware/xilinx/Kconfig23
-rw-r--r--drivers/firmware/xilinx/Makefile5
-rw-r--r--drivers/firmware/xilinx/zynqmp-debug.c250
-rw-r--r--drivers/firmware/xilinx/zynqmp-debug.h24
-rw-r--r--drivers/firmware/xilinx/zynqmp.c523
-rw-r--r--include/linux/firmware/xlnx-zynqmp.h113
10 files changed, 970 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt b/Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt
new file mode 100644
index 000000000000..1b431d9bbe44
--- /dev/null
+++ b/Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt
@@ -0,0 +1,29 @@
1-----------------------------------------------------------------
2Device Tree Bindings for the Xilinx Zynq MPSoC Firmware Interface
3-----------------------------------------------------------------
4
5The zynqmp-firmware node describes the interface to platform firmware.
6ZynqMP has an interface to communicate with secure firmware. Firmware
7driver provides an interface to firmware APIs. Interface APIs can be
8used by any driver to communicate to PMUFW(Platform Management Unit).
9These requests include clock management, pin control, device control,
10power management service, FPGA service and other platform management
11services.
12
13Required properties:
14 - compatible: Must contain: "xlnx,zynqmp-firmware"
15 - method: The method of calling the PM-API firmware layer.
16 Permitted values are:
17 - "smc" : SMC #0, following the SMCCC
18 - "hvc" : HVC #0, following the SMCCC
19
20-------
21Example
22-------
23
24firmware {
25 zynqmp_firmware: zynqmp-firmware {
26 compatible = "xlnx,zynqmp-firmware";
27 method = "smc";
28 };
29};
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index 393d2b524284..23f3928743c9 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -301,6 +301,7 @@ config ARCH_ZX
301 301
302config ARCH_ZYNQMP 302config ARCH_ZYNQMP
303 bool "Xilinx ZynqMP Family" 303 bool "Xilinx ZynqMP Family"
304 select ZYNQMP_FIRMWARE
304 help 305 help
305 This enables support for Xilinx ZynqMP Family 306 This enables support for Xilinx ZynqMP Family
306 307
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 6e83880046d7..36344cba764e 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -291,5 +291,6 @@ source "drivers/firmware/google/Kconfig"
291source "drivers/firmware/efi/Kconfig" 291source "drivers/firmware/efi/Kconfig"
292source "drivers/firmware/meson/Kconfig" 292source "drivers/firmware/meson/Kconfig"
293source "drivers/firmware/tegra/Kconfig" 293source "drivers/firmware/tegra/Kconfig"
294source "drivers/firmware/xilinx/Kconfig"
294 295
295endmenu 296endmenu
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index e18a041cfc53..99583d3df52f 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -32,3 +32,4 @@ obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
32obj-$(CONFIG_EFI) += efi/ 32obj-$(CONFIG_EFI) += efi/
33obj-$(CONFIG_UEFI_CPER) += efi/ 33obj-$(CONFIG_UEFI_CPER) += efi/
34obj-y += tegra/ 34obj-y += tegra/
35obj-y += xilinx/
diff --git a/drivers/firmware/xilinx/Kconfig b/drivers/firmware/xilinx/Kconfig
new file mode 100644
index 000000000000..8f44b9cd295a
--- /dev/null
+++ b/drivers/firmware/xilinx/Kconfig
@@ -0,0 +1,23 @@
1# SPDX-License-Identifier: GPL-2.0
2# Kconfig for Xilinx firmwares
3
4menu "Zynq MPSoC Firmware Drivers"
5 depends on ARCH_ZYNQMP
6
7config ZYNQMP_FIRMWARE
8 bool "Enable Xilinx Zynq MPSoC firmware interface"
9 help
10 Firmware interface driver is used by different
11 drivers to communicate with the firmware for
12 various platform management services.
13 Say yes to enable ZynqMP firmware interface driver.
14 If in doubt, say N.
15
16config ZYNQMP_FIRMWARE_DEBUG
17 bool "Enable Xilinx Zynq MPSoC firmware debug APIs"
18 depends on ZYNQMP_FIRMWARE && DEBUG_FS
19 help
20 Say yes to enable ZynqMP firmware interface debug APIs.
21 If in doubt, say N.
22
23endmenu
diff --git a/drivers/firmware/xilinx/Makefile b/drivers/firmware/xilinx/Makefile
new file mode 100644
index 000000000000..875a53703c82
--- /dev/null
+++ b/drivers/firmware/xilinx/Makefile
@@ -0,0 +1,5 @@
1# SPDX-License-Identifier: GPL-2.0
2# Makefile for Xilinx firmwares
3
4obj-$(CONFIG_ZYNQMP_FIRMWARE) += zynqmp.o
5obj-$(CONFIG_ZYNQMP_FIRMWARE_DEBUG) += zynqmp-debug.o
diff --git a/drivers/firmware/xilinx/zynqmp-debug.c b/drivers/firmware/xilinx/zynqmp-debug.c
new file mode 100644
index 000000000000..2771df6df379
--- /dev/null
+++ b/drivers/firmware/xilinx/zynqmp-debug.c
@@ -0,0 +1,250 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Xilinx Zynq MPSoC Firmware layer for debugfs APIs
4 *
5 * Copyright (C) 2014-2018 Xilinx, Inc.
6 *
7 * Michal Simek <michal.simek@xilinx.com>
8 * Davorin Mista <davorin.mista@aggios.com>
9 * Jolly Shah <jollys@xilinx.com>
10 * Rajan Vaja <rajanv@xilinx.com>
11 */
12
13#include <linux/compiler.h>
14#include <linux/module.h>
15#include <linux/slab.h>
16#include <linux/debugfs.h>
17#include <linux/uaccess.h>
18
19#include <linux/firmware/xlnx-zynqmp.h>
20#include "zynqmp-debug.h"
21
22#define PM_API_NAME_LEN 50
23
24struct pm_api_info {
25 u32 api_id;
26 char api_name[PM_API_NAME_LEN];
27 char api_name_len;
28};
29
30static char debugfs_buf[PAGE_SIZE];
31
32#define PM_API(id) {id, #id, strlen(#id)}
33static struct pm_api_info pm_api_list[] = {
34 PM_API(PM_GET_API_VERSION),
35 PM_API(PM_QUERY_DATA),
36};
37
38struct dentry *firmware_debugfs_root;
39
40/**
41 * zynqmp_pm_argument_value() - Extract argument value from a PM-API request
42 * @arg: Entered PM-API argument in string format
43 *
44 * Return: Argument value in unsigned integer format on success
45 * 0 otherwise
46 */
47static u64 zynqmp_pm_argument_value(char *arg)
48{
49 u64 value;
50
51 if (!arg)
52 return 0;
53
54 if (!kstrtou64(arg, 0, &value))
55 return value;
56
57 return 0;
58}
59
60/**
61 * get_pm_api_id() - Extract API-ID from a PM-API request
62 * @pm_api_req: Entered PM-API argument in string format
63 * @pm_id: API-ID
64 *
65 * Return: 0 on success else error code
66 */
67static int get_pm_api_id(char *pm_api_req, u32 *pm_id)
68{
69 int i;
70
71 for (i = 0; i < ARRAY_SIZE(pm_api_list) ; i++) {
72 if (!strncasecmp(pm_api_req, pm_api_list[i].api_name,
73 pm_api_list[i].api_name_len)) {
74 *pm_id = pm_api_list[i].api_id;
75 break;
76 }
77 }
78
79 /* If no name was entered look for PM-API ID instead */
80 if (i == ARRAY_SIZE(pm_api_list) && kstrtouint(pm_api_req, 10, pm_id))
81 return -EINVAL;
82
83 return 0;
84}
85
86static int process_api_request(u32 pm_id, u64 *pm_api_arg, u32 *pm_api_ret)
87{
88 const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
89 u32 pm_api_version;
90 int ret;
91 struct zynqmp_pm_query_data qdata = {0};
92
93 if (!eemi_ops)
94 return -ENXIO;
95
96 switch (pm_id) {
97 case PM_GET_API_VERSION:
98 ret = eemi_ops->get_api_version(&pm_api_version);
99 sprintf(debugfs_buf, "PM-API Version = %d.%d\n",
100 pm_api_version >> 16, pm_api_version & 0xffff);
101 break;
102 case PM_QUERY_DATA:
103 qdata.qid = pm_api_arg[0];
104 qdata.arg1 = pm_api_arg[1];
105 qdata.arg2 = pm_api_arg[2];
106 qdata.arg3 = pm_api_arg[3];
107
108 ret = eemi_ops->query_data(qdata, pm_api_ret);
109 if (ret)
110 break;
111
112 switch (qdata.qid) {
113 case PM_QID_CLOCK_GET_NAME:
114 sprintf(debugfs_buf, "Clock name = %s\n",
115 (char *)pm_api_ret);
116 break;
117 case PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS:
118 sprintf(debugfs_buf, "Multiplier = %d, Divider = %d\n",
119 pm_api_ret[1], pm_api_ret[2]);
120 break;
121 default:
122 sprintf(debugfs_buf,
123 "data[0] = 0x%08x\ndata[1] = 0x%08x\n data[2] = 0x%08x\ndata[3] = 0x%08x\n",
124 pm_api_ret[0], pm_api_ret[1],
125 pm_api_ret[2], pm_api_ret[3]);
126 }
127 break;
128 default:
129 sprintf(debugfs_buf, "Unsupported PM-API request\n");
130 ret = -EINVAL;
131 }
132
133 return ret;
134}
135
136/**
137 * zynqmp_pm_debugfs_api_write() - debugfs write function
138 * @file: User file
139 * @ptr: User entered PM-API string
140 * @len: Length of the userspace buffer
141 * @off: Offset within the file
142 *
143 * Used for triggering pm api functions by writing
144 * echo <pm_api_id> > /sys/kernel/debug/zynqmp_pm/power or
145 * echo <pm_api_name> > /sys/kernel/debug/zynqmp_pm/power
146 *
147 * Return: Number of bytes copied if PM-API request succeeds,
148 * the corresponding error code otherwise
149 */
150static ssize_t zynqmp_pm_debugfs_api_write(struct file *file,
151 const char __user *ptr, size_t len,
152 loff_t *off)
153{
154 char *kern_buff, *tmp_buff;
155 char *pm_api_req;
156 u32 pm_id = 0;
157 u64 pm_api_arg[4] = {0, 0, 0, 0};
158 /* Return values from PM APIs calls */
159 u32 pm_api_ret[4] = {0, 0, 0, 0};
160
161 int ret;
162 int i = 0;
163
164 strcpy(debugfs_buf, "");
165
166 if (*off != 0 || len == 0)
167 return -EINVAL;
168
169 kern_buff = kzalloc(len, GFP_KERNEL);
170 if (!kern_buff)
171 return -ENOMEM;
172
173 tmp_buff = kern_buff;
174
175 ret = strncpy_from_user(kern_buff, ptr, len);
176 if (ret < 0) {
177 ret = -EFAULT;
178 goto err;
179 }
180
181 /* Read the API name from a user request */
182 pm_api_req = strsep(&kern_buff, " ");
183
184 ret = get_pm_api_id(pm_api_req, &pm_id);
185 if (ret < 0)
186 goto err;
187
188 /* Read node_id and arguments from the PM-API request */
189 pm_api_req = strsep(&kern_buff, " ");
190 while ((i < ARRAY_SIZE(pm_api_arg)) && pm_api_req) {
191 pm_api_arg[i++] = zynqmp_pm_argument_value(pm_api_req);
192 pm_api_req = strsep(&kern_buff, " ");
193 }
194
195 ret = process_api_request(pm_id, pm_api_arg, pm_api_ret);
196
197err:
198 kfree(tmp_buff);
199 if (ret)
200 return ret;
201
202 return len;
203}
204
205/**
206 * zynqmp_pm_debugfs_api_read() - debugfs read function
207 * @file: User file
208 * @ptr: Requested pm_api_version string
209 * @len: Length of the userspace buffer
210 * @off: Offset within the file
211 *
212 * Return: Length of the version string on success
213 * else error code
214 */
215static ssize_t zynqmp_pm_debugfs_api_read(struct file *file, char __user *ptr,
216 size_t len, loff_t *off)
217{
218 return simple_read_from_buffer(ptr, len, off, debugfs_buf,
219 strlen(debugfs_buf));
220}
221
222/* Setup debugfs fops */
223static const struct file_operations fops_zynqmp_pm_dbgfs = {
224 .owner = THIS_MODULE,
225 .write = zynqmp_pm_debugfs_api_write,
226 .read = zynqmp_pm_debugfs_api_read,
227};
228
229/**
230 * zynqmp_pm_api_debugfs_init - Initialize debugfs interface
231 *
232 * Return: None
233 */
234void zynqmp_pm_api_debugfs_init(void)
235{
236 /* Initialize debugfs interface */
237 firmware_debugfs_root = debugfs_create_dir("zynqmp-firmware", NULL);
238 debugfs_create_file("pm", 0660, firmware_debugfs_root, NULL,
239 &fops_zynqmp_pm_dbgfs);
240}
241
242/**
243 * zynqmp_pm_api_debugfs_exit - Remove debugfs interface
244 *
245 * Return: None
246 */
247void zynqmp_pm_api_debugfs_exit(void)
248{
249 debugfs_remove_recursive(firmware_debugfs_root);
250}
diff --git a/drivers/firmware/xilinx/zynqmp-debug.h b/drivers/firmware/xilinx/zynqmp-debug.h
new file mode 100644
index 000000000000..9929f8b433f5
--- /dev/null
+++ b/drivers/firmware/xilinx/zynqmp-debug.h
@@ -0,0 +1,24 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Xilinx Zynq MPSoC Firmware layer
4 *
5 * Copyright (C) 2014-2018 Xilinx
6 *
7 * Michal Simek <michal.simek@xilinx.com>
8 * Davorin Mista <davorin.mista@aggios.com>
9 * Jolly Shah <jollys@xilinx.com>
10 * Rajan Vaja <rajanv@xilinx.com>
11 */
12
13#ifndef __FIRMWARE_ZYNQMP_DEBUG_H__
14#define __FIRMWARE_ZYNQMP_DEBUG_H__
15
16#if IS_REACHABLE(CONFIG_ZYNQMP_FIRMWARE_DEBUG)
17void zynqmp_pm_api_debugfs_init(void);
18void zynqmp_pm_api_debugfs_exit(void);
19#else
20static inline void zynqmp_pm_api_debugfs_init(void) { }
21static inline void zynqmp_pm_api_debugfs_exit(void) { }
22#endif
23
24#endif /* __FIRMWARE_ZYNQMP_DEBUG_H__ */
diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c
new file mode 100644
index 000000000000..84b3fd2eca8b
--- /dev/null
+++ b/drivers/firmware/xilinx/zynqmp.c
@@ -0,0 +1,523 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Xilinx Zynq MPSoC Firmware layer
4 *
5 * Copyright (C) 2014-2018 Xilinx, Inc.
6 *
7 * Michal Simek <michal.simek@xilinx.com>
8 * Davorin Mista <davorin.mista@aggios.com>
9 * Jolly Shah <jollys@xilinx.com>
10 * Rajan Vaja <rajanv@xilinx.com>
11 */
12
13#include <linux/arm-smccc.h>
14#include <linux/compiler.h>
15#include <linux/device.h>
16#include <linux/init.h>
17#include <linux/module.h>
18#include <linux/of.h>
19#include <linux/of_platform.h>
20#include <linux/slab.h>
21#include <linux/uaccess.h>
22
23#include <linux/firmware/xlnx-zynqmp.h>
24#include "zynqmp-debug.h"
25
26/**
27 * zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes
28 * @ret_status: PMUFW return code
29 *
30 * Return: corresponding Linux error code
31 */
32static int zynqmp_pm_ret_code(u32 ret_status)
33{
34 switch (ret_status) {
35 case XST_PM_SUCCESS:
36 case XST_PM_DOUBLE_REQ:
37 return 0;
38 case XST_PM_NO_ACCESS:
39 return -EACCES;
40 case XST_PM_ABORT_SUSPEND:
41 return -ECANCELED;
42 case XST_PM_INTERNAL:
43 case XST_PM_CONFLICT:
44 case XST_PM_INVALID_NODE:
45 default:
46 return -EINVAL;
47 }
48}
49
50static noinline int do_fw_call_fail(u64 arg0, u64 arg1, u64 arg2,
51 u32 *ret_payload)
52{
53 return -ENODEV;
54}
55
56/*
57 * PM function call wrapper
58 * Invoke do_fw_call_smc or do_fw_call_hvc, depending on the configuration
59 */
60static int (*do_fw_call)(u64, u64, u64, u32 *ret_payload) = do_fw_call_fail;
61
62/**
63 * do_fw_call_smc() - Call system-level platform management layer (SMC)
64 * @arg0: Argument 0 to SMC call
65 * @arg1: Argument 1 to SMC call
66 * @arg2: Argument 2 to SMC call
67 * @ret_payload: Returned value array
68 *
69 * Invoke platform management function via SMC call (no hypervisor present).
70 *
71 * Return: Returns status, either success or error+reason
72 */
73static noinline int do_fw_call_smc(u64 arg0, u64 arg1, u64 arg2,
74 u32 *ret_payload)
75{
76 struct arm_smccc_res res;
77
78 arm_smccc_smc(arg0, arg1, arg2, 0, 0, 0, 0, 0, &res);
79
80 if (ret_payload) {
81 ret_payload[0] = lower_32_bits(res.a0);
82 ret_payload[1] = upper_32_bits(res.a0);
83 ret_payload[2] = lower_32_bits(res.a1);
84 ret_payload[3] = upper_32_bits(res.a1);
85 }
86
87 return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);
88}
89
90/**
91 * do_fw_call_hvc() - Call system-level platform management layer (HVC)
92 * @arg0: Argument 0 to HVC call
93 * @arg1: Argument 1 to HVC call
94 * @arg2: Argument 2 to HVC call
95 * @ret_payload: Returned value array
96 *
97 * Invoke platform management function via HVC
98 * HVC-based for communication through hypervisor
99 * (no direct communication with ATF).
100 *
101 * Return: Returns status, either success or error+reason
102 */
103static noinline int do_fw_call_hvc(u64 arg0, u64 arg1, u64 arg2,
104 u32 *ret_payload)
105{
106 struct arm_smccc_res res;
107
108 arm_smccc_hvc(arg0, arg1, arg2, 0, 0, 0, 0, 0, &res);
109
110 if (ret_payload) {
111 ret_payload[0] = lower_32_bits(res.a0);
112 ret_payload[1] = upper_32_bits(res.a0);
113 ret_payload[2] = lower_32_bits(res.a1);
114 ret_payload[3] = upper_32_bits(res.a1);
115 }
116
117 return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);
118}
119
120/**
121 * zynqmp_pm_invoke_fn() - Invoke the system-level platform management layer
122 * caller function depending on the configuration
123 * @pm_api_id: Requested PM-API call
124 * @arg0: Argument 0 to requested PM-API call
125 * @arg1: Argument 1 to requested PM-API call
126 * @arg2: Argument 2 to requested PM-API call
127 * @arg3: Argument 3 to requested PM-API call
128 * @ret_payload: Returned value array
129 *
130 * Invoke platform management function for SMC or HVC call, depending on
131 * configuration.
132 * Following SMC Calling Convention (SMCCC) for SMC64:
133 * Pm Function Identifier,
134 * PM_SIP_SVC + PM_API_ID =
135 * ((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT)
136 * ((SMC_64) << FUNCID_CC_SHIFT)
137 * ((SIP_START) << FUNCID_OEN_SHIFT)
138 * ((PM_API_ID) & FUNCID_NUM_MASK))
139 *
140 * PM_SIP_SVC - Registered ZynqMP SIP Service Call.
141 * PM_API_ID - Platform Management API ID.
142 *
143 * Return: Returns status, either success or error+reason
144 */
145int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1,
146 u32 arg2, u32 arg3, u32 *ret_payload)
147{
148 /*
149 * Added SIP service call Function Identifier
150 * Make sure to stay in x0 register
151 */
152 u64 smc_arg[4];
153
154 smc_arg[0] = PM_SIP_SVC | pm_api_id;
155 smc_arg[1] = ((u64)arg1 << 32) | arg0;
156 smc_arg[2] = ((u64)arg3 << 32) | arg2;
157
158 return do_fw_call(smc_arg[0], smc_arg[1], smc_arg[2], ret_payload);
159}
160
161static u32 pm_api_version;
162static u32 pm_tz_version;
163
164/**
165 * zynqmp_pm_get_api_version() - Get version number of PMU PM firmware
166 * @version: Returned version value
167 *
168 * Return: Returns status, either success or error+reason
169 */
170static int zynqmp_pm_get_api_version(u32 *version)
171{
172 u32 ret_payload[PAYLOAD_ARG_CNT];
173 int ret;
174
175 if (!version)
176 return -EINVAL;
177
178 /* Check is PM API version already verified */
179 if (pm_api_version > 0) {
180 *version = pm_api_version;
181 return 0;
182 }
183 ret = zynqmp_pm_invoke_fn(PM_GET_API_VERSION, 0, 0, 0, 0, ret_payload);
184 *version = ret_payload[1];
185
186 return ret;
187}
188
189/**
190 * zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version
191 * @version: Returned version value
192 *
193 * Return: Returns status, either success or error+reason
194 */
195static int zynqmp_pm_get_trustzone_version(u32 *version)
196{
197 u32 ret_payload[PAYLOAD_ARG_CNT];
198 int ret;
199
200 if (!version)
201 return -EINVAL;
202
203 /* Check is PM trustzone version already verified */
204 if (pm_tz_version > 0) {
205 *version = pm_tz_version;
206 return 0;
207 }
208 ret = zynqmp_pm_invoke_fn(PM_GET_TRUSTZONE_VERSION, 0, 0,
209 0, 0, ret_payload);
210 *version = ret_payload[1];
211
212 return ret;
213}
214
215/**
216 * get_set_conduit_method() - Choose SMC or HVC based communication
217 * @np: Pointer to the device_node structure
218 *
219 * Use SMC or HVC-based functions to communicate with EL2/EL3.
220 *
221 * Return: Returns 0 on success or error code
222 */
223static int get_set_conduit_method(struct device_node *np)
224{
225 const char *method;
226
227 if (of_property_read_string(np, "method", &method)) {
228 pr_warn("%s missing \"method\" property\n", __func__);
229 return -ENXIO;
230 }
231
232 if (!strcmp("hvc", method)) {
233 do_fw_call = do_fw_call_hvc;
234 } else if (!strcmp("smc", method)) {
235 do_fw_call = do_fw_call_smc;
236 } else {
237 pr_warn("%s Invalid \"method\" property: %s\n",
238 __func__, method);
239 return -EINVAL;
240 }
241
242 return 0;
243}
244
245/**
246 * zynqmp_pm_query_data() - Get query data from firmware
247 * @qdata: Variable to the zynqmp_pm_query_data structure
248 * @out: Returned output value
249 *
250 * Return: Returns status, either success or error+reason
251 */
252static int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata, u32 *out)
253{
254 int ret;
255
256 ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, qdata.qid, qdata.arg1,
257 qdata.arg2, qdata.arg3, out);
258
259 /*
260 * For clock name query, all bytes in SMC response are clock name
261 * characters and return code is always success. For invalid clocks,
262 * clock name bytes would be zeros.
263 */
264 return qdata.qid == PM_QID_CLOCK_GET_NAME ? 0 : ret;
265}
266
267/**
268 * zynqmp_pm_clock_enable() - Enable the clock for given id
269 * @clock_id: ID of the clock to be enabled
270 *
271 * This function is used by master to enable the clock
272 * including peripherals and PLL clocks.
273 *
274 * Return: Returns status, either success or error+reason
275 */
276static int zynqmp_pm_clock_enable(u32 clock_id)
277{
278 return zynqmp_pm_invoke_fn(PM_CLOCK_ENABLE, clock_id, 0, 0, 0, NULL);
279}
280
281/**
282 * zynqmp_pm_clock_disable() - Disable the clock for given id
283 * @clock_id: ID of the clock to be disable
284 *
285 * This function is used by master to disable the clock
286 * including peripherals and PLL clocks.
287 *
288 * Return: Returns status, either success or error+reason
289 */
290static int zynqmp_pm_clock_disable(u32 clock_id)
291{
292 return zynqmp_pm_invoke_fn(PM_CLOCK_DISABLE, clock_id, 0, 0, 0, NULL);
293}
294
295/**
296 * zynqmp_pm_clock_getstate() - Get the clock state for given id
297 * @clock_id: ID of the clock to be queried
298 * @state: 1/0 (Enabled/Disabled)
299 *
300 * This function is used by master to get the state of clock
301 * including peripherals and PLL clocks.
302 *
303 * Return: Returns status, either success or error+reason
304 */
305static int zynqmp_pm_clock_getstate(u32 clock_id, u32 *state)
306{
307 u32 ret_payload[PAYLOAD_ARG_CNT];
308 int ret;
309
310 ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETSTATE, clock_id, 0,
311 0, 0, ret_payload);
312 *state = ret_payload[1];
313
314 return ret;
315}
316
317/**
318 * zynqmp_pm_clock_setdivider() - Set the clock divider for given id
319 * @clock_id: ID of the clock
320 * @divider: divider value
321 *
322 * This function is used by master to set divider for any clock
323 * to achieve desired rate.
324 *
325 * Return: Returns status, either success or error+reason
326 */
327static int zynqmp_pm_clock_setdivider(u32 clock_id, u32 divider)
328{
329 return zynqmp_pm_invoke_fn(PM_CLOCK_SETDIVIDER, clock_id, divider,
330 0, 0, NULL);
331}
332
333/**
334 * zynqmp_pm_clock_getdivider() - Get the clock divider for given id
335 * @clock_id: ID of the clock
336 * @divider: divider value
337 *
338 * This function is used by master to get divider values
339 * for any clock.
340 *
341 * Return: Returns status, either success or error+reason
342 */
343static int zynqmp_pm_clock_getdivider(u32 clock_id, u32 *divider)
344{
345 u32 ret_payload[PAYLOAD_ARG_CNT];
346 int ret;
347
348 ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETDIVIDER, clock_id, 0,
349 0, 0, ret_payload);
350 *divider = ret_payload[1];
351
352 return ret;
353}
354
355/**
356 * zynqmp_pm_clock_setrate() - Set the clock rate for given id
357 * @clock_id: ID of the clock
358 * @rate: rate value in hz
359 *
360 * This function is used by master to set rate for any clock.
361 *
362 * Return: Returns status, either success or error+reason
363 */
364static int zynqmp_pm_clock_setrate(u32 clock_id, u64 rate)
365{
366 return zynqmp_pm_invoke_fn(PM_CLOCK_SETRATE, clock_id,
367 lower_32_bits(rate),
368 upper_32_bits(rate),
369 0, NULL);
370}
371
372/**
373 * zynqmp_pm_clock_getrate() - Get the clock rate for given id
374 * @clock_id: ID of the clock
375 * @rate: rate value in hz
376 *
377 * This function is used by master to get rate
378 * for any clock.
379 *
380 * Return: Returns status, either success or error+reason
381 */
382static int zynqmp_pm_clock_getrate(u32 clock_id, u64 *rate)
383{
384 u32 ret_payload[PAYLOAD_ARG_CNT];
385 int ret;
386
387 ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETRATE, clock_id, 0,
388 0, 0, ret_payload);
389 *rate = ((u64)ret_payload[2] << 32) | ret_payload[1];
390
391 return ret;
392}
393
394/**
395 * zynqmp_pm_clock_setparent() - Set the clock parent for given id
396 * @clock_id: ID of the clock
397 * @parent_id: parent id
398 *
399 * This function is used by master to set parent for any clock.
400 *
401 * Return: Returns status, either success or error+reason
402 */
403static int zynqmp_pm_clock_setparent(u32 clock_id, u32 parent_id)
404{
405 return zynqmp_pm_invoke_fn(PM_CLOCK_SETPARENT, clock_id,
406 parent_id, 0, 0, NULL);
407}
408
409/**
410 * zynqmp_pm_clock_getparent() - Get the clock parent for given id
411 * @clock_id: ID of the clock
412 * @parent_id: parent id
413 *
414 * This function is used by master to get parent index
415 * for any clock.
416 *
417 * Return: Returns status, either success or error+reason
418 */
419static int zynqmp_pm_clock_getparent(u32 clock_id, u32 *parent_id)
420{
421 u32 ret_payload[PAYLOAD_ARG_CNT];
422 int ret;
423
424 ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETPARENT, clock_id, 0,
425 0, 0, ret_payload);
426 *parent_id = ret_payload[1];
427
428 return ret;
429}
430
431static const struct zynqmp_eemi_ops eemi_ops = {
432 .get_api_version = zynqmp_pm_get_api_version,
433 .query_data = zynqmp_pm_query_data,
434 .clock_enable = zynqmp_pm_clock_enable,
435 .clock_disable = zynqmp_pm_clock_disable,
436 .clock_getstate = zynqmp_pm_clock_getstate,
437 .clock_setdivider = zynqmp_pm_clock_setdivider,
438 .clock_getdivider = zynqmp_pm_clock_getdivider,
439 .clock_setrate = zynqmp_pm_clock_setrate,
440 .clock_getrate = zynqmp_pm_clock_getrate,
441 .clock_setparent = zynqmp_pm_clock_setparent,
442 .clock_getparent = zynqmp_pm_clock_getparent,
443};
444
445/**
446 * zynqmp_pm_get_eemi_ops - Get eemi ops functions
447 *
448 * Return: Pointer of eemi_ops structure
449 */
450const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void)
451{
452 return &eemi_ops;
453}
454EXPORT_SYMBOL_GPL(zynqmp_pm_get_eemi_ops);
455
456static int zynqmp_firmware_probe(struct platform_device *pdev)
457{
458 struct device *dev = &pdev->dev;
459 struct device_node *np;
460 int ret;
461
462 np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp");
463 if (!np)
464 return 0;
465 of_node_put(np);
466
467 ret = get_set_conduit_method(dev->of_node);
468 if (ret)
469 return ret;
470
471 /* Check PM API version number */
472 zynqmp_pm_get_api_version(&pm_api_version);
473 if (pm_api_version < ZYNQMP_PM_VERSION) {
474 panic("%s Platform Management API version error. Expected: v%d.%d - Found: v%d.%d\n",
475 __func__,
476 ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR,
477 pm_api_version >> 16, pm_api_version & 0xFFFF);
478 }
479
480 pr_info("%s Platform Management API v%d.%d\n", __func__,
481 pm_api_version >> 16, pm_api_version & 0xFFFF);
482
483 /* Check trustzone version number */
484 ret = zynqmp_pm_get_trustzone_version(&pm_tz_version);
485 if (ret)
486 panic("Legacy trustzone found without version support\n");
487
488 if (pm_tz_version < ZYNQMP_TZ_VERSION)
489 panic("%s Trustzone version error. Expected: v%d.%d - Found: v%d.%d\n",
490 __func__,
491 ZYNQMP_TZ_VERSION_MAJOR, ZYNQMP_TZ_VERSION_MINOR,
492 pm_tz_version >> 16, pm_tz_version & 0xFFFF);
493
494 pr_info("%s Trustzone version v%d.%d\n", __func__,
495 pm_tz_version >> 16, pm_tz_version & 0xFFFF);
496
497 zynqmp_pm_api_debugfs_init();
498
499 return of_platform_populate(dev->of_node, NULL, NULL, dev);
500}
501
502static int zynqmp_firmware_remove(struct platform_device *pdev)
503{
504 zynqmp_pm_api_debugfs_exit();
505
506 return 0;
507}
508
509static const struct of_device_id zynqmp_firmware_of_match[] = {
510 {.compatible = "xlnx,zynqmp-firmware"},
511 {},
512};
513MODULE_DEVICE_TABLE(of, zynqmp_firmware_of_match);
514
515static struct platform_driver zynqmp_firmware_driver = {
516 .driver = {
517 .name = "zynqmp_firmware",
518 .of_match_table = zynqmp_firmware_of_match,
519 },
520 .probe = zynqmp_firmware_probe,
521 .remove = zynqmp_firmware_remove,
522};
523module_platform_driver(zynqmp_firmware_driver);
diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h
new file mode 100644
index 000000000000..015e130431e6
--- /dev/null
+++ b/include/linux/firmware/xlnx-zynqmp.h
@@ -0,0 +1,113 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Xilinx Zynq MPSoC Firmware layer
4 *
5 * Copyright (C) 2014-2018 Xilinx
6 *
7 * Michal Simek <michal.simek@xilinx.com>
8 * Davorin Mista <davorin.mista@aggios.com>
9 * Jolly Shah <jollys@xilinx.com>
10 * Rajan Vaja <rajanv@xilinx.com>
11 */
12
13#ifndef __FIRMWARE_ZYNQMP_H__
14#define __FIRMWARE_ZYNQMP_H__
15
16#define ZYNQMP_PM_VERSION_MAJOR 1
17#define ZYNQMP_PM_VERSION_MINOR 0
18
19#define ZYNQMP_PM_VERSION ((ZYNQMP_PM_VERSION_MAJOR << 16) | \
20 ZYNQMP_PM_VERSION_MINOR)
21
22#define ZYNQMP_TZ_VERSION_MAJOR 1
23#define ZYNQMP_TZ_VERSION_MINOR 0
24
25#define ZYNQMP_TZ_VERSION ((ZYNQMP_TZ_VERSION_MAJOR << 16) | \
26 ZYNQMP_TZ_VERSION_MINOR)
27
28/* SMC SIP service Call Function Identifier Prefix */
29#define PM_SIP_SVC 0xC2000000
30#define PM_GET_TRUSTZONE_VERSION 0xa03
31
32/* Number of 32bits values in payload */
33#define PAYLOAD_ARG_CNT 4U
34
35enum pm_api_id {
36 PM_GET_API_VERSION = 1,
37 PM_QUERY_DATA = 35,
38 PM_CLOCK_ENABLE,
39 PM_CLOCK_DISABLE,
40 PM_CLOCK_GETSTATE,
41 PM_CLOCK_SETDIVIDER,
42 PM_CLOCK_GETDIVIDER,
43 PM_CLOCK_SETRATE,
44 PM_CLOCK_GETRATE,
45 PM_CLOCK_SETPARENT,
46 PM_CLOCK_GETPARENT,
47};
48
49/* PMU-FW return status codes */
50enum pm_ret_status {
51 XST_PM_SUCCESS = 0,
52 XST_PM_INTERNAL = 2000,
53 XST_PM_CONFLICT,
54 XST_PM_NO_ACCESS,
55 XST_PM_INVALID_NODE,
56 XST_PM_DOUBLE_REQ,
57 XST_PM_ABORT_SUSPEND,
58};
59
60enum pm_ioctl_id {
61 IOCTL_SET_PLL_FRAC_MODE = 8,
62 IOCTL_GET_PLL_FRAC_MODE,
63 IOCTL_SET_PLL_FRAC_DATA,
64 IOCTL_GET_PLL_FRAC_DATA,
65};
66
67enum pm_query_id {
68 PM_QID_INVALID,
69 PM_QID_CLOCK_GET_NAME,
70 PM_QID_CLOCK_GET_TOPOLOGY,
71 PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS,
72 PM_QID_CLOCK_GET_PARENTS,
73 PM_QID_CLOCK_GET_ATTRIBUTES,
74};
75
76/**
77 * struct zynqmp_pm_query_data - PM query data
78 * @qid: query ID
79 * @arg1: Argument 1 of query data
80 * @arg2: Argument 2 of query data
81 * @arg3: Argument 3 of query data
82 */
83struct zynqmp_pm_query_data {
84 u32 qid;
85 u32 arg1;
86 u32 arg2;
87 u32 arg3;
88};
89
90struct zynqmp_eemi_ops {
91 int (*get_api_version)(u32 *version);
92 int (*query_data)(struct zynqmp_pm_query_data qdata, u32 *out);
93 int (*clock_enable)(u32 clock_id);
94 int (*clock_disable)(u32 clock_id);
95 int (*clock_getstate)(u32 clock_id, u32 *state);
96 int (*clock_setdivider)(u32 clock_id, u32 divider);
97 int (*clock_getdivider)(u32 clock_id, u32 *divider);
98 int (*clock_setrate)(u32 clock_id, u64 rate);
99 int (*clock_getrate)(u32 clock_id, u64 *rate);
100 int (*clock_setparent)(u32 clock_id, u32 parent_id);
101 int (*clock_getparent)(u32 clock_id, u32 *parent_id);
102};
103
104#if IS_REACHABLE(CONFIG_ARCH_ZYNQMP)
105const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void);
106#else
107static inline struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void)
108{
109 return NULL;
110}
111#endif
112
113#endif /* __FIRMWARE_ZYNQMP_H__ */