Root/
Source at commit 8007d48 created 13 years 8 months ago. By juhosg, generic: rtl8366: add common rtl8366_sw_{get,set}_vlan_ports functions | |
---|---|
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/rtl8366rb.h> |
19 | |
20 | #include "rtl8366_smi.h" |
21 | |
22 | #define RTL8366RB_DRIVER_DESC "Realtek RTL8366RB ethernet switch driver" |
23 | #define RTL8366RB_DRIVER_VER "0.2.2" |
24 | |
25 | #define RTL8366RB_PHY_NO_MAX 4 |
26 | #define RTL8366RB_PHY_PAGE_MAX 7 |
27 | #define RTL8366RB_PHY_ADDR_MAX 31 |
28 | |
29 | /* Switch Global Configuration register */ |
30 | #define RTL8366RB_SGCR 0x0000 |
31 | #define RTL8366RB_SGCR_EN_BC_STORM_CTRL BIT(0) |
32 | #define RTL8366RB_SGCR_MAX_LENGTH(_x) (_x << 4) |
33 | #define RTL8366RB_SGCR_MAX_LENGTH_MASK RTL8366RB_SGCR_MAX_LENGTH(0x3) |
34 | #define RTL8366RB_SGCR_MAX_LENGTH_1522 RTL8366RB_SGCR_MAX_LENGTH(0x0) |
35 | #define RTL8366RB_SGCR_MAX_LENGTH_1536 RTL8366RB_SGCR_MAX_LENGTH(0x1) |
36 | #define RTL8366RB_SGCR_MAX_LENGTH_1552 RTL8366RB_SGCR_MAX_LENGTH(0x2) |
37 | #define RTL8366RB_SGCR_MAX_LENGTH_9216 RTL8366RB_SGCR_MAX_LENGTH(0x3) |
38 | #define RTL8366RB_SGCR_EN_VLAN BIT(13) |
39 | #define RTL8366RB_SGCR_EN_VLAN_4KTB BIT(14) |
40 | |
41 | /* Port Enable Control register */ |
42 | #define RTL8366RB_PECR 0x0001 |
43 | |
44 | /* Switch Security Control registers */ |
45 | #define RTL8366RB_SSCR0 0x0002 |
46 | #define RTL8366RB_SSCR1 0x0003 |
47 | #define RTL8366RB_SSCR2 0x0004 |
48 | #define RTL8366RB_SSCR2_DROP_UNKNOWN_DA BIT(0) |
49 | |
50 | #define RTL8366RB_RESET_CTRL_REG 0x0100 |
51 | #define RTL8366RB_CHIP_CTRL_RESET_HW 1 |
52 | #define RTL8366RB_CHIP_CTRL_RESET_SW (1 << 1) |
53 | |
54 | #define RTL8366RB_CHIP_VERSION_CTRL_REG 0x050A |
55 | #define RTL8366RB_CHIP_VERSION_MASK 0xf |
56 | #define RTL8366RB_CHIP_ID_REG 0x0509 |
57 | #define RTL8366RB_CHIP_ID_8366 0x5937 |
58 | |
59 | /* PHY registers control */ |
60 | #define RTL8366RB_PHY_ACCESS_CTRL_REG 0x8000 |
61 | #define RTL8366RB_PHY_ACCESS_DATA_REG 0x8002 |
62 | |
63 | #define RTL8366RB_PHY_CTRL_READ 1 |
64 | #define RTL8366RB_PHY_CTRL_WRITE 0 |
65 | |
66 | #define RTL8366RB_PHY_REG_MASK 0x1f |
67 | #define RTL8366RB_PHY_PAGE_OFFSET 5 |
68 | #define RTL8366RB_PHY_PAGE_MASK (0xf << 5) |
69 | #define RTL8366RB_PHY_NO_OFFSET 9 |
70 | #define RTL8366RB_PHY_NO_MASK (0x1f << 9) |
71 | |
72 | #define RTL8366RB_VLAN_INGRESS_CTRL2_REG 0x037f |
73 | |
74 | /* LED control registers */ |
75 | #define RTL8366RB_LED_BLINKRATE_REG 0x0430 |
76 | #define RTL8366RB_LED_BLINKRATE_BIT 0 |
77 | #define RTL8366RB_LED_BLINKRATE_MASK 0x0007 |
78 | |
79 | #define RTL8366RB_LED_CTRL_REG 0x0431 |
80 | #define RTL8366RB_LED_0_1_CTRL_REG 0x0432 |
81 | #define RTL8366RB_LED_2_3_CTRL_REG 0x0433 |
82 | |
83 | #define RTL8366RB_MIB_COUNT 33 |
84 | #define RTL8366RB_GLOBAL_MIB_COUNT 1 |
85 | #define RTL8366RB_MIB_COUNTER_PORT_OFFSET 0x0050 |
86 | #define RTL8366RB_MIB_COUNTER_BASE 0x1000 |
87 | #define RTL8366RB_MIB_CTRL_REG 0x13F0 |
88 | #define RTL8366RB_MIB_CTRL_USER_MASK 0x0FFC |
89 | #define RTL8366RB_MIB_CTRL_BUSY_MASK BIT(0) |
90 | #define RTL8366RB_MIB_CTRL_RESET_MASK BIT(1) |
91 | #define RTL8366RB_MIB_CTRL_PORT_RESET(_p) BIT(2 + (_p)) |
92 | #define RTL8366RB_MIB_CTRL_GLOBAL_RESET BIT(11) |
93 | |
94 | #define RTL8366RB_PORT_VLAN_CTRL_BASE 0x0063 |
95 | #define RTL8366RB_PORT_VLAN_CTRL_REG(_p) \ |
96 | (RTL8366RB_PORT_VLAN_CTRL_BASE + (_p) / 4) |
97 | #define RTL8366RB_PORT_VLAN_CTRL_MASK 0xf |
98 | #define RTL8366RB_PORT_VLAN_CTRL_SHIFT(_p) (4 * ((_p) % 4)) |
99 | |
100 | |
101 | #define RTL8366RB_VLAN_TABLE_READ_BASE 0x018C |
102 | #define RTL8366RB_VLAN_TABLE_WRITE_BASE 0x0185 |
103 | |
104 | |
105 | #define RTL8366RB_TABLE_ACCESS_CTRL_REG 0x0180 |
106 | #define RTL8366RB_TABLE_VLAN_READ_CTRL 0x0E01 |
107 | #define RTL8366RB_TABLE_VLAN_WRITE_CTRL 0x0F01 |
108 | |
109 | #define RTL8366RB_VLAN_MC_BASE(_x) (0x0020 + (_x) * 3) |
110 | |
111 | |
112 | #define RTL8366RB_PORT_LINK_STATUS_BASE 0x0014 |
113 | #define RTL8366RB_PORT_STATUS_SPEED_MASK 0x0003 |
114 | #define RTL8366RB_PORT_STATUS_DUPLEX_MASK 0x0004 |
115 | #define RTL8366RB_PORT_STATUS_LINK_MASK 0x0010 |
116 | #define RTL8366RB_PORT_STATUS_TXPAUSE_MASK 0x0020 |
117 | #define RTL8366RB_PORT_STATUS_RXPAUSE_MASK 0x0040 |
118 | #define RTL8366RB_PORT_STATUS_AN_MASK 0x0080 |
119 | |
120 | |
121 | #define RTL8366RB_PORT_NUM_CPU 5 |
122 | #define RTL8366RB_NUM_PORTS 6 |
123 | #define RTL8366RB_NUM_VLANS 16 |
124 | #define RTL8366RB_NUM_LEDGROUPS 4 |
125 | #define RTL8366RB_NUM_VIDS 4096 |
126 | #define RTL8366RB_PRIORITYMAX 7 |
127 | #define RTL8366RB_FIDMAX 7 |
128 | |
129 | |
130 | #define RTL8366RB_PORT_1 (1 << 0) /* In userspace port 0 */ |
131 | #define RTL8366RB_PORT_2 (1 << 1) /* In userspace port 1 */ |
132 | #define RTL8366RB_PORT_3 (1 << 2) /* In userspace port 2 */ |
133 | #define RTL8366RB_PORT_4 (1 << 3) /* In userspace port 3 */ |
134 | #define RTL8366RB_PORT_5 (1 << 4) /* In userspace port 4 */ |
135 | |
136 | #define RTL8366RB_PORT_CPU (1 << 5) /* CPU port */ |
137 | |
138 | #define RTL8366RB_PORT_ALL (RTL8366RB_PORT_1 | \ |
139 | RTL8366RB_PORT_2 | \ |
140 | RTL8366RB_PORT_3 | \ |
141 | RTL8366RB_PORT_4 | \ |
142 | RTL8366RB_PORT_5 | \ |
143 | RTL8366RB_PORT_CPU) |
144 | |
145 | #define RTL8366RB_PORT_ALL_BUT_CPU (RTL8366RB_PORT_1 | \ |
146 | RTL8366RB_PORT_2 | \ |
147 | RTL8366RB_PORT_3 | \ |
148 | RTL8366RB_PORT_4 | \ |
149 | RTL8366RB_PORT_5) |
150 | |
151 | #define RTL8366RB_PORT_ALL_EXTERNAL (RTL8366RB_PORT_1 | \ |
152 | RTL8366RB_PORT_2 | \ |
153 | RTL8366RB_PORT_3 | \ |
154 | RTL8366RB_PORT_4) |
155 | |
156 | #define RTL8366RB_PORT_ALL_INTERNAL RTL8366RB_PORT_CPU |
157 | |
158 | #define RTL8366RB_VLAN_VID_MASK 0xfff |
159 | #define RTL8366RB_VLAN_PRIORITY_SHIFT 12 |
160 | #define RTL8366RB_VLAN_PRIORITY_MASK 0x7 |
161 | #define RTL8366RB_VLAN_UNTAG_SHIFT 8 |
162 | #define RTL8366RB_VLAN_UNTAG_MASK 0xff |
163 | #define RTL8366RB_VLAN_MEMBER_MASK 0xff |
164 | #define RTL8366RB_VLAN_FID_MASK 0x7 |
165 | |
166 | static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = { |
167 | { 0, 0, 4, "IfInOctets" }, |
168 | { 0, 4, 4, "EtherStatsOctets" }, |
169 | { 0, 8, 2, "EtherStatsUnderSizePkts" }, |
170 | { 0, 10, 2, "EtherFragments" }, |
171 | { 0, 12, 2, "EtherStatsPkts64Octets" }, |
172 | { 0, 14, 2, "EtherStatsPkts65to127Octets" }, |
173 | { 0, 16, 2, "EtherStatsPkts128to255Octets" }, |
174 | { 0, 18, 2, "EtherStatsPkts256to511Octets" }, |
175 | { 0, 20, 2, "EtherStatsPkts512to1023Octets" }, |
176 | { 0, 22, 2, "EtherStatsPkts1024to1518Octets" }, |
177 | { 0, 24, 2, "EtherOversizeStats" }, |
178 | { 0, 26, 2, "EtherStatsJabbers" }, |
179 | { 0, 28, 2, "IfInUcastPkts" }, |
180 | { 0, 30, 2, "EtherStatsMulticastPkts" }, |
181 | { 0, 32, 2, "EtherStatsBroadcastPkts" }, |
182 | { 0, 34, 2, "EtherStatsDropEvents" }, |
183 | { 0, 36, 2, "Dot3StatsFCSErrors" }, |
184 | { 0, 38, 2, "Dot3StatsSymbolErrors" }, |
185 | { 0, 40, 2, "Dot3InPauseFrames" }, |
186 | { 0, 42, 2, "Dot3ControlInUnknownOpcodes" }, |
187 | { 0, 44, 4, "IfOutOctets" }, |
188 | { 0, 48, 2, "Dot3StatsSingleCollisionFrames" }, |
189 | { 0, 50, 2, "Dot3StatMultipleCollisionFrames" }, |
190 | { 0, 52, 2, "Dot3sDeferredTransmissions" }, |
191 | { 0, 54, 2, "Dot3StatsLateCollisions" }, |
192 | { 0, 56, 2, "EtherStatsCollisions" }, |
193 | { 0, 58, 2, "Dot3StatsExcessiveCollisions" }, |
194 | { 0, 60, 2, "Dot3OutPauseFrames" }, |
195 | { 0, 62, 2, "Dot1dBasePortDelayExceededDiscards" }, |
196 | { 0, 64, 2, "Dot1dTpPortInDiscards" }, |
197 | { 0, 66, 2, "IfOutUcastPkts" }, |
198 | { 0, 68, 2, "IfOutMulticastPkts" }, |
199 | { 0, 70, 2, "IfOutBroadcastPkts" }, |
200 | }; |
201 | |
202 | #define REG_WR(_smi, _reg, _val) \ |
203 | do { \ |
204 | err = rtl8366_smi_write_reg(_smi, _reg, _val); \ |
205 | if (err) \ |
206 | return err; \ |
207 | } while (0) |
208 | |
209 | #define REG_RMW(_smi, _reg, _mask, _val) \ |
210 | do { \ |
211 | err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val); \ |
212 | if (err) \ |
213 | return err; \ |
214 | } while (0) |
215 | |
216 | static int rtl8366rb_reset_chip(struct rtl8366_smi *smi) |
217 | { |
218 | int timeout = 10; |
219 | u32 data; |
220 | |
221 | rtl8366_smi_write_reg(smi, RTL8366RB_RESET_CTRL_REG, |
222 | RTL8366RB_CHIP_CTRL_RESET_HW); |
223 | do { |
224 | msleep(1); |
225 | if (rtl8366_smi_read_reg(smi, RTL8366RB_RESET_CTRL_REG, &data)) |
226 | return -EIO; |
227 | |
228 | if (!(data & RTL8366RB_CHIP_CTRL_RESET_HW)) |
229 | break; |
230 | } while (--timeout); |
231 | |
232 | if (!timeout) { |
233 | printk("Timeout waiting for the switch to reset\n"); |
234 | return -EIO; |
235 | } |
236 | |
237 | return 0; |
238 | } |
239 | |
240 | static int rtl8366rb_hw_init(struct rtl8366_smi *smi) |
241 | { |
242 | int err; |
243 | |
244 | /* set maximum packet length to 1536 bytes */ |
245 | REG_RMW(smi, RTL8366RB_SGCR, RTL8366RB_SGCR_MAX_LENGTH_MASK, |
246 | RTL8366RB_SGCR_MAX_LENGTH_1536); |
247 | |
248 | /* enable all ports */ |
249 | REG_WR(smi, RTL8366RB_PECR, 0); |
250 | |
251 | /* disable learning for all ports */ |
252 | REG_WR(smi, RTL8366RB_SSCR0, RTL8366RB_PORT_ALL); |
253 | |
254 | /* disable auto ageing for all ports */ |
255 | REG_WR(smi, RTL8366RB_SSCR1, RTL8366RB_PORT_ALL); |
256 | |
257 | /* |
258 | * discard VLAN tagged packets if the port is not a member of |
259 | * the VLAN with which the packets is associated. |
260 | */ |
261 | REG_WR(smi, RTL8366RB_VLAN_INGRESS_CTRL2_REG, RTL8366RB_PORT_ALL); |
262 | |
263 | /* don't drop packets whose DA has not been learned */ |
264 | REG_RMW(smi, RTL8366RB_SSCR2, RTL8366RB_SSCR2_DROP_UNKNOWN_DA, 0); |
265 | |
266 | return 0; |
267 | } |
268 | |
269 | static int rtl8366rb_read_phy_reg(struct rtl8366_smi *smi, |
270 | u32 phy_no, u32 page, u32 addr, u32 *data) |
271 | { |
272 | u32 reg; |
273 | int ret; |
274 | |
275 | if (phy_no > RTL8366RB_PHY_NO_MAX) |
276 | return -EINVAL; |
277 | |
278 | if (page > RTL8366RB_PHY_PAGE_MAX) |
279 | return -EINVAL; |
280 | |
281 | if (addr > RTL8366RB_PHY_ADDR_MAX) |
282 | return -EINVAL; |
283 | |
284 | ret = rtl8366_smi_write_reg(smi, RTL8366RB_PHY_ACCESS_CTRL_REG, |
285 | RTL8366RB_PHY_CTRL_READ); |
286 | if (ret) |
287 | return ret; |
288 | |
289 | reg = 0x8000 | (1 << (phy_no + RTL8366RB_PHY_NO_OFFSET)) | |
290 | ((page << RTL8366RB_PHY_PAGE_OFFSET) & RTL8366RB_PHY_PAGE_MASK) | |
291 | (addr & RTL8366RB_PHY_REG_MASK); |
292 | |
293 | ret = rtl8366_smi_write_reg(smi, reg, 0); |
294 | if (ret) |
295 | return ret; |
296 | |
297 | ret = rtl8366_smi_read_reg(smi, RTL8366RB_PHY_ACCESS_DATA_REG, data); |
298 | if (ret) |
299 | return ret; |
300 | |
301 | return 0; |
302 | } |
303 | |
304 | static int rtl8366rb_write_phy_reg(struct rtl8366_smi *smi, |
305 | u32 phy_no, u32 page, u32 addr, u32 data) |
306 | { |
307 | u32 reg; |
308 | int ret; |
309 | |
310 | if (phy_no > RTL8366RB_PHY_NO_MAX) |
311 | return -EINVAL; |
312 | |
313 | if (page > RTL8366RB_PHY_PAGE_MAX) |
314 | return -EINVAL; |
315 | |
316 | if (addr > RTL8366RB_PHY_ADDR_MAX) |
317 | return -EINVAL; |
318 | |
319 | ret = rtl8366_smi_write_reg(smi, RTL8366RB_PHY_ACCESS_CTRL_REG, |
320 | RTL8366RB_PHY_CTRL_WRITE); |
321 | if (ret) |
322 | return ret; |
323 | |
324 | reg = 0x8000 | (1 << (phy_no + RTL8366RB_PHY_NO_OFFSET)) | |
325 | ((page << RTL8366RB_PHY_PAGE_OFFSET) & RTL8366RB_PHY_PAGE_MASK) | |
326 | (addr & RTL8366RB_PHY_REG_MASK); |
327 | |
328 | ret = rtl8366_smi_write_reg(smi, reg, data); |
329 | if (ret) |
330 | return ret; |
331 | |
332 | return 0; |
333 | } |
334 | |
335 | static int rtl8366rb_get_mib_counter(struct rtl8366_smi *smi, int counter, |
336 | int port, unsigned long long *val) |
337 | { |
338 | int i; |
339 | int err; |
340 | u32 addr, data; |
341 | u64 mibvalue; |
342 | |
343 | if (port > RTL8366RB_NUM_PORTS || counter >= RTL8366RB_MIB_COUNT) |
344 | return -EINVAL; |
345 | |
346 | addr = RTL8366RB_MIB_COUNTER_BASE + |
347 | RTL8366RB_MIB_COUNTER_PORT_OFFSET * (port) + |
348 | rtl8366rb_mib_counters[counter].offset; |
349 | |
350 | /* |
351 | * Writing access counter address first |
352 | * then ASIC will prepare 64bits counter wait for being retrived |
353 | */ |
354 | data = 0; /* writing data will be discard by ASIC */ |
355 | err = rtl8366_smi_write_reg(smi, addr, data); |
356 | if (err) |
357 | return err; |
358 | |
359 | /* read MIB control register */ |
360 | err = rtl8366_smi_read_reg(smi, RTL8366RB_MIB_CTRL_REG, &data); |
361 | if (err) |
362 | return err; |
363 | |
364 | if (data & RTL8366RB_MIB_CTRL_BUSY_MASK) |
365 | return -EBUSY; |
366 | |
367 | if (data & RTL8366RB_MIB_CTRL_RESET_MASK) |
368 | return -EIO; |
369 | |
370 | mibvalue = 0; |
371 | for (i = rtl8366rb_mib_counters[counter].length; i > 0; i--) { |
372 | err = rtl8366_smi_read_reg(smi, addr + (i - 1), &data); |
373 | if (err) |
374 | return err; |
375 | |
376 | mibvalue = (mibvalue << 16) | (data & 0xFFFF); |
377 | } |
378 | |
379 | *val = mibvalue; |
380 | return 0; |
381 | } |
382 | |
383 | static int rtl8366rb_get_vlan_4k(struct rtl8366_smi *smi, u32 vid, |
384 | struct rtl8366_vlan_4k *vlan4k) |
385 | { |
386 | u32 data[3]; |
387 | int err; |
388 | int i; |
389 | |
390 | memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k)); |
391 | |
392 | if (vid >= RTL8366RB_NUM_VIDS) |
393 | return -EINVAL; |
394 | |
395 | /* write VID */ |
396 | err = rtl8366_smi_write_reg(smi, RTL8366RB_VLAN_TABLE_WRITE_BASE, |
397 | vid & RTL8366RB_VLAN_VID_MASK); |
398 | if (err) |
399 | return err; |
400 | |
401 | /* write table access control word */ |
402 | err = rtl8366_smi_write_reg(smi, RTL8366RB_TABLE_ACCESS_CTRL_REG, |
403 | RTL8366RB_TABLE_VLAN_READ_CTRL); |
404 | if (err) |
405 | return err; |
406 | |
407 | for (i = 0; i < 3; i++) { |
408 | err = rtl8366_smi_read_reg(smi, |
409 | RTL8366RB_VLAN_TABLE_READ_BASE + i, |
410 | &data[i]); |
411 | if (err) |
412 | return err; |
413 | } |
414 | |
415 | vlan4k->vid = vid; |
416 | vlan4k->untag = (data[1] >> RTL8366RB_VLAN_UNTAG_SHIFT) & |
417 | RTL8366RB_VLAN_UNTAG_MASK; |
418 | vlan4k->member = data[1] & RTL8366RB_VLAN_MEMBER_MASK; |
419 | vlan4k->fid = data[2] & RTL8366RB_VLAN_FID_MASK; |
420 | |
421 | return 0; |
422 | } |
423 | |
424 | static int rtl8366rb_set_vlan_4k(struct rtl8366_smi *smi, |
425 | const struct rtl8366_vlan_4k *vlan4k) |
426 | { |
427 | u32 data[3]; |
428 | int err; |
429 | int i; |
430 | |
431 | if (vlan4k->vid >= RTL8366RB_NUM_VIDS || |
432 | vlan4k->member > RTL8366RB_PORT_ALL || |
433 | vlan4k->untag > RTL8366RB_PORT_ALL || |
434 | vlan4k->fid > RTL8366RB_FIDMAX) |
435 | return -EINVAL; |
436 | |
437 | data[0] = vlan4k->vid & RTL8366RB_VLAN_VID_MASK; |
438 | data[1] = (vlan4k->member & RTL8366RB_VLAN_MEMBER_MASK) | |
439 | ((vlan4k->untag & RTL8366RB_VLAN_UNTAG_MASK) << |
440 | RTL8366RB_VLAN_UNTAG_SHIFT); |
441 | data[2] = vlan4k->fid & RTL8366RB_VLAN_FID_MASK; |
442 | |
443 | for (i = 0; i < 3; i++) { |
444 | err = rtl8366_smi_write_reg(smi, |
445 | RTL8366RB_VLAN_TABLE_WRITE_BASE + i, |
446 | data[i]); |
447 | if (err) |
448 | return err; |
449 | } |
450 | |
451 | /* write table access control word */ |
452 | err = rtl8366_smi_write_reg(smi, RTL8366RB_TABLE_ACCESS_CTRL_REG, |
453 | RTL8366RB_TABLE_VLAN_WRITE_CTRL); |
454 | |
455 | return err; |
456 | } |
457 | |
458 | static int rtl8366rb_get_vlan_mc(struct rtl8366_smi *smi, u32 index, |
459 | struct rtl8366_vlan_mc *vlanmc) |
460 | { |
461 | u32 data[3]; |
462 | int err; |
463 | int i; |
464 | |
465 | memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc)); |
466 | |
467 | if (index >= RTL8366RB_NUM_VLANS) |
468 | return -EINVAL; |
469 | |
470 | for (i = 0; i < 3; i++) { |
471 | err = rtl8366_smi_read_reg(smi, |
472 | RTL8366RB_VLAN_MC_BASE(index) + i, |
473 | &data[i]); |
474 | if (err) |
475 | return err; |
476 | } |
477 | |
478 | vlanmc->vid = data[0] & RTL8366RB_VLAN_VID_MASK; |
479 | vlanmc->priority = (data[0] >> RTL8366RB_VLAN_PRIORITY_SHIFT) & |
480 | RTL8366RB_VLAN_PRIORITY_MASK; |
481 | vlanmc->untag = (data[1] >> RTL8366RB_VLAN_UNTAG_SHIFT) & |
482 | RTL8366RB_VLAN_UNTAG_MASK; |
483 | vlanmc->member = data[1] & RTL8366RB_VLAN_MEMBER_MASK; |
484 | vlanmc->fid = data[2] & RTL8366RB_VLAN_FID_MASK; |
485 | |
486 | return 0; |
487 | } |
488 | |
489 | static int rtl8366rb_set_vlan_mc(struct rtl8366_smi *smi, u32 index, |
490 | const struct rtl8366_vlan_mc *vlanmc) |
491 | { |
492 | u32 data[3]; |
493 | int err; |
494 | int i; |
495 | |
496 | if (index >= RTL8366RB_NUM_VLANS || |
497 | vlanmc->vid >= RTL8366RB_NUM_VIDS || |
498 | vlanmc->priority > RTL8366RB_PRIORITYMAX || |
499 | vlanmc->member > RTL8366RB_PORT_ALL || |
500 | vlanmc->untag > RTL8366RB_PORT_ALL || |
501 | vlanmc->fid > RTL8366RB_FIDMAX) |
502 | return -EINVAL; |
503 | |
504 | data[0] = (vlanmc->vid & RTL8366RB_VLAN_VID_MASK) | |
505 | ((vlanmc->priority & RTL8366RB_VLAN_PRIORITY_MASK) << |
506 | RTL8366RB_VLAN_PRIORITY_SHIFT); |
507 | data[1] = (vlanmc->member & RTL8366RB_VLAN_MEMBER_MASK) | |
508 | ((vlanmc->untag & RTL8366RB_VLAN_UNTAG_MASK) << |
509 | RTL8366RB_VLAN_UNTAG_SHIFT); |
510 | data[2] = vlanmc->fid & RTL8366RB_VLAN_FID_MASK; |
511 | |
512 | for (i = 0; i < 3; i++) { |
513 | err = rtl8366_smi_write_reg(smi, |
514 | RTL8366RB_VLAN_MC_BASE(index) + i, |
515 | data[i]); |
516 | if (err) |
517 | return err; |
518 | } |
519 | |
520 | return 0; |
521 | } |
522 | |
523 | static int rtl8366rb_get_mc_index(struct rtl8366_smi *smi, int port, int *val) |
524 | { |
525 | u32 data; |
526 | int err; |
527 | |
528 | if (port >= RTL8366RB_NUM_PORTS) |
529 | return -EINVAL; |
530 | |
531 | err = rtl8366_smi_read_reg(smi, RTL8366RB_PORT_VLAN_CTRL_REG(port), |
532 | &data); |
533 | if (err) |
534 | return err; |
535 | |
536 | *val = (data >> RTL8366RB_PORT_VLAN_CTRL_SHIFT(port)) & |
537 | RTL8366RB_PORT_VLAN_CTRL_MASK; |
538 | |
539 | return 0; |
540 | |
541 | } |
542 | |
543 | static int rtl8366rb_set_mc_index(struct rtl8366_smi *smi, int port, int index) |
544 | { |
545 | if (port >= RTL8366RB_NUM_PORTS || index >= RTL8366RB_NUM_VLANS) |
546 | return -EINVAL; |
547 | |
548 | return rtl8366_smi_rmwr(smi, RTL8366RB_PORT_VLAN_CTRL_REG(port), |
549 | RTL8366RB_PORT_VLAN_CTRL_MASK << |
550 | RTL8366RB_PORT_VLAN_CTRL_SHIFT(port), |
551 | (index & RTL8366RB_PORT_VLAN_CTRL_MASK) << |
552 | RTL8366RB_PORT_VLAN_CTRL_SHIFT(port)); |
553 | } |
554 | |
555 | static int rtl8366rb_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan) |
556 | { |
557 | if (vlan == 0 || vlan >= RTL8366RB_NUM_VLANS) |
558 | return 0; |
559 | |
560 | return 1; |
561 | } |
562 | |
563 | static int rtl8366rb_vlan_set_vlan(struct rtl8366_smi *smi, int enable) |
564 | { |
565 | return rtl8366_smi_rmwr(smi, RTL8366RB_SGCR, RTL8366RB_SGCR_EN_VLAN, |
566 | (enable) ? RTL8366RB_SGCR_EN_VLAN : 0); |
567 | } |
568 | |
569 | static int rtl8366rb_vlan_set_4ktable(struct rtl8366_smi *smi, int enable) |
570 | { |
571 | return rtl8366_smi_rmwr(smi, RTL8366RB_SGCR, |
572 | RTL8366RB_SGCR_EN_VLAN_4KTB, |
573 | (enable) ? RTL8366RB_SGCR_EN_VLAN_4KTB : 0); |
574 | } |
575 | |
576 | static int rtl8366rb_sw_reset_mibs(struct switch_dev *dev, |
577 | const struct switch_attr *attr, |
578 | struct switch_val *val) |
579 | { |
580 | struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); |
581 | |
582 | return rtl8366_smi_rmwr(smi, RTL8366RB_MIB_CTRL_REG, 0, |
583 | RTL8366RB_MIB_CTRL_GLOBAL_RESET); |
584 | } |
585 | |
586 | static int rtl8366rb_sw_get_vlan_enable(struct switch_dev *dev, |
587 | const struct switch_attr *attr, |
588 | struct switch_val *val) |
589 | { |
590 | struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); |
591 | u32 data; |
592 | |
593 | if (attr->ofs == 1) { |
594 | rtl8366_smi_read_reg(smi, RTL8366RB_SGCR, &data); |
595 | |
596 | if (data & RTL8366RB_SGCR_EN_VLAN) |
597 | val->value.i = 1; |
598 | else |
599 | val->value.i = 0; |
600 | } else if (attr->ofs == 2) { |
601 | rtl8366_smi_read_reg(smi, RTL8366RB_SGCR, &data); |
602 | |
603 | if (data & RTL8366RB_SGCR_EN_VLAN_4KTB) |
604 | val->value.i = 1; |
605 | else |
606 | val->value.i = 0; |
607 | } |
608 | |
609 | return 0; |
610 | } |
611 | |
612 | static int rtl8366rb_sw_get_blinkrate(struct switch_dev *dev, |
613 | const struct switch_attr *attr, |
614 | struct switch_val *val) |
615 | { |
616 | struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); |
617 | u32 data; |
618 | |
619 | rtl8366_smi_read_reg(smi, RTL8366RB_LED_BLINKRATE_REG, &data); |
620 | |
621 | val->value.i = (data & (RTL8366RB_LED_BLINKRATE_MASK)); |
622 | |
623 | return 0; |
624 | } |
625 | |
626 | static int rtl8366rb_sw_set_blinkrate(struct switch_dev *dev, |
627 | const struct switch_attr *attr, |
628 | struct switch_val *val) |
629 | { |
630 | struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); |
631 | |
632 | if (val->value.i >= 6) |
633 | return -EINVAL; |
634 | |
635 | return rtl8366_smi_rmwr(smi, RTL8366RB_LED_BLINKRATE_REG, |
636 | RTL8366RB_LED_BLINKRATE_MASK, |
637 | val->value.i); |
638 | } |
639 | |
640 | static int rtl8366rb_sw_set_vlan_enable(struct switch_dev *dev, |
641 | const struct switch_attr *attr, |
642 | struct switch_val *val) |
643 | { |
644 | struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); |
645 | |
646 | if (attr->ofs == 1) |
647 | return rtl8366rb_vlan_set_vlan(smi, val->value.i); |
648 | else |
649 | return rtl8366rb_vlan_set_4ktable(smi, val->value.i); |
650 | } |
651 | |
652 | static const char *rtl8366rb_speed_str(unsigned speed) |
653 | { |
654 | switch (speed) { |
655 | case 0: |
656 | return "10baseT"; |
657 | case 1: |
658 | return "100baseT"; |
659 | case 2: |
660 | return "1000baseT"; |
661 | } |
662 | |
663 | return "unknown"; |
664 | } |
665 | |
666 | static int rtl8366rb_sw_get_port_link(struct switch_dev *dev, |
667 | const struct switch_attr *attr, |
668 | struct switch_val *val) |
669 | { |
670 | struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); |
671 | u32 len = 0, data = 0; |
672 | |
673 | if (val->port_vlan >= RTL8366RB_NUM_PORTS) |
674 | return -EINVAL; |
675 | |
676 | memset(smi->buf, '\0', sizeof(smi->buf)); |
677 | rtl8366_smi_read_reg(smi, RTL8366RB_PORT_LINK_STATUS_BASE + |
678 | (val->port_vlan / 2), &data); |
679 | |
680 | if (val->port_vlan % 2) |
681 | data = data >> 8; |
682 | |
683 | if (data & RTL8366RB_PORT_STATUS_LINK_MASK) { |
684 | len = snprintf(smi->buf, sizeof(smi->buf), |
685 | "port:%d link:up speed:%s %s-duplex %s%s%s", |
686 | val->port_vlan, |
687 | rtl8366rb_speed_str(data & |
688 | RTL8366RB_PORT_STATUS_SPEED_MASK), |
689 | (data & RTL8366RB_PORT_STATUS_DUPLEX_MASK) ? |
690 | "full" : "half", |
691 | (data & RTL8366RB_PORT_STATUS_TXPAUSE_MASK) ? |
692 | "tx-pause ": "", |
693 | (data & RTL8366RB_PORT_STATUS_RXPAUSE_MASK) ? |
694 | "rx-pause " : "", |
695 | (data & RTL8366RB_PORT_STATUS_AN_MASK) ? |
696 | "nway ": ""); |
697 | } else { |
698 | len = snprintf(smi->buf, sizeof(smi->buf), "port:%d link: down", |
699 | val->port_vlan); |
700 | } |
701 | |
702 | val->value.s = smi->buf; |
703 | val->len = len; |
704 | |
705 | return 0; |
706 | } |
707 | |
708 | static int rtl8366rb_sw_set_port_led(struct switch_dev *dev, |
709 | const struct switch_attr *attr, |
710 | struct switch_val *val) |
711 | { |
712 | struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); |
713 | u32 data; |
714 | u32 mask; |
715 | u32 reg; |
716 | |
717 | if (val->port_vlan >= RTL8366RB_NUM_PORTS) |
718 | return -EINVAL; |
719 | |
720 | if (val->port_vlan == RTL8366RB_PORT_NUM_CPU) { |
721 | reg = RTL8366RB_LED_BLINKRATE_REG; |
722 | mask = 0xF << 4; |
723 | data = val->value.i << 4; |
724 | } else { |
725 | reg = RTL8366RB_LED_CTRL_REG; |
726 | mask = 0xF << (val->port_vlan * 4), |
727 | data = val->value.i << (val->port_vlan * 4); |
728 | } |
729 | |
730 | return rtl8366_smi_rmwr(smi, RTL8366RB_LED_BLINKRATE_REG, mask, data); |
731 | } |
732 | |
733 | static int rtl8366rb_sw_get_port_led(struct switch_dev *dev, |
734 | const struct switch_attr *attr, |
735 | struct switch_val *val) |
736 | { |
737 | struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); |
738 | u32 data = 0; |
739 | |
740 | if (val->port_vlan >= RTL8366RB_NUM_LEDGROUPS) |
741 | return -EINVAL; |
742 | |
743 | rtl8366_smi_read_reg(smi, RTL8366RB_LED_CTRL_REG, &data); |
744 | val->value.i = (data >> (val->port_vlan * 4)) & 0x000F; |
745 | |
746 | return 0; |
747 | } |
748 | |
749 | static int rtl8366rb_sw_reset_port_mibs(struct switch_dev *dev, |
750 | const struct switch_attr *attr, |
751 | struct switch_val *val) |
752 | { |
753 | struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); |
754 | |
755 | if (val->port_vlan >= RTL8366RB_NUM_PORTS) |
756 | return -EINVAL; |
757 | |
758 | return rtl8366_smi_rmwr(smi, RTL8366RB_MIB_CTRL_REG, 0, |
759 | RTL8366RB_MIB_CTRL_PORT_RESET(val->port_vlan)); |
760 | } |
761 | |
762 | static int rtl8366rb_sw_reset_switch(struct switch_dev *dev) |
763 | { |
764 | struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); |
765 | int err; |
766 | |
767 | err = rtl8366rb_reset_chip(smi); |
768 | if (err) |
769 | return err; |
770 | |
771 | err = rtl8366rb_hw_init(smi); |
772 | if (err) |
773 | return err; |
774 | |
775 | return rtl8366_reset_vlan(smi); |
776 | } |
777 | |
778 | static struct switch_attr rtl8366rb_globals[] = { |
779 | { |
780 | .type = SWITCH_TYPE_INT, |
781 | .name = "enable_vlan", |
782 | .description = "Enable VLAN mode", |
783 | .set = rtl8366rb_sw_set_vlan_enable, |
784 | .get = rtl8366rb_sw_get_vlan_enable, |
785 | .max = 1, |
786 | .ofs = 1 |
787 | }, { |
788 | .type = SWITCH_TYPE_INT, |
789 | .name = "enable_vlan4k", |
790 | .description = "Enable VLAN 4K mode", |
791 | .set = rtl8366rb_sw_set_vlan_enable, |
792 | .get = rtl8366rb_sw_get_vlan_enable, |
793 | .max = 1, |
794 | .ofs = 2 |
795 | }, { |
796 | .type = SWITCH_TYPE_NOVAL, |
797 | .name = "reset_mibs", |
798 | .description = "Reset all MIB counters", |
799 | .set = rtl8366rb_sw_reset_mibs, |
800 | }, { |
801 | .type = SWITCH_TYPE_INT, |
802 | .name = "blinkrate", |
803 | .description = "Get/Set LED blinking rate (0 = 43ms, 1 = 84ms," |
804 | " 2 = 120ms, 3 = 170ms, 4 = 340ms, 5 = 670ms)", |
805 | .set = rtl8366rb_sw_set_blinkrate, |
806 | .get = rtl8366rb_sw_get_blinkrate, |
807 | .max = 5 |
808 | }, |
809 | }; |
810 | |
811 | static struct switch_attr rtl8366rb_port[] = { |
812 | { |
813 | .type = SWITCH_TYPE_STRING, |
814 | .name = "link", |
815 | .description = "Get port link information", |
816 | .max = 1, |
817 | .set = NULL, |
818 | .get = rtl8366rb_sw_get_port_link, |
819 | }, { |
820 | .type = SWITCH_TYPE_NOVAL, |
821 | .name = "reset_mib", |
822 | .description = "Reset single port MIB counters", |
823 | .set = rtl8366rb_sw_reset_port_mibs, |
824 | }, { |
825 | .type = SWITCH_TYPE_STRING, |
826 | .name = "mib", |
827 | .description = "Get MIB counters for port", |
828 | .max = 33, |
829 | .set = NULL, |
830 | .get = rtl8366_sw_get_port_mib, |
831 | }, { |
832 | .type = SWITCH_TYPE_INT, |
833 | .name = "led", |
834 | .description = "Get/Set port group (0 - 3) led mode (0 - 15)", |
835 | .max = 15, |
836 | .set = rtl8366rb_sw_set_port_led, |
837 | .get = rtl8366rb_sw_get_port_led, |
838 | }, |
839 | }; |
840 | |
841 | static struct switch_attr rtl8366rb_vlan[] = { |
842 | { |
843 | .type = SWITCH_TYPE_STRING, |
844 | .name = "info", |
845 | .description = "Get vlan information", |
846 | .max = 1, |
847 | .set = NULL, |
848 | .get = rtl8366_sw_get_vlan_info, |
849 | }, |
850 | }; |
851 | |
852 | /* template */ |
853 | static struct switch_dev rtl8366_switch_dev = { |
854 | .name = "RTL8366S", |
855 | .cpu_port = RTL8366RB_PORT_NUM_CPU, |
856 | .ports = RTL8366RB_NUM_PORTS, |
857 | .vlans = RTL8366RB_NUM_VLANS, |
858 | .attr_global = { |
859 | .attr = rtl8366rb_globals, |
860 | .n_attr = ARRAY_SIZE(rtl8366rb_globals), |
861 | }, |
862 | .attr_port = { |
863 | .attr = rtl8366rb_port, |
864 | .n_attr = ARRAY_SIZE(rtl8366rb_port), |
865 | }, |
866 | .attr_vlan = { |
867 | .attr = rtl8366rb_vlan, |
868 | .n_attr = ARRAY_SIZE(rtl8366rb_vlan), |
869 | }, |
870 | |
871 | .get_vlan_ports = rtl8366_sw_get_vlan_ports, |
872 | .set_vlan_ports = rtl8366_sw_set_vlan_ports, |
873 | .get_port_pvid = rtl8366_sw_get_port_pvid, |
874 | .set_port_pvid = rtl8366_sw_set_port_pvid, |
875 | .reset_switch = rtl8366rb_sw_reset_switch, |
876 | }; |
877 | |
878 | static int rtl8366rb_switch_init(struct rtl8366_smi *smi) |
879 | { |
880 | struct switch_dev *dev = &smi->sw_dev; |
881 | int err; |
882 | |
883 | memcpy(dev, &rtl8366_switch_dev, sizeof(struct switch_dev)); |
884 | dev->priv = smi; |
885 | dev->devname = dev_name(smi->parent); |
886 | |
887 | err = register_switch(dev, NULL); |
888 | if (err) |
889 | dev_err(smi->parent, "switch registration failed\n"); |
890 | |
891 | return err; |
892 | } |
893 | |
894 | static void rtl8366rb_switch_cleanup(struct rtl8366_smi *smi) |
895 | { |
896 | unregister_switch(&smi->sw_dev); |
897 | } |
898 | |
899 | static int rtl8366rb_mii_read(struct mii_bus *bus, int addr, int reg) |
900 | { |
901 | struct rtl8366_smi *smi = bus->priv; |
902 | u32 val = 0; |
903 | int err; |
904 | |
905 | err = rtl8366rb_read_phy_reg(smi, addr, 0, reg, &val); |
906 | if (err) |
907 | return 0xffff; |
908 | |
909 | return val; |
910 | } |
911 | |
912 | static int rtl8366rb_mii_write(struct mii_bus *bus, int addr, int reg, u16 val) |
913 | { |
914 | struct rtl8366_smi *smi = bus->priv; |
915 | u32 t; |
916 | int err; |
917 | |
918 | err = rtl8366rb_write_phy_reg(smi, addr, 0, reg, val); |
919 | /* flush write */ |
920 | (void) rtl8366rb_read_phy_reg(smi, addr, 0, reg, &t); |
921 | |
922 | return err; |
923 | } |
924 | |
925 | static int rtl8366rb_mii_bus_match(struct mii_bus *bus) |
926 | { |
927 | return (bus->read == rtl8366rb_mii_read && |
928 | bus->write == rtl8366rb_mii_write); |
929 | } |
930 | |
931 | static int rtl8366rb_setup(struct rtl8366_smi *smi) |
932 | { |
933 | int ret; |
934 | |
935 | ret = rtl8366rb_reset_chip(smi); |
936 | if (ret) |
937 | return ret; |
938 | |
939 | ret = rtl8366rb_hw_init(smi); |
940 | return ret; |
941 | } |
942 | |
943 | static int rtl8366rb_detect(struct rtl8366_smi *smi) |
944 | { |
945 | u32 chip_id = 0; |
946 | u32 chip_ver = 0; |
947 | int ret; |
948 | |
949 | ret = rtl8366_smi_read_reg(smi, RTL8366RB_CHIP_ID_REG, &chip_id); |
950 | if (ret) { |
951 | dev_err(smi->parent, "unable to read chip id\n"); |
952 | return ret; |
953 | } |
954 | |
955 | switch (chip_id) { |
956 | case RTL8366RB_CHIP_ID_8366: |
957 | break; |
958 | default: |
959 | dev_err(smi->parent, "unknown chip id (%04x)\n", chip_id); |
960 | return -ENODEV; |
961 | } |
962 | |
963 | ret = rtl8366_smi_read_reg(smi, RTL8366RB_CHIP_VERSION_CTRL_REG, |
964 | &chip_ver); |
965 | if (ret) { |
966 | dev_err(smi->parent, "unable to read chip version\n"); |
967 | return ret; |
968 | } |
969 | |
970 | dev_info(smi->parent, "RTL%04x ver. %u chip found\n", |
971 | chip_id, chip_ver & RTL8366RB_CHIP_VERSION_MASK); |
972 | |
973 | return 0; |
974 | } |
975 | |
976 | static struct rtl8366_smi_ops rtl8366rb_smi_ops = { |
977 | .detect = rtl8366rb_detect, |
978 | .setup = rtl8366rb_setup, |
979 | |
980 | .mii_read = rtl8366rb_mii_read, |
981 | .mii_write = rtl8366rb_mii_write, |
982 | |
983 | .get_vlan_mc = rtl8366rb_get_vlan_mc, |
984 | .set_vlan_mc = rtl8366rb_set_vlan_mc, |
985 | .get_vlan_4k = rtl8366rb_get_vlan_4k, |
986 | .set_vlan_4k = rtl8366rb_set_vlan_4k, |
987 | .get_mc_index = rtl8366rb_get_mc_index, |
988 | .set_mc_index = rtl8366rb_set_mc_index, |
989 | .get_mib_counter = rtl8366rb_get_mib_counter, |
990 | .is_vlan_valid = rtl8366rb_is_vlan_valid, |
991 | }; |
992 | |
993 | static int __init rtl8366rb_probe(struct platform_device *pdev) |
994 | { |
995 | static int rtl8366_smi_version_printed; |
996 | struct rtl8366rb_platform_data *pdata; |
997 | struct rtl8366_smi *smi; |
998 | int err; |
999 | |
1000 | if (!rtl8366_smi_version_printed++) |
1001 | printk(KERN_NOTICE RTL8366RB_DRIVER_DESC |
1002 | " version " RTL8366RB_DRIVER_VER"\n"); |
1003 | |
1004 | pdata = pdev->dev.platform_data; |
1005 | if (!pdata) { |
1006 | dev_err(&pdev->dev, "no platform data specified\n"); |
1007 | err = -EINVAL; |
1008 | goto err_out; |
1009 | } |
1010 | |
1011 | smi = rtl8366_smi_alloc(&pdev->dev); |
1012 | if (!smi) { |
1013 | err = -ENOMEM; |
1014 | goto err_out; |
1015 | } |
1016 | |
1017 | smi->gpio_sda = pdata->gpio_sda; |
1018 | smi->gpio_sck = pdata->gpio_sck; |
1019 | smi->ops = &rtl8366rb_smi_ops; |
1020 | smi->cpu_port = RTL8366RB_PORT_NUM_CPU; |
1021 | smi->num_ports = RTL8366RB_NUM_PORTS; |
1022 | smi->num_vlan_mc = RTL8366RB_NUM_VLANS; |
1023 | smi->mib_counters = rtl8366rb_mib_counters; |
1024 | smi->num_mib_counters = ARRAY_SIZE(rtl8366rb_mib_counters); |
1025 | |
1026 | err = rtl8366_smi_init(smi); |
1027 | if (err) |
1028 | goto err_free_smi; |
1029 | |
1030 | platform_set_drvdata(pdev, smi); |
1031 | |
1032 | err = rtl8366rb_switch_init(smi); |
1033 | if (err) |
1034 | goto err_clear_drvdata; |
1035 | |
1036 | return 0; |
1037 | |
1038 | err_clear_drvdata: |
1039 | platform_set_drvdata(pdev, NULL); |
1040 | rtl8366_smi_cleanup(smi); |
1041 | err_free_smi: |
1042 | kfree(smi); |
1043 | err_out: |
1044 | return err; |
1045 | } |
1046 | |
1047 | static int rtl8366rb_phy_config_init(struct phy_device *phydev) |
1048 | { |
1049 | if (!rtl8366rb_mii_bus_match(phydev->bus)) |
1050 | return -EINVAL; |
1051 | |
1052 | return 0; |
1053 | } |
1054 | |
1055 | static int rtl8366rb_phy_config_aneg(struct phy_device *phydev) |
1056 | { |
1057 | return 0; |
1058 | } |
1059 | |
1060 | static struct phy_driver rtl8366rb_phy_driver = { |
1061 | .phy_id = 0x001cc960, |
1062 | .name = "Realtek RTL8366RB", |
1063 | .phy_id_mask = 0x1ffffff0, |
1064 | .features = PHY_GBIT_FEATURES, |
1065 | .config_aneg = rtl8366rb_phy_config_aneg, |
1066 | .config_init = rtl8366rb_phy_config_init, |
1067 | .read_status = genphy_read_status, |
1068 | .driver = { |
1069 | .owner = THIS_MODULE, |
1070 | }, |
1071 | }; |
1072 | |
1073 | static int __devexit rtl8366rb_remove(struct platform_device *pdev) |
1074 | { |
1075 | struct rtl8366_smi *smi = platform_get_drvdata(pdev); |
1076 | |
1077 | if (smi) { |
1078 | rtl8366rb_switch_cleanup(smi); |
1079 | platform_set_drvdata(pdev, NULL); |
1080 | rtl8366_smi_cleanup(smi); |
1081 | kfree(smi); |
1082 | } |
1083 | |
1084 | return 0; |
1085 | } |
1086 | |
1087 | static struct platform_driver rtl8366rb_driver = { |
1088 | .driver = { |
1089 | .name = RTL8366RB_DRIVER_NAME, |
1090 | .owner = THIS_MODULE, |
1091 | }, |
1092 | .probe = rtl8366rb_probe, |
1093 | .remove = __devexit_p(rtl8366rb_remove), |
1094 | }; |
1095 | |
1096 | static int __init rtl8366rb_module_init(void) |
1097 | { |
1098 | int ret; |
1099 | ret = platform_driver_register(&rtl8366rb_driver); |
1100 | if (ret) |
1101 | return ret; |
1102 | |
1103 | ret = phy_driver_register(&rtl8366rb_phy_driver); |
1104 | if (ret) |
1105 | goto err_platform_unregister; |
1106 | |
1107 | return 0; |
1108 | |
1109 | err_platform_unregister: |
1110 | platform_driver_unregister(&rtl8366rb_driver); |
1111 | return ret; |
1112 | } |
1113 | module_init(rtl8366rb_module_init); |
1114 | |
1115 | static void __exit rtl8366rb_module_exit(void) |
1116 | { |
1117 | phy_driver_unregister(&rtl8366rb_phy_driver); |
1118 | platform_driver_unregister(&rtl8366rb_driver); |
1119 | } |
1120 | module_exit(rtl8366rb_module_exit); |
1121 | |
1122 | MODULE_DESCRIPTION(RTL8366RB_DRIVER_DESC); |
1123 | MODULE_VERSION(RTL8366RB_DRIVER_VER); |
1124 | MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); |
1125 | MODULE_AUTHOR("Antti Seppälä <a.seppala@gmail.com>"); |
1126 | MODULE_LICENSE("GPL v2"); |
1127 | MODULE_ALIAS("platform:" RTL8366RB_DRIVER_NAME); |
1128 |