Commit e26522cd authored by David S. Miller's avatar David S. Miller

Merge branch 'dsa-mv88e6xxx-monolithic'

Vivien Didelot says:

====================
net: dsa: mv88e6xxx: turn into monolithic driver

This patchset merges all mv88e6* drivers supported by the shared
mv88e6xxx code into a single mv88e6xxx DSA switch driver.

Some flags are added to describe the capabilities of a switch model,
such as the presence of a PPU, EEPROM, some old or new registers, etc.

First these flags are used to conditionally support the same set of
functions in every driver, then specific driver files are removed in
favor of the common mv88e6xxx driver.

Only the merge of driver specific setup code assumes a few differences.
If these differences such as frames priorities are really needed for
some models, they can easily be brought back in a future patch.

Some inconsistencies might show up, such as the need for
MV88E6XXX_FLAG_PPU and MV88E6XXX_FLAG_PPU_ACTIVE flags. But this
patchset does not aim to fix them yet. A future patch can do that if
they end up being unwanted.

The patchset has been tested on interconnected 88E6352 and 88E6185.

Changes v1 -> v2:
  - add missing MV88E6XXX_FLAG_EEPROM flag checks
  - remove a few remaining _ prefixes
  - remove MV88E6XXX_FLAG_CORE_TAG_TYPE which is a specific default

Changes RFC -> v1:
  - introduce flags in a separate patch
  - do not refactor anything yet
  - do not add new functions prefixed with _
  - drop packet discarding and mentioned tested platforms
  - factorize family flags
  - update text for NET_DSA_MV88E6XXX Kconfig entry
