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