1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation 3 */ 4 5 #ifndef _IXGBE_BYPASS_API_H_ 6 #define _IXGBE_BYPASS_API_H_ 7 8 #ifdef RTE_LIBRTE_IXGBE_BYPASS 9 10 #include "ixgbe_bypass_defines.h" 11 /** 12 * ixgbe_bypass_rw_generic - Bit bang data into by_pass FW 13 * 14 * @hw: pointer to hardware structure 15 * @cmd: Command we send to the FW 16 * @status: The reply from the FW 17 * 18 * Bit-bangs the cmd to the by_pass FW status points to what is returned. 19 **/ 20 #define IXGBE_BYPASS_BB_WAIT 1 21 static s32 ixgbe_bypass_rw_generic(struct ixgbe_hw *hw, u32 cmd, u32 *status) 22 { 23 int i; 24 u32 sck, sdi, sdo, dir_sck, dir_sdi, dir_sdo; 25 u32 esdp; 26 27 if (!status) 28 return IXGBE_ERR_PARAM; 29 30 *status = 0; 31 32 /* SDP vary by MAC type */ 33 switch (hw->mac.type) { 34 case ixgbe_mac_82599EB: 35 sck = IXGBE_ESDP_SDP7; 36 sdi = IXGBE_ESDP_SDP0; 37 sdo = IXGBE_ESDP_SDP6; 38 dir_sck = IXGBE_ESDP_SDP7_DIR; 39 dir_sdi = IXGBE_ESDP_SDP0_DIR; 40 dir_sdo = IXGBE_ESDP_SDP6_DIR; 41 break; 42 case ixgbe_mac_X540: 43 sck = IXGBE_ESDP_SDP2; 44 sdi = IXGBE_ESDP_SDP0; 45 sdo = IXGBE_ESDP_SDP1; 46 dir_sck = IXGBE_ESDP_SDP2_DIR; 47 dir_sdi = IXGBE_ESDP_SDP0_DIR; 48 dir_sdo = IXGBE_ESDP_SDP1_DIR; 49 break; 50 case ixgbe_mac_X550: 51 case ixgbe_mac_X550EM_x: 52 case ixgbe_mac_X550EM_a: 53 sck = IXGBE_ESDP_SDP2; 54 sdi = IXGBE_ESDP_SDP0; 55 sdo = IXGBE_ESDP_SDP1; 56 dir_sck = IXGBE_ESDP_SDP2_DIR; 57 dir_sdi = IXGBE_ESDP_SDP0_DIR; 58 dir_sdo = IXGBE_ESDP_SDP1_DIR; 59 break; 60 default: 61 return IXGBE_ERR_DEVICE_NOT_SUPPORTED; 62 } 63 64 /* Set SDP pins direction */ 65 esdp = IXGBE_READ_REG(hw, IXGBE_ESDP); 66 esdp |= dir_sck; /* SCK as output */ 67 esdp |= dir_sdi; /* SDI as output */ 68 esdp &= ~dir_sdo; /* SDO as input */ 69 esdp |= sck; 70 esdp |= sdi; 71 IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp); 72 IXGBE_WRITE_FLUSH(hw); 73 // TODO: 74 msleep(IXGBE_BYPASS_BB_WAIT); 75 76 /* Generate start condition */ 77 esdp &= ~sdi; 78 IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp); 79 IXGBE_WRITE_FLUSH(hw); 80 msleep(IXGBE_BYPASS_BB_WAIT); 81 82 esdp &= ~sck; 83 IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp); 84 IXGBE_WRITE_FLUSH(hw); 85 msleep(IXGBE_BYPASS_BB_WAIT); 86 87 /* Clock out the new control word and clock in the status */ 88 for (i = 0; i < 32; i++) { 89 if ((cmd >> (31 - i)) & 0x01) { 90 esdp |= sdi; 91 IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp); 92 } else { 93 esdp &= ~sdi; 94 IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp); 95 } 96 IXGBE_WRITE_FLUSH(hw); 97 msleep(IXGBE_BYPASS_BB_WAIT); 98 99 esdp |= sck; 100 IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp); 101 IXGBE_WRITE_FLUSH(hw); 102 msleep(IXGBE_BYPASS_BB_WAIT); 103 104 esdp &= ~sck; 105 IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp); 106 IXGBE_WRITE_FLUSH(hw); 107 msleep(IXGBE_BYPASS_BB_WAIT); 108 109 esdp = IXGBE_READ_REG(hw, IXGBE_ESDP); 110 if (esdp & sdo) 111 *status = (*status << 1) | 0x01; 112 else 113 *status = (*status << 1) | 0x00; 114 msleep(IXGBE_BYPASS_BB_WAIT); 115 } 116 117 /* stop condition */ 118 esdp |= sck; 119 esdp &= ~sdi; 120 IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp); 121 IXGBE_WRITE_FLUSH(hw); 122 msleep(IXGBE_BYPASS_BB_WAIT); 123 124 esdp |= sdi; 125 IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp); 126 IXGBE_WRITE_FLUSH(hw); 127 128 /* set the page bits to match the cmd that the status it belongs to */ 129 *status = (*status & 0x3fffffff) | (cmd & 0xc0000000); 130 131 return 0; 132 } 133 134 /** 135 * ixgbe_bypass_valid_rd_generic - Verify valid return from bit-bang. 136 * 137 * If we send a write we can't be sure it took until we can read back 138 * that same register. It can be a problem as some of the fields may 139 * for valid reasons change between the time wrote the register and 140 * we read it again to verify. So this function check everything we 141 * can check and then assumes it worked. 142 * 143 * @u32 in_reg - The register cmd for the bit-bang read. 144 * @u32 out_reg - The register returned from a bit-bang read. 145 **/ 146 static bool ixgbe_bypass_valid_rd_generic(u32 in_reg, u32 out_reg) 147 { 148 u32 mask; 149 150 /* Page must match for all control pages */ 151 if ((in_reg & BYPASS_PAGE_M) != (out_reg & BYPASS_PAGE_M)) 152 return false; 153 154 switch (in_reg & BYPASS_PAGE_M) { 155 case BYPASS_PAGE_CTL0: 156 /* All the following can't change since the last write 157 * - All the event actions 158 * - The timeout value 159 */ 160 mask = BYPASS_AUX_ON_M | BYPASS_MAIN_ON_M | 161 BYPASS_MAIN_OFF_M | BYPASS_AUX_OFF_M | 162 BYPASS_WDTIMEOUT_M | 163 BYPASS_WDT_VALUE_M; 164 if ((out_reg & mask) != (in_reg & mask)) 165 return false; 166 167 /* 0x0 is never a valid value for bypass status */ 168 if (!(out_reg & BYPASS_STATUS_OFF_M)) 169 return false; 170 break; 171 case BYPASS_PAGE_CTL1: 172 /* All the following can't change since the last write 173 * - time valid bit 174 * - time we last sent 175 */ 176 mask = BYPASS_CTL1_VALID_M | BYPASS_CTL1_TIME_M; 177 if ((out_reg & mask) != (in_reg & mask)) 178 return false; 179 break; 180 case BYPASS_PAGE_CTL2: 181 /* All we can check in this page is control number 182 * which is already done above. 183 */ 184 break; 185 } 186 187 /* We are as sure as we can be return true */ 188 return true; 189 } 190 191 /** 192 * ixgbe_bypass_set_generic - Set a bypass field in the FW CTRL Register. 193 * 194 * @hw: pointer to hardware structure 195 * @cmd: The control word we are setting. 196 * @event: The event we are setting in the FW. This also happens to 197 * be the mask for the event we are setting (handy) 198 * @action: The action we set the event to in the FW. This is in a 199 * bit field that happens to be what we want to put in 200 * the event spot (also handy) 201 **/ 202 static s32 ixgbe_bypass_set_generic(struct ixgbe_hw *hw, u32 ctrl, u32 event, 203 u32 action) 204 { 205 u32 by_ctl = 0; 206 u32 cmd, verify; 207 u32 count = 0; 208 209 /* Get current values */ 210 cmd = ctrl; /* just reading only need control number */ 211 if (ixgbe_bypass_rw_generic(hw, cmd, &by_ctl)) 212 return IXGBE_ERR_INVALID_ARGUMENT; 213 214 /* Set to new action */ 215 cmd = (by_ctl & ~event) | BYPASS_WE | action; 216 if (ixgbe_bypass_rw_generic(hw, cmd, &by_ctl)) 217 return IXGBE_ERR_INVALID_ARGUMENT; 218 219 /* Page 0 force a FW eeprom write which is slow so verify */ 220 if ((cmd & BYPASS_PAGE_M) == BYPASS_PAGE_CTL0) { 221 verify = BYPASS_PAGE_CTL0; 222 do { 223 if (count++ > 5) 224 return IXGBE_BYPASS_FW_WRITE_FAILURE; 225 226 if (ixgbe_bypass_rw_generic(hw, verify, &by_ctl)) 227 return IXGBE_ERR_INVALID_ARGUMENT; 228 } while (!ixgbe_bypass_valid_rd_generic(cmd, by_ctl)); 229 } else { 230 /* We have give the FW time for the write to stick */ 231 msleep(100); 232 } 233 234 return 0; 235 } 236 237 /** 238 * ixgbe_bypass_rd_eep_generic - Read the bypass FW eeprom address. 239 * 240 * @hw: pointer to hardware structure 241 * @addr: The bypass eeprom address to read. 242 * @value: The 8b of data at the address above. 243 **/ 244 static s32 ixgbe_bypass_rd_eep_generic(struct ixgbe_hw *hw, u32 addr, u8 *value) 245 { 246 u32 cmd; 247 u32 status; 248 249 250 /* send the request */ 251 cmd = BYPASS_PAGE_CTL2 | BYPASS_WE; 252 cmd |= (addr << BYPASS_CTL2_OFFSET_SHIFT) & BYPASS_CTL2_OFFSET_M; 253 if (ixgbe_bypass_rw_generic(hw, cmd, &status)) 254 return IXGBE_ERR_INVALID_ARGUMENT; 255 256 /* We have give the FW time for the write to stick */ 257 msleep(100); 258 259 /* now read the results */ 260 cmd &= ~BYPASS_WE; 261 if (ixgbe_bypass_rw_generic(hw, cmd, &status)) 262 return IXGBE_ERR_INVALID_ARGUMENT; 263 264 *value = status & BYPASS_CTL2_DATA_M; 265 266 return 0; 267 } 268 269 #endif /* RTE_LIBRTE_IXGBE_BYPASS */ 270 271 #endif /* _IXGBE_BYPASS_API_H_ */ 272