diff options
Diffstat (limited to 'target/linux/mediatek/patches-4.9/0092-dsa5.patch')
-rw-r--r-- | target/linux/mediatek/patches-4.9/0092-dsa5.patch | 1608 |
1 files changed, 1608 insertions, 0 deletions
diff --git a/target/linux/mediatek/patches-4.9/0092-dsa5.patch b/target/linux/mediatek/patches-4.9/0092-dsa5.patch new file mode 100644 index 0000000000..6c101377c3 --- /dev/null +++ b/target/linux/mediatek/patches-4.9/0092-dsa5.patch @@ -0,0 +1,1608 @@ +From patchwork Wed Mar 29 09:38:23 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [net-next, v3, + 5/5] net-next: dsa: add dsa support for Mediatek MT7530 switch +From: sean.wang@mediatek.com +X-Patchwork-Id: 9651095 +Message-Id: <1490780303-18598-6-git-send-email-sean.wang@mediatek.com> +To: <andrew@lunn.ch>, <f.fainelli@gmail.com>, + <vivien.didelot@savoirfairelinux.com>, <matthias.bgg@gmail.com>, + <robh+dt@kernel.org>, <mark.rutland@arm.com> +Cc: devicetree@vger.kernel.org, Landen.Chao@mediatek.com, keyhaede@gmail.com, + netdev@vger.kernel.org, sean.wang@mediatek.com, + linux-kernel@vger.kernel.org, + linux-mediatek@lists.infradead.org, objelf@gmail.com, davem@davemloft.net +Date: Wed, 29 Mar 2017 17:38:23 +0800 + +From: Sean Wang <sean.wang@mediatek.com> + +MT7530 is a 7-ports Gigabit Ethernet Switch that could be found on +Mediatek router platforms such as MT7623A or MT7623N platform which +includes 7-port Gigabit Ethernet MAC and 5-port Gigabit Ethernet PHY. +Among these ports, The port from 0 to 4 are the user ports connecting +with the remote devices while the port 5 and 6 are the CPU ports +connecting into Mediatek Ethernet GMAC. + +For port 6, it can communicate with the CPU via Mediatek Ethernet GMAC +through either the TRGMII or RGMII which could be controlled by phy-mode +in the dt-bindings to specify which mode is preferred to use. And for +port 5, only RGMII can be specified. However, currently, only port 6 is +being supported in this DSA driver. + +The driver is made with the reference to qca8k and other existing DSA +driver. The most of the essential callbacks of the DSA are already +support in the driver, including tag insert for user port distinguishing, +port control, bridge offloading, STP setup and ethtool operation to allow +DSA to model each user port into a standalone netdevice as the other DSA +driver had done. + +Signed-off-by: Sean Wang <sean.wang@mediatek.com> +Signed-off-by: Landen Chao <Landen.Chao@mediatek.com> +--- + drivers/net/dsa/Kconfig | 8 + + drivers/net/dsa/Makefile | 2 +- + drivers/net/dsa/mt7530.c | 1126 ++++++++++++++++++++++++++++++++++++++++++++++ + drivers/net/dsa/mt7530.h | 390 ++++++++++++++++ + 4 files changed, 1525 insertions(+), 1 deletion(-) + create mode 100644 drivers/net/dsa/mt7530.c + create mode 100644 drivers/net/dsa/mt7530.h + +diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig +index 0659846..5b322b4 100644 +--- a/drivers/net/dsa/Kconfig ++++ b/drivers/net/dsa/Kconfig +@@ -34,4 +34,12 @@ config NET_DSA_QCA8K + This enables support for the Qualcomm Atheros QCA8K Ethernet + switch chips. + ++config NET_DSA_MT7530 ++ tristate "Mediatek MT7530 Ethernet switch support" ++ depends on NET_DSA ++ select NET_DSA_TAG_MTK ++ ---help--- ++ This enables support for the Mediatek MT7530 Ethernet switch ++ chip. ++ + endmenu +diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile +index a3c9416..8e629c1 100644 +--- a/drivers/net/dsa/Makefile ++++ b/drivers/net/dsa/Makefile +@@ -2,6 +2,6 @@ obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o + obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm-sf2.o + bcm-sf2-objs := bcm_sf2.o bcm_sf2_cfp.o + obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o +- ++obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o + obj-y += b53/ + obj-y += mv88e6xxx/ +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +new file mode 100644 +index 0000000..ad2e6f8 +--- /dev/null ++++ b/drivers/net/dsa/mt7530.c +@@ -0,0 +1,1126 @@ ++/* ++ * Mediatek MT7530 DSA Switch driver ++ * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * 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. ++ */ ++#include <linux/etherdevice.h> ++#include <linux/if_bridge.h> ++#include <linux/iopoll.h> ++#include <linux/mdio.h> ++#include <linux/mfd/syscon.h> ++#include <linux/module.h> ++#include <linux/netdevice.h> ++#include <linux/of_gpio.h> ++#include <linux/of_mdio.h> ++#include <linux/of_net.h> ++#include <linux/of_platform.h> ++#include <linux/phy.h> ++#include <linux/regmap.h> ++#include <linux/regulator/consumer.h> ++#include <linux/reset.h> ++#include <net/dsa.h> ++#include <net/switchdev.h> ++ ++#include "mt7530.h" ++ ++/* String, offset, and register size in bytes if different from 4 bytes */ ++static const struct mt7530_mib_desc mt7530_mib[] = { ++ MIB_DESC(1, 0x00, "TxDrop"), ++ MIB_DESC(1, 0x04, "TxCrcErr"), ++ MIB_DESC(1, 0x08, "TxUnicast"), ++ MIB_DESC(1, 0x0c, "TxMulticast"), ++ MIB_DESC(1, 0x10, "TxBroadcast"), ++ MIB_DESC(1, 0x14, "TxCollision"), ++ MIB_DESC(1, 0x18, "TxSingleCollision"), ++ MIB_DESC(1, 0x1c, "TxMultipleCollision"), ++ MIB_DESC(1, 0x20, "TxDeferred"), ++ MIB_DESC(1, 0x24, "TxLateCollision"), ++ MIB_DESC(1, 0x28, "TxExcessiveCollistion"), ++ MIB_DESC(1, 0x2c, "TxPause"), ++ MIB_DESC(1, 0x30, "TxPktSz64"), ++ MIB_DESC(1, 0x34, "TxPktSz65To127"), ++ MIB_DESC(1, 0x38, "TxPktSz128To255"), ++ MIB_DESC(1, 0x3c, "TxPktSz256To511"), ++ MIB_DESC(1, 0x40, "TxPktSz512To1023"), ++ MIB_DESC(1, 0x44, "Tx1024ToMax"), ++ MIB_DESC(2, 0x48, "TxBytes"), ++ MIB_DESC(1, 0x60, "RxDrop"), ++ MIB_DESC(1, 0x64, "RxFiltering"), ++ MIB_DESC(1, 0x6c, "RxMulticast"), ++ MIB_DESC(1, 0x70, "RxBroadcast"), ++ MIB_DESC(1, 0x74, "RxAlignErr"), ++ MIB_DESC(1, 0x78, "RxCrcErr"), ++ MIB_DESC(1, 0x7c, "RxUnderSizeErr"), ++ MIB_DESC(1, 0x80, "RxFragErr"), ++ MIB_DESC(1, 0x84, "RxOverSzErr"), ++ MIB_DESC(1, 0x88, "RxJabberErr"), ++ MIB_DESC(1, 0x8c, "RxPause"), ++ MIB_DESC(1, 0x90, "RxPktSz64"), ++ MIB_DESC(1, 0x94, "RxPktSz65To127"), ++ MIB_DESC(1, 0x98, "RxPktSz128To255"), ++ MIB_DESC(1, 0x9c, "RxPktSz256To511"), ++ MIB_DESC(1, 0xa0, "RxPktSz512To1023"), ++ MIB_DESC(1, 0xa4, "RxPktSz1024ToMax"), ++ MIB_DESC(2, 0xa8, "RxBytes"), ++ MIB_DESC(1, 0xb0, "RxCtrlDrop"), ++ MIB_DESC(1, 0xb4, "RxIngressDrop"), ++ MIB_DESC(1, 0xb8, "RxArlDrop"), ++}; ++ ++static struct mt7530_priv *lpriv; ++static void mt7530_port_disable(struct dsa_switch *ds, int port, ++ struct phy_device *phy); ++static int mt7530_cpu_port_enable(struct mt7530_priv *priv, ++ int port); ++ ++static int ++mt7623_trgmii_write(struct mt7530_priv *priv, u32 reg, u32 val) ++{ ++ int ret; ++ ++ ret = regmap_write(priv->ethernet, TRGMII_BASE(reg), val); ++ if (ret < 0) ++ dev_err(priv->dev, ++ "failed to priv write register\n"); ++ return ret; ++} ++ ++static u32 ++mt7623_trgmii_read(struct mt7530_priv *priv, u32 reg) ++{ ++ int ret; ++ u32 val; ++ ++ ret = regmap_read(priv->ethernet, TRGMII_BASE(reg), &val); ++ if (ret < 0) { ++ dev_err(priv->dev, ++ "failed to priv read register\n"); ++ return ret; ++ } ++ ++ return val; ++} ++ ++static void ++mt7623_trgmii_rmw(struct mt7530_priv *priv, u32 reg, ++ u32 mask, u32 set) ++{ ++ u32 val; ++ ++ val = mt7623_trgmii_read(priv, reg); ++ val &= ~mask; ++ val |= set; ++ mt7623_trgmii_write(priv, reg, val); ++} ++ ++static void ++mt7623_trgmii_set(struct mt7530_priv *priv, u32 reg, u32 val) ++{ ++ mt7623_trgmii_rmw(priv, reg, 0, val); ++} ++ ++static void ++mt7623_trgmii_clear(struct mt7530_priv *priv, u32 reg, u32 val) ++{ ++ mt7623_trgmii_rmw(priv, reg, val, 0); ++} ++ ++static int ++core_read_mmd_indirect(struct mt7530_priv *priv, int prtad, int devad) ++{ ++ struct mii_bus *bus = priv->bus; ++ int value, ret; ++ ++ /* Write the desired MMD Devad */ ++ ret = bus->write(bus, 0, MII_MMD_CTRL, devad); ++ if (ret < 0) ++ goto err; ++ ++ /* Write the desired MMD register address */ ++ ret = bus->write(bus, 0, MII_MMD_DATA, prtad); ++ if (ret < 0) ++ goto err; ++ ++ /* Select the Function : DATA with no post increment */ ++ ret = bus->write(bus, 0, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); ++ if (ret < 0) ++ goto err; ++ ++ /* Read the content of the MMD's selected register */ ++ value = bus->read(bus, 0, MII_MMD_DATA); ++ ++ return value; ++err: ++ dev_err(&bus->dev, "failed to read mmd register\n"); ++ ++ return ret; ++} ++ ++static int ++core_write_mmd_indirect(struct mt7530_priv *priv, int prtad, ++ int devad, u32 data) ++{ ++ struct mii_bus *bus = priv->bus; ++ int ret; ++ ++ /* Write the desired MMD Devad */ ++ ret = bus->write(bus, 0, MII_MMD_CTRL, devad); ++ if (ret < 0) ++ goto err; ++ ++ /* Write the desired MMD register address */ ++ ret = bus->write(bus, 0, MII_MMD_DATA, prtad); ++ if (ret < 0) ++ goto err; ++ ++ /* Select the Function : DATA with no post increment */ ++ ret = bus->write(bus, 0, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); ++ if (ret < 0) ++ goto err; ++ ++ /* Write the data into MMD's selected register */ ++ ret = bus->write(bus, 0, MII_MMD_DATA, data); ++err: ++ if (ret < 0) ++ dev_err(&bus->dev, ++ "failed to write mmd register\n"); ++ return ret; ++} ++ ++static void ++core_write(struct mt7530_priv *priv, u32 reg, u32 val) ++{ ++ struct mii_bus *bus = priv->bus; ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val); ++ ++ mutex_unlock(&bus->mdio_lock); ++} ++ ++static void ++core_rmw(struct mt7530_priv *priv, u32 reg, u32 mask, u32 set) ++{ ++ struct mii_bus *bus = priv->bus; ++ u32 val; ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ val = core_read_mmd_indirect(priv, reg, MDIO_MMD_VEND2); ++ val &= ~mask; ++ val |= set; ++ core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val); ++ ++ mutex_unlock(&bus->mdio_lock); ++} ++ ++static void ++core_set(struct mt7530_priv *priv, u32 reg, u32 val) ++{ ++ core_rmw(priv, reg, 0, val); ++} ++ ++static void ++core_clear(struct mt7530_priv *priv, u32 reg, u32 val) ++{ ++ core_rmw(priv, reg, val, 0); ++} ++ ++static int ++mt7530_mii_write(struct mt7530_priv *priv, u32 reg, u32 val) ++{ ++ struct mii_bus *bus = priv->bus; ++ u16 page, r, lo, hi; ++ int ret; ++ ++ page = (reg >> 6) & 0x3ff; ++ r = (reg >> 2) & 0xf; ++ lo = val & 0xffff; ++ hi = val >> 16; ++ ++ /* MT7530 uses 31 as the pseudo port */ ++ ret = bus->write(bus, 0x1f, 0x1f, page); ++ if (ret < 0) ++ goto err; ++ ++ ret = bus->write(bus, 0x1f, r, lo); ++ if (ret < 0) ++ goto err; ++ ++ ret = bus->write(bus, 0x1f, 0x10, hi); ++err: ++ if (ret < 0) ++ dev_err(&bus->dev, ++ "failed to write mt7530 register\n"); ++ return ret; ++} ++ ++static u32 ++mt7530_mii_read(struct mt7530_priv *priv, u32 reg) ++{ ++ struct mii_bus *bus = priv->bus; ++ u16 page, r, lo, hi; ++ int ret; ++ ++ page = (reg >> 6) & 0x3ff; ++ r = (reg >> 2) & 0xf; ++ ++ /* MT7530 uses 31 as the pseudo port */ ++ ret = bus->write(bus, 0x1f, 0x1f, page); ++ if (ret < 0) { ++ dev_err(&bus->dev, ++ "failed to read mt7530 register\n"); ++ return ret; ++ } ++ ++ lo = bus->read(bus, 0x1f, r); ++ hi = bus->read(bus, 0x1f, 0x10); ++ ++ return (hi << 16) | (lo & 0xffff); ++} ++ ++static void ++mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val) ++{ ++ struct mii_bus *bus = priv->bus; ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ mt7530_mii_write(priv, reg, val); ++ ++ mutex_unlock(&bus->mdio_lock); ++} ++ ++static u32 ++_mt7530_read(u32 reg) ++{ ++ struct mt7530_priv *priv = lpriv; ++ struct mii_bus *bus = priv->bus; ++ u32 val; ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ val = mt7530_mii_read(priv, reg); ++ ++ mutex_unlock(&bus->mdio_lock); ++ ++ return val; ++} ++ ++static u32 ++mt7530_read(struct mt7530_priv *priv, u32 reg) ++{ ++ return _mt7530_read(reg); ++} ++ ++static void ++mt7530_rmw(struct mt7530_priv *priv, u32 reg, ++ u32 mask, u32 set) ++{ ++ struct mii_bus *bus = priv->bus; ++ u32 val; ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ val = mt7530_mii_read(priv, reg); ++ val &= ~mask; ++ val |= set; ++ mt7530_mii_write(priv, reg, val); ++ ++ mutex_unlock(&bus->mdio_lock); ++} ++ ++static void ++mt7530_set(struct mt7530_priv *priv, u32 reg, u32 val) ++{ ++ mt7530_rmw(priv, reg, 0, val); ++} ++ ++static void ++mt7530_clear(struct mt7530_priv *priv, u32 reg, u32 val) ++{ ++ mt7530_rmw(priv, reg, val, 0); ++} ++ ++static int ++mt7530_fdb_cmd(struct mt7530_priv *priv, enum mt7530_fdb_cmd cmd, u32 *rsp) ++{ ++ u32 val; ++ int ret; ++ ++ /* Set the command operating upon the MAC address entries */ ++ val = ATC_BUSY | ATC_MAT(0) | cmd; ++ mt7530_write(priv, MT7530_ATC, val); ++ ++ ret = readx_poll_timeout(_mt7530_read, MT7530_ATC, val, ++ !(val & ATC_BUSY), 20, 20000); ++ if (ret < 0) { ++ dev_err(priv->dev, "reset timeout\n"); ++ return ret; ++ } ++ ++ /* Additional sanity for read command if the specified ++ * entry is invalid ++ */ ++ val = mt7530_read(priv, MT7530_ATC); ++ if ((cmd == MT7530_FDB_READ) && (val & ATC_INVALID)) ++ return -EINVAL; ++ ++ if (rsp) ++ *rsp = val; ++ ++ return 0; ++} ++ ++static void ++mt7530_fdb_read(struct mt7530_priv *priv, struct mt7530_fdb *fdb) ++{ ++ u32 reg[3]; ++ int i; ++ ++ /* Read from ARL table into an array */ ++ for (i = 0; i < 3; i++) { ++ reg[i] = mt7530_read(priv, MT7530_TSRA1 + (i * 4)); ++ ++ dev_dbg(priv->dev, "%s(%d) reg[%d]=0x%x\n", ++ __func__, __LINE__, i, reg[i]); ++ } ++ ++ fdb->vid = (reg[1] >> CVID) & CVID_MASK; ++ fdb->aging = (reg[2] >> AGE_TIMER) & AGE_TIMER_MASK; ++ fdb->port_mask = (reg[2] >> PORT_MAP) & PORT_MAP_MASK; ++ fdb->mac[0] = (reg[0] >> MAC_BYTE_0) & MAC_BYTE_MASK; ++ fdb->mac[1] = (reg[0] >> MAC_BYTE_1) & MAC_BYTE_MASK; ++ fdb->mac[2] = (reg[0] >> MAC_BYTE_2) & MAC_BYTE_MASK; ++ fdb->mac[3] = (reg[0] >> MAC_BYTE_3) & MAC_BYTE_MASK; ++ fdb->mac[4] = (reg[1] >> MAC_BYTE_4) & MAC_BYTE_MASK; ++ fdb->mac[5] = (reg[1] >> MAC_BYTE_5) & MAC_BYTE_MASK; ++ fdb->noarp = ((reg[2] >> ENT_STATUS) & ENT_STATUS_MASK) == STATIC_ENT; ++} ++ ++static void ++mt7530_fdb_write(struct mt7530_priv *priv, u16 vid, ++ u8 port_mask, const u8 *mac, ++ u8 aging, u8 type) ++{ ++ u32 reg[3] = { 0 }; ++ int i; ++ ++ reg[1] |= vid & CVID_MASK; ++ reg[2] |= (aging & AGE_TIMER_MASK) << AGE_TIMER; ++ reg[2] |= (port_mask & PORT_MAP_MASK) << PORT_MAP; ++ /* STATIC_ENT indicate that entry is static wouldn't ++ * be aged out and STATIC_EMP specified as erasing an ++ * entry ++ */ ++ reg[2] |= (type & ENT_STATUS_MASK) << ENT_STATUS; ++ reg[1] |= mac[5] << MAC_BYTE_5; ++ reg[1] |= mac[4] << MAC_BYTE_4; ++ reg[0] |= mac[3] << MAC_BYTE_3; ++ reg[0] |= mac[2] << MAC_BYTE_2; ++ reg[0] |= mac[1] << MAC_BYTE_1; ++ reg[0] |= mac[0] << MAC_BYTE_0; ++ ++ /* Write array into the ARL table */ ++ for (i = 0; i < 3; i++) ++ mt7530_write(priv, MT7530_ATA1 + (i * 4), reg[i]); ++} ++ ++static int ++mt7530_pad_clk_setup(struct dsa_switch *ds, int mode) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ u32 ncpo1, ssc_delta, trgint, i; ++ ++ switch (mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ trgint = 0; ++ ncpo1 = 0x0c80; ++ ssc_delta = 0x87; ++ break; ++ case PHY_INTERFACE_MODE_TRGMII: ++ trgint = 1; ++ ncpo1 = 0x1400; ++ ssc_delta = 0x57; ++ break; ++ default: ++ dev_err(priv->dev, "xMII mode %d not supported\n", mode); ++ return -EINVAL; ++ } ++ ++ mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, ++ P6_INTF_MODE(trgint)); ++ ++ /* Lower Tx Driving for TRGMII path */ ++ for (i = 0 ; i < NUM_TRGMII_CTRL ; i++) ++ mt7530_write(priv, MT7530_TRGMII_TD_ODT(i), ++ TD_DM_DRVP(8) | TD_DM_DRVN(8)); ++ ++ /* Setup core clock for MT7530 */ ++ if (!trgint) { ++ /* Disable MT7530 core clock */ ++ core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN); ++ ++ /* Disable PLL, since phy_device has not yet been created ++ * provided for phy_[read,write]_mmd_indirect is called, we ++ * provide our own core_write_mmd_indirect to complete this ++ * function. ++ */ ++ core_write_mmd_indirect(priv, ++ CORE_GSWPLL_GRP1, ++ MDIO_MMD_VEND2, ++ 0); ++ ++ /* Set core clock into 500Mhz */ ++ core_write(priv, CORE_GSWPLL_GRP2, ++ RG_GSWPLL_POSDIV_500M(1) | ++ RG_GSWPLL_FBKDIV_500M(25)); ++ ++ /* Enable PLL */ ++ core_write(priv, CORE_GSWPLL_GRP1, ++ RG_GSWPLL_EN_PRE | ++ RG_GSWPLL_POSDIV_200M(2) | ++ RG_GSWPLL_FBKDIV_200M(32)); ++ ++ /* Enable MT7530 core clock */ ++ core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN); ++ } ++ ++ /* Setup the MT7530 TRGMII Tx Clock */ ++ core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN); ++ core_write(priv, CORE_PLL_GROUP5, RG_LCDDS_PCW_NCPO1(ncpo1)); ++ core_write(priv, CORE_PLL_GROUP6, RG_LCDDS_PCW_NCPO0(0)); ++ core_write(priv, CORE_PLL_GROUP10, RG_LCDDS_SSC_DELTA(ssc_delta)); ++ core_write(priv, CORE_PLL_GROUP11, RG_LCDDS_SSC_DELTA1(ssc_delta)); ++ core_write(priv, CORE_PLL_GROUP4, ++ RG_SYSPLL_DDSFBK_EN | RG_SYSPLL_BIAS_EN | ++ RG_SYSPLL_BIAS_LPF_EN); ++ core_write(priv, CORE_PLL_GROUP2, ++ RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN | ++ RG_SYSPLL_POSDIV(1)); ++ core_write(priv, CORE_PLL_GROUP7, ++ RG_LCDDS_PCW_NCPO_CHG | RG_LCCDS_C(3) | ++ RG_LCDDS_PWDB | RG_LCDDS_ISO_EN); ++ core_set(priv, CORE_TRGMII_GSW_CLK_CG, ++ REG_GSWCK_EN | REG_TRGMIICK_EN); ++ ++ if (!trgint) ++ for (i = 0 ; i < NUM_TRGMII_CTRL; i++) ++ mt7530_rmw(priv, MT7530_TRGMII_RD(i), ++ RD_TAP_MASK, RD_TAP(16)); ++ else ++ mt7623_trgmii_set(priv, GSW_INTF_MODE, INTF_MODE_TRGMII); ++ ++ return 0; ++} ++ ++static int ++mt7623_pad_clk_setup(struct dsa_switch *ds) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ int i; ++ ++ for (i = 0 ; i < NUM_TRGMII_CTRL; i++) ++ mt7623_trgmii_write(priv, GSW_TRGMII_TD_ODT(i), ++ TD_DM_DRVP(8) | TD_DM_DRVN(8)); ++ ++ mt7623_trgmii_set(priv, GSW_TRGMII_RCK_CTRL, RX_RST | RXC_DQSISEL); ++ mt7623_trgmii_clear(priv, GSW_TRGMII_RCK_CTRL, RX_RST); ++ ++ return 0; ++} ++ ++static void ++mt7530_mib_reset(struct dsa_switch *ds) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ ++ mt7530_write(priv, MT7530_MIB_CCR, CCR_MIB_FLUSH); ++ mt7530_write(priv, MT7530_MIB_CCR, CCR_MIB_ACTIVATE); ++} ++ ++static void ++mt7530_port_set_status(struct mt7530_priv *priv, int port, int enable) ++{ ++ u32 mask = PMCR_TX_EN | PMCR_RX_EN; ++ ++ if (enable) ++ mt7530_set(priv, MT7530_PMCR_P(port), mask); ++ else ++ mt7530_clear(priv, MT7530_PMCR_P(port), mask); ++} ++ ++static int ++mt7530_setup(struct dsa_switch *ds) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ int ret, i; ++ u32 id, val; ++ struct device_node *dn; ++ ++ /* The parent node of master_netdev which holds the common system ++ * controller also is the container for two GMACs nodes representing ++ * as two netdev instances. ++ */ ++ dn = ds->master_netdev->dev.of_node->parent; ++ priv->ethernet = syscon_node_to_regmap(dn); ++ if (IS_ERR(priv->ethernet)) ++ return PTR_ERR(priv->ethernet); ++ ++ regulator_set_voltage(priv->core_pwr, 1000000, 1000000); ++ ret = regulator_enable(priv->core_pwr); ++ if (ret < 0) { ++ dev_err(priv->dev, ++ "Failed to enable core power: %d\n", ret); ++ return ret; ++ } ++ ++ regulator_set_voltage(priv->io_pwr, 3300000, 3300000); ++ ret = regulator_enable(priv->io_pwr); ++ if (ret < 0) { ++ dev_err(priv->dev, "Failed to enable io pwr: %d\n", ++ ret); ++ return ret; ++ } ++ ++ /* Reset whole chip through gpio pin or memory-mapped registers for ++ * different type of hardware ++ */ ++ if (priv->mcm) { ++ reset_control_assert(priv->rstc); ++ usleep_range(1000, 1100); ++ reset_control_deassert(priv->rstc); ++ } else { ++ gpiod_set_value_cansleep(priv->reset, 0); ++ usleep_range(1000, 1100); ++ gpiod_set_value_cansleep(priv->reset, 1); ++ } ++ ++ /* Waiting for MT7530 got to stable */ ++ ret = readx_poll_timeout(_mt7530_read, MT7530_HWTRAP, val, val != 0, ++ 20, 1000000); ++ if (ret < 0) { ++ dev_err(priv->dev, "reset timeout\n"); ++ return ret; ++ } ++ ++ id = mt7530_read(priv, MT7530_CREV); ++ id >>= CHIP_NAME_SHIFT; ++ if (id != MT7530_ID) { ++ dev_err(priv->dev, "chip %x can't be supported\n", id); ++ return -ENODEV; ++ } ++ ++ /* Reset the switch through internal reset */ ++ mt7530_write(priv, MT7530_SYS_CTRL, ++ SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST | ++ SYS_CTRL_REG_RST); ++ ++ /* Enable Port 6 only; P5 as GMAC5 which currently is not supported */ ++ val = mt7530_read(priv, MT7530_MHWTRAP); ++ val &= ~MHWTRAP_P6_DIS & ~MHWTRAP_PHY_ACCESS; ++ val |= MHWTRAP_MANUAL; ++ mt7530_write(priv, MT7530_MHWTRAP, val); ++ ++ /* Enable and reset MIB counters */ ++ mt7530_mib_reset(ds); ++ ++ mt7530_clear(priv, MT7530_MFC, UNU_FFP_MASK); ++ ++ for (i = 0; i < MT7530_NUM_PORTS; i++) { ++ /* Disable forwarding by default on all ports */ ++ mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK, ++ PCR_MATRIX_CLR); ++ ++ if (dsa_is_cpu_port(ds, i)) ++ mt7530_cpu_port_enable(priv, i); ++ else ++ mt7530_port_disable(ds, i, NULL); ++ } ++ ++ /* Flush the FDB table */ ++ ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, 0); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int mt7530_phy_read(struct dsa_switch *ds, int port, int regnum) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ ++ return mdiobus_read_nested(priv->bus, port, regnum); ++} ++ ++int mt7530_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ ++ return mdiobus_write_nested(priv->bus, port, regnum, val); ++} ++ ++static void ++mt7530_get_strings(struct dsa_switch *ds, int port, uint8_t *data) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(mt7530_mib); i++) ++ strncpy(data + i * ETH_GSTRING_LEN, mt7530_mib[i].name, ++ ETH_GSTRING_LEN); ++} ++ ++static void ++mt7530_get_ethtool_stats(struct dsa_switch *ds, int port, ++ uint64_t *data) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ const struct mt7530_mib_desc *mib; ++ u32 reg, i; ++ u64 hi; ++ ++ for (i = 0; i < ARRAY_SIZE(mt7530_mib); i++) { ++ mib = &mt7530_mib[i]; ++ reg = MT7530_PORT_MIB_COUNTER(port) + mib->offset; ++ ++ data[i] = mt7530_read(priv, reg); ++ if (mib->size == 2) { ++ hi = mt7530_read(priv, reg + 4); ++ data[i] |= hi << 32; ++ } ++ } ++} ++ ++static int ++mt7530_get_sset_count(struct dsa_switch *ds) ++{ ++ return ARRAY_SIZE(mt7530_mib); ++} ++ ++static void mt7530_adjust_link(struct dsa_switch *ds, int port, ++ struct phy_device *phydev) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ ++ if (phy_is_pseudo_fixed_link(phydev)) { ++ dev_dbg(priv->dev, "phy-mode for master device = %x\n", ++ phydev->interface); ++ ++ /* Setup TX circuit incluing relevant PAD and driving */ ++ mt7530_pad_clk_setup(ds, phydev->interface); ++ ++ /* Setup RX circuit, relevant PAD and driving on the host ++ * which must be placed after the setup on the device side is ++ * all finished. ++ */ ++ mt7623_pad_clk_setup(ds); ++ } ++} ++ ++static int ++mt7530_cpu_port_enable(struct mt7530_priv *priv, ++ int port) ++{ ++ /* Enable Mediatek header mode on the cpu port */ ++ mt7530_write(priv, MT7530_PVC_P(port), ++ PORT_SPEC_TAG); ++ ++ /* Setup the MAC by default for the cpu port */ ++ mt7530_write(priv, MT7530_PMCR_P(port), PMCR_CPUP_LINK); ++ ++ /* Disable auto learning on the cpu port */ ++ mt7530_set(priv, MT7530_PSC_P(port), SA_DIS); ++ ++ /* Unknown unicast frame fordwarding to the cpu port */ ++ mt7530_set(priv, MT7530_MFC, UNU_FFP(BIT(port))); ++ ++ /* CPU port gets connected to all user ports of ++ * the switch ++ */ ++ mt7530_write(priv, MT7530_PCR_P(port), ++ PCR_MATRIX(priv->ds->enabled_port_mask)); ++ ++ return 0; ++} ++ ++static int ++mt7530_port_enable(struct dsa_switch *ds, int port, ++ struct phy_device *phy) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ /* Setup the MAC for the user port */ ++ mt7530_write(priv, MT7530_PMCR_P(port), PMCR_USERP_LINK); ++ ++ /* Allow the user port gets connected to the cpu port and also ++ * restore the port matrix if the port is the member of a certain ++ * bridge. ++ */ ++ priv->ports[port].pm |= PCR_MATRIX(BIT(MT7530_CPU_PORT)); ++ priv->ports[port].enable = true; ++ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, ++ priv->ports[port].pm); ++ mt7530_port_set_status(priv, port, 1); ++ ++ mutex_unlock(&priv->reg_mutex); ++ ++ return 0; ++} ++ ++static void ++mt7530_port_disable(struct dsa_switch *ds, int port, ++ struct phy_device *phy) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ /* Clear up all port matrix which could be restored in the next ++ * enablement for the port. ++ */ ++ priv->ports[port].enable = false; ++ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, ++ PCR_MATRIX_CLR); ++ mt7530_port_set_status(priv, port, 0); ++ ++ mutex_unlock(&priv->reg_mutex); ++} ++ ++static void ++mt7530_stp_state_set(struct dsa_switch *ds, int port, u8 state) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ u32 stp_state; ++ ++ switch (state) { ++ case BR_STATE_DISABLED: ++ stp_state = MT7530_STP_DISABLED; ++ break; ++ case BR_STATE_BLOCKING: ++ stp_state = MT7530_STP_BLOCKING; ++ break; ++ case BR_STATE_LISTENING: ++ stp_state = MT7530_STP_LISTENING; ++ break; ++ case BR_STATE_LEARNING: ++ stp_state = MT7530_STP_LEARNING; ++ break; ++ case BR_STATE_FORWARDING: ++ default: ++ stp_state = MT7530_STP_FORWARDING; ++ break; ++ } ++ ++ mt7530_rmw(priv, MT7530_SSP_P(port), FID_PST_MASK, stp_state); ++} ++ ++static int ++mt7530_port_bridge_join(struct dsa_switch *ds, int port, ++ struct net_device *bridge) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ u32 port_bitmap = BIT(MT7530_CPU_PORT); ++ int i; ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ for (i = 0; i < MT7530_NUM_PORTS; i++) { ++ /* Add this port to the port matrix of the other ports in the ++ * same bridge. If the port is disabled, port matrix is kept ++ * and not being setup until the port becomes enabled. ++ */ ++ if (ds->enabled_port_mask & BIT(i) && i != port) { ++ if (ds->ports[i].bridge_dev != bridge) ++ continue; ++ if (priv->ports[i].enable) ++ mt7530_set(priv, MT7530_PCR_P(i), ++ PCR_MATRIX(BIT(port))); ++ priv->ports[i].pm |= PCR_MATRIX(BIT(port)); ++ ++ port_bitmap |= BIT(i); ++ } ++ } ++ ++ /* Add the all other ports to this port matrix. */ ++ if (priv->ports[port].enable) ++ mt7530_rmw(priv, MT7530_PCR_P(port), ++ PCR_MATRIX_MASK, PCR_MATRIX(port_bitmap)); ++ priv->ports[port].pm |= PCR_MATRIX(port_bitmap); ++ ++ mutex_unlock(&priv->reg_mutex); ++ ++ return 0; ++} ++ ++static void ++mt7530_port_bridge_leave(struct dsa_switch *ds, int port, ++ struct net_device *bridge) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ int i; ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ for (i = 0; i < MT7530_NUM_PORTS; i++) { ++ /* Remove this port from the port matrix of the other ports ++ * in the same bridge. If the port is disabled, port matrix ++ * is kept and not being setup until the port becomes enabled. ++ */ ++ if (ds->enabled_port_mask & BIT(i) && i != port) { ++ if (ds->ports[i].bridge_dev != bridge) ++ continue; ++ if (priv->ports[i].enable) ++ mt7530_clear(priv, MT7530_PCR_P(i), ++ PCR_MATRIX(BIT(port))); ++ priv->ports[i].pm &= ~PCR_MATRIX(BIT(port)); ++ } ++ } ++ ++ /* Set the cpu port to be the only one in the port matrix of ++ * this port. ++ */ ++ if (priv->ports[port].enable) ++ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, ++ PCR_MATRIX(BIT(MT7530_CPU_PORT))); ++ priv->ports[port].pm = PCR_MATRIX(BIT(MT7530_CPU_PORT)); ++ ++ mutex_unlock(&priv->reg_mutex); ++} ++ ++static int ++mt7530_port_fdb_prepare(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_fdb *fdb, ++ struct switchdev_trans *trans) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ int ret; ++ ++ /* Because auto-learned entrie shares the same FDB table. ++ * an entry is reserved with no port_mask to make sure fdb_add ++ * is called while the entry is still available. ++ */ ++ mutex_lock(&priv->reg_mutex); ++ mt7530_fdb_write(priv, fdb->vid, 0, fdb->addr, -1, STATIC_ENT); ++ ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static void ++mt7530_port_fdb_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_fdb *fdb, ++ struct switchdev_trans *trans) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ u8 port_mask = BIT(port); ++ ++ mutex_lock(&priv->reg_mutex); ++ mt7530_fdb_write(priv, fdb->vid, port_mask, fdb->addr, -1, STATIC_ENT); ++ mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); ++ mutex_unlock(&priv->reg_mutex); ++} ++ ++static int ++mt7530_port_fdb_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_fdb *fdb) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ int ret; ++ u8 port_mask = BIT(port); ++ ++ mutex_lock(&priv->reg_mutex); ++ mt7530_fdb_write(priv, fdb->vid, port_mask, fdb->addr, -1, STATIC_EMP); ++ ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int ++mt7530_port_fdb_dump(struct dsa_switch *ds, int port, ++ struct switchdev_obj_port_fdb *fdb, ++ int (*cb)(struct switchdev_obj *obj)) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ struct mt7530_fdb _fdb = { 0 }; ++ int cnt = MT7530_NUM_FDB_RECORDS; ++ int ret = 0; ++ u32 rsp = 0; ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ ret = mt7530_fdb_cmd(priv, MT7530_FDB_START, &rsp); ++ if (ret < 0) ++ goto err; ++ ++ do { ++ if (rsp & ATC_SRCH_HIT) { ++ mt7530_fdb_read(priv, &_fdb); ++ if (_fdb.port_mask & BIT(port)) { ++ ether_addr_copy(fdb->addr, _fdb.mac); ++ fdb->vid = _fdb.vid; ++ fdb->ndm_state = _fdb.noarp ? ++ NUD_NOARP : NUD_REACHABLE; ++ ret = cb(&fdb->obj); ++ if (ret < 0) ++ break; ++ } ++ } ++ } while (--cnt && ++ !(rsp & ATC_SRCH_END) && ++ !mt7530_fdb_cmd(priv, MT7530_FDB_NEXT, &rsp)); ++err: ++ mutex_unlock(&priv->reg_mutex); ++ ++ return 0; ++} ++ ++static enum dsa_tag_protocol ++mtk_get_tag_protocol(struct dsa_switch *ds) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ ++ if (!dsa_is_cpu_port(ds, MT7530_CPU_PORT)) { ++ dev_warn(priv->dev, ++ "port not matched with tagging CPU port\n"); ++ return DSA_TAG_PROTO_NONE; ++ } else { ++ return DSA_TAG_PROTO_MTK; ++ } ++} ++ ++static struct dsa_switch_ops mt7530_switch_ops = { ++ .get_tag_protocol = mtk_get_tag_protocol, ++ .setup = mt7530_setup, ++ .get_strings = mt7530_get_strings, ++ .phy_read = mt7530_phy_read, ++ .phy_write = mt7530_phy_write, ++ .get_ethtool_stats = mt7530_get_ethtool_stats, ++ .get_sset_count = mt7530_get_sset_count, ++ .adjust_link = mt7530_adjust_link, ++ .port_enable = mt7530_port_enable, ++ .port_disable = mt7530_port_disable, ++ .port_stp_state_set = mt7530_stp_state_set, ++ .port_bridge_join = mt7530_port_bridge_join, ++ .port_bridge_leave = mt7530_port_bridge_leave, ++ .port_fdb_prepare = mt7530_port_fdb_prepare, ++ .port_fdb_add = mt7530_port_fdb_add, ++ .port_fdb_del = mt7530_port_fdb_del, ++ .port_fdb_dump = mt7530_port_fdb_dump, ++}; ++ ++static int ++mt7530_probe(struct mdio_device *mdiodev) ++{ ++ struct mt7530_priv *priv; ++ struct device_node *dn; ++ ++ dn = mdiodev->dev.of_node; ++ ++ priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS); ++ if (!priv->ds) ++ return -ENOMEM; ++ ++ /* Use medatek,mcm property to distinguish hardware type that would ++ * casues a little bit differences on power-on sequence. ++ */ ++ priv->mcm = of_property_read_bool(dn, "mediatek,mcm"); ++ if (priv->mcm) { ++ dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n"); ++ ++ priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm"); ++ if (IS_ERR(priv->rstc)) { ++ dev_err(&mdiodev->dev, "Couldn't get our reset line\n"); ++ return PTR_ERR(priv->rstc); ++ } ++ } ++ ++ priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core"); ++ if (IS_ERR(priv->core_pwr)) ++ return PTR_ERR(priv->core_pwr); ++ ++ priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io"); ++ if (IS_ERR(priv->io_pwr)) ++ return PTR_ERR(priv->io_pwr); ++ ++ /* Not MCM that indicates switch works as the remote standalone ++ * integrated circuit so the GPIO pin would be used to complete ++ * the reset, otherwise memory-mapped register accessing used ++ * through syscon provides in the case of MCM. ++ */ ++ if (!priv->mcm) { ++ priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(priv->reset)) { ++ dev_err(&mdiodev->dev, "Couldn't get our reset line\n"); ++ return PTR_ERR(priv->reset); ++ } ++ } ++ ++ priv->bus = mdiodev->bus; ++ priv->dev = &mdiodev->dev; ++ priv->ds->priv = priv; ++ priv->ds->ops = &mt7530_switch_ops; ++ mutex_init(&priv->reg_mutex); ++ lpriv = priv; ++ dev_set_drvdata(&mdiodev->dev, priv); ++ ++ return dsa_register_switch(priv->ds, &mdiodev->dev); ++} ++ ++static void ++mt7530_remove(struct mdio_device *mdiodev) ++{ ++ struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev); ++ int ret = 0; ++ ++ ret = regulator_disable(priv->core_pwr); ++ if (ret < 0) ++ dev_err(priv->dev, ++ "Failed to disable core power: %d\n", ret); ++ ++ ret = regulator_disable(priv->io_pwr); ++ if (ret < 0) ++ dev_err(priv->dev, "Failed to disable io pwr: %d\n", ++ ret); ++ ++ dsa_unregister_switch(priv->ds); ++ mutex_destroy(&priv->reg_mutex); ++} ++ ++static const struct of_device_id mt7530_of_match[] = { ++ { .compatible = "mediatek,mt7530" }, ++ { /* sentinel */ }, ++}; ++ ++static struct mdio_driver mt7530_mdio_driver = { ++ .probe = mt7530_probe, ++ .remove = mt7530_remove, ++ .mdiodrv.driver = { ++ .name = "mt7530", ++ .of_match_table = mt7530_of_match, ++ }, ++}; ++ ++mdio_module_driver(mt7530_mdio_driver); ++ ++MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); ++MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:mediatek-mt7530"); +diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h +new file mode 100644 +index 0000000..05a612f +--- /dev/null ++++ b/drivers/net/dsa/mt7530.h +@@ -0,0 +1,390 @@ ++/* ++ * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * 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. ++ */ ++ ++#ifndef __MT7530_H ++#define __MT7530_H ++ ++#define MT7530_NUM_PORTS 7 ++#define MT7530_CPU_PORT 6 ++#define MT7530_NUM_FDB_RECORDS 2048 ++ ++#define NUM_TRGMII_CTRL 5 ++ ++#define TRGMII_BASE(x) (0x10000 + (x)) ++ ++/* Registers to ethsys access */ ++#define ETHSYS_CLKCFG0 0x2c ++#define ETHSYS_TRGMII_CLK_SEL362_5 BIT(11) ++ ++#define SYSC_REG_RSTCTRL 0x34 ++#define RESET_MCM BIT(2) ++ ++/* Registers to mac forward control for unknown frames */ ++#define MT7530_MFC 0x10 ++#define BC_FFP(x) (((x) & 0xff) << 24) ++#define UNM_FFP(x) (((x) & 0xff) << 16) ++#define UNU_FFP(x) (((x) & 0xff) << 8) ++#define UNU_FFP_MASK UNU_FFP(~0) ++ ++/* Registers for address table access */ ++#define MT7530_ATA1 0x74 ++#define STATIC_EMP 0 ++#define STATIC_ENT 3 ++#define MT7530_ATA2 0x78 ++ ++/* Register for address table write data */ ++#define MT7530_ATWD 0x7c ++ ++/* Register for address table control */ ++#define MT7530_ATC 0x80 ++#define ATC_HASH (((x) & 0xfff) << 16) ++#define ATC_BUSY BIT(15) ++#define ATC_SRCH_END BIT(14) ++#define ATC_SRCH_HIT BIT(13) ++#define ATC_INVALID BIT(12) ++#define ATC_MAT(x) (((x) & 0xf) << 8) ++#define ATC_MAT_MACTAB ATC_MAT(0) ++ ++enum mt7530_fdb_cmd { ++ MT7530_FDB_READ = 0, ++ MT7530_FDB_WRITE = 1, ++ MT7530_FDB_FLUSH = 2, ++ MT7530_FDB_START = 4, ++ MT7530_FDB_NEXT = 5, ++}; ++ ++/* Registers for table search read address */ ++#define MT7530_TSRA1 0x84 ++#define MAC_BYTE_0 24 ++#define MAC_BYTE_1 16 ++#define MAC_BYTE_2 8 ++#define MAC_BYTE_3 0 ++#define MAC_BYTE_MASK 0xff ++ ++#define MT7530_TSRA2 0x88 ++#define MAC_BYTE_4 24 ++#define MAC_BYTE_5 16 ++#define CVID 0 ++#define CVID_MASK 0xfff ++ ++#define MT7530_ATRD 0x8C ++#define AGE_TIMER 24 ++#define AGE_TIMER_MASK 0xff ++#define PORT_MAP 4 ++#define PORT_MAP_MASK 0xff ++#define ENT_STATUS 2 ++#define ENT_STATUS_MASK 0x3 ++ ++/* Register for vlan table control */ ++#define MT7530_VTCR 0x90 ++#define VTCR_BUSY BIT(31) ++#define VTCR_FUNC (((x) & 0xf) << 12) ++#define VTCR_FUNC_RD_VID 0x1 ++#define VTCR_FUNC_WR_VID 0x2 ++#define VTCR_FUNC_INV_VID 0x3 ++#define VTCR_FUNC_VAL_VID 0x4 ++#define VTCR_VID ((x) & 0xfff) ++ ++/* Register for setup vlan and acl write data */ ++#define MT7530_VAWD1 0x94 ++#define PORT_STAG BIT(31) ++#define IVL_MAC BIT(30) ++#define PORT_MEM(x) (((x) & 0xff) << 16) ++#define VALID BIT(1) ++ ++#define MT7530_VAWD2 0x98 ++ ++/* Register for port STP state control */ ++#define MT7530_SSP_P(x) (0x2000 + ((x) * 0x100)) ++#define FID_PST(x) ((x) & 0x3) ++#define FID_PST_MASK FID_PST(0x3) ++ ++enum mt7530_stp_state { ++ MT7530_STP_DISABLED = 0, ++ MT7530_STP_BLOCKING = 1, ++ MT7530_STP_LISTENING = 1, ++ MT7530_STP_LEARNING = 2, ++ MT7530_STP_FORWARDING = 3 ++}; ++ ++/* Register for port control */ ++#define MT7530_PCR_P(x) (0x2004 + ((x) * 0x100)) ++#define PORT_VLAN(x) ((x) & 0x3) ++#define PCR_MATRIX(x) (((x) & 0xff) << 16) ++#define PORT_PRI(x) (((x) & 0x7) << 24) ++#define EG_TAG(x) (((x) & 0x3) << 28) ++#define PCR_MATRIX_MASK PCR_MATRIX(0xff) ++#define PCR_MATRIX_CLR PCR_MATRIX(0) ++ ++/* Register for port security control */ ++#define MT7530_PSC_P(x) (0x200c + ((x) * 0x100)) ++#define SA_DIS BIT(4) ++ ++/* Register for port vlan control */ ++#define MT7530_PVC_P(x) (0x2010 + ((x) * 0x100)) ++#define PORT_SPEC_TAG BIT(5) ++#define VLAN_ATTR(x) (((x) & 0x3) << 6) ++#define STAG_VPID (((x) & 0xffff) << 16) ++ ++/* Register for port port-and-protocol based vlan 1 control */ ++#define MT7530_PPBV1_P(x) (0x2014 + ((x) * 0x100)) ++ ++/* Register for port MAC control register */ ++#define MT7530_PMCR_P(x) (0x3000 + ((x) * 0x100)) ++#define PMCR_IFG_XMIT(x) (((x) & 0x3) << 18) ++#define PMCR_MAC_MODE BIT(16) ++#define PMCR_FORCE_MODE BIT(15) ++#define PMCR_TX_EN BIT(14) ++#define PMCR_RX_EN BIT(13) ++#define PMCR_BACKOFF_EN BIT(9) ++#define PMCR_BACKPR_EN BIT(8) ++#define PMCR_TX_FC_EN BIT(5) ++#define PMCR_RX_FC_EN BIT(4) ++#define PMCR_FORCE_SPEED_1000 BIT(3) ++#define PMCR_FORCE_FDX BIT(1) ++#define PMCR_FORCE_LNK BIT(0) ++#define PMCR_COMMON_LINK (PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | \ ++ PMCR_BACKOFF_EN | PMCR_BACKPR_EN | \ ++ PMCR_TX_EN | PMCR_RX_EN | \ ++ PMCR_TX_FC_EN | PMCR_RX_FC_EN) ++#define PMCR_CPUP_LINK (PMCR_COMMON_LINK | PMCR_FORCE_MODE | \ ++ PMCR_FORCE_SPEED_1000 | \ ++ PMCR_FORCE_FDX | \ ++ PMCR_FORCE_LNK) ++#define PMCR_USERP_LINK PMCR_COMMON_LINK ++#define PMCR_FIXED_LINK (PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | \ ++ PMCR_FORCE_MODE | PMCR_TX_EN | \ ++ PMCR_RX_EN | PMCR_BACKPR_EN | \ ++ PMCR_BACKOFF_EN | \ ++ PMCR_FORCE_SPEED_1000 | \ ++ PMCR_FORCE_FDX | \ ++ PMCR_FORCE_LNK) ++#define PMCR_FIXED_LINK_FC (PMCR_FIXED_LINK | \ ++ PMCR_TX_FC_EN | PMCR_RX_FC_EN) ++ ++#define MT7530_PMSR_P(x) (0x3008 + (x) * 0x100) ++ ++/* Register for MIB */ ++#define MT7530_PORT_MIB_COUNTER(x) (0x4000 + (x) * 0x100) ++#define MT7530_MIB_CCR 0x4fe0 ++#define CCR_MIB_ENABLE BIT(31) ++#define CCR_RX_OCT_CNT_GOOD BIT(7) ++#define CCR_RX_OCT_CNT_BAD BIT(6) ++#define CCR_TX_OCT_CNT_GOOD BIT(5) ++#define CCR_TX_OCT_CNT_BAD BIT(4) ++#define CCR_MIB_FLUSH (CCR_RX_OCT_CNT_GOOD | \ ++ CCR_RX_OCT_CNT_BAD | \ ++ CCR_TX_OCT_CNT_GOOD | \ ++ CCR_TX_OCT_CNT_BAD) ++#define CCR_MIB_ACTIVATE (CCR_MIB_ENABLE | \ ++ CCR_RX_OCT_CNT_GOOD | \ ++ CCR_RX_OCT_CNT_BAD | \ ++ CCR_TX_OCT_CNT_GOOD | \ ++ CCR_TX_OCT_CNT_BAD) ++/* Register for system reset */ ++#define MT7530_SYS_CTRL 0x7000 ++#define SYS_CTRL_PHY_RST BIT(2) ++#define SYS_CTRL_SW_RST BIT(1) ++#define SYS_CTRL_REG_RST BIT(0) ++ ++/* Register for hw trap status */ ++#define MT7530_HWTRAP 0x7800 ++ ++/* Register for hw trap modification */ ++#define MT7530_MHWTRAP 0x7804 ++#define MHWTRAP_MANUAL BIT(16) ++#define MHWTRAP_P5_MAC_SEL BIT(13) ++#define MHWTRAP_P6_DIS BIT(8) ++#define MHWTRAP_P5_RGMII_MODE BIT(7) ++#define MHWTRAP_P5_DIS BIT(6) ++#define MHWTRAP_PHY_ACCESS BIT(5) ++ ++/* Register for TOP signal control */ ++#define MT7530_TOP_SIG_CTRL 0x7808 ++#define TOP_SIG_CTRL_NORMAL (BIT(17) | BIT(16)) ++ ++#define MT7530_IO_DRV_CR 0x7810 ++#define P5_IO_CLK_DRV(x) ((x) & 0x3) ++#define P5_IO_DATA_DRV(x) (((x) & 0x3) << 4) ++ ++#define MT7530_P6ECR 0x7830 ++#define P6_INTF_MODE_MASK 0x3 ++#define P6_INTF_MODE(x) ((x) & 0x3) ++ ++/* Registers for TRGMII on the both side */ ++#define MT7530_TRGMII_RCK_CTRL 0x7a00 ++#define GSW_TRGMII_RCK_CTRL 0x300 ++#define RX_RST BIT(31) ++#define RXC_DQSISEL BIT(30) ++#define DQSI1_TAP_MASK (0x7f << 8) ++#define DQSI0_TAP_MASK 0x7f ++#define DQSI1_TAP(x) (((x) & 0x7f) << 8) ++#define DQSI0_TAP(x) ((x) & 0x7f) ++ ++#define MT7530_TRGMII_RCK_RTT 0x7a04 ++#define GSW_TRGMII_RCK_RTT 0x304 ++#define DQS1_GATE BIT(31) ++#define DQS0_GATE BIT(30) ++ ++#define MT7530_TRGMII_RD(x) (0x7a10 + (x) * 8) ++#define GSW_TRGMII_RD(x) (0x310 + (x) * 8) ++#define BSLIP_EN BIT(31) ++#define EDGE_CHK BIT(30) ++#define RD_TAP_MASK 0x7f ++#define RD_TAP(x) ((x) & 0x7f) ++ ++#define GSW_TRGMII_TXCTRL 0x340 ++#define MT7530_TRGMII_TXCTRL 0x7a40 ++#define TRAIN_TXEN BIT(31) ++#define TXC_INV BIT(30) ++#define TX_RST BIT(28) ++ ++#define MT7530_TRGMII_TD_ODT(i) (0x7a54 + 8 * (i)) ++#define GSW_TRGMII_TD_ODT(i) (0x354 + 8 * (i)) ++#define TD_DM_DRVP(x) ((x) & 0xf) ++#define TD_DM_DRVN(x) (((x) & 0xf) << 4) ++ ++#define GSW_INTF_MODE 0x390 ++#define INTF_MODE_TRGMII BIT(1) ++ ++#define MT7530_TRGMII_TCK_CTRL 0x7a78 ++#define TCK_TAP(x) (((x) & 0xf) << 8) ++ ++#define MT7530_P5RGMIIRXCR 0x7b00 ++#define CSR_RGMII_EDGE_ALIGN BIT(8) ++#define CSR_RGMII_RXC_0DEG_CFG(x) ((x) & 0xf) ++ ++#define MT7530_P5RGMIITXCR 0x7b04 ++#define CSR_RGMII_TXC_CFG(x) ((x) & 0x1f) ++ ++#define MT7530_CREV 0x7ffc ++#define CHIP_NAME_SHIFT 16 ++#define MT7530_ID 0x7530 ++ ++/* Registers for core PLL access through mmd indirect */ ++#define CORE_PLL_GROUP2 0x401 ++#define RG_SYSPLL_EN_NORMAL BIT(15) ++#define RG_SYSPLL_VODEN BIT(14) ++#define RG_SYSPLL_LF BIT(13) ++#define RG_SYSPLL_RST_DLY(x) (((x) & 0x3) << 12) ++#define RG_SYSPLL_LVROD_EN BIT(10) ++#define RG_SYSPLL_PREDIV(x) (((x) & 0x3) << 8) ++#define RG_SYSPLL_POSDIV(x) (((x) & 0x3) << 5) ++#define RG_SYSPLL_FBKSEL BIT(4) ++#define RT_SYSPLL_EN_AFE_OLT BIT(0) ++ ++#define CORE_PLL_GROUP4 0x403 ++#define RG_SYSPLL_DDSFBK_EN BIT(12) ++#define RG_SYSPLL_BIAS_EN BIT(11) ++#define RG_SYSPLL_BIAS_LPF_EN BIT(10) ++ ++#define CORE_PLL_GROUP5 0x404 ++#define RG_LCDDS_PCW_NCPO1(x) ((x) & 0xffff) ++ ++#define CORE_PLL_GROUP6 0x405 ++#define RG_LCDDS_PCW_NCPO0(x) ((x) & 0xffff) ++ ++#define CORE_PLL_GROUP7 0x406 ++#define RG_LCDDS_PWDB BIT(15) ++#define RG_LCDDS_ISO_EN BIT(13) ++#define RG_LCCDS_C(x) (((x) & 0x7) << 4) ++#define RG_LCDDS_PCW_NCPO_CHG BIT(3) ++ ++#define CORE_PLL_GROUP10 0x409 ++#define RG_LCDDS_SSC_DELTA(x) ((x) & 0xfff) ++ ++#define CORE_PLL_GROUP11 0x40a ++#define RG_LCDDS_SSC_DELTA1(x) ((x) & 0xfff) ++ ++#define CORE_GSWPLL_GRP1 0x40d ++#define RG_GSWPLL_PREDIV(x) (((x) & 0x3) << 14) ++#define RG_GSWPLL_POSDIV_200M(x) (((x) & 0x3) << 12) ++#define RG_GSWPLL_EN_PRE BIT(11) ++#define RG_GSWPLL_FBKSEL BIT(10) ++#define RG_GSWPLL_BP BIT(9) ++#define RG_GSWPLL_BR BIT(8) ++#define RG_GSWPLL_FBKDIV_200M(x) ((x) & 0xff) ++ ++#define CORE_GSWPLL_GRP2 0x40e ++#define RG_GSWPLL_POSDIV_500M(x) (((x) & 0x3) << 8) ++#define RG_GSWPLL_FBKDIV_500M(x) ((x) & 0xff) ++ ++#define CORE_TRGMII_GSW_CLK_CG 0x410 ++#define REG_GSWCK_EN BIT(0) ++#define REG_TRGMIICK_EN BIT(1) ++ ++#define MIB_DESC(_s, _o, _n) \ ++ { \ ++ .size = (_s), \ ++ .offset = (_o), \ ++ .name = (_n), \ ++ } ++ ++struct mt7530_mib_desc { ++ unsigned int size; ++ unsigned int offset; ++ const char *name; ++}; ++ ++struct mt7530_fdb { ++ u16 vid; ++ u8 port_mask; ++ u8 aging; ++ u8 mac[6]; ++ bool noarp; ++}; ++ ++struct mt7530_port { ++ bool enable; ++ u32 pm; ++}; ++ ++/* struct mt7530_priv - This is the main data structure for holding the state ++ * of the driver ++ * @dev: The device pointer ++ * @ds: The pointer to the dsa core structure ++ * @bus: The bus used for the device and built-in PHY ++ * @rstc: The pointer to reset control used by MCM ++ * @ethernet: The regmap used for access TRGMII-based registers ++ * @core_pwr: The power supplied into the core ++ * @io_pwr: The power supplied into the I/O ++ * @reset: The descriptor for GPIO line tied to its reset pin ++ * @mcm: Flag for distinguishing if standalone IC or module ++ * coupling ++ * @ports: Holding the state among ports ++ * @reg_mutex: The lock for protecting among process accessing ++ * registers ++ */ ++struct mt7530_priv { ++ struct device *dev; ++ struct dsa_switch *ds; ++ struct mii_bus *bus; ++ struct reset_control *rstc; ++ struct regmap *ethernet; ++ struct regulator *core_pwr; ++ struct regulator *io_pwr; ++ struct gpio_desc *reset; ++ bool mcm; ++ ++ struct mt7530_port ports[MT7530_NUM_PORTS]; ++ /* protect among processes for registers access*/ ++ struct mutex reg_mutex; ++}; ++ ++struct mt7530_hw_stats { ++ const char *string; ++ u16 reg; ++ u8 sizeof_stat; ++}; ++ ++#endif /* __MT7530_H */ |