5566a3e358
Replace the BSD license header with the SPDX tag for files with only an Intel copyright on them. Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
272 lines
6.9 KiB
C
272 lines
6.9 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(c) 2010-2014 Intel Corporation
|
|
*/
|
|
|
|
#ifndef _IXGBE_BYPASS_API_H_
|
|
#define _IXGBE_BYPASS_API_H_
|
|
|
|
#ifdef RTE_LIBRTE_IXGBE_BYPASS
|
|
|
|
#include "ixgbe_bypass_defines.h"
|
|
/**
|
|
* ixgbe_bypass_rw_generic - Bit bang data into by_pass FW
|
|
*
|
|
* @hw: pointer to hardware structure
|
|
* @cmd: Command we send to the FW
|
|
* @status: The reply from the FW
|
|
*
|
|
* Bit-bangs the cmd to the by_pass FW status points to what is returned.
|
|
**/
|
|
#define IXGBE_BYPASS_BB_WAIT 1
|
|
static s32 ixgbe_bypass_rw_generic(struct ixgbe_hw *hw, u32 cmd, u32 *status)
|
|
{
|
|
int i;
|
|
u32 sck, sdi, sdo, dir_sck, dir_sdi, dir_sdo;
|
|
u32 esdp;
|
|
|
|
if (!status)
|
|
return IXGBE_ERR_PARAM;
|
|
|
|
*status = 0;
|
|
|
|
/* SDP vary by MAC type */
|
|
switch (hw->mac.type) {
|
|
case ixgbe_mac_82599EB:
|
|
sck = IXGBE_ESDP_SDP7;
|
|
sdi = IXGBE_ESDP_SDP0;
|
|
sdo = IXGBE_ESDP_SDP6;
|
|
dir_sck = IXGBE_ESDP_SDP7_DIR;
|
|
dir_sdi = IXGBE_ESDP_SDP0_DIR;
|
|
dir_sdo = IXGBE_ESDP_SDP6_DIR;
|
|
break;
|
|
case ixgbe_mac_X540:
|
|
sck = IXGBE_ESDP_SDP2;
|
|
sdi = IXGBE_ESDP_SDP0;
|
|
sdo = IXGBE_ESDP_SDP1;
|
|
dir_sck = IXGBE_ESDP_SDP2_DIR;
|
|
dir_sdi = IXGBE_ESDP_SDP0_DIR;
|
|
dir_sdo = IXGBE_ESDP_SDP1_DIR;
|
|
break;
|
|
case ixgbe_mac_X550:
|
|
case ixgbe_mac_X550EM_x:
|
|
case ixgbe_mac_X550EM_a:
|
|
sck = IXGBE_ESDP_SDP2;
|
|
sdi = IXGBE_ESDP_SDP0;
|
|
sdo = IXGBE_ESDP_SDP1;
|
|
dir_sck = IXGBE_ESDP_SDP2_DIR;
|
|
dir_sdi = IXGBE_ESDP_SDP0_DIR;
|
|
dir_sdo = IXGBE_ESDP_SDP1_DIR;
|
|
break;
|
|
default:
|
|
return IXGBE_ERR_DEVICE_NOT_SUPPORTED;
|
|
}
|
|
|
|
/* Set SDP pins direction */
|
|
esdp = IXGBE_READ_REG(hw, IXGBE_ESDP);
|
|
esdp |= dir_sck; /* SCK as output */
|
|
esdp |= dir_sdi; /* SDI as output */
|
|
esdp &= ~dir_sdo; /* SDO as input */
|
|
esdp |= sck;
|
|
esdp |= sdi;
|
|
IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
|
|
IXGBE_WRITE_FLUSH(hw);
|
|
// TODO:
|
|
msleep(IXGBE_BYPASS_BB_WAIT);
|
|
|
|
/* Generate start condition */
|
|
esdp &= ~sdi;
|
|
IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
|
|
IXGBE_WRITE_FLUSH(hw);
|
|
msleep(IXGBE_BYPASS_BB_WAIT);
|
|
|
|
esdp &= ~sck;
|
|
IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
|
|
IXGBE_WRITE_FLUSH(hw);
|
|
msleep(IXGBE_BYPASS_BB_WAIT);
|
|
|
|
/* Clock out the new control word and clock in the status */
|
|
for (i = 0; i < 32; i++) {
|
|
if ((cmd >> (31 - i)) & 0x01) {
|
|
esdp |= sdi;
|
|
IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
|
|
} else {
|
|
esdp &= ~sdi;
|
|
IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
|
|
}
|
|
IXGBE_WRITE_FLUSH(hw);
|
|
msleep(IXGBE_BYPASS_BB_WAIT);
|
|
|
|
esdp |= sck;
|
|
IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
|
|
IXGBE_WRITE_FLUSH(hw);
|
|
msleep(IXGBE_BYPASS_BB_WAIT);
|
|
|
|
esdp &= ~sck;
|
|
IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
|
|
IXGBE_WRITE_FLUSH(hw);
|
|
msleep(IXGBE_BYPASS_BB_WAIT);
|
|
|
|
esdp = IXGBE_READ_REG(hw, IXGBE_ESDP);
|
|
if (esdp & sdo)
|
|
*status = (*status << 1) | 0x01;
|
|
else
|
|
*status = (*status << 1) | 0x00;
|
|
msleep(IXGBE_BYPASS_BB_WAIT);
|
|
}
|
|
|
|
/* stop condition */
|
|
esdp |= sck;
|
|
esdp &= ~sdi;
|
|
IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
|
|
IXGBE_WRITE_FLUSH(hw);
|
|
msleep(IXGBE_BYPASS_BB_WAIT);
|
|
|
|
esdp |= sdi;
|
|
IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
|
|
IXGBE_WRITE_FLUSH(hw);
|
|
|
|
/* set the page bits to match the cmd that the status it belongs to */
|
|
*status = (*status & 0x3fffffff) | (cmd & 0xc0000000);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ixgbe_bypass_valid_rd_generic - Verify valid return from bit-bang.
|
|
*
|
|
* If we send a write we can't be sure it took until we can read back
|
|
* that same register. It can be a problem as some of the feilds may
|
|
* for valid reasons change between the time wrote the register and
|
|
* we read it again to verify. So this function check everything we
|
|
* can check and then assumes it worked.
|
|
*
|
|
* @u32 in_reg - The register cmd for the bit-bang read.
|
|
* @u32 out_reg - The register returned from a bit-bang read.
|
|
**/
|
|
static bool ixgbe_bypass_valid_rd_generic(u32 in_reg, u32 out_reg)
|
|
{
|
|
u32 mask;
|
|
|
|
/* Page must match for all control pages */
|
|
if ((in_reg & BYPASS_PAGE_M) != (out_reg & BYPASS_PAGE_M))
|
|
return false;
|
|
|
|
switch (in_reg & BYPASS_PAGE_M) {
|
|
case BYPASS_PAGE_CTL0:
|
|
/* All the following can't change since the last write
|
|
* - All the event actions
|
|
* - The timeout value
|
|
*/
|
|
mask = BYPASS_AUX_ON_M | BYPASS_MAIN_ON_M |
|
|
BYPASS_MAIN_OFF_M | BYPASS_AUX_OFF_M |
|
|
BYPASS_WDTIMEOUT_M |
|
|
BYPASS_WDT_VALUE_M;
|
|
if ((out_reg & mask) != (in_reg & mask))
|
|
return false;
|
|
|
|
/* 0x0 is never a valid value for bypass status */
|
|
if (!(out_reg & BYPASS_STATUS_OFF_M))
|
|
return false;
|
|
break;
|
|
case BYPASS_PAGE_CTL1:
|
|
/* All the following can't change since the last write
|
|
* - time valid bit
|
|
* - time we last sent
|
|
*/
|
|
mask = BYPASS_CTL1_VALID_M | BYPASS_CTL1_TIME_M;
|
|
if ((out_reg & mask) != (in_reg & mask))
|
|
return false;
|
|
break;
|
|
case BYPASS_PAGE_CTL2:
|
|
/* All we can check in this page is control number
|
|
* which is already done above.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
/* We are as sure as we can be return true */
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* ixgbe_bypass_set_generic - Set a bypass field in the FW CTRL Regiter.
|
|
*
|
|
* @hw: pointer to hardware structure
|
|
* @cmd: The control word we are setting.
|
|
* @event: The event we are setting in the FW. This also happens to
|
|
* be the mask for the event we are setting (handy)
|
|
* @action: The action we set the event to in the FW. This is in a
|
|
* bit field that happens to be what we want to put in
|
|
* the event spot (also handy)
|
|
**/
|
|
static s32 ixgbe_bypass_set_generic(struct ixgbe_hw *hw, u32 ctrl, u32 event,
|
|
u32 action)
|
|
{
|
|
u32 by_ctl = 0;
|
|
u32 cmd, verify;
|
|
u32 count = 0;
|
|
|
|
/* Get current values */
|
|
cmd = ctrl; /* just reading only need control number */
|
|
if (ixgbe_bypass_rw_generic(hw, cmd, &by_ctl))
|
|
return IXGBE_ERR_INVALID_ARGUMENT;
|
|
|
|
/* Set to new action */
|
|
cmd = (by_ctl & ~event) | BYPASS_WE | action;
|
|
if (ixgbe_bypass_rw_generic(hw, cmd, &by_ctl))
|
|
return IXGBE_ERR_INVALID_ARGUMENT;
|
|
|
|
/* Page 0 force a FW eeprom write which is slow so verify */
|
|
if ((cmd & BYPASS_PAGE_M) == BYPASS_PAGE_CTL0) {
|
|
verify = BYPASS_PAGE_CTL0;
|
|
do {
|
|
if (count++ > 5)
|
|
return IXGBE_BYPASS_FW_WRITE_FAILURE;
|
|
|
|
if (ixgbe_bypass_rw_generic(hw, verify, &by_ctl))
|
|
return IXGBE_ERR_INVALID_ARGUMENT;
|
|
} while (!ixgbe_bypass_valid_rd_generic(cmd, by_ctl));
|
|
} else {
|
|
/* We have give the FW time for the write to stick */
|
|
msleep(100);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ixgbe_bypass_rd_eep_generic - Read the bypass FW eeprom address.
|
|
*
|
|
* @hw: pointer to hardware structure
|
|
* @addr: The bypass eeprom address to read.
|
|
* @value: The 8b of data at the address above.
|
|
**/
|
|
static s32 ixgbe_bypass_rd_eep_generic(struct ixgbe_hw *hw, u32 addr, u8 *value)
|
|
{
|
|
u32 cmd;
|
|
u32 status;
|
|
|
|
|
|
/* send the request */
|
|
cmd = BYPASS_PAGE_CTL2 | BYPASS_WE;
|
|
cmd |= (addr << BYPASS_CTL2_OFFSET_SHIFT) & BYPASS_CTL2_OFFSET_M;
|
|
if (ixgbe_bypass_rw_generic(hw, cmd, &status))
|
|
return IXGBE_ERR_INVALID_ARGUMENT;
|
|
|
|
/* We have give the FW time for the write to stick */
|
|
msleep(100);
|
|
|
|
/* now read the results */
|
|
cmd &= ~BYPASS_WE;
|
|
if (ixgbe_bypass_rw_generic(hw, cmd, &status))
|
|
return IXGBE_ERR_INVALID_ARGUMENT;
|
|
|
|
*value = status & BYPASS_CTL2_DATA_M;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* RTE_LIBRTE_IXGBE_BYPASS */
|
|
|
|
#endif /* _IXGBE_BYPASS_API_H_ */
|