aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>2021-11-29 15:32:42 +0530
committerManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>2022-02-26 13:47:16 +0530
commit9e37682c0971839368244baadb8d53ff0b4cb619 (patch)
treef6ee8506059d12b461cc1069b9e551a27d51a2e5
parentc1a1ffb8600369570dd4853c740da56738f48930 (diff)
bus: mhi: ep: Add support for queueing SKBs to the host
Add support for queueing SKBs to the host over the transfer ring of the relevant channel. The mhi_ep_queue_skb() API will be used by the client networking drivers to queue the SKBs to the host over MHI bus. The host will add ring elements to the transfer ring periodically for the device and the device will write SKBs to the ring elements. If a single SKB doesn't fit in a ring element (TRE), it will be placed in multiple ring elements and the overflow event will be sent for all ring elements except the last one. For the last ring element, the EOT event will be sent indicating the packet boundary. Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
-rw-r--r--drivers/bus/mhi/ep/main.c81
-rw-r--r--include/linux/mhi_ep.h9
2 files changed, 90 insertions, 0 deletions
diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
index 18344525b671..35203d957a9e 100644
--- a/drivers/bus/mhi/ep/main.c
+++ b/drivers/bus/mhi/ep/main.c
@@ -458,6 +458,87 @@ int mhi_ep_process_ch_ring(struct mhi_ep_ring *ring, struct mhi_ring_element *el
return 0;
}
+/* TODO: Handle partially formed TDs */
+int mhi_ep_queue_skb(struct mhi_ep_device *mhi_dev, struct sk_buff *skb)
+{
+ struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
+ struct mhi_ep_chan *mhi_chan = mhi_dev->dl_chan;
+ struct device *dev = &mhi_chan->mhi_dev->dev;
+ u32 buf_remaining, read_offset;
+ struct mhi_ring_element *el;
+ struct mhi_ep_ring *ring;
+ size_t bytes_to_write;
+ enum mhi_ev_ccs code;
+ void *read_from_loc;
+ u64 write_to_loc;
+ u32 tre_len;
+ int ret = 0;
+
+ buf_remaining = skb->len;
+ ring = &mhi_cntrl->mhi_chan[mhi_chan->chan].ring;
+
+ mutex_lock(&mhi_chan->lock);
+
+ do {
+ /* Don't process the transfer ring if the channel is not in RUNNING state */
+ if (mhi_chan->state != MHI_CH_STATE_RUNNING) {
+ dev_err(dev, "Channel not available\n");
+ ret = -ENODEV;
+ goto err_exit;
+ }
+
+ if (mhi_ep_queue_is_empty(mhi_dev, DMA_FROM_DEVICE)) {
+ dev_err(dev, "TRE not available!\n");
+ ret = -EINVAL;
+ goto err_exit;
+ }
+
+ el = &ring->ring_cache[ring->rd_offset];
+ tre_len = MHI_TRE_DATA_GET_LEN(el);
+
+ bytes_to_write = min(buf_remaining, tre_len);
+ read_offset = skb->len - buf_remaining;
+ read_from_loc = skb->data + read_offset;
+ write_to_loc = MHI_TRE_DATA_GET_PTR(el);
+
+ dev_dbg(dev, "Writing %zd bytes to channel (%d)\n", bytes_to_write, ring->ch_id);
+ ret = mhi_cntrl->write_to_host(mhi_cntrl, read_from_loc, write_to_loc,
+ bytes_to_write);
+ if (ret < 0)
+ goto err_exit;
+
+ buf_remaining -= bytes_to_write;
+ /*
+ * For all TREs queued by the host for DL channel, only the EOT flag will be set.
+ * If the packet doesn't fit into a single TRE, send the OVERFLOW event to
+ * the host so that the host can adjust the packet boundary to next TREs. Else send
+ * the EOT event to the host indicating the packet boundary.
+ */
+ if (buf_remaining)
+ code = MHI_EV_CC_OVERFLOW;
+ else
+ code = MHI_EV_CC_EOT;
+
+ ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, bytes_to_write, code);
+ if (ret) {
+ dev_err(dev, "Error sending completion event\n");
+ goto err_exit;
+ }
+
+ mhi_ep_ring_inc_index(ring);
+ } while (buf_remaining);
+
+ mutex_unlock(&mhi_chan->lock);
+
+ return 0;
+
+err_exit:
+ mutex_unlock(&mhi_chan->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mhi_ep_queue_skb);
+
static int mhi_ep_cache_host_cfg(struct mhi_ep_cntrl *mhi_cntrl)
{
size_t cmd_ctx_host_size, ch_ctx_host_size, ev_ctx_host_size;
diff --git a/include/linux/mhi_ep.h b/include/linux/mhi_ep.h
index 74170dad09f6..bd3ffde01f04 100644
--- a/include/linux/mhi_ep.h
+++ b/include/linux/mhi_ep.h
@@ -272,4 +272,13 @@ void mhi_ep_power_down(struct mhi_ep_cntrl *mhi_cntrl);
*/
bool mhi_ep_queue_is_empty(struct mhi_ep_device *mhi_dev, enum dma_data_direction dir);
+/**
+ * mhi_ep_queue_skb - Send SKBs to host over MHI Endpoint
+ * @mhi_dev: Device associated with the DL channel
+ * @skb: SKBs to be queued
+ *
+ * Return: 0 if the SKBs has been sent successfully, a negative error code otherwise.
+ */
+int mhi_ep_queue_skb(struct mhi_ep_device *mhi_dev, struct sk_buff *skb);
+
#endif