mv88e6131.c 4.8 KB
Newer Older
1
/*
2 3
 * net/dsa/mv88e6131.c - Marvell 88e6095/6095f/6131 switch chip support
 * Copyright (c) 2008-2009 Marvell Semiconductor
4 5 6 7 8 9 10
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

11 12
#include <linux/delay.h>
#include <linux/jiffies.h>
13
#include <linux/list.h>
14
#include <linux/module.h>
15 16
#include <linux/netdevice.h>
#include <linux/phy.h>
17
#include <net/dsa.h>
18 19
#include "mv88e6xxx.h"

20
static char *mv88e6131_probe(struct device *host_dev, int sw_addr)
21
{
22
	struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev);
23 24
	int ret;

25 26 27
	if (bus == NULL)
		return NULL;

28
	ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
29
	if (ret >= 0) {
30 31
		int ret_masked = ret & 0xfff0;

32
		if (ret_masked == PORT_SWITCH_ID_6085)
33
			return "Marvell 88E6085";
34
		if (ret_masked == PORT_SWITCH_ID_6095)
35
			return "Marvell 88E6095/88E6095F";
36
		if (ret == PORT_SWITCH_ID_6131_B2)
37
			return "Marvell 88E6131 (B2)";
38
		if (ret_masked == PORT_SWITCH_ID_6131)
39
			return "Marvell 88E6131";
40 41
		if (ret_masked == PORT_SWITCH_ID_6185)
			return "Marvell 88E6185";
42 43 44 45 46 47 48
	}

	return NULL;
}

static int mv88e6131_setup_global(struct dsa_switch *ds)
{
49
	u32 upstream_port = dsa_upstream_port(ds);
50
	int ret;
51
	u32 reg;
52 53 54 55

	ret = mv88e6xxx_setup_global(ds);
	if (ret)
		return ret;
56

57
	/* Enable the PHY polling unit, don't discard packets with
58 59 60 61
	 * excessive collisions, use a weighted fair queueing scheme
	 * to arbitrate between packet queues, set the maximum frame
	 * size to 1632, and mask all interrupt sources.
	 */
62 63
	REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL,
		  GLOBAL_CONTROL_PPU_ENABLE | GLOBAL_CONTROL_MAX_FRAME_1632);
64

65
	/* Set the VLAN ethertype to 0x8100. */
66
	REG_WRITE(REG_GLOBAL, GLOBAL_CORE_TAG_TYPE, 0x8100);
67

68
	/* Disable ARP mirroring, and configure the upstream port as
69 70
	 * the port to which ingress and egress monitor frames are to
	 * be sent.
71
	 */
72 73 74 75
	reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
		upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
		GLOBAL_MONITOR_CONTROL_ARP_DISABLED;
	REG_WRITE(REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg);
76

77
	/* Disable cascade port functionality unless this device
78
	 * is used in a cascade configuration, and set the switch's
79
	 * DSA device number.
80
	 */
81
	if (ds->dst->pd->nr_chips > 1)
82 83 84
		REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL_2,
			  GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
			  (ds->index & 0x1f));
85
	else
86 87 88
		REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL_2,
			  GLOBAL_CONTROL_2_NO_CASCADE |
			  (ds->index & 0x1f));
89

90
	/* Force the priority of IGMP/MLD snoop frames and ARP frames
91 92
	 * to the highest setting.
	 */
93 94 95 96 97
	REG_WRITE(REG_GLOBAL2, GLOBAL2_PRIO_OVERRIDE,
		  GLOBAL2_PRIO_OVERRIDE_FORCE_SNOOP |
		  7 << GLOBAL2_PRIO_OVERRIDE_SNOOP_SHIFT |
		  GLOBAL2_PRIO_OVERRIDE_FORCE_ARP |
		  7 << GLOBAL2_PRIO_OVERRIDE_ARP_SHIFT);
98 99 100 101 102 103

	return 0;
}

static int mv88e6131_setup(struct dsa_switch *ds)
{
104
	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
105 106
	int ret;

107 108 109
	ret = mv88e6xxx_setup_common(ds);
	if (ret < 0)
		return ret;
110

111
	mv88e6xxx_ppu_state_init(ds);
112

113
	switch (ps->id) {
114
	case PORT_SWITCH_ID_6085:
115
	case PORT_SWITCH_ID_6185:
116 117
		ps->num_ports = 10;
		break;
118
	case PORT_SWITCH_ID_6095:
119 120
		ps->num_ports = 11;
		break;
121 122
	case PORT_SWITCH_ID_6131:
	case PORT_SWITCH_ID_6131_B2:
123 124 125 126 127 128
		ps->num_ports = 8;
		break;
	default:
		return -ENODEV;
	}

129
	ret = mv88e6xxx_switch_reset(ds, false);
130 131 132 133 134 135 136
	if (ret < 0)
		return ret;

	ret = mv88e6131_setup_global(ds);
	if (ret < 0)
		return ret;

137
	return mv88e6xxx_setup_ports(ds);
138 139
}

140
static int mv88e6131_port_to_phy_addr(struct dsa_switch *ds, int port)
141
{
142 143 144
	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);

	if (port >= 0 && port < ps->num_ports)
145
		return port;
146 147

	return -EINVAL;
148 149 150 151 152
}

static int
mv88e6131_phy_read(struct dsa_switch *ds, int port, int regnum)
{
153 154 155 156 157
	int addr = mv88e6131_port_to_phy_addr(ds, port);

	if (addr < 0)
		return addr;

158 159 160 161 162 163 164
	return mv88e6xxx_phy_read_ppu(ds, addr, regnum);
}

static int
mv88e6131_phy_write(struct dsa_switch *ds,
			      int port, int regnum, u16 val)
{
165 166 167 168 169
	int addr = mv88e6131_port_to_phy_addr(ds, port);

	if (addr < 0)
		return addr;

170 171 172
	return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val);
}

173
struct dsa_switch_driver mv88e6131_switch_driver = {
174
	.tag_protocol		= DSA_TAG_PROTO_DSA,
175 176 177 178 179 180
	.priv_size		= sizeof(struct mv88e6xxx_priv_state),
	.probe			= mv88e6131_probe,
	.setup			= mv88e6131_setup,
	.set_addr		= mv88e6xxx_set_addr_direct,
	.phy_read		= mv88e6131_phy_read,
	.phy_write		= mv88e6131_phy_write,
181 182 183
	.get_strings		= mv88e6xxx_get_strings,
	.get_ethtool_stats	= mv88e6xxx_get_ethtool_stats,
	.get_sset_count		= mv88e6xxx_get_sset_count,
184
	.adjust_link		= mv88e6xxx_adjust_link,
185
};
186 187 188 189 190

MODULE_ALIAS("platform:mv88e6085");
MODULE_ALIAS("platform:mv88e6095");
MODULE_ALIAS("platform:mv88e6095f");
MODULE_ALIAS("platform:mv88e6131");