18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2016-2017, Linaro Ltd
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/idr.h>
78c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
88c2ecf20Sopenharmony_ci#include <linux/io.h>
98c2ecf20Sopenharmony_ci#include <linux/list.h>
108c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/of.h>
138c2ecf20Sopenharmony_ci#include <linux/of_address.h>
148c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/regmap.h>
178c2ecf20Sopenharmony_ci#include <linux/rpmsg.h>
188c2ecf20Sopenharmony_ci#include <linux/sizes.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
218c2ecf20Sopenharmony_ci#include <linux/mailbox_client.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "rpmsg_internal.h"
248c2ecf20Sopenharmony_ci#include "qcom_glink_native.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define GLINK_NAME_SIZE		32
278c2ecf20Sopenharmony_ci#define GLINK_VERSION_1		1
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define RPM_GLINK_CID_MIN	1
308c2ecf20Sopenharmony_ci#define RPM_GLINK_CID_MAX	65536
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct glink_msg {
338c2ecf20Sopenharmony_ci	__le16 cmd;
348c2ecf20Sopenharmony_ci	__le16 param1;
358c2ecf20Sopenharmony_ci	__le32 param2;
368c2ecf20Sopenharmony_ci	u8 data[];
378c2ecf20Sopenharmony_ci} __packed;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/**
408c2ecf20Sopenharmony_ci * struct glink_defer_cmd - deferred incoming control message
418c2ecf20Sopenharmony_ci * @node:	list node
428c2ecf20Sopenharmony_ci * @msg:	message header
438c2ecf20Sopenharmony_ci * @data:	payload of the message
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci * Copy of a received control message, to be added to @rx_queue and processed
468c2ecf20Sopenharmony_ci * by @rx_work of @qcom_glink.
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_cistruct glink_defer_cmd {
498c2ecf20Sopenharmony_ci	struct list_head node;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	struct glink_msg msg;
528c2ecf20Sopenharmony_ci	u8 data[];
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/**
568c2ecf20Sopenharmony_ci * struct glink_core_rx_intent - RX intent
578c2ecf20Sopenharmony_ci * RX intent
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci * @data: pointer to the data (may be NULL for zero-copy)
608c2ecf20Sopenharmony_ci * @id: remote or local intent ID
618c2ecf20Sopenharmony_ci * @size: size of the original intent (do not modify)
628c2ecf20Sopenharmony_ci * @reuse: To mark if the intent can be reused after first use
638c2ecf20Sopenharmony_ci * @in_use: To mark if intent is already in use for the channel
648c2ecf20Sopenharmony_ci * @offset: next write offset (initially 0)
658c2ecf20Sopenharmony_ci * @node:	list node
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_cistruct glink_core_rx_intent {
688c2ecf20Sopenharmony_ci	void *data;
698c2ecf20Sopenharmony_ci	u32 id;
708c2ecf20Sopenharmony_ci	size_t size;
718c2ecf20Sopenharmony_ci	bool reuse;
728c2ecf20Sopenharmony_ci	bool in_use;
738c2ecf20Sopenharmony_ci	u32 offset;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	struct list_head node;
768c2ecf20Sopenharmony_ci};
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/**
798c2ecf20Sopenharmony_ci * struct qcom_glink - driver context, relates to one remote subsystem
808c2ecf20Sopenharmony_ci * @dev:	reference to the associated struct device
818c2ecf20Sopenharmony_ci * @mbox_client: mailbox client
828c2ecf20Sopenharmony_ci * @mbox_chan:  mailbox channel
838c2ecf20Sopenharmony_ci * @rx_pipe:	pipe object for receive FIFO
848c2ecf20Sopenharmony_ci * @tx_pipe:	pipe object for transmit FIFO
858c2ecf20Sopenharmony_ci * @irq:	IRQ for signaling incoming events
868c2ecf20Sopenharmony_ci * @rx_work:	worker for handling received control messages
878c2ecf20Sopenharmony_ci * @rx_lock:	protects the @rx_queue
888c2ecf20Sopenharmony_ci * @rx_queue:	queue of received control messages to be processed in @rx_work
898c2ecf20Sopenharmony_ci * @tx_lock:	synchronizes operations on the tx fifo
908c2ecf20Sopenharmony_ci * @idr_lock:	synchronizes @lcids and @rcids modifications
918c2ecf20Sopenharmony_ci * @lcids:	idr of all channels with a known local channel id
928c2ecf20Sopenharmony_ci * @rcids:	idr of all channels with a known remote channel id
938c2ecf20Sopenharmony_ci * @features:	remote features
948c2ecf20Sopenharmony_ci * @intentless:	flag to indicate that there is no intent
958c2ecf20Sopenharmony_ci */
968c2ecf20Sopenharmony_cistruct qcom_glink {
978c2ecf20Sopenharmony_ci	struct device *dev;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	const char *name;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	struct mbox_client mbox_client;
1028c2ecf20Sopenharmony_ci	struct mbox_chan *mbox_chan;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	struct qcom_glink_pipe *rx_pipe;
1058c2ecf20Sopenharmony_ci	struct qcom_glink_pipe *tx_pipe;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	int irq;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	struct work_struct rx_work;
1108c2ecf20Sopenharmony_ci	spinlock_t rx_lock;
1118c2ecf20Sopenharmony_ci	struct list_head rx_queue;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	spinlock_t tx_lock;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	spinlock_t idr_lock;
1168c2ecf20Sopenharmony_ci	struct idr lcids;
1178c2ecf20Sopenharmony_ci	struct idr rcids;
1188c2ecf20Sopenharmony_ci	unsigned long features;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	bool intentless;
1218c2ecf20Sopenharmony_ci};
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cienum {
1248c2ecf20Sopenharmony_ci	GLINK_STATE_CLOSED,
1258c2ecf20Sopenharmony_ci	GLINK_STATE_OPENING,
1268c2ecf20Sopenharmony_ci	GLINK_STATE_OPEN,
1278c2ecf20Sopenharmony_ci	GLINK_STATE_CLOSING,
1288c2ecf20Sopenharmony_ci};
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci/**
1318c2ecf20Sopenharmony_ci * struct glink_channel - internal representation of a channel
1328c2ecf20Sopenharmony_ci * @rpdev:	rpdev reference, only used for primary endpoints
1338c2ecf20Sopenharmony_ci * @ept:	rpmsg endpoint this channel is associated with
1348c2ecf20Sopenharmony_ci * @glink:	qcom_glink context handle
1358c2ecf20Sopenharmony_ci * @refcount:	refcount for the channel object
1368c2ecf20Sopenharmony_ci * @recv_lock:	guard for @ept.cb
1378c2ecf20Sopenharmony_ci * @name:	unique channel name/identifier
1388c2ecf20Sopenharmony_ci * @lcid:	channel id, in local space
1398c2ecf20Sopenharmony_ci * @rcid:	channel id, in remote space
1408c2ecf20Sopenharmony_ci * @intent_lock: lock for protection of @liids, @riids
1418c2ecf20Sopenharmony_ci * @liids:	idr of all local intents
1428c2ecf20Sopenharmony_ci * @riids:	idr of all remote intents
1438c2ecf20Sopenharmony_ci * @intent_work: worker responsible for transmitting rx_done packets
1448c2ecf20Sopenharmony_ci * @done_intents: list of intents that needs to be announced rx_done
1458c2ecf20Sopenharmony_ci * @buf:	receive buffer, for gathering fragments
1468c2ecf20Sopenharmony_ci * @buf_offset:	write offset in @buf
1478c2ecf20Sopenharmony_ci * @buf_size:	size of current @buf
1488c2ecf20Sopenharmony_ci * @open_ack:	completed once remote has acked the open-request
1498c2ecf20Sopenharmony_ci * @open_req:	completed once open-request has been received
1508c2ecf20Sopenharmony_ci * @intent_req_lock: Synchronises multiple intent requests
1518c2ecf20Sopenharmony_ci * @intent_req_result: Result of intent request
1528c2ecf20Sopenharmony_ci * @intent_req_comp: Completion for intent_req signalling
1538c2ecf20Sopenharmony_ci */
1548c2ecf20Sopenharmony_cistruct glink_channel {
1558c2ecf20Sopenharmony_ci	struct rpmsg_endpoint ept;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	struct rpmsg_device *rpdev;
1588c2ecf20Sopenharmony_ci	struct qcom_glink *glink;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	struct kref refcount;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	spinlock_t recv_lock;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	char *name;
1658c2ecf20Sopenharmony_ci	unsigned int lcid;
1668c2ecf20Sopenharmony_ci	unsigned int rcid;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	spinlock_t intent_lock;
1698c2ecf20Sopenharmony_ci	struct idr liids;
1708c2ecf20Sopenharmony_ci	struct idr riids;
1718c2ecf20Sopenharmony_ci	struct work_struct intent_work;
1728c2ecf20Sopenharmony_ci	struct list_head done_intents;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	struct glink_core_rx_intent *buf;
1758c2ecf20Sopenharmony_ci	int buf_offset;
1768c2ecf20Sopenharmony_ci	int buf_size;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	struct completion open_ack;
1798c2ecf20Sopenharmony_ci	struct completion open_req;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	struct mutex intent_req_lock;
1828c2ecf20Sopenharmony_ci	bool intent_req_result;
1838c2ecf20Sopenharmony_ci	struct completion intent_req_comp;
1848c2ecf20Sopenharmony_ci};
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci#define to_glink_channel(_ept) container_of(_ept, struct glink_channel, ept)
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic const struct rpmsg_endpoint_ops glink_endpoint_ops;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci#define RPM_CMD_VERSION			0
1918c2ecf20Sopenharmony_ci#define RPM_CMD_VERSION_ACK		1
1928c2ecf20Sopenharmony_ci#define RPM_CMD_OPEN			2
1938c2ecf20Sopenharmony_ci#define RPM_CMD_CLOSE			3
1948c2ecf20Sopenharmony_ci#define RPM_CMD_OPEN_ACK		4
1958c2ecf20Sopenharmony_ci#define RPM_CMD_INTENT			5
1968c2ecf20Sopenharmony_ci#define RPM_CMD_RX_DONE			6
1978c2ecf20Sopenharmony_ci#define RPM_CMD_RX_INTENT_REQ		7
1988c2ecf20Sopenharmony_ci#define RPM_CMD_RX_INTENT_REQ_ACK	8
1998c2ecf20Sopenharmony_ci#define RPM_CMD_TX_DATA			9
2008c2ecf20Sopenharmony_ci#define RPM_CMD_CLOSE_ACK		11
2018c2ecf20Sopenharmony_ci#define RPM_CMD_TX_DATA_CONT		12
2028c2ecf20Sopenharmony_ci#define RPM_CMD_READ_NOTIF		13
2038c2ecf20Sopenharmony_ci#define RPM_CMD_RX_DONE_W_REUSE		14
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci#define GLINK_FEATURE_INTENTLESS	BIT(1)
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic void qcom_glink_rx_done_work(struct work_struct *work);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink,
2108c2ecf20Sopenharmony_ci						      const char *name)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct glink_channel *channel;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
2158c2ecf20Sopenharmony_ci	if (!channel)
2168c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/* Setup glink internal glink_channel data */
2198c2ecf20Sopenharmony_ci	spin_lock_init(&channel->recv_lock);
2208c2ecf20Sopenharmony_ci	spin_lock_init(&channel->intent_lock);
2218c2ecf20Sopenharmony_ci	mutex_init(&channel->intent_req_lock);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	channel->glink = glink;
2248c2ecf20Sopenharmony_ci	channel->name = kstrdup(name, GFP_KERNEL);
2258c2ecf20Sopenharmony_ci	if (!channel->name) {
2268c2ecf20Sopenharmony_ci		kfree(channel);
2278c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	init_completion(&channel->open_req);
2318c2ecf20Sopenharmony_ci	init_completion(&channel->open_ack);
2328c2ecf20Sopenharmony_ci	init_completion(&channel->intent_req_comp);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&channel->done_intents);
2358c2ecf20Sopenharmony_ci	INIT_WORK(&channel->intent_work, qcom_glink_rx_done_work);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	idr_init(&channel->liids);
2388c2ecf20Sopenharmony_ci	idr_init(&channel->riids);
2398c2ecf20Sopenharmony_ci	kref_init(&channel->refcount);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	return channel;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic void qcom_glink_channel_release(struct kref *ref)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct glink_channel *channel = container_of(ref, struct glink_channel,
2478c2ecf20Sopenharmony_ci						     refcount);
2488c2ecf20Sopenharmony_ci	struct glink_core_rx_intent *intent;
2498c2ecf20Sopenharmony_ci	struct glink_core_rx_intent *tmp;
2508c2ecf20Sopenharmony_ci	unsigned long flags;
2518c2ecf20Sopenharmony_ci	int iid;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	/* cancel pending rx_done work */
2548c2ecf20Sopenharmony_ci	cancel_work_sync(&channel->intent_work);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	spin_lock_irqsave(&channel->intent_lock, flags);
2578c2ecf20Sopenharmony_ci	/* Free all non-reuse intents pending rx_done work */
2588c2ecf20Sopenharmony_ci	list_for_each_entry_safe(intent, tmp, &channel->done_intents, node) {
2598c2ecf20Sopenharmony_ci		if (!intent->reuse) {
2608c2ecf20Sopenharmony_ci			kfree(intent->data);
2618c2ecf20Sopenharmony_ci			kfree(intent);
2628c2ecf20Sopenharmony_ci		}
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	idr_for_each_entry(&channel->liids, tmp, iid) {
2668c2ecf20Sopenharmony_ci		kfree(tmp->data);
2678c2ecf20Sopenharmony_ci		kfree(tmp);
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci	idr_destroy(&channel->liids);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	idr_for_each_entry(&channel->riids, tmp, iid)
2728c2ecf20Sopenharmony_ci		kfree(tmp);
2738c2ecf20Sopenharmony_ci	idr_destroy(&channel->riids);
2748c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&channel->intent_lock, flags);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	kfree(channel->name);
2778c2ecf20Sopenharmony_ci	kfree(channel);
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic size_t qcom_glink_rx_avail(struct qcom_glink *glink)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	return glink->rx_pipe->avail(glink->rx_pipe);
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic void qcom_glink_rx_peak(struct qcom_glink *glink,
2868c2ecf20Sopenharmony_ci			       void *data, unsigned int offset, size_t count)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	glink->rx_pipe->peak(glink->rx_pipe, data, offset, count);
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic void qcom_glink_rx_advance(struct qcom_glink *glink, size_t count)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	glink->rx_pipe->advance(glink->rx_pipe, count);
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic size_t qcom_glink_tx_avail(struct qcom_glink *glink)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	return glink->tx_pipe->avail(glink->tx_pipe);
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic void qcom_glink_tx_write(struct qcom_glink *glink,
3028c2ecf20Sopenharmony_ci				const void *hdr, size_t hlen,
3038c2ecf20Sopenharmony_ci				const void *data, size_t dlen)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	glink->tx_pipe->write(glink->tx_pipe, hdr, hlen, data, dlen);
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic int qcom_glink_tx(struct qcom_glink *glink,
3098c2ecf20Sopenharmony_ci			 const void *hdr, size_t hlen,
3108c2ecf20Sopenharmony_ci			 const void *data, size_t dlen, bool wait)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	unsigned int tlen = hlen + dlen;
3138c2ecf20Sopenharmony_ci	unsigned long flags;
3148c2ecf20Sopenharmony_ci	int ret = 0;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* Reject packets that are too big */
3178c2ecf20Sopenharmony_ci	if (tlen >= glink->tx_pipe->length)
3188c2ecf20Sopenharmony_ci		return -EINVAL;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	spin_lock_irqsave(&glink->tx_lock, flags);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	while (qcom_glink_tx_avail(glink) < tlen) {
3238c2ecf20Sopenharmony_ci		if (!wait) {
3248c2ecf20Sopenharmony_ci			ret = -EAGAIN;
3258c2ecf20Sopenharmony_ci			goto out;
3268c2ecf20Sopenharmony_ci		}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci		/* Wait without holding the tx_lock */
3298c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&glink->tx_lock, flags);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci		usleep_range(10000, 15000);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		spin_lock_irqsave(&glink->tx_lock, flags);
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	qcom_glink_tx_write(glink, hdr, hlen, data, dlen);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	mbox_send_message(glink->mbox_chan, NULL);
3398c2ecf20Sopenharmony_ci	mbox_client_txdone(glink->mbox_chan, 0);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ciout:
3428c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&glink->tx_lock, flags);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	return ret;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic int qcom_glink_send_version(struct qcom_glink *glink)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct glink_msg msg;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	msg.cmd = cpu_to_le16(RPM_CMD_VERSION);
3528c2ecf20Sopenharmony_ci	msg.param1 = cpu_to_le16(GLINK_VERSION_1);
3538c2ecf20Sopenharmony_ci	msg.param2 = cpu_to_le32(glink->features);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic void qcom_glink_send_version_ack(struct qcom_glink *glink)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	struct glink_msg msg;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	msg.cmd = cpu_to_le16(RPM_CMD_VERSION_ACK);
3638c2ecf20Sopenharmony_ci	msg.param1 = cpu_to_le16(GLINK_VERSION_1);
3648c2ecf20Sopenharmony_ci	msg.param2 = cpu_to_le32(glink->features);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic void qcom_glink_send_open_ack(struct qcom_glink *glink,
3708c2ecf20Sopenharmony_ci				     struct glink_channel *channel)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	struct glink_msg msg;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	msg.cmd = cpu_to_le16(RPM_CMD_OPEN_ACK);
3758c2ecf20Sopenharmony_ci	msg.param1 = cpu_to_le16(channel->rcid);
3768c2ecf20Sopenharmony_ci	msg.param2 = cpu_to_le32(0);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic void qcom_glink_handle_intent_req_ack(struct qcom_glink *glink,
3828c2ecf20Sopenharmony_ci					     unsigned int cid, bool granted)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	struct glink_channel *channel;
3858c2ecf20Sopenharmony_ci	unsigned long flags;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
3888c2ecf20Sopenharmony_ci	channel = idr_find(&glink->rcids, cid);
3898c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
3908c2ecf20Sopenharmony_ci	if (!channel) {
3918c2ecf20Sopenharmony_ci		dev_err(glink->dev, "unable to find channel\n");
3928c2ecf20Sopenharmony_ci		return;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	channel->intent_req_result = granted;
3968c2ecf20Sopenharmony_ci	complete(&channel->intent_req_comp);
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci/**
4008c2ecf20Sopenharmony_ci * qcom_glink_send_open_req() - send a RPM_CMD_OPEN request to the remote
4018c2ecf20Sopenharmony_ci * @glink: Ptr to the glink edge
4028c2ecf20Sopenharmony_ci * @channel: Ptr to the channel that the open req is sent
4038c2ecf20Sopenharmony_ci *
4048c2ecf20Sopenharmony_ci * Allocates a local channel id and sends a RPM_CMD_OPEN message to the remote.
4058c2ecf20Sopenharmony_ci * Will return with refcount held, regardless of outcome.
4068c2ecf20Sopenharmony_ci *
4078c2ecf20Sopenharmony_ci * Returns 0 on success, negative errno otherwise.
4088c2ecf20Sopenharmony_ci */
4098c2ecf20Sopenharmony_cistatic int qcom_glink_send_open_req(struct qcom_glink *glink,
4108c2ecf20Sopenharmony_ci				    struct glink_channel *channel)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	struct {
4138c2ecf20Sopenharmony_ci		struct glink_msg msg;
4148c2ecf20Sopenharmony_ci		u8 name[GLINK_NAME_SIZE];
4158c2ecf20Sopenharmony_ci	} __packed req;
4168c2ecf20Sopenharmony_ci	int name_len = strlen(channel->name) + 1;
4178c2ecf20Sopenharmony_ci	int req_len = ALIGN(sizeof(req.msg) + name_len, 8);
4188c2ecf20Sopenharmony_ci	int ret;
4198c2ecf20Sopenharmony_ci	unsigned long flags;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	kref_get(&channel->refcount);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
4248c2ecf20Sopenharmony_ci	ret = idr_alloc_cyclic(&glink->lcids, channel,
4258c2ecf20Sopenharmony_ci			       RPM_GLINK_CID_MIN, RPM_GLINK_CID_MAX,
4268c2ecf20Sopenharmony_ci			       GFP_ATOMIC);
4278c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
4288c2ecf20Sopenharmony_ci	if (ret < 0)
4298c2ecf20Sopenharmony_ci		return ret;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	channel->lcid = ret;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	req.msg.cmd = cpu_to_le16(RPM_CMD_OPEN);
4348c2ecf20Sopenharmony_ci	req.msg.param1 = cpu_to_le16(channel->lcid);
4358c2ecf20Sopenharmony_ci	req.msg.param2 = cpu_to_le32(name_len);
4368c2ecf20Sopenharmony_ci	strcpy(req.name, channel->name);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	ret = qcom_glink_tx(glink, &req, req_len, NULL, 0, true);
4398c2ecf20Sopenharmony_ci	if (ret)
4408c2ecf20Sopenharmony_ci		goto remove_idr;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	return 0;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ciremove_idr:
4458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
4468c2ecf20Sopenharmony_ci	idr_remove(&glink->lcids, channel->lcid);
4478c2ecf20Sopenharmony_ci	channel->lcid = 0;
4488c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	return ret;
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic void qcom_glink_send_close_req(struct qcom_glink *glink,
4548c2ecf20Sopenharmony_ci				      struct glink_channel *channel)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	struct glink_msg req;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	req.cmd = cpu_to_le16(RPM_CMD_CLOSE);
4598c2ecf20Sopenharmony_ci	req.param1 = cpu_to_le16(channel->lcid);
4608c2ecf20Sopenharmony_ci	req.param2 = 0;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true);
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic void qcom_glink_send_close_ack(struct qcom_glink *glink,
4668c2ecf20Sopenharmony_ci				      unsigned int rcid)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	struct glink_msg req;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	req.cmd = cpu_to_le16(RPM_CMD_CLOSE_ACK);
4718c2ecf20Sopenharmony_ci	req.param1 = cpu_to_le16(rcid);
4728c2ecf20Sopenharmony_ci	req.param2 = 0;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true);
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cistatic void qcom_glink_rx_done_work(struct work_struct *work)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	struct glink_channel *channel = container_of(work, struct glink_channel,
4808c2ecf20Sopenharmony_ci						     intent_work);
4818c2ecf20Sopenharmony_ci	struct qcom_glink *glink = channel->glink;
4828c2ecf20Sopenharmony_ci	struct glink_core_rx_intent *intent, *tmp;
4838c2ecf20Sopenharmony_ci	struct {
4848c2ecf20Sopenharmony_ci		u16 id;
4858c2ecf20Sopenharmony_ci		u16 lcid;
4868c2ecf20Sopenharmony_ci		u32 liid;
4878c2ecf20Sopenharmony_ci	} __packed cmd;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	unsigned int cid = channel->lcid;
4908c2ecf20Sopenharmony_ci	unsigned int iid;
4918c2ecf20Sopenharmony_ci	bool reuse;
4928c2ecf20Sopenharmony_ci	unsigned long flags;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	spin_lock_irqsave(&channel->intent_lock, flags);
4958c2ecf20Sopenharmony_ci	list_for_each_entry_safe(intent, tmp, &channel->done_intents, node) {
4968c2ecf20Sopenharmony_ci		list_del(&intent->node);
4978c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&channel->intent_lock, flags);
4988c2ecf20Sopenharmony_ci		iid = intent->id;
4998c2ecf20Sopenharmony_ci		reuse = intent->reuse;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci		cmd.id = reuse ? RPM_CMD_RX_DONE_W_REUSE : RPM_CMD_RX_DONE;
5028c2ecf20Sopenharmony_ci		cmd.lcid = cid;
5038c2ecf20Sopenharmony_ci		cmd.liid = iid;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci		qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true);
5068c2ecf20Sopenharmony_ci		if (!reuse) {
5078c2ecf20Sopenharmony_ci			kfree(intent->data);
5088c2ecf20Sopenharmony_ci			kfree(intent);
5098c2ecf20Sopenharmony_ci		}
5108c2ecf20Sopenharmony_ci		spin_lock_irqsave(&channel->intent_lock, flags);
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&channel->intent_lock, flags);
5138c2ecf20Sopenharmony_ci}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_cistatic void qcom_glink_rx_done(struct qcom_glink *glink,
5168c2ecf20Sopenharmony_ci			       struct glink_channel *channel,
5178c2ecf20Sopenharmony_ci			       struct glink_core_rx_intent *intent)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	/* We don't send RX_DONE to intentless systems */
5208c2ecf20Sopenharmony_ci	if (glink->intentless) {
5218c2ecf20Sopenharmony_ci		kfree(intent->data);
5228c2ecf20Sopenharmony_ci		kfree(intent);
5238c2ecf20Sopenharmony_ci		return;
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/* Take it off the tree of receive intents */
5278c2ecf20Sopenharmony_ci	if (!intent->reuse) {
5288c2ecf20Sopenharmony_ci		spin_lock(&channel->intent_lock);
5298c2ecf20Sopenharmony_ci		idr_remove(&channel->liids, intent->id);
5308c2ecf20Sopenharmony_ci		spin_unlock(&channel->intent_lock);
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	/* Schedule the sending of a rx_done indication */
5348c2ecf20Sopenharmony_ci	spin_lock(&channel->intent_lock);
5358c2ecf20Sopenharmony_ci	list_add_tail(&intent->node, &channel->done_intents);
5368c2ecf20Sopenharmony_ci	spin_unlock(&channel->intent_lock);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	schedule_work(&channel->intent_work);
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci/**
5428c2ecf20Sopenharmony_ci * qcom_glink_receive_version() - receive version/features from remote system
5438c2ecf20Sopenharmony_ci *
5448c2ecf20Sopenharmony_ci * @glink:	pointer to transport interface
5458c2ecf20Sopenharmony_ci * @version:	remote version
5468c2ecf20Sopenharmony_ci * @features:	remote features
5478c2ecf20Sopenharmony_ci *
5488c2ecf20Sopenharmony_ci * This function is called in response to a remote-initiated version/feature
5498c2ecf20Sopenharmony_ci * negotiation sequence.
5508c2ecf20Sopenharmony_ci */
5518c2ecf20Sopenharmony_cistatic void qcom_glink_receive_version(struct qcom_glink *glink,
5528c2ecf20Sopenharmony_ci				       u32 version,
5538c2ecf20Sopenharmony_ci				       u32 features)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	switch (version) {
5568c2ecf20Sopenharmony_ci	case 0:
5578c2ecf20Sopenharmony_ci		break;
5588c2ecf20Sopenharmony_ci	case GLINK_VERSION_1:
5598c2ecf20Sopenharmony_ci		glink->features &= features;
5608c2ecf20Sopenharmony_ci		fallthrough;
5618c2ecf20Sopenharmony_ci	default:
5628c2ecf20Sopenharmony_ci		qcom_glink_send_version_ack(glink);
5638c2ecf20Sopenharmony_ci		break;
5648c2ecf20Sopenharmony_ci	}
5658c2ecf20Sopenharmony_ci}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci/**
5688c2ecf20Sopenharmony_ci * qcom_glink_receive_version_ack() - receive negotiation ack from remote system
5698c2ecf20Sopenharmony_ci *
5708c2ecf20Sopenharmony_ci * @glink:	pointer to transport interface
5718c2ecf20Sopenharmony_ci * @version:	remote version response
5728c2ecf20Sopenharmony_ci * @features:	remote features response
5738c2ecf20Sopenharmony_ci *
5748c2ecf20Sopenharmony_ci * This function is called in response to a local-initiated version/feature
5758c2ecf20Sopenharmony_ci * negotiation sequence and is the counter-offer from the remote side based
5768c2ecf20Sopenharmony_ci * upon the initial version and feature set requested.
5778c2ecf20Sopenharmony_ci */
5788c2ecf20Sopenharmony_cistatic void qcom_glink_receive_version_ack(struct qcom_glink *glink,
5798c2ecf20Sopenharmony_ci					   u32 version,
5808c2ecf20Sopenharmony_ci					   u32 features)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	switch (version) {
5838c2ecf20Sopenharmony_ci	case 0:
5848c2ecf20Sopenharmony_ci		/* Version negotiation failed */
5858c2ecf20Sopenharmony_ci		break;
5868c2ecf20Sopenharmony_ci	case GLINK_VERSION_1:
5878c2ecf20Sopenharmony_ci		if (features == glink->features)
5888c2ecf20Sopenharmony_ci			break;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci		glink->features &= features;
5918c2ecf20Sopenharmony_ci		fallthrough;
5928c2ecf20Sopenharmony_ci	default:
5938c2ecf20Sopenharmony_ci		qcom_glink_send_version(glink);
5948c2ecf20Sopenharmony_ci		break;
5958c2ecf20Sopenharmony_ci	}
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci/**
5998c2ecf20Sopenharmony_ci * qcom_glink_send_intent_req_ack() - convert an rx intent request ack cmd to
6008c2ecf20Sopenharmony_ci * 	wire format and transmit
6018c2ecf20Sopenharmony_ci * @glink:	The transport to transmit on.
6028c2ecf20Sopenharmony_ci * @channel:	The glink channel
6038c2ecf20Sopenharmony_ci * @granted:	The request response to encode.
6048c2ecf20Sopenharmony_ci *
6058c2ecf20Sopenharmony_ci * Return: 0 on success or standard Linux error code.
6068c2ecf20Sopenharmony_ci */
6078c2ecf20Sopenharmony_cistatic int qcom_glink_send_intent_req_ack(struct qcom_glink *glink,
6088c2ecf20Sopenharmony_ci					  struct glink_channel *channel,
6098c2ecf20Sopenharmony_ci					  bool granted)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	struct glink_msg msg;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	msg.cmd = cpu_to_le16(RPM_CMD_RX_INTENT_REQ_ACK);
6148c2ecf20Sopenharmony_ci	msg.param1 = cpu_to_le16(channel->lcid);
6158c2ecf20Sopenharmony_ci	msg.param2 = cpu_to_le32(granted);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	return 0;
6208c2ecf20Sopenharmony_ci}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci/**
6238c2ecf20Sopenharmony_ci * qcom_glink_advertise_intent - convert an rx intent cmd to wire format and
6248c2ecf20Sopenharmony_ci *			   transmit
6258c2ecf20Sopenharmony_ci * @glink:	The transport to transmit on.
6268c2ecf20Sopenharmony_ci * @channel:	The local channel
6278c2ecf20Sopenharmony_ci * @intent:	The intent to pass on to remote.
6288c2ecf20Sopenharmony_ci *
6298c2ecf20Sopenharmony_ci * Return: 0 on success or standard Linux error code.
6308c2ecf20Sopenharmony_ci */
6318c2ecf20Sopenharmony_cistatic int qcom_glink_advertise_intent(struct qcom_glink *glink,
6328c2ecf20Sopenharmony_ci				       struct glink_channel *channel,
6338c2ecf20Sopenharmony_ci				       struct glink_core_rx_intent *intent)
6348c2ecf20Sopenharmony_ci{
6358c2ecf20Sopenharmony_ci	struct command {
6368c2ecf20Sopenharmony_ci		__le16 id;
6378c2ecf20Sopenharmony_ci		__le16 lcid;
6388c2ecf20Sopenharmony_ci		__le32 count;
6398c2ecf20Sopenharmony_ci		__le32 size;
6408c2ecf20Sopenharmony_ci		__le32 liid;
6418c2ecf20Sopenharmony_ci	} __packed;
6428c2ecf20Sopenharmony_ci	struct command cmd;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	cmd.id = cpu_to_le16(RPM_CMD_INTENT);
6458c2ecf20Sopenharmony_ci	cmd.lcid = cpu_to_le16(channel->lcid);
6468c2ecf20Sopenharmony_ci	cmd.count = cpu_to_le32(1);
6478c2ecf20Sopenharmony_ci	cmd.size = cpu_to_le32(intent->size);
6488c2ecf20Sopenharmony_ci	cmd.liid = cpu_to_le32(intent->id);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	return 0;
6538c2ecf20Sopenharmony_ci}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_cistatic struct glink_core_rx_intent *
6568c2ecf20Sopenharmony_ciqcom_glink_alloc_intent(struct qcom_glink *glink,
6578c2ecf20Sopenharmony_ci			struct glink_channel *channel,
6588c2ecf20Sopenharmony_ci			size_t size,
6598c2ecf20Sopenharmony_ci			bool reuseable)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	struct glink_core_rx_intent *intent;
6628c2ecf20Sopenharmony_ci	int ret;
6638c2ecf20Sopenharmony_ci	unsigned long flags;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	intent = kzalloc(sizeof(*intent), GFP_KERNEL);
6668c2ecf20Sopenharmony_ci	if (!intent)
6678c2ecf20Sopenharmony_ci		return NULL;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	intent->data = kzalloc(size, GFP_KERNEL);
6708c2ecf20Sopenharmony_ci	if (!intent->data)
6718c2ecf20Sopenharmony_ci		goto free_intent;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	spin_lock_irqsave(&channel->intent_lock, flags);
6748c2ecf20Sopenharmony_ci	ret = idr_alloc_cyclic(&channel->liids, intent, 1, -1, GFP_ATOMIC);
6758c2ecf20Sopenharmony_ci	if (ret < 0) {
6768c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&channel->intent_lock, flags);
6778c2ecf20Sopenharmony_ci		goto free_data;
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&channel->intent_lock, flags);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	intent->id = ret;
6828c2ecf20Sopenharmony_ci	intent->size = size;
6838c2ecf20Sopenharmony_ci	intent->reuse = reuseable;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	return intent;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_cifree_data:
6888c2ecf20Sopenharmony_ci	kfree(intent->data);
6898c2ecf20Sopenharmony_cifree_intent:
6908c2ecf20Sopenharmony_ci	kfree(intent);
6918c2ecf20Sopenharmony_ci	return NULL;
6928c2ecf20Sopenharmony_ci}
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_cistatic void qcom_glink_handle_rx_done(struct qcom_glink *glink,
6958c2ecf20Sopenharmony_ci				      u32 cid, uint32_t iid,
6968c2ecf20Sopenharmony_ci				      bool reuse)
6978c2ecf20Sopenharmony_ci{
6988c2ecf20Sopenharmony_ci	struct glink_core_rx_intent *intent;
6998c2ecf20Sopenharmony_ci	struct glink_channel *channel;
7008c2ecf20Sopenharmony_ci	unsigned long flags;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
7038c2ecf20Sopenharmony_ci	channel = idr_find(&glink->rcids, cid);
7048c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
7058c2ecf20Sopenharmony_ci	if (!channel) {
7068c2ecf20Sopenharmony_ci		dev_err(glink->dev, "invalid channel id received\n");
7078c2ecf20Sopenharmony_ci		return;
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	spin_lock_irqsave(&channel->intent_lock, flags);
7118c2ecf20Sopenharmony_ci	intent = idr_find(&channel->riids, iid);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	if (!intent) {
7148c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&channel->intent_lock, flags);
7158c2ecf20Sopenharmony_ci		dev_err(glink->dev, "invalid intent id received\n");
7168c2ecf20Sopenharmony_ci		return;
7178c2ecf20Sopenharmony_ci	}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	intent->in_use = false;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	if (!reuse) {
7228c2ecf20Sopenharmony_ci		idr_remove(&channel->riids, intent->id);
7238c2ecf20Sopenharmony_ci		kfree(intent);
7248c2ecf20Sopenharmony_ci	}
7258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&channel->intent_lock, flags);
7268c2ecf20Sopenharmony_ci}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci/**
7298c2ecf20Sopenharmony_ci * qcom_glink_handle_intent_req() - Receive a request for rx_intent
7308c2ecf20Sopenharmony_ci *					    from remote side
7318c2ecf20Sopenharmony_ci * @glink:      Pointer to the transport interface
7328c2ecf20Sopenharmony_ci * @cid:	Remote channel ID
7338c2ecf20Sopenharmony_ci * @size:	size of the intent
7348c2ecf20Sopenharmony_ci *
7358c2ecf20Sopenharmony_ci * The function searches for the local channel to which the request for
7368c2ecf20Sopenharmony_ci * rx_intent has arrived and allocates and notifies the remote back
7378c2ecf20Sopenharmony_ci */
7388c2ecf20Sopenharmony_cistatic void qcom_glink_handle_intent_req(struct qcom_glink *glink,
7398c2ecf20Sopenharmony_ci					 u32 cid, size_t size)
7408c2ecf20Sopenharmony_ci{
7418c2ecf20Sopenharmony_ci	struct glink_core_rx_intent *intent;
7428c2ecf20Sopenharmony_ci	struct glink_channel *channel;
7438c2ecf20Sopenharmony_ci	unsigned long flags;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
7468c2ecf20Sopenharmony_ci	channel = idr_find(&glink->rcids, cid);
7478c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	if (!channel) {
7508c2ecf20Sopenharmony_ci		pr_err("%s channel not found for cid %d\n", __func__, cid);
7518c2ecf20Sopenharmony_ci		return;
7528c2ecf20Sopenharmony_ci	}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	intent = qcom_glink_alloc_intent(glink, channel, size, false);
7558c2ecf20Sopenharmony_ci	if (intent)
7568c2ecf20Sopenharmony_ci		qcom_glink_advertise_intent(glink, channel, intent);
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	qcom_glink_send_intent_req_ack(glink, channel, !!intent);
7598c2ecf20Sopenharmony_ci}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cistatic int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	struct glink_defer_cmd *dcmd;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	extra = ALIGN(extra, 8);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	if (qcom_glink_rx_avail(glink) < sizeof(struct glink_msg) + extra) {
7688c2ecf20Sopenharmony_ci		dev_dbg(glink->dev, "Insufficient data in rx fifo");
7698c2ecf20Sopenharmony_ci		return -ENXIO;
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	dcmd = kzalloc(sizeof(*dcmd) + extra, GFP_ATOMIC);
7738c2ecf20Sopenharmony_ci	if (!dcmd)
7748c2ecf20Sopenharmony_ci		return -ENOMEM;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dcmd->node);
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	qcom_glink_rx_peak(glink, &dcmd->msg, 0, sizeof(dcmd->msg) + extra);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	spin_lock(&glink->rx_lock);
7818c2ecf20Sopenharmony_ci	list_add_tail(&dcmd->node, &glink->rx_queue);
7828c2ecf20Sopenharmony_ci	spin_unlock(&glink->rx_lock);
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	schedule_work(&glink->rx_work);
7858c2ecf20Sopenharmony_ci	qcom_glink_rx_advance(glink, sizeof(dcmd->msg) + extra);
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	return 0;
7888c2ecf20Sopenharmony_ci}
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_cistatic int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail)
7918c2ecf20Sopenharmony_ci{
7928c2ecf20Sopenharmony_ci	struct glink_core_rx_intent *intent;
7938c2ecf20Sopenharmony_ci	struct glink_channel *channel;
7948c2ecf20Sopenharmony_ci	struct {
7958c2ecf20Sopenharmony_ci		struct glink_msg msg;
7968c2ecf20Sopenharmony_ci		__le32 chunk_size;
7978c2ecf20Sopenharmony_ci		__le32 left_size;
7988c2ecf20Sopenharmony_ci	} __packed hdr;
7998c2ecf20Sopenharmony_ci	unsigned int chunk_size;
8008c2ecf20Sopenharmony_ci	unsigned int left_size;
8018c2ecf20Sopenharmony_ci	unsigned int rcid;
8028c2ecf20Sopenharmony_ci	unsigned int liid;
8038c2ecf20Sopenharmony_ci	int ret = 0;
8048c2ecf20Sopenharmony_ci	unsigned long flags;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	if (avail < sizeof(hdr)) {
8078c2ecf20Sopenharmony_ci		dev_dbg(glink->dev, "Not enough data in fifo\n");
8088c2ecf20Sopenharmony_ci		return -EAGAIN;
8098c2ecf20Sopenharmony_ci	}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	qcom_glink_rx_peak(glink, &hdr, 0, sizeof(hdr));
8128c2ecf20Sopenharmony_ci	chunk_size = le32_to_cpu(hdr.chunk_size);
8138c2ecf20Sopenharmony_ci	left_size = le32_to_cpu(hdr.left_size);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	if (avail < sizeof(hdr) + chunk_size) {
8168c2ecf20Sopenharmony_ci		dev_dbg(glink->dev, "Payload not yet in fifo\n");
8178c2ecf20Sopenharmony_ci		return -EAGAIN;
8188c2ecf20Sopenharmony_ci	}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	rcid = le16_to_cpu(hdr.msg.param1);
8218c2ecf20Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
8228c2ecf20Sopenharmony_ci	channel = idr_find(&glink->rcids, rcid);
8238c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
8248c2ecf20Sopenharmony_ci	if (!channel) {
8258c2ecf20Sopenharmony_ci		dev_dbg(glink->dev, "Data on non-existing channel\n");
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci		/* Drop the message */
8288c2ecf20Sopenharmony_ci		goto advance_rx;
8298c2ecf20Sopenharmony_ci	}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	if (glink->intentless) {
8328c2ecf20Sopenharmony_ci		/* Might have an ongoing, fragmented, message to append */
8338c2ecf20Sopenharmony_ci		if (!channel->buf) {
8348c2ecf20Sopenharmony_ci			intent = kzalloc(sizeof(*intent), GFP_ATOMIC);
8358c2ecf20Sopenharmony_ci			if (!intent)
8368c2ecf20Sopenharmony_ci				return -ENOMEM;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci			intent->data = kmalloc(chunk_size + left_size,
8398c2ecf20Sopenharmony_ci					       GFP_ATOMIC);
8408c2ecf20Sopenharmony_ci			if (!intent->data) {
8418c2ecf20Sopenharmony_ci				kfree(intent);
8428c2ecf20Sopenharmony_ci				return -ENOMEM;
8438c2ecf20Sopenharmony_ci			}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci			intent->id = 0xbabababa;
8468c2ecf20Sopenharmony_ci			intent->size = chunk_size + left_size;
8478c2ecf20Sopenharmony_ci			intent->offset = 0;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci			channel->buf = intent;
8508c2ecf20Sopenharmony_ci		} else {
8518c2ecf20Sopenharmony_ci			intent = channel->buf;
8528c2ecf20Sopenharmony_ci		}
8538c2ecf20Sopenharmony_ci	} else {
8548c2ecf20Sopenharmony_ci		liid = le32_to_cpu(hdr.msg.param2);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci		spin_lock_irqsave(&channel->intent_lock, flags);
8578c2ecf20Sopenharmony_ci		intent = idr_find(&channel->liids, liid);
8588c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&channel->intent_lock, flags);
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci		if (!intent) {
8618c2ecf20Sopenharmony_ci			dev_err(glink->dev,
8628c2ecf20Sopenharmony_ci				"no intent found for channel %s intent %d",
8638c2ecf20Sopenharmony_ci				channel->name, liid);
8648c2ecf20Sopenharmony_ci			ret = -ENOENT;
8658c2ecf20Sopenharmony_ci			goto advance_rx;
8668c2ecf20Sopenharmony_ci		}
8678c2ecf20Sopenharmony_ci	}
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	if (intent->size - intent->offset < chunk_size) {
8708c2ecf20Sopenharmony_ci		dev_err(glink->dev, "Insufficient space in intent\n");
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci		/* The packet header lied, drop payload */
8738c2ecf20Sopenharmony_ci		goto advance_rx;
8748c2ecf20Sopenharmony_ci	}
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	qcom_glink_rx_peak(glink, intent->data + intent->offset,
8778c2ecf20Sopenharmony_ci			   sizeof(hdr), chunk_size);
8788c2ecf20Sopenharmony_ci	intent->offset += chunk_size;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	/* Handle message when no fragments remain to be received */
8818c2ecf20Sopenharmony_ci	if (!left_size) {
8828c2ecf20Sopenharmony_ci		spin_lock(&channel->recv_lock);
8838c2ecf20Sopenharmony_ci		if (channel->ept.cb) {
8848c2ecf20Sopenharmony_ci			channel->ept.cb(channel->ept.rpdev,
8858c2ecf20Sopenharmony_ci					intent->data,
8868c2ecf20Sopenharmony_ci					intent->offset,
8878c2ecf20Sopenharmony_ci					channel->ept.priv,
8888c2ecf20Sopenharmony_ci					RPMSG_ADDR_ANY);
8898c2ecf20Sopenharmony_ci		}
8908c2ecf20Sopenharmony_ci		spin_unlock(&channel->recv_lock);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci		intent->offset = 0;
8938c2ecf20Sopenharmony_ci		channel->buf = NULL;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci		qcom_glink_rx_done(glink, channel, intent);
8968c2ecf20Sopenharmony_ci	}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ciadvance_rx:
8998c2ecf20Sopenharmony_ci	qcom_glink_rx_advance(glink, ALIGN(sizeof(hdr) + chunk_size, 8));
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	return ret;
9028c2ecf20Sopenharmony_ci}
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_cistatic void qcom_glink_handle_intent(struct qcom_glink *glink,
9058c2ecf20Sopenharmony_ci				     unsigned int cid,
9068c2ecf20Sopenharmony_ci				     unsigned int count,
9078c2ecf20Sopenharmony_ci				     size_t avail)
9088c2ecf20Sopenharmony_ci{
9098c2ecf20Sopenharmony_ci	struct glink_core_rx_intent *intent;
9108c2ecf20Sopenharmony_ci	struct glink_channel *channel;
9118c2ecf20Sopenharmony_ci	struct intent_pair {
9128c2ecf20Sopenharmony_ci		__le32 size;
9138c2ecf20Sopenharmony_ci		__le32 iid;
9148c2ecf20Sopenharmony_ci	};
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	struct {
9178c2ecf20Sopenharmony_ci		struct glink_msg msg;
9188c2ecf20Sopenharmony_ci		struct intent_pair intents[];
9198c2ecf20Sopenharmony_ci	} __packed * msg;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	const size_t msglen = struct_size(msg, intents, count);
9228c2ecf20Sopenharmony_ci	int ret;
9238c2ecf20Sopenharmony_ci	int i;
9248c2ecf20Sopenharmony_ci	unsigned long flags;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	if (avail < msglen) {
9278c2ecf20Sopenharmony_ci		dev_dbg(glink->dev, "Not enough data in fifo\n");
9288c2ecf20Sopenharmony_ci		return;
9298c2ecf20Sopenharmony_ci	}
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
9328c2ecf20Sopenharmony_ci	channel = idr_find(&glink->rcids, cid);
9338c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
9348c2ecf20Sopenharmony_ci	if (!channel) {
9358c2ecf20Sopenharmony_ci		dev_err(glink->dev, "intents for non-existing channel\n");
9368c2ecf20Sopenharmony_ci		qcom_glink_rx_advance(glink, ALIGN(msglen, 8));
9378c2ecf20Sopenharmony_ci		return;
9388c2ecf20Sopenharmony_ci	}
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	msg = kmalloc(msglen, GFP_ATOMIC);
9418c2ecf20Sopenharmony_ci	if (!msg)
9428c2ecf20Sopenharmony_ci		return;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	qcom_glink_rx_peak(glink, msg, 0, msglen);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	for (i = 0; i < count; ++i) {
9478c2ecf20Sopenharmony_ci		intent = kzalloc(sizeof(*intent), GFP_ATOMIC);
9488c2ecf20Sopenharmony_ci		if (!intent)
9498c2ecf20Sopenharmony_ci			break;
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci		intent->id = le32_to_cpu(msg->intents[i].iid);
9528c2ecf20Sopenharmony_ci		intent->size = le32_to_cpu(msg->intents[i].size);
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci		spin_lock_irqsave(&channel->intent_lock, flags);
9558c2ecf20Sopenharmony_ci		ret = idr_alloc(&channel->riids, intent,
9568c2ecf20Sopenharmony_ci				intent->id, intent->id + 1, GFP_ATOMIC);
9578c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&channel->intent_lock, flags);
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci		if (ret < 0)
9608c2ecf20Sopenharmony_ci			dev_err(glink->dev, "failed to store remote intent\n");
9618c2ecf20Sopenharmony_ci	}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	kfree(msg);
9648c2ecf20Sopenharmony_ci	qcom_glink_rx_advance(glink, ALIGN(msglen, 8));
9658c2ecf20Sopenharmony_ci}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_cistatic int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid)
9688c2ecf20Sopenharmony_ci{
9698c2ecf20Sopenharmony_ci	struct glink_channel *channel;
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	spin_lock(&glink->idr_lock);
9728c2ecf20Sopenharmony_ci	channel = idr_find(&glink->lcids, lcid);
9738c2ecf20Sopenharmony_ci	spin_unlock(&glink->idr_lock);
9748c2ecf20Sopenharmony_ci	if (!channel) {
9758c2ecf20Sopenharmony_ci		dev_err(glink->dev, "Invalid open ack packet\n");
9768c2ecf20Sopenharmony_ci		return -EINVAL;
9778c2ecf20Sopenharmony_ci	}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	complete_all(&channel->open_ack);
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	return 0;
9828c2ecf20Sopenharmony_ci}
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_cistatic irqreturn_t qcom_glink_native_intr(int irq, void *data)
9858c2ecf20Sopenharmony_ci{
9868c2ecf20Sopenharmony_ci	struct qcom_glink *glink = data;
9878c2ecf20Sopenharmony_ci	struct glink_msg msg;
9888c2ecf20Sopenharmony_ci	unsigned int param1;
9898c2ecf20Sopenharmony_ci	unsigned int param2;
9908c2ecf20Sopenharmony_ci	unsigned int avail;
9918c2ecf20Sopenharmony_ci	unsigned int cmd;
9928c2ecf20Sopenharmony_ci	int ret = 0;
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	for (;;) {
9958c2ecf20Sopenharmony_ci		avail = qcom_glink_rx_avail(glink);
9968c2ecf20Sopenharmony_ci		if (avail < sizeof(msg))
9978c2ecf20Sopenharmony_ci			break;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci		qcom_glink_rx_peak(glink, &msg, 0, sizeof(msg));
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci		cmd = le16_to_cpu(msg.cmd);
10028c2ecf20Sopenharmony_ci		param1 = le16_to_cpu(msg.param1);
10038c2ecf20Sopenharmony_ci		param2 = le32_to_cpu(msg.param2);
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci		switch (cmd) {
10068c2ecf20Sopenharmony_ci		case RPM_CMD_VERSION:
10078c2ecf20Sopenharmony_ci		case RPM_CMD_VERSION_ACK:
10088c2ecf20Sopenharmony_ci		case RPM_CMD_CLOSE:
10098c2ecf20Sopenharmony_ci		case RPM_CMD_CLOSE_ACK:
10108c2ecf20Sopenharmony_ci		case RPM_CMD_RX_INTENT_REQ:
10118c2ecf20Sopenharmony_ci			ret = qcom_glink_rx_defer(glink, 0);
10128c2ecf20Sopenharmony_ci			break;
10138c2ecf20Sopenharmony_ci		case RPM_CMD_OPEN_ACK:
10148c2ecf20Sopenharmony_ci			ret = qcom_glink_rx_open_ack(glink, param1);
10158c2ecf20Sopenharmony_ci			qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8));
10168c2ecf20Sopenharmony_ci			break;
10178c2ecf20Sopenharmony_ci		case RPM_CMD_OPEN:
10188c2ecf20Sopenharmony_ci			ret = qcom_glink_rx_defer(glink, param2);
10198c2ecf20Sopenharmony_ci			break;
10208c2ecf20Sopenharmony_ci		case RPM_CMD_TX_DATA:
10218c2ecf20Sopenharmony_ci		case RPM_CMD_TX_DATA_CONT:
10228c2ecf20Sopenharmony_ci			ret = qcom_glink_rx_data(glink, avail);
10238c2ecf20Sopenharmony_ci			break;
10248c2ecf20Sopenharmony_ci		case RPM_CMD_READ_NOTIF:
10258c2ecf20Sopenharmony_ci			qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8));
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci			mbox_send_message(glink->mbox_chan, NULL);
10288c2ecf20Sopenharmony_ci			mbox_client_txdone(glink->mbox_chan, 0);
10298c2ecf20Sopenharmony_ci			break;
10308c2ecf20Sopenharmony_ci		case RPM_CMD_INTENT:
10318c2ecf20Sopenharmony_ci			qcom_glink_handle_intent(glink, param1, param2, avail);
10328c2ecf20Sopenharmony_ci			break;
10338c2ecf20Sopenharmony_ci		case RPM_CMD_RX_DONE:
10348c2ecf20Sopenharmony_ci			qcom_glink_handle_rx_done(glink, param1, param2, false);
10358c2ecf20Sopenharmony_ci			qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8));
10368c2ecf20Sopenharmony_ci			break;
10378c2ecf20Sopenharmony_ci		case RPM_CMD_RX_DONE_W_REUSE:
10388c2ecf20Sopenharmony_ci			qcom_glink_handle_rx_done(glink, param1, param2, true);
10398c2ecf20Sopenharmony_ci			qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8));
10408c2ecf20Sopenharmony_ci			break;
10418c2ecf20Sopenharmony_ci		case RPM_CMD_RX_INTENT_REQ_ACK:
10428c2ecf20Sopenharmony_ci			qcom_glink_handle_intent_req_ack(glink, param1, param2);
10438c2ecf20Sopenharmony_ci			qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8));
10448c2ecf20Sopenharmony_ci			break;
10458c2ecf20Sopenharmony_ci		default:
10468c2ecf20Sopenharmony_ci			dev_err(glink->dev, "unhandled rx cmd: %d\n", cmd);
10478c2ecf20Sopenharmony_ci			ret = -EINVAL;
10488c2ecf20Sopenharmony_ci			break;
10498c2ecf20Sopenharmony_ci		}
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci		if (ret)
10528c2ecf20Sopenharmony_ci			break;
10538c2ecf20Sopenharmony_ci	}
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
10568c2ecf20Sopenharmony_ci}
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci/* Locally initiated rpmsg_create_ept */
10598c2ecf20Sopenharmony_cistatic struct glink_channel *qcom_glink_create_local(struct qcom_glink *glink,
10608c2ecf20Sopenharmony_ci						     const char *name)
10618c2ecf20Sopenharmony_ci{
10628c2ecf20Sopenharmony_ci	struct glink_channel *channel;
10638c2ecf20Sopenharmony_ci	int ret;
10648c2ecf20Sopenharmony_ci	unsigned long flags;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	channel = qcom_glink_alloc_channel(glink, name);
10678c2ecf20Sopenharmony_ci	if (IS_ERR(channel))
10688c2ecf20Sopenharmony_ci		return ERR_CAST(channel);
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	ret = qcom_glink_send_open_req(glink, channel);
10718c2ecf20Sopenharmony_ci	if (ret)
10728c2ecf20Sopenharmony_ci		goto release_channel;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	ret = wait_for_completion_timeout(&channel->open_ack, 5 * HZ);
10758c2ecf20Sopenharmony_ci	if (!ret)
10768c2ecf20Sopenharmony_ci		goto err_timeout;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	ret = wait_for_completion_timeout(&channel->open_req, 5 * HZ);
10798c2ecf20Sopenharmony_ci	if (!ret)
10808c2ecf20Sopenharmony_ci		goto err_timeout;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	qcom_glink_send_open_ack(glink, channel);
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	return channel;
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_cierr_timeout:
10878c2ecf20Sopenharmony_ci	/* qcom_glink_send_open_req() did register the channel in lcids*/
10888c2ecf20Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
10898c2ecf20Sopenharmony_ci	idr_remove(&glink->lcids, channel->lcid);
10908c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_cirelease_channel:
10938c2ecf20Sopenharmony_ci	/* Release qcom_glink_send_open_req() reference */
10948c2ecf20Sopenharmony_ci	kref_put(&channel->refcount, qcom_glink_channel_release);
10958c2ecf20Sopenharmony_ci	/* Release qcom_glink_alloc_channel() reference */
10968c2ecf20Sopenharmony_ci	kref_put(&channel->refcount, qcom_glink_channel_release);
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	return ERR_PTR(-ETIMEDOUT);
10998c2ecf20Sopenharmony_ci}
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci/* Remote initiated rpmsg_create_ept */
11028c2ecf20Sopenharmony_cistatic int qcom_glink_create_remote(struct qcom_glink *glink,
11038c2ecf20Sopenharmony_ci				    struct glink_channel *channel)
11048c2ecf20Sopenharmony_ci{
11058c2ecf20Sopenharmony_ci	int ret;
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	qcom_glink_send_open_ack(glink, channel);
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	ret = qcom_glink_send_open_req(glink, channel);
11108c2ecf20Sopenharmony_ci	if (ret)
11118c2ecf20Sopenharmony_ci		goto close_link;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	ret = wait_for_completion_timeout(&channel->open_ack, 5 * HZ);
11148c2ecf20Sopenharmony_ci	if (!ret) {
11158c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
11168c2ecf20Sopenharmony_ci		goto close_link;
11178c2ecf20Sopenharmony_ci	}
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	return 0;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ciclose_link:
11228c2ecf20Sopenharmony_ci	/*
11238c2ecf20Sopenharmony_ci	 * Send a close request to "undo" our open-ack. The close-ack will
11248c2ecf20Sopenharmony_ci	 * release qcom_glink_send_open_req() reference and the last reference
11258c2ecf20Sopenharmony_ci	 * will be relesed after receiving remote_close or transport unregister
11268c2ecf20Sopenharmony_ci	 * by calling qcom_glink_native_remove().
11278c2ecf20Sopenharmony_ci	 */
11288c2ecf20Sopenharmony_ci	qcom_glink_send_close_req(glink, channel);
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	return ret;
11318c2ecf20Sopenharmony_ci}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_cistatic struct rpmsg_endpoint *qcom_glink_create_ept(struct rpmsg_device *rpdev,
11348c2ecf20Sopenharmony_ci						    rpmsg_rx_cb_t cb,
11358c2ecf20Sopenharmony_ci						    void *priv,
11368c2ecf20Sopenharmony_ci						    struct rpmsg_channel_info
11378c2ecf20Sopenharmony_ci									chinfo)
11388c2ecf20Sopenharmony_ci{
11398c2ecf20Sopenharmony_ci	struct glink_channel *parent = to_glink_channel(rpdev->ept);
11408c2ecf20Sopenharmony_ci	struct glink_channel *channel;
11418c2ecf20Sopenharmony_ci	struct qcom_glink *glink = parent->glink;
11428c2ecf20Sopenharmony_ci	struct rpmsg_endpoint *ept;
11438c2ecf20Sopenharmony_ci	const char *name = chinfo.name;
11448c2ecf20Sopenharmony_ci	int cid;
11458c2ecf20Sopenharmony_ci	int ret;
11468c2ecf20Sopenharmony_ci	unsigned long flags;
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
11498c2ecf20Sopenharmony_ci	idr_for_each_entry(&glink->rcids, channel, cid) {
11508c2ecf20Sopenharmony_ci		if (!strcmp(channel->name, name))
11518c2ecf20Sopenharmony_ci			break;
11528c2ecf20Sopenharmony_ci	}
11538c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	if (!channel) {
11568c2ecf20Sopenharmony_ci		channel = qcom_glink_create_local(glink, name);
11578c2ecf20Sopenharmony_ci		if (IS_ERR(channel))
11588c2ecf20Sopenharmony_ci			return NULL;
11598c2ecf20Sopenharmony_ci	} else {
11608c2ecf20Sopenharmony_ci		ret = qcom_glink_create_remote(glink, channel);
11618c2ecf20Sopenharmony_ci		if (ret)
11628c2ecf20Sopenharmony_ci			return NULL;
11638c2ecf20Sopenharmony_ci	}
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	ept = &channel->ept;
11668c2ecf20Sopenharmony_ci	ept->rpdev = rpdev;
11678c2ecf20Sopenharmony_ci	ept->cb = cb;
11688c2ecf20Sopenharmony_ci	ept->priv = priv;
11698c2ecf20Sopenharmony_ci	ept->ops = &glink_endpoint_ops;
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	return ept;
11728c2ecf20Sopenharmony_ci}
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_cistatic int qcom_glink_announce_create(struct rpmsg_device *rpdev)
11758c2ecf20Sopenharmony_ci{
11768c2ecf20Sopenharmony_ci	struct glink_channel *channel = to_glink_channel(rpdev->ept);
11778c2ecf20Sopenharmony_ci	struct device_node *np = rpdev->dev.of_node;
11788c2ecf20Sopenharmony_ci	struct qcom_glink *glink = channel->glink;
11798c2ecf20Sopenharmony_ci	struct glink_core_rx_intent *intent;
11808c2ecf20Sopenharmony_ci	const struct property *prop = NULL;
11818c2ecf20Sopenharmony_ci	__be32 defaults[] = { cpu_to_be32(SZ_1K), cpu_to_be32(5) };
11828c2ecf20Sopenharmony_ci	int num_intents;
11838c2ecf20Sopenharmony_ci	int num_groups = 1;
11848c2ecf20Sopenharmony_ci	__be32 *val = defaults;
11858c2ecf20Sopenharmony_ci	int size;
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	if (glink->intentless || !completion_done(&channel->open_ack))
11888c2ecf20Sopenharmony_ci		return 0;
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	prop = of_find_property(np, "qcom,intents", NULL);
11918c2ecf20Sopenharmony_ci	if (prop) {
11928c2ecf20Sopenharmony_ci		val = prop->value;
11938c2ecf20Sopenharmony_ci		num_groups = prop->length / sizeof(u32) / 2;
11948c2ecf20Sopenharmony_ci	}
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	/* Channel is now open, advertise base set of intents */
11978c2ecf20Sopenharmony_ci	while (num_groups--) {
11988c2ecf20Sopenharmony_ci		size = be32_to_cpup(val++);
11998c2ecf20Sopenharmony_ci		num_intents = be32_to_cpup(val++);
12008c2ecf20Sopenharmony_ci		while (num_intents--) {
12018c2ecf20Sopenharmony_ci			intent = qcom_glink_alloc_intent(glink, channel, size,
12028c2ecf20Sopenharmony_ci							 true);
12038c2ecf20Sopenharmony_ci			if (!intent)
12048c2ecf20Sopenharmony_ci				break;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci			qcom_glink_advertise_intent(glink, channel, intent);
12078c2ecf20Sopenharmony_ci		}
12088c2ecf20Sopenharmony_ci	}
12098c2ecf20Sopenharmony_ci	return 0;
12108c2ecf20Sopenharmony_ci}
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_cistatic void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept)
12138c2ecf20Sopenharmony_ci{
12148c2ecf20Sopenharmony_ci	struct glink_channel *channel = to_glink_channel(ept);
12158c2ecf20Sopenharmony_ci	struct qcom_glink *glink = channel->glink;
12168c2ecf20Sopenharmony_ci	unsigned long flags;
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&channel->recv_lock, flags);
12198c2ecf20Sopenharmony_ci	channel->ept.cb = NULL;
12208c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&channel->recv_lock, flags);
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	/* Decouple the potential rpdev from the channel */
12238c2ecf20Sopenharmony_ci	channel->rpdev = NULL;
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	qcom_glink_send_close_req(glink, channel);
12268c2ecf20Sopenharmony_ci}
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_cistatic int qcom_glink_request_intent(struct qcom_glink *glink,
12298c2ecf20Sopenharmony_ci				     struct glink_channel *channel,
12308c2ecf20Sopenharmony_ci				     size_t size)
12318c2ecf20Sopenharmony_ci{
12328c2ecf20Sopenharmony_ci	struct {
12338c2ecf20Sopenharmony_ci		u16 id;
12348c2ecf20Sopenharmony_ci		u16 cid;
12358c2ecf20Sopenharmony_ci		u32 size;
12368c2ecf20Sopenharmony_ci	} __packed cmd;
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	int ret;
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	mutex_lock(&channel->intent_req_lock);
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	reinit_completion(&channel->intent_req_comp);
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	cmd.id = RPM_CMD_RX_INTENT_REQ;
12458c2ecf20Sopenharmony_ci	cmd.cid = channel->lcid;
12468c2ecf20Sopenharmony_ci	cmd.size = size;
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	ret = qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true);
12498c2ecf20Sopenharmony_ci	if (ret)
12508c2ecf20Sopenharmony_ci		goto unlock;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	ret = wait_for_completion_timeout(&channel->intent_req_comp, 10 * HZ);
12538c2ecf20Sopenharmony_ci	if (!ret) {
12548c2ecf20Sopenharmony_ci		dev_err(glink->dev, "intent request timed out\n");
12558c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
12568c2ecf20Sopenharmony_ci	} else {
12578c2ecf20Sopenharmony_ci		ret = channel->intent_req_result ? 0 : -ECANCELED;
12588c2ecf20Sopenharmony_ci	}
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ciunlock:
12618c2ecf20Sopenharmony_ci	mutex_unlock(&channel->intent_req_lock);
12628c2ecf20Sopenharmony_ci	return ret;
12638c2ecf20Sopenharmony_ci}
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_cistatic int __qcom_glink_send(struct glink_channel *channel,
12668c2ecf20Sopenharmony_ci			     void *data, int len, bool wait)
12678c2ecf20Sopenharmony_ci{
12688c2ecf20Sopenharmony_ci	struct qcom_glink *glink = channel->glink;
12698c2ecf20Sopenharmony_ci	struct glink_core_rx_intent *intent = NULL;
12708c2ecf20Sopenharmony_ci	struct glink_core_rx_intent *tmp;
12718c2ecf20Sopenharmony_ci	int iid = 0;
12728c2ecf20Sopenharmony_ci	struct {
12738c2ecf20Sopenharmony_ci		struct glink_msg msg;
12748c2ecf20Sopenharmony_ci		__le32 chunk_size;
12758c2ecf20Sopenharmony_ci		__le32 left_size;
12768c2ecf20Sopenharmony_ci	} __packed req;
12778c2ecf20Sopenharmony_ci	int ret;
12788c2ecf20Sopenharmony_ci	unsigned long flags;
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	if (!glink->intentless) {
12818c2ecf20Sopenharmony_ci		while (!intent) {
12828c2ecf20Sopenharmony_ci			spin_lock_irqsave(&channel->intent_lock, flags);
12838c2ecf20Sopenharmony_ci			idr_for_each_entry(&channel->riids, tmp, iid) {
12848c2ecf20Sopenharmony_ci				if (tmp->size >= len && !tmp->in_use) {
12858c2ecf20Sopenharmony_ci					if (!intent)
12868c2ecf20Sopenharmony_ci						intent = tmp;
12878c2ecf20Sopenharmony_ci					else if (intent->size > tmp->size)
12888c2ecf20Sopenharmony_ci						intent = tmp;
12898c2ecf20Sopenharmony_ci					if (intent->size == len)
12908c2ecf20Sopenharmony_ci						break;
12918c2ecf20Sopenharmony_ci				}
12928c2ecf20Sopenharmony_ci			}
12938c2ecf20Sopenharmony_ci			if (intent)
12948c2ecf20Sopenharmony_ci				intent->in_use = true;
12958c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&channel->intent_lock, flags);
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci			/* We found an available intent */
12988c2ecf20Sopenharmony_ci			if (intent)
12998c2ecf20Sopenharmony_ci				break;
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci			if (!wait)
13028c2ecf20Sopenharmony_ci				return -EBUSY;
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci			ret = qcom_glink_request_intent(glink, channel, len);
13058c2ecf20Sopenharmony_ci			if (ret < 0)
13068c2ecf20Sopenharmony_ci				return ret;
13078c2ecf20Sopenharmony_ci		}
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci		iid = intent->id;
13108c2ecf20Sopenharmony_ci	}
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	req.msg.cmd = cpu_to_le16(RPM_CMD_TX_DATA);
13138c2ecf20Sopenharmony_ci	req.msg.param1 = cpu_to_le16(channel->lcid);
13148c2ecf20Sopenharmony_ci	req.msg.param2 = cpu_to_le32(iid);
13158c2ecf20Sopenharmony_ci	req.chunk_size = cpu_to_le32(len);
13168c2ecf20Sopenharmony_ci	req.left_size = cpu_to_le32(0);
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	ret = qcom_glink_tx(glink, &req, sizeof(req), data, len, wait);
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	/* Mark intent available if we failed */
13218c2ecf20Sopenharmony_ci	if (ret && intent)
13228c2ecf20Sopenharmony_ci		intent->in_use = false;
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	return ret;
13258c2ecf20Sopenharmony_ci}
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_cistatic int qcom_glink_send(struct rpmsg_endpoint *ept, void *data, int len)
13288c2ecf20Sopenharmony_ci{
13298c2ecf20Sopenharmony_ci	struct glink_channel *channel = to_glink_channel(ept);
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	return __qcom_glink_send(channel, data, len, true);
13328c2ecf20Sopenharmony_ci}
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_cistatic int qcom_glink_trysend(struct rpmsg_endpoint *ept, void *data, int len)
13358c2ecf20Sopenharmony_ci{
13368c2ecf20Sopenharmony_ci	struct glink_channel *channel = to_glink_channel(ept);
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	return __qcom_glink_send(channel, data, len, false);
13398c2ecf20Sopenharmony_ci}
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci/*
13428c2ecf20Sopenharmony_ci * Finds the device_node for the glink child interested in this channel.
13438c2ecf20Sopenharmony_ci */
13448c2ecf20Sopenharmony_cistatic struct device_node *qcom_glink_match_channel(struct device_node *node,
13458c2ecf20Sopenharmony_ci						    const char *channel)
13468c2ecf20Sopenharmony_ci{
13478c2ecf20Sopenharmony_ci	struct device_node *child;
13488c2ecf20Sopenharmony_ci	const char *name;
13498c2ecf20Sopenharmony_ci	const char *key;
13508c2ecf20Sopenharmony_ci	int ret;
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	for_each_available_child_of_node(node, child) {
13538c2ecf20Sopenharmony_ci		key = "qcom,glink-channels";
13548c2ecf20Sopenharmony_ci		ret = of_property_read_string(child, key, &name);
13558c2ecf20Sopenharmony_ci		if (ret)
13568c2ecf20Sopenharmony_ci			continue;
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci		if (strcmp(name, channel) == 0)
13598c2ecf20Sopenharmony_ci			return child;
13608c2ecf20Sopenharmony_ci	}
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci	return NULL;
13638c2ecf20Sopenharmony_ci}
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_cistatic const struct rpmsg_device_ops glink_device_ops = {
13668c2ecf20Sopenharmony_ci	.create_ept = qcom_glink_create_ept,
13678c2ecf20Sopenharmony_ci	.announce_create = qcom_glink_announce_create,
13688c2ecf20Sopenharmony_ci};
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_cistatic const struct rpmsg_endpoint_ops glink_endpoint_ops = {
13718c2ecf20Sopenharmony_ci	.destroy_ept = qcom_glink_destroy_ept,
13728c2ecf20Sopenharmony_ci	.send = qcom_glink_send,
13738c2ecf20Sopenharmony_ci	.trysend = qcom_glink_trysend,
13748c2ecf20Sopenharmony_ci};
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_cistatic void qcom_glink_rpdev_release(struct device *dev)
13778c2ecf20Sopenharmony_ci{
13788c2ecf20Sopenharmony_ci	struct rpmsg_device *rpdev = to_rpmsg_device(dev);
13798c2ecf20Sopenharmony_ci	struct glink_channel *channel = to_glink_channel(rpdev->ept);
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_ci	channel->rpdev = NULL;
13828c2ecf20Sopenharmony_ci	kfree(rpdev->driver_override);
13838c2ecf20Sopenharmony_ci	kfree(rpdev);
13848c2ecf20Sopenharmony_ci}
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_cistatic int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid,
13878c2ecf20Sopenharmony_ci			      char *name)
13888c2ecf20Sopenharmony_ci{
13898c2ecf20Sopenharmony_ci	struct glink_channel *channel;
13908c2ecf20Sopenharmony_ci	struct rpmsg_device *rpdev;
13918c2ecf20Sopenharmony_ci	bool create_device = false;
13928c2ecf20Sopenharmony_ci	struct device_node *node;
13938c2ecf20Sopenharmony_ci	int lcid;
13948c2ecf20Sopenharmony_ci	int ret;
13958c2ecf20Sopenharmony_ci	unsigned long flags;
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
13988c2ecf20Sopenharmony_ci	idr_for_each_entry(&glink->lcids, channel, lcid) {
13998c2ecf20Sopenharmony_ci		if (!strcmp(channel->name, name))
14008c2ecf20Sopenharmony_ci			break;
14018c2ecf20Sopenharmony_ci	}
14028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci	if (!channel) {
14058c2ecf20Sopenharmony_ci		channel = qcom_glink_alloc_channel(glink, name);
14068c2ecf20Sopenharmony_ci		if (IS_ERR(channel))
14078c2ecf20Sopenharmony_ci			return PTR_ERR(channel);
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci		/* The opening dance was initiated by the remote */
14108c2ecf20Sopenharmony_ci		create_device = true;
14118c2ecf20Sopenharmony_ci	}
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
14148c2ecf20Sopenharmony_ci	ret = idr_alloc(&glink->rcids, channel, rcid, rcid + 1, GFP_ATOMIC);
14158c2ecf20Sopenharmony_ci	if (ret < 0) {
14168c2ecf20Sopenharmony_ci		dev_err(glink->dev, "Unable to insert channel into rcid list\n");
14178c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&glink->idr_lock, flags);
14188c2ecf20Sopenharmony_ci		goto free_channel;
14198c2ecf20Sopenharmony_ci	}
14208c2ecf20Sopenharmony_ci	channel->rcid = ret;
14218c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	complete_all(&channel->open_req);
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	if (create_device) {
14268c2ecf20Sopenharmony_ci		rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL);
14278c2ecf20Sopenharmony_ci		if (!rpdev) {
14288c2ecf20Sopenharmony_ci			ret = -ENOMEM;
14298c2ecf20Sopenharmony_ci			goto rcid_remove;
14308c2ecf20Sopenharmony_ci		}
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci		rpdev->ept = &channel->ept;
14338c2ecf20Sopenharmony_ci		strncpy(rpdev->id.name, name, RPMSG_NAME_SIZE);
14348c2ecf20Sopenharmony_ci		rpdev->src = RPMSG_ADDR_ANY;
14358c2ecf20Sopenharmony_ci		rpdev->dst = RPMSG_ADDR_ANY;
14368c2ecf20Sopenharmony_ci		rpdev->ops = &glink_device_ops;
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci		node = qcom_glink_match_channel(glink->dev->of_node, name);
14398c2ecf20Sopenharmony_ci		rpdev->dev.of_node = node;
14408c2ecf20Sopenharmony_ci		rpdev->dev.parent = glink->dev;
14418c2ecf20Sopenharmony_ci		rpdev->dev.release = qcom_glink_rpdev_release;
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci		ret = rpmsg_register_device(rpdev);
14448c2ecf20Sopenharmony_ci		if (ret)
14458c2ecf20Sopenharmony_ci			goto rcid_remove;
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci		channel->rpdev = rpdev;
14488c2ecf20Sopenharmony_ci	}
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	return 0;
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_circid_remove:
14538c2ecf20Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
14548c2ecf20Sopenharmony_ci	idr_remove(&glink->rcids, channel->rcid);
14558c2ecf20Sopenharmony_ci	channel->rcid = 0;
14568c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
14578c2ecf20Sopenharmony_cifree_channel:
14588c2ecf20Sopenharmony_ci	/* Release the reference, iff we took it */
14598c2ecf20Sopenharmony_ci	if (create_device)
14608c2ecf20Sopenharmony_ci		kref_put(&channel->refcount, qcom_glink_channel_release);
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	return ret;
14638c2ecf20Sopenharmony_ci}
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_cistatic void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid)
14668c2ecf20Sopenharmony_ci{
14678c2ecf20Sopenharmony_ci	struct rpmsg_channel_info chinfo;
14688c2ecf20Sopenharmony_ci	struct glink_channel *channel;
14698c2ecf20Sopenharmony_ci	unsigned long flags;
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
14728c2ecf20Sopenharmony_ci	channel = idr_find(&glink->rcids, rcid);
14738c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
14748c2ecf20Sopenharmony_ci	if (WARN(!channel, "close request on unknown channel\n"))
14758c2ecf20Sopenharmony_ci		return;
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	/* cancel pending rx_done work */
14788c2ecf20Sopenharmony_ci	cancel_work_sync(&channel->intent_work);
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	if (channel->rpdev) {
14818c2ecf20Sopenharmony_ci		strscpy_pad(chinfo.name, channel->name, sizeof(chinfo.name));
14828c2ecf20Sopenharmony_ci		chinfo.src = RPMSG_ADDR_ANY;
14838c2ecf20Sopenharmony_ci		chinfo.dst = RPMSG_ADDR_ANY;
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci		rpmsg_unregister_device(glink->dev, &chinfo);
14868c2ecf20Sopenharmony_ci	}
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	qcom_glink_send_close_ack(glink, channel->rcid);
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
14918c2ecf20Sopenharmony_ci	idr_remove(&glink->rcids, channel->rcid);
14928c2ecf20Sopenharmony_ci	channel->rcid = 0;
14938c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	kref_put(&channel->refcount, qcom_glink_channel_release);
14968c2ecf20Sopenharmony_ci}
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_cistatic void qcom_glink_rx_close_ack(struct qcom_glink *glink, unsigned int lcid)
14998c2ecf20Sopenharmony_ci{
15008c2ecf20Sopenharmony_ci	struct glink_channel *channel;
15018c2ecf20Sopenharmony_ci	unsigned long flags;
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	spin_lock_irqsave(&glink->idr_lock, flags);
15048c2ecf20Sopenharmony_ci	channel = idr_find(&glink->lcids, lcid);
15058c2ecf20Sopenharmony_ci	if (WARN(!channel, "close ack on unknown channel\n")) {
15068c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&glink->idr_lock, flags);
15078c2ecf20Sopenharmony_ci		return;
15088c2ecf20Sopenharmony_ci	}
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	idr_remove(&glink->lcids, channel->lcid);
15118c2ecf20Sopenharmony_ci	channel->lcid = 0;
15128c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&glink->idr_lock, flags);
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	kref_put(&channel->refcount, qcom_glink_channel_release);
15158c2ecf20Sopenharmony_ci}
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_cistatic void qcom_glink_work(struct work_struct *work)
15188c2ecf20Sopenharmony_ci{
15198c2ecf20Sopenharmony_ci	struct qcom_glink *glink = container_of(work, struct qcom_glink,
15208c2ecf20Sopenharmony_ci						rx_work);
15218c2ecf20Sopenharmony_ci	struct glink_defer_cmd *dcmd;
15228c2ecf20Sopenharmony_ci	struct glink_msg *msg;
15238c2ecf20Sopenharmony_ci	unsigned long flags;
15248c2ecf20Sopenharmony_ci	unsigned int param1;
15258c2ecf20Sopenharmony_ci	unsigned int param2;
15268c2ecf20Sopenharmony_ci	unsigned int cmd;
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	for (;;) {
15298c2ecf20Sopenharmony_ci		spin_lock_irqsave(&glink->rx_lock, flags);
15308c2ecf20Sopenharmony_ci		if (list_empty(&glink->rx_queue)) {
15318c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&glink->rx_lock, flags);
15328c2ecf20Sopenharmony_ci			break;
15338c2ecf20Sopenharmony_ci		}
15348c2ecf20Sopenharmony_ci		dcmd = list_first_entry(&glink->rx_queue,
15358c2ecf20Sopenharmony_ci					struct glink_defer_cmd, node);
15368c2ecf20Sopenharmony_ci		list_del(&dcmd->node);
15378c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&glink->rx_lock, flags);
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci		msg = &dcmd->msg;
15408c2ecf20Sopenharmony_ci		cmd = le16_to_cpu(msg->cmd);
15418c2ecf20Sopenharmony_ci		param1 = le16_to_cpu(msg->param1);
15428c2ecf20Sopenharmony_ci		param2 = le32_to_cpu(msg->param2);
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci		switch (cmd) {
15458c2ecf20Sopenharmony_ci		case RPM_CMD_VERSION:
15468c2ecf20Sopenharmony_ci			qcom_glink_receive_version(glink, param1, param2);
15478c2ecf20Sopenharmony_ci			break;
15488c2ecf20Sopenharmony_ci		case RPM_CMD_VERSION_ACK:
15498c2ecf20Sopenharmony_ci			qcom_glink_receive_version_ack(glink, param1, param2);
15508c2ecf20Sopenharmony_ci			break;
15518c2ecf20Sopenharmony_ci		case RPM_CMD_OPEN:
15528c2ecf20Sopenharmony_ci			qcom_glink_rx_open(glink, param1, msg->data);
15538c2ecf20Sopenharmony_ci			break;
15548c2ecf20Sopenharmony_ci		case RPM_CMD_CLOSE:
15558c2ecf20Sopenharmony_ci			qcom_glink_rx_close(glink, param1);
15568c2ecf20Sopenharmony_ci			break;
15578c2ecf20Sopenharmony_ci		case RPM_CMD_CLOSE_ACK:
15588c2ecf20Sopenharmony_ci			qcom_glink_rx_close_ack(glink, param1);
15598c2ecf20Sopenharmony_ci			break;
15608c2ecf20Sopenharmony_ci		case RPM_CMD_RX_INTENT_REQ:
15618c2ecf20Sopenharmony_ci			qcom_glink_handle_intent_req(glink, param1, param2);
15628c2ecf20Sopenharmony_ci			break;
15638c2ecf20Sopenharmony_ci		default:
15648c2ecf20Sopenharmony_ci			WARN(1, "Unknown defer object %d\n", cmd);
15658c2ecf20Sopenharmony_ci			break;
15668c2ecf20Sopenharmony_ci		}
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci		kfree(dcmd);
15698c2ecf20Sopenharmony_ci	}
15708c2ecf20Sopenharmony_ci}
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_cistatic void qcom_glink_cancel_rx_work(struct qcom_glink *glink)
15738c2ecf20Sopenharmony_ci{
15748c2ecf20Sopenharmony_ci	struct glink_defer_cmd *dcmd;
15758c2ecf20Sopenharmony_ci	struct glink_defer_cmd *tmp;
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci	/* cancel any pending deferred rx_work */
15788c2ecf20Sopenharmony_ci	cancel_work_sync(&glink->rx_work);
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	list_for_each_entry_safe(dcmd, tmp, &glink->rx_queue, node)
15818c2ecf20Sopenharmony_ci		kfree(dcmd);
15828c2ecf20Sopenharmony_ci}
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_cistatic ssize_t rpmsg_name_show(struct device *dev,
15858c2ecf20Sopenharmony_ci			       struct device_attribute *attr, char *buf)
15868c2ecf20Sopenharmony_ci{
15878c2ecf20Sopenharmony_ci	int ret = 0;
15888c2ecf20Sopenharmony_ci	const char *name;
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci	ret = of_property_read_string(dev->of_node, "label", &name);
15918c2ecf20Sopenharmony_ci	if (ret < 0)
15928c2ecf20Sopenharmony_ci		name = dev->of_node->name;
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci	return snprintf(buf, RPMSG_NAME_SIZE, "%s\n", name);
15958c2ecf20Sopenharmony_ci}
15968c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(rpmsg_name);
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_cistatic struct attribute *qcom_glink_attrs[] = {
15998c2ecf20Sopenharmony_ci	&dev_attr_rpmsg_name.attr,
16008c2ecf20Sopenharmony_ci	NULL
16018c2ecf20Sopenharmony_ci};
16028c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(qcom_glink);
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_cistatic void qcom_glink_device_release(struct device *dev)
16058c2ecf20Sopenharmony_ci{
16068c2ecf20Sopenharmony_ci	struct rpmsg_device *rpdev = to_rpmsg_device(dev);
16078c2ecf20Sopenharmony_ci	struct glink_channel *channel = to_glink_channel(rpdev->ept);
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci	/* Release qcom_glink_alloc_channel() reference */
16108c2ecf20Sopenharmony_ci	kref_put(&channel->refcount, qcom_glink_channel_release);
16118c2ecf20Sopenharmony_ci	kfree(rpdev->driver_override);
16128c2ecf20Sopenharmony_ci	kfree(rpdev);
16138c2ecf20Sopenharmony_ci}
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_cistatic int qcom_glink_create_chrdev(struct qcom_glink *glink)
16168c2ecf20Sopenharmony_ci{
16178c2ecf20Sopenharmony_ci	struct rpmsg_device *rpdev;
16188c2ecf20Sopenharmony_ci	struct glink_channel *channel;
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL);
16218c2ecf20Sopenharmony_ci	if (!rpdev)
16228c2ecf20Sopenharmony_ci		return -ENOMEM;
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci	channel = qcom_glink_alloc_channel(glink, "rpmsg_chrdev");
16258c2ecf20Sopenharmony_ci	if (IS_ERR(channel)) {
16268c2ecf20Sopenharmony_ci		kfree(rpdev);
16278c2ecf20Sopenharmony_ci		return PTR_ERR(channel);
16288c2ecf20Sopenharmony_ci	}
16298c2ecf20Sopenharmony_ci	channel->rpdev = rpdev;
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci	rpdev->ept = &channel->ept;
16328c2ecf20Sopenharmony_ci	rpdev->ops = &glink_device_ops;
16338c2ecf20Sopenharmony_ci	rpdev->dev.parent = glink->dev;
16348c2ecf20Sopenharmony_ci	rpdev->dev.release = qcom_glink_device_release;
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_ci	return rpmsg_chrdev_register_device(rpdev);
16378c2ecf20Sopenharmony_ci}
16388c2ecf20Sopenharmony_ci
16398c2ecf20Sopenharmony_cistruct qcom_glink *qcom_glink_native_probe(struct device *dev,
16408c2ecf20Sopenharmony_ci					   unsigned long features,
16418c2ecf20Sopenharmony_ci					   struct qcom_glink_pipe *rx,
16428c2ecf20Sopenharmony_ci					   struct qcom_glink_pipe *tx,
16438c2ecf20Sopenharmony_ci					   bool intentless)
16448c2ecf20Sopenharmony_ci{
16458c2ecf20Sopenharmony_ci	int irq;
16468c2ecf20Sopenharmony_ci	int ret;
16478c2ecf20Sopenharmony_ci	struct qcom_glink *glink;
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci	glink = devm_kzalloc(dev, sizeof(*glink), GFP_KERNEL);
16508c2ecf20Sopenharmony_ci	if (!glink)
16518c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ci	glink->dev = dev;
16548c2ecf20Sopenharmony_ci	glink->tx_pipe = tx;
16558c2ecf20Sopenharmony_ci	glink->rx_pipe = rx;
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	glink->features = features;
16588c2ecf20Sopenharmony_ci	glink->intentless = intentless;
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	spin_lock_init(&glink->tx_lock);
16618c2ecf20Sopenharmony_ci	spin_lock_init(&glink->rx_lock);
16628c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&glink->rx_queue);
16638c2ecf20Sopenharmony_ci	INIT_WORK(&glink->rx_work, qcom_glink_work);
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci	spin_lock_init(&glink->idr_lock);
16668c2ecf20Sopenharmony_ci	idr_init(&glink->lcids);
16678c2ecf20Sopenharmony_ci	idr_init(&glink->rcids);
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	glink->dev->groups = qcom_glink_groups;
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci	ret = device_add_groups(dev, qcom_glink_groups);
16728c2ecf20Sopenharmony_ci	if (ret)
16738c2ecf20Sopenharmony_ci		dev_err(dev, "failed to add groups\n");
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	ret = of_property_read_string(dev->of_node, "label", &glink->name);
16768c2ecf20Sopenharmony_ci	if (ret < 0)
16778c2ecf20Sopenharmony_ci		glink->name = dev->of_node->name;
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci	glink->mbox_client.dev = dev;
16808c2ecf20Sopenharmony_ci	glink->mbox_client.knows_txdone = true;
16818c2ecf20Sopenharmony_ci	glink->mbox_chan = mbox_request_channel(&glink->mbox_client, 0);
16828c2ecf20Sopenharmony_ci	if (IS_ERR(glink->mbox_chan)) {
16838c2ecf20Sopenharmony_ci		if (PTR_ERR(glink->mbox_chan) != -EPROBE_DEFER)
16848c2ecf20Sopenharmony_ci			dev_err(dev, "failed to acquire IPC channel\n");
16858c2ecf20Sopenharmony_ci		return ERR_CAST(glink->mbox_chan);
16868c2ecf20Sopenharmony_ci	}
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci	irq = of_irq_get(dev->of_node, 0);
16898c2ecf20Sopenharmony_ci	ret = devm_request_irq(dev, irq,
16908c2ecf20Sopenharmony_ci			       qcom_glink_native_intr,
16918c2ecf20Sopenharmony_ci			       IRQF_NO_SUSPEND | IRQF_SHARED,
16928c2ecf20Sopenharmony_ci			       "glink-native", glink);
16938c2ecf20Sopenharmony_ci	if (ret) {
16948c2ecf20Sopenharmony_ci		dev_err(dev, "failed to request IRQ\n");
16958c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
16968c2ecf20Sopenharmony_ci	}
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	glink->irq = irq;
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_ci	ret = qcom_glink_send_version(glink);
17018c2ecf20Sopenharmony_ci	if (ret)
17028c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci	ret = qcom_glink_create_chrdev(glink);
17058c2ecf20Sopenharmony_ci	if (ret)
17068c2ecf20Sopenharmony_ci		dev_err(glink->dev, "failed to register chrdev\n");
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci	return glink;
17098c2ecf20Sopenharmony_ci}
17108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_glink_native_probe);
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_cistatic int qcom_glink_remove_device(struct device *dev, void *data)
17138c2ecf20Sopenharmony_ci{
17148c2ecf20Sopenharmony_ci	device_unregister(dev);
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci	return 0;
17178c2ecf20Sopenharmony_ci}
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_civoid qcom_glink_native_remove(struct qcom_glink *glink)
17208c2ecf20Sopenharmony_ci{
17218c2ecf20Sopenharmony_ci	struct glink_channel *channel;
17228c2ecf20Sopenharmony_ci	int cid;
17238c2ecf20Sopenharmony_ci	int ret;
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	disable_irq(glink->irq);
17268c2ecf20Sopenharmony_ci	qcom_glink_cancel_rx_work(glink);
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	ret = device_for_each_child(glink->dev, NULL, qcom_glink_remove_device);
17298c2ecf20Sopenharmony_ci	if (ret)
17308c2ecf20Sopenharmony_ci		dev_warn(glink->dev, "Can't remove GLINK devices: %d\n", ret);
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	/* Release any defunct local channels, waiting for close-ack */
17338c2ecf20Sopenharmony_ci	idr_for_each_entry(&glink->lcids, channel, cid)
17348c2ecf20Sopenharmony_ci		kref_put(&channel->refcount, qcom_glink_channel_release);
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci	/* Release any defunct local channels, waiting for close-req */
17378c2ecf20Sopenharmony_ci	idr_for_each_entry(&glink->rcids, channel, cid)
17388c2ecf20Sopenharmony_ci		kref_put(&channel->refcount, qcom_glink_channel_release);
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	idr_destroy(&glink->lcids);
17418c2ecf20Sopenharmony_ci	idr_destroy(&glink->rcids);
17428c2ecf20Sopenharmony_ci	mbox_free_channel(glink->mbox_chan);
17438c2ecf20Sopenharmony_ci}
17448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_glink_native_remove);
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_civoid qcom_glink_native_unregister(struct qcom_glink *glink)
17478c2ecf20Sopenharmony_ci{
17488c2ecf20Sopenharmony_ci	device_unregister(glink->dev);
17498c2ecf20Sopenharmony_ci}
17508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_glink_native_unregister);
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm GLINK driver");
17538c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1754