1b4f8e52bSBjorn Andersson /* 2b4f8e52bSBjorn Andersson * Copyright (c) 2016-2017, Linaro Ltd 3b4f8e52bSBjorn Andersson * 4b4f8e52bSBjorn Andersson * This program is free software; you can redistribute it and/or modify 5b4f8e52bSBjorn Andersson * it under the terms of the GNU General Public License version 2 and 6b4f8e52bSBjorn Andersson * only version 2 as published by the Free Software Foundation. 7b4f8e52bSBjorn Andersson * 8b4f8e52bSBjorn Andersson * This program is distributed in the hope that it will be useful, 9b4f8e52bSBjorn Andersson * but WITHOUT ANY WARRANTY; without even the implied warranty of 10b4f8e52bSBjorn Andersson * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11b4f8e52bSBjorn Andersson * GNU General Public License for more details. 12b4f8e52bSBjorn Andersson */ 13b4f8e52bSBjorn Andersson 14b4f8e52bSBjorn Andersson #include <linux/idr.h> 15b4f8e52bSBjorn Andersson #include <linux/interrupt.h> 16b4f8e52bSBjorn Andersson #include <linux/io.h> 17b4f8e52bSBjorn Andersson #include <linux/list.h> 18b4f8e52bSBjorn Andersson #include <linux/mfd/syscon.h> 19b4f8e52bSBjorn Andersson #include <linux/module.h> 20b4f8e52bSBjorn Andersson #include <linux/of.h> 21b4f8e52bSBjorn Andersson #include <linux/of_address.h> 22b4f8e52bSBjorn Andersson #include <linux/platform_device.h> 23b4f8e52bSBjorn Andersson #include <linux/regmap.h> 24b4f8e52bSBjorn Andersson #include <linux/rpmsg.h> 25b4f8e52bSBjorn Andersson #include <linux/slab.h> 26b4f8e52bSBjorn Andersson #include <linux/workqueue.h> 27b4f8e52bSBjorn Andersson #include <linux/mailbox_client.h> 28b4f8e52bSBjorn Andersson 29b4f8e52bSBjorn Andersson #include "rpmsg_internal.h" 30b4f8e52bSBjorn Andersson 31b4f8e52bSBjorn Andersson #define RPM_TOC_SIZE 256 32b4f8e52bSBjorn Andersson #define RPM_TOC_MAGIC 0x67727430 /* grt0 */ 33b4f8e52bSBjorn Andersson #define RPM_TOC_MAX_ENTRIES ((RPM_TOC_SIZE - sizeof(struct rpm_toc)) / \ 34b4f8e52bSBjorn Andersson sizeof(struct rpm_toc_entry)) 35b4f8e52bSBjorn Andersson 36b4f8e52bSBjorn Andersson #define RPM_TX_FIFO_ID 0x61703272 /* ap2r */ 37b4f8e52bSBjorn Andersson #define RPM_RX_FIFO_ID 0x72326170 /* r2ap */ 38b4f8e52bSBjorn Andersson 39b4f8e52bSBjorn Andersson #define GLINK_NAME_SIZE 32 40b4f8e52bSBjorn Andersson 41b4f8e52bSBjorn Andersson #define RPM_GLINK_CID_MIN 1 42b4f8e52bSBjorn Andersson #define RPM_GLINK_CID_MAX 65536 43b4f8e52bSBjorn Andersson 44e45c5dc2SBjorn Andersson #define to_rpm_pipe(p) container_of(p, struct glink_rpm_pipe, native) 45e45c5dc2SBjorn Andersson 46b4f8e52bSBjorn Andersson struct rpm_toc_entry { 47b4f8e52bSBjorn Andersson __le32 id; 48b4f8e52bSBjorn Andersson __le32 offset; 49b4f8e52bSBjorn Andersson __le32 size; 50b4f8e52bSBjorn Andersson } __packed; 51b4f8e52bSBjorn Andersson 52e45c5dc2SBjorn Andersson struct qcom_glink; 53e45c5dc2SBjorn Andersson 54e45c5dc2SBjorn Andersson struct qcom_glink_pipe { 55e45c5dc2SBjorn Andersson size_t length; 56e45c5dc2SBjorn Andersson 57e45c5dc2SBjorn Andersson size_t (*avail)(struct qcom_glink_pipe *glink_pipe); 58e45c5dc2SBjorn Andersson void (*peak)(struct qcom_glink_pipe *glink_pipe, void *data, 59e45c5dc2SBjorn Andersson size_t count); 60e45c5dc2SBjorn Andersson void (*advance)(struct qcom_glink_pipe *glink_pipe, size_t count); 61e45c5dc2SBjorn Andersson void (*write)(struct qcom_glink_pipe *glink_pipe, 62e45c5dc2SBjorn Andersson const void *hdr, size_t hlen, 63e45c5dc2SBjorn Andersson const void *data, size_t dlen); 64e45c5dc2SBjorn Andersson }; 65e45c5dc2SBjorn Andersson 66b4f8e52bSBjorn Andersson struct rpm_toc { 67b4f8e52bSBjorn Andersson __le32 magic; 68b4f8e52bSBjorn Andersson __le32 count; 69b4f8e52bSBjorn Andersson 70b4f8e52bSBjorn Andersson struct rpm_toc_entry entries[]; 71b4f8e52bSBjorn Andersson } __packed; 72b4f8e52bSBjorn Andersson 73b4f8e52bSBjorn Andersson struct glink_msg { 74b4f8e52bSBjorn Andersson __le16 cmd; 75b4f8e52bSBjorn Andersson __le16 param1; 76b4f8e52bSBjorn Andersson __le32 param2; 77b4f8e52bSBjorn Andersson u8 data[]; 78b4f8e52bSBjorn Andersson } __packed; 79b4f8e52bSBjorn Andersson 80b4f8e52bSBjorn Andersson struct glink_rpm_pipe { 81e45c5dc2SBjorn Andersson struct qcom_glink_pipe native; 82e45c5dc2SBjorn Andersson 83b4f8e52bSBjorn Andersson void __iomem *tail; 84b4f8e52bSBjorn Andersson void __iomem *head; 85b4f8e52bSBjorn Andersson 86b4f8e52bSBjorn Andersson void __iomem *fifo; 87b4f8e52bSBjorn Andersson }; 88b4f8e52bSBjorn Andersson 89b4f8e52bSBjorn Andersson /** 90b4f8e52bSBjorn Andersson * struct glink_defer_cmd - deferred incoming control message 91b4f8e52bSBjorn Andersson * @node: list node 92b4f8e52bSBjorn Andersson * @msg: message header 93b4f8e52bSBjorn Andersson * data: payload of the message 94b4f8e52bSBjorn Andersson * 95b4f8e52bSBjorn Andersson * Copy of a received control message, to be added to @rx_queue and processed 96b4f8e52bSBjorn Andersson * by @rx_work of @glink_rpm. 97b4f8e52bSBjorn Andersson */ 98b4f8e52bSBjorn Andersson struct glink_defer_cmd { 99b4f8e52bSBjorn Andersson struct list_head node; 100b4f8e52bSBjorn Andersson 101b4f8e52bSBjorn Andersson struct glink_msg msg; 102b4f8e52bSBjorn Andersson u8 data[]; 103b4f8e52bSBjorn Andersson }; 104b4f8e52bSBjorn Andersson 105b4f8e52bSBjorn Andersson /** 106b4f8e52bSBjorn Andersson * struct glink_rpm - driver context, relates to one remote subsystem 107b4f8e52bSBjorn Andersson * @dev: reference to the associated struct device 108b4f8e52bSBjorn Andersson * @doorbell: "rpm_hlos" ipc doorbell 109b4f8e52bSBjorn Andersson * @rx_pipe: pipe object for receive FIFO 110b4f8e52bSBjorn Andersson * @tx_pipe: pipe object for transmit FIFO 111b4f8e52bSBjorn Andersson * @irq: IRQ for signaling incoming events 112b4f8e52bSBjorn Andersson * @rx_work: worker for handling received control messages 113b4f8e52bSBjorn Andersson * @rx_lock: protects the @rx_queue 114b4f8e52bSBjorn Andersson * @rx_queue: queue of received control messages to be processed in @rx_work 115b4f8e52bSBjorn Andersson * @tx_lock: synchronizes operations on the tx fifo 116b4f8e52bSBjorn Andersson * @idr_lock: synchronizes @lcids and @rcids modifications 117b4f8e52bSBjorn Andersson * @lcids: idr of all channels with a known local channel id 118b4f8e52bSBjorn Andersson * @rcids: idr of all channels with a known remote channel id 119b4f8e52bSBjorn Andersson */ 120d7101febSBjorn Andersson struct qcom_glink { 121b4f8e52bSBjorn Andersson struct device *dev; 122b4f8e52bSBjorn Andersson 123b4f8e52bSBjorn Andersson struct mbox_client mbox_client; 124b4f8e52bSBjorn Andersson struct mbox_chan *mbox_chan; 125b4f8e52bSBjorn Andersson 126e45c5dc2SBjorn Andersson struct qcom_glink_pipe *rx_pipe; 127e45c5dc2SBjorn Andersson struct qcom_glink_pipe *tx_pipe; 128b4f8e52bSBjorn Andersson 129b4f8e52bSBjorn Andersson int irq; 130b4f8e52bSBjorn Andersson 131b4f8e52bSBjorn Andersson struct work_struct rx_work; 132b4f8e52bSBjorn Andersson spinlock_t rx_lock; 133b4f8e52bSBjorn Andersson struct list_head rx_queue; 134b4f8e52bSBjorn Andersson 135b4f8e52bSBjorn Andersson struct mutex tx_lock; 136b4f8e52bSBjorn Andersson 137b4f8e52bSBjorn Andersson struct mutex idr_lock; 138b4f8e52bSBjorn Andersson struct idr lcids; 139b4f8e52bSBjorn Andersson struct idr rcids; 140b4f8e52bSBjorn Andersson }; 141b4f8e52bSBjorn Andersson 142b4f8e52bSBjorn Andersson enum { 143b4f8e52bSBjorn Andersson GLINK_STATE_CLOSED, 144b4f8e52bSBjorn Andersson GLINK_STATE_OPENING, 145b4f8e52bSBjorn Andersson GLINK_STATE_OPEN, 146b4f8e52bSBjorn Andersson GLINK_STATE_CLOSING, 147b4f8e52bSBjorn Andersson }; 148b4f8e52bSBjorn Andersson 149b4f8e52bSBjorn Andersson /** 150b4f8e52bSBjorn Andersson * struct glink_channel - internal representation of a channel 151b4f8e52bSBjorn Andersson * @rpdev: rpdev reference, only used for primary endpoints 152b4f8e52bSBjorn Andersson * @ept: rpmsg endpoint this channel is associated with 153d7101febSBjorn Andersson * @glink: qcom_glink context handle 154b4f8e52bSBjorn Andersson * @refcount: refcount for the channel object 155b4f8e52bSBjorn Andersson * @recv_lock: guard for @ept.cb 156b4f8e52bSBjorn Andersson * @name: unique channel name/identifier 157b4f8e52bSBjorn Andersson * @lcid: channel id, in local space 158b4f8e52bSBjorn Andersson * @rcid: channel id, in remote space 159b4f8e52bSBjorn Andersson * @buf: receive buffer, for gathering fragments 160b4f8e52bSBjorn Andersson * @buf_offset: write offset in @buf 161b4f8e52bSBjorn Andersson * @buf_size: size of current @buf 162b4f8e52bSBjorn Andersson * @open_ack: completed once remote has acked the open-request 163b4f8e52bSBjorn Andersson * @open_req: completed once open-request has been received 164b4f8e52bSBjorn Andersson */ 165b4f8e52bSBjorn Andersson struct glink_channel { 166b4f8e52bSBjorn Andersson struct rpmsg_endpoint ept; 167b4f8e52bSBjorn Andersson 168b4f8e52bSBjorn Andersson struct rpmsg_device *rpdev; 169d7101febSBjorn Andersson struct qcom_glink *glink; 170b4f8e52bSBjorn Andersson 171b4f8e52bSBjorn Andersson struct kref refcount; 172b4f8e52bSBjorn Andersson 173b4f8e52bSBjorn Andersson spinlock_t recv_lock; 174b4f8e52bSBjorn Andersson 175b4f8e52bSBjorn Andersson char *name; 176b4f8e52bSBjorn Andersson unsigned int lcid; 177b4f8e52bSBjorn Andersson unsigned int rcid; 178b4f8e52bSBjorn Andersson 179b4f8e52bSBjorn Andersson void *buf; 180b4f8e52bSBjorn Andersson int buf_offset; 181b4f8e52bSBjorn Andersson int buf_size; 182b4f8e52bSBjorn Andersson 183b4f8e52bSBjorn Andersson struct completion open_ack; 184b4f8e52bSBjorn Andersson struct completion open_req; 185b4f8e52bSBjorn Andersson }; 186b4f8e52bSBjorn Andersson 187b4f8e52bSBjorn Andersson #define to_glink_channel(_ept) container_of(_ept, struct glink_channel, ept) 188b4f8e52bSBjorn Andersson 189b4f8e52bSBjorn Andersson static const struct rpmsg_endpoint_ops glink_endpoint_ops; 190b4f8e52bSBjorn Andersson 191b4f8e52bSBjorn Andersson #define RPM_CMD_VERSION 0 192b4f8e52bSBjorn Andersson #define RPM_CMD_VERSION_ACK 1 193b4f8e52bSBjorn Andersson #define RPM_CMD_OPEN 2 194b4f8e52bSBjorn Andersson #define RPM_CMD_CLOSE 3 195b4f8e52bSBjorn Andersson #define RPM_CMD_OPEN_ACK 4 196b4f8e52bSBjorn Andersson #define RPM_CMD_TX_DATA 9 197b4f8e52bSBjorn Andersson #define RPM_CMD_CLOSE_ACK 11 198b4f8e52bSBjorn Andersson #define RPM_CMD_TX_DATA_CONT 12 199b4f8e52bSBjorn Andersson #define RPM_CMD_READ_NOTIF 13 200b4f8e52bSBjorn Andersson 201b4f8e52bSBjorn Andersson #define GLINK_FEATURE_INTENTLESS BIT(1) 202b4f8e52bSBjorn Andersson 203d7101febSBjorn Andersson static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, 204b4f8e52bSBjorn Andersson const char *name) 205b4f8e52bSBjorn Andersson { 206b4f8e52bSBjorn Andersson struct glink_channel *channel; 207b4f8e52bSBjorn Andersson 208b4f8e52bSBjorn Andersson channel = kzalloc(sizeof(*channel), GFP_KERNEL); 209b4f8e52bSBjorn Andersson if (!channel) 210b4f8e52bSBjorn Andersson return ERR_PTR(-ENOMEM); 211b4f8e52bSBjorn Andersson 212b4f8e52bSBjorn Andersson /* Setup glink internal glink_channel data */ 213b4f8e52bSBjorn Andersson spin_lock_init(&channel->recv_lock); 214b4f8e52bSBjorn Andersson channel->glink = glink; 215b4f8e52bSBjorn Andersson channel->name = kstrdup(name, GFP_KERNEL); 216b4f8e52bSBjorn Andersson 217b4f8e52bSBjorn Andersson init_completion(&channel->open_req); 218b4f8e52bSBjorn Andersson init_completion(&channel->open_ack); 219b4f8e52bSBjorn Andersson 220b4f8e52bSBjorn Andersson kref_init(&channel->refcount); 221b4f8e52bSBjorn Andersson 222b4f8e52bSBjorn Andersson return channel; 223b4f8e52bSBjorn Andersson } 224b4f8e52bSBjorn Andersson 225d7101febSBjorn Andersson static void qcom_glink_channel_release(struct kref *ref) 226b4f8e52bSBjorn Andersson { 227b4f8e52bSBjorn Andersson struct glink_channel *channel = container_of(ref, struct glink_channel, 228b4f8e52bSBjorn Andersson refcount); 229b4f8e52bSBjorn Andersson 230b4f8e52bSBjorn Andersson kfree(channel->name); 231b4f8e52bSBjorn Andersson kfree(channel); 232b4f8e52bSBjorn Andersson } 233b4f8e52bSBjorn Andersson 234e45c5dc2SBjorn Andersson static size_t glink_rpm_rx_avail(struct qcom_glink_pipe *glink_pipe) 235b4f8e52bSBjorn Andersson { 236e45c5dc2SBjorn Andersson struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 237b4f8e52bSBjorn Andersson unsigned int head; 238b4f8e52bSBjorn Andersson unsigned int tail; 239b4f8e52bSBjorn Andersson 240b4f8e52bSBjorn Andersson head = readl(pipe->head); 241b4f8e52bSBjorn Andersson tail = readl(pipe->tail); 242b4f8e52bSBjorn Andersson 243b4f8e52bSBjorn Andersson if (head < tail) 244e45c5dc2SBjorn Andersson return pipe->native.length - tail + head; 245b4f8e52bSBjorn Andersson else 246b4f8e52bSBjorn Andersson return head - tail; 247b4f8e52bSBjorn Andersson } 248b4f8e52bSBjorn Andersson 249e45c5dc2SBjorn Andersson static size_t qcom_glink_rx_avail(struct qcom_glink *glink) 250e45c5dc2SBjorn Andersson { 251e45c5dc2SBjorn Andersson return glink->rx_pipe->avail(glink->rx_pipe); 252e45c5dc2SBjorn Andersson } 253e45c5dc2SBjorn Andersson 254e45c5dc2SBjorn Andersson static void glink_rpm_rx_peak(struct qcom_glink_pipe *glink_pipe, 255b4f8e52bSBjorn Andersson void *data, size_t count) 256b4f8e52bSBjorn Andersson { 257e45c5dc2SBjorn Andersson struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 258b4f8e52bSBjorn Andersson unsigned int tail; 259b4f8e52bSBjorn Andersson size_t len; 260b4f8e52bSBjorn Andersson 261b4f8e52bSBjorn Andersson tail = readl(pipe->tail); 262b4f8e52bSBjorn Andersson 263e45c5dc2SBjorn Andersson len = min_t(size_t, count, pipe->native.length - tail); 264b4f8e52bSBjorn Andersson if (len) { 265b4f8e52bSBjorn Andersson __ioread32_copy(data, pipe->fifo + tail, 266b4f8e52bSBjorn Andersson len / sizeof(u32)); 267b4f8e52bSBjorn Andersson } 268b4f8e52bSBjorn Andersson 269b4f8e52bSBjorn Andersson if (len != count) { 270b4f8e52bSBjorn Andersson __ioread32_copy(data + len, pipe->fifo, 271b4f8e52bSBjorn Andersson (count - len) / sizeof(u32)); 272b4f8e52bSBjorn Andersson } 273b4f8e52bSBjorn Andersson } 274b4f8e52bSBjorn Andersson 275e45c5dc2SBjorn Andersson static void qcom_glink_rx_peak(struct qcom_glink *glink, 276e45c5dc2SBjorn Andersson void *data, size_t count) 277e45c5dc2SBjorn Andersson { 278e45c5dc2SBjorn Andersson glink->rx_pipe->peak(glink->rx_pipe, data, count); 279e45c5dc2SBjorn Andersson } 280e45c5dc2SBjorn Andersson 281e45c5dc2SBjorn Andersson static void glink_rpm_rx_advance(struct qcom_glink_pipe *glink_pipe, 282b4f8e52bSBjorn Andersson size_t count) 283b4f8e52bSBjorn Andersson { 284e45c5dc2SBjorn Andersson struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 285b4f8e52bSBjorn Andersson unsigned int tail; 286b4f8e52bSBjorn Andersson 287b4f8e52bSBjorn Andersson tail = readl(pipe->tail); 288b4f8e52bSBjorn Andersson 289b4f8e52bSBjorn Andersson tail += count; 290e45c5dc2SBjorn Andersson if (tail >= pipe->native.length) 291e45c5dc2SBjorn Andersson tail -= pipe->native.length; 292b4f8e52bSBjorn Andersson 293b4f8e52bSBjorn Andersson writel(tail, pipe->tail); 294b4f8e52bSBjorn Andersson } 295b4f8e52bSBjorn Andersson 296e45c5dc2SBjorn Andersson static void qcom_glink_rx_advance(struct qcom_glink *glink, size_t count) 297b4f8e52bSBjorn Andersson { 298e45c5dc2SBjorn Andersson glink->rx_pipe->advance(glink->rx_pipe, count); 299e45c5dc2SBjorn Andersson } 300e45c5dc2SBjorn Andersson 301e45c5dc2SBjorn Andersson static size_t glink_rpm_tx_avail(struct qcom_glink_pipe *glink_pipe) 302e45c5dc2SBjorn Andersson { 303e45c5dc2SBjorn Andersson struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 304b4f8e52bSBjorn Andersson unsigned int head; 305b4f8e52bSBjorn Andersson unsigned int tail; 306b4f8e52bSBjorn Andersson 307b4f8e52bSBjorn Andersson head = readl(pipe->head); 308b4f8e52bSBjorn Andersson tail = readl(pipe->tail); 309b4f8e52bSBjorn Andersson 310b4f8e52bSBjorn Andersson if (tail <= head) 311e45c5dc2SBjorn Andersson return pipe->native.length - head + tail; 312b4f8e52bSBjorn Andersson else 313b4f8e52bSBjorn Andersson return tail - head; 314b4f8e52bSBjorn Andersson } 315b4f8e52bSBjorn Andersson 316e45c5dc2SBjorn Andersson static size_t qcom_glink_tx_avail(struct qcom_glink *glink) 317e45c5dc2SBjorn Andersson { 318e45c5dc2SBjorn Andersson return glink->tx_pipe->avail(glink->tx_pipe); 319e45c5dc2SBjorn Andersson } 320e45c5dc2SBjorn Andersson 321e45c5dc2SBjorn Andersson static unsigned int glink_rpm_tx_write_one(struct glink_rpm_pipe *pipe, 322b4f8e52bSBjorn Andersson unsigned int head, 323b4f8e52bSBjorn Andersson const void *data, size_t count) 324b4f8e52bSBjorn Andersson { 325b4f8e52bSBjorn Andersson size_t len; 326b4f8e52bSBjorn Andersson 327e45c5dc2SBjorn Andersson len = min_t(size_t, count, pipe->native.length - head); 328b4f8e52bSBjorn Andersson if (len) { 329b4f8e52bSBjorn Andersson __iowrite32_copy(pipe->fifo + head, data, 330b4f8e52bSBjorn Andersson len / sizeof(u32)); 331b4f8e52bSBjorn Andersson } 332b4f8e52bSBjorn Andersson 333b4f8e52bSBjorn Andersson if (len != count) { 334b4f8e52bSBjorn Andersson __iowrite32_copy(pipe->fifo, data + len, 335b4f8e52bSBjorn Andersson (count - len) / sizeof(u32)); 336b4f8e52bSBjorn Andersson } 337b4f8e52bSBjorn Andersson 338b4f8e52bSBjorn Andersson head += count; 339e45c5dc2SBjorn Andersson if (head >= pipe->native.length) 340e45c5dc2SBjorn Andersson head -= pipe->native.length; 341b4f8e52bSBjorn Andersson 342b4f8e52bSBjorn Andersson return head; 343b4f8e52bSBjorn Andersson } 344b4f8e52bSBjorn Andersson 345e45c5dc2SBjorn Andersson static void glink_rpm_tx_write(struct qcom_glink_pipe *glink_pipe, 346e45c5dc2SBjorn Andersson const void *hdr, size_t hlen, 347e45c5dc2SBjorn Andersson const void *data, size_t dlen) 348e45c5dc2SBjorn Andersson { 349e45c5dc2SBjorn Andersson struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 350e45c5dc2SBjorn Andersson unsigned int head; 351e45c5dc2SBjorn Andersson 352e45c5dc2SBjorn Andersson head = readl(pipe->head); 353e45c5dc2SBjorn Andersson head = glink_rpm_tx_write_one(pipe, head, hdr, hlen); 354e45c5dc2SBjorn Andersson head = glink_rpm_tx_write_one(pipe, head, data, dlen); 355e45c5dc2SBjorn Andersson writel(head, pipe->head); 356e45c5dc2SBjorn Andersson } 357e45c5dc2SBjorn Andersson 358e45c5dc2SBjorn Andersson static void qcom_glink_tx_write(struct qcom_glink *glink, 359e45c5dc2SBjorn Andersson const void *hdr, size_t hlen, 360e45c5dc2SBjorn Andersson const void *data, size_t dlen) 361e45c5dc2SBjorn Andersson { 362e45c5dc2SBjorn Andersson glink->tx_pipe->write(glink->tx_pipe, hdr, hlen, data, dlen); 363e45c5dc2SBjorn Andersson } 364e45c5dc2SBjorn Andersson 365d7101febSBjorn Andersson static int qcom_glink_tx(struct qcom_glink *glink, 366b4f8e52bSBjorn Andersson const void *hdr, size_t hlen, 367b4f8e52bSBjorn Andersson const void *data, size_t dlen, bool wait) 368b4f8e52bSBjorn Andersson { 369b4f8e52bSBjorn Andersson unsigned int tlen = hlen + dlen; 370b4f8e52bSBjorn Andersson int ret; 371b4f8e52bSBjorn Andersson 372b4f8e52bSBjorn Andersson /* Reject packets that are too big */ 373e45c5dc2SBjorn Andersson if (tlen >= glink->tx_pipe->length) 374b4f8e52bSBjorn Andersson return -EINVAL; 375b4f8e52bSBjorn Andersson 376b4f8e52bSBjorn Andersson if (WARN(tlen % 8, "Unaligned TX request")) 377b4f8e52bSBjorn Andersson return -EINVAL; 378b4f8e52bSBjorn Andersson 379b4f8e52bSBjorn Andersson ret = mutex_lock_interruptible(&glink->tx_lock); 380b4f8e52bSBjorn Andersson if (ret) 381b4f8e52bSBjorn Andersson return ret; 382b4f8e52bSBjorn Andersson 383d7101febSBjorn Andersson while (qcom_glink_tx_avail(glink) < tlen) { 384b4f8e52bSBjorn Andersson if (!wait) { 385b4f8e52bSBjorn Andersson ret = -ENOMEM; 386b4f8e52bSBjorn Andersson goto out; 387b4f8e52bSBjorn Andersson } 388b4f8e52bSBjorn Andersson 389b4f8e52bSBjorn Andersson msleep(10); 390b4f8e52bSBjorn Andersson } 391b4f8e52bSBjorn Andersson 392e45c5dc2SBjorn Andersson qcom_glink_tx_write(glink, hdr, hlen, data, dlen); 393b4f8e52bSBjorn Andersson 394b4f8e52bSBjorn Andersson mbox_send_message(glink->mbox_chan, NULL); 395b4f8e52bSBjorn Andersson mbox_client_txdone(glink->mbox_chan, 0); 396b4f8e52bSBjorn Andersson 397b4f8e52bSBjorn Andersson out: 398b4f8e52bSBjorn Andersson mutex_unlock(&glink->tx_lock); 399b4f8e52bSBjorn Andersson 400b4f8e52bSBjorn Andersson return ret; 401b4f8e52bSBjorn Andersson } 402b4f8e52bSBjorn Andersson 403d7101febSBjorn Andersson static int qcom_glink_send_version(struct qcom_glink *glink) 404b4f8e52bSBjorn Andersson { 405b4f8e52bSBjorn Andersson struct glink_msg msg; 406b4f8e52bSBjorn Andersson 407b4f8e52bSBjorn Andersson msg.cmd = cpu_to_le16(RPM_CMD_VERSION); 408b4f8e52bSBjorn Andersson msg.param1 = cpu_to_le16(1); 409b4f8e52bSBjorn Andersson msg.param2 = cpu_to_le32(GLINK_FEATURE_INTENTLESS); 410b4f8e52bSBjorn Andersson 411d7101febSBjorn Andersson return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); 412b4f8e52bSBjorn Andersson } 413b4f8e52bSBjorn Andersson 414d7101febSBjorn Andersson static void qcom_glink_send_version_ack(struct qcom_glink *glink) 415b4f8e52bSBjorn Andersson { 416b4f8e52bSBjorn Andersson struct glink_msg msg; 417b4f8e52bSBjorn Andersson 418b4f8e52bSBjorn Andersson msg.cmd = cpu_to_le16(RPM_CMD_VERSION_ACK); 419b4f8e52bSBjorn Andersson msg.param1 = cpu_to_le16(1); 420b4f8e52bSBjorn Andersson msg.param2 = cpu_to_le32(0); 421b4f8e52bSBjorn Andersson 422d7101febSBjorn Andersson qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); 423b4f8e52bSBjorn Andersson } 424b4f8e52bSBjorn Andersson 425d7101febSBjorn Andersson static void qcom_glink_send_open_ack(struct qcom_glink *glink, 426b4f8e52bSBjorn Andersson struct glink_channel *channel) 427b4f8e52bSBjorn Andersson { 428b4f8e52bSBjorn Andersson struct glink_msg msg; 429b4f8e52bSBjorn Andersson 430b4f8e52bSBjorn Andersson msg.cmd = cpu_to_le16(RPM_CMD_OPEN_ACK); 431b4f8e52bSBjorn Andersson msg.param1 = cpu_to_le16(channel->rcid); 432b4f8e52bSBjorn Andersson msg.param2 = cpu_to_le32(0); 433b4f8e52bSBjorn Andersson 434d7101febSBjorn Andersson qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); 435b4f8e52bSBjorn Andersson } 436b4f8e52bSBjorn Andersson 437b4f8e52bSBjorn Andersson /** 438d7101febSBjorn Andersson * qcom_glink_send_open_req() - send a RPM_CMD_OPEN request to the remote 439b4f8e52bSBjorn Andersson * @glink: 440b4f8e52bSBjorn Andersson * @channel: 441b4f8e52bSBjorn Andersson * 442b4f8e52bSBjorn Andersson * Allocates a local channel id and sends a RPM_CMD_OPEN message to the remote. 443b4f8e52bSBjorn Andersson * Will return with refcount held, regardless of outcome. 444b4f8e52bSBjorn Andersson * 445b4f8e52bSBjorn Andersson * Returns 0 on success, negative errno otherwise. 446b4f8e52bSBjorn Andersson */ 447d7101febSBjorn Andersson static int qcom_glink_send_open_req(struct qcom_glink *glink, 448b4f8e52bSBjorn Andersson struct glink_channel *channel) 449b4f8e52bSBjorn Andersson { 450b4f8e52bSBjorn Andersson struct { 451b4f8e52bSBjorn Andersson struct glink_msg msg; 452b4f8e52bSBjorn Andersson u8 name[GLINK_NAME_SIZE]; 453b4f8e52bSBjorn Andersson } __packed req; 454b4f8e52bSBjorn Andersson int name_len = strlen(channel->name) + 1; 455b4f8e52bSBjorn Andersson int req_len = ALIGN(sizeof(req.msg) + name_len, 8); 456b4f8e52bSBjorn Andersson int ret; 457b4f8e52bSBjorn Andersson 458b4f8e52bSBjorn Andersson kref_get(&channel->refcount); 459b4f8e52bSBjorn Andersson 460b4f8e52bSBjorn Andersson mutex_lock(&glink->idr_lock); 461b4f8e52bSBjorn Andersson ret = idr_alloc_cyclic(&glink->lcids, channel, 462b4f8e52bSBjorn Andersson RPM_GLINK_CID_MIN, RPM_GLINK_CID_MAX, GFP_KERNEL); 463b4f8e52bSBjorn Andersson mutex_unlock(&glink->idr_lock); 464b4f8e52bSBjorn Andersson if (ret < 0) 465b4f8e52bSBjorn Andersson return ret; 466b4f8e52bSBjorn Andersson 467b4f8e52bSBjorn Andersson channel->lcid = ret; 468b4f8e52bSBjorn Andersson 469b4f8e52bSBjorn Andersson req.msg.cmd = cpu_to_le16(RPM_CMD_OPEN); 470b4f8e52bSBjorn Andersson req.msg.param1 = cpu_to_le16(channel->lcid); 471b4f8e52bSBjorn Andersson req.msg.param2 = cpu_to_le32(name_len); 472b4f8e52bSBjorn Andersson strcpy(req.name, channel->name); 473b4f8e52bSBjorn Andersson 474d7101febSBjorn Andersson ret = qcom_glink_tx(glink, &req, req_len, NULL, 0, true); 475b4f8e52bSBjorn Andersson if (ret) 476b4f8e52bSBjorn Andersson goto remove_idr; 477b4f8e52bSBjorn Andersson 478b4f8e52bSBjorn Andersson return 0; 479b4f8e52bSBjorn Andersson 480b4f8e52bSBjorn Andersson remove_idr: 481b4f8e52bSBjorn Andersson mutex_lock(&glink->idr_lock); 482b4f8e52bSBjorn Andersson idr_remove(&glink->lcids, channel->lcid); 483b4f8e52bSBjorn Andersson channel->lcid = 0; 484b4f8e52bSBjorn Andersson mutex_unlock(&glink->idr_lock); 485b4f8e52bSBjorn Andersson 486b4f8e52bSBjorn Andersson return ret; 487b4f8e52bSBjorn Andersson } 488b4f8e52bSBjorn Andersson 489d7101febSBjorn Andersson static void qcom_glink_send_close_req(struct qcom_glink *glink, 490b4f8e52bSBjorn Andersson struct glink_channel *channel) 491b4f8e52bSBjorn Andersson { 492b4f8e52bSBjorn Andersson struct glink_msg req; 493b4f8e52bSBjorn Andersson 494b4f8e52bSBjorn Andersson req.cmd = cpu_to_le16(RPM_CMD_CLOSE); 495b4f8e52bSBjorn Andersson req.param1 = cpu_to_le16(channel->lcid); 496b4f8e52bSBjorn Andersson req.param2 = 0; 497b4f8e52bSBjorn Andersson 498d7101febSBjorn Andersson qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true); 499b4f8e52bSBjorn Andersson } 500b4f8e52bSBjorn Andersson 501d7101febSBjorn Andersson static void qcom_glink_send_close_ack(struct qcom_glink *glink, 502d7101febSBjorn Andersson unsigned int rcid) 503b4f8e52bSBjorn Andersson { 504b4f8e52bSBjorn Andersson struct glink_msg req; 505b4f8e52bSBjorn Andersson 506b4f8e52bSBjorn Andersson req.cmd = cpu_to_le16(RPM_CMD_CLOSE_ACK); 507b4f8e52bSBjorn Andersson req.param1 = cpu_to_le16(rcid); 508b4f8e52bSBjorn Andersson req.param2 = 0; 509b4f8e52bSBjorn Andersson 510d7101febSBjorn Andersson qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true); 511b4f8e52bSBjorn Andersson } 512b4f8e52bSBjorn Andersson 513d7101febSBjorn Andersson static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra) 514b4f8e52bSBjorn Andersson { 515b4f8e52bSBjorn Andersson struct glink_defer_cmd *dcmd; 516b4f8e52bSBjorn Andersson 517b4f8e52bSBjorn Andersson extra = ALIGN(extra, 8); 518b4f8e52bSBjorn Andersson 519d7101febSBjorn Andersson if (qcom_glink_rx_avail(glink) < sizeof(struct glink_msg) + extra) { 520b4f8e52bSBjorn Andersson dev_dbg(glink->dev, "Insufficient data in rx fifo"); 521b4f8e52bSBjorn Andersson return -ENXIO; 522b4f8e52bSBjorn Andersson } 523b4f8e52bSBjorn Andersson 524b4f8e52bSBjorn Andersson dcmd = kzalloc(sizeof(*dcmd) + extra, GFP_ATOMIC); 525b4f8e52bSBjorn Andersson if (!dcmd) 526b4f8e52bSBjorn Andersson return -ENOMEM; 527b4f8e52bSBjorn Andersson 528b4f8e52bSBjorn Andersson INIT_LIST_HEAD(&dcmd->node); 529b4f8e52bSBjorn Andersson 530d7101febSBjorn Andersson qcom_glink_rx_peak(glink, &dcmd->msg, sizeof(dcmd->msg) + extra); 531b4f8e52bSBjorn Andersson 532b4f8e52bSBjorn Andersson spin_lock(&glink->rx_lock); 533b4f8e52bSBjorn Andersson list_add_tail(&dcmd->node, &glink->rx_queue); 534b4f8e52bSBjorn Andersson spin_unlock(&glink->rx_lock); 535b4f8e52bSBjorn Andersson 536b4f8e52bSBjorn Andersson schedule_work(&glink->rx_work); 537d7101febSBjorn Andersson qcom_glink_rx_advance(glink, sizeof(dcmd->msg) + extra); 538b4f8e52bSBjorn Andersson 539b4f8e52bSBjorn Andersson return 0; 540b4f8e52bSBjorn Andersson } 541b4f8e52bSBjorn Andersson 542d7101febSBjorn Andersson static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) 543b4f8e52bSBjorn Andersson { 544b4f8e52bSBjorn Andersson struct glink_channel *channel; 545b4f8e52bSBjorn Andersson struct { 546b4f8e52bSBjorn Andersson struct glink_msg msg; 547b4f8e52bSBjorn Andersson __le32 chunk_size; 548b4f8e52bSBjorn Andersson __le32 left_size; 549b4f8e52bSBjorn Andersson } __packed hdr; 550b4f8e52bSBjorn Andersson unsigned int chunk_size; 551b4f8e52bSBjorn Andersson unsigned int left_size; 552b4f8e52bSBjorn Andersson unsigned int rcid; 553b4f8e52bSBjorn Andersson 554b4f8e52bSBjorn Andersson if (avail < sizeof(hdr)) { 555b4f8e52bSBjorn Andersson dev_dbg(glink->dev, "Not enough data in fifo\n"); 556b4f8e52bSBjorn Andersson return -EAGAIN; 557b4f8e52bSBjorn Andersson } 558b4f8e52bSBjorn Andersson 559d7101febSBjorn Andersson qcom_glink_rx_peak(glink, &hdr, sizeof(hdr)); 560b4f8e52bSBjorn Andersson chunk_size = le32_to_cpu(hdr.chunk_size); 561b4f8e52bSBjorn Andersson left_size = le32_to_cpu(hdr.left_size); 562b4f8e52bSBjorn Andersson 563b4f8e52bSBjorn Andersson if (avail < sizeof(hdr) + chunk_size) { 564b4f8e52bSBjorn Andersson dev_dbg(glink->dev, "Payload not yet in fifo\n"); 565b4f8e52bSBjorn Andersson return -EAGAIN; 566b4f8e52bSBjorn Andersson } 567b4f8e52bSBjorn Andersson 568b4f8e52bSBjorn Andersson if (WARN(chunk_size % 4, "Incoming data must be word aligned\n")) 569b4f8e52bSBjorn Andersson return -EINVAL; 570b4f8e52bSBjorn Andersson 571b4f8e52bSBjorn Andersson rcid = le16_to_cpu(hdr.msg.param1); 572b4f8e52bSBjorn Andersson channel = idr_find(&glink->rcids, rcid); 573b4f8e52bSBjorn Andersson if (!channel) { 574b4f8e52bSBjorn Andersson dev_dbg(glink->dev, "Data on non-existing channel\n"); 575b4f8e52bSBjorn Andersson 576b4f8e52bSBjorn Andersson /* Drop the message */ 577d7101febSBjorn Andersson qcom_glink_rx_advance(glink, 578d7101febSBjorn Andersson ALIGN(sizeof(hdr) + chunk_size, 8)); 579b4f8e52bSBjorn Andersson return 0; 580b4f8e52bSBjorn Andersson } 581b4f8e52bSBjorn Andersson 582b4f8e52bSBjorn Andersson /* Might have an ongoing, fragmented, message to append */ 583b4f8e52bSBjorn Andersson if (!channel->buf) { 584b4f8e52bSBjorn Andersson channel->buf = kmalloc(chunk_size + left_size, GFP_ATOMIC); 585b4f8e52bSBjorn Andersson if (!channel->buf) 586b4f8e52bSBjorn Andersson return -ENOMEM; 587b4f8e52bSBjorn Andersson 588b4f8e52bSBjorn Andersson channel->buf_size = chunk_size + left_size; 589b4f8e52bSBjorn Andersson channel->buf_offset = 0; 590b4f8e52bSBjorn Andersson } 591b4f8e52bSBjorn Andersson 592d7101febSBjorn Andersson qcom_glink_rx_advance(glink, sizeof(hdr)); 593b4f8e52bSBjorn Andersson 594b4f8e52bSBjorn Andersson if (channel->buf_size - channel->buf_offset < chunk_size) { 595b4f8e52bSBjorn Andersson dev_err(glink->dev, "Insufficient space in input buffer\n"); 596b4f8e52bSBjorn Andersson 597b4f8e52bSBjorn Andersson /* The packet header lied, drop payload */ 598d7101febSBjorn Andersson qcom_glink_rx_advance(glink, chunk_size); 599b4f8e52bSBjorn Andersson return -ENOMEM; 600b4f8e52bSBjorn Andersson } 601b4f8e52bSBjorn Andersson 602d7101febSBjorn Andersson qcom_glink_rx_peak(glink, channel->buf + channel->buf_offset, 603d7101febSBjorn Andersson chunk_size); 604b4f8e52bSBjorn Andersson channel->buf_offset += chunk_size; 605b4f8e52bSBjorn Andersson 606b4f8e52bSBjorn Andersson /* Handle message when no fragments remain to be received */ 607b4f8e52bSBjorn Andersson if (!left_size) { 608b4f8e52bSBjorn Andersson spin_lock(&channel->recv_lock); 609b4f8e52bSBjorn Andersson if (channel->ept.cb) { 610b4f8e52bSBjorn Andersson channel->ept.cb(channel->ept.rpdev, 611b4f8e52bSBjorn Andersson channel->buf, 612b4f8e52bSBjorn Andersson channel->buf_offset, 613b4f8e52bSBjorn Andersson channel->ept.priv, 614b4f8e52bSBjorn Andersson RPMSG_ADDR_ANY); 615b4f8e52bSBjorn Andersson } 616b4f8e52bSBjorn Andersson spin_unlock(&channel->recv_lock); 617b4f8e52bSBjorn Andersson 618b4f8e52bSBjorn Andersson kfree(channel->buf); 619b4f8e52bSBjorn Andersson channel->buf = NULL; 620b4f8e52bSBjorn Andersson channel->buf_size = 0; 621b4f8e52bSBjorn Andersson } 622b4f8e52bSBjorn Andersson 623b4f8e52bSBjorn Andersson /* Each message starts at 8 byte aligned address */ 624d7101febSBjorn Andersson qcom_glink_rx_advance(glink, ALIGN(chunk_size, 8)); 625b4f8e52bSBjorn Andersson 626b4f8e52bSBjorn Andersson return 0; 627b4f8e52bSBjorn Andersson } 628b4f8e52bSBjorn Andersson 629d7101febSBjorn Andersson static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) 630b4f8e52bSBjorn Andersson { 631b4f8e52bSBjorn Andersson struct glink_channel *channel; 632b4f8e52bSBjorn Andersson 633b4f8e52bSBjorn Andersson channel = idr_find(&glink->lcids, lcid); 634b4f8e52bSBjorn Andersson if (!channel) { 635b4f8e52bSBjorn Andersson dev_err(glink->dev, "Invalid open ack packet\n"); 636b4f8e52bSBjorn Andersson return -EINVAL; 637b4f8e52bSBjorn Andersson } 638b4f8e52bSBjorn Andersson 639b4f8e52bSBjorn Andersson complete(&channel->open_ack); 640b4f8e52bSBjorn Andersson 641b4f8e52bSBjorn Andersson return 0; 642b4f8e52bSBjorn Andersson } 643b4f8e52bSBjorn Andersson 644d7101febSBjorn Andersson static irqreturn_t qcom_glink_intr(int irq, void *data) 645b4f8e52bSBjorn Andersson { 646d7101febSBjorn Andersson struct qcom_glink *glink = data; 647b4f8e52bSBjorn Andersson struct glink_msg msg; 648b4f8e52bSBjorn Andersson unsigned int param1; 649b4f8e52bSBjorn Andersson unsigned int param2; 650b4f8e52bSBjorn Andersson unsigned int avail; 651b4f8e52bSBjorn Andersson unsigned int cmd; 652b4f8e52bSBjorn Andersson int ret; 653b4f8e52bSBjorn Andersson 654b4f8e52bSBjorn Andersson for (;;) { 655d7101febSBjorn Andersson avail = qcom_glink_rx_avail(glink); 656b4f8e52bSBjorn Andersson if (avail < sizeof(msg)) 657b4f8e52bSBjorn Andersson break; 658b4f8e52bSBjorn Andersson 659d7101febSBjorn Andersson qcom_glink_rx_peak(glink, &msg, sizeof(msg)); 660b4f8e52bSBjorn Andersson 661b4f8e52bSBjorn Andersson cmd = le16_to_cpu(msg.cmd); 662b4f8e52bSBjorn Andersson param1 = le16_to_cpu(msg.param1); 663b4f8e52bSBjorn Andersson param2 = le32_to_cpu(msg.param2); 664b4f8e52bSBjorn Andersson 665b4f8e52bSBjorn Andersson switch (cmd) { 666b4f8e52bSBjorn Andersson case RPM_CMD_VERSION: 667b4f8e52bSBjorn Andersson case RPM_CMD_VERSION_ACK: 668b4f8e52bSBjorn Andersson case RPM_CMD_CLOSE: 669b4f8e52bSBjorn Andersson case RPM_CMD_CLOSE_ACK: 670d7101febSBjorn Andersson ret = qcom_glink_rx_defer(glink, 0); 671b4f8e52bSBjorn Andersson break; 672b4f8e52bSBjorn Andersson case RPM_CMD_OPEN_ACK: 673d7101febSBjorn Andersson ret = qcom_glink_rx_open_ack(glink, param1); 674d7101febSBjorn Andersson qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); 675b4f8e52bSBjorn Andersson break; 676b4f8e52bSBjorn Andersson case RPM_CMD_OPEN: 677d7101febSBjorn Andersson ret = qcom_glink_rx_defer(glink, param2); 678b4f8e52bSBjorn Andersson break; 679b4f8e52bSBjorn Andersson case RPM_CMD_TX_DATA: 680b4f8e52bSBjorn Andersson case RPM_CMD_TX_DATA_CONT: 681d7101febSBjorn Andersson ret = qcom_glink_rx_data(glink, avail); 682b4f8e52bSBjorn Andersson break; 683b4f8e52bSBjorn Andersson case RPM_CMD_READ_NOTIF: 684d7101febSBjorn Andersson qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); 685b4f8e52bSBjorn Andersson 686b4f8e52bSBjorn Andersson mbox_send_message(glink->mbox_chan, NULL); 687b4f8e52bSBjorn Andersson mbox_client_txdone(glink->mbox_chan, 0); 688b4f8e52bSBjorn Andersson 689b4f8e52bSBjorn Andersson ret = 0; 690b4f8e52bSBjorn Andersson break; 691b4f8e52bSBjorn Andersson default: 692b4f8e52bSBjorn Andersson dev_err(glink->dev, "unhandled rx cmd: %d\n", cmd); 693b4f8e52bSBjorn Andersson ret = -EINVAL; 694b4f8e52bSBjorn Andersson break; 695b4f8e52bSBjorn Andersson } 696b4f8e52bSBjorn Andersson 697b4f8e52bSBjorn Andersson if (ret) 698b4f8e52bSBjorn Andersson break; 699b4f8e52bSBjorn Andersson } 700b4f8e52bSBjorn Andersson 701b4f8e52bSBjorn Andersson return IRQ_HANDLED; 702b4f8e52bSBjorn Andersson } 703b4f8e52bSBjorn Andersson 704b4f8e52bSBjorn Andersson /* Locally initiated rpmsg_create_ept */ 705d7101febSBjorn Andersson static struct glink_channel *qcom_glink_create_local(struct qcom_glink *glink, 706b4f8e52bSBjorn Andersson const char *name) 707b4f8e52bSBjorn Andersson { 708b4f8e52bSBjorn Andersson struct glink_channel *channel; 709b4f8e52bSBjorn Andersson int ret; 710b4f8e52bSBjorn Andersson 711d7101febSBjorn Andersson channel = qcom_glink_alloc_channel(glink, name); 712b4f8e52bSBjorn Andersson if (IS_ERR(channel)) 713b4f8e52bSBjorn Andersson return ERR_CAST(channel); 714b4f8e52bSBjorn Andersson 715d7101febSBjorn Andersson ret = qcom_glink_send_open_req(glink, channel); 716b4f8e52bSBjorn Andersson if (ret) 717b4f8e52bSBjorn Andersson goto release_channel; 718b4f8e52bSBjorn Andersson 719b4f8e52bSBjorn Andersson ret = wait_for_completion_timeout(&channel->open_ack, 5 * HZ); 720b4f8e52bSBjorn Andersson if (!ret) 721b4f8e52bSBjorn Andersson goto err_timeout; 722b4f8e52bSBjorn Andersson 723b4f8e52bSBjorn Andersson ret = wait_for_completion_timeout(&channel->open_req, 5 * HZ); 724b4f8e52bSBjorn Andersson if (!ret) 725b4f8e52bSBjorn Andersson goto err_timeout; 726b4f8e52bSBjorn Andersson 727d7101febSBjorn Andersson qcom_glink_send_open_ack(glink, channel); 728b4f8e52bSBjorn Andersson 729b4f8e52bSBjorn Andersson return channel; 730b4f8e52bSBjorn Andersson 731b4f8e52bSBjorn Andersson err_timeout: 732d7101febSBjorn Andersson /* qcom_glink_send_open_req() did register the channel in lcids*/ 733b4f8e52bSBjorn Andersson mutex_lock(&glink->idr_lock); 734b4f8e52bSBjorn Andersson idr_remove(&glink->lcids, channel->lcid); 735b4f8e52bSBjorn Andersson mutex_unlock(&glink->idr_lock); 736b4f8e52bSBjorn Andersson 737b4f8e52bSBjorn Andersson release_channel: 738d7101febSBjorn Andersson /* Release qcom_glink_send_open_req() reference */ 739d7101febSBjorn Andersson kref_put(&channel->refcount, qcom_glink_channel_release); 740d7101febSBjorn Andersson /* Release qcom_glink_alloc_channel() reference */ 741d7101febSBjorn Andersson kref_put(&channel->refcount, qcom_glink_channel_release); 742b4f8e52bSBjorn Andersson 743b4f8e52bSBjorn Andersson return ERR_PTR(-ETIMEDOUT); 744b4f8e52bSBjorn Andersson } 745b4f8e52bSBjorn Andersson 746b4f8e52bSBjorn Andersson /* Remote initiated rpmsg_create_ept */ 747d7101febSBjorn Andersson static int qcom_glink_create_remote(struct qcom_glink *glink, 748b4f8e52bSBjorn Andersson struct glink_channel *channel) 749b4f8e52bSBjorn Andersson { 750b4f8e52bSBjorn Andersson int ret; 751b4f8e52bSBjorn Andersson 752d7101febSBjorn Andersson qcom_glink_send_open_ack(glink, channel); 753b4f8e52bSBjorn Andersson 754d7101febSBjorn Andersson ret = qcom_glink_send_open_req(glink, channel); 755b4f8e52bSBjorn Andersson if (ret) 756b4f8e52bSBjorn Andersson goto close_link; 757b4f8e52bSBjorn Andersson 758b4f8e52bSBjorn Andersson ret = wait_for_completion_timeout(&channel->open_ack, 5 * HZ); 759b4f8e52bSBjorn Andersson if (!ret) { 760b4f8e52bSBjorn Andersson ret = -ETIMEDOUT; 761b4f8e52bSBjorn Andersson goto close_link; 762b4f8e52bSBjorn Andersson } 763b4f8e52bSBjorn Andersson 764b4f8e52bSBjorn Andersson return 0; 765b4f8e52bSBjorn Andersson 766b4f8e52bSBjorn Andersson close_link: 767b4f8e52bSBjorn Andersson /* 768b4f8e52bSBjorn Andersson * Send a close request to "undo" our open-ack. The close-ack will 769b4f8e52bSBjorn Andersson * release the last reference. 770b4f8e52bSBjorn Andersson */ 771d7101febSBjorn Andersson qcom_glink_send_close_req(glink, channel); 772b4f8e52bSBjorn Andersson 773d7101febSBjorn Andersson /* Release qcom_glink_send_open_req() reference */ 774d7101febSBjorn Andersson kref_put(&channel->refcount, qcom_glink_channel_release); 775b4f8e52bSBjorn Andersson 776b4f8e52bSBjorn Andersson return ret; 777b4f8e52bSBjorn Andersson } 778b4f8e52bSBjorn Andersson 779d7101febSBjorn Andersson static struct rpmsg_endpoint *qcom_glink_create_ept(struct rpmsg_device *rpdev, 780d7101febSBjorn Andersson rpmsg_rx_cb_t cb, 781d7101febSBjorn Andersson void *priv, 782d7101febSBjorn Andersson struct rpmsg_channel_info 783d7101febSBjorn Andersson chinfo) 784b4f8e52bSBjorn Andersson { 785b4f8e52bSBjorn Andersson struct glink_channel *parent = to_glink_channel(rpdev->ept); 786b4f8e52bSBjorn Andersson struct glink_channel *channel; 787d7101febSBjorn Andersson struct qcom_glink *glink = parent->glink; 788b4f8e52bSBjorn Andersson struct rpmsg_endpoint *ept; 789b4f8e52bSBjorn Andersson const char *name = chinfo.name; 790b4f8e52bSBjorn Andersson int cid; 791b4f8e52bSBjorn Andersson int ret; 792b4f8e52bSBjorn Andersson 793b4f8e52bSBjorn Andersson idr_for_each_entry(&glink->rcids, channel, cid) { 794b4f8e52bSBjorn Andersson if (!strcmp(channel->name, name)) 795b4f8e52bSBjorn Andersson break; 796b4f8e52bSBjorn Andersson } 797b4f8e52bSBjorn Andersson 798b4f8e52bSBjorn Andersson if (!channel) { 799d7101febSBjorn Andersson channel = qcom_glink_create_local(glink, name); 800b4f8e52bSBjorn Andersson if (IS_ERR(channel)) 801b4f8e52bSBjorn Andersson return NULL; 802b4f8e52bSBjorn Andersson } else { 803d7101febSBjorn Andersson ret = qcom_glink_create_remote(glink, channel); 804b4f8e52bSBjorn Andersson if (ret) 805b4f8e52bSBjorn Andersson return NULL; 806b4f8e52bSBjorn Andersson } 807b4f8e52bSBjorn Andersson 808b4f8e52bSBjorn Andersson ept = &channel->ept; 809b4f8e52bSBjorn Andersson ept->rpdev = rpdev; 810b4f8e52bSBjorn Andersson ept->cb = cb; 811b4f8e52bSBjorn Andersson ept->priv = priv; 812b4f8e52bSBjorn Andersson ept->ops = &glink_endpoint_ops; 813b4f8e52bSBjorn Andersson 814b4f8e52bSBjorn Andersson return ept; 815b4f8e52bSBjorn Andersson } 816b4f8e52bSBjorn Andersson 817d7101febSBjorn Andersson static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept) 818b4f8e52bSBjorn Andersson { 819b4f8e52bSBjorn Andersson struct glink_channel *channel = to_glink_channel(ept); 820d7101febSBjorn Andersson struct qcom_glink *glink = channel->glink; 821b4f8e52bSBjorn Andersson unsigned long flags; 822b4f8e52bSBjorn Andersson 823b4f8e52bSBjorn Andersson spin_lock_irqsave(&channel->recv_lock, flags); 824b4f8e52bSBjorn Andersson channel->ept.cb = NULL; 825b4f8e52bSBjorn Andersson spin_unlock_irqrestore(&channel->recv_lock, flags); 826b4f8e52bSBjorn Andersson 827b4f8e52bSBjorn Andersson /* Decouple the potential rpdev from the channel */ 828b4f8e52bSBjorn Andersson channel->rpdev = NULL; 829b4f8e52bSBjorn Andersson 830d7101febSBjorn Andersson qcom_glink_send_close_req(glink, channel); 831b4f8e52bSBjorn Andersson } 832b4f8e52bSBjorn Andersson 833d7101febSBjorn Andersson static int __qcom_glink_send(struct glink_channel *channel, 834b4f8e52bSBjorn Andersson void *data, int len, bool wait) 835b4f8e52bSBjorn Andersson { 836d7101febSBjorn Andersson struct qcom_glink *glink = channel->glink; 837b4f8e52bSBjorn Andersson struct { 838b4f8e52bSBjorn Andersson struct glink_msg msg; 839b4f8e52bSBjorn Andersson __le32 chunk_size; 840b4f8e52bSBjorn Andersson __le32 left_size; 841b4f8e52bSBjorn Andersson } __packed req; 842b4f8e52bSBjorn Andersson 843b4f8e52bSBjorn Andersson if (WARN(len % 8, "RPM GLINK expects 8 byte aligned messages\n")) 844b4f8e52bSBjorn Andersson return -EINVAL; 845b4f8e52bSBjorn Andersson 846b4f8e52bSBjorn Andersson req.msg.cmd = cpu_to_le16(RPM_CMD_TX_DATA); 847b4f8e52bSBjorn Andersson req.msg.param1 = cpu_to_le16(channel->lcid); 848b4f8e52bSBjorn Andersson req.msg.param2 = cpu_to_le32(channel->rcid); 849b4f8e52bSBjorn Andersson req.chunk_size = cpu_to_le32(len); 850b4f8e52bSBjorn Andersson req.left_size = cpu_to_le32(0); 851b4f8e52bSBjorn Andersson 852d7101febSBjorn Andersson return qcom_glink_tx(glink, &req, sizeof(req), data, len, wait); 853b4f8e52bSBjorn Andersson } 854b4f8e52bSBjorn Andersson 855d7101febSBjorn Andersson static int qcom_glink_send(struct rpmsg_endpoint *ept, void *data, int len) 856b4f8e52bSBjorn Andersson { 857b4f8e52bSBjorn Andersson struct glink_channel *channel = to_glink_channel(ept); 858b4f8e52bSBjorn Andersson 859d7101febSBjorn Andersson return __qcom_glink_send(channel, data, len, true); 860b4f8e52bSBjorn Andersson } 861b4f8e52bSBjorn Andersson 862d7101febSBjorn Andersson static int qcom_glink_trysend(struct rpmsg_endpoint *ept, void *data, int len) 863b4f8e52bSBjorn Andersson { 864b4f8e52bSBjorn Andersson struct glink_channel *channel = to_glink_channel(ept); 865b4f8e52bSBjorn Andersson 866d7101febSBjorn Andersson return __qcom_glink_send(channel, data, len, false); 867b4f8e52bSBjorn Andersson } 868b4f8e52bSBjorn Andersson 869b4f8e52bSBjorn Andersson /* 870b4f8e52bSBjorn Andersson * Finds the device_node for the glink child interested in this channel. 871b4f8e52bSBjorn Andersson */ 872d7101febSBjorn Andersson static struct device_node *qcom_glink_match_channel(struct device_node *node, 873b4f8e52bSBjorn Andersson const char *channel) 874b4f8e52bSBjorn Andersson { 875b4f8e52bSBjorn Andersson struct device_node *child; 876b4f8e52bSBjorn Andersson const char *name; 877b4f8e52bSBjorn Andersson const char *key; 878b4f8e52bSBjorn Andersson int ret; 879b4f8e52bSBjorn Andersson 880b4f8e52bSBjorn Andersson for_each_available_child_of_node(node, child) { 881b4f8e52bSBjorn Andersson key = "qcom,glink-channels"; 882b4f8e52bSBjorn Andersson ret = of_property_read_string(child, key, &name); 883b4f8e52bSBjorn Andersson if (ret) 884b4f8e52bSBjorn Andersson continue; 885b4f8e52bSBjorn Andersson 886b4f8e52bSBjorn Andersson if (strcmp(name, channel) == 0) 887b4f8e52bSBjorn Andersson return child; 888b4f8e52bSBjorn Andersson } 889b4f8e52bSBjorn Andersson 890b4f8e52bSBjorn Andersson return NULL; 891b4f8e52bSBjorn Andersson } 892b4f8e52bSBjorn Andersson 893b4f8e52bSBjorn Andersson static const struct rpmsg_device_ops glink_device_ops = { 894d7101febSBjorn Andersson .create_ept = qcom_glink_create_ept, 895b4f8e52bSBjorn Andersson }; 896b4f8e52bSBjorn Andersson 897b4f8e52bSBjorn Andersson static const struct rpmsg_endpoint_ops glink_endpoint_ops = { 898d7101febSBjorn Andersson .destroy_ept = qcom_glink_destroy_ept, 899d7101febSBjorn Andersson .send = qcom_glink_send, 900d7101febSBjorn Andersson .trysend = qcom_glink_trysend, 901b4f8e52bSBjorn Andersson }; 902b4f8e52bSBjorn Andersson 903d7101febSBjorn Andersson static void qcom_glink_rpdev_release(struct device *dev) 904b4f8e52bSBjorn Andersson { 905b4f8e52bSBjorn Andersson struct rpmsg_device *rpdev = to_rpmsg_device(dev); 906b4f8e52bSBjorn Andersson struct glink_channel *channel = to_glink_channel(rpdev->ept); 907b4f8e52bSBjorn Andersson 908b4f8e52bSBjorn Andersson channel->rpdev = NULL; 909b4f8e52bSBjorn Andersson kfree(rpdev); 910b4f8e52bSBjorn Andersson } 911b4f8e52bSBjorn Andersson 912d7101febSBjorn Andersson static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid, 913b4f8e52bSBjorn Andersson char *name) 914b4f8e52bSBjorn Andersson { 915b4f8e52bSBjorn Andersson struct glink_channel *channel; 916b4f8e52bSBjorn Andersson struct rpmsg_device *rpdev; 917b4f8e52bSBjorn Andersson bool create_device = false; 918b4f8e52bSBjorn Andersson int lcid; 919b4f8e52bSBjorn Andersson int ret; 920d7101febSBjorn Andersson struct device_node *node; 921b4f8e52bSBjorn Andersson 922b4f8e52bSBjorn Andersson idr_for_each_entry(&glink->lcids, channel, lcid) { 923b4f8e52bSBjorn Andersson if (!strcmp(channel->name, name)) 924b4f8e52bSBjorn Andersson break; 925b4f8e52bSBjorn Andersson } 926b4f8e52bSBjorn Andersson 927b4f8e52bSBjorn Andersson if (!channel) { 928d7101febSBjorn Andersson channel = qcom_glink_alloc_channel(glink, name); 929b4f8e52bSBjorn Andersson if (IS_ERR(channel)) 930b4f8e52bSBjorn Andersson return PTR_ERR(channel); 931b4f8e52bSBjorn Andersson 932b4f8e52bSBjorn Andersson /* The opening dance was initiated by the remote */ 933b4f8e52bSBjorn Andersson create_device = true; 934b4f8e52bSBjorn Andersson } 935b4f8e52bSBjorn Andersson 936b4f8e52bSBjorn Andersson mutex_lock(&glink->idr_lock); 937b4f8e52bSBjorn Andersson ret = idr_alloc(&glink->rcids, channel, rcid, rcid + 1, GFP_KERNEL); 938b4f8e52bSBjorn Andersson if (ret < 0) { 939b4f8e52bSBjorn Andersson dev_err(glink->dev, "Unable to insert channel into rcid list\n"); 940b4f8e52bSBjorn Andersson mutex_unlock(&glink->idr_lock); 941b4f8e52bSBjorn Andersson goto free_channel; 942b4f8e52bSBjorn Andersson } 943b4f8e52bSBjorn Andersson channel->rcid = ret; 944b4f8e52bSBjorn Andersson mutex_unlock(&glink->idr_lock); 945b4f8e52bSBjorn Andersson 946b4f8e52bSBjorn Andersson complete(&channel->open_req); 947b4f8e52bSBjorn Andersson 948b4f8e52bSBjorn Andersson if (create_device) { 949b4f8e52bSBjorn Andersson rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL); 950b4f8e52bSBjorn Andersson if (!rpdev) { 951b4f8e52bSBjorn Andersson ret = -ENOMEM; 952b4f8e52bSBjorn Andersson goto rcid_remove; 953b4f8e52bSBjorn Andersson } 954b4f8e52bSBjorn Andersson 955b4f8e52bSBjorn Andersson rpdev->ept = &channel->ept; 956b4f8e52bSBjorn Andersson strncpy(rpdev->id.name, name, RPMSG_NAME_SIZE); 957b4f8e52bSBjorn Andersson rpdev->src = RPMSG_ADDR_ANY; 958b4f8e52bSBjorn Andersson rpdev->dst = RPMSG_ADDR_ANY; 959b4f8e52bSBjorn Andersson rpdev->ops = &glink_device_ops; 960b4f8e52bSBjorn Andersson 961d7101febSBjorn Andersson node = qcom_glink_match_channel(glink->dev->of_node, name); 962d7101febSBjorn Andersson rpdev->dev.of_node = node; 963b4f8e52bSBjorn Andersson rpdev->dev.parent = glink->dev; 964d7101febSBjorn Andersson rpdev->dev.release = qcom_glink_rpdev_release; 965b4f8e52bSBjorn Andersson 966b4f8e52bSBjorn Andersson ret = rpmsg_register_device(rpdev); 967b4f8e52bSBjorn Andersson if (ret) 968b4f8e52bSBjorn Andersson goto free_rpdev; 969b4f8e52bSBjorn Andersson 970b4f8e52bSBjorn Andersson channel->rpdev = rpdev; 971b4f8e52bSBjorn Andersson } 972b4f8e52bSBjorn Andersson 973b4f8e52bSBjorn Andersson return 0; 974b4f8e52bSBjorn Andersson 975b4f8e52bSBjorn Andersson free_rpdev: 976b4f8e52bSBjorn Andersson kfree(rpdev); 977b4f8e52bSBjorn Andersson rcid_remove: 978b4f8e52bSBjorn Andersson mutex_lock(&glink->idr_lock); 979b4f8e52bSBjorn Andersson idr_remove(&glink->rcids, channel->rcid); 980b4f8e52bSBjorn Andersson channel->rcid = 0; 981b4f8e52bSBjorn Andersson mutex_unlock(&glink->idr_lock); 982b4f8e52bSBjorn Andersson free_channel: 983b4f8e52bSBjorn Andersson /* Release the reference, iff we took it */ 984b4f8e52bSBjorn Andersson if (create_device) 985d7101febSBjorn Andersson kref_put(&channel->refcount, qcom_glink_channel_release); 986b4f8e52bSBjorn Andersson 987b4f8e52bSBjorn Andersson return ret; 988b4f8e52bSBjorn Andersson } 989b4f8e52bSBjorn Andersson 990d7101febSBjorn Andersson static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid) 991b4f8e52bSBjorn Andersson { 992b4f8e52bSBjorn Andersson struct rpmsg_channel_info chinfo; 993b4f8e52bSBjorn Andersson struct glink_channel *channel; 994b4f8e52bSBjorn Andersson 995b4f8e52bSBjorn Andersson channel = idr_find(&glink->rcids, rcid); 996b4f8e52bSBjorn Andersson if (WARN(!channel, "close request on unknown channel\n")) 997b4f8e52bSBjorn Andersson return; 998b4f8e52bSBjorn Andersson 999b4f8e52bSBjorn Andersson if (channel->rpdev) { 1000b4f8e52bSBjorn Andersson strncpy(chinfo.name, channel->name, sizeof(chinfo.name)); 1001b4f8e52bSBjorn Andersson chinfo.src = RPMSG_ADDR_ANY; 1002b4f8e52bSBjorn Andersson chinfo.dst = RPMSG_ADDR_ANY; 1003b4f8e52bSBjorn Andersson 1004b4f8e52bSBjorn Andersson rpmsg_unregister_device(glink->dev, &chinfo); 1005b4f8e52bSBjorn Andersson } 1006b4f8e52bSBjorn Andersson 1007d7101febSBjorn Andersson qcom_glink_send_close_ack(glink, channel->rcid); 1008b4f8e52bSBjorn Andersson 1009b4f8e52bSBjorn Andersson mutex_lock(&glink->idr_lock); 1010b4f8e52bSBjorn Andersson idr_remove(&glink->rcids, channel->rcid); 1011b4f8e52bSBjorn Andersson channel->rcid = 0; 1012b4f8e52bSBjorn Andersson mutex_unlock(&glink->idr_lock); 1013b4f8e52bSBjorn Andersson 1014d7101febSBjorn Andersson kref_put(&channel->refcount, qcom_glink_channel_release); 1015b4f8e52bSBjorn Andersson } 1016b4f8e52bSBjorn Andersson 1017d7101febSBjorn Andersson static void qcom_glink_rx_close_ack(struct qcom_glink *glink, unsigned int lcid) 1018b4f8e52bSBjorn Andersson { 1019b4f8e52bSBjorn Andersson struct glink_channel *channel; 1020b4f8e52bSBjorn Andersson 1021b4f8e52bSBjorn Andersson channel = idr_find(&glink->lcids, lcid); 1022b4f8e52bSBjorn Andersson if (WARN(!channel, "close ack on unknown channel\n")) 1023b4f8e52bSBjorn Andersson return; 1024b4f8e52bSBjorn Andersson 1025b4f8e52bSBjorn Andersson mutex_lock(&glink->idr_lock); 1026b4f8e52bSBjorn Andersson idr_remove(&glink->lcids, channel->lcid); 1027b4f8e52bSBjorn Andersson channel->lcid = 0; 1028b4f8e52bSBjorn Andersson mutex_unlock(&glink->idr_lock); 1029b4f8e52bSBjorn Andersson 1030d7101febSBjorn Andersson kref_put(&channel->refcount, qcom_glink_channel_release); 1031b4f8e52bSBjorn Andersson } 1032b4f8e52bSBjorn Andersson 1033d7101febSBjorn Andersson static void qcom_glink_work(struct work_struct *work) 1034b4f8e52bSBjorn Andersson { 1035d7101febSBjorn Andersson struct qcom_glink *glink = container_of(work, struct qcom_glink, 1036d7101febSBjorn Andersson rx_work); 1037b4f8e52bSBjorn Andersson struct glink_defer_cmd *dcmd; 1038b4f8e52bSBjorn Andersson struct glink_msg *msg; 1039b4f8e52bSBjorn Andersson unsigned long flags; 1040b4f8e52bSBjorn Andersson unsigned int param1; 1041b4f8e52bSBjorn Andersson unsigned int param2; 1042b4f8e52bSBjorn Andersson unsigned int cmd; 1043b4f8e52bSBjorn Andersson 1044b4f8e52bSBjorn Andersson for (;;) { 1045b4f8e52bSBjorn Andersson spin_lock_irqsave(&glink->rx_lock, flags); 1046b4f8e52bSBjorn Andersson if (list_empty(&glink->rx_queue)) { 1047b4f8e52bSBjorn Andersson spin_unlock_irqrestore(&glink->rx_lock, flags); 1048b4f8e52bSBjorn Andersson break; 1049b4f8e52bSBjorn Andersson } 1050b4f8e52bSBjorn Andersson dcmd = list_first_entry(&glink->rx_queue, struct glink_defer_cmd, node); 1051b4f8e52bSBjorn Andersson list_del(&dcmd->node); 1052b4f8e52bSBjorn Andersson spin_unlock_irqrestore(&glink->rx_lock, flags); 1053b4f8e52bSBjorn Andersson 1054b4f8e52bSBjorn Andersson msg = &dcmd->msg; 1055b4f8e52bSBjorn Andersson cmd = le16_to_cpu(msg->cmd); 1056b4f8e52bSBjorn Andersson param1 = le16_to_cpu(msg->param1); 1057b4f8e52bSBjorn Andersson param2 = le32_to_cpu(msg->param2); 1058b4f8e52bSBjorn Andersson 1059b4f8e52bSBjorn Andersson switch (cmd) { 1060b4f8e52bSBjorn Andersson case RPM_CMD_VERSION: 1061d7101febSBjorn Andersson qcom_glink_send_version_ack(glink); 1062b4f8e52bSBjorn Andersson break; 1063b4f8e52bSBjorn Andersson case RPM_CMD_VERSION_ACK: 1064b4f8e52bSBjorn Andersson break; 1065b4f8e52bSBjorn Andersson case RPM_CMD_OPEN: 1066d7101febSBjorn Andersson qcom_glink_rx_open(glink, param1, msg->data); 1067b4f8e52bSBjorn Andersson break; 1068b4f8e52bSBjorn Andersson case RPM_CMD_CLOSE: 1069d7101febSBjorn Andersson qcom_glink_rx_close(glink, param1); 1070b4f8e52bSBjorn Andersson break; 1071b4f8e52bSBjorn Andersson case RPM_CMD_CLOSE_ACK: 1072d7101febSBjorn Andersson qcom_glink_rx_close_ack(glink, param1); 1073b4f8e52bSBjorn Andersson break; 1074b4f8e52bSBjorn Andersson default: 1075b4f8e52bSBjorn Andersson WARN(1, "Unknown defer object %d\n", cmd); 1076b4f8e52bSBjorn Andersson break; 1077b4f8e52bSBjorn Andersson } 1078b4f8e52bSBjorn Andersson 1079b4f8e52bSBjorn Andersson kfree(dcmd); 1080b4f8e52bSBjorn Andersson } 1081b4f8e52bSBjorn Andersson } 1082b4f8e52bSBjorn Andersson 1083b4f8e52bSBjorn Andersson static int glink_rpm_parse_toc(struct device *dev, 1084b4f8e52bSBjorn Andersson void __iomem *msg_ram, 1085b4f8e52bSBjorn Andersson size_t msg_ram_size, 1086b4f8e52bSBjorn Andersson struct glink_rpm_pipe *rx, 1087b4f8e52bSBjorn Andersson struct glink_rpm_pipe *tx) 1088b4f8e52bSBjorn Andersson { 1089b4f8e52bSBjorn Andersson struct rpm_toc *toc; 1090b4f8e52bSBjorn Andersson int num_entries; 1091b4f8e52bSBjorn Andersson unsigned int id; 1092b4f8e52bSBjorn Andersson size_t offset; 1093b4f8e52bSBjorn Andersson size_t size; 1094b4f8e52bSBjorn Andersson void *buf; 1095b4f8e52bSBjorn Andersson int i; 1096b4f8e52bSBjorn Andersson 1097b4f8e52bSBjorn Andersson buf = kzalloc(RPM_TOC_SIZE, GFP_KERNEL); 1098b4f8e52bSBjorn Andersson if (!buf) 1099b4f8e52bSBjorn Andersson return -ENOMEM; 1100b4f8e52bSBjorn Andersson 1101b4f8e52bSBjorn Andersson __ioread32_copy(buf, msg_ram + msg_ram_size - RPM_TOC_SIZE, 1102b4f8e52bSBjorn Andersson RPM_TOC_SIZE / sizeof(u32)); 1103b4f8e52bSBjorn Andersson 1104b4f8e52bSBjorn Andersson toc = buf; 1105b4f8e52bSBjorn Andersson 1106b4f8e52bSBjorn Andersson if (le32_to_cpu(toc->magic) != RPM_TOC_MAGIC) { 1107b4f8e52bSBjorn Andersson dev_err(dev, "RPM TOC has invalid magic\n"); 1108b4f8e52bSBjorn Andersson goto err_inval; 1109b4f8e52bSBjorn Andersson } 1110b4f8e52bSBjorn Andersson 1111b4f8e52bSBjorn Andersson num_entries = le32_to_cpu(toc->count); 1112b4f8e52bSBjorn Andersson if (num_entries > RPM_TOC_MAX_ENTRIES) { 1113b4f8e52bSBjorn Andersson dev_err(dev, "Invalid number of toc entries\n"); 1114b4f8e52bSBjorn Andersson goto err_inval; 1115b4f8e52bSBjorn Andersson } 1116b4f8e52bSBjorn Andersson 1117b4f8e52bSBjorn Andersson for (i = 0; i < num_entries; i++) { 1118b4f8e52bSBjorn Andersson id = le32_to_cpu(toc->entries[i].id); 1119b4f8e52bSBjorn Andersson offset = le32_to_cpu(toc->entries[i].offset); 1120b4f8e52bSBjorn Andersson size = le32_to_cpu(toc->entries[i].size); 1121b4f8e52bSBjorn Andersson 1122b4f8e52bSBjorn Andersson if (offset > msg_ram_size || offset + size > msg_ram_size) { 1123b4f8e52bSBjorn Andersson dev_err(dev, "TOC entry with invalid size\n"); 1124b4f8e52bSBjorn Andersson continue; 1125b4f8e52bSBjorn Andersson } 1126b4f8e52bSBjorn Andersson 1127b4f8e52bSBjorn Andersson switch (id) { 1128b4f8e52bSBjorn Andersson case RPM_RX_FIFO_ID: 1129e45c5dc2SBjorn Andersson rx->native.length = size; 1130b4f8e52bSBjorn Andersson 1131b4f8e52bSBjorn Andersson rx->tail = msg_ram + offset; 1132b4f8e52bSBjorn Andersson rx->head = msg_ram + offset + sizeof(u32); 1133b4f8e52bSBjorn Andersson rx->fifo = msg_ram + offset + 2 * sizeof(u32); 1134b4f8e52bSBjorn Andersson break; 1135b4f8e52bSBjorn Andersson case RPM_TX_FIFO_ID: 1136e45c5dc2SBjorn Andersson tx->native.length = size; 1137b4f8e52bSBjorn Andersson 1138b4f8e52bSBjorn Andersson tx->tail = msg_ram + offset; 1139b4f8e52bSBjorn Andersson tx->head = msg_ram + offset + sizeof(u32); 1140b4f8e52bSBjorn Andersson tx->fifo = msg_ram + offset + 2 * sizeof(u32); 1141b4f8e52bSBjorn Andersson break; 1142b4f8e52bSBjorn Andersson } 1143b4f8e52bSBjorn Andersson } 1144b4f8e52bSBjorn Andersson 1145b4f8e52bSBjorn Andersson if (!rx->fifo || !tx->fifo) { 1146b4f8e52bSBjorn Andersson dev_err(dev, "Unable to find rx and tx descriptors\n"); 1147b4f8e52bSBjorn Andersson goto err_inval; 1148b4f8e52bSBjorn Andersson } 1149b4f8e52bSBjorn Andersson 1150b4f8e52bSBjorn Andersson kfree(buf); 1151b4f8e52bSBjorn Andersson return 0; 1152b4f8e52bSBjorn Andersson 1153b4f8e52bSBjorn Andersson err_inval: 1154b4f8e52bSBjorn Andersson kfree(buf); 1155b4f8e52bSBjorn Andersson return -EINVAL; 1156b4f8e52bSBjorn Andersson } 1157b4f8e52bSBjorn Andersson 1158b4f8e52bSBjorn Andersson static int glink_rpm_probe(struct platform_device *pdev) 1159b4f8e52bSBjorn Andersson { 1160d7101febSBjorn Andersson struct qcom_glink *glink; 1161e45c5dc2SBjorn Andersson struct glink_rpm_pipe *rx_pipe; 1162e45c5dc2SBjorn Andersson struct glink_rpm_pipe *tx_pipe; 1163b4f8e52bSBjorn Andersson struct device_node *np; 1164b4f8e52bSBjorn Andersson void __iomem *msg_ram; 1165b4f8e52bSBjorn Andersson size_t msg_ram_size; 1166b4f8e52bSBjorn Andersson struct device *dev = &pdev->dev; 1167b4f8e52bSBjorn Andersson struct resource r; 1168b4f8e52bSBjorn Andersson int irq; 1169b4f8e52bSBjorn Andersson int ret; 1170b4f8e52bSBjorn Andersson 1171b4f8e52bSBjorn Andersson glink = devm_kzalloc(dev, sizeof(*glink), GFP_KERNEL); 1172b4f8e52bSBjorn Andersson if (!glink) 1173b4f8e52bSBjorn Andersson return -ENOMEM; 1174b4f8e52bSBjorn Andersson 1175b4f8e52bSBjorn Andersson glink->dev = dev; 1176b4f8e52bSBjorn Andersson 1177e45c5dc2SBjorn Andersson rx_pipe = devm_kzalloc(&pdev->dev, sizeof(*rx_pipe), GFP_KERNEL); 1178e45c5dc2SBjorn Andersson tx_pipe = devm_kzalloc(&pdev->dev, sizeof(*tx_pipe), GFP_KERNEL); 1179e45c5dc2SBjorn Andersson if (!rx_pipe || !tx_pipe) 1180e45c5dc2SBjorn Andersson return -ENOMEM; 1181e45c5dc2SBjorn Andersson 1182b4f8e52bSBjorn Andersson mutex_init(&glink->tx_lock); 1183b4f8e52bSBjorn Andersson spin_lock_init(&glink->rx_lock); 1184b4f8e52bSBjorn Andersson INIT_LIST_HEAD(&glink->rx_queue); 1185d7101febSBjorn Andersson INIT_WORK(&glink->rx_work, qcom_glink_work); 1186b4f8e52bSBjorn Andersson 1187b4f8e52bSBjorn Andersson mutex_init(&glink->idr_lock); 1188b4f8e52bSBjorn Andersson idr_init(&glink->lcids); 1189b4f8e52bSBjorn Andersson idr_init(&glink->rcids); 1190b4f8e52bSBjorn Andersson 1191b4f8e52bSBjorn Andersson glink->mbox_client.dev = &pdev->dev; 1192b4f8e52bSBjorn Andersson glink->mbox_chan = mbox_request_channel(&glink->mbox_client, 0); 1193b4f8e52bSBjorn Andersson if (IS_ERR(glink->mbox_chan)) { 1194b4f8e52bSBjorn Andersson if (PTR_ERR(glink->mbox_chan) != -EPROBE_DEFER) 1195b4f8e52bSBjorn Andersson dev_err(&pdev->dev, "failed to acquire IPC channel\n"); 1196b4f8e52bSBjorn Andersson return PTR_ERR(glink->mbox_chan); 1197b4f8e52bSBjorn Andersson } 1198b4f8e52bSBjorn Andersson 1199b4f8e52bSBjorn Andersson np = of_parse_phandle(dev->of_node, "qcom,rpm-msg-ram", 0); 1200b4f8e52bSBjorn Andersson ret = of_address_to_resource(np, 0, &r); 1201b4f8e52bSBjorn Andersson of_node_put(np); 1202b4f8e52bSBjorn Andersson if (ret) 1203b4f8e52bSBjorn Andersson return ret; 1204b4f8e52bSBjorn Andersson 1205b4f8e52bSBjorn Andersson msg_ram = devm_ioremap(dev, r.start, resource_size(&r)); 1206b4f8e52bSBjorn Andersson msg_ram_size = resource_size(&r); 1207b4f8e52bSBjorn Andersson if (!msg_ram) 1208b4f8e52bSBjorn Andersson return -ENOMEM; 1209b4f8e52bSBjorn Andersson 1210b4f8e52bSBjorn Andersson ret = glink_rpm_parse_toc(dev, msg_ram, msg_ram_size, 1211e45c5dc2SBjorn Andersson rx_pipe, tx_pipe); 1212b4f8e52bSBjorn Andersson if (ret) 1213b4f8e52bSBjorn Andersson return ret; 1214b4f8e52bSBjorn Andersson 1215e45c5dc2SBjorn Andersson /* Pipe specific accessors */ 1216e45c5dc2SBjorn Andersson rx_pipe->native.avail = glink_rpm_rx_avail; 1217e45c5dc2SBjorn Andersson rx_pipe->native.peak = glink_rpm_rx_peak; 1218e45c5dc2SBjorn Andersson rx_pipe->native.advance = glink_rpm_rx_advance; 1219e45c5dc2SBjorn Andersson tx_pipe->native.avail = glink_rpm_tx_avail; 1220e45c5dc2SBjorn Andersson tx_pipe->native.write = glink_rpm_tx_write; 1221e45c5dc2SBjorn Andersson 1222e45c5dc2SBjorn Andersson glink->tx_pipe = &tx_pipe->native; 1223e45c5dc2SBjorn Andersson glink->rx_pipe = &rx_pipe->native; 1224e45c5dc2SBjorn Andersson 1225e45c5dc2SBjorn Andersson writel(0, tx_pipe->head); 1226e45c5dc2SBjorn Andersson writel(0, rx_pipe->tail); 1227b4f8e52bSBjorn Andersson 1228b4f8e52bSBjorn Andersson irq = platform_get_irq(pdev, 0); 1229b4f8e52bSBjorn Andersson ret = devm_request_irq(dev, irq, 1230d7101febSBjorn Andersson qcom_glink_intr, 1231b4f8e52bSBjorn Andersson IRQF_NO_SUSPEND | IRQF_SHARED, 1232b4f8e52bSBjorn Andersson "glink-rpm", glink); 1233b4f8e52bSBjorn Andersson if (ret) { 1234b4f8e52bSBjorn Andersson dev_err(dev, "Failed to request IRQ\n"); 1235b4f8e52bSBjorn Andersson return ret; 1236b4f8e52bSBjorn Andersson } 1237b4f8e52bSBjorn Andersson 1238b4f8e52bSBjorn Andersson glink->irq = irq; 1239b4f8e52bSBjorn Andersson 1240d7101febSBjorn Andersson ret = qcom_glink_send_version(glink); 1241b4f8e52bSBjorn Andersson if (ret) 1242b4f8e52bSBjorn Andersson return ret; 1243b4f8e52bSBjorn Andersson 1244b4f8e52bSBjorn Andersson platform_set_drvdata(pdev, glink); 1245b4f8e52bSBjorn Andersson 1246b4f8e52bSBjorn Andersson return 0; 1247b4f8e52bSBjorn Andersson } 1248b4f8e52bSBjorn Andersson 1249b4f8e52bSBjorn Andersson static int glink_rpm_remove_device(struct device *dev, void *data) 1250b4f8e52bSBjorn Andersson { 1251b4f8e52bSBjorn Andersson device_unregister(dev); 1252b4f8e52bSBjorn Andersson 1253b4f8e52bSBjorn Andersson return 0; 1254b4f8e52bSBjorn Andersson } 1255b4f8e52bSBjorn Andersson 1256b4f8e52bSBjorn Andersson static int glink_rpm_remove(struct platform_device *pdev) 1257b4f8e52bSBjorn Andersson { 1258d7101febSBjorn Andersson struct qcom_glink *glink = platform_get_drvdata(pdev); 1259b4f8e52bSBjorn Andersson struct glink_channel *channel; 1260b4f8e52bSBjorn Andersson int cid; 1261b4f8e52bSBjorn Andersson int ret; 1262b4f8e52bSBjorn Andersson 1263b4f8e52bSBjorn Andersson disable_irq(glink->irq); 1264b4f8e52bSBjorn Andersson cancel_work_sync(&glink->rx_work); 1265b4f8e52bSBjorn Andersson 1266b4f8e52bSBjorn Andersson ret = device_for_each_child(glink->dev, NULL, glink_rpm_remove_device); 1267b4f8e52bSBjorn Andersson if (ret) 1268b4f8e52bSBjorn Andersson dev_warn(glink->dev, "Can't remove GLINK devices: %d\n", ret); 1269b4f8e52bSBjorn Andersson 1270b4f8e52bSBjorn Andersson /* Release any defunct local channels, waiting for close-ack */ 1271b4f8e52bSBjorn Andersson idr_for_each_entry(&glink->lcids, channel, cid) 1272d7101febSBjorn Andersson kref_put(&channel->refcount, qcom_glink_channel_release); 1273b4f8e52bSBjorn Andersson 1274b4f8e52bSBjorn Andersson idr_destroy(&glink->lcids); 1275b4f8e52bSBjorn Andersson idr_destroy(&glink->rcids); 1276b4f8e52bSBjorn Andersson 1277b4f8e52bSBjorn Andersson return 0; 1278b4f8e52bSBjorn Andersson } 1279b4f8e52bSBjorn Andersson 1280b4f8e52bSBjorn Andersson static const struct of_device_id glink_rpm_of_match[] = { 1281b4f8e52bSBjorn Andersson { .compatible = "qcom,glink-rpm" }, 1282b4f8e52bSBjorn Andersson {} 1283b4f8e52bSBjorn Andersson }; 1284b4f8e52bSBjorn Andersson MODULE_DEVICE_TABLE(of, glink_rpm_of_match); 1285b4f8e52bSBjorn Andersson 1286b4f8e52bSBjorn Andersson static struct platform_driver glink_rpm_driver = { 1287b4f8e52bSBjorn Andersson .probe = glink_rpm_probe, 1288b4f8e52bSBjorn Andersson .remove = glink_rpm_remove, 1289b4f8e52bSBjorn Andersson .driver = { 1290b4f8e52bSBjorn Andersson .name = "qcom_glink_rpm", 1291b4f8e52bSBjorn Andersson .of_match_table = glink_rpm_of_match, 1292b4f8e52bSBjorn Andersson }, 1293b4f8e52bSBjorn Andersson }; 1294b4f8e52bSBjorn Andersson 1295b4f8e52bSBjorn Andersson static int __init glink_rpm_init(void) 1296b4f8e52bSBjorn Andersson { 1297b4f8e52bSBjorn Andersson return platform_driver_register(&glink_rpm_driver); 1298b4f8e52bSBjorn Andersson } 1299b4f8e52bSBjorn Andersson subsys_initcall(glink_rpm_init); 1300b4f8e52bSBjorn Andersson 1301b4f8e52bSBjorn Andersson static void __exit glink_rpm_exit(void) 1302b4f8e52bSBjorn Andersson { 1303b4f8e52bSBjorn Andersson platform_driver_unregister(&glink_rpm_driver); 1304b4f8e52bSBjorn Andersson } 1305b4f8e52bSBjorn Andersson module_exit(glink_rpm_exit); 1306b4f8e52bSBjorn Andersson 1307b4f8e52bSBjorn Andersson MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@linaro.org>"); 1308b4f8e52bSBjorn Andersson MODULE_DESCRIPTION("Qualcomm GLINK RPM driver"); 1309b4f8e52bSBjorn Andersson MODULE_LICENSE("GPL v2"); 1310