summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/rpmsg/qcom_glink_native.c167
-rw-r--r--drivers/rpmsg/qcom_glink_native.h3
-rw-r--r--drivers/rpmsg/qcom_glink_rpm.c3
-rw-r--r--drivers/rpmsg/qcom_glink_smem.c5
4 files changed, 173 insertions, 5 deletions
diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c
index f3edf971e8c5..aea8f5955e34 100644
--- a/drivers/rpmsg/qcom_glink_native.c
+++ b/drivers/rpmsg/qcom_glink_native.c
@@ -60,6 +60,26 @@ struct glink_defer_cmd {
};
/**
+ * struct glink_core_rx_intent - RX intent
+ * RX intent
+ *
+ * data: pointer to the data (may be NULL for zero-copy)
+ * id: remote or local intent ID
+ * size: size of the original intent (do not modify)
+ * reuse: To mark if the intent can be reused after first use
+ * in_use: To mark if intent is already in use for the channel
+ * offset: next write offset (initially 0)
+ */
+struct glink_core_rx_intent {
+ void *data;
+ u32 id;
+ size_t size;
+ bool reuse;
+ bool in_use;
+ u32 offset;
+};
+
+/**
* struct qcom_glink - driver context, relates to one remote subsystem
* @dev: reference to the associated struct device
* @mbox_client: mailbox client
@@ -117,6 +137,8 @@ enum {
* @name: unique channel name/identifier
* @lcid: channel id, in local space
* @rcid: channel id, in remote space
+ * @intent_lock: lock for protection of @liids
+ * @liids: idr of all local intents
* @buf: receive buffer, for gathering fragments
* @buf_offset: write offset in @buf
* @buf_size: size of current @buf
@@ -137,6 +159,9 @@ struct glink_channel {
unsigned int lcid;
unsigned int rcid;
+ spinlock_t intent_lock;
+ struct idr liids;
+
void *buf;
int buf_offset;
int buf_size;
@@ -154,6 +179,9 @@ static const struct rpmsg_endpoint_ops glink_endpoint_ops;
#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_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
@@ -172,12 +200,14 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink,
/* Setup glink internal glink_channel data */
spin_lock_init(&channel->recv_lock);
+ spin_lock_init(&channel->intent_lock);
channel->glink = glink;
channel->name = kstrdup(name, GFP_KERNEL);
init_completion(&channel->open_req);
init_completion(&channel->open_ack);
+ idr_init(&channel->liids);
kref_init(&channel->refcount);
return channel;
@@ -187,6 +217,11 @@ static void qcom_glink_channel_release(struct kref *ref)
{
struct glink_channel *channel = container_of(ref, struct glink_channel,
refcount);
+ unsigned long flags;
+
+ spin_lock_irqsave(&channel->intent_lock, flags);
+ idr_destroy(&channel->liids);
+ spin_unlock_irqrestore(&channel->intent_lock, flags);
kfree(channel->name);
kfree(channel);
@@ -424,6 +459,130 @@ static void qcom_glink_receive_version_ack(struct qcom_glink *glink,
}
}
+/**
+ * qcom_glink_send_intent_req_ack() - convert an rx intent request ack cmd to
+ wire format and transmit
+ * @glink: The transport to transmit on.
+ * @channel: The glink channel
+ * @granted: The request response to encode.
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int qcom_glink_send_intent_req_ack(struct qcom_glink *glink,
+ struct glink_channel *channel,
+ bool granted)
+{
+ struct glink_msg msg;
+
+ msg.cmd = cpu_to_le16(RPM_CMD_RX_INTENT_REQ_ACK);
+ msg.param1 = cpu_to_le16(channel->lcid);
+ msg.param2 = cpu_to_le32(granted);
+
+ qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
+
+ return 0;
+}
+
+/**
+ * qcom_glink_advertise_intent - convert an rx intent cmd to wire format and
+ * transmit
+ * @glink: The transport to transmit on.
+ * @channel: The local channel
+ * @size: The intent to pass on to remote.
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int qcom_glink_advertise_intent(struct qcom_glink *glink,
+ struct glink_channel *channel,
+ struct glink_core_rx_intent *intent)
+{
+ struct command {
+ u16 id;
+ u16 lcid;
+ u32 count;
+ u32 size;
+ u32 liid;
+ } __packed;
+ struct command cmd;
+
+ cmd.id = cpu_to_le16(RPM_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);
+
+ qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true);
+
+ return 0;
+}
+
+static struct glink_core_rx_intent *
+qcom_glink_alloc_intent(struct qcom_glink *glink,
+ struct glink_channel *channel,
+ size_t size,
+ bool reuseable)
+{
+ struct glink_core_rx_intent *intent;
+ int ret;
+ unsigned long flags;
+
+ intent = kzalloc(sizeof(*intent), GFP_KERNEL);
+
+ if (!intent)
+ return NULL;
+
+ intent->data = kzalloc(size, GFP_KERNEL);
+ if (!intent->data)
+ return NULL;
+
+ spin_lock_irqsave(&channel->intent_lock, flags);
+ ret = idr_alloc_cyclic(&channel->liids, intent, 1, -1, GFP_ATOMIC);
+ if (ret < 0) {
+ spin_unlock_irqrestore(&channel->intent_lock, flags);
+ return NULL;
+ }
+ spin_unlock_irqrestore(&channel->intent_lock, flags);
+
+ intent->id = ret;
+ intent->size = size;
+ intent->reuse = reuseable;
+
+ return intent;
+}
+
+/**
+ * qcom_glink_handle_intent_req() - Receive a request for rx_intent
+ * from remote side
+ * if_ptr: Pointer to the transport interface
+ * rcid: Remote channel ID
+ * size: size of the intent
+ *
+ * The function searches for the local channel to which the request for
+ * rx_intent has arrived and allocates and notifies the remote back
+ */
+static void qcom_glink_handle_intent_req(struct qcom_glink *glink,
+ u32 cid, size_t size)
+{
+ struct glink_core_rx_intent *intent;
+ struct glink_channel *channel;
+ unsigned long flags;
+
+ spin_lock_irqsave(&glink->idr_lock, flags);
+ channel = idr_find(&glink->rcids, cid);
+ spin_unlock_irqrestore(&glink->idr_lock, flags);
+
+ if (!channel) {
+ pr_err("%s channel not found for cid %d\n", __func__, cid);
+ return;
+ }
+
+ intent = qcom_glink_alloc_intent(glink, channel, size, false);
+ if (intent)
+ qcom_glink_advertise_intent(glink, channel, intent);
+
+ qcom_glink_send_intent_req_ack(glink, channel, !!intent);
+}
+
static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra)
{
struct glink_defer_cmd *dcmd;
@@ -586,6 +745,7 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data)
case RPM_CMD_VERSION_ACK:
case RPM_CMD_CLOSE:
case RPM_CMD_CLOSE_ACK:
+ case RPM_CMD_RX_INTENT_REQ:
ret = qcom_glink_rx_defer(glink, 0);
break;
case RPM_CMD_OPEN_ACK:
@@ -1002,6 +1162,9 @@ static void qcom_glink_work(struct work_struct *work)
case RPM_CMD_CLOSE_ACK:
qcom_glink_rx_close_ack(glink, param1);
break;
+ case RPM_CMD_RX_INTENT_REQ:
+ qcom_glink_handle_intent_req(glink, param1, param2);
+ break;
default:
WARN(1, "Unknown defer object %d\n", cmd);
break;
@@ -1014,7 +1177,8 @@ static void qcom_glink_work(struct work_struct *work)
struct qcom_glink *qcom_glink_native_probe(struct device *dev,
unsigned long features,
struct qcom_glink_pipe *rx,
- struct qcom_glink_pipe *tx)
+ struct qcom_glink_pipe *tx,
+ bool intentless)
{
int irq;
int ret;
@@ -1029,6 +1193,7 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev,
glink->rx_pipe = rx;
glink->features = features;
+ glink->intentless = intentless;
mutex_init(&glink->tx_lock);
spin_lock_init(&glink->rx_lock);
diff --git a/drivers/rpmsg/qcom_glink_native.h b/drivers/rpmsg/qcom_glink_native.h
index 456ec79811f7..92e088144128 100644
--- a/drivers/rpmsg/qcom_glink_native.h
+++ b/drivers/rpmsg/qcom_glink_native.h
@@ -37,7 +37,8 @@ struct qcom_glink;
struct qcom_glink *qcom_glink_native_probe(struct device *dev,
unsigned long features,
struct qcom_glink_pipe *rx,
- struct qcom_glink_pipe *tx);
+ struct qcom_glink_pipe *tx,
+ bool intentless);
void qcom_glink_native_remove(struct qcom_glink *glink);
void qcom_glink_native_unregister(struct qcom_glink *glink);
diff --git a/drivers/rpmsg/qcom_glink_rpm.c b/drivers/rpmsg/qcom_glink_rpm.c
index 7d039cd344cb..5a86e089a977 100644
--- a/drivers/rpmsg/qcom_glink_rpm.c
+++ b/drivers/rpmsg/qcom_glink_rpm.c
@@ -305,7 +305,8 @@ static int glink_rpm_probe(struct platform_device *pdev)
glink = qcom_glink_native_probe(&pdev->dev,
0,
&rx_pipe->native,
- &tx_pipe->native);
+ &tx_pipe->native,
+ true);
if (IS_ERR(glink))
return PTR_ERR(glink);
diff --git a/drivers/rpmsg/qcom_glink_smem.c b/drivers/rpmsg/qcom_glink_smem.c
index 8ad641a08cd8..9d73ced075e7 100644
--- a/drivers/rpmsg/qcom_glink_smem.c
+++ b/drivers/rpmsg/qcom_glink_smem.c
@@ -284,8 +284,9 @@ struct qcom_glink *qcom_glink_smem_register(struct device *parent,
*tx_pipe->head = 0;
glink = qcom_glink_native_probe(dev,
- 0,
- &rx_pipe->native, &tx_pipe->native);
+ GLINK_FEATURE_INTENT_REUSE,
+ &rx_pipe->native, &tx_pipe->native,
+ false);
if (IS_ERR(glink)) {
ret = PTR_ERR(glink);
goto err_put_dev;