net: dsa: mv88e6xxx: add switch info
[cascardo/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 const struct mv88e6xxx_info mv88e6352_table[] = {
26         {
27                 .prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
28                 .name = "Marvell 88E6320",
29         }, {
30                 .prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
31                 .name = "Marvell 88E6321",
32         }, {
33                 .prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
34                 .name = "Marvell 88E6172",
35         }, {
36                 .prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
37                 .name = "Marvell 88E6176",
38         }, {
39                 .prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
40                 .name = "Marvell 88E6240",
41         }, {
42                 .prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
43                 .name = "Marvell 88E6352",
44         }
45 };
46
47 static const char *mv88e6352_drv_probe(struct device *dsa_dev,
48                                        struct device *host_dev, int sw_addr,
49                                        void **priv)
50 {
51         return mv88e6xxx_drv_probe(dsa_dev, host_dev, sw_addr, priv,
52                                    mv88e6352_table,
53                                    ARRAY_SIZE(mv88e6352_table));
54 }
55
56 static int mv88e6352_setup_global(struct dsa_switch *ds)
57 {
58         u32 upstream_port = dsa_upstream_port(ds);
59         int ret;
60         u32 reg;
61
62         ret = mv88e6xxx_setup_global(ds);
63         if (ret)
64                 return ret;
65
66         /* Discard packets with excessive collisions,
67          * mask all interrupt sources, enable PPU (bit 14, undocumented).
68          */
69         ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_CONTROL,
70                                   GLOBAL_CONTROL_PPU_ENABLE |
71                                   GLOBAL_CONTROL_DISCARD_EXCESS);
72         if (ret)
73                 return ret;
74
75         /* Configure the upstream port, and configure the upstream
76          * port as the port to which ingress and egress monitor frames
77          * are to be sent.
78          */
79         reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
80                 upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
81                 upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
82         ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg);
83         if (ret)
84                 return ret;
85
86         /* Disable remote management for now, and set the switch's
87          * DSA device number.
88          */
89         return mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x1c, ds->index & 0x1f);
90 }
91
92 static int mv88e6352_setup(struct dsa_switch *ds)
93 {
94         struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
95         int ret;
96
97         ret = mv88e6xxx_setup_common(ds);
98         if (ret < 0)
99                 return ret;
100
101         ps->num_ports = 7;
102
103         mutex_init(&ps->eeprom_mutex);
104
105         ret = mv88e6xxx_switch_reset(ds, true);
106         if (ret < 0)
107                 return ret;
108
109         ret = mv88e6352_setup_global(ds);
110         if (ret < 0)
111                 return ret;
112
113         return mv88e6xxx_setup_ports(ds);
114 }
115
116 static int mv88e6352_read_eeprom_word(struct dsa_switch *ds, int addr)
117 {
118         struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
119         int ret;
120
121         mutex_lock(&ps->eeprom_mutex);
122
123         ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
124                                   GLOBAL2_EEPROM_OP_READ |
125                                   (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
126         if (ret < 0)
127                 goto error;
128
129         ret = mv88e6xxx_eeprom_busy_wait(ds);
130         if (ret < 0)
131                 goto error;
132
133         ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2, GLOBAL2_EEPROM_DATA);
134 error:
135         mutex_unlock(&ps->eeprom_mutex);
136         return ret;
137 }
138
139 static int mv88e6352_get_eeprom(struct dsa_switch *ds,
140                                 struct ethtool_eeprom *eeprom, u8 *data)
141 {
142         int offset;
143         int len;
144         int ret;
145
146         offset = eeprom->offset;
147         len = eeprom->len;
148         eeprom->len = 0;
149
150         eeprom->magic = 0xc3ec4951;
151
152         ret = mv88e6xxx_eeprom_load_wait(ds);
153         if (ret < 0)
154                 return ret;
155
156         if (offset & 1) {
157                 int word;
158
159                 word = mv88e6352_read_eeprom_word(ds, offset >> 1);
160                 if (word < 0)
161                         return word;
162
163                 *data++ = (word >> 8) & 0xff;
164
165                 offset++;
166                 len--;
167                 eeprom->len++;
168         }
169
170         while (len >= 2) {
171                 int word;
172
173                 word = mv88e6352_read_eeprom_word(ds, offset >> 1);
174                 if (word < 0)
175                         return word;
176
177                 *data++ = word & 0xff;
178                 *data++ = (word >> 8) & 0xff;
179
180                 offset += 2;
181                 len -= 2;
182                 eeprom->len += 2;
183         }
184
185         if (len) {
186                 int word;
187
188                 word = mv88e6352_read_eeprom_word(ds, offset >> 1);
189                 if (word < 0)
190                         return word;
191
192                 *data++ = word & 0xff;
193
194                 offset++;
195                 len--;
196                 eeprom->len++;
197         }
198
199         return 0;
200 }
201
202 static int mv88e6352_eeprom_is_readonly(struct dsa_switch *ds)
203 {
204         int ret;
205
206         ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP);
207         if (ret < 0)
208                 return ret;
209
210         if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN))
211                 return -EROFS;
212
213         return 0;
214 }
215
216 static int mv88e6352_write_eeprom_word(struct dsa_switch *ds, int addr,
217                                        u16 data)
218 {
219         struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
220         int ret;
221
222         mutex_lock(&ps->eeprom_mutex);
223
224         ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
225         if (ret < 0)
226                 goto error;
227
228         ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
229                                   GLOBAL2_EEPROM_OP_WRITE |
230                                   (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
231         if (ret < 0)
232                 goto error;
233
234         ret = mv88e6xxx_eeprom_busy_wait(ds);
235 error:
236         mutex_unlock(&ps->eeprom_mutex);
237         return ret;
238 }
239
240 static int mv88e6352_set_eeprom(struct dsa_switch *ds,
241                                 struct ethtool_eeprom *eeprom, u8 *data)
242 {
243         int offset;
244         int ret;
245         int len;
246
247         if (eeprom->magic != 0xc3ec4951)
248                 return -EINVAL;
249
250         ret = mv88e6352_eeprom_is_readonly(ds);
251         if (ret)
252                 return ret;
253
254         offset = eeprom->offset;
255         len = eeprom->len;
256         eeprom->len = 0;
257
258         ret = mv88e6xxx_eeprom_load_wait(ds);
259         if (ret < 0)
260                 return ret;
261
262         if (offset & 1) {
263                 int word;
264
265                 word = mv88e6352_read_eeprom_word(ds, offset >> 1);
266                 if (word < 0)
267                         return word;
268
269                 word = (*data++ << 8) | (word & 0xff);
270
271                 ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word);
272                 if (ret < 0)
273                         return ret;
274
275                 offset++;
276                 len--;
277                 eeprom->len++;
278         }
279
280         while (len >= 2) {
281                 int word;
282
283                 word = *data++;
284                 word |= *data++ << 8;
285
286                 ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word);
287                 if (ret < 0)
288                         return ret;
289
290                 offset += 2;
291                 len -= 2;
292                 eeprom->len += 2;
293         }
294
295         if (len) {
296                 int word;
297
298                 word = mv88e6352_read_eeprom_word(ds, offset >> 1);
299                 if (word < 0)
300                         return word;
301
302                 word = (word & 0xff00) | *data++;
303
304                 ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word);
305                 if (ret < 0)
306                         return ret;
307
308                 offset++;
309                 len--;
310                 eeprom->len++;
311         }
312
313         return 0;
314 }
315
316 struct dsa_switch_driver mv88e6352_switch_driver = {
317         .tag_protocol           = DSA_TAG_PROTO_EDSA,
318         .probe                  = mv88e6352_drv_probe,
319         .setup                  = mv88e6352_setup,
320         .set_addr               = mv88e6xxx_set_addr_indirect,
321         .phy_read               = mv88e6xxx_phy_read_indirect,
322         .phy_write              = mv88e6xxx_phy_write_indirect,
323         .get_strings            = mv88e6xxx_get_strings,
324         .get_ethtool_stats      = mv88e6xxx_get_ethtool_stats,
325         .get_sset_count         = mv88e6xxx_get_sset_count,
326         .adjust_link            = mv88e6xxx_adjust_link,
327         .set_eee                = mv88e6xxx_set_eee,
328         .get_eee                = mv88e6xxx_get_eee,
329 #ifdef CONFIG_NET_DSA_HWMON
330         .get_temp               = mv88e6xxx_get_temp,
331         .get_temp_limit         = mv88e6xxx_get_temp_limit,
332         .set_temp_limit         = mv88e6xxx_set_temp_limit,
333         .get_temp_alarm         = mv88e6xxx_get_temp_alarm,
334 #endif
335         .get_eeprom             = mv88e6352_get_eeprom,
336         .set_eeprom             = mv88e6352_set_eeprom,
337         .get_regs_len           = mv88e6xxx_get_regs_len,
338         .get_regs               = mv88e6xxx_get_regs,
339         .port_bridge_join       = mv88e6xxx_port_bridge_join,
340         .port_bridge_leave      = mv88e6xxx_port_bridge_leave,
341         .port_stp_state_set     = mv88e6xxx_port_stp_state_set,
342         .port_vlan_filtering    = mv88e6xxx_port_vlan_filtering,
343         .port_vlan_prepare      = mv88e6xxx_port_vlan_prepare,
344         .port_vlan_add          = mv88e6xxx_port_vlan_add,
345         .port_vlan_del          = mv88e6xxx_port_vlan_del,
346         .port_vlan_dump         = mv88e6xxx_port_vlan_dump,
347         .port_fdb_prepare       = mv88e6xxx_port_fdb_prepare,
348         .port_fdb_add           = mv88e6xxx_port_fdb_add,
349         .port_fdb_del           = mv88e6xxx_port_fdb_del,
350         .port_fdb_dump          = mv88e6xxx_port_fdb_dump,
351 };
352
353 MODULE_ALIAS("platform:mv88e6172");
354 MODULE_ALIAS("platform:mv88e6176");
355 MODULE_ALIAS("platform:mv88e6320");
356 MODULE_ALIAS("platform:mv88e6321");
357 MODULE_ALIAS("platform:mv88e6352");