]>
Commit | Line | Data |
---|---|---|
f03ae5f9 | 1 | /* net/dsa/mv88e6171.c - Marvell 88e6171/8826172 switch chip support |
42f27253 AL |
2 | * Copyright (c) 2008-2009 Marvell Semiconductor |
3 | * Copyright (c) 2014 Claudio Leite <leitec@staticky.com> | |
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/delay.h> | |
12 | #include <linux/jiffies.h> | |
13 | #include <linux/list.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/netdevice.h> | |
16 | #include <linux/phy.h> | |
17 | #include <net/dsa.h> | |
18 | #include "mv88e6xxx.h" | |
19 | ||
b4d2394d | 20 | static char *mv88e6171_probe(struct device *host_dev, int sw_addr) |
42f27253 | 21 | { |
b4d2394d | 22 | struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev); |
42f27253 AL |
23 | int ret; |
24 | ||
b4d2394d AD |
25 | if (bus == NULL) |
26 | return NULL; | |
27 | ||
cca8b133 | 28 | ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID); |
42f27253 | 29 | if (ret >= 0) { |
cca8b133 | 30 | if ((ret & 0xfff0) == PORT_SWITCH_ID_6171) |
42f27253 | 31 | return "Marvell 88E6171"; |
cca8b133 | 32 | if ((ret & 0xfff0) == PORT_SWITCH_ID_6172) |
f03ae5f9 | 33 | return "Marvell 88E6172"; |
42f27253 AL |
34 | } |
35 | ||
36 | return NULL; | |
37 | } | |
38 | ||
42f27253 AL |
39 | static int mv88e6171_setup_global(struct dsa_switch *ds) |
40 | { | |
44e50ddb | 41 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); |
42f27253 AL |
42 | int ret; |
43 | int i; | |
44 | ||
4c732668 AL |
45 | /* Discard packets with excessive collisions, mask all |
46 | * interrupt sources, enable PPU. | |
42f27253 | 47 | */ |
4c732668 | 48 | REG_WRITE(REG_GLOBAL, 0x04, 0x6000); |
42f27253 AL |
49 | |
50 | /* Set the default address aging time to 5 minutes, and | |
51 | * enable address learn messages to be sent to all message | |
52 | * ports. | |
53 | */ | |
54 | REG_WRITE(REG_GLOBAL, 0x0a, 0x0148); | |
55 | ||
56 | /* Configure the priority mapping registers. */ | |
57 | ret = mv88e6xxx_config_prio(ds); | |
58 | if (ret < 0) | |
59 | return ret; | |
60 | ||
61 | /* Configure the upstream port, and configure the upstream | |
62 | * port as the port to which ingress and egress monitor frames | |
63 | * are to be sent. | |
64 | */ | |
65 | if (REG_READ(REG_PORT(0), 0x03) == 0x1710) | |
66 | REG_WRITE(REG_GLOBAL, 0x1a, (dsa_upstream_port(ds) * 0x1111)); | |
67 | else | |
68 | REG_WRITE(REG_GLOBAL, 0x1a, (dsa_upstream_port(ds) * 0x1110)); | |
69 | ||
70 | /* Disable remote management for now, and set the switch's | |
71 | * DSA device number. | |
72 | */ | |
73 | REG_WRITE(REG_GLOBAL, 0x1c, ds->index & 0x1f); | |
74 | ||
75 | /* Send all frames with destination addresses matching | |
76 | * 01:80:c2:00:00:2x to the CPU port. | |
77 | */ | |
78 | REG_WRITE(REG_GLOBAL2, 0x02, 0xffff); | |
79 | ||
80 | /* Send all frames with destination addresses matching | |
81 | * 01:80:c2:00:00:0x to the CPU port. | |
82 | */ | |
83 | REG_WRITE(REG_GLOBAL2, 0x03, 0xffff); | |
84 | ||
85 | /* Disable the loopback filter, disable flow control | |
86 | * messages, disable flood broadcast override, disable | |
87 | * removing of provider tags, disable ATU age violation | |
88 | * interrupts, disable tag flow control, force flow | |
89 | * control priority to the highest, and send all special | |
90 | * multicast frames to the CPU at the highest priority. | |
91 | */ | |
92 | REG_WRITE(REG_GLOBAL2, 0x05, 0x00ff); | |
93 | ||
94 | /* Program the DSA routing table. */ | |
95 | for (i = 0; i < 32; i++) { | |
96 | int nexthop; | |
97 | ||
98 | nexthop = 0x1f; | |
99 | if (i != ds->index && i < ds->dst->pd->nr_chips) | |
100 | nexthop = ds->pd->rtable[i] & 0x1f; | |
101 | ||
102 | REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | nexthop); | |
103 | } | |
104 | ||
105 | /* Clear all trunk masks. */ | |
44e50ddb | 106 | for (i = 0; i < ps->num_ports; i++) |
42f27253 AL |
107 | REG_WRITE(REG_GLOBAL2, 0x07, 0x8000 | (i << 12) | 0xff); |
108 | ||
109 | /* Clear all trunk mappings. */ | |
110 | for (i = 0; i < 16; i++) | |
111 | REG_WRITE(REG_GLOBAL2, 0x08, 0x8000 | (i << 11)); | |
112 | ||
113 | /* Disable ingress rate limiting by resetting all ingress | |
114 | * rate limit registers to their initial state. | |
115 | */ | |
116 | for (i = 0; i < 6; i++) | |
117 | REG_WRITE(REG_GLOBAL2, 0x09, 0x9000 | (i << 8)); | |
118 | ||
119 | /* Initialise cross-chip port VLAN table to reset defaults. */ | |
120 | REG_WRITE(REG_GLOBAL2, 0x0b, 0x9000); | |
121 | ||
122 | /* Clear the priority override table. */ | |
123 | for (i = 0; i < 16; i++) | |
124 | REG_WRITE(REG_GLOBAL2, 0x0f, 0x8000 | (i << 8)); | |
125 | ||
126 | /* @@@ initialise AVB (22/23) watchdog (27) sdet (29) registers */ | |
127 | ||
128 | return 0; | |
129 | } | |
130 | ||
131 | static int mv88e6171_setup_port(struct dsa_switch *ds, int p) | |
132 | { | |
133 | int addr = REG_PORT(p); | |
134 | u16 val; | |
135 | ||
136 | /* MAC Forcing register: don't force link, speed, duplex | |
137 | * or flow control state to any particular values on physical | |
138 | * ports, but force the CPU port and all DSA ports to 1000 Mb/s | |
139 | * full duplex. | |
140 | */ | |
141 | val = REG_READ(addr, 0x01); | |
142 | if (dsa_is_cpu_port(ds, p) || ds->dsa_port_mask & (1 << p)) | |
143 | REG_WRITE(addr, 0x01, val | 0x003e); | |
144 | else | |
145 | REG_WRITE(addr, 0x01, val | 0x0003); | |
146 | ||
147 | /* Do not limit the period of time that this port can be | |
148 | * paused for by the remote end or the period of time that | |
149 | * this port can pause the remote end. | |
150 | */ | |
151 | REG_WRITE(addr, 0x02, 0x0000); | |
152 | ||
153 | /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock, | |
154 | * disable Header mode, enable IGMP/MLD snooping, disable VLAN | |
155 | * tunneling, determine priority by looking at 802.1p and IP | |
156 | * priority fields (IP prio has precedence), and set STP state | |
157 | * to Forwarding. | |
158 | * | |
159 | * If this is the CPU link, use DSA or EDSA tagging depending | |
160 | * on which tagging mode was configured. | |
161 | * | |
162 | * If this is a link to another switch, use DSA tagging mode. | |
163 | * | |
164 | * If this is the upstream port for this switch, enable | |
165 | * forwarding of unknown unicasts and multicasts. | |
166 | */ | |
167 | val = 0x0433; | |
168 | if (dsa_is_cpu_port(ds, p)) { | |
77b3a4dc | 169 | if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA) |
42f27253 AL |
170 | val |= 0x3300; |
171 | else | |
172 | val |= 0x0100; | |
173 | } | |
174 | if (ds->dsa_port_mask & (1 << p)) | |
175 | val |= 0x0100; | |
176 | if (p == dsa_upstream_port(ds)) | |
177 | val |= 0x000c; | |
178 | REG_WRITE(addr, 0x04, val); | |
179 | ||
42f27253 AL |
180 | /* Port Control 2: don't force a good FCS, set the maximum |
181 | * frame size to 10240 bytes, don't let the switch add or | |
182 | * strip 802.1q tags, don't discard tagged or untagged frames | |
183 | * on this port, do a destination address lookup on all | |
184 | * received packets as usual, disable ARP mirroring and don't | |
185 | * send a copy of all transmitted/received frames on this port | |
186 | * to the CPU. | |
187 | */ | |
188 | REG_WRITE(addr, 0x08, 0x2080); | |
189 | ||
190 | /* Egress rate control: disable egress rate control. */ | |
191 | REG_WRITE(addr, 0x09, 0x0001); | |
192 | ||
193 | /* Egress rate control 2: disable egress rate control. */ | |
194 | REG_WRITE(addr, 0x0a, 0x0000); | |
195 | ||
196 | /* Port Association Vector: when learning source addresses | |
197 | * of packets, add the address to the address database using | |
198 | * a port bitmap that has only the bit for this port set and | |
199 | * the other bits clear. | |
200 | */ | |
201 | REG_WRITE(addr, 0x0b, 1 << p); | |
202 | ||
203 | /* Port ATU control: disable limiting the number of address | |
204 | * database entries that this port is allowed to use. | |
205 | */ | |
206 | REG_WRITE(addr, 0x0c, 0x0000); | |
207 | ||
208 | /* Priority Override: disable DA, SA and VTU priority override. */ | |
209 | REG_WRITE(addr, 0x0d, 0x0000); | |
210 | ||
211 | /* Port Ethertype: use the Ethertype DSA Ethertype value. */ | |
212 | REG_WRITE(addr, 0x0f, ETH_P_EDSA); | |
213 | ||
214 | /* Tag Remap: use an identity 802.1p prio -> switch prio | |
215 | * mapping. | |
216 | */ | |
217 | REG_WRITE(addr, 0x18, 0x3210); | |
218 | ||
219 | /* Tag Remap 2: use an identity 802.1p prio -> switch prio | |
220 | * mapping. | |
221 | */ | |
222 | REG_WRITE(addr, 0x19, 0x7654); | |
223 | ||
b0019b70 | 224 | return mv88e6xxx_setup_port_common(ds, p); |
42f27253 AL |
225 | } |
226 | ||
227 | static int mv88e6171_setup(struct dsa_switch *ds) | |
228 | { | |
44e50ddb | 229 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); |
42f27253 AL |
230 | int i; |
231 | int ret; | |
232 | ||
acdaffcc GR |
233 | ret = mv88e6xxx_setup_common(ds); |
234 | if (ret < 0) | |
235 | return ret; | |
42f27253 | 236 | |
44e50ddb AL |
237 | ps->num_ports = 7; |
238 | ||
143a8307 | 239 | ret = mv88e6xxx_switch_reset(ds, true); |
42f27253 AL |
240 | if (ret < 0) |
241 | return ret; | |
242 | ||
243 | /* @@@ initialise vtu and atu */ | |
244 | ||
245 | ret = mv88e6171_setup_global(ds); | |
246 | if (ret < 0) | |
247 | return ret; | |
248 | ||
44e50ddb | 249 | for (i = 0; i < ps->num_ports; i++) { |
42f27253 AL |
250 | if (!(dsa_is_cpu_port(ds, i) || ds->phys_port_mask & (1 << i))) |
251 | continue; | |
252 | ||
253 | ret = mv88e6171_setup_port(ds, i); | |
254 | if (ret < 0) | |
255 | return ret; | |
256 | } | |
257 | ||
258 | return 0; | |
259 | } | |
260 | ||
baae51d5 AL |
261 | static int mv88e6171_get_eee(struct dsa_switch *ds, int port, |
262 | struct ethtool_eee *e) | |
263 | { | |
264 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | |
265 | ||
cca8b133 | 266 | if (ps->id == PORT_SWITCH_ID_6172) |
baae51d5 AL |
267 | return mv88e6xxx_get_eee(ds, port, e); |
268 | ||
269 | return -EOPNOTSUPP; | |
270 | } | |
271 | ||
272 | static int mv88e6171_set_eee(struct dsa_switch *ds, int port, | |
273 | struct phy_device *phydev, struct ethtool_eee *e) | |
274 | { | |
275 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | |
276 | ||
cca8b133 | 277 | if (ps->id == PORT_SWITCH_ID_6172) |
baae51d5 AL |
278 | return mv88e6xxx_set_eee(ds, port, phydev, e); |
279 | ||
280 | return -EOPNOTSUPP; | |
281 | } | |
282 | ||
42f27253 | 283 | struct dsa_switch_driver mv88e6171_switch_driver = { |
c146b778 | 284 | .tag_protocol = DSA_TAG_PROTO_EDSA, |
42f27253 AL |
285 | .priv_size = sizeof(struct mv88e6xxx_priv_state), |
286 | .probe = mv88e6171_probe, | |
287 | .setup = mv88e6171_setup, | |
288 | .set_addr = mv88e6xxx_set_addr_indirect, | |
fd3a0ee4 AL |
289 | .phy_read = mv88e6xxx_phy_read_indirect, |
290 | .phy_write = mv88e6xxx_phy_write_indirect, | |
42f27253 | 291 | .poll_link = mv88e6xxx_poll_link, |
e413e7e1 AL |
292 | .get_strings = mv88e6xxx_get_strings, |
293 | .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, | |
294 | .get_sset_count = mv88e6xxx_get_sset_count, | |
baae51d5 AL |
295 | .set_eee = mv88e6171_set_eee, |
296 | .get_eee = mv88e6171_get_eee, | |
4dd38cdb AL |
297 | #ifdef CONFIG_NET_DSA_HWMON |
298 | .get_temp = mv88e6xxx_get_temp, | |
299 | #endif | |
03d6faa9 AL |
300 | .get_regs_len = mv88e6xxx_get_regs_len, |
301 | .get_regs = mv88e6xxx_get_regs, | |
b2a6b93a AL |
302 | .port_join_bridge = mv88e6xxx_join_bridge, |
303 | .port_leave_bridge = mv88e6xxx_leave_bridge, | |
304 | .port_stp_update = mv88e6xxx_port_stp_update, | |
305 | .fdb_add = mv88e6xxx_port_fdb_add, | |
306 | .fdb_del = mv88e6xxx_port_fdb_del, | |
307 | .fdb_getnext = mv88e6xxx_port_fdb_getnext, | |
42f27253 AL |
308 | }; |
309 | ||
310 | MODULE_ALIAS("platform:mv88e6171"); | |
f03ae5f9 | 311 | MODULE_ALIAS("platform:mv88e6172"); |