Root/
Source at commit 5e66606 created 13 years 9 months ago. By juhosg, generic: rtl8366: move mii bus handling to the rtl8366_smi code | |
---|---|
1 | /* |
2 | * Platform driver for the Realtek RTL8366S ethernet switch |
3 | * |
4 | * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org> |
5 | * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com> |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify it |
8 | * under the terms of the GNU General Public License version 2 as published |
9 | * by the Free Software Foundation. |
10 | */ |
11 | |
12 | #include <linux/kernel.h> |
13 | #include <linux/module.h> |
14 | #include <linux/init.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/delay.h> |
17 | #include <linux/skbuff.h> |
18 | #include <linux/switch.h> |
19 | #include <linux/rtl8366s.h> |
20 | |
21 | #include "rtl8366_smi.h" |
22 | |
23 | #ifdef CONFIG_RTL8366S_PHY_DEBUG_FS |
24 | #include <linux/debugfs.h> |
25 | #endif |
26 | |
27 | #define RTL8366S_DRIVER_DESC "Realtek RTL8366S ethernet switch driver" |
28 | #define RTL8366S_DRIVER_VER "0.2.2" |
29 | |
30 | #define RTL8366S_PHY_NO_MAX 4 |
31 | #define RTL8366S_PHY_PAGE_MAX 7 |
32 | #define RTL8366S_PHY_ADDR_MAX 31 |
33 | |
34 | #define RTL8366_CHIP_GLOBAL_CTRL_REG 0x0000 |
35 | #define RTL8366_CHIP_CTRL_VLAN (1 << 13) |
36 | |
37 | #define RTL8366_RESET_CTRL_REG 0x0100 |
38 | #define RTL8366_CHIP_CTRL_RESET_HW 1 |
39 | #define RTL8366_CHIP_CTRL_RESET_SW (1 << 1) |
40 | |
41 | #define RTL8366S_CHIP_VERSION_CTRL_REG 0x0104 |
42 | #define RTL8366S_CHIP_VERSION_MASK 0xf |
43 | #define RTL8366S_CHIP_ID_REG 0x0105 |
44 | #define RTL8366S_CHIP_ID_8366 0x8366 |
45 | |
46 | /* PHY registers control */ |
47 | #define RTL8366S_PHY_ACCESS_CTRL_REG 0x8028 |
48 | #define RTL8366S_PHY_ACCESS_DATA_REG 0x8029 |
49 | |
50 | #define RTL8366S_PHY_CTRL_READ 1 |
51 | #define RTL8366S_PHY_CTRL_WRITE 0 |
52 | |
53 | #define RTL8366S_PHY_REG_MASK 0x1f |
54 | #define RTL8366S_PHY_PAGE_OFFSET 5 |
55 | #define RTL8366S_PHY_PAGE_MASK (0x7 << 5) |
56 | #define RTL8366S_PHY_NO_OFFSET 9 |
57 | #define RTL8366S_PHY_NO_MASK (0x1f << 9) |
58 | |
59 | /* LED control registers */ |
60 | #define RTL8366_LED_BLINKRATE_REG 0x0420 |
61 | #define RTL8366_LED_BLINKRATE_BIT 0 |
62 | #define RTL8366_LED_BLINKRATE_MASK 0x0007 |
63 | |
64 | #define RTL8366_LED_CTRL_REG 0x0421 |
65 | #define RTL8366_LED_0_1_CTRL_REG 0x0422 |
66 | #define RTL8366_LED_2_3_CTRL_REG 0x0423 |
67 | |
68 | #define RTL8366S_MIB_COUNT 33 |
69 | #define RTL8366S_GLOBAL_MIB_COUNT 1 |
70 | #define RTL8366S_MIB_COUNTER_PORT_OFFSET 0x0040 |
71 | #define RTL8366S_MIB_COUNTER_BASE 0x1000 |
72 | #define RTL8366S_MIB_CTRL_REG 0x11F0 |
73 | #define RTL8366S_MIB_CTRL_USER_MASK 0x01FF |
74 | #define RTL8366S_MIB_CTRL_BUSY_MASK 0x0001 |
75 | #define RTL8366S_MIB_CTRL_RESET_MASK 0x0002 |
76 | |
77 | #define RTL8366S_MIB_CTRL_GLOBAL_RESET_MASK 0x0004 |
78 | #define RTL8366S_MIB_CTRL_PORT_RESET_BIT 0x0003 |
79 | #define RTL8366S_MIB_CTRL_PORT_RESET_MASK 0x01FC |
80 | |
81 | |
82 | #define RTL8366S_PORT_VLAN_CTRL_BASE 0x0058 |
83 | #define RTL8366S_PORT_VLAN_CTRL_REG(_p) \ |
84 | (RTL8366S_PORT_VLAN_CTRL_BASE + (_p) / 4) |
85 | #define RTL8366S_PORT_VLAN_CTRL_MASK 0xf |
86 | #define RTL8366S_PORT_VLAN_CTRL_SHIFT(_p) (4 * ((_p) % 4)) |
87 | |
88 | |
89 | #define RTL8366S_VLAN_TABLE_READ_BASE 0x018B |
90 | #define RTL8366S_VLAN_TABLE_WRITE_BASE 0x0185 |
91 | |
92 | #define RTL8366S_VLAN_TB_CTRL_REG 0x010F |
93 | |
94 | #define RTL8366S_TABLE_ACCESS_CTRL_REG 0x0180 |
95 | #define RTL8366S_TABLE_VLAN_READ_CTRL 0x0E01 |
96 | #define RTL8366S_TABLE_VLAN_WRITE_CTRL 0x0F01 |
97 | |
98 | #define RTL8366S_VLAN_MEMCONF_BASE 0x0016 |
99 | |
100 | |
101 | #define RTL8366S_PORT_LINK_STATUS_BASE 0x0060 |
102 | #define RTL8366S_PORT_STATUS_SPEED_MASK 0x0003 |
103 | #define RTL8366S_PORT_STATUS_DUPLEX_MASK 0x0004 |
104 | #define RTL8366S_PORT_STATUS_LINK_MASK 0x0010 |
105 | #define RTL8366S_PORT_STATUS_TXPAUSE_MASK 0x0020 |
106 | #define RTL8366S_PORT_STATUS_RXPAUSE_MASK 0x0040 |
107 | #define RTL8366S_PORT_STATUS_AN_MASK 0x0080 |
108 | |
109 | |
110 | #define RTL8366_PORT_NUM_CPU 5 |
111 | #define RTL8366_NUM_PORTS 6 |
112 | #define RTL8366_NUM_VLANS 16 |
113 | #define RTL8366_NUM_LEDGROUPS 4 |
114 | #define RTL8366_NUM_VIDS 4096 |
115 | #define RTL8366S_PRIORITYMAX 7 |
116 | #define RTL8366S_FIDMAX 7 |
117 | |
118 | |
119 | #define RTL8366_PORT_1 (1 << 0) /* In userspace port 0 */ |
120 | #define RTL8366_PORT_2 (1 << 1) /* In userspace port 1 */ |
121 | #define RTL8366_PORT_3 (1 << 2) /* In userspace port 2 */ |
122 | #define RTL8366_PORT_4 (1 << 3) /* In userspace port 3 */ |
123 | |
124 | #define RTL8366_PORT_UNKNOWN (1 << 4) /* No known connection */ |
125 | #define RTL8366_PORT_CPU (1 << 5) /* CPU port */ |
126 | |
127 | #define RTL8366_PORT_ALL (RTL8366_PORT_1 | \ |
128 | RTL8366_PORT_2 | \ |
129 | RTL8366_PORT_3 | \ |
130 | RTL8366_PORT_4 | \ |
131 | RTL8366_PORT_UNKNOWN | \ |
132 | RTL8366_PORT_CPU) |
133 | |
134 | #define RTL8366_PORT_ALL_BUT_CPU (RTL8366_PORT_1 | \ |
135 | RTL8366_PORT_2 | \ |
136 | RTL8366_PORT_3 | \ |
137 | RTL8366_PORT_4 | \ |
138 | RTL8366_PORT_UNKNOWN) |
139 | |
140 | #define RTL8366_PORT_ALL_EXTERNAL (RTL8366_PORT_1 | \ |
141 | RTL8366_PORT_2 | \ |
142 | RTL8366_PORT_3 | \ |
143 | RTL8366_PORT_4) |
144 | |
145 | #define RTL8366_PORT_ALL_INTERNAL (RTL8366_PORT_UNKNOWN | \ |
146 | RTL8366_PORT_CPU) |
147 | |
148 | struct rtl8366s { |
149 | struct device *parent; |
150 | struct rtl8366_smi smi; |
151 | struct switch_dev dev; |
152 | char buf[4096]; |
153 | #ifdef CONFIG_RTL8366S_PHY_DEBUG_FS |
154 | struct dentry *debugfs_root; |
155 | #endif |
156 | }; |
157 | |
158 | struct rtl8366s_vlanconfig { |
159 | u16 reserved2:1; |
160 | u16 priority:3; |
161 | u16 vid:12; |
162 | |
163 | u16 reserved1:1; |
164 | u16 fid:3; |
165 | u16 untag:6; |
166 | u16 member:6; |
167 | }; |
168 | |
169 | struct rtl8366s_vlan4kentry { |
170 | u16 reserved1:4; |
171 | u16 vid:12; |
172 | |
173 | u16 reserved2:1; |
174 | u16 fid:3; |
175 | u16 untag:6; |
176 | u16 member:6; |
177 | }; |
178 | |
179 | #ifdef CONFIG_RTL8366S_PHY_DEBUG_FS |
180 | u16 g_dbg_reg; |
181 | #endif |
182 | |
183 | struct mib_counter { |
184 | unsigned offset; |
185 | unsigned length; |
186 | const char *name; |
187 | }; |
188 | |
189 | static struct mib_counter rtl8366s_mib_counters[RTL8366S_MIB_COUNT] = { |
190 | { 0, 4, "IfInOctets " }, |
191 | { 4, 4, "EtherStatsOctets " }, |
192 | { 8, 2, "EtherStatsUnderSizePkts " }, |
193 | { 10, 2, "EtherFregament " }, |
194 | { 12, 2, "EtherStatsPkts64Octets " }, |
195 | { 14, 2, "EtherStatsPkts65to127Octets " }, |
196 | { 16, 2, "EtherStatsPkts128to255Octets " }, |
197 | { 18, 2, "EtherStatsPkts256to511Octets " }, |
198 | { 20, 2, "EtherStatsPkts512to1023Octets " }, |
199 | { 22, 2, "EtherStatsPkts1024to1518Octets " }, |
200 | { 24, 2, "EtherOversizeStats " }, |
201 | { 26, 2, "EtherStatsJabbers " }, |
202 | { 28, 2, "IfInUcastPkts " }, |
203 | { 30, 2, "EtherStatsMulticastPkts " }, |
204 | { 32, 2, "EtherStatsBroadcastPkts " }, |
205 | { 34, 2, "EtherStatsDropEvents " }, |
206 | { 36, 2, "Dot3StatsFCSErrors " }, |
207 | { 38, 2, "Dot3StatsSymbolErrors " }, |
208 | { 40, 2, "Dot3InPauseFrames " }, |
209 | { 42, 2, "Dot3ControlInUnknownOpcodes " }, |
210 | { 44, 4, "IfOutOctets " }, |
211 | { 48, 2, "Dot3StatsSingleCollisionFrames " }, |
212 | { 50, 2, "Dot3StatMultipleCollisionFrames " }, |
213 | { 52, 2, "Dot3sDeferredTransmissions " }, |
214 | { 54, 2, "Dot3StatsLateCollisions " }, |
215 | { 56, 2, "EtherStatsCollisions " }, |
216 | { 58, 2, "Dot3StatsExcessiveCollisions " }, |
217 | { 60, 2, "Dot3OutPauseFrames " }, |
218 | { 62, 2, "Dot1dBasePortDelayExceededDiscards" }, |
219 | { 64, 2, "Dot1dTpPortInDiscards " }, |
220 | { 66, 2, "IfOutUcastPkts " }, |
221 | { 68, 2, "IfOutMulticastPkts " }, |
222 | { 70, 2, "IfOutBroadcastPkts " }, |
223 | }; |
224 | |
225 | static inline struct rtl8366s *smi_to_rtl8366s(struct rtl8366_smi *smi) |
226 | { |
227 | return container_of(smi, struct rtl8366s, smi); |
228 | } |
229 | |
230 | static inline struct rtl8366s *sw_to_rtl8366s(struct switch_dev *sw) |
231 | { |
232 | return container_of(sw, struct rtl8366s, dev); |
233 | } |
234 | |
235 | static int rtl8366s_reset_chip(struct rtl8366s *rtl) |
236 | { |
237 | struct rtl8366_smi *smi = &rtl->smi; |
238 | int timeout = 10; |
239 | u32 data; |
240 | |
241 | rtl8366_smi_write_reg(smi, RTL8366_RESET_CTRL_REG, |
242 | RTL8366_CHIP_CTRL_RESET_HW); |
243 | do { |
244 | msleep(1); |
245 | if (rtl8366_smi_read_reg(smi, RTL8366_RESET_CTRL_REG, &data)) |
246 | return -EIO; |
247 | |
248 | if (!(data & RTL8366_CHIP_CTRL_RESET_HW)) |
249 | break; |
250 | } while (--timeout); |
251 | |
252 | if (!timeout) { |
253 | printk("Timeout waiting for the switch to reset\n"); |
254 | return -EIO; |
255 | } |
256 | |
257 | return 0; |
258 | } |
259 | |
260 | static int rtl8366s_read_phy_reg(struct rtl8366s *rtl, |
261 | u32 phy_no, u32 page, u32 addr, u32 *data) |
262 | { |
263 | struct rtl8366_smi *smi = &rtl->smi; |
264 | u32 reg; |
265 | int ret; |
266 | |
267 | if (phy_no > RTL8366S_PHY_NO_MAX) |
268 | return -EINVAL; |
269 | |
270 | if (page > RTL8366S_PHY_PAGE_MAX) |
271 | return -EINVAL; |
272 | |
273 | if (addr > RTL8366S_PHY_ADDR_MAX) |
274 | return -EINVAL; |
275 | |
276 | ret = rtl8366_smi_write_reg(smi, RTL8366S_PHY_ACCESS_CTRL_REG, |
277 | RTL8366S_PHY_CTRL_READ); |
278 | if (ret) |
279 | return ret; |
280 | |
281 | reg = 0x8000 | (1 << (phy_no + RTL8366S_PHY_NO_OFFSET)) | |
282 | ((page << RTL8366S_PHY_PAGE_OFFSET) & RTL8366S_PHY_PAGE_MASK) | |
283 | (addr & RTL8366S_PHY_REG_MASK); |
284 | |
285 | ret = rtl8366_smi_write_reg(smi, reg, 0); |
286 | if (ret) |
287 | return ret; |
288 | |
289 | ret = rtl8366_smi_read_reg(smi, RTL8366S_PHY_ACCESS_DATA_REG, data); |
290 | if (ret) |
291 | return ret; |
292 | |
293 | return 0; |
294 | } |
295 | |
296 | static int rtl8366s_write_phy_reg(struct rtl8366s *rtl, |
297 | u32 phy_no, u32 page, u32 addr, u32 data) |
298 | { |
299 | struct rtl8366_smi *smi = &rtl->smi; |
300 | u32 reg; |
301 | int ret; |
302 | |
303 | if (phy_no > RTL8366S_PHY_NO_MAX) |
304 | return -EINVAL; |
305 | |
306 | if (page > RTL8366S_PHY_PAGE_MAX) |
307 | return -EINVAL; |
308 | |
309 | if (addr > RTL8366S_PHY_ADDR_MAX) |
310 | return -EINVAL; |
311 | |
312 | ret = rtl8366_smi_write_reg(smi, RTL8366S_PHY_ACCESS_CTRL_REG, |
313 | RTL8366S_PHY_CTRL_WRITE); |
314 | if (ret) |
315 | return ret; |
316 | |
317 | reg = 0x8000 | (1 << (phy_no + RTL8366S_PHY_NO_OFFSET)) | |
318 | ((page << RTL8366S_PHY_PAGE_OFFSET) & RTL8366S_PHY_PAGE_MASK) | |
319 | (addr & RTL8366S_PHY_REG_MASK); |
320 | |
321 | ret = rtl8366_smi_write_reg(smi, reg, data); |
322 | if (ret) |
323 | return ret; |
324 | |
325 | return 0; |
326 | } |
327 | |
328 | static int rtl8366_get_mib_counter(struct rtl8366s *rtl, int counter, |
329 | int port, unsigned long long *val) |
330 | { |
331 | struct rtl8366_smi *smi = &rtl->smi; |
332 | int i; |
333 | int err; |
334 | u32 addr, data; |
335 | u64 mibvalue; |
336 | |
337 | if (port > RTL8366_NUM_PORTS || counter >= RTL8366S_MIB_COUNT) |
338 | return -EINVAL; |
339 | |
340 | addr = RTL8366S_MIB_COUNTER_BASE + |
341 | RTL8366S_MIB_COUNTER_PORT_OFFSET * (port) + |
342 | rtl8366s_mib_counters[counter].offset; |
343 | |
344 | /* |
345 | * Writing access counter address first |
346 | * then ASIC will prepare 64bits counter wait for being retrived |
347 | */ |
348 | data = 0; /* writing data will be discard by ASIC */ |
349 | err = rtl8366_smi_write_reg(smi, addr, data); |
350 | if (err) |
351 | return err; |
352 | |
353 | /* read MIB control register */ |
354 | err = rtl8366_smi_read_reg(smi, RTL8366S_MIB_CTRL_REG, &data); |
355 | if (err) |
356 | return err; |
357 | |
358 | if (data & RTL8366S_MIB_CTRL_BUSY_MASK) |
359 | return -EBUSY; |
360 | |
361 | if (data & RTL8366S_MIB_CTRL_RESET_MASK) |
362 | return -EIO; |
363 | |
364 | mibvalue = 0; |
365 | for (i = rtl8366s_mib_counters[counter].length; i > 0; i--) { |
366 | err = rtl8366_smi_read_reg(smi, addr + (i - 1), &data); |
367 | if (err) |
368 | return err; |
369 | |
370 | mibvalue = (mibvalue << 16) | (data & 0xFFFF); |
371 | } |
372 | |
373 | *val = mibvalue; |
374 | return 0; |
375 | } |
376 | |
377 | static int rtl8366s_get_vlan_4k_entry(struct rtl8366s *rtl, u32 vid, |
378 | struct rtl8366s_vlan4kentry *vlan4k) |
379 | { |
380 | struct rtl8366_smi *smi = &rtl->smi; |
381 | int err; |
382 | u32 data; |
383 | u16 *tableaddr; |
384 | |
385 | memset(vlan4k, '\0', sizeof(struct rtl8366s_vlan4kentry)); |
386 | vlan4k->vid = vid; |
387 | |
388 | if (vid >= RTL8366_NUM_VIDS) |
389 | return -EINVAL; |
390 | |
391 | tableaddr = (u16 *)vlan4k; |
392 | |
393 | /* write VID */ |
394 | data = *tableaddr; |
395 | err = rtl8366_smi_write_reg(smi, RTL8366S_VLAN_TABLE_WRITE_BASE, data); |
396 | if (err) |
397 | return err; |
398 | |
399 | /* write table access control word */ |
400 | err = rtl8366_smi_write_reg(smi, RTL8366S_TABLE_ACCESS_CTRL_REG, |
401 | RTL8366S_TABLE_VLAN_READ_CTRL); |
402 | if (err) |
403 | return err; |
404 | |
405 | err = rtl8366_smi_read_reg(smi, RTL8366S_VLAN_TABLE_READ_BASE, &data); |
406 | if (err) |
407 | return err; |
408 | |
409 | *tableaddr = data; |
410 | tableaddr++; |
411 | |
412 | err = rtl8366_smi_read_reg(smi, RTL8366S_VLAN_TABLE_READ_BASE + 1, |
413 | &data); |
414 | if (err) |
415 | return err; |
416 | |
417 | *tableaddr = data; |
418 | vlan4k->vid = vid; |
419 | |
420 | return 0; |
421 | } |
422 | |
423 | static int rtl8366s_set_vlan_4k_entry(struct rtl8366s *rtl, |
424 | const struct rtl8366s_vlan4kentry *vlan4k) |
425 | { |
426 | struct rtl8366_smi *smi = &rtl->smi; |
427 | int err; |
428 | u32 data; |
429 | u16 *tableaddr; |
430 | |
431 | if (vlan4k->vid >= RTL8366_NUM_VIDS || |
432 | vlan4k->member > RTL8366_PORT_ALL || |
433 | vlan4k->untag > RTL8366_PORT_ALL || |
434 | vlan4k->fid > RTL8366S_FIDMAX) |
435 | return -EINVAL; |
436 | |
437 | tableaddr = (u16 *)vlan4k; |
438 | |
439 | data = *tableaddr; |
440 | |
441 | err = rtl8366_smi_write_reg(smi, RTL8366S_VLAN_TABLE_WRITE_BASE, data); |
442 | if (err) |
443 | return err; |
444 | |
445 | tableaddr++; |
446 | |
447 | data = *tableaddr; |
448 | |
449 | err = rtl8366_smi_write_reg(smi, RTL8366S_VLAN_TABLE_WRITE_BASE + 1, |
450 | data); |
451 | if (err) |
452 | return err; |
453 | |
454 | /* write table access control word */ |
455 | err = rtl8366_smi_write_reg(smi, RTL8366S_TABLE_ACCESS_CTRL_REG, |
456 | RTL8366S_TABLE_VLAN_WRITE_CTRL); |
457 | |
458 | return err; |
459 | } |
460 | |
461 | static int rtl8366s_get_vlan_member_config(struct rtl8366s *rtl, u32 index, |
462 | struct rtl8366s_vlanconfig *vlanmc) |
463 | { |
464 | struct rtl8366_smi *smi = &rtl->smi; |
465 | int err; |
466 | u32 addr; |
467 | u32 data; |
468 | u16 *tableaddr; |
469 | |
470 | memset(vlanmc, '\0', sizeof(struct rtl8366s_vlanconfig)); |
471 | |
472 | if (index >= RTL8366_NUM_VLANS) |
473 | return -EINVAL; |
474 | |
475 | tableaddr = (u16 *)vlanmc; |
476 | |
477 | addr = RTL8366S_VLAN_MEMCONF_BASE + (index << 1); |
478 | err = rtl8366_smi_read_reg(smi, addr, &data); |
479 | if (err) |
480 | return err; |
481 | |
482 | *tableaddr = data; |
483 | tableaddr++; |
484 | |
485 | addr = RTL8366S_VLAN_MEMCONF_BASE + 1 + (index << 1); |
486 | err = rtl8366_smi_read_reg(smi, addr, &data); |
487 | if (err) |
488 | return err; |
489 | |
490 | *tableaddr = data; |
491 | |
492 | return 0; |
493 | } |
494 | |
495 | static int rtl8366s_set_vlan_member_config(struct rtl8366s *rtl, u32 index, |
496 | const struct rtl8366s_vlanconfig |
497 | *vlanmc) |
498 | { |
499 | struct rtl8366_smi *smi = &rtl->smi; |
500 | int err; |
501 | u32 addr; |
502 | u32 data; |
503 | u16 *tableaddr; |
504 | |
505 | if (index >= RTL8366_NUM_VLANS || |
506 | vlanmc->vid >= RTL8366_NUM_VIDS || |
507 | vlanmc->priority > RTL8366S_PRIORITYMAX || |
508 | vlanmc->member > RTL8366_PORT_ALL || |
509 | vlanmc->untag > RTL8366_PORT_ALL || |
510 | vlanmc->fid > RTL8366S_FIDMAX) |
511 | return -EINVAL; |
512 | |
513 | addr = RTL8366S_VLAN_MEMCONF_BASE + (index << 1); |
514 | |
515 | tableaddr = (u16 *)vlanmc; |
516 | data = *tableaddr; |
517 | |
518 | err = rtl8366_smi_write_reg(smi, addr, data); |
519 | if (err) |
520 | return err; |
521 | |
522 | addr = RTL8366S_VLAN_MEMCONF_BASE + 1 + (index << 1); |
523 | |
524 | tableaddr++; |
525 | data = *tableaddr; |
526 | |
527 | err = rtl8366_smi_write_reg(smi, addr, data); |
528 | if (err) |
529 | return err; |
530 | |
531 | return 0; |
532 | } |
533 | |
534 | static int rtl8366s_get_port_vlan_index(struct rtl8366s *rtl, int port, |
535 | int *val) |
536 | { |
537 | struct rtl8366_smi *smi = &rtl->smi; |
538 | u32 data; |
539 | int err; |
540 | |
541 | if (port >= RTL8366_NUM_PORTS) |
542 | return -EINVAL; |
543 | |
544 | err = rtl8366_smi_read_reg(smi, RTL8366S_PORT_VLAN_CTRL_REG(port), |
545 | &data); |
546 | if (err) |
547 | return err; |
548 | |
549 | *val = (data >> RTL8366S_PORT_VLAN_CTRL_SHIFT(port)) & |
550 | RTL8366S_PORT_VLAN_CTRL_MASK; |
551 | |
552 | return 0; |
553 | |
554 | } |
555 | |
556 | static int rtl8366s_get_vlan_port_pvid(struct rtl8366s *rtl, int port, |
557 | int *val) |
558 | { |
559 | struct rtl8366s_vlanconfig vlanmc; |
560 | int err; |
561 | int index; |
562 | |
563 | err = rtl8366s_get_port_vlan_index(rtl, port, &index); |
564 | if (err) |
565 | return err; |
566 | |
567 | err = rtl8366s_get_vlan_member_config(rtl, index, &vlanmc); |
568 | if (err) |
569 | return err; |
570 | |
571 | *val = vlanmc.vid; |
572 | return 0; |
573 | } |
574 | |
575 | static int rtl8366s_set_port_vlan_index(struct rtl8366s *rtl, int port, |
576 | int index) |
577 | { |
578 | struct rtl8366_smi *smi = &rtl->smi; |
579 | u32 data; |
580 | int err; |
581 | |
582 | if (port >= RTL8366_NUM_PORTS || index >= RTL8366_NUM_VLANS) |
583 | return -EINVAL; |
584 | |
585 | err = rtl8366_smi_read_reg(smi, RTL8366S_PORT_VLAN_CTRL_REG(port), |
586 | &data); |
587 | if (err) |
588 | return err; |
589 | |
590 | data &= ~(RTL8366S_PORT_VLAN_CTRL_MASK << |
591 | RTL8366S_PORT_VLAN_CTRL_SHIFT(port)); |
592 | data |= (index & RTL8366S_PORT_VLAN_CTRL_MASK) << |
593 | RTL8366S_PORT_VLAN_CTRL_SHIFT(port); |
594 | |
595 | err = rtl8366_smi_write_reg(smi, RTL8366S_PORT_VLAN_CTRL_REG(port), |
596 | data); |
597 | return err; |
598 | } |
599 | |
600 | static int rtl8366s_set_vlan_port_pvid(struct rtl8366s *rtl, int port, int val) |
601 | { |
602 | int i; |
603 | struct rtl8366s_vlanconfig vlanmc; |
604 | struct rtl8366s_vlan4kentry vlan4k; |
605 | |
606 | if (port >= RTL8366_NUM_PORTS || val >= RTL8366_NUM_VIDS) |
607 | return -EINVAL; |
608 | |
609 | /* Updating the 4K entry; lookup it and change the port member set */ |
610 | rtl8366s_get_vlan_4k_entry(rtl, val, &vlan4k); |
611 | vlan4k.member |= ((1 << port) | RTL8366_PORT_CPU); |
612 | vlan4k.untag = RTL8366_PORT_ALL_BUT_CPU; |
613 | rtl8366s_set_vlan_4k_entry(rtl, &vlan4k); |
614 | |
615 | /* |
616 | * For the 16 entries more work needs to be done. First see if such |
617 | * VID is already there and change it |
618 | */ |
619 | for (i = 0; i < RTL8366_NUM_VLANS; ++i) { |
620 | rtl8366s_get_vlan_member_config(rtl, i, &vlanmc); |
621 | |
622 | /* Try to find an existing vid and update port member set */ |
623 | if (val == vlanmc.vid) { |
624 | vlanmc.member |= ((1 << port) | RTL8366_PORT_CPU); |
625 | rtl8366s_set_vlan_member_config(rtl, i, &vlanmc); |
626 | |
627 | /* Now update PVID register settings */ |
628 | rtl8366s_set_port_vlan_index(rtl, port, i); |
629 | |
630 | return 0; |
631 | } |
632 | } |
633 | |
634 | /* |
635 | * PVID could not be found from vlan table. Replace unused (one that |
636 | * has no member ports) with new one |
637 | */ |
638 | for (i = 0; i < RTL8366_NUM_VLANS; ++i) { |
639 | rtl8366s_get_vlan_member_config(rtl, i, &vlanmc); |
640 | |
641 | /* |
642 | * See if this vlan member configuration is unused. It is |
643 | * unused if member set contains no ports or CPU port only |
644 | */ |
645 | if (!vlanmc.member || vlanmc.member == RTL8366_PORT_CPU) { |
646 | vlanmc.vid = val; |
647 | vlanmc.priority = 0; |
648 | vlanmc.untag = RTL8366_PORT_ALL_BUT_CPU; |
649 | vlanmc.member = ((1 << port) | RTL8366_PORT_CPU); |
650 | vlanmc.fid = 0; |
651 | |
652 | rtl8366s_set_vlan_member_config(rtl, i, &vlanmc); |
653 | |
654 | /* Now update PVID register settings */ |
655 | rtl8366s_set_port_vlan_index(rtl, port, i); |
656 | |
657 | return 0; |
658 | } |
659 | } |
660 | |
661 | dev_err(rtl->parent, |
662 | "All 16 vlan member configurations are in use\n"); |
663 | |
664 | return -EINVAL; |
665 | } |
666 | |
667 | |
668 | static int rtl8366s_vlan_set_vlan(struct rtl8366s *rtl, int enable) |
669 | { |
670 | struct rtl8366_smi *smi = &rtl->smi; |
671 | u32 data = 0; |
672 | |
673 | rtl8366_smi_read_reg(smi, RTL8366_CHIP_GLOBAL_CTRL_REG, &data); |
674 | |
675 | if (enable) |
676 | data |= RTL8366_CHIP_CTRL_VLAN; |
677 | else |
678 | data &= ~RTL8366_CHIP_CTRL_VLAN; |
679 | |
680 | return rtl8366_smi_write_reg(smi, RTL8366_CHIP_GLOBAL_CTRL_REG, data); |
681 | } |
682 | |
683 | static int rtl8366s_vlan_set_4ktable(struct rtl8366s *rtl, int enable) |
684 | { |
685 | struct rtl8366_smi *smi = &rtl->smi; |
686 | u32 data = 0; |
687 | |
688 | rtl8366_smi_read_reg(smi, RTL8366S_VLAN_TB_CTRL_REG, &data); |
689 | |
690 | if (enable) |
691 | data |= 1; |
692 | else |
693 | data &= ~1; |
694 | |
695 | return rtl8366_smi_write_reg(smi, RTL8366S_VLAN_TB_CTRL_REG, data); |
696 | } |
697 | |
698 | static int rtl8366s_reset_vlan(struct rtl8366s *rtl) |
699 | { |
700 | struct rtl8366s_vlan4kentry vlan4k; |
701 | struct rtl8366s_vlanconfig vlanmc; |
702 | int err; |
703 | int i; |
704 | |
705 | /* clear 16 VLAN member configuration */ |
706 | vlanmc.vid = 0; |
707 | vlanmc.priority = 0; |
708 | vlanmc.member = 0; |
709 | vlanmc.untag = 0; |
710 | vlanmc.fid = 0; |
711 | for (i = 0; i < RTL8366_NUM_VLANS; i++) { |
712 | err = rtl8366s_set_vlan_member_config(rtl, i, &vlanmc); |
713 | if (err) |
714 | return err; |
715 | } |
716 | |
717 | /* Set a default VLAN with vid 1 to 4K table for all ports */ |
718 | vlan4k.vid = 1; |
719 | vlan4k.member = RTL8366_PORT_ALL; |
720 | vlan4k.untag = RTL8366_PORT_ALL; |
721 | vlan4k.fid = 0; |
722 | err = rtl8366s_set_vlan_4k_entry(rtl, &vlan4k); |
723 | if (err) |
724 | return err; |
725 | |
726 | /* Set all ports PVID to default VLAN */ |
727 | for (i = 0; i < RTL8366_NUM_PORTS; i++) { |
728 | err = rtl8366s_set_vlan_port_pvid(rtl, i, 0); |
729 | if (err) |
730 | return err; |
731 | } |
732 | |
733 | return 0; |
734 | } |
735 | |
736 | #ifdef CONFIG_RTL8366S_PHY_DEBUG_FS |
737 | static int rtl8366s_debugfs_open(struct inode *inode, struct file *file) |
738 | { |
739 | file->private_data = inode->i_private; |
740 | return 0; |
741 | } |
742 | |
743 | static ssize_t rtl8366s_read_debugfs_mibs(struct file *file, |
744 | char __user *user_buf, |
745 | size_t count, loff_t *ppos) |
746 | { |
747 | struct rtl8366s *rtl = (struct rtl8366s *)file->private_data; |
748 | int i, j, len = 0; |
749 | char *buf = rtl->buf; |
750 | |
751 | len += snprintf(buf + len, sizeof(rtl->buf) - len, "MIB Counters:\n"); |
752 | len += snprintf(buf + len, sizeof(rtl->buf) - len, "Counter" |
753 | " " |
754 | "Port 0 \t\t Port 1 \t\t Port 2 \t\t Port 3 \t\t " |
755 | "Port 4\n"); |
756 | |
757 | for (i = 0; i < 33; ++i) { |
758 | len += snprintf(buf + len, sizeof(rtl->buf) - len, "%d:%s ", |
759 | i, rtl8366s_mib_counters[i].name); |
760 | for (j = 0; j < RTL8366_NUM_PORTS; ++j) { |
761 | unsigned long long counter = 0; |
762 | |
763 | if (!rtl8366_get_mib_counter(rtl, i, j, &counter)) |
764 | len += snprintf(buf + len, |
765 | sizeof(rtl->buf) - len, |
766 | "[%llu]", counter); |
767 | else |
768 | len += snprintf(buf + len, |
769 | sizeof(rtl->buf) - len, |
770 | "[error]"); |
771 | |
772 | if (j != RTL8366_NUM_PORTS - 1) { |
773 | if (counter < 100000) |
774 | len += snprintf(buf + len, |
775 | sizeof(rtl->buf) - len, |
776 | "\t"); |
777 | |
778 | len += snprintf(buf + len, |
779 | sizeof(rtl->buf) - len, |
780 | "\t"); |
781 | } |
782 | } |
783 | len += snprintf(buf + len, sizeof(rtl->buf) - len, "\n"); |
784 | } |
785 | |
786 | len += snprintf(buf + len, sizeof(rtl->buf) - len, "\n"); |
787 | |
788 | return simple_read_from_buffer(user_buf, count, ppos, buf, len); |
789 | } |
790 | |
791 | static ssize_t rtl8366s_read_debugfs_vlan(struct file *file, |
792 | char __user *user_buf, |
793 | size_t count, loff_t *ppos) |
794 | { |
795 | struct rtl8366s *rtl = (struct rtl8366s *)file->private_data; |
796 | int i, j, len = 0; |
797 | char *buf = rtl->buf; |
798 | |
799 | len += snprintf(buf + len, sizeof(rtl->buf) - len, |
800 | "VLAN Member Config:\n"); |
801 | len += snprintf(buf + len, sizeof(rtl->buf) - len, |
802 | "\t id \t vid \t prio \t member \t untag \t fid " |
803 | "\tports\n"); |
804 | |
805 | for (i = 0; i < RTL8366_NUM_VLANS; ++i) { |
806 | struct rtl8366s_vlanconfig vlanmc; |
807 | |
808 | rtl8366s_get_vlan_member_config(rtl, i, &vlanmc); |
809 | |
810 | len += snprintf(buf + len, sizeof(rtl->buf) - len, |
811 | "\t[%d] \t %d \t %d \t 0x%04x \t 0x%04x \t %d " |
812 | "\t", i, vlanmc.vid, vlanmc.priority, |
813 | vlanmc.member, vlanmc.untag, vlanmc.fid); |
814 | |
815 | for (j = 0; j < RTL8366_NUM_PORTS; ++j) { |
816 | int index = 0; |
817 | if (!rtl8366s_get_port_vlan_index(rtl, j, &index)) { |
818 | if (index == i) |
819 | len += snprintf(buf + len, |
820 | sizeof(rtl->buf) - len, |
821 | "%d", j); |
822 | } |
823 | } |
824 | len += snprintf(buf + len, sizeof(rtl->buf) - len, "\n"); |
825 | } |
826 | |
827 | return simple_read_from_buffer(user_buf, count, ppos, buf, len); |
828 | } |
829 | |
830 | static ssize_t rtl8366s_read_debugfs_reg(struct file *file, |
831 | char __user *user_buf, |
832 | size_t count, loff_t *ppos) |
833 | { |
834 | struct rtl8366s *rtl = (struct rtl8366s *)file->private_data; |
835 | struct rtl8366_smi *smi = &rtl->smi; |
836 | u32 t, reg = g_dbg_reg; |
837 | int err, len = 0; |
838 | char *buf = rtl->buf; |
839 | |
840 | memset(buf, '\0', sizeof(rtl->buf)); |
841 | |
842 | err = rtl8366_smi_read_reg(smi, reg, &t); |
843 | if (err) { |
844 | len += snprintf(buf, sizeof(rtl->buf), |
845 | "Read failed (reg: 0x%04x)\n", reg); |
846 | return simple_read_from_buffer(user_buf, count, ppos, buf, len); |
847 | } |
848 | |
849 | len += snprintf(buf, sizeof(rtl->buf), "reg = 0x%04x, val = 0x%04x\n", |
850 | reg, t); |
851 | |
852 | return simple_read_from_buffer(user_buf, count, ppos, buf, len); |
853 | } |
854 | |
855 | static ssize_t rtl8366s_write_debugfs_reg(struct file *file, |
856 | const char __user *user_buf, |
857 | size_t count, loff_t *ppos) |
858 | { |
859 | struct rtl8366s *rtl = (struct rtl8366s *)file->private_data; |
860 | struct rtl8366_smi *smi = &rtl->smi; |
861 | unsigned long data; |
862 | u32 reg = g_dbg_reg; |
863 | int err; |
864 | size_t len; |
865 | char *buf = rtl->buf; |
866 | |
867 | len = min(count, sizeof(rtl->buf) - 1); |
868 | if (copy_from_user(buf, user_buf, len)) { |
869 | dev_err(rtl->parent, "copy from user failed\n"); |
870 | return -EFAULT; |
871 | } |
872 | |
873 | buf[len] = '\0'; |
874 | if (len > 0 && buf[len - 1] == '\n') |
875 | buf[len - 1] = '\0'; |
876 | |
877 | |
878 | if (strict_strtoul(buf, 16, &data)) { |
879 | dev_err(rtl->parent, "Invalid reg value %s\n", buf); |
880 | } else { |
881 | err = rtl8366_smi_write_reg(smi, reg, data); |
882 | if (err) { |
883 | dev_err(rtl->parent, |
884 | "writing reg 0x%04x val 0x%04lx failed\n", |
885 | reg, data); |
886 | } |
887 | } |
888 | |
889 | return count; |
890 | } |
891 | |
892 | static const struct file_operations fops_rtl8366s_regs = { |
893 | .read = rtl8366s_read_debugfs_reg, |
894 | .write = rtl8366s_write_debugfs_reg, |
895 | .open = rtl8366s_debugfs_open, |
896 | .owner = THIS_MODULE |
897 | }; |
898 | |
899 | static const struct file_operations fops_rtl8366s_vlan = { |
900 | .read = rtl8366s_read_debugfs_vlan, |
901 | .open = rtl8366s_debugfs_open, |
902 | .owner = THIS_MODULE |
903 | }; |
904 | |
905 | static const struct file_operations fops_rtl8366s_mibs = { |
906 | .read = rtl8366s_read_debugfs_mibs, |
907 | .open = rtl8366s_debugfs_open, |
908 | .owner = THIS_MODULE |
909 | }; |
910 | |
911 | static void rtl8366s_debugfs_init(struct rtl8366s *rtl) |
912 | { |
913 | struct dentry *node; |
914 | struct dentry *root; |
915 | |
916 | if (!rtl->debugfs_root) |
917 | rtl->debugfs_root = debugfs_create_dir("rtl8366s", NULL); |
918 | |
919 | if (!rtl->debugfs_root) { |
920 | dev_err(rtl->parent, "Unable to create debugfs dir\n"); |
921 | return; |
922 | } |
923 | root = rtl->debugfs_root; |
924 | |
925 | node = debugfs_create_x16("reg", S_IRUGO | S_IWUSR, root, &g_dbg_reg); |
926 | if (!node) { |
927 | dev_err(rtl->parent, "Creating debugfs file '%s' failed\n", |
928 | "reg"); |
929 | return; |
930 | } |
931 | |
932 | node = debugfs_create_file("val", S_IRUGO | S_IWUSR, root, rtl, |
933 | &fops_rtl8366s_regs); |
934 | if (!node) { |
935 | dev_err(rtl->parent, "Creating debugfs file '%s' failed\n", |
936 | "val"); |
937 | return; |
938 | } |
939 | |
940 | node = debugfs_create_file("vlan", S_IRUSR, root, rtl, |
941 | &fops_rtl8366s_vlan); |
942 | if (!node) { |
943 | dev_err(rtl->parent, "Creating debugfs file '%s' failed\n", |
944 | "vlan"); |
945 | return; |
946 | } |
947 | |
948 | node = debugfs_create_file("mibs", S_IRUSR, root, rtl, |
949 | &fops_rtl8366s_mibs); |
950 | if (!node) { |
951 | dev_err(rtl->parent, "Creating debugfs file '%s' failed\n", |
952 | "mibs"); |
953 | return; |
954 | } |
955 | } |
956 | |
957 | static void rtl8366s_debugfs_remove(struct rtl8366s *rtl) |
958 | { |
959 | if (rtl->debugfs_root) { |
960 | debugfs_remove_recursive(rtl->debugfs_root); |
961 | rtl->debugfs_root = NULL; |
962 | } |
963 | } |
964 | |
965 | #else |
966 | static inline void rtl8366s_debugfs_init(struct rtl8366s *rtl) {} |
967 | static inline void rtl8366s_debugfs_remove(struct rtl8366s *rtl) {} |
968 | #endif /* CONFIG_RTL8366S_PHY_DEBUG_FS */ |
969 | |
970 | static int rtl8366s_sw_reset_mibs(struct switch_dev *dev, |
971 | const struct switch_attr *attr, |
972 | struct switch_val *val) |
973 | { |
974 | struct rtl8366s *rtl = sw_to_rtl8366s(dev); |
975 | struct rtl8366_smi *smi = &rtl->smi; |
976 | u32 data = 0; |
977 | |
978 | if (val->value.i == 1) { |
979 | rtl8366_smi_read_reg(smi, RTL8366S_MIB_CTRL_REG, &data); |
980 | data |= (1 << 2); |
981 | rtl8366_smi_write_reg(smi, RTL8366S_MIB_CTRL_REG, data); |
982 | } |
983 | |
984 | return 0; |
985 | } |
986 | |
987 | static int rtl8366s_sw_get_vlan_enable(struct switch_dev *dev, |
988 | const struct switch_attr *attr, |
989 | struct switch_val *val) |
990 | { |
991 | struct rtl8366s *rtl = sw_to_rtl8366s(dev); |
992 | struct rtl8366_smi *smi = &rtl->smi; |
993 | u32 data; |
994 | |
995 | if (attr->ofs == 1) { |
996 | rtl8366_smi_read_reg(smi, RTL8366_CHIP_GLOBAL_CTRL_REG, &data); |
997 | |
998 | if (data & RTL8366_CHIP_CTRL_VLAN) |
999 | val->value.i = 1; |
1000 | else |
1001 | val->value.i = 0; |
1002 | } else if (attr->ofs == 2) { |
1003 | rtl8366_smi_read_reg(smi, RTL8366S_VLAN_TB_CTRL_REG, &data); |
1004 | |
1005 | if (data & 0x0001) |
1006 | val->value.i = 1; |
1007 | else |
1008 | val->value.i = 0; |
1009 | } |
1010 | |
1011 | return 0; |
1012 | } |
1013 | |
1014 | static int rtl8366s_sw_get_blinkrate(struct switch_dev *dev, |
1015 | const struct switch_attr *attr, |
1016 | struct switch_val *val) |
1017 | { |
1018 | struct rtl8366s *rtl = sw_to_rtl8366s(dev); |
1019 | struct rtl8366_smi *smi = &rtl->smi; |
1020 | u32 data; |
1021 | |
1022 | rtl8366_smi_read_reg(smi, RTL8366_LED_BLINKRATE_REG, &data); |
1023 | |
1024 | val->value.i = (data & (RTL8366_LED_BLINKRATE_MASK)); |
1025 | |
1026 | return 0; |
1027 | } |
1028 | |
1029 | static int rtl8366s_sw_set_blinkrate(struct switch_dev *dev, |
1030 | const struct switch_attr *attr, |
1031 | struct switch_val *val) |
1032 | { |
1033 | struct rtl8366s *rtl = sw_to_rtl8366s(dev); |
1034 | struct rtl8366_smi *smi = &rtl->smi; |
1035 | u32 data; |
1036 | |
1037 | if (val->value.i >= 6) |
1038 | return -EINVAL; |
1039 | |
1040 | rtl8366_smi_read_reg(smi, RTL8366_LED_BLINKRATE_REG, &data); |
1041 | |
1042 | data &= ~RTL8366_LED_BLINKRATE_MASK; |
1043 | data |= val->value.i; |
1044 | |
1045 | rtl8366_smi_write_reg(smi, RTL8366_LED_BLINKRATE_REG, data); |
1046 | |
1047 | return 0; |
1048 | } |
1049 | |
1050 | static int rtl8366s_sw_set_vlan_enable(struct switch_dev *dev, |
1051 | const struct switch_attr *attr, |
1052 | struct switch_val *val) |
1053 | { |
1054 | struct rtl8366s *rtl = sw_to_rtl8366s(dev); |
1055 | |
1056 | if (attr->ofs == 1) |
1057 | return rtl8366s_vlan_set_vlan(rtl, val->value.i); |
1058 | else |
1059 | return rtl8366s_vlan_set_4ktable(rtl, val->value.i); |
1060 | } |
1061 | |
1062 | static const char *rtl8366s_speed_str(unsigned speed) |
1063 | { |
1064 | switch (speed) { |
1065 | case 0: |
1066 | return "10baseT"; |
1067 | case 1: |
1068 | return "100baseT"; |
1069 | case 2: |
1070 | return "1000baseT"; |
1071 | } |
1072 | |
1073 | return "unknown"; |
1074 | } |
1075 | |
1076 | static int rtl8366s_sw_get_port_link(struct switch_dev *dev, |
1077 | const struct switch_attr *attr, |
1078 | struct switch_val *val) |
1079 | { |
1080 | struct rtl8366s *rtl = sw_to_rtl8366s(dev); |
1081 | struct rtl8366_smi *smi = &rtl->smi; |
1082 | u32 len = 0, data = 0; |
1083 | |
1084 | if (val->port_vlan >= RTL8366_NUM_PORTS) |
1085 | return -EINVAL; |
1086 | |
1087 | memset(rtl->buf, '\0', sizeof(rtl->buf)); |
1088 | rtl8366_smi_read_reg(smi, RTL8366S_PORT_LINK_STATUS_BASE + |
1089 | (val->port_vlan / 2), &data); |
1090 | |
1091 | if (val->port_vlan % 2) |
1092 | data = data >> 8; |
1093 | |
1094 | if (data & RTL8366S_PORT_STATUS_LINK_MASK) { |
1095 | len = snprintf(rtl->buf, sizeof(rtl->buf), |
1096 | "port:%d link:up speed:%s %s-duplex %s%s%s", |
1097 | val->port_vlan, |
1098 | rtl8366s_speed_str(data & |
1099 | RTL8366S_PORT_STATUS_SPEED_MASK), |
1100 | (data & RTL8366S_PORT_STATUS_DUPLEX_MASK) ? |
1101 | "full" : "half", |
1102 | (data & RTL8366S_PORT_STATUS_TXPAUSE_MASK) ? |
1103 | "tx-pause ": "", |
1104 | (data & RTL8366S_PORT_STATUS_RXPAUSE_MASK) ? |
1105 | "rx-pause " : "", |
1106 | (data & RTL8366S_PORT_STATUS_AN_MASK) ? |
1107 | "nway ": ""); |
1108 | } else { |
1109 | len = snprintf(rtl->buf, sizeof(rtl->buf), "port:%d link: down", |
1110 | val->port_vlan); |
1111 | } |
1112 | |
1113 | val->value.s = rtl->buf; |
1114 | val->len = len; |
1115 | |
1116 | return 0; |
1117 | } |
1118 | |
1119 | static int rtl8366s_sw_get_vlan_info(struct switch_dev *dev, |
1120 | const struct switch_attr *attr, |
1121 | struct switch_val *val) |
1122 | { |
1123 | int i; |
1124 | u32 len = 0; |
1125 | struct rtl8366s_vlanconfig vlanmc; |
1126 | struct rtl8366s_vlan4kentry vlan4k; |
1127 | struct rtl8366s *rtl = sw_to_rtl8366s(dev); |
1128 | char *buf = rtl->buf; |
1129 | |
1130 | if (val->port_vlan == 0 || val->port_vlan >= RTL8366_NUM_VLANS) |
1131 | return -EINVAL; |
1132 | |
1133 | memset(buf, '\0', sizeof(rtl->buf)); |
1134 | |
1135 | rtl8366s_get_vlan_member_config(rtl, val->port_vlan, &vlanmc); |
1136 | rtl8366s_get_vlan_4k_entry(rtl, vlanmc.vid, &vlan4k); |
1137 | |
1138 | len += snprintf(buf + len, sizeof(rtl->buf) - len, "VLAN %d: Ports: ", |
1139 | val->port_vlan); |
1140 | |
1141 | for (i = 0; i < RTL8366_NUM_PORTS; ++i) { |
1142 | int index = 0; |
1143 | if (!rtl8366s_get_port_vlan_index(rtl, i, &index) && |
1144 | index == val->port_vlan) |
1145 | len += snprintf(buf + len, sizeof(rtl->buf) - len, |
1146 | "%d", i); |
1147 | } |
1148 | len += snprintf(buf + len, sizeof(rtl->buf) - len, "\n"); |
1149 | |
1150 | len += snprintf(buf + len, sizeof(rtl->buf) - len, |
1151 | "\t\t vid \t prio \t member \t untag \t fid\n"); |
1152 | len += snprintf(buf + len, sizeof(rtl->buf) - len, "\tMC:\t"); |
1153 | len += snprintf(buf + len, sizeof(rtl->buf) - len, |
1154 | "%d \t %d \t 0x%04x \t 0x%04x \t %d\n", |
1155 | vlanmc.vid, vlanmc.priority, vlanmc.member, |
1156 | vlanmc.untag, vlanmc.fid); |
1157 | len += snprintf(buf + len, sizeof(rtl->buf) - len, "\t4K:\t"); |
1158 | len += snprintf(buf + len, sizeof(rtl->buf) - len, |
1159 | "%d \t \t 0x%04x \t 0x%04x \t %d", |
1160 | vlan4k.vid, vlan4k.member, vlan4k.untag, vlan4k.fid); |
1161 | |
1162 | val->value.s = buf; |
1163 | val->len = len; |
1164 | |
1165 | return 0; |
1166 | } |
1167 | |
1168 | static int rtl8366s_sw_set_port_led(struct switch_dev *dev, |
1169 | const struct switch_attr *attr, |
1170 | struct switch_val *val) |
1171 | { |
1172 | struct rtl8366s *rtl = sw_to_rtl8366s(dev); |
1173 | struct rtl8366_smi *smi = &rtl->smi; |
1174 | u32 data = 0; |
1175 | |
1176 | if (val->port_vlan >= RTL8366_NUM_PORTS || |
1177 | (1 << val->port_vlan) == RTL8366_PORT_UNKNOWN) |
1178 | return -EINVAL; |
1179 | |
1180 | if (val->port_vlan == RTL8366_PORT_NUM_CPU) { |
1181 | rtl8366_smi_read_reg(smi, RTL8366_LED_BLINKRATE_REG, &data); |
1182 | data = (data & (~(0xF << 4))) | (val->value.i << 4); |
1183 | rtl8366_smi_write_reg(smi, RTL8366_LED_BLINKRATE_REG, data); |
1184 | } else { |
1185 | rtl8366_smi_read_reg(smi, RTL8366_LED_CTRL_REG, &data); |
1186 | data = (data & (~(0xF << (val->port_vlan * 4)))) | |
1187 | (val->value.i << (val->port_vlan * 4)); |
1188 | rtl8366_smi_write_reg(smi, RTL8366_LED_CTRL_REG, data); |
1189 | } |
1190 | |
1191 | return 0; |
1192 | } |
1193 | |
1194 | static int rtl8366s_sw_get_port_led(struct switch_dev *dev, |
1195 | const struct switch_attr *attr, |
1196 | struct switch_val *val) |
1197 | { |
1198 | struct rtl8366s *rtl = sw_to_rtl8366s(dev); |
1199 | struct rtl8366_smi *smi = &rtl->smi; |
1200 | u32 data = 0; |
1201 | |
1202 | if (val->port_vlan >= RTL8366_NUM_LEDGROUPS) |
1203 | return -EINVAL; |
1204 | |
1205 | rtl8366_smi_read_reg(smi, RTL8366_LED_CTRL_REG, &data); |
1206 | val->value.i = (data >> (val->port_vlan * 4)) & 0x000F; |
1207 | |
1208 | return 0; |
1209 | } |
1210 | |
1211 | static int rtl8366s_sw_reset_port_mibs(struct switch_dev *dev, |
1212 | const struct switch_attr *attr, |
1213 | struct switch_val *val) |
1214 | { |
1215 | struct rtl8366s *rtl = sw_to_rtl8366s(dev); |
1216 | struct rtl8366_smi *smi = &rtl->smi; |
1217 | u32 data = 0; |
1218 | |
1219 | if (val->port_vlan >= RTL8366_NUM_PORTS) |
1220 | return -EINVAL; |
1221 | |
1222 | rtl8366_smi_read_reg(smi, RTL8366S_MIB_CTRL_REG, &data); |
1223 | data |= (1 << (val->port_vlan + 3)); |
1224 | rtl8366_smi_write_reg(smi, RTL8366S_MIB_CTRL_REG, data); |
1225 | |
1226 | return 0; |
1227 | } |
1228 | |
1229 | static int rtl8366s_sw_get_port_mib(struct switch_dev *dev, |
1230 | const struct switch_attr *attr, |
1231 | struct switch_val *val) |
1232 | { |
1233 | struct rtl8366s *rtl = sw_to_rtl8366s(dev); |
1234 | int i, len = 0; |
1235 | unsigned long long counter = 0; |
1236 | char *buf = rtl->buf; |
1237 | |
1238 | if (val->port_vlan >= RTL8366_NUM_PORTS) |
1239 | return -EINVAL; |
1240 | |
1241 | len += snprintf(buf + len, sizeof(rtl->buf) - len, |
1242 | "Port %d MIB counters\n", |
1243 | val->port_vlan); |
1244 | |
1245 | for (i = 0; i < RTL8366S_MIB_COUNT; ++i) { |
1246 | len += snprintf(buf + len, sizeof(rtl->buf) - len, |
1247 | "%d:%s\t", i, rtl8366s_mib_counters[i].name); |
1248 | if (!rtl8366_get_mib_counter(rtl, i, val->port_vlan, &counter)) |
1249 | len += snprintf(buf + len, sizeof(rtl->buf) - len, |
1250 | "[%llu]\n", counter); |
1251 | else |
1252 | len += snprintf(buf + len, sizeof(rtl->buf) - len, |
1253 | "[error]\n"); |
1254 | } |
1255 | |
1256 | val->value.s = buf; |
1257 | val->len = len; |
1258 | return 0; |
1259 | } |
1260 | |
1261 | static int rtl8366s_sw_get_vlan_ports(struct switch_dev *dev, |
1262 | struct switch_val *val) |
1263 | { |
1264 | struct rtl8366s_vlanconfig vlanmc; |
1265 | struct rtl8366s *rtl = sw_to_rtl8366s(dev); |
1266 | struct switch_port *port; |
1267 | int i; |
1268 | |
1269 | if (val->port_vlan == 0 || val->port_vlan >= RTL8366_NUM_VLANS) |
1270 | return -EINVAL; |
1271 | |
1272 | rtl8366s_get_vlan_member_config(rtl, val->port_vlan, &vlanmc); |
1273 | |
1274 | port = &val->value.ports[0]; |
1275 | val->len = 0; |
1276 | for (i = 0; i < RTL8366_NUM_PORTS; i++) { |
1277 | if (!(vlanmc.member & BIT(i))) |
1278 | continue; |
1279 | |
1280 | port->id = i; |
1281 | port->flags = (vlanmc.untag & BIT(i)) ? |
1282 | 0 : BIT(SWITCH_PORT_FLAG_TAGGED); |
1283 | val->len++; |
1284 | port++; |
1285 | } |
1286 | return 0; |
1287 | } |
1288 | |
1289 | static int rtl8366s_sw_set_vlan_ports(struct switch_dev *dev, |
1290 | struct switch_val *val) |
1291 | { |
1292 | struct rtl8366s_vlanconfig vlanmc; |
1293 | struct rtl8366s_vlan4kentry vlan4k; |
1294 | struct rtl8366s *rtl = sw_to_rtl8366s(dev); |
1295 | struct switch_port *port; |
1296 | int i; |
1297 | |
1298 | if (val->port_vlan == 0 || val->port_vlan >= RTL8366_NUM_VLANS) |
1299 | return -EINVAL; |
1300 | |
1301 | rtl8366s_get_vlan_member_config(rtl, val->port_vlan, &vlanmc); |
1302 | rtl8366s_get_vlan_4k_entry(rtl, vlanmc.vid, &vlan4k); |
1303 | |
1304 | vlanmc.untag = 0; |
1305 | vlanmc.member = 0; |
1306 | |
1307 | port = &val->value.ports[0]; |
1308 | for (i = 0; i < val->len; i++, port++) { |
1309 | vlanmc.member |= BIT(port->id); |
1310 | |
1311 | if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED))) |
1312 | vlanmc.untag |= BIT(port->id); |
1313 | } |
1314 | |
1315 | vlan4k.member = vlanmc.member; |
1316 | vlan4k.untag = vlanmc.untag; |
1317 | |
1318 | rtl8366s_set_vlan_member_config(rtl, val->port_vlan, &vlanmc); |
1319 | rtl8366s_set_vlan_4k_entry(rtl, &vlan4k); |
1320 | return 0; |
1321 | } |
1322 | |
1323 | static int rtl8366s_sw_get_port_pvid(struct switch_dev *dev, int port, int *val) |
1324 | { |
1325 | struct rtl8366s *rtl = sw_to_rtl8366s(dev); |
1326 | return rtl8366s_get_vlan_port_pvid(rtl, port, val); |
1327 | } |
1328 | |
1329 | static int rtl8366s_sw_set_port_pvid(struct switch_dev *dev, int port, int val) |
1330 | { |
1331 | struct rtl8366s *rtl = sw_to_rtl8366s(dev); |
1332 | return rtl8366s_set_vlan_port_pvid(rtl, port, val); |
1333 | } |
1334 | |
1335 | static int rtl8366s_sw_reset_switch(struct switch_dev *dev) |
1336 | { |
1337 | struct rtl8366s *rtl = sw_to_rtl8366s(dev); |
1338 | int err; |
1339 | |
1340 | err = rtl8366s_reset_chip(rtl); |
1341 | if (err) |
1342 | return err; |
1343 | |
1344 | return rtl8366s_reset_vlan(rtl); |
1345 | } |
1346 | |
1347 | static struct switch_attr rtl8366s_globals[] = { |
1348 | { |
1349 | .type = SWITCH_TYPE_INT, |
1350 | .name = "enable_vlan", |
1351 | .description = "Enable VLAN mode", |
1352 | .set = rtl8366s_sw_set_vlan_enable, |
1353 | .get = rtl8366s_sw_get_vlan_enable, |
1354 | .max = 1, |
1355 | .ofs = 1 |
1356 | }, { |
1357 | .type = SWITCH_TYPE_INT, |
1358 | .name = "enable_vlan4k", |
1359 | .description = "Enable VLAN 4K mode", |
1360 | .set = rtl8366s_sw_set_vlan_enable, |
1361 | .get = rtl8366s_sw_get_vlan_enable, |
1362 | .max = 1, |
1363 | .ofs = 2 |
1364 | }, { |
1365 | .type = SWITCH_TYPE_INT, |
1366 | .name = "reset_mibs", |
1367 | .description = "Reset all MIB counters", |
1368 | .set = rtl8366s_sw_reset_mibs, |
1369 | .get = NULL, |
1370 | .max = 1 |
1371 | }, { |
1372 | .type = SWITCH_TYPE_INT, |
1373 | .name = "blinkrate", |
1374 | .description = "Get/Set LED blinking rate (0 = 43ms, 1 = 84ms," |
1375 | " 2 = 120ms, 3 = 170ms, 4 = 340ms, 5 = 670ms)", |
1376 | .set = rtl8366s_sw_set_blinkrate, |
1377 | .get = rtl8366s_sw_get_blinkrate, |
1378 | .max = 5 |
1379 | }, |
1380 | }; |
1381 | |
1382 | static struct switch_attr rtl8366s_port[] = { |
1383 | { |
1384 | .type = SWITCH_TYPE_STRING, |
1385 | .name = "link", |
1386 | .description = "Get port link information", |
1387 | .max = 1, |
1388 | .set = NULL, |
1389 | .get = rtl8366s_sw_get_port_link, |
1390 | }, { |
1391 | .type = SWITCH_TYPE_INT, |
1392 | .name = "reset_mib", |
1393 | .description = "Reset single port MIB counters", |
1394 | .max = 1, |
1395 | .set = rtl8366s_sw_reset_port_mibs, |
1396 | .get = NULL, |
1397 | }, { |
1398 | .type = SWITCH_TYPE_STRING, |
1399 | .name = "mib", |
1400 | .description = "Get MIB counters for port", |
1401 | .max = 33, |
1402 | .set = NULL, |
1403 | .get = rtl8366s_sw_get_port_mib, |
1404 | }, { |
1405 | .type = SWITCH_TYPE_INT, |
1406 | .name = "led", |
1407 | .description = "Get/Set port group (0 - 3) led mode (0 - 15)", |
1408 | .max = 15, |
1409 | .set = rtl8366s_sw_set_port_led, |
1410 | .get = rtl8366s_sw_get_port_led, |
1411 | }, |
1412 | }; |
1413 | |
1414 | static struct switch_attr rtl8366s_vlan[] = { |
1415 | { |
1416 | .type = SWITCH_TYPE_STRING, |
1417 | .name = "info", |
1418 | .description = "Get vlan information", |
1419 | .max = 1, |
1420 | .set = NULL, |
1421 | .get = rtl8366s_sw_get_vlan_info, |
1422 | }, |
1423 | }; |
1424 | |
1425 | /* template */ |
1426 | static struct switch_dev rtl8366_switch_dev = { |
1427 | .name = "RTL8366S", |
1428 | .cpu_port = RTL8366_PORT_NUM_CPU, |
1429 | .ports = RTL8366_NUM_PORTS, |
1430 | .vlans = RTL8366_NUM_VLANS, |
1431 | .attr_global = { |
1432 | .attr = rtl8366s_globals, |
1433 | .n_attr = ARRAY_SIZE(rtl8366s_globals), |
1434 | }, |
1435 | .attr_port = { |
1436 | .attr = rtl8366s_port, |
1437 | .n_attr = ARRAY_SIZE(rtl8366s_port), |
1438 | }, |
1439 | .attr_vlan = { |
1440 | .attr = rtl8366s_vlan, |
1441 | .n_attr = ARRAY_SIZE(rtl8366s_vlan), |
1442 | }, |
1443 | |
1444 | .get_vlan_ports = rtl8366s_sw_get_vlan_ports, |
1445 | .set_vlan_ports = rtl8366s_sw_set_vlan_ports, |
1446 | .get_port_pvid = rtl8366s_sw_get_port_pvid, |
1447 | .set_port_pvid = rtl8366s_sw_set_port_pvid, |
1448 | .reset_switch = rtl8366s_sw_reset_switch, |
1449 | }; |
1450 | |
1451 | static int rtl8366s_switch_init(struct rtl8366s *rtl) |
1452 | { |
1453 | struct switch_dev *dev = &rtl->dev; |
1454 | int err; |
1455 | |
1456 | memcpy(dev, &rtl8366_switch_dev, sizeof(struct switch_dev)); |
1457 | dev->priv = rtl; |
1458 | dev->devname = dev_name(rtl->parent); |
1459 | |
1460 | err = register_switch(dev, NULL); |
1461 | if (err) |
1462 | dev_err(rtl->parent, "switch registration failed\n"); |
1463 | |
1464 | return err; |
1465 | } |
1466 | |
1467 | static void rtl8366s_switch_cleanup(struct rtl8366s *rtl) |
1468 | { |
1469 | unregister_switch(&rtl->dev); |
1470 | } |
1471 | |
1472 | static int rtl8366s_mii_read(struct mii_bus *bus, int addr, int reg) |
1473 | { |
1474 | struct rtl8366s *rtl = smi_to_rtl8366s(bus->priv); |
1475 | u32 val = 0; |
1476 | int err; |
1477 | |
1478 | err = rtl8366s_read_phy_reg(rtl, addr, 0, reg, &val); |
1479 | if (err) |
1480 | return 0xffff; |
1481 | |
1482 | return val; |
1483 | } |
1484 | |
1485 | static int rtl8366s_mii_write(struct mii_bus *bus, int addr, int reg, u16 val) |
1486 | { |
1487 | struct rtl8366s *rtl = smi_to_rtl8366s(bus->priv); |
1488 | u32 t; |
1489 | int err; |
1490 | |
1491 | err = rtl8366s_write_phy_reg(rtl, addr, 0, reg, val); |
1492 | /* flush write */ |
1493 | (void) rtl8366s_read_phy_reg(rtl, addr, 0, reg, &t); |
1494 | |
1495 | return err; |
1496 | } |
1497 | |
1498 | static int rtl8366s_mii_bus_match(struct mii_bus *bus) |
1499 | { |
1500 | return (bus->read == rtl8366s_mii_read && |
1501 | bus->write == rtl8366s_mii_write); |
1502 | } |
1503 | |
1504 | static int rtl8366s_setup(struct rtl8366s *rtl) |
1505 | { |
1506 | int ret; |
1507 | |
1508 | ret = rtl8366s_reset_chip(rtl); |
1509 | if (ret) |
1510 | return ret; |
1511 | |
1512 | rtl8366s_debugfs_init(rtl); |
1513 | return 0; |
1514 | } |
1515 | |
1516 | static int rtl8366s_detect(struct rtl8366_smi *smi) |
1517 | { |
1518 | u32 chip_id = 0; |
1519 | u32 chip_ver = 0; |
1520 | int ret; |
1521 | |
1522 | ret = rtl8366_smi_read_reg(smi, RTL8366S_CHIP_ID_REG, &chip_id); |
1523 | if (ret) { |
1524 | dev_err(smi->parent, "unable to read chip id\n"); |
1525 | return ret; |
1526 | } |
1527 | |
1528 | switch (chip_id) { |
1529 | case RTL8366S_CHIP_ID_8366: |
1530 | break; |
1531 | default: |
1532 | dev_err(smi->parent, "unknown chip id (%04x)\n", chip_id); |
1533 | return -ENODEV; |
1534 | } |
1535 | |
1536 | ret = rtl8366_smi_read_reg(smi, RTL8366S_CHIP_VERSION_CTRL_REG, |
1537 | &chip_ver); |
1538 | if (ret) { |
1539 | dev_err(smi->parent, "unable to read chip version\n"); |
1540 | return ret; |
1541 | } |
1542 | |
1543 | dev_info(smi->parent, "RTL%04x ver. %u chip found\n", |
1544 | chip_id, chip_ver & RTL8366S_CHIP_VERSION_MASK); |
1545 | |
1546 | return 0; |
1547 | } |
1548 | |
1549 | static struct rtl8366_smi_ops rtl8366s_smi_ops = { |
1550 | .detect = rtl8366s_detect, |
1551 | .mii_read = rtl8366s_mii_read, |
1552 | .mii_write = rtl8366s_mii_write, |
1553 | }; |
1554 | |
1555 | static int __init rtl8366s_probe(struct platform_device *pdev) |
1556 | { |
1557 | static int rtl8366_smi_version_printed; |
1558 | struct rtl8366s_platform_data *pdata; |
1559 | struct rtl8366s *rtl; |
1560 | struct rtl8366_smi *smi; |
1561 | int err; |
1562 | |
1563 | if (!rtl8366_smi_version_printed++) |
1564 | printk(KERN_NOTICE RTL8366S_DRIVER_DESC |
1565 | " version " RTL8366S_DRIVER_VER"\n"); |
1566 | |
1567 | pdata = pdev->dev.platform_data; |
1568 | if (!pdata) { |
1569 | dev_err(&pdev->dev, "no platform data specified\n"); |
1570 | err = -EINVAL; |
1571 | goto err_out; |
1572 | } |
1573 | |
1574 | rtl = kzalloc(sizeof(*rtl), GFP_KERNEL); |
1575 | if (!rtl) { |
1576 | dev_err(&pdev->dev, "no memory for private data\n"); |
1577 | err = -ENOMEM; |
1578 | goto err_out; |
1579 | } |
1580 | |
1581 | rtl->parent = &pdev->dev; |
1582 | |
1583 | smi = &rtl->smi; |
1584 | smi->parent = &pdev->dev; |
1585 | smi->gpio_sda = pdata->gpio_sda; |
1586 | smi->gpio_sck = pdata->gpio_sck; |
1587 | smi->ops = &rtl8366s_smi_ops; |
1588 | |
1589 | err = rtl8366_smi_init(smi); |
1590 | if (err) |
1591 | goto err_free_rtl; |
1592 | |
1593 | platform_set_drvdata(pdev, rtl); |
1594 | |
1595 | err = rtl8366s_setup(rtl); |
1596 | if (err) |
1597 | goto err_clear_drvdata; |
1598 | |
1599 | err = rtl8366s_switch_init(rtl); |
1600 | if (err) |
1601 | goto err_clear_drvdata; |
1602 | |
1603 | return 0; |
1604 | |
1605 | err_clear_drvdata: |
1606 | platform_set_drvdata(pdev, NULL); |
1607 | rtl8366_smi_cleanup(smi); |
1608 | err_free_rtl: |
1609 | kfree(rtl); |
1610 | err_out: |
1611 | return err; |
1612 | } |
1613 | |
1614 | static int rtl8366s_phy_config_init(struct phy_device *phydev) |
1615 | { |
1616 | if (!rtl8366s_mii_bus_match(phydev->bus)) |
1617 | return -EINVAL; |
1618 | |
1619 | return 0; |
1620 | } |
1621 | |
1622 | static int rtl8366s_phy_config_aneg(struct phy_device *phydev) |
1623 | { |
1624 | return 0; |
1625 | } |
1626 | |
1627 | static struct phy_driver rtl8366s_phy_driver = { |
1628 | .phy_id = 0x001cc960, |
1629 | .name = "Realtek RTL8366S", |
1630 | .phy_id_mask = 0x1ffffff0, |
1631 | .features = PHY_GBIT_FEATURES, |
1632 | .config_aneg = rtl8366s_phy_config_aneg, |
1633 | .config_init = rtl8366s_phy_config_init, |
1634 | .read_status = genphy_read_status, |
1635 | .driver = { |
1636 | .owner = THIS_MODULE, |
1637 | }, |
1638 | }; |
1639 | |
1640 | static int __devexit rtl8366s_remove(struct platform_device *pdev) |
1641 | { |
1642 | struct rtl8366s *rtl = platform_get_drvdata(pdev); |
1643 | |
1644 | if (rtl) { |
1645 | rtl8366s_switch_cleanup(rtl); |
1646 | rtl8366s_debugfs_remove(rtl); |
1647 | platform_set_drvdata(pdev, NULL); |
1648 | rtl8366_smi_cleanup(&rtl->smi); |
1649 | kfree(rtl); |
1650 | } |
1651 | |
1652 | return 0; |
1653 | } |
1654 | |
1655 | static struct platform_driver rtl8366s_driver = { |
1656 | .driver = { |
1657 | .name = RTL8366S_DRIVER_NAME, |
1658 | .owner = THIS_MODULE, |
1659 | }, |
1660 | .probe = rtl8366s_probe, |
1661 | .remove = __devexit_p(rtl8366s_remove), |
1662 | }; |
1663 | |
1664 | static int __init rtl8366s_module_init(void) |
1665 | { |
1666 | int ret; |
1667 | ret = platform_driver_register(&rtl8366s_driver); |
1668 | if (ret) |
1669 | return ret; |
1670 | |
1671 | ret = phy_driver_register(&rtl8366s_phy_driver); |
1672 | if (ret) |
1673 | goto err_platform_unregister; |
1674 | |
1675 | return 0; |
1676 | |
1677 | err_platform_unregister: |
1678 | platform_driver_unregister(&rtl8366s_driver); |
1679 | return ret; |
1680 | } |
1681 | module_init(rtl8366s_module_init); |
1682 | |
1683 | static void __exit rtl8366s_module_exit(void) |
1684 | { |
1685 | phy_driver_unregister(&rtl8366s_phy_driver); |
1686 | platform_driver_unregister(&rtl8366s_driver); |
1687 | } |
1688 | module_exit(rtl8366s_module_exit); |
1689 | |
1690 | MODULE_DESCRIPTION(RTL8366S_DRIVER_DESC); |
1691 | MODULE_VERSION(RTL8366S_DRIVER_VER); |
1692 | MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); |
1693 | MODULE_AUTHOR("Antti Seppälä <a.seppala@gmail.com>"); |
1694 | MODULE_LICENSE("GPL v2"); |
1695 | MODULE_ALIAS("platform:" RTL8366S_DRIVER_NAME); |
1696 |