====================
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Tested-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 908578e7 f81ec90f
...@@ -91,10 +91,7 @@ CONFIG_SATA_AHCI=y ...@@ -91,10 +91,7 @@ CONFIG_SATA_AHCI=y
CONFIG_SATA_MV=y CONFIG_SATA_MV=y
CONFIG_NETDEVICES=y CONFIG_NETDEVICES=y
CONFIG_NET_DSA_MV88E6060=y CONFIG_NET_DSA_MV88E6060=y
CONFIG_NET_DSA_MV88E6131=y CONFIG_NET_DSA_MV88E6XXX=y
CONFIG_NET_DSA_MV88E6123=y
CONFIG_NET_DSA_MV88E6171=y
CONFIG_NET_DSA_MV88E6352=y
CONFIG_MV643XX_ETH=y CONFIG_MV643XX_ETH=y
CONFIG_R8169=y CONFIG_R8169=y
CONFIG_MARVELL_PHY=y CONFIG_MARVELL_PHY=y
......
...@@ -66,7 +66,7 @@ CONFIG_SATA_AHCI=y ...@@ -66,7 +66,7 @@ CONFIG_SATA_AHCI=y
CONFIG_AHCI_MVEBU=y CONFIG_AHCI_MVEBU=y
CONFIG_SATA_MV=y CONFIG_SATA_MV=y
CONFIG_NETDEVICES=y CONFIG_NETDEVICES=y
CONFIG_NET_DSA_MV88E6171=y CONFIG_NET_DSA_MV88E6XXX=y
CONFIG_MV643XX_ETH=y CONFIG_MV643XX_ETH=y
CONFIG_MVNETA=y CONFIG_MVNETA=y
CONFIG_MVPP2=y CONFIG_MVPP2=y
......
...@@ -85,8 +85,7 @@ CONFIG_ATA=y ...@@ -85,8 +85,7 @@ CONFIG_ATA=y
CONFIG_SATA_MV=y CONFIG_SATA_MV=y
CONFIG_NETDEVICES=y CONFIG_NETDEVICES=y
CONFIG_MII=y CONFIG_MII=y
CONFIG_NET_DSA_MV88E6131=y CONFIG_NET_DSA_MV88E6XXX=y
CONFIG_NET_DSA_MV88E6123=y
CONFIG_MV643XX_ETH=y CONFIG_MV643XX_ETH=y
CONFIG_MARVELL_PHY=y CONFIG_MARVELL_PHY=y
# CONFIG_INPUT_MOUSEDEV is not set # CONFIG_INPUT_MOUSEDEV is not set
......
...@@ -221,8 +221,7 @@ CONFIG_NETCONSOLE_DYNAMIC=y ...@@ -221,8 +221,7 @@ CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_TUN=y CONFIG_TUN=y
CONFIG_VETH=m CONFIG_VETH=m
CONFIG_NET_DSA_MV88E6060=y CONFIG_NET_DSA_MV88E6060=y
CONFIG_NET_DSA_MV88E6131=y CONFIG_NET_DSA_MV88E6XXX=y
CONFIG_NET_DSA_MV88E6123=y
CONFIG_SKY2=y CONFIG_SKY2=y
CONFIG_PTP_1588_CLOCK_TILEGX=y CONFIG_PTP_1588_CLOCK_TILEGX=y
# CONFIG_WLAN is not set # CONFIG_WLAN is not set
......
...@@ -340,8 +340,7 @@ CONFIG_NETCONSOLE_DYNAMIC=y ...@@ -340,8 +340,7 @@ CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_TUN=y CONFIG_TUN=y
CONFIG_VETH=m CONFIG_VETH=m
CONFIG_NET_DSA_MV88E6060=y CONFIG_NET_DSA_MV88E6060=y
CONFIG_NET_DSA_MV88E6131=y CONFIG_NET_DSA_MV88E6XXX=y
CONFIG_NET_DSA_MV88E6123=y
# CONFIG_NET_VENDOR_3COM is not set # CONFIG_NET_VENDOR_3COM is not set
CONFIG_E1000E=y CONFIG_E1000E=y
# CONFIG_WLAN is not set # CONFIG_WLAN is not set
......
menu "Distributed Switch Architecture drivers" menu "Distributed Switch Architecture drivers"
depends on HAVE_NET_DSA depends on HAVE_NET_DSA
config NET_DSA_MV88E6XXX
tristate
default n
config NET_DSA_MV88E6060 config NET_DSA_MV88E6060
tristate "Marvell 88E6060 ethernet switch chip support" tristate "Marvell 88E6060 ethernet switch chip support"
depends on NET_DSA depends on NET_DSA
...@@ -13,46 +9,13 @@ config NET_DSA_MV88E6060 ...@@ -13,46 +9,13 @@ config NET_DSA_MV88E6060
This enables support for the Marvell 88E6060 ethernet switch This enables support for the Marvell 88E6060 ethernet switch
chip. chip.
config NET_DSA_MV88E6XXX_NEED_PPU config NET_DSA_MV88E6XXX
bool tristate "Marvell 88E6xxx Ethernet switch chip support"
default n
config NET_DSA_MV88E6131
tristate "Marvell 88E6085/6095/6095F/6131 ethernet switch chip support"
depends on NET_DSA
select NET_DSA_MV88E6XXX
select NET_DSA_MV88E6XXX_NEED_PPU
select NET_DSA_TAG_DSA
---help---
This enables support for the Marvell 88E6085/6095/6095F/6131
ethernet switch chips.
config NET_DSA_MV88E6123
tristate "Marvell 88E6123/6161/6165 ethernet switch chip support"
depends on NET_DSA
select NET_DSA_MV88E6XXX
select NET_DSA_TAG_EDSA
---help---
This enables support for the Marvell 88E6123/6161/6165
ethernet switch chips.
config NET_DSA_MV88E6171
tristate "Marvell 88E6171/6175/6350/6351 ethernet switch chip support"
depends on NET_DSA
select NET_DSA_MV88E6XXX
select NET_DSA_TAG_EDSA
---help---
This enables support for the Marvell 88E6171/6175/6350/6351
ethernet switches chips.
config NET_DSA_MV88E6352
tristate "Marvell 88E6172/6176/6320/6321/6352 ethernet switch chip support"
depends on NET_DSA depends on NET_DSA
select NET_DSA_MV88E6XXX
select NET_DSA_TAG_EDSA select NET_DSA_TAG_EDSA
---help--- ---help---
This enables support for the Marvell 88E6172, 88E6176, 88E6320, This enables support for most of the Marvell 88E6xxx models of
88E6321 and 88E6352 ethernet switch chips. Ethernet switch chips, except 88E6060.
config NET_DSA_BCM_SF2 config NET_DSA_BCM_SF2
tristate "Broadcom Starfighter 2 Ethernet switch support" tristate "Broadcom Starfighter 2 Ethernet switch support"
......
obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx_drv.o obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o
mv88e6xxx_drv-y += mv88e6xxx.o
ifdef CONFIG_NET_DSA_MV88E6123
mv88e6xxx_drv-y += mv88e6123.o
endif
ifdef CONFIG_NET_DSA_MV88E6131
mv88e6xxx_drv-y += mv88e6131.o
endif
ifdef CONFIG_NET_DSA_MV88E6352
mv88e6xxx_drv-y += mv88e6352.o
endif
ifdef CONFIG_NET_DSA_MV88E6171
mv88e6xxx_drv-y += mv88e6171.o
endif
obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm_sf2.o obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm_sf2.o
/*
* net/dsa/mv88e6123_61_65.c - Marvell 88e6123/6161/6165 switch chip support
* Copyright (c) 2008-2009 Marvell Semiconductor
*
* 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.
*/
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
#include <net/dsa.h>
#include "mv88e6xxx.h"
static const struct mv88e6xxx_info mv88e6123_table[] = {
{
.prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
.family = MV88E6XXX_FAMILY_6165,
.name = "Marvell 88E6123",
.num_databases = 4096,
.num_ports = 3,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
.family = MV88E6XXX_FAMILY_6165,
.name = "Marvell 88E6161",
.num_databases = 4096,
.num_ports = 6,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
.family = MV88E6XXX_FAMILY_6165,
.name = "Marvell 88E6165",
.num_databases = 4096,
.num_ports = 6,
}
};
static const char *mv88e6123_drv_probe(struct device *dsa_dev,
struct device *host_dev, int sw_addr,
void **priv)
{
return mv88e6xxx_drv_probe(dsa_dev, host_dev, sw_addr, priv,
mv88e6123_table,
ARRAY_SIZE(mv88e6123_table));
}
static int mv88e6123_setup_global(struct dsa_switch *ds)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u32 upstream_port = dsa_upstream_port(ds);
int ret;
u32 reg;
ret = mv88e6xxx_setup_global(ds);
if (ret)
return ret;
/* Disable the PHY polling unit (since there won't be any
* external PHYs to poll), don't discard packets with
* excessive collisions, and mask all interrupt sources.
*/
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, 0x0000);
if (ret)
return ret;
/* Configure the upstream port, and configure the upstream
* port as the port to which ingress and egress 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;
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg);
if (ret)
return ret;
/* Disable remote management for now, and set the switch's
* DSA device number.
*/
return mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL_2,
ds->index & 0x1f);
}
static int mv88e6123_setup(struct dsa_switch *ds)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
ps->ds = ds;
ret = mv88e6xxx_setup_common(ps);
if (ret < 0)
return ret;
ret = mv88e6xxx_switch_reset(ps, false);
if (ret < 0)
return ret;
ret = mv88e6123_setup_global(ds);
if (ret < 0)
return ret;
return mv88e6xxx_setup_ports(ds);
}
struct dsa_switch_driver mv88e6123_switch_driver = {
.tag_protocol = DSA_TAG_PROTO_EDSA,
.probe = mv88e6123_drv_probe,
.setup = mv88e6123_setup,
.set_addr = mv88e6xxx_set_addr_indirect,
.phy_read = mv88e6xxx_phy_read,
.phy_write = mv88e6xxx_phy_write,
.get_strings = mv88e6xxx_get_strings,
.get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
.get_sset_count = mv88e6xxx_get_sset_count,
.adjust_link = mv88e6xxx_adjust_link,
#ifdef CONFIG_NET_DSA_HWMON
.get_temp = mv88e6xxx_get_temp,
#endif
.get_regs_len = mv88e6xxx_get_regs_len,
.get_regs = mv88e6xxx_get_regs,
};
MODULE_ALIAS("platform:mv88e6123");
MODULE_ALIAS("platform:mv88e6161");
MODULE_ALIAS("platform:mv88e6165");
/*
* net/dsa/mv88e6131.c - Marvell 88e6095/6095f/6131 switch chip support
* Copyright (c) 2008-2009 Marvell Semiconductor
*
* 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.
*/
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
#include <net/dsa.h>
#include "mv88e6xxx.h"
static const struct mv88e6xxx_info mv88e6131_table[] = {
{
.prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
.family = MV88E6XXX_FAMILY_6095,
.name = "Marvell 88E6095/88E6095F",
.num_databases = 256,
.num_ports = 11,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
.family = MV88E6XXX_FAMILY_6097,
.name = "Marvell 88E6085",
.num_databases = 4096,
.num_ports = 10,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
.family = MV88E6XXX_FAMILY_6185,
.name = "Marvell 88E6131",
.num_databases = 256,
.num_ports = 8,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
.family = MV88E6XXX_FAMILY_6185,
.name = "Marvell 88E6185",
.num_databases = 256,
.num_ports = 10,
}
};
static const char *mv88e6131_drv_probe(struct device *dsa_dev,
struct device *host_dev, int sw_addr,
void **priv)
{
return mv88e6xxx_drv_probe(dsa_dev, host_dev, sw_addr, priv,
mv88e6131_table,
ARRAY_SIZE(mv88e6131_table));
}
static int mv88e6131_setup_global(struct dsa_switch *ds)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u32 upstream_port = dsa_upstream_port(ds);
int ret;
u32 reg;
ret = mv88e6xxx_setup_global(ds);
if (ret)
return ret;
/* Enable the PHY polling unit, don't discard packets with
* excessive collisions, use a weighted fair queueing scheme
* to arbitrate between packet queues, set the maximum frame
* size to 1632, and mask all interrupt sources.
*/
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL,
GLOBAL_CONTROL_PPU_ENABLE |
GLOBAL_CONTROL_MAX_FRAME_1632);
if (ret)
return ret;
/* Set the VLAN ethertype to 0x8100. */
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CORE_TAG_TYPE, 0x8100);
if (ret)
return ret;
/* Disable ARP mirroring, and configure the upstream port as
* the port to which ingress and egress monitor frames are to
* be sent.
*/
reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
GLOBAL_MONITOR_CONTROL_ARP_DISABLED;
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg);
if (ret)
return ret;
/* Disable cascade port functionality unless this device
* is used in a cascade configuration, and set the switch's
* DSA device number.
*/
if (ds->dst->pd->nr_chips > 1)
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL_2,
GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
(ds->index & 0x1f));
else
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL_2,
GLOBAL_CONTROL_2_NO_CASCADE |
(ds->index & 0x1f));
if (ret)
return ret;
/* Force the priority of IGMP/MLD snoop frames and ARP frames
* to the highest setting.
*/
return mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_PRIO_OVERRIDE,
GLOBAL2_PRIO_OVERRIDE_FORCE_SNOOP |
7 << GLOBAL2_PRIO_OVERRIDE_SNOOP_SHIFT |
GLOBAL2_PRIO_OVERRIDE_FORCE_ARP |
7 << GLOBAL2_PRIO_OVERRIDE_ARP_SHIFT);
}
static int mv88e6131_setup(struct dsa_switch *ds)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
ps->ds = ds;
ret = mv88e6xxx_setup_common(ps);
if (ret < 0)
return ret;
mv88e6xxx_ppu_state_init(ps);
ret = mv88e6xxx_switch_reset(ps, false);
if (ret < 0)
return ret;
ret = mv88e6131_setup_global(ds);
if (ret < 0)
return ret;
return mv88e6xxx_setup_ports(ds);
}
static int mv88e6131_port_to_phy_addr(struct dsa_switch *ds, int port)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
if (port >= 0 && port < ps->info->num_ports)
return port;
return -EINVAL;
}
static int
mv88e6131_phy_read(struct dsa_switch *ds, int port, int regnum)
{
int addr = mv88e6131_port_to_phy_addr(ds, port);
if (addr < 0)
return addr;
return mv88e6xxx_phy_read_ppu(ds, addr, regnum);
}
static int
mv88e6131_phy_write(struct dsa_switch *ds,
int port, int regnum, u16 val)
{
int addr = mv88e6131_port_to_phy_addr(ds, port);
if (addr < 0)
return addr;
return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val);
}
struct dsa_switch_driver mv88e6131_switch_driver = {
.tag_protocol = DSA_TAG_PROTO_DSA,
.probe = mv88e6131_drv_probe,
.setup = mv88e6131_setup,
.set_addr = mv88e6xxx_set_addr_direct,
.phy_read = mv88e6131_phy_read,
.phy_write = mv88e6131_phy_write,
.get_strings = mv88e6xxx_get_strings,
.get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
.get_sset_count = mv88e6xxx_get_sset_count,
.adjust_link = mv88e6xxx_adjust_link,
.port_bridge_join = mv88e6xxx_port_bridge_join,
.port_bridge_leave = mv88e6xxx_port_bridge_leave,
.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,
};
MODULE_ALIAS("platform:mv88e6085");
MODULE_ALIAS("platform:mv88e6095");
MODULE_ALIAS("platform:mv88e6095f");
MODULE_ALIAS("platform:mv88e6131");
/* net/dsa/mv88e6171.c - Marvell 88e6171 switch chip support
* Copyright (c) 2008-2009 Marvell Semiconductor
* Copyright (c) 2014 Claudio Leite <leitec@staticky.com>
*
* 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.
*/
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
#include <net/dsa.h>
#include "mv88e6xxx.h"
static const struct mv88e6xxx_info mv88e6171_table[] = {
{
.prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
.family = MV88E6XXX_FAMILY_6351,
.name = "Marvell 88E6171",
.num_databases = 4096,
.num_ports = 7,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
.family = MV88E6XXX_FAMILY_6351,
.name = "Marvell 88E6175",
.num_databases = 4096,
.num_ports = 7,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
.family = MV88E6XXX_FAMILY_6351,
.name = "Marvell 88E6350",
.num_databases = 4096,
.num_ports = 7,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
.family = MV88E6XXX_FAMILY_6351,
.name = "Marvell 88E6351",
.num_databases = 4096,
.num_ports = 7,
}
};
static const char *mv88e6171_drv_probe(struct device *dsa_dev,
struct device *host_dev, int sw_addr,
void **priv)
{
return mv88e6xxx_drv_probe(dsa_dev, host_dev, sw_addr, priv,
mv88e6171_table,
ARRAY_SIZE(mv88e6171_table));
}
static int mv88e6171_setup_global(struct dsa_switch *ds)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u32 upstream_port = dsa_upstream_port(ds);
int ret;
u32 reg;
ret = mv88e6xxx_setup_global(ds);
if (ret)
return ret;
/* Discard packets with excessive collisions, mask all
* interrupt sources, enable PPU.
*/
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL,
GLOBAL_CONTROL_PPU_ENABLE |
GLOBAL_CONTROL_DISCARD_EXCESS);
if (ret)
return ret;
/* Configure the upstream port, and configure the upstream
* port as the port to which ingress and egress 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 |
upstream_port << GLOBAL_MONITOR_CONTROL_MIRROR_SHIFT;
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg);
if (ret)
return ret;
/* Disable remote management for now, and set the switch's
* DSA device number.
*/
return mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL_2,
ds->index & 0x1f);
}
static int mv88e6171_setup(struct dsa_switch *ds)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
ps->ds = ds;
ret = mv88e6xxx_setup_common(ps);
if (ret < 0)
return ret;
ret = mv88e6xxx_switch_reset(ps, true);
if (ret < 0)
return ret;
ret = mv88e6171_setup_global(ds);
if (ret < 0)
return ret;
return mv88e6xxx_setup_ports(ds);
}
struct dsa_switch_driver mv88e6171_switch_driver = {
.tag_protocol = DSA_TAG_PROTO_EDSA,
.probe = mv88e6171_drv_probe,
.setup = mv88e6171_setup,
.set_addr = mv88e6xxx_set_addr_indirect,
.phy_read = mv88e6xxx_phy_read_indirect,
.phy_write = mv88e6xxx_phy_write_indirect,
.get_strings = mv88e6xxx_get_strings,
.get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
.get_sset_count = mv88e6xxx_get_sset_count,
.adjust_link = mv88e6xxx_adjust_link,
#ifdef CONFIG_NET_DSA_HWMON
.get_temp = mv88e6xxx_get_temp,
#endif
.get_regs_len = mv88e6xxx_get_regs_len,
.get_regs = mv88e6xxx_get_regs,
.port_bridge_join = mv88e6xxx_port_bridge_join,
.port_bridge_leave = mv88e6xxx_port_bridge_leave,
.port_stp_state_set = mv88e6xxx_port_stp_state_set,
.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,
};
MODULE_ALIAS("platform:mv88e6171");
MODULE_ALIAS("platform:mv88e6175");
MODULE_ALIAS("platform:mv88e6350");
MODULE_ALIAS("platform:mv88e6351");
/*
* net/dsa/mv88e6352.c - Marvell 88e6352 switch chip support
*
* Copyright (c) 2014 Guenter Roeck
*
* Derived from mv88e6123_61_65.c
* Copyright (c) 2008-2009 Marvell Semiconductor
*
* 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.
*/
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/platform_device.h>
#include <linux/phy.h>
#include <net/dsa.h>
#include "mv88e6xxx.h"
static const struct mv88e6xxx_info mv88e6352_table[] = {
{
.prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
.family = MV88E6XXX_FAMILY_6320,
.name = "Marvell 88E6320",
.num_databases = 4096,
.num_ports = 7,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
.family = MV88E6XXX_FAMILY_6320,
.name = "Marvell 88E6321",
.num_databases = 4096,
.num_ports = 7,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
.family = MV88E6XXX_FAMILY_6352,
.name = "Marvell 88E6172",
.num_databases = 4096,
.num_ports = 7,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
.family = MV88E6XXX_FAMILY_6352,
.name = "Marvell 88E6176",
.num_databases = 4096,
.num_ports = 7,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
.family = MV88E6XXX_FAMILY_6352,
.name = "Marvell 88E6240",
.num_databases = 4096,
.num_ports = 7,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
.family = MV88E6XXX_FAMILY_6352,
.name = "Marvell 88E6352",
.num_databases = 4096,
.num_ports = 7,
}
};
static const char *mv88e6352_drv_probe(struct device *dsa_dev,
struct device *host_dev, int sw_addr,
void **priv)
{
return mv88e6xxx_drv_probe(dsa_dev, host_dev, sw_addr, priv,
mv88e6352_table,
ARRAY_SIZE(mv88e6352_table));
}
static int mv88e6352_setup_global(struct dsa_switch *ds)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u32 upstream_port = dsa_upstream_port(ds);
int ret;
u32 reg;
ret = mv88e6xxx_setup_global(ds);
if (ret)
return ret;
/* Discard packets with excessive collisions,
* mask all interrupt sources, enable PPU (bit 14, undocumented).
*/
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL,
GLOBAL_CONTROL_PPU_ENABLE |
GLOBAL_CONTROL_DISCARD_EXCESS);
if (ret)
return ret;
/* Configure the upstream port, and configure the upstream
* port as the port to which ingress and egress 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;
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg);
if (ret)
return ret;
/* Disable remote management for now, and set the switch's
* DSA device number.
*/
return mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x1c, ds->index & 0x1f);
}
static int mv88e6352_setup(struct dsa_switch *ds)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
ps->ds = ds;
ret = mv88e6xxx_setup_common(ps);
if (ret < 0)
return ret;
mutex_init(&ps->eeprom_mutex);
ret = mv88e6xxx_switch_reset(ps, true);
if (ret < 0)
return ret;
ret = mv88e6352_setup_global(ds);
if (ret < 0)
return ret;
return mv88e6xxx_setup_ports(ds);
}
static int mv88e6352_read_eeprom_word(struct dsa_switch *ds, int addr)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
mutex_lock(&ps->eeprom_mutex);
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
GLOBAL2_EEPROM_OP_READ |
(addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
if (ret < 0)
goto error;
ret = mv88e6xxx_eeprom_busy_wait(ds);
if (ret < 0)
goto error;
ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA);
error:
mutex_unlock(&ps->eeprom_mutex);
return ret;
}
static int mv88e6352_get_eeprom(struct dsa_switch *ds,
struct ethtool_eeprom *eeprom, u8 *data)
{
int offset;
int len;
int ret;
offset = eeprom->offset;
len = eeprom->len;
eeprom->len = 0;
eeprom->magic = 0xc3ec4951;
ret = mv88e6xxx_eeprom_load_wait(ds);
if (ret < 0)
return ret;
if (offset & 1) {
int word;
word = mv88e6352_read_eeprom_word(ds, offset >> 1);
if (word < 0)
return word;
*data++ = (word >> 8) & 0xff;
offset++;
len--;
eeprom->len++;
}
while (len >= 2) {
int word;
word = mv88e6352_read_eeprom_word(ds, offset >> 1);
if (word < 0)
return word;
*data++ = word & 0xff;
*data++ = (word >> 8) & 0xff;
offset += 2;
len -= 2;
eeprom->len += 2;
}
if (len) {
int word;
word = mv88e6352_read_eeprom_word(ds, offset >> 1);
if (word < 0)
return word;
*data++ = word & 0xff;
offset++;
len--;
eeprom->len++;
}
return 0;
}
static int mv88e6352_eeprom_is_readonly(struct dsa_switch *ds)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP);
if (ret < 0)
return ret;
if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN))
return -EROFS;
return 0;
}
static int mv88e6352_write_eeprom_word(struct dsa_switch *ds, int addr,
u16 data)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
mutex_lock(&ps->eeprom_mutex);
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
if (ret < 0)
goto error;
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
GLOBAL2_EEPROM_OP_WRITE |
(addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
if (ret < 0)
goto error;
ret = mv88e6xxx_eeprom_busy_wait(ds);
error:
mutex_unlock(&ps->eeprom_mutex);
return ret;
}
static int mv88e6352_set_eeprom(struct dsa_switch *ds,
struct ethtool_eeprom *eeprom, u8 *data)
{
int offset;
int ret;
int len;
if (eeprom->magic != 0xc3ec4951)
return -EINVAL;
ret = mv88e6352_eeprom_is_readonly(ds);
if (ret)
return ret;
offset = eeprom->offset;
len = eeprom->len;
eeprom->len = 0;
ret = mv88e6xxx_eeprom_load_wait(ds);
if (ret < 0)
return ret;
if (offset & 1) {
int word;
word = mv88e6352_read_eeprom_word(ds, offset >> 1);
if (word < 0)
return word;
word = (*data++ << 8) | (word & 0xff);
ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word);
if (ret < 0)
return ret;
offset++;
len--;
eeprom->len++;
}
while (len >= 2) {
int word;
word = *data++;
word |= *data++ << 8;
ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word);
if (ret < 0)
return ret;
offset += 2;
len -= 2;
eeprom->len += 2;
}
if (len) {
int word;
word = mv88e6352_read_eeprom_word(ds, offset >> 1);
if (word < 0)
return word;
word = (word & 0xff00) | *data++;
ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word);
if (ret < 0)
return ret;
offset++;
len--;
eeprom->len++;
}
return 0;
}
struct dsa_switch_driver mv88e6352_switch_driver = {
.tag_protocol = DSA_TAG_PROTO_EDSA,
.probe = mv88e6352_drv_probe,
.setup = mv88e6352_setup,
.set_addr = mv88e6xxx_set_addr_indirect,
.phy_read = mv88e6xxx_phy_read_indirect,
.phy_write = mv88e6xxx_phy_write_indirect,
.get_strings = mv88e6xxx_get_strings,
.get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
.get_sset_count = mv88e6xxx_get_sset_count,
.adjust_link = mv88e6xxx_adjust_link,
.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
.get_eeprom = mv88e6352_get_eeprom,
.set_eeprom = mv88e6352_set_eeprom,
.get_regs_len = mv88e6xxx_get_regs_len,
.get_regs = mv88e6xxx_get_regs,
.port_bridge_join = mv88e6xxx_port_bridge_join,
.port_bridge_leave = mv88e6xxx_port_bridge_leave,
.port_stp_state_set = mv88e6xxx_port_stp_state_set,
.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,
};
MODULE_ALIAS("platform:mv88e6172");
MODULE_ALIAS("platform:mv88e6176");
MODULE_ALIAS("platform:mv88e6320");
MODULE_ALIAS("platform:mv88e6321");
MODULE_ALIAS("platform:mv88e6352");
...@@ -173,7 +173,7 @@ int mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr, ...@@ -173,7 +173,7 @@ int mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr,
return ret; return ret;
} }
int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr) static int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int err; int err;
...@@ -192,7 +192,7 @@ int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr) ...@@ -192,7 +192,7 @@ int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr)
(addr[4] << 8) | addr[5]); (addr[4] << 8) | addr[5]);
} }
int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) static int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret; int ret;
...@@ -225,6 +225,16 @@ int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) ...@@ -225,6 +225,16 @@ int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
return 0; return 0;
} }
int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SWITCH_MAC))
return mv88e6xxx_set_addr_indirect(ds, addr);
else
return mv88e6xxx_set_addr_direct(ds, addr);
}
static int _mv88e6xxx_phy_read(struct mv88e6xxx_priv_state *ps, int addr, static int _mv88e6xxx_phy_read(struct mv88e6xxx_priv_state *ps, int addr,
int regnum) int regnum)
{ {
...@@ -241,24 +251,23 @@ static int _mv88e6xxx_phy_write(struct mv88e6xxx_priv_state *ps, int addr, ...@@ -241,24 +251,23 @@ static int _mv88e6xxx_phy_write(struct mv88e6xxx_priv_state *ps, int addr,
return 0; return 0;
} }
#ifdef CONFIG_NET_DSA_MV88E6XXX_NEED_PPU
static int mv88e6xxx_ppu_disable(struct mv88e6xxx_priv_state *ps) static int mv88e6xxx_ppu_disable(struct mv88e6xxx_priv_state *ps)
{ {
int ret; int ret;
unsigned long timeout; unsigned long timeout;
ret = mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL); ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL,
ret & ~GLOBAL_CONTROL_PPU_ENABLE); ret & ~GLOBAL_CONTROL_PPU_ENABLE);
if (ret) if (ret)
return ret; return ret;
timeout = jiffies + 1 * HZ; timeout = jiffies + 1 * HZ;
while (time_before(jiffies, timeout)) { while (time_before(jiffies, timeout)) {
ret = mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS); ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -361,35 +370,33 @@ void mv88e6xxx_ppu_state_init(struct mv88e6xxx_priv_state *ps) ...@@ -361,35 +370,33 @@ void mv88e6xxx_ppu_state_init(struct mv88e6xxx_priv_state *ps)
ps->ppu_timer.function = mv88e6xxx_ppu_reenable_timer; ps->ppu_timer.function = mv88e6xxx_ppu_reenable_timer;
} }
int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum) static int mv88e6xxx_phy_read_ppu(struct mv88e6xxx_priv_state *ps, int addr,
int regnum)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret; int ret;
ret = mv88e6xxx_ppu_access_get(ps); ret = mv88e6xxx_ppu_access_get(ps);
if (ret >= 0) { if (ret >= 0) {
ret = mv88e6xxx_reg_read(ps, addr, regnum); ret = _mv88e6xxx_reg_read(ps, addr, regnum);
mv88e6xxx_ppu_access_put(ps); mv88e6xxx_ppu_access_put(ps);
} }
return ret; return ret;
} }
int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr, static int mv88e6xxx_phy_write_ppu(struct mv88e6xxx_priv_state *ps, int addr,
int regnum, u16 val) int regnum, u16 val)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret; int ret;
ret = mv88e6xxx_ppu_access_get(ps); ret = mv88e6xxx_ppu_access_get(ps);
if (ret >= 0) { if (ret >= 0) {
ret = mv88e6xxx_reg_write(ps, addr, regnum, val); ret = _mv88e6xxx_reg_write(ps, addr, regnum, val);
mv88e6xxx_ppu_access_put(ps); mv88e6xxx_ppu_access_put(ps);
} }
return ret; return ret;
} }
#endif
static bool mv88e6xxx_6065_family(struct mv88e6xxx_priv_state *ps) static bool mv88e6xxx_6065_family(struct mv88e6xxx_priv_state *ps)
{ {
...@@ -460,8 +467,8 @@ static bool mv88e6xxx_has_stu(struct mv88e6xxx_priv_state *ps) ...@@ -460,8 +467,8 @@ static bool mv88e6xxx_has_stu(struct mv88e6xxx_priv_state *ps)
* phy. However, in the case of a fixed link phy, we force the port * phy. However, in the case of a fixed link phy, we force the port
* settings from the fixed link settings. * settings from the fixed link settings.
*/ */
void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
struct phy_device *phydev) struct phy_device *phydev)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u32 reg; u32 reg;
...@@ -707,7 +714,8 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_priv_state *ps, ...@@ -707,7 +714,8 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_priv_state *ps,
return value; return value;
} }
void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data) static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
uint8_t *data)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
struct mv88e6xxx_hw_stat *stat; struct mv88e6xxx_hw_stat *stat;
...@@ -723,7 +731,7 @@ void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data) ...@@ -723,7 +731,7 @@ void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
} }
} }
int mv88e6xxx_get_sset_count(struct dsa_switch *ds) static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
struct mv88e6xxx_hw_stat *stat; struct mv88e6xxx_hw_stat *stat;
...@@ -737,9 +745,8 @@ int mv88e6xxx_get_sset_count(struct dsa_switch *ds) ...@@ -737,9 +745,8 @@ int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
return j; return j;
} }
void static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, uint64_t *data)
int port, uint64_t *data)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
struct mv88e6xxx_hw_stat *stat; struct mv88e6xxx_hw_stat *stat;
...@@ -764,13 +771,13 @@ mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, ...@@ -764,13 +771,13 @@ mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
mutex_unlock(&ps->smi_mutex); mutex_unlock(&ps->smi_mutex);
} }
int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port) static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
{ {
return 32 * sizeof(u16); return 32 * sizeof(u16);
} }
void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
struct ethtool_regs *regs, void *_p) struct ethtool_regs *regs, void *_p)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u16 *p = _p; u16 *p = _p;
...@@ -780,13 +787,17 @@ void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, ...@@ -780,13 +787,17 @@ void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
memset(p, 0xff, 32 * sizeof(u16)); memset(p, 0xff, 32 * sizeof(u16));
mutex_lock(&ps->smi_mutex);
for (i = 0; i < 32; i++) { for (i = 0; i < 32; i++) {
int ret; int ret;
ret = mv88e6xxx_reg_read(ps, REG_PORT(port), i); ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), i);
if (ret >= 0) if (ret >= 0)
p[i] = ret; p[i] = ret;
} }
mutex_unlock(&ps->smi_mutex);
} }
static int _mv88e6xxx_wait(struct mv88e6xxx_priv_state *ps, int reg, int offset, static int _mv88e6xxx_wait(struct mv88e6xxx_priv_state *ps, int reg, int offset,
...@@ -826,7 +837,7 @@ static int _mv88e6xxx_phy_wait(struct mv88e6xxx_priv_state *ps) ...@@ -826,7 +837,7 @@ static int _mv88e6xxx_phy_wait(struct mv88e6xxx_priv_state *ps)
GLOBAL2_SMI_OP_BUSY); GLOBAL2_SMI_OP_BUSY);
} }
int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds) static int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
...@@ -834,7 +845,7 @@ int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds) ...@@ -834,7 +845,7 @@ int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
GLOBAL2_EEPROM_OP_LOAD); GLOBAL2_EEPROM_OP_LOAD);
} }
int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds) static int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
...@@ -842,6 +853,215 @@ int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds) ...@@ -842,6 +853,215 @@ int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
GLOBAL2_EEPROM_OP_BUSY); GLOBAL2_EEPROM_OP_BUSY);
} }
static int mv88e6xxx_read_eeprom_word(struct dsa_switch *ds, int addr)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
mutex_lock(&ps->eeprom_mutex);
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
GLOBAL2_EEPROM_OP_READ |
(addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
if (ret < 0)
goto error;
ret = mv88e6xxx_eeprom_busy_wait(ds);
if (ret < 0)
goto error;
ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA);
error:
mutex_unlock(&ps->eeprom_mutex);
return ret;
}
static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
struct ethtool_eeprom *eeprom, u8 *data)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int offset;
int len;
int ret;
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
return -EOPNOTSUPP;
offset = eeprom->offset;
len = eeprom->len;
eeprom->len = 0;
eeprom->magic = 0xc3ec4951;
ret = mv88e6xxx_eeprom_load_wait(ds);
if (ret < 0)
return ret;
if (offset & 1) {
int word;
word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
if (word < 0)
return word;
*data++ = (word >> 8) & 0xff;
offset++;
len--;
eeprom->len++;
}
while (len >= 2) {
int word;
word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
if (word < 0)
return word;
*data++ = word & 0xff;
*data++ = (word >> 8) & 0xff;
offset += 2;
len -= 2;
eeprom->len += 2;
}
if (len) {
int word;
word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
if (word < 0)
return word;
*data++ = word & 0xff;
offset++;
len--;
eeprom->len++;
}
return 0;
}
static int mv88e6xxx_eeprom_is_readonly(struct dsa_switch *ds)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP);
if (ret < 0)
return ret;
if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN))
return -EROFS;
return 0;
}
static int mv88e6xxx_write_eeprom_word(struct dsa_switch *ds, int addr,
u16 data)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
mutex_lock(&ps->eeprom_mutex);
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
if (ret < 0)
goto error;
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
GLOBAL2_EEPROM_OP_WRITE |
(addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
if (ret < 0)
goto error;
ret = mv88e6xxx_eeprom_busy_wait(ds);
error:
mutex_unlock(&ps->eeprom_mutex);
return ret;
}
static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
struct ethtool_eeprom *eeprom, u8 *data)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int offset;
int ret;
int len;
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
return -EOPNOTSUPP;
if (eeprom->magic != 0xc3ec4951)
return -EINVAL;
ret = mv88e6xxx_eeprom_is_readonly(ds);
if (ret)
return ret;
offset = eeprom->offset;
len = eeprom->len;
eeprom->len = 0;
ret = mv88e6xxx_eeprom_load_wait(ds);
if (ret < 0)
return ret;
if (offset & 1) {
int word;
word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
if (word < 0)
return word;
word = (*data++ << 8) | (word & 0xff);
ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
if (ret < 0)
return ret;
offset++;
len--;
eeprom->len++;
}
while (len >= 2) {
int word;
word = *data++;
word |= *data++ << 8;
ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
if (ret < 0)
return ret;
offset += 2;
len -= 2;
eeprom->len += 2;
}
if (len) {
int word;
word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
if (word < 0)
return word;
word = (word & 0xff00) | *data++;
ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
if (ret < 0)
return ret;
offset++;
len--;
eeprom->len++;
}
return 0;
}
static int _mv88e6xxx_atu_wait(struct mv88e6xxx_priv_state *ps) static int _mv88e6xxx_atu_wait(struct mv88e6xxx_priv_state *ps)
{ {
return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_ATU_OP, return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_ATU_OP,
...@@ -884,11 +1104,15 @@ static int _mv88e6xxx_phy_write_indirect(struct mv88e6xxx_priv_state *ps, ...@@ -884,11 +1104,15 @@ static int _mv88e6xxx_phy_write_indirect(struct mv88e6xxx_priv_state *ps,
return _mv88e6xxx_phy_wait(ps); return _mv88e6xxx_phy_wait(ps);
} }
int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
struct ethtool_eee *e)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int reg; int reg;
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEE))
return -EOPNOTSUPP;
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
reg = _mv88e6xxx_phy_read_indirect(ps, port, 16); reg = _mv88e6xxx_phy_read_indirect(ps, port, 16);
...@@ -910,13 +1134,16 @@ int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) ...@@ -910,13 +1134,16 @@ int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
return reg; return reg;
} }
int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
struct phy_device *phydev, struct ethtool_eee *e) struct phy_device *phydev, struct ethtool_eee *e)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int reg; int reg;
int ret; int ret;
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEE))
return -EOPNOTSUPP;
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
ret = _mv88e6xxx_phy_read_indirect(ps, port, 16); ret = _mv88e6xxx_phy_read_indirect(ps, port, 16);
...@@ -1138,11 +1365,15 @@ static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_priv_state *ps, ...@@ -1138,11 +1365,15 @@ static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_priv_state *ps,
return _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN, reg); return _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN, reg);
} }
void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
u8 state)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int stp_state; int stp_state;
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_PORTSTATE))
return;
switch (state) { switch (state) {
case BR_STATE_DISABLED: case BR_STATE_DISABLED:
stp_state = PORT_CONTROL_STATE_DISABLED; stp_state = PORT_CONTROL_STATE_DISABLED;
...@@ -1358,15 +1589,18 @@ static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_priv_state *ps, ...@@ -1358,15 +1589,18 @@ static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_priv_state *ps,
return 0; return 0;
} }
int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_vlan *vlan, struct switchdev_obj_port_vlan *vlan,
int (*cb)(struct switchdev_obj *obj)) int (*cb)(struct switchdev_obj *obj))
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
struct mv88e6xxx_vtu_stu_entry next; struct mv88e6xxx_vtu_stu_entry next;
u16 pvid; u16 pvid;
int err; int err;
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
return -EOPNOTSUPP;
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
err = _mv88e6xxx_port_pvid_get(ps, port, &pvid); err = _mv88e6xxx_port_pvid_get(ps, port, &pvid);
...@@ -1782,14 +2016,17 @@ static const char * const mv88e6xxx_port_8021q_mode_names[] = { ...@@ -1782,14 +2016,17 @@ static const char * const mv88e6xxx_port_8021q_mode_names[] = {
[PORT_CONTROL_2_8021Q_SECURE] = "Secure", [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
}; };
int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
bool vlan_filtering) bool vlan_filtering)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE : u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
PORT_CONTROL_2_8021Q_DISABLED; PORT_CONTROL_2_8021Q_DISABLED;
int ret; int ret;
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
return -EOPNOTSUPP;
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_2); ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_2);
...@@ -1819,12 +2056,16 @@ int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, ...@@ -1819,12 +2056,16 @@ int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
return ret; return ret;
} }
int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, static int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan, const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans) struct switchdev_trans *trans)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int err; int err;
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
return -EOPNOTSUPP;
/* If the requested port doesn't belong to the same bridge as the VLAN /* 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. * members, do not support it (yet) and fallback to software VLAN.
*/ */
...@@ -1856,15 +2097,18 @@ static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_priv_state *ps, int port, ...@@ -1856,15 +2097,18 @@ static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_priv_state *ps, int port,
return _mv88e6xxx_vtu_loadpurge(ps, &vlan); return _mv88e6xxx_vtu_loadpurge(ps, &vlan);
} }
void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan, const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans) struct switchdev_trans *trans)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
u16 vid; u16 vid;
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
return;
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
...@@ -1915,13 +2159,16 @@ static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_priv_state *ps, ...@@ -1915,13 +2159,16 @@ static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_priv_state *ps,
return _mv88e6xxx_atu_remove(ps, vlan.fid, port, false); return _mv88e6xxx_atu_remove(ps, vlan.fid, port, false);
} }
int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan) const struct switchdev_obj_port_vlan *vlan)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u16 pvid, vid; u16 pvid, vid;
int err = 0; int err = 0;
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
return -EOPNOTSUPP;
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
err = _mv88e6xxx_port_pvid_get(ps, port, &pvid); err = _mv88e6xxx_port_pvid_get(ps, port, &pvid);
...@@ -2026,37 +2273,48 @@ static int _mv88e6xxx_port_fdb_load(struct mv88e6xxx_priv_state *ps, int port, ...@@ -2026,37 +2273,48 @@ static int _mv88e6xxx_port_fdb_load(struct mv88e6xxx_priv_state *ps, int port,
return _mv88e6xxx_atu_load(ps, &entry); return _mv88e6xxx_atu_load(ps, &entry);
} }
int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb, const struct switchdev_obj_port_fdb *fdb,
struct switchdev_trans *trans) struct switchdev_trans *trans)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
return -EOPNOTSUPP;
/* We don't need any dynamic resource from the kernel (yet), /* We don't need any dynamic resource from the kernel (yet),
* so skip the prepare phase. * so skip the prepare phase.
*/ */
return 0; return 0;
} }
void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb, const struct switchdev_obj_port_fdb *fdb,
struct switchdev_trans *trans) struct switchdev_trans *trans)
{ {
int state = is_multicast_ether_addr(fdb->addr) ? int state = is_multicast_ether_addr(fdb->addr) ?
GLOBAL_ATU_DATA_STATE_MC_STATIC : GLOBAL_ATU_DATA_STATE_MC_STATIC :
GLOBAL_ATU_DATA_STATE_UC_STATIC; GLOBAL_ATU_DATA_STATE_UC_STATIC;
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
return;
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
if (_mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid, state)) if (_mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid, state))
netdev_err(ds->ports[port], "failed to load MAC address\n"); netdev_err(ds->ports[port], "failed to load MAC address\n");
mutex_unlock(&ps->smi_mutex); mutex_unlock(&ps->smi_mutex);
} }
int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb) const struct switchdev_obj_port_fdb *fdb)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret; int ret;
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
return -EOPNOTSUPP;
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
ret = _mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid, ret = _mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid,
GLOBAL_ATU_DATA_STATE_UNUSED); GLOBAL_ATU_DATA_STATE_UNUSED);
...@@ -2151,9 +2409,9 @@ static int _mv88e6xxx_port_fdb_dump_one(struct mv88e6xxx_priv_state *ps, ...@@ -2151,9 +2409,9 @@ static int _mv88e6xxx_port_fdb_dump_one(struct mv88e6xxx_priv_state *ps,
return err; return err;
} }
int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_fdb *fdb, struct switchdev_obj_port_fdb *fdb,
int (*cb)(struct switchdev_obj *obj)) int (*cb)(struct switchdev_obj *obj))
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
struct mv88e6xxx_vtu_stu_entry vlan = { struct mv88e6xxx_vtu_stu_entry vlan = {
...@@ -2162,6 +2420,9 @@ int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, ...@@ -2162,6 +2420,9 @@ int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
u16 fid; u16 fid;
int err; int err;
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
return -EOPNOTSUPP;
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
/* Dump port's default Filtering Information Database (VLAN ID 0) */ /* Dump port's default Filtering Information Database (VLAN ID 0) */
...@@ -2198,12 +2459,15 @@ int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, ...@@ -2198,12 +2459,15 @@ int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
return err; return err;
} }
int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
struct net_device *bridge) struct net_device *bridge)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int i, err = 0; int i, err = 0;
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VLANTABLE))
return -EOPNOTSUPP;
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
/* Assign the bridge and remap each port's VLANTable */ /* Assign the bridge and remap each port's VLANTable */
...@@ -2222,12 +2486,15 @@ int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, ...@@ -2222,12 +2486,15 @@ int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
return err; return err;
} }
void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port) static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
struct net_device *bridge = ps->ports[port].bridge_dev; struct net_device *bridge = ps->ports[port].bridge_dev;
int i; int i;
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VLANTABLE))
return;
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
/* Unassign the bridge and remap each port's VLANTable */ /* Unassign the bridge and remap each port's VLANTable */
...@@ -2294,6 +2561,68 @@ static int _mv88e6xxx_phy_page_read(struct mv88e6xxx_priv_state *ps, ...@@ -2294,6 +2561,68 @@ static int _mv88e6xxx_phy_page_read(struct mv88e6xxx_priv_state *ps,
return ret; return ret;
} }
static int mv88e6xxx_switch_reset(struct mv88e6xxx_priv_state *ps)
{
bool ppu_active = mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU_ACTIVE);
u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
struct gpio_desc *gpiod = ps->ds->pd->reset;
unsigned long timeout;
int ret;
int i;
/* Set all ports to the disabled state. */
for (i = 0; i < ps->info->num_ports; i++) {
ret = _mv88e6xxx_reg_read(ps, REG_PORT(i), PORT_CONTROL);
if (ret < 0)
return ret;
ret = _mv88e6xxx_reg_write(ps, REG_PORT(i), PORT_CONTROL,
ret & 0xfffc);
if (ret)
return ret;
}
/* 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)
ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc000);
else
ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc400);
if (ret)
return ret;
/* Wait up to one second for reset to complete. */
timeout = jiffies + 1 * HZ;
while (time_before(jiffies, timeout)) {
ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, 0x00);
if (ret < 0)
return ret;
if ((ret & is_reset) == is_reset)
break;
usleep_range(1000, 2000);
}
if (time_after(jiffies, timeout))
ret = -ETIMEDOUT;
else
ret = 0;
return ret;
}
static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_priv_state *ps) static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_priv_state *ps)
{ {
int ret; int ret;
...@@ -2313,14 +2642,12 @@ static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_priv_state *ps) ...@@ -2313,14 +2642,12 @@ static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_priv_state *ps)
return ret; return ret;
} }
static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) static int mv88e6xxx_setup_port(struct mv88e6xxx_priv_state *ps, int port)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct dsa_switch *ds = ps->ds;
int ret; int ret;
u16 reg; u16 reg;
mutex_lock(&ps->smi_mutex);
if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
...@@ -2349,7 +2676,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ...@@ -2349,7 +2676,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
PORT_PCS_CTRL, reg); PORT_PCS_CTRL, reg);
if (ret) if (ret)
goto abort; return ret;
} }
/* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock, /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
...@@ -2413,7 +2740,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ...@@ -2413,7 +2740,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
PORT_CONTROL, reg); PORT_CONTROL, reg);
if (ret) if (ret)
goto abort; return ret;
} }
/* If this port is connected to a SerDes, make sure the SerDes is not /* If this port is connected to a SerDes, make sure the SerDes is not
...@@ -2422,14 +2749,14 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ...@@ -2422,14 +2749,14 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
if (mv88e6xxx_6352_family(ps)) { if (mv88e6xxx_6352_family(ps)) {
ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS); ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS);
if (ret < 0) if (ret < 0)
goto abort; return ret;
ret &= PORT_STATUS_CMODE_MASK; ret &= PORT_STATUS_CMODE_MASK;
if ((ret == PORT_STATUS_CMODE_100BASE_X) || if ((ret == PORT_STATUS_CMODE_100BASE_X) ||
(ret == PORT_STATUS_CMODE_1000BASE_X) || (ret == PORT_STATUS_CMODE_1000BASE_X) ||
(ret == PORT_STATUS_CMODE_SGMII)) { (ret == PORT_STATUS_CMODE_SGMII)) {
ret = mv88e6xxx_power_on_serdes(ps); ret = mv88e6xxx_power_on_serdes(ps);
if (ret < 0) if (ret < 0)
goto abort; return ret;
} }
} }
...@@ -2466,7 +2793,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ...@@ -2466,7 +2793,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
PORT_CONTROL_2, reg); PORT_CONTROL_2, reg);
if (ret) if (ret)
goto abort; return ret;
} }
/* Port Association Vector: when learning source addresses /* Port Association Vector: when learning source addresses
...@@ -2481,13 +2808,13 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ...@@ -2481,13 +2808,13 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_ASSOC_VECTOR, reg); ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_ASSOC_VECTOR, reg);
if (ret) if (ret)
goto abort; return ret;
/* Egress rate control 2: disable egress rate control. */ /* Egress rate control 2: disable egress rate control. */
ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_RATE_CONTROL_2, ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_RATE_CONTROL_2,
0x0000); 0x0000);
if (ret) if (ret)
goto abort; return ret;
if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
...@@ -2499,7 +2826,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ...@@ -2499,7 +2826,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
PORT_PAUSE_CTRL, 0x0000); PORT_PAUSE_CTRL, 0x0000);
if (ret) if (ret)
goto abort; return ret;
/* Port ATU control: disable limiting the number of /* Port ATU control: disable limiting the number of
* address database entries that this port is allowed * address database entries that this port is allowed
...@@ -2513,7 +2840,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ...@@ -2513,7 +2840,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
PORT_PRI_OVERRIDE, 0x0000); PORT_PRI_OVERRIDE, 0x0000);
if (ret) if (ret)
goto abort; return ret;
/* Port Ethertype: use the Ethertype DSA Ethertype /* Port Ethertype: use the Ethertype DSA Ethertype
* value. * value.
...@@ -2521,14 +2848,14 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ...@@ -2521,14 +2848,14 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
PORT_ETH_TYPE, ETH_P_EDSA); PORT_ETH_TYPE, ETH_P_EDSA);
if (ret) if (ret)
goto abort; return ret;
/* Tag Remap: use an identity 802.1p prio -> switch /* Tag Remap: use an identity 802.1p prio -> switch
* prio mapping. * prio mapping.
*/ */
ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
PORT_TAG_REGMAP_0123, 0x3210); PORT_TAG_REGMAP_0123, 0x3210);
if (ret) if (ret)
goto abort; return ret;
/* Tag Remap 2: use an identity 802.1p prio -> switch /* Tag Remap 2: use an identity 802.1p prio -> switch
* prio mapping. * prio mapping.
...@@ -2536,7 +2863,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ...@@ -2536,7 +2863,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
PORT_TAG_REGMAP_4567, 0x7654); PORT_TAG_REGMAP_4567, 0x7654);
if (ret) if (ret)
goto abort; return ret;
} }
if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
...@@ -2547,7 +2874,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ...@@ -2547,7 +2874,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
PORT_RATE_CONTROL, 0x0001); PORT_RATE_CONTROL, 0x0001);
if (ret) if (ret)
goto abort; return ret;
} }
/* Port Control 1: disable trunking, disable sending /* Port Control 1: disable trunking, disable sending
...@@ -2555,7 +2882,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ...@@ -2555,7 +2882,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
*/ */
ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1, 0x0000); ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1, 0x0000);
if (ret) if (ret)
goto abort; return ret;
/* Port based VLAN map: give each port the same default address /* Port based VLAN map: give each port the same default address
* database, and allow bidirectional communication between the * database, and allow bidirectional communication between the
...@@ -2563,52 +2890,60 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ...@@ -2563,52 +2890,60 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
*/ */
ret = _mv88e6xxx_port_fid_set(ps, port, 0); ret = _mv88e6xxx_port_fid_set(ps, port, 0);
if (ret) if (ret)
goto abort; return ret;
ret = _mv88e6xxx_port_based_vlan_map(ps, port); ret = _mv88e6xxx_port_based_vlan_map(ps, port);
if (ret) if (ret)
goto abort; return ret;
/* Default VLAN ID and priority: don't set a default VLAN /* Default VLAN ID and priority: don't set a default VLAN
* ID, and set the default packet priority to zero. * ID, and set the default packet priority to zero.
*/ */
ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_DEFAULT_VLAN, ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_DEFAULT_VLAN,
0x0000); 0x0000);
abort: if (ret)
mutex_unlock(&ps->smi_mutex); return ret;
return ret;
}
int mv88e6xxx_setup_ports(struct dsa_switch *ds)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
int i;
for (i = 0; i < ps->info->num_ports; i++) {
ret = mv88e6xxx_setup_port(ds, i);
if (ret < 0)
return ret;
}
return 0; return 0;
} }
int mv88e6xxx_setup_common(struct mv88e6xxx_priv_state *ps) static int mv88e6xxx_setup_global(struct mv88e6xxx_priv_state *ps)
{ {
mutex_init(&ps->smi_mutex); struct dsa_switch *ds = ps->ds;
u32 upstream_port = dsa_upstream_port(ds);
u16 reg;
int err;
int i;
INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work); /* Enable the PHY Polling Unit if present, don't discard any packets,
* and mask all interrupt sources.
*/
reg = 0;
if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU) ||
mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU_ACTIVE))
reg |= GLOBAL_CONTROL_PPU_ENABLE;
return 0; err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, reg);
} if (err)
return err;
int mv88e6xxx_setup_global(struct dsa_switch *ds) /* Configure the upstream port, and configure it as the port to which
{ * ingress and egress and ARP monitor frames are to be sent.
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); */
int err; reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
int i; upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg);
if (err)
return err;
/* Disable remote management, and set the switch's DSA device number. */
err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL_2,
GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
(ds->index & 0x1f));
if (err)
return err;
mutex_lock(&ps->smi_mutex);
/* Set the default address aging time to 5 minutes, and /* Set the default address aging time to 5 minutes, and
* enable address learn messages to be sent to all message * enable address learn messages to be sent to all message
* ports. * ports.
...@@ -2616,45 +2951,45 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) ...@@ -2616,45 +2951,45 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds)
err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL, err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL,
0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL); 0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL);
if (err) if (err)
goto unlock; return err;
/* Configure the IP ToS mapping registers. */ /* Configure the IP ToS mapping registers. */
err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000); err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
if (err) if (err)
goto unlock; return err;
err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000); err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
if (err) if (err)
goto unlock; return err;
err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555); err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
if (err) if (err)
goto unlock; return err;
err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555); err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
if (err) if (err)
goto unlock; return err;
err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa); err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
if (err) if (err)
goto unlock; return err;
err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa); err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
if (err) if (err)
goto unlock; return err;
err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff); err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
if (err) if (err)
goto unlock; return err;
err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff); err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
if (err) if (err)
goto unlock; return err;
/* Configure the IEEE 802.1p priority mapping register. */ /* Configure the IEEE 802.1p priority mapping register. */
err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41); err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
if (err) if (err)
goto unlock; return err;
/* Send all frames with destination addresses matching /* Send all frames with destination addresses matching
* 01:80:c2:00:00:0x to the CPU port. * 01:80:c2:00:00:0x to the CPU port.
*/ */
err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X, 0xffff); err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X, 0xffff);
if (err) if (err)
goto unlock; return err;
/* Ignore removed tag data on doubly tagged packets, disable /* Ignore removed tag data on doubly tagged packets, disable
* flow control messages, force flow control priority to the * flow control messages, force flow control priority to the
...@@ -2665,15 +3000,15 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) ...@@ -2665,15 +3000,15 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds)
0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 | 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 |
GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI); GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI);
if (err) if (err)
goto unlock; return err;
/* Program the DSA routing table. */ /* Program the DSA routing table. */
for (i = 0; i < 32; i++) { for (i = 0; i < 32; i++) {
int nexthop = 0x1f; int nexthop = 0x1f;
if (ds->pd->rtable && if (ps->ds->pd->rtable &&
i != ds->index && i < ds->dst->pd->nr_chips) i != ps->ds->index && i < ps->ds->dst->pd->nr_chips)
nexthop = ds->pd->rtable[i] & 0x1f; nexthop = ps->ds->pd->rtable[i] & 0x1f;
err = _mv88e6xxx_reg_write( err = _mv88e6xxx_reg_write(
ps, REG_GLOBAL2, ps, REG_GLOBAL2,
...@@ -2681,7 +3016,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) ...@@ -2681,7 +3016,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds)
GLOBAL2_DEVICE_MAPPING_UPDATE | GLOBAL2_DEVICE_MAPPING_UPDATE |
(i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) | nexthop); (i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) | nexthop);
if (err) if (err)
goto unlock; return err;
} }
/* Clear all trunk masks. */ /* Clear all trunk masks. */
...@@ -2691,7 +3026,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) ...@@ -2691,7 +3026,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds)
(i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) | (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) |
((1 << ps->info->num_ports) - 1)); ((1 << ps->info->num_ports) - 1));
if (err) if (err)
goto unlock; return err;
} }
/* Clear all trunk mappings. */ /* Clear all trunk mappings. */
...@@ -2702,7 +3037,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) ...@@ -2702,7 +3037,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds)
GLOBAL2_TRUNK_MAPPING_UPDATE | GLOBAL2_TRUNK_MAPPING_UPDATE |
(i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT)); (i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT));
if (err) if (err)
goto unlock; return err;
} }
if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
...@@ -2714,7 +3049,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) ...@@ -2714,7 +3049,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds)
err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
GLOBAL2_MGMT_EN_2X, 0xffff); GLOBAL2_MGMT_EN_2X, 0xffff);
if (err) if (err)
goto unlock; return err;
/* Initialise cross-chip port VLAN table to reset /* Initialise cross-chip port VLAN table to reset
* defaults. * defaults.
...@@ -2722,7 +3057,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) ...@@ -2722,7 +3057,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds)
err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
GLOBAL2_PVT_ADDR, 0x9000); GLOBAL2_PVT_ADDR, 0x9000);
if (err) if (err)
goto unlock; return err;
/* Clear the priority override table. */ /* Clear the priority override table. */
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
...@@ -2730,7 +3065,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) ...@@ -2730,7 +3065,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds)
GLOBAL2_PRIO_OVERRIDE, GLOBAL2_PRIO_OVERRIDE,
0x8000 | (i << 8)); 0x8000 | (i << 8));
if (err) if (err)
goto unlock; return err;
} }
} }
...@@ -2747,7 +3082,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) ...@@ -2747,7 +3082,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds)
GLOBAL2_INGRESS_OP, GLOBAL2_INGRESS_OP,
0x9000 | (i << 8)); 0x9000 | (i << 8));
if (err) if (err)
goto unlock; return err;
} }
} }
...@@ -2755,89 +3090,64 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) ...@@ -2755,89 +3090,64 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds)
err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP, err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
GLOBAL_STATS_OP_FLUSH_ALL); GLOBAL_STATS_OP_FLUSH_ALL);
if (err) if (err)
goto unlock; return err;
/* Wait for the flush to complete. */ /* Wait for the flush to complete. */
err = _mv88e6xxx_stats_wait(ps); err = _mv88e6xxx_stats_wait(ps);
if (err < 0) if (err)
goto unlock; return err;
/* Clear all ATU entries */ /* Clear all ATU entries */
err = _mv88e6xxx_atu_flush(ps, 0, true); err = _mv88e6xxx_atu_flush(ps, 0, true);
if (err < 0) if (err)
goto unlock; return err;
/* Clear all the VTU and STU entries */ /* Clear all the VTU and STU entries */
err = _mv88e6xxx_vtu_stu_flush(ps); err = _mv88e6xxx_vtu_stu_flush(ps);
unlock: if (err < 0)
mutex_unlock(&ps->smi_mutex); return err;
return err; return err;
} }
int mv88e6xxx_switch_reset(struct mv88e6xxx_priv_state *ps, bool ppu_active) static int mv88e6xxx_setup(struct dsa_switch *ds)
{ {
u16 is_reset = (ppu_active ? 0x8800 : 0xc800); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
struct gpio_desc *gpiod = ps->ds->pd->reset; int err;
unsigned long timeout;
int ret;
int i; int i;
mutex_lock(&ps->smi_mutex); ps->ds = ds;
/* Set all ports to the disabled state. */ mutex_init(&ps->smi_mutex);
for (i = 0; i < ps->info->num_ports; i++) {
ret = _mv88e6xxx_reg_read(ps, REG_PORT(i), PORT_CONTROL);
if (ret < 0)
goto unlock;
ret = _mv88e6xxx_reg_write(ps, REG_PORT(i), PORT_CONTROL, INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work);
ret & 0xfffc);
if (ret)
goto unlock;
}
/* Wait for transmit queues to drain. */ if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
usleep_range(2000, 4000); mutex_init(&ps->eeprom_mutex);
/* If there is a gpio connected to the reset pin, toggle it */ if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
if (gpiod) { mv88e6xxx_ppu_state_init(ps);
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 mutex_lock(&ps->smi_mutex);
* needs to be active to support indirect phy register access
* through global registers 0x18 and 0x19. err = mv88e6xxx_switch_reset(ps);
*/ if (err)
if (ppu_active)
ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc000);
else
ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc400);
if (ret)
goto unlock; goto unlock;
/* Wait up to one second for reset to complete. */ err = mv88e6xxx_setup_global(ps);
timeout = jiffies + 1 * HZ; if (err)
while (time_before(jiffies, timeout)) { goto unlock;
ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, 0x00);
if (ret < 0)
goto unlock;
if ((ret & is_reset) == is_reset) for (i = 0; i < ps->info->num_ports; i++) {
break; err = mv88e6xxx_setup_port(ps, i);
usleep_range(1000, 2000); if (err)
goto unlock;
} }
if (time_after(jiffies, timeout))
ret = -ETIMEDOUT;
else
ret = 0;
unlock: unlock:
mutex_unlock(&ps->smi_mutex); mutex_unlock(&ps->smi_mutex);
return ret; return err;
} }
int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg) int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg)
...@@ -2873,8 +3183,7 @@ static int mv88e6xxx_port_to_phy_addr(struct mv88e6xxx_priv_state *ps, ...@@ -2873,8 +3183,7 @@ static int mv88e6xxx_port_to_phy_addr(struct mv88e6xxx_priv_state *ps,
return -EINVAL; return -EINVAL;
} }
int static int mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int addr = mv88e6xxx_port_to_phy_addr(ps, port); int addr = mv88e6xxx_port_to_phy_addr(ps, port);
...@@ -2884,29 +3193,20 @@ mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum) ...@@ -2884,29 +3193,20 @@ mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
return 0xffff; return 0xffff;
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
ret = _mv88e6xxx_phy_read(ps, addr, regnum);
mutex_unlock(&ps->smi_mutex);
return ret;
}
int
mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int addr = mv88e6xxx_port_to_phy_addr(ps, port);
int ret;
if (addr < 0) if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
return 0xffff; ret = mv88e6xxx_phy_read_ppu(ps, addr, regnum);
else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
ret = _mv88e6xxx_phy_read_indirect(ps, addr, regnum);
else
ret = _mv88e6xxx_phy_read(ps, addr, regnum);
mutex_lock(&ps->smi_mutex);
ret = _mv88e6xxx_phy_write(ps, addr, regnum, val);
mutex_unlock(&ps->smi_mutex); mutex_unlock(&ps->smi_mutex);
return ret; return ret;
} }
int static int mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum,
mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum) u16 val)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int addr = mv88e6xxx_port_to_phy_addr(ps, port); int addr = mv88e6xxx_port_to_phy_addr(ps, port);
...@@ -2916,24 +3216,14 @@ mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum) ...@@ -2916,24 +3216,14 @@ mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum)
return 0xffff; return 0xffff;
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
ret = _mv88e6xxx_phy_read_indirect(ps, addr, regnum);
mutex_unlock(&ps->smi_mutex);
return ret;
}
int if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int port, int regnum, ret = mv88e6xxx_phy_write_ppu(ps, addr, regnum, val);
u16 val) else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
{ ret = _mv88e6xxx_phy_write_indirect(ps, addr, regnum, val);
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); else
int addr = mv88e6xxx_port_to_phy_addr(ps, port); ret = _mv88e6xxx_phy_write(ps, addr, regnum, val);
int ret;
if (addr < 0)
return addr;
mutex_lock(&ps->smi_mutex);
ret = _mv88e6xxx_phy_write_indirect(ps, addr, regnum, val);
mutex_unlock(&ps->smi_mutex); mutex_unlock(&ps->smi_mutex);
return ret; return ret;
} }
...@@ -3002,23 +3292,26 @@ static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp) ...@@ -3002,23 +3292,26 @@ static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
return 0; return 0;
} }
int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp) static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP))
return -EOPNOTSUPP;
if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps)) if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps))
return mv88e63xx_get_temp(ds, temp); return mv88e63xx_get_temp(ds, temp);
return mv88e61xx_get_temp(ds, temp); return mv88e61xx_get_temp(ds, temp);
} }
int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp) static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int phy = mv88e6xxx_6320_family(ps) ? 3 : 0; int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
int ret; int ret;
if (!mv88e6xxx_6320_family(ps) && !mv88e6xxx_6352_family(ps)) if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
return -EOPNOTSUPP; return -EOPNOTSUPP;
*temp = 0; *temp = 0;
...@@ -3032,13 +3325,13 @@ int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp) ...@@ -3032,13 +3325,13 @@ int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
return 0; return 0;
} }
int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp) static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int phy = mv88e6xxx_6320_family(ps) ? 3 : 0; int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
int ret; int ret;
if (!mv88e6xxx_6320_family(ps) && !mv88e6xxx_6352_family(ps)) if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
return -EOPNOTSUPP; return -EOPNOTSUPP;
ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26); ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
...@@ -3049,13 +3342,13 @@ int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp) ...@@ -3049,13 +3342,13 @@ int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
(ret & 0xe0ff) | (temp << 8)); (ret & 0xe0ff) | (temp << 8));
} }
int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm) static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int phy = mv88e6xxx_6320_family(ps) ? 3 : 0; int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
int ret; int ret;
if (!mv88e6xxx_6320_family(ps) && !mv88e6xxx_6352_family(ps)) if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
return -EOPNOTSUPP; return -EOPNOTSUPP;
*alarm = false; *alarm = false;
...@@ -3070,6 +3363,161 @@ int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm) ...@@ -3070,6 +3363,161 @@ int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
} }
#endif /* CONFIG_NET_DSA_HWMON */ #endif /* CONFIG_NET_DSA_HWMON */
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,
.flags = MV88E6XXX_FLAGS_FAMILY_6097,
},
[MV88E6095] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
.family = MV88E6XXX_FAMILY_6095,
.name = "Marvell 88E6095/88E6095F",
.num_databases = 256,
.num_ports = 11,
.flags = MV88E6XXX_FLAGS_FAMILY_6095,
},
[MV88E6123] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
.family = MV88E6XXX_FAMILY_6165,
.name = "Marvell 88E6123",
.num_databases = 4096,
.num_ports = 3,
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
},
[MV88E6131] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
.family = MV88E6XXX_FAMILY_6185,
.name = "Marvell 88E6131",
.num_databases = 256,
.num_ports = 8,
.flags = MV88E6XXX_FLAGS_FAMILY_6185,
},
[MV88E6161] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
.family = MV88E6XXX_FAMILY_6165,
.name = "Marvell 88E6161",
.num_databases = 4096,
.num_ports = 6,
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
},
[MV88E6165] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
.family = MV88E6XXX_FAMILY_6165,
.name = "Marvell 88E6165",
.num_databases = 4096,
.num_ports = 6,
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
},
[MV88E6171] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
.family = MV88E6XXX_FAMILY_6351,
.name = "Marvell 88E6171",
.num_databases = 4096,
.num_ports = 7,
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
},
[MV88E6172] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
.family = MV88E6XXX_FAMILY_6352,
.name = "Marvell 88E6172",
.num_databases = 4096,
.num_ports = 7,
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
},
[MV88E6175] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
.family = MV88E6XXX_FAMILY_6351,
.name = "Marvell 88E6175",
.num_databases = 4096,
.num_ports = 7,
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
},
[MV88E6176] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
.family = MV88E6XXX_FAMILY_6352,
.name = "Marvell 88E6176",
.num_databases = 4096,
.num_ports = 7,
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
},
[MV88E6185] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
.family = MV88E6XXX_FAMILY_6185,
.name = "Marvell 88E6185",
.num_databases = 256,
.num_ports = 10,
.flags = MV88E6XXX_FLAGS_FAMILY_6185,
},
[MV88E6240] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
.family = MV88E6XXX_FAMILY_6352,
.name = "Marvell 88E6240",
.num_databases = 4096,
.num_ports = 7,
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
},
[MV88E6320] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
.family = MV88E6XXX_FAMILY_6320,
.name = "Marvell 88E6320",
.num_databases = 4096,
.num_ports = 7,
.flags = MV88E6XXX_FLAGS_FAMILY_6320,
},
[MV88E6321] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
.family = MV88E6XXX_FAMILY_6320,
.name = "Marvell 88E6321",
.num_databases = 4096,
.num_ports = 7,
.flags = MV88E6XXX_FLAGS_FAMILY_6320,
},
[MV88E6350] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
.family = MV88E6XXX_FAMILY_6351,
.name = "Marvell 88E6350",
.num_databases = 4096,
.num_ports = 7,
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
},
[MV88E6351] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
.family = MV88E6XXX_FAMILY_6351,
.name = "Marvell 88E6351",
.num_databases = 4096,
.num_ports = 7,
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
},
[MV88E6352] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
.family = MV88E6XXX_FAMILY_6352,
.name = "Marvell 88E6352",
.num_databases = 4096,
.num_ports = 7,
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
},
};
static const struct mv88e6xxx_info * static const struct mv88e6xxx_info *
mv88e6xxx_lookup_info(unsigned int prod_num, const struct mv88e6xxx_info *table, mv88e6xxx_lookup_info(unsigned int prod_num, const struct mv88e6xxx_info *table,
unsigned int num) unsigned int num)
...@@ -3083,10 +3531,9 @@ mv88e6xxx_lookup_info(unsigned int prod_num, const struct mv88e6xxx_info *table, ...@@ -3083,10 +3531,9 @@ mv88e6xxx_lookup_info(unsigned int prod_num, const struct mv88e6xxx_info *table,
return NULL; return NULL;
} }
const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev, static const char *mv88e6xxx_probe(struct device *dsa_dev,
int sw_addr, void **priv, struct device *host_dev, int sw_addr,
const struct mv88e6xxx_info *table, void **priv)
unsigned int num)
{ {
const struct mv88e6xxx_info *info; const struct mv88e6xxx_info *info;
struct mv88e6xxx_priv_state *ps; struct mv88e6xxx_priv_state *ps;
...@@ -3105,7 +3552,8 @@ const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev, ...@@ -3105,7 +3552,8 @@ const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev,
prod_num = (id & 0xfff0) >> 4; prod_num = (id & 0xfff0) >> 4;
rev = id & 0x000f; rev = id & 0x000f;
info = mv88e6xxx_lookup_info(prod_num, table, num); info = mv88e6xxx_lookup_info(prod_num, mv88e6xxx_table,
ARRAY_SIZE(mv88e6xxx_table));
if (!info) if (!info)
return NULL; return NULL;
...@@ -3127,41 +3575,73 @@ const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev, ...@@ -3127,41 +3575,73 @@ const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev,
return name; return name;
} }
struct dsa_switch_driver mv88e6xxx_switch_driver = {
.tag_protocol = DSA_TAG_PROTO_EDSA,
.probe = mv88e6xxx_probe,
.setup = mv88e6xxx_setup,
.set_addr = mv88e6xxx_set_addr,
.phy_read = mv88e6xxx_phy_read,
.phy_write = mv88e6xxx_phy_write,
.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
.get_eeprom = mv88e6xxx_get_eeprom,
.set_eeprom = mv88e6xxx_set_eeprom,
.get_regs_len = mv88e6xxx_get_regs_len,
.get_regs = mv88e6xxx_get_regs,
.port_bridge_join = mv88e6xxx_port_bridge_join,
.port_bridge_leave = mv88e6xxx_port_bridge_leave,
.port_stp_state_set = mv88e6xxx_port_stp_state_set,
.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,
};
static int __init mv88e6xxx_init(void) static int __init mv88e6xxx_init(void)
{ {
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131) register_switch_driver(&mv88e6xxx_switch_driver);
register_switch_driver(&mv88e6131_switch_driver);
#endif
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123)
register_switch_driver(&mv88e6123_switch_driver);
#endif
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
register_switch_driver(&mv88e6352_switch_driver);
#endif
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
register_switch_driver(&mv88e6171_switch_driver);
#endif
return 0; return 0;
} }
module_init(mv88e6xxx_init); module_init(mv88e6xxx_init);
static void __exit mv88e6xxx_cleanup(void) static void __exit mv88e6xxx_cleanup(void)
{ {
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171) unregister_switch_driver(&mv88e6xxx_switch_driver);
unregister_switch_driver(&mv88e6171_switch_driver);
#endif
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
unregister_switch_driver(&mv88e6352_switch_driver);
#endif
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123)
unregister_switch_driver(&mv88e6123_switch_driver);
#endif
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
unregister_switch_driver(&mv88e6131_switch_driver);
#endif
} }
module_exit(mv88e6xxx_cleanup); module_exit(mv88e6xxx_cleanup);
MODULE_ALIAS("platform:mv88e6085");
MODULE_ALIAS("platform:mv88e6095");
MODULE_ALIAS("platform:mv88e6095f");
MODULE_ALIAS("platform:mv88e6123");
MODULE_ALIAS("platform:mv88e6131");
MODULE_ALIAS("platform:mv88e6161");
MODULE_ALIAS("platform:mv88e6165");
MODULE_ALIAS("platform:mv88e6171");
MODULE_ALIAS("platform:mv88e6172");
MODULE_ALIAS("platform:mv88e6175");
MODULE_ALIAS("platform:mv88e6176");
MODULE_ALIAS("platform:mv88e6320");
MODULE_ALIAS("platform:mv88e6321");
MODULE_ALIAS("platform:mv88e6350");
MODULE_ALIAS("platform:mv88e6351");
MODULE_ALIAS("platform:mv88e6352");
MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>"); MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips"); MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -338,6 +338,27 @@ ...@@ -338,6 +338,27 @@
#define MV88E6XXX_N_FID 4096 #define MV88E6XXX_N_FID 4096
/* List of supported models */
enum mv88e6xxx_model {
MV88E6085,
MV88E6095,
MV88E6123,
MV88E6131,
MV88E6161,
MV88E6165,
MV88E6171,
MV88E6172,
MV88E6175,
MV88E6176,
MV88E6185,
MV88E6240,
MV88E6320,
MV88E6321,
MV88E6350,
MV88E6351,
MV88E6352,
};
enum mv88e6xxx_family { enum mv88e6xxx_family {
MV88E6XXX_FAMILY_NONE, MV88E6XXX_FAMILY_NONE,
MV88E6XXX_FAMILY_6065, /* 6031 6035 6061 6065 */ MV88E6XXX_FAMILY_6065, /* 6031 6035 6061 6065 */
...@@ -350,12 +371,142 @@ enum mv88e6xxx_family { ...@@ -350,12 +371,142 @@ enum mv88e6xxx_family {
MV88E6XXX_FAMILY_6352, /* 6172 6176 6240 6352 */ MV88E6XXX_FAMILY_6352, /* 6172 6176 6240 6352 */
}; };
enum mv88e6xxx_cap {
/* Address Translation Unit.
* The ATU is used to lookup and learn MAC addresses. See GLOBAL_ATU_OP.
*/
MV88E6XXX_CAP_ATU,
/* Energy Efficient Ethernet.
*/
MV88E6XXX_CAP_EEE,
/* EEPROM Command and Data registers.
* See GLOBAL2_EEPROM_OP and GLOBAL2_EEPROM_DATA.
*/
MV88E6XXX_CAP_EEPROM,
/* Port State Filtering for 802.1D Spanning Tree.
* See PORT_CONTROL_STATE_* values in the PORT_CONTROL register.
*/
MV88E6XXX_CAP_PORTSTATE,
/* PHY Polling Unit.
* See GLOBAL_CONTROL_PPU_ENABLE and GLOBAL_STATUS_PPU_POLLING.
*/
MV88E6XXX_CAP_PPU,
MV88E6XXX_CAP_PPU_ACTIVE,
/* SMI PHY Command and Data registers.
* This requires an indirect access to PHY registers through
* GLOBAL2_SMI_OP, otherwise direct access to PHY registers is done.
*/
MV88E6XXX_CAP_SMI_PHY,
/* Switch MAC/WoL/WoF register.
* This requires an indirect access to set the switch MAC address
* through GLOBAL2_SWITCH_MAC, otherwise GLOBAL_MAC_01, GLOBAL_MAC_23,
* and GLOBAL_MAC_45 are used with a direct access.
*/
MV88E6XXX_CAP_SWITCH_MAC_WOL_WOF,
/* Internal temperature sensor.
* Available from any enabled port's PHY register 26, page 6.
*/
MV88E6XXX_CAP_TEMP,
MV88E6XXX_CAP_TEMP_LIMIT,
/* In-chip Port Based VLANs.
* Each port VLANTable register (see PORT_BASE_VLAN) is used to restrict
* the output (or egress) ports to which it is allowed to send frames.
*/
MV88E6XXX_CAP_VLANTABLE,
/* VLAN Table Unit.
* The VTU is used to program 802.1Q VLANs. See GLOBAL_VTU_OP.
*/
MV88E6XXX_CAP_VTU,
};
/* Bitmask of capabilities */
#define MV88E6XXX_FLAG_ATU BIT(MV88E6XXX_CAP_ATU)
#define MV88E6XXX_FLAG_EEE BIT(MV88E6XXX_CAP_EEE)
#define MV88E6XXX_FLAG_EEPROM BIT(MV88E6XXX_CAP_EEPROM)
#define MV88E6XXX_FLAG_PORTSTATE BIT(MV88E6XXX_CAP_PORTSTATE)
#define MV88E6XXX_FLAG_PPU BIT(MV88E6XXX_CAP_PPU)
#define MV88E6XXX_FLAG_PPU_ACTIVE BIT(MV88E6XXX_CAP_PPU_ACTIVE)
#define MV88E6XXX_FLAG_SMI_PHY BIT(MV88E6XXX_CAP_SMI_PHY)
#define MV88E6XXX_FLAG_SWITCH_MAC BIT(MV88E6XXX_CAP_SWITCH_MAC_WOL_WOF)
#define MV88E6XXX_FLAG_TEMP BIT(MV88E6XXX_CAP_TEMP)
#define MV88E6XXX_FLAG_TEMP_LIMIT BIT(MV88E6XXX_CAP_TEMP_LIMIT)
#define MV88E6XXX_FLAG_VLANTABLE BIT(MV88E6XXX_CAP_VLANTABLE)
#define MV88E6XXX_FLAG_VTU BIT(MV88E6XXX_CAP_VTU)
#define MV88E6XXX_FLAGS_FAMILY_6095 \
(MV88E6XXX_FLAG_ATU | \
MV88E6XXX_FLAG_PPU | \
MV88E6XXX_FLAG_VLANTABLE | \
MV88E6XXX_FLAG_VTU)
#define MV88E6XXX_FLAGS_FAMILY_6097 \
(MV88E6XXX_FLAG_ATU | \
MV88E6XXX_FLAG_PPU | \
MV88E6XXX_FLAG_VLANTABLE | \
MV88E6XXX_FLAG_VTU)
#define MV88E6XXX_FLAGS_FAMILY_6165 \
(MV88E6XXX_FLAG_SWITCH_MAC | \
MV88E6XXX_FLAG_TEMP)
#define MV88E6XXX_FLAGS_FAMILY_6185 \
(MV88E6XXX_FLAG_ATU | \
MV88E6XXX_FLAG_PPU | \
MV88E6XXX_FLAG_VLANTABLE | \
MV88E6XXX_FLAG_VTU)
#define MV88E6XXX_FLAGS_FAMILY_6320 \
(MV88E6XXX_FLAG_ATU | \
MV88E6XXX_FLAG_EEE | \
MV88E6XXX_FLAG_EEPROM | \
MV88E6XXX_FLAG_PORTSTATE | \
MV88E6XXX_FLAG_PPU_ACTIVE | \
MV88E6XXX_FLAG_SMI_PHY | \
MV88E6XXX_FLAG_SWITCH_MAC | \
MV88E6XXX_FLAG_TEMP | \
MV88E6XXX_FLAG_TEMP_LIMIT | \
MV88E6XXX_FLAG_VLANTABLE | \
MV88E6XXX_FLAG_VTU)
#define MV88E6XXX_FLAGS_FAMILY_6351 \
(MV88E6XXX_FLAG_ATU | \
MV88E6XXX_FLAG_PORTSTATE | \
MV88E6XXX_FLAG_PPU_ACTIVE | \
MV88E6XXX_FLAG_SMI_PHY | \
MV88E6XXX_FLAG_SWITCH_MAC | \
MV88E6XXX_FLAG_TEMP | \
MV88E6XXX_FLAG_VLANTABLE | \
MV88E6XXX_FLAG_VTU)
#define MV88E6XXX_FLAGS_FAMILY_6352 \
(MV88E6XXX_FLAG_ATU | \
MV88E6XXX_FLAG_EEE | \
MV88E6XXX_FLAG_EEPROM | \
MV88E6XXX_FLAG_PORTSTATE | \
MV88E6XXX_FLAG_PPU_ACTIVE | \
MV88E6XXX_FLAG_SMI_PHY | \
MV88E6XXX_FLAG_SWITCH_MAC | \
MV88E6XXX_FLAG_TEMP | \
MV88E6XXX_FLAG_TEMP_LIMIT | \
MV88E6XXX_FLAG_VLANTABLE | \
MV88E6XXX_FLAG_VTU)
struct mv88e6xxx_info { struct mv88e6xxx_info {
enum mv88e6xxx_family family; enum mv88e6xxx_family family;
u16 prod_num; u16 prod_num;
const char *name; const char *name;
unsigned int num_databases; unsigned int num_databases;
unsigned int num_ports; unsigned int num_ports;
unsigned long flags;
}; };
struct mv88e6xxx_atu_entry { struct mv88e6xxx_atu_entry {
...@@ -403,7 +554,6 @@ struct mv88e6xxx_priv_state { ...@@ -403,7 +554,6 @@ struct mv88e6xxx_priv_state {
struct mii_bus *bus; struct mii_bus *bus;
int sw_addr; int sw_addr;
#ifdef CONFIG_NET_DSA_MV88E6XXX_NEED_PPU
/* Handles automatic disabling and re-enabling of the PHY /* Handles automatic disabling and re-enabling of the PHY
* polling unit. * polling unit.
*/ */
...@@ -411,7 +561,6 @@ struct mv88e6xxx_priv_state { ...@@ -411,7 +561,6 @@ struct mv88e6xxx_priv_state {
int ppu_disabled; int ppu_disabled;
struct work_struct ppu_work; struct work_struct ppu_work;
struct timer_list ppu_timer; struct timer_list ppu_timer;
#endif
/* This mutex serialises access to the statistics unit. /* This mutex serialises access to the statistics unit.
* Hold this mutex over snapshot + dump sequences. * Hold this mutex over snapshot + dump sequences.
...@@ -449,86 +598,10 @@ struct mv88e6xxx_hw_stat { ...@@ -449,86 +598,10 @@ struct mv88e6xxx_hw_stat {
enum stat_type type; enum stat_type type;
}; };
int mv88e6xxx_switch_reset(struct mv88e6xxx_priv_state *ps, bool ppu_active); static inline bool mv88e6xxx_has(struct mv88e6xxx_priv_state *ps,
const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev, unsigned long flags)
int sw_addr, void **priv, {
const struct mv88e6xxx_info *table, return (ps->info->flags & flags) == flags;
unsigned int num); }
int mv88e6xxx_setup_ports(struct dsa_switch *ds);
int mv88e6xxx_setup_common(struct mv88e6xxx_priv_state *ps);
int mv88e6xxx_setup_global(struct dsa_switch *ds);
int mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps, int addr, int reg);
int mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr,
int reg, u16 val);
int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr);
int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr);
int mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum);
int mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val);
int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum);
int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int port, int regnum,
u16 val);
void mv88e6xxx_ppu_state_init(struct mv88e6xxx_priv_state *ps);
int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum);
int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr,
int regnum, u16 val);
void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data);
void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
uint64_t *data);
int mv88e6xxx_get_sset_count(struct dsa_switch *ds);
int mv88e6xxx_get_sset_count_basic(struct dsa_switch *ds);
void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
struct phy_device *phydev);
int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port);
void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
struct ethtool_regs *regs, void *_p);
int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp);
int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp);
int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp);
int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm);
int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds);
int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds);
int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr, int regnum);
int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, int regnum,
u16 val);
int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e);
int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
struct phy_device *phydev, struct ethtool_eee *e);
int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
struct net_device *bridge);
void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port);
void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state);
int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
bool vlan_filtering);
int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans);
void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans);
int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan);
int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_vlan *vlan,
int (*cb)(struct switchdev_obj *obj));
int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb,
struct switchdev_trans *trans);
void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb,
struct switchdev_trans *trans);
int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb);
int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_fdb *fdb,
int (*cb)(struct switchdev_obj *obj));
int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg);
int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
int reg, int val);
extern struct dsa_switch_driver mv88e6131_switch_driver;
extern struct dsa_switch_driver mv88e6123_switch_driver;
extern struct dsa_switch_driver mv88e6352_switch_driver;
extern struct dsa_switch_driver mv88e6171_switch_driver;
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment