162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2016-2017, Linaro Ltd
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/idr.h>
762306a36Sopenharmony_ci#include <linux/interrupt.h>
862306a36Sopenharmony_ci#include <linux/io.h>
962306a36Sopenharmony_ci#include <linux/list.h>
1062306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/of.h>
1362306a36Sopenharmony_ci#include <linux/of_address.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/regmap.h>
1662306a36Sopenharmony_ci#include <linux/rpmsg.h>
1762306a36Sopenharmony_ci#include <linux/sizes.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <linux/wait.h>
2062306a36Sopenharmony_ci#include <linux/workqueue.h>
2162306a36Sopenharmony_ci#include <linux/mailbox_client.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "rpmsg_internal.h"
2462306a36Sopenharmony_ci#include "qcom_glink_native.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define GLINK_NAME_SIZE		32
2762306a36Sopenharmony_ci#define GLINK_VERSION_1		1
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define RPM_GLINK_CID_MIN	1
3062306a36Sopenharmony_ci#define RPM_GLINK_CID_MAX	65536
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct glink_msg {
3362306a36Sopenharmony_ci	__le16 cmd;
3462306a36Sopenharmony_ci	__le16 param1;
3562306a36Sopenharmony_ci	__le32 param2;
3662306a36Sopenharmony_ci	u8 data[];
3762306a36Sopenharmony_ci} __packed;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/**
4062306a36Sopenharmony_ci * struct glink_defer_cmd - deferred incoming control message
4162306a36Sopenharmony_ci * @node:	list node
4262306a36Sopenharmony_ci * @msg:	message header
4362306a36Sopenharmony_ci * @data:	payload of the message
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * Copy of a received control message, to be added to @rx_queue and processed
4662306a36Sopenharmony_ci * by @rx_work of @qcom_glink.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_cistruct glink_defer_cmd {
4962306a36Sopenharmony_ci	struct list_head node;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	struct glink_msg msg;
5262306a36Sopenharmony_ci	u8 data[];
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/**
5662306a36Sopenharmony_ci * struct glink_core_rx_intent - RX intent
5762306a36Sopenharmony_ci * RX intent
5862306a36Sopenharmony_ci *
5962306a36Sopenharmony_ci * @data: pointer to the data (may be NULL for zero-copy)
6062306a36Sopenharmony_ci * @id: remote or local intent ID
6162306a36Sopenharmony_ci * @size: size of the original intent (do not modify)
6262306a36Sopenharmony_ci * @reuse: To mark if the intent can be reused after first use
6362306a36Sopenharmony_ci * @in_use: To mark if intent is already in use for the channel
6462306a36Sopenharmony_ci * @offset: next write offset (initially 0)
6562306a36Sopenharmony_ci * @node:	list node
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_cistruct glink_core_rx_intent {
6862306a36Sopenharmony_ci	void *data;
6962306a36Sopenharmony_ci	u32 id;
7062306a36Sopenharmony_ci	size_t size;
7162306a36Sopenharmony_ci	bool reuse;
7262306a36Sopenharmony_ci	bool in_use;
7362306a36Sopenharmony_ci	u32 offset;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	struct list_head node;
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/**
7962306a36Sopenharmony_ci * struct qcom_glink - driver context, relates to one remote subsystem
8062306a36Sopenharmony_ci * @dev:	reference to the associated struct device
8162306a36Sopenharmony_ci * @rx_pipe:	pipe object for receive FIFO
8262306a36Sopenharmony_ci * @tx_pipe:	pipe object for transmit FIFO
8362306a36Sopenharmony_ci * @rx_work:	worker for handling received control messages
8462306a36Sopenharmony_ci * @rx_lock:	protects the @rx_queue
8562306a36Sopenharmony_ci * @rx_queue:	queue of received control messages to be processed in @rx_work
8662306a36Sopenharmony_ci * @tx_lock:	synchronizes operations on the tx fifo
8762306a36Sopenharmony_ci * @idr_lock:	synchronizes @lcids and @rcids modifications
8862306a36Sopenharmony_ci * @lcids:	idr of all channels with a known local channel id
8962306a36Sopenharmony_ci * @rcids:	idr of all channels with a known remote channel id
9062306a36Sopenharmony_ci * @features:	remote features
9162306a36Sopenharmony_ci * @intentless:	flag to indicate that there is no intent
9262306a36Sopenharmony_ci * @tx_avail_notify: Waitqueue for pending tx tasks
9362306a36Sopenharmony_ci * @sent_read_notify: flag to check cmd sent or not
9462306a36Sopenharmony_ci * @abort_tx:	flag indicating that all tx attempts should fail
9562306a36Sopenharmony_ci */
9662306a36Sopenharmony_cistruct qcom_glink {
9762306a36Sopenharmony_ci	struct device *dev;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	struct qcom_glink_pipe *rx_pipe;
10062306a36Sopenharmony_ci	struct qcom_glink_pipe *tx_pipe;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	struct work_struct rx_work;
10362306a36Sopenharmony_ci	spinlock_t rx_lock;
10462306a36Sopenharmony_ci	struct list_head rx_queue;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	spinlock_t tx_lock;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	spinlock_t idr_lock;
10962306a36Sopenharmony_ci	struct idr lcids;
11062306a36Sopenharmony_ci	struct idr rcids;
11162306a36Sopenharmony_ci	unsigned long features;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	bool intentless;
11462306a36Sopenharmony_ci	wait_queue_head_t tx_avail_notify;
11562306a36Sopenharmony_ci	bool sent_read_notify;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	bool abort_tx;
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cienum {
12162306a36Sopenharmony_ci	GLINK_STATE_CLOSED,
12262306a36Sopenharmony_ci	GLINK_STATE_OPENING,
12362306a36Sopenharmony_ci	GLINK_STATE_OPEN,
12462306a36Sopenharmony_ci	GLINK_STATE_CLOSING,
12562306a36Sopenharmony_ci};
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/**
12862306a36Sopenharmony_ci * struct glink_channel - internal representation of a channel
12962306a36Sopenharmony_ci * @rpdev:	rpdev reference, only used for primary endpoints
13062306a36Sopenharmony_ci * @ept:	rpmsg endpoint this channel is associated with
13162306a36Sopenharmony_ci * @glink:	qcom_glink context handle
13262306a36Sopenharmony_ci * @refcount:	refcount for the channel object
13362306a36Sopenharmony_ci * @recv_lock:	guard for @ept.cb
13462306a36Sopenharmony_ci * @name:	unique channel name/identifier
13562306a36Sopenharmony_ci * @lcid:	channel id, in local space
13662306a36Sopenharmony_ci * @rcid:	channel id, in remote space
13762306a36Sopenharmony_ci * @intent_lock: lock for protection of @liids, @riids
13862306a36Sopenharmony_ci * @liids:	idr of all local intents
13962306a36Sopenharmony_ci * @riids:	idr of all remote intents
14062306a36Sopenharmony_ci * @intent_work: worker responsible for transmitting rx_done packets
14162306a36Sopenharmony_ci * @done_intents: list of intents that needs to be announced rx_done
14262306a36Sopenharmony_ci * @buf:	receive buffer, for gathering fragments
14362306a36Sopenharmony_ci * @buf_offset:	write offset in @buf
14462306a36Sopenharmony_ci * @buf_size:	size of current @buf
14562306a36Sopenharmony_ci * @open_ack:	completed once remote has acked the open-request
14662306a36Sopenharmony_ci * @open_req:	completed once open-request has been received
14762306a36Sopenharmony_ci * @intent_req_lock: Synchronises multiple intent requests
14862306a36Sopenharmony_ci * @intent_req_result: Result of intent request
14962306a36Sopenharmony_ci * @intent_received: flag indicating that an intent has been received
15062306a36Sopenharmony_ci * @intent_req_wq: wait queue for intent_req signalling
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_cistruct glink_channel {
15362306a36Sopenharmony_ci	struct rpmsg_endpoint ept;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	struct rpmsg_device *rpdev;
15662306a36Sopenharmony_ci	struct qcom_glink *glink;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	struct kref refcount;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	spinlock_t recv_lock;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	char *name;
16362306a36Sopenharmony_ci	unsigned int lcid;
16462306a36Sopenharmony_ci	unsigned int rcid;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	spinlock_t intent_lock;
16762306a36Sopenharmony_ci	struct idr liids;
16862306a36Sopenharmony_ci	struct idr riids;
16962306a36Sopenharmony_ci	struct work_struct intent_work;
17062306a36Sopenharmony_ci	struct list_head done_intents;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	struct glink_core_rx_intent *buf;
17362306a36Sopenharmony_ci	int buf_offset;
17462306a36Sopenharmony_ci	int buf_size;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	struct completion open_ack;
17762306a36Sopenharmony_ci	struct completion open_req;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	struct mutex intent_req_lock;
18062306a36Sopenharmony_ci	int intent_req_result;
18162306a36Sopenharmony_ci	bool intent_received;
18262306a36Sopenharmony_ci	wait_queue_head_t intent_req_wq;
18362306a36Sopenharmony_ci};
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci#define to_glink_channel(_ept) container_of(_ept, struct glink_channel, ept)
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic const struct rpmsg_endpoint_ops glink_endpoint_ops;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci#define GLINK_CMD_VERSION		0
19062306a36Sopenharmony_ci#define GLINK_CMD_VERSION_ACK		1
19162306a36Sopenharmony_ci#define GLINK_CMD_OPEN			2
19262306a36Sopenharmony_ci#define GLINK_CMD_CLOSE			3
19362306a36Sopenharmony_ci#define GLINK_CMD_OPEN_ACK		4
19462306a36Sopenharmony_ci#define GLINK_CMD_INTENT		5
19562306a36Sopenharmony_ci#define GLINK_CMD_RX_DONE		6
19662306a36Sopenharmony_ci#define GLINK_CMD_RX_INTENT_REQ		7
19762306a36Sopenharmony_ci#define GLINK_CMD_RX_INTENT_REQ_ACK	8
19862306a36Sopenharmony_ci#define GLINK_CMD_TX_DATA		9
19962306a36Sopenharmony_ci#define GLINK_CMD_CLOSE_ACK		11
20062306a36Sopenharmony_ci#define GLINK_CMD_TX_DATA_CONT		12
20162306a36Sopenharmony_ci#define GLINK_CMD_READ_NOTIF		13
20262306a36Sopenharmony_ci#define GLINK_CMD_RX_DONE_W_REUSE	14
20362306a36Sopenharmony_ci#define GLINK_CMD_SIGNALS		15
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci#define GLINK_FEATURE_INTENTLESS	BIT(1)
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci#define NATIVE_DTR_SIG			NATIVE_DSR_SIG
20862306a36Sopenharmony_ci#define NATIVE_DSR_SIG			BIT(31)
20962306a36Sopenharmony_ci#define NATIVE_RTS_SIG			NATIVE_CTS_SIG
21062306a36Sopenharmony_ci#define NATIVE_CTS_SIG			BIT(30)
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic void qcom_glink_rx_done_work(struct work_struct *work);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink,
21562306a36Sopenharmony_ci						      const char *name)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	struct glink_channel *channel;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
22062306a36Sopenharmony_ci	if (!channel)
22162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* Setup glink internal glink_channel data */
22462306a36Sopenharmony_ci	spin_lock_init(&channel->recv_lock);
22562306a36Sopenharmony_ci	spin_lock_init(&channel->intent_lock);
22662306a36Sopenharmony_ci	mutex_init(&channel->intent_req_lock);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	channel->glink = glink;
22962306a36Sopenharmony_ci	channel->name = kstrdup(name, GFP_KERNEL);
23062306a36Sopenharmony_ci	if (!channel->name) {
23162306a36Sopenharmony_ci		kfree(channel);
23262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	init_completion(&channel->open_req);
23662306a36Sopenharmony_ci	init_completion(&channel->open_ack);
23762306a36Sopenharmony_ci	init_waitqueue_head(&channel->intent_req_wq);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	INIT_LIST_HEAD(&channel->done_intents);
24062306a36Sopenharmony_ci	INIT_WORK(&channel->intent_work, qcom_glink_rx_done_work);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	idr_init(&channel->liids);
24362306a36Sopenharmony_ci	idr_init(&channel->riids);
24462306a36Sopenharmony_ci	kref_init(&channel->refcount);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	return channel;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic void qcom_glink_channel_release(struct kref *ref)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	struct glink_channel *channel = container_of(ref, struct glink_channel,
25262306a36Sopenharmony_ci						     refcount);
25362306a36Sopenharmony_ci	struct glink_core_rx_intent *intent;
25462306a36Sopenharmony_ci	struct glink_core_rx_intent *tmp;
25562306a36Sopenharmony_ci	unsigned long flags;
25662306a36Sopenharmony_ci	int iid;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* cancel pending rx_done work */
25962306a36Sopenharmony_ci	cancel_work_sync(&channel->intent_work);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	spin_lock_irqsave(&channel->intent_lock, flags);
26262306a36Sopenharmony_ci	/* Free all non-reuse intents pending rx_done work */
26362306a36Sopenharmony_ci	list_for_each_entry_safe(intent, tmp, &channel->done_intents, node) {
26462306a36Sopenharmony_ci		if (!intent->reuse) {
26562306a36Sopenharmony_ci			kfree(intent->data);
26662306a36Sopenharmony_ci			kfree(intent);
26762306a36Sopenharmony_ci		}
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	idr_for_each_entry(&channel->liids, tmp, iid) {
27162306a36Sopenharmony_ci		kfree(tmp->data);
27262306a36Sopenharmony_ci		kfree(tmp);
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci	idr_destroy(&channel->liids);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	idr_for_each_entry(&channel->riids, tmp, iid)
27762306a36Sopenharmony_ci		kfree(tmp);
27862306a36Sopenharmony_ci	idr_destroy(&channel->riids);
27962306a36Sopenharmony_ci	spin_unlock_irqrestore(&channel->intent_lock, flags);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	kfree(channel->name);
28262306a36Sopenharmony_ci	kfree(channel);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic size_t qcom_glink_rx_avail(struct qcom_glink *glink)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	return glink->rx_pipe->avail(glink->rx_pipe);
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic void qcom_glink_rx_peek(struct qcom_glink *glink,
29162306a36Sopenharmony_ci			       void *data, unsigned int offset, size_t count)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	glink->rx_pipe->peek(glink->rx_pipe, data, offset, count);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic void qcom_glink_rx_advance(struct qcom_glink *glink, size_t count)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	glink->rx_pipe->advance(glink->rx_pipe, count);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic size_t qcom_glink_tx_avail(struct qcom_glink *glink)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	return glink->tx_pipe->avail(glink->tx_pipe);
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic void qcom_glink_tx_write(struct qcom_glink *glink,
30762306a36Sopenharmony_ci				const void *hdr, size_t hlen,
30862306a36Sopenharmony_ci				const void *data, size_t dlen)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	glink->tx_pipe->write(glink->tx_pipe, hdr, hlen, data, dlen);
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic void qcom_glink_tx_kick(struct qcom_glink *glink)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	glink->tx_pipe->kick(glink->tx_pipe);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic void qcom_glink_send_read_notify(struct qcom_glink *glink)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	struct glink_msg msg;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	msg.cmd = cpu_to_le16(GLINK_CMD_READ_NOTIF);
32362306a36Sopenharmony_ci	msg.param1 = 0;
32462306a36Sopenharmony_ci	msg.param2 = 0;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	qcom_glink_tx_write(glink, &msg, sizeof(msg), NULL, 0);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	qcom_glink_tx_kick(glink);
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic int qcom_glink_tx(struct qcom_glink *glink,
33262306a36Sopenharmony_ci			 const void *hdr, size_t hlen,
33362306a36Sopenharmony_ci			 const void *data, size_t dlen, bool wait)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	unsigned int tlen = hlen + dlen;
33662306a36Sopenharmony_ci	unsigned long flags;
33762306a36Sopenharmony_ci	int ret = 0;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	/* Reject packets that are too big */
34062306a36Sopenharmony_ci	if (tlen >= glink->tx_pipe->length)
34162306a36Sopenharmony_ci		return -EINVAL;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	spin_lock_irqsave(&glink->tx_lock, flags);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	if (glink->abort_tx) {
34662306a36Sopenharmony_ci		ret = -EIO;
34762306a36Sopenharmony_ci		goto out;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	while (qcom_glink_tx_avail(glink) < tlen) {
35162306a36Sopenharmony_ci		if (!wait) {
35262306a36Sopenharmony_ci			ret = -EAGAIN;
35362306a36Sopenharmony_ci			goto out;
35462306a36Sopenharmony_ci		}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		if (glink->abort_tx) {
35762306a36Sopenharmony_ci			ret = -EIO;
35862306a36Sopenharmony_ci			goto out;
35962306a36Sopenharmony_ci		}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		if (!glink->sent_read_notify) {
36262306a36Sopenharmony_ci			glink->sent_read_notify = true;
36362306a36Sopenharmony_ci			qcom_glink_send_read_notify(glink);
36462306a36Sopenharmony_ci		}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci		/* Wait without holding the tx_lock */
36762306a36Sopenharmony_ci		spin_unlock_irqrestore(&glink->tx_lock, flags);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci		wait_event_timeout(glink->tx_avail_notify,
37062306a36Sopenharmony_ci				   qcom_glink_tx_avail(glink) >= tlen, 10 * HZ);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		spin_lock_irqsave(&glink->tx_lock, flags);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci		if (qcom_glink_tx_avail(glink) >= tlen)
37562306a36Sopenharmony_ci			glink->sent_read_notify = false;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	qcom_glink_tx_write(glink, hdr, hlen, data, dlen);
37962306a36Sopenharmony_ci	qcom_glink_tx_kick(glink);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ciout:
38262306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->tx_lock, flags);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	return ret;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic int qcom_glink_send_version(struct qcom_glink *glink)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	struct glink_msg msg;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	msg.cmd = cpu_to_le16(GLINK_CMD_VERSION);
39262306a36Sopenharmony_ci	msg.param1 = cpu_to_le16(GLINK_VERSION_1);
39362306a36Sopenharmony_ci	msg.param2 = cpu_to_le32(glink->features);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic void qcom_glink_send_version_ack(struct qcom_glink *glink)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	struct glink_msg msg;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	msg.cmd = cpu_to_le16(GLINK_CMD_VERSION_ACK);
40362306a36Sopenharmony_ci	msg.param1 = cpu_to_le16(GLINK_VERSION_1);
40462306a36Sopenharmony_ci	msg.param2 = cpu_to_le32(glink->features);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic void qcom_glink_send_open_ack(struct qcom_glink *glink,
41062306a36Sopenharmony_ci				     struct glink_channel *channel)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct glink_msg msg;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	msg.cmd = cpu_to_le16(GLINK_CMD_OPEN_ACK);
41562306a36Sopenharmony_ci	msg.param1 = cpu_to_le16(channel->rcid);
41662306a36Sopenharmony_ci	msg.param2 = cpu_to_le32(0);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic void qcom_glink_handle_intent_req_ack(struct qcom_glink *glink,
42262306a36Sopenharmony_ci					     unsigned int cid, bool granted)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct glink_channel *channel;
42562306a36Sopenharmony_ci	unsigned long flags;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
42862306a36Sopenharmony_ci	channel = idr_find(&glink->rcids, cid);
42962306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
43062306a36Sopenharmony_ci	if (!channel) {
43162306a36Sopenharmony_ci		dev_err(glink->dev, "unable to find channel\n");
43262306a36Sopenharmony_ci		return;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	WRITE_ONCE(channel->intent_req_result, granted);
43662306a36Sopenharmony_ci	wake_up_all(&channel->intent_req_wq);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic void qcom_glink_intent_req_abort(struct glink_channel *channel)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	WRITE_ONCE(channel->intent_req_result, 0);
44262306a36Sopenharmony_ci	wake_up_all(&channel->intent_req_wq);
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci/**
44662306a36Sopenharmony_ci * qcom_glink_send_open_req() - send a GLINK_CMD_OPEN request to the remote
44762306a36Sopenharmony_ci * @glink: Ptr to the glink edge
44862306a36Sopenharmony_ci * @channel: Ptr to the channel that the open req is sent
44962306a36Sopenharmony_ci *
45062306a36Sopenharmony_ci * Allocates a local channel id and sends a GLINK_CMD_OPEN message to the remote.
45162306a36Sopenharmony_ci * Will return with refcount held, regardless of outcome.
45262306a36Sopenharmony_ci *
45362306a36Sopenharmony_ci * Return: 0 on success, negative errno otherwise.
45462306a36Sopenharmony_ci */
45562306a36Sopenharmony_cistatic int qcom_glink_send_open_req(struct qcom_glink *glink,
45662306a36Sopenharmony_ci				    struct glink_channel *channel)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	struct {
45962306a36Sopenharmony_ci		struct glink_msg msg;
46062306a36Sopenharmony_ci		u8 name[GLINK_NAME_SIZE];
46162306a36Sopenharmony_ci	} __packed req;
46262306a36Sopenharmony_ci	int name_len = strlen(channel->name) + 1;
46362306a36Sopenharmony_ci	int req_len = ALIGN(sizeof(req.msg) + name_len, 8);
46462306a36Sopenharmony_ci	int ret;
46562306a36Sopenharmony_ci	unsigned long flags;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	kref_get(&channel->refcount);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
47062306a36Sopenharmony_ci	ret = idr_alloc_cyclic(&glink->lcids, channel,
47162306a36Sopenharmony_ci			       RPM_GLINK_CID_MIN, RPM_GLINK_CID_MAX,
47262306a36Sopenharmony_ci			       GFP_ATOMIC);
47362306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
47462306a36Sopenharmony_ci	if (ret < 0)
47562306a36Sopenharmony_ci		return ret;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	channel->lcid = ret;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	req.msg.cmd = cpu_to_le16(GLINK_CMD_OPEN);
48062306a36Sopenharmony_ci	req.msg.param1 = cpu_to_le16(channel->lcid);
48162306a36Sopenharmony_ci	req.msg.param2 = cpu_to_le32(name_len);
48262306a36Sopenharmony_ci	strcpy(req.name, channel->name);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	ret = qcom_glink_tx(glink, &req, req_len, NULL, 0, true);
48562306a36Sopenharmony_ci	if (ret)
48662306a36Sopenharmony_ci		goto remove_idr;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	return 0;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ciremove_idr:
49162306a36Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
49262306a36Sopenharmony_ci	idr_remove(&glink->lcids, channel->lcid);
49362306a36Sopenharmony_ci	channel->lcid = 0;
49462306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	return ret;
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic void qcom_glink_send_close_req(struct qcom_glink *glink,
50062306a36Sopenharmony_ci				      struct glink_channel *channel)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	struct glink_msg req;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	req.cmd = cpu_to_le16(GLINK_CMD_CLOSE);
50562306a36Sopenharmony_ci	req.param1 = cpu_to_le16(channel->lcid);
50662306a36Sopenharmony_ci	req.param2 = 0;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true);
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic void qcom_glink_send_close_ack(struct qcom_glink *glink,
51262306a36Sopenharmony_ci				      unsigned int rcid)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	struct glink_msg req;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	req.cmd = cpu_to_le16(GLINK_CMD_CLOSE_ACK);
51762306a36Sopenharmony_ci	req.param1 = cpu_to_le16(rcid);
51862306a36Sopenharmony_ci	req.param2 = 0;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true);
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic void qcom_glink_rx_done_work(struct work_struct *work)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct glink_channel *channel = container_of(work, struct glink_channel,
52662306a36Sopenharmony_ci						     intent_work);
52762306a36Sopenharmony_ci	struct qcom_glink *glink = channel->glink;
52862306a36Sopenharmony_ci	struct glink_core_rx_intent *intent, *tmp;
52962306a36Sopenharmony_ci	struct {
53062306a36Sopenharmony_ci		u16 id;
53162306a36Sopenharmony_ci		u16 lcid;
53262306a36Sopenharmony_ci		u32 liid;
53362306a36Sopenharmony_ci	} __packed cmd;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	unsigned int cid = channel->lcid;
53662306a36Sopenharmony_ci	unsigned int iid;
53762306a36Sopenharmony_ci	bool reuse;
53862306a36Sopenharmony_ci	unsigned long flags;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	spin_lock_irqsave(&channel->intent_lock, flags);
54162306a36Sopenharmony_ci	list_for_each_entry_safe(intent, tmp, &channel->done_intents, node) {
54262306a36Sopenharmony_ci		list_del(&intent->node);
54362306a36Sopenharmony_ci		spin_unlock_irqrestore(&channel->intent_lock, flags);
54462306a36Sopenharmony_ci		iid = intent->id;
54562306a36Sopenharmony_ci		reuse = intent->reuse;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		cmd.id = reuse ? GLINK_CMD_RX_DONE_W_REUSE : GLINK_CMD_RX_DONE;
54862306a36Sopenharmony_ci		cmd.lcid = cid;
54962306a36Sopenharmony_ci		cmd.liid = iid;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true);
55262306a36Sopenharmony_ci		if (!reuse) {
55362306a36Sopenharmony_ci			kfree(intent->data);
55462306a36Sopenharmony_ci			kfree(intent);
55562306a36Sopenharmony_ci		}
55662306a36Sopenharmony_ci		spin_lock_irqsave(&channel->intent_lock, flags);
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci	spin_unlock_irqrestore(&channel->intent_lock, flags);
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic void qcom_glink_rx_done(struct qcom_glink *glink,
56262306a36Sopenharmony_ci			       struct glink_channel *channel,
56362306a36Sopenharmony_ci			       struct glink_core_rx_intent *intent)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	/* We don't send RX_DONE to intentless systems */
56662306a36Sopenharmony_ci	if (glink->intentless) {
56762306a36Sopenharmony_ci		kfree(intent->data);
56862306a36Sopenharmony_ci		kfree(intent);
56962306a36Sopenharmony_ci		return;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* Take it off the tree of receive intents */
57362306a36Sopenharmony_ci	if (!intent->reuse) {
57462306a36Sopenharmony_ci		spin_lock(&channel->intent_lock);
57562306a36Sopenharmony_ci		idr_remove(&channel->liids, intent->id);
57662306a36Sopenharmony_ci		spin_unlock(&channel->intent_lock);
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	/* Schedule the sending of a rx_done indication */
58062306a36Sopenharmony_ci	spin_lock(&channel->intent_lock);
58162306a36Sopenharmony_ci	list_add_tail(&intent->node, &channel->done_intents);
58262306a36Sopenharmony_ci	spin_unlock(&channel->intent_lock);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	schedule_work(&channel->intent_work);
58562306a36Sopenharmony_ci}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci/**
58862306a36Sopenharmony_ci * qcom_glink_receive_version() - receive version/features from remote system
58962306a36Sopenharmony_ci *
59062306a36Sopenharmony_ci * @glink:	pointer to transport interface
59162306a36Sopenharmony_ci * @version:	remote version
59262306a36Sopenharmony_ci * @features:	remote features
59362306a36Sopenharmony_ci *
59462306a36Sopenharmony_ci * This function is called in response to a remote-initiated version/feature
59562306a36Sopenharmony_ci * negotiation sequence.
59662306a36Sopenharmony_ci */
59762306a36Sopenharmony_cistatic void qcom_glink_receive_version(struct qcom_glink *glink,
59862306a36Sopenharmony_ci				       u32 version,
59962306a36Sopenharmony_ci				       u32 features)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	switch (version) {
60262306a36Sopenharmony_ci	case 0:
60362306a36Sopenharmony_ci		break;
60462306a36Sopenharmony_ci	case GLINK_VERSION_1:
60562306a36Sopenharmony_ci		glink->features &= features;
60662306a36Sopenharmony_ci		fallthrough;
60762306a36Sopenharmony_ci	default:
60862306a36Sopenharmony_ci		qcom_glink_send_version_ack(glink);
60962306a36Sopenharmony_ci		break;
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci/**
61462306a36Sopenharmony_ci * qcom_glink_receive_version_ack() - receive negotiation ack from remote system
61562306a36Sopenharmony_ci *
61662306a36Sopenharmony_ci * @glink:	pointer to transport interface
61762306a36Sopenharmony_ci * @version:	remote version response
61862306a36Sopenharmony_ci * @features:	remote features response
61962306a36Sopenharmony_ci *
62062306a36Sopenharmony_ci * This function is called in response to a local-initiated version/feature
62162306a36Sopenharmony_ci * negotiation sequence and is the counter-offer from the remote side based
62262306a36Sopenharmony_ci * upon the initial version and feature set requested.
62362306a36Sopenharmony_ci */
62462306a36Sopenharmony_cistatic void qcom_glink_receive_version_ack(struct qcom_glink *glink,
62562306a36Sopenharmony_ci					   u32 version,
62662306a36Sopenharmony_ci					   u32 features)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	switch (version) {
62962306a36Sopenharmony_ci	case 0:
63062306a36Sopenharmony_ci		/* Version negotiation failed */
63162306a36Sopenharmony_ci		break;
63262306a36Sopenharmony_ci	case GLINK_VERSION_1:
63362306a36Sopenharmony_ci		if (features == glink->features)
63462306a36Sopenharmony_ci			break;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci		glink->features &= features;
63762306a36Sopenharmony_ci		fallthrough;
63862306a36Sopenharmony_ci	default:
63962306a36Sopenharmony_ci		qcom_glink_send_version(glink);
64062306a36Sopenharmony_ci		break;
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci/**
64562306a36Sopenharmony_ci * qcom_glink_send_intent_req_ack() - convert an rx intent request ack cmd to
64662306a36Sopenharmony_ci * 	wire format and transmit
64762306a36Sopenharmony_ci * @glink:	The transport to transmit on.
64862306a36Sopenharmony_ci * @channel:	The glink channel
64962306a36Sopenharmony_ci * @granted:	The request response to encode.
65062306a36Sopenharmony_ci *
65162306a36Sopenharmony_ci * Return: 0 on success or standard Linux error code.
65262306a36Sopenharmony_ci */
65362306a36Sopenharmony_cistatic int qcom_glink_send_intent_req_ack(struct qcom_glink *glink,
65462306a36Sopenharmony_ci					  struct glink_channel *channel,
65562306a36Sopenharmony_ci					  bool granted)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	struct glink_msg msg;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	msg.cmd = cpu_to_le16(GLINK_CMD_RX_INTENT_REQ_ACK);
66062306a36Sopenharmony_ci	msg.param1 = cpu_to_le16(channel->lcid);
66162306a36Sopenharmony_ci	msg.param2 = cpu_to_le32(granted);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	return 0;
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci/**
66962306a36Sopenharmony_ci * qcom_glink_advertise_intent - convert an rx intent cmd to wire format and
67062306a36Sopenharmony_ci *			   transmit
67162306a36Sopenharmony_ci * @glink:	The transport to transmit on.
67262306a36Sopenharmony_ci * @channel:	The local channel
67362306a36Sopenharmony_ci * @intent:	The intent to pass on to remote.
67462306a36Sopenharmony_ci *
67562306a36Sopenharmony_ci * Return: 0 on success or standard Linux error code.
67662306a36Sopenharmony_ci */
67762306a36Sopenharmony_cistatic int qcom_glink_advertise_intent(struct qcom_glink *glink,
67862306a36Sopenharmony_ci				       struct glink_channel *channel,
67962306a36Sopenharmony_ci				       struct glink_core_rx_intent *intent)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	struct command {
68262306a36Sopenharmony_ci		__le16 id;
68362306a36Sopenharmony_ci		__le16 lcid;
68462306a36Sopenharmony_ci		__le32 count;
68562306a36Sopenharmony_ci		__le32 size;
68662306a36Sopenharmony_ci		__le32 liid;
68762306a36Sopenharmony_ci	} __packed;
68862306a36Sopenharmony_ci	struct command cmd;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	cmd.id = cpu_to_le16(GLINK_CMD_INTENT);
69162306a36Sopenharmony_ci	cmd.lcid = cpu_to_le16(channel->lcid);
69262306a36Sopenharmony_ci	cmd.count = cpu_to_le32(1);
69362306a36Sopenharmony_ci	cmd.size = cpu_to_le32(intent->size);
69462306a36Sopenharmony_ci	cmd.liid = cpu_to_le32(intent->id);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	return 0;
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic struct glink_core_rx_intent *
70262306a36Sopenharmony_ciqcom_glink_alloc_intent(struct qcom_glink *glink,
70362306a36Sopenharmony_ci			struct glink_channel *channel,
70462306a36Sopenharmony_ci			size_t size,
70562306a36Sopenharmony_ci			bool reuseable)
70662306a36Sopenharmony_ci{
70762306a36Sopenharmony_ci	struct glink_core_rx_intent *intent;
70862306a36Sopenharmony_ci	int ret;
70962306a36Sopenharmony_ci	unsigned long flags;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	intent = kzalloc(sizeof(*intent), GFP_KERNEL);
71262306a36Sopenharmony_ci	if (!intent)
71362306a36Sopenharmony_ci		return NULL;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	intent->data = kzalloc(size, GFP_KERNEL);
71662306a36Sopenharmony_ci	if (!intent->data)
71762306a36Sopenharmony_ci		goto free_intent;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	spin_lock_irqsave(&channel->intent_lock, flags);
72062306a36Sopenharmony_ci	ret = idr_alloc_cyclic(&channel->liids, intent, 1, -1, GFP_ATOMIC);
72162306a36Sopenharmony_ci	if (ret < 0) {
72262306a36Sopenharmony_ci		spin_unlock_irqrestore(&channel->intent_lock, flags);
72362306a36Sopenharmony_ci		goto free_data;
72462306a36Sopenharmony_ci	}
72562306a36Sopenharmony_ci	spin_unlock_irqrestore(&channel->intent_lock, flags);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	intent->id = ret;
72862306a36Sopenharmony_ci	intent->size = size;
72962306a36Sopenharmony_ci	intent->reuse = reuseable;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	return intent;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_cifree_data:
73462306a36Sopenharmony_ci	kfree(intent->data);
73562306a36Sopenharmony_cifree_intent:
73662306a36Sopenharmony_ci	kfree(intent);
73762306a36Sopenharmony_ci	return NULL;
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic void qcom_glink_handle_rx_done(struct qcom_glink *glink,
74162306a36Sopenharmony_ci				      u32 cid, uint32_t iid,
74262306a36Sopenharmony_ci				      bool reuse)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	struct glink_core_rx_intent *intent;
74562306a36Sopenharmony_ci	struct glink_channel *channel;
74662306a36Sopenharmony_ci	unsigned long flags;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
74962306a36Sopenharmony_ci	channel = idr_find(&glink->rcids, cid);
75062306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
75162306a36Sopenharmony_ci	if (!channel) {
75262306a36Sopenharmony_ci		dev_err(glink->dev, "invalid channel id received\n");
75362306a36Sopenharmony_ci		return;
75462306a36Sopenharmony_ci	}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	spin_lock_irqsave(&channel->intent_lock, flags);
75762306a36Sopenharmony_ci	intent = idr_find(&channel->riids, iid);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	if (!intent) {
76062306a36Sopenharmony_ci		spin_unlock_irqrestore(&channel->intent_lock, flags);
76162306a36Sopenharmony_ci		dev_err(glink->dev, "invalid intent id received\n");
76262306a36Sopenharmony_ci		return;
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	intent->in_use = false;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	if (!reuse) {
76862306a36Sopenharmony_ci		idr_remove(&channel->riids, intent->id);
76962306a36Sopenharmony_ci		kfree(intent);
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci	spin_unlock_irqrestore(&channel->intent_lock, flags);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	if (reuse) {
77462306a36Sopenharmony_ci		WRITE_ONCE(channel->intent_received, true);
77562306a36Sopenharmony_ci		wake_up_all(&channel->intent_req_wq);
77662306a36Sopenharmony_ci	}
77762306a36Sopenharmony_ci}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci/**
78062306a36Sopenharmony_ci * qcom_glink_handle_intent_req() - Receive a request for rx_intent
78162306a36Sopenharmony_ci *					    from remote side
78262306a36Sopenharmony_ci * @glink:      Pointer to the transport interface
78362306a36Sopenharmony_ci * @cid:	Remote channel ID
78462306a36Sopenharmony_ci * @size:	size of the intent
78562306a36Sopenharmony_ci *
78662306a36Sopenharmony_ci * The function searches for the local channel to which the request for
78762306a36Sopenharmony_ci * rx_intent has arrived and allocates and notifies the remote back
78862306a36Sopenharmony_ci */
78962306a36Sopenharmony_cistatic void qcom_glink_handle_intent_req(struct qcom_glink *glink,
79062306a36Sopenharmony_ci					 u32 cid, size_t size)
79162306a36Sopenharmony_ci{
79262306a36Sopenharmony_ci	struct glink_core_rx_intent *intent;
79362306a36Sopenharmony_ci	struct glink_channel *channel;
79462306a36Sopenharmony_ci	unsigned long flags;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
79762306a36Sopenharmony_ci	channel = idr_find(&glink->rcids, cid);
79862306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	if (!channel) {
80162306a36Sopenharmony_ci		pr_err("%s channel not found for cid %d\n", __func__, cid);
80262306a36Sopenharmony_ci		return;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	intent = qcom_glink_alloc_intent(glink, channel, size, false);
80662306a36Sopenharmony_ci	if (intent)
80762306a36Sopenharmony_ci		qcom_glink_advertise_intent(glink, channel, intent);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	qcom_glink_send_intent_req_ack(glink, channel, !!intent);
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistatic int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra)
81362306a36Sopenharmony_ci{
81462306a36Sopenharmony_ci	struct glink_defer_cmd *dcmd;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	extra = ALIGN(extra, 8);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	if (qcom_glink_rx_avail(glink) < sizeof(struct glink_msg) + extra) {
81962306a36Sopenharmony_ci		dev_dbg(glink->dev, "Insufficient data in rx fifo");
82062306a36Sopenharmony_ci		return -ENXIO;
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	dcmd = kzalloc(struct_size(dcmd, data, extra), GFP_ATOMIC);
82462306a36Sopenharmony_ci	if (!dcmd)
82562306a36Sopenharmony_ci		return -ENOMEM;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	INIT_LIST_HEAD(&dcmd->node);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	qcom_glink_rx_peek(glink, &dcmd->msg, 0, sizeof(dcmd->msg) + extra);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	spin_lock(&glink->rx_lock);
83262306a36Sopenharmony_ci	list_add_tail(&dcmd->node, &glink->rx_queue);
83362306a36Sopenharmony_ci	spin_unlock(&glink->rx_lock);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	schedule_work(&glink->rx_work);
83662306a36Sopenharmony_ci	qcom_glink_rx_advance(glink, sizeof(dcmd->msg) + extra);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	return 0;
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cistatic int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	struct glink_core_rx_intent *intent;
84462306a36Sopenharmony_ci	struct glink_channel *channel;
84562306a36Sopenharmony_ci	struct {
84662306a36Sopenharmony_ci		struct glink_msg msg;
84762306a36Sopenharmony_ci		__le32 chunk_size;
84862306a36Sopenharmony_ci		__le32 left_size;
84962306a36Sopenharmony_ci	} __packed hdr;
85062306a36Sopenharmony_ci	unsigned int chunk_size;
85162306a36Sopenharmony_ci	unsigned int left_size;
85262306a36Sopenharmony_ci	unsigned int rcid;
85362306a36Sopenharmony_ci	unsigned int liid;
85462306a36Sopenharmony_ci	int ret = 0;
85562306a36Sopenharmony_ci	unsigned long flags;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	if (avail < sizeof(hdr)) {
85862306a36Sopenharmony_ci		dev_dbg(glink->dev, "Not enough data in fifo\n");
85962306a36Sopenharmony_ci		return -EAGAIN;
86062306a36Sopenharmony_ci	}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	qcom_glink_rx_peek(glink, &hdr, 0, sizeof(hdr));
86362306a36Sopenharmony_ci	chunk_size = le32_to_cpu(hdr.chunk_size);
86462306a36Sopenharmony_ci	left_size = le32_to_cpu(hdr.left_size);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	if (avail < sizeof(hdr) + chunk_size) {
86762306a36Sopenharmony_ci		dev_dbg(glink->dev, "Payload not yet in fifo\n");
86862306a36Sopenharmony_ci		return -EAGAIN;
86962306a36Sopenharmony_ci	}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	rcid = le16_to_cpu(hdr.msg.param1);
87262306a36Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
87362306a36Sopenharmony_ci	channel = idr_find(&glink->rcids, rcid);
87462306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
87562306a36Sopenharmony_ci	if (!channel) {
87662306a36Sopenharmony_ci		dev_dbg(glink->dev, "Data on non-existing channel\n");
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci		/* Drop the message */
87962306a36Sopenharmony_ci		goto advance_rx;
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	if (glink->intentless) {
88362306a36Sopenharmony_ci		/* Might have an ongoing, fragmented, message to append */
88462306a36Sopenharmony_ci		if (!channel->buf) {
88562306a36Sopenharmony_ci			intent = kzalloc(sizeof(*intent), GFP_ATOMIC);
88662306a36Sopenharmony_ci			if (!intent)
88762306a36Sopenharmony_ci				return -ENOMEM;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci			intent->data = kmalloc(chunk_size + left_size,
89062306a36Sopenharmony_ci					       GFP_ATOMIC);
89162306a36Sopenharmony_ci			if (!intent->data) {
89262306a36Sopenharmony_ci				kfree(intent);
89362306a36Sopenharmony_ci				return -ENOMEM;
89462306a36Sopenharmony_ci			}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci			intent->id = 0xbabababa;
89762306a36Sopenharmony_ci			intent->size = chunk_size + left_size;
89862306a36Sopenharmony_ci			intent->offset = 0;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci			channel->buf = intent;
90162306a36Sopenharmony_ci		} else {
90262306a36Sopenharmony_ci			intent = channel->buf;
90362306a36Sopenharmony_ci		}
90462306a36Sopenharmony_ci	} else {
90562306a36Sopenharmony_ci		liid = le32_to_cpu(hdr.msg.param2);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci		spin_lock_irqsave(&channel->intent_lock, flags);
90862306a36Sopenharmony_ci		intent = idr_find(&channel->liids, liid);
90962306a36Sopenharmony_ci		spin_unlock_irqrestore(&channel->intent_lock, flags);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci		if (!intent) {
91262306a36Sopenharmony_ci			dev_err(glink->dev,
91362306a36Sopenharmony_ci				"no intent found for channel %s intent %d",
91462306a36Sopenharmony_ci				channel->name, liid);
91562306a36Sopenharmony_ci			ret = -ENOENT;
91662306a36Sopenharmony_ci			goto advance_rx;
91762306a36Sopenharmony_ci		}
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	if (intent->size - intent->offset < chunk_size) {
92162306a36Sopenharmony_ci		dev_err(glink->dev, "Insufficient space in intent\n");
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci		/* The packet header lied, drop payload */
92462306a36Sopenharmony_ci		goto advance_rx;
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	qcom_glink_rx_peek(glink, intent->data + intent->offset,
92862306a36Sopenharmony_ci			   sizeof(hdr), chunk_size);
92962306a36Sopenharmony_ci	intent->offset += chunk_size;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	/* Handle message when no fragments remain to be received */
93262306a36Sopenharmony_ci	if (!left_size) {
93362306a36Sopenharmony_ci		spin_lock(&channel->recv_lock);
93462306a36Sopenharmony_ci		if (channel->ept.cb) {
93562306a36Sopenharmony_ci			channel->ept.cb(channel->ept.rpdev,
93662306a36Sopenharmony_ci					intent->data,
93762306a36Sopenharmony_ci					intent->offset,
93862306a36Sopenharmony_ci					channel->ept.priv,
93962306a36Sopenharmony_ci					RPMSG_ADDR_ANY);
94062306a36Sopenharmony_ci		}
94162306a36Sopenharmony_ci		spin_unlock(&channel->recv_lock);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci		intent->offset = 0;
94462306a36Sopenharmony_ci		channel->buf = NULL;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci		qcom_glink_rx_done(glink, channel, intent);
94762306a36Sopenharmony_ci	}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ciadvance_rx:
95062306a36Sopenharmony_ci	qcom_glink_rx_advance(glink, ALIGN(sizeof(hdr) + chunk_size, 8));
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	return ret;
95362306a36Sopenharmony_ci}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_cistatic void qcom_glink_handle_intent(struct qcom_glink *glink,
95662306a36Sopenharmony_ci				     unsigned int cid,
95762306a36Sopenharmony_ci				     unsigned int count,
95862306a36Sopenharmony_ci				     size_t avail)
95962306a36Sopenharmony_ci{
96062306a36Sopenharmony_ci	struct glink_core_rx_intent *intent;
96162306a36Sopenharmony_ci	struct glink_channel *channel;
96262306a36Sopenharmony_ci	struct intent_pair {
96362306a36Sopenharmony_ci		__le32 size;
96462306a36Sopenharmony_ci		__le32 iid;
96562306a36Sopenharmony_ci	};
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	struct {
96862306a36Sopenharmony_ci		struct glink_msg msg;
96962306a36Sopenharmony_ci		struct intent_pair intents[];
97062306a36Sopenharmony_ci	} __packed * msg;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	const size_t msglen = struct_size(msg, intents, count);
97362306a36Sopenharmony_ci	int ret;
97462306a36Sopenharmony_ci	int i;
97562306a36Sopenharmony_ci	unsigned long flags;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	if (avail < msglen) {
97862306a36Sopenharmony_ci		dev_dbg(glink->dev, "Not enough data in fifo\n");
97962306a36Sopenharmony_ci		return;
98062306a36Sopenharmony_ci	}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
98362306a36Sopenharmony_ci	channel = idr_find(&glink->rcids, cid);
98462306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
98562306a36Sopenharmony_ci	if (!channel) {
98662306a36Sopenharmony_ci		dev_err(glink->dev, "intents for non-existing channel\n");
98762306a36Sopenharmony_ci		qcom_glink_rx_advance(glink, ALIGN(msglen, 8));
98862306a36Sopenharmony_ci		return;
98962306a36Sopenharmony_ci	}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	msg = kmalloc(msglen, GFP_ATOMIC);
99262306a36Sopenharmony_ci	if (!msg)
99362306a36Sopenharmony_ci		return;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	qcom_glink_rx_peek(glink, msg, 0, msglen);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	for (i = 0; i < count; ++i) {
99862306a36Sopenharmony_ci		intent = kzalloc(sizeof(*intent), GFP_ATOMIC);
99962306a36Sopenharmony_ci		if (!intent)
100062306a36Sopenharmony_ci			break;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci		intent->id = le32_to_cpu(msg->intents[i].iid);
100362306a36Sopenharmony_ci		intent->size = le32_to_cpu(msg->intents[i].size);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci		spin_lock_irqsave(&channel->intent_lock, flags);
100662306a36Sopenharmony_ci		ret = idr_alloc(&channel->riids, intent,
100762306a36Sopenharmony_ci				intent->id, intent->id + 1, GFP_ATOMIC);
100862306a36Sopenharmony_ci		spin_unlock_irqrestore(&channel->intent_lock, flags);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci		if (ret < 0)
101162306a36Sopenharmony_ci			dev_err(glink->dev, "failed to store remote intent\n");
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	WRITE_ONCE(channel->intent_received, true);
101562306a36Sopenharmony_ci	wake_up_all(&channel->intent_req_wq);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	kfree(msg);
101862306a36Sopenharmony_ci	qcom_glink_rx_advance(glink, ALIGN(msglen, 8));
101962306a36Sopenharmony_ci}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_cistatic int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid)
102262306a36Sopenharmony_ci{
102362306a36Sopenharmony_ci	struct glink_channel *channel;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	spin_lock(&glink->idr_lock);
102662306a36Sopenharmony_ci	channel = idr_find(&glink->lcids, lcid);
102762306a36Sopenharmony_ci	spin_unlock(&glink->idr_lock);
102862306a36Sopenharmony_ci	if (!channel) {
102962306a36Sopenharmony_ci		dev_err(glink->dev, "Invalid open ack packet\n");
103062306a36Sopenharmony_ci		return -EINVAL;
103162306a36Sopenharmony_ci	}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	complete_all(&channel->open_ack);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	return 0;
103662306a36Sopenharmony_ci}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci/**
103962306a36Sopenharmony_ci * qcom_glink_set_flow_control() - convert a signal cmd to wire format and transmit
104062306a36Sopenharmony_ci * @ept:	Rpmsg endpoint for channel.
104162306a36Sopenharmony_ci * @pause:	Pause transmission
104262306a36Sopenharmony_ci * @dst:	destination address of the endpoint
104362306a36Sopenharmony_ci *
104462306a36Sopenharmony_ci * Return: 0 on success or standard Linux error code.
104562306a36Sopenharmony_ci */
104662306a36Sopenharmony_cistatic int qcom_glink_set_flow_control(struct rpmsg_endpoint *ept, bool pause, u32 dst)
104762306a36Sopenharmony_ci{
104862306a36Sopenharmony_ci	struct glink_channel *channel = to_glink_channel(ept);
104962306a36Sopenharmony_ci	struct qcom_glink *glink = channel->glink;
105062306a36Sopenharmony_ci	struct glink_msg msg;
105162306a36Sopenharmony_ci	u32 sigs = 0;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	if (pause)
105462306a36Sopenharmony_ci		sigs |= NATIVE_DTR_SIG | NATIVE_RTS_SIG;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	msg.cmd = cpu_to_le16(GLINK_CMD_SIGNALS);
105762306a36Sopenharmony_ci	msg.param1 = cpu_to_le16(channel->lcid);
105862306a36Sopenharmony_ci	msg.param2 = cpu_to_le32(sigs);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
106162306a36Sopenharmony_ci}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_cistatic void qcom_glink_handle_signals(struct qcom_glink *glink,
106462306a36Sopenharmony_ci				      unsigned int rcid, unsigned int sigs)
106562306a36Sopenharmony_ci{
106662306a36Sopenharmony_ci	struct glink_channel *channel;
106762306a36Sopenharmony_ci	unsigned long flags;
106862306a36Sopenharmony_ci	bool enable;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
107162306a36Sopenharmony_ci	channel = idr_find(&glink->rcids, rcid);
107262306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
107362306a36Sopenharmony_ci	if (!channel) {
107462306a36Sopenharmony_ci		dev_err(glink->dev, "signal for non-existing channel\n");
107562306a36Sopenharmony_ci		return;
107662306a36Sopenharmony_ci	}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	enable = sigs & NATIVE_DSR_SIG || sigs & NATIVE_CTS_SIG;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	if (channel->ept.flow_cb)
108162306a36Sopenharmony_ci		channel->ept.flow_cb(channel->ept.rpdev, channel->ept.priv, enable);
108262306a36Sopenharmony_ci}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_civoid qcom_glink_native_rx(struct qcom_glink *glink)
108562306a36Sopenharmony_ci{
108662306a36Sopenharmony_ci	struct glink_msg msg;
108762306a36Sopenharmony_ci	unsigned int param1;
108862306a36Sopenharmony_ci	unsigned int param2;
108962306a36Sopenharmony_ci	unsigned int avail;
109062306a36Sopenharmony_ci	unsigned int cmd;
109162306a36Sopenharmony_ci	int ret = 0;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	/* To wakeup any blocking writers */
109462306a36Sopenharmony_ci	wake_up_all(&glink->tx_avail_notify);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	for (;;) {
109762306a36Sopenharmony_ci		avail = qcom_glink_rx_avail(glink);
109862306a36Sopenharmony_ci		if (avail < sizeof(msg))
109962306a36Sopenharmony_ci			break;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci		qcom_glink_rx_peek(glink, &msg, 0, sizeof(msg));
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci		cmd = le16_to_cpu(msg.cmd);
110462306a36Sopenharmony_ci		param1 = le16_to_cpu(msg.param1);
110562306a36Sopenharmony_ci		param2 = le32_to_cpu(msg.param2);
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci		switch (cmd) {
110862306a36Sopenharmony_ci		case GLINK_CMD_VERSION:
110962306a36Sopenharmony_ci		case GLINK_CMD_VERSION_ACK:
111062306a36Sopenharmony_ci		case GLINK_CMD_CLOSE:
111162306a36Sopenharmony_ci		case GLINK_CMD_CLOSE_ACK:
111262306a36Sopenharmony_ci		case GLINK_CMD_RX_INTENT_REQ:
111362306a36Sopenharmony_ci			ret = qcom_glink_rx_defer(glink, 0);
111462306a36Sopenharmony_ci			break;
111562306a36Sopenharmony_ci		case GLINK_CMD_OPEN_ACK:
111662306a36Sopenharmony_ci			ret = qcom_glink_rx_open_ack(glink, param1);
111762306a36Sopenharmony_ci			qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8));
111862306a36Sopenharmony_ci			break;
111962306a36Sopenharmony_ci		case GLINK_CMD_OPEN:
112062306a36Sopenharmony_ci			ret = qcom_glink_rx_defer(glink, param2);
112162306a36Sopenharmony_ci			break;
112262306a36Sopenharmony_ci		case GLINK_CMD_TX_DATA:
112362306a36Sopenharmony_ci		case GLINK_CMD_TX_DATA_CONT:
112462306a36Sopenharmony_ci			ret = qcom_glink_rx_data(glink, avail);
112562306a36Sopenharmony_ci			break;
112662306a36Sopenharmony_ci		case GLINK_CMD_READ_NOTIF:
112762306a36Sopenharmony_ci			qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8));
112862306a36Sopenharmony_ci			qcom_glink_tx_kick(glink);
112962306a36Sopenharmony_ci			break;
113062306a36Sopenharmony_ci		case GLINK_CMD_INTENT:
113162306a36Sopenharmony_ci			qcom_glink_handle_intent(glink, param1, param2, avail);
113262306a36Sopenharmony_ci			break;
113362306a36Sopenharmony_ci		case GLINK_CMD_RX_DONE:
113462306a36Sopenharmony_ci			qcom_glink_handle_rx_done(glink, param1, param2, false);
113562306a36Sopenharmony_ci			qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8));
113662306a36Sopenharmony_ci			break;
113762306a36Sopenharmony_ci		case GLINK_CMD_RX_DONE_W_REUSE:
113862306a36Sopenharmony_ci			qcom_glink_handle_rx_done(glink, param1, param2, true);
113962306a36Sopenharmony_ci			qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8));
114062306a36Sopenharmony_ci			break;
114162306a36Sopenharmony_ci		case GLINK_CMD_RX_INTENT_REQ_ACK:
114262306a36Sopenharmony_ci			qcom_glink_handle_intent_req_ack(glink, param1, param2);
114362306a36Sopenharmony_ci			qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8));
114462306a36Sopenharmony_ci			break;
114562306a36Sopenharmony_ci		case GLINK_CMD_SIGNALS:
114662306a36Sopenharmony_ci			qcom_glink_handle_signals(glink, param1, param2);
114762306a36Sopenharmony_ci			qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8));
114862306a36Sopenharmony_ci			break;
114962306a36Sopenharmony_ci		default:
115062306a36Sopenharmony_ci			dev_err(glink->dev, "unhandled rx cmd: %d\n", cmd);
115162306a36Sopenharmony_ci			ret = -EINVAL;
115262306a36Sopenharmony_ci			break;
115362306a36Sopenharmony_ci		}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci		if (ret)
115662306a36Sopenharmony_ci			break;
115762306a36Sopenharmony_ci	}
115862306a36Sopenharmony_ci}
115962306a36Sopenharmony_ciEXPORT_SYMBOL(qcom_glink_native_rx);
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci/* Locally initiated rpmsg_create_ept */
116262306a36Sopenharmony_cistatic struct glink_channel *qcom_glink_create_local(struct qcom_glink *glink,
116362306a36Sopenharmony_ci						     const char *name)
116462306a36Sopenharmony_ci{
116562306a36Sopenharmony_ci	struct glink_channel *channel;
116662306a36Sopenharmony_ci	int ret;
116762306a36Sopenharmony_ci	unsigned long flags;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	channel = qcom_glink_alloc_channel(glink, name);
117062306a36Sopenharmony_ci	if (IS_ERR(channel))
117162306a36Sopenharmony_ci		return ERR_CAST(channel);
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	ret = qcom_glink_send_open_req(glink, channel);
117462306a36Sopenharmony_ci	if (ret)
117562306a36Sopenharmony_ci		goto release_channel;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	ret = wait_for_completion_timeout(&channel->open_ack, 5 * HZ);
117862306a36Sopenharmony_ci	if (!ret)
117962306a36Sopenharmony_ci		goto err_timeout;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	ret = wait_for_completion_timeout(&channel->open_req, 5 * HZ);
118262306a36Sopenharmony_ci	if (!ret)
118362306a36Sopenharmony_ci		goto err_timeout;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	qcom_glink_send_open_ack(glink, channel);
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	return channel;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_cierr_timeout:
119062306a36Sopenharmony_ci	/* qcom_glink_send_open_req() did register the channel in lcids*/
119162306a36Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
119262306a36Sopenharmony_ci	idr_remove(&glink->lcids, channel->lcid);
119362306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_cirelease_channel:
119662306a36Sopenharmony_ci	/* Release qcom_glink_send_open_req() reference */
119762306a36Sopenharmony_ci	kref_put(&channel->refcount, qcom_glink_channel_release);
119862306a36Sopenharmony_ci	/* Release qcom_glink_alloc_channel() reference */
119962306a36Sopenharmony_ci	kref_put(&channel->refcount, qcom_glink_channel_release);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	return ERR_PTR(-ETIMEDOUT);
120262306a36Sopenharmony_ci}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci/* Remote initiated rpmsg_create_ept */
120562306a36Sopenharmony_cistatic int qcom_glink_create_remote(struct qcom_glink *glink,
120662306a36Sopenharmony_ci				    struct glink_channel *channel)
120762306a36Sopenharmony_ci{
120862306a36Sopenharmony_ci	int ret;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	qcom_glink_send_open_ack(glink, channel);
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	ret = qcom_glink_send_open_req(glink, channel);
121362306a36Sopenharmony_ci	if (ret)
121462306a36Sopenharmony_ci		goto close_link;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	ret = wait_for_completion_timeout(&channel->open_ack, 5 * HZ);
121762306a36Sopenharmony_ci	if (!ret) {
121862306a36Sopenharmony_ci		ret = -ETIMEDOUT;
121962306a36Sopenharmony_ci		goto close_link;
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	return 0;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ciclose_link:
122562306a36Sopenharmony_ci	/*
122662306a36Sopenharmony_ci	 * Send a close request to "undo" our open-ack. The close-ack will
122762306a36Sopenharmony_ci	 * release qcom_glink_send_open_req() reference and the last reference
122862306a36Sopenharmony_ci	 * will be relesed after receiving remote_close or transport unregister
122962306a36Sopenharmony_ci	 * by calling qcom_glink_native_remove().
123062306a36Sopenharmony_ci	 */
123162306a36Sopenharmony_ci	qcom_glink_send_close_req(glink, channel);
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	return ret;
123462306a36Sopenharmony_ci}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_cistatic struct rpmsg_endpoint *qcom_glink_create_ept(struct rpmsg_device *rpdev,
123762306a36Sopenharmony_ci						    rpmsg_rx_cb_t cb,
123862306a36Sopenharmony_ci						    void *priv,
123962306a36Sopenharmony_ci						    struct rpmsg_channel_info
124062306a36Sopenharmony_ci									chinfo)
124162306a36Sopenharmony_ci{
124262306a36Sopenharmony_ci	struct glink_channel *parent = to_glink_channel(rpdev->ept);
124362306a36Sopenharmony_ci	struct glink_channel *channel;
124462306a36Sopenharmony_ci	struct qcom_glink *glink = parent->glink;
124562306a36Sopenharmony_ci	struct rpmsg_endpoint *ept;
124662306a36Sopenharmony_ci	const char *name = chinfo.name;
124762306a36Sopenharmony_ci	int cid;
124862306a36Sopenharmony_ci	int ret;
124962306a36Sopenharmony_ci	unsigned long flags;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
125262306a36Sopenharmony_ci	idr_for_each_entry(&glink->rcids, channel, cid) {
125362306a36Sopenharmony_ci		if (!strcmp(channel->name, name))
125462306a36Sopenharmony_ci			break;
125562306a36Sopenharmony_ci	}
125662306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	if (!channel) {
125962306a36Sopenharmony_ci		channel = qcom_glink_create_local(glink, name);
126062306a36Sopenharmony_ci		if (IS_ERR(channel))
126162306a36Sopenharmony_ci			return NULL;
126262306a36Sopenharmony_ci	} else {
126362306a36Sopenharmony_ci		ret = qcom_glink_create_remote(glink, channel);
126462306a36Sopenharmony_ci		if (ret)
126562306a36Sopenharmony_ci			return NULL;
126662306a36Sopenharmony_ci	}
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	ept = &channel->ept;
126962306a36Sopenharmony_ci	ept->rpdev = rpdev;
127062306a36Sopenharmony_ci	ept->cb = cb;
127162306a36Sopenharmony_ci	ept->priv = priv;
127262306a36Sopenharmony_ci	ept->ops = &glink_endpoint_ops;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	return ept;
127562306a36Sopenharmony_ci}
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_cistatic int qcom_glink_announce_create(struct rpmsg_device *rpdev)
127862306a36Sopenharmony_ci{
127962306a36Sopenharmony_ci	struct glink_channel *channel = to_glink_channel(rpdev->ept);
128062306a36Sopenharmony_ci	struct device_node *np = rpdev->dev.of_node;
128162306a36Sopenharmony_ci	struct qcom_glink *glink = channel->glink;
128262306a36Sopenharmony_ci	struct glink_core_rx_intent *intent;
128362306a36Sopenharmony_ci	const struct property *prop = NULL;
128462306a36Sopenharmony_ci	__be32 defaults[] = { cpu_to_be32(SZ_1K), cpu_to_be32(5) };
128562306a36Sopenharmony_ci	int num_intents;
128662306a36Sopenharmony_ci	int num_groups = 1;
128762306a36Sopenharmony_ci	__be32 *val = defaults;
128862306a36Sopenharmony_ci	int size;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	if (glink->intentless || !completion_done(&channel->open_ack))
129162306a36Sopenharmony_ci		return 0;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	prop = of_find_property(np, "qcom,intents", NULL);
129462306a36Sopenharmony_ci	if (prop) {
129562306a36Sopenharmony_ci		val = prop->value;
129662306a36Sopenharmony_ci		num_groups = prop->length / sizeof(u32) / 2;
129762306a36Sopenharmony_ci	}
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	/* Channel is now open, advertise base set of intents */
130062306a36Sopenharmony_ci	while (num_groups--) {
130162306a36Sopenharmony_ci		size = be32_to_cpup(val++);
130262306a36Sopenharmony_ci		num_intents = be32_to_cpup(val++);
130362306a36Sopenharmony_ci		while (num_intents--) {
130462306a36Sopenharmony_ci			intent = qcom_glink_alloc_intent(glink, channel, size,
130562306a36Sopenharmony_ci							 true);
130662306a36Sopenharmony_ci			if (!intent)
130762306a36Sopenharmony_ci				break;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci			qcom_glink_advertise_intent(glink, channel, intent);
131062306a36Sopenharmony_ci		}
131162306a36Sopenharmony_ci	}
131262306a36Sopenharmony_ci	return 0;
131362306a36Sopenharmony_ci}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_cistatic void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept)
131662306a36Sopenharmony_ci{
131762306a36Sopenharmony_ci	struct glink_channel *channel = to_glink_channel(ept);
131862306a36Sopenharmony_ci	struct qcom_glink *glink = channel->glink;
131962306a36Sopenharmony_ci	unsigned long flags;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	spin_lock_irqsave(&channel->recv_lock, flags);
132262306a36Sopenharmony_ci	channel->ept.cb = NULL;
132362306a36Sopenharmony_ci	spin_unlock_irqrestore(&channel->recv_lock, flags);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	/* Decouple the potential rpdev from the channel */
132662306a36Sopenharmony_ci	channel->rpdev = NULL;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	qcom_glink_send_close_req(glink, channel);
132962306a36Sopenharmony_ci}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_cistatic int qcom_glink_request_intent(struct qcom_glink *glink,
133262306a36Sopenharmony_ci				     struct glink_channel *channel,
133362306a36Sopenharmony_ci				     size_t size)
133462306a36Sopenharmony_ci{
133562306a36Sopenharmony_ci	struct {
133662306a36Sopenharmony_ci		u16 id;
133762306a36Sopenharmony_ci		u16 cid;
133862306a36Sopenharmony_ci		u32 size;
133962306a36Sopenharmony_ci	} __packed cmd;
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	int ret;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	mutex_lock(&channel->intent_req_lock);
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	WRITE_ONCE(channel->intent_req_result, -1);
134662306a36Sopenharmony_ci	WRITE_ONCE(channel->intent_received, false);
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	cmd.id = GLINK_CMD_RX_INTENT_REQ;
134962306a36Sopenharmony_ci	cmd.cid = channel->lcid;
135062306a36Sopenharmony_ci	cmd.size = size;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	ret = qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true);
135362306a36Sopenharmony_ci	if (ret)
135462306a36Sopenharmony_ci		goto unlock;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	ret = wait_event_timeout(channel->intent_req_wq,
135762306a36Sopenharmony_ci				 READ_ONCE(channel->intent_req_result) >= 0 &&
135862306a36Sopenharmony_ci				 READ_ONCE(channel->intent_received),
135962306a36Sopenharmony_ci				 10 * HZ);
136062306a36Sopenharmony_ci	if (!ret) {
136162306a36Sopenharmony_ci		dev_err(glink->dev, "intent request timed out\n");
136262306a36Sopenharmony_ci		ret = -ETIMEDOUT;
136362306a36Sopenharmony_ci	} else {
136462306a36Sopenharmony_ci		ret = READ_ONCE(channel->intent_req_result) ? 0 : -ECANCELED;
136562306a36Sopenharmony_ci	}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ciunlock:
136862306a36Sopenharmony_ci	mutex_unlock(&channel->intent_req_lock);
136962306a36Sopenharmony_ci	return ret;
137062306a36Sopenharmony_ci}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_cistatic int __qcom_glink_send(struct glink_channel *channel,
137362306a36Sopenharmony_ci			     void *data, int len, bool wait)
137462306a36Sopenharmony_ci{
137562306a36Sopenharmony_ci	struct qcom_glink *glink = channel->glink;
137662306a36Sopenharmony_ci	struct glink_core_rx_intent *intent = NULL;
137762306a36Sopenharmony_ci	struct glink_core_rx_intent *tmp;
137862306a36Sopenharmony_ci	int iid = 0;
137962306a36Sopenharmony_ci	struct {
138062306a36Sopenharmony_ci		struct glink_msg msg;
138162306a36Sopenharmony_ci		__le32 chunk_size;
138262306a36Sopenharmony_ci		__le32 left_size;
138362306a36Sopenharmony_ci	} __packed req;
138462306a36Sopenharmony_ci	int ret;
138562306a36Sopenharmony_ci	unsigned long flags;
138662306a36Sopenharmony_ci	int chunk_size = len;
138762306a36Sopenharmony_ci	size_t offset = 0;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	if (!glink->intentless) {
139062306a36Sopenharmony_ci		while (!intent) {
139162306a36Sopenharmony_ci			spin_lock_irqsave(&channel->intent_lock, flags);
139262306a36Sopenharmony_ci			idr_for_each_entry(&channel->riids, tmp, iid) {
139362306a36Sopenharmony_ci				if (tmp->size >= len && !tmp->in_use) {
139462306a36Sopenharmony_ci					if (!intent)
139562306a36Sopenharmony_ci						intent = tmp;
139662306a36Sopenharmony_ci					else if (intent->size > tmp->size)
139762306a36Sopenharmony_ci						intent = tmp;
139862306a36Sopenharmony_ci					if (intent->size == len)
139962306a36Sopenharmony_ci						break;
140062306a36Sopenharmony_ci				}
140162306a36Sopenharmony_ci			}
140262306a36Sopenharmony_ci			if (intent)
140362306a36Sopenharmony_ci				intent->in_use = true;
140462306a36Sopenharmony_ci			spin_unlock_irqrestore(&channel->intent_lock, flags);
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci			/* We found an available intent */
140762306a36Sopenharmony_ci			if (intent)
140862306a36Sopenharmony_ci				break;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci			if (!wait)
141162306a36Sopenharmony_ci				return -EBUSY;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci			ret = qcom_glink_request_intent(glink, channel, len);
141462306a36Sopenharmony_ci			if (ret < 0)
141562306a36Sopenharmony_ci				return ret;
141662306a36Sopenharmony_ci		}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci		iid = intent->id;
141962306a36Sopenharmony_ci	}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	while (offset < len) {
142262306a36Sopenharmony_ci		chunk_size = len - offset;
142362306a36Sopenharmony_ci		if (chunk_size > SZ_8K && wait)
142462306a36Sopenharmony_ci			chunk_size = SZ_8K;
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci		req.msg.cmd = cpu_to_le16(offset == 0 ? GLINK_CMD_TX_DATA : GLINK_CMD_TX_DATA_CONT);
142762306a36Sopenharmony_ci		req.msg.param1 = cpu_to_le16(channel->lcid);
142862306a36Sopenharmony_ci		req.msg.param2 = cpu_to_le32(iid);
142962306a36Sopenharmony_ci		req.chunk_size = cpu_to_le32(chunk_size);
143062306a36Sopenharmony_ci		req.left_size = cpu_to_le32(len - offset - chunk_size);
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci		ret = qcom_glink_tx(glink, &req, sizeof(req), data + offset, chunk_size, wait);
143362306a36Sopenharmony_ci		if (ret) {
143462306a36Sopenharmony_ci			/* Mark intent available if we failed */
143562306a36Sopenharmony_ci			if (intent)
143662306a36Sopenharmony_ci				intent->in_use = false;
143762306a36Sopenharmony_ci			return ret;
143862306a36Sopenharmony_ci		}
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci		offset += chunk_size;
144162306a36Sopenharmony_ci	}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	return 0;
144462306a36Sopenharmony_ci}
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_cistatic int qcom_glink_send(struct rpmsg_endpoint *ept, void *data, int len)
144762306a36Sopenharmony_ci{
144862306a36Sopenharmony_ci	struct glink_channel *channel = to_glink_channel(ept);
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	return __qcom_glink_send(channel, data, len, true);
145162306a36Sopenharmony_ci}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_cistatic int qcom_glink_trysend(struct rpmsg_endpoint *ept, void *data, int len)
145462306a36Sopenharmony_ci{
145562306a36Sopenharmony_ci	struct glink_channel *channel = to_glink_channel(ept);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	return __qcom_glink_send(channel, data, len, false);
145862306a36Sopenharmony_ci}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_cistatic int qcom_glink_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst)
146162306a36Sopenharmony_ci{
146262306a36Sopenharmony_ci	struct glink_channel *channel = to_glink_channel(ept);
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	return __qcom_glink_send(channel, data, len, true);
146562306a36Sopenharmony_ci}
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_cistatic int qcom_glink_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst)
146862306a36Sopenharmony_ci{
146962306a36Sopenharmony_ci	struct glink_channel *channel = to_glink_channel(ept);
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	return __qcom_glink_send(channel, data, len, false);
147262306a36Sopenharmony_ci}
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci/*
147562306a36Sopenharmony_ci * Finds the device_node for the glink child interested in this channel.
147662306a36Sopenharmony_ci */
147762306a36Sopenharmony_cistatic struct device_node *qcom_glink_match_channel(struct device_node *node,
147862306a36Sopenharmony_ci						    const char *channel)
147962306a36Sopenharmony_ci{
148062306a36Sopenharmony_ci	struct device_node *child;
148162306a36Sopenharmony_ci	const char *name;
148262306a36Sopenharmony_ci	const char *key;
148362306a36Sopenharmony_ci	int ret;
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	for_each_available_child_of_node(node, child) {
148662306a36Sopenharmony_ci		key = "qcom,glink-channels";
148762306a36Sopenharmony_ci		ret = of_property_read_string(child, key, &name);
148862306a36Sopenharmony_ci		if (ret)
148962306a36Sopenharmony_ci			continue;
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci		if (strcmp(name, channel) == 0)
149262306a36Sopenharmony_ci			return child;
149362306a36Sopenharmony_ci	}
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	return NULL;
149662306a36Sopenharmony_ci}
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_cistatic const struct rpmsg_device_ops glink_device_ops = {
149962306a36Sopenharmony_ci	.create_ept = qcom_glink_create_ept,
150062306a36Sopenharmony_ci	.announce_create = qcom_glink_announce_create,
150162306a36Sopenharmony_ci};
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_cistatic const struct rpmsg_endpoint_ops glink_endpoint_ops = {
150462306a36Sopenharmony_ci	.destroy_ept = qcom_glink_destroy_ept,
150562306a36Sopenharmony_ci	.send = qcom_glink_send,
150662306a36Sopenharmony_ci	.sendto = qcom_glink_sendto,
150762306a36Sopenharmony_ci	.trysend = qcom_glink_trysend,
150862306a36Sopenharmony_ci	.trysendto = qcom_glink_trysendto,
150962306a36Sopenharmony_ci	.set_flow_control = qcom_glink_set_flow_control,
151062306a36Sopenharmony_ci};
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_cistatic void qcom_glink_rpdev_release(struct device *dev)
151362306a36Sopenharmony_ci{
151462306a36Sopenharmony_ci	struct rpmsg_device *rpdev = to_rpmsg_device(dev);
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	kfree(rpdev->driver_override);
151762306a36Sopenharmony_ci	kfree(rpdev);
151862306a36Sopenharmony_ci}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_cistatic int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid,
152162306a36Sopenharmony_ci			      char *name)
152262306a36Sopenharmony_ci{
152362306a36Sopenharmony_ci	struct glink_channel *channel;
152462306a36Sopenharmony_ci	struct rpmsg_device *rpdev;
152562306a36Sopenharmony_ci	bool create_device = false;
152662306a36Sopenharmony_ci	struct device_node *node;
152762306a36Sopenharmony_ci	int lcid;
152862306a36Sopenharmony_ci	int ret;
152962306a36Sopenharmony_ci	unsigned long flags;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
153262306a36Sopenharmony_ci	idr_for_each_entry(&glink->lcids, channel, lcid) {
153362306a36Sopenharmony_ci		if (!strcmp(channel->name, name))
153462306a36Sopenharmony_ci			break;
153562306a36Sopenharmony_ci	}
153662306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	if (!channel) {
153962306a36Sopenharmony_ci		channel = qcom_glink_alloc_channel(glink, name);
154062306a36Sopenharmony_ci		if (IS_ERR(channel))
154162306a36Sopenharmony_ci			return PTR_ERR(channel);
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci		/* The opening dance was initiated by the remote */
154462306a36Sopenharmony_ci		create_device = true;
154562306a36Sopenharmony_ci	}
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
154862306a36Sopenharmony_ci	ret = idr_alloc(&glink->rcids, channel, rcid, rcid + 1, GFP_ATOMIC);
154962306a36Sopenharmony_ci	if (ret < 0) {
155062306a36Sopenharmony_ci		dev_err(glink->dev, "Unable to insert channel into rcid list\n");
155162306a36Sopenharmony_ci		spin_unlock_irqrestore(&glink->idr_lock, flags);
155262306a36Sopenharmony_ci		goto free_channel;
155362306a36Sopenharmony_ci	}
155462306a36Sopenharmony_ci	channel->rcid = ret;
155562306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	complete_all(&channel->open_req);
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	if (create_device) {
156062306a36Sopenharmony_ci		rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL);
156162306a36Sopenharmony_ci		if (!rpdev) {
156262306a36Sopenharmony_ci			ret = -ENOMEM;
156362306a36Sopenharmony_ci			goto rcid_remove;
156462306a36Sopenharmony_ci		}
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci		rpdev->ept = &channel->ept;
156762306a36Sopenharmony_ci		strscpy_pad(rpdev->id.name, name, RPMSG_NAME_SIZE);
156862306a36Sopenharmony_ci		rpdev->src = RPMSG_ADDR_ANY;
156962306a36Sopenharmony_ci		rpdev->dst = RPMSG_ADDR_ANY;
157062306a36Sopenharmony_ci		rpdev->ops = &glink_device_ops;
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci		node = qcom_glink_match_channel(glink->dev->of_node, name);
157362306a36Sopenharmony_ci		rpdev->dev.of_node = node;
157462306a36Sopenharmony_ci		rpdev->dev.parent = glink->dev;
157562306a36Sopenharmony_ci		rpdev->dev.release = qcom_glink_rpdev_release;
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci		ret = rpmsg_register_device(rpdev);
157862306a36Sopenharmony_ci		if (ret)
157962306a36Sopenharmony_ci			goto rcid_remove;
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci		channel->rpdev = rpdev;
158262306a36Sopenharmony_ci	}
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	return 0;
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_circid_remove:
158762306a36Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
158862306a36Sopenharmony_ci	idr_remove(&glink->rcids, channel->rcid);
158962306a36Sopenharmony_ci	channel->rcid = 0;
159062306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
159162306a36Sopenharmony_cifree_channel:
159262306a36Sopenharmony_ci	/* Release the reference, iff we took it */
159362306a36Sopenharmony_ci	if (create_device)
159462306a36Sopenharmony_ci		kref_put(&channel->refcount, qcom_glink_channel_release);
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	return ret;
159762306a36Sopenharmony_ci}
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_cistatic void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid)
160062306a36Sopenharmony_ci{
160162306a36Sopenharmony_ci	struct rpmsg_channel_info chinfo;
160262306a36Sopenharmony_ci	struct glink_channel *channel;
160362306a36Sopenharmony_ci	unsigned long flags;
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
160662306a36Sopenharmony_ci	channel = idr_find(&glink->rcids, rcid);
160762306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
160862306a36Sopenharmony_ci	if (WARN(!channel, "close request on unknown channel\n"))
160962306a36Sopenharmony_ci		return;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	/* cancel pending rx_done work */
161262306a36Sopenharmony_ci	cancel_work_sync(&channel->intent_work);
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	if (channel->rpdev) {
161562306a36Sopenharmony_ci		strscpy_pad(chinfo.name, channel->name, sizeof(chinfo.name));
161662306a36Sopenharmony_ci		chinfo.src = RPMSG_ADDR_ANY;
161762306a36Sopenharmony_ci		chinfo.dst = RPMSG_ADDR_ANY;
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci		rpmsg_unregister_device(glink->dev, &chinfo);
162062306a36Sopenharmony_ci	}
162162306a36Sopenharmony_ci	channel->rpdev = NULL;
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	qcom_glink_send_close_ack(glink, channel->rcid);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
162662306a36Sopenharmony_ci	idr_remove(&glink->rcids, channel->rcid);
162762306a36Sopenharmony_ci	channel->rcid = 0;
162862306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	kref_put(&channel->refcount, qcom_glink_channel_release);
163162306a36Sopenharmony_ci}
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_cistatic void qcom_glink_rx_close_ack(struct qcom_glink *glink, unsigned int lcid)
163462306a36Sopenharmony_ci{
163562306a36Sopenharmony_ci	struct rpmsg_channel_info chinfo;
163662306a36Sopenharmony_ci	struct glink_channel *channel;
163762306a36Sopenharmony_ci	unsigned long flags;
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	/* To wakeup any blocking writers */
164062306a36Sopenharmony_ci	wake_up_all(&glink->tx_avail_notify);
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
164362306a36Sopenharmony_ci	channel = idr_find(&glink->lcids, lcid);
164462306a36Sopenharmony_ci	if (WARN(!channel, "close ack on unknown channel\n")) {
164562306a36Sopenharmony_ci		spin_unlock_irqrestore(&glink->idr_lock, flags);
164662306a36Sopenharmony_ci		return;
164762306a36Sopenharmony_ci	}
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci	idr_remove(&glink->lcids, channel->lcid);
165062306a36Sopenharmony_ci	channel->lcid = 0;
165162306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	/* Decouple the potential rpdev from the channel */
165462306a36Sopenharmony_ci	if (channel->rpdev) {
165562306a36Sopenharmony_ci		strscpy(chinfo.name, channel->name, sizeof(chinfo.name));
165662306a36Sopenharmony_ci		chinfo.src = RPMSG_ADDR_ANY;
165762306a36Sopenharmony_ci		chinfo.dst = RPMSG_ADDR_ANY;
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci		rpmsg_unregister_device(glink->dev, &chinfo);
166062306a36Sopenharmony_ci	}
166162306a36Sopenharmony_ci	channel->rpdev = NULL;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	kref_put(&channel->refcount, qcom_glink_channel_release);
166462306a36Sopenharmony_ci}
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_cistatic void qcom_glink_work(struct work_struct *work)
166762306a36Sopenharmony_ci{
166862306a36Sopenharmony_ci	struct qcom_glink *glink = container_of(work, struct qcom_glink,
166962306a36Sopenharmony_ci						rx_work);
167062306a36Sopenharmony_ci	struct glink_defer_cmd *dcmd;
167162306a36Sopenharmony_ci	struct glink_msg *msg;
167262306a36Sopenharmony_ci	unsigned long flags;
167362306a36Sopenharmony_ci	unsigned int param1;
167462306a36Sopenharmony_ci	unsigned int param2;
167562306a36Sopenharmony_ci	unsigned int cmd;
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	for (;;) {
167862306a36Sopenharmony_ci		spin_lock_irqsave(&glink->rx_lock, flags);
167962306a36Sopenharmony_ci		if (list_empty(&glink->rx_queue)) {
168062306a36Sopenharmony_ci			spin_unlock_irqrestore(&glink->rx_lock, flags);
168162306a36Sopenharmony_ci			break;
168262306a36Sopenharmony_ci		}
168362306a36Sopenharmony_ci		dcmd = list_first_entry(&glink->rx_queue,
168462306a36Sopenharmony_ci					struct glink_defer_cmd, node);
168562306a36Sopenharmony_ci		list_del(&dcmd->node);
168662306a36Sopenharmony_ci		spin_unlock_irqrestore(&glink->rx_lock, flags);
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci		msg = &dcmd->msg;
168962306a36Sopenharmony_ci		cmd = le16_to_cpu(msg->cmd);
169062306a36Sopenharmony_ci		param1 = le16_to_cpu(msg->param1);
169162306a36Sopenharmony_ci		param2 = le32_to_cpu(msg->param2);
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci		switch (cmd) {
169462306a36Sopenharmony_ci		case GLINK_CMD_VERSION:
169562306a36Sopenharmony_ci			qcom_glink_receive_version(glink, param1, param2);
169662306a36Sopenharmony_ci			break;
169762306a36Sopenharmony_ci		case GLINK_CMD_VERSION_ACK:
169862306a36Sopenharmony_ci			qcom_glink_receive_version_ack(glink, param1, param2);
169962306a36Sopenharmony_ci			break;
170062306a36Sopenharmony_ci		case GLINK_CMD_OPEN:
170162306a36Sopenharmony_ci			qcom_glink_rx_open(glink, param1, msg->data);
170262306a36Sopenharmony_ci			break;
170362306a36Sopenharmony_ci		case GLINK_CMD_CLOSE:
170462306a36Sopenharmony_ci			qcom_glink_rx_close(glink, param1);
170562306a36Sopenharmony_ci			break;
170662306a36Sopenharmony_ci		case GLINK_CMD_CLOSE_ACK:
170762306a36Sopenharmony_ci			qcom_glink_rx_close_ack(glink, param1);
170862306a36Sopenharmony_ci			break;
170962306a36Sopenharmony_ci		case GLINK_CMD_RX_INTENT_REQ:
171062306a36Sopenharmony_ci			qcom_glink_handle_intent_req(glink, param1, param2);
171162306a36Sopenharmony_ci			break;
171262306a36Sopenharmony_ci		default:
171362306a36Sopenharmony_ci			WARN(1, "Unknown defer object %d\n", cmd);
171462306a36Sopenharmony_ci			break;
171562306a36Sopenharmony_ci		}
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci		kfree(dcmd);
171862306a36Sopenharmony_ci	}
171962306a36Sopenharmony_ci}
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_cistatic void qcom_glink_cancel_rx_work(struct qcom_glink *glink)
172262306a36Sopenharmony_ci{
172362306a36Sopenharmony_ci	struct glink_defer_cmd *dcmd;
172462306a36Sopenharmony_ci	struct glink_defer_cmd *tmp;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	/* cancel any pending deferred rx_work */
172762306a36Sopenharmony_ci	cancel_work_sync(&glink->rx_work);
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	list_for_each_entry_safe(dcmd, tmp, &glink->rx_queue, node)
173062306a36Sopenharmony_ci		kfree(dcmd);
173162306a36Sopenharmony_ci}
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_cistatic ssize_t rpmsg_name_show(struct device *dev,
173462306a36Sopenharmony_ci			       struct device_attribute *attr, char *buf)
173562306a36Sopenharmony_ci{
173662306a36Sopenharmony_ci	int ret = 0;
173762306a36Sopenharmony_ci	const char *name;
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	ret = of_property_read_string(dev->of_node, "label", &name);
174062306a36Sopenharmony_ci	if (ret < 0)
174162306a36Sopenharmony_ci		name = dev->of_node->name;
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", name);
174462306a36Sopenharmony_ci}
174562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(rpmsg_name);
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_cistatic struct attribute *qcom_glink_attrs[] = {
174862306a36Sopenharmony_ci	&dev_attr_rpmsg_name.attr,
174962306a36Sopenharmony_ci	NULL
175062306a36Sopenharmony_ci};
175162306a36Sopenharmony_ciATTRIBUTE_GROUPS(qcom_glink);
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_cistatic void qcom_glink_device_release(struct device *dev)
175462306a36Sopenharmony_ci{
175562306a36Sopenharmony_ci	struct rpmsg_device *rpdev = to_rpmsg_device(dev);
175662306a36Sopenharmony_ci	struct glink_channel *channel = to_glink_channel(rpdev->ept);
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	/* Release qcom_glink_alloc_channel() reference */
175962306a36Sopenharmony_ci	kref_put(&channel->refcount, qcom_glink_channel_release);
176062306a36Sopenharmony_ci	kfree(rpdev->driver_override);
176162306a36Sopenharmony_ci	kfree(rpdev);
176262306a36Sopenharmony_ci}
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_cistatic int qcom_glink_create_chrdev(struct qcom_glink *glink)
176562306a36Sopenharmony_ci{
176662306a36Sopenharmony_ci	struct rpmsg_device *rpdev;
176762306a36Sopenharmony_ci	struct glink_channel *channel;
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL);
177062306a36Sopenharmony_ci	if (!rpdev)
177162306a36Sopenharmony_ci		return -ENOMEM;
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	channel = qcom_glink_alloc_channel(glink, "rpmsg_chrdev");
177462306a36Sopenharmony_ci	if (IS_ERR(channel)) {
177562306a36Sopenharmony_ci		kfree(rpdev);
177662306a36Sopenharmony_ci		return PTR_ERR(channel);
177762306a36Sopenharmony_ci	}
177862306a36Sopenharmony_ci	channel->rpdev = rpdev;
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	rpdev->ept = &channel->ept;
178162306a36Sopenharmony_ci	rpdev->ops = &glink_device_ops;
178262306a36Sopenharmony_ci	rpdev->dev.parent = glink->dev;
178362306a36Sopenharmony_ci	rpdev->dev.release = qcom_glink_device_release;
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	return rpmsg_ctrldev_register_device(rpdev);
178662306a36Sopenharmony_ci}
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_cistruct qcom_glink *qcom_glink_native_probe(struct device *dev,
178962306a36Sopenharmony_ci					   unsigned long features,
179062306a36Sopenharmony_ci					   struct qcom_glink_pipe *rx,
179162306a36Sopenharmony_ci					   struct qcom_glink_pipe *tx,
179262306a36Sopenharmony_ci					   bool intentless)
179362306a36Sopenharmony_ci{
179462306a36Sopenharmony_ci	int ret;
179562306a36Sopenharmony_ci	struct qcom_glink *glink;
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	glink = devm_kzalloc(dev, sizeof(*glink), GFP_KERNEL);
179862306a36Sopenharmony_ci	if (!glink)
179962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	glink->dev = dev;
180262306a36Sopenharmony_ci	glink->tx_pipe = tx;
180362306a36Sopenharmony_ci	glink->rx_pipe = rx;
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	glink->features = features;
180662306a36Sopenharmony_ci	glink->intentless = intentless;
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	spin_lock_init(&glink->tx_lock);
180962306a36Sopenharmony_ci	spin_lock_init(&glink->rx_lock);
181062306a36Sopenharmony_ci	INIT_LIST_HEAD(&glink->rx_queue);
181162306a36Sopenharmony_ci	INIT_WORK(&glink->rx_work, qcom_glink_work);
181262306a36Sopenharmony_ci	init_waitqueue_head(&glink->tx_avail_notify);
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	spin_lock_init(&glink->idr_lock);
181562306a36Sopenharmony_ci	idr_init(&glink->lcids);
181662306a36Sopenharmony_ci	idr_init(&glink->rcids);
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	glink->dev->groups = qcom_glink_groups;
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	ret = device_add_groups(dev, qcom_glink_groups);
182162306a36Sopenharmony_ci	if (ret)
182262306a36Sopenharmony_ci		dev_err(dev, "failed to add groups\n");
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	ret = qcom_glink_send_version(glink);
182562306a36Sopenharmony_ci	if (ret)
182662306a36Sopenharmony_ci		return ERR_PTR(ret);
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci	ret = qcom_glink_create_chrdev(glink);
182962306a36Sopenharmony_ci	if (ret)
183062306a36Sopenharmony_ci		dev_err(glink->dev, "failed to register chrdev\n");
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ci	return glink;
183362306a36Sopenharmony_ci}
183462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_glink_native_probe);
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_cistatic int qcom_glink_remove_device(struct device *dev, void *data)
183762306a36Sopenharmony_ci{
183862306a36Sopenharmony_ci	device_unregister(dev);
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	return 0;
184162306a36Sopenharmony_ci}
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_civoid qcom_glink_native_remove(struct qcom_glink *glink)
184462306a36Sopenharmony_ci{
184562306a36Sopenharmony_ci	struct glink_channel *channel;
184662306a36Sopenharmony_ci	unsigned long flags;
184762306a36Sopenharmony_ci	int cid;
184862306a36Sopenharmony_ci	int ret;
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci	qcom_glink_cancel_rx_work(glink);
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	/* Fail all attempts at sending messages */
185362306a36Sopenharmony_ci	spin_lock_irqsave(&glink->tx_lock, flags);
185462306a36Sopenharmony_ci	glink->abort_tx = true;
185562306a36Sopenharmony_ci	wake_up_all(&glink->tx_avail_notify);
185662306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->tx_lock, flags);
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	/* Abort any senders waiting for intent requests */
185962306a36Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
186062306a36Sopenharmony_ci	idr_for_each_entry(&glink->lcids, channel, cid)
186162306a36Sopenharmony_ci		qcom_glink_intent_req_abort(channel);
186262306a36Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	ret = device_for_each_child(glink->dev, NULL, qcom_glink_remove_device);
186562306a36Sopenharmony_ci	if (ret)
186662306a36Sopenharmony_ci		dev_warn(glink->dev, "Can't remove GLINK devices: %d\n", ret);
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	/* Release any defunct local channels, waiting for close-ack */
186962306a36Sopenharmony_ci	idr_for_each_entry(&glink->lcids, channel, cid)
187062306a36Sopenharmony_ci		kref_put(&channel->refcount, qcom_glink_channel_release);
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	/* Release any defunct local channels, waiting for close-req */
187362306a36Sopenharmony_ci	idr_for_each_entry(&glink->rcids, channel, cid)
187462306a36Sopenharmony_ci		kref_put(&channel->refcount, qcom_glink_channel_release);
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	idr_destroy(&glink->lcids);
187762306a36Sopenharmony_ci	idr_destroy(&glink->rcids);
187862306a36Sopenharmony_ci}
187962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_glink_native_remove);
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm GLINK driver");
188262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1883