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