summaryrefslogtreecommitdiffstats
path: root/drivers/misc/mei/interrupt.c
diff options
context:
space:
mode:
authorTomas Winkler <tomas.winkler@intel.com>2020-08-18 13:51:38 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2020-08-18 15:44:43 +0200
commit0cd7c01a60f850987e727b8f08683ace9546eac0 (patch)
tree9e8ce820fd189df24abe15334d99a65aa6169c07 /drivers/misc/mei/interrupt.c
parentmei: add vtag support bit in client properties (diff)
downloadlinux-0cd7c01a60f850987e727b8f08683ace9546eac0.tar.xz
linux-0cd7c01a60f850987e727b8f08683ace9546eac0.zip
mei: add support for mei extended header.
Add an extend header beyond existing 4 bytes of the mei message header. The extension is of variable length, starting with meta header that contains the number of headers and the overall size of the extended headers excluding meta header itself followed by TLV list of extended headers. Currently only supported extension is the vtag. From the HW perspective the extended headers is already part of the payload. Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com> Link: https://lore.kernel.org/r/20200818115147.2567012-5-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc/mei/interrupt.c')
-rw-r--r--drivers/misc/mei/interrupt.c113
1 files changed, 99 insertions, 14 deletions
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c
index c70a8c74cc57..326955b04fda 100644
--- a/drivers/misc/mei/interrupt.c
+++ b/drivers/misc/mei/interrupt.c
@@ -61,16 +61,21 @@ static inline int mei_cl_hbm_equal(struct mei_cl *cl,
*
* @dev: mei device
* @hdr: message header
+ * @discard_len: the length of the message to discard (excluding header)
*/
-static void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr)
+static void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr,
+ size_t discard_len)
{
- if (hdr->dma_ring)
- mei_dma_ring_read(dev, NULL, hdr->extension[0]);
+ if (hdr->dma_ring) {
+ mei_dma_ring_read(dev, NULL,
+ hdr->extension[dev->rd_msg_hdr_count - 2]);
+ discard_len = 0;
+ }
/*
* no need to check for size as it is guarantied
* that length fits into rd_msg_buf
*/
- mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
+ mei_read_slots(dev, dev->rd_msg_buf, discard_len);
dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n",
MEI_HDR_PRM(hdr));
}
@@ -80,18 +85,29 @@ static void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr)
*
* @cl: reading client
* @mei_hdr: header of mei client message
+ * @meta: extend meta header
* @cmpl_list: completion list
*
* Return: always 0
*/
static int mei_cl_irq_read_msg(struct mei_cl *cl,
struct mei_msg_hdr *mei_hdr,
+ struct mei_ext_meta_hdr *meta,
struct list_head *cmpl_list)
{
struct mei_device *dev = cl->dev;
struct mei_cl_cb *cb;
+
size_t buf_sz;
u32 length;
+ int ext_len;
+
+ length = mei_hdr->length;
+ ext_len = 0;
+ if (mei_hdr->extended) {
+ ext_len = sizeof(*meta) + mei_slots2data(meta->size);
+ length -= ext_len;
+ }
cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list);
if (!cb) {
@@ -105,13 +121,50 @@ static int mei_cl_irq_read_msg(struct mei_cl *cl,
list_add_tail(&cb->list, &cl->rd_pending);
}
+ if (mei_hdr->extended) {
+ struct mei_ext_hdr *ext;
+ struct mei_ext_hdr *vtag = NULL;
+
+ ext = mei_ext_begin(meta);
+ do {
+ switch (ext->type) {
+ case MEI_EXT_HDR_VTAG:
+ vtag = ext;
+ break;
+ case MEI_EXT_HDR_NONE:
+ fallthrough;
+ default:
+ cb->status = -EPROTO;
+ break;
+ }
+
+ ext = mei_ext_next(ext);
+ } while (!mei_ext_last(meta, ext));
+
+ if (!vtag) {
+ cl_dbg(dev, cl, "vtag not found in extended header.\n");
+ cb->status = -EPROTO;
+ goto discard;
+ }
+
+ cl_dbg(dev, cl, "vtag: %d\n", vtag->ext_payload[0]);
+ if (cb->vtag && cb->vtag != vtag->ext_payload[0]) {
+ cl_err(dev, cl, "mismatched tag: %d != %d\n",
+ cb->vtag, vtag->ext_payload[0]);
+ cb->status = -EPROTO;
+ goto discard;
+ }
+ cb->vtag = vtag->ext_payload[0];
+ }
+
if (!mei_cl_is_connected(cl)) {
cl_dbg(dev, cl, "not connected\n");
cb->status = -ENODEV;
goto discard;
}
- length = mei_hdr->dma_ring ? mei_hdr->extension[0] : mei_hdr->length;
+ if (mei_hdr->dma_ring)
+ length = mei_hdr->extension[mei_data2slots(ext_len)];
buf_sz = length + cb->buf_idx;
/* catch for integer overflow */
@@ -129,11 +182,13 @@ static int mei_cl_irq_read_msg(struct mei_cl *cl,
goto discard;
}
- if (mei_hdr->dma_ring)
+ 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);
+ /* for DMA read 0 length to generate interrupt to the device */
+ mei_read_slots(dev, cb->buf.data + cb->buf_idx, 0);
+ } else {
+ mei_read_slots(dev, cb->buf.data + cb->buf_idx, length);
+ }
cb->buf_idx += length;
@@ -150,7 +205,7 @@ static int mei_cl_irq_read_msg(struct mei_cl *cl,
discard:
if (cb)
list_move_tail(&cb->list, cmpl_list);
- mei_irq_discard_msg(dev, mei_hdr);
+ mei_irq_discard_msg(dev, mei_hdr, length);
return 0;
}
@@ -265,11 +320,16 @@ int mei_irq_read_handler(struct mei_device *dev,
struct list_head *cmpl_list, s32 *slots)
{
struct mei_msg_hdr *mei_hdr;
+ struct mei_ext_meta_hdr *meta_hdr = NULL;
struct mei_cl *cl;
int ret;
+ u32 ext_meta_hdr_u32;
+ int i;
+ int ext_hdr_end;
if (!dev->rd_msg_hdr[0]) {
dev->rd_msg_hdr[0] = mei_read_hdr(dev);
+ dev->rd_msg_hdr_count = 1;
(*slots)--;
dev_dbg(dev->dev, "slots =%08x.\n", *slots);
@@ -292,10 +352,34 @@ int mei_irq_read_handler(struct mei_device *dev,
goto end;
}
+ ext_hdr_end = 1;
+
+ if (mei_hdr->extended) {
+ if (!dev->rd_msg_hdr[1]) {
+ ext_meta_hdr_u32 = mei_read_hdr(dev);
+ dev->rd_msg_hdr[1] = ext_meta_hdr_u32;
+ dev->rd_msg_hdr_count++;
+ (*slots)--;
+ dev_dbg(dev->dev, "extended header is %08x\n",
+ ext_meta_hdr_u32);
+ }
+ meta_hdr = ((struct mei_ext_meta_hdr *)
+ dev->rd_msg_hdr + 1);
+ ext_hdr_end = meta_hdr->size + 2;
+ for (i = dev->rd_msg_hdr_count; i < ext_hdr_end; i++) {
+ dev->rd_msg_hdr[i] = mei_read_hdr(dev);
+ dev_dbg(dev->dev, "extended header %d is %08x\n", i,
+ dev->rd_msg_hdr[i]);
+ dev->rd_msg_hdr_count++;
+ (*slots)--;
+ }
+ }
+
if (mei_hdr->dma_ring) {
- dev->rd_msg_hdr[1] = mei_read_hdr(dev);
+ dev->rd_msg_hdr[ext_hdr_end] = mei_read_hdr(dev);
+ dev->rd_msg_hdr_count++;
(*slots)--;
- mei_hdr->length = 0;
+ mei_hdr->length -= sizeof(dev->rd_msg_hdr[ext_hdr_end]);
}
/* HBM message */
@@ -326,7 +410,7 @@ int mei_irq_read_handler(struct mei_device *dev,
*/
if (hdr_is_fixed(mei_hdr) ||
dev->dev_state == MEI_DEV_POWER_DOWN) {
- mei_irq_discard_msg(dev, mei_hdr);
+ mei_irq_discard_msg(dev, mei_hdr, mei_hdr->length);
ret = 0;
goto reset_slots;
}
@@ -336,12 +420,13 @@ int mei_irq_read_handler(struct mei_device *dev,
goto end;
}
- ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list);
+ ret = mei_cl_irq_read_msg(cl, mei_hdr, meta_hdr, cmpl_list);
reset_slots:
/* reset the number of slots and header */
memset(dev->rd_msg_hdr, 0, sizeof(dev->rd_msg_hdr));
+ dev->rd_msg_hdr_count = 0;
*slots = mei_count_full_read_slots(dev);
if (*slots == -EOVERFLOW) {
/* overflow - reset */