Implement circular buffer protocol over receive dma
buffer. Add extension to the mei message header that holds
length of the buffer on the dma buffer.

Signed-off-by: Tomas Winkler <tomas.wink...@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usys...@intel.com>
---
 drivers/misc/mei/client.c    |  2 +-
 drivers/misc/mei/dma-ring.c  | 63 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/misc/mei/hw.h        |  4 +++
 drivers/misc/mei/init.c      |  2 +-
 drivers/misc/mei/interrupt.c | 41 +++++++++++++++++++---------
 drivers/misc/mei/mei_dev.h   |  3 ++-
 6 files changed, 100 insertions(+), 15 deletions(-)

diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 4ab6251d418e..859a5e1469c9 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -478,7 +478,7 @@ struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t 
length,
        if (length == 0)
                return cb;
 
-       cb->buf.data = kmalloc(length, GFP_KERNEL);
+       cb->buf.data = kmalloc(roundup(length, MEI_SLOT_SIZE), GFP_KERNEL);
        if (!cb->buf.data) {
                mei_io_cb_free(cb);
                return NULL;
diff --git a/drivers/misc/mei/dma-ring.c b/drivers/misc/mei/dma-ring.c
index 70901319534e..2d2ce6f4ef3b 100644
--- a/drivers/misc/mei/dma-ring.c
+++ b/drivers/misc/mei/dma-ring.c
@@ -122,3 +122,66 @@ void mei_dma_ring_reset(struct mei_device *dev)
 
        memset(ctrl, 0, sizeof(*ctrl));
 }
