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