Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d064080d authored by Chris Lew's avatar Chris Lew
Browse files

rpmsg: Add snapshot of RPMSG Glink drivers



This snapshot is taken as of msm-4.14 'commit <67942dbf>
("Merge "net: netfilter: IRC DCC for private clients"")'.

This change brings the rpmsg and rpmsg glink drivers up to date with
the fixes from msm-4.14 and also adds the spi and spss transports from
msm-4.14. In addition, change the copyrights to SPDX format.

Change-Id: Idc0c9ad00c0562a7f3e2807ea9cfca644680dd9d
Signed-off-by: default avatarChris Lew <clew@codeaurora.org>
parent 4e1105e2
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -39,6 +39,28 @@ config RPMSG_QCOM_GLINK_SMEM
	  which provides support for using the GLINK communication protocol
	  over SMEM.

config RPMSG_QCOM_GLINK_SPSS
	tristate "QTI SPSS Glink driver"
	select RPMSG_QCOM_GLINK_NATIVE
	depends on MAILBOX
	depends on QCOM_SMEM
	select QSEE_IPC_IRQ
	help
	  Say y here to enable support for the GLINK SPSS communication driver,
	  which provides support for using the GLINK communication protocol
	  over SMEM. This protocol maps the smem and then shares the mapped
	  region with the remote proc by writing the smem descriptor location
	  and size into shared registers.

config RPMSG_QCOM_GLINK_SPI
	tristate "QTI SPI Glink driver"
	help
	  Say y here to enable support for the GLINK SPI communication driver,
	  which provides support for using the GLINK communication protocol
	  over SPI. This transport performs marshaling of GLINK commands and
	  data to the appropriate SPI bus wire format and allows for GLINK
	  communication with remote subsystems that are external to the SoC.

config RPMSG_QCOM_SMD
	tristate "Qualcomm Shared Memory Driver (SMD)"
	depends on MAILBOX
+2 −0
Original line number Diff line number Diff line
@@ -4,5 +4,7 @@ obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o
obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o
obj-$(CONFIG_RPMSG_QCOM_GLINK_NATIVE) += qcom_glink_native.o
obj-$(CONFIG_RPMSG_QCOM_GLINK_SMEM) += qcom_glink_smem.o
obj-$(CONFIG_RPMSG_QCOM_GLINK_SPSS) += qcom_glink_spss.o
obj-$(CONFIG_RPMSG_QCOM_GLINK_SPI) += qcom_glink_spi.o
obj-$(CONFIG_RPMSG_QCOM_SMD)	+= qcom_smd.o
obj-$(CONFIG_RPMSG_VIRTIO)	+= virtio_rpmsg_bus.o
+375 −47
Original line number Diff line number Diff line
@@ -18,11 +18,35 @@
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/kthread.h>
#include <linux/mailbox_client.h>
#include <linux/ipc_logging.h>

#include "rpmsg_internal.h"
#include "qcom_glink_native.h"

