OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | /* |
2 | * ADM6996 switch driver |
||
3 | * |
||
4 | * swconfig interface based on ar8216.c |
||
5 | * |
||
6 | * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name> |
||
7 | * VLAN support Copyright (c) 2010, 2011 Peter Lebbing <peter@digitalbrains.com> |
||
8 | * Copyright (c) 2013 Hauke Mehrtens <hauke@hauke-m.de> |
||
9 | * Copyright (c) 2014 Matti Laakso <malaakso@elisanet.fi> |
||
10 | * |
||
11 | * This program is free software; you can redistribute it and/or modify it |
||
12 | * under the terms of the GNU General Public License v2 as published by the |
||
13 | * Free Software Foundation |
||
14 | */ |
||
15 | |||
16 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
||
17 | |||
18 | /*#define DEBUG 1*/ |
||
19 | #include <linux/kernel.h> |
||
20 | #include <linux/string.h> |
||
21 | #include <linux/errno.h> |
||
22 | #include <linux/unistd.h> |
||
23 | #include <linux/slab.h> |
||
24 | #include <linux/interrupt.h> |
||
25 | #include <linux/init.h> |
||
26 | #include <linux/delay.h> |
||
27 | #include <linux/gpio.h> |
||
28 | #include <linux/netdevice.h> |
||
29 | #include <linux/etherdevice.h> |
||
30 | #include <linux/skbuff.h> |
||
31 | #include <linux/spinlock.h> |
||
32 | #include <linux/mm.h> |
||
33 | #include <linux/module.h> |
||
34 | #include <linux/mii.h> |
||
35 | #include <linux/platform_device.h> |
||
36 | #include <linux/platform_data/adm6996-gpio.h> |
||
37 | #include <linux/ethtool.h> |
||
38 | #include <linux/phy.h> |
||
39 | #include <linux/switch.h> |
||
40 | |||
41 | #include <asm/io.h> |
||
42 | #include <asm/irq.h> |
||
43 | #include <asm/uaccess.h> |
||
44 | #include "adm6996.h" |
||
45 | |||
46 | MODULE_DESCRIPTION("Infineon ADM6996 Switch"); |
||
47 | MODULE_AUTHOR("Felix Fietkau, Peter Lebbing <peter@digitalbrains.com>"); |
||
48 | MODULE_LICENSE("GPL"); |
||
49 | |||
50 | static const char * const adm6996_model_name[] = |
||
51 | { |
||
52 | NULL, |
||
53 | "ADM6996FC", |
||
54 | "ADM6996M", |
||
55 | "ADM6996L" |
||
56 | }; |
||
57 | |||
58 | struct adm6996_mib_desc { |
||
59 | unsigned int offset; |
||
60 | const char *name; |
||
61 | }; |
||
62 | |||
63 | struct adm6996_priv { |
||
64 | struct switch_dev dev; |
||
65 | void *priv; |
||
66 | |||
67 | u8 eecs; |
||
68 | u8 eesk; |
||
69 | u8 eedi; |
||
70 | |||
71 | enum adm6996_model model; |
||
72 | |||
73 | bool enable_vlan; |
||
74 | bool vlan_enabled; /* Current hardware state */ |
||
75 | |||
76 | #ifdef DEBUG |
||
77 | u16 addr; /* Debugging: register address to operate on */ |
||
78 | #endif |
||
79 | |||
80 | u16 pvid[ADM_NUM_PORTS]; /* Primary VLAN ID */ |
||
81 | u8 tagged_ports; |
||
82 | |||
83 | u16 vlan_id[ADM_NUM_VLANS]; |
||
84 | u8 vlan_table[ADM_NUM_VLANS]; /* bitmap, 1 = port is member */ |
||
85 | u8 vlan_tagged[ADM_NUM_VLANS]; /* bitmap, 1 = tagged member */ |
||
86 | |||
87 | struct mutex mib_lock; |
||
88 | char buf[2048]; |
||
89 | |||
90 | struct mutex reg_mutex; |
||
91 | |||
92 | /* use abstraction for regops, we want to add gpio support in the future */ |
||
93 | u16 (*read)(struct adm6996_priv *priv, enum admreg reg); |
||
94 | void (*write)(struct adm6996_priv *priv, enum admreg reg, u16 val); |
||
95 | }; |
||
96 | |||
97 | #define to_adm(_dev) container_of(_dev, struct adm6996_priv, dev) |
||
98 | #define phy_to_adm(_phy) ((struct adm6996_priv *) (_phy)->priv) |
||
99 | |||
100 | #define MIB_DESC(_o, _n) \ |
||
101 | { \ |
||
102 | .offset = (_o), \ |
||
103 | .name = (_n), \ |
||
104 | } |
||
105 | |||
106 | static const struct adm6996_mib_desc adm6996_mibs[] = { |
||
107 | MIB_DESC(ADM_CL0, "RxPacket"), |
||
108 | MIB_DESC(ADM_CL6, "RxByte"), |
||
109 | MIB_DESC(ADM_CL12, "TxPacket"), |
||
110 | MIB_DESC(ADM_CL18, "TxByte"), |
||
111 | MIB_DESC(ADM_CL24, "Collision"), |
||
112 | MIB_DESC(ADM_CL30, "Error"), |
||
113 | }; |
||
114 | |||
115 | #define ADM6996_MIB_RXB_ID 1 |
||
116 | #define ADM6996_MIB_TXB_ID 3 |
||
117 | |||
118 | static inline u16 |
||
119 | r16(struct adm6996_priv *priv, enum admreg reg) |
||
120 | { |
||
121 | return priv->read(priv, reg); |
||
122 | } |
||
123 | |||
124 | static inline void |
||
125 | w16(struct adm6996_priv *priv, enum admreg reg, u16 val) |
||
126 | { |
||
127 | priv->write(priv, reg, val); |
||
128 | } |
||
129 | |||
130 | /* Minimum timing constants */ |
||
131 | #define EECK_EDGE_TIME 3 /* 3us - max(adm 2.5us, 93c 1us) */ |
||
132 | #define EEDI_SETUP_TIME 1 /* 1us - max(adm 10ns, 93c 400ns) */ |
||
133 | #define EECS_SETUP_TIME 1 /* 1us - max(adm no, 93c 200ns) */ |
||
134 | |||
135 | static void adm6996_gpio_write(struct adm6996_priv *priv, int cs, char *buf, unsigned int bits) |
||
136 | { |
||
137 | int i, len = (bits + 7) / 8; |
||
138 | u8 mask; |
||
139 | |||
140 | gpio_set_value(priv->eecs, cs); |
||
141 | udelay(EECK_EDGE_TIME); |
||
142 | |||
143 | /* Byte assemble from MSB to LSB */ |
||
144 | for (i = 0; i < len; i++) { |
||
145 | /* Bit bang from MSB to LSB */ |
||
146 | for (mask = 0x80; mask && bits > 0; mask >>= 1, bits --) { |
||
147 | /* Clock low */ |
||
148 | gpio_set_value(priv->eesk, 0); |
||
149 | udelay(EECK_EDGE_TIME); |
||
150 | |||
151 | /* Output on rising edge */ |
||
152 | gpio_set_value(priv->eedi, (mask & buf[i])); |
||
153 | udelay(EEDI_SETUP_TIME); |
||
154 | |||
155 | /* Clock high */ |
||
156 | gpio_set_value(priv->eesk, 1); |
||
157 | udelay(EECK_EDGE_TIME); |
||
158 | } |
||
159 | } |
||
160 | |||
161 | /* Clock low */ |
||
162 | gpio_set_value(priv->eesk, 0); |
||
163 | udelay(EECK_EDGE_TIME); |
||
164 | |||
165 | if (cs) |
||
166 | gpio_set_value(priv->eecs, 0); |
||
167 | } |
||
168 | |||
169 | static void adm6996_gpio_read(struct adm6996_priv *priv, int cs, char *buf, unsigned int bits) |
||
170 | { |
||
171 | int i, len = (bits + 7) / 8; |
||
172 | u8 mask; |
||
173 | |||
174 | gpio_set_value(priv->eecs, cs); |
||
175 | udelay(EECK_EDGE_TIME); |
||
176 | |||
177 | /* Byte assemble from MSB to LSB */ |
||
178 | for (i = 0; i < len; i++) { |
||
179 | u8 byte; |
||
180 | |||
181 | /* Bit bang from MSB to LSB */ |
||
182 | for (mask = 0x80, byte = 0; mask && bits > 0; mask >>= 1, bits --) { |
||
183 | u8 gp; |
||
184 | |||
185 | /* Clock low */ |
||
186 | gpio_set_value(priv->eesk, 0); |
||
187 | udelay(EECK_EDGE_TIME); |
||
188 | |||
189 | /* Input on rising edge */ |
||
190 | gp = gpio_get_value(priv->eedi); |
||
191 | if (gp) |
||
192 | byte |= mask; |
||
193 | |||
194 | /* Clock high */ |
||
195 | gpio_set_value(priv->eesk, 1); |
||
196 | udelay(EECK_EDGE_TIME); |
||
197 | } |
||
198 | |||
199 | *buf++ = byte; |
||
200 | } |
||
201 | |||
202 | /* Clock low */ |
||
203 | gpio_set_value(priv->eesk, 0); |
||
204 | udelay(EECK_EDGE_TIME); |
||
205 | |||
206 | if (cs) |
||
207 | gpio_set_value(priv->eecs, 0); |
||
208 | } |
||
209 | |||
210 | /* Advance clock(s) */ |
||
211 | static void adm6996_gpio_adclk(struct adm6996_priv *priv, int clocks) |
||
212 | { |
||
213 | int i; |
||
214 | for (i = 0; i < clocks; i++) { |
||
215 | /* Clock high */ |
||
216 | gpio_set_value(priv->eesk, 1); |
||
217 | udelay(EECK_EDGE_TIME); |
||
218 | |||
219 | /* Clock low */ |
||
220 | gpio_set_value(priv->eesk, 0); |
||
221 | udelay(EECK_EDGE_TIME); |
||
222 | } |
||
223 | } |
||
224 | |||
225 | static u16 |
||
226 | adm6996_read_gpio_reg(struct adm6996_priv *priv, enum admreg reg) |
||
227 | { |
||
228 | /* cmd: 01 10 T DD R RRRRRR */ |
||
229 | u8 bits[6] = { |
||
230 | 0xFF, 0xFF, 0xFF, 0xFF, |
||
231 | (0x06 << 4) | ((0 & 0x01) << 3 | (reg&64)>>6), |
||
232 | ((reg&63)<<2) |
||
233 | }; |
||
234 | |||
235 | u8 rbits[4]; |
||
236 | |||
237 | /* Enable GPIO outputs with all pins to 0 */ |
||
238 | gpio_direction_output(priv->eecs, 0); |
||
239 | gpio_direction_output(priv->eesk, 0); |
||
240 | gpio_direction_output(priv->eedi, 0); |
||
241 | |||
242 | adm6996_gpio_write(priv, 0, bits, 46); |
||
243 | gpio_direction_input(priv->eedi); |
||
244 | adm6996_gpio_adclk(priv, 2); |
||
245 | adm6996_gpio_read(priv, 0, rbits, 32); |
||
246 | |||
247 | /* Extra clock(s) required per datasheet */ |
||
248 | adm6996_gpio_adclk(priv, 2); |
||
249 | |||
250 | /* Disable GPIO outputs */ |
||
251 | gpio_direction_input(priv->eecs); |
||
252 | gpio_direction_input(priv->eesk); |
||
253 | |||
254 | /* EEPROM has 16-bit registers, but pumps out two registers in one request */ |
||
255 | return (reg & 0x01 ? (rbits[0]<<8) | rbits[1] : (rbits[2]<<8) | (rbits[3])); |
||
256 | } |
||
257 | |||
258 | /* Write chip configuration register */ |
||
259 | /* Follow 93c66 timing and chip's min EEPROM timing requirement */ |
||
260 | static void |
||
261 | adm6996_write_gpio_reg(struct adm6996_priv *priv, enum admreg reg, u16 val) |
||
262 | { |
||
263 | /* cmd(27bits): sb(1) + opc(01) + addr(bbbbbbbb) + data(bbbbbbbbbbbbbbbb) */ |
||
264 | u8 bits[4] = { |
||
265 | (0x05 << 5) | (reg >> 3), |
||
266 | (reg << 5) | (u8)(val >> 11), |
||
267 | (u8)(val >> 3), |
||
268 | (u8)(val << 5) |
||
269 | }; |
||
270 | |||
271 | /* Enable GPIO outputs with all pins to 0 */ |
||
272 | gpio_direction_output(priv->eecs, 0); |
||
273 | gpio_direction_output(priv->eesk, 0); |
||
274 | gpio_direction_output(priv->eedi, 0); |
||
275 | |||
276 | /* Write cmd. Total 27 bits */ |
||
277 | adm6996_gpio_write(priv, 1, bits, 27); |
||
278 | |||
279 | /* Extra clock(s) required per datasheet */ |
||
280 | adm6996_gpio_adclk(priv, 2); |
||
281 | |||
282 | /* Disable GPIO outputs */ |
||
283 | gpio_direction_input(priv->eecs); |
||
284 | gpio_direction_input(priv->eesk); |
||
285 | gpio_direction_input(priv->eedi); |
||
286 | } |
||
287 | |||
288 | static u16 |
||
289 | adm6996_read_mii_reg(struct adm6996_priv *priv, enum admreg reg) |
||
290 | { |
||
291 | struct phy_device *phydev = priv->priv; |
||
292 | struct mii_bus *bus = phydev->mdio.bus; |
||
293 | |||
294 | return bus->read(bus, PHYADDR(reg)); |
||
295 | } |
||
296 | |||
297 | static void |
||
298 | adm6996_write_mii_reg(struct adm6996_priv *priv, enum admreg reg, u16 val) |
||
299 | { |
||
300 | struct phy_device *phydev = priv->priv; |
||
301 | struct mii_bus *bus = phydev->mdio.bus; |
||
302 | |||
303 | bus->write(bus, PHYADDR(reg), val); |
||
304 | } |
||
305 | |||
306 | static int |
||
307 | adm6996_set_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr, |
||
308 | struct switch_val *val) |
||
309 | { |
||
310 | struct adm6996_priv *priv = to_adm(dev); |
||
311 | |||
312 | if (val->value.i > 1) |
||
313 | return -EINVAL; |
||
314 | |||
315 | priv->enable_vlan = val->value.i; |
||
316 | |||
317 | return 0; |
||
318 | }; |
||
319 | |||
320 | static int |
||
321 | adm6996_get_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr, |
||
322 | struct switch_val *val) |
||
323 | { |
||
324 | struct adm6996_priv *priv = to_adm(dev); |
||
325 | |||
326 | val->value.i = priv->enable_vlan; |
||
327 | |||
328 | return 0; |
||
329 | }; |
||
330 | |||
331 | #ifdef DEBUG |
||
332 | |||
333 | static int |
||
334 | adm6996_set_addr(struct switch_dev *dev, const struct switch_attr *attr, |
||
335 | struct switch_val *val) |
||
336 | { |
||
337 | struct adm6996_priv *priv = to_adm(dev); |
||
338 | |||
339 | if (val->value.i > 1023) |
||
340 | return -EINVAL; |
||
341 | |||
342 | priv->addr = val->value.i; |
||
343 | |||
344 | return 0; |
||
345 | }; |
||
346 | |||
347 | static int |
||
348 | adm6996_get_addr(struct switch_dev *dev, const struct switch_attr *attr, |
||
349 | struct switch_val *val) |
||
350 | { |
||
351 | struct adm6996_priv *priv = to_adm(dev); |
||
352 | |||
353 | val->value.i = priv->addr; |
||
354 | |||
355 | return 0; |
||
356 | }; |
||
357 | |||
358 | static int |
||
359 | adm6996_set_data(struct switch_dev *dev, const struct switch_attr *attr, |
||
360 | struct switch_val *val) |
||
361 | { |
||
362 | struct adm6996_priv *priv = to_adm(dev); |
||
363 | |||
364 | if (val->value.i > 65535) |
||
365 | return -EINVAL; |
||
366 | |||
367 | w16(priv, priv->addr, val->value.i); |
||
368 | |||
369 | return 0; |
||
370 | }; |
||
371 | |||
372 | static int |
||
373 | adm6996_get_data(struct switch_dev *dev, const struct switch_attr *attr, |
||
374 | struct switch_val *val) |
||
375 | { |
||
376 | struct adm6996_priv *priv = to_adm(dev); |
||
377 | |||
378 | val->value.i = r16(priv, priv->addr); |
||
379 | |||
380 | return 0; |
||
381 | }; |
||
382 | |||
383 | #endif /* def DEBUG */ |
||
384 | |||
385 | static int |
||
386 | adm6996_set_pvid(struct switch_dev *dev, int port, int vlan) |
||
387 | { |
||
388 | struct adm6996_priv *priv = to_adm(dev); |
||
389 | |||
390 | pr_devel("set_pvid port %d vlan %d\n", port, vlan); |
||
391 | |||
392 | if (vlan > ADM_VLAN_MAX_ID) |
||
393 | return -EINVAL; |
||
394 | |||
395 | priv->pvid[port] = vlan; |
||
396 | |||
397 | return 0; |
||
398 | } |
||
399 | |||
400 | static int |
||
401 | adm6996_get_pvid(struct switch_dev *dev, int port, int *vlan) |
||
402 | { |
||
403 | struct adm6996_priv *priv = to_adm(dev); |
||
404 | |||
405 | pr_devel("get_pvid port %d\n", port); |
||
406 | *vlan = priv->pvid[port]; |
||
407 | |||
408 | return 0; |
||
409 | } |
||
410 | |||
411 | static int |
||
412 | adm6996_set_vid(struct switch_dev *dev, const struct switch_attr *attr, |
||
413 | struct switch_val *val) |
||
414 | { |
||
415 | struct adm6996_priv *priv = to_adm(dev); |
||
416 | |||
417 | pr_devel("set_vid port %d vid %d\n", val->port_vlan, val->value.i); |
||
418 | |||
419 | if (val->value.i > ADM_VLAN_MAX_ID) |
||
420 | return -EINVAL; |
||
421 | |||
422 | priv->vlan_id[val->port_vlan] = val->value.i; |
||
423 | |||
424 | return 0; |
||
425 | }; |
||
426 | |||
427 | static int |
||
428 | adm6996_get_vid(struct switch_dev *dev, const struct switch_attr *attr, |
||
429 | struct switch_val *val) |
||
430 | { |
||
431 | struct adm6996_priv *priv = to_adm(dev); |
||
432 | |||
433 | pr_devel("get_vid port %d\n", val->port_vlan); |
||
434 | |||
435 | val->value.i = priv->vlan_id[val->port_vlan]; |
||
436 | |||
437 | return 0; |
||
438 | }; |
||
439 | |||
440 | static int |
||
441 | adm6996_get_ports(struct switch_dev *dev, struct switch_val *val) |
||
442 | { |
||
443 | struct adm6996_priv *priv = to_adm(dev); |
||
444 | u8 ports = priv->vlan_table[val->port_vlan]; |
||
445 | u8 tagged = priv->vlan_tagged[val->port_vlan]; |
||
446 | int i; |
||
447 | |||
448 | pr_devel("get_ports port_vlan %d\n", val->port_vlan); |
||
449 | |||
450 | val->len = 0; |
||
451 | |||
452 | for (i = 0; i < ADM_NUM_PORTS; i++) { |
||
453 | struct switch_port *p; |
||
454 | |||
455 | if (!(ports & (1 << i))) |
||
456 | continue; |
||
457 | |||
458 | p = &val->value.ports[val->len++]; |
||
459 | p->id = i; |
||
460 | if (tagged & (1 << i)) |
||
461 | p->flags = (1 << SWITCH_PORT_FLAG_TAGGED); |
||
462 | else |
||
463 | p->flags = 0; |
||
464 | } |
||
465 | |||
466 | return 0; |
||
467 | }; |
||
468 | |||
469 | static int |
||
470 | adm6996_set_ports(struct switch_dev *dev, struct switch_val *val) |
||
471 | { |
||
472 | struct adm6996_priv *priv = to_adm(dev); |
||
473 | u8 *ports = &priv->vlan_table[val->port_vlan]; |
||
474 | u8 *tagged = &priv->vlan_tagged[val->port_vlan]; |
||
475 | int i; |
||
476 | |||
477 | pr_devel("set_ports port_vlan %d ports", val->port_vlan); |
||
478 | |||
479 | *ports = 0; |
||
480 | *tagged = 0; |
||
481 | |||
482 | for (i = 0; i < val->len; i++) { |
||
483 | struct switch_port *p = &val->value.ports[i]; |
||
484 | |||
485 | #ifdef DEBUG |
||
486 | pr_cont(" %d%s", p->id, |
||
487 | ((p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) ? "T" : |
||
488 | "")); |
||
489 | #endif |
||
490 | |||
491 | if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) { |
||
492 | *tagged |= (1 << p->id); |
||
493 | priv->tagged_ports |= (1 << p->id); |
||
494 | } |
||
495 | |||
496 | *ports |= (1 << p->id); |
||
497 | } |
||
498 | |||
499 | #ifdef DEBUG |
||
500 | pr_cont("\n"); |
||
501 | #endif |
||
502 | |||
503 | return 0; |
||
504 | }; |
||
505 | |||
506 | /* |
||
507 | * Precondition: reg_mutex must be held |
||
508 | */ |
||
509 | static void |
||
510 | adm6996_enable_vlan(struct adm6996_priv *priv) |
||
511 | { |
||
512 | u16 reg; |
||
513 | |||
514 | reg = r16(priv, ADM_OTBE_P2_PVID); |
||
515 | reg &= ~(ADM_OTBE_MASK); |
||
516 | w16(priv, ADM_OTBE_P2_PVID, reg); |
||
517 | reg = r16(priv, ADM_IFNTE); |
||
518 | reg &= ~(ADM_IFNTE_MASK); |
||
519 | w16(priv, ADM_IFNTE, reg); |
||
520 | reg = r16(priv, ADM_VID_CHECK); |
||
521 | reg |= ADM_VID_CHECK_MASK; |
||
522 | w16(priv, ADM_VID_CHECK, reg); |
||
523 | reg = r16(priv, ADM_SYSC0); |
||
524 | reg |= ADM_NTTE; |
||
525 | reg &= ~(ADM_RVID1); |
||
526 | w16(priv, ADM_SYSC0, reg); |
||
527 | reg = r16(priv, ADM_SYSC3); |
||
528 | reg |= ADM_TBV; |
||
529 | w16(priv, ADM_SYSC3, reg); |
||
530 | } |
||
531 | |||
532 | static void |
||
533 | adm6996_enable_vlan_6996l(struct adm6996_priv *priv) |
||
534 | { |
||
535 | u16 reg; |
||
536 | |||
537 | reg = r16(priv, ADM_SYSC3); |
||
538 | reg |= ADM_TBV; |
||
539 | reg |= ADM_MAC_CLONE; |
||
540 | w16(priv, ADM_SYSC3, reg); |
||
541 | } |
||
542 | |||
543 | /* |
||
544 | * Disable VLANs |
||
545 | * |
||
546 | * Sets VLAN mapping for port-based VLAN with all ports connected to |
||
547 | * eachother (this is also the power-on default). |
||
548 | * |
||
549 | * Precondition: reg_mutex must be held |
||
550 | */ |
||
551 | static void |
||
552 | adm6996_disable_vlan(struct adm6996_priv *priv) |
||
553 | { |
||
554 | u16 reg; |
||
555 | int i; |
||
556 | |||
557 | for (i = 0; i < ADM_NUM_VLANS; i++) { |
||
558 | reg = ADM_VLAN_FILT_MEMBER_MASK; |
||
559 | w16(priv, ADM_VLAN_FILT_L(i), reg); |
||
560 | reg = ADM_VLAN_FILT_VALID | ADM_VLAN_FILT_VID(1); |
||
561 | w16(priv, ADM_VLAN_FILT_H(i), reg); |
||
562 | } |
||
563 | |||
564 | reg = r16(priv, ADM_OTBE_P2_PVID); |
||
565 | reg |= ADM_OTBE_MASK; |
||
566 | w16(priv, ADM_OTBE_P2_PVID, reg); |
||
567 | reg = r16(priv, ADM_IFNTE); |
||
568 | reg |= ADM_IFNTE_MASK; |
||
569 | w16(priv, ADM_IFNTE, reg); |
||
570 | reg = r16(priv, ADM_VID_CHECK); |
||
571 | reg &= ~(ADM_VID_CHECK_MASK); |
||
572 | w16(priv, ADM_VID_CHECK, reg); |
||
573 | reg = r16(priv, ADM_SYSC0); |
||
574 | reg &= ~(ADM_NTTE); |
||
575 | reg |= ADM_RVID1; |
||
576 | w16(priv, ADM_SYSC0, reg); |
||
577 | reg = r16(priv, ADM_SYSC3); |
||
578 | reg &= ~(ADM_TBV); |
||
579 | w16(priv, ADM_SYSC3, reg); |
||
580 | } |
||
581 | |||
582 | /* |
||
583 | * Disable VLANs |
||
584 | * |
||
585 | * Sets VLAN mapping for port-based VLAN with all ports connected to |
||
586 | * eachother (this is also the power-on default). |
||
587 | * |
||
588 | * Precondition: reg_mutex must be held |
||
589 | */ |
||
590 | static void |
||
591 | adm6996_disable_vlan_6996l(struct adm6996_priv *priv) |
||
592 | { |
||
593 | u16 reg; |
||
594 | int i; |
||
595 | |||
596 | for (i = 0; i < ADM_NUM_VLANS; i++) { |
||
597 | w16(priv, ADM_VLAN_MAP(i), 0); |
||
598 | } |
||
599 | |||
600 | reg = r16(priv, ADM_SYSC3); |
||
601 | reg &= ~(ADM_TBV); |
||
602 | reg &= ~(ADM_MAC_CLONE); |
||
603 | w16(priv, ADM_SYSC3, reg); |
||
604 | } |
||
605 | |||
606 | /* |
||
607 | * Precondition: reg_mutex must be held |
||
608 | */ |
||
609 | static void |
||
610 | adm6996_apply_port_pvids(struct adm6996_priv *priv) |
||
611 | { |
||
612 | u16 reg; |
||
613 | int i; |
||
614 | |||
615 | for (i = 0; i < ADM_NUM_PORTS; i++) { |
||
616 | reg = r16(priv, adm_portcfg[i]); |
||
617 | reg &= ~(ADM_PORTCFG_PVID_MASK); |
||
618 | reg |= ADM_PORTCFG_PVID(priv->pvid[i]); |
||
619 | if (priv->model == ADM6996L) { |
||
620 | if (priv->tagged_ports & (1 << i)) |
||
621 | reg |= (1 << 4); |
||
622 | else |
||
623 | reg &= ~(1 << 4); |
||
624 | } |
||
625 | w16(priv, adm_portcfg[i], reg); |
||
626 | } |
||
627 | |||
628 | w16(priv, ADM_P0_PVID, ADM_P0_PVID_VAL(priv->pvid[0])); |
||
629 | w16(priv, ADM_P1_PVID, ADM_P1_PVID_VAL(priv->pvid[1])); |
||
630 | reg = r16(priv, ADM_OTBE_P2_PVID); |
||
631 | reg &= ~(ADM_P2_PVID_MASK); |
||
632 | reg |= ADM_P2_PVID_VAL(priv->pvid[2]); |
||
633 | w16(priv, ADM_OTBE_P2_PVID, reg); |
||
634 | reg = ADM_P3_PVID_VAL(priv->pvid[3]); |
||
635 | reg |= ADM_P4_PVID_VAL(priv->pvid[4]); |
||
636 | w16(priv, ADM_P3_P4_PVID, reg); |
||
637 | reg = r16(priv, ADM_P5_PVID); |
||
638 | reg &= ~(ADM_P2_PVID_MASK); |
||
639 | reg |= ADM_P5_PVID_VAL(priv->pvid[5]); |
||
640 | w16(priv, ADM_P5_PVID, reg); |
||
641 | } |
||
642 | |||
643 | /* |
||
644 | * Precondition: reg_mutex must be held |
||
645 | */ |
||
646 | static void |
||
647 | adm6996_apply_vlan_filters(struct adm6996_priv *priv) |
||
648 | { |
||
649 | u8 ports, tagged; |
||
650 | u16 vid, reg; |
||
651 | int i; |
||
652 | |||
653 | for (i = 0; i < ADM_NUM_VLANS; i++) { |
||
654 | vid = priv->vlan_id[i]; |
||
655 | ports = priv->vlan_table[i]; |
||
656 | tagged = priv->vlan_tagged[i]; |
||
657 | |||
658 | if (ports == 0) { |
||
659 | /* Disable VLAN entry */ |
||
660 | w16(priv, ADM_VLAN_FILT_H(i), 0); |
||
661 | w16(priv, ADM_VLAN_FILT_L(i), 0); |
||
662 | continue; |
||
663 | } |
||
664 | |||
665 | reg = ADM_VLAN_FILT_MEMBER(ports); |
||
666 | reg |= ADM_VLAN_FILT_TAGGED(tagged); |
||
667 | w16(priv, ADM_VLAN_FILT_L(i), reg); |
||
668 | reg = ADM_VLAN_FILT_VALID | ADM_VLAN_FILT_VID(vid); |
||
669 | w16(priv, ADM_VLAN_FILT_H(i), reg); |
||
670 | } |
||
671 | } |
||
672 | |||
673 | static void |
||
674 | adm6996_apply_vlan_filters_6996l(struct adm6996_priv *priv) |
||
675 | { |
||
676 | u8 ports; |
||
677 | u16 reg; |
||
678 | int i; |
||
679 | |||
680 | for (i = 0; i < ADM_NUM_VLANS; i++) { |
||
681 | ports = priv->vlan_table[i]; |
||
682 | |||
683 | if (ports == 0) { |
||
684 | /* Disable VLAN entry */ |
||
685 | w16(priv, ADM_VLAN_MAP(i), 0); |
||
686 | continue; |
||
687 | } else { |
||
688 | reg = ADM_VLAN_FILT(ports); |
||
689 | w16(priv, ADM_VLAN_MAP(i), reg); |
||
690 | } |
||
691 | } |
||
692 | } |
||
693 | |||
694 | static int |
||
695 | adm6996_hw_apply(struct switch_dev *dev) |
||
696 | { |
||
697 | struct adm6996_priv *priv = to_adm(dev); |
||
698 | |||
699 | pr_devel("hw_apply\n"); |
||
700 | |||
701 | mutex_lock(&priv->reg_mutex); |
||
702 | |||
703 | if (!priv->enable_vlan) { |
||
704 | if (priv->vlan_enabled) { |
||
705 | if (priv->model == ADM6996L) |
||
706 | adm6996_disable_vlan_6996l(priv); |
||
707 | else |
||
708 | adm6996_disable_vlan(priv); |
||
709 | priv->vlan_enabled = 0; |
||
710 | } |
||
711 | goto out; |
||
712 | } |
||
713 | |||
714 | if (!priv->vlan_enabled) { |
||
715 | if (priv->model == ADM6996L) |
||
716 | adm6996_enable_vlan_6996l(priv); |
||
717 | else |
||
718 | adm6996_enable_vlan(priv); |
||
719 | priv->vlan_enabled = 1; |
||
720 | } |
||
721 | |||
722 | adm6996_apply_port_pvids(priv); |
||
723 | if (priv->model == ADM6996L) |
||
724 | adm6996_apply_vlan_filters_6996l(priv); |
||
725 | else |
||
726 | adm6996_apply_vlan_filters(priv); |
||
727 | |||
728 | out: |
||
729 | mutex_unlock(&priv->reg_mutex); |
||
730 | |||
731 | return 0; |
||
732 | } |
||
733 | |||
734 | /* |
||
735 | * Reset the switch |
||
736 | * |
||
737 | * The ADM6996 can't do a software-initiated reset, so we just initialise the |
||
738 | * registers we support in this driver. |
||
739 | * |
||
740 | * Precondition: reg_mutex must be held |
||
741 | */ |
||
742 | static void |
||
743 | adm6996_perform_reset (struct adm6996_priv *priv) |
||
744 | { |
||
745 | int i; |
||
746 | |||
747 | /* initialize port and vlan settings */ |
||
748 | for (i = 0; i < ADM_NUM_PORTS - 1; i++) { |
||
749 | w16(priv, adm_portcfg[i], ADM_PORTCFG_INIT | |
||
750 | ADM_PORTCFG_PVID(0)); |
||
751 | } |
||
752 | w16(priv, adm_portcfg[5], ADM_PORTCFG_CPU); |
||
753 | |||
754 | if (priv->model == ADM6996M || priv->model == ADM6996FC) { |
||
755 | /* reset all PHY ports */ |
||
756 | for (i = 0; i < ADM_PHY_PORTS; i++) { |
||
757 | w16(priv, ADM_PHY_PORT(i), ADM_PHYCFG_INIT); |
||
758 | } |
||
759 | } |
||
760 | |||
761 | priv->enable_vlan = 0; |
||
762 | priv->vlan_enabled = 0; |
||
763 | |||
764 | for (i = 0; i < ADM_NUM_PORTS; i++) { |
||
765 | priv->pvid[i] = 0; |
||
766 | } |
||
767 | |||
768 | for (i = 0; i < ADM_NUM_VLANS; i++) { |
||
769 | priv->vlan_id[i] = i; |
||
770 | priv->vlan_table[i] = 0; |
||
771 | priv->vlan_tagged[i] = 0; |
||
772 | } |
||
773 | |||
774 | if (priv->model == ADM6996M) { |
||
775 | /* Clear VLAN priority map so prio's are unused */ |
||
776 | w16 (priv, ADM_VLAN_PRIOMAP, 0); |
||
777 | |||
778 | adm6996_disable_vlan(priv); |
||
779 | adm6996_apply_port_pvids(priv); |
||
780 | } else if (priv->model == ADM6996L) { |
||
781 | /* Clear VLAN priority map so prio's are unused */ |
||
782 | w16 (priv, ADM_VLAN_PRIOMAP, 0); |
||
783 | |||
784 | adm6996_disable_vlan_6996l(priv); |
||
785 | adm6996_apply_port_pvids(priv); |
||
786 | } |
||
787 | } |
||
788 | |||
789 | static int |
||
790 | adm6996_reset_switch(struct switch_dev *dev) |
||
791 | { |
||
792 | struct adm6996_priv *priv = to_adm(dev); |
||
793 | |||
794 | pr_devel("reset\n"); |
||
795 | |||
796 | mutex_lock(&priv->reg_mutex); |
||
797 | adm6996_perform_reset (priv); |
||
798 | mutex_unlock(&priv->reg_mutex); |
||
799 | return 0; |
||
800 | } |
||
801 | |||
802 | static int |
||
803 | adm6996_get_port_link(struct switch_dev *dev, int port, |
||
804 | struct switch_port_link *link) |
||
805 | { |
||
806 | struct adm6996_priv *priv = to_adm(dev); |
||
807 | |||
808 | u16 reg = 0; |
||
809 | |||
810 | if (port >= ADM_NUM_PORTS) |
||
811 | return -EINVAL; |
||
812 | |||
813 | switch (port) { |
||
814 | case 0: |
||
815 | reg = r16(priv, ADM_PS0); |
||
816 | break; |
||
817 | case 1: |
||
818 | reg = r16(priv, ADM_PS0); |
||
819 | reg = reg >> 8; |
||
820 | break; |
||
821 | case 2: |
||
822 | reg = r16(priv, ADM_PS1); |
||
823 | break; |
||
824 | case 3: |
||
825 | reg = r16(priv, ADM_PS1); |
||
826 | reg = reg >> 8; |
||
827 | break; |
||
828 | case 4: |
||
829 | reg = r16(priv, ADM_PS1); |
||
830 | reg = reg >> 12; |
||
831 | break; |
||
832 | case 5: |
||
833 | reg = r16(priv, ADM_PS2); |
||
834 | /* Bits 0, 1, 3 and 4. */ |
||
835 | reg = (reg & 3) | ((reg & 24) >> 1); |
||
836 | break; |
||
837 | default: |
||
838 | return -EINVAL; |
||
839 | } |
||
840 | |||
841 | link->link = reg & ADM_PS_LS; |
||
842 | if (!link->link) |
||
843 | return 0; |
||
844 | link->aneg = true; |
||
845 | link->duplex = reg & ADM_PS_DS; |
||
846 | link->tx_flow = reg & ADM_PS_FCS; |
||
847 | link->rx_flow = reg & ADM_PS_FCS; |
||
848 | if (reg & ADM_PS_SS) |
||
849 | link->speed = SWITCH_PORT_SPEED_100; |
||
850 | else |
||
851 | link->speed = SWITCH_PORT_SPEED_10; |
||
852 | |||
853 | return 0; |
||
854 | } |
||
855 | |||
856 | static int |
||
857 | adm6996_sw_get_port_mib(struct switch_dev *dev, |
||
858 | const struct switch_attr *attr, |
||
859 | struct switch_val *val) |
||
860 | { |
||
861 | struct adm6996_priv *priv = to_adm(dev); |
||
862 | int port; |
||
863 | char *buf = priv->buf; |
||
864 | int i, len = 0; |
||
865 | u32 reg = 0; |
||
866 | |||
867 | port = val->port_vlan; |
||
868 | if (port >= ADM_NUM_PORTS) |
||
869 | return -EINVAL; |
||
870 | |||
871 | mutex_lock(&priv->mib_lock); |
||
872 | |||
873 | len += snprintf(buf + len, sizeof(priv->buf) - len, |
||
874 | "Port %d MIB counters\n", |
||
875 | port); |
||
876 | |||
877 | for (i = 0; i < ARRAY_SIZE(adm6996_mibs); i++) { |
||
878 | reg = r16(priv, adm6996_mibs[i].offset + ADM_OFFSET_PORT(port)); |
||
879 | reg += r16(priv, adm6996_mibs[i].offset + ADM_OFFSET_PORT(port) + 1) << 16; |
||
880 | len += snprintf(buf + len, sizeof(priv->buf) - len, |
||
881 | "%-12s: %u\n", |
||
882 | adm6996_mibs[i].name, |
||
883 | reg); |
||
884 | } |
||
885 | |||
886 | mutex_unlock(&priv->mib_lock); |
||
887 | |||
888 | val->value.s = buf; |
||
889 | val->len = len; |
||
890 | |||
891 | return 0; |
||
892 | } |
||
893 | |||
894 | static int |
||
895 | adm6996_get_port_stats(struct switch_dev *dev, int port, |
||
896 | struct switch_port_stats *stats) |
||
897 | { |
||
898 | struct adm6996_priv *priv = to_adm(dev); |
||
899 | int id; |
||
900 | u32 reg = 0; |
||
901 | |||
902 | if (port >= ADM_NUM_PORTS) |
||
903 | return -EINVAL; |
||
904 | |||
905 | mutex_lock(&priv->mib_lock); |
||
906 | |||
907 | id = ADM6996_MIB_TXB_ID; |
||
908 | reg = r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port)); |
||
909 | reg += r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port) + 1) << 16; |
||
910 | stats->tx_bytes = reg; |
||
911 | |||
912 | id = ADM6996_MIB_RXB_ID; |
||
913 | reg = r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port)); |
||
914 | reg += r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port) + 1) << 16; |
||
915 | stats->rx_bytes = reg; |
||
916 | |||
917 | mutex_unlock(&priv->mib_lock); |
||
918 | |||
919 | return 0; |
||
920 | } |
||
921 | |||
922 | static struct switch_attr adm6996_globals[] = { |
||
923 | { |
||
924 | .type = SWITCH_TYPE_INT, |
||
925 | .name = "enable_vlan", |
||
926 | .description = "Enable VLANs", |
||
927 | .set = adm6996_set_enable_vlan, |
||
928 | .get = adm6996_get_enable_vlan, |
||
929 | }, |
||
930 | #ifdef DEBUG |
||
931 | { |
||
932 | .type = SWITCH_TYPE_INT, |
||
933 | .name = "addr", |
||
934 | .description = |
||
935 | "Direct register access: set register address (0 - 1023)", |
||
936 | .set = adm6996_set_addr, |
||
937 | .get = adm6996_get_addr, |
||
938 | }, |
||
939 | { |
||
940 | .type = SWITCH_TYPE_INT, |
||
941 | .name = "data", |
||
942 | .description = |
||
943 | "Direct register access: read/write to register (0 - 65535)", |
||
944 | .set = adm6996_set_data, |
||
945 | .get = adm6996_get_data, |
||
946 | }, |
||
947 | #endif /* def DEBUG */ |
||
948 | }; |
||
949 | |||
950 | static struct switch_attr adm6996_port[] = { |
||
951 | { |
||
952 | .type = SWITCH_TYPE_STRING, |
||
953 | .name = "mib", |
||
954 | .description = "Get port's MIB counters", |
||
955 | .set = NULL, |
||
956 | .get = adm6996_sw_get_port_mib, |
||
957 | }, |
||
958 | }; |
||
959 | |||
960 | static struct switch_attr adm6996_vlan[] = { |
||
961 | { |
||
962 | .type = SWITCH_TYPE_INT, |
||
963 | .name = "vid", |
||
964 | .description = "VLAN ID", |
||
965 | .set = adm6996_set_vid, |
||
966 | .get = adm6996_get_vid, |
||
967 | }, |
||
968 | }; |
||
969 | |||
970 | static struct switch_dev_ops adm6996_ops = { |
||
971 | .attr_global = { |
||
972 | .attr = adm6996_globals, |
||
973 | .n_attr = ARRAY_SIZE(adm6996_globals), |
||
974 | }, |
||
975 | .attr_port = { |
||
976 | .attr = adm6996_port, |
||
977 | .n_attr = ARRAY_SIZE(adm6996_port), |
||
978 | }, |
||
979 | .attr_vlan = { |
||
980 | .attr = adm6996_vlan, |
||
981 | .n_attr = ARRAY_SIZE(adm6996_vlan), |
||
982 | }, |
||
983 | .get_port_pvid = adm6996_get_pvid, |
||
984 | .set_port_pvid = adm6996_set_pvid, |
||
985 | .get_vlan_ports = adm6996_get_ports, |
||
986 | .set_vlan_ports = adm6996_set_ports, |
||
987 | .apply_config = adm6996_hw_apply, |
||
988 | .reset_switch = adm6996_reset_switch, |
||
989 | .get_port_link = adm6996_get_port_link, |
||
990 | .get_port_stats = adm6996_get_port_stats, |
||
991 | }; |
||
992 | |||
993 | static int adm6996_switch_init(struct adm6996_priv *priv, const char *alias, struct net_device *netdev) |
||
994 | { |
||
995 | struct switch_dev *swdev; |
||
996 | u16 test, old; |
||
997 | |||
998 | if (!priv->model) { |
||
999 | /* Detect type of chip */ |
||
1000 | old = r16(priv, ADM_VID_CHECK); |
||
1001 | test = old ^ (1 << 12); |
||
1002 | w16(priv, ADM_VID_CHECK, test); |
||
1003 | test ^= r16(priv, ADM_VID_CHECK); |
||
1004 | if (test & (1 << 12)) { |
||
1005 | /* |
||
1006 | * Bit 12 of this register is read-only. |
||
1007 | * This is the FC model. |
||
1008 | */ |
||
1009 | priv->model = ADM6996FC; |
||
1010 | } else { |
||
1011 | /* Bit 12 is read-write. This is the M model. */ |
||
1012 | priv->model = ADM6996M; |
||
1013 | w16(priv, ADM_VID_CHECK, old); |
||
1014 | } |
||
1015 | } |
||
1016 | |||
1017 | swdev = &priv->dev; |
||
1018 | swdev->name = (adm6996_model_name[priv->model]); |
||
1019 | swdev->cpu_port = ADM_CPU_PORT; |
||
1020 | swdev->ports = ADM_NUM_PORTS; |
||
1021 | swdev->vlans = ADM_NUM_VLANS; |
||
1022 | swdev->ops = &adm6996_ops; |
||
1023 | swdev->alias = alias; |
||
1024 | |||
1025 | /* The ADM6996L connected through GPIOs does not support any switch |
||
1026 | status calls */ |
||
1027 | if (priv->model == ADM6996L) { |
||
1028 | adm6996_ops.attr_port.n_attr = 0; |
||
1029 | adm6996_ops.get_port_link = NULL; |
||
1030 | } |
||
1031 | |||
1032 | pr_info ("%s: %s model PHY found.\n", alias, swdev->name); |
||
1033 | |||
1034 | mutex_lock(&priv->reg_mutex); |
||
1035 | adm6996_perform_reset (priv); |
||
1036 | mutex_unlock(&priv->reg_mutex); |
||
1037 | |||
1038 | if (priv->model == ADM6996M || priv->model == ADM6996L) { |
||
1039 | return register_switch(swdev, netdev); |
||
1040 | } |
||
1041 | |||
1042 | return -ENODEV; |
||
1043 | } |
||
1044 | |||
1045 | static int adm6996_config_init(struct phy_device *pdev) |
||
1046 | { |
||
1047 | struct adm6996_priv *priv; |
||
1048 | int ret; |
||
1049 | |||
1050 | pdev->supported = ADVERTISED_100baseT_Full; |
||
1051 | pdev->advertising = ADVERTISED_100baseT_Full; |
||
1052 | |||
1053 | if (pdev->mdio.addr != 0) { |
||
1054 | pr_info ("%s: PHY overlaps ADM6996, providing fixed PHY 0x%x.\n" |
||
1055 | , pdev->attached_dev->name, pdev->mdio.addr); |
||
1056 | return 0; |
||
1057 | } |
||
1058 | |||
1059 | priv = devm_kzalloc(&pdev->mdio.dev, sizeof(struct adm6996_priv), GFP_KERNEL); |
||
1060 | if (!priv) |
||
1061 | return -ENOMEM; |
||
1062 | |||
1063 | mutex_init(&priv->reg_mutex); |
||
1064 | mutex_init(&priv->mib_lock); |
||
1065 | priv->priv = pdev; |
||
1066 | priv->read = adm6996_read_mii_reg; |
||
1067 | priv->write = adm6996_write_mii_reg; |
||
1068 | |||
1069 | ret = adm6996_switch_init(priv, pdev->attached_dev->name, pdev->attached_dev); |
||
1070 | if (ret < 0) |
||
1071 | return ret; |
||
1072 | |||
1073 | pdev->priv = priv; |
||
1074 | |||
1075 | return 0; |
||
1076 | } |
||
1077 | |||
1078 | /* |
||
1079 | * Warning: phydev->priv is NULL if phydev->mdio.addr != 0 |
||
1080 | */ |
||
1081 | static int adm6996_read_status(struct phy_device *phydev) |
||
1082 | { |
||
1083 | phydev->speed = SPEED_100; |
||
1084 | phydev->duplex = DUPLEX_FULL; |
||
1085 | phydev->link = 1; |
||
1086 | |||
1087 | phydev->state = PHY_RUNNING; |
||
1088 | netif_carrier_on(phydev->attached_dev); |
||
1089 | phydev->adjust_link(phydev->attached_dev); |
||
1090 | |||
1091 | return 0; |
||
1092 | } |
||
1093 | |||
1094 | /* |
||
1095 | * Warning: phydev->priv is NULL if phydev->mdio.addr != 0 |
||
1096 | */ |
||
1097 | static int adm6996_config_aneg(struct phy_device *phydev) |
||
1098 | { |
||
1099 | return 0; |
||
1100 | } |
||
1101 | |||
1102 | static int adm6996_fixup(struct phy_device *dev) |
||
1103 | { |
||
1104 | struct mii_bus *bus = dev->mdio.bus; |
||
1105 | u16 reg; |
||
1106 | |||
1107 | /* Our custom registers are at PHY addresses 0-10. Claim those. */ |
||
1108 | if (dev->mdio.addr > 10) |
||
1109 | return 0; |
||
1110 | |||
1111 | /* look for the switch on the bus */ |
||
1112 | reg = bus->read(bus, PHYADDR(ADM_SIG0)) & ADM_SIG0_MASK; |
||
1113 | if (reg != ADM_SIG0_VAL) |
||
1114 | return 0; |
||
1115 | |||
1116 | reg = bus->read(bus, PHYADDR(ADM_SIG1)) & ADM_SIG1_MASK; |
||
1117 | if (reg != ADM_SIG1_VAL) |
||
1118 | return 0; |
||
1119 | |||
1120 | dev->phy_id = (ADM_SIG0_VAL << 16) | ADM_SIG1_VAL; |
||
1121 | |||
1122 | return 0; |
||
1123 | } |
||
1124 | |||
1125 | static int adm6996_probe(struct phy_device *pdev) |
||
1126 | { |
||
1127 | return 0; |
||
1128 | } |
||
1129 | |||
1130 | static void adm6996_remove(struct phy_device *pdev) |
||
1131 | { |
||
1132 | struct adm6996_priv *priv = phy_to_adm(pdev); |
||
1133 | |||
1134 | if (priv && (priv->model == ADM6996M || priv->model == ADM6996L)) |
||
1135 | unregister_switch(&priv->dev); |
||
1136 | } |
||
1137 | |||
1138 | static int adm6996_soft_reset(struct phy_device *phydev) |
||
1139 | { |
||
1140 | /* we don't need an extra reset */ |
||
1141 | return 0; |
||
1142 | } |
||
1143 | |||
1144 | static struct phy_driver adm6996_phy_driver = { |
||
1145 | .name = "Infineon ADM6996", |
||
1146 | .phy_id = (ADM_SIG0_VAL << 16) | ADM_SIG1_VAL, |
||
1147 | .phy_id_mask = 0xffffffff, |
||
1148 | .features = PHY_BASIC_FEATURES, |
||
1149 | .probe = adm6996_probe, |
||
1150 | .remove = adm6996_remove, |
||
1151 | .config_init = &adm6996_config_init, |
||
1152 | .config_aneg = &adm6996_config_aneg, |
||
1153 | .read_status = &adm6996_read_status, |
||
1154 | .soft_reset = adm6996_soft_reset, |
||
1155 | }; |
||
1156 | |||
1157 | static int adm6996_gpio_probe(struct platform_device *pdev) |
||
1158 | { |
||
1159 | struct adm6996_gpio_platform_data *pdata = pdev->dev.platform_data; |
||
1160 | struct adm6996_priv *priv; |
||
1161 | int ret; |
||
1162 | |||
1163 | if (!pdata) |
||
1164 | return -EINVAL; |
||
1165 | |||
1166 | priv = devm_kzalloc(&pdev->dev, sizeof(struct adm6996_priv), GFP_KERNEL); |
||
1167 | if (!priv) |
||
1168 | return -ENOMEM; |
||
1169 | |||
1170 | mutex_init(&priv->reg_mutex); |
||
1171 | mutex_init(&priv->mib_lock); |
||
1172 | |||
1173 | priv->eecs = pdata->eecs; |
||
1174 | priv->eedi = pdata->eedi; |
||
1175 | priv->eesk = pdata->eesk; |
||
1176 | |||
1177 | priv->model = pdata->model; |
||
1178 | priv->read = adm6996_read_gpio_reg; |
||
1179 | priv->write = adm6996_write_gpio_reg; |
||
1180 | |||
1181 | ret = devm_gpio_request(&pdev->dev, priv->eecs, "adm_eecs"); |
||
1182 | if (ret) |
||
1183 | return ret; |
||
1184 | ret = devm_gpio_request(&pdev->dev, priv->eedi, "adm_eedi"); |
||
1185 | if (ret) |
||
1186 | return ret; |
||
1187 | ret = devm_gpio_request(&pdev->dev, priv->eesk, "adm_eesk"); |
||
1188 | if (ret) |
||
1189 | return ret; |
||
1190 | |||
1191 | ret = adm6996_switch_init(priv, dev_name(&pdev->dev), NULL); |
||
1192 | if (ret < 0) |
||
1193 | return ret; |
||
1194 | |||
1195 | platform_set_drvdata(pdev, priv); |
||
1196 | |||
1197 | return 0; |
||
1198 | } |
||
1199 | |||
1200 | static int adm6996_gpio_remove(struct platform_device *pdev) |
||
1201 | { |
||
1202 | struct adm6996_priv *priv = platform_get_drvdata(pdev); |
||
1203 | |||
1204 | if (priv && (priv->model == ADM6996M || priv->model == ADM6996L)) |
||
1205 | unregister_switch(&priv->dev); |
||
1206 | |||
1207 | return 0; |
||
1208 | } |
||
1209 | |||
1210 | static struct platform_driver adm6996_gpio_driver = { |
||
1211 | .probe = adm6996_gpio_probe, |
||
1212 | .remove = adm6996_gpio_remove, |
||
1213 | .driver = { |
||
1214 | .name = "adm6996_gpio", |
||
1215 | }, |
||
1216 | }; |
||
1217 | |||
1218 | static int __init adm6996_init(void) |
||
1219 | { |
||
1220 | int err; |
||
1221 | |||
1222 | phy_register_fixup_for_id(PHY_ANY_ID, adm6996_fixup); |
||
1223 | err = phy_driver_register(&adm6996_phy_driver, THIS_MODULE); |
||
1224 | if (err) |
||
1225 | return err; |
||
1226 | |||
1227 | err = platform_driver_register(&adm6996_gpio_driver); |
||
1228 | if (err) |
||
1229 | phy_driver_unregister(&adm6996_phy_driver); |
||
1230 | |||
1231 | return err; |
||
1232 | } |
||
1233 | |||
1234 | static void __exit adm6996_exit(void) |
||
1235 | { |
||
1236 | platform_driver_unregister(&adm6996_gpio_driver); |
||
1237 | phy_driver_unregister(&adm6996_phy_driver); |
||
1238 | } |
||
1239 | |||
1240 | module_init(adm6996_init); |
||
1241 | module_exit(adm6996_exit); |