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 25 struct 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 33 struct 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 */ 49 enum 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 65 static 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 80 static struct imx_sc_ipc *imx_sc_ipc_handle; 81 82 static 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 */ 92 int 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 } 100 EXPORT_SYMBOL(imx_scu_get_handle); 101 102 static 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 128 static 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 */ 156 int 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 188 out: 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 } 195 EXPORT_SYMBOL(imx_scu_call_rpc); 196 197 static 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 254 static const struct of_device_id imx_scu_match[] = { 255 { .compatible = "fsl,imx-scu", }, 256 { /* Sentinel */ } 257 }; 258 259 static 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 }; 266 builtin_platform_driver(imx_scu_driver); 267 268 MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>"); 269 MODULE_DESCRIPTION("IMX SCU firmware protocol driver"); 270 MODULE_LICENSE("GPL v2"); 271