#define GLINK_LOG_PAGE_CNT 2
#define GLINK_INFO(ctxt, x, ...)					  \
do {									  \
	if (ctxt)							  \
		ipc_log_string(ctxt, "[%s]: "x, __func__, ##__VA_ARGS__); \
} while (0)

#define CH_INFO(ch, x, ...)						     \
do {									     \
	if (ch->glink && ch->glink->ilc)				     \
		ipc_log_string(ch->glink->ilc, "%s[%d:%d] %s: "x, ch->name,  \
			       ch->lcid, ch->rcid, __func__, ##__VA_ARGS__); \
} while (0)


#define GLINK_ERR(ctxt, x, ...)						  \
do {									  \
	pr_err_ratelimited("[%s]: "x, __func__, ##__VA_ARGS__);		  \
	if (ctxt)							  \
		ipc_log_string(ctxt, "[%s]: "x, __func__, ##__VA_ARGS__); \
} while (0)

#define GLINK_NAME_SIZE		32
#define GLINK_VERSION_1		1

@@ -83,6 +107,8 @@ struct glink_core_rx_intent {
 * @rx_pipe:	pipe object for receive FIFO
 * @tx_pipe:	pipe object for transmit FIFO
 * @irq:	IRQ for signaling incoming events
 * @kworker:	kworker to handle rx_done work
 * @task:	kthread running @kworker
 * @rx_work:	worker for handling received control messages
 * @rx_lock:	protects the @rx_queue
 * @rx_queue:	queue of received control messages to be processed in @rx_work
@@ -90,8 +116,10 @@ struct glink_core_rx_intent {
 * @idr_lock:	synchronizes @lcids and @rcids modifications
 * @lcids:	idr of all channels with a known local channel id
 * @rcids:	idr of all channels with a known remote channel id
 * @in_reset:	reset status of this edge
 * @features:	remote features
 * @intentless:	flag to indicate that there is no intent
 * @ilc:	ipc logging context reference
 */
struct qcom_glink {
	struct device *dev;
@@ -106,6 +134,9 @@ struct qcom_glink {

	int irq;

	struct kthread_worker kworker;
	struct task_struct *task;

	struct work_struct rx_work;
	spinlock_t rx_lock;
	struct list_head rx_queue;
@@ -115,9 +146,13 @@ struct qcom_glink {
	spinlock_t idr_lock;
	struct idr lcids;
	struct idr rcids;

	atomic_t in_reset;
	unsigned long features;

	bool intentless;

	void *ilc;
};

enum {
@@ -149,7 +184,8 @@ enum {
 * @open_req:	completed once open-request has been received
 * @intent_req_lock: Synchronises multiple intent requests
 * @intent_req_result: Result of intent request
 * @intent_req_comp: Completion for intent_req signalling
 * @intent_req_comp: Status of intent request completion
 * @intent_req_event: Waitqueue for @intent_req_comp
 */
struct glink_channel {
	struct rpmsg_endpoint ept;
@@ -168,19 +204,23 @@ struct glink_channel {
	spinlock_t intent_lock;
	struct idr liids;
	struct idr riids;
	struct work_struct intent_work;
	struct kthread_work intent_work;
	struct list_head done_intents;

	struct glink_core_rx_intent *buf;
	int buf_offset;
	int buf_size;

	unsigned int lsigs;
	unsigned int rsigs;

	struct completion open_ack;
	struct completion open_req;

	struct mutex intent_req_lock;
	bool intent_req_result;
	struct completion intent_req_comp;
	atomic_t intent_req_comp;
	wait_queue_head_t intent_req_event;
};

#define to_glink_channel(_ept) container_of(_ept, struct glink_channel, ept)
@@ -201,10 +241,11 @@ static const struct rpmsg_endpoint_ops glink_endpoint_ops;
#define RPM_CMD_TX_DATA_CONT		12
#define RPM_CMD_READ_NOTIF		13
#define RPM_CMD_RX_DONE_W_REUSE		14
#define RPM_CMD_SIGNALS			15

#define GLINK_FEATURE_INTENTLESS	BIT(1)

static void qcom_glink_rx_done_work(struct work_struct *work);
static void qcom_glink_rx_done_work(struct kthread_work *work);

static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink,
						      const char *name)
@@ -225,10 +266,11 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink,

	init_completion(&channel->open_req);
	init_completion(&channel->open_ack);
	init_completion(&channel->intent_req_comp);
	atomic_set(&channel->intent_req_comp, 0);
	init_waitqueue_head(&channel->intent_req_event);

	INIT_LIST_HEAD(&channel->done_intents);
	INIT_WORK(&channel->intent_work, qcom_glink_rx_done_work);
	kthread_init_work(&channel->intent_work, qcom_glink_rx_done_work);

	idr_init(&channel->liids);
	idr_init(&channel->riids);
@@ -243,6 +285,9 @@ static void qcom_glink_channel_release(struct kref *ref)
						     refcount);
	unsigned long flags;

	CH_INFO(channel, "\n");
	wake_up(&channel->intent_req_event);

	spin_lock_irqsave(&channel->intent_lock, flags);
	idr_destroy(&channel->liids);
	idr_destroy(&channel->riids);
@@ -280,6 +325,15 @@ static void qcom_glink_tx_write(struct qcom_glink *glink,
	glink->tx_pipe->write(glink->tx_pipe, hdr, hlen, data, dlen);
}

static void qcom_glink_pipe_reset(struct qcom_glink *glink)
{
	if (glink->tx_pipe->reset)
		glink->tx_pipe->reset(glink->tx_pipe);

	if (glink->rx_pipe->reset)
		glink->rx_pipe->reset(glink->rx_pipe);
}

static int qcom_glink_tx(struct qcom_glink *glink,
			 const void *hdr, size_t hlen,
			 const void *data, size_t dlen, bool wait)
@@ -300,6 +354,11 @@ static int qcom_glink_tx(struct qcom_glink *glink,
			goto out;
		}

		if (atomic_read(&glink->in_reset)) {
			ret = -ECONNRESET;
			goto out;
		}

		/* Wait without holding the tx_lock */
		spin_unlock_irqrestore(&glink->tx_lock, flags);

@@ -327,6 +386,7 @@ static int qcom_glink_send_version(struct qcom_glink *glink)
	msg.param1 = cpu_to_le16(GLINK_VERSION_1);
	msg.param2 = cpu_to_le32(glink->features);

	GLINK_INFO(glink->ilc, "vers:%d features:%d\n", msg.param1, msg.param2);
	return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
}

@@ -338,6 +398,7 @@ static void qcom_glink_send_version_ack(struct qcom_glink *glink)
	msg.param1 = cpu_to_le16(GLINK_VERSION_1);
	msg.param2 = cpu_to_le32(glink->features);

	GLINK_INFO(glink->ilc, "vers:%d features:%d\n", msg.param1, msg.param2);
	qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
}

@@ -350,6 +411,7 @@ static void qcom_glink_send_open_ack(struct qcom_glink *glink,
	msg.param1 = cpu_to_le16(channel->rcid);
	msg.param2 = cpu_to_le32(0);

	CH_INFO(channel, "\n");
	qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
}

@@ -368,7 +430,9 @@ static void qcom_glink_handle_intent_req_ack(struct qcom_glink *glink,
	}

	channel->intent_req_result = granted;
	complete(&channel->intent_req_comp);
	atomic_inc(&channel->intent_req_comp);
	wake_up(&channel->intent_req_event);
	CH_INFO(channel, "\n");
}

/**
@@ -404,11 +468,12 @@ static int qcom_glink_send_open_req(struct qcom_glink *glink,
		return ret;

	channel->lcid = ret;
	CH_INFO(channel, "\n");

	req.msg.cmd = cpu_to_le16(RPM_CMD_OPEN);
	req.msg.param1 = cpu_to_le16(channel->lcid);
	req.msg.param2 = cpu_to_le32(name_len);
	strcpy(req.name, channel->name);
	strlcpy(req.name, channel->name, GLINK_NAME_SIZE);

	ret = qcom_glink_tx(glink, &req, req_len, NULL, 0, true);
	if (ret)
@@ -417,6 +482,8 @@ static int qcom_glink_send_open_req(struct qcom_glink *glink,
	return 0;

remove_idr:
	CH_INFO(channel, "remote_idr\n");

	spin_lock_irqsave(&glink->idr_lock, flags);
	idr_remove(&glink->lcids, channel->lcid);
	channel->lcid = 0;
@@ -434,6 +501,7 @@ static void qcom_glink_send_close_req(struct qcom_glink *glink,
	req.param1 = cpu_to_le16(channel->lcid);
	req.param2 = 0;

	CH_INFO(channel, "\n");
	qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true);
}

@@ -446,42 +514,58 @@ static void qcom_glink_send_close_ack(struct qcom_glink *glink,
	req.param1 = cpu_to_le16(rcid);
	req.param2 = 0;

	GLINK_INFO(glink->ilc, "rcid:%d\n", rcid);
	qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true);
}

static void qcom_glink_rx_done_work(struct work_struct *work)

static int __qcom_glink_rx_done(struct qcom_glink *glink,
				struct glink_channel *channel,
				struct glink_core_rx_intent *intent,
				bool wait)
{
	struct glink_channel *channel = container_of(work, struct glink_channel,
						     intent_work);
	struct qcom_glink *glink = channel->glink;
	struct glink_core_rx_intent *intent, *tmp;
	struct {
		u16 id;
		u16 lcid;
		u32 liid;
	} __packed cmd;

	unsigned int cid = channel->lcid;
	unsigned int iid;
	bool reuse;
	unsigned long flags;

	spin_lock_irqsave(&channel->intent_lock, flags);
	list_for_each_entry_safe(intent, tmp, &channel->done_intents, node) {
		list_del(&intent->node);
		spin_unlock_irqrestore(&channel->intent_lock, flags);
		iid = intent->id;
		reuse = intent->reuse;
	unsigned int iid = intent->id;
	bool reuse = intent->reuse;
	int ret;

	cmd.id = reuse ? RPM_CMD_RX_DONE_W_REUSE : RPM_CMD_RX_DONE;
	cmd.lcid = cid;
	cmd.liid = iid;

		qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true);
	ret = qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, wait);
	if (ret)
		return ret;

	if (!reuse) {
		kfree(intent->data);
		kfree(intent);
	}

	CH_INFO(channel, "reuse:%d liid:%d", reuse, iid);
	return 0;
}

static void qcom_glink_rx_done_work(struct kthread_work *work)
{
	struct glink_channel *channel = container_of(work, struct glink_channel,
						     intent_work);
	struct qcom_glink *glink = channel->glink;
	struct glink_core_rx_intent *intent, *tmp;
	unsigned long flags;

	spin_lock_irqsave(&channel->intent_lock, flags);
	list_for_each_entry_safe(intent, tmp, &channel->done_intents, node) {
		list_del(&intent->node);
		spin_unlock_irqrestore(&channel->intent_lock, flags);

		__qcom_glink_rx_done(glink, channel, intent, true);

		spin_lock_irqsave(&channel->intent_lock, flags);
	}
	spin_unlock_irqrestore(&channel->intent_lock, flags);
@@ -491,6 +575,8 @@ static void qcom_glink_rx_done(struct qcom_glink *glink,
			       struct glink_channel *channel,
			       struct glink_core_rx_intent *intent)
{
	int ret = -EAGAIN;

	/* We don't send RX_DONE to intentless systems */
	if (glink->intentless) {
		kfree(intent->data);
@@ -507,10 +593,14 @@ static void qcom_glink_rx_done(struct qcom_glink *glink,

	/* Schedule the sending of a rx_done indication */
	spin_lock(&channel->intent_lock);
	if (list_empty(&channel->done_intents))
		ret = __qcom_glink_rx_done(glink, channel, intent, false);

	if (ret) {
		list_add_tail(&intent->node, &channel->done_intents);
		kthread_queue_work(&glink->kworker, &channel->intent_work);
	}
	spin_unlock(&channel->intent_lock);

	schedule_work(&channel->intent_work);
}

/**
@@ -527,6 +617,8 @@ static void qcom_glink_receive_version(struct qcom_glink *glink,
				       u32 version,
				       u32 features)
{
	GLINK_INFO(glink->ilc, "vers:%d features:%d\n", version, features);

	switch (version) {
	case 0:
		break;
@@ -554,6 +646,8 @@ static void qcom_glink_receive_version_ack(struct qcom_glink *glink,
					   u32 version,
					   u32 features)
{
	GLINK_INFO(glink->ilc, "vers:%d features:%d\n", version, features);

	switch (version) {
	case 0:
		/* Version negotiation failed */
@@ -589,6 +683,7 @@ static int qcom_glink_send_intent_req_ack(struct qcom_glink *glink,
	msg.param1 = cpu_to_le16(channel->lcid);
	msg.param2 = cpu_to_le32(granted);

	CH_INFO(channel, "\n");
	qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);

	return 0;
@@ -622,6 +717,9 @@ static int qcom_glink_advertise_intent(struct qcom_glink *glink,
	cmd.size = cpu_to_le32(intent->size);
	cmd.liid = cpu_to_le32(intent->id);

	CH_INFO(channel, "count:%d size:%d liid:%d\n", 1,
		intent->size, intent->id);

	qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true);

	return 0;
@@ -692,6 +790,7 @@ static void qcom_glink_handle_rx_done(struct qcom_glink *glink,
	}

	intent->in_use = false;
	CH_INFO(channel, "reuse:%d iid:%d\n", reuse, intent->id);

	if (!reuse) {
		idr_remove(&channel->riids, intent->id);
@@ -792,9 +891,6 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail)
		return -EAGAIN;
	}

	if (WARN(chunk_size % 4, "Incoming data must be word aligned\n"))
		return -EINVAL;

	rcid = le16_to_cpu(hdr.msg.param1);
	spin_lock_irqsave(&glink->idr_lock, flags);
	channel = idr_find(&glink->rcids, rcid);
@@ -805,6 +901,7 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail)
		/* Drop the message */
		goto advance_rx;
	}
	CH_INFO(channel, "chunk_size:%d left_size:%d\n", chunk_size, left_size);

	if (glink->intentless) {
		/* Might have an ongoing, fragmented, message to append */
@@ -927,6 +1024,8 @@ static void qcom_glink_handle_intent(struct qcom_glink *glink,
		intent->id = le32_to_cpu(msg->intents[i].iid);
		intent->size = le32_to_cpu(msg->intents[i].size);

		CH_INFO(channel, "riid:%d size:%d\n", intent->id, intent->size);

		spin_lock_irqsave(&channel->intent_lock, flags);
		ret = idr_alloc(&channel->riids, intent,
				intent->id, intent->id + 1, GFP_ATOMIC);
@@ -934,6 +1033,7 @@ static void qcom_glink_handle_intent(struct qcom_glink *glink,

		if (ret < 0)
			dev_err(glink->dev, "failed to store remote intent\n");

	}

	kfree(msg);
@@ -952,7 +1052,56 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid)
		return -EINVAL;
	}

	complete(&channel->open_ack);
	CH_INFO(channel, "\n");
	complete_all(&channel->open_ack);

	return 0;
}

/**
 * qcom_glink_send_signals() - convert a signal  cmd to wire format and transmit
 * @glink:	The transport to transmit on.
 * @channel:	The glink channel
 * @sigs:	The signals to encode.
 *
 * Return: 0 on success or standard Linux error code.
 */
static int qcom_glink_send_signals(struct qcom_glink *glink,
				   struct glink_channel *channel,
				   u32 sigs)
{
	struct glink_msg msg;

	msg.cmd = cpu_to_le16(RPM_CMD_SIGNALS);
	msg.param1 = cpu_to_le16(channel->lcid);
	msg.param2 = cpu_to_le32(sigs);

	GLINK_INFO(glink->ilc, "sigs:%d\n", sigs);
	return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
}

static int qcom_glink_handle_signals(struct qcom_glink *glink,
				     unsigned int rcid, unsigned int signals)
{
	struct glink_channel *channel;
	unsigned long flags;
	u32 old;

	spin_lock_irqsave(&glink->idr_lock, flags);
	channel = idr_find(&glink->rcids, rcid);
	spin_unlock_irqrestore(&glink->idr_lock, flags);
	if (!channel) {
		dev_err(glink->dev, "signal for non-existing channel\n");
		return -EINVAL;
	}

	old = channel->rsigs;
	channel->rsigs = signals;

	if (channel->ept.sig_cb)
		channel->ept.sig_cb(channel->ept.rpdev, old, channel->rsigs);

	CH_INFO(channel, "old:%d new:%d\n", old, channel->rsigs);

	return 0;
}
@@ -1018,6 +1167,10 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data)
			qcom_glink_handle_intent_req_ack(glink, param1, param2);
			qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8));
			break;
		case RPM_CMD_SIGNALS:
			qcom_glink_handle_signals(glink, param1, param2);
			qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8));
			break;
		default:
			dev_err(glink->dev, "unhandled rx cmd: %d\n", cmd);
			ret = -EINVAL;
