1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright Sunplus Technology Co., Ltd. |
3 | * All rights reserved. |
4 | */ |
5 | |
6 | #include <linux/platform_device.h> |
7 | #include <linux/netdevice.h> |
8 | #include <linux/bitfield.h> |
9 | #include <linux/of_mdio.h> |
10 | |
11 | #include "spl2sw_register.h" |
12 | #include "spl2sw_define.h" |
13 | #include "spl2sw_desc.h" |
14 | #include "spl2sw_mac.h" |
15 | |
16 | void spl2sw_mac_hw_stop(struct spl2sw_common *comm) |
17 | { |
18 | u32 reg; |
19 | |
20 | if (comm->enable == 0) { |
21 | /* Mask and clear all interrupts. */ |
22 | writel(val: 0xffffffff, addr: comm->l2sw_reg_base + L2SW_SW_INT_MASK_0); |
23 | writel(val: 0xffffffff, addr: comm->l2sw_reg_base + L2SW_SW_INT_STATUS_0); |
24 | |
25 | /* Disable cpu 0 and cpu 1. */ |
26 | reg = readl(addr: comm->l2sw_reg_base + L2SW_CPU_CNTL); |
27 | reg |= MAC_DIS_SOC1_CPU | MAC_DIS_SOC0_CPU; |
28 | writel(val: reg, addr: comm->l2sw_reg_base + L2SW_CPU_CNTL); |
29 | } |
30 | |
31 | /* Disable LAN ports. */ |
32 | reg = readl(addr: comm->l2sw_reg_base + L2SW_PORT_CNTL0); |
33 | reg |= FIELD_PREP(MAC_DIS_PORT, ~comm->enable); |
34 | writel(val: reg, addr: comm->l2sw_reg_base + L2SW_PORT_CNTL0); |
35 | } |
36 | |
37 | void spl2sw_mac_hw_start(struct spl2sw_common *comm) |
38 | { |
39 | u32 reg; |
40 | |
41 | /* Enable cpu port 0 (6) & CRC padding (8) */ |
42 | reg = readl(addr: comm->l2sw_reg_base + L2SW_CPU_CNTL); |
43 | reg &= ~MAC_DIS_SOC0_CPU; |
44 | reg |= MAC_EN_CRC_SOC0; |
45 | writel(val: reg, addr: comm->l2sw_reg_base + L2SW_CPU_CNTL); |
46 | |
47 | /* Enable port 0 & port 1 */ |
48 | reg = readl(addr: comm->l2sw_reg_base + L2SW_PORT_CNTL0); |
49 | reg &= FIELD_PREP(MAC_DIS_PORT, ~comm->enable) | ~MAC_DIS_PORT; |
50 | writel(val: reg, addr: comm->l2sw_reg_base + L2SW_PORT_CNTL0); |
51 | } |
52 | |
53 | int spl2sw_mac_addr_add(struct spl2sw_mac *mac) |
54 | { |
55 | struct spl2sw_common *comm = mac->comm; |
56 | u32 reg; |
57 | int ret; |
58 | |
59 | /* Write 6-octet MAC address. */ |
60 | writel(val: (mac->mac_addr[0] << 0) + (mac->mac_addr[1] << 8), |
61 | addr: comm->l2sw_reg_base + L2SW_W_MAC_15_0); |
62 | writel(val: (mac->mac_addr[2] << 0) + (mac->mac_addr[3] << 8) + |
63 | (mac->mac_addr[4] << 16) + (mac->mac_addr[5] << 24), |
64 | addr: comm->l2sw_reg_base + L2SW_W_MAC_47_16); |
65 | |
66 | /* Set learn port = cpu_port, aging = 1 */ |
67 | reg = MAC_W_CPU_PORT_0 | FIELD_PREP(MAC_W_VID, mac->vlan_id) | |
68 | FIELD_PREP(MAC_W_AGE, 1) | MAC_W_MAC_CMD; |
69 | writel(val: reg, addr: comm->l2sw_reg_base + L2SW_WT_MAC_AD0); |
70 | |
71 | /* Wait for completing. */ |
72 | ret = read_poll_timeout(readl, reg, reg & MAC_W_MAC_DONE, 1, 200, true, |
73 | comm->l2sw_reg_base + L2SW_WT_MAC_AD0); |
74 | if (ret) { |
75 | netdev_err(dev: mac->ndev, format: "Failed to add address to table!\n" ); |
76 | return ret; |
77 | } |
78 | |
79 | netdev_dbg(mac->ndev, "mac_ad0 = %08x, mac_ad = %08x%04x\n" , |
80 | readl(comm->l2sw_reg_base + L2SW_WT_MAC_AD0), |
81 | (u32)FIELD_GET(MAC_W_MAC_47_16, |
82 | readl(comm->l2sw_reg_base + L2SW_W_MAC_47_16)), |
83 | (u32)FIELD_GET(MAC_W_MAC_15_0, |
84 | readl(comm->l2sw_reg_base + L2SW_W_MAC_15_0))); |
85 | return 0; |
86 | } |
87 | |
88 | int spl2sw_mac_addr_del(struct spl2sw_mac *mac) |
89 | { |
90 | struct spl2sw_common *comm = mac->comm; |
91 | u32 reg; |
92 | int ret; |
93 | |
94 | /* Write 6-octet MAC address. */ |
95 | writel(val: (mac->mac_addr[0] << 0) + (mac->mac_addr[1] << 8), |
96 | addr: comm->l2sw_reg_base + L2SW_W_MAC_15_0); |
97 | writel(val: (mac->mac_addr[2] << 0) + (mac->mac_addr[3] << 8) + |
98 | (mac->mac_addr[4] << 16) + (mac->mac_addr[5] << 24), |
99 | addr: comm->l2sw_reg_base + L2SW_W_MAC_47_16); |
100 | |
101 | /* Set learn port = lan_port0 and aging = 0 |
102 | * to wipe (age) out the entry. |
103 | */ |
104 | reg = MAC_W_LAN_PORT_0 | FIELD_PREP(MAC_W_VID, mac->vlan_id) | MAC_W_MAC_CMD; |
105 | writel(val: reg, addr: comm->l2sw_reg_base + L2SW_WT_MAC_AD0); |
106 | |
107 | /* Wait for completing. */ |
108 | ret = read_poll_timeout(readl, reg, reg & MAC_W_MAC_DONE, 1, 200, true, |
109 | comm->l2sw_reg_base + L2SW_WT_MAC_AD0); |
110 | if (ret) { |
111 | netdev_err(dev: mac->ndev, format: "Failed to delete address from table!\n" ); |
112 | return ret; |
113 | } |
114 | |
115 | netdev_dbg(mac->ndev, "mac_ad0 = %08x, mac_ad = %08x%04x\n" , |
116 | readl(comm->l2sw_reg_base + L2SW_WT_MAC_AD0), |
117 | (u32)FIELD_GET(MAC_W_MAC_47_16, |
118 | readl(comm->l2sw_reg_base + L2SW_W_MAC_47_16)), |
119 | (u32)FIELD_GET(MAC_W_MAC_15_0, |
120 | readl(comm->l2sw_reg_base + L2SW_W_MAC_15_0))); |
121 | return 0; |
122 | } |
123 | |
124 | void spl2sw_mac_hw_init(struct spl2sw_common *comm) |
125 | { |
126 | u32 reg; |
127 | |
128 | /* Disable cpu0 and cpu 1 port. */ |
129 | reg = readl(addr: comm->l2sw_reg_base + L2SW_CPU_CNTL); |
130 | reg |= MAC_DIS_SOC1_CPU | MAC_DIS_SOC0_CPU; |
131 | writel(val: reg, addr: comm->l2sw_reg_base + L2SW_CPU_CNTL); |
132 | |
133 | /* Set base addresses of TX and RX queues. */ |
134 | writel(val: comm->desc_dma, addr: comm->l2sw_reg_base + L2SW_TX_LBASE_ADDR_0); |
135 | writel(val: comm->desc_dma + sizeof(struct spl2sw_mac_desc) * TX_DESC_NUM, |
136 | addr: comm->l2sw_reg_base + L2SW_TX_HBASE_ADDR_0); |
137 | writel(val: comm->desc_dma + sizeof(struct spl2sw_mac_desc) * (TX_DESC_NUM + |
138 | MAC_GUARD_DESC_NUM), addr: comm->l2sw_reg_base + L2SW_RX_HBASE_ADDR_0); |
139 | writel(val: comm->desc_dma + sizeof(struct spl2sw_mac_desc) * (TX_DESC_NUM + |
140 | MAC_GUARD_DESC_NUM + RX_QUEUE0_DESC_NUM), |
141 | addr: comm->l2sw_reg_base + L2SW_RX_LBASE_ADDR_0); |
142 | |
143 | /* Fc_rls_th=0x4a, Fc_set_th=0x3a, Drop_rls_th=0x2d, Drop_set_th=0x1d */ |
144 | writel(val: 0x4a3a2d1d, addr: comm->l2sw_reg_base + L2SW_FL_CNTL_TH); |
145 | |
146 | /* Cpu_rls_th=0x4a, Cpu_set_th=0x3a, Cpu_th=0x12, Port_th=0x12 */ |
147 | writel(val: 0x4a3a1212, addr: comm->l2sw_reg_base + L2SW_CPU_FL_CNTL_TH); |
148 | |
149 | /* mtcc_lmt=0xf, Pri_th_l=6, Pri_th_h=6, weigh_8x_en=1 */ |
150 | writel(val: 0xf6680000, addr: comm->l2sw_reg_base + L2SW_PRI_FL_CNTL); |
151 | |
152 | /* High-active LED */ |
153 | reg = readl(addr: comm->l2sw_reg_base + L2SW_LED_PORT0); |
154 | reg |= MAC_LED_ACT_HI; |
155 | writel(val: reg, addr: comm->l2sw_reg_base + L2SW_LED_PORT0); |
156 | |
157 | /* Disable aging of cpu port 0 & 1. |
158 | * Disable SA learning of cpu port 0 & 1. |
159 | * Enable UC and MC packets |
160 | */ |
161 | reg = readl(addr: comm->l2sw_reg_base + L2SW_CPU_CNTL); |
162 | reg &= ~(MAC_EN_SOC1_AGING | MAC_EN_SOC0_AGING | |
163 | MAC_DIS_BC2CPU_P1 | MAC_DIS_BC2CPU_P0 | |
164 | MAC_DIS_MC2CPU_P1 | MAC_DIS_MC2CPU_P0); |
165 | reg |= MAC_DIS_LRN_SOC1 | MAC_DIS_LRN_SOC0; |
166 | writel(val: reg, addr: comm->l2sw_reg_base + L2SW_CPU_CNTL); |
167 | |
168 | /* Enable RMC2CPU for port 0 & 1 |
169 | * Enable Flow control for port 0 & 1 |
170 | * Enable Back pressure for port 0 & 1 |
171 | */ |
172 | reg = readl(addr: comm->l2sw_reg_base + L2SW_PORT_CNTL0); |
173 | reg &= ~(MAC_DIS_RMC2CPU_P1 | MAC_DIS_RMC2CPU_P0); |
174 | reg |= MAC_EN_FLOW_CTL_P1 | MAC_EN_FLOW_CTL_P0 | |
175 | MAC_EN_BACK_PRESS_P1 | MAC_EN_BACK_PRESS_P0; |
176 | writel(val: reg, addr: comm->l2sw_reg_base + L2SW_PORT_CNTL0); |
177 | |
178 | /* Disable LAN port SA learning. */ |
179 | reg = readl(addr: comm->l2sw_reg_base + L2SW_PORT_CNTL1); |
180 | reg |= MAC_DIS_SA_LRN_P1 | MAC_DIS_SA_LRN_P0; |
181 | writel(val: reg, addr: comm->l2sw_reg_base + L2SW_PORT_CNTL1); |
182 | |
183 | /* Enable rmii force mode and |
184 | * set both external phy-address to 31. |
185 | */ |
186 | reg = readl(addr: comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE); |
187 | reg &= ~(MAC_EXT_PHY1_ADDR | MAC_EXT_PHY0_ADDR); |
188 | reg |= FIELD_PREP(MAC_EXT_PHY1_ADDR, 31) | FIELD_PREP(MAC_EXT_PHY0_ADDR, 31); |
189 | reg |= MAC_FORCE_RMII_EN_1 | MAC_FORCE_RMII_EN_0; |
190 | writel(val: reg, addr: comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE); |
191 | |
192 | /* Port 0: VLAN group 0 |
193 | * Port 1: VLAN group 1 |
194 | */ |
195 | reg = FIELD_PREP(MAC_P1_PVID, 1) | FIELD_PREP(MAC_P0_PVID, 0); |
196 | writel(val: reg, addr: comm->l2sw_reg_base + L2SW_PVID_CONFIG0); |
197 | |
198 | /* VLAN group 0: cpu0 (bit3) + port0 (bit0) = 1001 = 0x9 |
199 | * VLAN group 1: cpu0 (bit3) + port1 (bit1) = 1010 = 0xa |
200 | */ |
201 | reg = FIELD_PREP(MAC_VLAN_MEMSET_1, 0xa) | FIELD_PREP(MAC_VLAN_MEMSET_0, 9); |
202 | writel(val: reg, addr: comm->l2sw_reg_base + L2SW_VLAN_MEMSET_CONFIG0); |
203 | |
204 | /* RMC forward: to_cpu (1) |
205 | * LED: 60mS (1) |
206 | * BC storm prev: 31 BC (1) |
207 | */ |
208 | reg = readl(addr: comm->l2sw_reg_base + L2SW_SW_GLB_CNTL); |
209 | reg &= ~(MAC_RMC_TB_FAULT_RULE | MAC_LED_FLASH_TIME | MAC_BC_STORM_PREV); |
210 | reg |= FIELD_PREP(MAC_RMC_TB_FAULT_RULE, 1) | |
211 | FIELD_PREP(MAC_LED_FLASH_TIME, 1) | |
212 | FIELD_PREP(MAC_BC_STORM_PREV, 1); |
213 | writel(val: reg, addr: comm->l2sw_reg_base + L2SW_SW_GLB_CNTL); |
214 | |
215 | writel(MAC_INT_MASK_DEF, addr: comm->l2sw_reg_base + L2SW_SW_INT_MASK_0); |
216 | } |
217 | |
218 | void spl2sw_mac_rx_mode_set(struct spl2sw_mac *mac) |
219 | { |
220 | struct spl2sw_common *comm = mac->comm; |
221 | struct net_device *ndev = mac->ndev; |
222 | u32 mask, reg, rx_mode; |
223 | |
224 | netdev_dbg(ndev, "ndev->flags = %08x\n" , ndev->flags); |
225 | mask = FIELD_PREP(MAC_DIS_MC2CPU, mac->lan_port) | |
226 | FIELD_PREP(MAC_DIS_UN2CPU, mac->lan_port); |
227 | reg = readl(addr: comm->l2sw_reg_base + L2SW_CPU_CNTL); |
228 | |
229 | if (ndev->flags & IFF_PROMISC) { |
230 | /* Allow MC and unknown UC packets */ |
231 | rx_mode = FIELD_PREP(MAC_DIS_MC2CPU, mac->lan_port) | |
232 | FIELD_PREP(MAC_DIS_UN2CPU, mac->lan_port); |
233 | } else if ((!netdev_mc_empty(ndev) && (ndev->flags & IFF_MULTICAST)) || |
234 | (ndev->flags & IFF_ALLMULTI)) { |
235 | /* Allow MC packets */ |
236 | rx_mode = FIELD_PREP(MAC_DIS_MC2CPU, mac->lan_port); |
237 | } else { |
238 | /* Disable MC and unknown UC packets */ |
239 | rx_mode = 0; |
240 | } |
241 | |
242 | writel(val: (reg & (~mask)) | ((~rx_mode) & mask), addr: comm->l2sw_reg_base + L2SW_CPU_CNTL); |
243 | netdev_dbg(ndev, "cpu_cntl = %08x\n" , readl(comm->l2sw_reg_base + L2SW_CPU_CNTL)); |
244 | } |
245 | |
246 | void spl2sw_mac_init(struct spl2sw_common *comm) |
247 | { |
248 | u32 i; |
249 | |
250 | for (i = 0; i < RX_DESC_QUEUE_NUM; i++) |
251 | comm->rx_pos[i] = 0; |
252 | mb(); /* make sure settings are effective. */ |
253 | |
254 | spl2sw_mac_hw_init(comm); |
255 | } |
256 | |
257 | void spl2sw_mac_soft_reset(struct spl2sw_common *comm) |
258 | { |
259 | u32 i; |
260 | |
261 | spl2sw_mac_hw_stop(comm); |
262 | |
263 | spl2sw_rx_descs_flush(comm); |
264 | comm->tx_pos = 0; |
265 | comm->tx_done_pos = 0; |
266 | comm->tx_desc_full = 0; |
267 | |
268 | for (i = 0; i < RX_DESC_QUEUE_NUM; i++) |
269 | comm->rx_pos[i] = 0; |
270 | mb(); /* make sure settings are effective. */ |
271 | |
272 | spl2sw_mac_hw_init(comm); |
273 | spl2sw_mac_hw_start(comm); |
274 | } |
275 | |