2018-11-23 12:19:57

by Tomas Winkler

[permalink] [raw]
Subject: [char-misc-next v2 0/7] mei: Add DMA ring

This is a resend of the second part for the mei dma ring,
that was left out.

This series adds an alternative method for
transferring data between the mei driver and the device
via a DMA ring. The DMA ring allows transferring
data in bigger chunks, up to 128K, than the HW ring 512B.
The actual sizes depend on particular MEI generations.
The HW ring is faster for packets that fits
into the HW ring while a packet that would require
fragmentation is faster to send via the DMA ring.

V2: 1. Use single license for the new dma-ring.c file.
2. Adjust kdoc to new requirements:
https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html

Alexander Usyskin (2):
mei: bump hbm version to 2.1
mei: me: mark CNP devices as having dma support

Tomas Winkler (5):
mei: dma ring buffers allocation
mei: hbm: setup dma ring
mei: hw: add dma ring control block
mei: dma ring: implement rx circular buffer logic
mei: dma ring: implement transmit flow

drivers/misc/mei/Makefile | 1 +
drivers/misc/mei/client.c | 66 +++++++--
drivers/misc/mei/dma-ring.c | 269 +++++++++++++++++++++++++++++++++++
drivers/misc/mei/hbm.c | 88 +++++++++++-
drivers/misc/mei/hbm.h | 2 +
drivers/misc/mei/hw-me.c | 6 +
drivers/misc/mei/hw.h | 29 +++-
drivers/misc/mei/init.c | 2 +-
drivers/misc/mei/interrupt.c | 41 ++++--
drivers/misc/mei/mei_dev.h | 26 +++-
drivers/misc/mei/pci-me.c | 4 +-
11 files changed, 500 insertions(+), 34 deletions(-)
create mode 100644 drivers/misc/mei/dma-ring.c

--
2.17.2



2018-11-23 09:55:42

by Tomas Winkler

[permalink] [raw]
Subject: [char-misc-next v2 2/7] mei: hbm: setup dma ring

DMA ring is allocated upon HBM handshake and the ring parameters are set
via dedicated HBM_DMA_SETUP request command. The firmware will perform
its setup and respond with a status. On failure the DMA buffers are
released.

Signed-off-by: Tomas Winkler <[email protected]>
Signed-off-by: Alexander Usyskin <[email protected]>
---
drivers/misc/mei/hbm.c | 86 ++++++++++++++++++++++++++++++++++++++++--
drivers/misc/mei/hbm.h | 2 +
2 files changed, 85 insertions(+), 3 deletions(-)

diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c
index fe67381b34c3..b458c5612245 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -65,6 +65,7 @@ const char *mei_hbm_state_str(enum mei_hbm_state state)
MEI_HBM_STATE(IDLE);
MEI_HBM_STATE(STARTING);
MEI_HBM_STATE(STARTED);
+ MEI_HBM_STATE(DR_SETUP);
MEI_HBM_STATE(ENUM_CLIENTS);
MEI_HBM_STATE(CLIENT_PROPERTIES);
MEI_HBM_STATE(STOPPED);
@@ -295,6 +296,46 @@ int mei_hbm_start_req(struct mei_device *dev)
return 0;
}

