]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/net/dsa/mv88e6352.c
Merge remote-tracking branch 'asoc/topic/ssm4567' into asoc-next
[karo-tx-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");