OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* This program is free software; you can redistribute it and/or modify |
2 | * it under the terms of the GNU General Public License as published by |
||
3 | * the Free Software Foundation; version 2 of the License |
||
4 | * |
||
5 | * This program is distributed in the hope that it will be useful, |
||
6 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
7 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
8 | * GNU General Public License for more details. |
||
9 | * |
||
10 | * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org> |
||
11 | * Copyright (C) 2009-2015 Felix Fietkau <nbd@nbd.name> |
||
12 | * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com> |
||
13 | * Copyright (C) 2016 Vittorio Gambaletta <openwrt@vittgam.net> |
||
14 | */ |
||
15 | |||
16 | #include <linux/module.h> |
||
17 | #include <linux/kernel.h> |
||
18 | #include <linux/platform_device.h> |
||
19 | #include <asm/mach-ralink/ralink_regs.h> |
||
20 | #include <linux/of_irq.h> |
||
21 | |||
22 | #include <linux/switch.h> |
||
23 | |||
24 | #include "mtk_eth_soc.h" |
||
25 | |||
26 | /* HW limitations for this switch: |
||
27 | * - No large frame support (PKT_MAX_LEN at most 1536) |
||
28 | * - Can't have untagged vlan and tagged vlan on one port at the same time, |
||
29 | * though this might be possible using the undocumented PPE. |
||
30 | */ |
||
31 | |||
32 | #define RT305X_ESW_REG_ISR 0x00 |
||
33 | #define RT305X_ESW_REG_IMR 0x04 |
||
34 | #define RT305X_ESW_REG_FCT0 0x08 |
||
35 | #define RT305X_ESW_REG_PFC1 0x14 |
||
36 | #define RT305X_ESW_REG_ATS 0x24 |
||
37 | #define RT305X_ESW_REG_ATS0 0x28 |
||
38 | #define RT305X_ESW_REG_ATS1 0x2c |
||
39 | #define RT305X_ESW_REG_ATS2 0x30 |
||
40 | #define RT305X_ESW_REG_PVIDC(_n) (0x40 + 4 * (_n)) |
||
41 | #define RT305X_ESW_REG_VLANI(_n) (0x50 + 4 * (_n)) |
||
42 | #define RT305X_ESW_REG_VMSC(_n) (0x70 + 4 * (_n)) |
||
43 | #define RT305X_ESW_REG_POA 0x80 |
||
44 | #define RT305X_ESW_REG_FPA 0x84 |
||
45 | #define RT305X_ESW_REG_SOCPC 0x8c |
||
46 | #define RT305X_ESW_REG_POC0 0x90 |
||
47 | #define RT305X_ESW_REG_POC1 0x94 |
||
48 | #define RT305X_ESW_REG_POC2 0x98 |
||
49 | #define RT305X_ESW_REG_SGC 0x9c |
||
50 | #define RT305X_ESW_REG_STRT 0xa0 |
||
51 | #define RT305X_ESW_REG_PCR0 0xc0 |
||
52 | #define RT305X_ESW_REG_PCR1 0xc4 |
||
53 | #define RT305X_ESW_REG_FPA2 0xc8 |
||
54 | #define RT305X_ESW_REG_FCT2 0xcc |
||
55 | #define RT305X_ESW_REG_SGC2 0xe4 |
||
56 | #define RT305X_ESW_REG_P0LED 0xa4 |
||
57 | #define RT305X_ESW_REG_P1LED 0xa8 |
||
58 | #define RT305X_ESW_REG_P2LED 0xac |
||
59 | #define RT305X_ESW_REG_P3LED 0xb0 |
||
60 | #define RT305X_ESW_REG_P4LED 0xb4 |
||
61 | #define RT305X_ESW_REG_PXPC(_x) (0xe8 + (4 * _x)) |
||
62 | #define RT305X_ESW_REG_P1PC 0xec |
||
63 | #define RT305X_ESW_REG_P2PC 0xf0 |
||
64 | #define RT305X_ESW_REG_P3PC 0xf4 |
||
65 | #define RT305X_ESW_REG_P4PC 0xf8 |
||
66 | #define RT305X_ESW_REG_P5PC 0xfc |
||
67 | |||
68 | #define RT305X_ESW_LED_LINK 0 |
||
69 | #define RT305X_ESW_LED_100M 1 |
||
70 | #define RT305X_ESW_LED_DUPLEX 2 |
||
71 | #define RT305X_ESW_LED_ACTIVITY 3 |
||
72 | #define RT305X_ESW_LED_COLLISION 4 |
||
73 | #define RT305X_ESW_LED_LINKACT 5 |
||
74 | #define RT305X_ESW_LED_DUPLCOLL 6 |
||
75 | #define RT305X_ESW_LED_10MACT 7 |
||
76 | #define RT305X_ESW_LED_100MACT 8 |
||
77 | /* Additional led states not in datasheet: */ |
||
78 | #define RT305X_ESW_LED_BLINK 10 |
||
79 | #define RT305X_ESW_LED_ON 12 |
||
80 | |||
81 | #define RT305X_ESW_LINK_S 25 |
||
82 | #define RT305X_ESW_DUPLEX_S 9 |
||
83 | #define RT305X_ESW_SPD_S 0 |
||
84 | |||
85 | #define RT305X_ESW_PCR0_WT_NWAY_DATA_S 16 |
||
86 | #define RT305X_ESW_PCR0_WT_PHY_CMD BIT(13) |
||
87 | #define RT305X_ESW_PCR0_CPU_PHY_REG_S 8 |
||
88 | |||
89 | #define RT305X_ESW_PCR1_WT_DONE BIT(0) |
||
90 | |||
91 | #define RT305X_ESW_ATS_TIMEOUT (5 * HZ) |
||
92 | #define RT305X_ESW_PHY_TIMEOUT (5 * HZ) |
||
93 | |||
94 | #define RT305X_ESW_PVIDC_PVID_M 0xfff |
||
95 | #define RT305X_ESW_PVIDC_PVID_S 12 |
||
96 | |||
97 | #define RT305X_ESW_VLANI_VID_M 0xfff |
||
98 | #define RT305X_ESW_VLANI_VID_S 12 |
||
99 | |||
100 | #define RT305X_ESW_VMSC_MSC_M 0xff |
||
101 | #define RT305X_ESW_VMSC_MSC_S 8 |
||
102 | |||
103 | #define RT305X_ESW_SOCPC_DISUN2CPU_S 0 |
||
104 | #define RT305X_ESW_SOCPC_DISMC2CPU_S 8 |
||
105 | #define RT305X_ESW_SOCPC_DISBC2CPU_S 16 |
||
106 | #define RT305X_ESW_SOCPC_CRC_PADDING BIT(25) |
||
107 | |||
108 | #define RT305X_ESW_POC0_EN_BP_S 0 |
||
109 | #define RT305X_ESW_POC0_EN_FC_S 8 |
||
110 | #define RT305X_ESW_POC0_DIS_RMC2CPU_S 16 |
||
111 | #define RT305X_ESW_POC0_DIS_PORT_M 0x7f |
||
112 | #define RT305X_ESW_POC0_DIS_PORT_S 23 |
||
113 | |||
114 | #define RT305X_ESW_POC2_UNTAG_EN_M 0xff |
||
115 | #define RT305X_ESW_POC2_UNTAG_EN_S 0 |
||
116 | #define RT305X_ESW_POC2_ENAGING_S 8 |
||
117 | #define RT305X_ESW_POC2_DIS_UC_PAUSE_S 16 |
||
118 | |||
119 | #define RT305X_ESW_SGC2_DOUBLE_TAG_M 0x7f |
||
120 | #define RT305X_ESW_SGC2_DOUBLE_TAG_S 0 |
||
121 | #define RT305X_ESW_SGC2_LAN_PMAP_M 0x3f |
||
122 | #define RT305X_ESW_SGC2_LAN_PMAP_S 24 |
||
123 | |||
124 | #define RT305X_ESW_PFC1_EN_VLAN_M 0xff |
||
125 | #define RT305X_ESW_PFC1_EN_VLAN_S 16 |
||
126 | #define RT305X_ESW_PFC1_EN_TOS_S 24 |
||
127 | |||
128 | #define RT305X_ESW_VLAN_NONE 0xfff |
||
129 | |||
130 | #define RT305X_ESW_GSC_BC_STROM_MASK 0x3 |
||
131 | #define RT305X_ESW_GSC_BC_STROM_SHIFT 4 |
||
132 | |||
133 | #define RT305X_ESW_GSC_LED_FREQ_MASK 0x3 |
||
134 | #define RT305X_ESW_GSC_LED_FREQ_SHIFT 23 |
||
135 | |||
136 | #define RT305X_ESW_POA_LINK_MASK 0x1f |
||
137 | #define RT305X_ESW_POA_LINK_SHIFT 25 |
||
138 | |||
139 | #define RT305X_ESW_PORT_ST_CHG BIT(26) |
||
140 | #define RT305X_ESW_PORT0 0 |
||
141 | #define RT305X_ESW_PORT1 1 |
||
142 | #define RT305X_ESW_PORT2 2 |
||
143 | #define RT305X_ESW_PORT3 3 |
||
144 | #define RT305X_ESW_PORT4 4 |
||
145 | #define RT305X_ESW_PORT5 5 |
||
146 | #define RT305X_ESW_PORT6 6 |
||
147 | |||
148 | #define RT305X_ESW_PORTS_NONE 0 |
||
149 | |||
150 | #define RT305X_ESW_PMAP_LLLLLL 0x3f |
||
151 | #define RT305X_ESW_PMAP_LLLLWL 0x2f |
||
152 | #define RT305X_ESW_PMAP_WLLLLL 0x3e |
||
153 | |||
154 | #define RT305X_ESW_PORTS_INTERNAL \ |
||
155 | (BIT(RT305X_ESW_PORT0) | BIT(RT305X_ESW_PORT1) | \ |
||
156 | BIT(RT305X_ESW_PORT2) | BIT(RT305X_ESW_PORT3) | \ |
||
157 | BIT(RT305X_ESW_PORT4)) |
||
158 | |||
159 | #define RT305X_ESW_PORTS_NOCPU \ |
||
160 | (RT305X_ESW_PORTS_INTERNAL | BIT(RT305X_ESW_PORT5)) |
||
161 | |||
162 | #define RT305X_ESW_PORTS_CPU BIT(RT305X_ESW_PORT6) |
||
163 | |||
164 | #define RT305X_ESW_PORTS_ALL \ |
||
165 | (RT305X_ESW_PORTS_NOCPU | RT305X_ESW_PORTS_CPU) |
||
166 | |||
167 | #define RT305X_ESW_NUM_VLANS 16 |
||
168 | #define RT305X_ESW_NUM_VIDS 4096 |
||
169 | #define RT305X_ESW_NUM_PORTS 7 |
||
170 | #define RT305X_ESW_NUM_LANWAN 6 |
||
171 | #define RT305X_ESW_NUM_LEDS 5 |
||
172 | |||
173 | #define RT5350_ESW_REG_PXTPC(_x) (0x150 + (4 * _x)) |
||
174 | #define RT5350_EWS_REG_LED_POLARITY 0x168 |
||
175 | #define RT5350_RESET_EPHY BIT(24) |
||
176 | |||
177 | enum { |
||
178 | /* Global attributes. */ |
||
179 | RT305X_ESW_ATTR_ENABLE_VLAN, |
||
180 | RT305X_ESW_ATTR_ALT_VLAN_DISABLE, |
||
181 | RT305X_ESW_ATTR_BC_STATUS, |
||
182 | RT305X_ESW_ATTR_LED_FREQ, |
||
183 | /* Port attributes. */ |
||
184 | RT305X_ESW_ATTR_PORT_DISABLE, |
||
185 | RT305X_ESW_ATTR_PORT_DOUBLETAG, |
||
186 | RT305X_ESW_ATTR_PORT_UNTAG, |
||
187 | RT305X_ESW_ATTR_PORT_LED, |
||
188 | RT305X_ESW_ATTR_PORT_LAN, |
||
189 | RT305X_ESW_ATTR_PORT_RECV_BAD, |
||
190 | RT305X_ESW_ATTR_PORT_RECV_GOOD, |
||
191 | RT5350_ESW_ATTR_PORT_TR_BAD, |
||
192 | RT5350_ESW_ATTR_PORT_TR_GOOD, |
||
193 | }; |
||
194 | |||
195 | struct esw_port { |
||
196 | bool disable; |
||
197 | bool doubletag; |
||
198 | bool untag; |
||
199 | u8 led; |
||
200 | u16 pvid; |
||
201 | }; |
||
202 | |||
203 | struct esw_vlan { |
||
204 | u8 ports; |
||
205 | u16 vid; |
||
206 | }; |
||
207 | |||
208 | enum { |
||
209 | RT305X_ESW_VLAN_CONFIG_NONE = 0, |
||
210 | RT305X_ESW_VLAN_CONFIG_LLLLW, |
||
211 | RT305X_ESW_VLAN_CONFIG_WLLLL, |
||
212 | }; |
||
213 | |||
214 | struct rt305x_esw { |
||
215 | struct device *dev; |
||
216 | void __iomem *base; |
||
217 | int irq; |
||
218 | |||
219 | /* Protects against concurrent register r/w operations. */ |
||
220 | spinlock_t reg_rw_lock; |
||
221 | |||
222 | unsigned char port_map; |
||
223 | unsigned char port_disable; |
||
224 | unsigned int reg_initval_fct2; |
||
225 | unsigned int reg_initval_fpa2; |
||
226 | unsigned int reg_led_polarity; |
||
227 | |||
228 | struct switch_dev swdev; |
||
229 | bool global_vlan_enable; |
||
230 | bool alt_vlan_disable; |
||
231 | int bc_storm_protect; |
||
232 | int led_frequency; |
||
233 | struct esw_vlan vlans[RT305X_ESW_NUM_VLANS]; |
||
234 | struct esw_port ports[RT305X_ESW_NUM_PORTS]; |
||
235 | |||
236 | }; |
||
237 | |||
238 | static inline void esw_w32(struct rt305x_esw *esw, u32 val, unsigned reg) |
||
239 | { |
||
240 | __raw_writel(val, esw->base + reg); |
||
241 | } |
||
242 | |||
243 | static inline u32 esw_r32(struct rt305x_esw *esw, unsigned reg) |
||
244 | { |
||
245 | return __raw_readl(esw->base + reg); |
||
246 | } |
||
247 | |||
248 | static inline void esw_rmw_raw(struct rt305x_esw *esw, unsigned reg, |
||
249 | unsigned long mask, unsigned long val) |
||
250 | { |
||
251 | unsigned long t; |
||
252 | |||
253 | t = __raw_readl(esw->base + reg) & ~mask; |
||
254 | __raw_writel(t | val, esw->base + reg); |
||
255 | } |
||
256 | |||
257 | static void esw_rmw(struct rt305x_esw *esw, unsigned reg, |
||
258 | unsigned long mask, unsigned long val) |
||
259 | { |
||
260 | unsigned long flags; |
||
261 | |||
262 | spin_lock_irqsave(&esw->reg_rw_lock, flags); |
||
263 | esw_rmw_raw(esw, reg, mask, val); |
||
264 | spin_unlock_irqrestore(&esw->reg_rw_lock, flags); |
||
265 | } |
||
266 | |||
267 | static u32 rt305x_mii_write(struct rt305x_esw *esw, u32 phy_addr, |
||
268 | u32 phy_register, u32 write_data) |
||
269 | { |
||
270 | unsigned long t_start = jiffies; |
||
271 | int ret = 0; |
||
272 | |||
273 | while (1) { |
||
274 | if (!(esw_r32(esw, RT305X_ESW_REG_PCR1) & |
||
275 | RT305X_ESW_PCR1_WT_DONE)) |
||
276 | break; |
||
277 | if (time_after(jiffies, t_start + RT305X_ESW_PHY_TIMEOUT)) { |
||
278 | ret = 1; |
||
279 | goto out; |
||
280 | } |
||
281 | } |
||
282 | |||
283 | write_data &= 0xffff; |
||
284 | esw_w32(esw, (write_data << RT305X_ESW_PCR0_WT_NWAY_DATA_S) | |
||
285 | (phy_register << RT305X_ESW_PCR0_CPU_PHY_REG_S) | |
||
286 | (phy_addr) | RT305X_ESW_PCR0_WT_PHY_CMD, |
||
287 | RT305X_ESW_REG_PCR0); |
||
288 | |||
289 | t_start = jiffies; |
||
290 | while (1) { |
||
291 | if (esw_r32(esw, RT305X_ESW_REG_PCR1) & |
||
292 | RT305X_ESW_PCR1_WT_DONE) |
||
293 | break; |
||
294 | |||
295 | if (time_after(jiffies, t_start + RT305X_ESW_PHY_TIMEOUT)) { |
||
296 | ret = 1; |
||
297 | break; |
||
298 | } |
||
299 | } |
||
300 | out: |
||
301 | if (ret) |
||
302 | dev_err(esw->dev, "ramips_eth: MDIO timeout\n"); |
||
303 | return ret; |
||
304 | } |
||
305 | |||
306 | static unsigned esw_get_vlan_id(struct rt305x_esw *esw, unsigned vlan) |
||
307 | { |
||
308 | unsigned s; |
||
309 | unsigned val; |
||
310 | |||
311 | s = RT305X_ESW_VLANI_VID_S * (vlan % 2); |
||
312 | val = esw_r32(esw, RT305X_ESW_REG_VLANI(vlan / 2)); |
||
313 | val = (val >> s) & RT305X_ESW_VLANI_VID_M; |
||
314 | |||
315 | return val; |
||
316 | } |
||
317 | |||
318 | static void esw_set_vlan_id(struct rt305x_esw *esw, unsigned vlan, unsigned vid) |
||
319 | { |
||
320 | unsigned s; |
||
321 | |||
322 | s = RT305X_ESW_VLANI_VID_S * (vlan % 2); |
||
323 | esw_rmw(esw, |
||
324 | RT305X_ESW_REG_VLANI(vlan / 2), |
||
325 | RT305X_ESW_VLANI_VID_M << s, |
||
326 | (vid & RT305X_ESW_VLANI_VID_M) << s); |
||
327 | } |
||
328 | |||
329 | static unsigned esw_get_pvid(struct rt305x_esw *esw, unsigned port) |
||
330 | { |
||
331 | unsigned s, val; |
||
332 | |||
333 | s = RT305X_ESW_PVIDC_PVID_S * (port % 2); |
||
334 | val = esw_r32(esw, RT305X_ESW_REG_PVIDC(port / 2)); |
||
335 | return (val >> s) & RT305X_ESW_PVIDC_PVID_M; |
||
336 | } |
||
337 | |||
338 | static void esw_set_pvid(struct rt305x_esw *esw, unsigned port, unsigned pvid) |
||
339 | { |
||
340 | unsigned s; |
||
341 | |||
342 | s = RT305X_ESW_PVIDC_PVID_S * (port % 2); |
||
343 | esw_rmw(esw, |
||
344 | RT305X_ESW_REG_PVIDC(port / 2), |
||
345 | RT305X_ESW_PVIDC_PVID_M << s, |
||
346 | (pvid & RT305X_ESW_PVIDC_PVID_M) << s); |
||
347 | } |
||
348 | |||
349 | static unsigned esw_get_vmsc(struct rt305x_esw *esw, unsigned vlan) |
||
350 | { |
||
351 | unsigned s, val; |
||
352 | |||
353 | s = RT305X_ESW_VMSC_MSC_S * (vlan % 4); |
||
354 | val = esw_r32(esw, RT305X_ESW_REG_VMSC(vlan / 4)); |
||
355 | val = (val >> s) & RT305X_ESW_VMSC_MSC_M; |
||
356 | |||
357 | return val; |
||
358 | } |
||
359 | |||
360 | static void esw_set_vmsc(struct rt305x_esw *esw, unsigned vlan, unsigned msc) |
||
361 | { |
||
362 | unsigned s; |
||
363 | |||
364 | s = RT305X_ESW_VMSC_MSC_S * (vlan % 4); |
||
365 | esw_rmw(esw, |
||
366 | RT305X_ESW_REG_VMSC(vlan / 4), |
||
367 | RT305X_ESW_VMSC_MSC_M << s, |
||
368 | (msc & RT305X_ESW_VMSC_MSC_M) << s); |
||
369 | } |
||
370 | |||
371 | static unsigned esw_get_port_disable(struct rt305x_esw *esw) |
||
372 | { |
||
373 | unsigned reg; |
||
374 | |||
375 | reg = esw_r32(esw, RT305X_ESW_REG_POC0); |
||
376 | return (reg >> RT305X_ESW_POC0_DIS_PORT_S) & |
||
377 | RT305X_ESW_POC0_DIS_PORT_M; |
||
378 | } |
||
379 | |||
380 | static void esw_set_port_disable(struct rt305x_esw *esw, unsigned disable_mask) |
||
381 | { |
||
382 | unsigned old_mask; |
||
383 | unsigned enable_mask; |
||
384 | unsigned changed; |
||
385 | int i; |
||
386 | |||
387 | old_mask = esw_get_port_disable(esw); |
||
388 | changed = old_mask ^ disable_mask; |
||
389 | enable_mask = old_mask & disable_mask; |
||
390 | |||
391 | /* enable before writing to MII */ |
||
392 | esw_rmw(esw, RT305X_ESW_REG_POC0, |
||
393 | (RT305X_ESW_POC0_DIS_PORT_M << |
||
394 | RT305X_ESW_POC0_DIS_PORT_S), |
||
395 | enable_mask << RT305X_ESW_POC0_DIS_PORT_S); |
||
396 | |||
397 | for (i = 0; i < RT305X_ESW_NUM_LEDS; i++) { |
||
398 | if (!(changed & (1 << i))) |
||
399 | continue; |
||
400 | if (disable_mask & (1 << i)) { |
||
401 | /* disable */ |
||
402 | rt305x_mii_write(esw, i, MII_BMCR, |
||
403 | BMCR_PDOWN); |
||
404 | } else { |
||
405 | /* enable */ |
||
406 | rt305x_mii_write(esw, i, MII_BMCR, |
||
407 | BMCR_FULLDPLX | |
||
408 | BMCR_ANENABLE | |
||
409 | BMCR_ANRESTART | |
||
410 | BMCR_SPEED100); |
||
411 | } |
||
412 | } |
||
413 | |||
414 | /* disable after writing to MII */ |
||
415 | esw_rmw(esw, RT305X_ESW_REG_POC0, |
||
416 | (RT305X_ESW_POC0_DIS_PORT_M << |
||
417 | RT305X_ESW_POC0_DIS_PORT_S), |
||
418 | disable_mask << RT305X_ESW_POC0_DIS_PORT_S); |
||
419 | } |
||
420 | |||
421 | static void esw_set_gsc(struct rt305x_esw *esw) |
||
422 | { |
||
423 | esw_rmw(esw, RT305X_ESW_REG_SGC, |
||
424 | RT305X_ESW_GSC_BC_STROM_MASK << RT305X_ESW_GSC_BC_STROM_SHIFT, |
||
425 | esw->bc_storm_protect << RT305X_ESW_GSC_BC_STROM_SHIFT); |
||
426 | esw_rmw(esw, RT305X_ESW_REG_SGC, |
||
427 | RT305X_ESW_GSC_LED_FREQ_MASK << RT305X_ESW_GSC_LED_FREQ_SHIFT, |
||
428 | esw->led_frequency << RT305X_ESW_GSC_LED_FREQ_SHIFT); |
||
429 | } |
||
430 | |||
431 | static int esw_apply_config(struct switch_dev *dev); |
||
432 | |||
433 | static void esw_hw_init(struct rt305x_esw *esw) |
||
434 | { |
||
435 | int i; |
||
436 | u8 port_disable = 0; |
||
437 | u8 port_map = RT305X_ESW_PMAP_LLLLLL; |
||
438 | |||
439 | /* vodoo from original driver */ |
||
440 | esw_w32(esw, 0xC8A07850, RT305X_ESW_REG_FCT0); |
||
441 | esw_w32(esw, 0x00000000, RT305X_ESW_REG_SGC2); |
||
442 | /* Port priority 1 for all ports, vlan enabled. */ |
||
443 | esw_w32(esw, 0x00005555 | |
||
444 | (RT305X_ESW_PORTS_ALL << RT305X_ESW_PFC1_EN_VLAN_S), |
||
445 | RT305X_ESW_REG_PFC1); |
||
446 | |||
447 | /* Enable all ports, Back Pressure and Flow Control */ |
||
448 | esw_w32(esw, ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_BP_S) | |
||
449 | (RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_FC_S)), |
||
450 | RT305X_ESW_REG_POC0); |
||
451 | |||
452 | /* Enable Aging, and VLAN TAG removal */ |
||
453 | esw_w32(esw, ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC2_ENAGING_S) | |
||
454 | (RT305X_ESW_PORTS_NOCPU << RT305X_ESW_POC2_UNTAG_EN_S)), |
||
455 | RT305X_ESW_REG_POC2); |
||
456 | |||
457 | if (esw->reg_initval_fct2) |
||
458 | esw_w32(esw, esw->reg_initval_fct2, RT305X_ESW_REG_FCT2); |
||
459 | else |
||
460 | esw_w32(esw, 0x0002500c, RT305X_ESW_REG_FCT2); |
||
461 | |||
462 | /* 300s aging timer, max packet len 1536, broadcast storm prevention |
||
463 | * disabled, disable collision abort, mac xor48 hash, 10 packet back |
||
464 | * pressure jam, GMII disable was_transmit, back pressure disabled, |
||
465 | * 30ms led flash, unmatched IGMP as broadcast, rmc tb fault to all |
||
466 | * ports. |
||
467 | */ |
||
468 | esw_w32(esw, 0x0008a301, RT305X_ESW_REG_SGC); |
||
469 | |||
470 | /* Setup SoC Port control register */ |
||
471 | esw_w32(esw, |
||
472 | (RT305X_ESW_SOCPC_CRC_PADDING | |
||
473 | (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISUN2CPU_S) | |
||
474 | (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISMC2CPU_S) | |
||
475 | (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISBC2CPU_S)), |
||
476 | RT305X_ESW_REG_SOCPC); |
||
477 | |||
478 | /* ext phy base addr 31, enable port 5 polling, rx/tx clock skew 1, |
||
479 | * turbo mii off, rgmi 3.3v off |
||
480 | * port5: disabled |
||
481 | * port6: enabled, gige, full-duplex, rx/tx-flow-control |
||
482 | */ |
||
483 | if (esw->reg_initval_fpa2) |
||
484 | esw_w32(esw, esw->reg_initval_fpa2, RT305X_ESW_REG_FPA2); |
||
485 | else |
||
486 | esw_w32(esw, 0x3f502b28, RT305X_ESW_REG_FPA2); |
||
487 | esw_w32(esw, 0x00000000, RT305X_ESW_REG_FPA); |
||
488 | |||
489 | /* Force Link/Activity on ports */ |
||
490 | esw_w32(esw, 0x00000005, RT305X_ESW_REG_P0LED); |
||
491 | esw_w32(esw, 0x00000005, RT305X_ESW_REG_P1LED); |
||
492 | esw_w32(esw, 0x00000005, RT305X_ESW_REG_P2LED); |
||
493 | esw_w32(esw, 0x00000005, RT305X_ESW_REG_P3LED); |
||
494 | esw_w32(esw, 0x00000005, RT305X_ESW_REG_P4LED); |
||
495 | |||
496 | /* Copy disabled port configuration from device tree setup */ |
||
497 | port_disable = esw->port_disable; |
||
498 | |||
499 | /* Disable nonexistent ports by reading the switch config |
||
500 | * after having enabled all possible ports above |
||
501 | */ |
||
502 | port_disable |= esw_get_port_disable(esw); |
||
503 | |||
504 | for (i = 0; i < 6; i++) |
||
505 | esw->ports[i].disable = (port_disable & (1 << i)) != 0; |
||
506 | |||
507 | if (ralink_soc == RT305X_SOC_RT3352) { |
||
508 | /* reset EPHY */ |
||
509 | fe_reset(RT5350_RESET_EPHY); |
||
510 | |||
511 | rt305x_mii_write(esw, 0, 31, 0x8000); |
||
512 | for (i = 0; i < 5; i++) { |
||
513 | if (esw->ports[i].disable) { |
||
514 | rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN); |
||
515 | } else { |
||
516 | rt305x_mii_write(esw, i, MII_BMCR, |
||
517 | BMCR_FULLDPLX | |
||
518 | BMCR_ANENABLE | |
||
519 | BMCR_SPEED100); |
||
520 | } |
||
521 | /* TX10 waveform coefficient LSB=0 disable PHY */ |
||
522 | rt305x_mii_write(esw, i, 26, 0x1601); |
||
523 | /* TX100/TX10 AD/DA current bias */ |
||
524 | rt305x_mii_write(esw, i, 29, 0x7016); |
||
525 | /* TX100 slew rate control */ |
||
526 | rt305x_mii_write(esw, i, 30, 0x0038); |
||
527 | } |
||
528 | |||
529 | /* select global register */ |
||
530 | rt305x_mii_write(esw, 0, 31, 0x0); |
||
531 | /* enlarge agcsel threshold 3 and threshold 2 */ |
||
532 | rt305x_mii_write(esw, 0, 1, 0x4a40); |
||
533 | /* enlarge agcsel threshold 5 and threshold 4 */ |
||
534 | rt305x_mii_write(esw, 0, 2, 0x6254); |
||
535 | /* enlarge agcsel threshold */ |
||
536 | rt305x_mii_write(esw, 0, 3, 0xa17f); |
||
537 | rt305x_mii_write(esw, 0, 12, 0x7eaa); |
||
538 | /* longer TP_IDL tail length */ |
||
539 | rt305x_mii_write(esw, 0, 14, 0x65); |
||
540 | /* increased squelch pulse count threshold. */ |
||
541 | rt305x_mii_write(esw, 0, 16, 0x0684); |
||
542 | /* set TX10 signal amplitude threshold to minimum */ |
||
543 | rt305x_mii_write(esw, 0, 17, 0x0fe0); |
||
544 | /* set squelch amplitude to higher threshold */ |
||
545 | rt305x_mii_write(esw, 0, 18, 0x40ba); |
||
546 | /* tune TP_IDL tail and head waveform, enable power |
||
547 | * down slew rate control |
||
548 | */ |
||
549 | rt305x_mii_write(esw, 0, 22, 0x253f); |
||
550 | /* set PLL/Receive bias current are calibrated */ |
||
551 | rt305x_mii_write(esw, 0, 27, 0x2fda); |
||
552 | /* change PLL/Receive bias current to internal(RT3350) */ |
||
553 | rt305x_mii_write(esw, 0, 28, 0xc410); |
||
554 | /* change PLL bias current to internal(RT3052_MP3) */ |
||
555 | rt305x_mii_write(esw, 0, 29, 0x598b); |
||
556 | /* select local register */ |
||
557 | rt305x_mii_write(esw, 0, 31, 0x8000); |
||
558 | } else if (ralink_soc == RT305X_SOC_RT5350) { |
||
559 | /* reset EPHY */ |
||
560 | fe_reset(RT5350_RESET_EPHY); |
||
561 | |||
562 | /* set the led polarity */ |
||
563 | esw_w32(esw, esw->reg_led_polarity & 0x1F, |
||
564 | RT5350_EWS_REG_LED_POLARITY); |
||
565 | |||
566 | /* local registers */ |
||
567 | rt305x_mii_write(esw, 0, 31, 0x8000); |
||
568 | for (i = 0; i < 5; i++) { |
||
569 | if (esw->ports[i].disable) { |
||
570 | rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN); |
||
571 | } else { |
||
572 | rt305x_mii_write(esw, i, MII_BMCR, |
||
573 | BMCR_FULLDPLX | |
||
574 | BMCR_ANENABLE | |
||
575 | BMCR_SPEED100); |
||
576 | } |
||
577 | /* TX10 waveform coefficient LSB=0 disable PHY */ |
||
578 | rt305x_mii_write(esw, i, 26, 0x1601); |
||
579 | /* TX100/TX10 AD/DA current bias */ |
||
580 | rt305x_mii_write(esw, i, 29, 0x7015); |
||
581 | /* TX100 slew rate control */ |
||
582 | rt305x_mii_write(esw, i, 30, 0x0038); |
||
583 | } |
||
584 | |||
585 | /* global registers */ |
||
586 | rt305x_mii_write(esw, 0, 31, 0x0); |
||
587 | /* enlarge agcsel threshold 3 and threshold 2 */ |
||
588 | rt305x_mii_write(esw, 0, 1, 0x4a40); |
||
589 | /* enlarge agcsel threshold 5 and threshold 4 */ |
||
590 | rt305x_mii_write(esw, 0, 2, 0x6254); |
||
591 | /* enlarge agcsel threshold 6 */ |
||
592 | rt305x_mii_write(esw, 0, 3, 0xa17f); |
||
593 | rt305x_mii_write(esw, 0, 12, 0x7eaa); |
||
594 | /* longer TP_IDL tail length */ |
||
595 | rt305x_mii_write(esw, 0, 14, 0x65); |
||
596 | /* increased squelch pulse count threshold. */ |
||
597 | rt305x_mii_write(esw, 0, 16, 0x0684); |
||
598 | /* set TX10 signal amplitude threshold to minimum */ |
||
599 | rt305x_mii_write(esw, 0, 17, 0x0fe0); |
||
600 | /* set squelch amplitude to higher threshold */ |
||
601 | rt305x_mii_write(esw, 0, 18, 0x40ba); |
||
602 | /* tune TP_IDL tail and head waveform, enable power |
||
603 | * down slew rate control |
||
604 | */ |
||
605 | rt305x_mii_write(esw, 0, 22, 0x253f); |
||
606 | /* set PLL/Receive bias current are calibrated */ |
||
607 | rt305x_mii_write(esw, 0, 27, 0x2fda); |
||
608 | /* change PLL/Receive bias current to internal(RT3350) */ |
||
609 | rt305x_mii_write(esw, 0, 28, 0xc410); |
||
610 | /* change PLL bias current to internal(RT3052_MP3) */ |
||
611 | rt305x_mii_write(esw, 0, 29, 0x598b); |
||
612 | /* select local register */ |
||
613 | rt305x_mii_write(esw, 0, 31, 0x8000); |
||
614 | } else if (ralink_soc == MT762X_SOC_MT7628AN || ralink_soc == MT762X_SOC_MT7688) { |
||
615 | int i; |
||
616 | |||
617 | /* reset EPHY */ |
||
618 | fe_reset(RT5350_RESET_EPHY); |
||
619 | |||
620 | rt305x_mii_write(esw, 0, 31, 0x2000); /* change G2 page */ |
||
621 | rt305x_mii_write(esw, 0, 26, 0x0020); |
||
622 | |||
623 | for (i = 0; i < 5; i++) { |
||
624 | rt305x_mii_write(esw, i, 31, 0x8000); |
||
625 | rt305x_mii_write(esw, i, 0, 0x3100); |
||
626 | rt305x_mii_write(esw, i, 30, 0xa000); |
||
627 | rt305x_mii_write(esw, i, 31, 0xa000); |
||
628 | rt305x_mii_write(esw, i, 16, 0x0606); |
||
629 | rt305x_mii_write(esw, i, 23, 0x0f0e); |
||
630 | rt305x_mii_write(esw, i, 24, 0x1610); |
||
631 | rt305x_mii_write(esw, i, 30, 0x1f15); |
||
632 | rt305x_mii_write(esw, i, 28, 0x6111); |
||
633 | rt305x_mii_write(esw, i, 31, 0x2000); |
||
634 | rt305x_mii_write(esw, i, 26, 0x0000); |
||
635 | } |
||
636 | |||
637 | /* 100Base AOI setting */ |
||
638 | rt305x_mii_write(esw, 0, 31, 0x5000); |
||
639 | rt305x_mii_write(esw, 0, 19, 0x004a); |
||
640 | rt305x_mii_write(esw, 0, 20, 0x015a); |
||
641 | rt305x_mii_write(esw, 0, 21, 0x00ee); |
||
642 | rt305x_mii_write(esw, 0, 22, 0x0033); |
||
643 | rt305x_mii_write(esw, 0, 23, 0x020a); |
||
644 | rt305x_mii_write(esw, 0, 24, 0x0000); |
||
645 | rt305x_mii_write(esw, 0, 25, 0x024a); |
||
646 | rt305x_mii_write(esw, 0, 26, 0x035a); |
||
647 | rt305x_mii_write(esw, 0, 27, 0x02ee); |
||
648 | rt305x_mii_write(esw, 0, 28, 0x0233); |
||
649 | rt305x_mii_write(esw, 0, 29, 0x000a); |
||
650 | rt305x_mii_write(esw, 0, 30, 0x0000); |
||
651 | } else { |
||
652 | rt305x_mii_write(esw, 0, 31, 0x8000); |
||
653 | for (i = 0; i < 5; i++) { |
||
654 | if (esw->ports[i].disable) { |
||
655 | rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN); |
||
656 | } else { |
||
657 | rt305x_mii_write(esw, i, MII_BMCR, |
||
658 | BMCR_FULLDPLX | |
||
659 | BMCR_ANENABLE | |
||
660 | BMCR_SPEED100); |
||
661 | } |
||
662 | /* TX10 waveform coefficient */ |
||
663 | rt305x_mii_write(esw, i, 26, 0x1601); |
||
664 | /* TX100/TX10 AD/DA current bias */ |
||
665 | rt305x_mii_write(esw, i, 29, 0x7058); |
||
666 | /* TX100 slew rate control */ |
||
667 | rt305x_mii_write(esw, i, 30, 0x0018); |
||
668 | } |
||
669 | |||
670 | /* PHY IOT */ |
||
671 | /* select global register */ |
||
672 | rt305x_mii_write(esw, 0, 31, 0x0); |
||
673 | /* tune TP_IDL tail and head waveform */ |
||
674 | rt305x_mii_write(esw, 0, 22, 0x052f); |
||
675 | /* set TX10 signal amplitude threshold to minimum */ |
||
676 | rt305x_mii_write(esw, 0, 17, 0x0fe0); |
||
677 | /* set squelch amplitude to higher threshold */ |
||
678 | rt305x_mii_write(esw, 0, 18, 0x40ba); |
||
679 | /* longer TP_IDL tail length */ |
||
680 | rt305x_mii_write(esw, 0, 14, 0x65); |
||
681 | /* select local register */ |
||
682 | rt305x_mii_write(esw, 0, 31, 0x8000); |
||
683 | } |
||
684 | |||
685 | if (esw->port_map) |
||
686 | port_map = esw->port_map; |
||
687 | else |
||
688 | port_map = RT305X_ESW_PMAP_LLLLLL; |
||
689 | |||
690 | /* Unused HW feature, but still nice to be consistent here... |
||
691 | * This is also exported to userspace ('lan' attribute) so it's |
||
692 | * conveniently usable to decide which ports go into the wan vlan by |
||
693 | * default. |
||
694 | */ |
||
695 | esw_rmw(esw, RT305X_ESW_REG_SGC2, |
||
696 | RT305X_ESW_SGC2_LAN_PMAP_M << RT305X_ESW_SGC2_LAN_PMAP_S, |
||
697 | port_map << RT305X_ESW_SGC2_LAN_PMAP_S); |
||
698 | |||
699 | /* make the switch leds blink */ |
||
700 | for (i = 0; i < RT305X_ESW_NUM_LEDS; i++) |
||
701 | esw->ports[i].led = 0x05; |
||
702 | |||
703 | /* Apply the empty config. */ |
||
704 | esw_apply_config(&esw->swdev); |
||
705 | |||
706 | /* Only unmask the port change interrupt */ |
||
707 | esw_w32(esw, ~RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_IMR); |
||
708 | } |
||
709 | |||
710 | static irqreturn_t esw_interrupt(int irq, void *_esw) |
||
711 | { |
||
712 | struct rt305x_esw *esw = (struct rt305x_esw *)_esw; |
||
713 | u32 status; |
||
714 | |||
715 | status = esw_r32(esw, RT305X_ESW_REG_ISR); |
||
716 | if (status & RT305X_ESW_PORT_ST_CHG) { |
||
717 | u32 link = esw_r32(esw, RT305X_ESW_REG_POA); |
||
718 | |||
719 | link >>= RT305X_ESW_POA_LINK_SHIFT; |
||
720 | link &= RT305X_ESW_POA_LINK_MASK; |
||
721 | dev_info(esw->dev, "link changed 0x%02X\n", link); |
||
722 | } |
||
723 | esw_w32(esw, status, RT305X_ESW_REG_ISR); |
||
724 | |||
725 | return IRQ_HANDLED; |
||
726 | } |
||
727 | |||
728 | static int esw_apply_config(struct switch_dev *dev) |
||
729 | { |
||
730 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
731 | int i; |
||
732 | u8 disable = 0; |
||
733 | u8 doubletag = 0; |
||
734 | u8 en_vlan = 0; |
||
735 | u8 untag = 0; |
||
736 | |||
737 | for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) { |
||
738 | u32 vid, vmsc; |
||
739 | if (esw->global_vlan_enable) { |
||
740 | vid = esw->vlans[i].vid; |
||
741 | vmsc = esw->vlans[i].ports; |
||
742 | } else { |
||
743 | vid = RT305X_ESW_VLAN_NONE; |
||
744 | vmsc = RT305X_ESW_PORTS_NONE; |
||
745 | } |
||
746 | esw_set_vlan_id(esw, i, vid); |
||
747 | esw_set_vmsc(esw, i, vmsc); |
||
748 | } |
||
749 | |||
750 | for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) { |
||
751 | u32 pvid; |
||
752 | disable |= esw->ports[i].disable << i; |
||
753 | if (esw->global_vlan_enable) { |
||
754 | doubletag |= esw->ports[i].doubletag << i; |
||
755 | en_vlan |= 1 << i; |
||
756 | untag |= esw->ports[i].untag << i; |
||
757 | pvid = esw->ports[i].pvid; |
||
758 | } else { |
||
759 | int x = esw->alt_vlan_disable ? 0 : 1; |
||
760 | doubletag |= x << i; |
||
761 | en_vlan |= x << i; |
||
762 | untag |= x << i; |
||
763 | pvid = 0; |
||
764 | } |
||
765 | esw_set_pvid(esw, i, pvid); |
||
766 | if (i < RT305X_ESW_NUM_LEDS) |
||
767 | esw_w32(esw, esw->ports[i].led, |
||
768 | RT305X_ESW_REG_P0LED + 4*i); |
||
769 | } |
||
770 | |||
771 | esw_set_gsc(esw); |
||
772 | esw_set_port_disable(esw, disable); |
||
773 | esw_rmw(esw, RT305X_ESW_REG_SGC2, |
||
774 | (RT305X_ESW_SGC2_DOUBLE_TAG_M << |
||
775 | RT305X_ESW_SGC2_DOUBLE_TAG_S), |
||
776 | doubletag << RT305X_ESW_SGC2_DOUBLE_TAG_S); |
||
777 | esw_rmw(esw, RT305X_ESW_REG_PFC1, |
||
778 | RT305X_ESW_PFC1_EN_VLAN_M << RT305X_ESW_PFC1_EN_VLAN_S, |
||
779 | en_vlan << RT305X_ESW_PFC1_EN_VLAN_S); |
||
780 | esw_rmw(esw, RT305X_ESW_REG_POC2, |
||
781 | RT305X_ESW_POC2_UNTAG_EN_M << RT305X_ESW_POC2_UNTAG_EN_S, |
||
782 | untag << RT305X_ESW_POC2_UNTAG_EN_S); |
||
783 | |||
784 | if (!esw->global_vlan_enable) { |
||
785 | /* |
||
786 | * Still need to put all ports into vlan 0 or they'll be |
||
787 | * isolated. |
||
788 | * NOTE: vlan 0 is special, no vlan tag is prepended |
||
789 | */ |
||
790 | esw_set_vlan_id(esw, 0, 0); |
||
791 | esw_set_vmsc(esw, 0, RT305X_ESW_PORTS_ALL); |
||
792 | } |
||
793 | |||
794 | return 0; |
||
795 | } |
||
796 | |||
797 | static int esw_reset_switch(struct switch_dev *dev) |
||
798 | { |
||
799 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
800 | |||
801 | esw->global_vlan_enable = 0; |
||
802 | memset(esw->ports, 0, sizeof(esw->ports)); |
||
803 | memset(esw->vlans, 0, sizeof(esw->vlans)); |
||
804 | esw_hw_init(esw); |
||
805 | |||
806 | return 0; |
||
807 | } |
||
808 | |||
809 | static int esw_get_vlan_enable(struct switch_dev *dev, |
||
810 | const struct switch_attr *attr, |
||
811 | struct switch_val *val) |
||
812 | { |
||
813 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
814 | |||
815 | val->value.i = esw->global_vlan_enable; |
||
816 | |||
817 | return 0; |
||
818 | } |
||
819 | |||
820 | static int esw_set_vlan_enable(struct switch_dev *dev, |
||
821 | const struct switch_attr *attr, |
||
822 | struct switch_val *val) |
||
823 | { |
||
824 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
825 | |||
826 | esw->global_vlan_enable = val->value.i != 0; |
||
827 | |||
828 | return 0; |
||
829 | } |
||
830 | |||
831 | static int esw_get_alt_vlan_disable(struct switch_dev *dev, |
||
832 | const struct switch_attr *attr, |
||
833 | struct switch_val *val) |
||
834 | { |
||
835 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
836 | |||
837 | val->value.i = esw->alt_vlan_disable; |
||
838 | |||
839 | return 0; |
||
840 | } |
||
841 | |||
842 | static int esw_set_alt_vlan_disable(struct switch_dev *dev, |
||
843 | const struct switch_attr *attr, |
||
844 | struct switch_val *val) |
||
845 | { |
||
846 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
847 | |||
848 | esw->alt_vlan_disable = val->value.i != 0; |
||
849 | |||
850 | return 0; |
||
851 | } |
||
852 | |||
853 | static int |
||
854 | rt305x_esw_set_bc_status(struct switch_dev *dev, |
||
855 | const struct switch_attr *attr, |
||
856 | struct switch_val *val) |
||
857 | { |
||
858 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
859 | |||
860 | esw->bc_storm_protect = val->value.i & RT305X_ESW_GSC_BC_STROM_MASK; |
||
861 | |||
862 | return 0; |
||
863 | } |
||
864 | |||
865 | static int |
||
866 | rt305x_esw_get_bc_status(struct switch_dev *dev, |
||
867 | const struct switch_attr *attr, |
||
868 | struct switch_val *val) |
||
869 | { |
||
870 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
871 | |||
872 | val->value.i = esw->bc_storm_protect; |
||
873 | |||
874 | return 0; |
||
875 | } |
||
876 | |||
877 | static int |
||
878 | rt305x_esw_set_led_freq(struct switch_dev *dev, |
||
879 | const struct switch_attr *attr, |
||
880 | struct switch_val *val) |
||
881 | { |
||
882 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
883 | |||
884 | esw->led_frequency = val->value.i & RT305X_ESW_GSC_LED_FREQ_MASK; |
||
885 | |||
886 | return 0; |
||
887 | } |
||
888 | |||
889 | static int |
||
890 | rt305x_esw_get_led_freq(struct switch_dev *dev, |
||
891 | const struct switch_attr *attr, |
||
892 | struct switch_val *val) |
||
893 | { |
||
894 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
895 | |||
896 | val->value.i = esw->led_frequency; |
||
897 | |||
898 | return 0; |
||
899 | } |
||
900 | |||
901 | static int esw_get_port_link(struct switch_dev *dev, |
||
902 | int port, |
||
903 | struct switch_port_link *link) |
||
904 | { |
||
905 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
906 | u32 speed, poa; |
||
907 | |||
908 | if (port < 0 || port >= RT305X_ESW_NUM_PORTS) |
||
909 | return -EINVAL; |
||
910 | |||
911 | poa = esw_r32(esw, RT305X_ESW_REG_POA) >> port; |
||
912 | |||
913 | link->link = (poa >> RT305X_ESW_LINK_S) & 1; |
||
914 | link->duplex = (poa >> RT305X_ESW_DUPLEX_S) & 1; |
||
915 | if (port < RT305X_ESW_NUM_LEDS) { |
||
916 | speed = (poa >> RT305X_ESW_SPD_S) & 1; |
||
917 | } else { |
||
918 | if (port == RT305X_ESW_NUM_PORTS - 1) |
||
919 | poa >>= 1; |
||
920 | speed = (poa >> RT305X_ESW_SPD_S) & 3; |
||
921 | } |
||
922 | switch (speed) { |
||
923 | case 0: |
||
924 | link->speed = SWITCH_PORT_SPEED_10; |
||
925 | break; |
||
926 | case 1: |
||
927 | link->speed = SWITCH_PORT_SPEED_100; |
||
928 | break; |
||
929 | case 2: |
||
930 | case 3: /* forced gige speed can be 2 or 3 */ |
||
931 | link->speed = SWITCH_PORT_SPEED_1000; |
||
932 | break; |
||
933 | default: |
||
934 | link->speed = SWITCH_PORT_SPEED_UNKNOWN; |
||
935 | break; |
||
936 | } |
||
937 | |||
938 | return 0; |
||
939 | } |
||
940 | |||
941 | static int esw_get_port_bool(struct switch_dev *dev, |
||
942 | const struct switch_attr *attr, |
||
943 | struct switch_val *val) |
||
944 | { |
||
945 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
946 | int idx = val->port_vlan; |
||
947 | u32 x, reg, shift; |
||
948 | |||
949 | if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS) |
||
950 | return -EINVAL; |
||
951 | |||
952 | switch (attr->id) { |
||
953 | case RT305X_ESW_ATTR_PORT_DISABLE: |
||
954 | reg = RT305X_ESW_REG_POC0; |
||
955 | shift = RT305X_ESW_POC0_DIS_PORT_S; |
||
956 | break; |
||
957 | case RT305X_ESW_ATTR_PORT_DOUBLETAG: |
||
958 | reg = RT305X_ESW_REG_SGC2; |
||
959 | shift = RT305X_ESW_SGC2_DOUBLE_TAG_S; |
||
960 | break; |
||
961 | case RT305X_ESW_ATTR_PORT_UNTAG: |
||
962 | reg = RT305X_ESW_REG_POC2; |
||
963 | shift = RT305X_ESW_POC2_UNTAG_EN_S; |
||
964 | break; |
||
965 | case RT305X_ESW_ATTR_PORT_LAN: |
||
966 | reg = RT305X_ESW_REG_SGC2; |
||
967 | shift = RT305X_ESW_SGC2_LAN_PMAP_S; |
||
968 | if (idx >= RT305X_ESW_NUM_LANWAN) |
||
969 | return -EINVAL; |
||
970 | break; |
||
971 | default: |
||
972 | return -EINVAL; |
||
973 | } |
||
974 | |||
975 | x = esw_r32(esw, reg); |
||
976 | val->value.i = (x >> (idx + shift)) & 1; |
||
977 | |||
978 | return 0; |
||
979 | } |
||
980 | |||
981 | static int esw_set_port_bool(struct switch_dev *dev, |
||
982 | const struct switch_attr *attr, |
||
983 | struct switch_val *val) |
||
984 | { |
||
985 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
986 | int idx = val->port_vlan; |
||
987 | |||
988 | if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS || |
||
989 | val->value.i < 0 || val->value.i > 1) |
||
990 | return -EINVAL; |
||
991 | |||
992 | switch (attr->id) { |
||
993 | case RT305X_ESW_ATTR_PORT_DISABLE: |
||
994 | esw->ports[idx].disable = val->value.i; |
||
995 | break; |
||
996 | case RT305X_ESW_ATTR_PORT_DOUBLETAG: |
||
997 | esw->ports[idx].doubletag = val->value.i; |
||
998 | break; |
||
999 | case RT305X_ESW_ATTR_PORT_UNTAG: |
||
1000 | esw->ports[idx].untag = val->value.i; |
||
1001 | break; |
||
1002 | default: |
||
1003 | return -EINVAL; |
||
1004 | } |
||
1005 | |||
1006 | return 0; |
||
1007 | } |
||
1008 | |||
1009 | static int esw_get_port_recv_badgood(struct switch_dev *dev, |
||
1010 | const struct switch_attr *attr, |
||
1011 | struct switch_val *val) |
||
1012 | { |
||
1013 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
1014 | int idx = val->port_vlan; |
||
1015 | int shift = attr->id == RT305X_ESW_ATTR_PORT_RECV_GOOD ? 0 : 16; |
||
1016 | u32 reg; |
||
1017 | |||
1018 | if (idx < 0 || idx >= RT305X_ESW_NUM_LANWAN) |
||
1019 | return -EINVAL; |
||
1020 | reg = esw_r32(esw, RT305X_ESW_REG_PXPC(idx)); |
||
1021 | val->value.i = (reg >> shift) & 0xffff; |
||
1022 | |||
1023 | return 0; |
||
1024 | } |
||
1025 | |||
1026 | static int |
||
1027 | esw_get_port_tr_badgood(struct switch_dev *dev, |
||
1028 | const struct switch_attr *attr, |
||
1029 | struct switch_val *val) |
||
1030 | { |
||
1031 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
1032 | |||
1033 | int idx = val->port_vlan; |
||
1034 | int shift = attr->id == RT5350_ESW_ATTR_PORT_TR_GOOD ? 0 : 16; |
||
1035 | u32 reg; |
||
1036 | |||
1037 | if ((ralink_soc != RT305X_SOC_RT5350) && (ralink_soc != MT762X_SOC_MT7628AN) && (ralink_soc != MT762X_SOC_MT7688)) |
||
1038 | return -EINVAL; |
||
1039 | |||
1040 | if (idx < 0 || idx >= RT305X_ESW_NUM_LANWAN) |
||
1041 | return -EINVAL; |
||
1042 | |||
1043 | reg = esw_r32(esw, RT5350_ESW_REG_PXTPC(idx)); |
||
1044 | val->value.i = (reg >> shift) & 0xffff; |
||
1045 | |||
1046 | return 0; |
||
1047 | } |
||
1048 | |||
1049 | static int esw_get_port_led(struct switch_dev *dev, |
||
1050 | const struct switch_attr *attr, |
||
1051 | struct switch_val *val) |
||
1052 | { |
||
1053 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
1054 | int idx = val->port_vlan; |
||
1055 | |||
1056 | if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS || |
||
1057 | idx >= RT305X_ESW_NUM_LEDS) |
||
1058 | return -EINVAL; |
||
1059 | |||
1060 | val->value.i = esw_r32(esw, RT305X_ESW_REG_P0LED + 4*idx); |
||
1061 | |||
1062 | return 0; |
||
1063 | } |
||
1064 | |||
1065 | static int esw_set_port_led(struct switch_dev *dev, |
||
1066 | const struct switch_attr *attr, |
||
1067 | struct switch_val *val) |
||
1068 | { |
||
1069 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
1070 | int idx = val->port_vlan; |
||
1071 | |||
1072 | if (idx < 0 || idx >= RT305X_ESW_NUM_LEDS) |
||
1073 | return -EINVAL; |
||
1074 | |||
1075 | esw->ports[idx].led = val->value.i; |
||
1076 | |||
1077 | return 0; |
||
1078 | } |
||
1079 | |||
1080 | static int esw_get_port_pvid(struct switch_dev *dev, int port, int *val) |
||
1081 | { |
||
1082 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
1083 | |||
1084 | if (port >= RT305X_ESW_NUM_PORTS) |
||
1085 | return -EINVAL; |
||
1086 | |||
1087 | *val = esw_get_pvid(esw, port); |
||
1088 | |||
1089 | return 0; |
||
1090 | } |
||
1091 | |||
1092 | static int esw_set_port_pvid(struct switch_dev *dev, int port, int val) |
||
1093 | { |
||
1094 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
1095 | |||
1096 | if (port >= RT305X_ESW_NUM_PORTS) |
||
1097 | return -EINVAL; |
||
1098 | |||
1099 | esw->ports[port].pvid = val; |
||
1100 | |||
1101 | return 0; |
||
1102 | } |
||
1103 | |||
1104 | static int esw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val) |
||
1105 | { |
||
1106 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
1107 | u32 vmsc, poc2; |
||
1108 | int vlan_idx = -1; |
||
1109 | int i; |
||
1110 | |||
1111 | val->len = 0; |
||
1112 | |||
1113 | if (val->port_vlan < 0 || val->port_vlan >= RT305X_ESW_NUM_VIDS) |
||
1114 | return -EINVAL; |
||
1115 | |||
1116 | /* valid vlan? */ |
||
1117 | for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) { |
||
1118 | if (esw_get_vlan_id(esw, i) == val->port_vlan && |
||
1119 | esw_get_vmsc(esw, i) != RT305X_ESW_PORTS_NONE) { |
||
1120 | vlan_idx = i; |
||
1121 | break; |
||
1122 | } |
||
1123 | } |
||
1124 | |||
1125 | if (vlan_idx == -1) |
||
1126 | return -EINVAL; |
||
1127 | |||
1128 | vmsc = esw_get_vmsc(esw, vlan_idx); |
||
1129 | poc2 = esw_r32(esw, RT305X_ESW_REG_POC2); |
||
1130 | |||
1131 | for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) { |
||
1132 | struct switch_port *p; |
||
1133 | int port_mask = 1 << i; |
||
1134 | |||
1135 | if (!(vmsc & port_mask)) |
||
1136 | continue; |
||
1137 | |||
1138 | p = &val->value.ports[val->len++]; |
||
1139 | p->id = i; |
||
1140 | if (poc2 & (port_mask << RT305X_ESW_POC2_UNTAG_EN_S)) |
||
1141 | p->flags = 0; |
||
1142 | else |
||
1143 | p->flags = 1 << SWITCH_PORT_FLAG_TAGGED; |
||
1144 | } |
||
1145 | |||
1146 | return 0; |
||
1147 | } |
||
1148 | |||
1149 | static int esw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val) |
||
1150 | { |
||
1151 | struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); |
||
1152 | int ports; |
||
1153 | int vlan_idx = -1; |
||
1154 | int i; |
||
1155 | |||
1156 | if (val->port_vlan < 0 || val->port_vlan >= RT305X_ESW_NUM_VIDS || |
||
1157 | val->len > RT305X_ESW_NUM_PORTS) |
||
1158 | return -EINVAL; |
||
1159 | |||
1160 | /* one of the already defined vlans? */ |
||
1161 | for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) { |
||
1162 | if (esw->vlans[i].vid == val->port_vlan && |
||
1163 | esw->vlans[i].ports != RT305X_ESW_PORTS_NONE) { |
||
1164 | vlan_idx = i; |
||
1165 | break; |
||
1166 | } |
||
1167 | } |
||
1168 | |||
1169 | /* select a free slot */ |
||
1170 | for (i = 0; vlan_idx == -1 && i < RT305X_ESW_NUM_VLANS; i++) { |
||
1171 | if (esw->vlans[i].ports == RT305X_ESW_PORTS_NONE) |
||
1172 | vlan_idx = i; |
||
1173 | } |
||
1174 | |||
1175 | /* bail if all slots are in use */ |
||
1176 | if (vlan_idx == -1) |
||
1177 | return -EINVAL; |
||
1178 | |||
1179 | ports = RT305X_ESW_PORTS_NONE; |
||
1180 | for (i = 0; i < val->len; i++) { |
||
1181 | struct switch_port *p = &val->value.ports[i]; |
||
1182 | int port_mask = 1 << p->id; |
||
1183 | bool untagged = !(p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)); |
||
1184 | |||
1185 | if (p->id >= RT305X_ESW_NUM_PORTS) |
||
1186 | return -EINVAL; |
||
1187 | |||
1188 | ports |= port_mask; |
||
1189 | esw->ports[p->id].untag = untagged; |
||
1190 | } |
||
1191 | esw->vlans[vlan_idx].ports = ports; |
||
1192 | if (ports == RT305X_ESW_PORTS_NONE) |
||
1193 | esw->vlans[vlan_idx].vid = RT305X_ESW_VLAN_NONE; |
||
1194 | else |
||
1195 | esw->vlans[vlan_idx].vid = val->port_vlan; |
||
1196 | |||
1197 | return 0; |
||
1198 | } |
||
1199 | |||
1200 | static const struct switch_attr esw_global[] = { |
||
1201 | { |
||
1202 | .type = SWITCH_TYPE_INT, |
||
1203 | .name = "enable_vlan", |
||
1204 | .description = "VLAN mode (1:enabled)", |
||
1205 | .max = 1, |
||
1206 | .id = RT305X_ESW_ATTR_ENABLE_VLAN, |
||
1207 | .get = esw_get_vlan_enable, |
||
1208 | .set = esw_set_vlan_enable, |
||
1209 | }, |
||
1210 | { |
||
1211 | .type = SWITCH_TYPE_INT, |
||
1212 | .name = "alternate_vlan_disable", |
||
1213 | .description = "Use en_vlan instead of doubletag to disable" |
||
1214 | " VLAN mode", |
||
1215 | .max = 1, |
||
1216 | .id = RT305X_ESW_ATTR_ALT_VLAN_DISABLE, |
||
1217 | .get = esw_get_alt_vlan_disable, |
||
1218 | .set = esw_set_alt_vlan_disable, |
||
1219 | }, |
||
1220 | { |
||
1221 | .type = SWITCH_TYPE_INT, |
||
1222 | .name = "bc_storm_protect", |
||
1223 | .description = "Global broadcast storm protection (0:Disable, 1:64 blocks, 2:96 blocks, 3:128 blocks)", |
||
1224 | .max = 3, |
||
1225 | .id = RT305X_ESW_ATTR_BC_STATUS, |
||
1226 | .get = rt305x_esw_get_bc_status, |
||
1227 | .set = rt305x_esw_set_bc_status, |
||
1228 | }, |
||
1229 | { |
||
1230 | .type = SWITCH_TYPE_INT, |
||
1231 | .name = "led_frequency", |
||
1232 | .description = "LED Flash frequency (0:30mS, 1:60mS, 2:240mS, 3:480mS)", |
||
1233 | .max = 3, |
||
1234 | .id = RT305X_ESW_ATTR_LED_FREQ, |
||
1235 | .get = rt305x_esw_get_led_freq, |
||
1236 | .set = rt305x_esw_set_led_freq, |
||
1237 | } |
||
1238 | }; |
||
1239 | |||
1240 | static const struct switch_attr esw_port[] = { |
||
1241 | { |
||
1242 | .type = SWITCH_TYPE_INT, |
||
1243 | .name = "disable", |
||
1244 | .description = "Port state (1:disabled)", |
||
1245 | .max = 1, |
||
1246 | .id = RT305X_ESW_ATTR_PORT_DISABLE, |
||
1247 | .get = esw_get_port_bool, |
||
1248 | .set = esw_set_port_bool, |
||
1249 | }, |
||
1250 | { |
||
1251 | .type = SWITCH_TYPE_INT, |
||
1252 | .name = "doubletag", |
||
1253 | .description = "Double tagging for incoming vlan packets " |
||
1254 | "(1:enabled)", |
||
1255 | .max = 1, |
||
1256 | .id = RT305X_ESW_ATTR_PORT_DOUBLETAG, |
||
1257 | .get = esw_get_port_bool, |
||
1258 | .set = esw_set_port_bool, |
||
1259 | }, |
||
1260 | { |
||
1261 | .type = SWITCH_TYPE_INT, |
||
1262 | .name = "untag", |
||
1263 | .description = "Untag (1:strip outgoing vlan tag)", |
||
1264 | .max = 1, |
||
1265 | .id = RT305X_ESW_ATTR_PORT_UNTAG, |
||
1266 | .get = esw_get_port_bool, |
||
1267 | .set = esw_set_port_bool, |
||
1268 | }, |
||
1269 | { |
||
1270 | .type = SWITCH_TYPE_INT, |
||
1271 | .name = "led", |
||
1272 | .description = "LED mode (0:link, 1:100m, 2:duplex, 3:activity," |
||
1273 | " 4:collision, 5:linkact, 6:duplcoll, 7:10mact," |
||
1274 | " 8:100mact, 10:blink, 11:off, 12:on)", |
||
1275 | .max = 15, |
||
1276 | .id = RT305X_ESW_ATTR_PORT_LED, |
||
1277 | .get = esw_get_port_led, |
||
1278 | .set = esw_set_port_led, |
||
1279 | }, |
||
1280 | { |
||
1281 | .type = SWITCH_TYPE_INT, |
||
1282 | .name = "lan", |
||
1283 | .description = "HW port group (0:wan, 1:lan)", |
||
1284 | .max = 1, |
||
1285 | .id = RT305X_ESW_ATTR_PORT_LAN, |
||
1286 | .get = esw_get_port_bool, |
||
1287 | }, |
||
1288 | { |
||
1289 | .type = SWITCH_TYPE_INT, |
||
1290 | .name = "recv_bad", |
||
1291 | .description = "Receive bad packet counter", |
||
1292 | .id = RT305X_ESW_ATTR_PORT_RECV_BAD, |
||
1293 | .get = esw_get_port_recv_badgood, |
||
1294 | }, |
||
1295 | { |
||
1296 | .type = SWITCH_TYPE_INT, |
||
1297 | .name = "recv_good", |
||
1298 | .description = "Receive good packet counter", |
||
1299 | .id = RT305X_ESW_ATTR_PORT_RECV_GOOD, |
||
1300 | .get = esw_get_port_recv_badgood, |
||
1301 | }, |
||
1302 | { |
||
1303 | .type = SWITCH_TYPE_INT, |
||
1304 | .name = "tr_bad", |
||
1305 | |||
1306 | .description = "Transmit bad packet counter. rt5350 only", |
||
1307 | .id = RT5350_ESW_ATTR_PORT_TR_BAD, |
||
1308 | .get = esw_get_port_tr_badgood, |
||
1309 | }, |
||
1310 | { |
||
1311 | .type = SWITCH_TYPE_INT, |
||
1312 | .name = "tr_good", |
||
1313 | |||
1314 | .description = "Transmit good packet counter. rt5350 only", |
||
1315 | .id = RT5350_ESW_ATTR_PORT_TR_GOOD, |
||
1316 | .get = esw_get_port_tr_badgood, |
||
1317 | }, |
||
1318 | }; |
||
1319 | |||
1320 | static const struct switch_attr esw_vlan[] = { |
||
1321 | }; |
||
1322 | |||
1323 | static const struct switch_dev_ops esw_ops = { |
||
1324 | .attr_global = { |
||
1325 | .attr = esw_global, |
||
1326 | .n_attr = ARRAY_SIZE(esw_global), |
||
1327 | }, |
||
1328 | .attr_port = { |
||
1329 | .attr = esw_port, |
||
1330 | .n_attr = ARRAY_SIZE(esw_port), |
||
1331 | }, |
||
1332 | .attr_vlan = { |
||
1333 | .attr = esw_vlan, |
||
1334 | .n_attr = ARRAY_SIZE(esw_vlan), |
||
1335 | }, |
||
1336 | .get_vlan_ports = esw_get_vlan_ports, |
||
1337 | .set_vlan_ports = esw_set_vlan_ports, |
||
1338 | .get_port_pvid = esw_get_port_pvid, |
||
1339 | .set_port_pvid = esw_set_port_pvid, |
||
1340 | .get_port_link = esw_get_port_link, |
||
1341 | .apply_config = esw_apply_config, |
||
1342 | .reset_switch = esw_reset_switch, |
||
1343 | }; |
||
1344 | |||
1345 | static int esw_probe(struct platform_device *pdev) |
||
1346 | { |
||
1347 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
||
1348 | struct device_node *np = pdev->dev.of_node; |
||
1349 | const __be32 *port_map, *port_disable, *reg_init; |
||
1350 | struct switch_dev *swdev; |
||
1351 | struct rt305x_esw *esw; |
||
1352 | int ret; |
||
1353 | |||
1354 | esw = devm_kzalloc(&pdev->dev, sizeof(*esw), GFP_KERNEL); |
||
1355 | if (!esw) |
||
1356 | return -ENOMEM; |
||
1357 | |||
1358 | esw->dev = &pdev->dev; |
||
1359 | esw->irq = irq_of_parse_and_map(np, 0); |
||
1360 | esw->base = devm_ioremap_resource(&pdev->dev, res); |
||
1361 | if (IS_ERR(esw->base)) |
||
1362 | return PTR_ERR(esw->base); |
||
1363 | |||
1364 | port_map = of_get_property(np, "mediatek,portmap", NULL); |
||
1365 | if (port_map) |
||
1366 | esw->port_map = be32_to_cpu(*port_map); |
||
1367 | |||
1368 | port_disable = of_get_property(np, "mediatek,portdisable", NULL); |
||
1369 | if (port_disable) |
||
1370 | esw->port_disable = be32_to_cpu(*port_disable); |
||
1371 | |||
1372 | reg_init = of_get_property(np, "ralink,fct2", NULL); |
||
1373 | if (reg_init) |
||
1374 | esw->reg_initval_fct2 = be32_to_cpu(*reg_init); |
||
1375 | |||
1376 | reg_init = of_get_property(np, "ralink,fpa2", NULL); |
||
1377 | if (reg_init) |
||
1378 | esw->reg_initval_fpa2 = be32_to_cpu(*reg_init); |
||
1379 | |||
1380 | reg_init = of_get_property(np, "mediatek,led_polarity", NULL); |
||
1381 | if (reg_init) |
||
1382 | esw->reg_led_polarity = be32_to_cpu(*reg_init); |
||
1383 | |||
1384 | swdev = &esw->swdev; |
||
1385 | swdev->of_node = pdev->dev.of_node; |
||
1386 | swdev->name = "rt305x-esw"; |
||
1387 | swdev->alias = "rt305x"; |
||
1388 | swdev->cpu_port = RT305X_ESW_PORT6; |
||
1389 | swdev->ports = RT305X_ESW_NUM_PORTS; |
||
1390 | swdev->vlans = RT305X_ESW_NUM_VIDS; |
||
1391 | swdev->ops = &esw_ops; |
||
1392 | |||
1393 | ret = register_switch(swdev, NULL); |
||
1394 | if (ret < 0) { |
||
1395 | dev_err(&pdev->dev, "register_switch failed\n"); |
||
1396 | return ret; |
||
1397 | } |
||
1398 | |||
1399 | platform_set_drvdata(pdev, esw); |
||
1400 | |||
1401 | spin_lock_init(&esw->reg_rw_lock); |
||
1402 | |||
1403 | esw_hw_init(esw); |
||
1404 | |||
1405 | reg_init = of_get_property(np, "ralink,rgmii", NULL); |
||
1406 | if (reg_init && be32_to_cpu(*reg_init) == 1) { |
||
1407 | /* |
||
1408 | * External switch connected to RGMII interface. |
||
1409 | * Unregister the switch device after initialization. |
||
1410 | */ |
||
1411 | dev_err(&pdev->dev, "RGMII mode, not exporting switch device.\n"); |
||
1412 | unregister_switch(&esw->swdev); |
||
1413 | platform_set_drvdata(pdev, NULL); |
||
1414 | return -ENODEV; |
||
1415 | } |
||
1416 | |||
1417 | ret = devm_request_irq(&pdev->dev, esw->irq, esw_interrupt, 0, "esw", |
||
1418 | esw); |
||
1419 | |||
1420 | if (!ret) { |
||
1421 | esw_w32(esw, RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_ISR); |
||
1422 | esw_w32(esw, ~RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_IMR); |
||
1423 | } |
||
1424 | |||
1425 | return ret; |
||
1426 | } |
||
1427 | |||
1428 | static int esw_remove(struct platform_device *pdev) |
||
1429 | { |
||
1430 | struct rt305x_esw *esw = platform_get_drvdata(pdev); |
||
1431 | |||
1432 | if (esw) { |
||
1433 | esw_w32(esw, ~0, RT305X_ESW_REG_IMR); |
||
1434 | platform_set_drvdata(pdev, NULL); |
||
1435 | } |
||
1436 | |||
1437 | return 0; |
||
1438 | } |
||
1439 | |||
1440 | static const struct of_device_id ralink_esw_match[] = { |
||
1441 | { .compatible = "ralink,rt3050-esw" }, |
||
1442 | {}, |
||
1443 | }; |
||
1444 | MODULE_DEVICE_TABLE(of, ralink_esw_match); |
||
1445 | |||
1446 | static struct platform_driver esw_driver = { |
||
1447 | .probe = esw_probe, |
||
1448 | .remove = esw_remove, |
||
1449 | .driver = { |
||
1450 | .name = "rt3050-esw", |
||
1451 | .owner = THIS_MODULE, |
||
1452 | .of_match_table = ralink_esw_match, |
||
1453 | }, |
||
1454 | }; |
||
1455 | |||
1456 | module_platform_driver(esw_driver); |
||
1457 | |||
1458 | MODULE_LICENSE("GPL"); |
||
1459 | MODULE_AUTHOR("John Crispin <blogic@openwrt.org>"); |
||
1460 | MODULE_DESCRIPTION("Switch driver for RT305X SoC"); |
||
1461 | MODULE_VERSION(MTK_FE_DRV_VERSION); |