Merge branch 'for-linus' of git://ftp.arm.linux.org.uk/~rmk/linux-arm
[deliverable/linux.git] / drivers / net / dsa / mv88e6352.c
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
25 static 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
33 ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
34 if (ret >= 0) {
35 if ((ret & 0xfff0) == PORT_SWITCH_ID_6172)
36 return "Marvell 88E6172";
37 if ((ret & 0xfff0) == PORT_SWITCH_ID_6176)
38 return "Marvell 88E6176";
39 if (ret == PORT_SWITCH_ID_6352_A0)
40 return "Marvell 88E6352 (A0)";
41 if (ret == PORT_SWITCH_ID_6352_A1)
42 return "Marvell 88E6352 (A1)";
43 if ((ret & 0xfff0) == PORT_SWITCH_ID_6352)
44 return "Marvell 88E6352";
45 }
46
47 return NULL;
48 }
49
50 static int mv88e6352_setup_global(struct dsa_switch *ds)
51 {
52 u32 upstream_port = dsa_upstream_port(ds);
53 int ret;
54 u32 reg;
55
56 ret = mv88e6xxx_setup_global(ds);
57 if (ret)
58 return ret;
59
60 /* Discard packets with excessive collisions,
61 * mask all interrupt sources, enable PPU (bit 14, undocumented).
62 */
63 REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL,
64 GLOBAL_CONTROL_PPU_ENABLE | GLOBAL_CONTROL_DISCARD_EXCESS);
65
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 */
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);
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
80 return 0;
81 }
82
83 #ifdef CONFIG_NET_DSA_HWMON
84
85 static int mv88e6352_get_temp(struct dsa_switch *ds, int *temp)
86 {
87 int ret;
88
89 *temp = 0;
90
91 ret = mv88e6xxx_phy_page_read(ds, 0, 6, 27);
92 if (ret < 0)
93 return ret;
94
95 *temp = (ret & 0xff) - 25;
96
97 return 0;
98 }
99
100 static int mv88e6352_get_temp_limit(struct dsa_switch *ds, int *temp)
101 {
102 int ret;
103
104 *temp = 0;
105
106 ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26);
107 if (ret < 0)
108 return ret;
109
110 *temp = (((ret >> 8) & 0x1f) * 5) - 25;
111
112 return 0;
113 }
114
115 static int mv88e6352_set_temp_limit(struct dsa_switch *ds, int temp)
116 {
117 int ret;
118
119 ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26);
120 if (ret < 0)
121 return ret;
122 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
123 return mv88e6xxx_phy_page_write(ds, 0, 6, 26,
124 (ret & 0xe0ff) | (temp << 8));
125 }
126
127 static int mv88e6352_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
128 {
129 int ret;
130
131 *alarm = false;
132
133 ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26);
134 if (ret < 0)
135 return ret;
136
137 *alarm = !!(ret & 0x40);
138
139 return 0;
140 }
141 #endif /* CONFIG_NET_DSA_HWMON */
142
143 static int mv88e6352_setup(struct dsa_switch *ds)
144 {
145 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
146 int ret;
147
148 ret = mv88e6xxx_setup_common(ds);
149 if (ret < 0)
150 return ret;
151
152 ps->num_ports = 7;
153
154 mutex_init(&ps->eeprom_mutex);
155
156 ret = mv88e6xxx_switch_reset(ds, true);
157 if (ret < 0)
158 return ret;
159
160 ret = mv88e6352_setup_global(ds);
161 if (ret < 0)
162 return ret;
163
164 return mv88e6xxx_setup_ports(ds);
165 }
166
167 static 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
179 ret = mv88e6xxx_eeprom_busy_wait(ds);
180 if (ret < 0)
181 goto error;
182
183 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2, 0x15);
184 error:
185 mutex_unlock(&ps->eeprom_mutex);
186 return ret;
187 }
188
189 static 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
202 ret = mv88e6xxx_eeprom_load_wait(ds);
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
252 static 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
266 static 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
283 ret = mv88e6xxx_eeprom_busy_wait(ds);
284 error:
285 mutex_unlock(&ps->eeprom_mutex);
286 return ret;
287 }
288
289 static 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
307 ret = mv88e6xxx_eeprom_load_wait(ds);
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
365 struct 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,
371 .phy_read = mv88e6xxx_phy_read_indirect,
372 .phy_write = mv88e6xxx_phy_write_indirect,
373 .poll_link = mv88e6xxx_poll_link,
374 .get_strings = mv88e6xxx_get_strings,
375 .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
376 .get_sset_count = mv88e6xxx_get_sset_count,
377 .set_eee = mv88e6xxx_set_eee,
378 .get_eee = mv88e6xxx_get_eee,
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
385 .get_eeprom = mv88e6352_get_eeprom,
386 .set_eeprom = mv88e6352_set_eeprom,
387 .get_regs_len = mv88e6xxx_get_regs_len,
388 .get_regs = mv88e6xxx_get_regs,
389 .port_join_bridge = mv88e6xxx_join_bridge,
390 .port_leave_bridge = mv88e6xxx_leave_bridge,
391 .port_stp_update = mv88e6xxx_port_stp_update,
392 .fdb_add = mv88e6xxx_port_fdb_add,
393 .fdb_del = mv88e6xxx_port_fdb_del,
394 .fdb_getnext = mv88e6xxx_port_fdb_getnext,
395 };
396
397 MODULE_ALIAS("platform:mv88e6352");
398 MODULE_ALIAS("platform:mv88e6172");
This page took 0.038024 seconds and 5 git commands to generate.