162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2015 - 2022 Beijing WangXun Technology Co., Ltd. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/etherdevice.h> 562306a36Sopenharmony_ci#include <linux/if_ether.h> 662306a36Sopenharmony_ci#include <linux/string.h> 762306a36Sopenharmony_ci#include <linux/iopoll.h> 862306a36Sopenharmony_ci#include <linux/types.h> 962306a36Sopenharmony_ci#include <linux/pci.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "../libwx/wx_type.h" 1262306a36Sopenharmony_ci#include "../libwx/wx_hw.h" 1362306a36Sopenharmony_ci#include "txgbe_type.h" 1462306a36Sopenharmony_ci#include "txgbe_hw.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/** 1762306a36Sopenharmony_ci * txgbe_disable_sec_tx_path - Stops the transmit data path 1862306a36Sopenharmony_ci * @wx: pointer to hardware structure 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Stops the transmit data path and waits for the HW to internally empty 2162306a36Sopenharmony_ci * the tx security block 2262306a36Sopenharmony_ci **/ 2362306a36Sopenharmony_ciint txgbe_disable_sec_tx_path(struct wx *wx) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci int val; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci wr32m(wx, WX_TSC_CTL, WX_TSC_CTL_TX_DIS, WX_TSC_CTL_TX_DIS); 2862306a36Sopenharmony_ci return read_poll_timeout(rd32, val, val & WX_TSC_ST_SECTX_RDY, 2962306a36Sopenharmony_ci 1000, 20000, false, wx, WX_TSC_ST); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/** 3362306a36Sopenharmony_ci * txgbe_enable_sec_tx_path - Enables the transmit data path 3462306a36Sopenharmony_ci * @wx: pointer to hardware structure 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * Enables the transmit data path. 3762306a36Sopenharmony_ci **/ 3862306a36Sopenharmony_civoid txgbe_enable_sec_tx_path(struct wx *wx) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci wr32m(wx, WX_TSC_CTL, WX_TSC_CTL_TX_DIS, 0); 4162306a36Sopenharmony_ci WX_WRITE_FLUSH(wx); 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/** 4562306a36Sopenharmony_ci * txgbe_init_thermal_sensor_thresh - Inits thermal sensor thresholds 4662306a36Sopenharmony_ci * @wx: pointer to hardware structure 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * Inits the thermal sensor thresholds according to the NVM map 4962306a36Sopenharmony_ci * and save off the threshold and location values into mac.thermal_sensor_data 5062306a36Sopenharmony_ci **/ 5162306a36Sopenharmony_cistatic void txgbe_init_thermal_sensor_thresh(struct wx *wx) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct wx_thermal_sensor_data *data = &wx->mac.sensor; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci memset(data, 0, sizeof(struct wx_thermal_sensor_data)); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* Only support thermal sensors attached to SP physical port 0 */ 5862306a36Sopenharmony_ci if (wx->bus.func) 5962306a36Sopenharmony_ci return; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci wr32(wx, TXGBE_TS_CTL, TXGBE_TS_CTL_EVAL_MD); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci wr32(wx, WX_TS_INT_EN, 6462306a36Sopenharmony_ci WX_TS_INT_EN_ALARM_INT_EN | WX_TS_INT_EN_DALARM_INT_EN); 6562306a36Sopenharmony_ci wr32(wx, WX_TS_EN, WX_TS_EN_ENA); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci data->alarm_thresh = 100; 6862306a36Sopenharmony_ci wr32(wx, WX_TS_ALARM_THRE, 677); 6962306a36Sopenharmony_ci data->dalarm_thresh = 90; 7062306a36Sopenharmony_ci wr32(wx, WX_TS_DALARM_THRE, 614); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/** 7462306a36Sopenharmony_ci * txgbe_read_pba_string - Reads part number string from EEPROM 7562306a36Sopenharmony_ci * @wx: pointer to hardware structure 7662306a36Sopenharmony_ci * @pba_num: stores the part number string from the EEPROM 7762306a36Sopenharmony_ci * @pba_num_size: part number string buffer length 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * Reads the part number string from the EEPROM. 8062306a36Sopenharmony_ci **/ 8162306a36Sopenharmony_ciint txgbe_read_pba_string(struct wx *wx, u8 *pba_num, u32 pba_num_size) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci u16 pba_ptr, offset, length, data; 8462306a36Sopenharmony_ci int ret_val; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (!pba_num) { 8762306a36Sopenharmony_ci wx_err(wx, "PBA string buffer was null\n"); 8862306a36Sopenharmony_ci return -EINVAL; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci ret_val = wx_read_ee_hostif(wx, 9262306a36Sopenharmony_ci wx->eeprom.sw_region_offset + TXGBE_PBANUM0_PTR, 9362306a36Sopenharmony_ci &data); 9462306a36Sopenharmony_ci if (ret_val != 0) { 9562306a36Sopenharmony_ci wx_err(wx, "NVM Read Error\n"); 9662306a36Sopenharmony_ci return ret_val; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci ret_val = wx_read_ee_hostif(wx, 10062306a36Sopenharmony_ci wx->eeprom.sw_region_offset + TXGBE_PBANUM1_PTR, 10162306a36Sopenharmony_ci &pba_ptr); 10262306a36Sopenharmony_ci if (ret_val != 0) { 10362306a36Sopenharmony_ci wx_err(wx, "NVM Read Error\n"); 10462306a36Sopenharmony_ci return ret_val; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* if data is not ptr guard the PBA must be in legacy format which 10862306a36Sopenharmony_ci * means pba_ptr is actually our second data word for the PBA number 10962306a36Sopenharmony_ci * and we can decode it into an ascii string 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci if (data != TXGBE_PBANUM_PTR_GUARD) { 11262306a36Sopenharmony_ci wx_err(wx, "NVM PBA number is not stored as string\n"); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* we will need 11 characters to store the PBA */ 11562306a36Sopenharmony_ci if (pba_num_size < 11) { 11662306a36Sopenharmony_ci wx_err(wx, "PBA string buffer too small\n"); 11762306a36Sopenharmony_ci return -ENOMEM; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* extract hex string from data and pba_ptr */ 12162306a36Sopenharmony_ci pba_num[0] = (data >> 12) & 0xF; 12262306a36Sopenharmony_ci pba_num[1] = (data >> 8) & 0xF; 12362306a36Sopenharmony_ci pba_num[2] = (data >> 4) & 0xF; 12462306a36Sopenharmony_ci pba_num[3] = data & 0xF; 12562306a36Sopenharmony_ci pba_num[4] = (pba_ptr >> 12) & 0xF; 12662306a36Sopenharmony_ci pba_num[5] = (pba_ptr >> 8) & 0xF; 12762306a36Sopenharmony_ci pba_num[6] = '-'; 12862306a36Sopenharmony_ci pba_num[7] = 0; 12962306a36Sopenharmony_ci pba_num[8] = (pba_ptr >> 4) & 0xF; 13062306a36Sopenharmony_ci pba_num[9] = pba_ptr & 0xF; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* put a null character on the end of our string */ 13362306a36Sopenharmony_ci pba_num[10] = '\0'; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* switch all the data but the '-' to hex char */ 13662306a36Sopenharmony_ci for (offset = 0; offset < 10; offset++) { 13762306a36Sopenharmony_ci if (pba_num[offset] < 0xA) 13862306a36Sopenharmony_ci pba_num[offset] += '0'; 13962306a36Sopenharmony_ci else if (pba_num[offset] < 0x10) 14062306a36Sopenharmony_ci pba_num[offset] += 'A' - 0xA; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci ret_val = wx_read_ee_hostif(wx, pba_ptr, &length); 14762306a36Sopenharmony_ci if (ret_val != 0) { 14862306a36Sopenharmony_ci wx_err(wx, "NVM Read Error\n"); 14962306a36Sopenharmony_ci return ret_val; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (length == 0xFFFF || length == 0) { 15362306a36Sopenharmony_ci wx_err(wx, "NVM PBA number section invalid length\n"); 15462306a36Sopenharmony_ci return -EINVAL; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* check if pba_num buffer is big enough */ 15862306a36Sopenharmony_ci if (pba_num_size < (((u32)length * 2) - 1)) { 15962306a36Sopenharmony_ci wx_err(wx, "PBA string buffer too small\n"); 16062306a36Sopenharmony_ci return -ENOMEM; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* trim pba length from start of string */ 16462306a36Sopenharmony_ci pba_ptr++; 16562306a36Sopenharmony_ci length--; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci for (offset = 0; offset < length; offset++) { 16862306a36Sopenharmony_ci ret_val = wx_read_ee_hostif(wx, pba_ptr + offset, &data); 16962306a36Sopenharmony_ci if (ret_val != 0) { 17062306a36Sopenharmony_ci wx_err(wx, "NVM Read Error\n"); 17162306a36Sopenharmony_ci return ret_val; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci pba_num[offset * 2] = (u8)(data >> 8); 17462306a36Sopenharmony_ci pba_num[(offset * 2) + 1] = (u8)(data & 0xFF); 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci pba_num[offset * 2] = '\0'; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return 0; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/** 18262306a36Sopenharmony_ci * txgbe_calc_eeprom_checksum - Calculates and returns the checksum 18362306a36Sopenharmony_ci * @wx: pointer to hardware structure 18462306a36Sopenharmony_ci * @checksum: pointer to cheksum 18562306a36Sopenharmony_ci * 18662306a36Sopenharmony_ci * Returns a negative error code on error 18762306a36Sopenharmony_ci **/ 18862306a36Sopenharmony_cistatic int txgbe_calc_eeprom_checksum(struct wx *wx, u16 *checksum) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci u16 *eeprom_ptrs = NULL; 19162306a36Sopenharmony_ci u16 *local_buffer; 19262306a36Sopenharmony_ci int status; 19362306a36Sopenharmony_ci u16 i; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci wx_init_eeprom_params(wx); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci eeprom_ptrs = kvmalloc_array(TXGBE_EEPROM_LAST_WORD, sizeof(u16), 19862306a36Sopenharmony_ci GFP_KERNEL); 19962306a36Sopenharmony_ci if (!eeprom_ptrs) 20062306a36Sopenharmony_ci return -ENOMEM; 20162306a36Sopenharmony_ci /* Read pointer area */ 20262306a36Sopenharmony_ci status = wx_read_ee_hostif_buffer(wx, 0, TXGBE_EEPROM_LAST_WORD, eeprom_ptrs); 20362306a36Sopenharmony_ci if (status != 0) { 20462306a36Sopenharmony_ci wx_err(wx, "Failed to read EEPROM image\n"); 20562306a36Sopenharmony_ci kvfree(eeprom_ptrs); 20662306a36Sopenharmony_ci return status; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci local_buffer = eeprom_ptrs; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci for (i = 0; i < TXGBE_EEPROM_LAST_WORD; i++) 21162306a36Sopenharmony_ci if (i != wx->eeprom.sw_region_offset + TXGBE_EEPROM_CHECKSUM) 21262306a36Sopenharmony_ci *checksum += local_buffer[i]; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (eeprom_ptrs) 21562306a36Sopenharmony_ci kvfree(eeprom_ptrs); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci *checksum = TXGBE_EEPROM_SUM - *checksum; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return 0; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci/** 22362306a36Sopenharmony_ci * txgbe_validate_eeprom_checksum - Validate EEPROM checksum 22462306a36Sopenharmony_ci * @wx: pointer to hardware structure 22562306a36Sopenharmony_ci * @checksum_val: calculated checksum 22662306a36Sopenharmony_ci * 22762306a36Sopenharmony_ci * Performs checksum calculation and validates the EEPROM checksum. If the 22862306a36Sopenharmony_ci * caller does not need checksum_val, the value can be NULL. 22962306a36Sopenharmony_ci **/ 23062306a36Sopenharmony_ciint txgbe_validate_eeprom_checksum(struct wx *wx, u16 *checksum_val) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci u16 read_checksum = 0; 23362306a36Sopenharmony_ci u16 checksum; 23462306a36Sopenharmony_ci int status; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* Read the first word from the EEPROM. If this times out or fails, do 23762306a36Sopenharmony_ci * not continue or we could be in for a very long wait while every 23862306a36Sopenharmony_ci * EEPROM read fails 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci status = wx_read_ee_hostif(wx, 0, &checksum); 24162306a36Sopenharmony_ci if (status) { 24262306a36Sopenharmony_ci wx_err(wx, "EEPROM read failed\n"); 24362306a36Sopenharmony_ci return status; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci checksum = 0; 24762306a36Sopenharmony_ci status = txgbe_calc_eeprom_checksum(wx, &checksum); 24862306a36Sopenharmony_ci if (status != 0) 24962306a36Sopenharmony_ci return status; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci status = wx_read_ee_hostif(wx, wx->eeprom.sw_region_offset + 25262306a36Sopenharmony_ci TXGBE_EEPROM_CHECKSUM, &read_checksum); 25362306a36Sopenharmony_ci if (status != 0) 25462306a36Sopenharmony_ci return status; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* Verify read checksum from EEPROM is the same as 25762306a36Sopenharmony_ci * calculated checksum 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ci if (read_checksum != checksum) { 26062306a36Sopenharmony_ci status = -EIO; 26162306a36Sopenharmony_ci wx_err(wx, "Invalid EEPROM checksum\n"); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* If the user cares, return the calculated checksum */ 26562306a36Sopenharmony_ci if (checksum_val) 26662306a36Sopenharmony_ci *checksum_val = checksum; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return status; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic void txgbe_reset_misc(struct wx *wx) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci wx_reset_misc(wx); 27462306a36Sopenharmony_ci txgbe_init_thermal_sensor_thresh(wx); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/** 27862306a36Sopenharmony_ci * txgbe_reset_hw - Perform hardware reset 27962306a36Sopenharmony_ci * @wx: pointer to wx structure 28062306a36Sopenharmony_ci * 28162306a36Sopenharmony_ci * Resets the hardware by resetting the transmit and receive units, masks 28262306a36Sopenharmony_ci * and clears all interrupts, perform a PHY reset, and perform a link (MAC) 28362306a36Sopenharmony_ci * reset. 28462306a36Sopenharmony_ci **/ 28562306a36Sopenharmony_ciint txgbe_reset_hw(struct wx *wx) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci int status; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* Call adapter stop to disable tx/rx and clear interrupts */ 29062306a36Sopenharmony_ci status = wx_stop_adapter(wx); 29162306a36Sopenharmony_ci if (status != 0) 29262306a36Sopenharmony_ci return status; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (wx->media_type != sp_media_copper) { 29562306a36Sopenharmony_ci u32 val; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci val = WX_MIS_RST_LAN_RST(wx->bus.func); 29862306a36Sopenharmony_ci wr32(wx, WX_MIS_RST, val | rd32(wx, WX_MIS_RST)); 29962306a36Sopenharmony_ci WX_WRITE_FLUSH(wx); 30062306a36Sopenharmony_ci usleep_range(10, 100); 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci status = wx_check_flash_load(wx, TXGBE_SPI_ILDR_STATUS_LAN_SW_RST(wx->bus.func)); 30462306a36Sopenharmony_ci if (status != 0) 30562306a36Sopenharmony_ci return status; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci txgbe_reset_misc(wx); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* Store the permanent mac address */ 31062306a36Sopenharmony_ci wx_get_mac_addr(wx, wx->mac.perm_addr); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* Store MAC address from RAR0, clear receive address registers, and 31362306a36Sopenharmony_ci * clear the multicast table. Also reset num_rar_entries to 128, 31462306a36Sopenharmony_ci * since we modify this value when programming the SAN MAC address. 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_ci wx->mac.num_rar_entries = TXGBE_SP_RAR_ENTRIES; 31762306a36Sopenharmony_ci wx_init_rx_addrs(wx); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci pci_set_master(wx->pdev); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci} 323