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