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