diff options
Diffstat (limited to 'net/dsa/mv88e6xxx.c')
-rw-r--r-- | net/dsa/mv88e6xxx.c | 522 |
1 files changed, 0 insertions, 522 deletions
diff --git a/net/dsa/mv88e6xxx.c b/net/dsa/mv88e6xxx.c deleted file mode 100644 index efe661a9def4..000000000000 --- a/net/dsa/mv88e6xxx.c +++ /dev/null | |||
@@ -1,522 +0,0 @@ | |||
1 | /* | ||
2 | * net/dsa/mv88e6xxx.c - Marvell 88e6xxx switch chip support | ||
3 | * Copyright (c) 2008 Marvell Semiconductor | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <linux/list.h> | ||
12 | #include <linux/netdevice.h> | ||
13 | #include <linux/phy.h> | ||
14 | #include "dsa_priv.h" | ||
15 | #include "mv88e6xxx.h" | ||
16 | |||
17 | /* | ||
18 | * If the switch's ADDR[4:0] strap pins are strapped to zero, it will | ||
19 | * use all 32 SMI bus addresses on its SMI bus, and all switch registers | ||
20 | * will be directly accessible on some {device address,register address} | ||
21 | * pair. If the ADDR[4:0] pins are not strapped to zero, the switch | ||
22 | * will only respond to SMI transactions to that specific address, and | ||
23 | * an indirect addressing mechanism needs to be used to access its | ||
24 | * registers. | ||
25 | */ | ||
26 | static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr) | ||
27 | { | ||
28 | int ret; | ||
29 | int i; | ||
30 | |||
31 | for (i = 0; i < 16; i++) { | ||
32 | ret = mdiobus_read(bus, sw_addr, 0); | ||
33 | if (ret < 0) | ||
34 | return ret; | ||
35 | |||
36 | if ((ret & 0x8000) == 0) | ||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | return -ETIMEDOUT; | ||
41 | } | ||
42 | |||
43 | int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg) | ||
44 | { | ||
45 | int ret; | ||
46 | |||
47 | if (sw_addr == 0) | ||
48 | return mdiobus_read(bus, addr, reg); | ||
49 | |||
50 | /* | ||
51 | * Wait for the bus to become free. | ||
52 | */ | ||
53 | ret = mv88e6xxx_reg_wait_ready(bus, sw_addr); | ||
54 | if (ret < 0) | ||
55 | return ret; | ||
56 | |||
57 | /* | ||
58 | * Transmit the read command. | ||
59 | */ | ||
60 | ret = mdiobus_write(bus, sw_addr, 0, 0x9800 | (addr << 5) | reg); | ||
61 | if (ret < 0) | ||
62 | return ret; | ||
63 | |||
64 | /* | ||
65 | * Wait for the read command to complete. | ||
66 | */ | ||
67 | ret = mv88e6xxx_reg_wait_ready(bus, sw_addr); | ||
68 | if (ret < 0) | ||
69 | return ret; | ||
70 | |||
71 | /* | ||
72 | * Read the data. | ||
73 | */ | ||
74 | ret = mdiobus_read(bus, sw_addr, 1); | ||
75 | if (ret < 0) | ||
76 | return ret; | ||
77 | |||
78 | return ret & 0xffff; | ||
79 | } | ||
80 | |||
81 | int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg) | ||
82 | { | ||
83 | struct mv88e6xxx_priv_state *ps = (void *)(ds + 1); | ||
84 | int ret; | ||
85 | |||
86 | mutex_lock(&ps->smi_mutex); | ||
87 | ret = __mv88e6xxx_reg_read(ds->master_mii_bus, | ||
88 | ds->pd->sw_addr, addr, reg); | ||
89 | mutex_unlock(&ps->smi_mutex); | ||
90 | |||
91 | return ret; | ||
92 | } | ||
93 | |||
94 | int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr, | ||
95 | int reg, u16 val) | ||
96 | { | ||
97 | int ret; | ||
98 | |||
99 | if (sw_addr == 0) | ||
100 | return mdiobus_write(bus, addr, reg, val); | ||
101 | |||
102 | /* | ||
103 | * Wait for the bus to become free. | ||
104 | */ | ||
105 | ret = mv88e6xxx_reg_wait_ready(bus, sw_addr); | ||
106 | if (ret < 0) | ||
107 | return ret; | ||
108 | |||
109 | /* | ||
110 | * Transmit the data to write. | ||
111 | */ | ||
112 | ret = mdiobus_write(bus, sw_addr, 1, val); | ||
113 | if (ret < 0) | ||
114 | return ret; | ||
115 | |||
116 | /* | ||
117 | * Transmit the write command. | ||
118 | */ | ||
119 | ret = mdiobus_write(bus, sw_addr, 0, 0x9400 | (addr << 5) | reg); | ||
120 | if (ret < 0) | ||
121 | return ret; | ||
122 | |||
123 | /* | ||
124 | * Wait for the write command to complete. | ||
125 | */ | ||
126 | ret = mv88e6xxx_reg_wait_ready(bus, sw_addr); | ||
127 | if (ret < 0) | ||
128 | return ret; | ||
129 | |||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val) | ||
134 | { | ||
135 | struct mv88e6xxx_priv_state *ps = (void *)(ds + 1); | ||
136 | int ret; | ||
137 | |||
138 | mutex_lock(&ps->smi_mutex); | ||
139 | ret = __mv88e6xxx_reg_write(ds->master_mii_bus, | ||
140 | ds->pd->sw_addr, addr, reg, val); | ||
141 | mutex_unlock(&ps->smi_mutex); | ||
142 | |||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | int mv88e6xxx_config_prio(struct dsa_switch *ds) | ||
147 | { | ||
148 | /* | ||
149 | * Configure the IP ToS mapping registers. | ||
150 | */ | ||
151 | REG_WRITE(REG_GLOBAL, 0x10, 0x0000); | ||
152 | REG_WRITE(REG_GLOBAL, 0x11, 0x0000); | ||
153 | REG_WRITE(REG_GLOBAL, 0x12, 0x5555); | ||
154 | REG_WRITE(REG_GLOBAL, 0x13, 0x5555); | ||
155 | REG_WRITE(REG_GLOBAL, 0x14, 0xaaaa); | ||
156 | REG_WRITE(REG_GLOBAL, 0x15, 0xaaaa); | ||
157 | REG_WRITE(REG_GLOBAL, 0x16, 0xffff); | ||
158 | REG_WRITE(REG_GLOBAL, 0x17, 0xffff); | ||
159 | |||
160 | /* | ||
161 | * Configure the IEEE 802.1p priority mapping register. | ||
162 | */ | ||
163 | REG_WRITE(REG_GLOBAL, 0x18, 0xfa41); | ||
164 | |||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr) | ||
169 | { | ||
170 | REG_WRITE(REG_GLOBAL, 0x01, (addr[0] << 8) | addr[1]); | ||
171 | REG_WRITE(REG_GLOBAL, 0x02, (addr[2] << 8) | addr[3]); | ||
172 | REG_WRITE(REG_GLOBAL, 0x03, (addr[4] << 8) | addr[5]); | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) | ||
178 | { | ||
179 | int i; | ||
180 | int ret; | ||
181 | |||
182 | for (i = 0; i < 6; i++) { | ||
183 | int j; | ||
184 | |||
185 | /* | ||
186 | * Write the MAC address byte. | ||
187 | */ | ||
188 | REG_WRITE(REG_GLOBAL2, 0x0d, 0x8000 | (i << 8) | addr[i]); | ||
189 | |||
190 | /* | ||
191 | * Wait for the write to complete. | ||
192 | */ | ||
193 | for (j = 0; j < 16; j++) { | ||
194 | ret = REG_READ(REG_GLOBAL2, 0x0d); | ||
195 | if ((ret & 0x8000) == 0) | ||
196 | break; | ||
197 | } | ||
198 | if (j == 16) | ||
199 | return -ETIMEDOUT; | ||
200 | } | ||
201 | |||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | int mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum) | ||
206 | { | ||
207 | if (addr >= 0) | ||
208 | return mv88e6xxx_reg_read(ds, addr, regnum); | ||
209 | return 0xffff; | ||
210 | } | ||
211 | |||
212 | int mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, u16 val) | ||
213 | { | ||
214 | if (addr >= 0) | ||
215 | return mv88e6xxx_reg_write(ds, addr, regnum, val); | ||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | #ifdef CONFIG_NET_DSA_MV88E6XXX_NEED_PPU | ||
220 | static int mv88e6xxx_ppu_disable(struct dsa_switch *ds) | ||
221 | { | ||
222 | int ret; | ||
223 | int i; | ||
224 | |||
225 | ret = REG_READ(REG_GLOBAL, 0x04); | ||
226 | REG_WRITE(REG_GLOBAL, 0x04, ret & ~0x4000); | ||
227 | |||
228 | for (i = 0; i < 1000; i++) { | ||
229 | ret = REG_READ(REG_GLOBAL, 0x00); | ||
230 | msleep(1); | ||
231 | if ((ret & 0xc000) != 0xc000) | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | return -ETIMEDOUT; | ||
236 | } | ||
237 | |||
238 | static int mv88e6xxx_ppu_enable(struct dsa_switch *ds) | ||
239 | { | ||
240 | int ret; | ||
241 | int i; | ||
242 | |||
243 | ret = REG_READ(REG_GLOBAL, 0x04); | ||
244 | REG_WRITE(REG_GLOBAL, 0x04, ret | 0x4000); | ||
245 | |||
246 | for (i = 0; i < 1000; i++) { | ||
247 | ret = REG_READ(REG_GLOBAL, 0x00); | ||
248 | msleep(1); | ||
249 | if ((ret & 0xc000) == 0xc000) | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | return -ETIMEDOUT; | ||
254 | } | ||
255 | |||
256 | static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly) | ||
257 | { | ||
258 | struct mv88e6xxx_priv_state *ps; | ||
259 | |||
260 | ps = container_of(ugly, struct mv88e6xxx_priv_state, ppu_work); | ||
261 | if (mutex_trylock(&ps->ppu_mutex)) { | ||
262 | struct dsa_switch *ds = ((struct dsa_switch *)ps) - 1; | ||
263 | |||
264 | if (mv88e6xxx_ppu_enable(ds) == 0) | ||
265 | ps->ppu_disabled = 0; | ||
266 | mutex_unlock(&ps->ppu_mutex); | ||
267 | } | ||
268 | } | ||
269 | |||
270 | static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps) | ||
271 | { | ||
272 | struct mv88e6xxx_priv_state *ps = (void *)_ps; | ||
273 | |||
274 | schedule_work(&ps->ppu_work); | ||
275 | } | ||
276 | |||
277 | static int mv88e6xxx_ppu_access_get(struct dsa_switch *ds) | ||
278 | { | ||
279 | struct mv88e6xxx_priv_state *ps = (void *)(ds + 1); | ||
280 | int ret; | ||
281 | |||
282 | mutex_lock(&ps->ppu_mutex); | ||
283 | |||
284 | /* | ||
285 | * If the PHY polling unit is enabled, disable it so that | ||
286 | * we can access the PHY registers. If it was already | ||
287 | * disabled, cancel the timer that is going to re-enable | ||
288 | * it. | ||
289 | */ | ||
290 | if (!ps->ppu_disabled) { | ||
291 | ret = mv88e6xxx_ppu_disable(ds); | ||
292 | if (ret < 0) { | ||
293 | mutex_unlock(&ps->ppu_mutex); | ||
294 | return ret; | ||
295 | } | ||
296 | ps->ppu_disabled = 1; | ||
297 | } else { | ||
298 | del_timer(&ps->ppu_timer); | ||
299 | ret = 0; | ||
300 | } | ||
301 | |||
302 | return ret; | ||
303 | } | ||
304 | |||
305 | static void mv88e6xxx_ppu_access_put(struct dsa_switch *ds) | ||
306 | { | ||
307 | struct mv88e6xxx_priv_state *ps = (void *)(ds + 1); | ||
308 | |||
309 | /* | ||
310 | * Schedule a timer to re-enable the PHY polling unit. | ||
311 | */ | ||
312 | mod_timer(&ps->ppu_timer, jiffies + msecs_to_jiffies(10)); | ||
313 | mutex_unlock(&ps->ppu_mutex); | ||
314 | } | ||
315 | |||
316 | void mv88e6xxx_ppu_state_init(struct dsa_switch *ds) | ||
317 | { | ||
318 | struct mv88e6xxx_priv_state *ps = (void *)(ds + 1); | ||
319 | |||
320 | mutex_init(&ps->ppu_mutex); | ||
321 | INIT_WORK(&ps->ppu_work, mv88e6xxx_ppu_reenable_work); | ||
322 | init_timer(&ps->ppu_timer); | ||
323 | ps->ppu_timer.data = (unsigned long)ps; | ||
324 | ps->ppu_timer.function = mv88e6xxx_ppu_reenable_timer; | ||
325 | } | ||
326 | |||
327 | int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum) | ||
328 | { | ||
329 | int ret; | ||
330 | |||
331 | ret = mv88e6xxx_ppu_access_get(ds); | ||
332 | if (ret >= 0) { | ||
333 | ret = mv88e6xxx_reg_read(ds, addr, regnum); | ||
334 | mv88e6xxx_ppu_access_put(ds); | ||
335 | } | ||
336 | |||
337 | return ret; | ||
338 | } | ||
339 | |||
340 | int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr, | ||
341 | int regnum, u16 val) | ||
342 | { | ||
343 | int ret; | ||
344 | |||
345 | ret = mv88e6xxx_ppu_access_get(ds); | ||
346 | if (ret >= 0) { | ||
347 | ret = mv88e6xxx_reg_write(ds, addr, regnum, val); | ||
348 | mv88e6xxx_ppu_access_put(ds); | ||
349 | } | ||
350 | |||
351 | return ret; | ||
352 | } | ||
353 | #endif | ||
354 | |||
355 | void mv88e6xxx_poll_link(struct dsa_switch *ds) | ||
356 | { | ||
357 | int i; | ||
358 | |||
359 | for (i = 0; i < DSA_MAX_PORTS; i++) { | ||
360 | struct net_device *dev; | ||
361 | int uninitialized_var(port_status); | ||
362 | int link; | ||
363 | int speed; | ||
364 | int duplex; | ||
365 | int fc; | ||
366 | |||
367 | dev = ds->ports[i]; | ||
368 | if (dev == NULL) | ||
369 | continue; | ||
370 | |||
371 | link = 0; | ||
372 | if (dev->flags & IFF_UP) { | ||
373 | port_status = mv88e6xxx_reg_read(ds, REG_PORT(i), 0x00); | ||
374 | if (port_status < 0) | ||
375 | continue; | ||
376 | |||
377 | link = !!(port_status & 0x0800); | ||
378 | } | ||
379 | |||
380 | if (!link) { | ||
381 | if (netif_carrier_ok(dev)) { | ||
382 | printk(KERN_INFO "%s: link down\n", dev->name); | ||
383 | netif_carrier_off(dev); | ||
384 | } | ||
385 | continue; | ||
386 | } | ||
387 | |||
388 | switch (port_status & 0x0300) { | ||
389 | case 0x0000: | ||
390 | speed = 10; | ||
391 | break; | ||
392 | case 0x0100: | ||
393 | speed = 100; | ||
394 | break; | ||
395 | case 0x0200: | ||
396 | speed = 1000; | ||
397 | break; | ||
398 | default: | ||
399 | speed = -1; | ||
400 | break; | ||
401 | } | ||
402 | duplex = (port_status & 0x0400) ? 1 : 0; | ||
403 | fc = (port_status & 0x8000) ? 1 : 0; | ||
404 | |||
405 | if (!netif_carrier_ok(dev)) { | ||
406 | printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, " | ||
407 | "flow control %sabled\n", dev->name, | ||
408 | speed, duplex ? "full" : "half", | ||
409 | fc ? "en" : "dis"); | ||
410 | netif_carrier_on(dev); | ||
411 | } | ||
412 | } | ||
413 | } | ||
414 | |||
415 | static int mv88e6xxx_stats_wait(struct dsa_switch *ds) | ||
416 | { | ||
417 | int ret; | ||
418 | int i; | ||
419 | |||
420 | for (i = 0; i < 10; i++) { | ||
421 | ret = REG_READ(REG_GLOBAL, 0x1d); | ||
422 | if ((ret & 0x8000) == 0) | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | return -ETIMEDOUT; | ||
427 | } | ||
428 | |||
429 | static int mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port) | ||
430 | { | ||
431 | int ret; | ||
432 | |||
433 | /* | ||
434 | * Snapshot the hardware statistics counters for this port. | ||
435 | */ | ||
436 | REG_WRITE(REG_GLOBAL, 0x1d, 0xdc00 | port); | ||
437 | |||
438 | /* | ||
439 | * Wait for the snapshotting to complete. | ||
440 | */ | ||
441 | ret = mv88e6xxx_stats_wait(ds); | ||
442 | if (ret < 0) | ||
443 | return ret; | ||
444 | |||
445 | return 0; | ||
446 | } | ||
447 | |||
448 | static void mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val) | ||
449 | { | ||
450 | u32 _val; | ||
451 | int ret; | ||
452 | |||
453 | *val = 0; | ||
454 | |||
455 | ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x1d, 0xcc00 | stat); | ||
456 | if (ret < 0) | ||
457 | return; | ||
458 | |||
459 | ret = mv88e6xxx_stats_wait(ds); | ||
460 | if (ret < 0) | ||
461 | return; | ||
462 | |||
463 | ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x1e); | ||
464 | if (ret < 0) | ||
465 | return; | ||
466 | |||
467 | _val = ret << 16; | ||
468 | |||
469 | ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x1f); | ||
470 | if (ret < 0) | ||
471 | return; | ||
472 | |||
473 | *val = _val | ret; | ||
474 | } | ||
475 | |||
476 | void mv88e6xxx_get_strings(struct dsa_switch *ds, | ||
477 | int nr_stats, struct mv88e6xxx_hw_stat *stats, | ||
478 | int port, uint8_t *data) | ||
479 | { | ||
480 | int i; | ||
481 | |||
482 | for (i = 0; i < nr_stats; i++) { | ||
483 | memcpy(data + i * ETH_GSTRING_LEN, | ||
484 | stats[i].string, ETH_GSTRING_LEN); | ||
485 | } | ||
486 | } | ||
487 | |||
488 | void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, | ||
489 | int nr_stats, struct mv88e6xxx_hw_stat *stats, | ||
490 | int port, uint64_t *data) | ||
491 | { | ||
492 | struct mv88e6xxx_priv_state *ps = (void *)(ds + 1); | ||
493 | int ret; | ||
494 | int i; | ||
495 | |||
496 | mutex_lock(&ps->stats_mutex); | ||
497 | |||
498 | ret = mv88e6xxx_stats_snapshot(ds, port); | ||
499 | if (ret < 0) { | ||
500 | mutex_unlock(&ps->stats_mutex); | ||
501 | return; | ||
502 | } | ||
503 | |||
504 | /* | ||
505 | * Read each of the counters. | ||
506 | */ | ||
507 | for (i = 0; i < nr_stats; i++) { | ||
508 | struct mv88e6xxx_hw_stat *s = stats + i; | ||
509 | u32 low; | ||
510 | u32 high; | ||
511 | |||
512 | mv88e6xxx_stats_read(ds, s->reg, &low); | ||
513 | if (s->sizeof_stat == 8) | ||
514 | mv88e6xxx_stats_read(ds, s->reg + 1, &high); | ||
515 | else | ||
516 | high = 0; | ||
517 | |||
518 | data[i] = (((u64)high) << 32) | low; | ||
519 | } | ||
520 | |||
521 | mutex_unlock(&ps->stats_mutex); | ||
522 | } | ||