+/**
+ * mei_hbm_dma_setup_req() - setup DMA request
+ * @dev: the device structure
+ *
+ * Return: 0 on success and < 0 on failure
+ */
+static int mei_hbm_dma_setup_req(struct mei_device *dev)
+{
+ struct mei_msg_hdr mei_hdr;
+ struct hbm_dma_setup_request req;
+ const size_t len = sizeof(struct hbm_dma_setup_request);
+ unsigned int i;
+ int ret;
+
+ mei_hbm_hdr(&mei_hdr, len);
+
+ memset(&req, 0, len);
+ req.hbm_cmd = MEI_HBM_DMA_SETUP_REQ_CMD;
+ for (i = 0; i < DMA_DSCR_NUM; i++) {
+ phys_addr_t paddr;
+
+ paddr = dev->dr_dscr[i].daddr;
+ req.dma_dscr[i].addr_hi = upper_32_bits(paddr);
+ req.dma_dscr[i].addr_lo = lower_32_bits(paddr);
+ req.dma_dscr[i].size = dev->dr_dscr[i].size;
+ }
+
+ ret = mei_hbm_write_message(dev, &mei_hdr, &req);
+ if (ret) {
+ dev_err(dev->dev, "dma setup request write failed: ret = %d.\n",
+ ret);
+ return ret;
+ }
+
+ dev->hbm_state = MEI_HBM_DR_SETUP;
+ dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
+ mei_schedule_stall_timer(dev);
+ return 0;
+}
+
/**
* mei_hbm_enum_clients_req - sends enumeration client request message.
*
@@ -1044,6 +1085,7 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
struct hbm_host_version_response *version_res;
struct hbm_props_response *props_res;
struct hbm_host_enum_response *enum_res;
+ struct hbm_dma_setup_response *dma_setup_res;
struct hbm_add_client_request *add_cl_req;
int ret;

@@ -1108,14 +1150,52 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
return -EPROTO;
}

- if (mei_hbm_enum_clients_req(dev)) {
- dev_err(dev->dev, "hbm: start: failed to send enumeration request\n");
- return -EIO;
+ if (dev->hbm_f_dr_supported) {
+ if (mei_dmam_ring_alloc(dev))
+ dev_info(dev->dev, "running w/o dma ring\n");
+ if (mei_dma_ring_is_allocated(dev)) {
+ if (mei_hbm_dma_setup_req(dev))
+ return -EIO;
+
+ wake_up(&dev->wait_hbm_start);
+ break;
+ }
}

+ dev->hbm_f_dr_supported = 0;
+ mei_dmam_ring_free(dev);
+
+ if (mei_hbm_enum_clients_req(dev))
+ return -EIO;
+
wake_up(&dev->wait_hbm_start);
break;

+ case MEI_HBM_DMA_SETUP_RES_CMD:
+ dev_dbg(dev->dev, "hbm: dma setup response: message received.\n");
+
+ dev->init_clients_timer = 0;
+
+ if (dev->hbm_state != MEI_HBM_DR_SETUP) {
+ dev_err(dev->dev, "hbm: dma setup response: state mismatch, [%d, %d]\n",
+ dev->dev_state, dev->hbm_state);
+ return -EPROTO;
+ }
+
+ dma_setup_res = (struct hbm_dma_setup_response *)mei_msg;
+
+ if (dma_setup_res->status) {
+ dev_info(dev->dev, "hbm: dma setup response: failure = %d %s\n",
+ dma_setup_res->status,
+ mei_hbm_status_str(dma_setup_res->status));
+ dev->hbm_f_dr_supported = 0;
+ mei_dmam_ring_free(dev);
+ }
+
+ if (mei_hbm_enum_clients_req(dev))
+ return -EIO;
+ break;
+
case CLIENT_CONNECT_RES_CMD:
dev_dbg(dev->dev, "hbm: client connect response: message received.\n");
mei_hbm_cl_res(dev, cl_cmd, MEI_FOP_CONNECT);
diff --git a/drivers/misc/mei/hbm.h b/drivers/misc/mei/hbm.h
index a2025a5083a3..0171a7e79bab 100644
--- a/drivers/misc/mei/hbm.h
+++ b/drivers/misc/mei/hbm.h
@@ -26,6 +26,7 @@ struct mei_cl;
*
* @MEI_HBM_IDLE : protocol not started
* @MEI_HBM_STARTING : start request message was sent
+ * @MEI_HBM_DR_SETUP : dma ring setup request message was sent
* @MEI_HBM_ENUM_CLIENTS : enumeration request was sent
* @MEI_HBM_CLIENT_PROPERTIES : acquiring clients properties
* @MEI_HBM_STARTED : enumeration was completed
@@ -34,6 +35,7 @@ struct mei_cl;
enum mei_hbm_state {
MEI_HBM_IDLE = 0,
MEI_HBM_STARTING,
+ MEI_HBM_DR_SETUP,
MEI_HBM_ENUM_CLIENTS,
MEI_HBM_CLIENT_PROPERTIES,
MEI_HBM_STARTED,
--
2.17.2


2018-11-23 12:04:36

by Tomas Winkler

[permalink] [raw]
Subject: [char-misc-next v2 4/7] mei: dma ring: implement rx circular buffer logic

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 <[email protected]>
Signed-off-by: Alexander Usyskin <[email protected]>
---
drivers/misc/mei/client.c | 2 +-
drivers/misc/mei/dma-ring.c | 61 ++++++++++++++++++++++++++++++++++++
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, 98 insertions(+), 15 deletions(-)

diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 1fe9426ce48b..5d15501af313 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -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;
diff --git a/drivers/misc/mei/dma-ring.c b/drivers/misc/mei/dma-ring.c
index b73d5ab54070..a0c40925049a 100644
--- a/drivers/misc/mei/dma-ring.c
+++ b/drivers/misc/mei/dma-ring.c
@@ -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);
+}
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.17.2


2018-11-23 12:04:54

by Tomas Winkler

[permalink] [raw]
Subject: [char-misc-next v2 5/7] mei: dma ring: implement transmit flow

Implement a circular buffer on allocated system memory. Read and write
indices are stored on the control block which is also shared between the
device and the host.
Two new functions are exported from the DMA module: mei_dma_ring_write,
and mei_dma_ring_empty_slots. The former simply copy a packet on the TX
DMA circular buffer and later, returns the number of empty slots on the
TX DMA circular buffer.

Signed-off-by: Tomas Winkler <[email protected]>
Signed-off-by: Alexander Usyskin <[email protected]>
---
drivers/misc/mei/client.c | 64 ++++++++++++++++++++------
drivers/misc/mei/dma-ring.c | 89 +++++++++++++++++++++++++++++++++++++
drivers/misc/mei/mei_dev.h | 2 +
3 files changed, 142 insertions(+), 13 deletions(-)

diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 5d15501af313..1fc8ea0f519b 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -1558,10 +1558,13 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_msg_hdr mei_hdr;
size_t hdr_len = sizeof(mei_hdr);
size_t len;
- size_t hbuf_len;
+ size_t hbuf_len, dr_len;
int hbuf_slots;
+ u32 dr_slots;
+ u32 dma_len;
int rets;
bool first_chunk;
+ const void *data;

if (WARN_ON(!cl || !cl->dev))
return -ENODEV;
@@ -1582,6 +1585,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
}

len = buf->size - cb->buf_idx;
+ data = buf->data + cb->buf_idx;
hbuf_slots = mei_hbuf_empty_slots(dev);
if (hbuf_slots < 0) {
rets = -EOVERFLOW;
@@ -1589,6 +1593,8 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
}

hbuf_len = mei_slots2data(hbuf_slots);
+ dr_slots = mei_dma_ring_empty_slots(dev);
+ dr_len = mei_slots2data(dr_slots);

mei_msg_hdr_init(&mei_hdr, cb);

@@ -1599,23 +1605,33 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
if (len + hdr_len <= hbuf_len) {
mei_hdr.length = len;
mei_hdr.msg_complete = 1;
+ } else if (dr_slots && hbuf_len >= hdr_len + sizeof(dma_len)) {
+ mei_hdr.dma_ring = 1;
+ if (len > dr_len)
+ len = dr_len;
+ else
+ mei_hdr.msg_complete = 1;
+
+ mei_hdr.length = sizeof(dma_len);
+ dma_len = len;
+ data = &dma_len;
} else if ((u32)hbuf_slots == mei_hbuf_depth(dev)) {
- mei_hdr.length = hbuf_len - hdr_len;
+ len = hbuf_len - hdr_len;
+ mei_hdr.length = len;
} else {
return 0;
}

- cl_dbg(dev, cl, "buf: size = %zu idx = %zu\n",
- cb->buf.size, cb->buf_idx);
+ if (mei_hdr.dma_ring)
+ mei_dma_ring_write(dev, buf->data + cb->buf_idx, len);

- rets = mei_write_message(dev, &mei_hdr, hdr_len,
- buf->data + cb->buf_idx, mei_hdr.length);
+ rets = mei_write_message(dev, &mei_hdr, hdr_len, data, mei_hdr.length);
if (rets)
goto err;

cl->status = 0;
cl->writing_state = MEI_WRITING;
- cb->buf_idx += mei_hdr.length;
+ cb->buf_idx += len;

if (first_chunk) {
if (mei_cl_tx_flow_ctrl_creds_reduce(cl)) {
@@ -1650,11 +1666,13 @@ ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
struct mei_msg_data *buf;
struct mei_msg_hdr mei_hdr;
size_t hdr_len = sizeof(mei_hdr);
- size_t len;
- size_t hbuf_len;
+ size_t len, hbuf_len, dr_len;
int hbuf_slots;
+ u32 dr_slots;
+ u32 dma_len;
ssize_t rets;
bool blocking;
+ const void *data;

if (WARN_ON(!cl || !cl->dev))
return -ENODEV;
@@ -1666,10 +1684,12 @@ ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)

buf = &cb->buf;
len = buf->size;
- blocking = cb->blocking;

cl_dbg(dev, cl, "len=%zd\n", len);

+ blocking = cb->blocking;
+ data = buf->data;
+
rets = pm_runtime_get(dev->dev);
if (rets < 0 && rets != -EINPROGRESS) {
pm_runtime_put_noidle(dev->dev);
@@ -1706,16 +1726,32 @@ ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
}

hbuf_len = mei_slots2data(hbuf_slots);
+ dr_slots = mei_dma_ring_empty_slots(dev);
+ dr_len = mei_slots2data(dr_slots);

if (len + hdr_len <= hbuf_len) {
mei_hdr.length = len;
mei_hdr.msg_complete = 1;
+ } else if (dr_slots && hbuf_len >= hdr_len + sizeof(dma_len)) {
+ mei_hdr.dma_ring = 1;
+ if (len > dr_len)
+ len = dr_len;
+ else
+ mei_hdr.msg_complete = 1;
+
+ mei_hdr.length = sizeof(dma_len);
+ dma_len = len;
+ data = &dma_len;
} else {
- mei_hdr.length = hbuf_len - hdr_len;
+ len = hbuf_len - hdr_len;
+ mei_hdr.length = len;
}

+ if (mei_hdr.dma_ring)
+ mei_dma_ring_write(dev, buf->data, len);
+
rets = mei_write_message(dev, &mei_hdr, hdr_len,
- buf->data, mei_hdr.length);
+ data, mei_hdr.length);
if (rets)
goto err;

@@ -1724,7 +1760,9 @@ ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
goto err;

cl->writing_state = MEI_WRITING;
- cb->buf_idx = mei_hdr.length;
+ cb->buf_idx = len;
+ /* restore return value */
+ len = buf->size;

