net: dsa: Keep the mii bus and address in the private structure
[deliverable/linux.git] / drivers / net / dsa / mv88e6352.c
CommitLineData
3ad50cca
GR
1/*
2 * net/dsa/mv88e6352.c - Marvell 88e6352 switch chip support
3 *
4 * Copyright (c) 2014 Guenter Roeck
5 *
6 * Derived from mv88e6123_61_65.c
7 * Copyright (c) 2008-2009 Marvell Semiconductor
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 */
14
15#include <linux/delay.h>
16#include <linux/jiffies.h>
17#include <linux/list.h>
18#include <linux/module.h>
19#include <linux/netdevice.h>
20#include <linux/platform_device.h>
21#include <linux/phy.h>
22#include <net/dsa.h>
23#include "mv88e6xxx.h"
24
b9b37713
VD
25static const struct mv88e6xxx_switch_id mv88e6352_table[] = {
26 { PORT_SWITCH_ID_6172, "Marvell 88E6172" },
27 { PORT_SWITCH_ID_6176, "Marvell 88E6176" },
bd16a724 28 { PORT_SWITCH_ID_6240, "Marvell 88E6240" },
b9b37713
VD
29 { PORT_SWITCH_ID_6320, "Marvell 88E6320" },
30 { PORT_SWITCH_ID_6320_A1, "Marvell 88E6320 (A1)" },
31 { PORT_SWITCH_ID_6320_A2, "Marvell 88e6320 (A2)" },
32 { PORT_SWITCH_ID_6321, "Marvell 88E6321" },
33 { PORT_SWITCH_ID_6321_A1, "Marvell 88E6321 (A1)" },
34 { PORT_SWITCH_ID_6321_A2, "Marvell 88e6321 (A2)" },
35 { PORT_SWITCH_ID_6352, "Marvell 88E6352" },
36 { PORT_SWITCH_ID_6352_A0, "Marvell 88E6352 (A0)" },
37 { PORT_SWITCH_ID_6352_A1, "Marvell 88E6352 (A1)" },
38};
39
bbb8d793 40static char *mv88e6352_probe(struct device *dsa_dev, struct device *host_dev,
7543a6d5 41 int sw_addr, void **priv)
3ad50cca 42{
a77d43f1
AL
43 return mv88e6xxx_drv_probe(dsa_dev, host_dev, sw_addr, priv,
44 mv88e6352_table,
45 ARRAY_SIZE(mv88e6352_table));
3ad50cca
GR
46}
47
3ad50cca
GR
48static int mv88e6352_setup_global(struct dsa_switch *ds)
49{
15966a2a 50 u32 upstream_port = dsa_upstream_port(ds);
3ad50cca 51 int ret;
15966a2a 52 u32 reg;
54d792f2
AL
53
54 ret = mv88e6xxx_setup_global(ds);
55 if (ret)
56 return ret;
3ad50cca
GR
57
58 /* Discard packets with excessive collisions,
59 * mask all interrupt sources, enable PPU (bit 14, undocumented).
60 */
15966a2a
AL
61 REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL,
62 GLOBAL_CONTROL_PPU_ENABLE | GLOBAL_CONTROL_DISCARD_EXCESS);
3ad50cca 63
3ad50cca
GR
64 /* Configure the upstream port, and configure the upstream
65 * port as the port to which ingress and egress monitor frames
66 * are to be sent.
67 */
15966a2a
AL
68 reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
69 upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
70 upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
71 REG_WRITE(REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg);
3ad50cca
GR
72
73 /* Disable remote management for now, and set the switch's
74 * DSA device number.
75 */
76 REG_WRITE(REG_GLOBAL, 0x1c, ds->index & 0x1f);
77
3ad50cca
GR
78 return 0;
79}
80
3ad50cca
GR
81static int mv88e6352_setup(struct dsa_switch *ds)
82{
83 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
84 int ret;
3ad50cca 85
7543a6d5
AL
86 ps->ds = ds;
87
acdaffcc
GR
88 ret = mv88e6xxx_setup_common(ds);
89 if (ret < 0)
90 return ret;
91
44e50ddb
AL
92 ps->num_ports = 7;
93
33b43df4 94 mutex_init(&ps->eeprom_mutex);
3ad50cca 95
143a8307 96 ret = mv88e6xxx_switch_reset(ds, true);
3ad50cca
GR
97 if (ret < 0)
98 return ret;
99
3ad50cca
GR
100 ret = mv88e6352_setup_global(ds);
101 if (ret < 0)
102 return ret;
103
dbde9e66 104 return mv88e6xxx_setup_ports(ds);
3ad50cca
GR
105}
106
33b43df4
GR
107static int mv88e6352_read_eeprom_word(struct dsa_switch *ds, int addr)
108{
109 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
110 int ret;
111
112 mutex_lock(&ps->eeprom_mutex);
113
966bce38
AL
114 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
115 GLOBAL2_EEPROM_OP_READ |
116 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
33b43df4
GR
117 if (ret < 0)
118 goto error;
119
f3044683 120 ret = mv88e6xxx_eeprom_busy_wait(ds);
33b43df4
GR
121 if (ret < 0)
122 goto error;
123
966bce38 124 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2, GLOBAL2_EEPROM_DATA);
33b43df4
GR
125error:
126 mutex_unlock(&ps->eeprom_mutex);
127 return ret;
128}
129
130static int mv88e6352_get_eeprom(struct dsa_switch *ds,
131 struct ethtool_eeprom *eeprom, u8 *data)
132{
133 int offset;
134 int len;
135 int ret;
136
137 offset = eeprom->offset;
138 len = eeprom->len;
139 eeprom->len = 0;
140
141 eeprom->magic = 0xc3ec4951;
142
f3044683 143 ret = mv88e6xxx_eeprom_load_wait(ds);
33b43df4
GR
144 if (ret < 0)
145 return ret;
146
147 if (offset & 1) {
148 int word;
149
150 word = mv88e6352_read_eeprom_word(ds, offset >> 1);
151 if (word < 0)
152 return word;
153
154 *data++ = (word >> 8) & 0xff;
155
156 offset++;
157 len--;
158 eeprom->len++;
159 }
160
161 while (len >= 2) {
162 int word;
163
164 word = mv88e6352_read_eeprom_word(ds, offset >> 1);
165 if (word < 0)
166 return word;
167
168 *data++ = word & 0xff;
169 *data++ = (word >> 8) & 0xff;
170
171 offset += 2;
172 len -= 2;
173 eeprom->len += 2;
174 }
175
176 if (len) {
177 int word;
178
179 word = mv88e6352_read_eeprom_word(ds, offset >> 1);
180 if (word < 0)
181 return word;
182
183 *data++ = word & 0xff;
184
185 offset++;
186 len--;
187 eeprom->len++;
188 }
189
190 return 0;
191}
192
193static int mv88e6352_eeprom_is_readonly(struct dsa_switch *ds)
194{
195 int ret;
196
966bce38 197 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP);
33b43df4
GR
198 if (ret < 0)
199 return ret;
200
966bce38 201 if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN))
33b43df4
GR
202 return -EROFS;
203
204 return 0;
205}
206
207static int mv88e6352_write_eeprom_word(struct dsa_switch *ds, int addr,
208 u16 data)
209{
210 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
211 int ret;
212
213 mutex_lock(&ps->eeprom_mutex);
214
966bce38 215 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
33b43df4
GR
216 if (ret < 0)
217 goto error;
218
966bce38
AL
219 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
220 GLOBAL2_EEPROM_OP_WRITE |
221 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
33b43df4
GR
222 if (ret < 0)
223 goto error;
224
f3044683 225 ret = mv88e6xxx_eeprom_busy_wait(ds);
33b43df4
GR
226error:
227 mutex_unlock(&ps->eeprom_mutex);
228 return ret;
229}
230
231static int mv88e6352_set_eeprom(struct dsa_switch *ds,
232 struct ethtool_eeprom *eeprom, u8 *data)
233{
234 int offset;
235 int ret;
236 int len;
237
238 if (eeprom->magic != 0xc3ec4951)
239 return -EINVAL;
240
241 ret = mv88e6352_eeprom_is_readonly(ds);
242 if (ret)
243 return ret;
244
245 offset = eeprom->offset;
246 len = eeprom->len;
247 eeprom->len = 0;
248
f3044683 249 ret = mv88e6xxx_eeprom_load_wait(ds);
33b43df4
GR
250 if (ret < 0)
251 return ret;
252
253 if (offset & 1) {
254 int word;
255
256 word = mv88e6352_read_eeprom_word(ds, offset >> 1);
257 if (word < 0)
258 return word;
259
260 word = (*data++ << 8) | (word & 0xff);
261
262 ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word);
263 if (ret < 0)
264 return ret;
265
266 offset++;
267 len--;
268 eeprom->len++;
269 }
270
271 while (len >= 2) {
272 int word;
273
274 word = *data++;
275 word |= *data++ << 8;
276
277 ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word);
278 if (ret < 0)
279 return ret;
280
281 offset += 2;
282 len -= 2;
283 eeprom->len += 2;
284 }
285
286 if (len) {
287 int word;
288
289 word = mv88e6352_read_eeprom_word(ds, offset >> 1);
290 if (word < 0)
291 return word;
292
293 word = (word & 0xff00) | *data++;
294
295 ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word);
296 if (ret < 0)
297 return ret;
298
299 offset++;
300 len--;
301 eeprom->len++;
302 }
303
304 return 0;
305}
306
3ad50cca
GR
307struct dsa_switch_driver mv88e6352_switch_driver = {
308 .tag_protocol = DSA_TAG_PROTO_EDSA,
3ad50cca
GR
309 .probe = mv88e6352_probe,
310 .setup = mv88e6352_setup,
311 .set_addr = mv88e6xxx_set_addr_indirect,
fd3a0ee4
AL
312 .phy_read = mv88e6xxx_phy_read_indirect,
313 .phy_write = mv88e6xxx_phy_write_indirect,
e413e7e1
AL
314 .get_strings = mv88e6xxx_get_strings,
315 .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
316 .get_sset_count = mv88e6xxx_get_sset_count,
dea87024 317 .adjust_link = mv88e6xxx_adjust_link,
04b0a80b
GR
318 .set_eee = mv88e6xxx_set_eee,
319 .get_eee = mv88e6xxx_get_eee,
276db3b1 320#ifdef CONFIG_NET_DSA_HWMON
c22995c5
GR
321 .get_temp = mv88e6xxx_get_temp,
322 .get_temp_limit = mv88e6xxx_get_temp_limit,
323 .set_temp_limit = mv88e6xxx_set_temp_limit,
324 .get_temp_alarm = mv88e6xxx_get_temp_alarm,
276db3b1 325#endif
33b43df4
GR
326 .get_eeprom = mv88e6352_get_eeprom,
327 .set_eeprom = mv88e6352_set_eeprom,
95d08b5a
GR
328 .get_regs_len = mv88e6xxx_get_regs_len,
329 .get_regs = mv88e6xxx_get_regs,
71327a4e
VD
330 .port_bridge_join = mv88e6xxx_port_bridge_join,
331 .port_bridge_leave = mv88e6xxx_port_bridge_leave,
43c44a9f 332 .port_stp_state_set = mv88e6xxx_port_stp_state_set,
214cdb99 333 .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
76e398a6 334 .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
0d3b33e6 335 .port_vlan_add = mv88e6xxx_port_vlan_add,
7dad08d7 336 .port_vlan_del = mv88e6xxx_port_vlan_del,
ceff5eff 337 .port_vlan_dump = mv88e6xxx_port_vlan_dump,
146a3206 338 .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
2a778e1b
VD
339 .port_fdb_add = mv88e6xxx_port_fdb_add,
340 .port_fdb_del = mv88e6xxx_port_fdb_del,
f33475bd 341 .port_fdb_dump = mv88e6xxx_port_fdb_dump,
3ad50cca
GR
342};
343
1636d883 344MODULE_ALIAS("platform:mv88e6172");
7c3d0d67
AK
345MODULE_ALIAS("platform:mv88e6176");
346MODULE_ALIAS("platform:mv88e6320");
347MODULE_ALIAS("platform:mv88e6321");
348MODULE_ALIAS("platform:mv88e6352");
This page took 0.118974 seconds and 5 git commands to generate.