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