mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-28 17:48:58 +00:00
566de813c3
By default, forwarding between all ports is allowed on init. This is problematic in cases where some ports are supposed to be isolated from each other, most commonly LAN/WAN separation. REG_ESW_PORT_PCR(port) has a destination mask for a particular port, controlling what other ports it is allowed to send packets to. Instead of initializing all to 0xff (all ports), allow each physical port to send to the CPU port, and the CPU port to send to all other ports. Signed-off-by: Felix Fietkau <nbd@nbd.name>
1178 lines
33 KiB
Diff
1178 lines
33 KiB
Diff
From cf5a08f1f16913da8bb24a96afaa2969b29d0827 Mon Sep 17 00:00:00 2001
|
|
From: John Crispin <blogic@openwrt.org>
|
|
Date: Mon, 14 Dec 2015 22:25:57 +0100
|
|
Subject: [PATCH 513/513] net: mediatek: add swconfig driver for gsw_mt762x
|
|
|
|
Signed-off-by: John Crispin <blogic@openwrt.org>
|
|
---
|
|
drivers/net/ethernet/mediatek/Makefile | 4 +-
|
|
drivers/net/ethernet/mediatek/gsw_mt7620.c | 3 +
|
|
drivers/net/ethernet/mediatek/gsw_mt7620.h | 3 +
|
|
drivers/net/ethernet/mediatek/mt7530.c | 884 ++++++++++++++++++++++++++++
|
|
drivers/net/ethernet/mediatek/mt7530.h | 186 ++++++
|
|
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 9 +-
|
|
drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 +
|
|
drivers/net/ethernet/mediatek/soc_mt7620.c | 1 +
|
|
8 files changed, 1087 insertions(+), 4 deletions(-)
|
|
create mode 100644 drivers/net/ethernet/mediatek/mt7530.c
|
|
create mode 100644 drivers/net/ethernet/mediatek/mt7530.h
|
|
|
|
--- a/drivers/net/ethernet/mediatek/Makefile
|
|
+++ b/drivers/net/ethernet/mediatek/Makefile
|
|
@@ -15,6 +15,6 @@ mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MT7620
|
|
mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MT7621) += soc_mt7621.o
|
|
|
|
obj-$(CONFIG_NET_MEDIATEK_ESW_RT3050) += esw_rt3050.o
|
|
-obj-$(CONFIG_NET_MEDIATEK_GSW_MT7620) += gsw_mt7620.o
|
|
-obj-$(CONFIG_NET_MEDIATEK_GSW_MT7621) += gsw_mt7621.o
|
|
+obj-$(CONFIG_NET_MEDIATEK_GSW_MT7620) += gsw_mt7620.o mt7530.o
|
|
+obj-$(CONFIG_NET_MEDIATEK_GSW_MT7621) += gsw_mt7621.o mt7530.o
|
|
obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk-eth-soc.o
|
|
--- a/drivers/net/ethernet/mediatek/gsw_mt7620.c
|
|
+++ b/drivers/net/ethernet/mediatek/gsw_mt7620.c
|
|
@@ -67,6 +67,9 @@ static void mt7620_hw_init(struct mt7620
|
|
rt_sysc_w32(rt_sysc_r32(SYSC_REG_CFG1) | BIT(8), SYSC_REG_CFG1);
|
|
mtk_switch_w32(gsw, mtk_switch_r32(gsw, GSW_REG_CKGCR) & ~(0x3 << 4), GSW_REG_CKGCR);
|
|
|
|
+ /* Enable MIB stats */
|
|
+ mtk_switch_w32(gsw, mtk_switch_r32(gsw, GSW_REG_MIB_CNT_EN) | (1 << 1), GSW_REG_MIB_CNT_EN);
|
|
+
|
|
if (of_property_read_bool(np, "mediatek,mt7530")) {
|
|
u32 val;
|
|
|
|
--- a/drivers/net/ethernet/mediatek/gsw_mt7620.h
|
|
+++ b/drivers/net/ethernet/mediatek/gsw_mt7620.h
|
|
@@ -35,6 +35,8 @@
|
|
#define GSW_MDIO_ADDR_SHIFT 20
|
|
#define GSW_MDIO_REG_SHIFT 25
|
|
|
|
+#define GSW_REG_MIB_CNT_EN 0x4000
|
|
+
|
|
#define GSW_REG_PORT_PMCR(x) (0x3000 + (x * 0x100))
|
|
#define GSW_REG_PORT_STATUS(x) (0x3008 + (x * 0x100))
|
|
#define GSW_REG_SMACCR0 0x3fE4
|
|
@@ -76,6 +78,7 @@
|
|
#define PHY_PRE_EN BIT(30)
|
|
#define PMY_MDC_CONF(_x) ((_x & 0x3f) << 24)
|
|
|
|
+
|
|
enum {
|
|
/* Global attributes. */
|
|
GSW_ATTR_ENABLE_VLAN,
|
|
--- /dev/null
|
|
+++ b/drivers/net/ethernet/mediatek/mt7530.c
|
|
@@ -0,0 +1,886 @@
|
|
+/*
|
|
+ * 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.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
|
+ * Copyright (C) 2016 Vitaly Chekryzhev <13hakta@gmail.com>
|
|
+ */
|
|
+
|
|
+#include <linux/if.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/if_ether.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <linux/netdevice.h>
|
|
+#include <linux/netlink.h>
|
|
+#include <linux/bitops.h>
|
|
+#include <net/genetlink.h>
|
|
+#include <linux/switch.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/phy.h>
|
|
+#include <linux/netdevice.h>
|
|
+#include <linux/etherdevice.h>
|
|
+#include <linux/lockdep.h>
|
|
+#include <linux/workqueue.h>
|
|
+#include <linux/of_device.h>
|
|
+
|
|
+#include "mt7530.h"
|
|
+
|
|
+#define MT7530_CPU_PORT 6
|
|
+#define MT7530_NUM_PORTS 8
|
|
+#define MT7530_NUM_VLANS 16
|
|
+#define MT7530_MAX_VID 4095
|
|
+#define MT7530_MIN_VID 0
|
|
+
|
|
+/* registers */
|
|
+#define REG_ESW_VLAN_VTCR 0x90
|
|
+#define REG_ESW_VLAN_VAWD1 0x94
|
|
+#define REG_ESW_VLAN_VAWD2 0x98
|
|
+#define REG_ESW_VLAN_VTIM(x) (0x100 + 4 * ((x) / 2))
|
|
+
|
|
+#define REG_ESW_VLAN_VAWD1_IVL_MAC BIT(30)
|
|
+#define REG_ESW_VLAN_VAWD1_VTAG_EN BIT(28)
|
|
+#define REG_ESW_VLAN_VAWD1_VALID BIT(0)
|
|
+
|
|
+/* vlan egress mode */
|
|
+enum {
|
|
+ ETAG_CTRL_UNTAG = 0,
|
|
+ ETAG_CTRL_TAG = 2,
|
|
+ ETAG_CTRL_SWAP = 1,
|
|
+ ETAG_CTRL_STACK = 3,
|
|
+};
|
|
+
|
|
+#define REG_ESW_PORT_PCR(x) (0x2004 | ((x) << 8))
|
|
+#define REG_ESW_PORT_PVC(x) (0x2010 | ((x) << 8))
|
|
+#define REG_ESW_PORT_PPBV1(x) (0x2014 | ((x) << 8))
|
|
+
|
|
+#define REG_HWTRAP 0x7804
|
|
+
|
|
+#define MIB_DESC(_s , _o, _n) \
|
|
+ { \
|
|
+ .size = (_s), \
|
|
+ .offset = (_o), \
|
|
+ .name = (_n), \
|
|
+ }
|
|
+
|
|
+struct mt7xxx_mib_desc {
|
|
+ unsigned int size;
|
|
+ unsigned int offset;
|
|
+ const char *name;
|
|
+};
|
|
+
|
|
+static const struct mt7xxx_mib_desc mt7620_mibs[] = {
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_PPE_AC_BCNT0, "PPE_AC_BCNT0"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_PPE_AC_PCNT0, "PPE_AC_PCNT0"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_PPE_AC_BCNT63, "PPE_AC_BCNT63"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_PPE_AC_PCNT63, "PPE_AC_PCNT63"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_PPE_MTR_CNT0, "PPE_MTR_CNT0"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_PPE_MTR_CNT63, "PPE_MTR_CNT63"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM1_TX_GBCNT, "GDM1_TX_GBCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM1_TX_GPCNT, "GDM1_TX_GPCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM1_TX_SKIPCNT, "GDM1_TX_SKIPCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM1_TX_COLCNT, "GDM1_TX_COLCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM1_RX_GBCNT1, "GDM1_RX_GBCNT1"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM1_RX_GPCNT1, "GDM1_RX_GPCNT1"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM1_RX_OERCNT, "GDM1_RX_OERCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM1_RX_FERCNT, "GDM1_RX_FERCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM1_RX_SERCNT, "GDM1_RX_SERCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM1_RX_LERCNT, "GDM1_RX_LERCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM1_RX_CERCNT, "GDM1_RX_CERCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM1_RX_FCCNT, "GDM1_RX_FCCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM2_TX_GBCNT, "GDM2_TX_GBCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM2_TX_GPCNT, "GDM2_TX_GPCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM2_TX_SKIPCNT, "GDM2_TX_SKIPCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM2_TX_COLCNT, "GDM2_TX_COLCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM2_RX_GBCNT, "GDM2_RX_GBCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM2_RX_GPCNT, "GDM2_RX_GPCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM2_RX_OERCNT, "GDM2_RX_OERCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM2_RX_FERCNT, "GDM2_RX_FERCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM2_RX_SERCNT, "GDM2_RX_SERCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM2_RX_LERCNT, "GDM2_RX_LERCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM2_RX_CERCNT, "GDM2_RX_CERCNT"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_GDM2_RX_FCCNT, "GDM2_RX_FCCNT")
|
|
+};
|
|
+
|
|
+static const struct mt7xxx_mib_desc mt7620_port_mibs[] = {
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_PORT_TGPCN, "TxGPC"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_PORT_TBOCN, "TxBOC"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_PORT_TGOCN, "TxGOC"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_PORT_TEPCN, "TxEPC"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_PORT_RGPCN, "RxGPC"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_PORT_RBOCN, "RxBOC"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_PORT_RGOCN, "RxGOC"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_PORT_REPC1N, "RxEPC1"),
|
|
+ MIB_DESC(1, MT7620_MIB_STATS_PORT_REPC2N, "RxEPC2")
|
|
+};
|
|
+
|
|
+static const struct mt7xxx_mib_desc mt7621_mibs[] = {
|
|
+ MIB_DESC(1, MT7621_STATS_TDPC, "TxDrop"),
|
|
+ MIB_DESC(1, MT7621_STATS_TCRC, "TxCRC"),
|
|
+ MIB_DESC(1, MT7621_STATS_TUPC, "TxUni"),
|
|
+ MIB_DESC(1, MT7621_STATS_TMPC, "TxMulti"),
|
|
+ MIB_DESC(1, MT7621_STATS_TBPC, "TxBroad"),
|
|
+ MIB_DESC(1, MT7621_STATS_TCEC, "TxCollision"),
|
|
+ MIB_DESC(1, MT7621_STATS_TSCEC, "TxSingleCol"),
|
|
+ MIB_DESC(1, MT7621_STATS_TMCEC, "TxMultiCol"),
|
|
+ MIB_DESC(1, MT7621_STATS_TDEC, "TxDefer"),
|
|
+ MIB_DESC(1, MT7621_STATS_TLCEC, "TxLateCol"),
|
|
+ MIB_DESC(1, MT7621_STATS_TXCEC, "TxExcCol"),
|
|
+ MIB_DESC(1, MT7621_STATS_TPPC, "TxPause"),
|
|
+ MIB_DESC(1, MT7621_STATS_TL64PC, "Tx64Byte"),
|
|
+ MIB_DESC(1, MT7621_STATS_TL65PC, "Tx65Byte"),
|
|
+ MIB_DESC(1, MT7621_STATS_TL128PC, "Tx128Byte"),
|
|
+ MIB_DESC(1, MT7621_STATS_TL256PC, "Tx256Byte"),
|
|
+ MIB_DESC(1, MT7621_STATS_TL512PC, "Tx512Byte"),
|
|
+ MIB_DESC(1, MT7621_STATS_TL1024PC, "Tx1024Byte"),
|
|
+ MIB_DESC(2, MT7621_STATS_TOC, "TxByte"),
|
|
+ MIB_DESC(1, MT7621_STATS_RDPC, "RxDrop"),
|
|
+ MIB_DESC(1, MT7621_STATS_RFPC, "RxFiltered"),
|
|
+ MIB_DESC(1, MT7621_STATS_RUPC, "RxUni"),
|
|
+ MIB_DESC(1, MT7621_STATS_RMPC, "RxMulti"),
|
|
+ MIB_DESC(1, MT7621_STATS_RBPC, "RxBroad"),
|
|
+ MIB_DESC(1, MT7621_STATS_RAEPC, "RxAlignErr"),
|
|
+ MIB_DESC(1, MT7621_STATS_RCEPC, "RxCRC"),
|
|
+ MIB_DESC(1, MT7621_STATS_RUSPC, "RxUnderSize"),
|
|
+ MIB_DESC(1, MT7621_STATS_RFEPC, "RxFragment"),
|
|
+ MIB_DESC(1, MT7621_STATS_ROSPC, "RxOverSize"),
|
|
+ MIB_DESC(1, MT7621_STATS_RJEPC, "RxJabber"),
|
|
+ MIB_DESC(1, MT7621_STATS_RPPC, "RxPause"),
|
|
+ MIB_DESC(1, MT7621_STATS_RL64PC, "Rx64Byte"),
|
|
+ MIB_DESC(1, MT7621_STATS_RL65PC, "Rx65Byte"),
|
|
+ MIB_DESC(1, MT7621_STATS_RL128PC, "Rx128Byte"),
|
|
+ MIB_DESC(1, MT7621_STATS_RL256PC, "Rx256Byte"),
|
|
+ MIB_DESC(1, MT7621_STATS_RL512PC, "Rx512Byte"),
|
|
+ MIB_DESC(1, MT7621_STATS_RL1024PC, "Rx1024Byte"),
|
|
+ MIB_DESC(2, MT7621_STATS_ROC, "RxByte"),
|
|
+ MIB_DESC(1, MT7621_STATS_RDPC_CTRL, "RxCtrlDrop"),
|
|
+ MIB_DESC(1, MT7621_STATS_RDPC_ING, "RxIngDrop"),
|
|
+ MIB_DESC(1, MT7621_STATS_RDPC_ARL, "RxARLDrop")
|
|
+};
|
|
+
|
|
+enum {
|
|
+ /* Global attributes. */
|
|
+ MT7530_ATTR_ENABLE_VLAN,
|
|
+};
|
|
+
|
|
+struct mt7530_port_entry {
|
|
+ u16 pvid;
|
|
+};
|
|
+
|
|
+struct mt7530_vlan_entry {
|
|
+ u16 vid;
|
|
+ u8 member;
|
|
+ u8 etags;
|
|
+};
|
|
+
|
|
+struct mt7530_priv {
|
|
+ void __iomem *base;
|
|
+ struct mii_bus *bus;
|
|
+ struct switch_dev swdev;
|
|
+
|
|
+ bool global_vlan_enable;
|
|
+ struct mt7530_vlan_entry vlan_entries[MT7530_NUM_VLANS];
|
|
+ struct mt7530_port_entry port_entries[MT7530_NUM_PORTS];
|
|
+};
|
|
+
|
|
+struct mt7530_mapping {
|
|
+ char *name;
|
|
+ u16 pvids[MT7530_NUM_PORTS];
|
|
+ u8 members[MT7530_NUM_VLANS];
|
|
+ u8 etags[MT7530_NUM_VLANS];
|
|
+ u16 vids[MT7530_NUM_VLANS];
|
|
+} mt7530_defaults[] = {
|
|
+ {
|
|
+ .name = "llllw",
|
|
+ .pvids = { 1, 1, 1, 1, 2, 1, 1 },
|
|
+ .members = { 0, 0x6f, 0x50 },
|
|
+ .etags = { 0, 0x40, 0x40 },
|
|
+ .vids = { 0, 1, 2 },
|
|
+ }, {
|
|
+ .name = "wllll",
|
|
+ .pvids = { 2, 1, 1, 1, 1, 1, 1 },
|
|
+ .members = { 0, 0x7e, 0x41 },
|
|
+ .etags = { 0, 0x40, 0x40 },
|
|
+ .vids = { 0, 1, 2 },
|
|
+ },
|
|
+};
|
|
+
|
|
+struct mt7530_mapping*
|
|
+mt7530_find_mapping(struct device_node *np)
|
|
+{
|
|
+ const char *map;
|
|
+ int i;
|
|
+
|
|
+ if (of_property_read_string(np, "mediatek,portmap", &map))
|
|
+ return NULL;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(mt7530_defaults); i++)
|
|
+ if (!strcmp(map, mt7530_defaults[i].name))
|
|
+ return &mt7530_defaults[i];
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void
|
|
+mt7530_apply_mapping(struct mt7530_priv *mt7530, struct mt7530_mapping *map)
|
|
+{
|
|
+ int i = 0;
|
|
+
|
|
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
|
|
+ mt7530->port_entries[i].pvid = map->pvids[i];
|
|
+
|
|
+ for (i = 0; i < MT7530_NUM_VLANS; i++) {
|
|
+ mt7530->vlan_entries[i].member = map->members[i];
|
|
+ mt7530->vlan_entries[i].etags = map->etags[i];
|
|
+ mt7530->vlan_entries[i].vid = map->vids[i];
|
|
+ }
|
|
+}
|
|
+
|
|
+static int
|
|
+mt7530_reset_switch(struct switch_dev *dev)
|
|
+{
|
|
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
|
+ int i;
|
|
+
|
|
+ memset(priv->port_entries, 0, sizeof(priv->port_entries));
|
|
+ memset(priv->vlan_entries, 0, sizeof(priv->vlan_entries));
|
|
+
|
|
+ /* set default vid of each vlan to the same number of vlan, so the vid
|
|
+ * won't need be set explicitly.
|
|
+ */
|
|
+ for (i = 0; i < MT7530_NUM_VLANS; i++) {
|
|
+ priv->vlan_entries[i].vid = i;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+mt7530_get_vlan_enable(struct switch_dev *dev,
|
|
+ const struct switch_attr *attr,
|
|
+ struct switch_val *val)
|
|
+{
|
|
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
|
+
|
|
+ val->value.i = priv->global_vlan_enable;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+mt7530_set_vlan_enable(struct switch_dev *dev,
|
|
+ const struct switch_attr *attr,
|
|
+ struct switch_val *val)
|
|
+{
|
|
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
|
+
|
|
+ priv->global_vlan_enable = val->value.i != 0;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static u32
|
|
+mt7530_r32(struct mt7530_priv *priv, u32 reg)
|
|
+{
|
|
+ u32 val;
|
|
+ if (priv->bus) {
|
|
+ u16 high, low;
|
|
+
|
|
+ mdiobus_write(priv->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
|
|
+ low = mdiobus_read(priv->bus, 0x1f, (reg >> 2) & 0xf);
|
|
+ high = mdiobus_read(priv->bus, 0x1f, 0x10);
|
|
+
|
|
+ return (high << 16) | (low & 0xffff);
|
|
+ }
|
|
+
|
|
+ val = ioread32(priv->base + reg);
|
|
+ pr_debug("MT7530 MDIO Read [%04x]=%08x\n", reg, val);
|
|
+
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static void
|
|
+mt7530_w32(struct mt7530_priv *priv, u32 reg, u32 val)
|
|
+{
|
|
+ if (priv->bus) {
|
|
+ mdiobus_write(priv->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
|
|
+ mdiobus_write(priv->bus, 0x1f, (reg >> 2) & 0xf, val & 0xffff);
|
|
+ mdiobus_write(priv->bus, 0x1f, 0x10, val >> 16);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ pr_debug("MT7530 MDIO Write[%04x]=%08x\n", reg, val);
|
|
+ iowrite32(val, priv->base + reg);
|
|
+}
|
|
+
|
|
+static void
|
|
+mt7530_vtcr(struct mt7530_priv *priv, u32 cmd, u32 val)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ mt7530_w32(priv, REG_ESW_VLAN_VTCR, BIT(31) | (cmd << 12) | val);
|
|
+
|
|
+ for (i = 0; i < 20; i++) {
|
|
+ u32 val = mt7530_r32(priv, REG_ESW_VLAN_VTCR);
|
|
+
|
|
+ if ((val & BIT(31)) == 0)
|
|
+ break;
|
|
+
|
|
+ udelay(1000);
|
|
+ }
|
|
+ if (i == 20)
|
|
+ printk("mt7530: vtcr timeout\n");
|
|
+}
|
|
+
|
|
+static int
|
|
+mt7530_get_port_pvid(struct switch_dev *dev, int port, int *val)
|
|
+{
|
|
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
|
+
|
|
+ if (port >= MT7530_NUM_PORTS)
|
|
+ return -EINVAL;
|
|
+
|
|
+ *val = mt7530_r32(priv, REG_ESW_PORT_PPBV1(port));
|
|
+ *val &= 0xfff;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+mt7530_set_port_pvid(struct switch_dev *dev, int port, int pvid)
|
|
+{
|
|
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
|
+
|
|
+ if (port >= MT7530_NUM_PORTS)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (pvid < MT7530_MIN_VID || pvid > MT7530_MAX_VID)
|
|
+ return -EINVAL;
|
|
+
|
|
+ priv->port_entries[port].pvid = pvid;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+mt7530_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
|
|
+{
|
|
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
|
+ u32 member;
|
|
+ u32 etags;
|
|
+ int i;
|
|
+
|
|
+ val->len = 0;
|
|
+
|
|
+ if (val->port_vlan < 0 || val->port_vlan >= MT7530_NUM_VLANS)
|
|
+ return -EINVAL;
|
|
+
|
|
+ mt7530_vtcr(priv, 0, val->port_vlan);
|
|
+
|
|
+ member = mt7530_r32(priv, REG_ESW_VLAN_VAWD1);
|
|
+ member >>= 16;
|
|
+ member &= 0xff;
|
|
+
|
|
+ etags = mt7530_r32(priv, REG_ESW_VLAN_VAWD2);
|
|
+
|
|
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
|
|
+ struct switch_port *p;
|
|
+ int etag;
|
|
+
|
|
+ if (!(member & BIT(i)))
|
|
+ continue;
|
|
+
|
|
+ p = &val->value.ports[val->len++];
|
|
+ p->id = i;
|
|
+
|
|
+ etag = (etags >> (i * 2)) & 0x3;
|
|
+
|
|
+ if (etag == ETAG_CTRL_TAG)
|
|
+ p->flags |= BIT(SWITCH_PORT_FLAG_TAGGED);
|
|
+ else if (etag != ETAG_CTRL_UNTAG)
|
|
+ printk("vlan egress tag control neither untag nor tag.\n");
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+mt7530_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
|
|
+{
|
|
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
|
+ u8 member = 0;
|
|
+ u8 etags = 0;
|
|
+ int i;
|
|
+
|
|
+ if (val->port_vlan < 0 || val->port_vlan >= MT7530_NUM_VLANS ||
|
|
+ val->len > MT7530_NUM_PORTS)
|
|
+ return -EINVAL;
|
|
+
|
|
+ for (i = 0; i < val->len; i++) {
|
|
+ struct switch_port *p = &val->value.ports[i];
|
|
+
|
|
+ if (p->id >= MT7530_NUM_PORTS)
|
|
+ return -EINVAL;
|
|
+
|
|
+ member |= BIT(p->id);
|
|
+
|
|
+ if (p->flags & BIT(SWITCH_PORT_FLAG_TAGGED))
|
|
+ etags |= BIT(p->id);
|
|
+ }
|
|
+ priv->vlan_entries[val->port_vlan].member = member;
|
|
+ priv->vlan_entries[val->port_vlan].etags = etags;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+mt7530_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
|
|
+ struct switch_val *val)
|
|
+{
|
|
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
|
+ int vlan;
|
|
+ u16 vid;
|
|
+
|
|
+ vlan = val->port_vlan;
|
|
+ vid = (u16)val->value.i;
|
|
+
|
|
+ if (vlan < 0 || vlan >= MT7530_NUM_VLANS)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (vid < MT7530_MIN_VID || vid > MT7530_MAX_VID)
|
|
+ return -EINVAL;
|
|
+
|
|
+ priv->vlan_entries[vlan].vid = vid;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+mt7530_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
|
|
+ struct switch_val *val)
|
|
+{
|
|
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
|
+ u32 vid;
|
|
+ int vlan;
|
|
+
|
|
+ vlan = val->port_vlan;
|
|
+
|
|
+ vid = mt7530_r32(priv, REG_ESW_VLAN_VTIM(vlan));
|
|
+ if (vlan & 1)
|
|
+ vid = vid >> 12;
|
|
+ vid &= 0xfff;
|
|
+
|
|
+ val->value.i = vid;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+mt7530_apply_config(struct switch_dev *dev)
|
|
+{
|
|
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
|
+ int i, j;
|
|
+ u8 tag_ports;
|
|
+ u8 untag_ports;
|
|
+
|
|
+ if (!priv->global_vlan_enable) {
|
|
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
|
|
+ mt7530_w32(priv, REG_ESW_PORT_PCR(i), 0x00400000);
|
|
+
|
|
+ mt7530_w32(priv, REG_ESW_PORT_PCR(MT7530_CPU_PORT), 0x00ff0000);
|
|
+
|
|
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
|
|
+ mt7530_w32(priv, REG_ESW_PORT_PVC(i), 0x810000c0);
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* set all ports as security mode */
|
|
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
|
|
+ mt7530_w32(priv, REG_ESW_PORT_PCR(i), 0x00ff0003);
|
|
+
|
|
+ /* check if a port is used in tag/untag vlan egress mode */
|
|
+ tag_ports = 0;
|
|
+ untag_ports = 0;
|
|
+
|
|
+ for (i = 0; i < MT7530_NUM_VLANS; i++) {
|
|
+ u8 member = priv->vlan_entries[i].member;
|
|
+ u8 etags = priv->vlan_entries[i].etags;
|
|
+
|
|
+ if (!member)
|
|
+ continue;
|
|
+
|
|
+ for (j = 0; j < MT7530_NUM_PORTS; j++) {
|
|
+ if (!(member & BIT(j)))
|
|
+ continue;
|
|
+
|
|
+ if (etags & BIT(j))
|
|
+ tag_ports |= 1u << j;
|
|
+ else
|
|
+ untag_ports |= 1u << j;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* set all untag-only ports as transparent and the rest as user port */
|
|
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
|
|
+ u32 pvc_mode = 0x81000000;
|
|
+
|
|
+ if (untag_ports & BIT(i) && !(tag_ports & BIT(i)))
|
|
+ pvc_mode = 0x810000c0;
|
|
+
|
|
+ mt7530_w32(priv, REG_ESW_PORT_PVC(i), pvc_mode);
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < MT7530_NUM_VLANS; i++) {
|
|
+ u16 vid = priv->vlan_entries[i].vid;
|
|
+ u8 member = priv->vlan_entries[i].member;
|
|
+ u8 etags = priv->vlan_entries[i].etags;
|
|
+ u32 val;
|
|
+
|
|
+ /* vid of vlan */
|
|
+ val = mt7530_r32(priv, REG_ESW_VLAN_VTIM(i));
|
|
+ if (i % 2 == 0) {
|
|
+ val &= 0xfff000;
|
|
+ val |= vid;
|
|
+ } else {
|
|
+ val &= 0xfff;
|
|
+ val |= (vid << 12);
|
|
+ }
|
|
+ mt7530_w32(priv, REG_ESW_VLAN_VTIM(i), val);
|
|
+
|
|
+ /* vlan port membership */
|
|
+ if (member)
|
|
+ mt7530_w32(priv, REG_ESW_VLAN_VAWD1, REG_ESW_VLAN_VAWD1_IVL_MAC |
|
|
+ REG_ESW_VLAN_VAWD1_VTAG_EN | (member << 16) |
|
|
+ REG_ESW_VLAN_VAWD1_VALID);
|
|
+ else
|
|
+ mt7530_w32(priv, REG_ESW_VLAN_VAWD1, 0);
|
|
+
|
|
+ /* egress mode */
|
|
+ val = 0;
|
|
+ for (j = 0; j < MT7530_NUM_PORTS; j++) {
|
|
+ if (etags & BIT(j))
|
|
+ val |= ETAG_CTRL_TAG << (j * 2);
|
|
+ else
|
|
+ val |= ETAG_CTRL_UNTAG << (j * 2);
|
|
+ }
|
|
+ mt7530_w32(priv, REG_ESW_VLAN_VAWD2, val);
|
|
+
|
|
+ /* write to vlan table */
|
|
+ mt7530_vtcr(priv, 1, i);
|
|
+ }
|
|
+
|
|
+ /* Port Default PVID */
|
|
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
|
|
+ u32 val;
|
|
+ val = mt7530_r32(priv, REG_ESW_PORT_PPBV1(i));
|
|
+ val &= ~0xfff;
|
|
+ val |= priv->port_entries[i].pvid;
|
|
+ mt7530_w32(priv, REG_ESW_PORT_PPBV1(i), val);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+mt7530_get_port_link(struct switch_dev *dev, int port,
|
|
+ struct switch_port_link *link)
|
|
+{
|
|
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
|
+ u32 speed, pmsr;
|
|
+
|
|
+ if (port < 0 || port >= MT7530_NUM_PORTS)
|
|
+ return -EINVAL;
|
|
+
|
|
+ pmsr = mt7530_r32(priv, 0x3008 + (0x100 * port));
|
|
+
|
|
+ link->link = pmsr & 1;
|
|
+ link->duplex = (pmsr >> 1) & 1;
|
|
+ speed = (pmsr >> 2) & 3;
|
|
+
|
|
+ switch (speed) {
|
|
+ case 0:
|
|
+ link->speed = SWITCH_PORT_SPEED_10;
|
|
+ break;
|
|
+ case 1:
|
|
+ link->speed = SWITCH_PORT_SPEED_100;
|
|
+ break;
|
|
+ case 2:
|
|
+ case 3: /* forced gige speed can be 2 or 3 */
|
|
+ link->speed = SWITCH_PORT_SPEED_1000;
|
|
+ break;
|
|
+ default:
|
|
+ link->speed = SWITCH_PORT_SPEED_UNKNOWN;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static u64 get_mib_counter(struct mt7530_priv *priv, int i, int port)
|
|
+{
|
|
+ unsigned int port_base;
|
|
+ u64 lo;
|
|
+
|
|
+ port_base = MT7621_MIB_COUNTER_BASE +
|
|
+ MT7621_MIB_COUNTER_PORT_OFFSET * port;
|
|
+
|
|
+ lo = mt7530_r32(priv, port_base + mt7621_mibs[i].offset);
|
|
+ if (mt7621_mibs[i].size == 2) {
|
|
+ u64 hi;
|
|
+
|
|
+ hi = mt7530_r32(priv, port_base + mt7621_mibs[i].offset + 4);
|
|
+ lo |= hi << 32;
|
|
+ }
|
|
+
|
|
+ return lo;
|
|
+}
|
|
+
|
|
+static int mt7621_sw_get_port_mib(struct switch_dev *dev,
|
|
+ const struct switch_attr *attr,
|
|
+ struct switch_val *val)
|
|
+{
|
|
+ static char buf[4096];
|
|
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
|
+ int i, len = 0;
|
|
+
|
|
+ if (val->port_vlan >= MT7530_NUM_PORTS)
|
|
+ return -EINVAL;
|
|
+
|
|
+ len += snprintf(buf + len, sizeof(buf) - len,
|
|
+ "Port %d MIB counters\n", val->port_vlan);
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(mt7621_mibs); ++i) {
|
|
+ u64 counter;
|
|
+ len += snprintf(buf + len, sizeof(buf) - len,
|
|
+ "%-11s: ", mt7621_mibs[i].name);
|
|
+ counter = get_mib_counter(priv, i, val->port_vlan);
|
|
+ len += snprintf(buf + len, sizeof(buf) - len, "%llu\n",
|
|
+ counter);
|
|
+ }
|
|
+
|
|
+ val->value.s = buf;
|
|
+ val->len = len;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static u64 get_mib_counter_7620(struct mt7530_priv *priv, int i)
|
|
+{
|
|
+ return mt7530_r32(priv, MT7620_MIB_COUNTER_BASE + mt7620_mibs[i].offset);
|
|
+}
|
|
+
|
|
+static u64 get_mib_counter_port_7620(struct mt7530_priv *priv, int i, int port)
|
|
+{
|
|
+ return mt7530_r32(priv,
|
|
+ MT7620_MIB_COUNTER_BASE_PORT +
|
|
+ (MT7620_MIB_COUNTER_PORT_OFFSET * port) +
|
|
+ mt7620_port_mibs[i].offset);
|
|
+}
|
|
+
|
|
+static int mt7530_sw_get_mib(struct switch_dev *dev,
|
|
+ const struct switch_attr *attr,
|
|
+ struct switch_val *val)
|
|
+{
|
|
+ static char buf[4096];
|
|
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
|
+ int i, len = 0;
|
|
+
|
|
+ len += snprintf(buf + len, sizeof(buf) - len, "Switch MIB counters\n");
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(mt7620_mibs); ++i) {
|
|
+ u64 counter;
|
|
+ len += snprintf(buf + len, sizeof(buf) - len,
|
|
+ "%-11s: ", mt7620_mibs[i].name);
|
|
+ counter = get_mib_counter_7620(priv, i);
|
|
+ len += snprintf(buf + len, sizeof(buf) - len, "%llu\n",
|
|
+ counter);
|
|
+ }
|
|
+
|
|
+ val->value.s = buf;
|
|
+ val->len = len;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mt7530_sw_get_port_mib(struct switch_dev *dev,
|
|
+ const struct switch_attr *attr,
|
|
+ struct switch_val *val)
|
|
+{
|
|
+ static char buf[4096];
|
|
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
|
+ int i, len = 0;
|
|
+
|
|
+ if (val->port_vlan >= MT7530_NUM_PORTS)
|
|
+ return -EINVAL;
|
|
+
|
|
+ len += snprintf(buf + len, sizeof(buf) - len,
|
|
+ "Port %d MIB counters\n", val->port_vlan);
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(mt7620_port_mibs); ++i) {
|
|
+ u64 counter;
|
|
+ len += snprintf(buf + len, sizeof(buf) - len,
|
|
+ "%-11s: ", mt7620_port_mibs[i].name);
|
|
+ counter = get_mib_counter_port_7620(priv, i, val->port_vlan);
|
|
+ len += snprintf(buf + len, sizeof(buf) - len, "%llu\n",
|
|
+ counter);
|
|
+ }
|
|
+
|
|
+ val->value.s = buf;
|
|
+ val->len = len;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct switch_attr mt7530_global[] = {
|
|
+ {
|
|
+ .type = SWITCH_TYPE_INT,
|
|
+ .name = "enable_vlan",
|
|
+ .description = "VLAN mode (1:enabled)",
|
|
+ .max = 1,
|
|
+ .id = MT7530_ATTR_ENABLE_VLAN,
|
|
+ .get = mt7530_get_vlan_enable,
|
|
+ .set = mt7530_set_vlan_enable,
|
|
+ }, {
|
|
+ .type = SWITCH_TYPE_STRING,
|
|
+ .name = "mib",
|
|
+ .description = "Get MIB counters for switch",
|
|
+ .get = mt7530_sw_get_mib,
|
|
+ .set = NULL,
|
|
+ },
|
|
+};
|
|
+
|
|
+static const struct switch_attr mt7621_port[] = {
|
|
+ {
|
|
+ .type = SWITCH_TYPE_STRING,
|
|
+ .name = "mib",
|
|
+ .description = "Get MIB counters for port",
|
|
+ .get = mt7621_sw_get_port_mib,
|
|
+ .set = NULL,
|
|
+ },
|
|
+};
|
|
+
|
|
+static const struct switch_attr mt7530_port[] = {
|
|
+ {
|
|
+ .type = SWITCH_TYPE_STRING,
|
|
+ .name = "mib",
|
|
+ .description = "Get MIB counters for port",
|
|
+ .get = mt7530_sw_get_port_mib,
|
|
+ .set = NULL,
|
|
+ },
|
|
+};
|
|
+
|
|
+static const struct switch_attr mt7530_vlan[] = {
|
|
+ {
|
|
+ .type = SWITCH_TYPE_INT,
|
|
+ .name = "vid",
|
|
+ .description = "VLAN ID (0-4094)",
|
|
+ .set = mt7530_set_vid,
|
|
+ .get = mt7530_get_vid,
|
|
+ .max = 4094,
|
|
+ },
|
|
+};
|
|
+
|
|
+static const struct switch_dev_ops mt7621_ops = {
|
|
+ .attr_global = {
|
|
+ .attr = mt7530_global,
|
|
+ .n_attr = ARRAY_SIZE(mt7530_global),
|
|
+ },
|
|
+ .attr_port = {
|
|
+ .attr = mt7621_port,
|
|
+ .n_attr = ARRAY_SIZE(mt7621_port),
|
|
+ },
|
|
+ .attr_vlan = {
|
|
+ .attr = mt7530_vlan,
|
|
+ .n_attr = ARRAY_SIZE(mt7530_vlan),
|
|
+ },
|
|
+ .get_vlan_ports = mt7530_get_vlan_ports,
|
|
+ .set_vlan_ports = mt7530_set_vlan_ports,
|
|
+ .get_port_pvid = mt7530_get_port_pvid,
|
|
+ .set_port_pvid = mt7530_set_port_pvid,
|
|
+ .get_port_link = mt7530_get_port_link,
|
|
+ .apply_config = mt7530_apply_config,
|
|
+ .reset_switch = mt7530_reset_switch,
|
|
+};
|
|
+
|
|
+static const struct switch_dev_ops mt7530_ops = {
|
|
+ .attr_global = {
|
|
+ .attr = mt7530_global,
|
|
+ .n_attr = ARRAY_SIZE(mt7530_global),
|
|
+ },
|
|
+ .attr_port = {
|
|
+ .attr = mt7530_port,
|
|
+ .n_attr = ARRAY_SIZE(mt7530_port),
|
|
+ },
|
|
+ .attr_vlan = {
|
|
+ .attr = mt7530_vlan,
|
|
+ .n_attr = ARRAY_SIZE(mt7530_vlan),
|
|
+ },
|
|
+ .get_vlan_ports = mt7530_get_vlan_ports,
|
|
+ .set_vlan_ports = mt7530_set_vlan_ports,
|
|
+ .get_port_pvid = mt7530_get_port_pvid,
|
|
+ .set_port_pvid = mt7530_set_port_pvid,
|
|
+ .get_port_link = mt7530_get_port_link,
|
|
+ .apply_config = mt7530_apply_config,
|
|
+ .reset_switch = mt7530_reset_switch,
|
|
+};
|
|
+
|
|
+int
|
|
+mt7530_probe(struct device *dev, void __iomem *base, struct mii_bus *bus, int vlan)
|
|
+{
|
|
+ struct switch_dev *swdev;
|
|
+ struct mt7530_priv *mt7530;
|
|
+ struct mt7530_mapping *map;
|
|
+ int ret;
|
|
+
|
|
+ mt7530 = devm_kzalloc(dev, sizeof(struct mt7530_priv), GFP_KERNEL);
|
|
+ if (!mt7530)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ mt7530->base = base;
|
|
+ mt7530->bus = bus;
|
|
+ mt7530->global_vlan_enable = vlan;
|
|
+
|
|
+ swdev = &mt7530->swdev;
|
|
+ if (bus) {
|
|
+ swdev->alias = "mt7530";
|
|
+ swdev->name = "mt7530";
|
|
+ } else if (IS_ENABLED(CONFIG_SOC_MT7621)) {
|
|
+ swdev->alias = "mt7621";
|
|
+ swdev->name = "mt7621";
|
|
+ } else {
|
|
+ swdev->alias = "mt7620";
|
|
+ swdev->name = "mt7620";
|
|
+ }
|
|
+ swdev->cpu_port = MT7530_CPU_PORT;
|
|
+ swdev->ports = MT7530_NUM_PORTS;
|
|
+ swdev->vlans = MT7530_NUM_VLANS;
|
|
+ if (IS_ENABLED(CONFIG_SOC_MT7621))
|
|
+ swdev->ops = &mt7621_ops;
|
|
+ else
|
|
+ swdev->ops = &mt7530_ops;
|
|
+
|
|
+ ret = register_switch(swdev, NULL);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "failed to register mt7530\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+
|
|
+ map = mt7530_find_mapping(dev->of_node);
|
|
+ if (map)
|
|
+ mt7530_apply_mapping(mt7530, map);
|
|
+ mt7530_apply_config(swdev);
|
|
+
|
|
+ /* magic vodoo */
|
|
+ if (!IS_ENABLED(CONFIG_SOC_MT7621) && bus && mt7530_r32(mt7530, REG_HWTRAP) != 0x1117edf) {
|
|
+ dev_info(dev, "fixing up MHWTRAP register - bootloader probably played with it\n");
|
|
+ mt7530_w32(mt7530, REG_HWTRAP, 0x1117edf);
|
|
+ }
|
|
+ dev_info(dev, "loaded %s driver\n", swdev->name);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
--- /dev/null
|
|
+++ b/drivers/net/ethernet/mediatek/mt7530.h
|
|
@@ -0,0 +1,186 @@
|
|
+/*
|
|
+ * 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.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
|
+ * Copyright (C) 2016 Vitaly Chekryzhev <13hakta@gmail.com>
|
|
+ */
|
|
+
|
|
+#ifndef _MT7530_H__
|
|
+#define _MT7530_H__
|
|
+
|
|
+#define MT7620_MIB_COUNTER_BASE_PORT 0x4000
|
|
+#define MT7620_MIB_COUNTER_PORT_OFFSET 0x100
|
|
+#define MT7620_MIB_COUNTER_BASE 0x1010
|
|
+
|
|
+/* PPE Accounting Group #0 Byte Counter */
|
|
+#define MT7620_MIB_STATS_PPE_AC_BCNT0 0x000
|
|
+
|
|
+/* PPE Accounting Group #0 Packet Counter */
|
|
+#define MT7620_MIB_STATS_PPE_AC_PCNT0 0x004
|
|
+
|
|
+/* PPE Accounting Group #63 Byte Counter */
|
|
+#define MT7620_MIB_STATS_PPE_AC_BCNT63 0x1F8
|
|
+
|
|
+/* PPE Accounting Group #63 Packet Counter */
|
|
+#define MT7620_MIB_STATS_PPE_AC_PCNT63 0x1FC
|
|
+
|
|
+/* PPE Meter Group #0 */
|
|
+#define MT7620_MIB_STATS_PPE_MTR_CNT0 0x200
|
|
+
|
|
+/* PPE Meter Group #63 */
|
|
+#define MT7620_MIB_STATS_PPE_MTR_CNT63 0x2FC
|
|
+
|
|
+/* Transmit good byte count for CPU GDM */
|
|
+#define MT7620_MIB_STATS_GDM1_TX_GBCNT 0x300
|
|
+
|
|
+/* Transmit good packet count for CPU GDM (exclude flow control frames) */
|
|
+#define MT7620_MIB_STATS_GDM1_TX_GPCNT 0x304
|
|
+
|
|
+/* Transmit abort count for CPU GDM */
|
|
+#define MT7620_MIB_STATS_GDM1_TX_SKIPCNT 0x308
|
|
+
|
|
+/* Transmit collision count for CPU GDM */
|
|
+#define MT7620_MIB_STATS_GDM1_TX_COLCNT 0x30C
|
|
+
|
|
+/* Received good byte count for CPU GDM */
|
|
+#define MT7620_MIB_STATS_GDM1_RX_GBCNT1 0x320
|
|
+
|
|
+/* Received good packet count for CPU GDM (exclude flow control frame) */
|
|
+#define MT7620_MIB_STATS_GDM1_RX_GPCNT1 0x324
|
|
+
|
|
+/* Received overflow error packet count for CPU GDM */
|
|
+#define MT7620_MIB_STATS_GDM1_RX_OERCNT 0x328
|
|
+
|
|
+/* Received FCS error packet count for CPU GDM */
|
|
+#define MT7620_MIB_STATS_GDM1_RX_FERCNT 0x32C
|
|
+
|
|
+/* Received too short error packet count for CPU GDM */
|
|
+#define MT7620_MIB_STATS_GDM1_RX_SERCNT 0x330
|
|
+
|
|
+/* Received too long error packet count for CPU GDM */
|
|
+#define MT7620_MIB_STATS_GDM1_RX_LERCNT 0x334
|
|
+
|
|
+/* Received IP/TCP/UDP checksum error packet count for CPU GDM */
|
|
+#define MT7620_MIB_STATS_GDM1_RX_CERCNT 0x338
|
|
+
|
|
+/* Received flow control pkt count for CPU GDM */
|
|
+#define MT7620_MIB_STATS_GDM1_RX_FCCNT 0x33C
|
|
+
|
|
+/* Transmit good byte count for PPE GDM */
|
|
+#define MT7620_MIB_STATS_GDM2_TX_GBCNT 0x340
|
|
+
|
|
+/* Transmit good packet count for PPE GDM (exclude flow control frames) */
|
|
+#define MT7620_MIB_STATS_GDM2_TX_GPCNT 0x344
|
|
+
|
|
+/* Transmit abort count for PPE GDM */
|
|
+#define MT7620_MIB_STATS_GDM2_TX_SKIPCNT 0x348
|
|
+
|
|
+/* Transmit collision count for PPE GDM */
|
|
+#define MT7620_MIB_STATS_GDM2_TX_COLCNT 0x34C
|
|
+
|
|
+/* Received good byte count for PPE GDM */
|
|
+#define MT7620_MIB_STATS_GDM2_RX_GBCNT 0x360
|
|
+
|
|
+/* Received good packet count for PPE GDM (exclude flow control frame) */
|
|
+#define MT7620_MIB_STATS_GDM2_RX_GPCNT 0x364
|
|
+
|
|
+/* Received overflow error packet count for PPE GDM */
|
|
+#define MT7620_MIB_STATS_GDM2_RX_OERCNT 0x368
|
|
+
|
|
+/* Received FCS error packet count for PPE GDM */
|
|
+#define MT7620_MIB_STATS_GDM2_RX_FERCNT 0x36C
|
|
+
|
|
+/* Received too short error packet count for PPE GDM */
|
|
+#define MT7620_MIB_STATS_GDM2_RX_SERCNT 0x370
|
|
+
|
|
+/* Received too long error packet count for PPE GDM */
|
|
+#define MT7620_MIB_STATS_GDM2_RX_LERCNT 0x374
|
|
+
|
|
+/* Received IP/TCP/UDP checksum error packet count for PPE GDM */
|
|
+#define MT7620_MIB_STATS_GDM2_RX_CERCNT 0x378
|
|
+
|
|
+/* Received flow control pkt count for PPE GDM */
|
|
+#define MT7620_MIB_STATS_GDM2_RX_FCCNT 0x37C
|
|
+
|
|
+/* Tx Packet Counter of Port n */
|
|
+#define MT7620_MIB_STATS_PORT_TGPCN 0x10
|
|
+
|
|
+/* Tx Bad Octet Counter of Port n */
|
|
+#define MT7620_MIB_STATS_PORT_TBOCN 0x14
|
|
+
|
|
+/* Tx Good Octet Counter of Port n */
|
|
+#define MT7620_MIB_STATS_PORT_TGOCN 0x18
|
|
+
|
|
+/* Tx Event Packet Counter of Port n */
|
|
+#define MT7620_MIB_STATS_PORT_TEPCN 0x1C
|
|
+
|
|
+/* Rx Packet Counter of Port n */
|
|
+#define MT7620_MIB_STATS_PORT_RGPCN 0x20
|
|
+
|
|
+/* Rx Bad Octet Counter of Port n */
|
|
+#define MT7620_MIB_STATS_PORT_RBOCN 0x24
|
|
+
|
|
+/* Rx Good Octet Counter of Port n */
|
|
+#define MT7620_MIB_STATS_PORT_RGOCN 0x28
|
|
+
|
|
+/* Rx Event Packet Counter of Port n */
|
|
+#define MT7620_MIB_STATS_PORT_REPC1N 0x2C
|
|
+
|
|
+/* Rx Event Packet Counter of Port n */
|
|
+#define MT7620_MIB_STATS_PORT_REPC2N 0x30
|
|
+
|
|
+#define MT7621_MIB_COUNTER_BASE 0x4000
|
|
+#define MT7621_MIB_COUNTER_PORT_OFFSET 0x100
|
|
+#define MT7621_STATS_TDPC 0x00
|
|
+#define MT7621_STATS_TCRC 0x04
|
|
+#define MT7621_STATS_TUPC 0x08
|
|
+#define MT7621_STATS_TMPC 0x0C
|
|
+#define MT7621_STATS_TBPC 0x10
|
|
+#define MT7621_STATS_TCEC 0x14
|
|
+#define MT7621_STATS_TSCEC 0x18
|
|
+#define MT7621_STATS_TMCEC 0x1C
|
|
+#define MT7621_STATS_TDEC 0x20
|
|
+#define MT7621_STATS_TLCEC 0x24
|
|
+#define MT7621_STATS_TXCEC 0x28
|
|
+#define MT7621_STATS_TPPC 0x2C
|
|
+#define MT7621_STATS_TL64PC 0x30
|
|
+#define MT7621_STATS_TL65PC 0x34
|
|
+#define MT7621_STATS_TL128PC 0x38
|
|
+#define MT7621_STATS_TL256PC 0x3C
|
|
+#define MT7621_STATS_TL512PC 0x40
|
|
+#define MT7621_STATS_TL1024PC 0x44
|
|
+#define MT7621_STATS_TOC 0x48
|
|
+#define MT7621_STATS_RDPC 0x60
|
|
+#define MT7621_STATS_RFPC 0x64
|
|
+#define MT7621_STATS_RUPC 0x68
|
|
+#define MT7621_STATS_RMPC 0x6C
|
|
+#define MT7621_STATS_RBPC 0x70
|
|
+#define MT7621_STATS_RAEPC 0x74
|
|
+#define MT7621_STATS_RCEPC 0x78
|
|
+#define MT7621_STATS_RUSPC 0x7C
|
|
+#define MT7621_STATS_RFEPC 0x80
|
|
+#define MT7621_STATS_ROSPC 0x84
|
|
+#define MT7621_STATS_RJEPC 0x88
|
|
+#define MT7621_STATS_RPPC 0x8C
|
|
+#define MT7621_STATS_RL64PC 0x90
|
|
+#define MT7621_STATS_RL65PC 0x94
|
|
+#define MT7621_STATS_RL128PC 0x98
|
|
+#define MT7621_STATS_RL256PC 0x9C
|
|
+#define MT7621_STATS_RL512PC 0xA0
|
|
+#define MT7621_STATS_RL1024PC 0xA4
|
|
+#define MT7621_STATS_ROC 0xA8
|
|
+#define MT7621_STATS_RDPC_CTRL 0xB0
|
|
+#define MT7621_STATS_RDPC_ING 0xB4
|
|
+#define MT7621_STATS_RDPC_ARL 0xB8
|
|
+
|
|
+int mt7530_probe(struct device *dev, void __iomem *base, struct mii_bus *bus, int vlan);
|
|
+
|
|
+#endif
|
|
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
|
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
|
@@ -1291,8 +1291,13 @@ static int __init fe_init(struct net_dev
|
|
}
|
|
|
|
err = fe_hw_init(dev);
|
|
- if (!err)
|
|
- return 0;
|
|
+ if (err)
|
|
+ goto err_phy_disconnect;
|
|
+
|
|
+ if ((priv->flags & FE_FLAG_HAS_SWITCH) && priv->soc->switch_config)
|
|
+ priv->soc->switch_config(priv);
|
|
+
|
|
+ return 0;
|
|
|
|
err_phy_disconnect:
|
|
if (priv->phy)
|
|
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
|
|
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
|
|
@@ -383,6 +383,7 @@ struct fe_soc_data {
|
|
int (*fwd_config)(struct fe_priv *priv);
|
|
void (*tx_dma)(struct fe_tx_dma *txd);
|
|
int (*switch_init)(struct fe_priv *priv);
|
|
+ int (*switch_config)(struct fe_priv *priv);
|
|
void (*port_init)(struct fe_priv *priv, struct device_node *port);
|
|
int (*has_carrier)(struct fe_priv *priv);
|
|
int (*mdio_init)(struct fe_priv *priv);
|
|
--- a/drivers/net/ethernet/mediatek/soc_mt7620.c
|
|
+++ b/drivers/net/ethernet/mediatek/soc_mt7620.c
|
|
@@ -313,6 +313,7 @@ static struct fe_soc_data mt7620_data =
|
|
.fwd_config = mt7620_fwd_config,
|
|
.tx_dma = mt7620_tx_dma,
|
|
.switch_init = mtk_gsw_init,
|
|
+ .switch_config = mt7620_gsw_config,
|
|
.port_init = mt7620_port_init,
|
|
.reg_table = mt7620_reg_table,
|
|
.pdma_glo_cfg = FE_PDMA_SIZE_16DWORDS,
|