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