Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 25static 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
 40static 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
 46static 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
 79static 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
103static 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);
121error:
122	mutex_unlock(&ps->eeprom_mutex);
123	return ret;
124}
125
126static 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
189static 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
203static 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);
222error:
223	mutex_unlock(&ps->eeprom_mutex);
224	return ret;
225}
226
227static 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
303struct 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_bridge_join	= mv88e6xxx_port_bridge_join,
328	.port_bridge_leave	= mv88e6xxx_port_bridge_leave,
329	.port_stp_update	= mv88e6xxx_port_stp_update,
330	.port_vlan_filtering	= mv88e6xxx_port_vlan_filtering,
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	.port_vlan_dump		= mv88e6xxx_port_vlan_dump,
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
341MODULE_ALIAS("platform:mv88e6172");
342MODULE_ALIAS("platform:mv88e6176");
343MODULE_ALIAS("platform:mv88e6320");
344MODULE_ALIAS("platform:mv88e6321");
345MODULE_ALIAS("platform:mv88e6352");