1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2016, Linaro Ltd 4 */ 5 6 #include <linux/io.h> 7 #include <linux/module.h> 8 #include <linux/of.h> 9 #include <linux/of_address.h> 10 #include <linux/interrupt.h> 11 #include <linux/platform_device.h> 12 #include <linux/mfd/syscon.h> 13 #include <linux/slab.h> 14 #include <linux/rpmsg.h> 15 #include <linux/idr.h> 16 #include <linux/circ_buf.h> 17 #include <linux/soc/qcom/smem.h> 18 #include <linux/sizes.h> 19 #include <linux/delay.h> 20 #include <linux/regmap.h> 21 #include <linux/workqueue.h> 22 #include <linux/list.h> 23 24 #include <linux/rpmsg/qcom_glink.h> 25 26 #include "qcom_glink_native.h" 27 28 #define FIFO_FULL_RESERVE 8 29 #define FIFO_ALIGNMENT 8 30 #define TX_BLOCKED_CMD_RESERVE 8 /* size of struct read_notif_request */ 31 32 #define SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR 478 33 #define SMEM_GLINK_NATIVE_XPRT_FIFO_0 479 34 #define SMEM_GLINK_NATIVE_XPRT_FIFO_1 480 35 36 struct qcom_glink_smem { 37 struct device dev; 38 39 struct qcom_glink *glink; 40 41 u32 remote_pid; 42 }; 43 44 struct glink_smem_pipe { 45 struct qcom_glink_pipe native; 46 47 __le32 *tail; 48 __le32 *head; 49 50 void *fifo; 51 52 struct qcom_glink_smem *smem; 53 }; 54 55 #define to_smem_pipe(p) container_of(p, struct glink_smem_pipe, native) 56 57 static size_t glink_smem_rx_avail(struct qcom_glink_pipe *np) 58 { 59 struct glink_smem_pipe *pipe = to_smem_pipe(np); 60 struct qcom_glink_smem *smem = pipe->smem; 61 size_t len; 62 void *fifo; 63 u32 head; 64 u32 tail; 65 66 if (!pipe->fifo) { 67 fifo = qcom_smem_get(smem->remote_pid, 68 SMEM_GLINK_NATIVE_XPRT_FIFO_1, &len); 69 if (IS_ERR(fifo)) { 70 pr_err("failed to acquire RX fifo handle: %ld\n", 71 PTR_ERR(fifo)); 72 return 0; 73 } 74 75 pipe->fifo = fifo; 76 pipe->native.length = len; 77 } 78 79 head = le32_to_cpu(*pipe->head); 80 tail = le32_to_cpu(*pipe->tail); 81 82 if (head < tail) 83 return pipe->native.length - tail + head; 84 else 85 return head - tail; 86 } 87 88 static void glink_smem_rx_peak(struct qcom_glink_pipe *np, 89 void *data, unsigned int offset, size_t count) 90 { 91 struct glink_smem_pipe *pipe = to_smem_pipe(np); 92 size_t len; 93 u32 tail; 94 95 tail = le32_to_cpu(*pipe->tail); 96 tail += offset; 97 if (tail >= pipe->native.length) 98 tail -= pipe->native.length; 99 100 len = min_t(size_t, count, pipe->native.length - tail); 101 if (len) 102 memcpy_fromio(data, pipe->fifo + tail, len); 103 104 if (len != count) 105 memcpy_fromio(data + len, pipe->fifo, (count - len)); 106 } 107 108 static void glink_smem_rx_advance(struct qcom_glink_pipe *np, 109 size_t count) 110 { 111 struct glink_smem_pipe *pipe = to_smem_pipe(np); 112 u32 tail; 113 114 tail = le32_to_cpu(*pipe->tail); 115 116 tail += count; 117 if (tail >= pipe->native.length) 118 tail -= pipe->native.length; 119 120 *pipe->tail = cpu_to_le32(tail); 121 } 122 123 static size_t glink_smem_tx_avail(struct qcom_glink_pipe *np) 124 { 125 struct glink_smem_pipe *pipe = to_smem_pipe(np); 126 u32 head; 127 u32 tail; 128 u32 avail; 129 130 head = le32_to_cpu(*pipe->head); 131 tail = le32_to_cpu(*pipe->tail); 132 133 if (tail <= head) 134 avail = pipe->native.length - head + tail; 135 else 136 avail = tail - head; 137 138 if (avail < (FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE)) 139 avail = 0; 140 else 141 avail -= FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE; 142 143 return avail; 144 } 145 146 static unsigned int glink_smem_tx_write_one(struct glink_smem_pipe *pipe, 147 unsigned int head, 148 const void *data, size_t count) 149 { 150 size_t len; 151 152 len = min_t(size_t, count, pipe->native.length - head); 153 if (len) 154 memcpy(pipe->fifo + head, data, len); 155 156 if (len != count) 157 memcpy(pipe->fifo, data + len, count - len); 158 159 head += count; 160 if (head >= pipe->native.length) 161 head -= pipe->native.length; 162 163 return head; 164 } 165 166 static void glink_smem_tx_write(struct qcom_glink_pipe *glink_pipe, 167 const void *hdr, size_t hlen, 168 const void *data, size_t dlen) 169 { 170 struct glink_smem_pipe *pipe = to_smem_pipe(glink_pipe); 171 unsigned int head; 172 173 head = le32_to_cpu(*pipe->head); 174 175 head = glink_smem_tx_write_one(pipe, head, hdr, hlen); 176 head = glink_smem_tx_write_one(pipe, head, data, dlen); 177 178 /* Ensure head is always aligned to 8 bytes */ 179 head = ALIGN(head, 8); 180 if (head >= pipe->native.length) 181 head -= pipe->native.length; 182 183 /* Ensure ordering of fifo and head update */ 184 wmb(); 185 186 *pipe->head = cpu_to_le32(head); 187 } 188 189 static void qcom_glink_smem_release(struct device *dev) 190 { 191 struct qcom_glink_smem *smem = container_of(dev, struct qcom_glink_smem, dev); 192 193 kfree(smem); 194 } 195 196 struct qcom_glink_smem *qcom_glink_smem_register(struct device *parent, 197 struct device_node *node) 198 { 199 struct glink_smem_pipe *rx_pipe; 200 struct glink_smem_pipe *tx_pipe; 201 struct qcom_glink_smem *smem; 202 struct qcom_glink *glink; 203 struct device *dev; 204 u32 remote_pid; 205 __le32 *descs; 206 size_t size; 207 int ret; 208 209 smem = kzalloc(sizeof(*smem), GFP_KERNEL); 210 if (!smem) 211 return ERR_PTR(-ENOMEM); 212 213 dev = &smem->dev; 214 215 dev->parent = parent; 216 dev->of_node = node; 217 dev->release = qcom_glink_smem_release; 218 dev_set_name(dev, "%s:%pOFn", dev_name(parent->parent), node); 219 ret = device_register(dev); 220 if (ret) { 221 pr_err("failed to register glink edge\n"); 222 put_device(dev); 223 return ERR_PTR(ret); 224 } 225 226 ret = of_property_read_u32(dev->of_node, "qcom,remote-pid", 227 &remote_pid); 228 if (ret) { 229 dev_err(dev, "failed to parse qcom,remote-pid\n"); 230 goto err_put_dev; 231 } 232 233 smem->remote_pid = remote_pid; 234 235 rx_pipe = devm_kzalloc(dev, sizeof(*rx_pipe), GFP_KERNEL); 236 tx_pipe = devm_kzalloc(dev, sizeof(*tx_pipe), GFP_KERNEL); 237 if (!rx_pipe || !tx_pipe) { 238 ret = -ENOMEM; 239 goto err_put_dev; 240 } 241 242 ret = qcom_smem_alloc(remote_pid, 243 SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, 32); 244 if (ret && ret != -EEXIST) { 245 dev_err(dev, "failed to allocate glink descriptors\n"); 246 goto err_put_dev; 247 } 248 249 descs = qcom_smem_get(remote_pid, 250 SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, &size); 251 if (IS_ERR(descs)) { 252 dev_err(dev, "failed to acquire xprt descriptor\n"); 253 ret = PTR_ERR(descs); 254 goto err_put_dev; 255 } 256 257 if (size != 32) { 258 dev_err(dev, "glink descriptor of invalid size\n"); 259 ret = -EINVAL; 260 goto err_put_dev; 261 } 262 263 tx_pipe->tail = &descs[0]; 264 tx_pipe->head = &descs[1]; 265 rx_pipe->tail = &descs[2]; 266 rx_pipe->head = &descs[3]; 267 268 ret = qcom_smem_alloc(remote_pid, SMEM_GLINK_NATIVE_XPRT_FIFO_0, 269 SZ_16K); 270 if (ret && ret != -EEXIST) { 271 dev_err(dev, "failed to allocate TX fifo\n"); 272 goto err_put_dev; 273 } 274 275 tx_pipe->fifo = qcom_smem_get(remote_pid, SMEM_GLINK_NATIVE_XPRT_FIFO_0, 276 &tx_pipe->native.length); 277 if (IS_ERR(tx_pipe->fifo)) { 278 dev_err(dev, "failed to acquire TX fifo\n"); 279 ret = PTR_ERR(tx_pipe->fifo); 280 goto err_put_dev; 281 } 282 283 rx_pipe->smem = smem; 284 rx_pipe->native.avail = glink_smem_rx_avail; 285 rx_pipe->native.peak = glink_smem_rx_peak; 286 rx_pipe->native.advance = glink_smem_rx_advance; 287 288 tx_pipe->smem = smem; 289 tx_pipe->native.avail = glink_smem_tx_avail; 290 tx_pipe->native.write = glink_smem_tx_write; 291 292 *rx_pipe->tail = 0; 293 *tx_pipe->head = 0; 294 295 glink = qcom_glink_native_probe(dev, 296 GLINK_FEATURE_INTENT_REUSE, 297 &rx_pipe->native, &tx_pipe->native, 298 false); 299 if (IS_ERR(glink)) { 300 ret = PTR_ERR(glink); 301 goto err_put_dev; 302 } 303 304 smem->glink = glink; 305 306 return smem; 307 308 309 err_put_dev: 310 device_unregister(dev); 311 312 return ERR_PTR(ret); 313 } 314 EXPORT_SYMBOL_GPL(qcom_glink_smem_register); 315 316 void qcom_glink_smem_unregister(struct qcom_glink_smem *smem) 317 { 318 struct qcom_glink *glink = smem->glink; 319 320 qcom_glink_native_remove(glink); 321 qcom_glink_native_unregister(glink); 322 } 323 EXPORT_SYMBOL_GPL(qcom_glink_smem_unregister); 324 325 MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@linaro.org>"); 326 MODULE_DESCRIPTION("Qualcomm GLINK SMEM driver"); 327 MODULE_LICENSE("GPL v2"); 328