+
+/**
+ * mei_dma_ring_reset - copy from dma ring into buffer
+ *
+ * @dev: mei device
+ * @buf: data buffer
+ * @offset: offset in slots.
+ * @n: number of slots to copy.
+ */
+static size_t mei_dma_copy_from(struct mei_device *dev, unsigned char *buf,
+                               u32 offset, u32 n)
+{
+       unsigned char *dbuf = dev->dr_dscr[DMA_DSCR_DEVICE].vaddr;
+
+       size_t b_offset = offset << 2;
+       size_t b_n = n << 2;
+
+       memcpy(buf, dbuf + b_offset, b_n);
+
+       return b_n;
+}
+
+/**
+ * mei_dma_ring_read - read data from the ring
+ *
+ * @dev: mei device
+ * @buf: buffer to read into: may be NULL in case of droping the data.
+ * @len: length to read.
+ */
+void mei_dma_ring_read(struct mei_device *dev, unsigned char *buf, u32 len)
+{
+       struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
+       u32 dbuf_depth;
+       u32 rd_idx, rem, slots;
+
+       if (WARN_ON(!ctrl))
+               return;
+
+       dev_dbg(dev->dev, "reading from dma %u bytes\n", len);
+
+       if (!len)
+               return;
+
+       dbuf_depth = dev->dr_dscr[DMA_DSCR_DEVICE].size >> 2;
+       rd_idx = READ_ONCE(ctrl->dbuf_rd_idx) & (dbuf_depth - 1);
+       slots = mei_data2slots(len);
+
+       /* if buf is NULL we drop the packet by advancing the pointer.*/
+       if (!buf)
+               goto out;
+
+       if (rd_idx + slots > dbuf_depth) {
+               buf += mei_dma_copy_from(dev, buf, rd_idx, dbuf_depth - rd_idx);
+               rem = slots - (dbuf_depth - rd_idx);
+               rd_idx = 0;
+       } else {
+               rem = slots;
+       }
+
+       mei_dma_copy_from(dev, buf, rd_idx, rem);
+out:
+       WRITE_ONCE(ctrl->dbuf_rd_idx, ctrl->dbuf_rd_idx + slots);
+}
diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h
index 4f09bbcdbc2a..acbccb8dba34 100644
--- a/drivers/misc/mei/hw.h
+++ b/drivers/misc/mei/hw.h
@@ -206,6 +206,7 @@ enum  mei_cl_disconnect_status {
  * @dma_ring: message is on dma ring
  * @internal: message is internal
  * @msg_complete: last packet of the message
+ * @extension: extension of the header
  */
 struct mei_msg_hdr {
        u32 me_addr:8;
@@ -215,8 +216,11 @@ struct mei_msg_hdr {
        u32 dma_ring:1;
        u32 internal:1;
        u32 msg_complete:1;
+       u32 extension[0];
 } __packed;
 
+#define MEI_MSG_HDR_MAX 2
+
 struct mei_bus_message {
        u8 hbm_cmd;
        u8 data[0];
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index 4888ebc076b7..eb026e2a0537 100644
--- a/drivers/misc/mei/init.c
+++ b/drivers/misc/mei/init.c
@@ -151,7 +151,7 @@ int mei_reset(struct mei_device *dev)
 
        mei_hbm_reset(dev);
 
-       dev->rd_msg_hdr = 0;
+       memset(dev->rd_msg_hdr, 0, sizeof(dev->rd_msg_hdr));
 
        if (ret) {
                dev_err(dev->dev, "hw_reset failed ret = %d\n", ret);
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c
index 5a661cbdf2ae..055c2d89b310 100644
--- a/drivers/misc/mei/interrupt.c
+++ b/drivers/misc/mei/interrupt.c
@@ -75,6 +75,8 @@ static inline int mei_cl_hbm_equal(struct mei_cl *cl,
  */
 static void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr 
*hdr)
 {
+       if (hdr->dma_ring)
+               mei_dma_ring_read(dev, NULL, hdr->extension[0]);
        /*
         * no need to check for size as it is guarantied
         * that length fits into rd_msg_buf
@@ -100,6 +102,7 @@ static int mei_cl_irq_read_msg(struct mei_cl *cl,
        struct mei_device *dev = cl->dev;
        struct mei_cl_cb *cb;
        size_t buf_sz;
+       u32 length;
 
        cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list);
        if (!cb) {
@@ -119,25 +122,31 @@ static int mei_cl_irq_read_msg(struct mei_cl *cl,
                goto discard;
        }
 
-       buf_sz = mei_hdr->length + cb->buf_idx;
+       length = mei_hdr->dma_ring ? mei_hdr->extension[0] : mei_hdr->length;
+
+       buf_sz = length + cb->buf_idx;
        /* catch for integer overflow */
        if (buf_sz < cb->buf_idx) {
                cl_err(dev, cl, "message is too big len %d idx %zu\n",
-                      mei_hdr->length, cb->buf_idx);
+                      length, cb->buf_idx);
                cb->status = -EMSGSIZE;
                goto discard;
        }
 
        if (cb->buf.size < buf_sz) {
                cl_dbg(dev, cl, "message overflow. size %zu len %d idx %zu\n",
-                       cb->buf.size, mei_hdr->length, cb->buf_idx);
+                       cb->buf.size, length, cb->buf_idx);
                cb->status = -EMSGSIZE;
                goto discard;
        }
 
+       if (mei_hdr->dma_ring)
+               mei_dma_ring_read(dev, cb->buf.data + cb->buf_idx, length);
+
+       /*  for DMA read 0 length to generate an interrupt to the device */
        mei_read_slots(dev, cb->buf.data + cb->buf_idx, mei_hdr->length);
 
-       cb->buf_idx += mei_hdr->length;
+       cb->buf_idx += length;
 
        if (mei_hdr->msg_complete) {
                cl_dbg(dev, cl, "completed read length = %zu\n", cb->buf_idx);
@@ -247,6 +256,9 @@ static inline int hdr_is_valid(u32 msg_hdr)
        if (!msg_hdr || mei_hdr->reserved)
                return -EBADMSG;
 
+       if (mei_hdr->dma_ring && mei_hdr->length != MEI_SLOT_SIZE)
+               return -EBADMSG;
+
        return 0;
 }
 
@@ -267,20 +279,20 @@ int mei_irq_read_handler(struct mei_device *dev,
        struct mei_cl *cl;
        int ret;
 
-       if (!dev->rd_msg_hdr) {
-               dev->rd_msg_hdr = mei_read_hdr(dev);
+       if (!dev->rd_msg_hdr[0]) {
+               dev->rd_msg_hdr[0] = mei_read_hdr(dev);
                (*slots)--;
                dev_dbg(dev->dev, "slots =%08x.\n", *slots);
 
-               ret = hdr_is_valid(dev->rd_msg_hdr);
+               ret = hdr_is_valid(dev->rd_msg_hdr[0]);
                if (ret) {
                        dev_err(dev->dev, "corrupted message header 0x%08X\n",
-                               dev->rd_msg_hdr);
+                               dev->rd_msg_hdr[0]);
                        goto end;
                }
        }
 
-       mei_hdr = (struct mei_msg_hdr *)&dev->rd_msg_hdr;
+       mei_hdr = (struct mei_msg_hdr *)dev->rd_msg_hdr;
        dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
 
        if (mei_slots2data(*slots) < mei_hdr->length) {
@@ -291,6 +303,12 @@ int mei_irq_read_handler(struct mei_device *dev,
                goto end;
        }
 
+       if (mei_hdr->dma_ring) {
+               dev->rd_msg_hdr[1] = mei_read_hdr(dev);
+               (*slots)--;
+               mei_hdr->length = 0;
+       }
+
        /*  HBM message */
        if (hdr_is_hbm(mei_hdr)) {
                ret = mei_hbm_dispatch(dev, mei_hdr);
@@ -324,7 +342,7 @@ int mei_irq_read_handler(struct mei_device *dev,
                        goto reset_slots;
                }
                dev_err(dev->dev, "no destination client found 0x%08X\n",
-                               dev->rd_msg_hdr);
+                               dev->rd_msg_hdr[0]);
                ret = -EBADMSG;
                goto end;
        }
@@ -334,9 +352,8 @@ int mei_irq_read_handler(struct mei_device *dev,
 
 reset_slots:
        /* reset the number of slots and header */
+       memset(dev->rd_msg_hdr, 0, sizeof(dev->rd_msg_hdr));
        *slots = mei_count_full_read_slots(dev);
-       dev->rd_msg_hdr = 0;
-
        if (*slots == -EOVERFLOW) {
                /* overflow - reset */
                dev_err(dev->dev, "resetting due to slots overflow.\n");
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index 033b5eff8e59..bfd181fbd90c 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -497,7 +497,7 @@ struct mei_device {
 #endif /* CONFIG_PM */
 
        unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE];
-       u32 rd_msg_hdr;
+       u32 rd_msg_hdr[MEI_MSG_HDR_MAX];
 
        /* write buffer */
        bool hbuf_is_ready;
@@ -598,6 +598,7 @@ int mei_dmam_ring_alloc(struct mei_device *dev);
 void mei_dmam_ring_free(struct mei_device *dev);
 bool mei_dma_ring_is_allocated(struct mei_device *dev);
 void mei_dma_ring_reset(struct mei_device *dev);
+void mei_dma_ring_read(struct mei_device *dev, unsigned char *buf, u32 len);
 
 /*
  *  MEI interrupt functions prototype
-- 
2.14.4

Reply via email to