@@ -1043,6 +1196,7 @@ static struct glink_channel *qcom_glink_create_local(struct qcom_glink *glink,
	if (IS_ERR(channel))
		return ERR_CAST(channel);

	CH_INFO(channel, "\n");
	ret = qcom_glink_send_open_req(glink, channel);
	if (ret)
		goto release_channel;
@@ -1060,12 +1214,15 @@ static struct glink_channel *qcom_glink_create_local(struct qcom_glink *glink,
	return channel;

err_timeout:
	CH_INFO(channel, "err_timeout\n");

	/* qcom_glink_send_open_req() did register the channel in lcids*/
	spin_lock_irqsave(&glink->idr_lock, flags);
	idr_remove(&glink->lcids, channel->lcid);
	spin_unlock_irqrestore(&glink->idr_lock, flags);

release_channel:
	CH_INFO(channel, "release_channel\n");
	/* Release qcom_glink_send_open_req() reference */
	kref_put(&channel->refcount, qcom_glink_channel_release);
	/* Release qcom_glink_alloc_channel() reference */
@@ -1080,6 +1237,8 @@ static int qcom_glink_create_remote(struct qcom_glink *glink,
{
	int ret;

	CH_INFO(channel, "\n");

	qcom_glink_send_open_ack(glink, channel);

	ret = qcom_glink_send_open_req(glink, channel);
@@ -1095,15 +1254,16 @@ static int qcom_glink_create_remote(struct qcom_glink *glink,
	return 0;

close_link:
	CH_INFO(channel, "close_link %d\n", ret);

	/*
	 * Send a close request to "undo" our open-ack. The close-ack will
	 * release the last reference.
	 * release qcom_glink_send_open_req() reference and the last reference
	 * will be relesed after receiving remote_close or transport unregister
	 * by calling qcom_glink_native_remove().
	 */
	qcom_glink_send_close_req(glink, channel);

	/* Release qcom_glink_send_open_req() reference */
	kref_put(&channel->refcount, qcom_glink_channel_release);

	return ret;
}

@@ -1161,7 +1321,7 @@ static int qcom_glink_announce_create(struct rpmsg_device *rpdev)
	__be32 *val = defaults;
	int size;

	if (glink->intentless)
	if (glink->intentless || !completion_done(&channel->open_ack))
		return 0;

	prop = of_find_property(np, "qcom,intents", NULL);
@@ -1216,20 +1376,27 @@ static int qcom_glink_request_intent(struct qcom_glink *glink,

	mutex_lock(&channel->intent_req_lock);

	reinit_completion(&channel->intent_req_comp);
	atomic_set(&channel->intent_req_comp, 0);

	cmd.id = RPM_CMD_RX_INTENT_REQ;
	cmd.cid = channel->lcid;
	cmd.size = size;

	CH_INFO(channel, "size:%d\n", size);

	ret = qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true);
	if (ret)
		goto unlock;

	ret = wait_for_completion_timeout(&channel->intent_req_comp, 10 * HZ);
	ret = wait_event_timeout(channel->intent_req_event,
				 atomic_read(&channel->intent_req_comp) ||
				 atomic_read(&glink->in_reset), 10 * HZ);
	if (!ret) {
		dev_err(glink->dev, "intent request timed out\n");
		ret = -ETIMEDOUT;
	} else if (atomic_read(&glink->in_reset)) {
		CH_INFO(channel, "ssr detected\n");
		ret = -ECONNRESET;
	} else {
		ret = channel->intent_req_result ? 0 : -ECANCELED;
	}
@@ -1315,6 +1482,27 @@ static int qcom_glink_trysend(struct rpmsg_endpoint *ept, void *data, int len)
	return __qcom_glink_send(channel, data, len, false);
}

static int qcom_glink_get_sigs(struct rpmsg_endpoint *ept,
			       u32 *lsigs, u32 *rsigs)
{
	struct glink_channel *channel = to_glink_channel(ept);

	*lsigs = channel->lsigs;
	*rsigs = channel->rsigs;

	return 0;
}

static int qcom_glink_set_sigs(struct rpmsg_endpoint *ept, u32 sigs)
{
	struct glink_channel *channel = to_glink_channel(ept);
	struct qcom_glink *glink = channel->glink;

	channel->lsigs = sigs;

	return qcom_glink_send_signals(glink, channel, sigs);
}

/*
 * Finds the device_node for the glink child interested in this channel.
 */
@@ -1348,6 +1536,8 @@ static const struct rpmsg_endpoint_ops glink_endpoint_ops = {
	.destroy_ept = qcom_glink_destroy_ept,
	.send = qcom_glink_send,
	.trysend = qcom_glink_trysend,
	.get_sigs = qcom_glink_get_sigs,
	.set_sigs = qcom_glink_set_sigs,
};

static void qcom_glink_rpdev_release(struct device *dev)
@@ -1396,7 +1586,7 @@ static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid,
	channel->rcid = ret;
	spin_unlock_irqrestore(&glink->idr_lock, flags);

	complete(&channel->open_req);
	complete_all(&channel->open_req);

	if (create_device) {
		rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL);
@@ -1406,7 +1596,7 @@ static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid,
		}

		rpdev->ept = &channel->ept;
		strncpy(rpdev->id.name, name, RPMSG_NAME_SIZE);
		strlcpy(rpdev->id.name, name, RPMSG_NAME_SIZE);
		rpdev->src = RPMSG_ADDR_ANY;
		rpdev->dst = RPMSG_ADDR_ANY;
		rpdev->ops = &glink_device_ops;
@@ -1422,17 +1612,21 @@ static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid,

		channel->rpdev = rpdev;
	}
	CH_INFO(channel, "\n");

	return 0;

free_rpdev:
	CH_INFO(channel, "free_rpdev\n");
	kfree(rpdev);
rcid_remove:
	CH_INFO(channel, "rcid_remove\n");
	spin_lock_irqsave(&glink->idr_lock, flags);
	idr_remove(&glink->rcids, channel->rcid);
	channel->rcid = 0;
	spin_unlock_irqrestore(&glink->idr_lock, flags);
free_channel:
	CH_INFO(channel, "free_channel\n");
	/* Release the reference, iff we took it */
	if (create_device)
		kref_put(&channel->refcount, qcom_glink_channel_release);
@@ -1451,12 +1645,13 @@ static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid)
	spin_unlock_irqrestore(&glink->idr_lock, flags);
	if (WARN(!channel, "close request on unknown channel\n"))
		return;
	CH_INFO(channel, "\n");

	/* cancel pending rx_done work */
	cancel_work_sync(&channel->intent_work);
	kthread_cancel_work_sync(&channel->intent_work);

	if (channel->rpdev) {
		strncpy(chinfo.name, channel->name, sizeof(chinfo.name));
		strlcpy(chinfo.name, channel->name, sizeof(chinfo.name));
		chinfo.src = RPMSG_ADDR_ANY;
		chinfo.dst = RPMSG_ADDR_ANY;

@@ -1484,6 +1679,7 @@ static void qcom_glink_rx_close_ack(struct qcom_glink *glink, unsigned int lcid)
		spin_unlock_irqrestore(&glink->idr_lock, flags);
		return;
	}
	CH_INFO(channel, "\n");

	idr_remove(&glink->lcids, channel->lcid);
	channel->lcid = 0;
