Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/net/dsa/mv88e6131.c
15109 views
1
/*
2
* net/dsa/mv88e6131.c - Marvell 88e6095/6095f/6131 switch chip support
3
* Copyright (c) 2008-2009 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
* Switch product IDs
19
*/
20
#define ID_6085 0x04a0
21
#define ID_6095 0x0950
22
#define ID_6131 0x1060
23
24
static char *mv88e6131_probe(struct mii_bus *bus, int sw_addr)
25
{
26
int ret;
27
28
ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03);
29
if (ret >= 0) {
30
ret &= 0xfff0;
31
if (ret == ID_6085)
32
return "Marvell 88E6085";
33
if (ret == ID_6095)
34
return "Marvell 88E6095/88E6095F";
35
if (ret == ID_6131)
36
return "Marvell 88E6131";
37
}
38
39
return NULL;
40
}
41
42
static int mv88e6131_switch_reset(struct dsa_switch *ds)
43
{
44
int i;
45
int ret;
46
47
/*
48
* Set all ports to the disabled state.
49
*/
50
for (i = 0; i < 11; i++) {
51
ret = REG_READ(REG_PORT(i), 0x04);
52
REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
53
}
54
55
/*
56
* Wait for transmit queues to drain.
57
*/
58
msleep(2);
59
60
/*
61
* Reset the switch.
62
*/
63
REG_WRITE(REG_GLOBAL, 0x04, 0xc400);
64
65
/*
66
* Wait up to one second for reset to complete.
67
*/
68
for (i = 0; i < 1000; i++) {
69
ret = REG_READ(REG_GLOBAL, 0x00);
70
if ((ret & 0xc800) == 0xc800)
71
break;
72
73
msleep(1);
74
}
75
if (i == 1000)
76
return -ETIMEDOUT;
77
78
return 0;
79
}
80
81
static int mv88e6131_setup_global(struct dsa_switch *ds)
82
{
83
int ret;
84
int i;
85
86
/*
87
* Enable the PHY polling unit, don't discard packets with
88
* excessive collisions, use a weighted fair queueing scheme
89
* to arbitrate between packet queues, set the maximum frame
90
* size to 1632, and mask all interrupt sources.
91
*/
92
REG_WRITE(REG_GLOBAL, 0x04, 0x4400);
93
94
/*
95
* Set the default address aging time to 5 minutes, and
96
* enable address learn messages to be sent to all message
97
* ports.
98
*/
99
REG_WRITE(REG_GLOBAL, 0x0a, 0x0148);
100
101
/*
102
* Configure the priority mapping registers.
103
*/
104
ret = mv88e6xxx_config_prio(ds);
105
if (ret < 0)
106
return ret;
107
108
/*
109
* Set the VLAN ethertype to 0x8100.
110
*/
111
REG_WRITE(REG_GLOBAL, 0x19, 0x8100);
112
113
/*
114
* Disable ARP mirroring, and configure the upstream port as
115
* the port to which ingress and egress monitor frames are to
116
* be sent.
117
*/
118
REG_WRITE(REG_GLOBAL, 0x1a, (dsa_upstream_port(ds) * 0x1100) | 0x00f0);
119
120
/*
121
* Disable cascade port functionality, and set the switch's
122
* DSA device number.
123
*/
124
REG_WRITE(REG_GLOBAL, 0x1c, 0xe000 | (ds->index & 0x1f));
125
126
/*
127
* Send all frames with destination addresses matching
128
* 01:80:c2:00:00:0x to the CPU port.
129
*/
130
REG_WRITE(REG_GLOBAL2, 0x03, 0xffff);
131
132
/*
133
* Ignore removed tag data on doubly tagged packets, disable
134
* flow control messages, force flow control priority to the
135
* highest, and send all special multicast frames to the CPU
136
* port at the highest priority.
137
*/
138
REG_WRITE(REG_GLOBAL2, 0x05, 0x00ff);
139
140
/*
141
* Program the DSA routing table.
142
*/
143
for (i = 0; i < 32; i++) {
144
int nexthop;
145
146
nexthop = 0x1f;
147
if (i != ds->index && i < ds->dst->pd->nr_chips)
148
nexthop = ds->pd->rtable[i] & 0x1f;
149
150
REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | nexthop);
151
}
152
153
/*
154
* Clear all trunk masks.
155
*/
156
for (i = 0; i < 8; i++)
157
REG_WRITE(REG_GLOBAL2, 0x07, 0x8000 | (i << 12) | 0x7ff);
158
159
/*
160
* Clear all trunk mappings.
161
*/
162
for (i = 0; i < 16; i++)
163
REG_WRITE(REG_GLOBAL2, 0x08, 0x8000 | (i << 11));
164
165
/*
166
* Force the priority of IGMP/MLD snoop frames and ARP frames
167
* to the highest setting.
168
*/
169
REG_WRITE(REG_GLOBAL2, 0x0f, 0x00ff);
170
171
return 0;
172
}
173
174
static int mv88e6131_setup_port(struct dsa_switch *ds, int p)
175
{
176
struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
177
int addr = REG_PORT(p);
178
u16 val;
179
180
/*
181
* MAC Forcing register: don't force link, speed, duplex
182
* or flow control state to any particular values on physical
183
* ports, but force the CPU port and all DSA ports to 1000 Mb/s
184
* (100 Mb/s on 6085) full duplex.
185
*/
186
if (dsa_is_cpu_port(ds, p) || ds->dsa_port_mask & (1 << p))
187
if (ps->id == ID_6085)
188
REG_WRITE(addr, 0x01, 0x003d); /* 100 Mb/s */
189
else
190
REG_WRITE(addr, 0x01, 0x003e); /* 1000 Mb/s */
191
else
192
REG_WRITE(addr, 0x01, 0x0003);
193
194
/*
195
* Port Control: disable Core Tag, disable Drop-on-Lock,
196
* transmit frames unmodified, disable Header mode,
197
* enable IGMP/MLD snoop, disable DoubleTag, disable VLAN
198
* tunneling, determine priority by looking at 802.1p and
199
* IP priority fields (IP prio has precedence), and set STP
200
* state to Forwarding.
201
*
202
* If this is the upstream port for this switch, enable
203
* forwarding of unknown unicasts, and enable DSA tagging
204
* mode.
205
*
206
* If this is the link to another switch, use DSA tagging
207
* mode, but do not enable forwarding of unknown unicasts.
208
*/
209
val = 0x0433;
210
if (p == dsa_upstream_port(ds)) {
211
val |= 0x0104;
212
/*
213
* On 6085, unknown multicast forward is controlled
214
* here rather than in Port Control 2 register.
215
*/
216
if (ps->id == ID_6085)
217
val |= 0x0008;
218
}
219
if (ds->dsa_port_mask & (1 << p))
220
val |= 0x0100;
221
REG_WRITE(addr, 0x04, val);
222
223
/*
224
* Port Control 1: disable trunking. Also, if this is the
225
* CPU port, enable learn messages to be sent to this port.
226
*/
227
REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000);
228
229
/*
230
* Port based VLAN map: give each port its own address
231
* database, allow the CPU port to talk to each of the 'real'
232
* ports, and allow each of the 'real' ports to only talk to
233
* the upstream port.
234
*/
235
val = (p & 0xf) << 12;
236
if (dsa_is_cpu_port(ds, p))
237
val |= ds->phys_port_mask;
238
else
239
val |= 1 << dsa_upstream_port(ds);
240
REG_WRITE(addr, 0x06, val);
241
242
/*
243
* Default VLAN ID and priority: don't set a default VLAN
244
* ID, and set the default packet priority to zero.
245
*/
246
REG_WRITE(addr, 0x07, 0x0000);
247
248
/*
249
* Port Control 2: don't force a good FCS, don't use
250
* VLAN-based, source address-based or destination
251
* address-based priority overrides, don't let the switch
252
* add or strip 802.1q tags, don't discard tagged or
253
* untagged frames on this port, do a destination address
254
* lookup on received packets as usual, don't send a copy
255
* of all transmitted/received frames on this port to the
256
* CPU, and configure the upstream port number.
257
*
258
* If this is the upstream port for this switch, enable
259
* forwarding of unknown multicast addresses.
260
*/
261
if (ps->id == ID_6085)
262
/*
263
* on 6085, bits 3:0 are reserved, bit 6 control ARP
264
* mirroring, and multicast forward is handled in
265
* Port Control register.
266
*/
267
REG_WRITE(addr, 0x08, 0x0080);
268
else {
269
val = 0x0080 | dsa_upstream_port(ds);
270
if (p == dsa_upstream_port(ds))
271
val |= 0x0040;
272
REG_WRITE(addr, 0x08, val);
273
}
274
275
/*
276
* Rate Control: disable ingress rate limiting.
277
*/
278
REG_WRITE(addr, 0x09, 0x0000);
279
280
/*
281
* Rate Control 2: disable egress rate limiting.
282
*/
283
REG_WRITE(addr, 0x0a, 0x0000);
284
285
/*
286
* Port Association Vector: when learning source addresses
287
* of packets, add the address to the address database using
288
* a port bitmap that has only the bit for this port set and
289
* the other bits clear.
290
*/
291
REG_WRITE(addr, 0x0b, 1 << p);
292
293
/*
294
* Tag Remap: use an identity 802.1p prio -> switch prio
295
* mapping.
296
*/
297
REG_WRITE(addr, 0x18, 0x3210);
298
299
/*
300
* Tag Remap 2: use an identity 802.1p prio -> switch prio
301
* mapping.
302
*/
303
REG_WRITE(addr, 0x19, 0x7654);
304
305
return 0;
306
}
307
308
static int mv88e6131_setup(struct dsa_switch *ds)
309
{
310
struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
311
int i;
312
int ret;
313
314
mutex_init(&ps->smi_mutex);
315
mv88e6xxx_ppu_state_init(ds);
316
mutex_init(&ps->stats_mutex);
317
318
ps->id = REG_READ(REG_PORT(0), 0x03) & 0xfff0;
319
320
ret = mv88e6131_switch_reset(ds);
321
if (ret < 0)
322
return ret;
323
324
/* @@@ initialise vtu and atu */
325
326
ret = mv88e6131_setup_global(ds);
327
if (ret < 0)
328
return ret;
329
330
for (i = 0; i < 11; i++) {
331
ret = mv88e6131_setup_port(ds, i);
332
if (ret < 0)
333
return ret;
334
}
335
336
return 0;
337
}
338
339
static int mv88e6131_port_to_phy_addr(int port)
340
{
341
if (port >= 0 && port <= 11)
342
return port;
343
return -1;
344
}
345
346
static int
347
mv88e6131_phy_read(struct dsa_switch *ds, int port, int regnum)
348
{
349
int addr = mv88e6131_port_to_phy_addr(port);
350
return mv88e6xxx_phy_read_ppu(ds, addr, regnum);
351
}
352
353
static int
354
mv88e6131_phy_write(struct dsa_switch *ds,
355
int port, int regnum, u16 val)
356
{
357
int addr = mv88e6131_port_to_phy_addr(port);
358
return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val);
359
}
360
361
static struct mv88e6xxx_hw_stat mv88e6131_hw_stats[] = {
362
{ "in_good_octets", 8, 0x00, },
363
{ "in_bad_octets", 4, 0x02, },
364
{ "in_unicast", 4, 0x04, },
365
{ "in_broadcasts", 4, 0x06, },
366
{ "in_multicasts", 4, 0x07, },
367
{ "in_pause", 4, 0x16, },
368
{ "in_undersize", 4, 0x18, },
369
{ "in_fragments", 4, 0x19, },
370
{ "in_oversize", 4, 0x1a, },
371
{ "in_jabber", 4, 0x1b, },
372
{ "in_rx_error", 4, 0x1c, },
373
{ "in_fcs_error", 4, 0x1d, },
374
{ "out_octets", 8, 0x0e, },
375
{ "out_unicast", 4, 0x10, },
376
{ "out_broadcasts", 4, 0x13, },
377
{ "out_multicasts", 4, 0x12, },
378
{ "out_pause", 4, 0x15, },
379
{ "excessive", 4, 0x11, },
380
{ "collisions", 4, 0x1e, },
381
{ "deferred", 4, 0x05, },
382
{ "single", 4, 0x14, },
383
{ "multiple", 4, 0x17, },
384
{ "out_fcs_error", 4, 0x03, },
385
{ "late", 4, 0x1f, },
386
{ "hist_64bytes", 4, 0x08, },
387
{ "hist_65_127bytes", 4, 0x09, },
388
{ "hist_128_255bytes", 4, 0x0a, },
389
{ "hist_256_511bytes", 4, 0x0b, },
390
{ "hist_512_1023bytes", 4, 0x0c, },
391
{ "hist_1024_max_bytes", 4, 0x0d, },
392
};
393
394
static void
395
mv88e6131_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
396
{
397
mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6131_hw_stats),
398
mv88e6131_hw_stats, port, data);
399
}
400
401
static void
402
mv88e6131_get_ethtool_stats(struct dsa_switch *ds,
403
int port, uint64_t *data)
404
{
405
mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6131_hw_stats),
406
mv88e6131_hw_stats, port, data);
407
}
408
409
static int mv88e6131_get_sset_count(struct dsa_switch *ds)
410
{
411
return ARRAY_SIZE(mv88e6131_hw_stats);
412
}
413
414
static struct dsa_switch_driver mv88e6131_switch_driver = {
415
.tag_protocol = cpu_to_be16(ETH_P_DSA),
416
.priv_size = sizeof(struct mv88e6xxx_priv_state),
417
.probe = mv88e6131_probe,
418
.setup = mv88e6131_setup,
419
.set_addr = mv88e6xxx_set_addr_direct,
420
.phy_read = mv88e6131_phy_read,
421
.phy_write = mv88e6131_phy_write,
422
.poll_link = mv88e6xxx_poll_link,
423
.get_strings = mv88e6131_get_strings,
424
.get_ethtool_stats = mv88e6131_get_ethtool_stats,
425
.get_sset_count = mv88e6131_get_sset_count,
426
};
427
428
static int __init mv88e6131_init(void)
429
{
430
register_switch_driver(&mv88e6131_switch_driver);
431
return 0;
432
}
433
module_init(mv88e6131_init);
434
435
static void __exit mv88e6131_cleanup(void)
436
{
437
unregister_switch_driver(&mv88e6131_switch_driver);
438
}
439
module_exit(mv88e6131_cleanup);
440
441