out:
if (mei_hdr.msg_complete)
diff --git a/drivers/misc/mei/dma-ring.c b/drivers/misc/mei/dma-ring.c
index a0c40925049a..795641b82181 100644
--- a/drivers/misc/mei/dma-ring.c
+++ b/drivers/misc/mei/dma-ring.c
@@ -138,6 +138,26 @@ static size_t mei_dma_copy_from(struct mei_device *dev, unsigned char *buf,
return b_n;
}

+/**
+ * mei_dma_copy_to() - copy to a buffer to the dma ring
+ * @dev: mei device
+ * @buf: data buffer
+ * @offset: offset in slots.
+ * @n: number of slots to copy.
+ */
+static size_t mei_dma_copy_to(struct mei_device *dev, unsigned char *buf,
+ u32 offset, u32 n)
+{
+ unsigned char *hbuf = dev->dr_dscr[DMA_DSCR_HOST].vaddr;
+
+ size_t b_offset = offset << 2;
+ size_t b_n = n << 2;
+
+ memcpy(hbuf + b_offset, buf, b_n);
+
+ return b_n;
+}
+
/**
* mei_dma_ring_read() - read data from the ring
* @dev: mei device
@@ -178,3 +198,72 @@ void mei_dma_ring_read(struct mei_device *dev, unsigned char *buf, u32 len)
out:
WRITE_ONCE(ctrl->dbuf_rd_idx, ctrl->dbuf_rd_idx + slots);
}
+
+static inline u32 mei_dma_ring_hbuf_depth(struct mei_device *dev)
+{
+ return dev->dr_dscr[DMA_DSCR_HOST].size >> 2;
+}
+
+/**
+ * mei_dma_ring_empty_slots() - calaculate number of empty slots in dma ring
+ * @dev: mei_device
+ *
+ * Return: number of empty slots
+ */
+u32 mei_dma_ring_empty_slots(struct mei_device *dev)
+{
+ struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
+ u32 wr_idx, rd_idx, hbuf_depth, empty;
+
+ if (!mei_dma_ring_is_allocated(dev))
+ return 0;
+
+ if (WARN_ON(!ctrl))
+ return 0;
+
+ /* easier to work in slots */
+ hbuf_depth = mei_dma_ring_hbuf_depth(dev);
+ rd_idx = READ_ONCE(ctrl->hbuf_rd_idx);
+ wr_idx = READ_ONCE(ctrl->hbuf_wr_idx);
+
+ if (rd_idx > wr_idx)
+ empty = rd_idx - wr_idx;
+ else
+ empty = hbuf_depth - (wr_idx - rd_idx);
+
+ return empty;
+}
+
+/**
+ * mei_dma_ring_write - write data to dma ring host buffer
+ *
+ * @dev: mei_device
+ * @buf: data will be written
+ * @len: data length
+ */
+void mei_dma_ring_write(struct mei_device *dev, unsigned char *buf, u32 len)
+{
+ struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
+ u32 hbuf_depth;
+ u32 wr_idx, rem, slots;
+
+ if (WARN_ON(!ctrl))
+ return;
+
+ dev_dbg(dev->dev, "writing to dma %u bytes\n", len);
+ hbuf_depth = mei_dma_ring_hbuf_depth(dev);
+ wr_idx = READ_ONCE(ctrl->hbuf_wr_idx) & (hbuf_depth - 1);
+ slots = mei_data2slots(len);
+
+ if (wr_idx + slots > hbuf_depth) {
+ buf += mei_dma_copy_to(dev, buf, wr_idx, hbuf_depth - wr_idx);
+ rem = slots - (hbuf_depth - wr_idx);
+ wr_idx = 0;
+ } else {
+ rem = slots;
+ }
+
+ mei_dma_copy_to(dev, buf, wr_idx, rem);
+
+ WRITE_ONCE(ctrl->hbuf_wr_idx, ctrl->hbuf_wr_idx + slots);
+}
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index bfd181fbd90c..685b78ce30a5 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -599,6 +599,8 @@ 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);
+void mei_dma_ring_write(struct mei_device *dev, unsigned char *buf, u32 len);
+u32 mei_dma_ring_empty_slots(struct mei_device *dev);

/*
* MEI interrupt functions prototype
--
2.17.2


2018-11-23 12:20:42

by Tomas Winkler

[permalink] [raw]
Subject: [char-misc-next v2 1/7] mei: dma ring buffers allocation

Allocate DMA ring buffers from managed coherent memory.

Signed-off-by: Tomas Winkler <[email protected]>
Signed-off-by: Alexander Usyskin <[email protected]>
---
drivers/misc/mei/Makefile | 1 +
drivers/misc/mei/dma-ring.c | 99 +++++++++++++++++++++++++++++++++++++
drivers/misc/mei/hw-me.c | 6 +++
drivers/misc/mei/mei_dev.h | 20 ++++++++
4 files changed, 126 insertions(+)
create mode 100644 drivers/misc/mei/dma-ring.c

diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile
index cd6825afa8e1..d9215fc4e499 100644
--- a/drivers/misc/mei/Makefile
+++ b/drivers/misc/mei/Makefile
@@ -9,6 +9,7 @@ mei-objs += hbm.o
mei-objs += interrupt.o
mei-objs += client.o
mei-objs += main.o
+mei-objs += dma-ring.o
mei-objs += bus.o
mei-objs += bus-fixup.o
mei-$(CONFIG_DEBUG_FS) += debugfs.o
diff --git a/drivers/misc/mei/dma-ring.c b/drivers/misc/mei/dma-ring.c
new file mode 100644
index 000000000000..56f4c0882a20
--- /dev/null
+++ b/drivers/misc/mei/dma-ring.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved.
+ */
+#include <linux/dma-mapping.h>
+#include <linux/mei.h>
+
+#include "mei_dev.h"
+
+/**
+ * mei_dmam_dscr_alloc() - allocate a managed coherent buffer
+ * for the dma descriptor
+ * @dev: mei_device
+ * @dscr: dma descriptor
+ *
+ * Return:
+ * * 0 - on success or zero allocation request
+ * * -EINVAL - if size is not power of 2
+ * * -ENOMEM - of allocation has failed
+ */
+static int mei_dmam_dscr_alloc(struct mei_device *dev,
+ struct mei_dma_dscr *dscr)
+{
+ if (!dscr->size)
+ return 0;
+
+ if (WARN_ON(!is_power_of_2(dscr->size)))
+ return -EINVAL;
+
+ if (dscr->vaddr)
+ return 0;
+
+ dscr->vaddr = dmam_alloc_coherent(dev->dev, dscr->size, &dscr->daddr,
+ GFP_KERNEL);
+ if (!dscr->vaddr)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * mei_dmam_dscr_free() - free a managed coherent buffer
+ * from the dma descriptor
+ * @dev: mei_device
+ * @dscr: dma descriptor
+ */
+static void mei_dmam_dscr_free(struct mei_device *dev,
+ struct mei_dma_dscr *dscr)
+{
+ if (!dscr->vaddr)
+ return;
+
+ dmam_free_coherent(dev->dev, dscr->size, dscr->vaddr, dscr->daddr);
+ dscr->vaddr = NULL;
+}
+
+/**
+ * mei_dmam_ring_free() - free dma ring buffers
+ * @dev: mei device
+ */
+void mei_dmam_ring_free(struct mei_device *dev)
+{
+ int i;
+
+ for (i = 0; i < DMA_DSCR_NUM; i++)
+ mei_dmam_dscr_free(dev, &dev->dr_dscr[i]);
+}
+
+/**
+ * mei_dmam_ring_alloc() - allocate dma ring buffers
+ * @dev: mei device
+ *
+ * Return: -ENOMEM on allocation failure 0 otherwise
+ */
+int mei_dmam_ring_alloc(struct mei_device *dev)
+{
+ int i;
+
+ for (i = 0; i < DMA_DSCR_NUM; i++)
+ if (mei_dmam_dscr_alloc(dev, &dev->dr_dscr[i]))
+ goto err;
+
+ return 0;
+
+err:
+ mei_dmam_ring_free(dev);
+ return -ENOMEM;
+}
+
+/**
+ * mei_dma_ring_is_allocated() - check if dma ring is allocated
+ * @dev: mei device
+ *
+ * Return: true if dma ring is allocated
+ */
+bool mei_dma_ring_is_allocated(struct mei_device *dev)
+{
+ return !!dev->dr_dscr[DMA_DSCR_HOST].vaddr;
+}
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index 0759c3a668de..3fbbadfa2ae1 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -1471,15 +1471,21 @@ struct mei_device *mei_me_dev_init(struct pci_dev *pdev,
{
struct mei_device *dev;
struct mei_me_hw *hw;
+ int i;

dev = devm_kzalloc(&pdev->dev, sizeof(struct mei_device) +
sizeof(struct mei_me_hw), GFP_KERNEL);
if (!dev)
return NULL;
+
hw = to_me_hw(dev);

+ for (i = 0; i < DMA_DSCR_NUM; i++)
+ dev->dr_dscr[i].size = cfg->dma_size[i];
+
mei_device_init(dev, &pdev->dev, &mei_me_hw_ops);
hw->cfg = cfg;
+
return dev;
}

diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index 377397e1b5a5..a6796e3f712b 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -122,6 +122,19 @@ struct mei_msg_data {
unsigned char *data;
};

+/**
+ * struct mei_dma_dscr - dma address descriptor
+ *
+ * @vaddr: dma buffer virtual address
+ * @daddr: dma buffer physical address
+ * @size : dma buffer size
+ */
+struct mei_dma_dscr {
+ void *vaddr;
+ dma_addr_t daddr;
+ size_t size;
+};
+
/* Maximum number of processed FW status registers */
#define MEI_FW_STATUS_MAX 6
/* Minimal buffer for FW status string (8 bytes in dw + space or '\0') */
@@ -409,6 +422,7 @@ struct mei_fw_version {
* @rd_msg_hdr : read message header storage
*
* @hbuf_is_ready : query if the host host/write buffer is ready
+ * @dr_dscr: DMA ring descriptors: TX, RX, and CTRL
*
* @version : HBM protocol version in use
* @hbm_f_pg_supported : hbm feature pgi protocol
@@ -488,6 +502,8 @@ struct mei_device {
/* write buffer */
bool hbuf_is_ready;

+ struct mei_dma_dscr dr_dscr[DMA_DSCR_NUM];
+
struct hbm_version version;
unsigned int hbm_f_pg_supported:1;
unsigned int hbm_f_dc_supported:1;
@@ -578,6 +594,10 @@ int mei_restart(struct mei_device *dev);
void mei_stop(struct mei_device *dev);
void mei_cancel_work(struct mei_device *dev);

+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);
+
/*
* MEI interrupt functions prototype
*/
--
2.17.2


2018-11-23 12:21:06

by Tomas Winkler

[permalink] [raw]
Subject: [char-misc-next v2 3/7] mei: hw: add dma ring control block

The DMA ring control block contains write and read
indices for host and device circular buffers.

Signed-off-by: Tomas Winkler <[email protected]>
Signed-off-by: Alexander Usyskin <[email protected]>
---
drivers/misc/mei/dma-ring.c | 20 ++++++++++++++++++++
drivers/misc/mei/hbm.c | 2 ++
drivers/misc/mei/hw.h | 23 +++++++++++++++++++++++
drivers/misc/mei/mei_dev.h | 1 +
4 files changed, 46 insertions(+)

diff --git a/drivers/misc/mei/dma-ring.c b/drivers/misc/mei/dma-ring.c
index 56f4c0882a20..b73d5ab54070 100644
--- a/drivers/misc/mei/dma-ring.c
+++ b/drivers/misc/mei/dma-ring.c
@@ -97,3 +97,23 @@ bool mei_dma_ring_is_allocated(struct mei_device *dev)
{
return !!dev->dr_dscr[DMA_DSCR_HOST].vaddr;
}
+
+static inline
+struct hbm_dma_ring_ctrl *mei_dma_ring_ctrl(struct mei_device *dev)
+{
+ return (struct hbm_dma_ring_ctrl *)dev->dr_dscr[DMA_DSCR_CTRL].vaddr;
+}
+
+/**
+ * mei_dma_ring_reset() - reset the dma control block
+ * @dev: mei device
+ */
+void mei_dma_ring_reset(struct mei_device *dev)
+{
+ struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
+
+ if (!ctrl)
+ return;
+
+ memset(ctrl, 0, sizeof(*ctrl));
+}
diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c
index b458c5612245..78c26cebf5d4 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -323,6 +323,8 @@ static int mei_hbm_dma_setup_req(struct mei_device *dev)
req.dma_dscr[i].size = dev->dr_dscr[i].size;
}

+ mei_dma_ring_reset(dev);
+
ret = mei_hbm_write_message(dev, &mei_hdr, &req);
if (ret) {
dev_err(dev->dev, "dma setup request write failed: ret = %d.\n",
diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h
index 65655925791a..4f09bbcdbc2a 100644
--- a/drivers/misc/mei/hw.h
+++ b/drivers/misc/mei/hw.h
@@ -512,4 +512,27 @@ struct hbm_dma_setup_response {
u8 reserved[2];
} __packed;

+/**
+ * struct mei_dma_ring_ctrl - dma ring control block
+ *
+ * @hbuf_wr_idx: host circular buffer write index in slots
+ * @reserved1: reserved for alignment
+ * @hbuf_rd_idx: host circular buffer read index in slots
+ * @reserved2: reserved for alignment
+ * @dbuf_wr_idx: device circular buffer write index in slots
+ * @reserved3: reserved for alignment
+ * @dbuf_rd_idx: device circular buffer read index in slots
+ * @reserved4: reserved for alignment
+ */
+struct hbm_dma_ring_ctrl {
+ u32 hbuf_wr_idx;
+ u32 reserved1;
+ u32 hbuf_rd_idx;
+ u32 reserved2;
+ u32 dbuf_wr_idx;
+ u32 reserved3;
+ u32 dbuf_rd_idx;
+ u32 reserved4;
+} __packed;
+
#endif
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index a6796e3f712b..033b5eff8e59 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -597,6 +597,7 @@ void mei_cancel_work(struct mei_device *dev);
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);

/*
* MEI interrupt functions prototype
--
2.17.2


2018-11-23 12:21:12

by Tomas Winkler

[permalink] [raw]
Subject: [char-misc-next v2 6/7] mei: bump hbm version to 2.1

From: Alexander Usyskin <[email protected]>

Bump HBM version to 2.1 to indicate DMA transfer support.

Signed-off-by: Alexander Usyskin <[email protected]>
Signed-off-by: Tomas Winkler <[email protected]>
---
drivers/misc/mei/hw.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h
index acbccb8dba34..2b7f7677f8cc 100644
--- a/drivers/misc/mei/hw.h
+++ b/drivers/misc/mei/hw.h
@@ -35,7 +35,7 @@
/*
* MEI Version
*/
-#define HBM_MINOR_VERSION 0
+#define HBM_MINOR_VERSION 1
#define HBM_MAJOR_VERSION 2

/*
--
2.17.2


2018-11-23 12:21:16

by Tomas Winkler

[permalink] [raw]
Subject: [char-misc-next v2 7/7] mei: me: mark CNP devices as having dma support

From: Alexander Usyskin <[email protected]>

Signed-off-by: Alexander Usyskin <[email protected]>
Signed-off-by: Tomas Winkler <[email protected]>
---
drivers/misc/mei/pci-me.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
index ea4e152270a3..73ace2d59dea 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -98,9 +98,9 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
{MEI_PCI_DEVICE(MEI_DEV_ID_KBP, MEI_ME_PCH8_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, MEI_ME_PCH8_CFG)},

- {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_LP, MEI_ME_PCH8_CFG)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_LP, MEI_ME_PCH12_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_CNP_LP_4, MEI_ME_PCH8_CFG)},
- {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H, MEI_ME_PCH8_CFG)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H, MEI_ME_PCH12_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H_4, MEI_ME_PCH8_CFG)},

/* required last entry */
--
2.17.2