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