mv88e6131.c 4.2 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
	int ret;

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

95
	mv88e6xxx_ppu_state_init(ds);
96

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

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

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

120
	return mv88e6xxx_setup_ports(ds);
121 122
}

123
static int mv88e6131_port_to_phy_addr(struct dsa_switch *ds, int port)
124
{
125 126 127
	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);

	if (port >= 0 && port < ps->num_ports)
128
		return port;
129 130

	return -EINVAL;
131 132 133 134 135
}

static int
mv88e6131_phy_read(struct dsa_switch *ds, int port, int regnum)
{
136 137 138 139 140
	int addr = mv88e6131_port_to_phy_addr(ds, port);

	if (addr < 0)
		return addr;

141 142 143 144 145 146 147
	return mv88e6xxx_phy_read_ppu(ds, addr, regnum);
}

static int
mv88e6131_phy_write(struct dsa_switch *ds,
			      int port, int regnum, u16 val)
{
148 149 150 151 152
	int addr = mv88e6131_port_to_phy_addr(ds, port);

	if (addr < 0)
		return addr;

153 154 155
	return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val);
}

156
struct dsa_switch_driver mv88e6131_switch_driver = {
157
	.tag_protocol		= DSA_TAG_PROTO_DSA,
158 159 160 161 162 163 164
	.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,
165 166 167
	.get_strings		= mv88e6xxx_get_strings,
	.get_ethtool_stats	= mv88e6xxx_get_ethtool_stats,
	.get_sset_count		= mv88e6xxx_get_sset_count,
168
};
169 170 171 172 173

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