blob: 2f72606ecc9ef18cb4d3a9b492ba4ecceb10d6b0 [file] [log] [blame]
Guenter Roeck3ad50cc2014-10-29 10:44:56 -07001/*
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
Vivien Didelotf6271e62016-04-17 13:23:59 -040025static 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 }
Vivien Didelotb9b37712015-10-30 19:39:48 -040045};
46
Vivien Didelot0209d142016-04-17 13:23:55 -040047static const char *mv88e6352_drv_probe(struct device *dsa_dev,
48 struct device *host_dev, int sw_addr,
49 void **priv)
Guenter Roeck3ad50cc2014-10-29 10:44:56 -070050{
Andrew Lunna77d43f2016-04-13 02:40:42 +020051 return mv88e6xxx_drv_probe(dsa_dev, host_dev, sw_addr, priv,
52 mv88e6352_table,
53 ARRAY_SIZE(mv88e6352_table));
Guenter Roeck3ad50cc2014-10-29 10:44:56 -070054}
55
Guenter Roeck3ad50cc2014-10-29 10:44:56 -070056static int mv88e6352_setup_global(struct dsa_switch *ds)
57{
Andrew Lunn15966a22015-05-06 01:09:49 +020058 u32 upstream_port = dsa_upstream_port(ds);
Guenter Roeck3ad50cc2014-10-29 10:44:56 -070059 int ret;
Andrew Lunn15966a22015-05-06 01:09:49 +020060 u32 reg;
Andrew Lunn54d792f2015-05-06 01:09:47 +020061
62 ret = mv88e6xxx_setup_global(ds);
63 if (ret)
64 return ret;
Guenter Roeck3ad50cc2014-10-29 10:44:56 -070065
66 /* Discard packets with excessive collisions,
67 * mask all interrupt sources, enable PPU (bit 14, undocumented).
68 */
Andrew Lunn48ace4e2016-04-14 23:47:12 +020069 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;
Guenter Roeck3ad50cc2014-10-29 10:44:56 -070074
Guenter Roeck3ad50cc2014-10-29 10:44:56 -070075 /* 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 */
Andrew Lunn15966a22015-05-06 01:09:49 +020079 reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
80 upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
81 upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
Andrew Lunn48ace4e2016-04-14 23:47:12 +020082 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg);
83 if (ret)
84 return ret;
Guenter Roeck3ad50cc2014-10-29 10:44:56 -070085
86 /* Disable remote management for now, and set the switch's
87 * DSA device number.
88 */
Andrew Lunn48ace4e2016-04-14 23:47:12 +020089 return mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x1c, ds->index & 0x1f);
Guenter Roeck3ad50cc2014-10-29 10:44:56 -070090}
91
Guenter Roeck3ad50cc2014-10-29 10:44:56 -070092static int mv88e6352_setup(struct dsa_switch *ds)
93{
94 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
95 int ret;
Guenter Roeck3ad50cc2014-10-29 10:44:56 -070096
Guenter Roeckacdaffc2015-03-26 18:36:28 -070097 ret = mv88e6xxx_setup_common(ds);
98 if (ret < 0)
99 return ret;
100
Andrew Lunn44e50dd2015-04-02 04:06:33 +0200101 ps->num_ports = 7;
102
Guenter Roeck33b43df2014-10-29 10:45:03 -0700103 mutex_init(&ps->eeprom_mutex);
Guenter Roeck3ad50cc2014-10-29 10:44:56 -0700104
Andrew Lunn143a8302015-04-02 04:06:34 +0200105 ret = mv88e6xxx_switch_reset(ds, true);
Guenter Roeck3ad50cc2014-10-29 10:44:56 -0700106 if (ret < 0)
107 return ret;
108
Guenter Roeck3ad50cc2014-10-29 10:44:56 -0700109 ret = mv88e6352_setup_global(ds);
110 if (ret < 0)
111 return ret;
112
Andrew Lunndbde9e62015-05-06 01:09:48 +0200113 return mv88e6xxx_setup_ports(ds);
Guenter Roeck3ad50cc2014-10-29 10:44:56 -0700114}
115
Guenter Roeck33b43df2014-10-29 10:45:03 -0700116static 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
Andrew Lunn966bce32015-08-08 17:04:50 +0200123 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
124 GLOBAL2_EEPROM_OP_READ |
125 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
Guenter Roeck33b43df2014-10-29 10:45:03 -0700126 if (ret < 0)
127 goto error;
128
Andrew Lunnf3044682015-02-14 19:17:50 +0100129 ret = mv88e6xxx_eeprom_busy_wait(ds);
Guenter Roeck33b43df2014-10-29 10:45:03 -0700130 if (ret < 0)
131 goto error;
132
Andrew Lunn966bce32015-08-08 17:04:50 +0200133 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2, GLOBAL2_EEPROM_DATA);
Guenter Roeck33b43df2014-10-29 10:45:03 -0700134error:
135 mutex_unlock(&ps->eeprom_mutex);
136 return ret;
137}
138
139static 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
Andrew Lunnf3044682015-02-14 19:17:50 +0100152 ret = mv88e6xxx_eeprom_load_wait(ds);
Guenter Roeck33b43df2014-10-29 10:45:03 -0700153 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
202static int mv88e6352_eeprom_is_readonly(struct dsa_switch *ds)
203{
204 int ret;
205
Andrew Lunn966bce32015-08-08 17:04:50 +0200206 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP);
Guenter Roeck33b43df2014-10-29 10:45:03 -0700207 if (ret < 0)
208 return ret;
209
Andrew Lunn966bce32015-08-08 17:04:50 +0200210 if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN))
Guenter Roeck33b43df2014-10-29 10:45:03 -0700211 return -EROFS;
212
213 return 0;
214}
215
216static 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
Andrew Lunn966bce32015-08-08 17:04:50 +0200224 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
Guenter Roeck33b43df2014-10-29 10:45:03 -0700225 if (ret < 0)
226 goto error;
227
Andrew Lunn966bce32015-08-08 17:04:50 +0200228 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
229 GLOBAL2_EEPROM_OP_WRITE |
230 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
Guenter Roeck33b43df2014-10-29 10:45:03 -0700231 if (ret < 0)
232 goto error;
233
Andrew Lunnf3044682015-02-14 19:17:50 +0100234 ret = mv88e6xxx_eeprom_busy_wait(ds);
Guenter Roeck33b43df2014-10-29 10:45:03 -0700235error:
236 mutex_unlock(&ps->eeprom_mutex);
237 return ret;
238}
239
240static 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
Andrew Lunnf3044682015-02-14 19:17:50 +0100258 ret = mv88e6xxx_eeprom_load_wait(ds);
Guenter Roeck33b43df2014-10-29 10:45:03 -0700259 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
Guenter Roeck3ad50cc2014-10-29 10:44:56 -0700316struct dsa_switch_driver mv88e6352_switch_driver = {
317 .tag_protocol = DSA_TAG_PROTO_EDSA,
Andrew Lunne49bad32016-04-13 02:40:43 +0200318 .probe = mv88e6352_drv_probe,
Guenter Roeck3ad50cc2014-10-29 10:44:56 -0700319 .setup = mv88e6352_setup,
320 .set_addr = mv88e6xxx_set_addr_indirect,
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200321 .phy_read = mv88e6xxx_phy_read_indirect,
322 .phy_write = mv88e6xxx_phy_write_indirect,
Andrew Lunne413e7e2015-04-02 04:06:38 +0200323 .get_strings = mv88e6xxx_get_strings,
324 .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
325 .get_sset_count = mv88e6xxx_get_sset_count,
Andrew Lunndea87022015-08-31 15:56:47 +0200326 .adjust_link = mv88e6xxx_adjust_link,
Guenter Roeck04b0a802015-03-06 22:23:52 -0800327 .set_eee = mv88e6xxx_set_eee,
328 .get_eee = mv88e6xxx_get_eee,
Guenter Roeck276db3b2014-10-29 10:44:59 -0700329#ifdef CONFIG_NET_DSA_HWMON
Guenter Roeckc22995c2015-07-25 09:42:28 -0700330 .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,
Guenter Roeck276db3b2014-10-29 10:44:59 -0700334#endif
Guenter Roeck33b43df2014-10-29 10:45:03 -0700335 .get_eeprom = mv88e6352_get_eeprom,
336 .set_eeprom = mv88e6352_set_eeprom,
Guenter Roeck95d08b52014-10-29 10:45:06 -0700337 .get_regs_len = mv88e6xxx_get_regs_len,
338 .get_regs = mv88e6xxx_get_regs,
Vivien Didelot71327a42016-03-13 16:21:32 -0400339 .port_bridge_join = mv88e6xxx_port_bridge_join,
340 .port_bridge_leave = mv88e6xxx_port_bridge_leave,
Vivien Didelot43c44a92016-04-06 11:55:03 -0400341 .port_stp_state_set = mv88e6xxx_port_stp_state_set,
Vivien Didelot214cdb92016-02-26 13:16:08 -0500342 .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
Vivien Didelot76e398a2015-11-01 12:33:55 -0500343 .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -0400344 .port_vlan_add = mv88e6xxx_port_vlan_add,
Vivien Didelot7dad08d2015-08-13 12:52:21 -0400345 .port_vlan_del = mv88e6xxx_port_vlan_del,
Vivien Didelotceff5ef2016-02-23 12:13:55 -0500346 .port_vlan_dump = mv88e6xxx_port_vlan_dump,
Vivien Didelot146a3202015-10-08 11:35:12 -0400347 .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
Vivien Didelot2a778e12015-08-10 09:09:49 -0400348 .port_fdb_add = mv88e6xxx_port_fdb_add,
349 .port_fdb_del = mv88e6xxx_port_fdb_del,
Vivien Didelotf33475b2015-10-22 09:34:41 -0400350 .port_fdb_dump = mv88e6xxx_port_fdb_dump,
Guenter Roeck3ad50cc2014-10-29 10:44:56 -0700351};
352
Andrew Lunn1636d882015-05-06 01:09:50 +0200353MODULE_ALIAS("platform:mv88e6172");
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700354MODULE_ALIAS("platform:mv88e6176");
355MODULE_ALIAS("platform:mv88e6320");
356MODULE_ALIAS("platform:mv88e6321");
357MODULE_ALIAS("platform:mv88e6352");