mv88e6131.c 4.3 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 40 41 42 43 44 45 46 47
			return "Marvell 88E6131";
	}

	return NULL;
}

static int mv88e6131_setup_global(struct dsa_switch *ds)
{
	int ret;
48 49 50 51

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

53
	/* Enable the PHY polling unit, don't discard packets with
54 55 56 57 58 59
	 * 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.
	 */
	REG_WRITE(REG_GLOBAL, 0x04, 0x4400);

60
	/* Set the VLAN ethertype to 0x8100. */
61 62
	REG_WRITE(REG_GLOBAL, 0x19, 0x8100);

63
	/* Disable ARP mirroring, and configure the upstream port as
64 65
	 * the port to which ingress and egress monitor frames are to
	 * be sent.
66
	 */
67
	REG_WRITE(REG_GLOBAL, 0x1a, (dsa_upstream_port(ds) * 0x1100) | 0x00f0);
68

69
	/* Disable cascade port functionality unless this device
70
	 * is used in a cascade configuration, and set the switch's
71
	 * DSA device number.
72
	 */
73 74 75 76
	if (ds->dst->pd->nr_chips > 1)
		REG_WRITE(REG_GLOBAL, 0x1c, 0xf000 | (ds->index & 0x1f));
	else
		REG_WRITE(REG_GLOBAL, 0x1c, 0xe000 | (ds->index & 0x1f));
77

78
	/* Force the priority of IGMP/MLD snoop frames and ARP frames
79 80 81 82 83 84 85 86 87
	 * to the highest setting.
	 */
	REG_WRITE(REG_GLOBAL2, 0x0f, 0x00ff);

	return 0;
}

static int mv88e6131_setup(struct dsa_switch *ds)
{
88
	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
89 90 91
	int i;
	int ret;

92 93 94
	ret = mv88e6xxx_setup_common(ds);
	if (ret < 0)
		return ret;
95

96
	mv88e6xxx_ppu_state_init(ds);
97

98
	switch (ps->id) {
99
	case PORT_SWITCH_ID_6085:
100 101
		ps->num_ports = 10;
		break;
102
	case PORT_SWITCH_ID_6095:
103 104
		ps->num_ports = 11;
		break;
105 106
	case PORT_SWITCH_ID_6131:
	case PORT_SWITCH_ID_6131_B2:
107 108 109 110 111 112
		ps->num_ports = 8;
		break;
	default:
		return -ENODEV;
	}

113
	ret = mv88e6xxx_switch_reset(ds, false);
114 115 116 117 118 119 120
	if (ret < 0)
		return ret;

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

121
	for (i = 0; i < ps->num_ports; i++) {
122
		ret = mv88e6xxx_setup_port(ds, i);
123 124 125 126 127 128 129
		if (ret < 0)
			return ret;
	}

	return 0;
}

130
static int mv88e6131_port_to_phy_addr(struct dsa_switch *ds, int port)
131
{
132 133 134
	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);

	if (port >= 0 && port < ps->num_ports)
135
		return port;
136 137

	return -EINVAL;
138 139 140 141 142
}

static int
mv88e6131_phy_read(struct dsa_switch *ds, int port, int regnum)
{
143 144 145 146 147
	int addr = mv88e6131_port_to_phy_addr(ds, port);

	if (addr < 0)
		return addr;

148 149 150 151 152 153 154
	return mv88e6xxx_phy_read_ppu(ds, addr, regnum);
}

static int
mv88e6131_phy_write(struct dsa_switch *ds,
			      int port, int regnum, u16 val)
{
155 156 157 158 159
	int addr = mv88e6131_port_to_phy_addr(ds, port);

	if (addr < 0)
		return addr;

160 161 162
	return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val);
}

163
struct dsa_switch_driver mv88e6131_switch_driver = {
164
	.tag_protocol		= DSA_TAG_PROTO_DSA,
165 166 167 168 169 170 171
	.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,
	.poll_link		= mv88e6xxx_poll_link,
172 173 174
	.get_strings		= mv88e6xxx_get_strings,
	.get_ethtool_stats	= mv88e6xxx_get_ethtool_stats,
	.get_sset_count		= mv88e6xxx_get_sset_count,
175
};
176 177 178 179 180

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