Merge tag 'armsoc-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm...
[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
3ad50cca
GR
25static char *mv88e6352_probe(struct device *host_dev, int sw_addr)
26{
27 struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev);
28 int ret;
29
30 if (bus == NULL)
31 return NULL;
32
cca8b133 33 ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
3ad50cca 34 if (ret >= 0) {
1636d883
AL
35 if ((ret & 0xfff0) == PORT_SWITCH_ID_6172)
36 return "Marvell 88E6172";
cca8b133 37 if ((ret & 0xfff0) == PORT_SWITCH_ID_6176)
2716777b 38 return "Marvell 88E6176";
cca8b133 39 if (ret == PORT_SWITCH_ID_6352_A0)
3ad50cca 40 return "Marvell 88E6352 (A0)";
cca8b133 41 if (ret == PORT_SWITCH_ID_6352_A1)
3ad50cca 42 return "Marvell 88E6352 (A1)";
cca8b133 43 if ((ret & 0xfff0) == PORT_SWITCH_ID_6352)
3ad50cca
GR
44 return "Marvell 88E6352";
45 }
46
47 return NULL;
48}
49
3ad50cca
GR
50static int mv88e6352_setup_global(struct dsa_switch *ds)
51{
15966a2a 52 u32 upstream_port = dsa_upstream_port(ds);
3ad50cca 53 int ret;
15966a2a 54 u32 reg;
54d792f2
AL
55
56 ret = mv88e6xxx_setup_global(ds);
57 if (ret)
58 return ret;
3ad50cca
GR
59
60 /* Discard packets with excessive collisions,
61 * mask all interrupt sources, enable PPU (bit 14, undocumented).
62 */
15966a2a
AL
63 REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL,
64 GLOBAL_CONTROL_PPU_ENABLE | GLOBAL_CONTROL_DISCARD_EXCESS);
3ad50cca 65
3ad50cca
GR
66 /* Configure the upstream port, and configure the upstream
67 * port as the port to which ingress and egress monitor frames
68 * are to be sent.
69 */
15966a2a
AL
70 reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
71 upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
72 upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
73 REG_WRITE(REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg);
3ad50cca
GR
74
75 /* Disable remote management for now, and set the switch's
76 * DSA device number.
77 */
78 REG_WRITE(REG_GLOBAL, 0x1c, ds->index & 0x1f);
79
3ad50cca
GR
80 return 0;
81}
82
276db3b1
GR
83#ifdef CONFIG_NET_DSA_HWMON
84
276db3b1
GR
85static int mv88e6352_get_temp(struct dsa_switch *ds, int *temp)
86{
87 int ret;
88
89 *temp = 0;
90
49143585 91 ret = mv88e6xxx_phy_page_read(ds, 0, 6, 27);
276db3b1
GR
92 if (ret < 0)
93 return ret;
94
95 *temp = (ret & 0xff) - 25;
96
97 return 0;
98}
99
100static int mv88e6352_get_temp_limit(struct dsa_switch *ds, int *temp)
101{
102 int ret;
103
104 *temp = 0;
105
49143585 106 ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26);
276db3b1
GR
107 if (ret < 0)
108 return ret;
109
110 *temp = (((ret >> 8) & 0x1f) * 5) - 25;
111
112 return 0;
113}
114
115static int mv88e6352_set_temp_limit(struct dsa_switch *ds, int temp)
116{
117 int ret;
118
49143585 119 ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26);
276db3b1
GR
120 if (ret < 0)
121 return ret;
122 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
49143585 123 return mv88e6xxx_phy_page_write(ds, 0, 6, 26,
276db3b1
GR
124 (ret & 0xe0ff) | (temp << 8));
125}
126
127static int mv88e6352_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
128{
129 int ret;
130
131 *alarm = false;
132
49143585 133 ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26);
276db3b1
GR
134 if (ret < 0)
135 return ret;
136
137 *alarm = !!(ret & 0x40);
138
139 return 0;
140}
141#endif /* CONFIG_NET_DSA_HWMON */
142
3ad50cca
GR
143static int mv88e6352_setup(struct dsa_switch *ds)
144{
145 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
146 int ret;
3ad50cca 147
acdaffcc
GR
148 ret = mv88e6xxx_setup_common(ds);
149 if (ret < 0)
150 return ret;
151
44e50ddb
AL
152 ps->num_ports = 7;
153
33b43df4 154 mutex_init(&ps->eeprom_mutex);
3ad50cca 155
143a8307 156 ret = mv88e6xxx_switch_reset(ds, true);
3ad50cca
GR
157 if (ret < 0)
158 return ret;
159
3ad50cca
GR
160 ret = mv88e6352_setup_global(ds);
161 if (ret < 0)
162 return ret;
163
dbde9e66 164 return mv88e6xxx_setup_ports(ds);
3ad50cca
GR
165}
166
33b43df4
GR
167static int mv88e6352_read_eeprom_word(struct dsa_switch *ds, int addr)
168{
169 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
170 int ret;
171
172 mutex_lock(&ps->eeprom_mutex);
173
174 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, 0x14,
175 0xc000 | (addr & 0xff));
176 if (ret < 0)
177 goto error;
178
f3044683 179 ret = mv88e6xxx_eeprom_busy_wait(ds);
33b43df4
GR
180 if (ret < 0)
181 goto error;
182
183 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2, 0x15);
184error:
185 mutex_unlock(&ps->eeprom_mutex);
186 return ret;
187}
188
189static int mv88e6352_get_eeprom(struct dsa_switch *ds,
190 struct ethtool_eeprom *eeprom, u8 *data)
191{
192 int offset;
193 int len;
194 int ret;
195
196 offset = eeprom->offset;
197 len = eeprom->len;
198 eeprom->len = 0;
199
200 eeprom->magic = 0xc3ec4951;
201
f3044683 202 ret = mv88e6xxx_eeprom_load_wait(ds);
33b43df4
GR
203 if (ret < 0)
204 return ret;
205
206 if (offset & 1) {
207 int word;
208
209 word = mv88e6352_read_eeprom_word(ds, offset >> 1);
210 if (word < 0)
211 return word;
212
213 *data++ = (word >> 8) & 0xff;
214
215 offset++;
216 len--;
217 eeprom->len++;
218 }
219
220 while (len >= 2) {
221 int word;
222
223 word = mv88e6352_read_eeprom_word(ds, offset >> 1);
224 if (word < 0)
225 return word;
226
227 *data++ = word & 0xff;
228 *data++ = (word >> 8) & 0xff;
229
230 offset += 2;
231 len -= 2;
232 eeprom->len += 2;
233 }
234
235 if (len) {
236 int word;
237
238 word = mv88e6352_read_eeprom_word(ds, offset >> 1);
239 if (word < 0)
240 return word;
241
242 *data++ = word & 0xff;
243
244 offset++;
245 len--;
246 eeprom->len++;
247 }
248
249 return 0;
250}
251
252static int mv88e6352_eeprom_is_readonly(struct dsa_switch *ds)
253{
254 int ret;
255
256 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2, 0x14);
257 if (ret < 0)
258 return ret;
259
260 if (!(ret & 0x0400))
261 return -EROFS;
262
263 return 0;
264}
265
266static int mv88e6352_write_eeprom_word(struct dsa_switch *ds, int addr,
267 u16 data)
268{
269 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
270 int ret;
271
272 mutex_lock(&ps->eeprom_mutex);
273
274 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, 0x15, data);
275 if (ret < 0)
276 goto error;
277
278 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, 0x14,
279 0xb000 | (addr & 0xff));
280 if (ret < 0)
281 goto error;
282
f3044683 283 ret = mv88e6xxx_eeprom_busy_wait(ds);
33b43df4
GR
284error:
285 mutex_unlock(&ps->eeprom_mutex);
286 return ret;
287}
288
289static int mv88e6352_set_eeprom(struct dsa_switch *ds,
290 struct ethtool_eeprom *eeprom, u8 *data)
291{
292 int offset;
293 int ret;
294 int len;
295
296 if (eeprom->magic != 0xc3ec4951)
297 return -EINVAL;
298
299 ret = mv88e6352_eeprom_is_readonly(ds);
300 if (ret)
301 return ret;
302
303 offset = eeprom->offset;
304 len = eeprom->len;
305 eeprom->len = 0;
306
f3044683 307 ret = mv88e6xxx_eeprom_load_wait(ds);
33b43df4
GR
308 if (ret < 0)
309 return ret;
310
311 if (offset & 1) {
312 int word;
313
314 word = mv88e6352_read_eeprom_word(ds, offset >> 1);
315 if (word < 0)
316 return word;
317
318 word = (*data++ << 8) | (word & 0xff);
319
320 ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word);
321 if (ret < 0)
322 return ret;
323
324 offset++;
325 len--;
326 eeprom->len++;
327 }
328
329 while (len >= 2) {
330 int word;
331
332 word = *data++;
333 word |= *data++ << 8;
334
335 ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word);
336 if (ret < 0)
337 return ret;
338
339 offset += 2;
340 len -= 2;
341 eeprom->len += 2;
342 }
343
344 if (len) {
345 int word;
346
347 word = mv88e6352_read_eeprom_word(ds, offset >> 1);
348 if (word < 0)
349 return word;
350
351 word = (word & 0xff00) | *data++;
352
353 ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word);
354 if (ret < 0)
355 return ret;
356
357 offset++;
358 len--;
359 eeprom->len++;
360 }
361
362 return 0;
363}
364
3ad50cca
GR
365struct dsa_switch_driver mv88e6352_switch_driver = {
366 .tag_protocol = DSA_TAG_PROTO_EDSA,
367 .priv_size = sizeof(struct mv88e6xxx_priv_state),
368 .probe = mv88e6352_probe,
369 .setup = mv88e6352_setup,
370 .set_addr = mv88e6xxx_set_addr_indirect,
fd3a0ee4
AL
371 .phy_read = mv88e6xxx_phy_read_indirect,
372 .phy_write = mv88e6xxx_phy_write_indirect,
3ad50cca 373 .poll_link = mv88e6xxx_poll_link,
e413e7e1
AL
374 .get_strings = mv88e6xxx_get_strings,
375 .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
376 .get_sset_count = mv88e6xxx_get_sset_count,
04b0a80b
GR
377 .set_eee = mv88e6xxx_set_eee,
378 .get_eee = mv88e6xxx_get_eee,
276db3b1
GR
379#ifdef CONFIG_NET_DSA_HWMON
380 .get_temp = mv88e6352_get_temp,
381 .get_temp_limit = mv88e6352_get_temp_limit,
382 .set_temp_limit = mv88e6352_set_temp_limit,
383 .get_temp_alarm = mv88e6352_get_temp_alarm,
384#endif
33b43df4
GR
385 .get_eeprom = mv88e6352_get_eeprom,
386 .set_eeprom = mv88e6352_set_eeprom,
95d08b5a
GR
387 .get_regs_len = mv88e6xxx_get_regs_len,
388 .get_regs = mv88e6xxx_get_regs,
3f244abb
GR
389 .port_join_bridge = mv88e6xxx_join_bridge,
390 .port_leave_bridge = mv88e6xxx_leave_bridge,
391 .port_stp_update = mv88e6xxx_port_stp_update,
4f431e56
GR
392 .fdb_add = mv88e6xxx_port_fdb_add,
393 .fdb_del = mv88e6xxx_port_fdb_del,
394 .fdb_getnext = mv88e6xxx_port_fdb_getnext,
3ad50cca
GR
395};
396
397MODULE_ALIAS("platform:mv88e6352");
1636d883 398MODULE_ALIAS("platform:mv88e6172");
This page took 0.095172 seconds and 5 git commands to generate.