aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2018-11-03 18:42:16 -0400
committerIngo Molnar <mingo@kernel.org>2018-11-03 18:42:16 -0400
commit23a12ddee1ce28065b71f14ccc695b5a0c8a64ff (patch)
treecedaa1cde5b2557116e523c31552187804704093 /drivers/firmware
parent98f76206b33504b934209d16196477dfa519a807 (diff)
parentbcb6fb5da77c2a228adf07cc9cb1a0c2aa2001c6 (diff)
Merge branch 'core/urgent' into x86/urgent, to pick up objtool fix
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/Kconfig2
-rw-r--r--drivers/firmware/Makefile2
-rw-r--r--drivers/firmware/arm_scmi/base.c2
-rw-r--r--drivers/firmware/arm_scmi/clock.c2
-rw-r--r--drivers/firmware/arm_scmi/perf.c30
-rw-r--r--drivers/firmware/arm_scmi/power.c2
-rw-r--r--drivers/firmware/arm_scmi/sensors.c2
-rw-r--r--drivers/firmware/dmi_scan.c2
-rw-r--r--drivers/firmware/efi/apple-properties.c4
-rw-r--r--drivers/firmware/efi/memmap.c2
-rw-r--r--drivers/firmware/imx/Kconfig11
-rw-r--r--drivers/firmware/imx/Makefile2
-rw-r--r--drivers/firmware/imx/imx-scu.c270
-rw-r--r--drivers/firmware/imx/misc.c99
-rw-r--r--drivers/firmware/iscsi_ibft_find.c2
-rw-r--r--drivers/firmware/memmap.c5
-rw-r--r--drivers/firmware/meson/meson_sm.c56
-rw-r--r--drivers/firmware/qcom_scm.c74
-rw-r--r--drivers/firmware/tegra/bpmp.c19
-rw-r--r--drivers/firmware/ti_sci.c24
-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.c565
25 files changed, 1431 insertions, 48 deletions
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 6e83880046d7..7670e8dda829 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -289,7 +289,9 @@ config HAVE_ARM_SMCCC
289source "drivers/firmware/broadcom/Kconfig" 289source "drivers/firmware/broadcom/Kconfig"
290source "drivers/firmware/google/Kconfig" 290source "drivers/firmware/google/Kconfig"
291source "drivers/firmware/efi/Kconfig" 291source "drivers/firmware/efi/Kconfig"
292source "drivers/firmware/imx/Kconfig"
292source "drivers/firmware/meson/Kconfig" 293source "drivers/firmware/meson/Kconfig"
293source "drivers/firmware/tegra/Kconfig" 294source "drivers/firmware/tegra/Kconfig"
295source "drivers/firmware/xilinx/Kconfig"
294 296
295endmenu 297endmenu
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index e18a041cfc53..13660a951437 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -31,4 +31,6 @@ obj-y += meson/
31obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ 31obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
32obj-$(CONFIG_EFI) += efi/ 32obj-$(CONFIG_EFI) += efi/
33obj-$(CONFIG_UEFI_CPER) += efi/ 33obj-$(CONFIG_UEFI_CPER) += efi/
34obj-y += imx/
34obj-y += tegra/ 35obj-y += tegra/
36obj-y += xilinx/
diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
index 9dff33ea6416..204390297f4b 100644
--- a/drivers/firmware/arm_scmi/base.c
+++ b/drivers/firmware/arm_scmi/base.c
@@ -208,7 +208,7 @@ static int scmi_base_discover_agent_get(const struct scmi_handle *handle,
208 208
209 ret = scmi_do_xfer(handle, t); 209 ret = scmi_do_xfer(handle, t);
210 if (!ret) 210 if (!ret)
211 memcpy(name, t->rx.buf, SCMI_MAX_STR_SIZE); 211 strlcpy(name, t->rx.buf, SCMI_MAX_STR_SIZE);
212 212
213 scmi_xfer_put(handle, t); 213 scmi_xfer_put(handle, t);
214 214
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index e4119eb34986..30fc04e28431 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -111,7 +111,7 @@ static int scmi_clock_attributes_get(const struct scmi_handle *handle,
111 111
112 ret = scmi_do_xfer(handle, t); 112 ret = scmi_do_xfer(handle, t);
113 if (!ret) 113 if (!ret)
114 memcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE); 114 strlcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE);
115 else 115 else
116 clk->name[0] = '\0'; 116 clk->name[0] = '\0';
117 117
diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
index 64342944d917..3c8ae7cc35de 100644
--- a/drivers/firmware/arm_scmi/perf.c
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -174,7 +174,7 @@ scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
174 dom_info->mult_factor = 174 dom_info->mult_factor =
175 (dom_info->sustained_freq_khz * 1000) / 175 (dom_info->sustained_freq_khz * 1000) /
176 dom_info->sustained_perf_level; 176 dom_info->sustained_perf_level;
177 memcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE); 177 strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
178 } 178 }
179 179
180 scmi_xfer_put(handle, t); 180 scmi_xfer_put(handle, t);
@@ -427,6 +427,33 @@ static int scmi_dvfs_freq_get(const struct scmi_handle *handle, u32 domain,
427 return ret; 427 return ret;
428} 428}
429 429
430static int scmi_dvfs_est_power_get(const struct scmi_handle *handle, u32 domain,
431 unsigned long *freq, unsigned long *power)
432{
433 struct scmi_perf_info *pi = handle->perf_priv;
434 struct perf_dom_info *dom;
435 unsigned long opp_freq;
436 int idx, ret = -EINVAL;
437 struct scmi_opp *opp;
438
439 dom = pi->dom_info + domain;
440 if (!dom)
441 return -EIO;
442
443 for (opp = dom->opp, idx = 0; idx < dom->opp_count; idx++, opp++) {
444 opp_freq = opp->perf * dom->mult_factor;
445 if (opp_freq < *freq)
446 continue;
447
448 *freq = opp_freq;
449 *power = opp->power;
450 ret = 0;
451 break;
452 }
453
454 return ret;
455}
456
430static struct scmi_perf_ops perf_ops = { 457static struct scmi_perf_ops perf_ops = {
431 .limits_set = scmi_perf_limits_set, 458 .limits_set = scmi_perf_limits_set,
432 .limits_get = scmi_perf_limits_get, 459 .limits_get = scmi_perf_limits_get,
@@ -437,6 +464,7 @@ static struct scmi_perf_ops perf_ops = {
437 .device_opps_add = scmi_dvfs_device_opps_add, 464 .device_opps_add = scmi_dvfs_device_opps_add,
438 .freq_set = scmi_dvfs_freq_set, 465 .freq_set = scmi_dvfs_freq_set,
439 .freq_get = scmi_dvfs_freq_get, 466 .freq_get = scmi_dvfs_freq_get,
467 .est_power_get = scmi_dvfs_est_power_get,
440}; 468};
441 469
442static int scmi_perf_protocol_init(struct scmi_handle *handle) 470static int scmi_perf_protocol_init(struct scmi_handle *handle)
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
index cfa033b05aed..62f3401a1f01 100644
--- a/drivers/firmware/arm_scmi/power.c
+++ b/drivers/firmware/arm_scmi/power.c
@@ -106,7 +106,7 @@ scmi_power_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
106 dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags); 106 dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags);
107 dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags); 107 dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags);
108 dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags); 108 dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags);
109 memcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE); 109 strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
110 } 110 }
111 111
112 scmi_xfer_put(handle, t); 112 scmi_xfer_put(handle, t);
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
index 27f2092b9882..b53d5cc9c9f6 100644
--- a/drivers/firmware/arm_scmi/sensors.c
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -140,7 +140,7 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle,
140 s = &si->sensors[desc_index + cnt]; 140 s = &si->sensors[desc_index + cnt];
141 s->id = le32_to_cpu(buf->desc[cnt].id); 141 s->id = le32_to_cpu(buf->desc[cnt].id);
142 s->type = SENSOR_TYPE(attrh); 142 s->type = SENSOR_TYPE(attrh);
143 memcpy(s->name, buf->desc[cnt].name, SCMI_MAX_STR_SIZE); 143 strlcpy(s->name, buf->desc[cnt].name, SCMI_MAX_STR_SIZE);
144 } 144 }
145 145
146 desc_index += num_returned; 146 desc_index += num_returned;
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index f2483548cde9..099d83e4e910 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -5,7 +5,7 @@
5#include <linux/ctype.h> 5#include <linux/ctype.h>
6#include <linux/dmi.h> 6#include <linux/dmi.h>
7#include <linux/efi.h> 7#include <linux/efi.h>
8#include <linux/bootmem.h> 8#include <linux/memblock.h>
9#include <linux/random.h> 9#include <linux/random.h>
10#include <asm/dmi.h> 10#include <asm/dmi.h>
11#include <asm/unaligned.h> 11#include <asm/unaligned.h>
diff --git a/drivers/firmware/efi/apple-properties.c b/drivers/firmware/efi/apple-properties.c
index 60a95719ecb8..ac1654f74dc7 100644
--- a/drivers/firmware/efi/apple-properties.c
+++ b/drivers/firmware/efi/apple-properties.c
@@ -20,7 +20,7 @@
20 20
21#define pr_fmt(fmt) "apple-properties: " fmt 21#define pr_fmt(fmt) "apple-properties: " fmt
22 22
23#include <linux/bootmem.h> 23#include <linux/memblock.h>
24#include <linux/efi.h> 24#include <linux/efi.h>
25#include <linux/io.h> 25#include <linux/io.h>
26#include <linux/platform_data/x86/apple.h> 26#include <linux/platform_data/x86/apple.h>
@@ -235,7 +235,7 @@ static int __init map_properties(void)
235 */ 235 */
236 data->len = 0; 236 data->len = 0;
237 memunmap(data); 237 memunmap(data);
238 free_bootmem_late(pa_data + sizeof(*data), data_len); 238 memblock_free_late(pa_data + sizeof(*data), data_len);
239 239
240 return ret; 240 return ret;
241 } 241 }
diff --git a/drivers/firmware/efi/memmap.c b/drivers/firmware/efi/memmap.c
index 5fc70520e04c..fa2904fb841f 100644
--- a/drivers/firmware/efi/memmap.c
+++ b/drivers/firmware/efi/memmap.c
@@ -15,7 +15,7 @@
15 15
16static phys_addr_t __init __efi_memmap_alloc_early(unsigned long size) 16static phys_addr_t __init __efi_memmap_alloc_early(unsigned long size)
17{ 17{
18 return memblock_alloc(size, 0); 18 return memblock_phys_alloc(size, SMP_CACHE_BYTES);
19} 19}
20 20
21static phys_addr_t __init __efi_memmap_alloc_late(unsigned long size) 21static phys_addr_t __init __efi_memmap_alloc_late(unsigned long size)
diff --git a/drivers/firmware/imx/Kconfig b/drivers/firmware/imx/Kconfig
new file mode 100644
index 000000000000..b170c2851e48
--- /dev/null
+++ b/drivers/firmware/imx/Kconfig
@@ -0,0 +1,11 @@
1config IMX_SCU
2 bool "IMX SCU Protocol driver"
3 depends on IMX_MBOX
4 help
5 The System Controller Firmware (SCFW) is a low-level system function
6 which runs on a dedicated Cortex-M core to provide power, clock, and
7 resource management. It exists on some i.MX8 processors. e.g. i.MX8QM
8 (QM, QP), and i.MX8QX (QXP, DX).
9
10 This driver manages the IPC interface between host CPU and the
11 SCU firmware running on M4.
diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile
new file mode 100644
index 000000000000..0ac04dfda8d4
--- /dev/null
+++ b/drivers/firmware/imx/Makefile
@@ -0,0 +1,2 @@
1# SPDX-License-Identifier: GPL-2.0
2obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o
diff --git a/drivers/firmware/imx/imx-scu.c b/drivers/firmware/imx/imx-scu.c
new file mode 100644
index 000000000000..2bb1a19c413f
--- /dev/null
+++ b/drivers/firmware/imx/imx-scu.c
@@ -0,0 +1,270 @@
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2018 NXP
4 * Author: Dong Aisheng <aisheng.dong@nxp.com>
5 *
6 * Implementation of the SCU IPC functions using MUs (client side).
7 *
8 */
9
10#include <linux/err.h>
11#include <linux/firmware/imx/types.h>
12#include <linux/firmware/imx/ipc.h>
13#include <linux/interrupt.h>
14#include <linux/irq.h>
15#include <linux/kernel.h>
16#include <linux/mailbox_client.h>
17#include <linux/module.h>
18#include <linux/mutex.h>
19#include <linux/of_platform.h>
20#include <linux/platform_device.h>
21
22#define SCU_MU_CHAN_NUM 8
23#define MAX_RX_TIMEOUT (msecs_to_jiffies(30))
24
25struct imx_sc_chan {
26 struct imx_sc_ipc *sc_ipc;
27
28 struct mbox_client cl;
29 struct mbox_chan *ch;
30 int idx;
31};
32
33struct imx_sc_ipc {
34 /* SCU uses 4 Tx and 4 Rx channels */
35 struct imx_sc_chan chans[SCU_MU_CHAN_NUM];
36 struct device *dev;
37 struct mutex lock;
38 struct completion done;
39
40 /* temporarily store the SCU msg */
41 u32 *msg;
42 u8 rx_size;
43 u8 count;
44};
45
46/*
47 * This type is used to indicate error response for most functions.
48 */
49enum imx_sc_error_codes {
50 IMX_SC_ERR_NONE = 0, /* Success */
51 IMX_SC_ERR_VERSION = 1, /* Incompatible API version */
52 IMX_SC_ERR_CONFIG = 2, /* Configuration error */
53 IMX_SC_ERR_PARM = 3, /* Bad parameter */
54 IMX_SC_ERR_NOACCESS = 4, /* Permission error (no access) */
55 IMX_SC_ERR_LOCKED = 5, /* Permission error (locked) */
56 IMX_SC_ERR_UNAVAILABLE = 6, /* Unavailable (out of resources) */
57 IMX_SC_ERR_NOTFOUND = 7, /* Not found */
58 IMX_SC_ERR_NOPOWER = 8, /* No power */
59 IMX_SC_ERR_IPC = 9, /* Generic IPC error */
60 IMX_SC_ERR_BUSY = 10, /* Resource is currently busy/active */
61 IMX_SC_ERR_FAIL = 11, /* General I/O failure */
62 IMX_SC_ERR_LAST
63};
64
65static int imx_sc_linux_errmap[IMX_SC_ERR_LAST] = {
66 0, /* IMX_SC_ERR_NONE */
67 -EINVAL, /* IMX_SC_ERR_VERSION */
68 -EINVAL, /* IMX_SC_ERR_CONFIG */
69 -EINVAL, /* IMX_SC_ERR_PARM */
70 -EACCES, /* IMX_SC_ERR_NOACCESS */
71 -EACCES, /* IMX_SC_ERR_LOCKED */
72 -ERANGE, /* IMX_SC_ERR_UNAVAILABLE */
73 -EEXIST, /* IMX_SC_ERR_NOTFOUND */
74 -EPERM, /* IMX_SC_ERR_NOPOWER */
75 -EPIPE, /* IMX_SC_ERR_IPC */
76 -EBUSY, /* IMX_SC_ERR_BUSY */
77 -EIO, /* IMX_SC_ERR_FAIL */
78};
79
80static struct imx_sc_ipc *imx_sc_ipc_handle;
81
82static inline int imx_sc_to_linux_errno(int errno)
83{
84 if (errno >= IMX_SC_ERR_NONE && errno < IMX_SC_ERR_LAST)
85 return imx_sc_linux_errmap[errno];
86 return -EIO;
87}
88
89/*
90 * Get the default handle used by SCU
91 */
92int imx_scu_get_handle(struct imx_sc_ipc **ipc)
93{
94 if (!imx_sc_ipc_handle)
95 return -EPROBE_DEFER;
96
97 *ipc = imx_sc_ipc_handle;
98 return 0;
99}
100EXPORT_SYMBOL(imx_scu_get_handle);
101
102static void imx_scu_rx_callback(struct mbox_client *c, void *msg)
103{
104 struct imx_sc_chan *sc_chan = container_of(c, struct imx_sc_chan, cl);
105 struct imx_sc_ipc *sc_ipc = sc_chan->sc_ipc;
106 struct imx_sc_rpc_msg *hdr;
107 u32 *data = msg;
108
109 if (sc_chan->idx == 0) {
110 hdr = msg;
111 sc_ipc->rx_size = hdr->size;
112 dev_dbg(sc_ipc->dev, "msg rx size %u\n", sc_ipc->rx_size);
113 if (sc_ipc->rx_size > 4)
114 dev_warn(sc_ipc->dev, "RPC does not support receiving over 4 words: %u\n",
115 sc_ipc->rx_size);
116 }
117
118 sc_ipc->msg[sc_chan->idx] = *data;
119 sc_ipc->count++;
120
121 dev_dbg(sc_ipc->dev, "mu %u msg %u 0x%x\n", sc_chan->idx,
122 sc_ipc->count, *data);
123
124 if ((sc_ipc->rx_size != 0) && (sc_ipc->count == sc_ipc->rx_size))
125 complete(&sc_ipc->done);
126}
127
128static int imx_scu_ipc_write(struct imx_sc_ipc *sc_ipc, void *msg)
129{
130 struct imx_sc_rpc_msg *hdr = msg;
131 struct imx_sc_chan *sc_chan;
132 u32 *data = msg;
133 int ret;
134 int i;
135
136 /* Check size */
137 if (hdr->size > IMX_SC_RPC_MAX_MSG)
138 return -EINVAL;
139
140 dev_dbg(sc_ipc->dev, "RPC SVC %u FUNC %u SIZE %u\n", hdr->svc,
141 hdr->func, hdr->size);
142
143 for (i = 0; i < hdr->size; i++) {
144 sc_chan = &sc_ipc->chans[i % 4];
145 ret = mbox_send_message(sc_chan->ch, &data[i]);
146 if (ret < 0)
147 return ret;
148 }
149
150 return 0;
151}
152
153/*
154 * RPC command/response
155 */
156int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp)
157{
158 struct imx_sc_rpc_msg *hdr;
159 int ret;
160
161 if (WARN_ON(!sc_ipc || !msg))
162 return -EINVAL;
163
164 mutex_lock(&sc_ipc->lock);
165 reinit_completion(&sc_ipc->done);
166
167 sc_ipc->msg = msg;
168 sc_ipc->count = 0;
169 ret = imx_scu_ipc_write(sc_ipc, msg);
170 if (ret < 0) {
171 dev_err(sc_ipc->dev, "RPC send msg failed: %d\n", ret);
172 goto out;
173 }
174
175 if (have_resp) {
176 if (!wait_for_completion_timeout(&sc_ipc->done,
177 MAX_RX_TIMEOUT)) {
178 dev_err(sc_ipc->dev, "RPC send msg timeout\n");
179 mutex_unlock(&sc_ipc->lock);
180 return -ETIMEDOUT;
181 }
182
183 /* response status is stored in hdr->func field */
184 hdr = msg;
185 ret = hdr->func;
186 }
187
188out:
189 mutex_unlock(&sc_ipc->lock);
190
191 dev_dbg(sc_ipc->dev, "RPC SVC done\n");
192
193 return imx_sc_to_linux_errno(ret);
194}
195EXPORT_SYMBOL(imx_scu_call_rpc);
196
197static int imx_scu_probe(struct platform_device *pdev)
198{
199 struct device *dev = &pdev->dev;
200 struct imx_sc_ipc *sc_ipc;
201 struct imx_sc_chan *sc_chan;
202 struct mbox_client *cl;
203 char *chan_name;
204 int ret;
205 int i;
206
207 sc_ipc = devm_kzalloc(dev, sizeof(*sc_ipc), GFP_KERNEL);
208 if (!sc_ipc)
209 return -ENOMEM;
210
211 for (i = 0; i < SCU_MU_CHAN_NUM; i++) {
212 if (i < 4)
213 chan_name = kasprintf(GFP_KERNEL, "tx%d", i);
214 else
215 chan_name = kasprintf(GFP_KERNEL, "rx%d", i - 4);
216
217 if (!chan_name)
218 return -ENOMEM;
219
220 sc_chan = &sc_ipc->chans[i];
221 cl = &sc_chan->cl;
222 cl->dev = dev;
223 cl->tx_block = false;
224 cl->knows_txdone = true;
225 cl->rx_callback = imx_scu_rx_callback;
226
227 sc_chan->sc_ipc = sc_ipc;
228 sc_chan->idx = i % 4;
229 sc_chan->ch = mbox_request_channel_byname(cl, chan_name);
230 if (IS_ERR(sc_chan->ch)) {
231 ret = PTR_ERR(sc_chan->ch);
232 if (ret != -EPROBE_DEFER)
233 dev_err(dev, "Failed to request mbox chan %s ret %d\n",
234 chan_name, ret);
235 return ret;
236 }
237
238 dev_dbg(dev, "request mbox chan %s\n", chan_name);
239 /* chan_name is not used anymore by framework */
240 kfree(chan_name);
241 }
242
243 sc_ipc->dev = dev;
244 mutex_init(&sc_ipc->lock);
245 init_completion(&sc_ipc->done);
246
247 imx_sc_ipc_handle = sc_ipc;
248
249 dev_info(dev, "NXP i.MX SCU Initialized\n");
250
251 return devm_of_platform_populate(dev);
252}
253
254static const struct of_device_id imx_scu_match[] = {
255 { .compatible = "fsl,imx-scu", },
256 { /* Sentinel */ }
257};
258
259static struct platform_driver imx_scu_driver = {
260 .driver = {
261 .name = "imx-scu",
262 .of_match_table = imx_scu_match,
263 },
264 .probe = imx_scu_probe,
265};
266builtin_platform_driver(imx_scu_driver);
267
268MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>");
269MODULE_DESCRIPTION("IMX SCU firmware protocol driver");
270MODULE_LICENSE("GPL v2");
diff --git a/drivers/firmware/imx/misc.c b/drivers/firmware/imx/misc.c
new file mode 100644
index 000000000000..97f5424dbac9
--- /dev/null
+++ b/drivers/firmware/imx/misc.c
@@ -0,0 +1,99 @@
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2016 Freescale Semiconductor, Inc.
4 * Copyright 2017~2018 NXP
5 * Author: Dong Aisheng <aisheng.dong@nxp.com>
6 *
7 * File containing client-side RPC functions for the MISC service. These
8 * function are ported to clients that communicate to the SC.
9 *
10 */
11
12#include <linux/firmware/imx/svc/misc.h>
13
14struct imx_sc_msg_req_misc_set_ctrl {
15 struct imx_sc_rpc_msg hdr;
16 u32 ctrl;
17 u32 val;
18 u16 resource;
19} __packed;
20
21struct imx_sc_msg_req_misc_get_ctrl {
22 struct imx_sc_rpc_msg hdr;
23 u32 ctrl;
24 u16 resource;
25} __packed;
26
27struct imx_sc_msg_resp_misc_get_ctrl {
28 struct imx_sc_rpc_msg hdr;
29 u32 val;
30} __packed;
31
32/*
33 * This function sets a miscellaneous control value.
34 *
35 * @param[in] ipc IPC handle
36 * @param[in] resource resource the control is associated with
37 * @param[in] ctrl control to change
38 * @param[in] val value to apply to the control
39 *
40 * @return Returns 0 for success and < 0 for errors.
41 */
42
43int imx_sc_misc_set_control(struct imx_sc_ipc *ipc, u32 resource,
44 u8 ctrl, u32 val)
45{
46 struct imx_sc_msg_req_misc_set_ctrl msg;
47 struct imx_sc_rpc_msg *hdr = &msg.hdr;
48
49 hdr->ver = IMX_SC_RPC_VERSION;
50 hdr->svc = (uint8_t)IMX_SC_RPC_SVC_MISC;
51 hdr->func = (uint8_t)IMX_SC_MISC_FUNC_SET_CONTROL;
52 hdr->size = 4;
53
54 msg.ctrl = ctrl;
55 msg.val = val;
56 msg.resource = resource;
57
58 return imx_scu_call_rpc(ipc, &msg, true);
59}
60EXPORT_SYMBOL(imx_sc_misc_set_control);
61
62/*
63 * This function gets a miscellaneous control value.
64 *
65 * @param[in] ipc IPC handle
66 * @param[in] resource resource the control is associated with
67 * @param[in] ctrl control to get
68 * @param[out] val pointer to return the control value
69 *
70 * @return Returns 0 for success and < 0 for errors.
71 */
72
73int imx_sc_misc_get_control(struct imx_sc_ipc *ipc, u32 resource,
74 u8 ctrl, u32 *val)
75{
76 struct imx_sc_msg_req_misc_get_ctrl msg;
77 struct imx_sc_msg_resp_misc_get_ctrl *resp;
78 struct imx_sc_rpc_msg *hdr = &msg.hdr;
79 int ret;
80
81 hdr->ver = IMX_SC_RPC_VERSION;
82 hdr->svc = (uint8_t)IMX_SC_RPC_SVC_MISC;
83 hdr->func = (uint8_t)IMX_SC_MISC_FUNC_GET_CONTROL;
84 hdr->size = 3;
85
86 msg.ctrl = ctrl;
87 msg.resource = resource;
88
89 ret = imx_scu_call_rpc(ipc, &msg, true);
90 if (ret)
91 return ret;
92
93 resp = (struct imx_sc_msg_resp_misc_get_ctrl *)&msg;
94 if (val != NULL)
95 *val = resp->val;
96
97 return 0;
98}
99EXPORT_SYMBOL(imx_sc_misc_get_control);
diff --git a/drivers/firmware/iscsi_ibft_find.c b/drivers/firmware/iscsi_ibft_find.c
index 2224f1dc074b..72d9ea18270b 100644
--- a/drivers/firmware/iscsi_ibft_find.c
+++ b/drivers/firmware/iscsi_ibft_find.c
@@ -18,7 +18,7 @@
18 * GNU General Public License for more details. 18 * GNU General Public License for more details.
19 */ 19 */
20 20
21#include <linux/bootmem.h> 21#include <linux/memblock.h>
22#include <linux/blkdev.h> 22#include <linux/blkdev.h>
23#include <linux/ctype.h> 23#include <linux/ctype.h>
24#include <linux/device.h> 24#include <linux/device.h>
diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c
index 5de3ed29282c..d168c87c7d30 100644
--- a/drivers/firmware/memmap.c
+++ b/drivers/firmware/memmap.c
@@ -19,7 +19,7 @@
19#include <linux/kernel.h> 19#include <linux/kernel.h>
20#include <linux/module.h> 20#include <linux/module.h>
21#include <linux/types.h> 21#include <linux/types.h>
22#include <linux/bootmem.h> 22#include <linux/memblock.h>
23#include <linux/slab.h> 23#include <linux/slab.h>
24#include <linux/mm.h> 24#include <linux/mm.h>
25 25
@@ -333,7 +333,8 @@ int __init firmware_map_add_early(u64 start, u64 end, const char *type)
333{ 333{
334 struct firmware_map_entry *entry; 334 struct firmware_map_entry *entry;
335 335
336 entry = memblock_virt_alloc(sizeof(struct firmware_map_entry), 0); 336 entry = memblock_alloc(sizeof(struct firmware_map_entry),
337 SMP_CACHE_BYTES);
337 if (WARN_ON(!entry)) 338 if (WARN_ON(!entry))
338 return -ENOMEM; 339 return -ENOMEM;
339 340
diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c
index 0ec2ca87318c..29fbc818a573 100644
--- a/drivers/firmware/meson/meson_sm.c
+++ b/drivers/firmware/meson/meson_sm.c
@@ -24,6 +24,7 @@
24#include <linux/printk.h> 24#include <linux/printk.h>
25#include <linux/types.h> 25#include <linux/types.h>
26#include <linux/sizes.h> 26#include <linux/sizes.h>
27 #include <linux/slab.h>
27 28
28#include <linux/firmware/meson/meson_sm.h> 29#include <linux/firmware/meson/meson_sm.h>
29 30
@@ -48,6 +49,7 @@ struct meson_sm_chip gxbb_chip = {
48 CMD(SM_EFUSE_READ, 0x82000030), 49 CMD(SM_EFUSE_READ, 0x82000030),
49 CMD(SM_EFUSE_WRITE, 0x82000031), 50 CMD(SM_EFUSE_WRITE, 0x82000031),
50 CMD(SM_EFUSE_USER_MAX, 0x82000033), 51 CMD(SM_EFUSE_USER_MAX, 0x82000033),
52 CMD(SM_GET_CHIP_ID, 0x82000044),
51 { /* sentinel */ }, 53 { /* sentinel */ },
52 }, 54 },
53}; 55};
@@ -214,6 +216,57 @@ int meson_sm_call_write(void *buffer, unsigned int size, unsigned int cmd_index,
214} 216}
215EXPORT_SYMBOL(meson_sm_call_write); 217EXPORT_SYMBOL(meson_sm_call_write);
216 218
219#define SM_CHIP_ID_LENGTH 119
220#define SM_CHIP_ID_OFFSET 4
221#define SM_CHIP_ID_SIZE 12
222
223static ssize_t serial_show(struct device *dev, struct device_attribute *attr,
224 char *buf)
225{
226 uint8_t *id_buf;
227 int ret;
228
229 id_buf = kmalloc(SM_CHIP_ID_LENGTH, GFP_KERNEL);
230 if (!id_buf)
231 return -ENOMEM;
232
233 ret = meson_sm_call_read(id_buf, SM_CHIP_ID_LENGTH, SM_GET_CHIP_ID,
234 0, 0, 0, 0, 0);
235 if (ret < 0) {
236 kfree(id_buf);
237 return ret;
238 }
239
240 ret = sprintf(buf, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
241 id_buf[SM_CHIP_ID_OFFSET + 0],
242 id_buf[SM_CHIP_ID_OFFSET + 1],
243 id_buf[SM_CHIP_ID_OFFSET + 2],
244 id_buf[SM_CHIP_ID_OFFSET + 3],
245 id_buf[SM_CHIP_ID_OFFSET + 4],
246 id_buf[SM_CHIP_ID_OFFSET + 5],
247 id_buf[SM_CHIP_ID_OFFSET + 6],
248 id_buf[SM_CHIP_ID_OFFSET + 7],
249 id_buf[SM_CHIP_ID_OFFSET + 8],
250 id_buf[SM_CHIP_ID_OFFSET + 9],
251 id_buf[SM_CHIP_ID_OFFSET + 10],
252 id_buf[SM_CHIP_ID_OFFSET + 11]);
253
254 kfree(id_buf);
255
256 return ret;
257}
258
259static DEVICE_ATTR_RO(serial);
260
261static struct attribute *meson_sm_sysfs_attributes[] = {
262 &dev_attr_serial.attr,
263 NULL,
264};
265
266static const struct attribute_group meson_sm_sysfs_attr_group = {
267 .attrs = meson_sm_sysfs_attributes,
268};
269
217static const struct of_device_id meson_sm_ids[] = { 270static const struct of_device_id meson_sm_ids[] = {
218 { .compatible = "amlogic,meson-gxbb-sm", .data = &gxbb_chip }, 271 { .compatible = "amlogic,meson-gxbb-sm", .data = &gxbb_chip },
219 { /* sentinel */ }, 272 { /* sentinel */ },
@@ -242,6 +295,9 @@ static int __init meson_sm_probe(struct platform_device *pdev)
242 fw.chip = chip; 295 fw.chip = chip;
243 pr_info("secure-monitor enabled\n"); 296 pr_info("secure-monitor enabled\n");
244 297
298 if (sysfs_create_group(&pdev->dev.kobj, &meson_sm_sysfs_attr_group))
299 goto out_in_base;
300
245 return 0; 301 return 0;
246 302
247out_in_base: 303out_in_base:
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c
index e778af766fae..af4eee86919d 100644
--- a/drivers/firmware/qcom_scm.c
+++ b/drivers/firmware/qcom_scm.c
@@ -525,34 +525,44 @@ static int qcom_scm_probe(struct platform_device *pdev)
525 return ret; 525 return ret;
526 526
527 clks = (unsigned long)of_device_get_match_data(&pdev->dev); 527 clks = (unsigned long)of_device_get_match_data(&pdev->dev);
528 if (clks & SCM_HAS_CORE_CLK) { 528
529 scm->core_clk = devm_clk_get(&pdev->dev, "core"); 529 scm->core_clk = devm_clk_get(&pdev->dev, "core");
530 if (IS_ERR(scm->core_clk)) { 530 if (IS_ERR(scm->core_clk)) {
531 if (PTR_ERR(scm->core_clk) != -EPROBE_DEFER) 531 if (PTR_ERR(scm->core_clk) == -EPROBE_DEFER)
532 dev_err(&pdev->dev, 532 return PTR_ERR(scm->core_clk);
533 "failed to acquire core clk\n"); 533
534 if (clks & SCM_HAS_CORE_CLK) {
535 dev_err(&pdev->dev, "failed to acquire core clk\n");
534 return PTR_ERR(scm->core_clk); 536 return PTR_ERR(scm->core_clk);
535 } 537 }
538
539 scm->core_clk = NULL;
536 } 540 }
537 541
538 if (clks & SCM_HAS_IFACE_CLK) { 542 scm->iface_clk = devm_clk_get(&pdev->dev, "iface");
539 scm->iface_clk = devm_clk_get(&pdev->dev, "iface"); 543 if (IS_ERR(scm->iface_clk)) {
540 if (IS_ERR(scm->iface_clk)) { 544 if (PTR_ERR(scm->iface_clk) == -EPROBE_DEFER)
541 if (PTR_ERR(scm->iface_clk) != -EPROBE_DEFER) 545 return PTR_ERR(scm->iface_clk);
542 dev_err(&pdev->dev, 546
543 "failed to acquire iface clk\n"); 547 if (clks & SCM_HAS_IFACE_CLK) {
548 dev_err(&pdev->dev, "failed to acquire iface clk\n");
544 return PTR_ERR(scm->iface_clk); 549 return PTR_ERR(scm->iface_clk);
545 } 550 }
551
552 scm->iface_clk = NULL;
546 } 553 }
547 554
548 if (clks & SCM_HAS_BUS_CLK) { 555 scm->bus_clk = devm_clk_get(&pdev->dev, "bus");
549 scm->bus_clk = devm_clk_get(&pdev->dev, "bus"); 556 if (IS_ERR(scm->bus_clk)) {
550 if (IS_ERR(scm->bus_clk)) { 557 if (PTR_ERR(scm->bus_clk) == -EPROBE_DEFER)
551 if (PTR_ERR(scm->bus_clk) != -EPROBE_DEFER) 558 return PTR_ERR(scm->bus_clk);
552 dev_err(&pdev->dev, 559
553 "failed to acquire bus clk\n"); 560 if (clks & SCM_HAS_BUS_CLK) {
561 dev_err(&pdev->dev, "failed to acquire bus clk\n");
554 return PTR_ERR(scm->bus_clk); 562 return PTR_ERR(scm->bus_clk);
555 } 563 }
564
565 scm->bus_clk = NULL;
556 } 566 }
557 567
558 scm->reset.ops = &qcom_scm_pas_reset_ops; 568 scm->reset.ops = &qcom_scm_pas_reset_ops;
@@ -594,23 +604,23 @@ static const struct of_device_id qcom_scm_dt_match[] = {
594 { .compatible = "qcom,scm-apq8064", 604 { .compatible = "qcom,scm-apq8064",
595 /* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */ 605 /* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */
596 }, 606 },
597 { .compatible = "qcom,scm-msm8660", 607 { .compatible = "qcom,scm-apq8084", .data = (void *)(SCM_HAS_CORE_CLK |
598 .data = (void *) SCM_HAS_CORE_CLK, 608 SCM_HAS_IFACE_CLK |
599 }, 609 SCM_HAS_BUS_CLK)
600 { .compatible = "qcom,scm-msm8960",
601 .data = (void *) SCM_HAS_CORE_CLK,
602 },
603 { .compatible = "qcom,scm-msm8996",
604 .data = NULL, /* no clocks */
605 }, 610 },
606 { .compatible = "qcom,scm-ipq4019", 611 { .compatible = "qcom,scm-ipq4019" },
607 .data = NULL, /* no clocks */ 612 { .compatible = "qcom,scm-msm8660", .data = (void *) SCM_HAS_CORE_CLK },
613 { .compatible = "qcom,scm-msm8960", .data = (void *) SCM_HAS_CORE_CLK },
614 { .compatible = "qcom,scm-msm8916", .data = (void *)(SCM_HAS_CORE_CLK |
615 SCM_HAS_IFACE_CLK |
616 SCM_HAS_BUS_CLK)
608 }, 617 },
609 { .compatible = "qcom,scm", 618 { .compatible = "qcom,scm-msm8974", .data = (void *)(SCM_HAS_CORE_CLK |
610 .data = (void *)(SCM_HAS_CORE_CLK 619 SCM_HAS_IFACE_CLK |
611 | SCM_HAS_IFACE_CLK 620 SCM_HAS_BUS_CLK)
612 | SCM_HAS_BUS_CLK),
613 }, 621 },
622 { .compatible = "qcom,scm-msm8996" },
623 { .compatible = "qcom,scm" },
614 {} 624 {}
615}; 625};
616 626
diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c
index 14a456afa379..a3d5b518c10e 100644
--- a/drivers/firmware/tegra/bpmp.c
+++ b/drivers/firmware/tegra/bpmp.c
@@ -18,6 +18,7 @@
18#include <linux/of_address.h> 18#include <linux/of_address.h>
19#include <linux/of_device.h> 19#include <linux/of_device.h>
20#include <linux/platform_device.h> 20#include <linux/platform_device.h>
21#include <linux/pm.h>
21#include <linux/semaphore.h> 22#include <linux/semaphore.h>
22#include <linux/sched/clock.h> 23#include <linux/sched/clock.h>
23 24
@@ -843,6 +844,23 @@ free_tx:
843 return err; 844 return err;
844} 845}
845 846
847static int __maybe_unused tegra_bpmp_resume(struct device *dev)
848{
849 struct tegra_bpmp *bpmp = dev_get_drvdata(dev);
850 unsigned int i;
851
852 /* reset message channels */
853 tegra_bpmp_channel_reset(bpmp->tx_channel);
854 tegra_bpmp_channel_reset(bpmp->rx_channel);
855
856 for (i = 0; i < bpmp->threaded.count; i++)
857 tegra_bpmp_channel_reset(&bpmp->threaded_channels[i]);
858
859 return 0;
860}
861
862static SIMPLE_DEV_PM_OPS(tegra_bpmp_pm_ops, NULL, tegra_bpmp_resume);
863
846static const struct tegra_bpmp_soc tegra186_soc = { 864static const struct tegra_bpmp_soc tegra186_soc = {
847 .channels = { 865 .channels = {
848 .cpu_tx = { 866 .cpu_tx = {
@@ -871,6 +889,7 @@ static struct platform_driver tegra_bpmp_driver = {
871 .driver = { 889 .driver = {
872 .name = "tegra-bpmp", 890 .name = "tegra-bpmp",
873 .of_match_table = tegra_bpmp_match, 891 .of_match_table = tegra_bpmp_match,
892 .pm = &tegra_bpmp_pm_ops,
874 }, 893 },
875 .probe = tegra_bpmp_probe, 894 .probe = tegra_bpmp_probe,
876}; 895};
diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c
index 7fa744793bc5..69ed1464175c 100644
--- a/drivers/firmware/ti_sci.c
+++ b/drivers/firmware/ti_sci.c
@@ -66,14 +66,14 @@ struct ti_sci_xfers_info {
66 66
67/** 67/**
68 * struct ti_sci_desc - Description of SoC integration 68 * struct ti_sci_desc - Description of SoC integration
69 * @host_id: Host identifier representing the compute entity 69 * @default_host_id: Host identifier representing the compute entity
70 * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds) 70 * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds)
71 * @max_msgs: Maximum number of messages that can be pending 71 * @max_msgs: Maximum number of messages that can be pending
72 * simultaneously in the system 72 * simultaneously in the system
73 * @max_msg_size: Maximum size of data per message that can be handled. 73 * @max_msg_size: Maximum size of data per message that can be handled.
74 */ 74 */
75struct ti_sci_desc { 75struct ti_sci_desc {
76 u8 host_id; 76 u8 default_host_id;
77 int max_rx_timeout_ms; 77 int max_rx_timeout_ms;
78 int max_msgs; 78 int max_msgs;
79 int max_msg_size; 79 int max_msg_size;
@@ -94,6 +94,7 @@ struct ti_sci_desc {
94 * @chan_rx: Receive mailbox channel 94 * @chan_rx: Receive mailbox channel
95 * @minfo: Message info 95 * @minfo: Message info
96 * @node: list head 96 * @node: list head
97 * @host_id: Host ID
97 * @users: Number of users of this instance 98 * @users: Number of users of this instance
98 */ 99 */
99struct ti_sci_info { 100struct ti_sci_info {
@@ -110,6 +111,7 @@ struct ti_sci_info {
110 struct mbox_chan *chan_rx; 111 struct mbox_chan *chan_rx;
111 struct ti_sci_xfers_info minfo; 112 struct ti_sci_xfers_info minfo;
112 struct list_head node; 113 struct list_head node;
114 u8 host_id;
113 /* protected by ti_sci_list_mutex */ 115 /* protected by ti_sci_list_mutex */
114 int users; 116 int users;
115 117
@@ -370,7 +372,7 @@ static struct ti_sci_xfer *ti_sci_get_one_xfer(struct ti_sci_info *info,
370 372
371 hdr->seq = xfer_id; 373 hdr->seq = xfer_id;
372 hdr->type = msg_type; 374 hdr->type = msg_type;
373 hdr->host = info->desc->host_id; 375 hdr->host = info->host_id;
374 hdr->flags = msg_flags; 376 hdr->flags = msg_flags;
375 377
376 return xfer; 378 return xfer;
@@ -1793,7 +1795,7 @@ static int tisci_reboot_handler(struct notifier_block *nb, unsigned long mode,
1793 1795
1794/* Description for K2G */ 1796/* Description for K2G */
1795static const struct ti_sci_desc ti_sci_pmmc_k2g_desc = { 1797static const struct ti_sci_desc ti_sci_pmmc_k2g_desc = {
1796 .host_id = 2, 1798 .default_host_id = 2,
1797 /* Conservative duration */ 1799 /* Conservative duration */
1798 .max_rx_timeout_ms = 1000, 1800 .max_rx_timeout_ms = 1000,
1799 /* Limited by MBOX_TX_QUEUE_LEN. K2G can handle upto 128 messages! */ 1801 /* Limited by MBOX_TX_QUEUE_LEN. K2G can handle upto 128 messages! */
@@ -1819,6 +1821,7 @@ static int ti_sci_probe(struct platform_device *pdev)
1819 int ret = -EINVAL; 1821 int ret = -EINVAL;
1820 int i; 1822 int i;
1821 int reboot = 0; 1823 int reboot = 0;
1824 u32 h_id;
1822 1825
1823 of_id = of_match_device(ti_sci_of_match, dev); 1826 of_id = of_match_device(ti_sci_of_match, dev);
1824 if (!of_id) { 1827 if (!of_id) {
@@ -1833,6 +1836,19 @@ static int ti_sci_probe(struct platform_device *pdev)
1833 1836
1834 info->dev = dev; 1837 info->dev = dev;
1835 info->desc = desc; 1838 info->desc = desc;
1839 ret = of_property_read_u32(dev->of_node, "ti,host-id", &h_id);
1840 /* if the property is not present in DT, use a default from desc */
1841 if (ret < 0) {
1842 info->host_id = info->desc->default_host_id;
1843 } else {
1844 if (!h_id) {
1845 dev_warn(dev, "Host ID 0 is reserved for firmware\n");
1846 info->host_id = info->desc->default_host_id;
1847 } else {
1848 info->host_id = h_id;
1849 }
1850 }
1851
1836 reboot = of_property_read_bool(dev->of_node, 1852 reboot = of_property_read_bool(dev->of_node,
1837 "ti,system-reboot-controller"); 1853 "ti,system-reboot-controller");
1838 INIT_LIST_HEAD(&info->node); 1854 INIT_LIST_HEAD(&info->node);
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..9a1c72a9280f
--- /dev/null
+++ b/drivers/firmware/xilinx/zynqmp.c
@@ -0,0 +1,565 @@
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
431/**
432 * zynqmp_is_valid_ioctl() - Check whether IOCTL ID is valid or not
433 * @ioctl_id: IOCTL ID
434 *
435 * Return: 1 if IOCTL is valid else 0
436 */
437static inline int zynqmp_is_valid_ioctl(u32 ioctl_id)
438{
439 switch (ioctl_id) {
440 case IOCTL_SET_PLL_FRAC_MODE:
441 case IOCTL_GET_PLL_FRAC_MODE:
442 case IOCTL_SET_PLL_FRAC_DATA:
443 case IOCTL_GET_PLL_FRAC_DATA:
444 return 1;
445 default:
446 return 0;
447 }
448}
449
450/**
451 * zynqmp_pm_ioctl() - PM IOCTL API for device control and configs
452 * @node_id: Node ID of the device
453 * @ioctl_id: ID of the requested IOCTL
454 * @arg1: Argument 1 to requested IOCTL call
455 * @arg2: Argument 2 to requested IOCTL call
456 * @out: Returned output value
457 *
458 * This function calls IOCTL to firmware for device control and configuration.
459 *
460 * Return: Returns status, either success or error+reason
461 */
462static int zynqmp_pm_ioctl(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2,
463 u32 *out)
464{
465 if (!zynqmp_is_valid_ioctl(ioctl_id))
466 return -EINVAL;
467
468 return zynqmp_pm_invoke_fn(PM_IOCTL, node_id, ioctl_id,
469 arg1, arg2, out);
470}
471
472static const struct zynqmp_eemi_ops eemi_ops = {
473 .get_api_version = zynqmp_pm_get_api_version,
474 .query_data = zynqmp_pm_query_data,
475 .clock_enable = zynqmp_pm_clock_enable,
476 .clock_disable = zynqmp_pm_clock_disable,
477 .clock_getstate = zynqmp_pm_clock_getstate,
478 .clock_setdivider = zynqmp_pm_clock_setdivider,
479 .clock_getdivider = zynqmp_pm_clock_getdivider,
480 .clock_setrate = zynqmp_pm_clock_setrate,
481 .clock_getrate = zynqmp_pm_clock_getrate,
482 .clock_setparent = zynqmp_pm_clock_setparent,
483 .clock_getparent = zynqmp_pm_clock_getparent,
484 .ioctl = zynqmp_pm_ioctl,
485};
486
487/**
488 * zynqmp_pm_get_eemi_ops - Get eemi ops functions
489 *
490 * Return: Pointer of eemi_ops structure
491 */
492const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void)
493{
494 return &eemi_ops;
495}
496EXPORT_SYMBOL_GPL(zynqmp_pm_get_eemi_ops);
497
498static int zynqmp_firmware_probe(struct platform_device *pdev)
499{
500 struct device *dev = &pdev->dev;
501 struct device_node *np;
502 int ret;
503
504 np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp");
505 if (!np)
506 return 0;
507 of_node_put(np);
508
509 ret = get_set_conduit_method(dev->of_node);
510 if (ret)
511 return ret;
512
513 /* Check PM API version number */
514 zynqmp_pm_get_api_version(&pm_api_version);
515 if (pm_api_version < ZYNQMP_PM_VERSION) {
516 panic("%s Platform Management API version error. Expected: v%d.%d - Found: v%d.%d\n",
517 __func__,
518 ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR,
519 pm_api_version >> 16, pm_api_version & 0xFFFF);
520 }
521
522 pr_info("%s Platform Management API v%d.%d\n", __func__,
523 pm_api_version >> 16, pm_api_version & 0xFFFF);
524
525 /* Check trustzone version number */
526 ret = zynqmp_pm_get_trustzone_version(&pm_tz_version);
527 if (ret)
528 panic("Legacy trustzone found without version support\n");
529
530 if (pm_tz_version < ZYNQMP_TZ_VERSION)
531 panic("%s Trustzone version error. Expected: v%d.%d - Found: v%d.%d\n",
532 __func__,
533 ZYNQMP_TZ_VERSION_MAJOR, ZYNQMP_TZ_VERSION_MINOR,
534 pm_tz_version >> 16, pm_tz_version & 0xFFFF);
535
536 pr_info("%s Trustzone version v%d.%d\n", __func__,
537 pm_tz_version >> 16, pm_tz_version & 0xFFFF);
538
539 zynqmp_pm_api_debugfs_init();
540
541 return of_platform_populate(dev->of_node, NULL, NULL, dev);
542}
543
544static int zynqmp_firmware_remove(struct platform_device *pdev)
545{
546 zynqmp_pm_api_debugfs_exit();
547
548 return 0;
549}
550
551static const struct of_device_id zynqmp_firmware_of_match[] = {
552 {.compatible = "xlnx,zynqmp-firmware"},
553 {},
554};
555MODULE_DEVICE_TABLE(of, zynqmp_firmware_of_match);
556
557static struct platform_driver zynqmp_firmware_driver = {
558 .driver = {
559 .name = "zynqmp_firmware",
560 .of_match_table = zynqmp_firmware_of_match,
561 },
562 .probe = zynqmp_firmware_probe,
563 .remove = zynqmp_firmware_remove,
564};
565module_platform_driver(zynqmp_firmware_driver);