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/netdevice.h> 662306a36Sopenharmony_ci#include <linux/if_ether.h> 762306a36Sopenharmony_ci#include <linux/if_vlan.h> 862306a36Sopenharmony_ci#include <linux/iopoll.h> 962306a36Sopenharmony_ci#include <linux/pci.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "wx_type.h" 1262306a36Sopenharmony_ci#include "wx_lib.h" 1362306a36Sopenharmony_ci#include "wx_hw.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic void wx_intr_disable(struct wx *wx, u64 qmask) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci u32 mask; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci mask = (qmask & U32_MAX); 2062306a36Sopenharmony_ci if (mask) 2162306a36Sopenharmony_ci wr32(wx, WX_PX_IMS(0), mask); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci if (wx->mac.type == wx_mac_sp) { 2462306a36Sopenharmony_ci mask = (qmask >> 32); 2562306a36Sopenharmony_ci if (mask) 2662306a36Sopenharmony_ci wr32(wx, WX_PX_IMS(1), mask); 2762306a36Sopenharmony_ci } 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_civoid wx_intr_enable(struct wx *wx, u64 qmask) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci u32 mask; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci mask = (qmask & U32_MAX); 3562306a36Sopenharmony_ci if (mask) 3662306a36Sopenharmony_ci wr32(wx, WX_PX_IMC(0), mask); 3762306a36Sopenharmony_ci if (wx->mac.type == wx_mac_sp) { 3862306a36Sopenharmony_ci mask = (qmask >> 32); 3962306a36Sopenharmony_ci if (mask) 4062306a36Sopenharmony_ci wr32(wx, WX_PX_IMC(1), mask); 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ciEXPORT_SYMBOL(wx_intr_enable); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/** 4662306a36Sopenharmony_ci * wx_irq_disable - Mask off interrupt generation on the NIC 4762306a36Sopenharmony_ci * @wx: board private structure 4862306a36Sopenharmony_ci **/ 4962306a36Sopenharmony_civoid wx_irq_disable(struct wx *wx) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct pci_dev *pdev = wx->pdev; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci wr32(wx, WX_PX_MISC_IEN, 0); 5462306a36Sopenharmony_ci wx_intr_disable(wx, WX_INTR_ALL); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (pdev->msix_enabled) { 5762306a36Sopenharmony_ci int vector; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci for (vector = 0; vector < wx->num_q_vectors; vector++) 6062306a36Sopenharmony_ci synchronize_irq(wx->msix_entries[vector].vector); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci synchronize_irq(wx->msix_entries[vector].vector); 6362306a36Sopenharmony_ci } else { 6462306a36Sopenharmony_ci synchronize_irq(pdev->irq); 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ciEXPORT_SYMBOL(wx_irq_disable); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* cmd_addr is used for some special command: 7062306a36Sopenharmony_ci * 1. to be sector address, when implemented erase sector command 7162306a36Sopenharmony_ci * 2. to be flash address when implemented read, write flash address 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_cistatic int wx_fmgr_cmd_op(struct wx *wx, u32 cmd, u32 cmd_addr) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci u32 cmd_val = 0, val = 0; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci cmd_val = WX_SPI_CMD_CMD(cmd) | 7862306a36Sopenharmony_ci WX_SPI_CMD_CLK(WX_SPI_CLK_DIV) | 7962306a36Sopenharmony_ci cmd_addr; 8062306a36Sopenharmony_ci wr32(wx, WX_SPI_CMD, cmd_val); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return read_poll_timeout(rd32, val, (val & 0x1), 10, 100000, 8362306a36Sopenharmony_ci false, wx, WX_SPI_STATUS); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int wx_flash_read_dword(struct wx *wx, u32 addr, u32 *data) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci int ret = 0; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci ret = wx_fmgr_cmd_op(wx, WX_SPI_CMD_READ_DWORD, addr); 9162306a36Sopenharmony_ci if (ret < 0) 9262306a36Sopenharmony_ci return ret; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci *data = rd32(wx, WX_SPI_DATA); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return ret; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ciint wx_check_flash_load(struct wx *hw, u32 check_bit) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci u32 reg = 0; 10262306a36Sopenharmony_ci int err = 0; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* if there's flash existing */ 10562306a36Sopenharmony_ci if (!(rd32(hw, WX_SPI_STATUS) & 10662306a36Sopenharmony_ci WX_SPI_STATUS_FLASH_BYPASS)) { 10762306a36Sopenharmony_ci /* wait hw load flash done */ 10862306a36Sopenharmony_ci err = read_poll_timeout(rd32, reg, !(reg & check_bit), 20000, 2000000, 10962306a36Sopenharmony_ci false, hw, WX_SPI_ILDR_STATUS); 11062306a36Sopenharmony_ci if (err < 0) 11162306a36Sopenharmony_ci wx_err(hw, "Check flash load timeout.\n"); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return err; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ciEXPORT_SYMBOL(wx_check_flash_load); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_civoid wx_control_hw(struct wx *wx, bool drv) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci /* True : Let firmware know the driver has taken over 12162306a36Sopenharmony_ci * False : Let firmware take over control of hw 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci wr32m(wx, WX_CFG_PORT_CTL, WX_CFG_PORT_CTL_DRV_LOAD, 12462306a36Sopenharmony_ci drv ? WX_CFG_PORT_CTL_DRV_LOAD : 0); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ciEXPORT_SYMBOL(wx_control_hw); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/** 12962306a36Sopenharmony_ci * wx_mng_present - returns 0 when management capability is present 13062306a36Sopenharmony_ci * @wx: pointer to hardware structure 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ciint wx_mng_present(struct wx *wx) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci u32 fwsm; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci fwsm = rd32(wx, WX_MIS_ST); 13762306a36Sopenharmony_ci if (fwsm & WX_MIS_ST_MNG_INIT_DN) 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci else 14062306a36Sopenharmony_ci return -EACCES; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ciEXPORT_SYMBOL(wx_mng_present); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* Software lock to be held while software semaphore is being accessed. */ 14562306a36Sopenharmony_cistatic DEFINE_MUTEX(wx_sw_sync_lock); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/** 14862306a36Sopenharmony_ci * wx_release_sw_sync - Release SW semaphore 14962306a36Sopenharmony_ci * @wx: pointer to hardware structure 15062306a36Sopenharmony_ci * @mask: Mask to specify which semaphore to release 15162306a36Sopenharmony_ci * 15262306a36Sopenharmony_ci * Releases the SW semaphore for the specified 15362306a36Sopenharmony_ci * function (CSR, PHY0, PHY1, EEPROM, Flash) 15462306a36Sopenharmony_ci **/ 15562306a36Sopenharmony_cistatic void wx_release_sw_sync(struct wx *wx, u32 mask) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci mutex_lock(&wx_sw_sync_lock); 15862306a36Sopenharmony_ci wr32m(wx, WX_MNG_SWFW_SYNC, mask, 0); 15962306a36Sopenharmony_ci mutex_unlock(&wx_sw_sync_lock); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/** 16362306a36Sopenharmony_ci * wx_acquire_sw_sync - Acquire SW semaphore 16462306a36Sopenharmony_ci * @wx: pointer to hardware structure 16562306a36Sopenharmony_ci * @mask: Mask to specify which semaphore to acquire 16662306a36Sopenharmony_ci * 16762306a36Sopenharmony_ci * Acquires the SW semaphore for the specified 16862306a36Sopenharmony_ci * function (CSR, PHY0, PHY1, EEPROM, Flash) 16962306a36Sopenharmony_ci **/ 17062306a36Sopenharmony_cistatic int wx_acquire_sw_sync(struct wx *wx, u32 mask) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci u32 sem = 0; 17362306a36Sopenharmony_ci int ret = 0; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci mutex_lock(&wx_sw_sync_lock); 17662306a36Sopenharmony_ci ret = read_poll_timeout(rd32, sem, !(sem & mask), 17762306a36Sopenharmony_ci 5000, 2000000, false, wx, WX_MNG_SWFW_SYNC); 17862306a36Sopenharmony_ci if (!ret) { 17962306a36Sopenharmony_ci sem |= mask; 18062306a36Sopenharmony_ci wr32(wx, WX_MNG_SWFW_SYNC, sem); 18162306a36Sopenharmony_ci } else { 18262306a36Sopenharmony_ci wx_err(wx, "SW Semaphore not granted: 0x%x.\n", sem); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci mutex_unlock(&wx_sw_sync_lock); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return ret; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/** 19062306a36Sopenharmony_ci * wx_host_interface_command - Issue command to manageability block 19162306a36Sopenharmony_ci * @wx: pointer to the HW structure 19262306a36Sopenharmony_ci * @buffer: contains the command to write and where the return status will 19362306a36Sopenharmony_ci * be placed 19462306a36Sopenharmony_ci * @length: length of buffer, must be multiple of 4 bytes 19562306a36Sopenharmony_ci * @timeout: time in ms to wait for command completion 19662306a36Sopenharmony_ci * @return_data: read and return data from the buffer (true) or not (false) 19762306a36Sopenharmony_ci * Needed because FW structures are big endian and decoding of 19862306a36Sopenharmony_ci * these fields can be 8 bit or 16 bit based on command. Decoding 19962306a36Sopenharmony_ci * is not easily understood without making a table of commands. 20062306a36Sopenharmony_ci * So we will leave this up to the caller to read back the data 20162306a36Sopenharmony_ci * in these cases. 20262306a36Sopenharmony_ci **/ 20362306a36Sopenharmony_ciint wx_host_interface_command(struct wx *wx, u32 *buffer, 20462306a36Sopenharmony_ci u32 length, u32 timeout, bool return_data) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci u32 hdr_size = sizeof(struct wx_hic_hdr); 20762306a36Sopenharmony_ci u32 hicr, i, bi, buf[64] = {}; 20862306a36Sopenharmony_ci int status = 0; 20962306a36Sopenharmony_ci u32 dword_len; 21062306a36Sopenharmony_ci u16 buf_len; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (length == 0 || length > WX_HI_MAX_BLOCK_BYTE_LENGTH) { 21362306a36Sopenharmony_ci wx_err(wx, "Buffer length failure buffersize=%d.\n", length); 21462306a36Sopenharmony_ci return -EINVAL; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci status = wx_acquire_sw_sync(wx, WX_MNG_SWFW_SYNC_SW_MB); 21862306a36Sopenharmony_ci if (status != 0) 21962306a36Sopenharmony_ci return status; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* Calculate length in DWORDs. We must be DWORD aligned */ 22262306a36Sopenharmony_ci if ((length % (sizeof(u32))) != 0) { 22362306a36Sopenharmony_ci wx_err(wx, "Buffer length failure, not aligned to dword"); 22462306a36Sopenharmony_ci status = -EINVAL; 22562306a36Sopenharmony_ci goto rel_out; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci dword_len = length >> 2; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* The device driver writes the relevant command block 23162306a36Sopenharmony_ci * into the ram area. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci for (i = 0; i < dword_len; i++) { 23462306a36Sopenharmony_ci wr32a(wx, WX_MNG_MBOX, i, (__force u32)cpu_to_le32(buffer[i])); 23562306a36Sopenharmony_ci /* write flush */ 23662306a36Sopenharmony_ci buf[i] = rd32a(wx, WX_MNG_MBOX, i); 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci /* Setting this bit tells the ARC that a new command is pending. */ 23962306a36Sopenharmony_ci wr32m(wx, WX_MNG_MBOX_CTL, 24062306a36Sopenharmony_ci WX_MNG_MBOX_CTL_SWRDY, WX_MNG_MBOX_CTL_SWRDY); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci status = read_poll_timeout(rd32, hicr, hicr & WX_MNG_MBOX_CTL_FWRDY, 1000, 24362306a36Sopenharmony_ci timeout * 1000, false, wx, WX_MNG_MBOX_CTL); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Check command completion */ 24662306a36Sopenharmony_ci if (status) { 24762306a36Sopenharmony_ci wx_dbg(wx, "Command has failed with no status valid.\n"); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci buf[0] = rd32(wx, WX_MNG_MBOX); 25062306a36Sopenharmony_ci if ((buffer[0] & 0xff) != (~buf[0] >> 24)) { 25162306a36Sopenharmony_ci status = -EINVAL; 25262306a36Sopenharmony_ci goto rel_out; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci if ((buf[0] & 0xff0000) >> 16 == 0x80) { 25562306a36Sopenharmony_ci wx_dbg(wx, "It's unknown cmd.\n"); 25662306a36Sopenharmony_ci status = -EINVAL; 25762306a36Sopenharmony_ci goto rel_out; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci wx_dbg(wx, "write value:\n"); 26162306a36Sopenharmony_ci for (i = 0; i < dword_len; i++) 26262306a36Sopenharmony_ci wx_dbg(wx, "%x ", buffer[i]); 26362306a36Sopenharmony_ci wx_dbg(wx, "read value:\n"); 26462306a36Sopenharmony_ci for (i = 0; i < dword_len; i++) 26562306a36Sopenharmony_ci wx_dbg(wx, "%x ", buf[i]); 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (!return_data) 26962306a36Sopenharmony_ci goto rel_out; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Calculate length in DWORDs */ 27262306a36Sopenharmony_ci dword_len = hdr_size >> 2; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* first pull in the header so we know the buffer length */ 27562306a36Sopenharmony_ci for (bi = 0; bi < dword_len; bi++) { 27662306a36Sopenharmony_ci buffer[bi] = rd32a(wx, WX_MNG_MBOX, bi); 27762306a36Sopenharmony_ci le32_to_cpus(&buffer[bi]); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* If there is any thing in data position pull it in */ 28162306a36Sopenharmony_ci buf_len = ((struct wx_hic_hdr *)buffer)->buf_len; 28262306a36Sopenharmony_ci if (buf_len == 0) 28362306a36Sopenharmony_ci goto rel_out; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (length < buf_len + hdr_size) { 28662306a36Sopenharmony_ci wx_err(wx, "Buffer not large enough for reply message.\n"); 28762306a36Sopenharmony_ci status = -EFAULT; 28862306a36Sopenharmony_ci goto rel_out; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* Calculate length in DWORDs, add 3 for odd lengths */ 29262306a36Sopenharmony_ci dword_len = (buf_len + 3) >> 2; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* Pull in the rest of the buffer (bi is where we left off) */ 29562306a36Sopenharmony_ci for (; bi <= dword_len; bi++) { 29662306a36Sopenharmony_ci buffer[bi] = rd32a(wx, WX_MNG_MBOX, bi); 29762306a36Sopenharmony_ci le32_to_cpus(&buffer[bi]); 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cirel_out: 30162306a36Sopenharmony_ci wx_release_sw_sync(wx, WX_MNG_SWFW_SYNC_SW_MB); 30262306a36Sopenharmony_ci return status; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ciEXPORT_SYMBOL(wx_host_interface_command); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/** 30762306a36Sopenharmony_ci * wx_read_ee_hostif_data - Read EEPROM word using a host interface cmd 30862306a36Sopenharmony_ci * assuming that the semaphore is already obtained. 30962306a36Sopenharmony_ci * @wx: pointer to hardware structure 31062306a36Sopenharmony_ci * @offset: offset of word in the EEPROM to read 31162306a36Sopenharmony_ci * @data: word read from the EEPROM 31262306a36Sopenharmony_ci * 31362306a36Sopenharmony_ci * Reads a 16 bit word from the EEPROM using the hostif. 31462306a36Sopenharmony_ci **/ 31562306a36Sopenharmony_cistatic int wx_read_ee_hostif_data(struct wx *wx, u16 offset, u16 *data) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct wx_hic_read_shadow_ram buffer; 31862306a36Sopenharmony_ci int status; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci buffer.hdr.req.cmd = FW_READ_SHADOW_RAM_CMD; 32162306a36Sopenharmony_ci buffer.hdr.req.buf_lenh = 0; 32262306a36Sopenharmony_ci buffer.hdr.req.buf_lenl = FW_READ_SHADOW_RAM_LEN; 32362306a36Sopenharmony_ci buffer.hdr.req.checksum = FW_DEFAULT_CHECKSUM; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* convert offset from words to bytes */ 32662306a36Sopenharmony_ci buffer.address = (__force u32)cpu_to_be32(offset * 2); 32762306a36Sopenharmony_ci /* one word */ 32862306a36Sopenharmony_ci buffer.length = (__force u16)cpu_to_be16(sizeof(u16)); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci status = wx_host_interface_command(wx, (u32 *)&buffer, sizeof(buffer), 33162306a36Sopenharmony_ci WX_HI_COMMAND_TIMEOUT, false); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (status != 0) 33462306a36Sopenharmony_ci return status; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci *data = (u16)rd32a(wx, WX_MNG_MBOX, FW_NVM_DATA_OFFSET); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return status; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci/** 34262306a36Sopenharmony_ci * wx_read_ee_hostif - Read EEPROM word using a host interface cmd 34362306a36Sopenharmony_ci * @wx: pointer to hardware structure 34462306a36Sopenharmony_ci * @offset: offset of word in the EEPROM to read 34562306a36Sopenharmony_ci * @data: word read from the EEPROM 34662306a36Sopenharmony_ci * 34762306a36Sopenharmony_ci * Reads a 16 bit word from the EEPROM using the hostif. 34862306a36Sopenharmony_ci **/ 34962306a36Sopenharmony_ciint wx_read_ee_hostif(struct wx *wx, u16 offset, u16 *data) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci int status = 0; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci status = wx_acquire_sw_sync(wx, WX_MNG_SWFW_SYNC_SW_FLASH); 35462306a36Sopenharmony_ci if (status == 0) { 35562306a36Sopenharmony_ci status = wx_read_ee_hostif_data(wx, offset, data); 35662306a36Sopenharmony_ci wx_release_sw_sync(wx, WX_MNG_SWFW_SYNC_SW_FLASH); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci return status; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ciEXPORT_SYMBOL(wx_read_ee_hostif); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/** 36462306a36Sopenharmony_ci * wx_read_ee_hostif_buffer- Read EEPROM word(s) using hostif 36562306a36Sopenharmony_ci * @wx: pointer to hardware structure 36662306a36Sopenharmony_ci * @offset: offset of word in the EEPROM to read 36762306a36Sopenharmony_ci * @words: number of words 36862306a36Sopenharmony_ci * @data: word(s) read from the EEPROM 36962306a36Sopenharmony_ci * 37062306a36Sopenharmony_ci * Reads a 16 bit word(s) from the EEPROM using the hostif. 37162306a36Sopenharmony_ci **/ 37262306a36Sopenharmony_ciint wx_read_ee_hostif_buffer(struct wx *wx, 37362306a36Sopenharmony_ci u16 offset, u16 words, u16 *data) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct wx_hic_read_shadow_ram buffer; 37662306a36Sopenharmony_ci u32 current_word = 0; 37762306a36Sopenharmony_ci u16 words_to_read; 37862306a36Sopenharmony_ci u32 value = 0; 37962306a36Sopenharmony_ci int status; 38062306a36Sopenharmony_ci u32 i; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* Take semaphore for the entire operation. */ 38362306a36Sopenharmony_ci status = wx_acquire_sw_sync(wx, WX_MNG_SWFW_SYNC_SW_FLASH); 38462306a36Sopenharmony_ci if (status != 0) 38562306a36Sopenharmony_ci return status; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci while (words) { 38862306a36Sopenharmony_ci if (words > FW_MAX_READ_BUFFER_SIZE / 2) 38962306a36Sopenharmony_ci words_to_read = FW_MAX_READ_BUFFER_SIZE / 2; 39062306a36Sopenharmony_ci else 39162306a36Sopenharmony_ci words_to_read = words; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci buffer.hdr.req.cmd = FW_READ_SHADOW_RAM_CMD; 39462306a36Sopenharmony_ci buffer.hdr.req.buf_lenh = 0; 39562306a36Sopenharmony_ci buffer.hdr.req.buf_lenl = FW_READ_SHADOW_RAM_LEN; 39662306a36Sopenharmony_ci buffer.hdr.req.checksum = FW_DEFAULT_CHECKSUM; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* convert offset from words to bytes */ 39962306a36Sopenharmony_ci buffer.address = (__force u32)cpu_to_be32((offset + current_word) * 2); 40062306a36Sopenharmony_ci buffer.length = (__force u16)cpu_to_be16(words_to_read * 2); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci status = wx_host_interface_command(wx, (u32 *)&buffer, 40362306a36Sopenharmony_ci sizeof(buffer), 40462306a36Sopenharmony_ci WX_HI_COMMAND_TIMEOUT, 40562306a36Sopenharmony_ci false); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (status != 0) { 40862306a36Sopenharmony_ci wx_err(wx, "Host interface command failed\n"); 40962306a36Sopenharmony_ci goto out; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci for (i = 0; i < words_to_read; i++) { 41362306a36Sopenharmony_ci u32 reg = WX_MNG_MBOX + (FW_NVM_DATA_OFFSET << 2) + 2 * i; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci value = rd32(wx, reg); 41662306a36Sopenharmony_ci data[current_word] = (u16)(value & 0xffff); 41762306a36Sopenharmony_ci current_word++; 41862306a36Sopenharmony_ci i++; 41962306a36Sopenharmony_ci if (i < words_to_read) { 42062306a36Sopenharmony_ci value >>= 16; 42162306a36Sopenharmony_ci data[current_word] = (u16)(value & 0xffff); 42262306a36Sopenharmony_ci current_word++; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci words -= words_to_read; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ciout: 42962306a36Sopenharmony_ci wx_release_sw_sync(wx, WX_MNG_SWFW_SYNC_SW_FLASH); 43062306a36Sopenharmony_ci return status; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ciEXPORT_SYMBOL(wx_read_ee_hostif_buffer); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci/** 43562306a36Sopenharmony_ci * wx_init_eeprom_params - Initialize EEPROM params 43662306a36Sopenharmony_ci * @wx: pointer to hardware structure 43762306a36Sopenharmony_ci * 43862306a36Sopenharmony_ci * Initializes the EEPROM parameters wx_eeprom_info within the 43962306a36Sopenharmony_ci * wx_hw struct in order to set up EEPROM access. 44062306a36Sopenharmony_ci **/ 44162306a36Sopenharmony_civoid wx_init_eeprom_params(struct wx *wx) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct wx_eeprom_info *eeprom = &wx->eeprom; 44462306a36Sopenharmony_ci u16 eeprom_size; 44562306a36Sopenharmony_ci u16 data = 0x80; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (eeprom->type == wx_eeprom_uninitialized) { 44862306a36Sopenharmony_ci eeprom->semaphore_delay = 10; 44962306a36Sopenharmony_ci eeprom->type = wx_eeprom_none; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (!(rd32(wx, WX_SPI_STATUS) & 45262306a36Sopenharmony_ci WX_SPI_STATUS_FLASH_BYPASS)) { 45362306a36Sopenharmony_ci eeprom->type = wx_flash; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci eeprom_size = 4096; 45662306a36Sopenharmony_ci eeprom->word_size = eeprom_size >> 1; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci wx_dbg(wx, "Eeprom params: type = %d, size = %d\n", 45962306a36Sopenharmony_ci eeprom->type, eeprom->word_size); 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (wx->mac.type == wx_mac_sp) { 46462306a36Sopenharmony_ci if (wx_read_ee_hostif(wx, WX_SW_REGION_PTR, &data)) { 46562306a36Sopenharmony_ci wx_err(wx, "NVM Read Error\n"); 46662306a36Sopenharmony_ci return; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci data = data >> 1; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci eeprom->sw_region_offset = data; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ciEXPORT_SYMBOL(wx_init_eeprom_params); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci/** 47662306a36Sopenharmony_ci * wx_get_mac_addr - Generic get MAC address 47762306a36Sopenharmony_ci * @wx: pointer to hardware structure 47862306a36Sopenharmony_ci * @mac_addr: Adapter MAC address 47962306a36Sopenharmony_ci * 48062306a36Sopenharmony_ci * Reads the adapter's MAC address from first Receive Address Register (RAR0) 48162306a36Sopenharmony_ci * A reset of the adapter must be performed prior to calling this function 48262306a36Sopenharmony_ci * in order for the MAC address to have been loaded from the EEPROM into RAR0 48362306a36Sopenharmony_ci **/ 48462306a36Sopenharmony_civoid wx_get_mac_addr(struct wx *wx, u8 *mac_addr) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci u32 rar_high; 48762306a36Sopenharmony_ci u32 rar_low; 48862306a36Sopenharmony_ci u16 i; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci wr32(wx, WX_PSR_MAC_SWC_IDX, 0); 49162306a36Sopenharmony_ci rar_high = rd32(wx, WX_PSR_MAC_SWC_AD_H); 49262306a36Sopenharmony_ci rar_low = rd32(wx, WX_PSR_MAC_SWC_AD_L); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci for (i = 0; i < 2; i++) 49562306a36Sopenharmony_ci mac_addr[i] = (u8)(rar_high >> (1 - i) * 8); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci for (i = 0; i < 4; i++) 49862306a36Sopenharmony_ci mac_addr[i + 2] = (u8)(rar_low >> (3 - i) * 8); 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ciEXPORT_SYMBOL(wx_get_mac_addr); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci/** 50362306a36Sopenharmony_ci * wx_set_rar - Set Rx address register 50462306a36Sopenharmony_ci * @wx: pointer to hardware structure 50562306a36Sopenharmony_ci * @index: Receive address register to write 50662306a36Sopenharmony_ci * @addr: Address to put into receive address register 50762306a36Sopenharmony_ci * @pools: VMDq "set" or "pool" index 50862306a36Sopenharmony_ci * @enable_addr: set flag that address is active 50962306a36Sopenharmony_ci * 51062306a36Sopenharmony_ci * Puts an ethernet address into a receive address register. 51162306a36Sopenharmony_ci **/ 51262306a36Sopenharmony_cistatic int wx_set_rar(struct wx *wx, u32 index, u8 *addr, u64 pools, 51362306a36Sopenharmony_ci u32 enable_addr) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci u32 rar_entries = wx->mac.num_rar_entries; 51662306a36Sopenharmony_ci u32 rar_low, rar_high; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* Make sure we are using a valid rar index range */ 51962306a36Sopenharmony_ci if (index >= rar_entries) { 52062306a36Sopenharmony_ci wx_err(wx, "RAR index %d is out of range.\n", index); 52162306a36Sopenharmony_ci return -EINVAL; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* select the MAC address */ 52562306a36Sopenharmony_ci wr32(wx, WX_PSR_MAC_SWC_IDX, index); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* setup VMDq pool mapping */ 52862306a36Sopenharmony_ci wr32(wx, WX_PSR_MAC_SWC_VM_L, pools & 0xFFFFFFFF); 52962306a36Sopenharmony_ci if (wx->mac.type == wx_mac_sp) 53062306a36Sopenharmony_ci wr32(wx, WX_PSR_MAC_SWC_VM_H, pools >> 32); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* HW expects these in little endian so we reverse the byte 53362306a36Sopenharmony_ci * order from network order (big endian) to little endian 53462306a36Sopenharmony_ci * 53562306a36Sopenharmony_ci * Some parts put the VMDq setting in the extra RAH bits, 53662306a36Sopenharmony_ci * so save everything except the lower 16 bits that hold part 53762306a36Sopenharmony_ci * of the address and the address valid bit. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ci rar_low = ((u32)addr[5] | 54062306a36Sopenharmony_ci ((u32)addr[4] << 8) | 54162306a36Sopenharmony_ci ((u32)addr[3] << 16) | 54262306a36Sopenharmony_ci ((u32)addr[2] << 24)); 54362306a36Sopenharmony_ci rar_high = ((u32)addr[1] | 54462306a36Sopenharmony_ci ((u32)addr[0] << 8)); 54562306a36Sopenharmony_ci if (enable_addr != 0) 54662306a36Sopenharmony_ci rar_high |= WX_PSR_MAC_SWC_AD_H_AV; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci wr32(wx, WX_PSR_MAC_SWC_AD_L, rar_low); 54962306a36Sopenharmony_ci wr32m(wx, WX_PSR_MAC_SWC_AD_H, 55062306a36Sopenharmony_ci (WX_PSR_MAC_SWC_AD_H_AD(U16_MAX) | 55162306a36Sopenharmony_ci WX_PSR_MAC_SWC_AD_H_ADTYPE(1) | 55262306a36Sopenharmony_ci WX_PSR_MAC_SWC_AD_H_AV), 55362306a36Sopenharmony_ci rar_high); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci return 0; 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci/** 55962306a36Sopenharmony_ci * wx_clear_rar - Remove Rx address register 56062306a36Sopenharmony_ci * @wx: pointer to hardware structure 56162306a36Sopenharmony_ci * @index: Receive address register to write 56262306a36Sopenharmony_ci * 56362306a36Sopenharmony_ci * Clears an ethernet address from a receive address register. 56462306a36Sopenharmony_ci **/ 56562306a36Sopenharmony_cistatic int wx_clear_rar(struct wx *wx, u32 index) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci u32 rar_entries = wx->mac.num_rar_entries; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* Make sure we are using a valid rar index range */ 57062306a36Sopenharmony_ci if (index >= rar_entries) { 57162306a36Sopenharmony_ci wx_err(wx, "RAR index %d is out of range.\n", index); 57262306a36Sopenharmony_ci return -EINVAL; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* Some parts put the VMDq setting in the extra RAH bits, 57662306a36Sopenharmony_ci * so save everything except the lower 16 bits that hold part 57762306a36Sopenharmony_ci * of the address and the address valid bit. 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_ci wr32(wx, WX_PSR_MAC_SWC_IDX, index); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci wr32(wx, WX_PSR_MAC_SWC_VM_L, 0); 58262306a36Sopenharmony_ci wr32(wx, WX_PSR_MAC_SWC_VM_H, 0); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci wr32(wx, WX_PSR_MAC_SWC_AD_L, 0); 58562306a36Sopenharmony_ci wr32m(wx, WX_PSR_MAC_SWC_AD_H, 58662306a36Sopenharmony_ci (WX_PSR_MAC_SWC_AD_H_AD(U16_MAX) | 58762306a36Sopenharmony_ci WX_PSR_MAC_SWC_AD_H_ADTYPE(1) | 58862306a36Sopenharmony_ci WX_PSR_MAC_SWC_AD_H_AV), 58962306a36Sopenharmony_ci 0); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci return 0; 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci/** 59562306a36Sopenharmony_ci * wx_clear_vmdq - Disassociate a VMDq pool index from a rx address 59662306a36Sopenharmony_ci * @wx: pointer to hardware struct 59762306a36Sopenharmony_ci * @rar: receive address register index to disassociate 59862306a36Sopenharmony_ci * @vmdq: VMDq pool index to remove from the rar 59962306a36Sopenharmony_ci **/ 60062306a36Sopenharmony_cistatic int wx_clear_vmdq(struct wx *wx, u32 rar, u32 __maybe_unused vmdq) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci u32 rar_entries = wx->mac.num_rar_entries; 60362306a36Sopenharmony_ci u32 mpsar_lo, mpsar_hi; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci /* Make sure we are using a valid rar index range */ 60662306a36Sopenharmony_ci if (rar >= rar_entries) { 60762306a36Sopenharmony_ci wx_err(wx, "RAR index %d is out of range.\n", rar); 60862306a36Sopenharmony_ci return -EINVAL; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci wr32(wx, WX_PSR_MAC_SWC_IDX, rar); 61262306a36Sopenharmony_ci mpsar_lo = rd32(wx, WX_PSR_MAC_SWC_VM_L); 61362306a36Sopenharmony_ci mpsar_hi = rd32(wx, WX_PSR_MAC_SWC_VM_H); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (!mpsar_lo && !mpsar_hi) 61662306a36Sopenharmony_ci return 0; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci /* was that the last pool using this rar? */ 61962306a36Sopenharmony_ci if (mpsar_lo == 0 && mpsar_hi == 0 && rar != 0) 62062306a36Sopenharmony_ci wx_clear_rar(wx, rar); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci return 0; 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci/** 62662306a36Sopenharmony_ci * wx_init_uta_tables - Initialize the Unicast Table Array 62762306a36Sopenharmony_ci * @wx: pointer to hardware structure 62862306a36Sopenharmony_ci **/ 62962306a36Sopenharmony_cistatic void wx_init_uta_tables(struct wx *wx) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci int i; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci wx_dbg(wx, " Clearing UTA\n"); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci for (i = 0; i < 128; i++) 63662306a36Sopenharmony_ci wr32(wx, WX_PSR_UC_TBL(i), 0); 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci/** 64062306a36Sopenharmony_ci * wx_init_rx_addrs - Initializes receive address filters. 64162306a36Sopenharmony_ci * @wx: pointer to hardware structure 64262306a36Sopenharmony_ci * 64362306a36Sopenharmony_ci * Places the MAC address in receive address register 0 and clears the rest 64462306a36Sopenharmony_ci * of the receive address registers. Clears the multicast table. Assumes 64562306a36Sopenharmony_ci * the receiver is in reset when the routine is called. 64662306a36Sopenharmony_ci **/ 64762306a36Sopenharmony_civoid wx_init_rx_addrs(struct wx *wx) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci u32 rar_entries = wx->mac.num_rar_entries; 65062306a36Sopenharmony_ci u32 psrctl; 65162306a36Sopenharmony_ci int i; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* If the current mac address is valid, assume it is a software override 65462306a36Sopenharmony_ci * to the permanent address. 65562306a36Sopenharmony_ci * Otherwise, use the permanent address from the eeprom. 65662306a36Sopenharmony_ci */ 65762306a36Sopenharmony_ci if (!is_valid_ether_addr(wx->mac.addr)) { 65862306a36Sopenharmony_ci /* Get the MAC address from the RAR0 for later reference */ 65962306a36Sopenharmony_ci wx_get_mac_addr(wx, wx->mac.addr); 66062306a36Sopenharmony_ci wx_dbg(wx, "Keeping Current RAR0 Addr = %pM\n", wx->mac.addr); 66162306a36Sopenharmony_ci } else { 66262306a36Sopenharmony_ci /* Setup the receive address. */ 66362306a36Sopenharmony_ci wx_dbg(wx, "Overriding MAC Address in RAR[0]\n"); 66462306a36Sopenharmony_ci wx_dbg(wx, "New MAC Addr = %pM\n", wx->mac.addr); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci wx_set_rar(wx, 0, wx->mac.addr, 0, WX_PSR_MAC_SWC_AD_H_AV); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (wx->mac.type == wx_mac_sp) { 66962306a36Sopenharmony_ci /* clear VMDq pool/queue selection for RAR 0 */ 67062306a36Sopenharmony_ci wx_clear_vmdq(wx, 0, WX_CLEAR_VMDQ_ALL); 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* Zero out the other receive addresses. */ 67562306a36Sopenharmony_ci wx_dbg(wx, "Clearing RAR[1-%d]\n", rar_entries - 1); 67662306a36Sopenharmony_ci for (i = 1; i < rar_entries; i++) { 67762306a36Sopenharmony_ci wr32(wx, WX_PSR_MAC_SWC_IDX, i); 67862306a36Sopenharmony_ci wr32(wx, WX_PSR_MAC_SWC_AD_L, 0); 67962306a36Sopenharmony_ci wr32(wx, WX_PSR_MAC_SWC_AD_H, 0); 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* Clear the MTA */ 68362306a36Sopenharmony_ci wx->addr_ctrl.mta_in_use = 0; 68462306a36Sopenharmony_ci psrctl = rd32(wx, WX_PSR_CTL); 68562306a36Sopenharmony_ci psrctl &= ~(WX_PSR_CTL_MO | WX_PSR_CTL_MFE); 68662306a36Sopenharmony_ci psrctl |= wx->mac.mc_filter_type << WX_PSR_CTL_MO_SHIFT; 68762306a36Sopenharmony_ci wr32(wx, WX_PSR_CTL, psrctl); 68862306a36Sopenharmony_ci wx_dbg(wx, " Clearing MTA\n"); 68962306a36Sopenharmony_ci for (i = 0; i < wx->mac.mcft_size; i++) 69062306a36Sopenharmony_ci wr32(wx, WX_PSR_MC_TBL(i), 0); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci wx_init_uta_tables(wx); 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ciEXPORT_SYMBOL(wx_init_rx_addrs); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cistatic void wx_sync_mac_table(struct wx *wx) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci int i; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci for (i = 0; i < wx->mac.num_rar_entries; i++) { 70162306a36Sopenharmony_ci if (wx->mac_table[i].state & WX_MAC_STATE_MODIFIED) { 70262306a36Sopenharmony_ci if (wx->mac_table[i].state & WX_MAC_STATE_IN_USE) { 70362306a36Sopenharmony_ci wx_set_rar(wx, i, 70462306a36Sopenharmony_ci wx->mac_table[i].addr, 70562306a36Sopenharmony_ci wx->mac_table[i].pools, 70662306a36Sopenharmony_ci WX_PSR_MAC_SWC_AD_H_AV); 70762306a36Sopenharmony_ci } else { 70862306a36Sopenharmony_ci wx_clear_rar(wx, i); 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci wx->mac_table[i].state &= ~(WX_MAC_STATE_MODIFIED); 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci/* this function destroys the first RAR entry */ 71662306a36Sopenharmony_civoid wx_mac_set_default_filter(struct wx *wx, u8 *addr) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci memcpy(&wx->mac_table[0].addr, addr, ETH_ALEN); 71962306a36Sopenharmony_ci wx->mac_table[0].pools = 1ULL; 72062306a36Sopenharmony_ci wx->mac_table[0].state = (WX_MAC_STATE_DEFAULT | WX_MAC_STATE_IN_USE); 72162306a36Sopenharmony_ci wx_set_rar(wx, 0, wx->mac_table[0].addr, 72262306a36Sopenharmony_ci wx->mac_table[0].pools, 72362306a36Sopenharmony_ci WX_PSR_MAC_SWC_AD_H_AV); 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ciEXPORT_SYMBOL(wx_mac_set_default_filter); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_civoid wx_flush_sw_mac_table(struct wx *wx) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci u32 i; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci for (i = 0; i < wx->mac.num_rar_entries; i++) { 73262306a36Sopenharmony_ci if (!(wx->mac_table[i].state & WX_MAC_STATE_IN_USE)) 73362306a36Sopenharmony_ci continue; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci wx->mac_table[i].state |= WX_MAC_STATE_MODIFIED; 73662306a36Sopenharmony_ci wx->mac_table[i].state &= ~WX_MAC_STATE_IN_USE; 73762306a36Sopenharmony_ci memset(wx->mac_table[i].addr, 0, ETH_ALEN); 73862306a36Sopenharmony_ci wx->mac_table[i].pools = 0; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci wx_sync_mac_table(wx); 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ciEXPORT_SYMBOL(wx_flush_sw_mac_table); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic int wx_add_mac_filter(struct wx *wx, u8 *addr, u16 pool) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci u32 i; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci if (is_zero_ether_addr(addr)) 74962306a36Sopenharmony_ci return -EINVAL; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci for (i = 0; i < wx->mac.num_rar_entries; i++) { 75262306a36Sopenharmony_ci if (wx->mac_table[i].state & WX_MAC_STATE_IN_USE) { 75362306a36Sopenharmony_ci if (ether_addr_equal(addr, wx->mac_table[i].addr)) { 75462306a36Sopenharmony_ci if (wx->mac_table[i].pools != (1ULL << pool)) { 75562306a36Sopenharmony_ci memcpy(wx->mac_table[i].addr, addr, ETH_ALEN); 75662306a36Sopenharmony_ci wx->mac_table[i].pools |= (1ULL << pool); 75762306a36Sopenharmony_ci wx_sync_mac_table(wx); 75862306a36Sopenharmony_ci return i; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci if (wx->mac_table[i].state & WX_MAC_STATE_IN_USE) 76462306a36Sopenharmony_ci continue; 76562306a36Sopenharmony_ci wx->mac_table[i].state |= (WX_MAC_STATE_MODIFIED | 76662306a36Sopenharmony_ci WX_MAC_STATE_IN_USE); 76762306a36Sopenharmony_ci memcpy(wx->mac_table[i].addr, addr, ETH_ALEN); 76862306a36Sopenharmony_ci wx->mac_table[i].pools |= (1ULL << pool); 76962306a36Sopenharmony_ci wx_sync_mac_table(wx); 77062306a36Sopenharmony_ci return i; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci return -ENOMEM; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistatic int wx_del_mac_filter(struct wx *wx, u8 *addr, u16 pool) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci u32 i; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci if (is_zero_ether_addr(addr)) 78062306a36Sopenharmony_ci return -EINVAL; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci /* search table for addr, if found, set to 0 and sync */ 78362306a36Sopenharmony_ci for (i = 0; i < wx->mac.num_rar_entries; i++) { 78462306a36Sopenharmony_ci if (!ether_addr_equal(addr, wx->mac_table[i].addr)) 78562306a36Sopenharmony_ci continue; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci wx->mac_table[i].state |= WX_MAC_STATE_MODIFIED; 78862306a36Sopenharmony_ci wx->mac_table[i].pools &= ~(1ULL << pool); 78962306a36Sopenharmony_ci if (!wx->mac_table[i].pools) { 79062306a36Sopenharmony_ci wx->mac_table[i].state &= ~WX_MAC_STATE_IN_USE; 79162306a36Sopenharmony_ci memset(wx->mac_table[i].addr, 0, ETH_ALEN); 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci wx_sync_mac_table(wx); 79462306a36Sopenharmony_ci return 0; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci return -ENOMEM; 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic int wx_available_rars(struct wx *wx) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci u32 i, count = 0; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci for (i = 0; i < wx->mac.num_rar_entries; i++) { 80462306a36Sopenharmony_ci if (wx->mac_table[i].state == 0) 80562306a36Sopenharmony_ci count++; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci return count; 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci/** 81262306a36Sopenharmony_ci * wx_write_uc_addr_list - write unicast addresses to RAR table 81362306a36Sopenharmony_ci * @netdev: network interface device structure 81462306a36Sopenharmony_ci * @pool: index for mac table 81562306a36Sopenharmony_ci * 81662306a36Sopenharmony_ci * Writes unicast address list to the RAR table. 81762306a36Sopenharmony_ci * Returns: -ENOMEM on failure/insufficient address space 81862306a36Sopenharmony_ci * 0 on no addresses written 81962306a36Sopenharmony_ci * X on writing X addresses to the RAR table 82062306a36Sopenharmony_ci **/ 82162306a36Sopenharmony_cistatic int wx_write_uc_addr_list(struct net_device *netdev, int pool) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci struct wx *wx = netdev_priv(netdev); 82462306a36Sopenharmony_ci int count = 0; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci /* return ENOMEM indicating insufficient memory for addresses */ 82762306a36Sopenharmony_ci if (netdev_uc_count(netdev) > wx_available_rars(wx)) 82862306a36Sopenharmony_ci return -ENOMEM; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (!netdev_uc_empty(netdev)) { 83162306a36Sopenharmony_ci struct netdev_hw_addr *ha; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci netdev_for_each_uc_addr(ha, netdev) { 83462306a36Sopenharmony_ci wx_del_mac_filter(wx, ha->addr, pool); 83562306a36Sopenharmony_ci wx_add_mac_filter(wx, ha->addr, pool); 83662306a36Sopenharmony_ci count++; 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci return count; 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci/** 84362306a36Sopenharmony_ci * wx_mta_vector - Determines bit-vector in multicast table to set 84462306a36Sopenharmony_ci * @wx: pointer to private structure 84562306a36Sopenharmony_ci * @mc_addr: the multicast address 84662306a36Sopenharmony_ci * 84762306a36Sopenharmony_ci * Extracts the 12 bits, from a multicast address, to determine which 84862306a36Sopenharmony_ci * bit-vector to set in the multicast table. The hardware uses 12 bits, from 84962306a36Sopenharmony_ci * incoming rx multicast addresses, to determine the bit-vector to check in 85062306a36Sopenharmony_ci * the MTA. Which of the 4 combination, of 12-bits, the hardware uses is set 85162306a36Sopenharmony_ci * by the MO field of the MCSTCTRL. The MO field is set during initialization 85262306a36Sopenharmony_ci * to mc_filter_type. 85362306a36Sopenharmony_ci **/ 85462306a36Sopenharmony_cistatic u32 wx_mta_vector(struct wx *wx, u8 *mc_addr) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci u32 vector = 0; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci switch (wx->mac.mc_filter_type) { 85962306a36Sopenharmony_ci case 0: /* use bits [47:36] of the address */ 86062306a36Sopenharmony_ci vector = ((mc_addr[4] >> 4) | (((u16)mc_addr[5]) << 4)); 86162306a36Sopenharmony_ci break; 86262306a36Sopenharmony_ci case 1: /* use bits [46:35] of the address */ 86362306a36Sopenharmony_ci vector = ((mc_addr[4] >> 3) | (((u16)mc_addr[5]) << 5)); 86462306a36Sopenharmony_ci break; 86562306a36Sopenharmony_ci case 2: /* use bits [45:34] of the address */ 86662306a36Sopenharmony_ci vector = ((mc_addr[4] >> 2) | (((u16)mc_addr[5]) << 6)); 86762306a36Sopenharmony_ci break; 86862306a36Sopenharmony_ci case 3: /* use bits [43:32] of the address */ 86962306a36Sopenharmony_ci vector = ((mc_addr[4]) | (((u16)mc_addr[5]) << 8)); 87062306a36Sopenharmony_ci break; 87162306a36Sopenharmony_ci default: /* Invalid mc_filter_type */ 87262306a36Sopenharmony_ci wx_err(wx, "MC filter type param set incorrectly\n"); 87362306a36Sopenharmony_ci break; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci /* vector can only be 12-bits or boundary will be exceeded */ 87762306a36Sopenharmony_ci vector &= 0xFFF; 87862306a36Sopenharmony_ci return vector; 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci/** 88262306a36Sopenharmony_ci * wx_set_mta - Set bit-vector in multicast table 88362306a36Sopenharmony_ci * @wx: pointer to private structure 88462306a36Sopenharmony_ci * @mc_addr: Multicast address 88562306a36Sopenharmony_ci * 88662306a36Sopenharmony_ci * Sets the bit-vector in the multicast table. 88762306a36Sopenharmony_ci **/ 88862306a36Sopenharmony_cistatic void wx_set_mta(struct wx *wx, u8 *mc_addr) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci u32 vector, vector_bit, vector_reg; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci wx->addr_ctrl.mta_in_use++; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci vector = wx_mta_vector(wx, mc_addr); 89562306a36Sopenharmony_ci wx_dbg(wx, " bit-vector = 0x%03X\n", vector); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci /* The MTA is a register array of 128 32-bit registers. It is treated 89862306a36Sopenharmony_ci * like an array of 4096 bits. We want to set bit 89962306a36Sopenharmony_ci * BitArray[vector_value]. So we figure out what register the bit is 90062306a36Sopenharmony_ci * in, read it, OR in the new bit, then write back the new value. The 90162306a36Sopenharmony_ci * register is determined by the upper 7 bits of the vector value and 90262306a36Sopenharmony_ci * the bit within that register are determined by the lower 5 bits of 90362306a36Sopenharmony_ci * the value. 90462306a36Sopenharmony_ci */ 90562306a36Sopenharmony_ci vector_reg = (vector >> 5) & 0x7F; 90662306a36Sopenharmony_ci vector_bit = vector & 0x1F; 90762306a36Sopenharmony_ci wx->mac.mta_shadow[vector_reg] |= (1 << vector_bit); 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci/** 91162306a36Sopenharmony_ci * wx_update_mc_addr_list - Updates MAC list of multicast addresses 91262306a36Sopenharmony_ci * @wx: pointer to private structure 91362306a36Sopenharmony_ci * @netdev: pointer to net device structure 91462306a36Sopenharmony_ci * 91562306a36Sopenharmony_ci * The given list replaces any existing list. Clears the MC addrs from receive 91662306a36Sopenharmony_ci * address registers and the multicast table. Uses unused receive address 91762306a36Sopenharmony_ci * registers for the first multicast addresses, and hashes the rest into the 91862306a36Sopenharmony_ci * multicast table. 91962306a36Sopenharmony_ci **/ 92062306a36Sopenharmony_cistatic void wx_update_mc_addr_list(struct wx *wx, struct net_device *netdev) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci struct netdev_hw_addr *ha; 92362306a36Sopenharmony_ci u32 i, psrctl; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci /* Set the new number of MC addresses that we are being requested to 92662306a36Sopenharmony_ci * use. 92762306a36Sopenharmony_ci */ 92862306a36Sopenharmony_ci wx->addr_ctrl.num_mc_addrs = netdev_mc_count(netdev); 92962306a36Sopenharmony_ci wx->addr_ctrl.mta_in_use = 0; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci /* Clear mta_shadow */ 93262306a36Sopenharmony_ci wx_dbg(wx, " Clearing MTA\n"); 93362306a36Sopenharmony_ci memset(&wx->mac.mta_shadow, 0, sizeof(wx->mac.mta_shadow)); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci /* Update mta_shadow */ 93662306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) { 93762306a36Sopenharmony_ci wx_dbg(wx, " Adding the multicast addresses:\n"); 93862306a36Sopenharmony_ci wx_set_mta(wx, ha->addr); 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci /* Enable mta */ 94262306a36Sopenharmony_ci for (i = 0; i < wx->mac.mcft_size; i++) 94362306a36Sopenharmony_ci wr32a(wx, WX_PSR_MC_TBL(0), i, 94462306a36Sopenharmony_ci wx->mac.mta_shadow[i]); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci if (wx->addr_ctrl.mta_in_use > 0) { 94762306a36Sopenharmony_ci psrctl = rd32(wx, WX_PSR_CTL); 94862306a36Sopenharmony_ci psrctl &= ~(WX_PSR_CTL_MO | WX_PSR_CTL_MFE); 94962306a36Sopenharmony_ci psrctl |= WX_PSR_CTL_MFE | 95062306a36Sopenharmony_ci (wx->mac.mc_filter_type << WX_PSR_CTL_MO_SHIFT); 95162306a36Sopenharmony_ci wr32(wx, WX_PSR_CTL, psrctl); 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci wx_dbg(wx, "Update mc addr list Complete\n"); 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci/** 95862306a36Sopenharmony_ci * wx_write_mc_addr_list - write multicast addresses to MTA 95962306a36Sopenharmony_ci * @netdev: network interface device structure 96062306a36Sopenharmony_ci * 96162306a36Sopenharmony_ci * Writes multicast address list to the MTA hash table. 96262306a36Sopenharmony_ci * Returns: 0 on no addresses written 96362306a36Sopenharmony_ci * X on writing X addresses to MTA 96462306a36Sopenharmony_ci **/ 96562306a36Sopenharmony_cistatic int wx_write_mc_addr_list(struct net_device *netdev) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci struct wx *wx = netdev_priv(netdev); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci if (!netif_running(netdev)) 97062306a36Sopenharmony_ci return 0; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci wx_update_mc_addr_list(wx, netdev); 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci return netdev_mc_count(netdev); 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci/** 97862306a36Sopenharmony_ci * wx_set_mac - Change the Ethernet Address of the NIC 97962306a36Sopenharmony_ci * @netdev: network interface device structure 98062306a36Sopenharmony_ci * @p: pointer to an address structure 98162306a36Sopenharmony_ci * 98262306a36Sopenharmony_ci * Returns 0 on success, negative on failure 98362306a36Sopenharmony_ci **/ 98462306a36Sopenharmony_ciint wx_set_mac(struct net_device *netdev, void *p) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci struct wx *wx = netdev_priv(netdev); 98762306a36Sopenharmony_ci struct sockaddr *addr = p; 98862306a36Sopenharmony_ci int retval; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci retval = eth_prepare_mac_addr_change(netdev, addr); 99162306a36Sopenharmony_ci if (retval) 99262306a36Sopenharmony_ci return retval; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci wx_del_mac_filter(wx, wx->mac.addr, 0); 99562306a36Sopenharmony_ci eth_hw_addr_set(netdev, addr->sa_data); 99662306a36Sopenharmony_ci memcpy(wx->mac.addr, addr->sa_data, netdev->addr_len); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci wx_mac_set_default_filter(wx, wx->mac.addr); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci return 0; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ciEXPORT_SYMBOL(wx_set_mac); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_civoid wx_disable_rx(struct wx *wx) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci u32 pfdtxgswc; 100762306a36Sopenharmony_ci u32 rxctrl; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci rxctrl = rd32(wx, WX_RDB_PB_CTL); 101062306a36Sopenharmony_ci if (rxctrl & WX_RDB_PB_CTL_RXEN) { 101162306a36Sopenharmony_ci pfdtxgswc = rd32(wx, WX_PSR_CTL); 101262306a36Sopenharmony_ci if (pfdtxgswc & WX_PSR_CTL_SW_EN) { 101362306a36Sopenharmony_ci pfdtxgswc &= ~WX_PSR_CTL_SW_EN; 101462306a36Sopenharmony_ci wr32(wx, WX_PSR_CTL, pfdtxgswc); 101562306a36Sopenharmony_ci wx->mac.set_lben = true; 101662306a36Sopenharmony_ci } else { 101762306a36Sopenharmony_ci wx->mac.set_lben = false; 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci rxctrl &= ~WX_RDB_PB_CTL_RXEN; 102062306a36Sopenharmony_ci wr32(wx, WX_RDB_PB_CTL, rxctrl); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci if (!(((wx->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP) || 102362306a36Sopenharmony_ci ((wx->subsystem_device_id & WX_WOL_MASK) == WX_WOL_SUP))) { 102462306a36Sopenharmony_ci /* disable mac receiver */ 102562306a36Sopenharmony_ci wr32m(wx, WX_MAC_RX_CFG, 102662306a36Sopenharmony_ci WX_MAC_RX_CFG_RE, 0); 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci } 102962306a36Sopenharmony_ci} 103062306a36Sopenharmony_ciEXPORT_SYMBOL(wx_disable_rx); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_cistatic void wx_enable_rx(struct wx *wx) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci u32 psrctl; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci /* enable mac receiver */ 103762306a36Sopenharmony_ci wr32m(wx, WX_MAC_RX_CFG, 103862306a36Sopenharmony_ci WX_MAC_RX_CFG_RE, WX_MAC_RX_CFG_RE); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci wr32m(wx, WX_RDB_PB_CTL, 104162306a36Sopenharmony_ci WX_RDB_PB_CTL_RXEN, WX_RDB_PB_CTL_RXEN); 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci if (wx->mac.set_lben) { 104462306a36Sopenharmony_ci psrctl = rd32(wx, WX_PSR_CTL); 104562306a36Sopenharmony_ci psrctl |= WX_PSR_CTL_SW_EN; 104662306a36Sopenharmony_ci wr32(wx, WX_PSR_CTL, psrctl); 104762306a36Sopenharmony_ci wx->mac.set_lben = false; 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci} 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci/** 105262306a36Sopenharmony_ci * wx_set_rxpba - Initialize Rx packet buffer 105362306a36Sopenharmony_ci * @wx: pointer to private structure 105462306a36Sopenharmony_ci **/ 105562306a36Sopenharmony_cistatic void wx_set_rxpba(struct wx *wx) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci u32 rxpktsize, txpktsize, txpbthresh; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci rxpktsize = wx->mac.rx_pb_size << WX_RDB_PB_SZ_SHIFT; 106062306a36Sopenharmony_ci wr32(wx, WX_RDB_PB_SZ(0), rxpktsize); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci /* Only support an equally distributed Tx packet buffer strategy. */ 106362306a36Sopenharmony_ci txpktsize = wx->mac.tx_pb_size; 106462306a36Sopenharmony_ci txpbthresh = (txpktsize / 1024) - WX_TXPKT_SIZE_MAX; 106562306a36Sopenharmony_ci wr32(wx, WX_TDB_PB_SZ(0), txpktsize); 106662306a36Sopenharmony_ci wr32(wx, WX_TDM_PB_THRE(0), txpbthresh); 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_cistatic void wx_configure_port(struct wx *wx) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci u32 value, i; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci value = WX_CFG_PORT_CTL_D_VLAN | WX_CFG_PORT_CTL_QINQ; 107462306a36Sopenharmony_ci wr32m(wx, WX_CFG_PORT_CTL, 107562306a36Sopenharmony_ci WX_CFG_PORT_CTL_D_VLAN | 107662306a36Sopenharmony_ci WX_CFG_PORT_CTL_QINQ, 107762306a36Sopenharmony_ci value); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci wr32(wx, WX_CFG_TAG_TPID(0), 108062306a36Sopenharmony_ci ETH_P_8021Q | ETH_P_8021AD << 16); 108162306a36Sopenharmony_ci wx->tpid[0] = ETH_P_8021Q; 108262306a36Sopenharmony_ci wx->tpid[1] = ETH_P_8021AD; 108362306a36Sopenharmony_ci for (i = 1; i < 4; i++) 108462306a36Sopenharmony_ci wr32(wx, WX_CFG_TAG_TPID(i), 108562306a36Sopenharmony_ci ETH_P_8021Q | ETH_P_8021Q << 16); 108662306a36Sopenharmony_ci for (i = 2; i < 8; i++) 108762306a36Sopenharmony_ci wx->tpid[i] = ETH_P_8021Q; 108862306a36Sopenharmony_ci} 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci/** 109162306a36Sopenharmony_ci * wx_disable_sec_rx_path - Stops the receive data path 109262306a36Sopenharmony_ci * @wx: pointer to private structure 109362306a36Sopenharmony_ci * 109462306a36Sopenharmony_ci * Stops the receive data path and waits for the HW to internally empty 109562306a36Sopenharmony_ci * the Rx security block 109662306a36Sopenharmony_ci **/ 109762306a36Sopenharmony_cistatic int wx_disable_sec_rx_path(struct wx *wx) 109862306a36Sopenharmony_ci{ 109962306a36Sopenharmony_ci u32 secrx; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci wr32m(wx, WX_RSC_CTL, 110262306a36Sopenharmony_ci WX_RSC_CTL_RX_DIS, WX_RSC_CTL_RX_DIS); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci return read_poll_timeout(rd32, secrx, secrx & WX_RSC_ST_RSEC_RDY, 110562306a36Sopenharmony_ci 1000, 40000, false, wx, WX_RSC_ST); 110662306a36Sopenharmony_ci} 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci/** 110962306a36Sopenharmony_ci * wx_enable_sec_rx_path - Enables the receive data path 111062306a36Sopenharmony_ci * @wx: pointer to private structure 111162306a36Sopenharmony_ci * 111262306a36Sopenharmony_ci * Enables the receive data path. 111362306a36Sopenharmony_ci **/ 111462306a36Sopenharmony_cistatic void wx_enable_sec_rx_path(struct wx *wx) 111562306a36Sopenharmony_ci{ 111662306a36Sopenharmony_ci wr32m(wx, WX_RSC_CTL, WX_RSC_CTL_RX_DIS, 0); 111762306a36Sopenharmony_ci WX_WRITE_FLUSH(wx); 111862306a36Sopenharmony_ci} 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_cistatic void wx_vlan_strip_control(struct wx *wx, bool enable) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci int i, j; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci for (i = 0; i < wx->num_rx_queues; i++) { 112562306a36Sopenharmony_ci struct wx_ring *ring = wx->rx_ring[i]; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci j = ring->reg_idx; 112862306a36Sopenharmony_ci wr32m(wx, WX_PX_RR_CFG(j), WX_PX_RR_CFG_VLAN, 112962306a36Sopenharmony_ci enable ? WX_PX_RR_CFG_VLAN : 0); 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci} 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_civoid wx_set_rx_mode(struct net_device *netdev) 113462306a36Sopenharmony_ci{ 113562306a36Sopenharmony_ci struct wx *wx = netdev_priv(netdev); 113662306a36Sopenharmony_ci netdev_features_t features; 113762306a36Sopenharmony_ci u32 fctrl, vmolr, vlnctrl; 113862306a36Sopenharmony_ci int count; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci features = netdev->features; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci /* Check for Promiscuous and All Multicast modes */ 114362306a36Sopenharmony_ci fctrl = rd32(wx, WX_PSR_CTL); 114462306a36Sopenharmony_ci fctrl &= ~(WX_PSR_CTL_UPE | WX_PSR_CTL_MPE); 114562306a36Sopenharmony_ci vmolr = rd32(wx, WX_PSR_VM_L2CTL(0)); 114662306a36Sopenharmony_ci vmolr &= ~(WX_PSR_VM_L2CTL_UPE | 114762306a36Sopenharmony_ci WX_PSR_VM_L2CTL_MPE | 114862306a36Sopenharmony_ci WX_PSR_VM_L2CTL_ROPE | 114962306a36Sopenharmony_ci WX_PSR_VM_L2CTL_ROMPE); 115062306a36Sopenharmony_ci vlnctrl = rd32(wx, WX_PSR_VLAN_CTL); 115162306a36Sopenharmony_ci vlnctrl &= ~(WX_PSR_VLAN_CTL_VFE | WX_PSR_VLAN_CTL_CFIEN); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci /* set all bits that we expect to always be set */ 115462306a36Sopenharmony_ci fctrl |= WX_PSR_CTL_BAM | WX_PSR_CTL_MFE; 115562306a36Sopenharmony_ci vmolr |= WX_PSR_VM_L2CTL_BAM | 115662306a36Sopenharmony_ci WX_PSR_VM_L2CTL_AUPE | 115762306a36Sopenharmony_ci WX_PSR_VM_L2CTL_VACC; 115862306a36Sopenharmony_ci vlnctrl |= WX_PSR_VLAN_CTL_VFE; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci wx->addr_ctrl.user_set_promisc = false; 116162306a36Sopenharmony_ci if (netdev->flags & IFF_PROMISC) { 116262306a36Sopenharmony_ci wx->addr_ctrl.user_set_promisc = true; 116362306a36Sopenharmony_ci fctrl |= WX_PSR_CTL_UPE | WX_PSR_CTL_MPE; 116462306a36Sopenharmony_ci /* pf don't want packets routing to vf, so clear UPE */ 116562306a36Sopenharmony_ci vmolr |= WX_PSR_VM_L2CTL_MPE; 116662306a36Sopenharmony_ci vlnctrl &= ~WX_PSR_VLAN_CTL_VFE; 116762306a36Sopenharmony_ci } 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci if (netdev->flags & IFF_ALLMULTI) { 117062306a36Sopenharmony_ci fctrl |= WX_PSR_CTL_MPE; 117162306a36Sopenharmony_ci vmolr |= WX_PSR_VM_L2CTL_MPE; 117262306a36Sopenharmony_ci } 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci if (netdev->features & NETIF_F_RXALL) { 117562306a36Sopenharmony_ci vmolr |= (WX_PSR_VM_L2CTL_UPE | WX_PSR_VM_L2CTL_MPE); 117662306a36Sopenharmony_ci vlnctrl &= ~WX_PSR_VLAN_CTL_VFE; 117762306a36Sopenharmony_ci /* receive bad packets */ 117862306a36Sopenharmony_ci wr32m(wx, WX_RSC_CTL, 117962306a36Sopenharmony_ci WX_RSC_CTL_SAVE_MAC_ERR, 118062306a36Sopenharmony_ci WX_RSC_CTL_SAVE_MAC_ERR); 118162306a36Sopenharmony_ci } else { 118262306a36Sopenharmony_ci vmolr |= WX_PSR_VM_L2CTL_ROPE | WX_PSR_VM_L2CTL_ROMPE; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci /* Write addresses to available RAR registers, if there is not 118662306a36Sopenharmony_ci * sufficient space to store all the addresses then enable 118762306a36Sopenharmony_ci * unicast promiscuous mode 118862306a36Sopenharmony_ci */ 118962306a36Sopenharmony_ci count = wx_write_uc_addr_list(netdev, 0); 119062306a36Sopenharmony_ci if (count < 0) { 119162306a36Sopenharmony_ci vmolr &= ~WX_PSR_VM_L2CTL_ROPE; 119262306a36Sopenharmony_ci vmolr |= WX_PSR_VM_L2CTL_UPE; 119362306a36Sopenharmony_ci } 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci /* Write addresses to the MTA, if the attempt fails 119662306a36Sopenharmony_ci * then we should just turn on promiscuous mode so 119762306a36Sopenharmony_ci * that we can at least receive multicast traffic 119862306a36Sopenharmony_ci */ 119962306a36Sopenharmony_ci count = wx_write_mc_addr_list(netdev); 120062306a36Sopenharmony_ci if (count < 0) { 120162306a36Sopenharmony_ci vmolr &= ~WX_PSR_VM_L2CTL_ROMPE; 120262306a36Sopenharmony_ci vmolr |= WX_PSR_VM_L2CTL_MPE; 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci wr32(wx, WX_PSR_VLAN_CTL, vlnctrl); 120662306a36Sopenharmony_ci wr32(wx, WX_PSR_CTL, fctrl); 120762306a36Sopenharmony_ci wr32(wx, WX_PSR_VM_L2CTL(0), vmolr); 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci if ((features & NETIF_F_HW_VLAN_CTAG_RX) && 121062306a36Sopenharmony_ci (features & NETIF_F_HW_VLAN_STAG_RX)) 121162306a36Sopenharmony_ci wx_vlan_strip_control(wx, true); 121262306a36Sopenharmony_ci else 121362306a36Sopenharmony_ci wx_vlan_strip_control(wx, false); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci} 121662306a36Sopenharmony_ciEXPORT_SYMBOL(wx_set_rx_mode); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_cistatic void wx_set_rx_buffer_len(struct wx *wx) 121962306a36Sopenharmony_ci{ 122062306a36Sopenharmony_ci struct net_device *netdev = wx->netdev; 122162306a36Sopenharmony_ci u32 mhadd, max_frame; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci max_frame = netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; 122462306a36Sopenharmony_ci /* adjust max frame to be at least the size of a standard frame */ 122562306a36Sopenharmony_ci if (max_frame < (ETH_FRAME_LEN + ETH_FCS_LEN)) 122662306a36Sopenharmony_ci max_frame = (ETH_FRAME_LEN + ETH_FCS_LEN); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci mhadd = rd32(wx, WX_PSR_MAX_SZ); 122962306a36Sopenharmony_ci if (max_frame != mhadd) 123062306a36Sopenharmony_ci wr32(wx, WX_PSR_MAX_SZ, max_frame); 123162306a36Sopenharmony_ci} 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci/** 123462306a36Sopenharmony_ci * wx_change_mtu - Change the Maximum Transfer Unit 123562306a36Sopenharmony_ci * @netdev: network interface device structure 123662306a36Sopenharmony_ci * @new_mtu: new value for maximum frame size 123762306a36Sopenharmony_ci * 123862306a36Sopenharmony_ci * Returns 0 on success, negative on failure 123962306a36Sopenharmony_ci **/ 124062306a36Sopenharmony_ciint wx_change_mtu(struct net_device *netdev, int new_mtu) 124162306a36Sopenharmony_ci{ 124262306a36Sopenharmony_ci struct wx *wx = netdev_priv(netdev); 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci netdev->mtu = new_mtu; 124562306a36Sopenharmony_ci wx_set_rx_buffer_len(wx); 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci return 0; 124862306a36Sopenharmony_ci} 124962306a36Sopenharmony_ciEXPORT_SYMBOL(wx_change_mtu); 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci/* Disable the specified rx queue */ 125262306a36Sopenharmony_civoid wx_disable_rx_queue(struct wx *wx, struct wx_ring *ring) 125362306a36Sopenharmony_ci{ 125462306a36Sopenharmony_ci u8 reg_idx = ring->reg_idx; 125562306a36Sopenharmony_ci u32 rxdctl; 125662306a36Sopenharmony_ci int ret; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci /* write value back with RRCFG.EN bit cleared */ 125962306a36Sopenharmony_ci wr32m(wx, WX_PX_RR_CFG(reg_idx), 126062306a36Sopenharmony_ci WX_PX_RR_CFG_RR_EN, 0); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci /* the hardware may take up to 100us to really disable the rx queue */ 126362306a36Sopenharmony_ci ret = read_poll_timeout(rd32, rxdctl, !(rxdctl & WX_PX_RR_CFG_RR_EN), 126462306a36Sopenharmony_ci 10, 100, true, wx, WX_PX_RR_CFG(reg_idx)); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci if (ret == -ETIMEDOUT) { 126762306a36Sopenharmony_ci /* Just for information */ 126862306a36Sopenharmony_ci wx_err(wx, 126962306a36Sopenharmony_ci "RRCFG.EN on Rx queue %d not cleared within the polling period\n", 127062306a36Sopenharmony_ci reg_idx); 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ciEXPORT_SYMBOL(wx_disable_rx_queue); 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_cistatic void wx_enable_rx_queue(struct wx *wx, struct wx_ring *ring) 127662306a36Sopenharmony_ci{ 127762306a36Sopenharmony_ci u8 reg_idx = ring->reg_idx; 127862306a36Sopenharmony_ci u32 rxdctl; 127962306a36Sopenharmony_ci int ret; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci ret = read_poll_timeout(rd32, rxdctl, rxdctl & WX_PX_RR_CFG_RR_EN, 128262306a36Sopenharmony_ci 1000, 10000, true, wx, WX_PX_RR_CFG(reg_idx)); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci if (ret == -ETIMEDOUT) { 128562306a36Sopenharmony_ci /* Just for information */ 128662306a36Sopenharmony_ci wx_err(wx, 128762306a36Sopenharmony_ci "RRCFG.EN on Rx queue %d not set within the polling period\n", 128862306a36Sopenharmony_ci reg_idx); 128962306a36Sopenharmony_ci } 129062306a36Sopenharmony_ci} 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_cistatic void wx_configure_srrctl(struct wx *wx, 129362306a36Sopenharmony_ci struct wx_ring *rx_ring) 129462306a36Sopenharmony_ci{ 129562306a36Sopenharmony_ci u16 reg_idx = rx_ring->reg_idx; 129662306a36Sopenharmony_ci u32 srrctl; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci srrctl = rd32(wx, WX_PX_RR_CFG(reg_idx)); 129962306a36Sopenharmony_ci srrctl &= ~(WX_PX_RR_CFG_RR_HDR_SZ | 130062306a36Sopenharmony_ci WX_PX_RR_CFG_RR_BUF_SZ | 130162306a36Sopenharmony_ci WX_PX_RR_CFG_SPLIT_MODE); 130262306a36Sopenharmony_ci /* configure header buffer length, needed for RSC */ 130362306a36Sopenharmony_ci srrctl |= WX_RXBUFFER_256 << WX_PX_RR_CFG_BHDRSIZE_SHIFT; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci /* configure the packet buffer length */ 130662306a36Sopenharmony_ci srrctl |= WX_RX_BUFSZ >> WX_PX_RR_CFG_BSIZEPKT_SHIFT; 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci wr32(wx, WX_PX_RR_CFG(reg_idx), srrctl); 130962306a36Sopenharmony_ci} 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_cistatic void wx_configure_tx_ring(struct wx *wx, 131262306a36Sopenharmony_ci struct wx_ring *ring) 131362306a36Sopenharmony_ci{ 131462306a36Sopenharmony_ci u32 txdctl = WX_PX_TR_CFG_ENABLE; 131562306a36Sopenharmony_ci u8 reg_idx = ring->reg_idx; 131662306a36Sopenharmony_ci u64 tdba = ring->dma; 131762306a36Sopenharmony_ci int ret; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci /* disable queue to avoid issues while updating state */ 132062306a36Sopenharmony_ci wr32(wx, WX_PX_TR_CFG(reg_idx), WX_PX_TR_CFG_SWFLSH); 132162306a36Sopenharmony_ci WX_WRITE_FLUSH(wx); 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci wr32(wx, WX_PX_TR_BAL(reg_idx), tdba & DMA_BIT_MASK(32)); 132462306a36Sopenharmony_ci wr32(wx, WX_PX_TR_BAH(reg_idx), upper_32_bits(tdba)); 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci /* reset head and tail pointers */ 132762306a36Sopenharmony_ci wr32(wx, WX_PX_TR_RP(reg_idx), 0); 132862306a36Sopenharmony_ci wr32(wx, WX_PX_TR_WP(reg_idx), 0); 132962306a36Sopenharmony_ci ring->tail = wx->hw_addr + WX_PX_TR_WP(reg_idx); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci if (ring->count < WX_MAX_TXD) 133262306a36Sopenharmony_ci txdctl |= ring->count / 128 << WX_PX_TR_CFG_TR_SIZE_SHIFT; 133362306a36Sopenharmony_ci txdctl |= 0x20 << WX_PX_TR_CFG_WTHRESH_SHIFT; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci /* reinitialize tx_buffer_info */ 133662306a36Sopenharmony_ci memset(ring->tx_buffer_info, 0, 133762306a36Sopenharmony_ci sizeof(struct wx_tx_buffer) * ring->count); 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci /* enable queue */ 134062306a36Sopenharmony_ci wr32(wx, WX_PX_TR_CFG(reg_idx), txdctl); 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci /* poll to verify queue is enabled */ 134362306a36Sopenharmony_ci ret = read_poll_timeout(rd32, txdctl, txdctl & WX_PX_TR_CFG_ENABLE, 134462306a36Sopenharmony_ci 1000, 10000, true, wx, WX_PX_TR_CFG(reg_idx)); 134562306a36Sopenharmony_ci if (ret == -ETIMEDOUT) 134662306a36Sopenharmony_ci wx_err(wx, "Could not enable Tx Queue %d\n", reg_idx); 134762306a36Sopenharmony_ci} 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_cistatic void wx_configure_rx_ring(struct wx *wx, 135062306a36Sopenharmony_ci struct wx_ring *ring) 135162306a36Sopenharmony_ci{ 135262306a36Sopenharmony_ci u16 reg_idx = ring->reg_idx; 135362306a36Sopenharmony_ci union wx_rx_desc *rx_desc; 135462306a36Sopenharmony_ci u64 rdba = ring->dma; 135562306a36Sopenharmony_ci u32 rxdctl; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci /* disable queue to avoid issues while updating state */ 135862306a36Sopenharmony_ci rxdctl = rd32(wx, WX_PX_RR_CFG(reg_idx)); 135962306a36Sopenharmony_ci wx_disable_rx_queue(wx, ring); 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci wr32(wx, WX_PX_RR_BAL(reg_idx), rdba & DMA_BIT_MASK(32)); 136262306a36Sopenharmony_ci wr32(wx, WX_PX_RR_BAH(reg_idx), upper_32_bits(rdba)); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci if (ring->count == WX_MAX_RXD) 136562306a36Sopenharmony_ci rxdctl |= 0 << WX_PX_RR_CFG_RR_SIZE_SHIFT; 136662306a36Sopenharmony_ci else 136762306a36Sopenharmony_ci rxdctl |= (ring->count / 128) << WX_PX_RR_CFG_RR_SIZE_SHIFT; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci rxdctl |= 0x1 << WX_PX_RR_CFG_RR_THER_SHIFT; 137062306a36Sopenharmony_ci wr32(wx, WX_PX_RR_CFG(reg_idx), rxdctl); 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci /* reset head and tail pointers */ 137362306a36Sopenharmony_ci wr32(wx, WX_PX_RR_RP(reg_idx), 0); 137462306a36Sopenharmony_ci wr32(wx, WX_PX_RR_WP(reg_idx), 0); 137562306a36Sopenharmony_ci ring->tail = wx->hw_addr + WX_PX_RR_WP(reg_idx); 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci wx_configure_srrctl(wx, ring); 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci /* initialize rx_buffer_info */ 138062306a36Sopenharmony_ci memset(ring->rx_buffer_info, 0, 138162306a36Sopenharmony_ci sizeof(struct wx_rx_buffer) * ring->count); 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci /* initialize Rx descriptor 0 */ 138462306a36Sopenharmony_ci rx_desc = WX_RX_DESC(ring, 0); 138562306a36Sopenharmony_ci rx_desc->wb.upper.length = 0; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci /* enable receive descriptor ring */ 138862306a36Sopenharmony_ci wr32m(wx, WX_PX_RR_CFG(reg_idx), 138962306a36Sopenharmony_ci WX_PX_RR_CFG_RR_EN, WX_PX_RR_CFG_RR_EN); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci wx_enable_rx_queue(wx, ring); 139262306a36Sopenharmony_ci wx_alloc_rx_buffers(ring, wx_desc_unused(ring)); 139362306a36Sopenharmony_ci} 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci/** 139662306a36Sopenharmony_ci * wx_configure_tx - Configure Transmit Unit after Reset 139762306a36Sopenharmony_ci * @wx: pointer to private structure 139862306a36Sopenharmony_ci * 139962306a36Sopenharmony_ci * Configure the Tx unit of the MAC after a reset. 140062306a36Sopenharmony_ci **/ 140162306a36Sopenharmony_cistatic void wx_configure_tx(struct wx *wx) 140262306a36Sopenharmony_ci{ 140362306a36Sopenharmony_ci u32 i; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci /* TDM_CTL.TE must be before Tx queues are enabled */ 140662306a36Sopenharmony_ci wr32m(wx, WX_TDM_CTL, 140762306a36Sopenharmony_ci WX_TDM_CTL_TE, WX_TDM_CTL_TE); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci /* Setup the HW Tx Head and Tail descriptor pointers */ 141062306a36Sopenharmony_ci for (i = 0; i < wx->num_tx_queues; i++) 141162306a36Sopenharmony_ci wx_configure_tx_ring(wx, wx->tx_ring[i]); 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci wr32m(wx, WX_TSC_BUF_AE, WX_TSC_BUF_AE_THR, 0x10); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci if (wx->mac.type == wx_mac_em) 141662306a36Sopenharmony_ci wr32m(wx, WX_TSC_CTL, WX_TSC_CTL_TX_DIS | WX_TSC_CTL_TSEC_DIS, 0x1); 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci /* enable mac transmitter */ 141962306a36Sopenharmony_ci wr32m(wx, WX_MAC_TX_CFG, 142062306a36Sopenharmony_ci WX_MAC_TX_CFG_TE, WX_MAC_TX_CFG_TE); 142162306a36Sopenharmony_ci} 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_cistatic void wx_restore_vlan(struct wx *wx) 142462306a36Sopenharmony_ci{ 142562306a36Sopenharmony_ci u16 vid = 1; 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci wx_vlan_rx_add_vid(wx->netdev, htons(ETH_P_8021Q), 0); 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci for_each_set_bit_from(vid, wx->active_vlans, VLAN_N_VID) 143062306a36Sopenharmony_ci wx_vlan_rx_add_vid(wx->netdev, htons(ETH_P_8021Q), vid); 143162306a36Sopenharmony_ci} 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci/** 143462306a36Sopenharmony_ci * wx_configure_rx - Configure Receive Unit after Reset 143562306a36Sopenharmony_ci * @wx: pointer to private structure 143662306a36Sopenharmony_ci * 143762306a36Sopenharmony_ci * Configure the Rx unit of the MAC after a reset. 143862306a36Sopenharmony_ci **/ 143962306a36Sopenharmony_civoid wx_configure_rx(struct wx *wx) 144062306a36Sopenharmony_ci{ 144162306a36Sopenharmony_ci u32 psrtype, i; 144262306a36Sopenharmony_ci int ret; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci wx_disable_rx(wx); 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci psrtype = WX_RDB_PL_CFG_L4HDR | 144762306a36Sopenharmony_ci WX_RDB_PL_CFG_L3HDR | 144862306a36Sopenharmony_ci WX_RDB_PL_CFG_L2HDR | 144962306a36Sopenharmony_ci WX_RDB_PL_CFG_TUN_TUNHDR; 145062306a36Sopenharmony_ci wr32(wx, WX_RDB_PL_CFG(0), psrtype); 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci /* enable hw crc stripping */ 145362306a36Sopenharmony_ci wr32m(wx, WX_RSC_CTL, WX_RSC_CTL_CRC_STRIP, WX_RSC_CTL_CRC_STRIP); 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci if (wx->mac.type == wx_mac_sp) { 145662306a36Sopenharmony_ci u32 psrctl; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci /* RSC Setup */ 145962306a36Sopenharmony_ci psrctl = rd32(wx, WX_PSR_CTL); 146062306a36Sopenharmony_ci psrctl |= WX_PSR_CTL_RSC_ACK; /* Disable RSC for ACK packets */ 146162306a36Sopenharmony_ci psrctl |= WX_PSR_CTL_RSC_DIS; 146262306a36Sopenharmony_ci wr32(wx, WX_PSR_CTL, psrctl); 146362306a36Sopenharmony_ci } 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci /* set_rx_buffer_len must be called before ring initialization */ 146662306a36Sopenharmony_ci wx_set_rx_buffer_len(wx); 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci /* Setup the HW Rx Head and Tail Descriptor Pointers and 146962306a36Sopenharmony_ci * the Base and Length of the Rx Descriptor Ring 147062306a36Sopenharmony_ci */ 147162306a36Sopenharmony_ci for (i = 0; i < wx->num_rx_queues; i++) 147262306a36Sopenharmony_ci wx_configure_rx_ring(wx, wx->rx_ring[i]); 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci /* Enable all receives, disable security engine prior to block traffic */ 147562306a36Sopenharmony_ci ret = wx_disable_sec_rx_path(wx); 147662306a36Sopenharmony_ci if (ret < 0) 147762306a36Sopenharmony_ci wx_err(wx, "The register status is abnormal, please check device."); 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci wx_enable_rx(wx); 148062306a36Sopenharmony_ci wx_enable_sec_rx_path(wx); 148162306a36Sopenharmony_ci} 148262306a36Sopenharmony_ciEXPORT_SYMBOL(wx_configure_rx); 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_cistatic void wx_configure_isb(struct wx *wx) 148562306a36Sopenharmony_ci{ 148662306a36Sopenharmony_ci /* set ISB Address */ 148762306a36Sopenharmony_ci wr32(wx, WX_PX_ISB_ADDR_L, wx->isb_dma & DMA_BIT_MASK(32)); 148862306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT)) 148962306a36Sopenharmony_ci wr32(wx, WX_PX_ISB_ADDR_H, upper_32_bits(wx->isb_dma)); 149062306a36Sopenharmony_ci} 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_civoid wx_configure(struct wx *wx) 149362306a36Sopenharmony_ci{ 149462306a36Sopenharmony_ci wx_set_rxpba(wx); 149562306a36Sopenharmony_ci wx_configure_port(wx); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci wx_set_rx_mode(wx->netdev); 149862306a36Sopenharmony_ci wx_restore_vlan(wx); 149962306a36Sopenharmony_ci wx_enable_sec_rx_path(wx); 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci wx_configure_tx(wx); 150262306a36Sopenharmony_ci wx_configure_rx(wx); 150362306a36Sopenharmony_ci wx_configure_isb(wx); 150462306a36Sopenharmony_ci} 150562306a36Sopenharmony_ciEXPORT_SYMBOL(wx_configure); 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci/** 150862306a36Sopenharmony_ci * wx_disable_pcie_master - Disable PCI-express master access 150962306a36Sopenharmony_ci * @wx: pointer to hardware structure 151062306a36Sopenharmony_ci * 151162306a36Sopenharmony_ci * Disables PCI-Express master access and verifies there are no pending 151262306a36Sopenharmony_ci * requests. 151362306a36Sopenharmony_ci **/ 151462306a36Sopenharmony_ciint wx_disable_pcie_master(struct wx *wx) 151562306a36Sopenharmony_ci{ 151662306a36Sopenharmony_ci int status = 0; 151762306a36Sopenharmony_ci u32 val; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci /* Always set this bit to ensure any future transactions are blocked */ 152062306a36Sopenharmony_ci pci_clear_master(wx->pdev); 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci /* Exit if master requests are blocked */ 152362306a36Sopenharmony_ci if (!(rd32(wx, WX_PX_TRANSACTION_PENDING))) 152462306a36Sopenharmony_ci return 0; 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci /* Poll for master request bit to clear */ 152762306a36Sopenharmony_ci status = read_poll_timeout(rd32, val, !val, 100, WX_PCI_MASTER_DISABLE_TIMEOUT, 152862306a36Sopenharmony_ci false, wx, WX_PX_TRANSACTION_PENDING); 152962306a36Sopenharmony_ci if (status < 0) 153062306a36Sopenharmony_ci wx_err(wx, "PCIe transaction pending bit did not clear.\n"); 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci return status; 153362306a36Sopenharmony_ci} 153462306a36Sopenharmony_ciEXPORT_SYMBOL(wx_disable_pcie_master); 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci/** 153762306a36Sopenharmony_ci * wx_stop_adapter - Generic stop Tx/Rx units 153862306a36Sopenharmony_ci * @wx: pointer to hardware structure 153962306a36Sopenharmony_ci * 154062306a36Sopenharmony_ci * Sets the adapter_stopped flag within wx_hw struct. Clears interrupts, 154162306a36Sopenharmony_ci * disables transmit and receive units. The adapter_stopped flag is used by 154262306a36Sopenharmony_ci * the shared code and drivers to determine if the adapter is in a stopped 154362306a36Sopenharmony_ci * state and should not touch the hardware. 154462306a36Sopenharmony_ci **/ 154562306a36Sopenharmony_ciint wx_stop_adapter(struct wx *wx) 154662306a36Sopenharmony_ci{ 154762306a36Sopenharmony_ci u16 i; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci /* Set the adapter_stopped flag so other driver functions stop touching 155062306a36Sopenharmony_ci * the hardware 155162306a36Sopenharmony_ci */ 155262306a36Sopenharmony_ci wx->adapter_stopped = true; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci /* Disable the receive unit */ 155562306a36Sopenharmony_ci wx_disable_rx(wx); 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci /* Set interrupt mask to stop interrupts from being generated */ 155862306a36Sopenharmony_ci wx_intr_disable(wx, WX_INTR_ALL); 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci /* Clear any pending interrupts, flush previous writes */ 156162306a36Sopenharmony_ci wr32(wx, WX_PX_MISC_IC, 0xffffffff); 156262306a36Sopenharmony_ci wr32(wx, WX_BME_CTL, 0x3); 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci /* Disable the transmit unit. Each queue must be disabled. */ 156562306a36Sopenharmony_ci for (i = 0; i < wx->mac.max_tx_queues; i++) { 156662306a36Sopenharmony_ci wr32m(wx, WX_PX_TR_CFG(i), 156762306a36Sopenharmony_ci WX_PX_TR_CFG_SWFLSH | WX_PX_TR_CFG_ENABLE, 156862306a36Sopenharmony_ci WX_PX_TR_CFG_SWFLSH); 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci /* Disable the receive unit by stopping each queue */ 157262306a36Sopenharmony_ci for (i = 0; i < wx->mac.max_rx_queues; i++) { 157362306a36Sopenharmony_ci wr32m(wx, WX_PX_RR_CFG(i), 157462306a36Sopenharmony_ci WX_PX_RR_CFG_RR_EN, 0); 157562306a36Sopenharmony_ci } 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci /* flush all queues disables */ 157862306a36Sopenharmony_ci WX_WRITE_FLUSH(wx); 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci /* Prevent the PCI-E bus from hanging by disabling PCI-E master 158162306a36Sopenharmony_ci * access and verify no pending requests 158262306a36Sopenharmony_ci */ 158362306a36Sopenharmony_ci return wx_disable_pcie_master(wx); 158462306a36Sopenharmony_ci} 158562306a36Sopenharmony_ciEXPORT_SYMBOL(wx_stop_adapter); 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_civoid wx_reset_misc(struct wx *wx) 158862306a36Sopenharmony_ci{ 158962306a36Sopenharmony_ci int i; 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci /* receive packets that size > 2048 */ 159262306a36Sopenharmony_ci wr32m(wx, WX_MAC_RX_CFG, WX_MAC_RX_CFG_JE, WX_MAC_RX_CFG_JE); 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci /* clear counters on read */ 159562306a36Sopenharmony_ci wr32m(wx, WX_MMC_CONTROL, 159662306a36Sopenharmony_ci WX_MMC_CONTROL_RSTONRD, WX_MMC_CONTROL_RSTONRD); 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci wr32m(wx, WX_MAC_RX_FLOW_CTRL, 159962306a36Sopenharmony_ci WX_MAC_RX_FLOW_CTRL_RFE, WX_MAC_RX_FLOW_CTRL_RFE); 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci wr32(wx, WX_MAC_PKT_FLT, WX_MAC_PKT_FLT_PR); 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci wr32m(wx, WX_MIS_RST_ST, 160462306a36Sopenharmony_ci WX_MIS_RST_ST_RST_INIT, 0x1E00); 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci /* errata 4: initialize mng flex tbl and wakeup flex tbl*/ 160762306a36Sopenharmony_ci wr32(wx, WX_PSR_MNG_FLEX_SEL, 0); 160862306a36Sopenharmony_ci for (i = 0; i < 16; i++) { 160962306a36Sopenharmony_ci wr32(wx, WX_PSR_MNG_FLEX_DW_L(i), 0); 161062306a36Sopenharmony_ci wr32(wx, WX_PSR_MNG_FLEX_DW_H(i), 0); 161162306a36Sopenharmony_ci wr32(wx, WX_PSR_MNG_FLEX_MSK(i), 0); 161262306a36Sopenharmony_ci } 161362306a36Sopenharmony_ci wr32(wx, WX_PSR_LAN_FLEX_SEL, 0); 161462306a36Sopenharmony_ci for (i = 0; i < 16; i++) { 161562306a36Sopenharmony_ci wr32(wx, WX_PSR_LAN_FLEX_DW_L(i), 0); 161662306a36Sopenharmony_ci wr32(wx, WX_PSR_LAN_FLEX_DW_H(i), 0); 161762306a36Sopenharmony_ci wr32(wx, WX_PSR_LAN_FLEX_MSK(i), 0); 161862306a36Sopenharmony_ci } 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci /* set pause frame dst mac addr */ 162162306a36Sopenharmony_ci wr32(wx, WX_RDB_PFCMACDAL, 0xC2000001); 162262306a36Sopenharmony_ci wr32(wx, WX_RDB_PFCMACDAH, 0x0180); 162362306a36Sopenharmony_ci} 162462306a36Sopenharmony_ciEXPORT_SYMBOL(wx_reset_misc); 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci/** 162762306a36Sopenharmony_ci * wx_get_pcie_msix_counts - Gets MSI-X vector count 162862306a36Sopenharmony_ci * @wx: pointer to hardware structure 162962306a36Sopenharmony_ci * @msix_count: number of MSI interrupts that can be obtained 163062306a36Sopenharmony_ci * @max_msix_count: number of MSI interrupts that mac need 163162306a36Sopenharmony_ci * 163262306a36Sopenharmony_ci * Read PCIe configuration space, and get the MSI-X vector count from 163362306a36Sopenharmony_ci * the capabilities table. 163462306a36Sopenharmony_ci **/ 163562306a36Sopenharmony_ciint wx_get_pcie_msix_counts(struct wx *wx, u16 *msix_count, u16 max_msix_count) 163662306a36Sopenharmony_ci{ 163762306a36Sopenharmony_ci struct pci_dev *pdev = wx->pdev; 163862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 163962306a36Sopenharmony_ci int pos; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci *msix_count = 1; 164262306a36Sopenharmony_ci pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX); 164362306a36Sopenharmony_ci if (!pos) { 164462306a36Sopenharmony_ci dev_err(dev, "Unable to find MSI-X Capabilities\n"); 164562306a36Sopenharmony_ci return -EINVAL; 164662306a36Sopenharmony_ci } 164762306a36Sopenharmony_ci pci_read_config_word(pdev, 164862306a36Sopenharmony_ci pos + PCI_MSIX_FLAGS, 164962306a36Sopenharmony_ci msix_count); 165062306a36Sopenharmony_ci *msix_count &= WX_PCIE_MSIX_TBL_SZ_MASK; 165162306a36Sopenharmony_ci /* MSI-X count is zero-based in HW */ 165262306a36Sopenharmony_ci *msix_count += 1; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci if (*msix_count > max_msix_count) 165562306a36Sopenharmony_ci *msix_count = max_msix_count; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci return 0; 165862306a36Sopenharmony_ci} 165962306a36Sopenharmony_ciEXPORT_SYMBOL(wx_get_pcie_msix_counts); 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ciint wx_sw_init(struct wx *wx) 166262306a36Sopenharmony_ci{ 166362306a36Sopenharmony_ci struct pci_dev *pdev = wx->pdev; 166462306a36Sopenharmony_ci u32 ssid = 0; 166562306a36Sopenharmony_ci int err = 0; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci wx->vendor_id = pdev->vendor; 166862306a36Sopenharmony_ci wx->device_id = pdev->device; 166962306a36Sopenharmony_ci wx->revision_id = pdev->revision; 167062306a36Sopenharmony_ci wx->oem_svid = pdev->subsystem_vendor; 167162306a36Sopenharmony_ci wx->oem_ssid = pdev->subsystem_device; 167262306a36Sopenharmony_ci wx->bus.device = PCI_SLOT(pdev->devfn); 167362306a36Sopenharmony_ci wx->bus.func = PCI_FUNC(pdev->devfn); 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci if (wx->oem_svid == PCI_VENDOR_ID_WANGXUN) { 167662306a36Sopenharmony_ci wx->subsystem_vendor_id = pdev->subsystem_vendor; 167762306a36Sopenharmony_ci wx->subsystem_device_id = pdev->subsystem_device; 167862306a36Sopenharmony_ci } else { 167962306a36Sopenharmony_ci err = wx_flash_read_dword(wx, 0xfffdc, &ssid); 168062306a36Sopenharmony_ci if (err < 0) { 168162306a36Sopenharmony_ci wx_err(wx, "read of internal subsystem device id failed\n"); 168262306a36Sopenharmony_ci return err; 168362306a36Sopenharmony_ci } 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci wx->subsystem_device_id = swab16((u16)ssid); 168662306a36Sopenharmony_ci } 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci wx->mac_table = kcalloc(wx->mac.num_rar_entries, 168962306a36Sopenharmony_ci sizeof(struct wx_mac_addr), 169062306a36Sopenharmony_ci GFP_KERNEL); 169162306a36Sopenharmony_ci if (!wx->mac_table) { 169262306a36Sopenharmony_ci wx_err(wx, "mac_table allocation failed\n"); 169362306a36Sopenharmony_ci return -ENOMEM; 169462306a36Sopenharmony_ci } 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci return 0; 169762306a36Sopenharmony_ci} 169862306a36Sopenharmony_ciEXPORT_SYMBOL(wx_sw_init); 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci/** 170162306a36Sopenharmony_ci * wx_find_vlvf_slot - find the vlanid or the first empty slot 170262306a36Sopenharmony_ci * @wx: pointer to hardware structure 170362306a36Sopenharmony_ci * @vlan: VLAN id to write to VLAN filter 170462306a36Sopenharmony_ci * 170562306a36Sopenharmony_ci * return the VLVF index where this VLAN id should be placed 170662306a36Sopenharmony_ci * 170762306a36Sopenharmony_ci **/ 170862306a36Sopenharmony_cistatic int wx_find_vlvf_slot(struct wx *wx, u32 vlan) 170962306a36Sopenharmony_ci{ 171062306a36Sopenharmony_ci u32 bits = 0, first_empty_slot = 0; 171162306a36Sopenharmony_ci int regindex; 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci /* short cut the special case */ 171462306a36Sopenharmony_ci if (vlan == 0) 171562306a36Sopenharmony_ci return 0; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci /* Search for the vlan id in the VLVF entries. Save off the first empty 171862306a36Sopenharmony_ci * slot found along the way 171962306a36Sopenharmony_ci */ 172062306a36Sopenharmony_ci for (regindex = 1; regindex < WX_PSR_VLAN_SWC_ENTRIES; regindex++) { 172162306a36Sopenharmony_ci wr32(wx, WX_PSR_VLAN_SWC_IDX, regindex); 172262306a36Sopenharmony_ci bits = rd32(wx, WX_PSR_VLAN_SWC); 172362306a36Sopenharmony_ci if (!bits && !(first_empty_slot)) 172462306a36Sopenharmony_ci first_empty_slot = regindex; 172562306a36Sopenharmony_ci else if ((bits & 0x0FFF) == vlan) 172662306a36Sopenharmony_ci break; 172762306a36Sopenharmony_ci } 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci if (regindex >= WX_PSR_VLAN_SWC_ENTRIES) { 173062306a36Sopenharmony_ci if (first_empty_slot) 173162306a36Sopenharmony_ci regindex = first_empty_slot; 173262306a36Sopenharmony_ci else 173362306a36Sopenharmony_ci regindex = -ENOMEM; 173462306a36Sopenharmony_ci } 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci return regindex; 173762306a36Sopenharmony_ci} 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci/** 174062306a36Sopenharmony_ci * wx_set_vlvf - Set VLAN Pool Filter 174162306a36Sopenharmony_ci * @wx: pointer to hardware structure 174262306a36Sopenharmony_ci * @vlan: VLAN id to write to VLAN filter 174362306a36Sopenharmony_ci * @vind: VMDq output index that maps queue to VLAN id in VFVFB 174462306a36Sopenharmony_ci * @vlan_on: boolean flag to turn on/off VLAN in VFVF 174562306a36Sopenharmony_ci * @vfta_changed: pointer to boolean flag which indicates whether VFTA 174662306a36Sopenharmony_ci * should be changed 174762306a36Sopenharmony_ci * 174862306a36Sopenharmony_ci * Turn on/off specified bit in VLVF table. 174962306a36Sopenharmony_ci **/ 175062306a36Sopenharmony_cistatic int wx_set_vlvf(struct wx *wx, u32 vlan, u32 vind, bool vlan_on, 175162306a36Sopenharmony_ci bool *vfta_changed) 175262306a36Sopenharmony_ci{ 175362306a36Sopenharmony_ci int vlvf_index; 175462306a36Sopenharmony_ci u32 vt, bits; 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci /* If VT Mode is set 175762306a36Sopenharmony_ci * Either vlan_on 175862306a36Sopenharmony_ci * make sure the vlan is in VLVF 175962306a36Sopenharmony_ci * set the vind bit in the matching VLVFB 176062306a36Sopenharmony_ci * Or !vlan_on 176162306a36Sopenharmony_ci * clear the pool bit and possibly the vind 176262306a36Sopenharmony_ci */ 176362306a36Sopenharmony_ci vt = rd32(wx, WX_CFG_PORT_CTL); 176462306a36Sopenharmony_ci if (!(vt & WX_CFG_PORT_CTL_NUM_VT_MASK)) 176562306a36Sopenharmony_ci return 0; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci vlvf_index = wx_find_vlvf_slot(wx, vlan); 176862306a36Sopenharmony_ci if (vlvf_index < 0) 176962306a36Sopenharmony_ci return vlvf_index; 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci wr32(wx, WX_PSR_VLAN_SWC_IDX, vlvf_index); 177262306a36Sopenharmony_ci if (vlan_on) { 177362306a36Sopenharmony_ci /* set the pool bit */ 177462306a36Sopenharmony_ci if (vind < 32) { 177562306a36Sopenharmony_ci bits = rd32(wx, WX_PSR_VLAN_SWC_VM_L); 177662306a36Sopenharmony_ci bits |= (1 << vind); 177762306a36Sopenharmony_ci wr32(wx, WX_PSR_VLAN_SWC_VM_L, bits); 177862306a36Sopenharmony_ci } else { 177962306a36Sopenharmony_ci bits = rd32(wx, WX_PSR_VLAN_SWC_VM_H); 178062306a36Sopenharmony_ci bits |= (1 << (vind - 32)); 178162306a36Sopenharmony_ci wr32(wx, WX_PSR_VLAN_SWC_VM_H, bits); 178262306a36Sopenharmony_ci } 178362306a36Sopenharmony_ci } else { 178462306a36Sopenharmony_ci /* clear the pool bit */ 178562306a36Sopenharmony_ci if (vind < 32) { 178662306a36Sopenharmony_ci bits = rd32(wx, WX_PSR_VLAN_SWC_VM_L); 178762306a36Sopenharmony_ci bits &= ~(1 << vind); 178862306a36Sopenharmony_ci wr32(wx, WX_PSR_VLAN_SWC_VM_L, bits); 178962306a36Sopenharmony_ci bits |= rd32(wx, WX_PSR_VLAN_SWC_VM_H); 179062306a36Sopenharmony_ci } else { 179162306a36Sopenharmony_ci bits = rd32(wx, WX_PSR_VLAN_SWC_VM_H); 179262306a36Sopenharmony_ci bits &= ~(1 << (vind - 32)); 179362306a36Sopenharmony_ci wr32(wx, WX_PSR_VLAN_SWC_VM_H, bits); 179462306a36Sopenharmony_ci bits |= rd32(wx, WX_PSR_VLAN_SWC_VM_L); 179562306a36Sopenharmony_ci } 179662306a36Sopenharmony_ci } 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci if (bits) { 179962306a36Sopenharmony_ci wr32(wx, WX_PSR_VLAN_SWC, (WX_PSR_VLAN_SWC_VIEN | vlan)); 180062306a36Sopenharmony_ci if (!vlan_on && vfta_changed) 180162306a36Sopenharmony_ci *vfta_changed = false; 180262306a36Sopenharmony_ci } else { 180362306a36Sopenharmony_ci wr32(wx, WX_PSR_VLAN_SWC, 0); 180462306a36Sopenharmony_ci } 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci return 0; 180762306a36Sopenharmony_ci} 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci/** 181062306a36Sopenharmony_ci * wx_set_vfta - Set VLAN filter table 181162306a36Sopenharmony_ci * @wx: pointer to hardware structure 181262306a36Sopenharmony_ci * @vlan: VLAN id to write to VLAN filter 181362306a36Sopenharmony_ci * @vind: VMDq output index that maps queue to VLAN id in VFVFB 181462306a36Sopenharmony_ci * @vlan_on: boolean flag to turn on/off VLAN in VFVF 181562306a36Sopenharmony_ci * 181662306a36Sopenharmony_ci * Turn on/off specified VLAN in the VLAN filter table. 181762306a36Sopenharmony_ci **/ 181862306a36Sopenharmony_cistatic int wx_set_vfta(struct wx *wx, u32 vlan, u32 vind, bool vlan_on) 181962306a36Sopenharmony_ci{ 182062306a36Sopenharmony_ci u32 bitindex, vfta, targetbit; 182162306a36Sopenharmony_ci bool vfta_changed = false; 182262306a36Sopenharmony_ci int regindex, ret; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci /* this is a 2 part operation - first the VFTA, then the 182562306a36Sopenharmony_ci * VLVF and VLVFB if VT Mode is set 182662306a36Sopenharmony_ci * We don't write the VFTA until we know the VLVF part succeeded. 182762306a36Sopenharmony_ci */ 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci /* Part 1 183062306a36Sopenharmony_ci * The VFTA is a bitstring made up of 128 32-bit registers 183162306a36Sopenharmony_ci * that enable the particular VLAN id, much like the MTA: 183262306a36Sopenharmony_ci * bits[11-5]: which register 183362306a36Sopenharmony_ci * bits[4-0]: which bit in the register 183462306a36Sopenharmony_ci */ 183562306a36Sopenharmony_ci regindex = (vlan >> 5) & 0x7F; 183662306a36Sopenharmony_ci bitindex = vlan & 0x1F; 183762306a36Sopenharmony_ci targetbit = (1 << bitindex); 183862306a36Sopenharmony_ci /* errata 5 */ 183962306a36Sopenharmony_ci vfta = wx->mac.vft_shadow[regindex]; 184062306a36Sopenharmony_ci if (vlan_on) { 184162306a36Sopenharmony_ci if (!(vfta & targetbit)) { 184262306a36Sopenharmony_ci vfta |= targetbit; 184362306a36Sopenharmony_ci vfta_changed = true; 184462306a36Sopenharmony_ci } 184562306a36Sopenharmony_ci } else { 184662306a36Sopenharmony_ci if ((vfta & targetbit)) { 184762306a36Sopenharmony_ci vfta &= ~targetbit; 184862306a36Sopenharmony_ci vfta_changed = true; 184962306a36Sopenharmony_ci } 185062306a36Sopenharmony_ci } 185162306a36Sopenharmony_ci /* Part 2 185262306a36Sopenharmony_ci * Call wx_set_vlvf to set VLVFB and VLVF 185362306a36Sopenharmony_ci */ 185462306a36Sopenharmony_ci ret = wx_set_vlvf(wx, vlan, vind, vlan_on, &vfta_changed); 185562306a36Sopenharmony_ci if (ret != 0) 185662306a36Sopenharmony_ci return ret; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci if (vfta_changed) 185962306a36Sopenharmony_ci wr32(wx, WX_PSR_VLAN_TBL(regindex), vfta); 186062306a36Sopenharmony_ci wx->mac.vft_shadow[regindex] = vfta; 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci return 0; 186362306a36Sopenharmony_ci} 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci/** 186662306a36Sopenharmony_ci * wx_clear_vfta - Clear VLAN filter table 186762306a36Sopenharmony_ci * @wx: pointer to hardware structure 186862306a36Sopenharmony_ci * 186962306a36Sopenharmony_ci * Clears the VLAN filer table, and the VMDq index associated with the filter 187062306a36Sopenharmony_ci **/ 187162306a36Sopenharmony_cistatic void wx_clear_vfta(struct wx *wx) 187262306a36Sopenharmony_ci{ 187362306a36Sopenharmony_ci u32 offset; 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci for (offset = 0; offset < wx->mac.vft_size; offset++) { 187662306a36Sopenharmony_ci wr32(wx, WX_PSR_VLAN_TBL(offset), 0); 187762306a36Sopenharmony_ci wx->mac.vft_shadow[offset] = 0; 187862306a36Sopenharmony_ci } 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci for (offset = 0; offset < WX_PSR_VLAN_SWC_ENTRIES; offset++) { 188162306a36Sopenharmony_ci wr32(wx, WX_PSR_VLAN_SWC_IDX, offset); 188262306a36Sopenharmony_ci wr32(wx, WX_PSR_VLAN_SWC, 0); 188362306a36Sopenharmony_ci wr32(wx, WX_PSR_VLAN_SWC_VM_L, 0); 188462306a36Sopenharmony_ci wr32(wx, WX_PSR_VLAN_SWC_VM_H, 0); 188562306a36Sopenharmony_ci } 188662306a36Sopenharmony_ci} 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ciint wx_vlan_rx_add_vid(struct net_device *netdev, 188962306a36Sopenharmony_ci __be16 proto, u16 vid) 189062306a36Sopenharmony_ci{ 189162306a36Sopenharmony_ci struct wx *wx = netdev_priv(netdev); 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci /* add VID to filter table */ 189462306a36Sopenharmony_ci wx_set_vfta(wx, vid, VMDQ_P(0), true); 189562306a36Sopenharmony_ci set_bit(vid, wx->active_vlans); 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci return 0; 189862306a36Sopenharmony_ci} 189962306a36Sopenharmony_ciEXPORT_SYMBOL(wx_vlan_rx_add_vid); 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ciint wx_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid) 190262306a36Sopenharmony_ci{ 190362306a36Sopenharmony_ci struct wx *wx = netdev_priv(netdev); 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci /* remove VID from filter table */ 190662306a36Sopenharmony_ci if (vid) 190762306a36Sopenharmony_ci wx_set_vfta(wx, vid, VMDQ_P(0), false); 190862306a36Sopenharmony_ci clear_bit(vid, wx->active_vlans); 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci return 0; 191162306a36Sopenharmony_ci} 191262306a36Sopenharmony_ciEXPORT_SYMBOL(wx_vlan_rx_kill_vid); 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci/** 191562306a36Sopenharmony_ci * wx_start_hw - Prepare hardware for Tx/Rx 191662306a36Sopenharmony_ci * @wx: pointer to hardware structure 191762306a36Sopenharmony_ci * 191862306a36Sopenharmony_ci * Starts the hardware using the generic start_hw function 191962306a36Sopenharmony_ci * and the generation start_hw function. 192062306a36Sopenharmony_ci * Then performs revision-specific operations, if any. 192162306a36Sopenharmony_ci **/ 192262306a36Sopenharmony_civoid wx_start_hw(struct wx *wx) 192362306a36Sopenharmony_ci{ 192462306a36Sopenharmony_ci int i; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci /* Clear the VLAN filter table */ 192762306a36Sopenharmony_ci wx_clear_vfta(wx); 192862306a36Sopenharmony_ci WX_WRITE_FLUSH(wx); 192962306a36Sopenharmony_ci /* Clear the rate limiters */ 193062306a36Sopenharmony_ci for (i = 0; i < wx->mac.max_tx_queues; i++) { 193162306a36Sopenharmony_ci wr32(wx, WX_TDM_RP_IDX, i); 193262306a36Sopenharmony_ci wr32(wx, WX_TDM_RP_RATE, 0); 193362306a36Sopenharmony_ci } 193462306a36Sopenharmony_ci} 193562306a36Sopenharmony_ciEXPORT_SYMBOL(wx_start_hw); 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1938