diff options
Diffstat (limited to 'drivers/rpmsg/qcom_glink_native.c')
| -rw-r--r-- | drivers/rpmsg/qcom_glink_native.c | 558 |
1 files changed, 355 insertions, 203 deletions
diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 1030cfa80e04..5ea096acc858 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -11,18 +11,21 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> -#include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/rpmsg.h> #include <linux/sizes.h> #include <linux/slab.h> +#include <linux/wait.h> #include <linux/workqueue.h> #include <linux/mailbox_client.h> #include "rpmsg_internal.h" #include "qcom_glink_native.h" +#define CREATE_TRACE_POINTS +#include "qcom_glink_trace.h" + #define GLINK_NAME_SIZE 32 #define GLINK_VERSION_1 1 @@ -30,11 +33,16 @@ #define RPM_GLINK_CID_MAX 65536 struct glink_msg { - __le16 cmd; - __le16 param1; - __le32 param2; + /* New members MUST be added within the __struct_group() macro below. */ + __struct_group(glink_msg_hdr, hdr, __packed, + __le16 cmd; + __le16 param1; + __le32 param2; + ); u8 data[]; } __packed; +static_assert(offsetof(struct glink_msg, data) == sizeof(struct glink_msg_hdr), + "struct member likely outside of __struct_group()"); /** * struct glink_defer_cmd - deferred incoming control message @@ -48,7 +56,7 @@ struct glink_msg { struct glink_defer_cmd { struct list_head node; - struct glink_msg msg; + struct glink_msg_hdr msg; u8 data[]; }; @@ -78,11 +86,9 @@ struct glink_core_rx_intent { /** * struct qcom_glink - driver context, relates to one remote subsystem * @dev: reference to the associated struct device - * @mbox_client: mailbox client - * @mbox_chan: mailbox channel + * @label: identifier of the glink edge * @rx_pipe: pipe object for receive FIFO * @tx_pipe: pipe object for transmit FIFO - * @irq: IRQ for signaling incoming events * @rx_work: worker for handling received control messages * @rx_lock: protects the @rx_queue * @rx_queue: queue of received control messages to be processed in @rx_work @@ -94,20 +100,16 @@ struct glink_core_rx_intent { * @intentless: flag to indicate that there is no intent * @tx_avail_notify: Waitqueue for pending tx tasks * @sent_read_notify: flag to check cmd sent or not + * @abort_tx: flag indicating that all tx attempts should fail */ struct qcom_glink { struct device *dev; - const char *name; - - struct mbox_client mbox_client; - struct mbox_chan *mbox_chan; + const char *label; struct qcom_glink_pipe *rx_pipe; struct qcom_glink_pipe *tx_pipe; - int irq; - struct work_struct rx_work; spinlock_t rx_lock; struct list_head rx_queue; @@ -122,6 +124,8 @@ struct qcom_glink { bool intentless; wait_queue_head_t tx_avail_notify; bool sent_read_notify; + + bool abort_tx; }; enum { @@ -153,7 +157,8 @@ enum { * @open_req: completed once open-request has been received * @intent_req_lock: Synchronises multiple intent requests * @intent_req_result: Result of intent request - * @intent_req_comp: Completion for intent_req signalling + * @intent_received: flag indicating that an intent has been received + * @intent_req_wq: wait queue for intent_req signalling */ struct glink_channel { struct rpmsg_endpoint ept; @@ -183,31 +188,38 @@ struct glink_channel { struct completion open_req; struct mutex intent_req_lock; - bool intent_req_result; - struct completion intent_req_comp; + int intent_req_result; + bool intent_received; + wait_queue_head_t intent_req_wq; }; #define to_glink_channel(_ept) container_of(_ept, struct glink_channel, ept) static const struct rpmsg_endpoint_ops glink_endpoint_ops; -#define RPM_CMD_VERSION 0 -#define RPM_CMD_VERSION_ACK 1 -#define RPM_CMD_OPEN 2 -#define RPM_CMD_CLOSE 3 -#define RPM_CMD_OPEN_ACK 4 -#define RPM_CMD_INTENT 5 -#define RPM_CMD_RX_DONE 6 -#define RPM_CMD_RX_INTENT_REQ 7 -#define RPM_CMD_RX_INTENT_REQ_ACK 8 -#define RPM_CMD_TX_DATA 9 -#define RPM_CMD_CLOSE_ACK 11 -#define RPM_CMD_TX_DATA_CONT 12 -#define RPM_CMD_READ_NOTIF 13 -#define RPM_CMD_RX_DONE_W_REUSE 14 +#define GLINK_CMD_VERSION 0 +#define GLINK_CMD_VERSION_ACK 1 +#define GLINK_CMD_OPEN 2 +#define GLINK_CMD_CLOSE 3 +#define GLINK_CMD_OPEN_ACK 4 +#define GLINK_CMD_INTENT 5 +#define GLINK_CMD_RX_DONE 6 +#define GLINK_CMD_RX_INTENT_REQ 7 +#define GLINK_CMD_RX_INTENT_REQ_ACK 8 +#define GLINK_CMD_TX_DATA 9 +#define GLINK_CMD_CLOSE_ACK 11 +#define GLINK_CMD_TX_DATA_CONT 12 +#define GLINK_CMD_READ_NOTIF 13 +#define GLINK_CMD_RX_DONE_W_REUSE 14 +#define GLINK_CMD_SIGNALS 15 #define GLINK_FEATURE_INTENTLESS BIT(1) +#define NATIVE_DTR_SIG NATIVE_DSR_SIG +#define NATIVE_DSR_SIG BIT(31) +#define NATIVE_RTS_SIG NATIVE_CTS_SIG +#define NATIVE_CTS_SIG BIT(30) + static void qcom_glink_rx_done_work(struct work_struct *work); static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, @@ -226,10 +238,14 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, channel->glink = glink; channel->name = kstrdup(name, GFP_KERNEL); + if (!channel->name) { + kfree(channel); + return ERR_PTR(-ENOMEM); + } init_completion(&channel->open_req); init_completion(&channel->open_ack); - init_completion(&channel->intent_req_comp); + init_waitqueue_head(&channel->intent_req_wq); INIT_LIST_HEAD(&channel->done_intents); INIT_WORK(&channel->intent_work, qcom_glink_rx_done_work); @@ -282,10 +298,10 @@ static size_t qcom_glink_rx_avail(struct qcom_glink *glink) return glink->rx_pipe->avail(glink->rx_pipe); } -static void qcom_glink_rx_peak(struct qcom_glink *glink, +static void qcom_glink_rx_peek(struct qcom_glink *glink, void *data, unsigned int offset, size_t count) { - glink->rx_pipe->peak(glink->rx_pipe, data, offset, count); + glink->rx_pipe->peek(glink->rx_pipe, data, offset, count); } static void qcom_glink_rx_advance(struct qcom_glink *glink, size_t count) @@ -305,18 +321,22 @@ static void qcom_glink_tx_write(struct qcom_glink *glink, glink->tx_pipe->write(glink->tx_pipe, hdr, hlen, data, dlen); } +static void qcom_glink_tx_kick(struct qcom_glink *glink) +{ + glink->tx_pipe->kick(glink->tx_pipe); +} + static void qcom_glink_send_read_notify(struct qcom_glink *glink) { struct glink_msg msg; - msg.cmd = cpu_to_le16(RPM_CMD_READ_NOTIF); + msg.cmd = cpu_to_le16(GLINK_CMD_READ_NOTIF); msg.param1 = 0; msg.param2 = 0; qcom_glink_tx_write(glink, &msg, sizeof(msg), NULL, 0); - mbox_send_message(glink->mbox_chan, NULL); - mbox_client_txdone(glink->mbox_chan, 0); + qcom_glink_tx_kick(glink); } static int qcom_glink_tx(struct qcom_glink *glink, @@ -333,12 +353,22 @@ static int qcom_glink_tx(struct qcom_glink *glink, spin_lock_irqsave(&glink->tx_lock, flags); + if (glink->abort_tx) { + ret = -EIO; + goto out; + } + while (qcom_glink_tx_avail(glink) < tlen) { if (!wait) { ret = -EAGAIN; goto out; } + if (glink->abort_tx) { + ret = -EIO; + goto out; + } + if (!glink->sent_read_notify) { glink->sent_read_notify = true; qcom_glink_send_read_notify(glink); @@ -357,9 +387,7 @@ static int qcom_glink_tx(struct qcom_glink *glink, } qcom_glink_tx_write(glink, hdr, hlen, data, dlen); - - mbox_send_message(glink->mbox_chan, NULL); - mbox_client_txdone(glink->mbox_chan, 0); + qcom_glink_tx_kick(glink); out: spin_unlock_irqrestore(&glink->tx_lock, flags); @@ -371,10 +399,12 @@ static int qcom_glink_send_version(struct qcom_glink *glink) { struct glink_msg msg; - msg.cmd = cpu_to_le16(RPM_CMD_VERSION); + msg.cmd = cpu_to_le16(GLINK_CMD_VERSION); msg.param1 = cpu_to_le16(GLINK_VERSION_1); msg.param2 = cpu_to_le32(glink->features); + trace_qcom_glink_cmd_version_tx(glink->label, GLINK_VERSION_1, glink->features); + return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); } @@ -382,10 +412,12 @@ static void qcom_glink_send_version_ack(struct qcom_glink *glink) { struct glink_msg msg; - msg.cmd = cpu_to_le16(RPM_CMD_VERSION_ACK); + msg.cmd = cpu_to_le16(GLINK_CMD_VERSION_ACK); msg.param1 = cpu_to_le16(GLINK_VERSION_1); msg.param2 = cpu_to_le32(glink->features); + trace_qcom_glink_cmd_version_ack_tx(glink->label, msg.param1, msg.param2); + qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); } @@ -394,10 +426,13 @@ static void qcom_glink_send_open_ack(struct qcom_glink *glink, { struct glink_msg msg; - msg.cmd = cpu_to_le16(RPM_CMD_OPEN_ACK); + msg.cmd = cpu_to_le16(GLINK_CMD_OPEN_ACK); msg.param1 = cpu_to_le16(channel->rcid); msg.param2 = cpu_to_le32(0); + trace_qcom_glink_cmd_open_ack_tx(glink->label, channel->name, + channel->lcid, channel->rcid); + qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); } @@ -407,24 +442,37 @@ static void qcom_glink_handle_intent_req_ack(struct qcom_glink *glink, struct glink_channel *channel; unsigned long flags; + qcom_glink_rx_advance(glink, ALIGN(sizeof(struct glink_msg), 8)); + spin_lock_irqsave(&glink->idr_lock, flags); channel = idr_find(&glink->rcids, cid); spin_unlock_irqrestore(&glink->idr_lock, flags); + + trace_qcom_glink_cmd_rx_intent_req_ack_rx(glink->label, + channel ? channel->name : NULL, + channel ? channel->lcid : 0, + cid, granted); if (!channel) { dev_err(glink->dev, "unable to find channel\n"); return; } - channel->intent_req_result = granted; - complete(&channel->intent_req_comp); + WRITE_ONCE(channel->intent_req_result, granted); + wake_up_all(&channel->intent_req_wq); +} + +static void qcom_glink_intent_req_abort(struct glink_channel *channel) +{ + WRITE_ONCE(channel->intent_req_result, 0); + wake_up_all(&channel->intent_req_wq); } /** - * qcom_glink_send_open_req() - send a RPM_CMD_OPEN request to the remote + * qcom_glink_send_open_req() - send a GLINK_CMD_OPEN request to the remote * @glink: Ptr to the glink edge * @channel: Ptr to the channel that the open req is sent * - * Allocates a local channel id and sends a RPM_CMD_OPEN message to the remote. + * Allocates a local channel id and sends a GLINK_CMD_OPEN message to the remote. * Will return with refcount held, regardless of outcome. * * Return: 0 on success, negative errno otherwise. @@ -432,12 +480,9 @@ static void qcom_glink_handle_intent_req_ack(struct qcom_glink *glink, static int qcom_glink_send_open_req(struct qcom_glink *glink, struct glink_channel *channel) { - struct { - struct glink_msg msg; - u8 name[GLINK_NAME_SIZE]; - } __packed req; + DEFINE_RAW_FLEX(struct glink_msg, req, data, GLINK_NAME_SIZE); int name_len = strlen(channel->name) + 1; - int req_len = ALIGN(sizeof(req.msg) + name_len, 8); + int req_len = ALIGN(sizeof(*req) + name_len, 8); int ret; unsigned long flags; @@ -453,12 +498,15 @@ static int qcom_glink_send_open_req(struct qcom_glink *glink, channel->lcid = ret; - req.msg.cmd = cpu_to_le16(RPM_CMD_OPEN); - req.msg.param1 = cpu_to_le16(channel->lcid); - req.msg.param2 = cpu_to_le32(name_len); - strcpy(req.name, channel->name); + req->cmd = cpu_to_le16(GLINK_CMD_OPEN); + req->param1 = cpu_to_le16(channel->lcid); + req->param2 = cpu_to_le32(name_len); + strcpy(req->data, channel->name); + + trace_qcom_glink_cmd_open_tx(glink->label, channel->name, + channel->lcid, channel->rcid); - ret = qcom_glink_tx(glink, &req, req_len, NULL, 0, true); + ret = qcom_glink_tx(glink, req, req_len, NULL, 0, true); if (ret) goto remove_idr; @@ -478,22 +526,28 @@ static void qcom_glink_send_close_req(struct qcom_glink *glink, { struct glink_msg req; - req.cmd = cpu_to_le16(RPM_CMD_CLOSE); + req.cmd = cpu_to_le16(GLINK_CMD_CLOSE); req.param1 = cpu_to_le16(channel->lcid); req.param2 = 0; + trace_qcom_glink_cmd_close_tx(glink->label, channel->name, + channel->lcid, channel->rcid); + qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true); } static void qcom_glink_send_close_ack(struct qcom_glink *glink, - unsigned int rcid) + struct glink_channel *channel) { struct glink_msg req; - req.cmd = cpu_to_le16(RPM_CMD_CLOSE_ACK); - req.param1 = cpu_to_le16(rcid); + req.cmd = cpu_to_le16(GLINK_CMD_CLOSE_ACK); + req.param1 = cpu_to_le16(channel->rcid); req.param2 = 0; + trace_qcom_glink_cmd_close_ack_tx(glink->label, channel->name, + channel->lcid, channel->rcid); + qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true); } @@ -521,10 +575,13 @@ static void qcom_glink_rx_done_work(struct work_struct *work) iid = intent->id; reuse = intent->reuse; - cmd.id = reuse ? RPM_CMD_RX_DONE_W_REUSE : RPM_CMD_RX_DONE; + cmd.id = reuse ? GLINK_CMD_RX_DONE_W_REUSE : GLINK_CMD_RX_DONE; cmd.lcid = cid; cmd.liid = iid; + trace_qcom_glink_cmd_rx_done_tx(glink->label, channel->name, + channel->lcid, channel->rcid, cmd.liid, reuse); + qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true); if (!reuse) { kfree(intent->data); @@ -575,6 +632,8 @@ static void qcom_glink_receive_version(struct qcom_glink *glink, u32 version, u32 features) { + trace_qcom_glink_cmd_version_rx(glink->label, version, features); + switch (version) { case 0: break; @@ -602,6 +661,8 @@ static void qcom_glink_receive_version_ack(struct qcom_glink *glink, u32 version, u32 features) { + trace_qcom_glink_cmd_version_ack_rx(glink->label, version, features); + switch (version) { case 0: /* Version negotiation failed */ @@ -633,7 +694,11 @@ static int qcom_glink_send_intent_req_ack(struct qcom_glink *glink, { struct glink_msg msg; - msg.cmd = cpu_to_le16(RPM_CMD_RX_INTENT_REQ_ACK); + trace_qcom_glink_cmd_rx_intent_req_ack_tx(glink->label, channel->name, + channel->lcid, channel->rcid, + granted); + + msg.cmd = cpu_to_le16(GLINK_CMD_RX_INTENT_REQ_ACK); msg.param1 = cpu_to_le16(channel->lcid); msg.param2 = cpu_to_le32(granted); @@ -664,12 +729,16 @@ static int qcom_glink_advertise_intent(struct qcom_glink *glink, } __packed; struct command cmd; - cmd.id = cpu_to_le16(RPM_CMD_INTENT); + cmd.id = cpu_to_le16(GLINK_CMD_INTENT); cmd.lcid = cpu_to_le16(channel->lcid); cmd.count = cpu_to_le32(1); cmd.size = cpu_to_le32(intent->size); cmd.liid = cpu_to_le32(intent->id); + trace_qcom_glink_cmd_intent_tx(glink->label, channel->name, + channel->lcid, channel->rcid, + cmd.count, cmd.size, cmd.liid); + qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true); return 0; @@ -722,9 +791,14 @@ static void qcom_glink_handle_rx_done(struct qcom_glink *glink, struct glink_channel *channel; unsigned long flags; + qcom_glink_rx_advance(glink, ALIGN(sizeof(struct glink_msg), 8)); + spin_lock_irqsave(&glink->idr_lock, flags); channel = idr_find(&glink->rcids, cid); spin_unlock_irqrestore(&glink->idr_lock, flags); + + trace_qcom_glink_cmd_rx_done_rx(glink->label, channel ? channel->name : NULL, + channel ? channel->lcid : 0, cid, iid, reuse); if (!channel) { dev_err(glink->dev, "invalid channel id received\n"); return; @@ -746,6 +820,11 @@ static void qcom_glink_handle_rx_done(struct qcom_glink *glink, kfree(intent); } spin_unlock_irqrestore(&channel->intent_lock, flags); + + if (reuse) { + WRITE_ONCE(channel->intent_received, true); + wake_up_all(&channel->intent_req_wq); + } } /** @@ -769,6 +848,10 @@ static void qcom_glink_handle_intent_req(struct qcom_glink *glink, channel = idr_find(&glink->rcids, cid); spin_unlock_irqrestore(&glink->idr_lock, flags); + trace_qcom_glink_cmd_rx_intent_req_rx(glink->label, + channel ? channel->name : NULL, + channel ? channel->lcid : 0, + cid, size); if (!channel) { pr_err("%s channel not found for cid %d\n", __func__, cid); return; @@ -792,13 +875,15 @@ static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra) return -ENXIO; } - dcmd = kzalloc(sizeof(*dcmd) + extra, GFP_ATOMIC); + dcmd = kzalloc(struct_size(dcmd, data, extra), GFP_ATOMIC); if (!dcmd) return -ENOMEM; INIT_LIST_HEAD(&dcmd->node); - qcom_glink_rx_peak(glink, &dcmd->msg, 0, sizeof(dcmd->msg) + extra); + qcom_glink_rx_peek(glink, + container_of(&dcmd->msg, struct glink_msg, hdr), 0, + sizeof(dcmd->msg) + extra); spin_lock(&glink->rx_lock); list_add_tail(&dcmd->node, &glink->rx_queue); @@ -815,7 +900,7 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) struct glink_core_rx_intent *intent; struct glink_channel *channel; struct { - struct glink_msg msg; + struct glink_msg_hdr msg; __le32 chunk_size; __le32 left_size; } __packed hdr; @@ -831,7 +916,7 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) return -EAGAIN; } - qcom_glink_rx_peak(glink, &hdr, 0, sizeof(hdr)); + qcom_glink_rx_peek(glink, &hdr, 0, sizeof(hdr)); chunk_size = le32_to_cpu(hdr.chunk_size); left_size = le32_to_cpu(hdr.left_size); @@ -841,9 +926,15 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) } rcid = le16_to_cpu(hdr.msg.param1); + liid = le32_to_cpu(hdr.msg.param2); spin_lock_irqsave(&glink->idr_lock, flags); channel = idr_find(&glink->rcids, rcid); spin_unlock_irqrestore(&glink->idr_lock, flags); + + trace_qcom_glink_cmd_tx_data_rx(glink->label, channel ? channel->name : NULL, + channel ? channel->lcid : 0, rcid, + liid, chunk_size, left_size, + hdr.msg.cmd == GLINK_CMD_TX_DATA_CONT); if (!channel) { dev_dbg(glink->dev, "Data on non-existing channel\n"); @@ -874,8 +965,6 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) intent = channel->buf; } } else { - liid = le32_to_cpu(hdr.msg.param2); - spin_lock_irqsave(&channel->intent_lock, flags); intent = idr_find(&channel->liids, liid); spin_unlock_irqrestore(&channel->intent_lock, flags); @@ -896,7 +985,7 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) goto advance_rx; } - qcom_glink_rx_peak(glink, intent->data + intent->offset, + qcom_glink_rx_peek(glink, intent->data + intent->offset, sizeof(hdr), chunk_size); intent->offset += chunk_size; @@ -924,6 +1013,14 @@ advance_rx: return ret; } +static void qcom_glink_rx_read_notif(struct qcom_glink *glink) +{ + trace_qcom_glink_cmd_read_notif_rx(glink->label); + + qcom_glink_rx_advance(glink, ALIGN(sizeof(struct glink_msg), 8)); + qcom_glink_tx_kick(glink); +} + static void qcom_glink_handle_intent(struct qcom_glink *glink, unsigned int cid, unsigned int count, @@ -937,7 +1034,7 @@ static void qcom_glink_handle_intent(struct qcom_glink *glink, }; struct { - struct glink_msg msg; + struct glink_msg_hdr msg; struct intent_pair intents[]; } __packed * msg; @@ -955,7 +1052,9 @@ static void qcom_glink_handle_intent(struct qcom_glink *glink, channel = idr_find(&glink->rcids, cid); spin_unlock_irqrestore(&glink->idr_lock, flags); if (!channel) { + trace_qcom_glink_cmd_intent_rx(glink->label, NULL, 0, cid, count, 0, 0); dev_err(glink->dev, "intents for non-existing channel\n"); + qcom_glink_rx_advance(glink, ALIGN(msglen, 8)); return; } @@ -963,7 +1062,12 @@ static void qcom_glink_handle_intent(struct qcom_glink *glink, if (!msg) return; - qcom_glink_rx_peak(glink, msg, 0, msglen); + qcom_glink_rx_peek(glink, msg, 0, msglen); + + trace_qcom_glink_cmd_intent_rx(glink->label, channel->name, + channel->lcid, cid, count, + count > 0 ? msg->intents[0].size : 0, + count > 0 ? msg->intents[0].iid : 0); for (i = 0; i < count; ++i) { intent = kzalloc(sizeof(*intent), GFP_ATOMIC); @@ -982,6 +1086,9 @@ static void qcom_glink_handle_intent(struct qcom_glink *glink, dev_err(glink->dev, "failed to store remote intent\n"); } + WRITE_ONCE(channel->intent_received, true); + wake_up_all(&channel->intent_req_wq); + kfree(msg); qcom_glink_rx_advance(glink, ALIGN(msglen, 8)); } @@ -990,9 +1097,14 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) { struct glink_channel *channel; + qcom_glink_rx_advance(glink, ALIGN(sizeof(struct glink_msg), 8)); + spin_lock(&glink->idr_lock); channel = idr_find(&glink->lcids, lcid); spin_unlock(&glink->idr_lock); + + trace_qcom_glink_cmd_open_ack_rx(glink->label, channel ? channel->name : NULL, + lcid, channel ? channel->rcid : 0); if (!channel) { dev_err(glink->dev, "Invalid open ack packet\n"); return -EINVAL; @@ -1003,9 +1115,62 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) return 0; } -static irqreturn_t qcom_glink_native_intr(int irq, void *data) +/** + * qcom_glink_set_flow_control() - convert a signal cmd to wire format and transmit + * @ept: Rpmsg endpoint for channel. + * @pause: Pause transmission + * @dst: destination address of the endpoint + * + * Return: 0 on success or standard Linux error code. + */ +static int qcom_glink_set_flow_control(struct rpmsg_endpoint *ept, bool pause, u32 dst) +{ + struct glink_channel *channel = to_glink_channel(ept); + struct qcom_glink *glink = channel->glink; + struct glink_msg msg; + u32 sigs = 0; + + if (pause) + sigs |= NATIVE_DTR_SIG | NATIVE_RTS_SIG; + + msg.cmd = cpu_to_le16(GLINK_CMD_SIGNALS); + msg.param1 = cpu_to_le16(channel->lcid); + msg.param2 = cpu_to_le32(sigs); + + trace_qcom_glink_cmd_signal_tx(glink->label, channel->name, + channel->lcid, channel->rcid, sigs); + + return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); +} + +static void qcom_glink_handle_signals(struct qcom_glink *glink, + unsigned int rcid, unsigned int sigs) +{ + struct glink_channel *channel; + unsigned long flags; + bool enable; + + qcom_glink_rx_advance(glink, ALIGN(sizeof(struct glink_msg), 8)); + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, rcid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + + trace_qcom_glink_cmd_signal_rx(glink->label, channel ? channel->name : NULL, + channel ? channel->lcid : 0, rcid, sigs); + if (!channel) { + dev_err(glink->dev, "signal for non-existing channel\n"); + return; + } + + enable = sigs & NATIVE_DSR_SIG || sigs & NATIVE_CTS_SIG; + + if (channel->ept.flow_cb) + channel->ept.flow_cb(channel->ept.rpdev, channel->ept.priv, enable); +} + +void qcom_glink_native_rx(struct qcom_glink *glink) { - struct qcom_glink *glink = data; struct glink_msg msg; unsigned int param1; unsigned int param2; @@ -1021,51 +1186,48 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data) if (avail < sizeof(msg)) break; - qcom_glink_rx_peak(glink, &msg, 0, sizeof(msg)); + qcom_glink_rx_peek(glink, &msg, 0, sizeof(msg)); cmd = le16_to_cpu(msg.cmd); param1 = le16_to_cpu(msg.param1); param2 = le32_to_cpu(msg.param2); switch (cmd) { - case RPM_CMD_VERSION: - case RPM_CMD_VERSION_ACK: - case RPM_CMD_CLOSE: - case RPM_CMD_CLOSE_ACK: - case RPM_CMD_RX_INTENT_REQ: + case GLINK_CMD_VERSION: + case GLINK_CMD_VERSION_ACK: + case GLINK_CMD_CLOSE: + case GLINK_CMD_CLOSE_ACK: + case GLINK_CMD_RX_INTENT_REQ: ret = qcom_glink_rx_defer(glink, 0); break; - case RPM_CMD_OPEN_ACK: + case GLINK_CMD_OPEN_ACK: ret = qcom_glink_rx_open_ack(glink, param1); - qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); break; - case RPM_CMD_OPEN: - ret = qcom_glink_rx_defer(glink, param2); + case GLINK_CMD_OPEN: + /* upper 16 bits of param2 are the "prio" field */ + ret = qcom_glink_rx_defer(glink, param2 & 0xffff); break; - case RPM_CMD_TX_DATA: - case RPM_CMD_TX_DATA_CONT: + case GLINK_CMD_TX_DATA: + case GLINK_CMD_TX_DATA_CONT: ret = qcom_glink_rx_data(glink, avail); break; - case RPM_CMD_READ_NOTIF: - qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); - - mbox_send_message(glink->mbox_chan, NULL); - mbox_client_txdone(glink->mbox_chan, 0); + case GLINK_CMD_READ_NOTIF: + qcom_glink_rx_read_notif(glink); break; - case RPM_CMD_INTENT: + case GLINK_CMD_INTENT: qcom_glink_handle_intent(glink, param1, param2, avail); break; - case RPM_CMD_RX_DONE: + case GLINK_CMD_RX_DONE: qcom_glink_handle_rx_done(glink, param1, param2, false); - qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); break; - case RPM_CMD_RX_DONE_W_REUSE: + case GLINK_CMD_RX_DONE_W_REUSE: qcom_glink_handle_rx_done(glink, param1, param2, true); - qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); break; - case RPM_CMD_RX_INTENT_REQ_ACK: + case GLINK_CMD_RX_INTENT_REQ_ACK: qcom_glink_handle_intent_req_ack(glink, param1, param2); - qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); + break; + case GLINK_CMD_SIGNALS: + qcom_glink_handle_signals(glink, param1, param2); break; default: dev_err(glink->dev, "unhandled rx cmd: %d\n", cmd); @@ -1076,9 +1238,8 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data) if (ret) break; } - - return IRQ_HANDLED; } +EXPORT_SYMBOL(qcom_glink_native_rx); /* Locally initiated rpmsg_create_ept */ static struct glink_channel *qcom_glink_create_local(struct qcom_glink *glink, @@ -1234,6 +1395,19 @@ static int qcom_glink_announce_create(struct rpmsg_device *rpdev) return 0; } +static void qcom_glink_remove_rpmsg_device(struct qcom_glink *glink, struct glink_channel *channel) +{ + struct rpmsg_channel_info chinfo; + + if (channel->rpdev) { + strscpy_pad(chinfo.name, channel->name, sizeof(chinfo.name)); + chinfo.src = RPMSG_ADDR_ANY; + chinfo.dst = RPMSG_ADDR_ANY; + rpmsg_unregister_device(glink->dev, &chinfo); + } + channel->rpdev = NULL; +} + static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept) { struct glink_channel *channel = to_glink_channel(ept); @@ -1245,7 +1419,7 @@ static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept) spin_unlock_irqrestore(&channel->recv_lock, flags); /* Decouple the potential rpdev from the channel */ - channel->rpdev = NULL; + qcom_glink_remove_rpmsg_device(glink, channel); qcom_glink_send_close_req(glink, channel); } @@ -1264,22 +1438,34 @@ static int qcom_glink_request_intent(struct qcom_glink *glink, mutex_lock(&channel->intent_req_lock); - reinit_completion(&channel->intent_req_comp); + WRITE_ONCE(channel->intent_req_result, -1); + WRITE_ONCE(channel->intent_received, false); - cmd.id = RPM_CMD_RX_INTENT_REQ; + cmd.id = GLINK_CMD_RX_INTENT_REQ; cmd.cid = channel->lcid; cmd.size = size; + trace_qcom_glink_cmd_rx_intent_req_tx(glink->label, channel->name, + channel->lcid, channel->rcid, + cmd.size); + ret = qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true); if (ret) goto unlock; - ret = wait_for_completion_timeout(&channel->intent_req_comp, 10 * HZ); + ret = wait_event_timeout(channel->intent_req_wq, + READ_ONCE(channel->intent_req_result) == 0 || + (READ_ONCE(channel->intent_req_result) > 0 && + READ_ONCE(channel->intent_received)) || + glink->abort_tx, + 10 * HZ); if (!ret) { dev_err(glink->dev, "intent request timed out\n"); ret = -ETIMEDOUT; + } else if (glink->abort_tx) { + ret = -ECANCELED; } else { - ret = channel->intent_req_result ? 0 : -ECANCELED; + ret = READ_ONCE(channel->intent_req_result) ? 0 : -EAGAIN; } unlock: @@ -1295,14 +1481,14 @@ static int __qcom_glink_send(struct glink_channel *channel, struct glink_core_rx_intent *tmp; int iid = 0; struct { - struct glink_msg msg; + struct glink_msg_hdr msg; __le32 chunk_size; __le32 left_size; } __packed req; int ret; unsigned long flags; int chunk_size = len; - int left_size = 0; + size_t offset = 0; if (!glink->intentless) { while (!intent) { @@ -1336,47 +1522,35 @@ static int __qcom_glink_send(struct glink_channel *channel, iid = intent->id; } - if (wait && chunk_size > SZ_8K) { - chunk_size = SZ_8K; - left_size = len - chunk_size; - } - req.msg.cmd = cpu_to_le16(RPM_CMD_TX_DATA); - req.msg.param1 = cpu_to_le16(channel->lcid); - req.msg.param2 = cpu_to_le32(iid); - req.chunk_size = cpu_to_le32(chunk_size); - req.left_size = cpu_to_le32(left_size); - - ret = qcom_glink_tx(glink, &req, sizeof(req), data, chunk_size, wait); - - /* Mark intent available if we failed */ - if (ret && intent) { - intent->in_use = false; - return ret; - } - - while (left_size > 0) { - data = (void *)((char *)data + chunk_size); - chunk_size = left_size; - if (chunk_size > SZ_8K) + while (offset < len) { + chunk_size = len - offset; + if (chunk_size > SZ_8K && wait) chunk_size = SZ_8K; - left_size -= chunk_size; - req.msg.cmd = cpu_to_le16(RPM_CMD_TX_DATA_CONT); + req.msg.cmd = cpu_to_le16(offset == 0 ? GLINK_CMD_TX_DATA : GLINK_CMD_TX_DATA_CONT); req.msg.param1 = cpu_to_le16(channel->lcid); req.msg.param2 = cpu_to_le32(iid); req.chunk_size = cpu_to_le32(chunk_size); - req.left_size = cpu_to_le32(left_size); + req.left_size = cpu_to_le32(len - offset - chunk_size); - ret = qcom_glink_tx(glink, &req, sizeof(req), data, - chunk_size, wait); + trace_qcom_glink_cmd_tx_data_tx(glink->label, channel->name, + channel->lcid, channel->rcid, + iid, chunk_size, + len - offset - chunk_size, + offset > 0); - /* Mark intent available if we failed */ - if (ret && intent) { - intent->in_use = false; - break; + ret = qcom_glink_tx(glink, &req, sizeof(req), data + offset, chunk_size, wait); + if (ret) { + /* Mark intent available if we failed */ + if (intent) + intent->in_use = false; + return ret; } + + offset += chunk_size; } - return ret; + + return 0; } static int qcom_glink_send(struct rpmsg_endpoint *ept, void *data, int len) @@ -1442,12 +1616,14 @@ static const struct rpmsg_endpoint_ops glink_endpoint_ops = { .sendto = qcom_glink_sendto, .trysend = qcom_glink_trysend, .trysendto = qcom_glink_trysendto, + .set_flow_control = qcom_glink_set_flow_control, }; static void qcom_glink_rpdev_release(struct device *dev) { struct rpmsg_device *rpdev = to_rpmsg_device(dev); + kfree(rpdev->driver_override); kfree(rpdev); } @@ -1478,6 +1654,8 @@ static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid, create_device = true; } + trace_qcom_glink_cmd_open_rx(glink->label, name, channel->lcid, rcid); + spin_lock_irqsave(&glink->idr_lock, flags); ret = idr_alloc(&glink->rcids, channel, rcid, rcid + 1, GFP_ATOMIC); if (ret < 0) { @@ -1498,7 +1676,7 @@ static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid, } rpdev->ept = &channel->ept; - strscpy_pad(rpdev->id.name, name, RPMSG_NAME_SIZE); + strscpy(rpdev->id.name, name); rpdev->src = RPMSG_ADDR_ANY; rpdev->dst = RPMSG_ADDR_ANY; rpdev->ops = &glink_device_ops; @@ -1532,29 +1710,24 @@ free_channel: static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid) { - struct rpmsg_channel_info chinfo; struct glink_channel *channel; unsigned long flags; spin_lock_irqsave(&glink->idr_lock, flags); channel = idr_find(&glink->rcids, rcid); spin_unlock_irqrestore(&glink->idr_lock, flags); + + trace_qcom_glink_cmd_close_rx(glink->label, channel ? channel->name : NULL, + channel ? channel->lcid : 0, rcid); if (WARN(!channel, "close request on unknown channel\n")) return; /* cancel pending rx_done work */ cancel_work_sync(&channel->intent_work); - if (channel->rpdev) { - strncpy(chinfo.name, channel->name, sizeof(chinfo.name)); - chinfo.src = RPMSG_ADDR_ANY; - chinfo.dst = RPMSG_ADDR_ANY; - - rpmsg_unregister_device(glink->dev, &chinfo); - } - channel->rpdev = NULL; + qcom_glink_remove_rpmsg_device(glink, channel); - qcom_glink_send_close_ack(glink, channel->rcid); + qcom_glink_send_close_ack(glink, channel); spin_lock_irqsave(&glink->idr_lock, flags); idr_remove(&glink->rcids, channel->rcid); @@ -1566,7 +1739,6 @@ static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid) static void qcom_glink_rx_close_ack(struct qcom_glink *glink, unsigned int lcid) { - struct rpmsg_channel_info chinfo; struct glink_channel *channel; unsigned long flags; @@ -1575,6 +1747,9 @@ static void qcom_glink_rx_close_ack(struct qcom_glink *glink, unsigned int lcid) spin_lock_irqsave(&glink->idr_lock, flags); channel = idr_find(&glink->lcids, lcid); + + trace_qcom_glink_cmd_close_ack_rx(glink->label, channel ? channel->name : NULL, + lcid, channel ? channel->rcid : 0); if (WARN(!channel, "close ack on unknown channel\n")) { spin_unlock_irqrestore(&glink->idr_lock, flags); return; @@ -1585,14 +1760,7 @@ static void qcom_glink_rx_close_ack(struct qcom_glink *glink, unsigned int lcid) spin_unlock_irqrestore(&glink->idr_lock, flags); /* Decouple the potential rpdev from the channel */ - if (channel->rpdev) { - strscpy(chinfo.name, channel->name, sizeof(chinfo.name)); - chinfo.src = RPMSG_ADDR_ANY; - chinfo.dst = RPMSG_ADDR_ANY; - - rpmsg_unregister_device(glink->dev, &chinfo); - } - channel->rpdev = NULL; + qcom_glink_remove_rpmsg_device(glink, channel); kref_put(&channel->refcount, qcom_glink_channel_release); } @@ -1619,28 +1787,28 @@ static void qcom_glink_work(struct work_struct *work) list_del(&dcmd->node); spin_unlock_irqrestore(&glink->rx_lock, flags); - msg = &dcmd->msg; + msg = container_of(&dcmd->msg, struct glink_msg, hdr); cmd = le16_to_cpu(msg->cmd); param1 = le16_to_cpu(msg->param1); param2 = le32_to_cpu(msg->param2); switch (cmd) { - case RPM_CMD_VERSION: + case GLINK_CMD_VERSION: qcom_glink_receive_version(glink, param1, param2); break; - case RPM_CMD_VERSION_ACK: + case GLINK_CMD_VERSION_ACK: qcom_glink_receive_version_ack(glink, param1, param2); break; - case RPM_CMD_OPEN: + case GLINK_CMD_OPEN: qcom_glink_rx_open(glink, param1, msg->data); break; - case RPM_CMD_CLOSE: + case GLINK_CMD_CLOSE: qcom_glink_rx_close(glink, param1); break; - case RPM_CMD_CLOSE_ACK: + case GLINK_CMD_CLOSE_ACK: qcom_glink_rx_close_ack(glink, param1); break; - case RPM_CMD_RX_INTENT_REQ: + case GLINK_CMD_RX_INTENT_REQ: qcom_glink_handle_intent_req(glink, param1, param2); break; default: @@ -1674,7 +1842,7 @@ static ssize_t rpmsg_name_show(struct device *dev, if (ret < 0) name = dev->of_node->name; - return snprintf(buf, RPMSG_NAME_SIZE, "%s\n", name); + return sysfs_emit(buf, "%s\n", name); } static DEVICE_ATTR_RO(rpmsg_name); @@ -1691,6 +1859,7 @@ static void qcom_glink_device_release(struct device *dev) /* Release qcom_glink_alloc_channel() reference */ kref_put(&channel->refcount, qcom_glink_channel_release); + kfree(rpdev->driver_override); kfree(rpdev); } @@ -1715,7 +1884,7 @@ static int qcom_glink_create_chrdev(struct qcom_glink *glink) rpdev->dev.parent = glink->dev; rpdev->dev.release = qcom_glink_device_release; - return rpmsg_chrdev_register_device(rpdev); + return rpmsg_ctrldev_register_device(rpdev); } struct qcom_glink *qcom_glink_native_probe(struct device *dev, @@ -1724,7 +1893,6 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, struct qcom_glink_pipe *tx, bool intentless) { - int irq; int ret; struct qcom_glink *glink; @@ -1749,37 +1917,16 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, idr_init(&glink->lcids); idr_init(&glink->rcids); + ret = of_property_read_string(dev->of_node, "label", &glink->label); + if (ret < 0) + glink->label = dev->of_node->name; + glink->dev->groups = qcom_glink_groups; ret = device_add_groups(dev, qcom_glink_groups); if (ret) dev_err(dev, "failed to add groups\n"); - ret = of_property_read_string(dev->of_node, "label", &glink->name); - if (ret < 0) - glink->name = dev->of_node->name; - - glink->mbox_client.dev = dev; - glink->mbox_client.knows_txdone = true; - glink->mbox_chan = mbox_request_channel(&glink->mbox_client, 0); - if (IS_ERR(glink->mbox_chan)) { - if (PTR_ERR(glink->mbox_chan) != -EPROBE_DEFER) - dev_err(dev, "failed to acquire IPC channel\n"); - return ERR_CAST(glink->mbox_chan); - } - - irq = of_irq_get(dev->of_node, 0); - ret = devm_request_irq(dev, irq, - qcom_glink_native_intr, - IRQF_NO_SUSPEND | IRQF_SHARED, - "glink-native", glink); - if (ret) { - dev_err(dev, "failed to request IRQ\n"); - return ERR_PTR(ret); - } - - glink->irq = irq; - ret = qcom_glink_send_version(glink); if (ret) return ERR_PTR(ret); @@ -1802,12 +1949,24 @@ static int qcom_glink_remove_device(struct device *dev, void *data) void qcom_glink_native_remove(struct qcom_glink *glink) { struct glink_channel *channel; + unsigned long flags; int cid; int ret; - disable_irq(glink->irq); qcom_glink_cancel_rx_work(glink); + /* Fail all attempts at sending messages */ + spin_lock_irqsave(&glink->tx_lock, flags); + glink->abort_tx = true; + wake_up_all(&glink->tx_avail_notify); + spin_unlock_irqrestore(&glink->tx_lock, flags); + + /* Abort any senders waiting for intent requests */ + spin_lock_irqsave(&glink->idr_lock, flags); + idr_for_each_entry(&glink->lcids, channel, cid) + qcom_glink_intent_req_abort(channel); + spin_unlock_irqrestore(&glink->idr_lock, flags); + ret = device_for_each_child(glink->dev, NULL, qcom_glink_remove_device); if (ret) dev_warn(glink->dev, "Can't remove GLINK devices: %d\n", ret); @@ -1822,15 +1981,8 @@ void qcom_glink_native_remove(struct qcom_glink *glink) idr_destroy(&glink->lcids); idr_destroy(&glink->rcids); - mbox_free_channel(glink->mbox_chan); } EXPORT_SYMBOL_GPL(qcom_glink_native_remove); -void qcom_glink_native_unregister(struct qcom_glink *glink) -{ - device_unregister(glink->dev); -} -EXPORT_SYMBOL_GPL(qcom_glink_native_unregister); - MODULE_DESCRIPTION("Qualcomm GLINK driver"); MODULE_LICENSE("GPL v2"); |
