diff options
-rw-r--r-- | drivers/rpmsg/qcom_glink_native.c | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 1beb40a1d3df..2db82f67e689 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -200,9 +200,15 @@ static const struct rpmsg_endpoint_ops glink_endpoint_ops; #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, @@ -1025,6 +1031,50 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) return 0; } +/** + * 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); + + 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; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, rcid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) + dev_err(glink->dev, "signal for non-existing channel\n"); + + 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 glink_msg msg; @@ -1086,6 +1136,10 @@ void qcom_glink_native_rx(struct qcom_glink *glink) 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); + qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); + break; default: dev_err(glink->dev, "unhandled rx cmd: %d\n", cmd); ret = -EINVAL; @@ -1446,6 +1500,7 @@ 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) |