18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright(c) 2013 - 2019 Intel Corporation. */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include "fm10k_common.h" 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/** 78c2ecf20Sopenharmony_ci * fm10k_fifo_init - Initialize a message FIFO 88c2ecf20Sopenharmony_ci * @fifo: pointer to FIFO 98c2ecf20Sopenharmony_ci * @buffer: pointer to memory to be used to store FIFO 108c2ecf20Sopenharmony_ci * @size: maximum message size to store in FIFO, must be 2^n - 1 118c2ecf20Sopenharmony_ci **/ 128c2ecf20Sopenharmony_cistatic void fm10k_fifo_init(struct fm10k_mbx_fifo *fifo, u32 *buffer, u16 size) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci fifo->buffer = buffer; 158c2ecf20Sopenharmony_ci fifo->size = size; 168c2ecf20Sopenharmony_ci fifo->head = 0; 178c2ecf20Sopenharmony_ci fifo->tail = 0; 188c2ecf20Sopenharmony_ci} 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/** 218c2ecf20Sopenharmony_ci * fm10k_fifo_used - Retrieve used space in FIFO 228c2ecf20Sopenharmony_ci * @fifo: pointer to FIFO 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * This function returns the number of DWORDs used in the FIFO 258c2ecf20Sopenharmony_ci **/ 268c2ecf20Sopenharmony_cistatic u16 fm10k_fifo_used(struct fm10k_mbx_fifo *fifo) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci return fifo->tail - fifo->head; 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/** 328c2ecf20Sopenharmony_ci * fm10k_fifo_unused - Retrieve unused space in FIFO 338c2ecf20Sopenharmony_ci * @fifo: pointer to FIFO 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * This function returns the number of unused DWORDs in the FIFO 368c2ecf20Sopenharmony_ci **/ 378c2ecf20Sopenharmony_cistatic u16 fm10k_fifo_unused(struct fm10k_mbx_fifo *fifo) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci return fifo->size + fifo->head - fifo->tail; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/** 438c2ecf20Sopenharmony_ci * fm10k_fifo_empty - Test to verify if FIFO is empty 448c2ecf20Sopenharmony_ci * @fifo: pointer to FIFO 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * This function returns true if the FIFO is empty, else false 478c2ecf20Sopenharmony_ci **/ 488c2ecf20Sopenharmony_cistatic bool fm10k_fifo_empty(struct fm10k_mbx_fifo *fifo) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci return fifo->head == fifo->tail; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/** 548c2ecf20Sopenharmony_ci * fm10k_fifo_head_offset - returns indices of head with given offset 558c2ecf20Sopenharmony_ci * @fifo: pointer to FIFO 568c2ecf20Sopenharmony_ci * @offset: offset to add to head 578c2ecf20Sopenharmony_ci * 588c2ecf20Sopenharmony_ci * This function returns the indices into the FIFO based on head + offset 598c2ecf20Sopenharmony_ci **/ 608c2ecf20Sopenharmony_cistatic u16 fm10k_fifo_head_offset(struct fm10k_mbx_fifo *fifo, u16 offset) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci return (fifo->head + offset) & (fifo->size - 1); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/** 668c2ecf20Sopenharmony_ci * fm10k_fifo_tail_offset - returns indices of tail with given offset 678c2ecf20Sopenharmony_ci * @fifo: pointer to FIFO 688c2ecf20Sopenharmony_ci * @offset: offset to add to tail 698c2ecf20Sopenharmony_ci * 708c2ecf20Sopenharmony_ci * This function returns the indices into the FIFO based on tail + offset 718c2ecf20Sopenharmony_ci **/ 728c2ecf20Sopenharmony_cistatic u16 fm10k_fifo_tail_offset(struct fm10k_mbx_fifo *fifo, u16 offset) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci return (fifo->tail + offset) & (fifo->size - 1); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/** 788c2ecf20Sopenharmony_ci * fm10k_fifo_head_len - Retrieve length of first message in FIFO 798c2ecf20Sopenharmony_ci * @fifo: pointer to FIFO 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * This function returns the size of the first message in the FIFO 828c2ecf20Sopenharmony_ci **/ 838c2ecf20Sopenharmony_cistatic u16 fm10k_fifo_head_len(struct fm10k_mbx_fifo *fifo) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci u32 *head = fifo->buffer + fm10k_fifo_head_offset(fifo, 0); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* verify there is at least 1 DWORD in the fifo so *head is valid */ 888c2ecf20Sopenharmony_ci if (fm10k_fifo_empty(fifo)) 898c2ecf20Sopenharmony_ci return 0; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* retieve the message length */ 928c2ecf20Sopenharmony_ci return FM10K_TLV_DWORD_LEN(*head); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/** 968c2ecf20Sopenharmony_ci * fm10k_fifo_head_drop - Drop the first message in FIFO 978c2ecf20Sopenharmony_ci * @fifo: pointer to FIFO 988c2ecf20Sopenharmony_ci * 998c2ecf20Sopenharmony_ci * This function returns the size of the message dropped from the FIFO 1008c2ecf20Sopenharmony_ci **/ 1018c2ecf20Sopenharmony_cistatic u16 fm10k_fifo_head_drop(struct fm10k_mbx_fifo *fifo) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci u16 len = fm10k_fifo_head_len(fifo); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* update head so it is at the start of next frame */ 1068c2ecf20Sopenharmony_ci fifo->head += len; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return len; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/** 1128c2ecf20Sopenharmony_ci * fm10k_fifo_drop_all - Drop all messages in FIFO 1138c2ecf20Sopenharmony_ci * @fifo: pointer to FIFO 1148c2ecf20Sopenharmony_ci * 1158c2ecf20Sopenharmony_ci * This function resets the head pointer to drop all messages in the FIFO and 1168c2ecf20Sopenharmony_ci * ensure the FIFO is empty. 1178c2ecf20Sopenharmony_ci **/ 1188c2ecf20Sopenharmony_cistatic void fm10k_fifo_drop_all(struct fm10k_mbx_fifo *fifo) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci fifo->head = fifo->tail; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/** 1248c2ecf20Sopenharmony_ci * fm10k_mbx_index_len - Convert a head/tail index into a length value 1258c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 1268c2ecf20Sopenharmony_ci * @head: head index 1278c2ecf20Sopenharmony_ci * @tail: head index 1288c2ecf20Sopenharmony_ci * 1298c2ecf20Sopenharmony_ci * This function takes the head and tail index and determines the length 1308c2ecf20Sopenharmony_ci * of the data indicated by this pair. 1318c2ecf20Sopenharmony_ci **/ 1328c2ecf20Sopenharmony_cistatic u16 fm10k_mbx_index_len(struct fm10k_mbx_info *mbx, u16 head, u16 tail) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci u16 len = tail - head; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* we wrapped so subtract 2, one for index 0, one for all 1s index */ 1378c2ecf20Sopenharmony_ci if (len > tail) 1388c2ecf20Sopenharmony_ci len -= 2; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return len & ((mbx->mbmem_len << 1) - 1); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/** 1448c2ecf20Sopenharmony_ci * fm10k_mbx_tail_add - Determine new tail value with added offset 1458c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 1468c2ecf20Sopenharmony_ci * @offset: length to add to tail offset 1478c2ecf20Sopenharmony_ci * 1488c2ecf20Sopenharmony_ci * This function takes the local tail index and recomputes it for 1498c2ecf20Sopenharmony_ci * a given length added as an offset. 1508c2ecf20Sopenharmony_ci **/ 1518c2ecf20Sopenharmony_cistatic u16 fm10k_mbx_tail_add(struct fm10k_mbx_info *mbx, u16 offset) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci u16 tail = (mbx->tail + offset + 1) & ((mbx->mbmem_len << 1) - 1); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* add/sub 1 because we cannot have offset 0 or all 1s */ 1568c2ecf20Sopenharmony_ci return (tail > mbx->tail) ? --tail : ++tail; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/** 1608c2ecf20Sopenharmony_ci * fm10k_mbx_tail_sub - Determine new tail value with subtracted offset 1618c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 1628c2ecf20Sopenharmony_ci * @offset: length to add to tail offset 1638c2ecf20Sopenharmony_ci * 1648c2ecf20Sopenharmony_ci * This function takes the local tail index and recomputes it for 1658c2ecf20Sopenharmony_ci * a given length added as an offset. 1668c2ecf20Sopenharmony_ci **/ 1678c2ecf20Sopenharmony_cistatic u16 fm10k_mbx_tail_sub(struct fm10k_mbx_info *mbx, u16 offset) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci u16 tail = (mbx->tail - offset - 1) & ((mbx->mbmem_len << 1) - 1); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* sub/add 1 because we cannot have offset 0 or all 1s */ 1728c2ecf20Sopenharmony_ci return (tail < mbx->tail) ? ++tail : --tail; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/** 1768c2ecf20Sopenharmony_ci * fm10k_mbx_head_add - Determine new head value with added offset 1778c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 1788c2ecf20Sopenharmony_ci * @offset: length to add to head offset 1798c2ecf20Sopenharmony_ci * 1808c2ecf20Sopenharmony_ci * This function takes the local head index and recomputes it for 1818c2ecf20Sopenharmony_ci * a given length added as an offset. 1828c2ecf20Sopenharmony_ci **/ 1838c2ecf20Sopenharmony_cistatic u16 fm10k_mbx_head_add(struct fm10k_mbx_info *mbx, u16 offset) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci u16 head = (mbx->head + offset + 1) & ((mbx->mbmem_len << 1) - 1); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* add/sub 1 because we cannot have offset 0 or all 1s */ 1888c2ecf20Sopenharmony_ci return (head > mbx->head) ? --head : ++head; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/** 1928c2ecf20Sopenharmony_ci * fm10k_mbx_head_sub - Determine new head value with subtracted offset 1938c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 1948c2ecf20Sopenharmony_ci * @offset: length to add to head offset 1958c2ecf20Sopenharmony_ci * 1968c2ecf20Sopenharmony_ci * This function takes the local head index and recomputes it for 1978c2ecf20Sopenharmony_ci * a given length added as an offset. 1988c2ecf20Sopenharmony_ci **/ 1998c2ecf20Sopenharmony_cistatic u16 fm10k_mbx_head_sub(struct fm10k_mbx_info *mbx, u16 offset) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci u16 head = (mbx->head - offset - 1) & ((mbx->mbmem_len << 1) - 1); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* sub/add 1 because we cannot have offset 0 or all 1s */ 2048c2ecf20Sopenharmony_ci return (head < mbx->head) ? ++head : --head; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/** 2088c2ecf20Sopenharmony_ci * fm10k_mbx_pushed_tail_len - Retrieve the length of message being pushed 2098c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 2108c2ecf20Sopenharmony_ci * 2118c2ecf20Sopenharmony_ci * This function will return the length of the message currently being 2128c2ecf20Sopenharmony_ci * pushed onto the tail of the Rx queue. 2138c2ecf20Sopenharmony_ci **/ 2148c2ecf20Sopenharmony_cistatic u16 fm10k_mbx_pushed_tail_len(struct fm10k_mbx_info *mbx) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci u32 *tail = mbx->rx.buffer + fm10k_fifo_tail_offset(&mbx->rx, 0); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* pushed tail is only valid if pushed is set */ 2198c2ecf20Sopenharmony_ci if (!mbx->pushed) 2208c2ecf20Sopenharmony_ci return 0; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return FM10K_TLV_DWORD_LEN(*tail); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci/** 2268c2ecf20Sopenharmony_ci * fm10k_fifo_write_copy - pulls data off of msg and places it in FIFO 2278c2ecf20Sopenharmony_ci * @fifo: pointer to FIFO 2288c2ecf20Sopenharmony_ci * @msg: message array to populate 2298c2ecf20Sopenharmony_ci * @tail_offset: additional offset to add to tail pointer 2308c2ecf20Sopenharmony_ci * @len: length of FIFO to copy into message header 2318c2ecf20Sopenharmony_ci * 2328c2ecf20Sopenharmony_ci * This function will take a message and copy it into a section of the 2338c2ecf20Sopenharmony_ci * FIFO. In order to get something into a location other than just 2348c2ecf20Sopenharmony_ci * the tail you can use tail_offset to adjust the pointer. 2358c2ecf20Sopenharmony_ci **/ 2368c2ecf20Sopenharmony_cistatic void fm10k_fifo_write_copy(struct fm10k_mbx_fifo *fifo, 2378c2ecf20Sopenharmony_ci const u32 *msg, u16 tail_offset, u16 len) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci u16 end = fm10k_fifo_tail_offset(fifo, tail_offset); 2408c2ecf20Sopenharmony_ci u32 *tail = fifo->buffer + end; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* track when we should cross the end of the FIFO */ 2438c2ecf20Sopenharmony_ci end = fifo->size - end; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* copy end of message before start of message */ 2468c2ecf20Sopenharmony_ci if (end < len) 2478c2ecf20Sopenharmony_ci memcpy(fifo->buffer, msg + end, (len - end) << 2); 2488c2ecf20Sopenharmony_ci else 2498c2ecf20Sopenharmony_ci end = len; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* Copy remaining message into Tx FIFO */ 2528c2ecf20Sopenharmony_ci memcpy(tail, msg, end << 2); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/** 2568c2ecf20Sopenharmony_ci * fm10k_fifo_enqueue - Enqueues the message to the tail of the FIFO 2578c2ecf20Sopenharmony_ci * @fifo: pointer to FIFO 2588c2ecf20Sopenharmony_ci * @msg: message array to read 2598c2ecf20Sopenharmony_ci * 2608c2ecf20Sopenharmony_ci * This function enqueues a message up to the size specified by the length 2618c2ecf20Sopenharmony_ci * contained in the first DWORD of the message and will place at the tail 2628c2ecf20Sopenharmony_ci * of the FIFO. It will return 0 on success, or a negative value on error. 2638c2ecf20Sopenharmony_ci **/ 2648c2ecf20Sopenharmony_cistatic s32 fm10k_fifo_enqueue(struct fm10k_mbx_fifo *fifo, const u32 *msg) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci u16 len = FM10K_TLV_DWORD_LEN(*msg); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* verify parameters */ 2698c2ecf20Sopenharmony_ci if (len > fifo->size) 2708c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_SIZE; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* verify there is room for the message */ 2738c2ecf20Sopenharmony_ci if (len > fm10k_fifo_unused(fifo)) 2748c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_NO_SPACE; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* Copy message into FIFO */ 2778c2ecf20Sopenharmony_ci fm10k_fifo_write_copy(fifo, msg, 0, len); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* memory barrier to guarantee FIFO is written before tail update */ 2808c2ecf20Sopenharmony_ci wmb(); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* Update Tx FIFO tail */ 2838c2ecf20Sopenharmony_ci fifo->tail += len; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/** 2898c2ecf20Sopenharmony_ci * fm10k_mbx_validate_msg_size - Validate incoming message based on size 2908c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 2918c2ecf20Sopenharmony_ci * @len: length of data pushed onto buffer 2928c2ecf20Sopenharmony_ci * 2938c2ecf20Sopenharmony_ci * This function analyzes the frame and will return a non-zero value when 2948c2ecf20Sopenharmony_ci * the start of a message larger than the mailbox is detected. 2958c2ecf20Sopenharmony_ci **/ 2968c2ecf20Sopenharmony_cistatic u16 fm10k_mbx_validate_msg_size(struct fm10k_mbx_info *mbx, u16 len) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct fm10k_mbx_fifo *fifo = &mbx->rx; 2998c2ecf20Sopenharmony_ci u16 total_len = 0, msg_len; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* length should include previous amounts pushed */ 3028c2ecf20Sopenharmony_ci len += mbx->pushed; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* offset in message is based off of current message size */ 3058c2ecf20Sopenharmony_ci do { 3068c2ecf20Sopenharmony_ci u32 *msg; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci msg = fifo->buffer + fm10k_fifo_tail_offset(fifo, total_len); 3098c2ecf20Sopenharmony_ci msg_len = FM10K_TLV_DWORD_LEN(*msg); 3108c2ecf20Sopenharmony_ci total_len += msg_len; 3118c2ecf20Sopenharmony_ci } while (total_len < len); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* message extends out of pushed section, but fits in FIFO */ 3148c2ecf20Sopenharmony_ci if ((len < total_len) && (msg_len <= mbx->max_size)) 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* return length of invalid section */ 3188c2ecf20Sopenharmony_ci return (len < total_len) ? len : (len - total_len); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci/** 3228c2ecf20Sopenharmony_ci * fm10k_mbx_write_copy - pulls data off of Tx FIFO and places it in mbmem 3238c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 3248c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 3258c2ecf20Sopenharmony_ci * 3268c2ecf20Sopenharmony_ci * This function will take a section of the Tx FIFO and copy it into the 3278c2ecf20Sopenharmony_ci * mailbox memory. The offset in mbmem is based on the lower bits of the 3288c2ecf20Sopenharmony_ci * tail and len determines the length to copy. 3298c2ecf20Sopenharmony_ci **/ 3308c2ecf20Sopenharmony_cistatic void fm10k_mbx_write_copy(struct fm10k_hw *hw, 3318c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct fm10k_mbx_fifo *fifo = &mbx->tx; 3348c2ecf20Sopenharmony_ci u32 mbmem = mbx->mbmem_reg; 3358c2ecf20Sopenharmony_ci u32 *head = fifo->buffer; 3368c2ecf20Sopenharmony_ci u16 end, len, tail, mask; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (!mbx->tail_len) 3398c2ecf20Sopenharmony_ci return; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* determine data length and mbmem tail index */ 3428c2ecf20Sopenharmony_ci mask = mbx->mbmem_len - 1; 3438c2ecf20Sopenharmony_ci len = mbx->tail_len; 3448c2ecf20Sopenharmony_ci tail = fm10k_mbx_tail_sub(mbx, len); 3458c2ecf20Sopenharmony_ci if (tail > mask) 3468c2ecf20Sopenharmony_ci tail++; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* determine offset in the ring */ 3498c2ecf20Sopenharmony_ci end = fm10k_fifo_head_offset(fifo, mbx->pulled); 3508c2ecf20Sopenharmony_ci head += end; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* memory barrier to guarantee data is ready to be read */ 3538c2ecf20Sopenharmony_ci rmb(); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* Copy message from Tx FIFO */ 3568c2ecf20Sopenharmony_ci for (end = fifo->size - end; len; head = fifo->buffer) { 3578c2ecf20Sopenharmony_ci do { 3588c2ecf20Sopenharmony_ci /* adjust tail to match offset for FIFO */ 3598c2ecf20Sopenharmony_ci tail &= mask; 3608c2ecf20Sopenharmony_ci if (!tail) 3618c2ecf20Sopenharmony_ci tail++; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci mbx->tx_mbmem_pulled++; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* write message to hardware FIFO */ 3668c2ecf20Sopenharmony_ci fm10k_write_reg(hw, mbmem + tail++, *(head++)); 3678c2ecf20Sopenharmony_ci } while (--len && --end); 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci/** 3728c2ecf20Sopenharmony_ci * fm10k_mbx_pull_head - Pulls data off of head of Tx FIFO 3738c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 3748c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 3758c2ecf20Sopenharmony_ci * @head: acknowledgement number last received 3768c2ecf20Sopenharmony_ci * 3778c2ecf20Sopenharmony_ci * This function will push the tail index forward based on the remote 3788c2ecf20Sopenharmony_ci * head index. It will then pull up to mbmem_len DWORDs off of the 3798c2ecf20Sopenharmony_ci * head of the FIFO and will place it in the MBMEM registers 3808c2ecf20Sopenharmony_ci * associated with the mailbox. 3818c2ecf20Sopenharmony_ci **/ 3828c2ecf20Sopenharmony_cistatic void fm10k_mbx_pull_head(struct fm10k_hw *hw, 3838c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx, u16 head) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci u16 mbmem_len, len, ack = fm10k_mbx_index_len(mbx, head, mbx->tail); 3868c2ecf20Sopenharmony_ci struct fm10k_mbx_fifo *fifo = &mbx->tx; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* update number of bytes pulled and update bytes in transit */ 3898c2ecf20Sopenharmony_ci mbx->pulled += mbx->tail_len - ack; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* determine length of data to pull, reserve space for mbmem header */ 3928c2ecf20Sopenharmony_ci mbmem_len = mbx->mbmem_len - 1; 3938c2ecf20Sopenharmony_ci len = fm10k_fifo_used(fifo) - mbx->pulled; 3948c2ecf20Sopenharmony_ci if (len > mbmem_len) 3958c2ecf20Sopenharmony_ci len = mbmem_len; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci /* update tail and record number of bytes in transit */ 3988c2ecf20Sopenharmony_ci mbx->tail = fm10k_mbx_tail_add(mbx, len - ack); 3998c2ecf20Sopenharmony_ci mbx->tail_len = len; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* drop pulled messages from the FIFO */ 4028c2ecf20Sopenharmony_ci for (len = fm10k_fifo_head_len(fifo); 4038c2ecf20Sopenharmony_ci len && (mbx->pulled >= len); 4048c2ecf20Sopenharmony_ci len = fm10k_fifo_head_len(fifo)) { 4058c2ecf20Sopenharmony_ci mbx->pulled -= fm10k_fifo_head_drop(fifo); 4068c2ecf20Sopenharmony_ci mbx->tx_messages++; 4078c2ecf20Sopenharmony_ci mbx->tx_dwords += len; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* Copy message out from the Tx FIFO */ 4118c2ecf20Sopenharmony_ci fm10k_mbx_write_copy(hw, mbx); 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci/** 4158c2ecf20Sopenharmony_ci * fm10k_mbx_read_copy - pulls data off of mbmem and places it in Rx FIFO 4168c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 4178c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 4188c2ecf20Sopenharmony_ci * 4198c2ecf20Sopenharmony_ci * This function will take a section of the mailbox memory and copy it 4208c2ecf20Sopenharmony_ci * into the Rx FIFO. The offset is based on the lower bits of the 4218c2ecf20Sopenharmony_ci * head and len determines the length to copy. 4228c2ecf20Sopenharmony_ci **/ 4238c2ecf20Sopenharmony_cistatic void fm10k_mbx_read_copy(struct fm10k_hw *hw, 4248c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct fm10k_mbx_fifo *fifo = &mbx->rx; 4278c2ecf20Sopenharmony_ci u32 mbmem = mbx->mbmem_reg ^ mbx->mbmem_len; 4288c2ecf20Sopenharmony_ci u32 *tail = fifo->buffer; 4298c2ecf20Sopenharmony_ci u16 end, len, head; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* determine data length and mbmem head index */ 4328c2ecf20Sopenharmony_ci len = mbx->head_len; 4338c2ecf20Sopenharmony_ci head = fm10k_mbx_head_sub(mbx, len); 4348c2ecf20Sopenharmony_ci if (head >= mbx->mbmem_len) 4358c2ecf20Sopenharmony_ci head++; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* determine offset in the ring */ 4388c2ecf20Sopenharmony_ci end = fm10k_fifo_tail_offset(fifo, mbx->pushed); 4398c2ecf20Sopenharmony_ci tail += end; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* Copy message into Rx FIFO */ 4428c2ecf20Sopenharmony_ci for (end = fifo->size - end; len; tail = fifo->buffer) { 4438c2ecf20Sopenharmony_ci do { 4448c2ecf20Sopenharmony_ci /* adjust head to match offset for FIFO */ 4458c2ecf20Sopenharmony_ci head &= mbx->mbmem_len - 1; 4468c2ecf20Sopenharmony_ci if (!head) 4478c2ecf20Sopenharmony_ci head++; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci mbx->rx_mbmem_pushed++; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* read message from hardware FIFO */ 4528c2ecf20Sopenharmony_ci *(tail++) = fm10k_read_reg(hw, mbmem + head++); 4538c2ecf20Sopenharmony_ci } while (--len && --end); 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* memory barrier to guarantee FIFO is written before tail update */ 4578c2ecf20Sopenharmony_ci wmb(); 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci/** 4618c2ecf20Sopenharmony_ci * fm10k_mbx_push_tail - Pushes up to 15 DWORDs on to tail of FIFO 4628c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 4638c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 4648c2ecf20Sopenharmony_ci * @tail: tail index of message 4658c2ecf20Sopenharmony_ci * 4668c2ecf20Sopenharmony_ci * This function will first validate the tail index and size for the 4678c2ecf20Sopenharmony_ci * incoming message. It then updates the acknowledgment number and 4688c2ecf20Sopenharmony_ci * copies the data into the FIFO. It will return the number of messages 4698c2ecf20Sopenharmony_ci * dequeued on success and a negative value on error. 4708c2ecf20Sopenharmony_ci **/ 4718c2ecf20Sopenharmony_cistatic s32 fm10k_mbx_push_tail(struct fm10k_hw *hw, 4728c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx, 4738c2ecf20Sopenharmony_ci u16 tail) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct fm10k_mbx_fifo *fifo = &mbx->rx; 4768c2ecf20Sopenharmony_ci u16 len, seq = fm10k_mbx_index_len(mbx, mbx->head, tail); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* determine length of data to push */ 4798c2ecf20Sopenharmony_ci len = fm10k_fifo_unused(fifo) - mbx->pushed; 4808c2ecf20Sopenharmony_ci if (len > seq) 4818c2ecf20Sopenharmony_ci len = seq; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* update head and record bytes received */ 4848c2ecf20Sopenharmony_ci mbx->head = fm10k_mbx_head_add(mbx, len); 4858c2ecf20Sopenharmony_ci mbx->head_len = len; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* nothing to do if there is no data */ 4888c2ecf20Sopenharmony_ci if (!len) 4898c2ecf20Sopenharmony_ci return 0; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* Copy msg into Rx FIFO */ 4928c2ecf20Sopenharmony_ci fm10k_mbx_read_copy(hw, mbx); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci /* determine if there are any invalid lengths in message */ 4958c2ecf20Sopenharmony_ci if (fm10k_mbx_validate_msg_size(mbx, len)) 4968c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_SIZE; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* Update pushed */ 4998c2ecf20Sopenharmony_ci mbx->pushed += len; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* flush any completed messages */ 5028c2ecf20Sopenharmony_ci for (len = fm10k_mbx_pushed_tail_len(mbx); 5038c2ecf20Sopenharmony_ci len && (mbx->pushed >= len); 5048c2ecf20Sopenharmony_ci len = fm10k_mbx_pushed_tail_len(mbx)) { 5058c2ecf20Sopenharmony_ci fifo->tail += len; 5068c2ecf20Sopenharmony_ci mbx->pushed -= len; 5078c2ecf20Sopenharmony_ci mbx->rx_messages++; 5088c2ecf20Sopenharmony_ci mbx->rx_dwords += len; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return 0; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci/* pre-generated data for generating the CRC based on the poly 0xAC9A. */ 5158c2ecf20Sopenharmony_cistatic const u16 fm10k_crc_16b_table[256] = { 5168c2ecf20Sopenharmony_ci 0x0000, 0x7956, 0xF2AC, 0x8BFA, 0xBC6D, 0xC53B, 0x4EC1, 0x3797, 5178c2ecf20Sopenharmony_ci 0x21EF, 0x58B9, 0xD343, 0xAA15, 0x9D82, 0xE4D4, 0x6F2E, 0x1678, 5188c2ecf20Sopenharmony_ci 0x43DE, 0x3A88, 0xB172, 0xC824, 0xFFB3, 0x86E5, 0x0D1F, 0x7449, 5198c2ecf20Sopenharmony_ci 0x6231, 0x1B67, 0x909D, 0xE9CB, 0xDE5C, 0xA70A, 0x2CF0, 0x55A6, 5208c2ecf20Sopenharmony_ci 0x87BC, 0xFEEA, 0x7510, 0x0C46, 0x3BD1, 0x4287, 0xC97D, 0xB02B, 5218c2ecf20Sopenharmony_ci 0xA653, 0xDF05, 0x54FF, 0x2DA9, 0x1A3E, 0x6368, 0xE892, 0x91C4, 5228c2ecf20Sopenharmony_ci 0xC462, 0xBD34, 0x36CE, 0x4F98, 0x780F, 0x0159, 0x8AA3, 0xF3F5, 5238c2ecf20Sopenharmony_ci 0xE58D, 0x9CDB, 0x1721, 0x6E77, 0x59E0, 0x20B6, 0xAB4C, 0xD21A, 5248c2ecf20Sopenharmony_ci 0x564D, 0x2F1B, 0xA4E1, 0xDDB7, 0xEA20, 0x9376, 0x188C, 0x61DA, 5258c2ecf20Sopenharmony_ci 0x77A2, 0x0EF4, 0x850E, 0xFC58, 0xCBCF, 0xB299, 0x3963, 0x4035, 5268c2ecf20Sopenharmony_ci 0x1593, 0x6CC5, 0xE73F, 0x9E69, 0xA9FE, 0xD0A8, 0x5B52, 0x2204, 5278c2ecf20Sopenharmony_ci 0x347C, 0x4D2A, 0xC6D0, 0xBF86, 0x8811, 0xF147, 0x7ABD, 0x03EB, 5288c2ecf20Sopenharmony_ci 0xD1F1, 0xA8A7, 0x235D, 0x5A0B, 0x6D9C, 0x14CA, 0x9F30, 0xE666, 5298c2ecf20Sopenharmony_ci 0xF01E, 0x8948, 0x02B2, 0x7BE4, 0x4C73, 0x3525, 0xBEDF, 0xC789, 5308c2ecf20Sopenharmony_ci 0x922F, 0xEB79, 0x6083, 0x19D5, 0x2E42, 0x5714, 0xDCEE, 0xA5B8, 5318c2ecf20Sopenharmony_ci 0xB3C0, 0xCA96, 0x416C, 0x383A, 0x0FAD, 0x76FB, 0xFD01, 0x8457, 5328c2ecf20Sopenharmony_ci 0xAC9A, 0xD5CC, 0x5E36, 0x2760, 0x10F7, 0x69A1, 0xE25B, 0x9B0D, 5338c2ecf20Sopenharmony_ci 0x8D75, 0xF423, 0x7FD9, 0x068F, 0x3118, 0x484E, 0xC3B4, 0xBAE2, 5348c2ecf20Sopenharmony_ci 0xEF44, 0x9612, 0x1DE8, 0x64BE, 0x5329, 0x2A7F, 0xA185, 0xD8D3, 5358c2ecf20Sopenharmony_ci 0xCEAB, 0xB7FD, 0x3C07, 0x4551, 0x72C6, 0x0B90, 0x806A, 0xF93C, 5368c2ecf20Sopenharmony_ci 0x2B26, 0x5270, 0xD98A, 0xA0DC, 0x974B, 0xEE1D, 0x65E7, 0x1CB1, 5378c2ecf20Sopenharmony_ci 0x0AC9, 0x739F, 0xF865, 0x8133, 0xB6A4, 0xCFF2, 0x4408, 0x3D5E, 5388c2ecf20Sopenharmony_ci 0x68F8, 0x11AE, 0x9A54, 0xE302, 0xD495, 0xADC3, 0x2639, 0x5F6F, 5398c2ecf20Sopenharmony_ci 0x4917, 0x3041, 0xBBBB, 0xC2ED, 0xF57A, 0x8C2C, 0x07D6, 0x7E80, 5408c2ecf20Sopenharmony_ci 0xFAD7, 0x8381, 0x087B, 0x712D, 0x46BA, 0x3FEC, 0xB416, 0xCD40, 5418c2ecf20Sopenharmony_ci 0xDB38, 0xA26E, 0x2994, 0x50C2, 0x6755, 0x1E03, 0x95F9, 0xECAF, 5428c2ecf20Sopenharmony_ci 0xB909, 0xC05F, 0x4BA5, 0x32F3, 0x0564, 0x7C32, 0xF7C8, 0x8E9E, 5438c2ecf20Sopenharmony_ci 0x98E6, 0xE1B0, 0x6A4A, 0x131C, 0x248B, 0x5DDD, 0xD627, 0xAF71, 5448c2ecf20Sopenharmony_ci 0x7D6B, 0x043D, 0x8FC7, 0xF691, 0xC106, 0xB850, 0x33AA, 0x4AFC, 5458c2ecf20Sopenharmony_ci 0x5C84, 0x25D2, 0xAE28, 0xD77E, 0xE0E9, 0x99BF, 0x1245, 0x6B13, 5468c2ecf20Sopenharmony_ci 0x3EB5, 0x47E3, 0xCC19, 0xB54F, 0x82D8, 0xFB8E, 0x7074, 0x0922, 5478c2ecf20Sopenharmony_ci 0x1F5A, 0x660C, 0xEDF6, 0x94A0, 0xA337, 0xDA61, 0x519B, 0x28CD }; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci/** 5508c2ecf20Sopenharmony_ci * fm10k_crc_16b - Generate a 16 bit CRC for a region of 16 bit data 5518c2ecf20Sopenharmony_ci * @data: pointer to data to process 5528c2ecf20Sopenharmony_ci * @seed: seed value for CRC 5538c2ecf20Sopenharmony_ci * @len: length measured in 16 bits words 5548c2ecf20Sopenharmony_ci * 5558c2ecf20Sopenharmony_ci * This function will generate a CRC based on the polynomial 0xAC9A and 5568c2ecf20Sopenharmony_ci * whatever value is stored in the seed variable. Note that this 5578c2ecf20Sopenharmony_ci * value inverts the local seed and the result in order to capture all 5588c2ecf20Sopenharmony_ci * leading and trailing zeros. 5598c2ecf20Sopenharmony_ci */ 5608c2ecf20Sopenharmony_cistatic u16 fm10k_crc_16b(const u32 *data, u16 seed, u16 len) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci u32 result = seed; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci while (len--) { 5658c2ecf20Sopenharmony_ci result ^= *(data++); 5668c2ecf20Sopenharmony_ci result = (result >> 8) ^ fm10k_crc_16b_table[result & 0xFF]; 5678c2ecf20Sopenharmony_ci result = (result >> 8) ^ fm10k_crc_16b_table[result & 0xFF]; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (!(len--)) 5708c2ecf20Sopenharmony_ci break; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci result = (result >> 8) ^ fm10k_crc_16b_table[result & 0xFF]; 5738c2ecf20Sopenharmony_ci result = (result >> 8) ^ fm10k_crc_16b_table[result & 0xFF]; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci return (u16)result; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci/** 5808c2ecf20Sopenharmony_ci * fm10k_fifo_crc - generate a CRC based off of FIFO data 5818c2ecf20Sopenharmony_ci * @fifo: pointer to FIFO 5828c2ecf20Sopenharmony_ci * @offset: offset point for start of FIFO 5838c2ecf20Sopenharmony_ci * @len: number of DWORDS words to process 5848c2ecf20Sopenharmony_ci * @seed: seed value for CRC 5858c2ecf20Sopenharmony_ci * 5868c2ecf20Sopenharmony_ci * This function generates a CRC for some region of the FIFO 5878c2ecf20Sopenharmony_ci **/ 5888c2ecf20Sopenharmony_cistatic u16 fm10k_fifo_crc(struct fm10k_mbx_fifo *fifo, u16 offset, 5898c2ecf20Sopenharmony_ci u16 len, u16 seed) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci u32 *data = fifo->buffer + offset; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* track when we should cross the end of the FIFO */ 5948c2ecf20Sopenharmony_ci offset = fifo->size - offset; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* if we are in 2 blocks process the end of the FIFO first */ 5978c2ecf20Sopenharmony_ci if (offset < len) { 5988c2ecf20Sopenharmony_ci seed = fm10k_crc_16b(data, seed, offset * 2); 5998c2ecf20Sopenharmony_ci data = fifo->buffer; 6008c2ecf20Sopenharmony_ci len -= offset; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci /* process any remaining bits */ 6048c2ecf20Sopenharmony_ci return fm10k_crc_16b(data, seed, len * 2); 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci/** 6088c2ecf20Sopenharmony_ci * fm10k_mbx_update_local_crc - Update the local CRC for outgoing data 6098c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 6108c2ecf20Sopenharmony_ci * @head: head index provided by remote mailbox 6118c2ecf20Sopenharmony_ci * 6128c2ecf20Sopenharmony_ci * This function will generate the CRC for all data from the end of the 6138c2ecf20Sopenharmony_ci * last head update to the current one. It uses the result of the 6148c2ecf20Sopenharmony_ci * previous CRC as the seed for this update. The result is stored in 6158c2ecf20Sopenharmony_ci * mbx->local. 6168c2ecf20Sopenharmony_ci **/ 6178c2ecf20Sopenharmony_cistatic void fm10k_mbx_update_local_crc(struct fm10k_mbx_info *mbx, u16 head) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci u16 len = mbx->tail_len - fm10k_mbx_index_len(mbx, head, mbx->tail); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci /* determine the offset for the start of the region to be pulled */ 6228c2ecf20Sopenharmony_ci head = fm10k_fifo_head_offset(&mbx->tx, mbx->pulled); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci /* update local CRC to include all of the pulled data */ 6258c2ecf20Sopenharmony_ci mbx->local = fm10k_fifo_crc(&mbx->tx, head, len, mbx->local); 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci/** 6298c2ecf20Sopenharmony_ci * fm10k_mbx_verify_remote_crc - Verify the CRC is correct for current data 6308c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 6318c2ecf20Sopenharmony_ci * 6328c2ecf20Sopenharmony_ci * This function will take all data that has been provided from the remote 6338c2ecf20Sopenharmony_ci * end and generate a CRC for it. This is stored in mbx->remote. The 6348c2ecf20Sopenharmony_ci * CRC for the header is then computed and if the result is non-zero this 6358c2ecf20Sopenharmony_ci * is an error and we signal an error dropping all data and resetting the 6368c2ecf20Sopenharmony_ci * connection. 6378c2ecf20Sopenharmony_ci */ 6388c2ecf20Sopenharmony_cistatic s32 fm10k_mbx_verify_remote_crc(struct fm10k_mbx_info *mbx) 6398c2ecf20Sopenharmony_ci{ 6408c2ecf20Sopenharmony_ci struct fm10k_mbx_fifo *fifo = &mbx->rx; 6418c2ecf20Sopenharmony_ci u16 len = mbx->head_len; 6428c2ecf20Sopenharmony_ci u16 offset = fm10k_fifo_tail_offset(fifo, mbx->pushed) - len; 6438c2ecf20Sopenharmony_ci u16 crc; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci /* update the remote CRC if new data has been received */ 6468c2ecf20Sopenharmony_ci if (len) 6478c2ecf20Sopenharmony_ci mbx->remote = fm10k_fifo_crc(fifo, offset, len, mbx->remote); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci /* process the full header as we have to validate the CRC */ 6508c2ecf20Sopenharmony_ci crc = fm10k_crc_16b(&mbx->mbx_hdr, mbx->remote, 1); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci /* notify other end if we have a problem */ 6538c2ecf20Sopenharmony_ci return crc ? FM10K_MBX_ERR_CRC : 0; 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci/** 6578c2ecf20Sopenharmony_ci * fm10k_mbx_rx_ready - Indicates that a message is ready in the Rx FIFO 6588c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 6598c2ecf20Sopenharmony_ci * 6608c2ecf20Sopenharmony_ci * This function returns true if there is a message in the Rx FIFO to dequeue. 6618c2ecf20Sopenharmony_ci **/ 6628c2ecf20Sopenharmony_cistatic bool fm10k_mbx_rx_ready(struct fm10k_mbx_info *mbx) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci u16 msg_size = fm10k_fifo_head_len(&mbx->rx); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci return msg_size && (fm10k_fifo_used(&mbx->rx) >= msg_size); 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci/** 6708c2ecf20Sopenharmony_ci * fm10k_mbx_tx_ready - Indicates that the mailbox is in state ready for Tx 6718c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 6728c2ecf20Sopenharmony_ci * @len: verify free space is >= this value 6738c2ecf20Sopenharmony_ci * 6748c2ecf20Sopenharmony_ci * This function returns true if the mailbox is in a state ready to transmit. 6758c2ecf20Sopenharmony_ci **/ 6768c2ecf20Sopenharmony_cistatic bool fm10k_mbx_tx_ready(struct fm10k_mbx_info *mbx, u16 len) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci u16 fifo_unused = fm10k_fifo_unused(&mbx->tx); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci return (mbx->state == FM10K_STATE_OPEN) && (fifo_unused >= len); 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci/** 6848c2ecf20Sopenharmony_ci * fm10k_mbx_tx_complete - Indicates that the Tx FIFO has been emptied 6858c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 6868c2ecf20Sopenharmony_ci * 6878c2ecf20Sopenharmony_ci * This function returns true if the Tx FIFO is empty. 6888c2ecf20Sopenharmony_ci **/ 6898c2ecf20Sopenharmony_cistatic bool fm10k_mbx_tx_complete(struct fm10k_mbx_info *mbx) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci return fm10k_fifo_empty(&mbx->tx); 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci/** 6958c2ecf20Sopenharmony_ci * fm10k_mbx_deqeueue_rx - Dequeues the message from the head in the Rx FIFO 6968c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 6978c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 6988c2ecf20Sopenharmony_ci * 6998c2ecf20Sopenharmony_ci * This function dequeues messages and hands them off to the TLV parser. 7008c2ecf20Sopenharmony_ci * It will return the number of messages processed when called. 7018c2ecf20Sopenharmony_ci **/ 7028c2ecf20Sopenharmony_cistatic u16 fm10k_mbx_dequeue_rx(struct fm10k_hw *hw, 7038c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci struct fm10k_mbx_fifo *fifo = &mbx->rx; 7068c2ecf20Sopenharmony_ci s32 err; 7078c2ecf20Sopenharmony_ci u16 cnt; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* parse Rx messages out of the Rx FIFO to empty it */ 7108c2ecf20Sopenharmony_ci for (cnt = 0; !fm10k_fifo_empty(fifo); cnt++) { 7118c2ecf20Sopenharmony_ci err = fm10k_tlv_msg_parse(hw, fifo->buffer + fifo->head, 7128c2ecf20Sopenharmony_ci mbx, mbx->msg_data); 7138c2ecf20Sopenharmony_ci if (err < 0) 7148c2ecf20Sopenharmony_ci mbx->rx_parse_err++; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci fm10k_fifo_head_drop(fifo); 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci /* shift remaining bytes back to start of FIFO */ 7208c2ecf20Sopenharmony_ci memmove(fifo->buffer, fifo->buffer + fifo->tail, mbx->pushed << 2); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci /* shift head and tail based on the memory we moved */ 7238c2ecf20Sopenharmony_ci fifo->tail -= fifo->head; 7248c2ecf20Sopenharmony_ci fifo->head = 0; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci return cnt; 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci/** 7308c2ecf20Sopenharmony_ci * fm10k_mbx_enqueue_tx - Enqueues the message to the tail of the Tx FIFO 7318c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 7328c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 7338c2ecf20Sopenharmony_ci * @msg: message array to read 7348c2ecf20Sopenharmony_ci * 7358c2ecf20Sopenharmony_ci * This function enqueues a message up to the size specified by the length 7368c2ecf20Sopenharmony_ci * contained in the first DWORD of the message and will place at the tail 7378c2ecf20Sopenharmony_ci * of the FIFO. It will return 0 on success, or a negative value on error. 7388c2ecf20Sopenharmony_ci **/ 7398c2ecf20Sopenharmony_cistatic s32 fm10k_mbx_enqueue_tx(struct fm10k_hw *hw, 7408c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx, const u32 *msg) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci u32 countdown = mbx->timeout; 7438c2ecf20Sopenharmony_ci s32 err; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci switch (mbx->state) { 7468c2ecf20Sopenharmony_ci case FM10K_STATE_CLOSED: 7478c2ecf20Sopenharmony_ci case FM10K_STATE_DISCONNECT: 7488c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_NO_MBX; 7498c2ecf20Sopenharmony_ci default: 7508c2ecf20Sopenharmony_ci break; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci /* enqueue the message on the Tx FIFO */ 7548c2ecf20Sopenharmony_ci err = fm10k_fifo_enqueue(&mbx->tx, msg); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* if it failed give the FIFO a chance to drain */ 7578c2ecf20Sopenharmony_ci while (err && countdown) { 7588c2ecf20Sopenharmony_ci countdown--; 7598c2ecf20Sopenharmony_ci udelay(mbx->udelay); 7608c2ecf20Sopenharmony_ci mbx->ops.process(hw, mbx); 7618c2ecf20Sopenharmony_ci err = fm10k_fifo_enqueue(&mbx->tx, msg); 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* if we failed treat the error */ 7658c2ecf20Sopenharmony_ci if (err) { 7668c2ecf20Sopenharmony_ci mbx->timeout = 0; 7678c2ecf20Sopenharmony_ci mbx->tx_busy++; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci /* begin processing message, ignore errors as this is just meant 7718c2ecf20Sopenharmony_ci * to start the mailbox flow so we are not concerned if there 7728c2ecf20Sopenharmony_ci * is a bad error, or the mailbox is already busy with a request 7738c2ecf20Sopenharmony_ci */ 7748c2ecf20Sopenharmony_ci if (!mbx->tail_len) 7758c2ecf20Sopenharmony_ci mbx->ops.process(hw, mbx); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci return 0; 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci/** 7818c2ecf20Sopenharmony_ci * fm10k_mbx_read - Copies the mbmem to local message buffer 7828c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 7838c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 7848c2ecf20Sopenharmony_ci * 7858c2ecf20Sopenharmony_ci * This function copies the message from the mbmem to the message array 7868c2ecf20Sopenharmony_ci **/ 7878c2ecf20Sopenharmony_cistatic s32 fm10k_mbx_read(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx) 7888c2ecf20Sopenharmony_ci{ 7898c2ecf20Sopenharmony_ci /* only allow one reader in here at a time */ 7908c2ecf20Sopenharmony_ci if (mbx->mbx_hdr) 7918c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_BUSY; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci /* read to capture initial interrupt bits */ 7948c2ecf20Sopenharmony_ci if (fm10k_read_reg(hw, mbx->mbx_reg) & FM10K_MBX_REQ_INTERRUPT) 7958c2ecf20Sopenharmony_ci mbx->mbx_lock = FM10K_MBX_ACK; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci /* write back interrupt bits to clear */ 7988c2ecf20Sopenharmony_ci fm10k_write_reg(hw, mbx->mbx_reg, 7998c2ecf20Sopenharmony_ci FM10K_MBX_REQ_INTERRUPT | FM10K_MBX_ACK_INTERRUPT); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci /* read remote header */ 8028c2ecf20Sopenharmony_ci mbx->mbx_hdr = fm10k_read_reg(hw, mbx->mbmem_reg ^ mbx->mbmem_len); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci return 0; 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci/** 8088c2ecf20Sopenharmony_ci * fm10k_mbx_write - Copies the local message buffer to mbmem 8098c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 8108c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 8118c2ecf20Sopenharmony_ci * 8128c2ecf20Sopenharmony_ci * This function copies the message from the the message array to mbmem 8138c2ecf20Sopenharmony_ci **/ 8148c2ecf20Sopenharmony_cistatic void fm10k_mbx_write(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci u32 mbmem = mbx->mbmem_reg; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* write new msg header to notify recipient of change */ 8198c2ecf20Sopenharmony_ci fm10k_write_reg(hw, mbmem, mbx->mbx_hdr); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci /* write mailbox to send interrupt */ 8228c2ecf20Sopenharmony_ci if (mbx->mbx_lock) 8238c2ecf20Sopenharmony_ci fm10k_write_reg(hw, mbx->mbx_reg, mbx->mbx_lock); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci /* we no longer are using the header so free it */ 8268c2ecf20Sopenharmony_ci mbx->mbx_hdr = 0; 8278c2ecf20Sopenharmony_ci mbx->mbx_lock = 0; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci/** 8318c2ecf20Sopenharmony_ci * fm10k_mbx_create_connect_hdr - Generate a connect mailbox header 8328c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 8338c2ecf20Sopenharmony_ci * 8348c2ecf20Sopenharmony_ci * This function returns a connection mailbox header 8358c2ecf20Sopenharmony_ci **/ 8368c2ecf20Sopenharmony_cistatic void fm10k_mbx_create_connect_hdr(struct fm10k_mbx_info *mbx) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci mbx->mbx_lock |= FM10K_MBX_REQ; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci mbx->mbx_hdr = FM10K_MSG_HDR_FIELD_SET(FM10K_MSG_CONNECT, TYPE) | 8418c2ecf20Sopenharmony_ci FM10K_MSG_HDR_FIELD_SET(mbx->head, HEAD) | 8428c2ecf20Sopenharmony_ci FM10K_MSG_HDR_FIELD_SET(mbx->rx.size - 1, CONNECT_SIZE); 8438c2ecf20Sopenharmony_ci} 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci/** 8468c2ecf20Sopenharmony_ci * fm10k_mbx_create_data_hdr - Generate a data mailbox header 8478c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 8488c2ecf20Sopenharmony_ci * 8498c2ecf20Sopenharmony_ci * This function returns a data mailbox header 8508c2ecf20Sopenharmony_ci **/ 8518c2ecf20Sopenharmony_cistatic void fm10k_mbx_create_data_hdr(struct fm10k_mbx_info *mbx) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci u32 hdr = FM10K_MSG_HDR_FIELD_SET(FM10K_MSG_DATA, TYPE) | 8548c2ecf20Sopenharmony_ci FM10K_MSG_HDR_FIELD_SET(mbx->tail, TAIL) | 8558c2ecf20Sopenharmony_ci FM10K_MSG_HDR_FIELD_SET(mbx->head, HEAD); 8568c2ecf20Sopenharmony_ci struct fm10k_mbx_fifo *fifo = &mbx->tx; 8578c2ecf20Sopenharmony_ci u16 crc; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (mbx->tail_len) 8608c2ecf20Sopenharmony_ci mbx->mbx_lock |= FM10K_MBX_REQ; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci /* generate CRC for data in flight and header */ 8638c2ecf20Sopenharmony_ci crc = fm10k_fifo_crc(fifo, fm10k_fifo_head_offset(fifo, mbx->pulled), 8648c2ecf20Sopenharmony_ci mbx->tail_len, mbx->local); 8658c2ecf20Sopenharmony_ci crc = fm10k_crc_16b(&hdr, crc, 1); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci /* load header to memory to be written */ 8688c2ecf20Sopenharmony_ci mbx->mbx_hdr = hdr | FM10K_MSG_HDR_FIELD_SET(crc, CRC); 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci/** 8728c2ecf20Sopenharmony_ci * fm10k_mbx_create_disconnect_hdr - Generate a disconnect mailbox header 8738c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 8748c2ecf20Sopenharmony_ci * 8758c2ecf20Sopenharmony_ci * This function returns a disconnect mailbox header 8768c2ecf20Sopenharmony_ci **/ 8778c2ecf20Sopenharmony_cistatic void fm10k_mbx_create_disconnect_hdr(struct fm10k_mbx_info *mbx) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci u32 hdr = FM10K_MSG_HDR_FIELD_SET(FM10K_MSG_DISCONNECT, TYPE) | 8808c2ecf20Sopenharmony_ci FM10K_MSG_HDR_FIELD_SET(mbx->tail, TAIL) | 8818c2ecf20Sopenharmony_ci FM10K_MSG_HDR_FIELD_SET(mbx->head, HEAD); 8828c2ecf20Sopenharmony_ci u16 crc = fm10k_crc_16b(&hdr, mbx->local, 1); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci mbx->mbx_lock |= FM10K_MBX_ACK; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci /* load header to memory to be written */ 8878c2ecf20Sopenharmony_ci mbx->mbx_hdr = hdr | FM10K_MSG_HDR_FIELD_SET(crc, CRC); 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci/** 8918c2ecf20Sopenharmony_ci * fm10k_mbx_create_fake_disconnect_hdr - Generate a false disconnect mbox hdr 8928c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 8938c2ecf20Sopenharmony_ci * 8948c2ecf20Sopenharmony_ci * This function creates a fake disconnect header for loading into remote 8958c2ecf20Sopenharmony_ci * mailbox header. The primary purpose is to prevent errors on immediate 8968c2ecf20Sopenharmony_ci * start up after mbx->connect. 8978c2ecf20Sopenharmony_ci **/ 8988c2ecf20Sopenharmony_cistatic void fm10k_mbx_create_fake_disconnect_hdr(struct fm10k_mbx_info *mbx) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci u32 hdr = FM10K_MSG_HDR_FIELD_SET(FM10K_MSG_DISCONNECT, TYPE) | 9018c2ecf20Sopenharmony_ci FM10K_MSG_HDR_FIELD_SET(mbx->head, TAIL) | 9028c2ecf20Sopenharmony_ci FM10K_MSG_HDR_FIELD_SET(mbx->tail, HEAD); 9038c2ecf20Sopenharmony_ci u16 crc = fm10k_crc_16b(&hdr, mbx->local, 1); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci mbx->mbx_lock |= FM10K_MBX_ACK; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* load header to memory to be written */ 9088c2ecf20Sopenharmony_ci mbx->mbx_hdr = hdr | FM10K_MSG_HDR_FIELD_SET(crc, CRC); 9098c2ecf20Sopenharmony_ci} 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci/** 9128c2ecf20Sopenharmony_ci * fm10k_mbx_create_error_msg - Generate an error message 9138c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 9148c2ecf20Sopenharmony_ci * @err: local error encountered 9158c2ecf20Sopenharmony_ci * 9168c2ecf20Sopenharmony_ci * This function will interpret the error provided by err, and based on 9178c2ecf20Sopenharmony_ci * that it may shift the message by 1 DWORD and then place an error header 9188c2ecf20Sopenharmony_ci * at the start of the message. 9198c2ecf20Sopenharmony_ci **/ 9208c2ecf20Sopenharmony_cistatic void fm10k_mbx_create_error_msg(struct fm10k_mbx_info *mbx, s32 err) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci /* only generate an error message for these types */ 9238c2ecf20Sopenharmony_ci switch (err) { 9248c2ecf20Sopenharmony_ci case FM10K_MBX_ERR_TAIL: 9258c2ecf20Sopenharmony_ci case FM10K_MBX_ERR_HEAD: 9268c2ecf20Sopenharmony_ci case FM10K_MBX_ERR_TYPE: 9278c2ecf20Sopenharmony_ci case FM10K_MBX_ERR_SIZE: 9288c2ecf20Sopenharmony_ci case FM10K_MBX_ERR_RSVD0: 9298c2ecf20Sopenharmony_ci case FM10K_MBX_ERR_CRC: 9308c2ecf20Sopenharmony_ci break; 9318c2ecf20Sopenharmony_ci default: 9328c2ecf20Sopenharmony_ci return; 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci mbx->mbx_lock |= FM10K_MBX_REQ; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci mbx->mbx_hdr = FM10K_MSG_HDR_FIELD_SET(FM10K_MSG_ERROR, TYPE) | 9388c2ecf20Sopenharmony_ci FM10K_MSG_HDR_FIELD_SET(err, ERR_NO) | 9398c2ecf20Sopenharmony_ci FM10K_MSG_HDR_FIELD_SET(mbx->head, HEAD); 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci/** 9438c2ecf20Sopenharmony_ci * fm10k_mbx_validate_msg_hdr - Validate common fields in the message header 9448c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 9458c2ecf20Sopenharmony_ci * 9468c2ecf20Sopenharmony_ci * This function will parse up the fields in the mailbox header and return 9478c2ecf20Sopenharmony_ci * an error if the header contains any of a number of invalid configurations 9488c2ecf20Sopenharmony_ci * including unrecognized type, invalid route, or a malformed message. 9498c2ecf20Sopenharmony_ci **/ 9508c2ecf20Sopenharmony_cistatic s32 fm10k_mbx_validate_msg_hdr(struct fm10k_mbx_info *mbx) 9518c2ecf20Sopenharmony_ci{ 9528c2ecf20Sopenharmony_ci u16 type, rsvd0, head, tail, size; 9538c2ecf20Sopenharmony_ci const u32 *hdr = &mbx->mbx_hdr; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci type = FM10K_MSG_HDR_FIELD_GET(*hdr, TYPE); 9568c2ecf20Sopenharmony_ci rsvd0 = FM10K_MSG_HDR_FIELD_GET(*hdr, RSVD0); 9578c2ecf20Sopenharmony_ci tail = FM10K_MSG_HDR_FIELD_GET(*hdr, TAIL); 9588c2ecf20Sopenharmony_ci head = FM10K_MSG_HDR_FIELD_GET(*hdr, HEAD); 9598c2ecf20Sopenharmony_ci size = FM10K_MSG_HDR_FIELD_GET(*hdr, CONNECT_SIZE); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci if (rsvd0) 9628c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_RSVD0; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci switch (type) { 9658c2ecf20Sopenharmony_ci case FM10K_MSG_DISCONNECT: 9668c2ecf20Sopenharmony_ci /* validate that all data has been received */ 9678c2ecf20Sopenharmony_ci if (tail != mbx->head) 9688c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_TAIL; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci fallthrough; 9718c2ecf20Sopenharmony_ci case FM10K_MSG_DATA: 9728c2ecf20Sopenharmony_ci /* validate that head is moving correctly */ 9738c2ecf20Sopenharmony_ci if (!head || (head == FM10K_MSG_HDR_MASK(HEAD))) 9748c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_HEAD; 9758c2ecf20Sopenharmony_ci if (fm10k_mbx_index_len(mbx, head, mbx->tail) > mbx->tail_len) 9768c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_HEAD; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci /* validate that tail is moving correctly */ 9798c2ecf20Sopenharmony_ci if (!tail || (tail == FM10K_MSG_HDR_MASK(TAIL))) 9808c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_TAIL; 9818c2ecf20Sopenharmony_ci if (fm10k_mbx_index_len(mbx, mbx->head, tail) < mbx->mbmem_len) 9828c2ecf20Sopenharmony_ci break; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_TAIL; 9858c2ecf20Sopenharmony_ci case FM10K_MSG_CONNECT: 9868c2ecf20Sopenharmony_ci /* validate size is in range and is power of 2 mask */ 9878c2ecf20Sopenharmony_ci if ((size < FM10K_VFMBX_MSG_MTU) || (size & (size + 1))) 9888c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_SIZE; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci fallthrough; 9918c2ecf20Sopenharmony_ci case FM10K_MSG_ERROR: 9928c2ecf20Sopenharmony_ci if (!head || (head == FM10K_MSG_HDR_MASK(HEAD))) 9938c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_HEAD; 9948c2ecf20Sopenharmony_ci /* neither create nor error include a tail offset */ 9958c2ecf20Sopenharmony_ci if (tail) 9968c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_TAIL; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci break; 9998c2ecf20Sopenharmony_ci default: 10008c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_TYPE; 10018c2ecf20Sopenharmony_ci } 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci return 0; 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci/** 10078c2ecf20Sopenharmony_ci * fm10k_mbx_create_reply - Generate reply based on state and remote head 10088c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 10098c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 10108c2ecf20Sopenharmony_ci * @head: acknowledgement number 10118c2ecf20Sopenharmony_ci * 10128c2ecf20Sopenharmony_ci * This function will generate an outgoing message based on the current 10138c2ecf20Sopenharmony_ci * mailbox state and the remote FIFO head. It will return the length 10148c2ecf20Sopenharmony_ci * of the outgoing message excluding header on success, and a negative value 10158c2ecf20Sopenharmony_ci * on error. 10168c2ecf20Sopenharmony_ci **/ 10178c2ecf20Sopenharmony_cistatic s32 fm10k_mbx_create_reply(struct fm10k_hw *hw, 10188c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx, u16 head) 10198c2ecf20Sopenharmony_ci{ 10208c2ecf20Sopenharmony_ci switch (mbx->state) { 10218c2ecf20Sopenharmony_ci case FM10K_STATE_OPEN: 10228c2ecf20Sopenharmony_ci case FM10K_STATE_DISCONNECT: 10238c2ecf20Sopenharmony_ci /* update our checksum for the outgoing data */ 10248c2ecf20Sopenharmony_ci fm10k_mbx_update_local_crc(mbx, head); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* as long as other end recognizes us keep sending data */ 10278c2ecf20Sopenharmony_ci fm10k_mbx_pull_head(hw, mbx, head); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci /* generate new header based on data */ 10308c2ecf20Sopenharmony_ci if (mbx->tail_len || (mbx->state == FM10K_STATE_OPEN)) 10318c2ecf20Sopenharmony_ci fm10k_mbx_create_data_hdr(mbx); 10328c2ecf20Sopenharmony_ci else 10338c2ecf20Sopenharmony_ci fm10k_mbx_create_disconnect_hdr(mbx); 10348c2ecf20Sopenharmony_ci break; 10358c2ecf20Sopenharmony_ci case FM10K_STATE_CONNECT: 10368c2ecf20Sopenharmony_ci /* send disconnect even if we aren't connected */ 10378c2ecf20Sopenharmony_ci fm10k_mbx_create_connect_hdr(mbx); 10388c2ecf20Sopenharmony_ci break; 10398c2ecf20Sopenharmony_ci case FM10K_STATE_CLOSED: 10408c2ecf20Sopenharmony_ci /* generate new header based on data */ 10418c2ecf20Sopenharmony_ci fm10k_mbx_create_disconnect_hdr(mbx); 10428c2ecf20Sopenharmony_ci default: 10438c2ecf20Sopenharmony_ci break; 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci return 0; 10478c2ecf20Sopenharmony_ci} 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci/** 10508c2ecf20Sopenharmony_ci * fm10k_mbx_reset_work- Reset internal pointers for any pending work 10518c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 10528c2ecf20Sopenharmony_ci * 10538c2ecf20Sopenharmony_ci * This function will reset all internal pointers so any work in progress 10548c2ecf20Sopenharmony_ci * is dropped. This call should occur every time we transition from the 10558c2ecf20Sopenharmony_ci * open state to the connect state. 10568c2ecf20Sopenharmony_ci **/ 10578c2ecf20Sopenharmony_cistatic void fm10k_mbx_reset_work(struct fm10k_mbx_info *mbx) 10588c2ecf20Sopenharmony_ci{ 10598c2ecf20Sopenharmony_ci u16 len, head, ack; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci /* reset our outgoing max size back to Rx limits */ 10628c2ecf20Sopenharmony_ci mbx->max_size = mbx->rx.size - 1; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci /* update mbx->pulled to account for tail_len and ack */ 10658c2ecf20Sopenharmony_ci head = FM10K_MSG_HDR_FIELD_GET(mbx->mbx_hdr, HEAD); 10668c2ecf20Sopenharmony_ci ack = fm10k_mbx_index_len(mbx, head, mbx->tail); 10678c2ecf20Sopenharmony_ci mbx->pulled += mbx->tail_len - ack; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci /* now drop any messages which have started or finished transmitting */ 10708c2ecf20Sopenharmony_ci while (fm10k_fifo_head_len(&mbx->tx) && mbx->pulled) { 10718c2ecf20Sopenharmony_ci len = fm10k_fifo_head_drop(&mbx->tx); 10728c2ecf20Sopenharmony_ci mbx->tx_dropped++; 10738c2ecf20Sopenharmony_ci if (mbx->pulled >= len) 10748c2ecf20Sopenharmony_ci mbx->pulled -= len; 10758c2ecf20Sopenharmony_ci else 10768c2ecf20Sopenharmony_ci mbx->pulled = 0; 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci /* just do a quick resysnc to start of message */ 10808c2ecf20Sopenharmony_ci mbx->pushed = 0; 10818c2ecf20Sopenharmony_ci mbx->pulled = 0; 10828c2ecf20Sopenharmony_ci mbx->tail_len = 0; 10838c2ecf20Sopenharmony_ci mbx->head_len = 0; 10848c2ecf20Sopenharmony_ci mbx->rx.tail = 0; 10858c2ecf20Sopenharmony_ci mbx->rx.head = 0; 10868c2ecf20Sopenharmony_ci} 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci/** 10898c2ecf20Sopenharmony_ci * fm10k_mbx_update_max_size - Update the max_size and drop any large messages 10908c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 10918c2ecf20Sopenharmony_ci * @size: new value for max_size 10928c2ecf20Sopenharmony_ci * 10938c2ecf20Sopenharmony_ci * This function updates the max_size value and drops any outgoing messages 10948c2ecf20Sopenharmony_ci * at the head of the Tx FIFO if they are larger than max_size. It does not 10958c2ecf20Sopenharmony_ci * drop all messages, as this is too difficult to parse and remove them from 10968c2ecf20Sopenharmony_ci * the FIFO. Instead, rely on the checking to ensure that messages larger 10978c2ecf20Sopenharmony_ci * than max_size aren't pushed into the memory buffer. 10988c2ecf20Sopenharmony_ci **/ 10998c2ecf20Sopenharmony_cistatic void fm10k_mbx_update_max_size(struct fm10k_mbx_info *mbx, u16 size) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci u16 len; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci mbx->max_size = size; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci /* flush any oversized messages from the queue */ 11068c2ecf20Sopenharmony_ci for (len = fm10k_fifo_head_len(&mbx->tx); 11078c2ecf20Sopenharmony_ci len > size; 11088c2ecf20Sopenharmony_ci len = fm10k_fifo_head_len(&mbx->tx)) { 11098c2ecf20Sopenharmony_ci fm10k_fifo_head_drop(&mbx->tx); 11108c2ecf20Sopenharmony_ci mbx->tx_dropped++; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci} 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci/** 11158c2ecf20Sopenharmony_ci * fm10k_mbx_connect_reset - Reset following request for reset 11168c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 11178c2ecf20Sopenharmony_ci * 11188c2ecf20Sopenharmony_ci * This function resets the mailbox to either a disconnected state 11198c2ecf20Sopenharmony_ci * or a connect state depending on the current mailbox state 11208c2ecf20Sopenharmony_ci **/ 11218c2ecf20Sopenharmony_cistatic void fm10k_mbx_connect_reset(struct fm10k_mbx_info *mbx) 11228c2ecf20Sopenharmony_ci{ 11238c2ecf20Sopenharmony_ci /* just do a quick resysnc to start of frame */ 11248c2ecf20Sopenharmony_ci fm10k_mbx_reset_work(mbx); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci /* reset CRC seeds */ 11278c2ecf20Sopenharmony_ci mbx->local = FM10K_MBX_CRC_SEED; 11288c2ecf20Sopenharmony_ci mbx->remote = FM10K_MBX_CRC_SEED; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci /* we cannot exit connect until the size is good */ 11318c2ecf20Sopenharmony_ci if (mbx->state == FM10K_STATE_OPEN) 11328c2ecf20Sopenharmony_ci mbx->state = FM10K_STATE_CONNECT; 11338c2ecf20Sopenharmony_ci else 11348c2ecf20Sopenharmony_ci mbx->state = FM10K_STATE_CLOSED; 11358c2ecf20Sopenharmony_ci} 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci/** 11388c2ecf20Sopenharmony_ci * fm10k_mbx_process_connect - Process connect header 11398c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 11408c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 11418c2ecf20Sopenharmony_ci * 11428c2ecf20Sopenharmony_ci * This function will read an incoming connect header and reply with the 11438c2ecf20Sopenharmony_ci * appropriate message. It will return a value indicating the number of 11448c2ecf20Sopenharmony_ci * data DWORDs on success, or will return a negative value on failure. 11458c2ecf20Sopenharmony_ci **/ 11468c2ecf20Sopenharmony_cistatic s32 fm10k_mbx_process_connect(struct fm10k_hw *hw, 11478c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci const enum fm10k_mbx_state state = mbx->state; 11508c2ecf20Sopenharmony_ci const u32 *hdr = &mbx->mbx_hdr; 11518c2ecf20Sopenharmony_ci u16 size, head; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci /* we will need to pull all of the fields for verification */ 11548c2ecf20Sopenharmony_ci size = FM10K_MSG_HDR_FIELD_GET(*hdr, CONNECT_SIZE); 11558c2ecf20Sopenharmony_ci head = FM10K_MSG_HDR_FIELD_GET(*hdr, HEAD); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci switch (state) { 11588c2ecf20Sopenharmony_ci case FM10K_STATE_DISCONNECT: 11598c2ecf20Sopenharmony_ci case FM10K_STATE_OPEN: 11608c2ecf20Sopenharmony_ci /* reset any in-progress work */ 11618c2ecf20Sopenharmony_ci fm10k_mbx_connect_reset(mbx); 11628c2ecf20Sopenharmony_ci break; 11638c2ecf20Sopenharmony_ci case FM10K_STATE_CONNECT: 11648c2ecf20Sopenharmony_ci /* we cannot exit connect until the size is good */ 11658c2ecf20Sopenharmony_ci if (size > mbx->rx.size) { 11668c2ecf20Sopenharmony_ci mbx->max_size = mbx->rx.size - 1; 11678c2ecf20Sopenharmony_ci } else { 11688c2ecf20Sopenharmony_ci /* record the remote system requesting connection */ 11698c2ecf20Sopenharmony_ci mbx->state = FM10K_STATE_OPEN; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci fm10k_mbx_update_max_size(mbx, size); 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci break; 11748c2ecf20Sopenharmony_ci default: 11758c2ecf20Sopenharmony_ci break; 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci /* align our tail index to remote head index */ 11798c2ecf20Sopenharmony_ci mbx->tail = head; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci return fm10k_mbx_create_reply(hw, mbx, head); 11828c2ecf20Sopenharmony_ci} 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci/** 11858c2ecf20Sopenharmony_ci * fm10k_mbx_process_data - Process data header 11868c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 11878c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 11888c2ecf20Sopenharmony_ci * 11898c2ecf20Sopenharmony_ci * This function will read an incoming data header and reply with the 11908c2ecf20Sopenharmony_ci * appropriate message. It will return a value indicating the number of 11918c2ecf20Sopenharmony_ci * data DWORDs on success, or will return a negative value on failure. 11928c2ecf20Sopenharmony_ci **/ 11938c2ecf20Sopenharmony_cistatic s32 fm10k_mbx_process_data(struct fm10k_hw *hw, 11948c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx) 11958c2ecf20Sopenharmony_ci{ 11968c2ecf20Sopenharmony_ci const u32 *hdr = &mbx->mbx_hdr; 11978c2ecf20Sopenharmony_ci u16 head, tail; 11988c2ecf20Sopenharmony_ci s32 err; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci /* we will need to pull all of the fields for verification */ 12018c2ecf20Sopenharmony_ci head = FM10K_MSG_HDR_FIELD_GET(*hdr, HEAD); 12028c2ecf20Sopenharmony_ci tail = FM10K_MSG_HDR_FIELD_GET(*hdr, TAIL); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci /* if we are in connect just update our data and go */ 12058c2ecf20Sopenharmony_ci if (mbx->state == FM10K_STATE_CONNECT) { 12068c2ecf20Sopenharmony_ci mbx->tail = head; 12078c2ecf20Sopenharmony_ci mbx->state = FM10K_STATE_OPEN; 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci /* abort on message size errors */ 12118c2ecf20Sopenharmony_ci err = fm10k_mbx_push_tail(hw, mbx, tail); 12128c2ecf20Sopenharmony_ci if (err < 0) 12138c2ecf20Sopenharmony_ci return err; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci /* verify the checksum on the incoming data */ 12168c2ecf20Sopenharmony_ci err = fm10k_mbx_verify_remote_crc(mbx); 12178c2ecf20Sopenharmony_ci if (err) 12188c2ecf20Sopenharmony_ci return err; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci /* process messages if we have received any */ 12218c2ecf20Sopenharmony_ci fm10k_mbx_dequeue_rx(hw, mbx); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci return fm10k_mbx_create_reply(hw, mbx, head); 12248c2ecf20Sopenharmony_ci} 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci/** 12278c2ecf20Sopenharmony_ci * fm10k_mbx_process_disconnect - Process disconnect header 12288c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 12298c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 12308c2ecf20Sopenharmony_ci * 12318c2ecf20Sopenharmony_ci * This function will read an incoming disconnect header and reply with the 12328c2ecf20Sopenharmony_ci * appropriate message. It will return a value indicating the number of 12338c2ecf20Sopenharmony_ci * data DWORDs on success, or will return a negative value on failure. 12348c2ecf20Sopenharmony_ci **/ 12358c2ecf20Sopenharmony_cistatic s32 fm10k_mbx_process_disconnect(struct fm10k_hw *hw, 12368c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx) 12378c2ecf20Sopenharmony_ci{ 12388c2ecf20Sopenharmony_ci const enum fm10k_mbx_state state = mbx->state; 12398c2ecf20Sopenharmony_ci const u32 *hdr = &mbx->mbx_hdr; 12408c2ecf20Sopenharmony_ci u16 head; 12418c2ecf20Sopenharmony_ci s32 err; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci /* we will need to pull the header field for verification */ 12448c2ecf20Sopenharmony_ci head = FM10K_MSG_HDR_FIELD_GET(*hdr, HEAD); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci /* We should not be receiving disconnect if Rx is incomplete */ 12478c2ecf20Sopenharmony_ci if (mbx->pushed) 12488c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_TAIL; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci /* we have already verified mbx->head == tail so we know this is 0 */ 12518c2ecf20Sopenharmony_ci mbx->head_len = 0; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci /* verify the checksum on the incoming header is correct */ 12548c2ecf20Sopenharmony_ci err = fm10k_mbx_verify_remote_crc(mbx); 12558c2ecf20Sopenharmony_ci if (err) 12568c2ecf20Sopenharmony_ci return err; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci switch (state) { 12598c2ecf20Sopenharmony_ci case FM10K_STATE_DISCONNECT: 12608c2ecf20Sopenharmony_ci case FM10K_STATE_OPEN: 12618c2ecf20Sopenharmony_ci /* state doesn't change if we still have work to do */ 12628c2ecf20Sopenharmony_ci if (!fm10k_mbx_tx_complete(mbx)) 12638c2ecf20Sopenharmony_ci break; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci /* verify the head indicates we completed all transmits */ 12668c2ecf20Sopenharmony_ci if (head != mbx->tail) 12678c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_HEAD; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci /* reset any in-progress work */ 12708c2ecf20Sopenharmony_ci fm10k_mbx_connect_reset(mbx); 12718c2ecf20Sopenharmony_ci break; 12728c2ecf20Sopenharmony_ci default: 12738c2ecf20Sopenharmony_ci break; 12748c2ecf20Sopenharmony_ci } 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci return fm10k_mbx_create_reply(hw, mbx, head); 12778c2ecf20Sopenharmony_ci} 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci/** 12808c2ecf20Sopenharmony_ci * fm10k_mbx_process_error - Process error header 12818c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 12828c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 12838c2ecf20Sopenharmony_ci * 12848c2ecf20Sopenharmony_ci * This function will read an incoming error header and reply with the 12858c2ecf20Sopenharmony_ci * appropriate message. It will return a value indicating the number of 12868c2ecf20Sopenharmony_ci * data DWORDs on success, or will return a negative value on failure. 12878c2ecf20Sopenharmony_ci **/ 12888c2ecf20Sopenharmony_cistatic s32 fm10k_mbx_process_error(struct fm10k_hw *hw, 12898c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx) 12908c2ecf20Sopenharmony_ci{ 12918c2ecf20Sopenharmony_ci const u32 *hdr = &mbx->mbx_hdr; 12928c2ecf20Sopenharmony_ci u16 head; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci /* we will need to pull all of the fields for verification */ 12958c2ecf20Sopenharmony_ci head = FM10K_MSG_HDR_FIELD_GET(*hdr, HEAD); 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci switch (mbx->state) { 12988c2ecf20Sopenharmony_ci case FM10K_STATE_OPEN: 12998c2ecf20Sopenharmony_ci case FM10K_STATE_DISCONNECT: 13008c2ecf20Sopenharmony_ci /* flush any uncompleted work */ 13018c2ecf20Sopenharmony_ci fm10k_mbx_reset_work(mbx); 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci /* reset CRC seeds */ 13048c2ecf20Sopenharmony_ci mbx->local = FM10K_MBX_CRC_SEED; 13058c2ecf20Sopenharmony_ci mbx->remote = FM10K_MBX_CRC_SEED; 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci /* reset tail index and size to prepare for reconnect */ 13088c2ecf20Sopenharmony_ci mbx->tail = head; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci /* if open then reset max_size and go back to connect */ 13118c2ecf20Sopenharmony_ci if (mbx->state == FM10K_STATE_OPEN) { 13128c2ecf20Sopenharmony_ci mbx->state = FM10K_STATE_CONNECT; 13138c2ecf20Sopenharmony_ci break; 13148c2ecf20Sopenharmony_ci } 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci /* send a connect message to get data flowing again */ 13178c2ecf20Sopenharmony_ci fm10k_mbx_create_connect_hdr(mbx); 13188c2ecf20Sopenharmony_ci return 0; 13198c2ecf20Sopenharmony_ci default: 13208c2ecf20Sopenharmony_ci break; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci return fm10k_mbx_create_reply(hw, mbx, mbx->tail); 13248c2ecf20Sopenharmony_ci} 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci/** 13278c2ecf20Sopenharmony_ci * fm10k_mbx_process - Process mailbox interrupt 13288c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 13298c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 13308c2ecf20Sopenharmony_ci * 13318c2ecf20Sopenharmony_ci * This function will process incoming mailbox events and generate mailbox 13328c2ecf20Sopenharmony_ci * replies. It will return a value indicating the number of DWORDs 13338c2ecf20Sopenharmony_ci * transmitted excluding header on success or a negative value on error. 13348c2ecf20Sopenharmony_ci **/ 13358c2ecf20Sopenharmony_cistatic s32 fm10k_mbx_process(struct fm10k_hw *hw, 13368c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx) 13378c2ecf20Sopenharmony_ci{ 13388c2ecf20Sopenharmony_ci s32 err; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci /* we do not read mailbox if closed */ 13418c2ecf20Sopenharmony_ci if (mbx->state == FM10K_STATE_CLOSED) 13428c2ecf20Sopenharmony_ci return 0; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci /* copy data from mailbox */ 13458c2ecf20Sopenharmony_ci err = fm10k_mbx_read(hw, mbx); 13468c2ecf20Sopenharmony_ci if (err) 13478c2ecf20Sopenharmony_ci return err; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci /* validate type, source, and destination */ 13508c2ecf20Sopenharmony_ci err = fm10k_mbx_validate_msg_hdr(mbx); 13518c2ecf20Sopenharmony_ci if (err < 0) 13528c2ecf20Sopenharmony_ci goto msg_err; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci switch (FM10K_MSG_HDR_FIELD_GET(mbx->mbx_hdr, TYPE)) { 13558c2ecf20Sopenharmony_ci case FM10K_MSG_CONNECT: 13568c2ecf20Sopenharmony_ci err = fm10k_mbx_process_connect(hw, mbx); 13578c2ecf20Sopenharmony_ci break; 13588c2ecf20Sopenharmony_ci case FM10K_MSG_DATA: 13598c2ecf20Sopenharmony_ci err = fm10k_mbx_process_data(hw, mbx); 13608c2ecf20Sopenharmony_ci break; 13618c2ecf20Sopenharmony_ci case FM10K_MSG_DISCONNECT: 13628c2ecf20Sopenharmony_ci err = fm10k_mbx_process_disconnect(hw, mbx); 13638c2ecf20Sopenharmony_ci break; 13648c2ecf20Sopenharmony_ci case FM10K_MSG_ERROR: 13658c2ecf20Sopenharmony_ci err = fm10k_mbx_process_error(hw, mbx); 13668c2ecf20Sopenharmony_ci break; 13678c2ecf20Sopenharmony_ci default: 13688c2ecf20Sopenharmony_ci err = FM10K_MBX_ERR_TYPE; 13698c2ecf20Sopenharmony_ci break; 13708c2ecf20Sopenharmony_ci } 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_cimsg_err: 13738c2ecf20Sopenharmony_ci /* notify partner of errors on our end */ 13748c2ecf20Sopenharmony_ci if (err < 0) 13758c2ecf20Sopenharmony_ci fm10k_mbx_create_error_msg(mbx, err); 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci /* copy data from mailbox */ 13788c2ecf20Sopenharmony_ci fm10k_mbx_write(hw, mbx); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci return err; 13818c2ecf20Sopenharmony_ci} 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci/** 13848c2ecf20Sopenharmony_ci * fm10k_mbx_disconnect - Shutdown mailbox connection 13858c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 13868c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 13878c2ecf20Sopenharmony_ci * 13888c2ecf20Sopenharmony_ci * This function will shut down the mailbox. It places the mailbox first 13898c2ecf20Sopenharmony_ci * in the disconnect state, it then allows up to a predefined timeout for 13908c2ecf20Sopenharmony_ci * the mailbox to transition to close on its own. If this does not occur 13918c2ecf20Sopenharmony_ci * then the mailbox will be forced into the closed state. 13928c2ecf20Sopenharmony_ci * 13938c2ecf20Sopenharmony_ci * Any mailbox transactions not completed before calling this function 13948c2ecf20Sopenharmony_ci * are not guaranteed to complete and may be dropped. 13958c2ecf20Sopenharmony_ci **/ 13968c2ecf20Sopenharmony_cistatic void fm10k_mbx_disconnect(struct fm10k_hw *hw, 13978c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx) 13988c2ecf20Sopenharmony_ci{ 13998c2ecf20Sopenharmony_ci int timeout = mbx->timeout ? FM10K_MBX_DISCONNECT_TIMEOUT : 0; 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci /* Place mbx in ready to disconnect state */ 14028c2ecf20Sopenharmony_ci mbx->state = FM10K_STATE_DISCONNECT; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci /* trigger interrupt to start shutdown process */ 14058c2ecf20Sopenharmony_ci fm10k_write_reg(hw, mbx->mbx_reg, FM10K_MBX_REQ | 14068c2ecf20Sopenharmony_ci FM10K_MBX_INTERRUPT_DISABLE); 14078c2ecf20Sopenharmony_ci do { 14088c2ecf20Sopenharmony_ci udelay(FM10K_MBX_POLL_DELAY); 14098c2ecf20Sopenharmony_ci mbx->ops.process(hw, mbx); 14108c2ecf20Sopenharmony_ci timeout -= FM10K_MBX_POLL_DELAY; 14118c2ecf20Sopenharmony_ci } while ((timeout > 0) && (mbx->state != FM10K_STATE_CLOSED)); 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci /* in case we didn't close, just force the mailbox into shutdown and 14148c2ecf20Sopenharmony_ci * drop all left over messages in the FIFO. 14158c2ecf20Sopenharmony_ci */ 14168c2ecf20Sopenharmony_ci fm10k_mbx_connect_reset(mbx); 14178c2ecf20Sopenharmony_ci fm10k_fifo_drop_all(&mbx->tx); 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci fm10k_write_reg(hw, mbx->mbmem_reg, 0); 14208c2ecf20Sopenharmony_ci} 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci/** 14238c2ecf20Sopenharmony_ci * fm10k_mbx_connect - Start mailbox connection 14248c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 14258c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 14268c2ecf20Sopenharmony_ci * 14278c2ecf20Sopenharmony_ci * This function will initiate a mailbox connection. It will populate the 14288c2ecf20Sopenharmony_ci * mailbox with a broadcast connect message and then initialize the lock. 14298c2ecf20Sopenharmony_ci * This is safe since the connect message is a single DWORD so the mailbox 14308c2ecf20Sopenharmony_ci * transaction is guaranteed to be atomic. 14318c2ecf20Sopenharmony_ci * 14328c2ecf20Sopenharmony_ci * This function will return an error if the mailbox has not been initiated 14338c2ecf20Sopenharmony_ci * or is currently in use. 14348c2ecf20Sopenharmony_ci **/ 14358c2ecf20Sopenharmony_cistatic s32 fm10k_mbx_connect(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx) 14368c2ecf20Sopenharmony_ci{ 14378c2ecf20Sopenharmony_ci /* we cannot connect an uninitialized mailbox */ 14388c2ecf20Sopenharmony_ci if (!mbx->rx.buffer) 14398c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_NO_SPACE; 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci /* we cannot connect an already connected mailbox */ 14428c2ecf20Sopenharmony_ci if (mbx->state != FM10K_STATE_CLOSED) 14438c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_BUSY; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci /* mailbox timeout can now become active */ 14468c2ecf20Sopenharmony_ci mbx->timeout = FM10K_MBX_INIT_TIMEOUT; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci /* Place mbx in ready to connect state */ 14498c2ecf20Sopenharmony_ci mbx->state = FM10K_STATE_CONNECT; 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci fm10k_mbx_reset_work(mbx); 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci /* initialize header of remote mailbox */ 14548c2ecf20Sopenharmony_ci fm10k_mbx_create_fake_disconnect_hdr(mbx); 14558c2ecf20Sopenharmony_ci fm10k_write_reg(hw, mbx->mbmem_reg ^ mbx->mbmem_len, mbx->mbx_hdr); 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci /* enable interrupt and notify other party of new message */ 14588c2ecf20Sopenharmony_ci mbx->mbx_lock = FM10K_MBX_REQ_INTERRUPT | FM10K_MBX_ACK_INTERRUPT | 14598c2ecf20Sopenharmony_ci FM10K_MBX_INTERRUPT_ENABLE; 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci /* generate and load connect header into mailbox */ 14628c2ecf20Sopenharmony_ci fm10k_mbx_create_connect_hdr(mbx); 14638c2ecf20Sopenharmony_ci fm10k_mbx_write(hw, mbx); 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci return 0; 14668c2ecf20Sopenharmony_ci} 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci/** 14698c2ecf20Sopenharmony_ci * fm10k_mbx_validate_handlers - Validate layout of message parsing data 14708c2ecf20Sopenharmony_ci * @msg_data: handlers for mailbox events 14718c2ecf20Sopenharmony_ci * 14728c2ecf20Sopenharmony_ci * This function validates the layout of the message parsing data. This 14738c2ecf20Sopenharmony_ci * should be mostly static, but it is important to catch any errors that 14748c2ecf20Sopenharmony_ci * are made when constructing the parsers. 14758c2ecf20Sopenharmony_ci **/ 14768c2ecf20Sopenharmony_cistatic s32 fm10k_mbx_validate_handlers(const struct fm10k_msg_data *msg_data) 14778c2ecf20Sopenharmony_ci{ 14788c2ecf20Sopenharmony_ci const struct fm10k_tlv_attr *attr; 14798c2ecf20Sopenharmony_ci unsigned int id; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci /* Allow NULL mailboxes that transmit but don't receive */ 14828c2ecf20Sopenharmony_ci if (!msg_data) 14838c2ecf20Sopenharmony_ci return 0; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci while (msg_data->id != FM10K_TLV_ERROR) { 14868c2ecf20Sopenharmony_ci /* all messages should have a function handler */ 14878c2ecf20Sopenharmony_ci if (!msg_data->func) 14888c2ecf20Sopenharmony_ci return FM10K_ERR_PARAM; 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci /* parser is optional */ 14918c2ecf20Sopenharmony_ci attr = msg_data->attr; 14928c2ecf20Sopenharmony_ci if (attr) { 14938c2ecf20Sopenharmony_ci while (attr->id != FM10K_TLV_ERROR) { 14948c2ecf20Sopenharmony_ci id = attr->id; 14958c2ecf20Sopenharmony_ci attr++; 14968c2ecf20Sopenharmony_ci /* ID should always be increasing */ 14978c2ecf20Sopenharmony_ci if (id >= attr->id) 14988c2ecf20Sopenharmony_ci return FM10K_ERR_PARAM; 14998c2ecf20Sopenharmony_ci /* ID should fit in results array */ 15008c2ecf20Sopenharmony_ci if (id >= FM10K_TLV_RESULTS_MAX) 15018c2ecf20Sopenharmony_ci return FM10K_ERR_PARAM; 15028c2ecf20Sopenharmony_ci } 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci /* verify terminator is in the list */ 15058c2ecf20Sopenharmony_ci if (attr->id != FM10K_TLV_ERROR) 15068c2ecf20Sopenharmony_ci return FM10K_ERR_PARAM; 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci id = msg_data->id; 15108c2ecf20Sopenharmony_ci msg_data++; 15118c2ecf20Sopenharmony_ci /* ID should always be increasing */ 15128c2ecf20Sopenharmony_ci if (id >= msg_data->id) 15138c2ecf20Sopenharmony_ci return FM10K_ERR_PARAM; 15148c2ecf20Sopenharmony_ci } 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci /* verify terminator is in the list */ 15178c2ecf20Sopenharmony_ci if ((msg_data->id != FM10K_TLV_ERROR) || !msg_data->func) 15188c2ecf20Sopenharmony_ci return FM10K_ERR_PARAM; 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci return 0; 15218c2ecf20Sopenharmony_ci} 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci/** 15248c2ecf20Sopenharmony_ci * fm10k_mbx_register_handlers - Register a set of handler ops for mailbox 15258c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 15268c2ecf20Sopenharmony_ci * @msg_data: handlers for mailbox events 15278c2ecf20Sopenharmony_ci * 15288c2ecf20Sopenharmony_ci * This function associates a set of message handling ops with a mailbox. 15298c2ecf20Sopenharmony_ci **/ 15308c2ecf20Sopenharmony_cistatic s32 fm10k_mbx_register_handlers(struct fm10k_mbx_info *mbx, 15318c2ecf20Sopenharmony_ci const struct fm10k_msg_data *msg_data) 15328c2ecf20Sopenharmony_ci{ 15338c2ecf20Sopenharmony_ci /* validate layout of handlers before assigning them */ 15348c2ecf20Sopenharmony_ci if (fm10k_mbx_validate_handlers(msg_data)) 15358c2ecf20Sopenharmony_ci return FM10K_ERR_PARAM; 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci /* initialize the message handlers */ 15388c2ecf20Sopenharmony_ci mbx->msg_data = msg_data; 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci return 0; 15418c2ecf20Sopenharmony_ci} 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci/** 15448c2ecf20Sopenharmony_ci * fm10k_pfvf_mbx_init - Initialize mailbox memory for PF/VF mailbox 15458c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 15468c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 15478c2ecf20Sopenharmony_ci * @msg_data: handlers for mailbox events 15488c2ecf20Sopenharmony_ci * @id: ID reference for PF as it supports up to 64 PF/VF mailboxes 15498c2ecf20Sopenharmony_ci * 15508c2ecf20Sopenharmony_ci * This function initializes the mailbox for use. It will split the 15518c2ecf20Sopenharmony_ci * buffer provided and use that to populate both the Tx and Rx FIFO by 15528c2ecf20Sopenharmony_ci * evenly splitting it. In order to allow for easy masking of head/tail 15538c2ecf20Sopenharmony_ci * the value reported in size must be a power of 2 and is reported in 15548c2ecf20Sopenharmony_ci * DWORDs, not bytes. Any invalid values will cause the mailbox to return 15558c2ecf20Sopenharmony_ci * error. 15568c2ecf20Sopenharmony_ci **/ 15578c2ecf20Sopenharmony_cis32 fm10k_pfvf_mbx_init(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx, 15588c2ecf20Sopenharmony_ci const struct fm10k_msg_data *msg_data, u8 id) 15598c2ecf20Sopenharmony_ci{ 15608c2ecf20Sopenharmony_ci /* initialize registers */ 15618c2ecf20Sopenharmony_ci switch (hw->mac.type) { 15628c2ecf20Sopenharmony_ci case fm10k_mac_vf: 15638c2ecf20Sopenharmony_ci mbx->mbx_reg = FM10K_VFMBX; 15648c2ecf20Sopenharmony_ci mbx->mbmem_reg = FM10K_VFMBMEM(FM10K_VFMBMEM_VF_XOR); 15658c2ecf20Sopenharmony_ci break; 15668c2ecf20Sopenharmony_ci case fm10k_mac_pf: 15678c2ecf20Sopenharmony_ci /* there are only 64 VF <-> PF mailboxes */ 15688c2ecf20Sopenharmony_ci if (id < 64) { 15698c2ecf20Sopenharmony_ci mbx->mbx_reg = FM10K_MBX(id); 15708c2ecf20Sopenharmony_ci mbx->mbmem_reg = FM10K_MBMEM_VF(id, 0); 15718c2ecf20Sopenharmony_ci break; 15728c2ecf20Sopenharmony_ci } 15738c2ecf20Sopenharmony_ci fallthrough; 15748c2ecf20Sopenharmony_ci default: 15758c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_NO_MBX; 15768c2ecf20Sopenharmony_ci } 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci /* start out in closed state */ 15798c2ecf20Sopenharmony_ci mbx->state = FM10K_STATE_CLOSED; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci /* validate layout of handlers before assigning them */ 15828c2ecf20Sopenharmony_ci if (fm10k_mbx_validate_handlers(msg_data)) 15838c2ecf20Sopenharmony_ci return FM10K_ERR_PARAM; 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci /* initialize the message handlers */ 15868c2ecf20Sopenharmony_ci mbx->msg_data = msg_data; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci /* start mailbox as timed out and let the reset_hw call 15898c2ecf20Sopenharmony_ci * set the timeout value to begin communications 15908c2ecf20Sopenharmony_ci */ 15918c2ecf20Sopenharmony_ci mbx->timeout = 0; 15928c2ecf20Sopenharmony_ci mbx->udelay = FM10K_MBX_INIT_DELAY; 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci /* initialize tail and head */ 15958c2ecf20Sopenharmony_ci mbx->tail = 1; 15968c2ecf20Sopenharmony_ci mbx->head = 1; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci /* initialize CRC seeds */ 15998c2ecf20Sopenharmony_ci mbx->local = FM10K_MBX_CRC_SEED; 16008c2ecf20Sopenharmony_ci mbx->remote = FM10K_MBX_CRC_SEED; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci /* Split buffer for use by Tx/Rx FIFOs */ 16038c2ecf20Sopenharmony_ci mbx->max_size = FM10K_MBX_MSG_MAX_SIZE; 16048c2ecf20Sopenharmony_ci mbx->mbmem_len = FM10K_VFMBMEM_VF_XOR; 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci /* initialize the FIFOs, sizes are in 4 byte increments */ 16078c2ecf20Sopenharmony_ci fm10k_fifo_init(&mbx->tx, mbx->buffer, FM10K_MBX_TX_BUFFER_SIZE); 16088c2ecf20Sopenharmony_ci fm10k_fifo_init(&mbx->rx, &mbx->buffer[FM10K_MBX_TX_BUFFER_SIZE], 16098c2ecf20Sopenharmony_ci FM10K_MBX_RX_BUFFER_SIZE); 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci /* initialize function pointers */ 16128c2ecf20Sopenharmony_ci mbx->ops.connect = fm10k_mbx_connect; 16138c2ecf20Sopenharmony_ci mbx->ops.disconnect = fm10k_mbx_disconnect; 16148c2ecf20Sopenharmony_ci mbx->ops.rx_ready = fm10k_mbx_rx_ready; 16158c2ecf20Sopenharmony_ci mbx->ops.tx_ready = fm10k_mbx_tx_ready; 16168c2ecf20Sopenharmony_ci mbx->ops.tx_complete = fm10k_mbx_tx_complete; 16178c2ecf20Sopenharmony_ci mbx->ops.enqueue_tx = fm10k_mbx_enqueue_tx; 16188c2ecf20Sopenharmony_ci mbx->ops.process = fm10k_mbx_process; 16198c2ecf20Sopenharmony_ci mbx->ops.register_handlers = fm10k_mbx_register_handlers; 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci return 0; 16228c2ecf20Sopenharmony_ci} 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci/** 16258c2ecf20Sopenharmony_ci * fm10k_sm_mbx_create_data_hdr - Generate a mailbox header for local FIFO 16268c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 16278c2ecf20Sopenharmony_ci * 16288c2ecf20Sopenharmony_ci * This function returns a data mailbox header 16298c2ecf20Sopenharmony_ci **/ 16308c2ecf20Sopenharmony_cistatic void fm10k_sm_mbx_create_data_hdr(struct fm10k_mbx_info *mbx) 16318c2ecf20Sopenharmony_ci{ 16328c2ecf20Sopenharmony_ci if (mbx->tail_len) 16338c2ecf20Sopenharmony_ci mbx->mbx_lock |= FM10K_MBX_REQ; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci mbx->mbx_hdr = FM10K_MSG_HDR_FIELD_SET(mbx->tail, SM_TAIL) | 16368c2ecf20Sopenharmony_ci FM10K_MSG_HDR_FIELD_SET(mbx->remote, SM_VER) | 16378c2ecf20Sopenharmony_ci FM10K_MSG_HDR_FIELD_SET(mbx->head, SM_HEAD); 16388c2ecf20Sopenharmony_ci} 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci/** 16418c2ecf20Sopenharmony_ci * fm10k_sm_mbx_create_connect_hdr - Generate a mailbox header for local FIFO 16428c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 16438c2ecf20Sopenharmony_ci * @err: error flags to report if any 16448c2ecf20Sopenharmony_ci * 16458c2ecf20Sopenharmony_ci * This function returns a connection mailbox header 16468c2ecf20Sopenharmony_ci **/ 16478c2ecf20Sopenharmony_cistatic void fm10k_sm_mbx_create_connect_hdr(struct fm10k_mbx_info *mbx, u8 err) 16488c2ecf20Sopenharmony_ci{ 16498c2ecf20Sopenharmony_ci if (mbx->local) 16508c2ecf20Sopenharmony_ci mbx->mbx_lock |= FM10K_MBX_REQ; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci mbx->mbx_hdr = FM10K_MSG_HDR_FIELD_SET(mbx->tail, SM_TAIL) | 16538c2ecf20Sopenharmony_ci FM10K_MSG_HDR_FIELD_SET(mbx->remote, SM_VER) | 16548c2ecf20Sopenharmony_ci FM10K_MSG_HDR_FIELD_SET(mbx->head, SM_HEAD) | 16558c2ecf20Sopenharmony_ci FM10K_MSG_HDR_FIELD_SET(err, SM_ERR); 16568c2ecf20Sopenharmony_ci} 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci/** 16598c2ecf20Sopenharmony_ci * fm10k_sm_mbx_connect_reset - Reset following request for reset 16608c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 16618c2ecf20Sopenharmony_ci * 16628c2ecf20Sopenharmony_ci * This function resets the mailbox to a just connected state 16638c2ecf20Sopenharmony_ci **/ 16648c2ecf20Sopenharmony_cistatic void fm10k_sm_mbx_connect_reset(struct fm10k_mbx_info *mbx) 16658c2ecf20Sopenharmony_ci{ 16668c2ecf20Sopenharmony_ci /* flush any uncompleted work */ 16678c2ecf20Sopenharmony_ci fm10k_mbx_reset_work(mbx); 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci /* set local version to max and remote version to 0 */ 16708c2ecf20Sopenharmony_ci mbx->local = FM10K_SM_MBX_VERSION; 16718c2ecf20Sopenharmony_ci mbx->remote = 0; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci /* initialize tail and head */ 16748c2ecf20Sopenharmony_ci mbx->tail = 1; 16758c2ecf20Sopenharmony_ci mbx->head = 1; 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci /* reset state back to connect */ 16788c2ecf20Sopenharmony_ci mbx->state = FM10K_STATE_CONNECT; 16798c2ecf20Sopenharmony_ci} 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci/** 16828c2ecf20Sopenharmony_ci * fm10k_sm_mbx_connect - Start switch manager mailbox connection 16838c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 16848c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 16858c2ecf20Sopenharmony_ci * 16868c2ecf20Sopenharmony_ci * This function will initiate a mailbox connection with the switch 16878c2ecf20Sopenharmony_ci * manager. To do this it will first disconnect the mailbox, and then 16888c2ecf20Sopenharmony_ci * reconnect it in order to complete a reset of the mailbox. 16898c2ecf20Sopenharmony_ci * 16908c2ecf20Sopenharmony_ci * This function will return an error if the mailbox has not been initiated 16918c2ecf20Sopenharmony_ci * or is currently in use. 16928c2ecf20Sopenharmony_ci **/ 16938c2ecf20Sopenharmony_cistatic s32 fm10k_sm_mbx_connect(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx) 16948c2ecf20Sopenharmony_ci{ 16958c2ecf20Sopenharmony_ci /* we cannot connect an uninitialized mailbox */ 16968c2ecf20Sopenharmony_ci if (!mbx->rx.buffer) 16978c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_NO_SPACE; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci /* we cannot connect an already connected mailbox */ 17008c2ecf20Sopenharmony_ci if (mbx->state != FM10K_STATE_CLOSED) 17018c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_BUSY; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci /* mailbox timeout can now become active */ 17048c2ecf20Sopenharmony_ci mbx->timeout = FM10K_MBX_INIT_TIMEOUT; 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci /* Place mbx in ready to connect state */ 17078c2ecf20Sopenharmony_ci mbx->state = FM10K_STATE_CONNECT; 17088c2ecf20Sopenharmony_ci mbx->max_size = FM10K_MBX_MSG_MAX_SIZE; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci /* reset interface back to connect */ 17118c2ecf20Sopenharmony_ci fm10k_sm_mbx_connect_reset(mbx); 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci /* enable interrupt and notify other party of new message */ 17148c2ecf20Sopenharmony_ci mbx->mbx_lock = FM10K_MBX_REQ_INTERRUPT | FM10K_MBX_ACK_INTERRUPT | 17158c2ecf20Sopenharmony_ci FM10K_MBX_INTERRUPT_ENABLE; 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci /* generate and load connect header into mailbox */ 17188c2ecf20Sopenharmony_ci fm10k_sm_mbx_create_connect_hdr(mbx, 0); 17198c2ecf20Sopenharmony_ci fm10k_mbx_write(hw, mbx); 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci return 0; 17228c2ecf20Sopenharmony_ci} 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci/** 17258c2ecf20Sopenharmony_ci * fm10k_sm_mbx_disconnect - Shutdown mailbox connection 17268c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 17278c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 17288c2ecf20Sopenharmony_ci * 17298c2ecf20Sopenharmony_ci * This function will shut down the mailbox. It places the mailbox first 17308c2ecf20Sopenharmony_ci * in the disconnect state, it then allows up to a predefined timeout for 17318c2ecf20Sopenharmony_ci * the mailbox to transition to close on its own. If this does not occur 17328c2ecf20Sopenharmony_ci * then the mailbox will be forced into the closed state. 17338c2ecf20Sopenharmony_ci * 17348c2ecf20Sopenharmony_ci * Any mailbox transactions not completed before calling this function 17358c2ecf20Sopenharmony_ci * are not guaranteed to complete and may be dropped. 17368c2ecf20Sopenharmony_ci **/ 17378c2ecf20Sopenharmony_cistatic void fm10k_sm_mbx_disconnect(struct fm10k_hw *hw, 17388c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx) 17398c2ecf20Sopenharmony_ci{ 17408c2ecf20Sopenharmony_ci int timeout = mbx->timeout ? FM10K_MBX_DISCONNECT_TIMEOUT : 0; 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci /* Place mbx in ready to disconnect state */ 17438c2ecf20Sopenharmony_ci mbx->state = FM10K_STATE_DISCONNECT; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci /* trigger interrupt to start shutdown process */ 17468c2ecf20Sopenharmony_ci fm10k_write_reg(hw, mbx->mbx_reg, FM10K_MBX_REQ | 17478c2ecf20Sopenharmony_ci FM10K_MBX_INTERRUPT_DISABLE); 17488c2ecf20Sopenharmony_ci do { 17498c2ecf20Sopenharmony_ci udelay(FM10K_MBX_POLL_DELAY); 17508c2ecf20Sopenharmony_ci mbx->ops.process(hw, mbx); 17518c2ecf20Sopenharmony_ci timeout -= FM10K_MBX_POLL_DELAY; 17528c2ecf20Sopenharmony_ci } while ((timeout > 0) && (mbx->state != FM10K_STATE_CLOSED)); 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci /* in case we didn't close just force the mailbox into shutdown */ 17558c2ecf20Sopenharmony_ci mbx->state = FM10K_STATE_CLOSED; 17568c2ecf20Sopenharmony_ci mbx->remote = 0; 17578c2ecf20Sopenharmony_ci fm10k_mbx_reset_work(mbx); 17588c2ecf20Sopenharmony_ci fm10k_fifo_drop_all(&mbx->tx); 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci fm10k_write_reg(hw, mbx->mbmem_reg, 0); 17618c2ecf20Sopenharmony_ci} 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci/** 17648c2ecf20Sopenharmony_ci * fm10k_sm_mbx_validate_fifo_hdr - Validate fields in the remote FIFO header 17658c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 17668c2ecf20Sopenharmony_ci * 17678c2ecf20Sopenharmony_ci * This function will parse up the fields in the mailbox header and return 17688c2ecf20Sopenharmony_ci * an error if the header contains any of a number of invalid configurations 17698c2ecf20Sopenharmony_ci * including unrecognized offsets or version numbers. 17708c2ecf20Sopenharmony_ci **/ 17718c2ecf20Sopenharmony_cistatic s32 fm10k_sm_mbx_validate_fifo_hdr(struct fm10k_mbx_info *mbx) 17728c2ecf20Sopenharmony_ci{ 17738c2ecf20Sopenharmony_ci const u32 *hdr = &mbx->mbx_hdr; 17748c2ecf20Sopenharmony_ci u16 tail, head, ver; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci tail = FM10K_MSG_HDR_FIELD_GET(*hdr, SM_TAIL); 17778c2ecf20Sopenharmony_ci ver = FM10K_MSG_HDR_FIELD_GET(*hdr, SM_VER); 17788c2ecf20Sopenharmony_ci head = FM10K_MSG_HDR_FIELD_GET(*hdr, SM_HEAD); 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci switch (ver) { 17818c2ecf20Sopenharmony_ci case 0: 17828c2ecf20Sopenharmony_ci break; 17838c2ecf20Sopenharmony_ci case FM10K_SM_MBX_VERSION: 17848c2ecf20Sopenharmony_ci if (!head || head > FM10K_SM_MBX_FIFO_LEN) 17858c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_HEAD; 17868c2ecf20Sopenharmony_ci if (!tail || tail > FM10K_SM_MBX_FIFO_LEN) 17878c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_TAIL; 17888c2ecf20Sopenharmony_ci if (mbx->tail < head) 17898c2ecf20Sopenharmony_ci head += mbx->mbmem_len - 1; 17908c2ecf20Sopenharmony_ci if (tail < mbx->head) 17918c2ecf20Sopenharmony_ci tail += mbx->mbmem_len - 1; 17928c2ecf20Sopenharmony_ci if (fm10k_mbx_index_len(mbx, head, mbx->tail) > mbx->tail_len) 17938c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_HEAD; 17948c2ecf20Sopenharmony_ci if (fm10k_mbx_index_len(mbx, mbx->head, tail) < mbx->mbmem_len) 17958c2ecf20Sopenharmony_ci break; 17968c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_TAIL; 17978c2ecf20Sopenharmony_ci default: 17988c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_SRC; 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci return 0; 18028c2ecf20Sopenharmony_ci} 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci/** 18058c2ecf20Sopenharmony_ci * fm10k_sm_mbx_process_error - Process header with error flag set 18068c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 18078c2ecf20Sopenharmony_ci * 18088c2ecf20Sopenharmony_ci * This function is meant to respond to a request where the error flag 18098c2ecf20Sopenharmony_ci * is set. As a result we will terminate a connection if one is present 18108c2ecf20Sopenharmony_ci * and fall back into the reset state with a connection header of version 18118c2ecf20Sopenharmony_ci * 0 (RESET). 18128c2ecf20Sopenharmony_ci **/ 18138c2ecf20Sopenharmony_cistatic void fm10k_sm_mbx_process_error(struct fm10k_mbx_info *mbx) 18148c2ecf20Sopenharmony_ci{ 18158c2ecf20Sopenharmony_ci const enum fm10k_mbx_state state = mbx->state; 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci switch (state) { 18188c2ecf20Sopenharmony_ci case FM10K_STATE_DISCONNECT: 18198c2ecf20Sopenharmony_ci /* if there is an error just disconnect */ 18208c2ecf20Sopenharmony_ci mbx->remote = 0; 18218c2ecf20Sopenharmony_ci break; 18228c2ecf20Sopenharmony_ci case FM10K_STATE_OPEN: 18238c2ecf20Sopenharmony_ci /* flush any uncompleted work */ 18248c2ecf20Sopenharmony_ci fm10k_sm_mbx_connect_reset(mbx); 18258c2ecf20Sopenharmony_ci break; 18268c2ecf20Sopenharmony_ci case FM10K_STATE_CONNECT: 18278c2ecf20Sopenharmony_ci /* try connnecting at lower version */ 18288c2ecf20Sopenharmony_ci if (mbx->remote) { 18298c2ecf20Sopenharmony_ci while (mbx->local > 1) 18308c2ecf20Sopenharmony_ci mbx->local--; 18318c2ecf20Sopenharmony_ci mbx->remote = 0; 18328c2ecf20Sopenharmony_ci } 18338c2ecf20Sopenharmony_ci break; 18348c2ecf20Sopenharmony_ci default: 18358c2ecf20Sopenharmony_ci break; 18368c2ecf20Sopenharmony_ci } 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci fm10k_sm_mbx_create_connect_hdr(mbx, 0); 18398c2ecf20Sopenharmony_ci} 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci/** 18428c2ecf20Sopenharmony_ci * fm10k_sm_mbx_create_error_msg - Process an error in FIFO header 18438c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 18448c2ecf20Sopenharmony_ci * @err: local error encountered 18458c2ecf20Sopenharmony_ci * 18468c2ecf20Sopenharmony_ci * This function will interpret the error provided by err, and based on 18478c2ecf20Sopenharmony_ci * that it may set the error bit in the local message header 18488c2ecf20Sopenharmony_ci **/ 18498c2ecf20Sopenharmony_cistatic void fm10k_sm_mbx_create_error_msg(struct fm10k_mbx_info *mbx, s32 err) 18508c2ecf20Sopenharmony_ci{ 18518c2ecf20Sopenharmony_ci /* only generate an error message for these types */ 18528c2ecf20Sopenharmony_ci switch (err) { 18538c2ecf20Sopenharmony_ci case FM10K_MBX_ERR_TAIL: 18548c2ecf20Sopenharmony_ci case FM10K_MBX_ERR_HEAD: 18558c2ecf20Sopenharmony_ci case FM10K_MBX_ERR_SRC: 18568c2ecf20Sopenharmony_ci case FM10K_MBX_ERR_SIZE: 18578c2ecf20Sopenharmony_ci case FM10K_MBX_ERR_RSVD0: 18588c2ecf20Sopenharmony_ci break; 18598c2ecf20Sopenharmony_ci default: 18608c2ecf20Sopenharmony_ci return; 18618c2ecf20Sopenharmony_ci } 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci /* process it as though we received an error, and send error reply */ 18648c2ecf20Sopenharmony_ci fm10k_sm_mbx_process_error(mbx); 18658c2ecf20Sopenharmony_ci fm10k_sm_mbx_create_connect_hdr(mbx, 1); 18668c2ecf20Sopenharmony_ci} 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci/** 18698c2ecf20Sopenharmony_ci * fm10k_sm_mbx_receive - Take message from Rx mailbox FIFO and put it in Rx 18708c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 18718c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 18728c2ecf20Sopenharmony_ci * @tail: tail index of message 18738c2ecf20Sopenharmony_ci * 18748c2ecf20Sopenharmony_ci * This function will dequeue one message from the Rx switch manager mailbox 18758c2ecf20Sopenharmony_ci * FIFO and place it in the Rx mailbox FIFO for processing by software. 18768c2ecf20Sopenharmony_ci **/ 18778c2ecf20Sopenharmony_cistatic s32 fm10k_sm_mbx_receive(struct fm10k_hw *hw, 18788c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx, 18798c2ecf20Sopenharmony_ci u16 tail) 18808c2ecf20Sopenharmony_ci{ 18818c2ecf20Sopenharmony_ci /* reduce length by 1 to convert to a mask */ 18828c2ecf20Sopenharmony_ci u16 mbmem_len = mbx->mbmem_len - 1; 18838c2ecf20Sopenharmony_ci s32 err; 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci /* push tail in front of head */ 18868c2ecf20Sopenharmony_ci if (tail < mbx->head) 18878c2ecf20Sopenharmony_ci tail += mbmem_len; 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci /* copy data to the Rx FIFO */ 18908c2ecf20Sopenharmony_ci err = fm10k_mbx_push_tail(hw, mbx, tail); 18918c2ecf20Sopenharmony_ci if (err < 0) 18928c2ecf20Sopenharmony_ci return err; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci /* process messages if we have received any */ 18958c2ecf20Sopenharmony_ci fm10k_mbx_dequeue_rx(hw, mbx); 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci /* guarantee head aligns with the end of the last message */ 18988c2ecf20Sopenharmony_ci mbx->head = fm10k_mbx_head_sub(mbx, mbx->pushed); 18998c2ecf20Sopenharmony_ci mbx->pushed = 0; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci /* clear any extra bits left over since index adds 1 extra bit */ 19028c2ecf20Sopenharmony_ci if (mbx->head > mbmem_len) 19038c2ecf20Sopenharmony_ci mbx->head -= mbmem_len; 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci return err; 19068c2ecf20Sopenharmony_ci} 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci/** 19098c2ecf20Sopenharmony_ci * fm10k_sm_mbx_transmit - Take message from Tx and put it in Tx mailbox FIFO 19108c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 19118c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 19128c2ecf20Sopenharmony_ci * @head: head index of message 19138c2ecf20Sopenharmony_ci * 19148c2ecf20Sopenharmony_ci * This function will dequeue one message from the Tx mailbox FIFO and place 19158c2ecf20Sopenharmony_ci * it in the Tx switch manager mailbox FIFO for processing by hardware. 19168c2ecf20Sopenharmony_ci **/ 19178c2ecf20Sopenharmony_cistatic void fm10k_sm_mbx_transmit(struct fm10k_hw *hw, 19188c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx, u16 head) 19198c2ecf20Sopenharmony_ci{ 19208c2ecf20Sopenharmony_ci struct fm10k_mbx_fifo *fifo = &mbx->tx; 19218c2ecf20Sopenharmony_ci /* reduce length by 1 to convert to a mask */ 19228c2ecf20Sopenharmony_ci u16 mbmem_len = mbx->mbmem_len - 1; 19238c2ecf20Sopenharmony_ci u16 tail_len, len = 0; 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci /* push head behind tail */ 19268c2ecf20Sopenharmony_ci if (mbx->tail < head) 19278c2ecf20Sopenharmony_ci head += mbmem_len; 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci fm10k_mbx_pull_head(hw, mbx, head); 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci /* determine msg aligned offset for end of buffer */ 19328c2ecf20Sopenharmony_ci do { 19338c2ecf20Sopenharmony_ci u32 *msg; 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci msg = fifo->buffer + fm10k_fifo_head_offset(fifo, len); 19368c2ecf20Sopenharmony_ci tail_len = len; 19378c2ecf20Sopenharmony_ci len += FM10K_TLV_DWORD_LEN(*msg); 19388c2ecf20Sopenharmony_ci } while ((len <= mbx->tail_len) && (len < mbmem_len)); 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci /* guarantee we stop on a message boundary */ 19418c2ecf20Sopenharmony_ci if (mbx->tail_len > tail_len) { 19428c2ecf20Sopenharmony_ci mbx->tail = fm10k_mbx_tail_sub(mbx, mbx->tail_len - tail_len); 19438c2ecf20Sopenharmony_ci mbx->tail_len = tail_len; 19448c2ecf20Sopenharmony_ci } 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci /* clear any extra bits left over since index adds 1 extra bit */ 19478c2ecf20Sopenharmony_ci if (mbx->tail > mbmem_len) 19488c2ecf20Sopenharmony_ci mbx->tail -= mbmem_len; 19498c2ecf20Sopenharmony_ci} 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci/** 19528c2ecf20Sopenharmony_ci * fm10k_sm_mbx_create_reply - Generate reply based on state and remote head 19538c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 19548c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 19558c2ecf20Sopenharmony_ci * @head: acknowledgement number 19568c2ecf20Sopenharmony_ci * 19578c2ecf20Sopenharmony_ci * This function will generate an outgoing message based on the current 19588c2ecf20Sopenharmony_ci * mailbox state and the remote FIFO head. It will return the length 19598c2ecf20Sopenharmony_ci * of the outgoing message excluding header on success, and a negative value 19608c2ecf20Sopenharmony_ci * on error. 19618c2ecf20Sopenharmony_ci **/ 19628c2ecf20Sopenharmony_cistatic void fm10k_sm_mbx_create_reply(struct fm10k_hw *hw, 19638c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx, u16 head) 19648c2ecf20Sopenharmony_ci{ 19658c2ecf20Sopenharmony_ci switch (mbx->state) { 19668c2ecf20Sopenharmony_ci case FM10K_STATE_OPEN: 19678c2ecf20Sopenharmony_ci case FM10K_STATE_DISCONNECT: 19688c2ecf20Sopenharmony_ci /* flush out Tx data */ 19698c2ecf20Sopenharmony_ci fm10k_sm_mbx_transmit(hw, mbx, head); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci /* generate new header based on data */ 19728c2ecf20Sopenharmony_ci if (mbx->tail_len || (mbx->state == FM10K_STATE_OPEN)) { 19738c2ecf20Sopenharmony_ci fm10k_sm_mbx_create_data_hdr(mbx); 19748c2ecf20Sopenharmony_ci } else { 19758c2ecf20Sopenharmony_ci mbx->remote = 0; 19768c2ecf20Sopenharmony_ci fm10k_sm_mbx_create_connect_hdr(mbx, 0); 19778c2ecf20Sopenharmony_ci } 19788c2ecf20Sopenharmony_ci break; 19798c2ecf20Sopenharmony_ci case FM10K_STATE_CONNECT: 19808c2ecf20Sopenharmony_ci case FM10K_STATE_CLOSED: 19818c2ecf20Sopenharmony_ci fm10k_sm_mbx_create_connect_hdr(mbx, 0); 19828c2ecf20Sopenharmony_ci break; 19838c2ecf20Sopenharmony_ci default: 19848c2ecf20Sopenharmony_ci break; 19858c2ecf20Sopenharmony_ci } 19868c2ecf20Sopenharmony_ci} 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci/** 19898c2ecf20Sopenharmony_ci * fm10k_sm_mbx_process_reset - Process header with version == 0 (RESET) 19908c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 19918c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 19928c2ecf20Sopenharmony_ci * 19938c2ecf20Sopenharmony_ci * This function is meant to respond to a request where the version data 19948c2ecf20Sopenharmony_ci * is set to 0. As such we will either terminate the connection or go 19958c2ecf20Sopenharmony_ci * into the connect state in order to re-establish the connection. This 19968c2ecf20Sopenharmony_ci * function can also be used to respond to an error as the connection 19978c2ecf20Sopenharmony_ci * resetting would also be a means of dealing with errors. 19988c2ecf20Sopenharmony_ci **/ 19998c2ecf20Sopenharmony_cistatic s32 fm10k_sm_mbx_process_reset(struct fm10k_hw *hw, 20008c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx) 20018c2ecf20Sopenharmony_ci{ 20028c2ecf20Sopenharmony_ci s32 err = 0; 20038c2ecf20Sopenharmony_ci const enum fm10k_mbx_state state = mbx->state; 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci switch (state) { 20068c2ecf20Sopenharmony_ci case FM10K_STATE_DISCONNECT: 20078c2ecf20Sopenharmony_ci /* drop remote connections and disconnect */ 20088c2ecf20Sopenharmony_ci mbx->state = FM10K_STATE_CLOSED; 20098c2ecf20Sopenharmony_ci mbx->remote = 0; 20108c2ecf20Sopenharmony_ci mbx->local = 0; 20118c2ecf20Sopenharmony_ci break; 20128c2ecf20Sopenharmony_ci case FM10K_STATE_OPEN: 20138c2ecf20Sopenharmony_ci /* flush any incomplete work */ 20148c2ecf20Sopenharmony_ci fm10k_sm_mbx_connect_reset(mbx); 20158c2ecf20Sopenharmony_ci err = FM10K_ERR_RESET_REQUESTED; 20168c2ecf20Sopenharmony_ci break; 20178c2ecf20Sopenharmony_ci case FM10K_STATE_CONNECT: 20188c2ecf20Sopenharmony_ci /* Update remote value to match local value */ 20198c2ecf20Sopenharmony_ci mbx->remote = mbx->local; 20208c2ecf20Sopenharmony_ci default: 20218c2ecf20Sopenharmony_ci break; 20228c2ecf20Sopenharmony_ci } 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci fm10k_sm_mbx_create_reply(hw, mbx, mbx->tail); 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci return err; 20278c2ecf20Sopenharmony_ci} 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_ci/** 20308c2ecf20Sopenharmony_ci * fm10k_sm_mbx_process_version_1 - Process header with version == 1 20318c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 20328c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 20338c2ecf20Sopenharmony_ci * 20348c2ecf20Sopenharmony_ci * This function is meant to process messages received when the remote 20358c2ecf20Sopenharmony_ci * mailbox is active. 20368c2ecf20Sopenharmony_ci **/ 20378c2ecf20Sopenharmony_cistatic s32 fm10k_sm_mbx_process_version_1(struct fm10k_hw *hw, 20388c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx) 20398c2ecf20Sopenharmony_ci{ 20408c2ecf20Sopenharmony_ci const u32 *hdr = &mbx->mbx_hdr; 20418c2ecf20Sopenharmony_ci u16 head, tail; 20428c2ecf20Sopenharmony_ci s32 len; 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci /* pull all fields needed for verification */ 20458c2ecf20Sopenharmony_ci tail = FM10K_MSG_HDR_FIELD_GET(*hdr, SM_TAIL); 20468c2ecf20Sopenharmony_ci head = FM10K_MSG_HDR_FIELD_GET(*hdr, SM_HEAD); 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci /* if we are in connect and wanting version 1 then start up and go */ 20498c2ecf20Sopenharmony_ci if (mbx->state == FM10K_STATE_CONNECT) { 20508c2ecf20Sopenharmony_ci if (!mbx->remote) 20518c2ecf20Sopenharmony_ci goto send_reply; 20528c2ecf20Sopenharmony_ci if (mbx->remote != 1) 20538c2ecf20Sopenharmony_ci return FM10K_MBX_ERR_SRC; 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci mbx->state = FM10K_STATE_OPEN; 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci do { 20598c2ecf20Sopenharmony_ci /* abort on message size errors */ 20608c2ecf20Sopenharmony_ci len = fm10k_sm_mbx_receive(hw, mbx, tail); 20618c2ecf20Sopenharmony_ci if (len < 0) 20628c2ecf20Sopenharmony_ci return len; 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci /* continue until we have flushed the Rx FIFO */ 20658c2ecf20Sopenharmony_ci } while (len); 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_cisend_reply: 20688c2ecf20Sopenharmony_ci fm10k_sm_mbx_create_reply(hw, mbx, head); 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci return 0; 20718c2ecf20Sopenharmony_ci} 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci/** 20748c2ecf20Sopenharmony_ci * fm10k_sm_mbx_process - Process switch manager mailbox interrupt 20758c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 20768c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 20778c2ecf20Sopenharmony_ci * 20788c2ecf20Sopenharmony_ci * This function will process incoming mailbox events and generate mailbox 20798c2ecf20Sopenharmony_ci * replies. It will return a value indicating the number of DWORDs 20808c2ecf20Sopenharmony_ci * transmitted excluding header on success or a negative value on error. 20818c2ecf20Sopenharmony_ci **/ 20828c2ecf20Sopenharmony_cistatic s32 fm10k_sm_mbx_process(struct fm10k_hw *hw, 20838c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx) 20848c2ecf20Sopenharmony_ci{ 20858c2ecf20Sopenharmony_ci s32 err; 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci /* we do not read mailbox if closed */ 20888c2ecf20Sopenharmony_ci if (mbx->state == FM10K_STATE_CLOSED) 20898c2ecf20Sopenharmony_ci return 0; 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_ci /* retrieve data from switch manager */ 20928c2ecf20Sopenharmony_ci err = fm10k_mbx_read(hw, mbx); 20938c2ecf20Sopenharmony_ci if (err) 20948c2ecf20Sopenharmony_ci return err; 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci err = fm10k_sm_mbx_validate_fifo_hdr(mbx); 20978c2ecf20Sopenharmony_ci if (err < 0) 20988c2ecf20Sopenharmony_ci goto fifo_err; 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_ci if (FM10K_MSG_HDR_FIELD_GET(mbx->mbx_hdr, SM_ERR)) { 21018c2ecf20Sopenharmony_ci fm10k_sm_mbx_process_error(mbx); 21028c2ecf20Sopenharmony_ci goto fifo_err; 21038c2ecf20Sopenharmony_ci } 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci switch (FM10K_MSG_HDR_FIELD_GET(mbx->mbx_hdr, SM_VER)) { 21068c2ecf20Sopenharmony_ci case 0: 21078c2ecf20Sopenharmony_ci err = fm10k_sm_mbx_process_reset(hw, mbx); 21088c2ecf20Sopenharmony_ci break; 21098c2ecf20Sopenharmony_ci case FM10K_SM_MBX_VERSION: 21108c2ecf20Sopenharmony_ci err = fm10k_sm_mbx_process_version_1(hw, mbx); 21118c2ecf20Sopenharmony_ci break; 21128c2ecf20Sopenharmony_ci } 21138c2ecf20Sopenharmony_ci 21148c2ecf20Sopenharmony_cififo_err: 21158c2ecf20Sopenharmony_ci if (err < 0) 21168c2ecf20Sopenharmony_ci fm10k_sm_mbx_create_error_msg(mbx, err); 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci /* report data to switch manager */ 21198c2ecf20Sopenharmony_ci fm10k_mbx_write(hw, mbx); 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci return err; 21228c2ecf20Sopenharmony_ci} 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci/** 21258c2ecf20Sopenharmony_ci * fm10k_sm_mbx_init - Initialize mailbox memory for PF/SM mailbox 21268c2ecf20Sopenharmony_ci * @hw: pointer to hardware structure 21278c2ecf20Sopenharmony_ci * @mbx: pointer to mailbox 21288c2ecf20Sopenharmony_ci * @msg_data: handlers for mailbox events 21298c2ecf20Sopenharmony_ci * 21308c2ecf20Sopenharmony_ci * This function initializes the PF/SM mailbox for use. It will split the 21318c2ecf20Sopenharmony_ci * buffer provided and use that to populate both the Tx and Rx FIFO by 21328c2ecf20Sopenharmony_ci * evenly splitting it. In order to allow for easy masking of head/tail 21338c2ecf20Sopenharmony_ci * the value reported in size must be a power of 2 and is reported in 21348c2ecf20Sopenharmony_ci * DWORDs, not bytes. Any invalid values will cause the mailbox to return 21358c2ecf20Sopenharmony_ci * error. 21368c2ecf20Sopenharmony_ci **/ 21378c2ecf20Sopenharmony_cis32 fm10k_sm_mbx_init(struct fm10k_hw __always_unused *hw, 21388c2ecf20Sopenharmony_ci struct fm10k_mbx_info *mbx, 21398c2ecf20Sopenharmony_ci const struct fm10k_msg_data *msg_data) 21408c2ecf20Sopenharmony_ci{ 21418c2ecf20Sopenharmony_ci mbx->mbx_reg = FM10K_GMBX; 21428c2ecf20Sopenharmony_ci mbx->mbmem_reg = FM10K_MBMEM_PF(0); 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci /* start out in closed state */ 21458c2ecf20Sopenharmony_ci mbx->state = FM10K_STATE_CLOSED; 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci /* validate layout of handlers before assigning them */ 21488c2ecf20Sopenharmony_ci if (fm10k_mbx_validate_handlers(msg_data)) 21498c2ecf20Sopenharmony_ci return FM10K_ERR_PARAM; 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci /* initialize the message handlers */ 21528c2ecf20Sopenharmony_ci mbx->msg_data = msg_data; 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci /* start mailbox as timed out and let the reset_hw call 21558c2ecf20Sopenharmony_ci * set the timeout value to begin communications 21568c2ecf20Sopenharmony_ci */ 21578c2ecf20Sopenharmony_ci mbx->timeout = 0; 21588c2ecf20Sopenharmony_ci mbx->udelay = FM10K_MBX_INIT_DELAY; 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci /* Split buffer for use by Tx/Rx FIFOs */ 21618c2ecf20Sopenharmony_ci mbx->max_size = FM10K_MBX_MSG_MAX_SIZE; 21628c2ecf20Sopenharmony_ci mbx->mbmem_len = FM10K_MBMEM_PF_XOR; 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci /* initialize the FIFOs, sizes are in 4 byte increments */ 21658c2ecf20Sopenharmony_ci fm10k_fifo_init(&mbx->tx, mbx->buffer, FM10K_MBX_TX_BUFFER_SIZE); 21668c2ecf20Sopenharmony_ci fm10k_fifo_init(&mbx->rx, &mbx->buffer[FM10K_MBX_TX_BUFFER_SIZE], 21678c2ecf20Sopenharmony_ci FM10K_MBX_RX_BUFFER_SIZE); 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci /* initialize function pointers */ 21708c2ecf20Sopenharmony_ci mbx->ops.connect = fm10k_sm_mbx_connect; 21718c2ecf20Sopenharmony_ci mbx->ops.disconnect = fm10k_sm_mbx_disconnect; 21728c2ecf20Sopenharmony_ci mbx->ops.rx_ready = fm10k_mbx_rx_ready; 21738c2ecf20Sopenharmony_ci mbx->ops.tx_ready = fm10k_mbx_tx_ready; 21748c2ecf20Sopenharmony_ci mbx->ops.tx_complete = fm10k_mbx_tx_complete; 21758c2ecf20Sopenharmony_ci mbx->ops.enqueue_tx = fm10k_mbx_enqueue_tx; 21768c2ecf20Sopenharmony_ci mbx->ops.process = fm10k_sm_mbx_process; 21778c2ecf20Sopenharmony_ci mbx->ops.register_handlers = fm10k_mbx_register_handlers; 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci return 0; 21808c2ecf20Sopenharmony_ci} 2181