mei: dma ring: implement rx circular buffer logic
authorTomas Winkler <tomas.winkler@intel.com>
Thu, 22 Nov 2018 11:11:39 +0000 (13:11 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 26 Nov 2018 15:59:48 +0000 (16:59 +0100)
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.winkler@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/misc/mei/client.c
drivers/misc/mei/dma-ring.c
drivers/misc/mei/hw.h
drivers/misc/mei/init.c
drivers/misc/mei/interrupt.c
drivers/misc/mei/mei_dev.h

index 1fe9426ce48becfcf29d12fd39a850e9ab004463..5d15501af31329df09a9073ac12371670a702c62 100644 (file)
@@ -461,7 +461,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;
index b73d5ab5407055431df63e1df77e9ac733d8cef4..a0c40925049a2287174c5ca69ad6aea02724dedf 100644 (file)
@@ -117,3 +117,64 @@ void mei_dma_ring_reset(struct mei_device *dev)
 
        memset(ctrl, 0, sizeof(*ctrl));
 }
+
+/**
+ * mei_dma_copy_from() - 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);
+}
index 4f09bbcdbc2a7fca68d5c95339f336f323660c83..acbccb8dba34c5dbdd9f1b8374fce0684c44d77c 100644 (file)
@@ -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];
index 4888ebc076b74b5f8ee9cdf3d956e73dd2929fdb..eb026e2a0537a07a180575843c37a6c9c4baeebd 100644 (file)
@@ -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);
index 5a661cbdf2aefa7c8ad5a671cbcd96ff042017d1..055c2d89b310819521bd83a68b2de30f7524da7d 100644 (file)
@@ -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");
index 033b5eff8e594721e573aca2ba60707d098e8a64..bfd181fbd90cea61d8118195c6185044f9b6586c 100644 (file)
@@ -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