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