18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2003-2018, Intel Corporation. All rights reserved. 48c2ecf20Sopenharmony_ci * Intel Management Engine Interface (Intel MEI) Linux driver 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/export.h> 88c2ecf20Sopenharmony_ci#include <linux/kthread.h> 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include <linux/fs.h> 118c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/mei.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "mei_dev.h" 188c2ecf20Sopenharmony_ci#include "hbm.h" 198c2ecf20Sopenharmony_ci#include "client.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/** 238c2ecf20Sopenharmony_ci * mei_irq_compl_handler - dispatch complete handlers 248c2ecf20Sopenharmony_ci * for the completed callbacks 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * @dev: mei device 278c2ecf20Sopenharmony_ci * @cmpl_list: list of completed cbs 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_civoid mei_irq_compl_handler(struct mei_device *dev, struct list_head *cmpl_list) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct mei_cl_cb *cb, *next; 328c2ecf20Sopenharmony_ci struct mei_cl *cl; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci list_for_each_entry_safe(cb, next, cmpl_list, list) { 358c2ecf20Sopenharmony_ci cl = cb->cl; 368c2ecf20Sopenharmony_ci list_del_init(&cb->list); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "completing call back.\n"); 398c2ecf20Sopenharmony_ci mei_cl_complete(cl, cb); 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_irq_compl_handler); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/** 458c2ecf20Sopenharmony_ci * mei_cl_hbm_equal - check if hbm is addressed to the client 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * @cl: host client 488c2ecf20Sopenharmony_ci * @mei_hdr: header of mei client message 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * Return: true if matches, false otherwise 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_cistatic inline int mei_cl_hbm_equal(struct mei_cl *cl, 538c2ecf20Sopenharmony_ci struct mei_msg_hdr *mei_hdr) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci return mei_cl_host_addr(cl) == mei_hdr->host_addr && 568c2ecf20Sopenharmony_ci mei_cl_me_id(cl) == mei_hdr->me_addr; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/** 608c2ecf20Sopenharmony_ci * mei_irq_discard_msg - discard received message 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * @dev: mei device 638c2ecf20Sopenharmony_ci * @hdr: message header 648c2ecf20Sopenharmony_ci * @discard_len: the length of the message to discard (excluding header) 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_cistatic void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr, 678c2ecf20Sopenharmony_ci size_t discard_len) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci if (hdr->dma_ring) { 708c2ecf20Sopenharmony_ci mei_dma_ring_read(dev, NULL, 718c2ecf20Sopenharmony_ci hdr->extension[dev->rd_msg_hdr_count - 2]); 728c2ecf20Sopenharmony_ci discard_len = 0; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci /* 758c2ecf20Sopenharmony_ci * no need to check for size as it is guarantied 768c2ecf20Sopenharmony_ci * that length fits into rd_msg_buf 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ci mei_read_slots(dev, dev->rd_msg_buf, discard_len); 798c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n", 808c2ecf20Sopenharmony_ci MEI_HDR_PRM(hdr)); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/** 848c2ecf20Sopenharmony_ci * mei_cl_irq_read_msg - process client message 858c2ecf20Sopenharmony_ci * 868c2ecf20Sopenharmony_ci * @cl: reading client 878c2ecf20Sopenharmony_ci * @mei_hdr: header of mei client message 888c2ecf20Sopenharmony_ci * @meta: extend meta header 898c2ecf20Sopenharmony_ci * @cmpl_list: completion list 908c2ecf20Sopenharmony_ci * 918c2ecf20Sopenharmony_ci * Return: always 0 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_cistatic int mei_cl_irq_read_msg(struct mei_cl *cl, 948c2ecf20Sopenharmony_ci struct mei_msg_hdr *mei_hdr, 958c2ecf20Sopenharmony_ci struct mei_ext_meta_hdr *meta, 968c2ecf20Sopenharmony_ci struct list_head *cmpl_list) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct mei_device *dev = cl->dev; 998c2ecf20Sopenharmony_ci struct mei_cl_cb *cb; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci size_t buf_sz; 1028c2ecf20Sopenharmony_ci u32 length; 1038c2ecf20Sopenharmony_ci int ext_len; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci length = mei_hdr->length; 1068c2ecf20Sopenharmony_ci ext_len = 0; 1078c2ecf20Sopenharmony_ci if (mei_hdr->extended) { 1088c2ecf20Sopenharmony_ci ext_len = sizeof(*meta) + mei_slots2data(meta->size); 1098c2ecf20Sopenharmony_ci length -= ext_len; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list); 1138c2ecf20Sopenharmony_ci if (!cb) { 1148c2ecf20Sopenharmony_ci if (!mei_cl_is_fixed_address(cl)) { 1158c2ecf20Sopenharmony_ci cl_err(dev, cl, "pending read cb not found\n"); 1168c2ecf20Sopenharmony_ci goto discard; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci cb = mei_cl_alloc_cb(cl, mei_cl_mtu(cl), MEI_FOP_READ, cl->fp); 1198c2ecf20Sopenharmony_ci if (!cb) 1208c2ecf20Sopenharmony_ci goto discard; 1218c2ecf20Sopenharmony_ci list_add_tail(&cb->list, &cl->rd_pending); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (mei_hdr->extended) { 1258c2ecf20Sopenharmony_ci struct mei_ext_hdr *ext; 1268c2ecf20Sopenharmony_ci struct mei_ext_hdr *vtag = NULL; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci ext = mei_ext_begin(meta); 1298c2ecf20Sopenharmony_ci do { 1308c2ecf20Sopenharmony_ci switch (ext->type) { 1318c2ecf20Sopenharmony_ci case MEI_EXT_HDR_VTAG: 1328c2ecf20Sopenharmony_ci vtag = ext; 1338c2ecf20Sopenharmony_ci break; 1348c2ecf20Sopenharmony_ci case MEI_EXT_HDR_NONE: 1358c2ecf20Sopenharmony_ci fallthrough; 1368c2ecf20Sopenharmony_ci default: 1378c2ecf20Sopenharmony_ci cb->status = -EPROTO; 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci ext = mei_ext_next(ext); 1428c2ecf20Sopenharmony_ci } while (!mei_ext_last(meta, ext)); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (!vtag) { 1458c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "vtag not found in extended header.\n"); 1468c2ecf20Sopenharmony_ci cb->status = -EPROTO; 1478c2ecf20Sopenharmony_ci goto discard; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "vtag: %d\n", vtag->ext_payload[0]); 1518c2ecf20Sopenharmony_ci if (cb->vtag && cb->vtag != vtag->ext_payload[0]) { 1528c2ecf20Sopenharmony_ci cl_err(dev, cl, "mismatched tag: %d != %d\n", 1538c2ecf20Sopenharmony_ci cb->vtag, vtag->ext_payload[0]); 1548c2ecf20Sopenharmony_ci cb->status = -EPROTO; 1558c2ecf20Sopenharmony_ci goto discard; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci cb->vtag = vtag->ext_payload[0]; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (!mei_cl_is_connected(cl)) { 1618c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "not connected\n"); 1628c2ecf20Sopenharmony_ci cb->status = -ENODEV; 1638c2ecf20Sopenharmony_ci goto discard; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (mei_hdr->dma_ring) 1678c2ecf20Sopenharmony_ci length = mei_hdr->extension[mei_data2slots(ext_len)]; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci buf_sz = length + cb->buf_idx; 1708c2ecf20Sopenharmony_ci /* catch for integer overflow */ 1718c2ecf20Sopenharmony_ci if (buf_sz < cb->buf_idx) { 1728c2ecf20Sopenharmony_ci cl_err(dev, cl, "message is too big len %d idx %zu\n", 1738c2ecf20Sopenharmony_ci length, cb->buf_idx); 1748c2ecf20Sopenharmony_ci cb->status = -EMSGSIZE; 1758c2ecf20Sopenharmony_ci goto discard; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (cb->buf.size < buf_sz) { 1798c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "message overflow. size %zu len %d idx %zu\n", 1808c2ecf20Sopenharmony_ci cb->buf.size, length, cb->buf_idx); 1818c2ecf20Sopenharmony_ci cb->status = -EMSGSIZE; 1828c2ecf20Sopenharmony_ci goto discard; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (mei_hdr->dma_ring) { 1868c2ecf20Sopenharmony_ci mei_dma_ring_read(dev, cb->buf.data + cb->buf_idx, length); 1878c2ecf20Sopenharmony_ci /* for DMA read 0 length to generate interrupt to the device */ 1888c2ecf20Sopenharmony_ci mei_read_slots(dev, cb->buf.data + cb->buf_idx, 0); 1898c2ecf20Sopenharmony_ci } else { 1908c2ecf20Sopenharmony_ci mei_read_slots(dev, cb->buf.data + cb->buf_idx, length); 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci cb->buf_idx += length; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (mei_hdr->msg_complete) { 1968c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "completed read length = %zu\n", cb->buf_idx); 1978c2ecf20Sopenharmony_ci list_move_tail(&cb->list, cmpl_list); 1988c2ecf20Sopenharmony_ci } else { 1998c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev->dev); 2008c2ecf20Sopenharmony_ci pm_request_autosuspend(dev->dev); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cidiscard: 2068c2ecf20Sopenharmony_ci if (cb) 2078c2ecf20Sopenharmony_ci list_move_tail(&cb->list, cmpl_list); 2088c2ecf20Sopenharmony_ci mei_irq_discard_msg(dev, mei_hdr, length); 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/** 2138c2ecf20Sopenharmony_ci * mei_cl_irq_disconnect_rsp - send disconnection response message 2148c2ecf20Sopenharmony_ci * 2158c2ecf20Sopenharmony_ci * @cl: client 2168c2ecf20Sopenharmony_ci * @cb: callback block. 2178c2ecf20Sopenharmony_ci * @cmpl_list: complete list. 2188c2ecf20Sopenharmony_ci * 2198c2ecf20Sopenharmony_ci * Return: 0, OK; otherwise, error. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_cistatic int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, 2228c2ecf20Sopenharmony_ci struct list_head *cmpl_list) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct mei_device *dev = cl->dev; 2258c2ecf20Sopenharmony_ci u32 msg_slots; 2268c2ecf20Sopenharmony_ci int slots; 2278c2ecf20Sopenharmony_ci int ret; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci msg_slots = mei_hbm2slots(sizeof(struct hbm_client_connect_response)); 2308c2ecf20Sopenharmony_ci slots = mei_hbuf_empty_slots(dev); 2318c2ecf20Sopenharmony_ci if (slots < 0) 2328c2ecf20Sopenharmony_ci return -EOVERFLOW; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if ((u32)slots < msg_slots) 2358c2ecf20Sopenharmony_ci return -EMSGSIZE; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci ret = mei_hbm_cl_disconnect_rsp(dev, cl); 2388c2ecf20Sopenharmony_ci list_move_tail(&cb->list, cmpl_list); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return ret; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/** 2448c2ecf20Sopenharmony_ci * mei_cl_irq_read - processes client read related operation from the 2458c2ecf20Sopenharmony_ci * interrupt thread context - request for flow control credits 2468c2ecf20Sopenharmony_ci * 2478c2ecf20Sopenharmony_ci * @cl: client 2488c2ecf20Sopenharmony_ci * @cb: callback block. 2498c2ecf20Sopenharmony_ci * @cmpl_list: complete list. 2508c2ecf20Sopenharmony_ci * 2518c2ecf20Sopenharmony_ci * Return: 0, OK; otherwise, error. 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_cistatic int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, 2548c2ecf20Sopenharmony_ci struct list_head *cmpl_list) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct mei_device *dev = cl->dev; 2578c2ecf20Sopenharmony_ci u32 msg_slots; 2588c2ecf20Sopenharmony_ci int slots; 2598c2ecf20Sopenharmony_ci int ret; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (!list_empty(&cl->rd_pending)) 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci msg_slots = mei_hbm2slots(sizeof(struct hbm_flow_control)); 2658c2ecf20Sopenharmony_ci slots = mei_hbuf_empty_slots(dev); 2668c2ecf20Sopenharmony_ci if (slots < 0) 2678c2ecf20Sopenharmony_ci return -EOVERFLOW; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if ((u32)slots < msg_slots) 2708c2ecf20Sopenharmony_ci return -EMSGSIZE; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci ret = mei_hbm_cl_flow_control_req(dev, cl); 2738c2ecf20Sopenharmony_ci if (ret) { 2748c2ecf20Sopenharmony_ci cl->status = ret; 2758c2ecf20Sopenharmony_ci cb->buf_idx = 0; 2768c2ecf20Sopenharmony_ci list_move_tail(&cb->list, cmpl_list); 2778c2ecf20Sopenharmony_ci return ret; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev->dev); 2818c2ecf20Sopenharmony_ci pm_request_autosuspend(dev->dev); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci list_move_tail(&cb->list, &cl->rd_pending); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic inline bool hdr_is_hbm(struct mei_msg_hdr *mei_hdr) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci return mei_hdr->host_addr == 0 && mei_hdr->me_addr == 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic inline bool hdr_is_fixed(struct mei_msg_hdr *mei_hdr) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci return mei_hdr->host_addr == 0 && mei_hdr->me_addr != 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic inline int hdr_is_valid(u32 msg_hdr) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct mei_msg_hdr *mei_hdr; 3018c2ecf20Sopenharmony_ci u32 expected_len = 0; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci mei_hdr = (struct mei_msg_hdr *)&msg_hdr; 3048c2ecf20Sopenharmony_ci if (!msg_hdr || mei_hdr->reserved) 3058c2ecf20Sopenharmony_ci return -EBADMSG; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (mei_hdr->dma_ring) 3088c2ecf20Sopenharmony_ci expected_len += MEI_SLOT_SIZE; 3098c2ecf20Sopenharmony_ci if (mei_hdr->extended) 3108c2ecf20Sopenharmony_ci expected_len += MEI_SLOT_SIZE; 3118c2ecf20Sopenharmony_ci if (mei_hdr->length < expected_len) 3128c2ecf20Sopenharmony_ci return -EBADMSG; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci/** 3188c2ecf20Sopenharmony_ci * mei_irq_read_handler - bottom half read routine after ISR to 3198c2ecf20Sopenharmony_ci * handle the read processing. 3208c2ecf20Sopenharmony_ci * 3218c2ecf20Sopenharmony_ci * @dev: the device structure 3228c2ecf20Sopenharmony_ci * @cmpl_list: An instance of our list structure 3238c2ecf20Sopenharmony_ci * @slots: slots to read. 3248c2ecf20Sopenharmony_ci * 3258c2ecf20Sopenharmony_ci * Return: 0 on success, <0 on failure. 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_ciint mei_irq_read_handler(struct mei_device *dev, 3288c2ecf20Sopenharmony_ci struct list_head *cmpl_list, s32 *slots) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci struct mei_msg_hdr *mei_hdr; 3318c2ecf20Sopenharmony_ci struct mei_ext_meta_hdr *meta_hdr = NULL; 3328c2ecf20Sopenharmony_ci struct mei_cl *cl; 3338c2ecf20Sopenharmony_ci int ret; 3348c2ecf20Sopenharmony_ci u32 ext_meta_hdr_u32; 3358c2ecf20Sopenharmony_ci u32 hdr_size_left; 3368c2ecf20Sopenharmony_ci u32 hdr_size_ext; 3378c2ecf20Sopenharmony_ci int i; 3388c2ecf20Sopenharmony_ci int ext_hdr_end; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (!dev->rd_msg_hdr[0]) { 3418c2ecf20Sopenharmony_ci dev->rd_msg_hdr[0] = mei_read_hdr(dev); 3428c2ecf20Sopenharmony_ci dev->rd_msg_hdr_count = 1; 3438c2ecf20Sopenharmony_ci (*slots)--; 3448c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "slots =%08x.\n", *slots); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci ret = hdr_is_valid(dev->rd_msg_hdr[0]); 3478c2ecf20Sopenharmony_ci if (ret) { 3488c2ecf20Sopenharmony_ci dev_err(dev->dev, "corrupted message header 0x%08X\n", 3498c2ecf20Sopenharmony_ci dev->rd_msg_hdr[0]); 3508c2ecf20Sopenharmony_ci goto end; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci mei_hdr = (struct mei_msg_hdr *)dev->rd_msg_hdr; 3558c2ecf20Sopenharmony_ci dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (mei_slots2data(*slots) < mei_hdr->length) { 3588c2ecf20Sopenharmony_ci dev_err(dev->dev, "less data available than length=%08x.\n", 3598c2ecf20Sopenharmony_ci *slots); 3608c2ecf20Sopenharmony_ci /* we can't read the message */ 3618c2ecf20Sopenharmony_ci ret = -ENODATA; 3628c2ecf20Sopenharmony_ci goto end; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci ext_hdr_end = 1; 3668c2ecf20Sopenharmony_ci hdr_size_left = mei_hdr->length; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (mei_hdr->extended) { 3698c2ecf20Sopenharmony_ci if (!dev->rd_msg_hdr[1]) { 3708c2ecf20Sopenharmony_ci ext_meta_hdr_u32 = mei_read_hdr(dev); 3718c2ecf20Sopenharmony_ci dev->rd_msg_hdr[1] = ext_meta_hdr_u32; 3728c2ecf20Sopenharmony_ci dev->rd_msg_hdr_count++; 3738c2ecf20Sopenharmony_ci (*slots)--; 3748c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "extended header is %08x\n", 3758c2ecf20Sopenharmony_ci ext_meta_hdr_u32); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci meta_hdr = ((struct mei_ext_meta_hdr *)dev->rd_msg_hdr + 1); 3788c2ecf20Sopenharmony_ci if (check_add_overflow((u32)sizeof(*meta_hdr), 3798c2ecf20Sopenharmony_ci mei_slots2data(meta_hdr->size), 3808c2ecf20Sopenharmony_ci &hdr_size_ext)) { 3818c2ecf20Sopenharmony_ci dev_err(dev->dev, "extended message size too big %d\n", 3828c2ecf20Sopenharmony_ci meta_hdr->size); 3838c2ecf20Sopenharmony_ci return -EBADMSG; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci if (hdr_size_left < hdr_size_ext) { 3868c2ecf20Sopenharmony_ci dev_err(dev->dev, "corrupted message header len %d\n", 3878c2ecf20Sopenharmony_ci mei_hdr->length); 3888c2ecf20Sopenharmony_ci return -EBADMSG; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci hdr_size_left -= hdr_size_ext; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci ext_hdr_end = meta_hdr->size + 2; 3938c2ecf20Sopenharmony_ci for (i = dev->rd_msg_hdr_count; i < ext_hdr_end; i++) { 3948c2ecf20Sopenharmony_ci dev->rd_msg_hdr[i] = mei_read_hdr(dev); 3958c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "extended header %d is %08x\n", i, 3968c2ecf20Sopenharmony_ci dev->rd_msg_hdr[i]); 3978c2ecf20Sopenharmony_ci dev->rd_msg_hdr_count++; 3988c2ecf20Sopenharmony_ci (*slots)--; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (mei_hdr->dma_ring) { 4038c2ecf20Sopenharmony_ci if (hdr_size_left != sizeof(dev->rd_msg_hdr[ext_hdr_end])) { 4048c2ecf20Sopenharmony_ci dev_err(dev->dev, "corrupted message header len %d\n", 4058c2ecf20Sopenharmony_ci mei_hdr->length); 4068c2ecf20Sopenharmony_ci return -EBADMSG; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci dev->rd_msg_hdr[ext_hdr_end] = mei_read_hdr(dev); 4108c2ecf20Sopenharmony_ci dev->rd_msg_hdr_count++; 4118c2ecf20Sopenharmony_ci (*slots)--; 4128c2ecf20Sopenharmony_ci mei_hdr->length -= sizeof(dev->rd_msg_hdr[ext_hdr_end]); 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* HBM message */ 4168c2ecf20Sopenharmony_ci if (hdr_is_hbm(mei_hdr)) { 4178c2ecf20Sopenharmony_ci ret = mei_hbm_dispatch(dev, mei_hdr); 4188c2ecf20Sopenharmony_ci if (ret) { 4198c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "mei_hbm_dispatch failed ret = %d\n", 4208c2ecf20Sopenharmony_ci ret); 4218c2ecf20Sopenharmony_ci goto end; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci goto reset_slots; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* find recipient cl */ 4278c2ecf20Sopenharmony_ci list_for_each_entry(cl, &dev->file_list, link) { 4288c2ecf20Sopenharmony_ci if (mei_cl_hbm_equal(cl, mei_hdr)) { 4298c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "got a message\n"); 4308c2ecf20Sopenharmony_ci ret = mei_cl_irq_read_msg(cl, mei_hdr, meta_hdr, cmpl_list); 4318c2ecf20Sopenharmony_ci goto reset_slots; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* if no recipient cl was found we assume corrupted header */ 4368c2ecf20Sopenharmony_ci /* A message for not connected fixed address clients 4378c2ecf20Sopenharmony_ci * should be silently discarded 4388c2ecf20Sopenharmony_ci * On power down client may be force cleaned, 4398c2ecf20Sopenharmony_ci * silently discard such messages 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci if (hdr_is_fixed(mei_hdr) || 4428c2ecf20Sopenharmony_ci dev->dev_state == MEI_DEV_POWER_DOWN) { 4438c2ecf20Sopenharmony_ci mei_irq_discard_msg(dev, mei_hdr, mei_hdr->length); 4448c2ecf20Sopenharmony_ci ret = 0; 4458c2ecf20Sopenharmony_ci goto reset_slots; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci dev_err(dev->dev, "no destination client found 0x%08X\n", dev->rd_msg_hdr[0]); 4488c2ecf20Sopenharmony_ci ret = -EBADMSG; 4498c2ecf20Sopenharmony_ci goto end; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cireset_slots: 4528c2ecf20Sopenharmony_ci /* reset the number of slots and header */ 4538c2ecf20Sopenharmony_ci memset(dev->rd_msg_hdr, 0, sizeof(dev->rd_msg_hdr)); 4548c2ecf20Sopenharmony_ci dev->rd_msg_hdr_count = 0; 4558c2ecf20Sopenharmony_ci *slots = mei_count_full_read_slots(dev); 4568c2ecf20Sopenharmony_ci if (*slots == -EOVERFLOW) { 4578c2ecf20Sopenharmony_ci /* overflow - reset */ 4588c2ecf20Sopenharmony_ci dev_err(dev->dev, "resetting due to slots overflow.\n"); 4598c2ecf20Sopenharmony_ci /* set the event since message has been read */ 4608c2ecf20Sopenharmony_ci ret = -ERANGE; 4618c2ecf20Sopenharmony_ci goto end; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ciend: 4648c2ecf20Sopenharmony_ci return ret; 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_irq_read_handler); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci/** 4708c2ecf20Sopenharmony_ci * mei_irq_write_handler - dispatch write requests 4718c2ecf20Sopenharmony_ci * after irq received 4728c2ecf20Sopenharmony_ci * 4738c2ecf20Sopenharmony_ci * @dev: the device structure 4748c2ecf20Sopenharmony_ci * @cmpl_list: An instance of our list structure 4758c2ecf20Sopenharmony_ci * 4768c2ecf20Sopenharmony_ci * Return: 0 on success, <0 on failure. 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_ciint mei_irq_write_handler(struct mei_device *dev, struct list_head *cmpl_list) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci struct mei_cl *cl; 4828c2ecf20Sopenharmony_ci struct mei_cl_cb *cb, *next; 4838c2ecf20Sopenharmony_ci s32 slots; 4848c2ecf20Sopenharmony_ci int ret; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (!mei_hbuf_acquire(dev)) 4888c2ecf20Sopenharmony_ci return 0; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci slots = mei_hbuf_empty_slots(dev); 4918c2ecf20Sopenharmony_ci if (slots < 0) 4928c2ecf20Sopenharmony_ci return -EOVERFLOW; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (slots == 0) 4958c2ecf20Sopenharmony_ci return -EMSGSIZE; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* complete all waiting for write CB */ 4988c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "complete all waiting for write cb.\n"); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci list_for_each_entry_safe(cb, next, &dev->write_waiting_list, list) { 5018c2ecf20Sopenharmony_ci cl = cb->cl; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci cl->status = 0; 5048c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "MEI WRITE COMPLETE\n"); 5058c2ecf20Sopenharmony_ci cl->writing_state = MEI_WRITE_COMPLETE; 5068c2ecf20Sopenharmony_ci list_move_tail(&cb->list, cmpl_list); 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* complete control write list CB */ 5108c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "complete control write list cb.\n"); 5118c2ecf20Sopenharmony_ci list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list, list) { 5128c2ecf20Sopenharmony_ci cl = cb->cl; 5138c2ecf20Sopenharmony_ci switch (cb->fop_type) { 5148c2ecf20Sopenharmony_ci case MEI_FOP_DISCONNECT: 5158c2ecf20Sopenharmony_ci /* send disconnect message */ 5168c2ecf20Sopenharmony_ci ret = mei_cl_irq_disconnect(cl, cb, cmpl_list); 5178c2ecf20Sopenharmony_ci if (ret) 5188c2ecf20Sopenharmony_ci return ret; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci break; 5218c2ecf20Sopenharmony_ci case MEI_FOP_READ: 5228c2ecf20Sopenharmony_ci /* send flow control message */ 5238c2ecf20Sopenharmony_ci ret = mei_cl_irq_read(cl, cb, cmpl_list); 5248c2ecf20Sopenharmony_ci if (ret) 5258c2ecf20Sopenharmony_ci return ret; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci break; 5288c2ecf20Sopenharmony_ci case MEI_FOP_CONNECT: 5298c2ecf20Sopenharmony_ci /* connect message */ 5308c2ecf20Sopenharmony_ci ret = mei_cl_irq_connect(cl, cb, cmpl_list); 5318c2ecf20Sopenharmony_ci if (ret) 5328c2ecf20Sopenharmony_ci return ret; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci break; 5358c2ecf20Sopenharmony_ci case MEI_FOP_DISCONNECT_RSP: 5368c2ecf20Sopenharmony_ci /* send disconnect resp */ 5378c2ecf20Sopenharmony_ci ret = mei_cl_irq_disconnect_rsp(cl, cb, cmpl_list); 5388c2ecf20Sopenharmony_ci if (ret) 5398c2ecf20Sopenharmony_ci return ret; 5408c2ecf20Sopenharmony_ci break; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci case MEI_FOP_NOTIFY_START: 5438c2ecf20Sopenharmony_ci case MEI_FOP_NOTIFY_STOP: 5448c2ecf20Sopenharmony_ci ret = mei_cl_irq_notify(cl, cb, cmpl_list); 5458c2ecf20Sopenharmony_ci if (ret) 5468c2ecf20Sopenharmony_ci return ret; 5478c2ecf20Sopenharmony_ci break; 5488c2ecf20Sopenharmony_ci default: 5498c2ecf20Sopenharmony_ci BUG(); 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci /* complete write list CB */ 5548c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "complete write list cb.\n"); 5558c2ecf20Sopenharmony_ci list_for_each_entry_safe(cb, next, &dev->write_list, list) { 5568c2ecf20Sopenharmony_ci cl = cb->cl; 5578c2ecf20Sopenharmony_ci ret = mei_cl_irq_write(cl, cb, cmpl_list); 5588c2ecf20Sopenharmony_ci if (ret) 5598c2ecf20Sopenharmony_ci return ret; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci return 0; 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_irq_write_handler); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci/** 5678c2ecf20Sopenharmony_ci * mei_connect_timeout - connect/disconnect timeouts 5688c2ecf20Sopenharmony_ci * 5698c2ecf20Sopenharmony_ci * @cl: host client 5708c2ecf20Sopenharmony_ci */ 5718c2ecf20Sopenharmony_cistatic void mei_connect_timeout(struct mei_cl *cl) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci struct mei_device *dev = cl->dev; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (cl->state == MEI_FILE_CONNECTING) { 5768c2ecf20Sopenharmony_ci if (dev->hbm_f_dot_supported) { 5778c2ecf20Sopenharmony_ci cl->state = MEI_FILE_DISCONNECT_REQUIRED; 5788c2ecf20Sopenharmony_ci wake_up(&cl->wait); 5798c2ecf20Sopenharmony_ci return; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci mei_reset(dev); 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci#define MEI_STALL_TIMER_FREQ (2 * HZ) 5868c2ecf20Sopenharmony_ci/** 5878c2ecf20Sopenharmony_ci * mei_schedule_stall_timer - re-arm stall_timer work 5888c2ecf20Sopenharmony_ci * 5898c2ecf20Sopenharmony_ci * Schedule stall timer 5908c2ecf20Sopenharmony_ci * 5918c2ecf20Sopenharmony_ci * @dev: the device structure 5928c2ecf20Sopenharmony_ci */ 5938c2ecf20Sopenharmony_civoid mei_schedule_stall_timer(struct mei_device *dev) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci schedule_delayed_work(&dev->timer_work, MEI_STALL_TIMER_FREQ); 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci/** 5998c2ecf20Sopenharmony_ci * mei_timer - timer function. 6008c2ecf20Sopenharmony_ci * 6018c2ecf20Sopenharmony_ci * @work: pointer to the work_struct structure 6028c2ecf20Sopenharmony_ci * 6038c2ecf20Sopenharmony_ci */ 6048c2ecf20Sopenharmony_civoid mei_timer(struct work_struct *work) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci struct mei_cl *cl; 6078c2ecf20Sopenharmony_ci struct mei_device *dev = container_of(work, 6088c2ecf20Sopenharmony_ci struct mei_device, timer_work.work); 6098c2ecf20Sopenharmony_ci bool reschedule_timer = false; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci mutex_lock(&dev->device_lock); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* Catch interrupt stalls during HBM init handshake */ 6148c2ecf20Sopenharmony_ci if (dev->dev_state == MEI_DEV_INIT_CLIENTS && 6158c2ecf20Sopenharmony_ci dev->hbm_state != MEI_HBM_IDLE) { 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (dev->init_clients_timer) { 6188c2ecf20Sopenharmony_ci if (--dev->init_clients_timer == 0) { 6198c2ecf20Sopenharmony_ci dev_err(dev->dev, "timer: init clients timeout hbm_state = %d.\n", 6208c2ecf20Sopenharmony_ci dev->hbm_state); 6218c2ecf20Sopenharmony_ci mei_reset(dev); 6228c2ecf20Sopenharmony_ci goto out; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci reschedule_timer = true; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (dev->dev_state != MEI_DEV_ENABLED) 6298c2ecf20Sopenharmony_ci goto out; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci /*** connect/disconnect timeouts ***/ 6328c2ecf20Sopenharmony_ci list_for_each_entry(cl, &dev->file_list, link) { 6338c2ecf20Sopenharmony_ci if (cl->timer_count) { 6348c2ecf20Sopenharmony_ci if (--cl->timer_count == 0) { 6358c2ecf20Sopenharmony_ci dev_err(dev->dev, "timer: connect/disconnect timeout.\n"); 6368c2ecf20Sopenharmony_ci mei_connect_timeout(cl); 6378c2ecf20Sopenharmony_ci goto out; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci reschedule_timer = true; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ciout: 6448c2ecf20Sopenharmony_ci if (dev->dev_state != MEI_DEV_DISABLED && reschedule_timer) 6458c2ecf20Sopenharmony_ci mei_schedule_stall_timer(dev); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci mutex_unlock(&dev->device_lock); 6488c2ecf20Sopenharmony_ci} 649