brintos

brintos / linux-shallow public Read only

0
0
Text · 8.6 KiB · 7a982c6 Raw
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