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