@@ -17,6 +17,7 @@
#include <linux/rpmsg.h>
#include <linux/sizes.h>
#include <linux/slab.h>
+#include <linux/termios.h>
#include <linux/workqueue.h>
#include <linux/mailbox_client.h>
@@ -203,9 +204,15 @@ static const struct rpmsg_endpoint_ops glink_endpoint_ops;
#define RPM_CMD_TX_DATA_CONT 12
#define RPM_CMD_READ_NOTIF 13
#define RPM_CMD_RX_DONE_W_REUSE 14
+#define RPM_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,
@@ -1001,6 +1008,58 @@ 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.
+ * @enable: True/False - enable or disable flow control
+ * @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 enable, u32 dst)
+{
+ struct glink_channel *channel = to_glink_channel(ept);
+ struct qcom_glink *glink = channel->glink;
+ struct glink_msg msg;
+ u32 sigs = 0;
+
+ if (enable)
+ sigs |= NATIVE_DTR_SIG | NATIVE_RTS_SIG;
+
+ msg.cmd = cpu_to_le16(RPM_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 int qcom_glink_handle_signals(struct qcom_glink *glink,
+ unsigned int rcid, unsigned int sigs)
+{
+ struct glink_channel *channel;
+ unsigned long flags;
+ bool enable = false;
+
+ 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");
+ return -EINVAL;
+ }
+
+ if (!channel->ept.flow_cb)
+ return 0;
+
+ if (sigs & (NATIVE_DSR_SIG | NATIVE_CTS_SIG))
+ enable = true;
+
+ channel->ept.flow_cb(channel->ept.rpdev, channel->ept.priv, enable);
+
+ return 0;
+}
+
static irqreturn_t qcom_glink_native_intr(int irq, void *data)
{
struct qcom_glink *glink = data;
@@ -1065,6 +1124,10 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data)
qcom_glink_handle_intent_req_ack(glink, param1, param2);
qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8));
break;
+ case RPM_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;
@@ -1440,6 +1503,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)