@@ -1547,21 +1743,93 @@ static void qcom_glink_work(struct work_struct *work)
	}
}

static ssize_t rpmsg_name_show(struct device *dev,
			       struct device_attribute *attr, char *buf)
{
	struct rpmsg_device *rpdev = to_rpmsg_device(dev);
	struct glink_channel *channel = to_glink_channel(rpdev->ept);

	return snprintf(buf, RPMSG_NAME_SIZE, "%s\n", channel->glink->name);
}
static DEVICE_ATTR_RO(rpmsg_name);

static struct attribute *qcom_glink_attrs[] = {
	&dev_attr_rpmsg_name.attr,
	NULL
};
ATTRIBUTE_GROUPS(qcom_glink);

static void qcom_glink_device_release(struct device *dev)
{
	struct rpmsg_device *rpdev = to_rpmsg_device(dev);
	struct glink_channel *channel = to_glink_channel(rpdev->ept);

	/* Release qcom_glink_alloc_channel() reference */
	kref_put(&channel->refcount, qcom_glink_channel_release);
	kfree(rpdev);
}

static int qcom_glink_create_chrdev(struct qcom_glink *glink)
{
	struct rpmsg_device *rpdev;
	struct glink_channel *channel;

	rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL);
	if (!rpdev)
		return -ENOMEM;

	channel = qcom_glink_alloc_channel(glink, "rpmsg_chrdev");
	if (IS_ERR(channel)) {
		kfree(rpdev);
		return PTR_ERR(channel);
	}
	channel->rpdev = rpdev;

	rpdev->ept = &channel->ept;
	rpdev->ops = &glink_device_ops;
	rpdev->dev.parent = glink->dev;
	rpdev->dev.release = qcom_glink_device_release;

	return rpmsg_chrdev_register_device(rpdev);
}

