162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright Sunplus Technology Co., Ltd. 362306a36Sopenharmony_ci * All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/platform_device.h> 762306a36Sopenharmony_ci#include <linux/netdevice.h> 862306a36Sopenharmony_ci#include <linux/bitfield.h> 962306a36Sopenharmony_ci#include <linux/of_mdio.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "spl2sw_register.h" 1262306a36Sopenharmony_ci#include "spl2sw_define.h" 1362306a36Sopenharmony_ci#include "spl2sw_desc.h" 1462306a36Sopenharmony_ci#include "spl2sw_mac.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_civoid spl2sw_mac_hw_stop(struct spl2sw_common *comm) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci u32 reg; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci if (comm->enable == 0) { 2162306a36Sopenharmony_ci /* Mask and clear all interrupts. */ 2262306a36Sopenharmony_ci writel(0xffffffff, comm->l2sw_reg_base + L2SW_SW_INT_MASK_0); 2362306a36Sopenharmony_ci writel(0xffffffff, comm->l2sw_reg_base + L2SW_SW_INT_STATUS_0); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci /* Disable cpu 0 and cpu 1. */ 2662306a36Sopenharmony_ci reg = readl(comm->l2sw_reg_base + L2SW_CPU_CNTL); 2762306a36Sopenharmony_ci reg |= MAC_DIS_SOC1_CPU | MAC_DIS_SOC0_CPU; 2862306a36Sopenharmony_ci writel(reg, comm->l2sw_reg_base + L2SW_CPU_CNTL); 2962306a36Sopenharmony_ci } 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci /* Disable LAN ports. */ 3262306a36Sopenharmony_ci reg = readl(comm->l2sw_reg_base + L2SW_PORT_CNTL0); 3362306a36Sopenharmony_ci reg |= FIELD_PREP(MAC_DIS_PORT, ~comm->enable); 3462306a36Sopenharmony_ci writel(reg, comm->l2sw_reg_base + L2SW_PORT_CNTL0); 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_civoid spl2sw_mac_hw_start(struct spl2sw_common *comm) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci u32 reg; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci /* Enable cpu port 0 (6) & CRC padding (8) */ 4262306a36Sopenharmony_ci reg = readl(comm->l2sw_reg_base + L2SW_CPU_CNTL); 4362306a36Sopenharmony_ci reg &= ~MAC_DIS_SOC0_CPU; 4462306a36Sopenharmony_ci reg |= MAC_EN_CRC_SOC0; 4562306a36Sopenharmony_ci writel(reg, comm->l2sw_reg_base + L2SW_CPU_CNTL); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* Enable port 0 & port 1 */ 4862306a36Sopenharmony_ci reg = readl(comm->l2sw_reg_base + L2SW_PORT_CNTL0); 4962306a36Sopenharmony_ci reg &= FIELD_PREP(MAC_DIS_PORT, ~comm->enable) | ~MAC_DIS_PORT; 5062306a36Sopenharmony_ci writel(reg, comm->l2sw_reg_base + L2SW_PORT_CNTL0); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ciint spl2sw_mac_addr_add(struct spl2sw_mac *mac) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct spl2sw_common *comm = mac->comm; 5662306a36Sopenharmony_ci u32 reg; 5762306a36Sopenharmony_ci int ret; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* Write 6-octet MAC address. */ 6062306a36Sopenharmony_ci writel((mac->mac_addr[0] << 0) + (mac->mac_addr[1] << 8), 6162306a36Sopenharmony_ci comm->l2sw_reg_base + L2SW_W_MAC_15_0); 6262306a36Sopenharmony_ci writel((mac->mac_addr[2] << 0) + (mac->mac_addr[3] << 8) + 6362306a36Sopenharmony_ci (mac->mac_addr[4] << 16) + (mac->mac_addr[5] << 24), 6462306a36Sopenharmony_ci comm->l2sw_reg_base + L2SW_W_MAC_47_16); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* Set learn port = cpu_port, aging = 1 */ 6762306a36Sopenharmony_ci reg = MAC_W_CPU_PORT_0 | FIELD_PREP(MAC_W_VID, mac->vlan_id) | 6862306a36Sopenharmony_ci FIELD_PREP(MAC_W_AGE, 1) | MAC_W_MAC_CMD; 6962306a36Sopenharmony_ci writel(reg, comm->l2sw_reg_base + L2SW_WT_MAC_AD0); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* Wait for completing. */ 7262306a36Sopenharmony_ci ret = read_poll_timeout(readl, reg, reg & MAC_W_MAC_DONE, 1, 200, true, 7362306a36Sopenharmony_ci comm->l2sw_reg_base + L2SW_WT_MAC_AD0); 7462306a36Sopenharmony_ci if (ret) { 7562306a36Sopenharmony_ci netdev_err(mac->ndev, "Failed to add address to table!\n"); 7662306a36Sopenharmony_ci return ret; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci netdev_dbg(mac->ndev, "mac_ad0 = %08x, mac_ad = %08x%04x\n", 8062306a36Sopenharmony_ci readl(comm->l2sw_reg_base + L2SW_WT_MAC_AD0), 8162306a36Sopenharmony_ci (u32)FIELD_GET(MAC_W_MAC_47_16, 8262306a36Sopenharmony_ci readl(comm->l2sw_reg_base + L2SW_W_MAC_47_16)), 8362306a36Sopenharmony_ci (u32)FIELD_GET(MAC_W_MAC_15_0, 8462306a36Sopenharmony_ci readl(comm->l2sw_reg_base + L2SW_W_MAC_15_0))); 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ciint spl2sw_mac_addr_del(struct spl2sw_mac *mac) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct spl2sw_common *comm = mac->comm; 9162306a36Sopenharmony_ci u32 reg; 9262306a36Sopenharmony_ci int ret; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Write 6-octet MAC address. */ 9562306a36Sopenharmony_ci writel((mac->mac_addr[0] << 0) + (mac->mac_addr[1] << 8), 9662306a36Sopenharmony_ci comm->l2sw_reg_base + L2SW_W_MAC_15_0); 9762306a36Sopenharmony_ci writel((mac->mac_addr[2] << 0) + (mac->mac_addr[3] << 8) + 9862306a36Sopenharmony_ci (mac->mac_addr[4] << 16) + (mac->mac_addr[5] << 24), 9962306a36Sopenharmony_ci comm->l2sw_reg_base + L2SW_W_MAC_47_16); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Set learn port = lan_port0 and aging = 0 10262306a36Sopenharmony_ci * to wipe (age) out the entry. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ci reg = MAC_W_LAN_PORT_0 | FIELD_PREP(MAC_W_VID, mac->vlan_id) | MAC_W_MAC_CMD; 10562306a36Sopenharmony_ci writel(reg, comm->l2sw_reg_base + L2SW_WT_MAC_AD0); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Wait for completing. */ 10862306a36Sopenharmony_ci ret = read_poll_timeout(readl, reg, reg & MAC_W_MAC_DONE, 1, 200, true, 10962306a36Sopenharmony_ci comm->l2sw_reg_base + L2SW_WT_MAC_AD0); 11062306a36Sopenharmony_ci if (ret) { 11162306a36Sopenharmony_ci netdev_err(mac->ndev, "Failed to delete address from table!\n"); 11262306a36Sopenharmony_ci return ret; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci netdev_dbg(mac->ndev, "mac_ad0 = %08x, mac_ad = %08x%04x\n", 11662306a36Sopenharmony_ci readl(comm->l2sw_reg_base + L2SW_WT_MAC_AD0), 11762306a36Sopenharmony_ci (u32)FIELD_GET(MAC_W_MAC_47_16, 11862306a36Sopenharmony_ci readl(comm->l2sw_reg_base + L2SW_W_MAC_47_16)), 11962306a36Sopenharmony_ci (u32)FIELD_GET(MAC_W_MAC_15_0, 12062306a36Sopenharmony_ci readl(comm->l2sw_reg_base + L2SW_W_MAC_15_0))); 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_civoid spl2sw_mac_hw_init(struct spl2sw_common *comm) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci u32 reg; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* Disable cpu0 and cpu 1 port. */ 12962306a36Sopenharmony_ci reg = readl(comm->l2sw_reg_base + L2SW_CPU_CNTL); 13062306a36Sopenharmony_ci reg |= MAC_DIS_SOC1_CPU | MAC_DIS_SOC0_CPU; 13162306a36Sopenharmony_ci writel(reg, comm->l2sw_reg_base + L2SW_CPU_CNTL); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* Set base addresses of TX and RX queues. */ 13462306a36Sopenharmony_ci writel(comm->desc_dma, comm->l2sw_reg_base + L2SW_TX_LBASE_ADDR_0); 13562306a36Sopenharmony_ci writel(comm->desc_dma + sizeof(struct spl2sw_mac_desc) * TX_DESC_NUM, 13662306a36Sopenharmony_ci comm->l2sw_reg_base + L2SW_TX_HBASE_ADDR_0); 13762306a36Sopenharmony_ci writel(comm->desc_dma + sizeof(struct spl2sw_mac_desc) * (TX_DESC_NUM + 13862306a36Sopenharmony_ci MAC_GUARD_DESC_NUM), comm->l2sw_reg_base + L2SW_RX_HBASE_ADDR_0); 13962306a36Sopenharmony_ci writel(comm->desc_dma + sizeof(struct spl2sw_mac_desc) * (TX_DESC_NUM + 14062306a36Sopenharmony_ci MAC_GUARD_DESC_NUM + RX_QUEUE0_DESC_NUM), 14162306a36Sopenharmony_ci comm->l2sw_reg_base + L2SW_RX_LBASE_ADDR_0); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* Fc_rls_th=0x4a, Fc_set_th=0x3a, Drop_rls_th=0x2d, Drop_set_th=0x1d */ 14462306a36Sopenharmony_ci writel(0x4a3a2d1d, comm->l2sw_reg_base + L2SW_FL_CNTL_TH); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* Cpu_rls_th=0x4a, Cpu_set_th=0x3a, Cpu_th=0x12, Port_th=0x12 */ 14762306a36Sopenharmony_ci writel(0x4a3a1212, comm->l2sw_reg_base + L2SW_CPU_FL_CNTL_TH); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* mtcc_lmt=0xf, Pri_th_l=6, Pri_th_h=6, weigh_8x_en=1 */ 15062306a36Sopenharmony_ci writel(0xf6680000, comm->l2sw_reg_base + L2SW_PRI_FL_CNTL); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* High-active LED */ 15362306a36Sopenharmony_ci reg = readl(comm->l2sw_reg_base + L2SW_LED_PORT0); 15462306a36Sopenharmony_ci reg |= MAC_LED_ACT_HI; 15562306a36Sopenharmony_ci writel(reg, comm->l2sw_reg_base + L2SW_LED_PORT0); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* Disable aging of cpu port 0 & 1. 15862306a36Sopenharmony_ci * Disable SA learning of cpu port 0 & 1. 15962306a36Sopenharmony_ci * Enable UC and MC packets 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci reg = readl(comm->l2sw_reg_base + L2SW_CPU_CNTL); 16262306a36Sopenharmony_ci reg &= ~(MAC_EN_SOC1_AGING | MAC_EN_SOC0_AGING | 16362306a36Sopenharmony_ci MAC_DIS_BC2CPU_P1 | MAC_DIS_BC2CPU_P0 | 16462306a36Sopenharmony_ci MAC_DIS_MC2CPU_P1 | MAC_DIS_MC2CPU_P0); 16562306a36Sopenharmony_ci reg |= MAC_DIS_LRN_SOC1 | MAC_DIS_LRN_SOC0; 16662306a36Sopenharmony_ci writel(reg, comm->l2sw_reg_base + L2SW_CPU_CNTL); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Enable RMC2CPU for port 0 & 1 16962306a36Sopenharmony_ci * Enable Flow control for port 0 & 1 17062306a36Sopenharmony_ci * Enable Back pressure for port 0 & 1 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci reg = readl(comm->l2sw_reg_base + L2SW_PORT_CNTL0); 17362306a36Sopenharmony_ci reg &= ~(MAC_DIS_RMC2CPU_P1 | MAC_DIS_RMC2CPU_P0); 17462306a36Sopenharmony_ci reg |= MAC_EN_FLOW_CTL_P1 | MAC_EN_FLOW_CTL_P0 | 17562306a36Sopenharmony_ci MAC_EN_BACK_PRESS_P1 | MAC_EN_BACK_PRESS_P0; 17662306a36Sopenharmony_ci writel(reg, comm->l2sw_reg_base + L2SW_PORT_CNTL0); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* Disable LAN port SA learning. */ 17962306a36Sopenharmony_ci reg = readl(comm->l2sw_reg_base + L2SW_PORT_CNTL1); 18062306a36Sopenharmony_ci reg |= MAC_DIS_SA_LRN_P1 | MAC_DIS_SA_LRN_P0; 18162306a36Sopenharmony_ci writel(reg, comm->l2sw_reg_base + L2SW_PORT_CNTL1); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Enable rmii force mode and 18462306a36Sopenharmony_ci * set both external phy-address to 31. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci reg = readl(comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE); 18762306a36Sopenharmony_ci reg &= ~(MAC_EXT_PHY1_ADDR | MAC_EXT_PHY0_ADDR); 18862306a36Sopenharmony_ci reg |= FIELD_PREP(MAC_EXT_PHY1_ADDR, 31) | FIELD_PREP(MAC_EXT_PHY0_ADDR, 31); 18962306a36Sopenharmony_ci reg |= MAC_FORCE_RMII_EN_1 | MAC_FORCE_RMII_EN_0; 19062306a36Sopenharmony_ci writel(reg, comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* Port 0: VLAN group 0 19362306a36Sopenharmony_ci * Port 1: VLAN group 1 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_ci reg = FIELD_PREP(MAC_P1_PVID, 1) | FIELD_PREP(MAC_P0_PVID, 0); 19662306a36Sopenharmony_ci writel(reg, comm->l2sw_reg_base + L2SW_PVID_CONFIG0); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* VLAN group 0: cpu0 (bit3) + port0 (bit0) = 1001 = 0x9 19962306a36Sopenharmony_ci * VLAN group 1: cpu0 (bit3) + port1 (bit1) = 1010 = 0xa 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ci reg = FIELD_PREP(MAC_VLAN_MEMSET_1, 0xa) | FIELD_PREP(MAC_VLAN_MEMSET_0, 9); 20262306a36Sopenharmony_ci writel(reg, comm->l2sw_reg_base + L2SW_VLAN_MEMSET_CONFIG0); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* RMC forward: to_cpu (1) 20562306a36Sopenharmony_ci * LED: 60mS (1) 20662306a36Sopenharmony_ci * BC storm prev: 31 BC (1) 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci reg = readl(comm->l2sw_reg_base + L2SW_SW_GLB_CNTL); 20962306a36Sopenharmony_ci reg &= ~(MAC_RMC_TB_FAULT_RULE | MAC_LED_FLASH_TIME | MAC_BC_STORM_PREV); 21062306a36Sopenharmony_ci reg |= FIELD_PREP(MAC_RMC_TB_FAULT_RULE, 1) | 21162306a36Sopenharmony_ci FIELD_PREP(MAC_LED_FLASH_TIME, 1) | 21262306a36Sopenharmony_ci FIELD_PREP(MAC_BC_STORM_PREV, 1); 21362306a36Sopenharmony_ci writel(reg, comm->l2sw_reg_base + L2SW_SW_GLB_CNTL); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci writel(MAC_INT_MASK_DEF, comm->l2sw_reg_base + L2SW_SW_INT_MASK_0); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_civoid spl2sw_mac_rx_mode_set(struct spl2sw_mac *mac) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct spl2sw_common *comm = mac->comm; 22162306a36Sopenharmony_ci struct net_device *ndev = mac->ndev; 22262306a36Sopenharmony_ci u32 mask, reg, rx_mode; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci netdev_dbg(ndev, "ndev->flags = %08x\n", ndev->flags); 22562306a36Sopenharmony_ci mask = FIELD_PREP(MAC_DIS_MC2CPU, mac->lan_port) | 22662306a36Sopenharmony_ci FIELD_PREP(MAC_DIS_UN2CPU, mac->lan_port); 22762306a36Sopenharmony_ci reg = readl(comm->l2sw_reg_base + L2SW_CPU_CNTL); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (ndev->flags & IFF_PROMISC) { 23062306a36Sopenharmony_ci /* Allow MC and unknown UC packets */ 23162306a36Sopenharmony_ci rx_mode = FIELD_PREP(MAC_DIS_MC2CPU, mac->lan_port) | 23262306a36Sopenharmony_ci FIELD_PREP(MAC_DIS_UN2CPU, mac->lan_port); 23362306a36Sopenharmony_ci } else if ((!netdev_mc_empty(ndev) && (ndev->flags & IFF_MULTICAST)) || 23462306a36Sopenharmony_ci (ndev->flags & IFF_ALLMULTI)) { 23562306a36Sopenharmony_ci /* Allow MC packets */ 23662306a36Sopenharmony_ci rx_mode = FIELD_PREP(MAC_DIS_MC2CPU, mac->lan_port); 23762306a36Sopenharmony_ci } else { 23862306a36Sopenharmony_ci /* Disable MC and unknown UC packets */ 23962306a36Sopenharmony_ci rx_mode = 0; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci writel((reg & (~mask)) | ((~rx_mode) & mask), comm->l2sw_reg_base + L2SW_CPU_CNTL); 24362306a36Sopenharmony_ci netdev_dbg(ndev, "cpu_cntl = %08x\n", readl(comm->l2sw_reg_base + L2SW_CPU_CNTL)); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_civoid spl2sw_mac_init(struct spl2sw_common *comm) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci u32 i; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci for (i = 0; i < RX_DESC_QUEUE_NUM; i++) 25162306a36Sopenharmony_ci comm->rx_pos[i] = 0; 25262306a36Sopenharmony_ci mb(); /* make sure settings are effective. */ 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci spl2sw_mac_hw_init(comm); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_civoid spl2sw_mac_soft_reset(struct spl2sw_common *comm) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci u32 i; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci spl2sw_mac_hw_stop(comm); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci spl2sw_rx_descs_flush(comm); 26462306a36Sopenharmony_ci comm->tx_pos = 0; 26562306a36Sopenharmony_ci comm->tx_done_pos = 0; 26662306a36Sopenharmony_ci comm->tx_desc_full = 0; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci for (i = 0; i < RX_DESC_QUEUE_NUM; i++) 26962306a36Sopenharmony_ci comm->rx_pos[i] = 0; 27062306a36Sopenharmony_ci mb(); /* make sure settings are effective. */ 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci spl2sw_mac_hw_init(comm); 27362306a36Sopenharmony_ci spl2sw_mac_hw_start(comm); 27462306a36Sopenharmony_ci} 275