Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/net/dsa/mv88e6060.c
15109 views
1
/*
2
* net/dsa/mv88e6060.c - Driver for Marvell 88e6060 switch chips
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
16
#define REG_PORT(p) (8 + (p))
17
#define REG_GLOBAL 0x0f
18
19
static int reg_read(struct dsa_switch *ds, int addr, int reg)
20
{
21
return mdiobus_read(ds->master_mii_bus, ds->pd->sw_addr + addr, reg);
22
}
23
24
#define REG_READ(addr, reg) \
25
({ \
26
int __ret; \
27
\
28
__ret = reg_read(ds, addr, reg); \
29
if (__ret < 0) \
30
return __ret; \
31
__ret; \
32
})
33
34
35
static int reg_write(struct dsa_switch *ds, int addr, int reg, u16 val)
36
{
37
return mdiobus_write(ds->master_mii_bus, ds->pd->sw_addr + addr,
38
reg, val);
39
}
40
41
#define REG_WRITE(addr, reg, val) \
42
({ \
43
int __ret; \
44
\
45
__ret = reg_write(ds, addr, reg, val); \
46
if (__ret < 0) \
47
return __ret; \
48
})
49
50
static char *mv88e6060_probe(struct mii_bus *bus, int sw_addr)
51
{
52
int ret;
53
54
ret = mdiobus_read(bus, sw_addr + REG_PORT(0), 0x03);
55
if (ret >= 0) {
56
ret &= 0xfff0;
57
if (ret == 0x0600)
58
return "Marvell 88E6060";
59
}
60
61
return NULL;
62
}
63
64
static int mv88e6060_switch_reset(struct dsa_switch *ds)
65
{
66
int i;
67
int ret;
68
69
/*
70
* Set all ports to the disabled state.
71
*/
72
for (i = 0; i < 6; i++) {
73
ret = REG_READ(REG_PORT(i), 0x04);
74
REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
75
}
76
77
/*
78
* Wait for transmit queues to drain.
79
*/
80
msleep(2);
81
82
/*
83
* Reset the switch.
84
*/
85
REG_WRITE(REG_GLOBAL, 0x0a, 0xa130);
86
87
/*
88
* Wait up to one second for reset to complete.
89
*/
90
for (i = 0; i < 1000; i++) {
91
ret = REG_READ(REG_GLOBAL, 0x00);
92
if ((ret & 0x8000) == 0x0000)
93
break;
94
95
msleep(1);
96
}
97
if (i == 1000)
98
return -ETIMEDOUT;
99
100
return 0;
101
}
102
103
static int mv88e6060_setup_global(struct dsa_switch *ds)
104
{
105
/*
106
* Disable discarding of frames with excessive collisions,
107
* set the maximum frame size to 1536 bytes, and mask all
108
* interrupt sources.
109
*/
110
REG_WRITE(REG_GLOBAL, 0x04, 0x0800);
111
112
/*
113
* Enable automatic address learning, set the address
114
* database size to 1024 entries, and set the default aging
115
* time to 5 minutes.
116
*/
117
REG_WRITE(REG_GLOBAL, 0x0a, 0x2130);
118
119
return 0;
120
}
121
122
static int mv88e6060_setup_port(struct dsa_switch *ds, int p)
123
{
124
int addr = REG_PORT(p);
125
126
/*
127
* Do not force flow control, disable Ingress and Egress
128
* Header tagging, disable VLAN tunneling, and set the port
129
* state to Forwarding. Additionally, if this is the CPU
130
* port, enable Ingress and Egress Trailer tagging mode.
131
*/
132
REG_WRITE(addr, 0x04, dsa_is_cpu_port(ds, p) ? 0x4103 : 0x0003);
133
134
/*
135
* Port based VLAN map: give each port its own address
136
* database, allow the CPU port to talk to each of the 'real'
137
* ports, and allow each of the 'real' ports to only talk to
138
* the CPU port.
139
*/
140
REG_WRITE(addr, 0x06,
141
((p & 0xf) << 12) |
142
(dsa_is_cpu_port(ds, p) ?
143
ds->phys_port_mask :
144
(1 << ds->dst->cpu_port)));
145
146
/*
147
* Port Association Vector: when learning source addresses
148
* of packets, add the address to the address database using
149
* a port bitmap that has only the bit for this port set and
150
* the other bits clear.
151
*/
152
REG_WRITE(addr, 0x0b, 1 << p);
153
154
return 0;
155
}
156
157
static int mv88e6060_setup(struct dsa_switch *ds)
158
{
159
int i;
160
int ret;
161
162
ret = mv88e6060_switch_reset(ds);
163
if (ret < 0)
164
return ret;
165
166
/* @@@ initialise atu */
167
168
ret = mv88e6060_setup_global(ds);
169
if (ret < 0)
170
return ret;
171
172
for (i = 0; i < 6; i++) {
173
ret = mv88e6060_setup_port(ds, i);
174
if (ret < 0)
175
return ret;
176
}
177
178
return 0;
179
}
180
181
static int mv88e6060_set_addr(struct dsa_switch *ds, u8 *addr)
182
{
183
REG_WRITE(REG_GLOBAL, 0x01, (addr[0] << 8) | addr[1]);
184
REG_WRITE(REG_GLOBAL, 0x02, (addr[2] << 8) | addr[3]);
185
REG_WRITE(REG_GLOBAL, 0x03, (addr[4] << 8) | addr[5]);
186
187
return 0;
188
}
189
190
static int mv88e6060_port_to_phy_addr(int port)
191
{
192
if (port >= 0 && port <= 5)
193
return port;
194
return -1;
195
}
196
197
static int mv88e6060_phy_read(struct dsa_switch *ds, int port, int regnum)
198
{
199
int addr;
200
201
addr = mv88e6060_port_to_phy_addr(port);
202
if (addr == -1)
203
return 0xffff;
204
205
return reg_read(ds, addr, regnum);
206
}
207
208
static int
209
mv88e6060_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
210
{
211
int addr;
212
213
addr = mv88e6060_port_to_phy_addr(port);
214
if (addr == -1)
215
return 0xffff;
216
217
return reg_write(ds, addr, regnum, val);
218
}
219
220
static void mv88e6060_poll_link(struct dsa_switch *ds)
221
{
222
int i;
223
224
for (i = 0; i < DSA_MAX_PORTS; i++) {
225
struct net_device *dev;
226
int uninitialized_var(port_status);
227
int link;
228
int speed;
229
int duplex;
230
int fc;
231
232
dev = ds->ports[i];
233
if (dev == NULL)
234
continue;
235
236
link = 0;
237
if (dev->flags & IFF_UP) {
238
port_status = reg_read(ds, REG_PORT(i), 0x00);
239
if (port_status < 0)
240
continue;
241
242
link = !!(port_status & 0x1000);
243
}
244
245
if (!link) {
246
if (netif_carrier_ok(dev)) {
247
printk(KERN_INFO "%s: link down\n", dev->name);
248
netif_carrier_off(dev);
249
}
250
continue;
251
}
252
253
speed = (port_status & 0x0100) ? 100 : 10;
254
duplex = (port_status & 0x0200) ? 1 : 0;
255
fc = ((port_status & 0xc000) == 0xc000) ? 1 : 0;
256
257
if (!netif_carrier_ok(dev)) {
258
printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, "
259
"flow control %sabled\n", dev->name,
260
speed, duplex ? "full" : "half",
261
fc ? "en" : "dis");
262
netif_carrier_on(dev);
263
}
264
}
265
}
266
267
static struct dsa_switch_driver mv88e6060_switch_driver = {
268
.tag_protocol = htons(ETH_P_TRAILER),
269
.probe = mv88e6060_probe,
270
.setup = mv88e6060_setup,
271
.set_addr = mv88e6060_set_addr,
272
.phy_read = mv88e6060_phy_read,
273
.phy_write = mv88e6060_phy_write,
274
.poll_link = mv88e6060_poll_link,
275
};
276
277
static int __init mv88e6060_init(void)
278
{
279
register_switch_driver(&mv88e6060_switch_driver);
280
return 0;
281
}
282
module_init(mv88e6060_init);
283
284
static void __exit mv88e6060_cleanup(void)
285
{
286
unregister_switch_driver(&mv88e6060_switch_driver);
287
}
288
module_exit(mv88e6060_cleanup);
289
290