static void qcom_glink_set_affinity(struct qcom_glink *glink, u32 *arr,
				    size_t size)
{
	struct cpumask cpumask;
	int i;

	cpumask_clear(&cpumask);
	for (i = 0; i < size; i++) {
		if (arr[i] < num_possible_cpus())
			cpumask_set_cpu(arr[i], &cpumask);
	}
	if (irq_set_affinity(glink->irq, &cpumask))
		dev_err(glink->dev, "failed to set irq affinity\n");
	if (sched_setaffinity(glink->task->pid, &cpumask))
		dev_err(glink->dev, "failed to set task affinity\n");
}


struct qcom_glink *qcom_glink_native_probe(struct device *dev,
					   unsigned long features,
					   struct qcom_glink_pipe *rx,
					   struct qcom_glink_pipe *tx,
					   bool intentless)
{
	struct qcom_glink *glink;
	u32 *arr;
	int size;
	int irq;
	int ret;
	struct qcom_glink *glink;

	glink = devm_kzalloc(dev, sizeof(*glink), GFP_KERNEL);
	if (!glink)
		return ERR_PTR(-ENOMEM);

	glink->dev = dev;
	glink->dev->groups = qcom_glink_groups;

	glink->tx_pipe = tx;
	glink->rx_pipe = rx;

@@ -1576,6 +1844,7 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev,
	spin_lock_init(&glink->idr_lock);
	idr_init(&glink->lcids);
	idr_init(&glink->rcids);
	atomic_set(&glink->in_reset, 0);

	ret = of_property_read_string(dev->of_node, "label", &glink->name);
	if (ret < 0)
@@ -1590,6 +1859,15 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev,
		return ERR_CAST(glink->mbox_chan);
	}

	kthread_init_worker(&glink->kworker);
	glink->task = kthread_run(kthread_worker_fn, &glink->kworker,
				  "glink_%s", glink->name);
	if (IS_ERR(glink->task)) {
		dev_err(dev, "failed to spawn intent kthread %d\n",
			PTR_ERR(glink->task));
		return ERR_CAST(glink->task);
	}

	irq = of_irq_get(dev->of_node, 0);
	ret = devm_request_irq(dev, irq,
			       qcom_glink_native_intr,
@@ -1602,9 +1880,29 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev,

	glink->irq = irq;

	size = of_property_count_u32_elems(dev->of_node, "cpu-affinity");
	if (size > 0) {
		arr = kmalloc_array(size, sizeof(u32), GFP_KERNEL);
		if (!arr)
			return ERR_PTR(-ENOMEM);
		ret = of_property_read_u32_array(dev->of_node, "cpu-affinity",
						 arr, size);
		if (!ret)
			qcom_glink_set_affinity(glink, arr, size);
		kfree(arr);
	}

	ret = qcom_glink_send_version(glink);
	if (ret)
	if (ret) {
		dev_err(dev, "failed to send version %d\n", ret);
		return ERR_PTR(ret);
	}

	ret = qcom_glink_create_chrdev(glink);
	if (ret)
		dev_err(glink->dev, "failed to register chrdev\n");

	glink->ilc = ipc_log_context_create(GLINK_LOG_PAGE_CNT, glink->name, 0);

	return glink;
}
@@ -1624,21 +1922,51 @@ void qcom_glink_native_remove(struct qcom_glink *glink)
	int ret;
	unsigned long flags;

	atomic_inc(&glink->in_reset);
	disable_irq(glink->irq);
	cancel_work_sync(&glink->rx_work);

	/* Signal all threads to cancel tx */
	spin_lock_irqsave(&glink->idr_lock, flags);
	idr_for_each_entry(&glink->lcids, channel, cid) {
		wake_up(&channel->intent_req_event);
	}
	spin_unlock_irqrestore(&glink->idr_lock, flags);

	ret = device_for_each_child(glink->dev, NULL, qcom_glink_remove_device);
	if (ret)
		dev_warn(glink->dev, "Can't remove GLINK devices: %d\n", ret);

	spin_lock_irqsave(&glink->idr_lock, flags);
	idr_for_each_entry(&glink->lcids, channel, cid) {
		spin_unlock_irqrestore(&glink->idr_lock, flags);
		/* cancel pending rx_done work for each channel*/
		kthread_cancel_work_sync(&channel->intent_work);
		spin_lock_irqsave(&glink->idr_lock, flags);
	}
	spin_unlock_irqrestore(&glink->idr_lock, flags);

	spin_lock_irqsave(&glink->idr_lock, flags);

	/* Release any defunct local channels, waiting for close-ack */
	idr_for_each_entry(&glink->lcids, channel, cid)
	idr_for_each_entry(&glink->lcids, channel, cid) {
		kref_put(&channel->refcount, qcom_glink_channel_release);
		idr_remove(&glink->lcids, cid);
	}

	/* Release any defunct local channels, waiting for close-req */
	idr_for_each_entry(&glink->rcids, channel, cid) {
		kref_put(&channel->refcount, qcom_glink_channel_release);
		idr_remove(&glink->rcids, cid);
	}

	idr_destroy(&glink->lcids);
	idr_destroy(&glink->rcids);
	spin_unlock_irqrestore(&glink->idr_lock, flags);

	kthread_flush_worker(&glink->kworker);
	kthread_stop(glink->task);
	qcom_glink_pipe_reset(glink);
	mbox_free_channel(glink->mbox_chan);
}
EXPORT_SYMBOL_GPL(qcom_glink_native_remove);
+2 −0
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ struct qcom_glink_pipe {
	void (*write)(struct qcom_glink_pipe *glink_pipe,
		      const void *hdr, size_t hlen,
		      const void *data, size_t dlen);

	void (*reset)(struct qcom_glink_pipe *glink_pipe);
};

struct qcom_glink;
+4 −5
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2016, Linaro Ltd
 * Copyright (c) 2018, The Linux Foundation, All rights reserved.
 */

#include <linux/io.h>
@@ -90,13 +91,11 @@ static void glink_smem_rx_peak(struct qcom_glink_pipe *np,

	len = min_t(size_t, count, pipe->native.length - tail);
	if (len) {
		__ioread32_copy(data, pipe->fifo + tail,
				len / sizeof(u32));
		memcpy_fromio(data, pipe->fifo + tail, len);
	}

	if (len != count) {
		__ioread32_copy(data + len, pipe->fifo,
				(count - len) / sizeof(u32));
		memcpy_fromio(data + len, pipe->fifo, (count - len));
	}
}

@@ -109,7 +108,7 @@ static void glink_smem_rx_advance(struct qcom_glink_pipe *np,
	tail = le32_to_cpu(*pipe->tail);

	tail += count;
	if (tail > pipe->native.length)
	if (tail >= pipe->native.length)
		tail -= pipe->native.length;

	*pipe->tail = cpu_to_le32(tail);
Loading