xref: /openbmc/linux/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1ae06c70bSJeff Kirsher // SPDX-License-Identifier: GPL-2.0
2d5c2f395SJacob Keller /* Copyright(c) 2013 - 2019 Intel Corporation. */
31337e6b9SAlexander Duyck 
41337e6b9SAlexander Duyck #include "fm10k_common.h"
51337e6b9SAlexander Duyck 
61337e6b9SAlexander Duyck /**
71337e6b9SAlexander Duyck  *  fm10k_fifo_init - Initialize a message FIFO
81337e6b9SAlexander Duyck  *  @fifo: pointer to FIFO
91337e6b9SAlexander Duyck  *  @buffer: pointer to memory to be used to store FIFO
101337e6b9SAlexander Duyck  *  @size: maximum message size to store in FIFO, must be 2^n - 1
111337e6b9SAlexander Duyck  **/
fm10k_fifo_init(struct fm10k_mbx_fifo * fifo,u32 * buffer,u16 size)121337e6b9SAlexander Duyck static void fm10k_fifo_init(struct fm10k_mbx_fifo *fifo, u32 *buffer, u16 size)
131337e6b9SAlexander Duyck {
141337e6b9SAlexander Duyck 	fifo->buffer = buffer;
151337e6b9SAlexander Duyck 	fifo->size = size;
161337e6b9SAlexander Duyck 	fifo->head = 0;
171337e6b9SAlexander Duyck 	fifo->tail = 0;
181337e6b9SAlexander Duyck }
191337e6b9SAlexander Duyck 
201337e6b9SAlexander Duyck /**
211337e6b9SAlexander Duyck  *  fm10k_fifo_used - Retrieve used space in FIFO
221337e6b9SAlexander Duyck  *  @fifo: pointer to FIFO
231337e6b9SAlexander Duyck  *
241337e6b9SAlexander Duyck  *  This function returns the number of DWORDs used in the FIFO
251337e6b9SAlexander Duyck  **/
fm10k_fifo_used(struct fm10k_mbx_fifo * fifo)261337e6b9SAlexander Duyck static u16 fm10k_fifo_used(struct fm10k_mbx_fifo *fifo)
271337e6b9SAlexander Duyck {
281337e6b9SAlexander Duyck 	return fifo->tail - fifo->head;
291337e6b9SAlexander Duyck }
301337e6b9SAlexander Duyck 
311337e6b9SAlexander Duyck /**
321337e6b9SAlexander Duyck  *  fm10k_fifo_unused - Retrieve unused space in FIFO
331337e6b9SAlexander Duyck  *  @fifo: pointer to FIFO
341337e6b9SAlexander Duyck  *
351337e6b9SAlexander Duyck  *  This function returns the number of unused DWORDs in the FIFO
361337e6b9SAlexander Duyck  **/
fm10k_fifo_unused(struct fm10k_mbx_fifo * fifo)371337e6b9SAlexander Duyck static u16 fm10k_fifo_unused(struct fm10k_mbx_fifo *fifo)
381337e6b9SAlexander Duyck {
391337e6b9SAlexander Duyck 	return fifo->size + fifo->head - fifo->tail;
401337e6b9SAlexander Duyck }
411337e6b9SAlexander Duyck 
421337e6b9SAlexander Duyck /**
43f632fed3SBruce Allan  *  fm10k_fifo_empty - Test to verify if FIFO is empty
441337e6b9SAlexander Duyck  *  @fifo: pointer to FIFO
451337e6b9SAlexander Duyck  *
461337e6b9SAlexander Duyck  *  This function returns true if the FIFO is empty, else false
471337e6b9SAlexander Duyck  **/
fm10k_fifo_empty(struct fm10k_mbx_fifo * fifo)481337e6b9SAlexander Duyck static bool fm10k_fifo_empty(struct fm10k_mbx_fifo *fifo)
491337e6b9SAlexander Duyck {
501337e6b9SAlexander Duyck 	return fifo->head == fifo->tail;
511337e6b9SAlexander Duyck }
521337e6b9SAlexander Duyck 
531337e6b9SAlexander Duyck /**
541337e6b9SAlexander Duyck  *  fm10k_fifo_head_offset - returns indices of head with given offset
551337e6b9SAlexander Duyck  *  @fifo: pointer to FIFO
561337e6b9SAlexander Duyck  *  @offset: offset to add to head
571337e6b9SAlexander Duyck  *
58f632fed3SBruce Allan  *  This function returns the indices into the FIFO based on head + offset
591337e6b9SAlexander Duyck  **/
fm10k_fifo_head_offset(struct fm10k_mbx_fifo * fifo,u16 offset)601337e6b9SAlexander Duyck static u16 fm10k_fifo_head_offset(struct fm10k_mbx_fifo *fifo, u16 offset)
611337e6b9SAlexander Duyck {
621337e6b9SAlexander Duyck 	return (fifo->head + offset) & (fifo->size - 1);
631337e6b9SAlexander Duyck }
641337e6b9SAlexander Duyck 
651337e6b9SAlexander Duyck /**
661337e6b9SAlexander Duyck  *  fm10k_fifo_tail_offset - returns indices of tail with given offset
671337e6b9SAlexander Duyck  *  @fifo: pointer to FIFO
681337e6b9SAlexander Duyck  *  @offset: offset to add to tail
691337e6b9SAlexander Duyck  *
70f632fed3SBruce Allan  *  This function returns the indices into the FIFO based on tail + offset
711337e6b9SAlexander Duyck  **/
fm10k_fifo_tail_offset(struct fm10k_mbx_fifo * fifo,u16 offset)721337e6b9SAlexander Duyck static u16 fm10k_fifo_tail_offset(struct fm10k_mbx_fifo *fifo, u16 offset)
731337e6b9SAlexander Duyck {
741337e6b9SAlexander Duyck 	return (fifo->tail + offset) & (fifo->size - 1);
751337e6b9SAlexander Duyck }
761337e6b9SAlexander Duyck 
771337e6b9SAlexander Duyck /**
781337e6b9SAlexander Duyck  *  fm10k_fifo_head_len - Retrieve length of first message in FIFO
791337e6b9SAlexander Duyck  *  @fifo: pointer to FIFO
801337e6b9SAlexander Duyck  *
811337e6b9SAlexander Duyck  *  This function returns the size of the first message in the FIFO
821337e6b9SAlexander Duyck  **/
fm10k_fifo_head_len(struct fm10k_mbx_fifo * fifo)831337e6b9SAlexander Duyck static u16 fm10k_fifo_head_len(struct fm10k_mbx_fifo *fifo)
841337e6b9SAlexander Duyck {
851337e6b9SAlexander Duyck 	u32 *head = fifo->buffer + fm10k_fifo_head_offset(fifo, 0);
861337e6b9SAlexander Duyck 
871337e6b9SAlexander Duyck 	/* verify there is at least 1 DWORD in the fifo so *head is valid */
881337e6b9SAlexander Duyck 	if (fm10k_fifo_empty(fifo))
891337e6b9SAlexander Duyck 		return 0;
901337e6b9SAlexander Duyck 
911337e6b9SAlexander Duyck 	/* retieve the message length */
921337e6b9SAlexander Duyck 	return FM10K_TLV_DWORD_LEN(*head);
931337e6b9SAlexander Duyck }
941337e6b9SAlexander Duyck 
951337e6b9SAlexander Duyck /**
961337e6b9SAlexander Duyck  *  fm10k_fifo_head_drop - Drop the first message in FIFO
971337e6b9SAlexander Duyck  *  @fifo: pointer to FIFO
981337e6b9SAlexander Duyck  *
991337e6b9SAlexander Duyck  *  This function returns the size of the message dropped from the FIFO
1001337e6b9SAlexander Duyck  **/
fm10k_fifo_head_drop(struct fm10k_mbx_fifo * fifo)1011337e6b9SAlexander Duyck static u16 fm10k_fifo_head_drop(struct fm10k_mbx_fifo *fifo)
1021337e6b9SAlexander Duyck {
1031337e6b9SAlexander Duyck 	u16 len = fm10k_fifo_head_len(fifo);
1041337e6b9SAlexander Duyck 
1051337e6b9SAlexander Duyck 	/* update head so it is at the start of next frame */
1061337e6b9SAlexander Duyck 	fifo->head += len;
1071337e6b9SAlexander Duyck 
1081337e6b9SAlexander Duyck 	return len;
1091337e6b9SAlexander Duyck }
1101337e6b9SAlexander Duyck 
1111337e6b9SAlexander Duyck /**
11278288e37SJeff Kirsher  *  fm10k_fifo_drop_all - Drop all messages in FIFO
11378288e37SJeff Kirsher  *  @fifo: pointer to FIFO
11478288e37SJeff Kirsher  *
11515aa49cbSJacob Keller  *  This function resets the head pointer to drop all messages in the FIFO and
11615aa49cbSJacob Keller  *  ensure the FIFO is empty.
11778288e37SJeff Kirsher  **/
fm10k_fifo_drop_all(struct fm10k_mbx_fifo * fifo)11878288e37SJeff Kirsher static void fm10k_fifo_drop_all(struct fm10k_mbx_fifo *fifo)
11978288e37SJeff Kirsher {
12078288e37SJeff Kirsher 	fifo->head = fifo->tail;
12178288e37SJeff Kirsher }
12278288e37SJeff Kirsher 
12378288e37SJeff Kirsher /**
1241337e6b9SAlexander Duyck  *  fm10k_mbx_index_len - Convert a head/tail index into a length value
1251337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
1261337e6b9SAlexander Duyck  *  @head: head index
1271337e6b9SAlexander Duyck  *  @tail: head index
1281337e6b9SAlexander Duyck  *
1291337e6b9SAlexander Duyck  *  This function takes the head and tail index and determines the length
1301337e6b9SAlexander Duyck  *  of the data indicated by this pair.
1311337e6b9SAlexander Duyck  **/
fm10k_mbx_index_len(struct fm10k_mbx_info * mbx,u16 head,u16 tail)1321337e6b9SAlexander Duyck static u16 fm10k_mbx_index_len(struct fm10k_mbx_info *mbx, u16 head, u16 tail)
1331337e6b9SAlexander Duyck {
1341337e6b9SAlexander Duyck 	u16 len = tail - head;
1351337e6b9SAlexander Duyck 
1361337e6b9SAlexander Duyck 	/* we wrapped so subtract 2, one for index 0, one for all 1s index */
1371337e6b9SAlexander Duyck 	if (len > tail)
1381337e6b9SAlexander Duyck 		len -= 2;
1391337e6b9SAlexander Duyck 
1401337e6b9SAlexander Duyck 	return len & ((mbx->mbmem_len << 1) - 1);
1411337e6b9SAlexander Duyck }
1421337e6b9SAlexander Duyck 
1431337e6b9SAlexander Duyck /**
1441337e6b9SAlexander Duyck  *  fm10k_mbx_tail_add - Determine new tail value with added offset
1451337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
146f632fed3SBruce Allan  *  @offset: length to add to tail offset
1471337e6b9SAlexander Duyck  *
1481337e6b9SAlexander Duyck  *  This function takes the local tail index and recomputes it for
1491337e6b9SAlexander Duyck  *  a given length added as an offset.
1501337e6b9SAlexander Duyck  **/
fm10k_mbx_tail_add(struct fm10k_mbx_info * mbx,u16 offset)1511337e6b9SAlexander Duyck static u16 fm10k_mbx_tail_add(struct fm10k_mbx_info *mbx, u16 offset)
1521337e6b9SAlexander Duyck {
1531337e6b9SAlexander Duyck 	u16 tail = (mbx->tail + offset + 1) & ((mbx->mbmem_len << 1) - 1);
1541337e6b9SAlexander Duyck 
1551337e6b9SAlexander Duyck 	/* add/sub 1 because we cannot have offset 0 or all 1s */
1561337e6b9SAlexander Duyck 	return (tail > mbx->tail) ? --tail : ++tail;
1571337e6b9SAlexander Duyck }
1581337e6b9SAlexander Duyck 
1591337e6b9SAlexander Duyck /**
1601337e6b9SAlexander Duyck  *  fm10k_mbx_tail_sub - Determine new tail value with subtracted offset
1611337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
162f632fed3SBruce Allan  *  @offset: length to add to tail offset
1631337e6b9SAlexander Duyck  *
1641337e6b9SAlexander Duyck  *  This function takes the local tail index and recomputes it for
1651337e6b9SAlexander Duyck  *  a given length added as an offset.
1661337e6b9SAlexander Duyck  **/
fm10k_mbx_tail_sub(struct fm10k_mbx_info * mbx,u16 offset)1671337e6b9SAlexander Duyck static u16 fm10k_mbx_tail_sub(struct fm10k_mbx_info *mbx, u16 offset)
1681337e6b9SAlexander Duyck {
1691337e6b9SAlexander Duyck 	u16 tail = (mbx->tail - offset - 1) & ((mbx->mbmem_len << 1) - 1);
1701337e6b9SAlexander Duyck 
1711337e6b9SAlexander Duyck 	/* sub/add 1 because we cannot have offset 0 or all 1s */
1721337e6b9SAlexander Duyck 	return (tail < mbx->tail) ? ++tail : --tail;
1731337e6b9SAlexander Duyck }
1741337e6b9SAlexander Duyck 
1751337e6b9SAlexander Duyck /**
1761337e6b9SAlexander Duyck  *  fm10k_mbx_head_add - Determine new head value with added offset
1771337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
1781337e6b9SAlexander Duyck  *  @offset: length to add to head offset
1791337e6b9SAlexander Duyck  *
1801337e6b9SAlexander Duyck  *  This function takes the local head index and recomputes it for
1811337e6b9SAlexander Duyck  *  a given length added as an offset.
1821337e6b9SAlexander Duyck  **/
fm10k_mbx_head_add(struct fm10k_mbx_info * mbx,u16 offset)1831337e6b9SAlexander Duyck static u16 fm10k_mbx_head_add(struct fm10k_mbx_info *mbx, u16 offset)
1841337e6b9SAlexander Duyck {
1851337e6b9SAlexander Duyck 	u16 head = (mbx->head + offset + 1) & ((mbx->mbmem_len << 1) - 1);
1861337e6b9SAlexander Duyck 
1871337e6b9SAlexander Duyck 	/* add/sub 1 because we cannot have offset 0 or all 1s */
1881337e6b9SAlexander Duyck 	return (head > mbx->head) ? --head : ++head;
1891337e6b9SAlexander Duyck }
1901337e6b9SAlexander Duyck 
1911337e6b9SAlexander Duyck /**
1921337e6b9SAlexander Duyck  *  fm10k_mbx_head_sub - Determine new head value with subtracted offset
1931337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
1941337e6b9SAlexander Duyck  *  @offset: length to add to head offset
1951337e6b9SAlexander Duyck  *
1961337e6b9SAlexander Duyck  *  This function takes the local head index and recomputes it for
1971337e6b9SAlexander Duyck  *  a given length added as an offset.
1981337e6b9SAlexander Duyck  **/
fm10k_mbx_head_sub(struct fm10k_mbx_info * mbx,u16 offset)1991337e6b9SAlexander Duyck static u16 fm10k_mbx_head_sub(struct fm10k_mbx_info *mbx, u16 offset)
2001337e6b9SAlexander Duyck {
2011337e6b9SAlexander Duyck 	u16 head = (mbx->head - offset - 1) & ((mbx->mbmem_len << 1) - 1);
2021337e6b9SAlexander Duyck 
2031337e6b9SAlexander Duyck 	/* sub/add 1 because we cannot have offset 0 or all 1s */
2041337e6b9SAlexander Duyck 	return (head < mbx->head) ? ++head : --head;
2051337e6b9SAlexander Duyck }
2061337e6b9SAlexander Duyck 
2071337e6b9SAlexander Duyck /**
2081337e6b9SAlexander Duyck  *  fm10k_mbx_pushed_tail_len - Retrieve the length of message being pushed
2091337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
2101337e6b9SAlexander Duyck  *
2111337e6b9SAlexander Duyck  *  This function will return the length of the message currently being
2121337e6b9SAlexander Duyck  *  pushed onto the tail of the Rx queue.
2131337e6b9SAlexander Duyck  **/
fm10k_mbx_pushed_tail_len(struct fm10k_mbx_info * mbx)2141337e6b9SAlexander Duyck static u16 fm10k_mbx_pushed_tail_len(struct fm10k_mbx_info *mbx)
2151337e6b9SAlexander Duyck {
2161337e6b9SAlexander Duyck 	u32 *tail = mbx->rx.buffer + fm10k_fifo_tail_offset(&mbx->rx, 0);
2171337e6b9SAlexander Duyck 
2181337e6b9SAlexander Duyck 	/* pushed tail is only valid if pushed is set */
2191337e6b9SAlexander Duyck 	if (!mbx->pushed)
2201337e6b9SAlexander Duyck 		return 0;
2211337e6b9SAlexander Duyck 
2221337e6b9SAlexander Duyck 	return FM10K_TLV_DWORD_LEN(*tail);
2231337e6b9SAlexander Duyck }
2241337e6b9SAlexander Duyck 
2251337e6b9SAlexander Duyck /**
226f632fed3SBruce Allan  *  fm10k_fifo_write_copy - pulls data off of msg and places it in FIFO
2271337e6b9SAlexander Duyck  *  @fifo: pointer to FIFO
2281337e6b9SAlexander Duyck  *  @msg: message array to populate
2291337e6b9SAlexander Duyck  *  @tail_offset: additional offset to add to tail pointer
2301337e6b9SAlexander Duyck  *  @len: length of FIFO to copy into message header
2311337e6b9SAlexander Duyck  *
2321337e6b9SAlexander Duyck  *  This function will take a message and copy it into a section of the
2331337e6b9SAlexander Duyck  *  FIFO.  In order to get something into a location other than just
2341337e6b9SAlexander Duyck  *  the tail you can use tail_offset to adjust the pointer.
2351337e6b9SAlexander Duyck  **/
fm10k_fifo_write_copy(struct fm10k_mbx_fifo * fifo,const u32 * msg,u16 tail_offset,u16 len)2361337e6b9SAlexander Duyck static void fm10k_fifo_write_copy(struct fm10k_mbx_fifo *fifo,
2371337e6b9SAlexander Duyck 				  const u32 *msg, u16 tail_offset, u16 len)
2381337e6b9SAlexander Duyck {
2391337e6b9SAlexander Duyck 	u16 end = fm10k_fifo_tail_offset(fifo, tail_offset);
2401337e6b9SAlexander Duyck 	u32 *tail = fifo->buffer + end;
2411337e6b9SAlexander Duyck 
2421337e6b9SAlexander Duyck 	/* track when we should cross the end of the FIFO */
2431337e6b9SAlexander Duyck 	end = fifo->size - end;
2441337e6b9SAlexander Duyck 
2451337e6b9SAlexander Duyck 	/* copy end of message before start of message */
2461337e6b9SAlexander Duyck 	if (end < len)
2471337e6b9SAlexander Duyck 		memcpy(fifo->buffer, msg + end, (len - end) << 2);
2481337e6b9SAlexander Duyck 	else
2491337e6b9SAlexander Duyck 		end = len;
2501337e6b9SAlexander Duyck 
2511337e6b9SAlexander Duyck 	/* Copy remaining message into Tx FIFO */
2521337e6b9SAlexander Duyck 	memcpy(tail, msg, end << 2);
2531337e6b9SAlexander Duyck }
2541337e6b9SAlexander Duyck 
2551337e6b9SAlexander Duyck /**
2561337e6b9SAlexander Duyck  *  fm10k_fifo_enqueue - Enqueues the message to the tail of the FIFO
2571337e6b9SAlexander Duyck  *  @fifo: pointer to FIFO
2581337e6b9SAlexander Duyck  *  @msg: message array to read
2591337e6b9SAlexander Duyck  *
2601337e6b9SAlexander Duyck  *  This function enqueues a message up to the size specified by the length
2611337e6b9SAlexander Duyck  *  contained in the first DWORD of the message and will place at the tail
2621337e6b9SAlexander Duyck  *  of the FIFO.  It will return 0 on success, or a negative value on error.
2631337e6b9SAlexander Duyck  **/
fm10k_fifo_enqueue(struct fm10k_mbx_fifo * fifo,const u32 * msg)2641337e6b9SAlexander Duyck static s32 fm10k_fifo_enqueue(struct fm10k_mbx_fifo *fifo, const u32 *msg)
2651337e6b9SAlexander Duyck {
2661337e6b9SAlexander Duyck 	u16 len = FM10K_TLV_DWORD_LEN(*msg);
2671337e6b9SAlexander Duyck 
2681337e6b9SAlexander Duyck 	/* verify parameters */
2691337e6b9SAlexander Duyck 	if (len > fifo->size)
2701337e6b9SAlexander Duyck 		return FM10K_MBX_ERR_SIZE;
2711337e6b9SAlexander Duyck 
2721337e6b9SAlexander Duyck 	/* verify there is room for the message */
2731337e6b9SAlexander Duyck 	if (len > fm10k_fifo_unused(fifo))
2741337e6b9SAlexander Duyck 		return FM10K_MBX_ERR_NO_SPACE;
2751337e6b9SAlexander Duyck 
2761337e6b9SAlexander Duyck 	/* Copy message into FIFO */
2771337e6b9SAlexander Duyck 	fm10k_fifo_write_copy(fifo, msg, 0, len);
2781337e6b9SAlexander Duyck 
2791337e6b9SAlexander Duyck 	/* memory barrier to guarantee FIFO is written before tail update */
2801337e6b9SAlexander Duyck 	wmb();
2811337e6b9SAlexander Duyck 
2821337e6b9SAlexander Duyck 	/* Update Tx FIFO tail */
2831337e6b9SAlexander Duyck 	fifo->tail += len;
2841337e6b9SAlexander Duyck 
2851337e6b9SAlexander Duyck 	return 0;
2861337e6b9SAlexander Duyck }
2871337e6b9SAlexander Duyck 
2881337e6b9SAlexander Duyck /**
2891337e6b9SAlexander Duyck  *  fm10k_mbx_validate_msg_size - Validate incoming message based on size
2901337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
2911337e6b9SAlexander Duyck  *  @len: length of data pushed onto buffer
2921337e6b9SAlexander Duyck  *
2931337e6b9SAlexander Duyck  *  This function analyzes the frame and will return a non-zero value when
2941337e6b9SAlexander Duyck  *  the start of a message larger than the mailbox is detected.
2951337e6b9SAlexander Duyck  **/
fm10k_mbx_validate_msg_size(struct fm10k_mbx_info * mbx,u16 len)2961337e6b9SAlexander Duyck static u16 fm10k_mbx_validate_msg_size(struct fm10k_mbx_info *mbx, u16 len)
2971337e6b9SAlexander Duyck {
2981337e6b9SAlexander Duyck 	struct fm10k_mbx_fifo *fifo = &mbx->rx;
2991337e6b9SAlexander Duyck 	u16 total_len = 0, msg_len;
3001337e6b9SAlexander Duyck 
3011337e6b9SAlexander Duyck 	/* length should include previous amounts pushed */
3021337e6b9SAlexander Duyck 	len += mbx->pushed;
3031337e6b9SAlexander Duyck 
3041337e6b9SAlexander Duyck 	/* offset in message is based off of current message size */
3051337e6b9SAlexander Duyck 	do {
30671974d7eSJacob Keller 		u32 *msg;
30771974d7eSJacob Keller 
3081337e6b9SAlexander Duyck 		msg = fifo->buffer + fm10k_fifo_tail_offset(fifo, total_len);
3091337e6b9SAlexander Duyck 		msg_len = FM10K_TLV_DWORD_LEN(*msg);
3101337e6b9SAlexander Duyck 		total_len += msg_len;
3111337e6b9SAlexander Duyck 	} while (total_len < len);
3121337e6b9SAlexander Duyck 
3131337e6b9SAlexander Duyck 	/* message extends out of pushed section, but fits in FIFO */
314da61b367SJeff Kirsher 	if ((len < total_len) && (msg_len <= mbx->max_size))
3151337e6b9SAlexander Duyck 		return 0;
3161337e6b9SAlexander Duyck 
3171337e6b9SAlexander Duyck 	/* return length of invalid section */
3181337e6b9SAlexander Duyck 	return (len < total_len) ? len : (len - total_len);
3191337e6b9SAlexander Duyck }
3201337e6b9SAlexander Duyck 
3211337e6b9SAlexander Duyck /**
3221337e6b9SAlexander Duyck  *  fm10k_mbx_write_copy - pulls data off of Tx FIFO and places it in mbmem
323f632fed3SBruce Allan  *  @hw: pointer to hardware structure
3241337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
3251337e6b9SAlexander Duyck  *
32641857562SJeff Kirsher  *  This function will take a section of the Tx FIFO and copy it into the
3271337e6b9SAlexander Duyck  *  mailbox memory.  The offset in mbmem is based on the lower bits of the
3281337e6b9SAlexander Duyck  *  tail and len determines the length to copy.
3291337e6b9SAlexander Duyck  **/
fm10k_mbx_write_copy(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx)3301337e6b9SAlexander Duyck static void fm10k_mbx_write_copy(struct fm10k_hw *hw,
3311337e6b9SAlexander Duyck 				 struct fm10k_mbx_info *mbx)
3321337e6b9SAlexander Duyck {
3331337e6b9SAlexander Duyck 	struct fm10k_mbx_fifo *fifo = &mbx->tx;
3341337e6b9SAlexander Duyck 	u32 mbmem = mbx->mbmem_reg;
3351337e6b9SAlexander Duyck 	u32 *head = fifo->buffer;
3361337e6b9SAlexander Duyck 	u16 end, len, tail, mask;
3371337e6b9SAlexander Duyck 
3381337e6b9SAlexander Duyck 	if (!mbx->tail_len)
3391337e6b9SAlexander Duyck 		return;
3401337e6b9SAlexander Duyck 
3411337e6b9SAlexander Duyck 	/* determine data length and mbmem tail index */
3421337e6b9SAlexander Duyck 	mask = mbx->mbmem_len - 1;
3431337e6b9SAlexander Duyck 	len = mbx->tail_len;
3441337e6b9SAlexander Duyck 	tail = fm10k_mbx_tail_sub(mbx, len);
3451337e6b9SAlexander Duyck 	if (tail > mask)
3461337e6b9SAlexander Duyck 		tail++;
3471337e6b9SAlexander Duyck 
3481337e6b9SAlexander Duyck 	/* determine offset in the ring */
3491337e6b9SAlexander Duyck 	end = fm10k_fifo_head_offset(fifo, mbx->pulled);
3501337e6b9SAlexander Duyck 	head += end;
3511337e6b9SAlexander Duyck 
3521337e6b9SAlexander Duyck 	/* memory barrier to guarantee data is ready to be read */
3531337e6b9SAlexander Duyck 	rmb();
3541337e6b9SAlexander Duyck 
3551337e6b9SAlexander Duyck 	/* Copy message from Tx FIFO */
3561337e6b9SAlexander Duyck 	for (end = fifo->size - end; len; head = fifo->buffer) {
3571337e6b9SAlexander Duyck 		do {
3581337e6b9SAlexander Duyck 			/* adjust tail to match offset for FIFO */
3591337e6b9SAlexander Duyck 			tail &= mask;
3601337e6b9SAlexander Duyck 			if (!tail)
3611337e6b9SAlexander Duyck 				tail++;
3621337e6b9SAlexander Duyck 
36317d39facSJacob Keller 			mbx->tx_mbmem_pulled++;
36417d39facSJacob Keller 
3651337e6b9SAlexander Duyck 			/* write message to hardware FIFO */
3661337e6b9SAlexander Duyck 			fm10k_write_reg(hw, mbmem + tail++, *(head++));
3671337e6b9SAlexander Duyck 		} while (--len && --end);
3681337e6b9SAlexander Duyck 	}
3691337e6b9SAlexander Duyck }
3701337e6b9SAlexander Duyck 
3711337e6b9SAlexander Duyck /**
3721337e6b9SAlexander Duyck  *  fm10k_mbx_pull_head - Pulls data off of head of Tx FIFO
3731337e6b9SAlexander Duyck  *  @hw: pointer to hardware structure
3741337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
3751337e6b9SAlexander Duyck  *  @head: acknowledgement number last received
3761337e6b9SAlexander Duyck  *
3771337e6b9SAlexander Duyck  *  This function will push the tail index forward based on the remote
3781337e6b9SAlexander Duyck  *  head index.  It will then pull up to mbmem_len DWORDs off of the
3791337e6b9SAlexander Duyck  *  head of the FIFO and will place it in the MBMEM registers
3801337e6b9SAlexander Duyck  *  associated with the mailbox.
3811337e6b9SAlexander Duyck  **/
fm10k_mbx_pull_head(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx,u16 head)3821337e6b9SAlexander Duyck static void fm10k_mbx_pull_head(struct fm10k_hw *hw,
3831337e6b9SAlexander Duyck 				struct fm10k_mbx_info *mbx, u16 head)
3841337e6b9SAlexander Duyck {
3851337e6b9SAlexander Duyck 	u16 mbmem_len, len, ack = fm10k_mbx_index_len(mbx, head, mbx->tail);
3861337e6b9SAlexander Duyck 	struct fm10k_mbx_fifo *fifo = &mbx->tx;
3871337e6b9SAlexander Duyck 
3881337e6b9SAlexander Duyck 	/* update number of bytes pulled and update bytes in transit */
3891337e6b9SAlexander Duyck 	mbx->pulled += mbx->tail_len - ack;
3901337e6b9SAlexander Duyck 
3911337e6b9SAlexander Duyck 	/* determine length of data to pull, reserve space for mbmem header */
3921337e6b9SAlexander Duyck 	mbmem_len = mbx->mbmem_len - 1;
3931337e6b9SAlexander Duyck 	len = fm10k_fifo_used(fifo) - mbx->pulled;
3941337e6b9SAlexander Duyck 	if (len > mbmem_len)
3951337e6b9SAlexander Duyck 		len = mbmem_len;
3961337e6b9SAlexander Duyck 
3971337e6b9SAlexander Duyck 	/* update tail and record number of bytes in transit */
3981337e6b9SAlexander Duyck 	mbx->tail = fm10k_mbx_tail_add(mbx, len - ack);
3991337e6b9SAlexander Duyck 	mbx->tail_len = len;
4001337e6b9SAlexander Duyck 
4011337e6b9SAlexander Duyck 	/* drop pulled messages from the FIFO */
4021337e6b9SAlexander Duyck 	for (len = fm10k_fifo_head_len(fifo);
4031337e6b9SAlexander Duyck 	     len && (mbx->pulled >= len);
4041337e6b9SAlexander Duyck 	     len = fm10k_fifo_head_len(fifo)) {
4051337e6b9SAlexander Duyck 		mbx->pulled -= fm10k_fifo_head_drop(fifo);
4061337e6b9SAlexander Duyck 		mbx->tx_messages++;
4071337e6b9SAlexander Duyck 		mbx->tx_dwords += len;
4081337e6b9SAlexander Duyck 	}
4091337e6b9SAlexander Duyck 
4101337e6b9SAlexander Duyck 	/* Copy message out from the Tx FIFO */
4111337e6b9SAlexander Duyck 	fm10k_mbx_write_copy(hw, mbx);
4121337e6b9SAlexander Duyck }
4131337e6b9SAlexander Duyck 
4141337e6b9SAlexander Duyck /**
4151337e6b9SAlexander Duyck  *  fm10k_mbx_read_copy - pulls data off of mbmem and places it in Rx FIFO
4161337e6b9SAlexander Duyck  *  @hw: pointer to hardware structure
4171337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
4181337e6b9SAlexander Duyck  *
419eca32047SMatthew Vick  *  This function will take a section of the mailbox memory and copy it
4201337e6b9SAlexander Duyck  *  into the Rx FIFO.  The offset is based on the lower bits of the
4211337e6b9SAlexander Duyck  *  head and len determines the length to copy.
4221337e6b9SAlexander Duyck  **/
fm10k_mbx_read_copy(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx)4231337e6b9SAlexander Duyck static void fm10k_mbx_read_copy(struct fm10k_hw *hw,
4241337e6b9SAlexander Duyck 				struct fm10k_mbx_info *mbx)
4251337e6b9SAlexander Duyck {
4261337e6b9SAlexander Duyck 	struct fm10k_mbx_fifo *fifo = &mbx->rx;
4271337e6b9SAlexander Duyck 	u32 mbmem = mbx->mbmem_reg ^ mbx->mbmem_len;
4281337e6b9SAlexander Duyck 	u32 *tail = fifo->buffer;
4291337e6b9SAlexander Duyck 	u16 end, len, head;
4301337e6b9SAlexander Duyck 
4311337e6b9SAlexander Duyck 	/* determine data length and mbmem head index */
4321337e6b9SAlexander Duyck 	len = mbx->head_len;
4331337e6b9SAlexander Duyck 	head = fm10k_mbx_head_sub(mbx, len);
4341337e6b9SAlexander Duyck 	if (head >= mbx->mbmem_len)
4351337e6b9SAlexander Duyck 		head++;
4361337e6b9SAlexander Duyck 
4371337e6b9SAlexander Duyck 	/* determine offset in the ring */
4381337e6b9SAlexander Duyck 	end = fm10k_fifo_tail_offset(fifo, mbx->pushed);
4391337e6b9SAlexander Duyck 	tail += end;
4401337e6b9SAlexander Duyck 
4411337e6b9SAlexander Duyck 	/* Copy message into Rx FIFO */
4421337e6b9SAlexander Duyck 	for (end = fifo->size - end; len; tail = fifo->buffer) {
4431337e6b9SAlexander Duyck 		do {
4441337e6b9SAlexander Duyck 			/* adjust head to match offset for FIFO */
4451337e6b9SAlexander Duyck 			head &= mbx->mbmem_len - 1;
4461337e6b9SAlexander Duyck 			if (!head)
4471337e6b9SAlexander Duyck 				head++;
4481337e6b9SAlexander Duyck 
44917d39facSJacob Keller 			mbx->rx_mbmem_pushed++;
45017d39facSJacob Keller 
4511337e6b9SAlexander Duyck 			/* read message from hardware FIFO */
4521337e6b9SAlexander Duyck 			*(tail++) = fm10k_read_reg(hw, mbmem + head++);
4531337e6b9SAlexander Duyck 		} while (--len && --end);
4541337e6b9SAlexander Duyck 	}
4551337e6b9SAlexander Duyck 
4561337e6b9SAlexander Duyck 	/* memory barrier to guarantee FIFO is written before tail update */
4571337e6b9SAlexander Duyck 	wmb();
4581337e6b9SAlexander Duyck }
4591337e6b9SAlexander Duyck 
4601337e6b9SAlexander Duyck /**
4611337e6b9SAlexander Duyck  *  fm10k_mbx_push_tail - Pushes up to 15 DWORDs on to tail of FIFO
4621337e6b9SAlexander Duyck  *  @hw: pointer to hardware structure
4631337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
4641337e6b9SAlexander Duyck  *  @tail: tail index of message
4651337e6b9SAlexander Duyck  *
4661337e6b9SAlexander Duyck  *  This function will first validate the tail index and size for the
467eca32047SMatthew Vick  *  incoming message.  It then updates the acknowledgment number and
4681337e6b9SAlexander Duyck  *  copies the data into the FIFO.  It will return the number of messages
4691337e6b9SAlexander Duyck  *  dequeued on success and a negative value on error.
4701337e6b9SAlexander Duyck  **/
fm10k_mbx_push_tail(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx,u16 tail)4711337e6b9SAlexander Duyck static s32 fm10k_mbx_push_tail(struct fm10k_hw *hw,
4721337e6b9SAlexander Duyck 			       struct fm10k_mbx_info *mbx,
4731337e6b9SAlexander Duyck 			       u16 tail)
4741337e6b9SAlexander Duyck {
4751337e6b9SAlexander Duyck 	struct fm10k_mbx_fifo *fifo = &mbx->rx;
4761337e6b9SAlexander Duyck 	u16 len, seq = fm10k_mbx_index_len(mbx, mbx->head, tail);
4771337e6b9SAlexander Duyck 
4781337e6b9SAlexander Duyck 	/* determine length of data to push */
4791337e6b9SAlexander Duyck 	len = fm10k_fifo_unused(fifo) - mbx->pushed;
4801337e6b9SAlexander Duyck 	if (len > seq)
4811337e6b9SAlexander Duyck 		len = seq;
4821337e6b9SAlexander Duyck 
4831337e6b9SAlexander Duyck 	/* update head and record bytes received */
4841337e6b9SAlexander Duyck 	mbx->head = fm10k_mbx_head_add(mbx, len);
4851337e6b9SAlexander Duyck 	mbx->head_len = len;
4861337e6b9SAlexander Duyck 
4871337e6b9SAlexander Duyck 	/* nothing to do if there is no data */
4881337e6b9SAlexander Duyck 	if (!len)
4891337e6b9SAlexander Duyck 		return 0;
4901337e6b9SAlexander Duyck 
4911337e6b9SAlexander Duyck 	/* Copy msg into Rx FIFO */
4921337e6b9SAlexander Duyck 	fm10k_mbx_read_copy(hw, mbx);
4931337e6b9SAlexander Duyck 
4941337e6b9SAlexander Duyck 	/* determine if there are any invalid lengths in message */
4951337e6b9SAlexander Duyck 	if (fm10k_mbx_validate_msg_size(mbx, len))
4961337e6b9SAlexander Duyck 		return FM10K_MBX_ERR_SIZE;
4971337e6b9SAlexander Duyck 
4981337e6b9SAlexander Duyck 	/* Update pushed */
4991337e6b9SAlexander Duyck 	mbx->pushed += len;
5001337e6b9SAlexander Duyck 
5011337e6b9SAlexander Duyck 	/* flush any completed messages */
5021337e6b9SAlexander Duyck 	for (len = fm10k_mbx_pushed_tail_len(mbx);
5031337e6b9SAlexander Duyck 	     len && (mbx->pushed >= len);
5041337e6b9SAlexander Duyck 	     len = fm10k_mbx_pushed_tail_len(mbx)) {
5051337e6b9SAlexander Duyck 		fifo->tail += len;
5061337e6b9SAlexander Duyck 		mbx->pushed -= len;
5071337e6b9SAlexander Duyck 		mbx->rx_messages++;
5081337e6b9SAlexander Duyck 		mbx->rx_dwords += len;
5091337e6b9SAlexander Duyck 	}
5101337e6b9SAlexander Duyck 
5111337e6b9SAlexander Duyck 	return 0;
5121337e6b9SAlexander Duyck }
5131337e6b9SAlexander Duyck 
514b651957cSAlexander Duyck /* pre-generated data for generating the CRC based on the poly 0xAC9A. */
515b651957cSAlexander Duyck static const u16 fm10k_crc_16b_table[256] = {
516b651957cSAlexander Duyck 	0x0000, 0x7956, 0xF2AC, 0x8BFA, 0xBC6D, 0xC53B, 0x4EC1, 0x3797,
517b651957cSAlexander Duyck 	0x21EF, 0x58B9, 0xD343, 0xAA15, 0x9D82, 0xE4D4, 0x6F2E, 0x1678,
518b651957cSAlexander Duyck 	0x43DE, 0x3A88, 0xB172, 0xC824, 0xFFB3, 0x86E5, 0x0D1F, 0x7449,
519b651957cSAlexander Duyck 	0x6231, 0x1B67, 0x909D, 0xE9CB, 0xDE5C, 0xA70A, 0x2CF0, 0x55A6,
520b651957cSAlexander Duyck 	0x87BC, 0xFEEA, 0x7510, 0x0C46, 0x3BD1, 0x4287, 0xC97D, 0xB02B,
521b651957cSAlexander Duyck 	0xA653, 0xDF05, 0x54FF, 0x2DA9, 0x1A3E, 0x6368, 0xE892, 0x91C4,
522b651957cSAlexander Duyck 	0xC462, 0xBD34, 0x36CE, 0x4F98, 0x780F, 0x0159, 0x8AA3, 0xF3F5,
523b651957cSAlexander Duyck 	0xE58D, 0x9CDB, 0x1721, 0x6E77, 0x59E0, 0x20B6, 0xAB4C, 0xD21A,
524b651957cSAlexander Duyck 	0x564D, 0x2F1B, 0xA4E1, 0xDDB7, 0xEA20, 0x9376, 0x188C, 0x61DA,
525b651957cSAlexander Duyck 	0x77A2, 0x0EF4, 0x850E, 0xFC58, 0xCBCF, 0xB299, 0x3963, 0x4035,
526b651957cSAlexander Duyck 	0x1593, 0x6CC5, 0xE73F, 0x9E69, 0xA9FE, 0xD0A8, 0x5B52, 0x2204,
527b651957cSAlexander Duyck 	0x347C, 0x4D2A, 0xC6D0, 0xBF86, 0x8811, 0xF147, 0x7ABD, 0x03EB,
528b651957cSAlexander Duyck 	0xD1F1, 0xA8A7, 0x235D, 0x5A0B, 0x6D9C, 0x14CA, 0x9F30, 0xE666,
529b651957cSAlexander Duyck 	0xF01E, 0x8948, 0x02B2, 0x7BE4, 0x4C73, 0x3525, 0xBEDF, 0xC789,
530b651957cSAlexander Duyck 	0x922F, 0xEB79, 0x6083, 0x19D5, 0x2E42, 0x5714, 0xDCEE, 0xA5B8,
531b651957cSAlexander Duyck 	0xB3C0, 0xCA96, 0x416C, 0x383A, 0x0FAD, 0x76FB, 0xFD01, 0x8457,
532b651957cSAlexander Duyck 	0xAC9A, 0xD5CC, 0x5E36, 0x2760, 0x10F7, 0x69A1, 0xE25B, 0x9B0D,
533b651957cSAlexander Duyck 	0x8D75, 0xF423, 0x7FD9, 0x068F, 0x3118, 0x484E, 0xC3B4, 0xBAE2,
534b651957cSAlexander Duyck 	0xEF44, 0x9612, 0x1DE8, 0x64BE, 0x5329, 0x2A7F, 0xA185, 0xD8D3,
535b651957cSAlexander Duyck 	0xCEAB, 0xB7FD, 0x3C07, 0x4551, 0x72C6, 0x0B90, 0x806A, 0xF93C,
536b651957cSAlexander Duyck 	0x2B26, 0x5270, 0xD98A, 0xA0DC, 0x974B, 0xEE1D, 0x65E7, 0x1CB1,
537b651957cSAlexander Duyck 	0x0AC9, 0x739F, 0xF865, 0x8133, 0xB6A4, 0xCFF2, 0x4408, 0x3D5E,
538b651957cSAlexander Duyck 	0x68F8, 0x11AE, 0x9A54, 0xE302, 0xD495, 0xADC3, 0x2639, 0x5F6F,
539b651957cSAlexander Duyck 	0x4917, 0x3041, 0xBBBB, 0xC2ED, 0xF57A, 0x8C2C, 0x07D6, 0x7E80,
540b651957cSAlexander Duyck 	0xFAD7, 0x8381, 0x087B, 0x712D, 0x46BA, 0x3FEC, 0xB416, 0xCD40,
541b651957cSAlexander Duyck 	0xDB38, 0xA26E, 0x2994, 0x50C2, 0x6755, 0x1E03, 0x95F9, 0xECAF,
542b651957cSAlexander Duyck 	0xB909, 0xC05F, 0x4BA5, 0x32F3, 0x0564, 0x7C32, 0xF7C8, 0x8E9E,
543b651957cSAlexander Duyck 	0x98E6, 0xE1B0, 0x6A4A, 0x131C, 0x248B, 0x5DDD, 0xD627, 0xAF71,
544b651957cSAlexander Duyck 	0x7D6B, 0x043D, 0x8FC7, 0xF691, 0xC106, 0xB850, 0x33AA, 0x4AFC,
545b651957cSAlexander Duyck 	0x5C84, 0x25D2, 0xAE28, 0xD77E, 0xE0E9, 0x99BF, 0x1245, 0x6B13,
546b651957cSAlexander Duyck 	0x3EB5, 0x47E3, 0xCC19, 0xB54F, 0x82D8, 0xFB8E, 0x7074, 0x0922,
547b651957cSAlexander Duyck 	0x1F5A, 0x660C, 0xEDF6, 0x94A0, 0xA337, 0xDA61, 0x519B, 0x28CD };
548b651957cSAlexander Duyck 
549b651957cSAlexander Duyck /**
550b651957cSAlexander Duyck  *  fm10k_crc_16b - Generate a 16 bit CRC for a region of 16 bit data
551b651957cSAlexander Duyck  *  @data: pointer to data to process
552b651957cSAlexander Duyck  *  @seed: seed value for CRC
553b651957cSAlexander Duyck  *  @len: length measured in 16 bits words
554b651957cSAlexander Duyck  *
555b651957cSAlexander Duyck  *  This function will generate a CRC based on the polynomial 0xAC9A and
556b651957cSAlexander Duyck  *  whatever value is stored in the seed variable.  Note that this
557b651957cSAlexander Duyck  *  value inverts the local seed and the result in order to capture all
558b651957cSAlexander Duyck  *  leading and trailing zeros.
559b651957cSAlexander Duyck  */
fm10k_crc_16b(const u32 * data,u16 seed,u16 len)560b651957cSAlexander Duyck static u16 fm10k_crc_16b(const u32 *data, u16 seed, u16 len)
561b651957cSAlexander Duyck {
562b651957cSAlexander Duyck 	u32 result = seed;
563b651957cSAlexander Duyck 
564b651957cSAlexander Duyck 	while (len--) {
565b651957cSAlexander Duyck 		result ^= *(data++);
566b651957cSAlexander Duyck 		result = (result >> 8) ^ fm10k_crc_16b_table[result & 0xFF];
567b651957cSAlexander Duyck 		result = (result >> 8) ^ fm10k_crc_16b_table[result & 0xFF];
568b651957cSAlexander Duyck 
569b651957cSAlexander Duyck 		if (!(len--))
570b651957cSAlexander Duyck 			break;
571b651957cSAlexander Duyck 
572b651957cSAlexander Duyck 		result = (result >> 8) ^ fm10k_crc_16b_table[result & 0xFF];
573b651957cSAlexander Duyck 		result = (result >> 8) ^ fm10k_crc_16b_table[result & 0xFF];
574b651957cSAlexander Duyck 	}
575b651957cSAlexander Duyck 
576b651957cSAlexander Duyck 	return (u16)result;
577b651957cSAlexander Duyck }
578b651957cSAlexander Duyck 
579b651957cSAlexander Duyck /**
580b651957cSAlexander Duyck  *  fm10k_fifo_crc - generate a CRC based off of FIFO data
581b651957cSAlexander Duyck  *  @fifo: pointer to FIFO
582b651957cSAlexander Duyck  *  @offset: offset point for start of FIFO
583b651957cSAlexander Duyck  *  @len: number of DWORDS words to process
584b651957cSAlexander Duyck  *  @seed: seed value for CRC
585b651957cSAlexander Duyck  *
586b651957cSAlexander Duyck  *  This function generates a CRC for some region of the FIFO
587b651957cSAlexander Duyck  **/
fm10k_fifo_crc(struct fm10k_mbx_fifo * fifo,u16 offset,u16 len,u16 seed)588b651957cSAlexander Duyck static u16 fm10k_fifo_crc(struct fm10k_mbx_fifo *fifo, u16 offset,
589b651957cSAlexander Duyck 			  u16 len, u16 seed)
590b651957cSAlexander Duyck {
591b651957cSAlexander Duyck 	u32 *data = fifo->buffer + offset;
592b651957cSAlexander Duyck 
593b651957cSAlexander Duyck 	/* track when we should cross the end of the FIFO */
594b651957cSAlexander Duyck 	offset = fifo->size - offset;
595b651957cSAlexander Duyck 
596b651957cSAlexander Duyck 	/* if we are in 2 blocks process the end of the FIFO first */
597b651957cSAlexander Duyck 	if (offset < len) {
598b651957cSAlexander Duyck 		seed = fm10k_crc_16b(data, seed, offset * 2);
599b651957cSAlexander Duyck 		data = fifo->buffer;
600b651957cSAlexander Duyck 		len -= offset;
601b651957cSAlexander Duyck 	}
602b651957cSAlexander Duyck 
603b651957cSAlexander Duyck 	/* process any remaining bits */
604b651957cSAlexander Duyck 	return fm10k_crc_16b(data, seed, len * 2);
605b651957cSAlexander Duyck }
606b651957cSAlexander Duyck 
607b651957cSAlexander Duyck /**
608b651957cSAlexander Duyck  *  fm10k_mbx_update_local_crc - Update the local CRC for outgoing data
609b651957cSAlexander Duyck  *  @mbx: pointer to mailbox
610b651957cSAlexander Duyck  *  @head: head index provided by remote mailbox
611b651957cSAlexander Duyck  *
612b651957cSAlexander Duyck  *  This function will generate the CRC for all data from the end of the
613b651957cSAlexander Duyck  *  last head update to the current one.  It uses the result of the
614b651957cSAlexander Duyck  *  previous CRC as the seed for this update.  The result is stored in
615b651957cSAlexander Duyck  *  mbx->local.
616b651957cSAlexander Duyck  **/
fm10k_mbx_update_local_crc(struct fm10k_mbx_info * mbx,u16 head)617b651957cSAlexander Duyck static void fm10k_mbx_update_local_crc(struct fm10k_mbx_info *mbx, u16 head)
618b651957cSAlexander Duyck {
619b651957cSAlexander Duyck 	u16 len = mbx->tail_len - fm10k_mbx_index_len(mbx, head, mbx->tail);
620b651957cSAlexander Duyck 
621b651957cSAlexander Duyck 	/* determine the offset for the start of the region to be pulled */
622b651957cSAlexander Duyck 	head = fm10k_fifo_head_offset(&mbx->tx, mbx->pulled);
623b651957cSAlexander Duyck 
624b651957cSAlexander Duyck 	/* update local CRC to include all of the pulled data */
625b651957cSAlexander Duyck 	mbx->local = fm10k_fifo_crc(&mbx->tx, head, len, mbx->local);
626b651957cSAlexander Duyck }
627b651957cSAlexander Duyck 
628b651957cSAlexander Duyck /**
629b651957cSAlexander Duyck  *  fm10k_mbx_verify_remote_crc - Verify the CRC is correct for current data
630b651957cSAlexander Duyck  *  @mbx: pointer to mailbox
631b651957cSAlexander Duyck  *
632b651957cSAlexander Duyck  *  This function will take all data that has been provided from the remote
633b651957cSAlexander Duyck  *  end and generate a CRC for it.  This is stored in mbx->remote.  The
634b651957cSAlexander Duyck  *  CRC for the header is then computed and if the result is non-zero this
635b651957cSAlexander Duyck  *  is an error and we signal an error dropping all data and resetting the
636b651957cSAlexander Duyck  *  connection.
637b651957cSAlexander Duyck  */
fm10k_mbx_verify_remote_crc(struct fm10k_mbx_info * mbx)638b651957cSAlexander Duyck static s32 fm10k_mbx_verify_remote_crc(struct fm10k_mbx_info *mbx)
639b651957cSAlexander Duyck {
640b651957cSAlexander Duyck 	struct fm10k_mbx_fifo *fifo = &mbx->rx;
641b651957cSAlexander Duyck 	u16 len = mbx->head_len;
642b651957cSAlexander Duyck 	u16 offset = fm10k_fifo_tail_offset(fifo, mbx->pushed) - len;
643b651957cSAlexander Duyck 	u16 crc;
644b651957cSAlexander Duyck 
645b651957cSAlexander Duyck 	/* update the remote CRC if new data has been received */
646b651957cSAlexander Duyck 	if (len)
647b651957cSAlexander Duyck 		mbx->remote = fm10k_fifo_crc(fifo, offset, len, mbx->remote);
648b651957cSAlexander Duyck 
649b651957cSAlexander Duyck 	/* process the full header as we have to validate the CRC */
650b651957cSAlexander Duyck 	crc = fm10k_crc_16b(&mbx->mbx_hdr, mbx->remote, 1);
651b651957cSAlexander Duyck 
652b651957cSAlexander Duyck 	/* notify other end if we have a problem */
653b651957cSAlexander Duyck 	return crc ? FM10K_MBX_ERR_CRC : 0;
654b651957cSAlexander Duyck }
655b651957cSAlexander Duyck 
6561337e6b9SAlexander Duyck /**
6571337e6b9SAlexander Duyck  *  fm10k_mbx_rx_ready - Indicates that a message is ready in the Rx FIFO
6581337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
6591337e6b9SAlexander Duyck  *
6601337e6b9SAlexander Duyck  *  This function returns true if there is a message in the Rx FIFO to dequeue.
6611337e6b9SAlexander Duyck  **/
fm10k_mbx_rx_ready(struct fm10k_mbx_info * mbx)6621337e6b9SAlexander Duyck static bool fm10k_mbx_rx_ready(struct fm10k_mbx_info *mbx)
6631337e6b9SAlexander Duyck {
6641337e6b9SAlexander Duyck 	u16 msg_size = fm10k_fifo_head_len(&mbx->rx);
6651337e6b9SAlexander Duyck 
6661337e6b9SAlexander Duyck 	return msg_size && (fm10k_fifo_used(&mbx->rx) >= msg_size);
6671337e6b9SAlexander Duyck }
6681337e6b9SAlexander Duyck 
6691337e6b9SAlexander Duyck /**
6701337e6b9SAlexander Duyck  *  fm10k_mbx_tx_ready - Indicates that the mailbox is in state ready for Tx
6711337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
6721337e6b9SAlexander Duyck  *  @len: verify free space is >= this value
6731337e6b9SAlexander Duyck  *
6741337e6b9SAlexander Duyck  *  This function returns true if the mailbox is in a state ready to transmit.
6751337e6b9SAlexander Duyck  **/
fm10k_mbx_tx_ready(struct fm10k_mbx_info * mbx,u16 len)6761337e6b9SAlexander Duyck static bool fm10k_mbx_tx_ready(struct fm10k_mbx_info *mbx, u16 len)
6771337e6b9SAlexander Duyck {
6781337e6b9SAlexander Duyck 	u16 fifo_unused = fm10k_fifo_unused(&mbx->tx);
6791337e6b9SAlexander Duyck 
6801337e6b9SAlexander Duyck 	return (mbx->state == FM10K_STATE_OPEN) && (fifo_unused >= len);
6811337e6b9SAlexander Duyck }
6821337e6b9SAlexander Duyck 
6831337e6b9SAlexander Duyck /**
6841337e6b9SAlexander Duyck  *  fm10k_mbx_tx_complete - Indicates that the Tx FIFO has been emptied
6851337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
6861337e6b9SAlexander Duyck  *
6871337e6b9SAlexander Duyck  *  This function returns true if the Tx FIFO is empty.
6881337e6b9SAlexander Duyck  **/
fm10k_mbx_tx_complete(struct fm10k_mbx_info * mbx)6891337e6b9SAlexander Duyck static bool fm10k_mbx_tx_complete(struct fm10k_mbx_info *mbx)
6901337e6b9SAlexander Duyck {
6911337e6b9SAlexander Duyck 	return fm10k_fifo_empty(&mbx->tx);
6921337e6b9SAlexander Duyck }
6931337e6b9SAlexander Duyck 
6941337e6b9SAlexander Duyck /**
695262de08fSJesse Brandeburg  *  fm10k_mbx_dequeue_rx - Dequeues the message from the head in the Rx FIFO
6961337e6b9SAlexander Duyck  *  @hw: pointer to hardware structure
6971337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
6981337e6b9SAlexander Duyck  *
699f632fed3SBruce Allan  *  This function dequeues messages and hands them off to the TLV parser.
7001337e6b9SAlexander Duyck  *  It will return the number of messages processed when called.
7011337e6b9SAlexander Duyck  **/
fm10k_mbx_dequeue_rx(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx)7021337e6b9SAlexander Duyck static u16 fm10k_mbx_dequeue_rx(struct fm10k_hw *hw,
7031337e6b9SAlexander Duyck 				struct fm10k_mbx_info *mbx)
7041337e6b9SAlexander Duyck {
7051337e6b9SAlexander Duyck 	struct fm10k_mbx_fifo *fifo = &mbx->rx;
7061337e6b9SAlexander Duyck 	s32 err;
7071337e6b9SAlexander Duyck 	u16 cnt;
7081337e6b9SAlexander Duyck 
7091337e6b9SAlexander Duyck 	/* parse Rx messages out of the Rx FIFO to empty it */
7101337e6b9SAlexander Duyck 	for (cnt = 0; !fm10k_fifo_empty(fifo); cnt++) {
7111337e6b9SAlexander Duyck 		err = fm10k_tlv_msg_parse(hw, fifo->buffer + fifo->head,
7121337e6b9SAlexander Duyck 					  mbx, mbx->msg_data);
7131337e6b9SAlexander Duyck 		if (err < 0)
7141337e6b9SAlexander Duyck 			mbx->rx_parse_err++;
7151337e6b9SAlexander Duyck 
7161337e6b9SAlexander Duyck 		fm10k_fifo_head_drop(fifo);
7171337e6b9SAlexander Duyck 	}
7181337e6b9SAlexander Duyck 
7191337e6b9SAlexander Duyck 	/* shift remaining bytes back to start of FIFO */
7201337e6b9SAlexander Duyck 	memmove(fifo->buffer, fifo->buffer + fifo->tail, mbx->pushed << 2);
7211337e6b9SAlexander Duyck 
7221337e6b9SAlexander Duyck 	/* shift head and tail based on the memory we moved */
7231337e6b9SAlexander Duyck 	fifo->tail -= fifo->head;
7241337e6b9SAlexander Duyck 	fifo->head = 0;
7251337e6b9SAlexander Duyck 
7261337e6b9SAlexander Duyck 	return cnt;
7271337e6b9SAlexander Duyck }
7281337e6b9SAlexander Duyck 
7291337e6b9SAlexander Duyck /**
7301337e6b9SAlexander Duyck  *  fm10k_mbx_enqueue_tx - Enqueues the message to the tail of the Tx FIFO
7311337e6b9SAlexander Duyck  *  @hw: pointer to hardware structure
7321337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
7331337e6b9SAlexander Duyck  *  @msg: message array to read
7341337e6b9SAlexander Duyck  *
7351337e6b9SAlexander Duyck  *  This function enqueues a message up to the size specified by the length
7361337e6b9SAlexander Duyck  *  contained in the first DWORD of the message and will place at the tail
7371337e6b9SAlexander Duyck  *  of the FIFO.  It will return 0 on success, or a negative value on error.
7381337e6b9SAlexander Duyck  **/
fm10k_mbx_enqueue_tx(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx,const u32 * msg)7391337e6b9SAlexander Duyck static s32 fm10k_mbx_enqueue_tx(struct fm10k_hw *hw,
7401337e6b9SAlexander Duyck 				struct fm10k_mbx_info *mbx, const u32 *msg)
7411337e6b9SAlexander Duyck {
7421337e6b9SAlexander Duyck 	u32 countdown = mbx->timeout;
7431337e6b9SAlexander Duyck 	s32 err;
7441337e6b9SAlexander Duyck 
7451337e6b9SAlexander Duyck 	switch (mbx->state) {
7461337e6b9SAlexander Duyck 	case FM10K_STATE_CLOSED:
7471337e6b9SAlexander Duyck 	case FM10K_STATE_DISCONNECT:
7481337e6b9SAlexander Duyck 		return FM10K_MBX_ERR_NO_MBX;
7491337e6b9SAlexander Duyck 	default:
7501337e6b9SAlexander Duyck 		break;
7511337e6b9SAlexander Duyck 	}
7521337e6b9SAlexander Duyck 
7531337e6b9SAlexander Duyck 	/* enqueue the message on the Tx FIFO */
7541337e6b9SAlexander Duyck 	err = fm10k_fifo_enqueue(&mbx->tx, msg);
7551337e6b9SAlexander Duyck 
7561337e6b9SAlexander Duyck 	/* if it failed give the FIFO a chance to drain */
7571337e6b9SAlexander Duyck 	while (err && countdown) {
7581337e6b9SAlexander Duyck 		countdown--;
7591337e6b9SAlexander Duyck 		udelay(mbx->udelay);
7601337e6b9SAlexander Duyck 		mbx->ops.process(hw, mbx);
7611337e6b9SAlexander Duyck 		err = fm10k_fifo_enqueue(&mbx->tx, msg);
7621337e6b9SAlexander Duyck 	}
7631337e6b9SAlexander Duyck 
764eca32047SMatthew Vick 	/* if we failed treat the error */
7651337e6b9SAlexander Duyck 	if (err) {
7661337e6b9SAlexander Duyck 		mbx->timeout = 0;
7671337e6b9SAlexander Duyck 		mbx->tx_busy++;
7681337e6b9SAlexander Duyck 	}
7691337e6b9SAlexander Duyck 
7701337e6b9SAlexander Duyck 	/* begin processing message, ignore errors as this is just meant
7711337e6b9SAlexander Duyck 	 * to start the mailbox flow so we are not concerned if there
7721337e6b9SAlexander Duyck 	 * is a bad error, or the mailbox is already busy with a request
7731337e6b9SAlexander Duyck 	 */
7741337e6b9SAlexander Duyck 	if (!mbx->tail_len)
7751337e6b9SAlexander Duyck 		mbx->ops.process(hw, mbx);
7761337e6b9SAlexander Duyck 
7771337e6b9SAlexander Duyck 	return 0;
7781337e6b9SAlexander Duyck }
7791337e6b9SAlexander Duyck 
7801337e6b9SAlexander Duyck /**
7811337e6b9SAlexander Duyck  *  fm10k_mbx_read - Copies the mbmem to local message buffer
7821337e6b9SAlexander Duyck  *  @hw: pointer to hardware structure
7831337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
7841337e6b9SAlexander Duyck  *
7851337e6b9SAlexander Duyck  *  This function copies the message from the mbmem to the message array
7861337e6b9SAlexander Duyck  **/
fm10k_mbx_read(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx)7871337e6b9SAlexander Duyck static s32 fm10k_mbx_read(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx)
7881337e6b9SAlexander Duyck {
7891337e6b9SAlexander Duyck 	/* only allow one reader in here at a time */
7901337e6b9SAlexander Duyck 	if (mbx->mbx_hdr)
7911337e6b9SAlexander Duyck 		return FM10K_MBX_ERR_BUSY;
7921337e6b9SAlexander Duyck 
7931337e6b9SAlexander Duyck 	/* read to capture initial interrupt bits */
7941337e6b9SAlexander Duyck 	if (fm10k_read_reg(hw, mbx->mbx_reg) & FM10K_MBX_REQ_INTERRUPT)
7951337e6b9SAlexander Duyck 		mbx->mbx_lock = FM10K_MBX_ACK;
7961337e6b9SAlexander Duyck 
7971337e6b9SAlexander Duyck 	/* write back interrupt bits to clear */
7981337e6b9SAlexander Duyck 	fm10k_write_reg(hw, mbx->mbx_reg,
7991337e6b9SAlexander Duyck 			FM10K_MBX_REQ_INTERRUPT | FM10K_MBX_ACK_INTERRUPT);
8001337e6b9SAlexander Duyck 
8011337e6b9SAlexander Duyck 	/* read remote header */
8021337e6b9SAlexander Duyck 	mbx->mbx_hdr = fm10k_read_reg(hw, mbx->mbmem_reg ^ mbx->mbmem_len);
8031337e6b9SAlexander Duyck 
8041337e6b9SAlexander Duyck 	return 0;
8051337e6b9SAlexander Duyck }
8061337e6b9SAlexander Duyck 
8071337e6b9SAlexander Duyck /**
8081337e6b9SAlexander Duyck  *  fm10k_mbx_write - Copies the local message buffer to mbmem
8091337e6b9SAlexander Duyck  *  @hw: pointer to hardware structure
8101337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
8111337e6b9SAlexander Duyck  *
812*a5f97658SJiang Jian  *  This function copies the message from the message array to mbmem
8131337e6b9SAlexander Duyck  **/
fm10k_mbx_write(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx)8141337e6b9SAlexander Duyck static void fm10k_mbx_write(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx)
8151337e6b9SAlexander Duyck {
8161337e6b9SAlexander Duyck 	u32 mbmem = mbx->mbmem_reg;
8171337e6b9SAlexander Duyck 
818eca32047SMatthew Vick 	/* write new msg header to notify recipient of change */
8191337e6b9SAlexander Duyck 	fm10k_write_reg(hw, mbmem, mbx->mbx_hdr);
8201337e6b9SAlexander Duyck 
821a4bcea83SJeff Kirsher 	/* write mailbox to send interrupt */
8221337e6b9SAlexander Duyck 	if (mbx->mbx_lock)
8231337e6b9SAlexander Duyck 		fm10k_write_reg(hw, mbx->mbx_reg, mbx->mbx_lock);
8241337e6b9SAlexander Duyck 
8251337e6b9SAlexander Duyck 	/* we no longer are using the header so free it */
8261337e6b9SAlexander Duyck 	mbx->mbx_hdr = 0;
8271337e6b9SAlexander Duyck 	mbx->mbx_lock = 0;
8281337e6b9SAlexander Duyck }
8291337e6b9SAlexander Duyck 
8301337e6b9SAlexander Duyck /**
831b651957cSAlexander Duyck  *  fm10k_mbx_create_connect_hdr - Generate a connect mailbox header
832b651957cSAlexander Duyck  *  @mbx: pointer to mailbox
833b651957cSAlexander Duyck  *
834b651957cSAlexander Duyck  *  This function returns a connection mailbox header
835b651957cSAlexander Duyck  **/
fm10k_mbx_create_connect_hdr(struct fm10k_mbx_info * mbx)836b651957cSAlexander Duyck static void fm10k_mbx_create_connect_hdr(struct fm10k_mbx_info *mbx)
837b651957cSAlexander Duyck {
838b651957cSAlexander Duyck 	mbx->mbx_lock |= FM10K_MBX_REQ;
839b651957cSAlexander Duyck 
840b651957cSAlexander Duyck 	mbx->mbx_hdr = FM10K_MSG_HDR_FIELD_SET(FM10K_MSG_CONNECT, TYPE) |
841b651957cSAlexander Duyck 		       FM10K_MSG_HDR_FIELD_SET(mbx->head, HEAD) |
842b651957cSAlexander Duyck 		       FM10K_MSG_HDR_FIELD_SET(mbx->rx.size - 1, CONNECT_SIZE);
843b651957cSAlexander Duyck }
844b651957cSAlexander Duyck 
845b651957cSAlexander Duyck /**
846b651957cSAlexander Duyck  *  fm10k_mbx_create_data_hdr - Generate a data mailbox header
847b651957cSAlexander Duyck  *  @mbx: pointer to mailbox
848b651957cSAlexander Duyck  *
849b651957cSAlexander Duyck  *  This function returns a data mailbox header
850b651957cSAlexander Duyck  **/
fm10k_mbx_create_data_hdr(struct fm10k_mbx_info * mbx)851b651957cSAlexander Duyck static void fm10k_mbx_create_data_hdr(struct fm10k_mbx_info *mbx)
852b651957cSAlexander Duyck {
853b651957cSAlexander Duyck 	u32 hdr = FM10K_MSG_HDR_FIELD_SET(FM10K_MSG_DATA, TYPE) |
854b651957cSAlexander Duyck 		  FM10K_MSG_HDR_FIELD_SET(mbx->tail, TAIL) |
855b651957cSAlexander Duyck 		  FM10K_MSG_HDR_FIELD_SET(mbx->head, HEAD);
856b651957cSAlexander Duyck 	struct fm10k_mbx_fifo *fifo = &mbx->tx;
857b651957cSAlexander Duyck 	u16 crc;
858b651957cSAlexander Duyck 
859b651957cSAlexander Duyck 	if (mbx->tail_len)
860b651957cSAlexander Duyck 		mbx->mbx_lock |= FM10K_MBX_REQ;
861b651957cSAlexander Duyck 
862b651957cSAlexander Duyck 	/* generate CRC for data in flight and header */
863b651957cSAlexander Duyck 	crc = fm10k_fifo_crc(fifo, fm10k_fifo_head_offset(fifo, mbx->pulled),
864b651957cSAlexander Duyck 			     mbx->tail_len, mbx->local);
865b651957cSAlexander Duyck 	crc = fm10k_crc_16b(&hdr, crc, 1);
866b651957cSAlexander Duyck 
867b651957cSAlexander Duyck 	/* load header to memory to be written */
868b651957cSAlexander Duyck 	mbx->mbx_hdr = hdr | FM10K_MSG_HDR_FIELD_SET(crc, CRC);
869b651957cSAlexander Duyck }
870b651957cSAlexander Duyck 
871b651957cSAlexander Duyck /**
872b651957cSAlexander Duyck  *  fm10k_mbx_create_disconnect_hdr - Generate a disconnect mailbox header
873b651957cSAlexander Duyck  *  @mbx: pointer to mailbox
874b651957cSAlexander Duyck  *
875b651957cSAlexander Duyck  *  This function returns a disconnect mailbox header
876b651957cSAlexander Duyck  **/
fm10k_mbx_create_disconnect_hdr(struct fm10k_mbx_info * mbx)877b651957cSAlexander Duyck static void fm10k_mbx_create_disconnect_hdr(struct fm10k_mbx_info *mbx)
878b651957cSAlexander Duyck {
879b651957cSAlexander Duyck 	u32 hdr = FM10K_MSG_HDR_FIELD_SET(FM10K_MSG_DISCONNECT, TYPE) |
880b651957cSAlexander Duyck 		  FM10K_MSG_HDR_FIELD_SET(mbx->tail, TAIL) |
881b651957cSAlexander Duyck 		  FM10K_MSG_HDR_FIELD_SET(mbx->head, HEAD);
882b651957cSAlexander Duyck 	u16 crc = fm10k_crc_16b(&hdr, mbx->local, 1);
883b651957cSAlexander Duyck 
884b651957cSAlexander Duyck 	mbx->mbx_lock |= FM10K_MBX_ACK;
885b651957cSAlexander Duyck 
886b651957cSAlexander Duyck 	/* load header to memory to be written */
887b651957cSAlexander Duyck 	mbx->mbx_hdr = hdr | FM10K_MSG_HDR_FIELD_SET(crc, CRC);
888b651957cSAlexander Duyck }
889b651957cSAlexander Duyck 
890b651957cSAlexander Duyck /**
8913d02b3dfSBruce Allan  *  fm10k_mbx_create_fake_disconnect_hdr - Generate a false disconnect mbox hdr
892afadfd22SJacob Keller  *  @mbx: pointer to mailbox
893afadfd22SJacob Keller  *
894afadfd22SJacob Keller  *  This function creates a fake disconnect header for loading into remote
895afadfd22SJacob Keller  *  mailbox header. The primary purpose is to prevent errors on immediate
896afadfd22SJacob Keller  *  start up after mbx->connect.
897afadfd22SJacob Keller  **/
fm10k_mbx_create_fake_disconnect_hdr(struct fm10k_mbx_info * mbx)898afadfd22SJacob Keller static void fm10k_mbx_create_fake_disconnect_hdr(struct fm10k_mbx_info *mbx)
899afadfd22SJacob Keller {
900afadfd22SJacob Keller 	u32 hdr = FM10K_MSG_HDR_FIELD_SET(FM10K_MSG_DISCONNECT, TYPE) |
901afadfd22SJacob Keller 		  FM10K_MSG_HDR_FIELD_SET(mbx->head, TAIL) |
902afadfd22SJacob Keller 		  FM10K_MSG_HDR_FIELD_SET(mbx->tail, HEAD);
903afadfd22SJacob Keller 	u16 crc = fm10k_crc_16b(&hdr, mbx->local, 1);
904afadfd22SJacob Keller 
905afadfd22SJacob Keller 	mbx->mbx_lock |= FM10K_MBX_ACK;
906afadfd22SJacob Keller 
907afadfd22SJacob Keller 	/* load header to memory to be written */
908afadfd22SJacob Keller 	mbx->mbx_hdr = hdr | FM10K_MSG_HDR_FIELD_SET(crc, CRC);
909afadfd22SJacob Keller }
910afadfd22SJacob Keller 
911afadfd22SJacob Keller /**
912f632fed3SBruce Allan  *  fm10k_mbx_create_error_msg - Generate an error message
913b651957cSAlexander Duyck  *  @mbx: pointer to mailbox
914b651957cSAlexander Duyck  *  @err: local error encountered
915b651957cSAlexander Duyck  *
916b651957cSAlexander Duyck  *  This function will interpret the error provided by err, and based on
917b651957cSAlexander Duyck  *  that it may shift the message by 1 DWORD and then place an error header
918b651957cSAlexander Duyck  *  at the start of the message.
919b651957cSAlexander Duyck  **/
fm10k_mbx_create_error_msg(struct fm10k_mbx_info * mbx,s32 err)920b651957cSAlexander Duyck static void fm10k_mbx_create_error_msg(struct fm10k_mbx_info *mbx, s32 err)
921b651957cSAlexander Duyck {
922b651957cSAlexander Duyck 	/* only generate an error message for these types */
923b651957cSAlexander Duyck 	switch (err) {
924b651957cSAlexander Duyck 	case FM10K_MBX_ERR_TAIL:
925b651957cSAlexander Duyck 	case FM10K_MBX_ERR_HEAD:
926b651957cSAlexander Duyck 	case FM10K_MBX_ERR_TYPE:
927b651957cSAlexander Duyck 	case FM10K_MBX_ERR_SIZE:
928b651957cSAlexander Duyck 	case FM10K_MBX_ERR_RSVD0:
929b651957cSAlexander Duyck 	case FM10K_MBX_ERR_CRC:
930b651957cSAlexander Duyck 		break;
931b651957cSAlexander Duyck 	default:
932b651957cSAlexander Duyck 		return;
933b651957cSAlexander Duyck 	}
934b651957cSAlexander Duyck 
935b651957cSAlexander Duyck 	mbx->mbx_lock |= FM10K_MBX_REQ;
936b651957cSAlexander Duyck 
937b651957cSAlexander Duyck 	mbx->mbx_hdr = FM10K_MSG_HDR_FIELD_SET(FM10K_MSG_ERROR, TYPE) |
938b651957cSAlexander Duyck 		       FM10K_MSG_HDR_FIELD_SET(err, ERR_NO) |
939b651957cSAlexander Duyck 		       FM10K_MSG_HDR_FIELD_SET(mbx->head, HEAD);
940b651957cSAlexander Duyck }
941b651957cSAlexander Duyck 
942b651957cSAlexander Duyck /**
943b651957cSAlexander Duyck  *  fm10k_mbx_validate_msg_hdr - Validate common fields in the message header
944b651957cSAlexander Duyck  *  @mbx: pointer to mailbox
945b651957cSAlexander Duyck  *
946b651957cSAlexander Duyck  *  This function will parse up the fields in the mailbox header and return
947b651957cSAlexander Duyck  *  an error if the header contains any of a number of invalid configurations
948b651957cSAlexander Duyck  *  including unrecognized type, invalid route, or a malformed message.
949b651957cSAlexander Duyck  **/
fm10k_mbx_validate_msg_hdr(struct fm10k_mbx_info * mbx)950b651957cSAlexander Duyck static s32 fm10k_mbx_validate_msg_hdr(struct fm10k_mbx_info *mbx)
951b651957cSAlexander Duyck {
952b651957cSAlexander Duyck 	u16 type, rsvd0, head, tail, size;
953b651957cSAlexander Duyck 	const u32 *hdr = &mbx->mbx_hdr;
954b651957cSAlexander Duyck 
955b651957cSAlexander Duyck 	type = FM10K_MSG_HDR_FIELD_GET(*hdr, TYPE);
956b651957cSAlexander Duyck 	rsvd0 = FM10K_MSG_HDR_FIELD_GET(*hdr, RSVD0);
957b651957cSAlexander Duyck 	tail = FM10K_MSG_HDR_FIELD_GET(*hdr, TAIL);
958b651957cSAlexander Duyck 	head = FM10K_MSG_HDR_FIELD_GET(*hdr, HEAD);
959b651957cSAlexander Duyck 	size = FM10K_MSG_HDR_FIELD_GET(*hdr, CONNECT_SIZE);
960b651957cSAlexander Duyck 
961b651957cSAlexander Duyck 	if (rsvd0)
962b651957cSAlexander Duyck 		return FM10K_MBX_ERR_RSVD0;
963b651957cSAlexander Duyck 
964b651957cSAlexander Duyck 	switch (type) {
965b651957cSAlexander Duyck 	case FM10K_MSG_DISCONNECT:
966b651957cSAlexander Duyck 		/* validate that all data has been received */
967b651957cSAlexander Duyck 		if (tail != mbx->head)
968b651957cSAlexander Duyck 			return FM10K_MBX_ERR_TAIL;
969b651957cSAlexander Duyck 
9705463fce6SJeff Kirsher 		fallthrough;
971b651957cSAlexander Duyck 	case FM10K_MSG_DATA:
972b651957cSAlexander Duyck 		/* validate that head is moving correctly */
973b651957cSAlexander Duyck 		if (!head || (head == FM10K_MSG_HDR_MASK(HEAD)))
974b651957cSAlexander Duyck 			return FM10K_MBX_ERR_HEAD;
975b651957cSAlexander Duyck 		if (fm10k_mbx_index_len(mbx, head, mbx->tail) > mbx->tail_len)
976b651957cSAlexander Duyck 			return FM10K_MBX_ERR_HEAD;
977b651957cSAlexander Duyck 
978b651957cSAlexander Duyck 		/* validate that tail is moving correctly */
979b651957cSAlexander Duyck 		if (!tail || (tail == FM10K_MSG_HDR_MASK(TAIL)))
980b651957cSAlexander Duyck 			return FM10K_MBX_ERR_TAIL;
981b651957cSAlexander Duyck 		if (fm10k_mbx_index_len(mbx, mbx->head, tail) < mbx->mbmem_len)
982b651957cSAlexander Duyck 			break;
983b651957cSAlexander Duyck 
984b651957cSAlexander Duyck 		return FM10K_MBX_ERR_TAIL;
985b651957cSAlexander Duyck 	case FM10K_MSG_CONNECT:
986b651957cSAlexander Duyck 		/* validate size is in range and is power of 2 mask */
987b651957cSAlexander Duyck 		if ((size < FM10K_VFMBX_MSG_MTU) || (size & (size + 1)))
988b651957cSAlexander Duyck 			return FM10K_MBX_ERR_SIZE;
989b651957cSAlexander Duyck 
9905463fce6SJeff Kirsher 		fallthrough;
991b651957cSAlexander Duyck 	case FM10K_MSG_ERROR:
992b651957cSAlexander Duyck 		if (!head || (head == FM10K_MSG_HDR_MASK(HEAD)))
993b651957cSAlexander Duyck 			return FM10K_MBX_ERR_HEAD;
994b651957cSAlexander Duyck 		/* neither create nor error include a tail offset */
995b651957cSAlexander Duyck 		if (tail)
996b651957cSAlexander Duyck 			return FM10K_MBX_ERR_TAIL;
997b651957cSAlexander Duyck 
998b651957cSAlexander Duyck 		break;
999b651957cSAlexander Duyck 	default:
1000b651957cSAlexander Duyck 		return FM10K_MBX_ERR_TYPE;
1001b651957cSAlexander Duyck 	}
1002b651957cSAlexander Duyck 
1003b651957cSAlexander Duyck 	return 0;
1004b651957cSAlexander Duyck }
1005b651957cSAlexander Duyck 
1006b651957cSAlexander Duyck /**
1007b651957cSAlexander Duyck  *  fm10k_mbx_create_reply - Generate reply based on state and remote head
1008f632fed3SBruce Allan  *  @hw: pointer to hardware structure
1009b651957cSAlexander Duyck  *  @mbx: pointer to mailbox
1010b651957cSAlexander Duyck  *  @head: acknowledgement number
1011b651957cSAlexander Duyck  *
1012b651957cSAlexander Duyck  *  This function will generate an outgoing message based on the current
1013f632fed3SBruce Allan  *  mailbox state and the remote FIFO head.  It will return the length
1014b651957cSAlexander Duyck  *  of the outgoing message excluding header on success, and a negative value
1015b651957cSAlexander Duyck  *  on error.
1016b651957cSAlexander Duyck  **/
fm10k_mbx_create_reply(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx,u16 head)1017b651957cSAlexander Duyck static s32 fm10k_mbx_create_reply(struct fm10k_hw *hw,
1018b651957cSAlexander Duyck 				  struct fm10k_mbx_info *mbx, u16 head)
1019b651957cSAlexander Duyck {
1020b651957cSAlexander Duyck 	switch (mbx->state) {
1021b651957cSAlexander Duyck 	case FM10K_STATE_OPEN:
1022b651957cSAlexander Duyck 	case FM10K_STATE_DISCONNECT:
1023b651957cSAlexander Duyck 		/* update our checksum for the outgoing data */
1024b651957cSAlexander Duyck 		fm10k_mbx_update_local_crc(mbx, head);
1025b651957cSAlexander Duyck 
1026b651957cSAlexander Duyck 		/* as long as other end recognizes us keep sending data */
1027b651957cSAlexander Duyck 		fm10k_mbx_pull_head(hw, mbx, head);
1028b651957cSAlexander Duyck 
1029b651957cSAlexander Duyck 		/* generate new header based on data */
1030b651957cSAlexander Duyck 		if (mbx->tail_len || (mbx->state == FM10K_STATE_OPEN))
1031b651957cSAlexander Duyck 			fm10k_mbx_create_data_hdr(mbx);
1032b651957cSAlexander Duyck 		else
1033b651957cSAlexander Duyck 			fm10k_mbx_create_disconnect_hdr(mbx);
1034b651957cSAlexander Duyck 		break;
1035b651957cSAlexander Duyck 	case FM10K_STATE_CONNECT:
1036b651957cSAlexander Duyck 		/* send disconnect even if we aren't connected */
1037b651957cSAlexander Duyck 		fm10k_mbx_create_connect_hdr(mbx);
1038b651957cSAlexander Duyck 		break;
1039b651957cSAlexander Duyck 	case FM10K_STATE_CLOSED:
1040b651957cSAlexander Duyck 		/* generate new header based on data */
1041b651957cSAlexander Duyck 		fm10k_mbx_create_disconnect_hdr(mbx);
1042f83a0d0aSGustavo A. R. Silva 		break;
1043b651957cSAlexander Duyck 	default:
1044b651957cSAlexander Duyck 		break;
1045b651957cSAlexander Duyck 	}
1046b651957cSAlexander Duyck 
1047b651957cSAlexander Duyck 	return 0;
1048b651957cSAlexander Duyck }
1049b651957cSAlexander Duyck 
1050b651957cSAlexander Duyck /**
10511337e6b9SAlexander Duyck  *  fm10k_mbx_reset_work- Reset internal pointers for any pending work
10521337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
10531337e6b9SAlexander Duyck  *
10541337e6b9SAlexander Duyck  *  This function will reset all internal pointers so any work in progress
10551337e6b9SAlexander Duyck  *  is dropped.  This call should occur every time we transition from the
10561337e6b9SAlexander Duyck  *  open state to the connect state.
10571337e6b9SAlexander Duyck  **/
fm10k_mbx_reset_work(struct fm10k_mbx_info * mbx)10581337e6b9SAlexander Duyck static void fm10k_mbx_reset_work(struct fm10k_mbx_info *mbx)
10591337e6b9SAlexander Duyck {
10604b09728eSJacob Keller 	u16 len, head, ack;
10614b09728eSJacob Keller 
10621337e6b9SAlexander Duyck 	/* reset our outgoing max size back to Rx limits */
10631337e6b9SAlexander Duyck 	mbx->max_size = mbx->rx.size - 1;
10641337e6b9SAlexander Duyck 
10654b09728eSJacob Keller 	/* update mbx->pulled to account for tail_len and ack */
10664b09728eSJacob Keller 	head = FM10K_MSG_HDR_FIELD_GET(mbx->mbx_hdr, HEAD);
10674b09728eSJacob Keller 	ack = fm10k_mbx_index_len(mbx, head, mbx->tail);
10684b09728eSJacob Keller 	mbx->pulled += mbx->tail_len - ack;
10694b09728eSJacob Keller 
10704b09728eSJacob Keller 	/* now drop any messages which have started or finished transmitting */
10714b09728eSJacob Keller 	while (fm10k_fifo_head_len(&mbx->tx) && mbx->pulled) {
10724b09728eSJacob Keller 		len = fm10k_fifo_head_drop(&mbx->tx);
10734b09728eSJacob Keller 		mbx->tx_dropped++;
10744b09728eSJacob Keller 		if (mbx->pulled >= len)
10754b09728eSJacob Keller 			mbx->pulled -= len;
10764b09728eSJacob Keller 		else
10774b09728eSJacob Keller 			mbx->pulled = 0;
10784b09728eSJacob Keller 	}
10794b09728eSJacob Keller 
10801337e6b9SAlexander Duyck 	/* just do a quick resysnc to start of message */
10811337e6b9SAlexander Duyck 	mbx->pushed = 0;
10821337e6b9SAlexander Duyck 	mbx->pulled = 0;
10831337e6b9SAlexander Duyck 	mbx->tail_len = 0;
10841337e6b9SAlexander Duyck 	mbx->head_len = 0;
10851337e6b9SAlexander Duyck 	mbx->rx.tail = 0;
10861337e6b9SAlexander Duyck 	mbx->rx.head = 0;
10871337e6b9SAlexander Duyck }
10881337e6b9SAlexander Duyck 
10891337e6b9SAlexander Duyck /**
10901337e6b9SAlexander Duyck  *  fm10k_mbx_update_max_size - Update the max_size and drop any large messages
10911337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
10921337e6b9SAlexander Duyck  *  @size: new value for max_size
10931337e6b9SAlexander Duyck  *
1094da61b367SJeff Kirsher  *  This function updates the max_size value and drops any outgoing messages
1095da61b367SJeff Kirsher  *  at the head of the Tx FIFO if they are larger than max_size. It does not
1096da61b367SJeff Kirsher  *  drop all messages, as this is too difficult to parse and remove them from
1097da61b367SJeff Kirsher  *  the FIFO. Instead, rely on the checking to ensure that messages larger
1098da61b367SJeff Kirsher  *  than max_size aren't pushed into the memory buffer.
10991337e6b9SAlexander Duyck  **/
fm10k_mbx_update_max_size(struct fm10k_mbx_info * mbx,u16 size)11001337e6b9SAlexander Duyck static void fm10k_mbx_update_max_size(struct fm10k_mbx_info *mbx, u16 size)
11011337e6b9SAlexander Duyck {
11021337e6b9SAlexander Duyck 	u16 len;
11031337e6b9SAlexander Duyck 
11041337e6b9SAlexander Duyck 	mbx->max_size = size;
11051337e6b9SAlexander Duyck 
11061337e6b9SAlexander Duyck 	/* flush any oversized messages from the queue */
11071337e6b9SAlexander Duyck 	for (len = fm10k_fifo_head_len(&mbx->tx);
11081337e6b9SAlexander Duyck 	     len > size;
11091337e6b9SAlexander Duyck 	     len = fm10k_fifo_head_len(&mbx->tx)) {
11101337e6b9SAlexander Duyck 		fm10k_fifo_head_drop(&mbx->tx);
11111337e6b9SAlexander Duyck 		mbx->tx_dropped++;
11121337e6b9SAlexander Duyck 	}
11131337e6b9SAlexander Duyck }
11141337e6b9SAlexander Duyck 
11151337e6b9SAlexander Duyck /**
1116b651957cSAlexander Duyck  *  fm10k_mbx_connect_reset - Reset following request for reset
1117b651957cSAlexander Duyck  *  @mbx: pointer to mailbox
1118b651957cSAlexander Duyck  *
1119b651957cSAlexander Duyck  *  This function resets the mailbox to either a disconnected state
1120b651957cSAlexander Duyck  *  or a connect state depending on the current mailbox state
1121b651957cSAlexander Duyck  **/
fm10k_mbx_connect_reset(struct fm10k_mbx_info * mbx)1122b651957cSAlexander Duyck static void fm10k_mbx_connect_reset(struct fm10k_mbx_info *mbx)
1123b651957cSAlexander Duyck {
1124b651957cSAlexander Duyck 	/* just do a quick resysnc to start of frame */
1125b651957cSAlexander Duyck 	fm10k_mbx_reset_work(mbx);
1126b651957cSAlexander Duyck 
1127b651957cSAlexander Duyck 	/* reset CRC seeds */
1128b651957cSAlexander Duyck 	mbx->local = FM10K_MBX_CRC_SEED;
1129b651957cSAlexander Duyck 	mbx->remote = FM10K_MBX_CRC_SEED;
1130b651957cSAlexander Duyck 
1131b651957cSAlexander Duyck 	/* we cannot exit connect until the size is good */
1132b651957cSAlexander Duyck 	if (mbx->state == FM10K_STATE_OPEN)
1133b651957cSAlexander Duyck 		mbx->state = FM10K_STATE_CONNECT;
1134b651957cSAlexander Duyck 	else
1135b651957cSAlexander Duyck 		mbx->state = FM10K_STATE_CLOSED;
1136b651957cSAlexander Duyck }
1137b651957cSAlexander Duyck 
1138b651957cSAlexander Duyck /**
1139b651957cSAlexander Duyck  *  fm10k_mbx_process_connect - Process connect header
1140f632fed3SBruce Allan  *  @hw: pointer to hardware structure
1141b651957cSAlexander Duyck  *  @mbx: pointer to mailbox
1142b651957cSAlexander Duyck  *
1143b651957cSAlexander Duyck  *  This function will read an incoming connect header and reply with the
1144b651957cSAlexander Duyck  *  appropriate message.  It will return a value indicating the number of
1145b651957cSAlexander Duyck  *  data DWORDs on success, or will return a negative value on failure.
1146b651957cSAlexander Duyck  **/
fm10k_mbx_process_connect(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx)1147b651957cSAlexander Duyck static s32 fm10k_mbx_process_connect(struct fm10k_hw *hw,
1148b651957cSAlexander Duyck 				     struct fm10k_mbx_info *mbx)
1149b651957cSAlexander Duyck {
1150b651957cSAlexander Duyck 	const enum fm10k_mbx_state state = mbx->state;
1151b651957cSAlexander Duyck 	const u32 *hdr = &mbx->mbx_hdr;
1152b651957cSAlexander Duyck 	u16 size, head;
1153b651957cSAlexander Duyck 
1154b651957cSAlexander Duyck 	/* we will need to pull all of the fields for verification */
1155b651957cSAlexander Duyck 	size = FM10K_MSG_HDR_FIELD_GET(*hdr, CONNECT_SIZE);
1156b651957cSAlexander Duyck 	head = FM10K_MSG_HDR_FIELD_GET(*hdr, HEAD);
1157b651957cSAlexander Duyck 
1158b651957cSAlexander Duyck 	switch (state) {
1159b651957cSAlexander Duyck 	case FM10K_STATE_DISCONNECT:
1160b651957cSAlexander Duyck 	case FM10K_STATE_OPEN:
1161b651957cSAlexander Duyck 		/* reset any in-progress work */
1162b651957cSAlexander Duyck 		fm10k_mbx_connect_reset(mbx);
1163b651957cSAlexander Duyck 		break;
1164b651957cSAlexander Duyck 	case FM10K_STATE_CONNECT:
1165b651957cSAlexander Duyck 		/* we cannot exit connect until the size is good */
1166b651957cSAlexander Duyck 		if (size > mbx->rx.size) {
1167b651957cSAlexander Duyck 			mbx->max_size = mbx->rx.size - 1;
1168b651957cSAlexander Duyck 		} else {
1169b651957cSAlexander Duyck 			/* record the remote system requesting connection */
1170b651957cSAlexander Duyck 			mbx->state = FM10K_STATE_OPEN;
1171b651957cSAlexander Duyck 
1172b651957cSAlexander Duyck 			fm10k_mbx_update_max_size(mbx, size);
1173b651957cSAlexander Duyck 		}
1174b651957cSAlexander Duyck 		break;
1175b651957cSAlexander Duyck 	default:
1176b651957cSAlexander Duyck 		break;
1177b651957cSAlexander Duyck 	}
1178b651957cSAlexander Duyck 
1179b651957cSAlexander Duyck 	/* align our tail index to remote head index */
1180b651957cSAlexander Duyck 	mbx->tail = head;
1181b651957cSAlexander Duyck 
1182b651957cSAlexander Duyck 	return fm10k_mbx_create_reply(hw, mbx, head);
1183b651957cSAlexander Duyck }
1184b651957cSAlexander Duyck 
1185b651957cSAlexander Duyck /**
1186b651957cSAlexander Duyck  *  fm10k_mbx_process_data - Process data header
1187f632fed3SBruce Allan  *  @hw: pointer to hardware structure
1188b651957cSAlexander Duyck  *  @mbx: pointer to mailbox
1189b651957cSAlexander Duyck  *
1190b651957cSAlexander Duyck  *  This function will read an incoming data header and reply with the
1191b651957cSAlexander Duyck  *  appropriate message.  It will return a value indicating the number of
1192b651957cSAlexander Duyck  *  data DWORDs on success, or will return a negative value on failure.
1193b651957cSAlexander Duyck  **/
fm10k_mbx_process_data(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx)1194b651957cSAlexander Duyck static s32 fm10k_mbx_process_data(struct fm10k_hw *hw,
1195b651957cSAlexander Duyck 				  struct fm10k_mbx_info *mbx)
1196b651957cSAlexander Duyck {
1197b651957cSAlexander Duyck 	const u32 *hdr = &mbx->mbx_hdr;
1198b651957cSAlexander Duyck 	u16 head, tail;
1199b651957cSAlexander Duyck 	s32 err;
1200b651957cSAlexander Duyck 
1201b651957cSAlexander Duyck 	/* we will need to pull all of the fields for verification */
1202b651957cSAlexander Duyck 	head = FM10K_MSG_HDR_FIELD_GET(*hdr, HEAD);
1203b651957cSAlexander Duyck 	tail = FM10K_MSG_HDR_FIELD_GET(*hdr, TAIL);
1204b651957cSAlexander Duyck 
1205b651957cSAlexander Duyck 	/* if we are in connect just update our data and go */
1206b651957cSAlexander Duyck 	if (mbx->state == FM10K_STATE_CONNECT) {
1207b651957cSAlexander Duyck 		mbx->tail = head;
1208b651957cSAlexander Duyck 		mbx->state = FM10K_STATE_OPEN;
1209b651957cSAlexander Duyck 	}
1210b651957cSAlexander Duyck 
1211b651957cSAlexander Duyck 	/* abort on message size errors */
1212b651957cSAlexander Duyck 	err = fm10k_mbx_push_tail(hw, mbx, tail);
1213b651957cSAlexander Duyck 	if (err < 0)
1214b651957cSAlexander Duyck 		return err;
1215b651957cSAlexander Duyck 
1216b651957cSAlexander Duyck 	/* verify the checksum on the incoming data */
1217b651957cSAlexander Duyck 	err = fm10k_mbx_verify_remote_crc(mbx);
1218b651957cSAlexander Duyck 	if (err)
1219b651957cSAlexander Duyck 		return err;
1220b651957cSAlexander Duyck 
1221b651957cSAlexander Duyck 	/* process messages if we have received any */
1222b651957cSAlexander Duyck 	fm10k_mbx_dequeue_rx(hw, mbx);
1223b651957cSAlexander Duyck 
1224b651957cSAlexander Duyck 	return fm10k_mbx_create_reply(hw, mbx, head);
1225b651957cSAlexander Duyck }
1226b651957cSAlexander Duyck 
1227b651957cSAlexander Duyck /**
1228b651957cSAlexander Duyck  *  fm10k_mbx_process_disconnect - Process disconnect header
1229f632fed3SBruce Allan  *  @hw: pointer to hardware structure
1230b651957cSAlexander Duyck  *  @mbx: pointer to mailbox
1231b651957cSAlexander Duyck  *
1232b651957cSAlexander Duyck  *  This function will read an incoming disconnect header and reply with the
1233b651957cSAlexander Duyck  *  appropriate message.  It will return a value indicating the number of
1234b651957cSAlexander Duyck  *  data DWORDs on success, or will return a negative value on failure.
1235b651957cSAlexander Duyck  **/
fm10k_mbx_process_disconnect(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx)1236b651957cSAlexander Duyck static s32 fm10k_mbx_process_disconnect(struct fm10k_hw *hw,
1237b651957cSAlexander Duyck 					struct fm10k_mbx_info *mbx)
1238b651957cSAlexander Duyck {
1239b651957cSAlexander Duyck 	const enum fm10k_mbx_state state = mbx->state;
1240b651957cSAlexander Duyck 	const u32 *hdr = &mbx->mbx_hdr;
1241f4a80f1eSMatthew Vick 	u16 head;
1242b651957cSAlexander Duyck 	s32 err;
1243b651957cSAlexander Duyck 
1244f4a80f1eSMatthew Vick 	/* we will need to pull the header field for verification */
1245b651957cSAlexander Duyck 	head = FM10K_MSG_HDR_FIELD_GET(*hdr, HEAD);
1246b651957cSAlexander Duyck 
1247b651957cSAlexander Duyck 	/* We should not be receiving disconnect if Rx is incomplete */
1248b651957cSAlexander Duyck 	if (mbx->pushed)
1249b651957cSAlexander Duyck 		return FM10K_MBX_ERR_TAIL;
1250b651957cSAlexander Duyck 
1251b651957cSAlexander Duyck 	/* we have already verified mbx->head == tail so we know this is 0 */
1252b651957cSAlexander Duyck 	mbx->head_len = 0;
1253b651957cSAlexander Duyck 
1254b651957cSAlexander Duyck 	/* verify the checksum on the incoming header is correct */
1255b651957cSAlexander Duyck 	err = fm10k_mbx_verify_remote_crc(mbx);
1256b651957cSAlexander Duyck 	if (err)
1257b651957cSAlexander Duyck 		return err;
1258b651957cSAlexander Duyck 
1259b651957cSAlexander Duyck 	switch (state) {
1260b651957cSAlexander Duyck 	case FM10K_STATE_DISCONNECT:
1261b651957cSAlexander Duyck 	case FM10K_STATE_OPEN:
1262b651957cSAlexander Duyck 		/* state doesn't change if we still have work to do */
1263b651957cSAlexander Duyck 		if (!fm10k_mbx_tx_complete(mbx))
1264b651957cSAlexander Duyck 			break;
1265b651957cSAlexander Duyck 
1266b651957cSAlexander Duyck 		/* verify the head indicates we completed all transmits */
1267b651957cSAlexander Duyck 		if (head != mbx->tail)
1268b651957cSAlexander Duyck 			return FM10K_MBX_ERR_HEAD;
1269b651957cSAlexander Duyck 
1270b651957cSAlexander Duyck 		/* reset any in-progress work */
1271b651957cSAlexander Duyck 		fm10k_mbx_connect_reset(mbx);
1272b651957cSAlexander Duyck 		break;
1273b651957cSAlexander Duyck 	default:
1274b651957cSAlexander Duyck 		break;
1275b651957cSAlexander Duyck 	}
1276b651957cSAlexander Duyck 
1277b651957cSAlexander Duyck 	return fm10k_mbx_create_reply(hw, mbx, head);
1278b651957cSAlexander Duyck }
1279b651957cSAlexander Duyck 
1280b651957cSAlexander Duyck /**
1281b651957cSAlexander Duyck  *  fm10k_mbx_process_error - Process error header
1282f632fed3SBruce Allan  *  @hw: pointer to hardware structure
1283b651957cSAlexander Duyck  *  @mbx: pointer to mailbox
1284b651957cSAlexander Duyck  *
1285b651957cSAlexander Duyck  *  This function will read an incoming error header and reply with the
1286b651957cSAlexander Duyck  *  appropriate message.  It will return a value indicating the number of
1287b651957cSAlexander Duyck  *  data DWORDs on success, or will return a negative value on failure.
1288b651957cSAlexander Duyck  **/
fm10k_mbx_process_error(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx)1289b651957cSAlexander Duyck static s32 fm10k_mbx_process_error(struct fm10k_hw *hw,
1290b651957cSAlexander Duyck 				   struct fm10k_mbx_info *mbx)
1291b651957cSAlexander Duyck {
1292b651957cSAlexander Duyck 	const u32 *hdr = &mbx->mbx_hdr;
1293b651957cSAlexander Duyck 	u16 head;
1294b651957cSAlexander Duyck 
1295b651957cSAlexander Duyck 	/* we will need to pull all of the fields for verification */
1296b651957cSAlexander Duyck 	head = FM10K_MSG_HDR_FIELD_GET(*hdr, HEAD);
1297b651957cSAlexander Duyck 
1298b651957cSAlexander Duyck 	switch (mbx->state) {
1299b651957cSAlexander Duyck 	case FM10K_STATE_OPEN:
1300b651957cSAlexander Duyck 	case FM10K_STATE_DISCONNECT:
1301b651957cSAlexander Duyck 		/* flush any uncompleted work */
1302b651957cSAlexander Duyck 		fm10k_mbx_reset_work(mbx);
1303b651957cSAlexander Duyck 
1304b651957cSAlexander Duyck 		/* reset CRC seeds */
1305b651957cSAlexander Duyck 		mbx->local = FM10K_MBX_CRC_SEED;
1306b651957cSAlexander Duyck 		mbx->remote = FM10K_MBX_CRC_SEED;
1307b651957cSAlexander Duyck 
1308b651957cSAlexander Duyck 		/* reset tail index and size to prepare for reconnect */
1309b651957cSAlexander Duyck 		mbx->tail = head;
1310b651957cSAlexander Duyck 
1311b651957cSAlexander Duyck 		/* if open then reset max_size and go back to connect */
1312b651957cSAlexander Duyck 		if (mbx->state == FM10K_STATE_OPEN) {
1313b651957cSAlexander Duyck 			mbx->state = FM10K_STATE_CONNECT;
1314b651957cSAlexander Duyck 			break;
1315b651957cSAlexander Duyck 		}
1316b651957cSAlexander Duyck 
1317b651957cSAlexander Duyck 		/* send a connect message to get data flowing again */
1318b651957cSAlexander Duyck 		fm10k_mbx_create_connect_hdr(mbx);
1319b651957cSAlexander Duyck 		return 0;
1320b651957cSAlexander Duyck 	default:
1321b651957cSAlexander Duyck 		break;
1322b651957cSAlexander Duyck 	}
1323b651957cSAlexander Duyck 
1324b651957cSAlexander Duyck 	return fm10k_mbx_create_reply(hw, mbx, mbx->tail);
1325b651957cSAlexander Duyck }
1326b651957cSAlexander Duyck 
1327b651957cSAlexander Duyck /**
1328b651957cSAlexander Duyck  *  fm10k_mbx_process - Process mailbox interrupt
1329b651957cSAlexander Duyck  *  @hw: pointer to hardware structure
1330b651957cSAlexander Duyck  *  @mbx: pointer to mailbox
1331b651957cSAlexander Duyck  *
1332b651957cSAlexander Duyck  *  This function will process incoming mailbox events and generate mailbox
1333b651957cSAlexander Duyck  *  replies.  It will return a value indicating the number of DWORDs
1334b651957cSAlexander Duyck  *  transmitted excluding header on success or a negative value on error.
1335b651957cSAlexander Duyck  **/
fm10k_mbx_process(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx)1336b651957cSAlexander Duyck static s32 fm10k_mbx_process(struct fm10k_hw *hw,
1337b651957cSAlexander Duyck 			     struct fm10k_mbx_info *mbx)
1338b651957cSAlexander Duyck {
1339b651957cSAlexander Duyck 	s32 err;
1340b651957cSAlexander Duyck 
1341b651957cSAlexander Duyck 	/* we do not read mailbox if closed */
1342b651957cSAlexander Duyck 	if (mbx->state == FM10K_STATE_CLOSED)
1343b651957cSAlexander Duyck 		return 0;
1344b651957cSAlexander Duyck 
1345b651957cSAlexander Duyck 	/* copy data from mailbox */
1346b651957cSAlexander Duyck 	err = fm10k_mbx_read(hw, mbx);
1347b651957cSAlexander Duyck 	if (err)
1348b651957cSAlexander Duyck 		return err;
1349b651957cSAlexander Duyck 
1350b651957cSAlexander Duyck 	/* validate type, source, and destination */
1351b651957cSAlexander Duyck 	err = fm10k_mbx_validate_msg_hdr(mbx);
1352b651957cSAlexander Duyck 	if (err < 0)
1353b651957cSAlexander Duyck 		goto msg_err;
1354b651957cSAlexander Duyck 
1355b651957cSAlexander Duyck 	switch (FM10K_MSG_HDR_FIELD_GET(mbx->mbx_hdr, TYPE)) {
1356b651957cSAlexander Duyck 	case FM10K_MSG_CONNECT:
1357b651957cSAlexander Duyck 		err = fm10k_mbx_process_connect(hw, mbx);
1358b651957cSAlexander Duyck 		break;
1359b651957cSAlexander Duyck 	case FM10K_MSG_DATA:
1360b651957cSAlexander Duyck 		err = fm10k_mbx_process_data(hw, mbx);
1361b651957cSAlexander Duyck 		break;
1362b651957cSAlexander Duyck 	case FM10K_MSG_DISCONNECT:
1363b651957cSAlexander Duyck 		err = fm10k_mbx_process_disconnect(hw, mbx);
1364b651957cSAlexander Duyck 		break;
1365b651957cSAlexander Duyck 	case FM10K_MSG_ERROR:
1366b651957cSAlexander Duyck 		err = fm10k_mbx_process_error(hw, mbx);
1367b651957cSAlexander Duyck 		break;
1368b651957cSAlexander Duyck 	default:
1369b651957cSAlexander Duyck 		err = FM10K_MBX_ERR_TYPE;
1370b651957cSAlexander Duyck 		break;
1371b651957cSAlexander Duyck 	}
1372b651957cSAlexander Duyck 
1373b651957cSAlexander Duyck msg_err:
1374b651957cSAlexander Duyck 	/* notify partner of errors on our end */
1375b651957cSAlexander Duyck 	if (err < 0)
1376b651957cSAlexander Duyck 		fm10k_mbx_create_error_msg(mbx, err);
1377b651957cSAlexander Duyck 
1378b651957cSAlexander Duyck 	/* copy data from mailbox */
1379b651957cSAlexander Duyck 	fm10k_mbx_write(hw, mbx);
1380b651957cSAlexander Duyck 
1381b651957cSAlexander Duyck 	return err;
1382b651957cSAlexander Duyck }
1383b651957cSAlexander Duyck 
1384b651957cSAlexander Duyck /**
1385b651957cSAlexander Duyck  *  fm10k_mbx_disconnect - Shutdown mailbox connection
1386b651957cSAlexander Duyck  *  @hw: pointer to hardware structure
1387b651957cSAlexander Duyck  *  @mbx: pointer to mailbox
1388b651957cSAlexander Duyck  *
1389b651957cSAlexander Duyck  *  This function will shut down the mailbox.  It places the mailbox first
1390b651957cSAlexander Duyck  *  in the disconnect state, it then allows up to a predefined timeout for
1391b651957cSAlexander Duyck  *  the mailbox to transition to close on its own.  If this does not occur
1392b651957cSAlexander Duyck  *  then the mailbox will be forced into the closed state.
1393b651957cSAlexander Duyck  *
1394b651957cSAlexander Duyck  *  Any mailbox transactions not completed before calling this function
1395b651957cSAlexander Duyck  *  are not guaranteed to complete and may be dropped.
1396b651957cSAlexander Duyck  **/
fm10k_mbx_disconnect(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx)1397b651957cSAlexander Duyck static void fm10k_mbx_disconnect(struct fm10k_hw *hw,
1398b651957cSAlexander Duyck 				 struct fm10k_mbx_info *mbx)
1399b651957cSAlexander Duyck {
1400b651957cSAlexander Duyck 	int timeout = mbx->timeout ? FM10K_MBX_DISCONNECT_TIMEOUT : 0;
1401b651957cSAlexander Duyck 
1402b651957cSAlexander Duyck 	/* Place mbx in ready to disconnect state */
1403b651957cSAlexander Duyck 	mbx->state = FM10K_STATE_DISCONNECT;
1404b651957cSAlexander Duyck 
1405b651957cSAlexander Duyck 	/* trigger interrupt to start shutdown process */
1406b651957cSAlexander Duyck 	fm10k_write_reg(hw, mbx->mbx_reg, FM10K_MBX_REQ |
1407b651957cSAlexander Duyck 					  FM10K_MBX_INTERRUPT_DISABLE);
1408b651957cSAlexander Duyck 	do {
1409b651957cSAlexander Duyck 		udelay(FM10K_MBX_POLL_DELAY);
1410b651957cSAlexander Duyck 		mbx->ops.process(hw, mbx);
1411b651957cSAlexander Duyck 		timeout -= FM10K_MBX_POLL_DELAY;
1412b651957cSAlexander Duyck 	} while ((timeout > 0) && (mbx->state != FM10K_STATE_CLOSED));
1413b651957cSAlexander Duyck 
141478288e37SJeff Kirsher 	/* in case we didn't close, just force the mailbox into shutdown and
141578288e37SJeff Kirsher 	 * drop all left over messages in the FIFO.
141678288e37SJeff Kirsher 	 */
1417b651957cSAlexander Duyck 	fm10k_mbx_connect_reset(mbx);
141878288e37SJeff Kirsher 	fm10k_fifo_drop_all(&mbx->tx);
1419b651957cSAlexander Duyck 
1420b651957cSAlexander Duyck 	fm10k_write_reg(hw, mbx->mbmem_reg, 0);
1421b651957cSAlexander Duyck }
1422b651957cSAlexander Duyck 
1423b651957cSAlexander Duyck /**
1424b651957cSAlexander Duyck  *  fm10k_mbx_connect - Start mailbox connection
1425b651957cSAlexander Duyck  *  @hw: pointer to hardware structure
1426b651957cSAlexander Duyck  *  @mbx: pointer to mailbox
1427b651957cSAlexander Duyck  *
1428b651957cSAlexander Duyck  *  This function will initiate a mailbox connection.  It will populate the
1429b651957cSAlexander Duyck  *  mailbox with a broadcast connect message and then initialize the lock.
1430b651957cSAlexander Duyck  *  This is safe since the connect message is a single DWORD so the mailbox
1431b651957cSAlexander Duyck  *  transaction is guaranteed to be atomic.
1432b651957cSAlexander Duyck  *
1433b651957cSAlexander Duyck  *  This function will return an error if the mailbox has not been initiated
1434b651957cSAlexander Duyck  *  or is currently in use.
1435b651957cSAlexander Duyck  **/
fm10k_mbx_connect(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx)1436b651957cSAlexander Duyck static s32 fm10k_mbx_connect(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx)
1437b651957cSAlexander Duyck {
1438b651957cSAlexander Duyck 	/* we cannot connect an uninitialized mailbox */
1439b651957cSAlexander Duyck 	if (!mbx->rx.buffer)
1440b651957cSAlexander Duyck 		return FM10K_MBX_ERR_NO_SPACE;
1441b651957cSAlexander Duyck 
1442b651957cSAlexander Duyck 	/* we cannot connect an already connected mailbox */
1443b651957cSAlexander Duyck 	if (mbx->state != FM10K_STATE_CLOSED)
1444b651957cSAlexander Duyck 		return FM10K_MBX_ERR_BUSY;
1445b651957cSAlexander Duyck 
1446b651957cSAlexander Duyck 	/* mailbox timeout can now become active */
1447b651957cSAlexander Duyck 	mbx->timeout = FM10K_MBX_INIT_TIMEOUT;
1448b651957cSAlexander Duyck 
1449b651957cSAlexander Duyck 	/* Place mbx in ready to connect state */
1450b651957cSAlexander Duyck 	mbx->state = FM10K_STATE_CONNECT;
1451b651957cSAlexander Duyck 
1452afadfd22SJacob Keller 	fm10k_mbx_reset_work(mbx);
1453afadfd22SJacob Keller 
1454b651957cSAlexander Duyck 	/* initialize header of remote mailbox */
1455afadfd22SJacob Keller 	fm10k_mbx_create_fake_disconnect_hdr(mbx);
1456b651957cSAlexander Duyck 	fm10k_write_reg(hw, mbx->mbmem_reg ^ mbx->mbmem_len, mbx->mbx_hdr);
1457b651957cSAlexander Duyck 
1458b651957cSAlexander Duyck 	/* enable interrupt and notify other party of new message */
1459b651957cSAlexander Duyck 	mbx->mbx_lock = FM10K_MBX_REQ_INTERRUPT | FM10K_MBX_ACK_INTERRUPT |
1460b651957cSAlexander Duyck 			FM10K_MBX_INTERRUPT_ENABLE;
1461b651957cSAlexander Duyck 
1462b651957cSAlexander Duyck 	/* generate and load connect header into mailbox */
1463b651957cSAlexander Duyck 	fm10k_mbx_create_connect_hdr(mbx);
1464b651957cSAlexander Duyck 	fm10k_mbx_write(hw, mbx);
1465b651957cSAlexander Duyck 
1466b651957cSAlexander Duyck 	return 0;
1467b651957cSAlexander Duyck }
1468b651957cSAlexander Duyck 
1469b651957cSAlexander Duyck /**
14701337e6b9SAlexander Duyck  *  fm10k_mbx_validate_handlers - Validate layout of message parsing data
14711337e6b9SAlexander Duyck  *  @msg_data: handlers for mailbox events
14721337e6b9SAlexander Duyck  *
14731337e6b9SAlexander Duyck  *  This function validates the layout of the message parsing data.  This
14741337e6b9SAlexander Duyck  *  should be mostly static, but it is important to catch any errors that
14751337e6b9SAlexander Duyck  *  are made when constructing the parsers.
14761337e6b9SAlexander Duyck  **/
fm10k_mbx_validate_handlers(const struct fm10k_msg_data * msg_data)14771337e6b9SAlexander Duyck static s32 fm10k_mbx_validate_handlers(const struct fm10k_msg_data *msg_data)
14781337e6b9SAlexander Duyck {
14791337e6b9SAlexander Duyck 	const struct fm10k_tlv_attr *attr;
14801337e6b9SAlexander Duyck 	unsigned int id;
14811337e6b9SAlexander Duyck 
14821337e6b9SAlexander Duyck 	/* Allow NULL mailboxes that transmit but don't receive */
14831337e6b9SAlexander Duyck 	if (!msg_data)
14841337e6b9SAlexander Duyck 		return 0;
14851337e6b9SAlexander Duyck 
14861337e6b9SAlexander Duyck 	while (msg_data->id != FM10K_TLV_ERROR) {
14871337e6b9SAlexander Duyck 		/* all messages should have a function handler */
14881337e6b9SAlexander Duyck 		if (!msg_data->func)
14891337e6b9SAlexander Duyck 			return FM10K_ERR_PARAM;
14901337e6b9SAlexander Duyck 
14911337e6b9SAlexander Duyck 		/* parser is optional */
14921337e6b9SAlexander Duyck 		attr = msg_data->attr;
14931337e6b9SAlexander Duyck 		if (attr) {
14941337e6b9SAlexander Duyck 			while (attr->id != FM10K_TLV_ERROR) {
14951337e6b9SAlexander Duyck 				id = attr->id;
14961337e6b9SAlexander Duyck 				attr++;
14971337e6b9SAlexander Duyck 				/* ID should always be increasing */
14981337e6b9SAlexander Duyck 				if (id >= attr->id)
14991337e6b9SAlexander Duyck 					return FM10K_ERR_PARAM;
15001337e6b9SAlexander Duyck 				/* ID should fit in results array */
15011337e6b9SAlexander Duyck 				if (id >= FM10K_TLV_RESULTS_MAX)
15021337e6b9SAlexander Duyck 					return FM10K_ERR_PARAM;
15031337e6b9SAlexander Duyck 			}
15041337e6b9SAlexander Duyck 
15051337e6b9SAlexander Duyck 			/* verify terminator is in the list */
15061337e6b9SAlexander Duyck 			if (attr->id != FM10K_TLV_ERROR)
15071337e6b9SAlexander Duyck 				return FM10K_ERR_PARAM;
15081337e6b9SAlexander Duyck 		}
15091337e6b9SAlexander Duyck 
15101337e6b9SAlexander Duyck 		id = msg_data->id;
15111337e6b9SAlexander Duyck 		msg_data++;
15121337e6b9SAlexander Duyck 		/* ID should always be increasing */
15131337e6b9SAlexander Duyck 		if (id >= msg_data->id)
15141337e6b9SAlexander Duyck 			return FM10K_ERR_PARAM;
15151337e6b9SAlexander Duyck 	}
15161337e6b9SAlexander Duyck 
15171337e6b9SAlexander Duyck 	/* verify terminator is in the list */
15181337e6b9SAlexander Duyck 	if ((msg_data->id != FM10K_TLV_ERROR) || !msg_data->func)
15191337e6b9SAlexander Duyck 		return FM10K_ERR_PARAM;
15201337e6b9SAlexander Duyck 
15211337e6b9SAlexander Duyck 	return 0;
15221337e6b9SAlexander Duyck }
15231337e6b9SAlexander Duyck 
15241337e6b9SAlexander Duyck /**
15251337e6b9SAlexander Duyck  *  fm10k_mbx_register_handlers - Register a set of handler ops for mailbox
15261337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
15271337e6b9SAlexander Duyck  *  @msg_data: handlers for mailbox events
15281337e6b9SAlexander Duyck  *
15291337e6b9SAlexander Duyck  *  This function associates a set of message handling ops with a mailbox.
15301337e6b9SAlexander Duyck  **/
fm10k_mbx_register_handlers(struct fm10k_mbx_info * mbx,const struct fm10k_msg_data * msg_data)15311337e6b9SAlexander Duyck static s32 fm10k_mbx_register_handlers(struct fm10k_mbx_info *mbx,
15321337e6b9SAlexander Duyck 				       const struct fm10k_msg_data *msg_data)
15331337e6b9SAlexander Duyck {
15341337e6b9SAlexander Duyck 	/* validate layout of handlers before assigning them */
15351337e6b9SAlexander Duyck 	if (fm10k_mbx_validate_handlers(msg_data))
15361337e6b9SAlexander Duyck 		return FM10K_ERR_PARAM;
15371337e6b9SAlexander Duyck 
15381337e6b9SAlexander Duyck 	/* initialize the message handlers */
15391337e6b9SAlexander Duyck 	mbx->msg_data = msg_data;
15401337e6b9SAlexander Duyck 
15411337e6b9SAlexander Duyck 	return 0;
15421337e6b9SAlexander Duyck }
15431337e6b9SAlexander Duyck 
15441337e6b9SAlexander Duyck /**
1545b651957cSAlexander Duyck  *  fm10k_pfvf_mbx_init - Initialize mailbox memory for PF/VF mailbox
1546b651957cSAlexander Duyck  *  @hw: pointer to hardware structure
1547b651957cSAlexander Duyck  *  @mbx: pointer to mailbox
1548b651957cSAlexander Duyck  *  @msg_data: handlers for mailbox events
1549b651957cSAlexander Duyck  *  @id: ID reference for PF as it supports up to 64 PF/VF mailboxes
1550b651957cSAlexander Duyck  *
1551b651957cSAlexander Duyck  *  This function initializes the mailbox for use.  It will split the
1552f632fed3SBruce Allan  *  buffer provided and use that to populate both the Tx and Rx FIFO by
1553b651957cSAlexander Duyck  *  evenly splitting it.  In order to allow for easy masking of head/tail
1554b651957cSAlexander Duyck  *  the value reported in size must be a power of 2 and is reported in
1555b651957cSAlexander Duyck  *  DWORDs, not bytes.  Any invalid values will cause the mailbox to return
1556b651957cSAlexander Duyck  *  error.
1557b651957cSAlexander Duyck  **/
fm10k_pfvf_mbx_init(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx,const struct fm10k_msg_data * msg_data,u8 id)1558b651957cSAlexander Duyck s32 fm10k_pfvf_mbx_init(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx,
1559b651957cSAlexander Duyck 			const struct fm10k_msg_data *msg_data, u8 id)
1560b651957cSAlexander Duyck {
1561b651957cSAlexander Duyck 	/* initialize registers */
1562b651957cSAlexander Duyck 	switch (hw->mac.type) {
15635cb8db4aSAlexander Duyck 	case fm10k_mac_vf:
15645cb8db4aSAlexander Duyck 		mbx->mbx_reg = FM10K_VFMBX;
15655cb8db4aSAlexander Duyck 		mbx->mbmem_reg = FM10K_VFMBMEM(FM10K_VFMBMEM_VF_XOR);
15665cb8db4aSAlexander Duyck 		break;
1567b651957cSAlexander Duyck 	case fm10k_mac_pf:
1568b651957cSAlexander Duyck 		/* there are only 64 VF <-> PF mailboxes */
1569b651957cSAlexander Duyck 		if (id < 64) {
1570b651957cSAlexander Duyck 			mbx->mbx_reg = FM10K_MBX(id);
1571b651957cSAlexander Duyck 			mbx->mbmem_reg = FM10K_MBMEM_VF(id, 0);
1572b651957cSAlexander Duyck 			break;
1573b651957cSAlexander Duyck 		}
15745463fce6SJeff Kirsher 		fallthrough;
1575b651957cSAlexander Duyck 	default:
1576b651957cSAlexander Duyck 		return FM10K_MBX_ERR_NO_MBX;
1577b651957cSAlexander Duyck 	}
1578b651957cSAlexander Duyck 
1579b651957cSAlexander Duyck 	/* start out in closed state */
1580b651957cSAlexander Duyck 	mbx->state = FM10K_STATE_CLOSED;
1581b651957cSAlexander Duyck 
1582b651957cSAlexander Duyck 	/* validate layout of handlers before assigning them */
1583b651957cSAlexander Duyck 	if (fm10k_mbx_validate_handlers(msg_data))
1584b651957cSAlexander Duyck 		return FM10K_ERR_PARAM;
1585b651957cSAlexander Duyck 
1586b651957cSAlexander Duyck 	/* initialize the message handlers */
1587b651957cSAlexander Duyck 	mbx->msg_data = msg_data;
1588b651957cSAlexander Duyck 
1589b651957cSAlexander Duyck 	/* start mailbox as timed out and let the reset_hw call
1590b651957cSAlexander Duyck 	 * set the timeout value to begin communications
1591b651957cSAlexander Duyck 	 */
1592b651957cSAlexander Duyck 	mbx->timeout = 0;
1593b651957cSAlexander Duyck 	mbx->udelay = FM10K_MBX_INIT_DELAY;
1594b651957cSAlexander Duyck 
1595eca32047SMatthew Vick 	/* initialize tail and head */
1596b651957cSAlexander Duyck 	mbx->tail = 1;
1597b651957cSAlexander Duyck 	mbx->head = 1;
1598b651957cSAlexander Duyck 
1599b651957cSAlexander Duyck 	/* initialize CRC seeds */
1600b651957cSAlexander Duyck 	mbx->local = FM10K_MBX_CRC_SEED;
1601b651957cSAlexander Duyck 	mbx->remote = FM10K_MBX_CRC_SEED;
1602b651957cSAlexander Duyck 
1603b651957cSAlexander Duyck 	/* Split buffer for use by Tx/Rx FIFOs */
1604b651957cSAlexander Duyck 	mbx->max_size = FM10K_MBX_MSG_MAX_SIZE;
1605b651957cSAlexander Duyck 	mbx->mbmem_len = FM10K_VFMBMEM_VF_XOR;
1606b651957cSAlexander Duyck 
1607b651957cSAlexander Duyck 	/* initialize the FIFOs, sizes are in 4 byte increments */
1608b651957cSAlexander Duyck 	fm10k_fifo_init(&mbx->tx, mbx->buffer, FM10K_MBX_TX_BUFFER_SIZE);
1609b651957cSAlexander Duyck 	fm10k_fifo_init(&mbx->rx, &mbx->buffer[FM10K_MBX_TX_BUFFER_SIZE],
1610b651957cSAlexander Duyck 			FM10K_MBX_RX_BUFFER_SIZE);
1611b651957cSAlexander Duyck 
1612b651957cSAlexander Duyck 	/* initialize function pointers */
1613b651957cSAlexander Duyck 	mbx->ops.connect = fm10k_mbx_connect;
1614b651957cSAlexander Duyck 	mbx->ops.disconnect = fm10k_mbx_disconnect;
1615b651957cSAlexander Duyck 	mbx->ops.rx_ready = fm10k_mbx_rx_ready;
1616b651957cSAlexander Duyck 	mbx->ops.tx_ready = fm10k_mbx_tx_ready;
1617b651957cSAlexander Duyck 	mbx->ops.tx_complete = fm10k_mbx_tx_complete;
1618b651957cSAlexander Duyck 	mbx->ops.enqueue_tx = fm10k_mbx_enqueue_tx;
1619b651957cSAlexander Duyck 	mbx->ops.process = fm10k_mbx_process;
1620b651957cSAlexander Duyck 	mbx->ops.register_handlers = fm10k_mbx_register_handlers;
1621b651957cSAlexander Duyck 
1622b651957cSAlexander Duyck 	return 0;
1623b651957cSAlexander Duyck }
1624b651957cSAlexander Duyck 
1625b651957cSAlexander Duyck /**
16261337e6b9SAlexander Duyck  *  fm10k_sm_mbx_create_data_hdr - Generate a mailbox header for local FIFO
16271337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
16281337e6b9SAlexander Duyck  *
1629f632fed3SBruce Allan  *  This function returns a data mailbox header
16301337e6b9SAlexander Duyck  **/
fm10k_sm_mbx_create_data_hdr(struct fm10k_mbx_info * mbx)16311337e6b9SAlexander Duyck static void fm10k_sm_mbx_create_data_hdr(struct fm10k_mbx_info *mbx)
16321337e6b9SAlexander Duyck {
16331337e6b9SAlexander Duyck 	if (mbx->tail_len)
16341337e6b9SAlexander Duyck 		mbx->mbx_lock |= FM10K_MBX_REQ;
16351337e6b9SAlexander Duyck 
16361337e6b9SAlexander Duyck 	mbx->mbx_hdr = FM10K_MSG_HDR_FIELD_SET(mbx->tail, SM_TAIL) |
16371337e6b9SAlexander Duyck 		       FM10K_MSG_HDR_FIELD_SET(mbx->remote, SM_VER) |
16381337e6b9SAlexander Duyck 		       FM10K_MSG_HDR_FIELD_SET(mbx->head, SM_HEAD);
16391337e6b9SAlexander Duyck }
16401337e6b9SAlexander Duyck 
16411337e6b9SAlexander Duyck /**
16421337e6b9SAlexander Duyck  *  fm10k_sm_mbx_create_connect_hdr - Generate a mailbox header for local FIFO
16431337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
16441337e6b9SAlexander Duyck  *  @err: error flags to report if any
16451337e6b9SAlexander Duyck  *
16461337e6b9SAlexander Duyck  *  This function returns a connection mailbox header
16471337e6b9SAlexander Duyck  **/
fm10k_sm_mbx_create_connect_hdr(struct fm10k_mbx_info * mbx,u8 err)16481337e6b9SAlexander Duyck static void fm10k_sm_mbx_create_connect_hdr(struct fm10k_mbx_info *mbx, u8 err)
16491337e6b9SAlexander Duyck {
16501337e6b9SAlexander Duyck 	if (mbx->local)
16511337e6b9SAlexander Duyck 		mbx->mbx_lock |= FM10K_MBX_REQ;
16521337e6b9SAlexander Duyck 
16531337e6b9SAlexander Duyck 	mbx->mbx_hdr = FM10K_MSG_HDR_FIELD_SET(mbx->tail, SM_TAIL) |
16541337e6b9SAlexander Duyck 		       FM10K_MSG_HDR_FIELD_SET(mbx->remote, SM_VER) |
16551337e6b9SAlexander Duyck 		       FM10K_MSG_HDR_FIELD_SET(mbx->head, SM_HEAD) |
16561337e6b9SAlexander Duyck 		       FM10K_MSG_HDR_FIELD_SET(err, SM_ERR);
16571337e6b9SAlexander Duyck }
16581337e6b9SAlexander Duyck 
16591337e6b9SAlexander Duyck /**
16601337e6b9SAlexander Duyck  *  fm10k_sm_mbx_connect_reset - Reset following request for reset
16611337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
16621337e6b9SAlexander Duyck  *
16631337e6b9SAlexander Duyck  *  This function resets the mailbox to a just connected state
16641337e6b9SAlexander Duyck  **/
fm10k_sm_mbx_connect_reset(struct fm10k_mbx_info * mbx)16651337e6b9SAlexander Duyck static void fm10k_sm_mbx_connect_reset(struct fm10k_mbx_info *mbx)
16661337e6b9SAlexander Duyck {
16671337e6b9SAlexander Duyck 	/* flush any uncompleted work */
16681337e6b9SAlexander Duyck 	fm10k_mbx_reset_work(mbx);
16691337e6b9SAlexander Duyck 
16701337e6b9SAlexander Duyck 	/* set local version to max and remote version to 0 */
16711337e6b9SAlexander Duyck 	mbx->local = FM10K_SM_MBX_VERSION;
16721337e6b9SAlexander Duyck 	mbx->remote = 0;
16731337e6b9SAlexander Duyck 
1674eca32047SMatthew Vick 	/* initialize tail and head */
16751337e6b9SAlexander Duyck 	mbx->tail = 1;
16761337e6b9SAlexander Duyck 	mbx->head = 1;
16771337e6b9SAlexander Duyck 
16781337e6b9SAlexander Duyck 	/* reset state back to connect */
16791337e6b9SAlexander Duyck 	mbx->state = FM10K_STATE_CONNECT;
16801337e6b9SAlexander Duyck }
16811337e6b9SAlexander Duyck 
16821337e6b9SAlexander Duyck /**
16831337e6b9SAlexander Duyck  *  fm10k_sm_mbx_connect - Start switch manager mailbox connection
16841337e6b9SAlexander Duyck  *  @hw: pointer to hardware structure
16851337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
16861337e6b9SAlexander Duyck  *
16871337e6b9SAlexander Duyck  *  This function will initiate a mailbox connection with the switch
16881337e6b9SAlexander Duyck  *  manager.  To do this it will first disconnect the mailbox, and then
16891337e6b9SAlexander Duyck  *  reconnect it in order to complete a reset of the mailbox.
16901337e6b9SAlexander Duyck  *
16911337e6b9SAlexander Duyck  *  This function will return an error if the mailbox has not been initiated
16921337e6b9SAlexander Duyck  *  or is currently in use.
16931337e6b9SAlexander Duyck  **/
fm10k_sm_mbx_connect(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx)16941337e6b9SAlexander Duyck static s32 fm10k_sm_mbx_connect(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx)
16951337e6b9SAlexander Duyck {
16961337e6b9SAlexander Duyck 	/* we cannot connect an uninitialized mailbox */
16971337e6b9SAlexander Duyck 	if (!mbx->rx.buffer)
16981337e6b9SAlexander Duyck 		return FM10K_MBX_ERR_NO_SPACE;
16991337e6b9SAlexander Duyck 
17001337e6b9SAlexander Duyck 	/* we cannot connect an already connected mailbox */
17011337e6b9SAlexander Duyck 	if (mbx->state != FM10K_STATE_CLOSED)
17021337e6b9SAlexander Duyck 		return FM10K_MBX_ERR_BUSY;
17031337e6b9SAlexander Duyck 
17041337e6b9SAlexander Duyck 	/* mailbox timeout can now become active */
17051337e6b9SAlexander Duyck 	mbx->timeout = FM10K_MBX_INIT_TIMEOUT;
17061337e6b9SAlexander Duyck 
17071337e6b9SAlexander Duyck 	/* Place mbx in ready to connect state */
17081337e6b9SAlexander Duyck 	mbx->state = FM10K_STATE_CONNECT;
17091337e6b9SAlexander Duyck 	mbx->max_size = FM10K_MBX_MSG_MAX_SIZE;
17101337e6b9SAlexander Duyck 
17111337e6b9SAlexander Duyck 	/* reset interface back to connect */
17121337e6b9SAlexander Duyck 	fm10k_sm_mbx_connect_reset(mbx);
17131337e6b9SAlexander Duyck 
17141337e6b9SAlexander Duyck 	/* enable interrupt and notify other party of new message */
17151337e6b9SAlexander Duyck 	mbx->mbx_lock = FM10K_MBX_REQ_INTERRUPT | FM10K_MBX_ACK_INTERRUPT |
17161337e6b9SAlexander Duyck 			FM10K_MBX_INTERRUPT_ENABLE;
17171337e6b9SAlexander Duyck 
17181337e6b9SAlexander Duyck 	/* generate and load connect header into mailbox */
17191337e6b9SAlexander Duyck 	fm10k_sm_mbx_create_connect_hdr(mbx, 0);
17201337e6b9SAlexander Duyck 	fm10k_mbx_write(hw, mbx);
17211337e6b9SAlexander Duyck 
17221337e6b9SAlexander Duyck 	return 0;
17231337e6b9SAlexander Duyck }
17241337e6b9SAlexander Duyck 
17251337e6b9SAlexander Duyck /**
17261337e6b9SAlexander Duyck  *  fm10k_sm_mbx_disconnect - Shutdown mailbox connection
17271337e6b9SAlexander Duyck  *  @hw: pointer to hardware structure
17281337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
17291337e6b9SAlexander Duyck  *
17301337e6b9SAlexander Duyck  *  This function will shut down the mailbox.  It places the mailbox first
17311337e6b9SAlexander Duyck  *  in the disconnect state, it then allows up to a predefined timeout for
17321337e6b9SAlexander Duyck  *  the mailbox to transition to close on its own.  If this does not occur
17331337e6b9SAlexander Duyck  *  then the mailbox will be forced into the closed state.
17341337e6b9SAlexander Duyck  *
17351337e6b9SAlexander Duyck  *  Any mailbox transactions not completed before calling this function
17361337e6b9SAlexander Duyck  *  are not guaranteed to complete and may be dropped.
17371337e6b9SAlexander Duyck  **/
fm10k_sm_mbx_disconnect(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx)17381337e6b9SAlexander Duyck static void fm10k_sm_mbx_disconnect(struct fm10k_hw *hw,
17391337e6b9SAlexander Duyck 				    struct fm10k_mbx_info *mbx)
17401337e6b9SAlexander Duyck {
17411337e6b9SAlexander Duyck 	int timeout = mbx->timeout ? FM10K_MBX_DISCONNECT_TIMEOUT : 0;
17421337e6b9SAlexander Duyck 
17431337e6b9SAlexander Duyck 	/* Place mbx in ready to disconnect state */
17441337e6b9SAlexander Duyck 	mbx->state = FM10K_STATE_DISCONNECT;
17451337e6b9SAlexander Duyck 
17461337e6b9SAlexander Duyck 	/* trigger interrupt to start shutdown process */
17471337e6b9SAlexander Duyck 	fm10k_write_reg(hw, mbx->mbx_reg, FM10K_MBX_REQ |
17481337e6b9SAlexander Duyck 					  FM10K_MBX_INTERRUPT_DISABLE);
17491337e6b9SAlexander Duyck 	do {
17501337e6b9SAlexander Duyck 		udelay(FM10K_MBX_POLL_DELAY);
17511337e6b9SAlexander Duyck 		mbx->ops.process(hw, mbx);
17521337e6b9SAlexander Duyck 		timeout -= FM10K_MBX_POLL_DELAY;
17531337e6b9SAlexander Duyck 	} while ((timeout > 0) && (mbx->state != FM10K_STATE_CLOSED));
17541337e6b9SAlexander Duyck 
17551337e6b9SAlexander Duyck 	/* in case we didn't close just force the mailbox into shutdown */
17561337e6b9SAlexander Duyck 	mbx->state = FM10K_STATE_CLOSED;
17571337e6b9SAlexander Duyck 	mbx->remote = 0;
17581337e6b9SAlexander Duyck 	fm10k_mbx_reset_work(mbx);
17594b09728eSJacob Keller 	fm10k_fifo_drop_all(&mbx->tx);
17601337e6b9SAlexander Duyck 
17611337e6b9SAlexander Duyck 	fm10k_write_reg(hw, mbx->mbmem_reg, 0);
17621337e6b9SAlexander Duyck }
17631337e6b9SAlexander Duyck 
17641337e6b9SAlexander Duyck /**
1765f632fed3SBruce Allan  *  fm10k_sm_mbx_validate_fifo_hdr - Validate fields in the remote FIFO header
17661337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
17671337e6b9SAlexander Duyck  *
17681337e6b9SAlexander Duyck  *  This function will parse up the fields in the mailbox header and return
17691337e6b9SAlexander Duyck  *  an error if the header contains any of a number of invalid configurations
17701337e6b9SAlexander Duyck  *  including unrecognized offsets or version numbers.
17711337e6b9SAlexander Duyck  **/
fm10k_sm_mbx_validate_fifo_hdr(struct fm10k_mbx_info * mbx)17721337e6b9SAlexander Duyck static s32 fm10k_sm_mbx_validate_fifo_hdr(struct fm10k_mbx_info *mbx)
17731337e6b9SAlexander Duyck {
17741337e6b9SAlexander Duyck 	const u32 *hdr = &mbx->mbx_hdr;
17751337e6b9SAlexander Duyck 	u16 tail, head, ver;
17761337e6b9SAlexander Duyck 
17771337e6b9SAlexander Duyck 	tail = FM10K_MSG_HDR_FIELD_GET(*hdr, SM_TAIL);
17781337e6b9SAlexander Duyck 	ver = FM10K_MSG_HDR_FIELD_GET(*hdr, SM_VER);
17791337e6b9SAlexander Duyck 	head = FM10K_MSG_HDR_FIELD_GET(*hdr, SM_HEAD);
17801337e6b9SAlexander Duyck 
17811337e6b9SAlexander Duyck 	switch (ver) {
17821337e6b9SAlexander Duyck 	case 0:
17831337e6b9SAlexander Duyck 		break;
17841337e6b9SAlexander Duyck 	case FM10K_SM_MBX_VERSION:
17851337e6b9SAlexander Duyck 		if (!head || head > FM10K_SM_MBX_FIFO_LEN)
17861337e6b9SAlexander Duyck 			return FM10K_MBX_ERR_HEAD;
17871337e6b9SAlexander Duyck 		if (!tail || tail > FM10K_SM_MBX_FIFO_LEN)
17881337e6b9SAlexander Duyck 			return FM10K_MBX_ERR_TAIL;
17891337e6b9SAlexander Duyck 		if (mbx->tail < head)
17901337e6b9SAlexander Duyck 			head += mbx->mbmem_len - 1;
17911337e6b9SAlexander Duyck 		if (tail < mbx->head)
17921337e6b9SAlexander Duyck 			tail += mbx->mbmem_len - 1;
17931337e6b9SAlexander Duyck 		if (fm10k_mbx_index_len(mbx, head, mbx->tail) > mbx->tail_len)
17941337e6b9SAlexander Duyck 			return FM10K_MBX_ERR_HEAD;
17951337e6b9SAlexander Duyck 		if (fm10k_mbx_index_len(mbx, mbx->head, tail) < mbx->mbmem_len)
17961337e6b9SAlexander Duyck 			break;
17971337e6b9SAlexander Duyck 		return FM10K_MBX_ERR_TAIL;
17981337e6b9SAlexander Duyck 	default:
17991337e6b9SAlexander Duyck 		return FM10K_MBX_ERR_SRC;
18001337e6b9SAlexander Duyck 	}
18011337e6b9SAlexander Duyck 
18021337e6b9SAlexander Duyck 	return 0;
18031337e6b9SAlexander Duyck }
18041337e6b9SAlexander Duyck 
18051337e6b9SAlexander Duyck /**
18061337e6b9SAlexander Duyck  *  fm10k_sm_mbx_process_error - Process header with error flag set
18071337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
18081337e6b9SAlexander Duyck  *
18091337e6b9SAlexander Duyck  *  This function is meant to respond to a request where the error flag
18101337e6b9SAlexander Duyck  *  is set.  As a result we will terminate a connection if one is present
18111337e6b9SAlexander Duyck  *  and fall back into the reset state with a connection header of version
18121337e6b9SAlexander Duyck  *  0 (RESET).
18131337e6b9SAlexander Duyck  **/
fm10k_sm_mbx_process_error(struct fm10k_mbx_info * mbx)18141337e6b9SAlexander Duyck static void fm10k_sm_mbx_process_error(struct fm10k_mbx_info *mbx)
18151337e6b9SAlexander Duyck {
18161337e6b9SAlexander Duyck 	const enum fm10k_mbx_state state = mbx->state;
18171337e6b9SAlexander Duyck 
18181337e6b9SAlexander Duyck 	switch (state) {
18191337e6b9SAlexander Duyck 	case FM10K_STATE_DISCONNECT:
18201337e6b9SAlexander Duyck 		/* if there is an error just disconnect */
18211337e6b9SAlexander Duyck 		mbx->remote = 0;
18221337e6b9SAlexander Duyck 		break;
18231337e6b9SAlexander Duyck 	case FM10K_STATE_OPEN:
18241337e6b9SAlexander Duyck 		/* flush any uncompleted work */
18251337e6b9SAlexander Duyck 		fm10k_sm_mbx_connect_reset(mbx);
18261337e6b9SAlexander Duyck 		break;
18271337e6b9SAlexander Duyck 	case FM10K_STATE_CONNECT:
1828138f9f50SJulia Lawall 		/* try connecting at lower version */
18291337e6b9SAlexander Duyck 		if (mbx->remote) {
18301337e6b9SAlexander Duyck 			while (mbx->local > 1)
18311337e6b9SAlexander Duyck 				mbx->local--;
18321337e6b9SAlexander Duyck 			mbx->remote = 0;
18331337e6b9SAlexander Duyck 		}
18341337e6b9SAlexander Duyck 		break;
18351337e6b9SAlexander Duyck 	default:
18361337e6b9SAlexander Duyck 		break;
18371337e6b9SAlexander Duyck 	}
18381337e6b9SAlexander Duyck 
18391337e6b9SAlexander Duyck 	fm10k_sm_mbx_create_connect_hdr(mbx, 0);
18401337e6b9SAlexander Duyck }
18411337e6b9SAlexander Duyck 
18421337e6b9SAlexander Duyck /**
1843f632fed3SBruce Allan  *  fm10k_sm_mbx_create_error_msg - Process an error in FIFO header
18441337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
18451337e6b9SAlexander Duyck  *  @err: local error encountered
18461337e6b9SAlexander Duyck  *
18471337e6b9SAlexander Duyck  *  This function will interpret the error provided by err, and based on
18481337e6b9SAlexander Duyck  *  that it may set the error bit in the local message header
18491337e6b9SAlexander Duyck  **/
fm10k_sm_mbx_create_error_msg(struct fm10k_mbx_info * mbx,s32 err)18501337e6b9SAlexander Duyck static void fm10k_sm_mbx_create_error_msg(struct fm10k_mbx_info *mbx, s32 err)
18511337e6b9SAlexander Duyck {
18521337e6b9SAlexander Duyck 	/* only generate an error message for these types */
18531337e6b9SAlexander Duyck 	switch (err) {
18541337e6b9SAlexander Duyck 	case FM10K_MBX_ERR_TAIL:
18551337e6b9SAlexander Duyck 	case FM10K_MBX_ERR_HEAD:
18561337e6b9SAlexander Duyck 	case FM10K_MBX_ERR_SRC:
18571337e6b9SAlexander Duyck 	case FM10K_MBX_ERR_SIZE:
18581337e6b9SAlexander Duyck 	case FM10K_MBX_ERR_RSVD0:
18591337e6b9SAlexander Duyck 		break;
18601337e6b9SAlexander Duyck 	default:
18611337e6b9SAlexander Duyck 		return;
18621337e6b9SAlexander Duyck 	}
18631337e6b9SAlexander Duyck 
18641337e6b9SAlexander Duyck 	/* process it as though we received an error, and send error reply */
18651337e6b9SAlexander Duyck 	fm10k_sm_mbx_process_error(mbx);
18661337e6b9SAlexander Duyck 	fm10k_sm_mbx_create_connect_hdr(mbx, 1);
18671337e6b9SAlexander Duyck }
18681337e6b9SAlexander Duyck 
18691337e6b9SAlexander Duyck /**
18701337e6b9SAlexander Duyck  *  fm10k_sm_mbx_receive - Take message from Rx mailbox FIFO and put it in Rx
18711337e6b9SAlexander Duyck  *  @hw: pointer to hardware structure
18721337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
1873f632fed3SBruce Allan  *  @tail: tail index of message
18741337e6b9SAlexander Duyck  *
18751337e6b9SAlexander Duyck  *  This function will dequeue one message from the Rx switch manager mailbox
18761337e6b9SAlexander Duyck  *  FIFO and place it in the Rx mailbox FIFO for processing by software.
18771337e6b9SAlexander Duyck  **/
fm10k_sm_mbx_receive(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx,u16 tail)18781337e6b9SAlexander Duyck static s32 fm10k_sm_mbx_receive(struct fm10k_hw *hw,
18791337e6b9SAlexander Duyck 				struct fm10k_mbx_info *mbx,
18801337e6b9SAlexander Duyck 				u16 tail)
18811337e6b9SAlexander Duyck {
18821337e6b9SAlexander Duyck 	/* reduce length by 1 to convert to a mask */
18831337e6b9SAlexander Duyck 	u16 mbmem_len = mbx->mbmem_len - 1;
18841337e6b9SAlexander Duyck 	s32 err;
18851337e6b9SAlexander Duyck 
18861337e6b9SAlexander Duyck 	/* push tail in front of head */
18871337e6b9SAlexander Duyck 	if (tail < mbx->head)
18881337e6b9SAlexander Duyck 		tail += mbmem_len;
18891337e6b9SAlexander Duyck 
18901337e6b9SAlexander Duyck 	/* copy data to the Rx FIFO */
18911337e6b9SAlexander Duyck 	err = fm10k_mbx_push_tail(hw, mbx, tail);
18921337e6b9SAlexander Duyck 	if (err < 0)
18931337e6b9SAlexander Duyck 		return err;
18941337e6b9SAlexander Duyck 
18951337e6b9SAlexander Duyck 	/* process messages if we have received any */
18961337e6b9SAlexander Duyck 	fm10k_mbx_dequeue_rx(hw, mbx);
18971337e6b9SAlexander Duyck 
18981337e6b9SAlexander Duyck 	/* guarantee head aligns with the end of the last message */
18991337e6b9SAlexander Duyck 	mbx->head = fm10k_mbx_head_sub(mbx, mbx->pushed);
19001337e6b9SAlexander Duyck 	mbx->pushed = 0;
19011337e6b9SAlexander Duyck 
19021337e6b9SAlexander Duyck 	/* clear any extra bits left over since index adds 1 extra bit */
19031337e6b9SAlexander Duyck 	if (mbx->head > mbmem_len)
19041337e6b9SAlexander Duyck 		mbx->head -= mbmem_len;
19051337e6b9SAlexander Duyck 
19061337e6b9SAlexander Duyck 	return err;
19071337e6b9SAlexander Duyck }
19081337e6b9SAlexander Duyck 
19091337e6b9SAlexander Duyck /**
19101337e6b9SAlexander Duyck  *  fm10k_sm_mbx_transmit - Take message from Tx and put it in Tx mailbox FIFO
19111337e6b9SAlexander Duyck  *  @hw: pointer to hardware structure
19121337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
1913f632fed3SBruce Allan  *  @head: head index of message
19141337e6b9SAlexander Duyck  *
19151337e6b9SAlexander Duyck  *  This function will dequeue one message from the Tx mailbox FIFO and place
19161337e6b9SAlexander Duyck  *  it in the Tx switch manager mailbox FIFO for processing by hardware.
19171337e6b9SAlexander Duyck  **/
fm10k_sm_mbx_transmit(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx,u16 head)19181337e6b9SAlexander Duyck static void fm10k_sm_mbx_transmit(struct fm10k_hw *hw,
19191337e6b9SAlexander Duyck 				  struct fm10k_mbx_info *mbx, u16 head)
19201337e6b9SAlexander Duyck {
19211337e6b9SAlexander Duyck 	struct fm10k_mbx_fifo *fifo = &mbx->tx;
19221337e6b9SAlexander Duyck 	/* reduce length by 1 to convert to a mask */
19231337e6b9SAlexander Duyck 	u16 mbmem_len = mbx->mbmem_len - 1;
19241337e6b9SAlexander Duyck 	u16 tail_len, len = 0;
19251337e6b9SAlexander Duyck 
19261337e6b9SAlexander Duyck 	/* push head behind tail */
19271337e6b9SAlexander Duyck 	if (mbx->tail < head)
19281337e6b9SAlexander Duyck 		head += mbmem_len;
19291337e6b9SAlexander Duyck 
19301337e6b9SAlexander Duyck 	fm10k_mbx_pull_head(hw, mbx, head);
19311337e6b9SAlexander Duyck 
19321337e6b9SAlexander Duyck 	/* determine msg aligned offset for end of buffer */
19331337e6b9SAlexander Duyck 	do {
193471974d7eSJacob Keller 		u32 *msg;
193571974d7eSJacob Keller 
19361337e6b9SAlexander Duyck 		msg = fifo->buffer + fm10k_fifo_head_offset(fifo, len);
19371337e6b9SAlexander Duyck 		tail_len = len;
19381337e6b9SAlexander Duyck 		len += FM10K_TLV_DWORD_LEN(*msg);
19391337e6b9SAlexander Duyck 	} while ((len <= mbx->tail_len) && (len < mbmem_len));
19401337e6b9SAlexander Duyck 
19411337e6b9SAlexander Duyck 	/* guarantee we stop on a message boundary */
19421337e6b9SAlexander Duyck 	if (mbx->tail_len > tail_len) {
19431337e6b9SAlexander Duyck 		mbx->tail = fm10k_mbx_tail_sub(mbx, mbx->tail_len - tail_len);
19441337e6b9SAlexander Duyck 		mbx->tail_len = tail_len;
19451337e6b9SAlexander Duyck 	}
19461337e6b9SAlexander Duyck 
19471337e6b9SAlexander Duyck 	/* clear any extra bits left over since index adds 1 extra bit */
19481337e6b9SAlexander Duyck 	if (mbx->tail > mbmem_len)
19491337e6b9SAlexander Duyck 		mbx->tail -= mbmem_len;
19501337e6b9SAlexander Duyck }
19511337e6b9SAlexander Duyck 
19521337e6b9SAlexander Duyck /**
19531337e6b9SAlexander Duyck  *  fm10k_sm_mbx_create_reply - Generate reply based on state and remote head
1954f632fed3SBruce Allan  *  @hw: pointer to hardware structure
19551337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
19561337e6b9SAlexander Duyck  *  @head: acknowledgement number
19571337e6b9SAlexander Duyck  *
19581337e6b9SAlexander Duyck  *  This function will generate an outgoing message based on the current
1959f632fed3SBruce Allan  *  mailbox state and the remote FIFO head.  It will return the length
19601337e6b9SAlexander Duyck  *  of the outgoing message excluding header on success, and a negative value
19611337e6b9SAlexander Duyck  *  on error.
19621337e6b9SAlexander Duyck  **/
fm10k_sm_mbx_create_reply(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx,u16 head)19631337e6b9SAlexander Duyck static void fm10k_sm_mbx_create_reply(struct fm10k_hw *hw,
19641337e6b9SAlexander Duyck 				      struct fm10k_mbx_info *mbx, u16 head)
19651337e6b9SAlexander Duyck {
19661337e6b9SAlexander Duyck 	switch (mbx->state) {
19671337e6b9SAlexander Duyck 	case FM10K_STATE_OPEN:
19681337e6b9SAlexander Duyck 	case FM10K_STATE_DISCONNECT:
19691337e6b9SAlexander Duyck 		/* flush out Tx data */
19701337e6b9SAlexander Duyck 		fm10k_sm_mbx_transmit(hw, mbx, head);
19711337e6b9SAlexander Duyck 
19721337e6b9SAlexander Duyck 		/* generate new header based on data */
19731337e6b9SAlexander Duyck 		if (mbx->tail_len || (mbx->state == FM10K_STATE_OPEN)) {
19741337e6b9SAlexander Duyck 			fm10k_sm_mbx_create_data_hdr(mbx);
19751337e6b9SAlexander Duyck 		} else {
19761337e6b9SAlexander Duyck 			mbx->remote = 0;
19771337e6b9SAlexander Duyck 			fm10k_sm_mbx_create_connect_hdr(mbx, 0);
19781337e6b9SAlexander Duyck 		}
19791337e6b9SAlexander Duyck 		break;
19801337e6b9SAlexander Duyck 	case FM10K_STATE_CONNECT:
19811337e6b9SAlexander Duyck 	case FM10K_STATE_CLOSED:
19821337e6b9SAlexander Duyck 		fm10k_sm_mbx_create_connect_hdr(mbx, 0);
19831337e6b9SAlexander Duyck 		break;
19841337e6b9SAlexander Duyck 	default:
19851337e6b9SAlexander Duyck 		break;
19861337e6b9SAlexander Duyck 	}
19871337e6b9SAlexander Duyck }
19881337e6b9SAlexander Duyck 
19891337e6b9SAlexander Duyck /**
19901337e6b9SAlexander Duyck  *  fm10k_sm_mbx_process_reset - Process header with version == 0 (RESET)
19911337e6b9SAlexander Duyck  *  @hw: pointer to hardware structure
19921337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
19931337e6b9SAlexander Duyck  *
19941337e6b9SAlexander Duyck  *  This function is meant to respond to a request where the version data
19951337e6b9SAlexander Duyck  *  is set to 0.  As such we will either terminate the connection or go
19961337e6b9SAlexander Duyck  *  into the connect state in order to re-establish the connection.  This
19971337e6b9SAlexander Duyck  *  function can also be used to respond to an error as the connection
19981337e6b9SAlexander Duyck  *  resetting would also be a means of dealing with errors.
19991337e6b9SAlexander Duyck  **/
fm10k_sm_mbx_process_reset(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx)20002f3fc1e6SNgai-Mint Kwan static s32 fm10k_sm_mbx_process_reset(struct fm10k_hw *hw,
20011337e6b9SAlexander Duyck 				      struct fm10k_mbx_info *mbx)
20021337e6b9SAlexander Duyck {
20032f3fc1e6SNgai-Mint Kwan 	s32 err = 0;
20041337e6b9SAlexander Duyck 	const enum fm10k_mbx_state state = mbx->state;
20051337e6b9SAlexander Duyck 
20061337e6b9SAlexander Duyck 	switch (state) {
20071337e6b9SAlexander Duyck 	case FM10K_STATE_DISCONNECT:
20081337e6b9SAlexander Duyck 		/* drop remote connections and disconnect */
20091337e6b9SAlexander Duyck 		mbx->state = FM10K_STATE_CLOSED;
20101337e6b9SAlexander Duyck 		mbx->remote = 0;
20111337e6b9SAlexander Duyck 		mbx->local = 0;
20121337e6b9SAlexander Duyck 		break;
20131337e6b9SAlexander Duyck 	case FM10K_STATE_OPEN:
20141337e6b9SAlexander Duyck 		/* flush any incomplete work */
20151337e6b9SAlexander Duyck 		fm10k_sm_mbx_connect_reset(mbx);
20162f3fc1e6SNgai-Mint Kwan 		err = FM10K_ERR_RESET_REQUESTED;
20171337e6b9SAlexander Duyck 		break;
20181337e6b9SAlexander Duyck 	case FM10K_STATE_CONNECT:
20191337e6b9SAlexander Duyck 		/* Update remote value to match local value */
20201337e6b9SAlexander Duyck 		mbx->remote = mbx->local;
2021f83a0d0aSGustavo A. R. Silva 		break;
20221337e6b9SAlexander Duyck 	default:
20231337e6b9SAlexander Duyck 		break;
20241337e6b9SAlexander Duyck 	}
20251337e6b9SAlexander Duyck 
20261337e6b9SAlexander Duyck 	fm10k_sm_mbx_create_reply(hw, mbx, mbx->tail);
20272f3fc1e6SNgai-Mint Kwan 
20282f3fc1e6SNgai-Mint Kwan 	return err;
20291337e6b9SAlexander Duyck }
20301337e6b9SAlexander Duyck 
20311337e6b9SAlexander Duyck /**
20321337e6b9SAlexander Duyck  *  fm10k_sm_mbx_process_version_1 - Process header with version == 1
20331337e6b9SAlexander Duyck  *  @hw: pointer to hardware structure
20341337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
20351337e6b9SAlexander Duyck  *
20361337e6b9SAlexander Duyck  *  This function is meant to process messages received when the remote
20371337e6b9SAlexander Duyck  *  mailbox is active.
20381337e6b9SAlexander Duyck  **/
fm10k_sm_mbx_process_version_1(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx)20391337e6b9SAlexander Duyck static s32 fm10k_sm_mbx_process_version_1(struct fm10k_hw *hw,
20401337e6b9SAlexander Duyck 					  struct fm10k_mbx_info *mbx)
20411337e6b9SAlexander Duyck {
20421337e6b9SAlexander Duyck 	const u32 *hdr = &mbx->mbx_hdr;
20431337e6b9SAlexander Duyck 	u16 head, tail;
20441337e6b9SAlexander Duyck 	s32 len;
20451337e6b9SAlexander Duyck 
20461337e6b9SAlexander Duyck 	/* pull all fields needed for verification */
20471337e6b9SAlexander Duyck 	tail = FM10K_MSG_HDR_FIELD_GET(*hdr, SM_TAIL);
20481337e6b9SAlexander Duyck 	head = FM10K_MSG_HDR_FIELD_GET(*hdr, SM_HEAD);
20491337e6b9SAlexander Duyck 
20501337e6b9SAlexander Duyck 	/* if we are in connect and wanting version 1 then start up and go */
20511337e6b9SAlexander Duyck 	if (mbx->state == FM10K_STATE_CONNECT) {
20521337e6b9SAlexander Duyck 		if (!mbx->remote)
20531337e6b9SAlexander Duyck 			goto send_reply;
20541337e6b9SAlexander Duyck 		if (mbx->remote != 1)
20551337e6b9SAlexander Duyck 			return FM10K_MBX_ERR_SRC;
20561337e6b9SAlexander Duyck 
20571337e6b9SAlexander Duyck 		mbx->state = FM10K_STATE_OPEN;
20581337e6b9SAlexander Duyck 	}
20591337e6b9SAlexander Duyck 
20601337e6b9SAlexander Duyck 	do {
20611337e6b9SAlexander Duyck 		/* abort on message size errors */
20621337e6b9SAlexander Duyck 		len = fm10k_sm_mbx_receive(hw, mbx, tail);
20631337e6b9SAlexander Duyck 		if (len < 0)
20641337e6b9SAlexander Duyck 			return len;
20651337e6b9SAlexander Duyck 
20661337e6b9SAlexander Duyck 		/* continue until we have flushed the Rx FIFO */
20671337e6b9SAlexander Duyck 	} while (len);
20681337e6b9SAlexander Duyck 
20691337e6b9SAlexander Duyck send_reply:
20701337e6b9SAlexander Duyck 	fm10k_sm_mbx_create_reply(hw, mbx, head);
20711337e6b9SAlexander Duyck 
20721337e6b9SAlexander Duyck 	return 0;
20731337e6b9SAlexander Duyck }
20741337e6b9SAlexander Duyck 
20751337e6b9SAlexander Duyck /**
2076f632fed3SBruce Allan  *  fm10k_sm_mbx_process - Process switch manager mailbox interrupt
20771337e6b9SAlexander Duyck  *  @hw: pointer to hardware structure
20781337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
20791337e6b9SAlexander Duyck  *
20801337e6b9SAlexander Duyck  *  This function will process incoming mailbox events and generate mailbox
20811337e6b9SAlexander Duyck  *  replies.  It will return a value indicating the number of DWORDs
20821337e6b9SAlexander Duyck  *  transmitted excluding header on success or a negative value on error.
20831337e6b9SAlexander Duyck  **/
fm10k_sm_mbx_process(struct fm10k_hw * hw,struct fm10k_mbx_info * mbx)20841337e6b9SAlexander Duyck static s32 fm10k_sm_mbx_process(struct fm10k_hw *hw,
20851337e6b9SAlexander Duyck 				struct fm10k_mbx_info *mbx)
20861337e6b9SAlexander Duyck {
20871337e6b9SAlexander Duyck 	s32 err;
20881337e6b9SAlexander Duyck 
20891337e6b9SAlexander Duyck 	/* we do not read mailbox if closed */
20901337e6b9SAlexander Duyck 	if (mbx->state == FM10K_STATE_CLOSED)
20911337e6b9SAlexander Duyck 		return 0;
20921337e6b9SAlexander Duyck 
20931337e6b9SAlexander Duyck 	/* retrieve data from switch manager */
20941337e6b9SAlexander Duyck 	err = fm10k_mbx_read(hw, mbx);
20951337e6b9SAlexander Duyck 	if (err)
20961337e6b9SAlexander Duyck 		return err;
20971337e6b9SAlexander Duyck 
20981337e6b9SAlexander Duyck 	err = fm10k_sm_mbx_validate_fifo_hdr(mbx);
20991337e6b9SAlexander Duyck 	if (err < 0)
21001337e6b9SAlexander Duyck 		goto fifo_err;
21011337e6b9SAlexander Duyck 
21021337e6b9SAlexander Duyck 	if (FM10K_MSG_HDR_FIELD_GET(mbx->mbx_hdr, SM_ERR)) {
21031337e6b9SAlexander Duyck 		fm10k_sm_mbx_process_error(mbx);
21041337e6b9SAlexander Duyck 		goto fifo_err;
21051337e6b9SAlexander Duyck 	}
21061337e6b9SAlexander Duyck 
21071337e6b9SAlexander Duyck 	switch (FM10K_MSG_HDR_FIELD_GET(mbx->mbx_hdr, SM_VER)) {
21081337e6b9SAlexander Duyck 	case 0:
21092f3fc1e6SNgai-Mint Kwan 		err = fm10k_sm_mbx_process_reset(hw, mbx);
21101337e6b9SAlexander Duyck 		break;
21111337e6b9SAlexander Duyck 	case FM10K_SM_MBX_VERSION:
21121337e6b9SAlexander Duyck 		err = fm10k_sm_mbx_process_version_1(hw, mbx);
21131337e6b9SAlexander Duyck 		break;
21141337e6b9SAlexander Duyck 	}
21151337e6b9SAlexander Duyck 
21161337e6b9SAlexander Duyck fifo_err:
21171337e6b9SAlexander Duyck 	if (err < 0)
21181337e6b9SAlexander Duyck 		fm10k_sm_mbx_create_error_msg(mbx, err);
21191337e6b9SAlexander Duyck 
21201337e6b9SAlexander Duyck 	/* report data to switch manager */
21211337e6b9SAlexander Duyck 	fm10k_mbx_write(hw, mbx);
21221337e6b9SAlexander Duyck 
21231337e6b9SAlexander Duyck 	return err;
21241337e6b9SAlexander Duyck }
21251337e6b9SAlexander Duyck 
21261337e6b9SAlexander Duyck /**
21271337e6b9SAlexander Duyck  *  fm10k_sm_mbx_init - Initialize mailbox memory for PF/SM mailbox
21281337e6b9SAlexander Duyck  *  @hw: pointer to hardware structure
21291337e6b9SAlexander Duyck  *  @mbx: pointer to mailbox
21301337e6b9SAlexander Duyck  *  @msg_data: handlers for mailbox events
21311337e6b9SAlexander Duyck  *
2132f632fed3SBruce Allan  *  This function initializes the PF/SM mailbox for use.  It will split the
2133f632fed3SBruce Allan  *  buffer provided and use that to populate both the Tx and Rx FIFO by
2134f632fed3SBruce Allan  *  evenly splitting it.  In order to allow for easy masking of head/tail
2135f632fed3SBruce Allan  *  the value reported in size must be a power of 2 and is reported in
2136f632fed3SBruce Allan  *  DWORDs, not bytes.  Any invalid values will cause the mailbox to return
2137f632fed3SBruce Allan  *  error.
21381337e6b9SAlexander Duyck  **/
fm10k_sm_mbx_init(struct fm10k_hw __always_unused * hw,struct fm10k_mbx_info * mbx,const struct fm10k_msg_data * msg_data)2139d5c2f395SJacob Keller s32 fm10k_sm_mbx_init(struct fm10k_hw __always_unused *hw,
2140d5c2f395SJacob Keller 		      struct fm10k_mbx_info *mbx,
21411337e6b9SAlexander Duyck 		      const struct fm10k_msg_data *msg_data)
21421337e6b9SAlexander Duyck {
21431337e6b9SAlexander Duyck 	mbx->mbx_reg = FM10K_GMBX;
21441337e6b9SAlexander Duyck 	mbx->mbmem_reg = FM10K_MBMEM_PF(0);
2145a4fcad65SBruce Allan 
21461337e6b9SAlexander Duyck 	/* start out in closed state */
21471337e6b9SAlexander Duyck 	mbx->state = FM10K_STATE_CLOSED;
21481337e6b9SAlexander Duyck 
21491337e6b9SAlexander Duyck 	/* validate layout of handlers before assigning them */
21501337e6b9SAlexander Duyck 	if (fm10k_mbx_validate_handlers(msg_data))
21511337e6b9SAlexander Duyck 		return FM10K_ERR_PARAM;
21521337e6b9SAlexander Duyck 
21531337e6b9SAlexander Duyck 	/* initialize the message handlers */
21541337e6b9SAlexander Duyck 	mbx->msg_data = msg_data;
21551337e6b9SAlexander Duyck 
21561337e6b9SAlexander Duyck 	/* start mailbox as timed out and let the reset_hw call
21571337e6b9SAlexander Duyck 	 * set the timeout value to begin communications
21581337e6b9SAlexander Duyck 	 */
21591337e6b9SAlexander Duyck 	mbx->timeout = 0;
21601337e6b9SAlexander Duyck 	mbx->udelay = FM10K_MBX_INIT_DELAY;
21611337e6b9SAlexander Duyck 
21621337e6b9SAlexander Duyck 	/* Split buffer for use by Tx/Rx FIFOs */
21631337e6b9SAlexander Duyck 	mbx->max_size = FM10K_MBX_MSG_MAX_SIZE;
21641337e6b9SAlexander Duyck 	mbx->mbmem_len = FM10K_MBMEM_PF_XOR;
21651337e6b9SAlexander Duyck 
21661337e6b9SAlexander Duyck 	/* initialize the FIFOs, sizes are in 4 byte increments */
21671337e6b9SAlexander Duyck 	fm10k_fifo_init(&mbx->tx, mbx->buffer, FM10K_MBX_TX_BUFFER_SIZE);
21681337e6b9SAlexander Duyck 	fm10k_fifo_init(&mbx->rx, &mbx->buffer[FM10K_MBX_TX_BUFFER_SIZE],
21691337e6b9SAlexander Duyck 			FM10K_MBX_RX_BUFFER_SIZE);
21701337e6b9SAlexander Duyck 
21711337e6b9SAlexander Duyck 	/* initialize function pointers */
21721337e6b9SAlexander Duyck 	mbx->ops.connect = fm10k_sm_mbx_connect;
21731337e6b9SAlexander Duyck 	mbx->ops.disconnect = fm10k_sm_mbx_disconnect;
21741337e6b9SAlexander Duyck 	mbx->ops.rx_ready = fm10k_mbx_rx_ready;
21751337e6b9SAlexander Duyck 	mbx->ops.tx_ready = fm10k_mbx_tx_ready;
21761337e6b9SAlexander Duyck 	mbx->ops.tx_complete = fm10k_mbx_tx_complete;
21771337e6b9SAlexander Duyck 	mbx->ops.enqueue_tx = fm10k_mbx_enqueue_tx;
21781337e6b9SAlexander Duyck 	mbx->ops.process = fm10k_sm_mbx_process;
21791337e6b9SAlexander Duyck 	mbx->ops.register_handlers = fm10k_mbx_register_handlers;
21801337e6b9SAlexander Duyck 
21811337e6b9SAlexander Duyck 	return 0;
21821337e6b9SAlexander Duyck }
2183