net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
/*
|
2016-06-22 00:28:19 +08:00
|
|
|
* Marvell 88e6xxx Ethernet switch single-chip support
|
|
|
|
*
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
* Copyright (c) 2008 Marvell Semiconductor
|
|
|
|
*
|
2015-08-14 00:52:19 +08:00
|
|
|
* Copyright (c) 2015 CMC Electronics, Inc.
|
|
|
|
* Added support for VLAN Table Unit operations
|
|
|
|
*
|
2016-05-11 05:27:21 +08:00
|
|
|
* Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch>
|
|
|
|
*
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2013-01-09 00:05:54 +08:00
|
|
|
#include <linux/delay.h>
|
2015-03-27 09:36:38 +08:00
|
|
|
#include <linux/etherdevice.h>
|
2015-08-31 21:56:47 +08:00
|
|
|
#include <linux/ethtool.h>
|
2015-03-27 09:36:35 +08:00
|
|
|
#include <linux/if_bridge.h>
|
2016-10-17 01:56:49 +08:00
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/irq.h>
|
|
|
|
#include <linux/irqdomain.h>
|
2013-01-09 00:05:54 +08:00
|
|
|
#include <linux/jiffies.h>
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
#include <linux/list.h>
|
2016-05-11 05:27:21 +08:00
|
|
|
#include <linux/mdio.h>
|
2012-01-24 18:41:40 +08:00
|
|
|
#include <linux/module.h>
|
2016-06-21 01:14:09 +08:00
|
|
|
#include <linux/of_device.h>
|
2016-10-17 01:56:49 +08:00
|
|
|
#include <linux/of_irq.h>
|
2016-06-05 03:17:06 +08:00
|
|
|
#include <linux/of_mdio.h>
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
#include <linux/netdevice.h>
|
2015-11-20 10:56:24 +08:00
|
|
|
#include <linux/gpio/consumer.h>
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
#include <linux/phy.h>
|
2011-11-28 01:06:08 +08:00
|
|
|
#include <net/dsa.h>
|
2015-10-08 23:35:13 +08:00
|
|
|
#include <net/switchdev.h>
|
2016-09-03 02:45:33 +08:00
|
|
|
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
#include "mv88e6xxx.h"
|
2016-09-30 00:21:53 +08:00
|
|
|
#include "global1.h"
|
2016-09-03 02:45:33 +08:00
|
|
|
#include "global2.h"
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static void assert_reg_lock(struct mv88e6xxx_chip *chip)
|
2015-10-31 06:56:45 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
if (unlikely(!mutex_is_locked(&chip->reg_lock))) {
|
|
|
|
dev_err(chip->dev, "Switch registers lock not held!\n");
|
2015-10-31 06:56:45 +08:00
|
|
|
dump_stack();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-21 01:14:11 +08:00
|
|
|
/* The switch ADDR[4:1] configuration pins define the chip SMI device address
|
|
|
|
* (ADDR[0] is always zero, thus only even SMI addresses can be strapped).
|
|
|
|
*
|
|
|
|
* When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it
|
|
|
|
* is the only device connected to the SMI master. In this mode it responds to
|
|
|
|
* all 32 possible SMI addresses, and thus maps directly the internal devices.
|
|
|
|
*
|
|
|
|
* When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing
|
|
|
|
* multiple devices to share the SMI interface. In this mode it responds to only
|
|
|
|
* 2 registers, used to indirectly access the internal SMI devices.
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
*/
|
2016-06-21 01:14:11 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_smi_read(struct mv88e6xxx_chip *chip,
|
2016-06-21 01:14:11 +08:00
|
|
|
int addr, int reg, u16 *val)
|
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
if (!chip->smi_ops)
|
2016-06-21 01:14:11 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
return chip->smi_ops->read(chip, addr, reg, val);
|
2016-06-21 01:14:11 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_smi_write(struct mv88e6xxx_chip *chip,
|
2016-06-21 01:14:11 +08:00
|
|
|
int addr, int reg, u16 val)
|
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
if (!chip->smi_ops)
|
2016-06-21 01:14:11 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
return chip->smi_ops->write(chip, addr, reg, val);
|
2016-06-21 01:14:11 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_smi_single_chip_read(struct mv88e6xxx_chip *chip,
|
2016-06-21 01:14:11 +08:00
|
|
|
int addr, int reg, u16 *val)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
ret = mdiobus_read_nested(chip->bus, addr, reg);
|
2016-06-21 01:14:11 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
*val = ret & 0xffff;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_smi_single_chip_write(struct mv88e6xxx_chip *chip,
|
2016-06-21 01:14:11 +08:00
|
|
|
int addr, int reg, u16 val)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
ret = mdiobus_write_nested(chip->bus, addr, reg, val);
|
2016-06-21 01:14:11 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-30 00:21:59 +08:00
|
|
|
static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_single_chip_ops = {
|
2016-06-21 01:14:11 +08:00
|
|
|
.read = mv88e6xxx_smi_single_chip_read,
|
|
|
|
.write = mv88e6xxx_smi_single_chip_write,
|
|
|
|
};
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_smi_multi_chip_wait(struct mv88e6xxx_chip *chip)
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 16; i++) {
|
2016-06-22 00:28:20 +08:00
|
|
|
ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_CMD);
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2015-04-02 10:06:39 +08:00
|
|
|
if ((ret & SMI_CMD_BUSY) == 0)
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_smi_multi_chip_read(struct mv88e6xxx_chip *chip,
|
2016-06-21 01:14:11 +08:00
|
|
|
int addr, int reg, u16 *val)
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2013-01-09 00:05:53 +08:00
|
|
|
/* Wait for the bus to become free. */
|
2016-06-22 00:28:20 +08:00
|
|
|
ret = mv88e6xxx_smi_multi_chip_wait(chip);
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2013-01-09 00:05:53 +08:00
|
|
|
/* Transmit the read command. */
|
2016-06-22 00:28:20 +08:00
|
|
|
ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD,
|
2015-10-22 16:37:53 +08:00
|
|
|
SMI_CMD_OP_22_READ | (addr << 5) | reg);
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2013-01-09 00:05:53 +08:00
|
|
|
/* Wait for the read command to complete. */
|
2016-06-22 00:28:20 +08:00
|
|
|
ret = mv88e6xxx_smi_multi_chip_wait(chip);
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2013-01-09 00:05:53 +08:00
|
|
|
/* Read the data. */
|
2016-06-22 00:28:20 +08:00
|
|
|
ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_DATA);
|
2015-01-24 05:10:36 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2016-06-21 01:14:11 +08:00
|
|
|
*val = ret & 0xffff;
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
|
2016-06-21 01:14:11 +08:00
|
|
|
return 0;
|
2015-03-27 09:36:31 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_smi_multi_chip_write(struct mv88e6xxx_chip *chip,
|
2016-06-21 01:14:11 +08:00
|
|
|
int addr, int reg, u16 val)
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2013-01-09 00:05:53 +08:00
|
|
|
/* Wait for the bus to become free. */
|
2016-06-22 00:28:20 +08:00
|
|
|
ret = mv88e6xxx_smi_multi_chip_wait(chip);
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2013-01-09 00:05:53 +08:00
|
|
|
/* Transmit the data to write. */
|
2016-06-22 00:28:20 +08:00
|
|
|
ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_DATA, val);
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2013-01-09 00:05:53 +08:00
|
|
|
/* Transmit the write command. */
|
2016-06-22 00:28:20 +08:00
|
|
|
ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD,
|
2015-10-22 16:37:53 +08:00
|
|
|
SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2013-01-09 00:05:53 +08:00
|
|
|
/* Wait for the write command to complete. */
|
2016-06-22 00:28:20 +08:00
|
|
|
ret = mv88e6xxx_smi_multi_chip_wait(chip);
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-30 00:21:59 +08:00
|
|
|
static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_multi_chip_ops = {
|
2016-06-21 01:14:11 +08:00
|
|
|
.read = mv88e6xxx_smi_multi_chip_read,
|
|
|
|
.write = mv88e6xxx_smi_multi_chip_write,
|
|
|
|
};
|
|
|
|
|
2016-09-03 02:45:33 +08:00
|
|
|
int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val)
|
2016-06-21 01:14:11 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
assert_reg_lock(chip);
|
2016-06-21 01:14:11 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = mv88e6xxx_smi_read(chip, addr, reg, val);
|
2016-06-21 01:14:11 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
dev_dbg(chip->dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
|
2016-06-21 01:14:11 +08:00
|
|
|
addr, reg, *val);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-03 02:45:33 +08:00
|
|
|
int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val)
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
{
|
2016-06-21 01:14:11 +08:00
|
|
|
int err;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
assert_reg_lock(chip);
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = mv88e6xxx_smi_write(chip, addr, reg, val);
|
2016-06-21 01:14:11 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
dev_dbg(chip->dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
|
2015-01-24 05:10:36 +08:00
|
|
|
addr, reg, val);
|
|
|
|
|
2016-06-21 01:14:11 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-25 23:43:02 +08:00
|
|
|
static int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
|
|
|
|
u16 *val)
|
2016-09-21 07:40:31 +08:00
|
|
|
{
|
|
|
|
int addr = chip->info->port_base_addr + port;
|
|
|
|
|
|
|
|
return mv88e6xxx_read(chip, addr, reg, val);
|
|
|
|
}
|
|
|
|
|
2016-09-25 23:43:02 +08:00
|
|
|
static int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
|
|
|
|
u16 val)
|
2016-09-21 07:40:31 +08:00
|
|
|
{
|
|
|
|
int addr = chip->info->port_base_addr + port;
|
|
|
|
|
|
|
|
return mv88e6xxx_write(chip, addr, reg, val);
|
|
|
|
}
|
|
|
|
|
2016-08-16 05:19:00 +08:00
|
|
|
static int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy,
|
|
|
|
int reg, u16 *val)
|
|
|
|
{
|
|
|
|
int addr = phy; /* PHY devices addresses start at 0x0 */
|
|
|
|
|
2016-09-30 00:22:00 +08:00
|
|
|
if (!chip->info->ops->phy_read)
|
2016-08-16 05:19:00 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2016-09-30 00:22:00 +08:00
|
|
|
return chip->info->ops->phy_read(chip, addr, reg, val);
|
2016-08-16 05:19:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy,
|
|
|
|
int reg, u16 val)
|
|
|
|
{
|
|
|
|
int addr = phy; /* PHY devices addresses start at 0x0 */
|
|
|
|
|
2016-09-30 00:22:00 +08:00
|
|
|
if (!chip->info->ops->phy_write)
|
2016-08-16 05:19:00 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2016-09-30 00:22:00 +08:00
|
|
|
return chip->info->ops->phy_write(chip, addr, reg, val);
|
2016-08-16 05:19:00 +08:00
|
|
|
}
|
|
|
|
|
2016-08-16 05:19:01 +08:00
|
|
|
static int mv88e6xxx_phy_page_get(struct mv88e6xxx_chip *chip, int phy, u8 page)
|
|
|
|
{
|
|
|
|
if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_PHY_PAGE))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return mv88e6xxx_phy_write(chip, phy, PHY_PAGE, page);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mv88e6xxx_phy_page_put(struct mv88e6xxx_chip *chip, int phy)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* Restore PHY page Copper 0x0 for access via the registered MDIO bus */
|
|
|
|
err = mv88e6xxx_phy_write(chip, phy, PHY_PAGE, PHY_PAGE_COPPER);
|
|
|
|
if (unlikely(err)) {
|
|
|
|
dev_err(chip->dev, "failed to restore PHY %d page Copper (%d)\n",
|
|
|
|
phy, err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mv88e6xxx_phy_page_read(struct mv88e6xxx_chip *chip, int phy,
|
|
|
|
u8 page, int reg, u16 *val)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* There is no paging for registers 22 */
|
|
|
|
if (reg == PHY_PAGE)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
err = mv88e6xxx_phy_page_get(chip, phy, page);
|
|
|
|
if (!err) {
|
|
|
|
err = mv88e6xxx_phy_read(chip, phy, reg, val);
|
|
|
|
mv88e6xxx_phy_page_put(chip, phy);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy,
|
|
|
|
u8 page, int reg, u16 val)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* There is no paging for registers 22 */
|
|
|
|
if (reg == PHY_PAGE)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
err = mv88e6xxx_phy_page_get(chip, phy, page);
|
|
|
|
if (!err) {
|
|
|
|
err = mv88e6xxx_phy_write(chip, phy, PHY_PAGE, page);
|
|
|
|
mv88e6xxx_phy_page_put(chip, phy);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mv88e6xxx_serdes_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
|
|
|
|
{
|
|
|
|
return mv88e6xxx_phy_page_read(chip, ADDR_SERDES, SERDES_PAGE_FIBER,
|
|
|
|
reg, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mv88e6xxx_serdes_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
|
|
|
|
{
|
|
|
|
return mv88e6xxx_phy_page_write(chip, ADDR_SERDES, SERDES_PAGE_FIBER,
|
|
|
|
reg, val);
|
|
|
|
}
|
|
|
|
|
2016-10-17 01:56:49 +08:00
|
|
|
static void mv88e6xxx_g1_irq_mask(struct irq_data *d)
|
|
|
|
{
|
|
|
|
struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
|
|
|
|
unsigned int n = d->hwirq;
|
|
|
|
|
|
|
|
chip->g1_irq.masked |= (1 << n);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mv88e6xxx_g1_irq_unmask(struct irq_data *d)
|
|
|
|
{
|
|
|
|
struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
|
|
|
|
unsigned int n = d->hwirq;
|
|
|
|
|
|
|
|
chip->g1_irq.masked &= ~(1 << n);
|
|
|
|
}
|
|
|
|
|
|
|
|
static irqreturn_t mv88e6xxx_g1_irq_thread_fn(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct mv88e6xxx_chip *chip = dev_id;
|
|
|
|
unsigned int nhandled = 0;
|
|
|
|
unsigned int sub_irq;
|
|
|
|
unsigned int n;
|
|
|
|
u16 reg;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, ®);
|
|
|
|
mutex_unlock(&chip->reg_lock);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
for (n = 0; n < chip->g1_irq.nirqs; ++n) {
|
|
|
|
if (reg & (1 << n)) {
|
|
|
|
sub_irq = irq_find_mapping(chip->g1_irq.domain, n);
|
|
|
|
handle_nested_irq(sub_irq);
|
|
|
|
++nhandled;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mv88e6xxx_g1_irq_bus_lock(struct irq_data *d)
|
|
|
|
{
|
|
|
|
struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
|
|
|
|
|
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mv88e6xxx_g1_irq_bus_sync_unlock(struct irq_data *d)
|
|
|
|
{
|
|
|
|
struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
|
|
|
|
u16 mask = GENMASK(chip->g1_irq.nirqs, 0);
|
|
|
|
u16 reg;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, ®);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
reg &= ~mask;
|
|
|
|
reg |= (~chip->g1_irq.masked & mask);
|
|
|
|
|
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&chip->reg_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct irq_chip mv88e6xxx_g1_irq_chip = {
|
|
|
|
.name = "mv88e6xxx-g1",
|
|
|
|
.irq_mask = mv88e6xxx_g1_irq_mask,
|
|
|
|
.irq_unmask = mv88e6xxx_g1_irq_unmask,
|
|
|
|
.irq_bus_lock = mv88e6xxx_g1_irq_bus_lock,
|
|
|
|
.irq_bus_sync_unlock = mv88e6xxx_g1_irq_bus_sync_unlock,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int mv88e6xxx_g1_irq_domain_map(struct irq_domain *d,
|
|
|
|
unsigned int irq,
|
|
|
|
irq_hw_number_t hwirq)
|
|
|
|
{
|
|
|
|
struct mv88e6xxx_chip *chip = d->host_data;
|
|
|
|
|
|
|
|
irq_set_chip_data(irq, d->host_data);
|
|
|
|
irq_set_chip_and_handler(irq, &chip->g1_irq.chip, handle_level_irq);
|
|
|
|
irq_set_noprobe(irq);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct irq_domain_ops mv88e6xxx_g1_irq_domain_ops = {
|
|
|
|
.map = mv88e6xxx_g1_irq_domain_map,
|
|
|
|
.xlate = irq_domain_xlate_twocell,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
|
|
|
|
{
|
|
|
|
int irq, virq;
|
|
|
|
|
|
|
|
for (irq = 0; irq < 16; irq++) {
|
|
|
|
virq = irq_find_mapping(chip->g2_irq.domain, irq);
|
|
|
|
irq_dispose_mapping(virq);
|
|
|
|
}
|
|
|
|
|
|
|
|
irq_domain_remove(chip->g2_irq.domain);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
|
|
|
|
{
|
|
|
|
int err, irq;
|
|
|
|
u16 reg;
|
|
|
|
|
|
|
|
chip->g1_irq.nirqs = chip->info->g1_irqs;
|
|
|
|
chip->g1_irq.domain = irq_domain_add_simple(
|
|
|
|
NULL, chip->g1_irq.nirqs, 0,
|
|
|
|
&mv88e6xxx_g1_irq_domain_ops, chip);
|
|
|
|
if (!chip->g1_irq.domain)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
for (irq = 0; irq < chip->g1_irq.nirqs; irq++)
|
|
|
|
irq_create_mapping(chip->g1_irq.domain, irq);
|
|
|
|
|
|
|
|
chip->g1_irq.chip = mv88e6xxx_g1_irq_chip;
|
|
|
|
chip->g1_irq.masked = ~0;
|
|
|
|
|
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, ®);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
reg &= ~GENMASK(chip->g1_irq.nirqs, 0);
|
|
|
|
|
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Reading the interrupt status clears (most of) them */
|
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, ®);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = request_threaded_irq(chip->irq, NULL,
|
|
|
|
mv88e6xxx_g1_irq_thread_fn,
|
|
|
|
IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
|
|
|
|
dev_name(chip->dev), chip);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
mv88e6xxx_g1_irq_free(chip);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-09-03 02:45:33 +08:00
|
|
|
int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask)
|
2016-08-16 05:18:57 +08:00
|
|
|
{
|
2016-08-19 06:01:55 +08:00
|
|
|
int i;
|
2016-08-16 05:18:57 +08:00
|
|
|
|
2016-08-19 06:01:55 +08:00
|
|
|
for (i = 0; i < 16; i++) {
|
2016-08-16 05:18:57 +08:00
|
|
|
u16 val;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = mv88e6xxx_read(chip, addr, reg, &val);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (!(val & mask))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
usleep_range(1000, 2000);
|
|
|
|
}
|
|
|
|
|
2016-08-19 06:01:57 +08:00
|
|
|
dev_err(chip->dev, "Timeout while waiting for switch\n");
|
2016-08-16 05:18:57 +08:00
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
2016-07-19 08:45:31 +08:00
|
|
|
/* Indirect write to single pointer-data register with an Update bit */
|
2016-09-03 02:45:33 +08:00
|
|
|
int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, u16 update)
|
2016-07-19 08:45:31 +08:00
|
|
|
{
|
|
|
|
u16 val;
|
2016-08-19 06:01:56 +08:00
|
|
|
int err;
|
2016-07-19 08:45:31 +08:00
|
|
|
|
|
|
|
/* Wait until the previous operation is completed */
|
2016-08-19 06:01:56 +08:00
|
|
|
err = mv88e6xxx_wait(chip, addr, reg, BIT(15));
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-07-19 08:45:31 +08:00
|
|
|
|
|
|
|
/* Set the Update bit to trigger a write operation */
|
|
|
|
val = BIT(15) | update;
|
|
|
|
|
|
|
|
return mv88e6xxx_write(chip, addr, reg, val);
|
|
|
|
}
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
static int mv88e6xxx_ppu_disable(struct mv88e6xxx_chip *chip)
|
2016-06-21 01:14:11 +08:00
|
|
|
{
|
|
|
|
u16 val;
|
2016-09-30 00:21:53 +08:00
|
|
|
int i, err;
|
2016-06-21 01:14:11 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
|
2016-06-21 01:14:11 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL,
|
|
|
|
val & ~GLOBAL_CONTROL_PPU_ENABLE);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2008-10-07 21:45:18 +08:00
|
|
|
|
2016-08-19 06:01:55 +08:00
|
|
|
for (i = 0; i < 16; i++) {
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-04-15 05:47:12 +08:00
|
|
|
|
2013-01-09 00:05:54 +08:00
|
|
|
usleep_range(1000, 2000);
|
2016-09-30 00:21:53 +08:00
|
|
|
if ((val & GLOBAL_STATUS_PPU_MASK) != GLOBAL_STATUS_PPU_POLLING)
|
2013-01-09 00:05:56 +08:00
|
|
|
return 0;
|
2008-10-07 21:45:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_ppu_enable(struct mv88e6xxx_chip *chip)
|
2008-10-07 21:45:18 +08:00
|
|
|
{
|
2016-09-30 00:21:53 +08:00
|
|
|
u16 val;
|
|
|
|
int i, err;
|
2008-10-07 21:45:18 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-04-15 05:47:12 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL,
|
|
|
|
val | GLOBAL_CONTROL_PPU_ENABLE);
|
2016-04-15 05:47:12 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
2008-10-07 21:45:18 +08:00
|
|
|
|
2016-08-19 06:01:55 +08:00
|
|
|
for (i = 0; i < 16; i++) {
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-04-15 05:47:12 +08:00
|
|
|
|
2013-01-09 00:05:54 +08:00
|
|
|
usleep_range(1000, 2000);
|
2016-09-30 00:21:53 +08:00
|
|
|
if ((val & GLOBAL_STATUS_PPU_MASK) == GLOBAL_STATUS_PPU_POLLING)
|
2013-01-09 00:05:56 +08:00
|
|
|
return 0;
|
2008-10-07 21:45:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
|
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
struct mv88e6xxx_chip *chip;
|
2008-10-07 21:45:18 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
chip = container_of(ugly, struct mv88e6xxx_chip, ppu_work);
|
2016-06-05 03:16:54 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2016-06-05 03:16:54 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mutex_trylock(&chip->ppu_mutex)) {
|
|
|
|
if (mv88e6xxx_ppu_enable(chip) == 0)
|
|
|
|
chip->ppu_disabled = 0;
|
|
|
|
mutex_unlock(&chip->ppu_mutex);
|
2008-10-07 21:45:18 +08:00
|
|
|
}
|
2016-06-05 03:16:54 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2008-10-07 21:45:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
|
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
struct mv88e6xxx_chip *chip = (void *)_ps;
|
2008-10-07 21:45:18 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
schedule_work(&chip->ppu_work);
|
2008-10-07 21:45:18 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_chip *chip)
|
2008-10-07 21:45:18 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->ppu_mutex);
|
2008-10-07 21:45:18 +08:00
|
|
|
|
2013-01-09 00:05:53 +08:00
|
|
|
/* If the PHY polling unit is enabled, disable it so that
|
2008-10-07 21:45:18 +08:00
|
|
|
* we can access the PHY registers. If it was already
|
|
|
|
* disabled, cancel the timer that is going to re-enable
|
|
|
|
* it.
|
|
|
|
*/
|
2016-06-22 00:28:20 +08:00
|
|
|
if (!chip->ppu_disabled) {
|
|
|
|
ret = mv88e6xxx_ppu_disable(chip);
|
2013-01-09 00:05:56 +08:00
|
|
|
if (ret < 0) {
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->ppu_mutex);
|
2013-01-09 00:05:56 +08:00
|
|
|
return ret;
|
|
|
|
}
|
2016-06-22 00:28:20 +08:00
|
|
|
chip->ppu_disabled = 1;
|
2008-10-07 21:45:18 +08:00
|
|
|
} else {
|
2016-06-22 00:28:20 +08:00
|
|
|
del_timer(&chip->ppu_timer);
|
2013-01-09 00:05:56 +08:00
|
|
|
ret = 0;
|
2008-10-07 21:45:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static void mv88e6xxx_ppu_access_put(struct mv88e6xxx_chip *chip)
|
2008-10-07 21:45:18 +08:00
|
|
|
{
|
2013-01-09 00:05:53 +08:00
|
|
|
/* Schedule a timer to re-enable the PHY polling unit. */
|
2016-06-22 00:28:20 +08:00
|
|
|
mod_timer(&chip->ppu_timer, jiffies + msecs_to_jiffies(10));
|
|
|
|
mutex_unlock(&chip->ppu_mutex);
|
2008-10-07 21:45:18 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static void mv88e6xxx_ppu_state_init(struct mv88e6xxx_chip *chip)
|
2008-10-07 21:45:18 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_init(&chip->ppu_mutex);
|
|
|
|
INIT_WORK(&chip->ppu_work, mv88e6xxx_ppu_reenable_work);
|
|
|
|
init_timer(&chip->ppu_timer);
|
|
|
|
chip->ppu_timer.data = (unsigned long)chip;
|
|
|
|
chip->ppu_timer.function = mv88e6xxx_ppu_reenable_timer;
|
2008-10-07 21:45:18 +08:00
|
|
|
}
|
|
|
|
|
2016-08-22 22:01:03 +08:00
|
|
|
static void mv88e6xxx_ppu_state_destroy(struct mv88e6xxx_chip *chip)
|
|
|
|
{
|
|
|
|
del_timer_sync(&chip->ppu_timer);
|
|
|
|
}
|
|
|
|
|
2016-08-16 05:19:00 +08:00
|
|
|
static int mv88e6xxx_phy_ppu_read(struct mv88e6xxx_chip *chip, int addr,
|
|
|
|
int reg, u16 *val)
|
2008-10-07 21:45:18 +08:00
|
|
|
{
|
2016-08-16 05:19:00 +08:00
|
|
|
int err;
|
2008-10-07 21:45:18 +08:00
|
|
|
|
2016-08-16 05:19:00 +08:00
|
|
|
err = mv88e6xxx_ppu_access_get(chip);
|
|
|
|
if (!err) {
|
|
|
|
err = mv88e6xxx_read(chip, addr, reg, val);
|
2016-06-22 00:28:20 +08:00
|
|
|
mv88e6xxx_ppu_access_put(chip);
|
2008-10-07 21:45:18 +08:00
|
|
|
}
|
|
|
|
|
2016-08-16 05:19:00 +08:00
|
|
|
return err;
|
2008-10-07 21:45:18 +08:00
|
|
|
}
|
|
|
|
|
2016-08-16 05:19:00 +08:00
|
|
|
static int mv88e6xxx_phy_ppu_write(struct mv88e6xxx_chip *chip, int addr,
|
|
|
|
int reg, u16 val)
|
2008-10-07 21:45:18 +08:00
|
|
|
{
|
2016-08-16 05:19:00 +08:00
|
|
|
int err;
|
2008-10-07 21:45:18 +08:00
|
|
|
|
2016-08-16 05:19:00 +08:00
|
|
|
err = mv88e6xxx_ppu_access_get(chip);
|
|
|
|
if (!err) {
|
|
|
|
err = mv88e6xxx_write(chip, addr, reg, val);
|
2016-06-22 00:28:20 +08:00
|
|
|
mv88e6xxx_ppu_access_put(chip);
|
2008-10-07 21:45:18 +08:00
|
|
|
}
|
|
|
|
|
2016-08-16 05:19:00 +08:00
|
|
|
return err;
|
2008-10-07 21:45:18 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static bool mv88e6xxx_6065_family(struct mv88e6xxx_chip *chip)
|
2015-05-06 07:09:47 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
return chip->info->family == MV88E6XXX_FAMILY_6065;
|
2015-05-06 07:09:47 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static bool mv88e6xxx_6095_family(struct mv88e6xxx_chip *chip)
|
2015-05-06 07:09:47 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
return chip->info->family == MV88E6XXX_FAMILY_6095;
|
2015-05-06 07:09:47 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static bool mv88e6xxx_6097_family(struct mv88e6xxx_chip *chip)
|
2015-05-06 07:09:47 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
return chip->info->family == MV88E6XXX_FAMILY_6097;
|
2015-05-06 07:09:47 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static bool mv88e6xxx_6165_family(struct mv88e6xxx_chip *chip)
|
2015-05-06 07:09:47 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
return chip->info->family == MV88E6XXX_FAMILY_6165;
|
2015-05-06 07:09:47 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static bool mv88e6xxx_6185_family(struct mv88e6xxx_chip *chip)
|
2015-05-06 07:09:47 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
return chip->info->family == MV88E6XXX_FAMILY_6185;
|
2015-05-06 07:09:47 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static bool mv88e6xxx_6320_family(struct mv88e6xxx_chip *chip)
|
2015-07-08 11:38:15 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
return chip->info->family == MV88E6XXX_FAMILY_6320;
|
2015-07-08 11:38:15 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static bool mv88e6xxx_6351_family(struct mv88e6xxx_chip *chip)
|
2015-05-06 07:09:47 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
return chip->info->family == MV88E6XXX_FAMILY_6351;
|
2015-05-06 07:09:47 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static bool mv88e6xxx_6352_family(struct mv88e6xxx_chip *chip)
|
2015-04-02 10:06:40 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
return chip->info->family == MV88E6XXX_FAMILY_6352;
|
2015-04-02 10:06:40 +08:00
|
|
|
}
|
|
|
|
|
2015-08-31 21:56:47 +08:00
|
|
|
/* We expect the switch to perform auto negotiation if there is a real
|
|
|
|
* phy. However, in the case of a fixed link phy, we force the port
|
|
|
|
* settings from the fixed link settings.
|
|
|
|
*/
|
2016-05-10 01:22:58 +08:00
|
|
|
static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
|
|
|
|
struct phy_device *phydev)
|
2015-08-31 21:56:47 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-09-21 07:40:31 +08:00
|
|
|
u16 reg;
|
|
|
|
int err;
|
2015-08-31 21:56:47 +08:00
|
|
|
|
|
|
|
if (!phy_is_pseudo_fixed_link(phydev))
|
|
|
|
return;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2015-08-31 21:56:47 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®);
|
|
|
|
if (err)
|
2015-08-31 21:56:47 +08:00
|
|
|
goto out;
|
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
reg &= ~(PORT_PCS_CTRL_LINK_UP |
|
|
|
|
PORT_PCS_CTRL_FORCE_LINK |
|
|
|
|
PORT_PCS_CTRL_DUPLEX_FULL |
|
|
|
|
PORT_PCS_CTRL_FORCE_DUPLEX |
|
|
|
|
PORT_PCS_CTRL_UNFORCED);
|
2015-08-31 21:56:47 +08:00
|
|
|
|
|
|
|
reg |= PORT_PCS_CTRL_FORCE_LINK;
|
|
|
|
if (phydev->link)
|
2016-06-21 01:13:58 +08:00
|
|
|
reg |= PORT_PCS_CTRL_LINK_UP;
|
2015-08-31 21:56:47 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_6065_family(chip) && phydev->speed > SPEED_100)
|
2015-08-31 21:56:47 +08:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
switch (phydev->speed) {
|
|
|
|
case SPEED_1000:
|
|
|
|
reg |= PORT_PCS_CTRL_1000;
|
|
|
|
break;
|
|
|
|
case SPEED_100:
|
|
|
|
reg |= PORT_PCS_CTRL_100;
|
|
|
|
break;
|
|
|
|
case SPEED_10:
|
|
|
|
reg |= PORT_PCS_CTRL_10;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
pr_info("Unknown speed");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
|
|
|
|
if (phydev->duplex == DUPLEX_FULL)
|
|
|
|
reg |= PORT_PCS_CTRL_DUPLEX_FULL;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if ((mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip)) &&
|
2016-09-30 00:21:57 +08:00
|
|
|
(port >= mv88e6xxx_num_ports(chip) - 2)) {
|
2015-08-31 21:56:51 +08:00
|
|
|
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
|
|
|
|
reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
|
|
|
|
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
|
|
|
|
reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
|
|
|
|
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
|
|
|
|
reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
|
|
|
|
PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
|
|
|
|
}
|
2016-09-21 07:40:31 +08:00
|
|
|
mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
|
2015-08-31 21:56:47 +08:00
|
|
|
|
|
|
|
out:
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2015-08-31 21:56:47 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip)
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
{
|
2016-09-30 00:21:53 +08:00
|
|
|
u16 val;
|
|
|
|
int i, err;
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
|
|
|
|
for (i = 0; i < 10; i++) {
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_OP, &val);
|
|
|
|
if ((val & GLOBAL_STATS_OP_BUSY) == 0)
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
{
|
2016-09-30 00:21:53 +08:00
|
|
|
int err;
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
|
2015-04-02 10:06:40 +08:00
|
|
|
port = (port + 1) << 5;
|
|
|
|
|
2013-01-09 00:05:53 +08:00
|
|
|
/* Snapshot the hardware statistics counters for this port. */
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
|
|
|
|
GLOBAL_STATS_OP_CAPTURE_PORT |
|
|
|
|
GLOBAL_STATS_OP_HIST_RX_TX | port);
|
|
|
|
if (err)
|
|
|
|
return err;
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
|
2013-01-09 00:05:53 +08:00
|
|
|
/* Wait for the snapshotting to complete. */
|
2016-09-30 00:21:53 +08:00
|
|
|
return _mv88e6xxx_stats_wait(chip);
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static void _mv88e6xxx_stats_read(struct mv88e6xxx_chip *chip,
|
2016-04-29 09:24:06 +08:00
|
|
|
int stat, u32 *val)
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
{
|
2016-09-30 00:21:53 +08:00
|
|
|
u32 value;
|
|
|
|
u16 reg;
|
|
|
|
int err;
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
|
|
|
|
*val = 0;
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
|
|
|
|
GLOBAL_STATS_OP_READ_CAPTURED |
|
|
|
|
GLOBAL_STATS_OP_HIST_RX_TX | stat);
|
|
|
|
if (err)
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
return;
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = _mv88e6xxx_stats_wait(chip);
|
|
|
|
if (err)
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
return;
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_32, ®);
|
|
|
|
if (err)
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
return;
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
value = reg << 16;
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_01, ®);
|
|
|
|
if (err)
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
return;
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
*val = value | reg;
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
}
|
|
|
|
|
2015-04-02 10:06:38 +08:00
|
|
|
static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
|
2015-12-23 20:23:17 +08:00
|
|
|
{ "in_good_octets", 8, 0x00, BANK0, },
|
|
|
|
{ "in_bad_octets", 4, 0x02, BANK0, },
|
|
|
|
{ "in_unicast", 4, 0x04, BANK0, },
|
|
|
|
{ "in_broadcasts", 4, 0x06, BANK0, },
|
|
|
|
{ "in_multicasts", 4, 0x07, BANK0, },
|
|
|
|
{ "in_pause", 4, 0x16, BANK0, },
|
|
|
|
{ "in_undersize", 4, 0x18, BANK0, },
|
|
|
|
{ "in_fragments", 4, 0x19, BANK0, },
|
|
|
|
{ "in_oversize", 4, 0x1a, BANK0, },
|
|
|
|
{ "in_jabber", 4, 0x1b, BANK0, },
|
|
|
|
{ "in_rx_error", 4, 0x1c, BANK0, },
|
|
|
|
{ "in_fcs_error", 4, 0x1d, BANK0, },
|
|
|
|
{ "out_octets", 8, 0x0e, BANK0, },
|
|
|
|
{ "out_unicast", 4, 0x10, BANK0, },
|
|
|
|
{ "out_broadcasts", 4, 0x13, BANK0, },
|
|
|
|
{ "out_multicasts", 4, 0x12, BANK0, },
|
|
|
|
{ "out_pause", 4, 0x15, BANK0, },
|
|
|
|
{ "excessive", 4, 0x11, BANK0, },
|
|
|
|
{ "collisions", 4, 0x1e, BANK0, },
|
|
|
|
{ "deferred", 4, 0x05, BANK0, },
|
|
|
|
{ "single", 4, 0x14, BANK0, },
|
|
|
|
{ "multiple", 4, 0x17, BANK0, },
|
|
|
|
{ "out_fcs_error", 4, 0x03, BANK0, },
|
|
|
|
{ "late", 4, 0x1f, BANK0, },
|
|
|
|
{ "hist_64bytes", 4, 0x08, BANK0, },
|
|
|
|
{ "hist_65_127bytes", 4, 0x09, BANK0, },
|
|
|
|
{ "hist_128_255bytes", 4, 0x0a, BANK0, },
|
|
|
|
{ "hist_256_511bytes", 4, 0x0b, BANK0, },
|
|
|
|
{ "hist_512_1023bytes", 4, 0x0c, BANK0, },
|
|
|
|
{ "hist_1024_max_bytes", 4, 0x0d, BANK0, },
|
|
|
|
{ "sw_in_discards", 4, 0x10, PORT, },
|
|
|
|
{ "sw_in_filtered", 2, 0x12, PORT, },
|
|
|
|
{ "sw_out_filtered", 2, 0x13, PORT, },
|
|
|
|
{ "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
|
|
|
{ "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
|
2015-04-02 10:06:38 +08:00
|
|
|
};
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static bool mv88e6xxx_has_stat(struct mv88e6xxx_chip *chip,
|
2015-12-23 20:23:17 +08:00
|
|
|
struct mv88e6xxx_hw_stat *stat)
|
2015-04-02 10:06:38 +08:00
|
|
|
{
|
2015-12-23 20:23:17 +08:00
|
|
|
switch (stat->type) {
|
|
|
|
case BANK0:
|
2015-04-02 10:06:38 +08:00
|
|
|
return true;
|
2015-12-23 20:23:17 +08:00
|
|
|
case BANK1:
|
2016-06-22 00:28:20 +08:00
|
|
|
return mv88e6xxx_6320_family(chip);
|
2015-12-23 20:23:17 +08:00
|
|
|
case PORT:
|
2016-06-22 00:28:20 +08:00
|
|
|
return mv88e6xxx_6095_family(chip) ||
|
|
|
|
mv88e6xxx_6185_family(chip) ||
|
|
|
|
mv88e6xxx_6097_family(chip) ||
|
|
|
|
mv88e6xxx_6165_family(chip) ||
|
|
|
|
mv88e6xxx_6351_family(chip) ||
|
|
|
|
mv88e6xxx_6352_family(chip);
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
}
|
2015-12-23 20:23:17 +08:00
|
|
|
return false;
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
|
2015-12-23 20:23:17 +08:00
|
|
|
struct mv88e6xxx_hw_stat *s,
|
2015-06-21 00:42:30 +08:00
|
|
|
int port)
|
|
|
|
{
|
|
|
|
u32 low;
|
|
|
|
u32 high = 0;
|
2016-09-21 07:40:31 +08:00
|
|
|
int err;
|
|
|
|
u16 reg;
|
2015-06-21 00:42:30 +08:00
|
|
|
u64 value;
|
|
|
|
|
2015-12-23 20:23:17 +08:00
|
|
|
switch (s->type) {
|
|
|
|
case PORT:
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, s->reg, ®);
|
|
|
|
if (err)
|
2015-06-21 00:42:30 +08:00
|
|
|
return UINT64_MAX;
|
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
low = reg;
|
2015-06-21 00:42:30 +08:00
|
|
|
if (s->sizeof_stat == 4) {
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, s->reg + 1, ®);
|
|
|
|
if (err)
|
2015-06-21 00:42:30 +08:00
|
|
|
return UINT64_MAX;
|
2016-09-21 07:40:31 +08:00
|
|
|
high = reg;
|
2015-06-21 00:42:30 +08:00
|
|
|
}
|
2015-12-23 20:23:17 +08:00
|
|
|
break;
|
|
|
|
case BANK0:
|
|
|
|
case BANK1:
|
2016-06-22 00:28:20 +08:00
|
|
|
_mv88e6xxx_stats_read(chip, s->reg, &low);
|
2015-06-21 00:42:30 +08:00
|
|
|
if (s->sizeof_stat == 8)
|
2016-06-22 00:28:20 +08:00
|
|
|
_mv88e6xxx_stats_read(chip, s->reg + 1, &high);
|
2015-06-21 00:42:30 +08:00
|
|
|
}
|
|
|
|
value = (((u64)high) << 16) | low;
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
|
|
|
|
uint8_t *data)
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2015-12-23 20:23:17 +08:00
|
|
|
struct mv88e6xxx_hw_stat *stat;
|
|
|
|
int i, j;
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
|
2015-12-23 20:23:17 +08:00
|
|
|
for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
|
|
|
|
stat = &mv88e6xxx_hw_stats[i];
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_has_stat(chip, stat)) {
|
2015-12-23 20:23:17 +08:00
|
|
|
memcpy(data + j * ETH_GSTRING_LEN, stat->string,
|
|
|
|
ETH_GSTRING_LEN);
|
|
|
|
j++;
|
|
|
|
}
|
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware
switch chips. It consists of a set of MII management registers and
commands to configure the switch, and an ethernet header format to
signal which of the ports of the switch a packet was received from
or is intended to be sent to.
The switches that this driver supports are typically embedded in
access points and routers, and a typical setup with a DSA switch
looks something like this:
+-----------+ +-----------+
| | RGMII | |
| +-------+ +------ 1000baseT MDI ("WAN")
| | | 6-port +------ 1000baseT MDI ("LAN1")
| CPU | | ethernet +------ 1000baseT MDI ("LAN2")
| |MIImgmt| switch +------ 1000baseT MDI ("LAN3")
| +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4")
| | | |
+-----------+ +-----------+
The switch driver presents each port on the switch as a separate
network interface to Linux, polls the switch to maintain software
link state of those ports, forwards MII management interface
accesses to those network interfaces (e.g. as done by ethtool) to
the switch, and exposes the switch's hardware statistics counters
via the appropriate Linux kernel interfaces.
This initial patch supports the MII management interface register
layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and
supports the "Ethertype DSA" packet tagging format.
(There is no officially registered ethertype for the Ethertype DSA
packet format, so we just grab a random one. The ethertype to use
is programmed into the switch, and the switch driver uses the value
of ETH_P_EDSA for this, so this define can be changed at any time in
the future if the one we chose is allocated to another protocol or
if Ethertype DSA gets its own officially registered ethertype, and
everything will continue to work.)
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Tested-by: Nicolas Pitre <nico@marvell.com>
Tested-by: Byron Bradley <byron.bbradley@gmail.com>
Tested-by: Tim Ellis <tim.ellis@mac.com>
Tested-by: Peter van Valderen <linux@ddcrew.com>
Tested-by: Dirk Teurlings <dirk@upexia.nl>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-07 21:44:02 +08:00
|
|
|
}
|
2015-04-02 10:06:38 +08:00
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
|
2015-04-02 10:06:38 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2015-12-23 20:23:17 +08:00
|
|
|
struct mv88e6xxx_hw_stat *stat;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
|
|
|
|
stat = &mv88e6xxx_hw_stats[i];
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_has_stat(chip, stat))
|
2015-12-23 20:23:17 +08:00
|
|
|
j++;
|
|
|
|
}
|
|
|
|
return j;
|
2015-04-02 10:06:38 +08:00
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
|
|
|
|
uint64_t *data)
|
2015-04-02 10:06:38 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2015-12-23 20:23:17 +08:00
|
|
|
struct mv88e6xxx_hw_stat *stat;
|
|
|
|
int ret;
|
|
|
|
int i, j;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2015-12-23 20:23:17 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
ret = _mv88e6xxx_stats_snapshot(chip, port);
|
2015-12-23 20:23:17 +08:00
|
|
|
if (ret < 0) {
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2015-12-23 20:23:17 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
|
|
|
|
stat = &mv88e6xxx_hw_stats[i];
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_has_stat(chip, stat)) {
|
|
|
|
data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port);
|
2015-12-23 20:23:17 +08:00
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2015-04-02 10:06:38 +08:00
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
|
2014-10-30 01:45:05 +08:00
|
|
|
{
|
|
|
|
return 32 * sizeof(u16);
|
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
|
|
|
|
struct ethtool_regs *regs, void *_p)
|
2014-10-30 01:45:05 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-09-21 07:40:31 +08:00
|
|
|
int err;
|
|
|
|
u16 reg;
|
2014-10-30 01:45:05 +08:00
|
|
|
u16 *p = _p;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
regs->version = 0;
|
|
|
|
|
|
|
|
memset(p, 0xff, 32 * sizeof(u16));
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2016-05-10 01:22:45 +08:00
|
|
|
|
2014-10-30 01:45:05 +08:00
|
|
|
for (i = 0; i < 32; i++) {
|
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, i, ®);
|
|
|
|
if (!err)
|
|
|
|
p[i] = reg;
|
2014-10-30 01:45:05 +08:00
|
|
|
}
|
2016-05-10 01:22:45 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2014-10-30 01:45:05 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_atu_wait(struct mv88e6xxx_chip *chip)
|
2015-03-27 09:36:35 +08:00
|
|
|
{
|
2016-09-30 00:21:53 +08:00
|
|
|
return mv88e6xxx_g1_wait(chip, GLOBAL_ATU_OP, GLOBAL_ATU_OP_BUSY);
|
2015-03-27 09:36:35 +08:00
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
|
|
|
|
struct ethtool_eee *e)
|
2015-03-07 14:23:51 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-08-16 05:19:02 +08:00
|
|
|
u16 reg;
|
|
|
|
int err;
|
2015-03-07 14:23:51 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
|
2016-05-10 01:22:44 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2015-04-02 10:06:37 +08:00
|
|
|
|
2016-08-16 05:19:02 +08:00
|
|
|
err = mv88e6xxx_phy_read(chip, port, 16, ®);
|
|
|
|
if (err)
|
2015-04-02 10:06:37 +08:00
|
|
|
goto out;
|
2015-03-07 14:23:51 +08:00
|
|
|
|
|
|
|
e->eee_enabled = !!(reg & 0x0200);
|
|
|
|
e->tx_lpi_enabled = !!(reg & 0x0100);
|
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, PORT_STATUS, ®);
|
2016-08-16 05:19:02 +08:00
|
|
|
if (err)
|
2015-04-02 10:06:37 +08:00
|
|
|
goto out;
|
2015-03-07 14:23:51 +08:00
|
|
|
|
2015-04-02 10:06:39 +08:00
|
|
|
e->eee_active = !!(reg & PORT_STATUS_EEE);
|
2015-04-02 10:06:37 +08:00
|
|
|
out:
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2016-08-16 05:19:02 +08:00
|
|
|
|
|
|
|
return err;
|
2015-03-07 14:23:51 +08:00
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
|
|
|
|
struct phy_device *phydev, struct ethtool_eee *e)
|
2015-03-07 14:23:51 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-08-16 05:19:02 +08:00
|
|
|
u16 reg;
|
|
|
|
int err;
|
2015-03-07 14:23:51 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
|
2016-05-10 01:22:44 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2015-03-07 14:23:51 +08:00
|
|
|
|
2016-08-16 05:19:02 +08:00
|
|
|
err = mv88e6xxx_phy_read(chip, port, 16, ®);
|
|
|
|
if (err)
|
2015-04-02 10:06:37 +08:00
|
|
|
goto out;
|
|
|
|
|
2016-08-16 05:19:02 +08:00
|
|
|
reg &= ~0x0300;
|
2015-04-02 10:06:37 +08:00
|
|
|
if (e->eee_enabled)
|
|
|
|
reg |= 0x0200;
|
|
|
|
if (e->tx_lpi_enabled)
|
|
|
|
reg |= 0x0100;
|
|
|
|
|
2016-08-16 05:19:02 +08:00
|
|
|
err = mv88e6xxx_phy_write(chip, port, 16, reg);
|
2015-04-02 10:06:37 +08:00
|
|
|
out:
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2015-04-02 10:06:37 +08:00
|
|
|
|
2016-08-16 05:19:02 +08:00
|
|
|
return err;
|
2015-03-07 14:23:51 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_chip *chip, u16 fid, u16 cmd)
|
2015-03-27 09:36:35 +08:00
|
|
|
{
|
2016-09-30 00:21:53 +08:00
|
|
|
u16 val;
|
|
|
|
int err;
|
2015-03-27 09:36:35 +08:00
|
|
|
|
2016-09-30 00:21:55 +08:00
|
|
|
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_ATU_FID)) {
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_FID, fid);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-06-22 00:28:20 +08:00
|
|
|
} else if (mv88e6xxx_num_databases(chip) == 256) {
|
2016-04-01 04:53:44 +08:00
|
|
|
/* ATU DBNum[7:4] are located in ATU Control 15:12 */
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-04-01 04:53:44 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL,
|
|
|
|
(val & 0xfff) | ((fid << 8) & 0xf000));
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-04-01 04:53:44 +08:00
|
|
|
|
|
|
|
/* ATU DBNum[3:0] are located in ATU Operation 3:0 */
|
|
|
|
cmd |= fid & 0xf;
|
2016-04-01 04:53:42 +08:00
|
|
|
}
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_OP, cmd);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-03-27 09:36:35 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_atu_wait(chip);
|
2015-03-27 09:36:35 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_chip *chip,
|
2015-09-05 02:34:11 +08:00
|
|
|
struct mv88e6xxx_atu_entry *entry)
|
|
|
|
{
|
|
|
|
u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
|
|
|
|
|
|
|
|
if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
|
|
|
|
unsigned int mask, shift;
|
|
|
|
|
|
|
|
if (entry->trunk) {
|
|
|
|
data |= GLOBAL_ATU_DATA_TRUNK;
|
|
|
|
mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
|
|
|
|
shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
|
|
|
|
} else {
|
|
|
|
mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
|
|
|
|
shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
|
|
|
|
}
|
|
|
|
|
|
|
|
data |= (entry->portv_trunkid << shift) & mask;
|
|
|
|
}
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
return mv88e6xxx_g1_write(chip, GLOBAL_ATU_DATA, data);
|
2015-09-05 02:34:11 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_chip *chip,
|
2015-09-05 02:34:12 +08:00
|
|
|
struct mv88e6xxx_atu_entry *entry,
|
|
|
|
bool static_too)
|
2015-03-27 09:36:35 +08:00
|
|
|
{
|
2015-09-05 02:34:12 +08:00
|
|
|
int op;
|
|
|
|
int err;
|
2015-03-27 09:36:35 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_atu_wait(chip);
|
2015-09-05 02:34:12 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
2015-03-27 09:36:35 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_atu_data_write(chip, entry);
|
2015-09-05 02:34:12 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (entry->fid) {
|
|
|
|
op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
|
|
|
|
GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
|
|
|
|
} else {
|
|
|
|
op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
|
|
|
|
GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_atu_cmd(chip, entry->fid, op);
|
2015-09-05 02:34:12 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_atu_flush(struct mv88e6xxx_chip *chip,
|
2016-04-29 09:24:06 +08:00
|
|
|
u16 fid, bool static_too)
|
2015-09-05 02:34:12 +08:00
|
|
|
{
|
|
|
|
struct mv88e6xxx_atu_entry entry = {
|
|
|
|
.fid = fid,
|
|
|
|
.state = 0, /* EntryState bits must be 0 */
|
|
|
|
};
|
2015-09-05 02:34:10 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
|
2015-09-05 02:34:12 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_atu_move(struct mv88e6xxx_chip *chip, u16 fid,
|
2016-04-29 09:24:06 +08:00
|
|
|
int from_port, int to_port, bool static_too)
|
2015-09-05 02:34:15 +08:00
|
|
|
{
|
|
|
|
struct mv88e6xxx_atu_entry entry = {
|
|
|
|
.trunk = false,
|
|
|
|
.fid = fid,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* EntryState bits must be 0xF */
|
|
|
|
entry.state = GLOBAL_ATU_DATA_STATE_MASK;
|
|
|
|
|
|
|
|
/* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
|
|
|
|
entry.portv_trunkid = (to_port & 0x0f) << 4;
|
|
|
|
entry.portv_trunkid |= from_port & 0x0f;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
|
2015-09-05 02:34:15 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid,
|
2016-04-29 09:24:06 +08:00
|
|
|
int port, bool static_too)
|
2015-09-05 02:34:15 +08:00
|
|
|
{
|
|
|
|
/* Destination port 0xF means remove the entries */
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_atu_move(chip, fid, port, 0x0f, static_too);
|
2015-09-05 02:34:15 +08:00
|
|
|
}
|
|
|
|
|
2016-03-08 07:24:17 +08:00
|
|
|
static const char * const mv88e6xxx_port_state_names[] = {
|
|
|
|
[PORT_CONTROL_STATE_DISABLED] = "Disabled",
|
|
|
|
[PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
|
|
|
|
[PORT_CONTROL_STATE_LEARNING] = "Learning",
|
|
|
|
[PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
|
|
|
|
};
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_port_state(struct mv88e6xxx_chip *chip, int port,
|
2016-04-29 09:24:06 +08:00
|
|
|
u8 state)
|
2015-03-27 09:36:35 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
struct dsa_switch *ds = chip->ds;
|
2016-09-21 07:40:31 +08:00
|
|
|
u16 reg;
|
|
|
|
int err;
|
2015-03-27 09:36:35 +08:00
|
|
|
u8 oldstate;
|
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-03-27 09:36:35 +08:00
|
|
|
|
2015-04-02 10:06:39 +08:00
|
|
|
oldstate = reg & PORT_CONTROL_STATE_MASK;
|
2016-03-08 07:24:17 +08:00
|
|
|
|
2016-09-23 04:49:24 +08:00
|
|
|
reg &= ~PORT_CONTROL_STATE_MASK;
|
|
|
|
reg |= state;
|
2016-03-08 07:24:17 +08:00
|
|
|
|
2016-09-23 04:49:24 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-03-08 07:24:17 +08:00
|
|
|
|
2016-09-23 04:49:24 +08:00
|
|
|
netdev_dbg(ds->ports[port].netdev, "PortState %s (was %s)\n",
|
|
|
|
mv88e6xxx_port_state_names[state],
|
|
|
|
mv88e6xxx_port_state_names[oldstate]);
|
2015-03-27 09:36:35 +08:00
|
|
|
|
2016-09-23 04:49:24 +08:00
|
|
|
return 0;
|
2015-03-27 09:36:35 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port)
|
2015-03-27 09:36:35 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
struct net_device *bridge = chip->ports[port].bridge_dev;
|
2016-09-30 00:21:57 +08:00
|
|
|
const u16 mask = (1 << mv88e6xxx_num_ports(chip)) - 1;
|
2016-06-22 00:28:20 +08:00
|
|
|
struct dsa_switch *ds = chip->ds;
|
2016-02-27 02:16:06 +08:00
|
|
|
u16 output_ports = 0;
|
2016-09-21 07:40:31 +08:00
|
|
|
u16 reg;
|
|
|
|
int err;
|
2016-02-27 02:16:06 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
/* allow CPU port or DSA link(s) to send frames to every port */
|
|
|
|
if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
|
|
|
|
output_ports = mask;
|
|
|
|
} else {
|
2016-09-30 00:21:57 +08:00
|
|
|
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
|
2016-02-27 02:16:06 +08:00
|
|
|
/* allow sending frames to every group member */
|
2016-06-22 00:28:20 +08:00
|
|
|
if (bridge && chip->ports[i].bridge_dev == bridge)
|
2016-02-27 02:16:06 +08:00
|
|
|
output_ports |= BIT(i);
|
|
|
|
|
|
|
|
/* allow sending frames to CPU port and DSA link(s) */
|
|
|
|
if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
|
|
|
|
output_ports |= BIT(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* prevent frames from going back out of the port they came in on */
|
|
|
|
output_ports &= ~BIT(port);
|
2015-03-27 09:36:35 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-03-27 09:36:35 +08:00
|
|
|
|
2015-10-12 06:08:35 +08:00
|
|
|
reg &= ~mask;
|
|
|
|
reg |= output_ports & mask;
|
2015-03-27 09:36:35 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
|
2015-03-27 09:36:35 +08:00
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
|
|
|
|
u8 state)
|
2015-03-27 09:36:35 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2015-03-27 09:36:35 +08:00
|
|
|
int stp_state;
|
2016-05-14 08:38:23 +08:00
|
|
|
int err;
|
2015-03-27 09:36:35 +08:00
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case BR_STATE_DISABLED:
|
2015-04-02 10:06:39 +08:00
|
|
|
stp_state = PORT_CONTROL_STATE_DISABLED;
|
2015-03-27 09:36:35 +08:00
|
|
|
break;
|
|
|
|
case BR_STATE_BLOCKING:
|
|
|
|
case BR_STATE_LISTENING:
|
2015-04-02 10:06:39 +08:00
|
|
|
stp_state = PORT_CONTROL_STATE_BLOCKING;
|
2015-03-27 09:36:35 +08:00
|
|
|
break;
|
|
|
|
case BR_STATE_LEARNING:
|
2015-04-02 10:06:39 +08:00
|
|
|
stp_state = PORT_CONTROL_STATE_LEARNING;
|
2015-03-27 09:36:35 +08:00
|
|
|
break;
|
|
|
|
case BR_STATE_FORWARDING:
|
|
|
|
default:
|
2015-04-02 10:06:39 +08:00
|
|
|
stp_state = PORT_CONTROL_STATE_FORWARDING;
|
2015-03-27 09:36:35 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
err = _mv88e6xxx_port_state(chip, port, stp_state);
|
|
|
|
mutex_unlock(&chip->reg_lock);
|
2016-05-14 08:38:23 +08:00
|
|
|
|
|
|
|
if (err)
|
2016-06-05 03:16:57 +08:00
|
|
|
netdev_err(ds->ports[port].netdev,
|
|
|
|
"failed to update state to %s\n",
|
2016-05-14 08:38:23 +08:00
|
|
|
mv88e6xxx_port_state_names[stp_state]);
|
2015-03-27 09:36:35 +08:00
|
|
|
}
|
|
|
|
|
2016-09-23 04:49:24 +08:00
|
|
|
static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
|
|
|
|
{
|
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
err = _mv88e6xxx_atu_remove(chip, 0, port, false);
|
|
|
|
mutex_unlock(&chip->reg_lock);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
netdev_err(ds->ports[port].netdev, "failed to flush ATU\n");
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_port_pvid(struct mv88e6xxx_chip *chip, int port,
|
2016-04-29 09:24:06 +08:00
|
|
|
u16 *new, u16 *old)
|
2015-11-02 01:33:55 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
struct dsa_switch *ds = chip->ds;
|
2016-09-21 07:40:31 +08:00
|
|
|
u16 pvid, reg;
|
|
|
|
int err;
|
2015-11-02 01:33:55 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-11-02 01:33:55 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
pvid = reg & PORT_DEFAULT_VLAN_MASK;
|
2016-03-08 07:24:39 +08:00
|
|
|
|
|
|
|
if (new) {
|
2016-09-21 07:40:31 +08:00
|
|
|
reg &= ~PORT_DEFAULT_VLAN_MASK;
|
|
|
|
reg |= *new & PORT_DEFAULT_VLAN_MASK;
|
2016-03-08 07:24:39 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-03-08 07:24:39 +08:00
|
|
|
|
2016-06-05 03:16:57 +08:00
|
|
|
netdev_dbg(ds->ports[port].netdev,
|
|
|
|
"DefaultVID %d (was %d)\n", *new, pvid);
|
2016-03-08 07:24:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (old)
|
|
|
|
*old = pvid;
|
2015-11-02 01:33:55 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_chip *chip,
|
2016-04-29 09:24:06 +08:00
|
|
|
int port, u16 *pvid)
|
2016-03-08 07:24:39 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_port_pvid(chip, port, NULL, pvid);
|
2016-03-08 07:24:39 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_chip *chip,
|
2016-04-29 09:24:06 +08:00
|
|
|
int port, u16 pvid)
|
2015-08-14 00:52:22 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_port_pvid(chip, port, &pvid, NULL);
|
2015-08-14 00:52:22 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_chip *chip)
|
2015-08-14 00:52:18 +08:00
|
|
|
{
|
2016-09-30 00:21:53 +08:00
|
|
|
return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY);
|
2015-08-14 00:52:18 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_chip *chip, u16 op)
|
2015-08-14 00:52:18 +08:00
|
|
|
{
|
2016-09-30 00:21:53 +08:00
|
|
|
int err;
|
2015-08-14 00:52:18 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_OP, op);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:18 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_vtu_wait(chip);
|
2015-08-14 00:52:18 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_chip *chip)
|
2015-08-14 00:52:18 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
ret = _mv88e6xxx_vtu_wait(chip);
|
2015-08-14 00:52:18 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_FLUSH_ALL);
|
2015-08-14 00:52:18 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_chip *chip,
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry *entry,
|
2015-08-14 00:52:19 +08:00
|
|
|
unsigned int nibble_offset)
|
|
|
|
{
|
|
|
|
u16 regs[3];
|
2016-09-30 00:21:53 +08:00
|
|
|
int i, err;
|
2015-08-14 00:52:19 +08:00
|
|
|
|
|
|
|
for (i = 0; i < 3; ++i) {
|
2016-09-30 00:21:53 +08:00
|
|
|
u16 *reg = ®s[i];
|
2015-08-14 00:52:19 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:19 +08:00
|
|
|
}
|
|
|
|
|
2016-09-30 00:21:57 +08:00
|
|
|
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
|
2015-08-14 00:52:19 +08:00
|
|
|
unsigned int shift = (i % 4) * 4 + nibble_offset;
|
|
|
|
u16 reg = regs[i / 4];
|
|
|
|
|
|
|
|
entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_chip *chip,
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry *entry)
|
2016-05-11 03:44:28 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_vtu_stu_data_read(chip, entry, 0);
|
2016-05-11 03:44:28 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_stu_data_read(struct mv88e6xxx_chip *chip,
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry *entry)
|
2016-05-11 03:44:28 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_vtu_stu_data_read(chip, entry, 2);
|
2016-05-11 03:44:28 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_chip *chip,
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry *entry,
|
2015-08-14 00:52:21 +08:00
|
|
|
unsigned int nibble_offset)
|
|
|
|
{
|
|
|
|
u16 regs[3] = { 0 };
|
2016-09-30 00:21:53 +08:00
|
|
|
int i, err;
|
2015-08-14 00:52:21 +08:00
|
|
|
|
2016-09-30 00:21:57 +08:00
|
|
|
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
|
2015-08-14 00:52:21 +08:00
|
|
|
unsigned int shift = (i % 4) * 4 + nibble_offset;
|
|
|
|
u8 data = entry->data[i];
|
|
|
|
|
|
|
|
regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 3; ++i) {
|
2016-09-30 00:21:53 +08:00
|
|
|
u16 reg = regs[i];
|
|
|
|
|
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_chip *chip,
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry *entry)
|
2016-05-11 03:44:28 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_vtu_stu_data_write(chip, entry, 0);
|
2016-05-11 03:44:28 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_stu_data_write(struct mv88e6xxx_chip *chip,
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry *entry)
|
2016-05-11 03:44:28 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_vtu_stu_data_write(chip, entry, 2);
|
2016-05-11 03:44:28 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_chip *chip, u16 vid)
|
2015-10-22 21:34:39 +08:00
|
|
|
{
|
2016-09-30 00:21:53 +08:00
|
|
|
return mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID,
|
|
|
|
vid & GLOBAL_VTU_VID_MASK);
|
2015-10-22 21:34:39 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip,
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry *entry)
|
2015-08-14 00:52:19 +08:00
|
|
|
{
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry next = { 0 };
|
2016-09-30 00:21:53 +08:00
|
|
|
u16 val;
|
|
|
|
int err;
|
2015-08-14 00:52:19 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = _mv88e6xxx_vtu_wait(chip);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:19 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_VTU_GET_NEXT);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:19 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:19 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
next.vid = val & GLOBAL_VTU_VID_MASK;
|
|
|
|
next.valid = !!(val & GLOBAL_VTU_VID_VALID);
|
2015-08-14 00:52:19 +08:00
|
|
|
|
|
|
|
if (next.valid) {
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_vtu_data_read(chip, &next);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:19 +08:00
|
|
|
|
2016-09-30 00:21:55 +08:00
|
|
|
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) {
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_FID, &val);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:19 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
next.fid = val & GLOBAL_VTU_FID_MASK;
|
2016-06-22 00:28:20 +08:00
|
|
|
} else if (mv88e6xxx_num_databases(chip) == 256) {
|
2016-04-01 04:53:44 +08:00
|
|
|
/* VTU DBNum[7:4] are located in VTU Operation 11:8, and
|
|
|
|
* VTU DBNum[3:0] are located in VTU Operation 3:0
|
|
|
|
*/
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_OP, &val);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-04-01 04:53:44 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
next.fid = (val & 0xf00) >> 4;
|
|
|
|
next.fid |= val & 0xf;
|
2016-04-01 04:53:41 +08:00
|
|
|
}
|
2015-08-14 00:52:19 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:19 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
next.sid = val & GLOBAL_VTU_SID_MASK;
|
2015-08-14 00:52:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*entry = next;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
|
|
|
|
struct switchdev_obj_port_vlan *vlan,
|
|
|
|
int (*cb)(struct switchdev_obj *obj))
|
2016-02-24 01:13:55 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry next;
|
2016-02-24 01:13:55 +08:00
|
|
|
u16 pvid;
|
|
|
|
int err;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
|
2016-05-10 01:22:47 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2016-02-24 01:13:55 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_port_pvid_get(chip, port, &pvid);
|
2016-02-24 01:13:55 +08:00
|
|
|
if (err)
|
|
|
|
goto unlock;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
|
2016-02-24 01:13:55 +08:00
|
|
|
if (err)
|
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
do {
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_vtu_getnext(chip, &next);
|
2016-02-24 01:13:55 +08:00
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (!next.valid)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* reinit and dump this VLAN obj */
|
2016-06-21 01:13:58 +08:00
|
|
|
vlan->vid_begin = next.vid;
|
|
|
|
vlan->vid_end = next.vid;
|
2016-02-24 01:13:55 +08:00
|
|
|
vlan->flags = 0;
|
|
|
|
|
|
|
|
if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
|
|
|
|
vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
|
|
|
|
|
|
|
|
if (next.vid == pvid)
|
|
|
|
vlan->flags |= BRIDGE_VLAN_INFO_PVID;
|
|
|
|
|
|
|
|
err = cb(&vlan->obj);
|
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
} while (next.vid < GLOBAL_VTU_VID_MASK);
|
|
|
|
|
|
|
|
unlock:
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2016-02-24 01:13:55 +08:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip,
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry *entry)
|
2015-08-14 00:52:21 +08:00
|
|
|
{
|
2016-04-01 04:53:44 +08:00
|
|
|
u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
|
2015-08-14 00:52:21 +08:00
|
|
|
u16 reg = 0;
|
2016-09-30 00:21:53 +08:00
|
|
|
int err;
|
2015-08-14 00:52:21 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = _mv88e6xxx_vtu_wait(chip);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:21 +08:00
|
|
|
|
|
|
|
if (!entry->valid)
|
|
|
|
goto loadpurge;
|
|
|
|
|
|
|
|
/* Write port member tags */
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_vtu_data_write(chip, entry);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:21 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
|
2015-08-14 00:52:21 +08:00
|
|
|
reg = entry->sid & GLOBAL_VTU_SID_MASK;
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, reg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-04-01 04:53:42 +08:00
|
|
|
}
|
2015-08-14 00:52:21 +08:00
|
|
|
|
2016-09-30 00:21:55 +08:00
|
|
|
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) {
|
2015-08-14 00:52:21 +08:00
|
|
|
reg = entry->fid & GLOBAL_VTU_FID_MASK;
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_FID, reg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-06-22 00:28:20 +08:00
|
|
|
} else if (mv88e6xxx_num_databases(chip) == 256) {
|
2016-04-01 04:53:44 +08:00
|
|
|
/* VTU DBNum[7:4] are located in VTU Operation 11:8, and
|
|
|
|
* VTU DBNum[3:0] are located in VTU Operation 3:0
|
|
|
|
*/
|
|
|
|
op |= (entry->fid & 0xf0) << 8;
|
|
|
|
op |= entry->fid & 0xf;
|
2015-08-14 00:52:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
reg = GLOBAL_VTU_VID_VALID;
|
|
|
|
loadpurge:
|
|
|
|
reg |= entry->vid & GLOBAL_VTU_VID_MASK;
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, reg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:21 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_vtu_cmd(chip, op);
|
2015-08-14 00:52:21 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid,
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry *entry)
|
2015-08-14 00:52:22 +08:00
|
|
|
{
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry next = { 0 };
|
2016-09-30 00:21:53 +08:00
|
|
|
u16 val;
|
|
|
|
int err;
|
2015-08-14 00:52:22 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = _mv88e6xxx_vtu_wait(chip);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:22 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID,
|
|
|
|
sid & GLOBAL_VTU_SID_MASK);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:22 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_GET_NEXT);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:22 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:22 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
next.sid = val & GLOBAL_VTU_SID_MASK;
|
2015-08-14 00:52:22 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:22 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
next.valid = !!(val & GLOBAL_VTU_VID_VALID);
|
2015-08-14 00:52:22 +08:00
|
|
|
|
|
|
|
if (next.valid) {
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_stu_data_read(chip, &next);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
*entry = next;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip,
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry *entry)
|
2015-08-14 00:52:22 +08:00
|
|
|
{
|
|
|
|
u16 reg = 0;
|
2016-09-30 00:21:53 +08:00
|
|
|
int err;
|
2015-08-14 00:52:22 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = _mv88e6xxx_vtu_wait(chip);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:22 +08:00
|
|
|
|
|
|
|
if (!entry->valid)
|
|
|
|
goto loadpurge;
|
|
|
|
|
|
|
|
/* Write port states */
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_stu_data_write(chip, entry);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:22 +08:00
|
|
|
|
|
|
|
reg = GLOBAL_VTU_VID_VALID;
|
|
|
|
loadpurge:
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, reg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:22 +08:00
|
|
|
|
|
|
|
reg = entry->sid & GLOBAL_VTU_SID_MASK;
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, reg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:22 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE);
|
2015-08-14 00:52:22 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_port_fid(struct mv88e6xxx_chip *chip, int port,
|
2016-04-29 09:24:06 +08:00
|
|
|
u16 *new, u16 *old)
|
2016-02-27 02:16:04 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
struct dsa_switch *ds = chip->ds;
|
2016-04-01 04:53:43 +08:00
|
|
|
u16 upper_mask;
|
2016-02-27 02:16:04 +08:00
|
|
|
u16 fid;
|
2016-09-21 07:40:31 +08:00
|
|
|
u16 reg;
|
|
|
|
int err;
|
2016-02-27 02:16:04 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_num_databases(chip) == 4096)
|
2016-04-01 04:53:43 +08:00
|
|
|
upper_mask = 0xff;
|
2016-06-22 00:28:20 +08:00
|
|
|
else if (mv88e6xxx_num_databases(chip) == 256)
|
2016-04-01 04:53:44 +08:00
|
|
|
upper_mask = 0xf;
|
2016-04-01 04:53:43 +08:00
|
|
|
else
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2016-02-27 02:16:04 +08:00
|
|
|
/* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-02-27 02:16:04 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
fid = (reg & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
|
2016-02-27 02:16:04 +08:00
|
|
|
|
|
|
|
if (new) {
|
2016-09-21 07:40:31 +08:00
|
|
|
reg &= ~PORT_BASE_VLAN_FID_3_0_MASK;
|
|
|
|
reg |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
|
2016-02-27 02:16:04 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-02-27 02:16:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-02-27 02:16:04 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
fid |= (reg & upper_mask) << 4;
|
2016-02-27 02:16:04 +08:00
|
|
|
|
|
|
|
if (new) {
|
2016-09-21 07:40:31 +08:00
|
|
|
reg &= ~upper_mask;
|
|
|
|
reg |= (*new >> 4) & upper_mask;
|
2016-02-27 02:16:04 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-02-27 02:16:04 +08:00
|
|
|
|
2016-06-05 03:16:57 +08:00
|
|
|
netdev_dbg(ds->ports[port].netdev,
|
|
|
|
"FID %d (was %d)\n", *new, fid);
|
2016-02-27 02:16:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (old)
|
|
|
|
*old = fid;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_chip *chip,
|
2016-04-29 09:24:06 +08:00
|
|
|
int port, u16 *fid)
|
2016-02-27 02:16:04 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_port_fid(chip, port, NULL, fid);
|
2016-02-27 02:16:04 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_chip *chip,
|
2016-04-29 09:24:06 +08:00
|
|
|
int port, u16 fid)
|
2016-02-27 02:16:04 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_port_fid(chip, port, &fid, NULL);
|
2016-02-27 02:16:04 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)
|
2016-02-27 02:16:03 +08:00
|
|
|
{
|
|
|
|
DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry vlan;
|
2016-02-27 02:16:04 +08:00
|
|
|
int i, err;
|
2016-02-27 02:16:03 +08:00
|
|
|
|
|
|
|
bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
|
|
|
|
|
2016-02-27 02:16:04 +08:00
|
|
|
/* Set every FID bit used by the (un)bridged ports */
|
2016-09-30 00:21:57 +08:00
|
|
|
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_port_fid_get(chip, i, fid);
|
2016-02-27 02:16:04 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
set_bit(*fid, fid_bitmap);
|
|
|
|
}
|
|
|
|
|
2016-02-27 02:16:03 +08:00
|
|
|
/* Set every FID bit used by the VLAN entries */
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
|
2016-02-27 02:16:03 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
do {
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_vtu_getnext(chip, &vlan);
|
2016-02-27 02:16:03 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (!vlan.valid)
|
|
|
|
break;
|
|
|
|
|
|
|
|
set_bit(vlan.fid, fid_bitmap);
|
|
|
|
} while (vlan.vid < GLOBAL_VTU_VID_MASK);
|
|
|
|
|
|
|
|
/* The reset value 0x000 is used to indicate that multiple address
|
|
|
|
* databases are not needed. Return the next positive available.
|
|
|
|
*/
|
|
|
|
*fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
|
2016-06-22 00:28:20 +08:00
|
|
|
if (unlikely(*fid >= mv88e6xxx_num_databases(chip)))
|
2016-02-27 02:16:03 +08:00
|
|
|
return -ENOSPC;
|
|
|
|
|
|
|
|
/* Clear the database */
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_atu_flush(chip, *fid, true);
|
2016-02-27 02:16:03 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_vtu_new(struct mv88e6xxx_chip *chip, u16 vid,
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry *entry)
|
2015-08-14 00:52:22 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
struct dsa_switch *ds = chip->ds;
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry vlan = {
|
2015-08-14 00:52:22 +08:00
|
|
|
.valid = true,
|
|
|
|
.vid = vid,
|
|
|
|
};
|
2016-02-27 02:16:03 +08:00
|
|
|
int i, err;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_fid_new(chip, &vlan.fid);
|
2016-02-27 02:16:03 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-14 00:52:22 +08:00
|
|
|
|
2015-11-03 23:52:52 +08:00
|
|
|
/* exclude all ports except the CPU and DSA ports */
|
2016-09-30 00:21:57 +08:00
|
|
|
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
|
2015-11-03 23:52:52 +08:00
|
|
|
vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
|
|
|
|
? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
|
|
|
|
: GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
|
2015-08-14 00:52:22 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_6097_family(chip) || mv88e6xxx_6165_family(chip) ||
|
|
|
|
mv88e6xxx_6351_family(chip) || mv88e6xxx_6352_family(chip)) {
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry vstp;
|
2015-08-14 00:52:22 +08:00
|
|
|
|
|
|
|
/* Adding a VTU entry requires a valid STU entry. As VSTP is not
|
|
|
|
* implemented, only one STU entry is needed to cover all VTU
|
|
|
|
* entries. Thus, validate the SID 0.
|
|
|
|
*/
|
|
|
|
vlan.sid = 0;
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_stu_getnext(chip, GLOBAL_VTU_SID_MASK, &vstp);
|
2015-08-14 00:52:22 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (vstp.sid != vlan.sid || !vstp.valid) {
|
|
|
|
memset(&vstp, 0, sizeof(vstp));
|
|
|
|
vstp.valid = true;
|
|
|
|
vstp.sid = vlan.sid;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_stu_loadpurge(chip, &vstp);
|
2015-08-14 00:52:22 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*entry = vlan;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry *entry, bool creat)
|
2016-02-27 02:16:01 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!vid)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_vtu_vid_write(chip, vid - 1);
|
2016-02-27 02:16:01 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_vtu_getnext(chip, entry);
|
2016-02-27 02:16:01 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (entry->vid != vid || !entry->valid) {
|
|
|
|
if (!creat)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
/* -ENOENT would've been more appropriate, but switchdev expects
|
|
|
|
* -EOPNOTSUPP to inform bridge about an eventual software VLAN.
|
|
|
|
*/
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_vtu_new(chip, vid, entry);
|
2016-02-27 02:16:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-02-13 01:09:40 +08:00
|
|
|
static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
|
|
|
|
u16 vid_begin, u16 vid_end)
|
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry vlan;
|
2016-02-13 01:09:40 +08:00
|
|
|
int i, err;
|
|
|
|
|
|
|
|
if (!vid_begin)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2016-02-13 01:09:40 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_vtu_vid_write(chip, vid_begin - 1);
|
2016-02-13 01:09:40 +08:00
|
|
|
if (err)
|
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
do {
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_vtu_getnext(chip, &vlan);
|
2016-02-13 01:09:40 +08:00
|
|
|
if (err)
|
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
if (!vlan.valid)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (vlan.vid > vid_end)
|
|
|
|
break;
|
|
|
|
|
2016-09-30 00:21:57 +08:00
|
|
|
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
|
2016-02-13 01:09:40 +08:00
|
|
|
if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (vlan.data[i] ==
|
|
|
|
GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
|
|
|
|
continue;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (chip->ports[i].bridge_dev ==
|
|
|
|
chip->ports[port].bridge_dev)
|
2016-02-13 01:09:40 +08:00
|
|
|
break; /* same bridge, check next VLAN */
|
|
|
|
|
2016-06-05 03:16:57 +08:00
|
|
|
netdev_warn(ds->ports[port].netdev,
|
2016-02-13 01:09:40 +08:00
|
|
|
"hardware VLAN %d already used by %s\n",
|
|
|
|
vlan.vid,
|
2016-06-22 00:28:20 +08:00
|
|
|
netdev_name(chip->ports[i].bridge_dev));
|
2016-02-13 01:09:40 +08:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
} while (vlan.vid < vid_end);
|
|
|
|
|
|
|
|
unlock:
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2016-02-13 01:09:40 +08:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-02-27 02:16:08 +08:00
|
|
|
static const char * const mv88e6xxx_port_8021q_mode_names[] = {
|
|
|
|
[PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
|
|
|
|
[PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
|
|
|
|
[PORT_CONTROL_2_8021Q_CHECK] = "Check",
|
|
|
|
[PORT_CONTROL_2_8021Q_SECURE] = "Secure",
|
|
|
|
};
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
|
|
|
|
bool vlan_filtering)
|
2016-02-27 02:16:08 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-02-27 02:16:08 +08:00
|
|
|
u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
|
|
|
|
PORT_CONTROL_2_8021Q_DISABLED;
|
2016-09-21 07:40:31 +08:00
|
|
|
u16 reg;
|
|
|
|
int err;
|
2016-02-27 02:16:08 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
|
2016-05-10 01:22:47 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2016-02-27 02:16:08 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, ®);
|
|
|
|
if (err)
|
2016-02-27 02:16:08 +08:00
|
|
|
goto unlock;
|
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
old = reg & PORT_CONTROL_2_8021Q_MASK;
|
2016-02-27 02:16:08 +08:00
|
|
|
|
2016-03-08 07:24:52 +08:00
|
|
|
if (new != old) {
|
2016-09-21 07:40:31 +08:00
|
|
|
reg &= ~PORT_CONTROL_2_8021Q_MASK;
|
|
|
|
reg |= new & PORT_CONTROL_2_8021Q_MASK;
|
2016-02-27 02:16:08 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
|
|
|
|
if (err)
|
2016-03-08 07:24:52 +08:00
|
|
|
goto unlock;
|
|
|
|
|
2016-06-05 03:16:57 +08:00
|
|
|
netdev_dbg(ds->ports[port].netdev, "802.1Q Mode %s (was %s)\n",
|
2016-03-08 07:24:52 +08:00
|
|
|
mv88e6xxx_port_8021q_mode_names[new],
|
|
|
|
mv88e6xxx_port_8021q_mode_names[old]);
|
|
|
|
}
|
2016-02-27 02:16:08 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
err = 0;
|
2016-02-27 02:16:08 +08:00
|
|
|
unlock:
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2016-02-27 02:16:08 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
return err;
|
2016-02-27 02:16:08 +08:00
|
|
|
}
|
|
|
|
|
2016-06-21 01:13:58 +08:00
|
|
|
static int
|
|
|
|
mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
|
|
|
|
const struct switchdev_obj_port_vlan *vlan,
|
|
|
|
struct switchdev_trans *trans)
|
2015-11-02 01:33:55 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-02-13 01:09:40 +08:00
|
|
|
int err;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
|
2016-05-10 01:22:47 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2016-02-13 01:09:40 +08:00
|
|
|
/* If the requested port doesn't belong to the same bridge as the VLAN
|
|
|
|
* members, do not support it (yet) and fallback to software VLAN.
|
|
|
|
*/
|
|
|
|
err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
|
|
|
|
vlan->vid_end);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2015-11-02 01:33:55 +08:00
|
|
|
/* We don't need any dynamic resource from the kernel (yet),
|
|
|
|
* so skip the prepare phase.
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port,
|
2016-04-29 09:24:06 +08:00
|
|
|
u16 vid, bool untagged)
|
2015-08-14 00:52:22 +08:00
|
|
|
{
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry vlan;
|
2015-08-14 00:52:22 +08:00
|
|
|
int err;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_vtu_get(chip, vid, &vlan, true);
|
2015-08-14 00:52:22 +08:00
|
|
|
if (err)
|
2015-11-02 01:33:55 +08:00
|
|
|
return err;
|
2015-08-14 00:52:22 +08:00
|
|
|
|
|
|
|
vlan.data[port] = untagged ?
|
|
|
|
GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
|
|
|
|
GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_vtu_loadpurge(chip, &vlan);
|
2015-11-02 01:33:55 +08:00
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
|
|
|
|
const struct switchdev_obj_port_vlan *vlan,
|
|
|
|
struct switchdev_trans *trans)
|
2015-11-02 01:33:55 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2015-11-02 01:33:55 +08:00
|
|
|
bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
|
|
|
|
bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
|
|
|
|
u16 vid;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
|
2016-05-10 01:22:47 +08:00
|
|
|
return;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2015-11-02 01:33:55 +08:00
|
|
|
|
2016-04-06 23:55:05 +08:00
|
|
|
for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
|
2016-06-22 00:28:20 +08:00
|
|
|
if (_mv88e6xxx_port_vlan_add(chip, port, vid, untagged))
|
2016-06-05 03:16:57 +08:00
|
|
|
netdev_err(ds->ports[port].netdev,
|
|
|
|
"failed to add VLAN %d%c\n",
|
2016-04-06 23:55:05 +08:00
|
|
|
vid, untagged ? 'u' : 't');
|
2015-11-02 01:33:55 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (pvid && _mv88e6xxx_port_pvid_set(chip, port, vlan->vid_end))
|
2016-06-05 03:16:57 +08:00
|
|
|
netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n",
|
2016-04-06 23:55:05 +08:00
|
|
|
vlan->vid_end);
|
2015-08-14 00:52:22 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2015-08-14 00:52:22 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip,
|
2016-04-29 09:24:06 +08:00
|
|
|
int port, u16 vid)
|
2015-08-14 00:52:21 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
struct dsa_switch *ds = chip->ds;
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry vlan;
|
2015-08-14 00:52:21 +08:00
|
|
|
int i, err;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
|
2015-08-14 00:52:21 +08:00
|
|
|
if (err)
|
2015-11-02 01:33:55 +08:00
|
|
|
return err;
|
2015-08-14 00:52:21 +08:00
|
|
|
|
2016-02-27 02:16:01 +08:00
|
|
|
/* Tell switchdev if this VLAN is handled in software */
|
|
|
|
if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
|
2016-02-06 03:04:39 +08:00
|
|
|
return -EOPNOTSUPP;
|
2015-08-14 00:52:21 +08:00
|
|
|
|
|
|
|
vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
|
|
|
|
|
|
|
|
/* keep the VLAN unless all ports are excluded */
|
2015-10-12 06:08:36 +08:00
|
|
|
vlan.valid = false;
|
2016-09-30 00:21:57 +08:00
|
|
|
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
|
2015-11-03 23:52:52 +08:00
|
|
|
if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
|
2015-08-14 00:52:21 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
|
2015-10-12 06:08:36 +08:00
|
|
|
vlan.valid = true;
|
2015-08-14 00:52:21 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_vtu_loadpurge(chip, &vlan);
|
2015-11-02 01:33:55 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_atu_remove(chip, vlan.fid, port, false);
|
2015-11-02 01:33:55 +08:00
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
|
|
|
|
const struct switchdev_obj_port_vlan *vlan)
|
2015-11-02 01:33:55 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2015-11-02 01:33:55 +08:00
|
|
|
u16 pvid, vid;
|
|
|
|
int err = 0;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
|
2016-05-10 01:22:47 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2015-11-02 01:33:55 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_port_pvid_get(chip, port, &pvid);
|
2015-08-14 00:52:21 +08:00
|
|
|
if (err)
|
|
|
|
goto unlock;
|
|
|
|
|
2015-11-02 01:33:55 +08:00
|
|
|
for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_port_vlan_del(chip, port, vid);
|
2015-11-02 01:33:55 +08:00
|
|
|
if (err)
|
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
if (vid == pvid) {
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_port_pvid_set(chip, port, 0);
|
2015-11-02 01:33:55 +08:00
|
|
|
if (err)
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-14 00:52:21 +08:00
|
|
|
unlock:
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2015-08-14 00:52:21 +08:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_chip *chip,
|
2015-08-10 21:09:48 +08:00
|
|
|
const unsigned char *addr)
|
2015-03-27 09:36:38 +08:00
|
|
|
{
|
2016-09-30 00:21:53 +08:00
|
|
|
int i, err;
|
2015-03-27 09:36:38 +08:00
|
|
|
|
|
|
|
for (i = 0; i < 3; i++) {
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_MAC_01 + i,
|
|
|
|
(addr[i * 2] << 8) | addr[i * 2 + 1]);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-03-27 09:36:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_chip *chip,
|
2016-04-29 09:24:06 +08:00
|
|
|
unsigned char *addr)
|
2015-03-27 09:36:38 +08:00
|
|
|
{
|
2016-09-30 00:21:53 +08:00
|
|
|
u16 val;
|
|
|
|
int i, err;
|
2015-03-27 09:36:38 +08:00
|
|
|
|
|
|
|
for (i = 0; i < 3; i++) {
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_MAC_01 + i, &val);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
addr[i * 2] = val >> 8;
|
|
|
|
addr[i * 2 + 1] = val & 0xff;
|
2015-03-27 09:36:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_atu_load(struct mv88e6xxx_chip *chip,
|
2015-08-10 21:09:50 +08:00
|
|
|
struct mv88e6xxx_atu_entry *entry)
|
2015-03-27 09:36:38 +08:00
|
|
|
{
|
2015-08-06 13:44:07 +08:00
|
|
|
int ret;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
ret = _mv88e6xxx_atu_wait(chip);
|
2015-03-27 09:36:38 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
ret = _mv88e6xxx_atu_mac_write(chip, entry->mac);
|
2015-03-27 09:36:38 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
ret = _mv88e6xxx_atu_data_write(chip, entry);
|
2015-08-10 21:09:50 +08:00
|
|
|
if (ret < 0)
|
2015-08-06 13:44:08 +08:00
|
|
|
return ret;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_atu_cmd(chip, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
|
2015-08-10 21:09:50 +08:00
|
|
|
}
|
2015-08-06 13:44:08 +08:00
|
|
|
|
2016-09-20 07:56:11 +08:00
|
|
|
static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
|
|
|
|
struct mv88e6xxx_atu_entry *entry);
|
|
|
|
|
|
|
|
static int mv88e6xxx_atu_get(struct mv88e6xxx_chip *chip, int fid,
|
|
|
|
const u8 *addr, struct mv88e6xxx_atu_entry *entry)
|
|
|
|
{
|
|
|
|
struct mv88e6xxx_atu_entry next;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
eth_broadcast_addr(next.mac);
|
|
|
|
|
|
|
|
err = _mv88e6xxx_atu_mac_write(chip, next.mac);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
do {
|
|
|
|
err = _mv88e6xxx_atu_getnext(chip, fid, &next);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (next.state == GLOBAL_ATU_DATA_STATE_UNUSED)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (ether_addr_equal(next.mac, addr)) {
|
|
|
|
*entry = next;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} while (!is_broadcast_ether_addr(next.mac));
|
|
|
|
|
|
|
|
memset(entry, 0, sizeof(*entry));
|
|
|
|
entry->fid = fid;
|
|
|
|
ether_addr_copy(entry->mac, addr);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-08-31 23:50:04 +08:00
|
|
|
static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
const unsigned char *addr, u16 vid,
|
|
|
|
u8 state)
|
2015-08-10 21:09:50 +08:00
|
|
|
{
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry vlan;
|
2016-09-20 07:56:11 +08:00
|
|
|
struct mv88e6xxx_atu_entry entry;
|
2016-02-27 02:16:03 +08:00
|
|
|
int err;
|
|
|
|
|
2016-02-27 02:16:04 +08:00
|
|
|
/* Null VLAN ID corresponds to the port private database */
|
|
|
|
if (vid == 0)
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_port_fid_get(chip, port, &vlan.fid);
|
2016-02-27 02:16:04 +08:00
|
|
|
else
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
|
2016-02-27 02:16:03 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-10 21:09:50 +08:00
|
|
|
|
2016-09-20 07:56:11 +08:00
|
|
|
err = mv88e6xxx_atu_get(chip, vlan.fid, addr, &entry);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* Purge the ATU entry only if no port is using it anymore */
|
|
|
|
if (state == GLOBAL_ATU_DATA_STATE_UNUSED) {
|
|
|
|
entry.portv_trunkid &= ~BIT(port);
|
|
|
|
if (!entry.portv_trunkid)
|
|
|
|
entry.state = GLOBAL_ATU_DATA_STATE_UNUSED;
|
|
|
|
} else {
|
|
|
|
entry.portv_trunkid |= BIT(port);
|
|
|
|
entry.state = state;
|
2015-08-10 21:09:50 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
return _mv88e6xxx_atu_load(chip, &entry);
|
2015-08-06 13:44:08 +08:00
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
|
|
|
|
const struct switchdev_obj_port_fdb *fdb,
|
|
|
|
struct switchdev_trans *trans)
|
2015-10-08 23:35:12 +08:00
|
|
|
{
|
|
|
|
/* We don't need any dynamic resource from the kernel (yet),
|
|
|
|
* so skip the prepare phase.
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
|
|
|
|
const struct switchdev_obj_port_fdb *fdb,
|
|
|
|
struct switchdev_trans *trans)
|
2015-08-06 13:44:08 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2015-08-06 13:44:08 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2016-08-31 23:50:04 +08:00
|
|
|
if (mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
|
|
|
|
GLOBAL_ATU_DATA_STATE_UC_STATIC))
|
|
|
|
netdev_err(ds->ports[port].netdev, "failed to load unicast MAC address\n");
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2015-08-06 13:44:08 +08:00
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
|
|
|
|
const struct switchdev_obj_port_fdb *fdb)
|
2015-08-06 13:44:08 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-08-31 23:50:04 +08:00
|
|
|
int err;
|
2015-08-06 13:44:08 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2016-08-31 23:50:04 +08:00
|
|
|
err = mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
|
|
|
|
GLOBAL_ATU_DATA_STATE_UNUSED);
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2015-08-06 13:44:08 +08:00
|
|
|
|
2016-08-31 23:50:04 +08:00
|
|
|
return err;
|
2015-08-06 13:44:08 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
|
2015-08-10 21:09:51 +08:00
|
|
|
struct mv88e6xxx_atu_entry *entry)
|
2015-08-06 13:44:07 +08:00
|
|
|
{
|
2015-08-10 21:09:51 +08:00
|
|
|
struct mv88e6xxx_atu_entry next = { 0 };
|
2016-09-30 00:21:53 +08:00
|
|
|
u16 val;
|
|
|
|
int err;
|
2015-08-10 21:09:51 +08:00
|
|
|
|
|
|
|
next.fid = fid;
|
2015-03-27 09:36:38 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = _mv88e6xxx_atu_wait(chip);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-06 13:44:07 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = _mv88e6xxx_atu_cmd(chip, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-06 13:44:07 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = _mv88e6xxx_atu_mac_read(chip, next.mac);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-06 13:44:07 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_DATA, &val);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-08-06 13:44:07 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
next.state = val & GLOBAL_ATU_DATA_STATE_MASK;
|
2015-08-10 21:09:51 +08:00
|
|
|
if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
|
|
|
|
unsigned int mask, shift;
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
if (val & GLOBAL_ATU_DATA_TRUNK) {
|
2015-08-10 21:09:51 +08:00
|
|
|
next.trunk = true;
|
|
|
|
mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
|
|
|
|
shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
|
|
|
|
} else {
|
|
|
|
next.trunk = false;
|
|
|
|
mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
|
|
|
|
shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
|
|
|
|
}
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
next.portv_trunkid = (val & mask) >> shift;
|
2015-08-10 21:09:51 +08:00
|
|
|
}
|
2015-08-12 03:00:37 +08:00
|
|
|
|
2015-08-10 21:09:51 +08:00
|
|
|
*entry = next;
|
2015-08-12 03:00:37 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-08-31 23:50:04 +08:00
|
|
|
static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
|
|
|
|
u16 fid, u16 vid, int port,
|
|
|
|
struct switchdev_obj *obj,
|
|
|
|
int (*cb)(struct switchdev_obj *obj))
|
2016-02-27 02:16:02 +08:00
|
|
|
{
|
|
|
|
struct mv88e6xxx_atu_entry addr = {
|
|
|
|
.mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
|
|
|
|
};
|
|
|
|
int err;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_atu_mac_write(chip, addr.mac);
|
2016-02-27 02:16:02 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
do {
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_atu_getnext(chip, fid, &addr);
|
2016-02-27 02:16:02 +08:00
|
|
|
if (err)
|
2016-08-31 23:50:04 +08:00
|
|
|
return err;
|
2016-02-27 02:16:02 +08:00
|
|
|
|
|
|
|
if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
|
|
|
|
break;
|
|
|
|
|
2016-08-31 23:50:04 +08:00
|
|
|
if (addr.trunk || (addr.portv_trunkid & BIT(port)) == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (obj->id == SWITCHDEV_OBJ_ID_PORT_FDB) {
|
|
|
|
struct switchdev_obj_port_fdb *fdb;
|
2016-02-27 02:16:02 +08:00
|
|
|
|
2016-08-31 23:50:04 +08:00
|
|
|
if (!is_unicast_ether_addr(addr.mac))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
fdb = SWITCHDEV_OBJ_PORT_FDB(obj);
|
2016-02-27 02:16:02 +08:00
|
|
|
fdb->vid = vid;
|
|
|
|
ether_addr_copy(fdb->addr, addr.mac);
|
2016-08-31 23:50:04 +08:00
|
|
|
if (addr.state == GLOBAL_ATU_DATA_STATE_UC_STATIC)
|
|
|
|
fdb->ndm_state = NUD_NOARP;
|
|
|
|
else
|
|
|
|
fdb->ndm_state = NUD_REACHABLE;
|
2016-08-31 23:50:05 +08:00
|
|
|
} else if (obj->id == SWITCHDEV_OBJ_ID_PORT_MDB) {
|
|
|
|
struct switchdev_obj_port_mdb *mdb;
|
|
|
|
|
|
|
|
if (!is_multicast_ether_addr(addr.mac))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
|
|
|
|
mdb->vid = vid;
|
|
|
|
ether_addr_copy(mdb->addr, addr.mac);
|
2016-08-31 23:50:04 +08:00
|
|
|
} else {
|
|
|
|
return -EOPNOTSUPP;
|
2016-02-27 02:16:02 +08:00
|
|
|
}
|
2016-08-31 23:50:04 +08:00
|
|
|
|
|
|
|
err = cb(obj);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-02-27 02:16:02 +08:00
|
|
|
} while (!is_broadcast_ether_addr(addr.mac));
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-08-31 23:50:04 +08:00
|
|
|
static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
struct switchdev_obj *obj,
|
|
|
|
int (*cb)(struct switchdev_obj *obj))
|
2015-10-22 21:34:41 +08:00
|
|
|
{
|
2016-09-30 00:21:58 +08:00
|
|
|
struct mv88e6xxx_vtu_entry vlan = {
|
2015-10-22 21:34:41 +08:00
|
|
|
.vid = GLOBAL_VTU_VID_MASK, /* all ones */
|
|
|
|
};
|
2016-02-27 02:16:04 +08:00
|
|
|
u16 fid;
|
2015-10-22 21:34:41 +08:00
|
|
|
int err;
|
|
|
|
|
2016-02-27 02:16:04 +08:00
|
|
|
/* Dump port's default Filtering Information Database (VLAN ID 0) */
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_port_fid_get(chip, port, &fid);
|
2016-02-27 02:16:04 +08:00
|
|
|
if (err)
|
2016-08-31 23:50:04 +08:00
|
|
|
return err;
|
2016-02-27 02:16:04 +08:00
|
|
|
|
2016-08-31 23:50:04 +08:00
|
|
|
err = mv88e6xxx_port_db_dump_fid(chip, fid, 0, port, obj, cb);
|
2016-02-27 02:16:04 +08:00
|
|
|
if (err)
|
2016-08-31 23:50:04 +08:00
|
|
|
return err;
|
2016-02-27 02:16:04 +08:00
|
|
|
|
2016-02-27 02:16:02 +08:00
|
|
|
/* Dump VLANs' Filtering Information Databases */
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_vtu_vid_write(chip, vlan.vid);
|
2015-10-22 21:34:41 +08:00
|
|
|
if (err)
|
2016-08-31 23:50:04 +08:00
|
|
|
return err;
|
2015-10-22 21:34:41 +08:00
|
|
|
|
|
|
|
do {
|
2016-06-22 00:28:20 +08:00
|
|
|
err = _mv88e6xxx_vtu_getnext(chip, &vlan);
|
2015-10-22 21:34:41 +08:00
|
|
|
if (err)
|
2016-08-31 23:50:04 +08:00
|
|
|
return err;
|
2015-10-22 21:34:41 +08:00
|
|
|
|
|
|
|
if (!vlan.valid)
|
|
|
|
break;
|
|
|
|
|
2016-08-31 23:50:04 +08:00
|
|
|
err = mv88e6xxx_port_db_dump_fid(chip, vlan.fid, vlan.vid, port,
|
|
|
|
obj, cb);
|
2015-10-22 21:34:41 +08:00
|
|
|
if (err)
|
2016-08-31 23:50:04 +08:00
|
|
|
return err;
|
2015-10-22 21:34:41 +08:00
|
|
|
} while (vlan.vid < GLOBAL_VTU_VID_MASK);
|
|
|
|
|
2016-08-31 23:50:04 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
|
|
|
|
struct switchdev_obj_port_fdb *fdb,
|
|
|
|
int (*cb)(struct switchdev_obj *obj))
|
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-08-31 23:50:04 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
err = mv88e6xxx_port_db_dump(chip, port, &fdb->obj, cb);
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2015-10-22 21:34:41 +08:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
|
|
|
|
struct net_device *bridge)
|
2015-11-05 06:23:40 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-04-26 06:11:22 +08:00
|
|
|
int i, err = 0;
|
2016-02-27 02:16:05 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2016-02-27 02:16:05 +08:00
|
|
|
|
2016-02-27 02:16:06 +08:00
|
|
|
/* Assign the bridge and remap each port's VLANTable */
|
2016-06-22 00:28:20 +08:00
|
|
|
chip->ports[port].bridge_dev = bridge;
|
2016-02-27 02:16:06 +08:00
|
|
|
|
2016-09-30 00:21:57 +08:00
|
|
|
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
|
2016-06-22 00:28:20 +08:00
|
|
|
if (chip->ports[i].bridge_dev == bridge) {
|
|
|
|
err = _mv88e6xxx_port_based_vlan_map(chip, i);
|
2016-02-27 02:16:06 +08:00
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2016-02-13 01:09:39 +08:00
|
|
|
|
2016-02-27 02:16:05 +08:00
|
|
|
return err;
|
2015-11-05 06:23:40 +08:00
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
|
2016-02-06 03:07:14 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-06-22 00:28:20 +08:00
|
|
|
struct net_device *bridge = chip->ports[port].bridge_dev;
|
2016-03-14 04:21:33 +08:00
|
|
|
int i;
|
2016-02-27 02:16:05 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2016-02-27 02:16:05 +08:00
|
|
|
|
2016-02-27 02:16:06 +08:00
|
|
|
/* Unassign the bridge and remap each port's VLANTable */
|
2016-06-22 00:28:20 +08:00
|
|
|
chip->ports[port].bridge_dev = NULL;
|
2016-02-27 02:16:06 +08:00
|
|
|
|
2016-09-30 00:21:57 +08:00
|
|
|
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
|
2016-06-22 00:28:20 +08:00
|
|
|
if (i == port || chip->ports[i].bridge_dev == bridge)
|
|
|
|
if (_mv88e6xxx_port_based_vlan_map(chip, i))
|
2016-06-05 03:16:57 +08:00
|
|
|
netdev_warn(ds->ports[i].netdev,
|
|
|
|
"failed to remap\n");
|
2016-02-27 02:16:06 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2016-02-06 03:07:14 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
|
2016-05-10 01:22:49 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE);
|
2016-05-10 01:22:49 +08:00
|
|
|
u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
|
2016-06-22 00:28:20 +08:00
|
|
|
struct gpio_desc *gpiod = chip->reset;
|
2016-05-10 01:22:49 +08:00
|
|
|
unsigned long timeout;
|
2016-09-21 07:40:31 +08:00
|
|
|
u16 reg;
|
2016-09-30 00:21:53 +08:00
|
|
|
int err;
|
2016-05-10 01:22:49 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Set all ports to the disabled state. */
|
2016-09-30 00:21:57 +08:00
|
|
|
for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, i, PORT_CONTROL, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-05-10 01:22:49 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, i, PORT_CONTROL,
|
|
|
|
reg & 0xfffc);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-05-10 01:22:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait for transmit queues to drain. */
|
|
|
|
usleep_range(2000, 4000);
|
|
|
|
|
|
|
|
/* If there is a gpio connected to the reset pin, toggle it */
|
|
|
|
if (gpiod) {
|
|
|
|
gpiod_set_value_cansleep(gpiod, 1);
|
|
|
|
usleep_range(10000, 20000);
|
|
|
|
gpiod_set_value_cansleep(gpiod, 0);
|
|
|
|
usleep_range(10000, 20000);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reset the switch. Keep the PPU active if requested. The PPU
|
|
|
|
* needs to be active to support indirect phy register access
|
|
|
|
* through global registers 0x18 and 0x19.
|
|
|
|
*/
|
|
|
|
if (ppu_active)
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, 0x04, 0xc000);
|
2016-05-10 01:22:49 +08:00
|
|
|
else
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, 0x04, 0xc400);
|
2016-09-21 07:40:31 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
2016-05-10 01:22:49 +08:00
|
|
|
|
|
|
|
/* Wait up to one second for reset to complete. */
|
|
|
|
timeout = jiffies + 1 * HZ;
|
|
|
|
while (time_before(jiffies, timeout)) {
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, 0x00, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-05-10 01:22:49 +08:00
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
if ((reg & is_reset) == is_reset)
|
2016-05-10 01:22:49 +08:00
|
|
|
break;
|
|
|
|
usleep_range(1000, 2000);
|
|
|
|
}
|
|
|
|
if (time_after(jiffies, timeout))
|
2016-09-21 07:40:31 +08:00
|
|
|
err = -ETIMEDOUT;
|
2016-05-10 01:22:49 +08:00
|
|
|
else
|
2016-09-21 07:40:31 +08:00
|
|
|
err = 0;
|
2016-05-10 01:22:49 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
return err;
|
2016-05-10 01:22:49 +08:00
|
|
|
}
|
|
|
|
|
2016-08-16 05:19:01 +08:00
|
|
|
static int mv88e6xxx_serdes_power_on(struct mv88e6xxx_chip *chip)
|
2016-03-30 09:39:41 +08:00
|
|
|
{
|
2016-08-16 05:19:01 +08:00
|
|
|
u16 val;
|
|
|
|
int err;
|
2016-03-30 09:39:41 +08:00
|
|
|
|
2016-08-16 05:19:01 +08:00
|
|
|
/* Clear Power Down bit */
|
|
|
|
err = mv88e6xxx_serdes_read(chip, MII_BMCR, &val);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-03-30 09:39:41 +08:00
|
|
|
|
2016-08-16 05:19:01 +08:00
|
|
|
if (val & BMCR_PDOWN) {
|
|
|
|
val &= ~BMCR_PDOWN;
|
|
|
|
err = mv88e6xxx_serdes_write(chip, MII_BMCR, val);
|
2016-03-30 09:39:41 +08:00
|
|
|
}
|
|
|
|
|
2016-08-16 05:19:01 +08:00
|
|
|
return err;
|
2016-03-30 09:39:41 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
|
2015-03-27 09:36:29 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
struct dsa_switch *ds = chip->ds;
|
2016-09-21 07:40:31 +08:00
|
|
|
int err;
|
2015-05-06 07:09:47 +08:00
|
|
|
u16 reg;
|
2015-03-27 09:36:29 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
|
|
|
|
mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
|
|
|
|
mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) ||
|
|
|
|
mv88e6xxx_6065_family(chip) || mv88e6xxx_6320_family(chip)) {
|
2015-05-06 07:09:47 +08:00
|
|
|
/* MAC Forcing register: don't force link, speed,
|
|
|
|
* duplex or flow control state to any particular
|
|
|
|
* values on physical ports, but force the CPU port
|
|
|
|
* and all DSA ports to their maximum bandwidth and
|
|
|
|
* full duplex.
|
|
|
|
*/
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®);
|
2015-08-18 05:52:51 +08:00
|
|
|
if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
|
net: dsa: actually force the speed on the CPU port
Commit 54d792f257c6 ("net: dsa: Centralise global and port setup
code into mv88e6xxx.") merged in the 4.2 merge window broke the link
speed forcing for the CPU port of Marvell DSA switches. The original
code was:
/* MAC Forcing register: don't force link, speed, duplex
* or flow control state to any particular values on physical
* ports, but force the CPU port and all DSA ports to 1000 Mb/s
* full duplex.
*/
if (dsa_is_cpu_port(ds, p) || ds->dsa_port_mask & (1 << p))
REG_WRITE(addr, 0x01, 0x003e);
else
REG_WRITE(addr, 0x01, 0x0003);
but the new code does a read-modify-write:
reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_PCS_CTRL);
if (dsa_is_cpu_port(ds, port) ||
ds->dsa_port_mask & (1 << port)) {
reg |= PORT_PCS_CTRL_FORCE_LINK |
PORT_PCS_CTRL_LINK_UP |
PORT_PCS_CTRL_DUPLEX_FULL |
PORT_PCS_CTRL_FORCE_DUPLEX;
if (mv88e6xxx_6065_family(ds))
reg |= PORT_PCS_CTRL_100;
else
reg |= PORT_PCS_CTRL_1000;
The link speed in the PCS control register is a two bit field. Forcing
the link speed in this way doesn't ensure that the bit field is set to
the correct value - on the hardware I have here, the speed bitfield
remains set to 0x03, resulting in the speed not being forced to gigabit.
We must clear both bits before forcing the link speed.
Fixes: 54d792f257c6 ("net: dsa: Centralise global and port setup code into mv88e6xxx.")
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Acked-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-09-22 04:42:59 +08:00
|
|
|
reg &= ~PORT_PCS_CTRL_UNFORCED;
|
2015-05-06 07:09:47 +08:00
|
|
|
reg |= PORT_PCS_CTRL_FORCE_LINK |
|
|
|
|
PORT_PCS_CTRL_LINK_UP |
|
|
|
|
PORT_PCS_CTRL_DUPLEX_FULL |
|
|
|
|
PORT_PCS_CTRL_FORCE_DUPLEX;
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_6065_family(chip))
|
2015-05-06 07:09:47 +08:00
|
|
|
reg |= PORT_PCS_CTRL_100;
|
|
|
|
else
|
|
|
|
reg |= PORT_PCS_CTRL_1000;
|
|
|
|
} else {
|
|
|
|
reg |= PORT_PCS_CTRL_UNFORCED;
|
|
|
|
}
|
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-05-06 07:09:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
|
|
|
|
* disable Header mode, enable IGMP/MLD snooping, disable VLAN
|
|
|
|
* tunneling, determine priority by looking at 802.1p and IP
|
|
|
|
* priority fields (IP prio has precedence), and set STP state
|
|
|
|
* to Forwarding.
|
|
|
|
*
|
|
|
|
* If this is the CPU link, use DSA or EDSA tagging depending
|
|
|
|
* on which tagging mode was configured.
|
|
|
|
*
|
|
|
|
* If this is a link to another switch, use DSA tagging mode.
|
|
|
|
*
|
|
|
|
* If this is the upstream port for this switch, enable
|
|
|
|
* forwarding of unknown unicasts and multicasts.
|
|
|
|
*/
|
|
|
|
reg = 0;
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
|
|
|
|
mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
|
|
|
|
mv88e6xxx_6095_family(chip) || mv88e6xxx_6065_family(chip) ||
|
|
|
|
mv88e6xxx_6185_family(chip) || mv88e6xxx_6320_family(chip))
|
2015-05-06 07:09:47 +08:00
|
|
|
reg = PORT_CONTROL_IGMP_MLD_SNOOP |
|
|
|
|
PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
|
|
|
|
PORT_CONTROL_STATE_FORWARDING;
|
|
|
|
if (dsa_is_cpu_port(ds, port)) {
|
2016-08-22 22:01:02 +08:00
|
|
|
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA))
|
2016-06-05 03:17:02 +08:00
|
|
|
reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA |
|
2015-09-29 07:50:56 +08:00
|
|
|
PORT_CONTROL_FORWARD_UNKNOWN_MC;
|
2016-08-22 22:01:02 +08:00
|
|
|
else
|
|
|
|
reg |= PORT_CONTROL_DSA_TAG;
|
2016-08-22 22:01:04 +08:00
|
|
|
reg |= PORT_CONTROL_EGRESS_ADD_TAG |
|
|
|
|
PORT_CONTROL_FORWARD_UNKNOWN;
|
2015-05-06 07:09:47 +08:00
|
|
|
}
|
2015-08-18 05:52:52 +08:00
|
|
|
if (dsa_is_dsa_port(ds, port)) {
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_6095_family(chip) ||
|
|
|
|
mv88e6xxx_6185_family(chip))
|
2015-08-18 05:52:52 +08:00
|
|
|
reg |= PORT_CONTROL_DSA_TAG;
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_6352_family(chip) ||
|
|
|
|
mv88e6xxx_6351_family(chip) ||
|
|
|
|
mv88e6xxx_6165_family(chip) ||
|
|
|
|
mv88e6xxx_6097_family(chip) ||
|
|
|
|
mv88e6xxx_6320_family(chip)) {
|
2015-05-06 07:09:47 +08:00
|
|
|
reg |= PORT_CONTROL_FRAME_MODE_DSA;
|
2015-08-18 05:52:52 +08:00
|
|
|
}
|
|
|
|
|
2015-05-06 07:09:47 +08:00
|
|
|
if (port == dsa_upstream_port(ds))
|
|
|
|
reg |= PORT_CONTROL_FORWARD_UNKNOWN |
|
|
|
|
PORT_CONTROL_FORWARD_UNKNOWN_MC;
|
|
|
|
}
|
|
|
|
if (reg) {
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-05-06 07:09:47 +08:00
|
|
|
}
|
|
|
|
|
2016-03-30 09:39:41 +08:00
|
|
|
/* If this port is connected to a SerDes, make sure the SerDes is not
|
|
|
|
* powered down.
|
|
|
|
*/
|
2016-08-16 05:19:01 +08:00
|
|
|
if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_SERDES)) {
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, PORT_STATUS, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
reg &= PORT_STATUS_CMODE_MASK;
|
|
|
|
if ((reg == PORT_STATUS_CMODE_100BASE_X) ||
|
|
|
|
(reg == PORT_STATUS_CMODE_1000BASE_X) ||
|
|
|
|
(reg == PORT_STATUS_CMODE_SGMII)) {
|
|
|
|
err = mv88e6xxx_serdes_power_on(chip);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2016-03-30 09:39:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-14 00:52:23 +08:00
|
|
|
/* Port Control 2: don't force a good FCS, set the maximum frame size to
|
2016-02-27 02:16:07 +08:00
|
|
|
* 10240 bytes, disable 802.1q tags checking, don't discard tagged or
|
2015-08-14 00:52:23 +08:00
|
|
|
* untagged frames on this port, do a destination address lookup on all
|
|
|
|
* received packets as usual, disable ARP mirroring and don't send a
|
|
|
|
* copy of all transmitted/received frames on this port to the CPU.
|
2015-05-06 07:09:47 +08:00
|
|
|
*/
|
|
|
|
reg = 0;
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
|
|
|
|
mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
|
|
|
|
mv88e6xxx_6095_family(chip) || mv88e6xxx_6320_family(chip) ||
|
|
|
|
mv88e6xxx_6185_family(chip))
|
2015-05-06 07:09:47 +08:00
|
|
|
reg = PORT_CONTROL_2_MAP_DA;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
|
|
|
|
mv88e6xxx_6165_family(chip) || mv88e6xxx_6320_family(chip))
|
2015-05-06 07:09:47 +08:00
|
|
|
reg |= PORT_CONTROL_2_JUMBO_10240;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_6095_family(chip) || mv88e6xxx_6185_family(chip)) {
|
2015-05-06 07:09:47 +08:00
|
|
|
/* Set the upstream port this port should use */
|
|
|
|
reg |= dsa_upstream_port(ds);
|
|
|
|
/* enable forwarding of unknown multicast addresses to
|
|
|
|
* the upstream port
|
|
|
|
*/
|
|
|
|
if (port == dsa_upstream_port(ds))
|
|
|
|
reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
2016-02-27 02:16:07 +08:00
|
|
|
reg |= PORT_CONTROL_2_8021Q_DISABLED;
|
2015-08-14 00:52:23 +08:00
|
|
|
|
2015-05-06 07:09:47 +08:00
|
|
|
if (reg) {
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-05-06 07:09:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Port Association Vector: when learning source addresses
|
|
|
|
* of packets, add the address to the address database using
|
|
|
|
* a port bitmap that has only the bit for this port set and
|
|
|
|
* the other bits clear.
|
|
|
|
*/
|
2015-11-03 23:52:36 +08:00
|
|
|
reg = 1 << port;
|
2016-04-15 02:42:08 +08:00
|
|
|
/* Disable learning for CPU port */
|
|
|
|
if (dsa_is_cpu_port(ds, port))
|
2016-04-15 02:42:07 +08:00
|
|
|
reg = 0;
|
2015-11-03 23:52:36 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_ASSOC_VECTOR, reg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-05-06 07:09:47 +08:00
|
|
|
|
|
|
|
/* Egress rate control 2: disable egress rate control. */
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL_2, 0x0000);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-05-06 07:09:47 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
|
|
|
|
mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
|
|
|
|
mv88e6xxx_6320_family(chip)) {
|
2015-05-06 07:09:47 +08:00
|
|
|
/* Do not limit the period of time that this port can
|
|
|
|
* be paused for by the remote end or the period of
|
|
|
|
* time that this port can pause the remote end.
|
|
|
|
*/
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, 0x0000);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-05-06 07:09:47 +08:00
|
|
|
|
|
|
|
/* Port ATU control: disable limiting the number of
|
|
|
|
* address database entries that this port is allowed
|
|
|
|
* to use.
|
|
|
|
*/
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_ATU_CONTROL,
|
|
|
|
0x0000);
|
2015-05-06 07:09:47 +08:00
|
|
|
/* Priority Override: disable DA, SA and VTU priority
|
|
|
|
* override.
|
|
|
|
*/
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_PRI_OVERRIDE,
|
|
|
|
0x0000);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-05-06 07:09:47 +08:00
|
|
|
|
|
|
|
/* Port Ethertype: use the Ethertype DSA Ethertype
|
|
|
|
* value.
|
|
|
|
*/
|
2016-08-22 22:01:02 +08:00
|
|
|
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA)) {
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_ETH_TYPE,
|
|
|
|
ETH_P_EDSA);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-08-22 22:01:02 +08:00
|
|
|
}
|
|
|
|
|
2015-05-06 07:09:47 +08:00
|
|
|
/* Tag Remap: use an identity 802.1p prio -> switch
|
|
|
|
* prio mapping.
|
|
|
|
*/
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_0123,
|
|
|
|
0x3210);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-05-06 07:09:47 +08:00
|
|
|
|
|
|
|
/* Tag Remap 2: use an identity 802.1p prio -> switch
|
|
|
|
* prio mapping.
|
|
|
|
*/
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_4567,
|
|
|
|
0x7654);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-05-06 07:09:47 +08:00
|
|
|
}
|
|
|
|
|
2016-08-23 05:47:08 +08:00
|
|
|
/* Rate Control: disable ingress rate limiting. */
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
|
|
|
|
mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
|
|
|
|
mv88e6xxx_6320_family(chip)) {
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL,
|
|
|
|
0x0001);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-08-23 05:47:08 +08:00
|
|
|
} else if (mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip)) {
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL,
|
|
|
|
0x0000);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-05-06 07:09:47 +08:00
|
|
|
}
|
|
|
|
|
2015-03-27 09:36:30 +08:00
|
|
|
/* Port Control 1: disable trunking, disable sending
|
|
|
|
* learning messages to this port.
|
2015-03-27 09:36:29 +08:00
|
|
|
*/
|
2016-09-21 07:40:31 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, 0x0000);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-03-27 09:36:29 +08:00
|
|
|
|
net: dsa: mv88e6xxx: share the same default FDB
For hardware cross-chip bridging to work, user ports *and* DSA ports
need to share a common address database, in order to switch a frame to
the correct interconnected device.
This is currently working for VLAN filtering aware systems, since Linux
will implement a bridge group as a 802.1Q VLAN, which has its own FDB,
including DSA and CPU links as members.
However when the system doesn't support VLAN filtering, Linux only
relies on the port-based VLAN to implement a bridge group.
To fix hardware cross-chip bridging for such systems, set the same
default address database 0 for user and DSA ports, instead of giving
them all a different default database.
Note that the bridging code prevents frames to egress between unbridged
ports, and flushes FDB entries of a port when changing its STP state.
Also note that the FID 0 is special and means "all" for ATU operations,
but it's OK since it is used as a default forwarding address database.
Fixes: 2db9ce1fd9a3 ("net: dsa: mv88e6xxx: assign default FDB to ports")
Fixes: 466dfa077022 ("net: dsa: mv88e6xxx: assign dynamic FDB to bridges")
Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-15 02:42:09 +08:00
|
|
|
/* Port based VLAN map: give each port the same default address
|
2016-02-27 02:16:06 +08:00
|
|
|
* database, and allow bidirectional communication between the
|
|
|
|
* CPU and DSA port(s), and the other ports.
|
2015-03-27 09:36:29 +08:00
|
|
|
*/
|
2016-09-21 07:40:31 +08:00
|
|
|
err = _mv88e6xxx_port_fid_set(chip, port, 0);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-02-27 02:16:04 +08:00
|
|
|
|
2016-09-21 07:40:31 +08:00
|
|
|
err = _mv88e6xxx_port_based_vlan_map(chip, port);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-03-27 09:36:29 +08:00
|
|
|
|
|
|
|
/* Default VLAN ID and priority: don't set a default VLAN
|
|
|
|
* ID, and set the default packet priority to zero.
|
|
|
|
*/
|
2016-09-21 07:40:31 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, 0x0000);
|
2015-05-06 07:09:48 +08:00
|
|
|
}
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
|
2016-07-19 08:45:34 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_01, (addr[0] << 8) | addr[1]);
|
2016-07-19 08:45:34 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]);
|
2016-07-19 08:45:34 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
return 0;
|
2016-07-19 08:45:34 +08:00
|
|
|
}
|
|
|
|
|
2016-07-19 08:45:39 +08:00
|
|
|
static int mv88e6xxx_g1_set_age_time(struct mv88e6xxx_chip *chip,
|
|
|
|
unsigned int msecs)
|
|
|
|
{
|
|
|
|
const unsigned int coeff = chip->info->age_time_coeff;
|
|
|
|
const unsigned int min = 0x01 * coeff;
|
|
|
|
const unsigned int max = 0xff * coeff;
|
|
|
|
u8 age_time;
|
|
|
|
u16 val;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (msecs < min || msecs > max)
|
|
|
|
return -ERANGE;
|
|
|
|
|
|
|
|
/* Round to nearest multiple of coeff */
|
|
|
|
age_time = (msecs + coeff / 2) / coeff;
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val);
|
2016-07-19 08:45:39 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* AgeTime is 11:4 bits */
|
|
|
|
val &= ~0xff0;
|
|
|
|
val |= age_time << 4;
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
return mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val);
|
2016-07-19 08:45:39 +08:00
|
|
|
}
|
|
|
|
|
2016-07-19 08:45:40 +08:00
|
|
|
static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds,
|
|
|
|
unsigned int ageing_time)
|
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-07-19 08:45:40 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
err = mv88e6xxx_g1_set_age_time(chip, ageing_time);
|
|
|
|
mutex_unlock(&chip->reg_lock);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-07-19 08:45:30 +08:00
|
|
|
static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)
|
2015-03-27 09:36:28 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
struct dsa_switch *ds = chip->ds;
|
2016-05-10 01:22:53 +08:00
|
|
|
u32 upstream_port = dsa_upstream_port(ds);
|
2016-05-10 01:22:51 +08:00
|
|
|
u16 reg;
|
2016-05-10 01:22:49 +08:00
|
|
|
int err;
|
2015-05-06 07:09:47 +08:00
|
|
|
|
2016-05-10 01:22:51 +08:00
|
|
|
/* Enable the PHY Polling Unit if present, don't discard any packets,
|
|
|
|
* and mask all interrupt sources.
|
|
|
|
*/
|
2016-10-17 01:56:49 +08:00
|
|
|
err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, ®);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
reg &= ~GLOBAL_CONTROL_PPU_ENABLE;
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU) ||
|
|
|
|
mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE))
|
2016-05-10 01:22:51 +08:00
|
|
|
reg |= GLOBAL_CONTROL_PPU_ENABLE;
|
|
|
|
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg);
|
2016-05-10 01:22:51 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2016-05-10 01:22:53 +08:00
|
|
|
/* Configure the upstream port, and configure it as the port to which
|
|
|
|
* ingress and egress and ARP monitor frames are to be sent.
|
|
|
|
*/
|
|
|
|
reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
|
|
|
|
upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
|
|
|
|
upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg);
|
2016-05-10 01:22:53 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2016-05-10 01:22:54 +08:00
|
|
|
/* Disable remote management, and set the switch's DSA device number. */
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL_2,
|
|
|
|
GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
|
|
|
|
(ds->index & 0x1f));
|
2016-05-10 01:22:54 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2016-07-19 08:45:39 +08:00
|
|
|
/* Clear all the VTU and STU entries */
|
|
|
|
err = _mv88e6xxx_vtu_stu_flush(chip);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2015-05-06 07:09:47 +08:00
|
|
|
/* Set the default address aging time to 5 minutes, and
|
|
|
|
* enable address learn messages to be sent to all message
|
|
|
|
* ports.
|
|
|
|
*/
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL,
|
|
|
|
GLOBAL_ATU_CONTROL_LEARN2ALL);
|
2016-04-15 05:47:12 +08:00
|
|
|
if (err)
|
2016-05-10 01:22:50 +08:00
|
|
|
return err;
|
2015-05-06 07:09:47 +08:00
|
|
|
|
2016-07-19 08:45:39 +08:00
|
|
|
err = mv88e6xxx_g1_set_age_time(chip, 300000);
|
|
|
|
if (err)
|
2016-07-19 08:45:30 +08:00
|
|
|
return err;
|
|
|
|
|
|
|
|
/* Clear all ATU entries */
|
|
|
|
err = _mv88e6xxx_atu_flush(chip, 0, true);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2015-05-06 07:09:47 +08:00
|
|
|
/* Configure the IP ToS mapping registers. */
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_0, 0x0000);
|
2016-04-15 05:47:12 +08:00
|
|
|
if (err)
|
2016-05-10 01:22:50 +08:00
|
|
|
return err;
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_1, 0x0000);
|
2016-04-15 05:47:12 +08:00
|
|
|
if (err)
|
2016-05-10 01:22:50 +08:00
|
|
|
return err;
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_2, 0x5555);
|
2016-04-15 05:47:12 +08:00
|
|
|
if (err)
|
2016-05-10 01:22:50 +08:00
|
|
|
return err;
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_3, 0x5555);
|
2016-04-15 05:47:12 +08:00
|
|
|
if (err)
|
2016-05-10 01:22:50 +08:00
|
|
|
return err;
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_4, 0xaaaa);
|
2016-04-15 05:47:12 +08:00
|
|
|
if (err)
|
2016-05-10 01:22:50 +08:00
|
|
|
return err;
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_5, 0xaaaa);
|
2016-04-15 05:47:12 +08:00
|
|
|
if (err)
|
2016-05-10 01:22:50 +08:00
|
|
|
return err;
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_6, 0xffff);
|
2016-04-15 05:47:12 +08:00
|
|
|
if (err)
|
2016-05-10 01:22:50 +08:00
|
|
|
return err;
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_7, 0xffff);
|
2016-04-15 05:47:12 +08:00
|
|
|
if (err)
|
2016-05-10 01:22:50 +08:00
|
|
|
return err;
|
2015-05-06 07:09:47 +08:00
|
|
|
|
|
|
|
/* Configure the IEEE 802.1p priority mapping register. */
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_IEEE_PRI, 0xfa41);
|
2016-04-15 05:47:12 +08:00
|
|
|
if (err)
|
2016-05-10 01:22:50 +08:00
|
|
|
return err;
|
2015-05-06 07:09:47 +08:00
|
|
|
|
2016-07-19 08:45:30 +08:00
|
|
|
/* Clear the statistics counters for all ports */
|
2016-09-30 00:21:53 +08:00
|
|
|
err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
|
|
|
|
GLOBAL_STATS_OP_FLUSH_ALL);
|
2016-07-19 08:45:30 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* Wait for the flush to complete. */
|
|
|
|
err = _mv88e6xxx_stats_wait(chip);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static int mv88e6xxx_setup(struct dsa_switch *ds)
|
2016-05-10 01:22:50 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-05-10 01:22:50 +08:00
|
|
|
int err;
|
2016-05-10 01:22:56 +08:00
|
|
|
int i;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
chip->ds = ds;
|
|
|
|
ds->slave_mii_bus = chip->mdio_bus;
|
2016-05-10 01:22:50 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2016-05-10 01:22:50 +08:00
|
|
|
|
2016-07-19 08:45:30 +08:00
|
|
|
/* Setup Switch Port Registers */
|
2016-09-30 00:21:57 +08:00
|
|
|
for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
|
2016-07-19 08:45:30 +08:00
|
|
|
err = mv88e6xxx_setup_port(chip, i);
|
|
|
|
if (err)
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Setup Switch Global 1 Registers */
|
|
|
|
err = mv88e6xxx_g1_setup(chip);
|
2016-05-10 01:22:56 +08:00
|
|
|
if (err)
|
|
|
|
goto unlock;
|
|
|
|
|
2016-07-19 08:45:30 +08:00
|
|
|
/* Setup Switch Global 2 Registers */
|
|
|
|
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_GLOBAL2)) {
|
|
|
|
err = mv88e6xxx_g2_setup(chip);
|
2016-05-10 01:22:56 +08:00
|
|
|
if (err)
|
|
|
|
goto unlock;
|
|
|
|
}
|
2016-05-10 01:22:50 +08:00
|
|
|
|
2015-08-14 00:52:18 +08:00
|
|
|
unlock:
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2015-06-21 03:31:29 +08:00
|
|
|
|
2016-04-15 05:47:12 +08:00
|
|
|
return err;
|
2015-05-06 07:09:47 +08:00
|
|
|
}
|
|
|
|
|
2016-07-19 08:45:34 +08:00
|
|
|
static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
|
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-07-19 08:45:34 +08:00
|
|
|
int err;
|
|
|
|
|
2016-09-30 00:22:01 +08:00
|
|
|
if (!chip->info->ops->set_switch_mac)
|
|
|
|
return -EOPNOTSUPP;
|
2016-07-19 08:45:34 +08:00
|
|
|
|
2016-09-30 00:22:01 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
err = chip->info->ops->set_switch_mac(chip, addr);
|
2016-07-19 08:45:34 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-08-16 05:19:00 +08:00
|
|
|
static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
|
2015-04-02 10:06:36 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
struct mv88e6xxx_chip *chip = bus->priv;
|
2016-08-16 05:19:00 +08:00
|
|
|
u16 val;
|
|
|
|
int err;
|
2015-04-02 10:06:36 +08:00
|
|
|
|
2016-09-30 00:21:57 +08:00
|
|
|
if (phy >= mv88e6xxx_num_ports(chip))
|
2016-04-29 09:24:06 +08:00
|
|
|
return 0xffff;
|
2015-04-02 10:06:36 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2016-08-16 05:19:00 +08:00
|
|
|
err = mv88e6xxx_phy_read(chip, phy, reg, &val);
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2016-08-16 05:19:00 +08:00
|
|
|
|
|
|
|
return err ? err : val;
|
2015-04-02 10:06:36 +08:00
|
|
|
}
|
|
|
|
|
2016-08-16 05:19:00 +08:00
|
|
|
static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
|
2015-04-02 10:06:36 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
struct mv88e6xxx_chip *chip = bus->priv;
|
2016-08-16 05:19:00 +08:00
|
|
|
int err;
|
2015-04-02 10:06:36 +08:00
|
|
|
|
2016-09-30 00:21:57 +08:00
|
|
|
if (phy >= mv88e6xxx_num_ports(chip))
|
2016-04-29 09:24:06 +08:00
|
|
|
return 0xffff;
|
2015-04-02 10:06:36 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2016-08-16 05:19:00 +08:00
|
|
|
err = mv88e6xxx_phy_write(chip, phy, reg, val);
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2016-08-16 05:19:00 +08:00
|
|
|
|
|
|
|
return err;
|
2015-04-02 10:06:36 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
|
2016-06-05 03:17:06 +08:00
|
|
|
struct device_node *np)
|
|
|
|
{
|
|
|
|
static int index;
|
|
|
|
struct mii_bus *bus;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (np)
|
2016-06-22 00:28:20 +08:00
|
|
|
chip->mdio_np = of_get_child_by_name(np, "mdio");
|
2016-06-05 03:17:06 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
bus = devm_mdiobus_alloc(chip->dev);
|
2016-06-05 03:17:06 +08:00
|
|
|
if (!bus)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
bus->priv = (void *)chip;
|
2016-06-05 03:17:06 +08:00
|
|
|
if (np) {
|
|
|
|
bus->name = np->full_name;
|
|
|
|
snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name);
|
|
|
|
} else {
|
|
|
|
bus->name = "mv88e6xxx SMI";
|
|
|
|
snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++);
|
|
|
|
}
|
|
|
|
|
|
|
|
bus->read = mv88e6xxx_mdio_read;
|
|
|
|
bus->write = mv88e6xxx_mdio_write;
|
2016-06-22 00:28:20 +08:00
|
|
|
bus->parent = chip->dev;
|
2016-06-05 03:17:06 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (chip->mdio_np)
|
|
|
|
err = of_mdiobus_register(bus, chip->mdio_np);
|
2016-06-05 03:17:06 +08:00
|
|
|
else
|
|
|
|
err = mdiobus_register(bus);
|
|
|
|
if (err) {
|
2016-06-22 00:28:20 +08:00
|
|
|
dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
|
2016-06-05 03:17:06 +08:00
|
|
|
goto out;
|
|
|
|
}
|
2016-06-22 00:28:20 +08:00
|
|
|
chip->mdio_bus = bus;
|
2016-06-05 03:17:06 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out:
|
2016-06-22 00:28:20 +08:00
|
|
|
if (chip->mdio_np)
|
|
|
|
of_node_put(chip->mdio_np);
|
2016-06-05 03:17:06 +08:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_chip *chip)
|
2016-06-05 03:17:06 +08:00
|
|
|
|
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
struct mii_bus *bus = chip->mdio_bus;
|
2016-06-05 03:17:06 +08:00
|
|
|
|
|
|
|
mdiobus_unregister(bus);
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (chip->mdio_np)
|
|
|
|
of_node_put(chip->mdio_np);
|
2016-06-05 03:17:06 +08:00
|
|
|
}
|
|
|
|
|
2015-07-26 00:42:28 +08:00
|
|
|
#ifdef CONFIG_NET_DSA_HWMON
|
|
|
|
|
|
|
|
static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
|
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-08-16 05:19:02 +08:00
|
|
|
u16 val;
|
2015-07-26 00:42:28 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
*temp = 0;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
2015-07-26 00:42:28 +08:00
|
|
|
|
2016-08-16 05:19:02 +08:00
|
|
|
ret = mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x6);
|
2015-07-26 00:42:28 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* Enable temperature sensor */
|
2016-08-16 05:19:02 +08:00
|
|
|
ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val);
|
2015-07-26 00:42:28 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto error;
|
|
|
|
|
2016-08-16 05:19:02 +08:00
|
|
|
ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val | (1 << 5));
|
2015-07-26 00:42:28 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* Wait for temperature to stabilize */
|
|
|
|
usleep_range(10000, 12000);
|
|
|
|
|
2016-08-16 05:19:02 +08:00
|
|
|
ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val);
|
|
|
|
if (ret < 0)
|
2015-07-26 00:42:28 +08:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* Disable temperature sensor */
|
2016-08-16 05:19:02 +08:00
|
|
|
ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val & ~(1 << 5));
|
2015-07-26 00:42:28 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
*temp = ((val & 0x1f) - 5) * 5;
|
|
|
|
|
|
|
|
error:
|
2016-08-16 05:19:02 +08:00
|
|
|
mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x0);
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
2015-07-26 00:42:28 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
|
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-06-22 00:28:20 +08:00
|
|
|
int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
|
2016-08-16 05:19:02 +08:00
|
|
|
u16 val;
|
2015-07-26 00:42:28 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
*temp = 0;
|
|
|
|
|
2016-08-16 05:19:02 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
ret = mv88e6xxx_phy_page_read(chip, phy, 6, 27, &val);
|
|
|
|
mutex_unlock(&chip->reg_lock);
|
2015-07-26 00:42:28 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2016-08-16 05:19:02 +08:00
|
|
|
*temp = (val & 0xff) - 25;
|
2015-07-26 00:42:28 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
|
2015-07-26 00:42:28 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-04-29 09:24:06 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP))
|
2016-05-10 01:22:42 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
|
2015-07-26 00:42:28 +08:00
|
|
|
return mv88e63xx_get_temp(ds, temp);
|
|
|
|
|
|
|
|
return mv88e61xx_get_temp(ds, temp);
|
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
|
2015-07-26 00:42:28 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-06-22 00:28:20 +08:00
|
|
|
int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
|
2016-08-16 05:19:02 +08:00
|
|
|
u16 val;
|
2015-07-26 00:42:28 +08:00
|
|
|
int ret;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
|
2015-07-26 00:42:28 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
*temp = 0;
|
|
|
|
|
2016-08-16 05:19:02 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
|
|
|
|
mutex_unlock(&chip->reg_lock);
|
2015-07-26 00:42:28 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2016-08-16 05:19:02 +08:00
|
|
|
*temp = (((val >> 8) & 0x1f) * 5) - 25;
|
2015-07-26 00:42:28 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
|
2015-07-26 00:42:28 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-06-22 00:28:20 +08:00
|
|
|
int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
|
2016-08-16 05:19:02 +08:00
|
|
|
u16 val;
|
|
|
|
int err;
|
2015-07-26 00:42:28 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
|
2015-07-26 00:42:28 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2016-08-16 05:19:02 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
err = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
|
|
|
|
if (err)
|
|
|
|
goto unlock;
|
2015-07-26 00:42:28 +08:00
|
|
|
temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
|
2016-08-16 05:19:02 +08:00
|
|
|
err = mv88e6xxx_phy_page_write(chip, phy, 6, 26,
|
|
|
|
(val & 0xe0ff) | (temp << 8));
|
|
|
|
unlock:
|
|
|
|
mutex_unlock(&chip->reg_lock);
|
|
|
|
|
|
|
|
return err;
|
2015-07-26 00:42:28 +08:00
|
|
|
}
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
|
2015-07-26 00:42:28 +08:00
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-06-22 00:28:20 +08:00
|
|
|
int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
|
2016-08-16 05:19:02 +08:00
|
|
|
u16 val;
|
2015-07-26 00:42:28 +08:00
|
|
|
int ret;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
|
2015-07-26 00:42:28 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
*alarm = false;
|
|
|
|
|
2016-08-16 05:19:02 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
|
|
|
|
mutex_unlock(&chip->reg_lock);
|
2015-07-26 00:42:28 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2016-08-16 05:19:02 +08:00
|
|
|
*alarm = !!(val & 0x40);
|
2015-07-26 00:42:28 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_DSA_HWMON */
|
|
|
|
|
2016-07-21 06:18:35 +08:00
|
|
|
static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
|
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-07-21 06:18:35 +08:00
|
|
|
|
|
|
|
return chip->eeprom_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
|
|
|
|
struct ethtool_eeprom *eeprom, u8 *data)
|
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-07-21 06:18:35 +08:00
|
|
|
int err;
|
|
|
|
|
2016-09-30 00:22:02 +08:00
|
|
|
if (!chip->info->ops->get_eeprom)
|
|
|
|
return -EOPNOTSUPP;
|
2016-07-21 06:18:35 +08:00
|
|
|
|
2016-09-30 00:22:02 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
err = chip->info->ops->get_eeprom(chip, eeprom, data);
|
2016-07-21 06:18:35 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
eeprom->magic = 0xc3ec4951;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
|
|
|
|
struct ethtool_eeprom *eeprom, u8 *data)
|
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-07-21 06:18:35 +08:00
|
|
|
int err;
|
|
|
|
|
2016-09-30 00:22:02 +08:00
|
|
|
if (!chip->info->ops->set_eeprom)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2016-07-21 06:18:35 +08:00
|
|
|
if (eeprom->magic != 0xc3ec4951)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
mutex_lock(&chip->reg_lock);
|
2016-09-30 00:22:02 +08:00
|
|
|
err = chip->info->ops->set_eeprom(chip, eeprom, data);
|
2016-07-21 06:18:35 +08:00
|
|
|
mutex_unlock(&chip->reg_lock);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-09-30 00:22:00 +08:00
|
|
|
static const struct mv88e6xxx_ops mv88e6085_ops = {
|
2016-09-30 00:22:01 +08:00
|
|
|
.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
|
2016-09-30 00:22:00 +08:00
|
|
|
.phy_read = mv88e6xxx_phy_ppu_read,
|
|
|
|
.phy_write = mv88e6xxx_phy_ppu_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mv88e6xxx_ops mv88e6095_ops = {
|
2016-09-30 00:22:01 +08:00
|
|
|
.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
|
2016-09-30 00:22:00 +08:00
|
|
|
.phy_read = mv88e6xxx_phy_ppu_read,
|
|
|
|
.phy_write = mv88e6xxx_phy_ppu_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mv88e6xxx_ops mv88e6123_ops = {
|
2016-09-30 00:22:01 +08:00
|
|
|
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
|
2016-09-30 00:22:00 +08:00
|
|
|
.phy_read = mv88e6xxx_read,
|
|
|
|
.phy_write = mv88e6xxx_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mv88e6xxx_ops mv88e6131_ops = {
|
2016-09-30 00:22:01 +08:00
|
|
|
.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
|
2016-09-30 00:22:00 +08:00
|
|
|
.phy_read = mv88e6xxx_phy_ppu_read,
|
|
|
|
.phy_write = mv88e6xxx_phy_ppu_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mv88e6xxx_ops mv88e6161_ops = {
|
2016-09-30 00:22:01 +08:00
|
|
|
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
|
2016-09-30 00:22:00 +08:00
|
|
|
.phy_read = mv88e6xxx_read,
|
|
|
|
.phy_write = mv88e6xxx_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mv88e6xxx_ops mv88e6165_ops = {
|
2016-09-30 00:22:01 +08:00
|
|
|
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
|
2016-09-30 00:22:00 +08:00
|
|
|
.phy_read = mv88e6xxx_read,
|
|
|
|
.phy_write = mv88e6xxx_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mv88e6xxx_ops mv88e6171_ops = {
|
2016-09-30 00:22:01 +08:00
|
|
|
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
|
2016-09-30 00:22:00 +08:00
|
|
|
.phy_read = mv88e6xxx_g2_smi_phy_read,
|
|
|
|
.phy_write = mv88e6xxx_g2_smi_phy_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mv88e6xxx_ops mv88e6172_ops = {
|
2016-09-30 00:22:02 +08:00
|
|
|
.get_eeprom = mv88e6xxx_g2_get_eeprom16,
|
|
|
|
.set_eeprom = mv88e6xxx_g2_set_eeprom16,
|
2016-09-30 00:22:01 +08:00
|
|
|
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
|
2016-09-30 00:22:00 +08:00
|
|
|
.phy_read = mv88e6xxx_g2_smi_phy_read,
|
|
|
|
.phy_write = mv88e6xxx_g2_smi_phy_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mv88e6xxx_ops mv88e6175_ops = {
|
2016-09-30 00:22:01 +08:00
|
|
|
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
|
2016-09-30 00:22:00 +08:00
|
|
|
.phy_read = mv88e6xxx_g2_smi_phy_read,
|
|
|
|
.phy_write = mv88e6xxx_g2_smi_phy_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mv88e6xxx_ops mv88e6176_ops = {
|
2016-09-30 00:22:02 +08:00
|
|
|
.get_eeprom = mv88e6xxx_g2_get_eeprom16,
|
|
|
|
.set_eeprom = mv88e6xxx_g2_set_eeprom16,
|
2016-09-30 00:22:01 +08:00
|
|
|
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
|
2016-09-30 00:22:00 +08:00
|
|
|
.phy_read = mv88e6xxx_g2_smi_phy_read,
|
|
|
|
.phy_write = mv88e6xxx_g2_smi_phy_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mv88e6xxx_ops mv88e6185_ops = {
|
2016-09-30 00:22:01 +08:00
|
|
|
.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
|
2016-09-30 00:22:00 +08:00
|
|
|
.phy_read = mv88e6xxx_phy_ppu_read,
|
|
|
|
.phy_write = mv88e6xxx_phy_ppu_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mv88e6xxx_ops mv88e6240_ops = {
|
2016-09-30 00:22:02 +08:00
|
|
|
.get_eeprom = mv88e6xxx_g2_get_eeprom16,
|
|
|
|
.set_eeprom = mv88e6xxx_g2_set_eeprom16,
|
2016-09-30 00:22:01 +08:00
|
|
|
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
|
2016-09-30 00:22:00 +08:00
|
|
|
.phy_read = mv88e6xxx_g2_smi_phy_read,
|
|
|
|
.phy_write = mv88e6xxx_g2_smi_phy_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mv88e6xxx_ops mv88e6320_ops = {
|
2016-09-30 00:22:02 +08:00
|
|
|
.get_eeprom = mv88e6xxx_g2_get_eeprom16,
|
|
|
|
.set_eeprom = mv88e6xxx_g2_set_eeprom16,
|
2016-09-30 00:22:01 +08:00
|
|
|
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
|
2016-09-30 00:22:00 +08:00
|
|
|
.phy_read = mv88e6xxx_g2_smi_phy_read,
|
|
|
|
.phy_write = mv88e6xxx_g2_smi_phy_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mv88e6xxx_ops mv88e6321_ops = {
|
2016-09-30 00:22:02 +08:00
|
|
|
.get_eeprom = mv88e6xxx_g2_get_eeprom16,
|
|
|
|
.set_eeprom = mv88e6xxx_g2_set_eeprom16,
|
2016-09-30 00:22:01 +08:00
|
|
|
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
|
2016-09-30 00:22:00 +08:00
|
|
|
.phy_read = mv88e6xxx_g2_smi_phy_read,
|
|
|
|
.phy_write = mv88e6xxx_g2_smi_phy_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mv88e6xxx_ops mv88e6350_ops = {
|
2016-09-30 00:22:01 +08:00
|
|
|
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
|
2016-09-30 00:22:00 +08:00
|
|
|
.phy_read = mv88e6xxx_g2_smi_phy_read,
|
|
|
|
.phy_write = mv88e6xxx_g2_smi_phy_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mv88e6xxx_ops mv88e6351_ops = {
|
2016-09-30 00:22:01 +08:00
|
|
|
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
|
2016-09-30 00:22:00 +08:00
|
|
|
.phy_read = mv88e6xxx_g2_smi_phy_read,
|
|
|
|
.phy_write = mv88e6xxx_g2_smi_phy_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mv88e6xxx_ops mv88e6352_ops = {
|
2016-09-30 00:22:02 +08:00
|
|
|
.get_eeprom = mv88e6xxx_g2_get_eeprom16,
|
|
|
|
.set_eeprom = mv88e6xxx_g2_set_eeprom16,
|
2016-09-30 00:22:01 +08:00
|
|
|
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
|
2016-09-30 00:22:00 +08:00
|
|
|
.phy_read = mv88e6xxx_g2_smi_phy_read,
|
|
|
|
.phy_write = mv88e6xxx_g2_smi_phy_write,
|
|
|
|
};
|
|
|
|
|
2016-05-10 01:22:58 +08:00
|
|
|
static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
|
[MV88E6085] = {
|
|
|
|
.prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
|
|
|
|
.family = MV88E6XXX_FAMILY_6097,
|
|
|
|
.name = "Marvell 88E6085",
|
|
|
|
.num_databases = 4096,
|
|
|
|
.num_ports = 10,
|
2016-06-21 01:14:10 +08:00
|
|
|
.port_base_addr = 0x10,
|
2016-09-30 00:21:53 +08:00
|
|
|
.global1_addr = 0x1b,
|
2016-07-19 08:45:39 +08:00
|
|
|
.age_time_coeff = 15000,
|
2016-10-17 01:56:49 +08:00
|
|
|
.g1_irqs = 8,
|
2016-05-10 01:22:58 +08:00
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6097,
|
2016-09-30 00:22:00 +08:00
|
|
|
.ops = &mv88e6085_ops,
|
2016-05-10 01:22:58 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
[MV88E6095] = {
|
|
|
|
.prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
|
|
|
|
.family = MV88E6XXX_FAMILY_6095,
|
|
|
|
.name = "Marvell 88E6095/88E6095F",
|
|
|
|
.num_databases = 256,
|
|
|
|
.num_ports = 11,
|
2016-06-21 01:14:10 +08:00
|
|
|
.port_base_addr = 0x10,
|
2016-09-30 00:21:53 +08:00
|
|
|
.global1_addr = 0x1b,
|
2016-07-19 08:45:39 +08:00
|
|
|
.age_time_coeff = 15000,
|
2016-10-17 01:56:49 +08:00
|
|
|
.g1_irqs = 8,
|
2016-05-10 01:22:58 +08:00
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6095,
|
2016-09-30 00:22:00 +08:00
|
|
|
.ops = &mv88e6095_ops,
|
2016-05-10 01:22:58 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
[MV88E6123] = {
|
|
|
|
.prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
|
|
|
|
.family = MV88E6XXX_FAMILY_6165,
|
|
|
|
.name = "Marvell 88E6123",
|
|
|
|
.num_databases = 4096,
|
|
|
|
.num_ports = 3,
|
2016-06-21 01:14:10 +08:00
|
|
|
.port_base_addr = 0x10,
|
2016-09-30 00:21:53 +08:00
|
|
|
.global1_addr = 0x1b,
|
2016-07-19 08:45:39 +08:00
|
|
|
.age_time_coeff = 15000,
|
2016-10-17 01:56:49 +08:00
|
|
|
.g1_irqs = 9,
|
2016-05-10 01:22:58 +08:00
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
|
2016-09-30 00:22:00 +08:00
|
|
|
.ops = &mv88e6123_ops,
|
2016-05-10 01:22:58 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
[MV88E6131] = {
|
|
|
|
.prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
|
|
|
|
.family = MV88E6XXX_FAMILY_6185,
|
|
|
|
.name = "Marvell 88E6131",
|
|
|
|
.num_databases = 256,
|
|
|
|
.num_ports = 8,
|
2016-06-21 01:14:10 +08:00
|
|
|
.port_base_addr = 0x10,
|
2016-09-30 00:21:53 +08:00
|
|
|
.global1_addr = 0x1b,
|
2016-07-19 08:45:39 +08:00
|
|
|
.age_time_coeff = 15000,
|
2016-10-17 01:56:49 +08:00
|
|
|
.g1_irqs = 9,
|
2016-05-10 01:22:58 +08:00
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6185,
|
2016-09-30 00:22:00 +08:00
|
|
|
.ops = &mv88e6131_ops,
|
2016-05-10 01:22:58 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
[MV88E6161] = {
|
|
|
|
.prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
|
|
|
|
.family = MV88E6XXX_FAMILY_6165,
|
|
|
|
.name = "Marvell 88E6161",
|
|
|
|
.num_databases = 4096,
|
|
|
|
.num_ports = 6,
|
2016-06-21 01:14:10 +08:00
|
|
|
.port_base_addr = 0x10,
|
2016-09-30 00:21:53 +08:00
|
|
|
.global1_addr = 0x1b,
|
2016-07-19 08:45:39 +08:00
|
|
|
.age_time_coeff = 15000,
|
2016-10-17 01:56:49 +08:00
|
|
|
.g1_irqs = 9,
|
2016-05-10 01:22:58 +08:00
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
|
2016-09-30 00:22:00 +08:00
|
|
|
.ops = &mv88e6161_ops,
|
2016-05-10 01:22:58 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
[MV88E6165] = {
|
|
|
|
.prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
|
|
|
|
.family = MV88E6XXX_FAMILY_6165,
|
|
|
|
.name = "Marvell 88E6165",
|
|
|
|
.num_databases = 4096,
|
|
|
|
.num_ports = 6,
|
2016-06-21 01:14:10 +08:00
|
|
|
.port_base_addr = 0x10,
|
2016-09-30 00:21:53 +08:00
|
|
|
.global1_addr = 0x1b,
|
2016-07-19 08:45:39 +08:00
|
|
|
.age_time_coeff = 15000,
|
2016-10-17 01:56:49 +08:00
|
|
|
.g1_irqs = 9,
|
2016-05-10 01:22:58 +08:00
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
|
2016-09-30 00:22:00 +08:00
|
|
|
.ops = &mv88e6165_ops,
|
2016-05-10 01:22:58 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
[MV88E6171] = {
|
|
|
|
.prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
|
|
|
|
.family = MV88E6XXX_FAMILY_6351,
|
|
|
|
.name = "Marvell 88E6171",
|
|
|
|
.num_databases = 4096,
|
|
|
|
.num_ports = 7,
|
2016-06-21 01:14:10 +08:00
|
|
|
.port_base_addr = 0x10,
|
2016-09-30 00:21:53 +08:00
|
|
|
.global1_addr = 0x1b,
|
2016-07-19 08:45:39 +08:00
|
|
|
.age_time_coeff = 15000,
|
2016-10-17 01:56:49 +08:00
|
|
|
.g1_irqs = 9,
|
2016-05-10 01:22:58 +08:00
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
|
2016-09-30 00:22:00 +08:00
|
|
|
.ops = &mv88e6171_ops,
|
2016-05-10 01:22:58 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
[MV88E6172] = {
|
|
|
|
.prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
|
|
|
|
.family = MV88E6XXX_FAMILY_6352,
|
|
|
|
.name = "Marvell 88E6172",
|
|
|
|
.num_databases = 4096,
|
|
|
|
.num_ports = 7,
|
2016-06-21 01:14:10 +08:00
|
|
|
.port_base_addr = 0x10,
|
2016-09-30 00:21:53 +08:00
|
|
|
.global1_addr = 0x1b,
|
2016-07-19 08:45:39 +08:00
|
|
|
.age_time_coeff = 15000,
|
2016-10-17 01:56:49 +08:00
|
|
|
.g1_irqs = 9,
|
2016-05-10 01:22:58 +08:00
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
|
2016-09-30 00:22:00 +08:00
|
|
|
.ops = &mv88e6172_ops,
|
2016-05-10 01:22:58 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
[MV88E6175] = {
|
|
|
|
.prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
|
|
|
|
.family = MV88E6XXX_FAMILY_6351,
|
|
|
|
.name = "Marvell 88E6175",
|
|
|
|
.num_databases = 4096,
|
|
|
|
.num_ports = 7,
|
2016-06-21 01:14:10 +08:00
|
|
|
.port_base_addr = 0x10,
|
2016-09-30 00:21:53 +08:00
|
|
|
.global1_addr = 0x1b,
|
2016-07-19 08:45:39 +08:00
|
|
|
.age_time_coeff = 15000,
|
2016-10-17 01:56:49 +08:00
|
|
|
.g1_irqs = 9,
|
2016-05-10 01:22:58 +08:00
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
|
2016-09-30 00:22:00 +08:00
|
|
|
.ops = &mv88e6175_ops,
|
2016-05-10 01:22:58 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
[MV88E6176] = {
|
|
|
|
.prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
|
|
|
|
.family = MV88E6XXX_FAMILY_6352,
|
|
|
|
.name = "Marvell 88E6176",
|
|
|
|
.num_databases = 4096,
|
|
|
|
.num_ports = 7,
|
2016-06-21 01:14:10 +08:00
|
|
|
.port_base_addr = 0x10,
|
2016-09-30 00:21:53 +08:00
|
|
|
.global1_addr = 0x1b,
|
2016-07-19 08:45:39 +08:00
|
|
|
.age_time_coeff = 15000,
|
2016-10-17 01:56:49 +08:00
|
|
|
.g1_irqs = 9,
|
2016-05-10 01:22:58 +08:00
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
|
2016-09-30 00:22:00 +08:00
|
|
|
.ops = &mv88e6176_ops,
|
2016-05-10 01:22:58 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
[MV88E6185] = {
|
|
|
|
.prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
|
|
|
|
.family = MV88E6XXX_FAMILY_6185,
|
|
|
|
.name = "Marvell 88E6185",
|
|
|
|
.num_databases = 256,
|
|
|
|
.num_ports = 10,
|
2016-06-21 01:14:10 +08:00
|
|
|
.port_base_addr = 0x10,
|
2016-09-30 00:21:53 +08:00
|
|
|
.global1_addr = 0x1b,
|
2016-07-19 08:45:39 +08:00
|
|
|
.age_time_coeff = 15000,
|
2016-10-17 01:56:49 +08:00
|
|
|
.g1_irqs = 8,
|
2016-05-10 01:22:58 +08:00
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6185,
|
2016-09-30 00:22:00 +08:00
|
|
|
.ops = &mv88e6185_ops,
|
2016-05-10 01:22:58 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
[MV88E6240] = {
|
|
|
|
.prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
|
|
|
|
.family = MV88E6XXX_FAMILY_6352,
|
|
|
|
.name = "Marvell 88E6240",
|
|
|
|
.num_databases = 4096,
|
|
|
|
.num_ports = 7,
|
2016-06-21 01:14:10 +08:00
|
|
|
.port_base_addr = 0x10,
|
2016-09-30 00:21:53 +08:00
|
|
|
.global1_addr = 0x1b,
|
2016-07-19 08:45:39 +08:00
|
|
|
.age_time_coeff = 15000,
|
2016-10-17 01:56:49 +08:00
|
|
|
.g1_irqs = 9,
|
2016-05-10 01:22:58 +08:00
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
|
2016-09-30 00:22:00 +08:00
|
|
|
.ops = &mv88e6240_ops,
|
2016-05-10 01:22:58 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
[MV88E6320] = {
|
|
|
|
.prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
|
|
|
|
.family = MV88E6XXX_FAMILY_6320,
|
|
|
|
.name = "Marvell 88E6320",
|
|
|
|
.num_databases = 4096,
|
|
|
|
.num_ports = 7,
|
2016-06-21 01:14:10 +08:00
|
|
|
.port_base_addr = 0x10,
|
2016-09-30 00:21:53 +08:00
|
|
|
.global1_addr = 0x1b,
|
2016-07-19 08:45:39 +08:00
|
|
|
.age_time_coeff = 15000,
|
2016-10-17 01:56:49 +08:00
|
|
|
.g1_irqs = 8,
|
2016-05-10 01:22:58 +08:00
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6320,
|
2016-09-30 00:22:00 +08:00
|
|
|
.ops = &mv88e6320_ops,
|
2016-05-10 01:22:58 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
[MV88E6321] = {
|
|
|
|
.prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
|
|
|
|
.family = MV88E6XXX_FAMILY_6320,
|
|
|
|
.name = "Marvell 88E6321",
|
|
|
|
.num_databases = 4096,
|
|
|
|
.num_ports = 7,
|
2016-06-21 01:14:10 +08:00
|
|
|
.port_base_addr = 0x10,
|
2016-09-30 00:21:53 +08:00
|
|
|
.global1_addr = 0x1b,
|
2016-07-19 08:45:39 +08:00
|
|
|
.age_time_coeff = 15000,
|
2016-10-17 01:56:49 +08:00
|
|
|
.g1_irqs = 8,
|
2016-05-10 01:22:58 +08:00
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6320,
|
2016-09-30 00:22:00 +08:00
|
|
|
.ops = &mv88e6321_ops,
|
2016-05-10 01:22:58 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
[MV88E6350] = {
|
|
|
|
.prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
|
|
|
|
.family = MV88E6XXX_FAMILY_6351,
|
|
|
|
.name = "Marvell 88E6350",
|
|
|
|
.num_databases = 4096,
|
|
|
|
.num_ports = 7,
|
2016-06-21 01:14:10 +08:00
|
|
|
.port_base_addr = 0x10,
|
2016-09-30 00:21:53 +08:00
|
|
|
.global1_addr = 0x1b,
|
2016-07-19 08:45:39 +08:00
|
|
|
.age_time_coeff = 15000,
|
2016-10-17 01:56:49 +08:00
|
|
|
.g1_irqs = 9,
|
2016-05-10 01:22:58 +08:00
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
|
2016-09-30 00:22:00 +08:00
|
|
|
.ops = &mv88e6350_ops,
|
2016-05-10 01:22:58 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
[MV88E6351] = {
|
|
|
|
.prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
|
|
|
|
.family = MV88E6XXX_FAMILY_6351,
|
|
|
|
.name = "Marvell 88E6351",
|
|
|
|
.num_databases = 4096,
|
|
|
|
.num_ports = 7,
|
2016-06-21 01:14:10 +08:00
|
|
|
.port_base_addr = 0x10,
|
2016-09-30 00:21:53 +08:00
|
|
|
.global1_addr = 0x1b,
|
2016-07-19 08:45:39 +08:00
|
|
|
.age_time_coeff = 15000,
|
2016-10-17 01:56:49 +08:00
|
|
|
.g1_irqs = 9,
|
2016-05-10 01:22:58 +08:00
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
|
2016-09-30 00:22:00 +08:00
|
|
|
.ops = &mv88e6351_ops,
|
2016-05-10 01:22:58 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
[MV88E6352] = {
|
|
|
|
.prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
|
|
|
|
.family = MV88E6XXX_FAMILY_6352,
|
|
|
|
.name = "Marvell 88E6352",
|
|
|
|
.num_databases = 4096,
|
|
|
|
.num_ports = 7,
|
2016-06-21 01:14:10 +08:00
|
|
|
.port_base_addr = 0x10,
|
2016-09-30 00:21:53 +08:00
|
|
|
.global1_addr = 0x1b,
|
2016-07-19 08:45:39 +08:00
|
|
|
.age_time_coeff = 15000,
|
2016-10-17 01:56:49 +08:00
|
|
|
.g1_irqs = 9,
|
2016-05-10 01:22:58 +08:00
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
|
2016-09-30 00:22:00 +08:00
|
|
|
.ops = &mv88e6352_ops,
|
2016-05-10 01:22:58 +08:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2016-06-21 01:14:04 +08:00
|
|
|
static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num)
|
2015-10-31 07:39:48 +08:00
|
|
|
{
|
2016-04-18 01:23:58 +08:00
|
|
|
int i;
|
2015-10-31 07:39:48 +08:00
|
|
|
|
2016-06-21 01:14:04 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(mv88e6xxx_table); ++i)
|
|
|
|
if (mv88e6xxx_table[i].prod_num == prod_num)
|
|
|
|
return &mv88e6xxx_table[i];
|
2015-10-31 07:39:48 +08:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip)
|
2016-06-21 01:14:08 +08:00
|
|
|
{
|
|
|
|
const struct mv88e6xxx_info *info;
|
2016-07-21 06:18:36 +08:00
|
|
|
unsigned int prod_num, rev;
|
|
|
|
u16 id;
|
|
|
|
int err;
|
2016-06-21 01:14:08 +08:00
|
|
|
|
2016-07-21 06:18:36 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
err = mv88e6xxx_port_read(chip, 0, PORT_SWITCH_ID, &id);
|
|
|
|
mutex_unlock(&chip->reg_lock);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-06-21 01:14:08 +08:00
|
|
|
|
|
|
|
prod_num = (id & 0xfff0) >> 4;
|
|
|
|
rev = id & 0x000f;
|
|
|
|
|
|
|
|
info = mv88e6xxx_lookup_info(prod_num);
|
|
|
|
if (!info)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2016-06-21 01:14:09 +08:00
|
|
|
/* Update the compatible info with the probed one */
|
2016-06-22 00:28:20 +08:00
|
|
|
chip->info = info;
|
2016-06-21 01:14:08 +08:00
|
|
|
|
2016-09-03 02:45:34 +08:00
|
|
|
err = mv88e6xxx_g2_require(chip);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
dev_info(chip->dev, "switch 0x%x detected: %s, revision %u\n",
|
|
|
|
chip->info->prod_num, chip->info->name, rev);
|
2016-06-21 01:14:08 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)
|
2016-06-21 01:14:06 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
struct mv88e6xxx_chip *chip;
|
2016-06-21 01:14:06 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
|
|
|
if (!chip)
|
2016-06-21 01:14:06 +08:00
|
|
|
return NULL;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
chip->dev = dev;
|
2016-06-21 01:14:06 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
mutex_init(&chip->reg_lock);
|
2016-06-21 01:14:06 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
return chip;
|
2016-06-21 01:14:06 +08:00
|
|
|
}
|
|
|
|
|
2016-08-16 05:19:00 +08:00
|
|
|
static void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip)
|
|
|
|
{
|
2016-09-30 00:22:00 +08:00
|
|
|
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU))
|
2016-08-16 05:19:00 +08:00
|
|
|
mv88e6xxx_ppu_state_init(chip);
|
|
|
|
}
|
|
|
|
|
2016-08-22 22:01:03 +08:00
|
|
|
static void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip)
|
|
|
|
{
|
2016-09-30 00:22:00 +08:00
|
|
|
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU))
|
2016-08-22 22:01:03 +08:00
|
|
|
mv88e6xxx_ppu_state_destroy(chip);
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
|
2016-06-21 01:14:07 +08:00
|
|
|
struct mii_bus *bus, int sw_addr)
|
|
|
|
{
|
|
|
|
/* ADDR[0] pin is unavailable externally and considered zero */
|
|
|
|
if (sw_addr & 0x1)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2016-06-21 01:14:11 +08:00
|
|
|
if (sw_addr == 0)
|
2016-06-22 00:28:20 +08:00
|
|
|
chip->smi_ops = &mv88e6xxx_smi_single_chip_ops;
|
2016-08-16 05:18:58 +08:00
|
|
|
else if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_MULTI_CHIP))
|
2016-06-22 00:28:20 +08:00
|
|
|
chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops;
|
2016-06-21 01:14:11 +08:00
|
|
|
else
|
|
|
|
return -EINVAL;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
chip->bus = bus;
|
|
|
|
chip->sw_addr = sw_addr;
|
2016-06-21 01:14:07 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-08-22 22:01:01 +08:00
|
|
|
static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds)
|
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-08-22 22:01:02 +08:00
|
|
|
|
|
|
|
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA))
|
|
|
|
return DSA_TAG_PROTO_EDSA;
|
|
|
|
|
|
|
|
return DSA_TAG_PROTO_DSA;
|
2016-08-22 22:01:01 +08:00
|
|
|
}
|
|
|
|
|
2016-05-11 05:27:20 +08:00
|
|
|
static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
|
|
|
|
struct device *host_dev, int sw_addr,
|
|
|
|
void **priv)
|
2016-04-13 08:40:42 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
struct mv88e6xxx_chip *chip;
|
2016-04-18 01:23:58 +08:00
|
|
|
struct mii_bus *bus;
|
2016-06-05 03:17:06 +08:00
|
|
|
int err;
|
2016-04-13 08:40:42 +08:00
|
|
|
|
2016-04-18 01:23:58 +08:00
|
|
|
bus = dsa_host_dev_to_mii_bus(host_dev);
|
2016-04-13 08:40:45 +08:00
|
|
|
if (!bus)
|
|
|
|
return NULL;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
chip = mv88e6xxx_alloc_chip(dsa_dev);
|
|
|
|
if (!chip)
|
2016-06-21 01:14:06 +08:00
|
|
|
return NULL;
|
|
|
|
|
2016-06-21 01:14:09 +08:00
|
|
|
/* Legacy SMI probing will only support chips similar to 88E6085 */
|
2016-06-22 00:28:20 +08:00
|
|
|
chip->info = &mv88e6xxx_table[MV88E6085];
|
2016-06-21 01:14:09 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = mv88e6xxx_smi_init(chip, bus, sw_addr);
|
2016-06-21 01:14:07 +08:00
|
|
|
if (err)
|
|
|
|
goto free;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = mv88e6xxx_detect(chip);
|
2016-06-21 01:14:08 +08:00
|
|
|
if (err)
|
2016-06-21 01:14:06 +08:00
|
|
|
goto free;
|
2016-04-18 01:23:58 +08:00
|
|
|
|
2016-10-17 01:56:49 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
err = mv88e6xxx_switch_reset(chip);
|
|
|
|
mutex_unlock(&chip->reg_lock);
|
|
|
|
if (err)
|
|
|
|
goto free;
|
|
|
|
|
2016-08-16 05:19:00 +08:00
|
|
|
mv88e6xxx_phy_init(chip);
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = mv88e6xxx_mdio_register(chip, NULL);
|
2016-06-05 03:17:06 +08:00
|
|
|
if (err)
|
2016-06-21 01:14:06 +08:00
|
|
|
goto free;
|
2016-06-05 03:17:06 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
*priv = chip;
|
2016-04-18 01:23:58 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
return chip->info->name;
|
2016-06-21 01:14:06 +08:00
|
|
|
free:
|
2016-06-22 00:28:20 +08:00
|
|
|
devm_kfree(dsa_dev, chip);
|
2016-06-21 01:14:06 +08:00
|
|
|
|
|
|
|
return NULL;
|
2016-04-13 08:40:42 +08:00
|
|
|
}
|
|
|
|
|
2016-08-31 23:50:05 +08:00
|
|
|
static int mv88e6xxx_port_mdb_prepare(struct dsa_switch *ds, int port,
|
|
|
|
const struct switchdev_obj_port_mdb *mdb,
|
|
|
|
struct switchdev_trans *trans)
|
|
|
|
{
|
|
|
|
/* We don't need any dynamic resource from the kernel (yet),
|
|
|
|
* so skip the prepare phase.
|
|
|
|
*/
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
|
|
|
|
const struct switchdev_obj_port_mdb *mdb,
|
|
|
|
struct switchdev_trans *trans)
|
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-08-31 23:50:05 +08:00
|
|
|
|
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
if (mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
|
|
|
|
GLOBAL_ATU_DATA_STATE_MC_STATIC))
|
|
|
|
netdev_err(ds->ports[port].netdev, "failed to load multicast MAC address\n");
|
|
|
|
mutex_unlock(&chip->reg_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port,
|
|
|
|
const struct switchdev_obj_port_mdb *mdb)
|
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-08-31 23:50:05 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
|
|
|
|
GLOBAL_ATU_DATA_STATE_UNUSED);
|
|
|
|
mutex_unlock(&chip->reg_lock);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mv88e6xxx_port_mdb_dump(struct dsa_switch *ds, int port,
|
|
|
|
struct switchdev_obj_port_mdb *mdb,
|
|
|
|
int (*cb)(struct switchdev_obj *obj))
|
|
|
|
{
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-08-31 23:50:05 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
err = mv88e6xxx_port_db_dump(chip, port, &mdb->obj, cb);
|
|
|
|
mutex_unlock(&chip->reg_lock);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-08-24 00:38:56 +08:00
|
|
|
static struct dsa_switch_ops mv88e6xxx_switch_ops = {
|
2016-05-11 05:27:20 +08:00
|
|
|
.probe = mv88e6xxx_drv_probe,
|
2016-08-22 22:01:01 +08:00
|
|
|
.get_tag_protocol = mv88e6xxx_get_tag_protocol,
|
2016-05-10 01:22:58 +08:00
|
|
|
.setup = mv88e6xxx_setup,
|
|
|
|
.set_addr = mv88e6xxx_set_addr,
|
|
|
|
.adjust_link = mv88e6xxx_adjust_link,
|
|
|
|
.get_strings = mv88e6xxx_get_strings,
|
|
|
|
.get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
|
|
|
|
.get_sset_count = mv88e6xxx_get_sset_count,
|
|
|
|
.set_eee = mv88e6xxx_set_eee,
|
|
|
|
.get_eee = mv88e6xxx_get_eee,
|
|
|
|
#ifdef CONFIG_NET_DSA_HWMON
|
|
|
|
.get_temp = mv88e6xxx_get_temp,
|
|
|
|
.get_temp_limit = mv88e6xxx_get_temp_limit,
|
|
|
|
.set_temp_limit = mv88e6xxx_set_temp_limit,
|
|
|
|
.get_temp_alarm = mv88e6xxx_get_temp_alarm,
|
|
|
|
#endif
|
2016-05-11 05:27:25 +08:00
|
|
|
.get_eeprom_len = mv88e6xxx_get_eeprom_len,
|
2016-05-10 01:22:58 +08:00
|
|
|
.get_eeprom = mv88e6xxx_get_eeprom,
|
|
|
|
.set_eeprom = mv88e6xxx_set_eeprom,
|
|
|
|
.get_regs_len = mv88e6xxx_get_regs_len,
|
|
|
|
.get_regs = mv88e6xxx_get_regs,
|
2016-07-19 08:45:40 +08:00
|
|
|
.set_ageing_time = mv88e6xxx_set_ageing_time,
|
2016-05-10 01:22:58 +08:00
|
|
|
.port_bridge_join = mv88e6xxx_port_bridge_join,
|
|
|
|
.port_bridge_leave = mv88e6xxx_port_bridge_leave,
|
|
|
|
.port_stp_state_set = mv88e6xxx_port_stp_state_set,
|
2016-09-23 04:49:24 +08:00
|
|
|
.port_fast_age = mv88e6xxx_port_fast_age,
|
2016-05-10 01:22:58 +08:00
|
|
|
.port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
|
|
|
|
.port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
|
|
|
|
.port_vlan_add = mv88e6xxx_port_vlan_add,
|
|
|
|
.port_vlan_del = mv88e6xxx_port_vlan_del,
|
|
|
|
.port_vlan_dump = mv88e6xxx_port_vlan_dump,
|
|
|
|
.port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
|
|
|
|
.port_fdb_add = mv88e6xxx_port_fdb_add,
|
|
|
|
.port_fdb_del = mv88e6xxx_port_fdb_del,
|
|
|
|
.port_fdb_dump = mv88e6xxx_port_fdb_dump,
|
2016-08-31 23:50:05 +08:00
|
|
|
.port_mdb_prepare = mv88e6xxx_port_mdb_prepare,
|
|
|
|
.port_mdb_add = mv88e6xxx_port_mdb_add,
|
|
|
|
.port_mdb_del = mv88e6xxx_port_mdb_del,
|
|
|
|
.port_mdb_dump = mv88e6xxx_port_mdb_dump,
|
2016-05-10 01:22:58 +08:00
|
|
|
};
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip,
|
2016-06-21 01:14:02 +08:00
|
|
|
struct device_node *np)
|
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
struct device *dev = chip->dev;
|
2016-06-21 01:14:02 +08:00
|
|
|
struct dsa_switch *ds;
|
|
|
|
|
|
|
|
ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
|
|
|
|
if (!ds)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ds->dev = dev;
|
2016-06-22 00:28:20 +08:00
|
|
|
ds->priv = chip;
|
2016-08-24 00:38:56 +08:00
|
|
|
ds->ops = &mv88e6xxx_switch_ops;
|
2016-06-21 01:14:02 +08:00
|
|
|
|
|
|
|
dev_set_drvdata(dev, ds);
|
|
|
|
|
|
|
|
return dsa_register_switch(ds, np);
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
static void mv88e6xxx_unregister_switch(struct mv88e6xxx_chip *chip)
|
2016-06-21 01:14:02 +08:00
|
|
|
{
|
2016-06-22 00:28:20 +08:00
|
|
|
dsa_unregister_switch(chip->ds);
|
2016-06-21 01:14:02 +08:00
|
|
|
}
|
|
|
|
|
2016-06-21 01:13:58 +08:00
|
|
|
static int mv88e6xxx_probe(struct mdio_device *mdiodev)
|
2011-11-25 22:36:19 +08:00
|
|
|
{
|
2016-05-11 05:27:21 +08:00
|
|
|
struct device *dev = &mdiodev->dev;
|
2016-05-11 05:27:25 +08:00
|
|
|
struct device_node *np = dev->of_node;
|
2016-06-21 01:14:09 +08:00
|
|
|
const struct mv88e6xxx_info *compat_info;
|
2016-06-22 00:28:20 +08:00
|
|
|
struct mv88e6xxx_chip *chip;
|
2016-05-11 05:27:25 +08:00
|
|
|
u32 eeprom_len;
|
2016-05-11 05:27:22 +08:00
|
|
|
int err;
|
2016-05-11 05:27:21 +08:00
|
|
|
|
2016-06-21 01:14:09 +08:00
|
|
|
compat_info = of_device_get_match_data(dev);
|
|
|
|
if (!compat_info)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
chip = mv88e6xxx_alloc_chip(dev);
|
|
|
|
if (!chip)
|
2016-05-11 05:27:21 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
chip->info = compat_info;
|
2016-06-21 01:14:09 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr);
|
2016-06-21 01:14:07 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
2016-05-11 05:27:21 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = mv88e6xxx_detect(chip);
|
2016-06-21 01:14:08 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
2016-05-11 05:27:21 +08:00
|
|
|
|
2016-08-16 05:19:00 +08:00
|
|
|
mv88e6xxx_phy_init(chip);
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
|
|
|
|
if (IS_ERR(chip->reset))
|
|
|
|
return PTR_ERR(chip->reset);
|
2016-05-11 05:27:22 +08:00
|
|
|
|
2016-09-30 00:22:02 +08:00
|
|
|
if (chip->info->ops->get_eeprom &&
|
2016-05-11 05:27:25 +08:00
|
|
|
!of_property_read_u32(np, "eeprom-length", &eeprom_len))
|
2016-06-22 00:28:20 +08:00
|
|
|
chip->eeprom_len = eeprom_len;
|
2016-05-11 05:27:25 +08:00
|
|
|
|
2016-10-17 01:56:49 +08:00
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
err = mv88e6xxx_switch_reset(chip);
|
|
|
|
mutex_unlock(&chip->reg_lock);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
chip->irq = of_irq_get(np, 0);
|
|
|
|
if (chip->irq == -EPROBE_DEFER) {
|
|
|
|
err = chip->irq;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chip->irq > 0) {
|
|
|
|
/* Has to be performed before the MDIO bus is created,
|
|
|
|
* because the PHYs will link there interrupts to these
|
|
|
|
* interrupt controllers
|
|
|
|
*/
|
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
err = mv88e6xxx_g1_irq_setup(chip);
|
|
|
|
mutex_unlock(&chip->reg_lock);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) {
|
|
|
|
err = mv88e6xxx_g2_irq_setup(chip);
|
|
|
|
if (err)
|
|
|
|
goto out_g1_irq;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = mv88e6xxx_mdio_register(chip, np);
|
2016-06-05 03:17:06 +08:00
|
|
|
if (err)
|
2016-10-17 01:56:49 +08:00
|
|
|
goto out_g2_irq;
|
2016-06-05 03:17:06 +08:00
|
|
|
|
2016-06-22 00:28:20 +08:00
|
|
|
err = mv88e6xxx_register_switch(chip, np);
|
2016-10-17 01:56:49 +08:00
|
|
|
if (err)
|
|
|
|
goto out_mdio;
|
2016-06-05 03:17:07 +08:00
|
|
|
|
2011-11-25 22:36:19 +08:00
|
|
|
return 0;
|
2016-10-17 01:56:49 +08:00
|
|
|
|
|
|
|
out_mdio:
|
|
|
|
mv88e6xxx_mdio_unregister(chip);
|
|
|
|
out_g2_irq:
|
|
|
|
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT))
|
|
|
|
mv88e6xxx_g2_irq_free(chip);
|
|
|
|
out_g1_irq:
|
|
|
|
mv88e6xxx_g1_irq_free(chip);
|
|
|
|
out:
|
|
|
|
return err;
|
2011-11-25 22:36:19 +08:00
|
|
|
}
|
2016-05-11 05:27:21 +08:00
|
|
|
|
|
|
|
static void mv88e6xxx_remove(struct mdio_device *mdiodev)
|
|
|
|
{
|
|
|
|
struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
|
2016-09-01 06:06:13 +08:00
|
|
|
struct mv88e6xxx_chip *chip = ds->priv;
|
2016-05-11 05:27:21 +08:00
|
|
|
|
2016-08-22 22:01:03 +08:00
|
|
|
mv88e6xxx_phy_destroy(chip);
|
2016-06-22 00:28:20 +08:00
|
|
|
mv88e6xxx_unregister_switch(chip);
|
|
|
|
mv88e6xxx_mdio_unregister(chip);
|
2016-10-17 01:56:49 +08:00
|
|
|
|
|
|
|
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT))
|
|
|
|
mv88e6xxx_g2_irq_free(chip);
|
|
|
|
mv88e6xxx_g1_irq_free(chip);
|
2016-05-11 05:27:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct of_device_id mv88e6xxx_of_match[] = {
|
2016-06-21 01:14:09 +08:00
|
|
|
{
|
|
|
|
.compatible = "marvell,mv88e6085",
|
|
|
|
.data = &mv88e6xxx_table[MV88E6085],
|
|
|
|
},
|
2016-05-11 05:27:21 +08:00
|
|
|
{ /* sentinel */ },
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);
|
|
|
|
|
|
|
|
static struct mdio_driver mv88e6xxx_driver = {
|
|
|
|
.probe = mv88e6xxx_probe,
|
|
|
|
.remove = mv88e6xxx_remove,
|
|
|
|
.mdiodrv.driver = {
|
|
|
|
.name = "mv88e6085",
|
|
|
|
.of_match_table = mv88e6xxx_of_match,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init mv88e6xxx_init(void)
|
|
|
|
{
|
2016-08-24 00:38:56 +08:00
|
|
|
register_switch_driver(&mv88e6xxx_switch_ops);
|
2016-05-11 05:27:21 +08:00
|
|
|
return mdio_driver_register(&mv88e6xxx_driver);
|
|
|
|
}
|
2011-11-25 22:36:19 +08:00
|
|
|
module_init(mv88e6xxx_init);
|
|
|
|
|
|
|
|
static void __exit mv88e6xxx_cleanup(void)
|
|
|
|
{
|
2016-05-11 05:27:21 +08:00
|
|
|
mdio_driver_unregister(&mv88e6xxx_driver);
|
2016-08-24 00:38:56 +08:00
|
|
|
unregister_switch_driver(&mv88e6xxx_switch_ops);
|
2011-11-25 22:36:19 +08:00
|
|
|
}
|
|
|
|
module_exit(mv88e6xxx_cleanup);
|
2011-11-25 22:37:16 +08:00
|
|
|
|
|
|
|
MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
|
|
|
|
MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
|
|
|
|
MODULE_LICENSE("GPL");
|