openwrt/target/linux/mediatek/patches-4.4/0089-net-mediatek-add-gsw-mt7530-driver.patch

2378 lines
67 KiB
Diff
Raw Normal View History

From 4c23129f65d9bac10d9cf88c45fced58597400e6 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Mon, 11 Apr 2016 03:11:54 +0200
Subject: [PATCH 89/91] net: mediatek add gsw/mt7530 driver
Signed-off-by: John Crispin <blogic@openwrt.org>
---
drivers/net/ethernet/mediatek/Makefile | 2 +-
drivers/net/ethernet/mediatek/gsw_mt7620.h | 251 +++++++
drivers/net/ethernet/mediatek/gsw_mt7623.c | 1081 +++++++++++++++++++++++++++
drivers/net/ethernet/mediatek/mt7530.c | 808 ++++++++++++++++++++
drivers/net/ethernet/mediatek/mt7530.h | 20 +
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 59 +-
drivers/net/ethernet/mediatek/mtk_eth_soc.h | 4 +
7 files changed, 2195 insertions(+), 30 deletions(-)
create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7620.h
create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7623.c
create mode 100644 drivers/net/ethernet/mediatek/mt7530.c
create mode 100644 drivers/net/ethernet/mediatek/mt7530.h
diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile
index aa3f1c8..82001c4 100644
--- a/drivers/net/ethernet/mediatek/Makefile
+++ b/drivers/net/ethernet/mediatek/Makefile
@@ -2,4 +2,4 @@
# Makefile for the Mediatek SoCs built-in ethernet macs
#
-obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth_soc.o
+obj-$(CONFIG_NET_MEDIATEK_SOC) += mt7530.o gsw_mt7623.o mtk_eth_soc.o
diff --git a/drivers/net/ethernet/mediatek/gsw_mt7620.h b/drivers/net/ethernet/mediatek/gsw_mt7620.h
new file mode 100644
index 0000000..6fca8f2
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/gsw_mt7620.h
@@ -0,0 +1,251 @@
+/* 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; version 2 of the License
+ *
+ * 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) 2009-2016 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
+ */
+
+#ifndef _RALINK_GSW_MT7620_H__
+#define _RALINK_GSW_MT7620_H__
+
+#define GSW_REG_PHY_TIMEOUT (5 * HZ)
+
+#define MT7620_GSW_REG_PIAC 0x0004
+
+#define GSW_NUM_VLANS 16
+#define GSW_NUM_VIDS 4096
+#define GSW_NUM_PORTS 7
+#define GSW_PORT6 6
+
+#define GSW_MDIO_ACCESS BIT(31)
+#define GSW_MDIO_READ BIT(19)
+#define GSW_MDIO_WRITE BIT(18)
+#define GSW_MDIO_START BIT(16)
+#define GSW_MDIO_ADDR_SHIFT 20
+#define GSW_MDIO_REG_SHIFT 25
+
+#define GSW_REG_PORT_PMCR(x) (0x3000 + (x * 0x100))
+#define GSW_REG_PORT_STATUS(x) (0x3008 + (x * 0x100))
+#define GSW_REG_SMACCR0 0x3fE4
+#define GSW_REG_SMACCR1 0x3fE8
+#define GSW_REG_CKGCR 0x3ff0
+
+#define GSW_REG_IMR 0x7008
+#define GSW_REG_ISR 0x700c
+#define GSW_REG_GPC1 0x7014
+
+#define SYSC_REG_CHIP_REV_ID 0x0c
+#define SYSC_REG_CFG 0x10
+#define SYSC_REG_CFG1 0x14
+#define RST_CTRL_MCM BIT(2)
+#define SYSC_PAD_RGMII2_MDIO 0x58
+#define SYSC_GPIO_MODE 0x60
+
+#define PORT_IRQ_ST_CHG 0x7f
+
+#define MT7621_ESW_PHY_POLLING 0x0000
+#define MT7620_ESW_PHY_POLLING 0x7000
+
+#define PMCR_IPG BIT(18)
+#define PMCR_MAC_MODE BIT(16)
+#define PMCR_FORCE BIT(15)
+#define PMCR_TX_EN BIT(14)
+#define PMCR_RX_EN BIT(13)
+#define PMCR_BACKOFF BIT(9)
+#define PMCR_BACKPRES BIT(8)
+#define PMCR_RX_FC BIT(5)
+#define PMCR_TX_FC BIT(4)
+#define PMCR_SPEED(_x) (_x << 2)
+#define PMCR_DUPLEX BIT(1)
+#define PMCR_LINK BIT(0)
+
+#define PHY_AN_EN BIT(31)
+#define PHY_PRE_EN BIT(30)
+#define PMY_MDC_CONF(_x) ((_x & 0x3f) << 24)
+
+/* ethernet subsystem config register */
+#define ETHSYS_SYSCFG0 0x14
+/* ethernet subsystem clock register */
+#define ETHSYS_CLKCFG0 0x2c
+#define ETHSYS_TRGMII_CLK_SEL362_5 BIT(11)
+
+/* p5 RGMII wrapper TX clock control register */
+#define MT7530_P5RGMIITXCR 0x7b04
+/* p5 RGMII wrapper RX clock control register */
+#define MT7530_P5RGMIIRXCR 0x7b00
+/* TRGMII TDX ODT registers */
+#define MT7530_TRGMII_TD0_ODT 0x7a54
+#define MT7530_TRGMII_TD1_ODT 0x7a5c
+#define MT7530_TRGMII_TD2_ODT 0x7a64
+#define MT7530_TRGMII_TD3_ODT 0x7a6c
+#define MT7530_TRGMII_TD4_ODT 0x7a74
+#define MT7530_TRGMII_TD5_ODT 0x7a7c
+/* TRGMII TCK ctrl register */
+#define MT7530_TRGMII_TCK_CTRL 0x7a78
+/* TRGMII Tx ctrl register */
+#define MT7530_TRGMII_TXCTRL 0x7a40
+/* port 6 extended control register */
+#define MT7530_P6ECR 0x7830
+/* IO driver control register */
+#define MT7530_IO_DRV_CR 0x7810
+/* top signal control register */
+#define MT7530_TOP_SIG_CTRL 0x7808
+/* modified hwtrap register */
+#define MT7530_MHWTRAP 0x7804
+/* hwtrap status register */
+#define MT7530_HWTRAP 0x7800
+/* status interrupt register */
+#define MT7530_SYS_INT_STS 0x700c
+/* system nterrupt register */
+#define MT7530_SYS_INT_EN 0x7008
+/* system control register */
+#define MT7530_SYS_CTRL 0x7000
+/* port MAC status register */
+#define MT7530_PMSR_P(x) (0x3008 + (x * 0x100))
+/* port MAC control register */
+#define MT7530_PMCR_P(x) (0x3000 + (x * 0x100))
+
+#define MT7621_XTAL_SHIFT 6
+#define MT7621_XTAL_MASK 0x7
+#define MT7621_XTAL_25 6
+#define MT7621_XTAL_40 3
+#define MT7621_MDIO_DRV_MASK (3 << 4)
+#define MT7621_GE1_MODE_MASK (3 << 12)
+
+#define TRGMII_TXCTRL_TXC_INV BIT(30)
+#define P6ECR_INTF_MODE_RGMII BIT(1)
+#define P5RGMIIRXCR_C_ALIGN BIT(8)
+#define P5RGMIIRXCR_DELAY_2 BIT(1)
+#define P5RGMIITXCR_DELAY_2 (BIT(8) | BIT(2))
+
+/* TOP_SIG_CTRL bits */
+#define TOP_SIG_CTRL_NORMAL (BIT(17) | BIT(16))
+
+/* MHWTRAP bits */
+#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)
+
+/* HWTRAP bits */
+#define HWTRAP_XTAL_SHIFT 9
+#define HWTRAP_XTAL_MASK 0x3
+
+/* SYS_CTRL bits */
+#define SYS_CTRL_SW_RST BIT(1)
+#define SYS_CTRL_REG_RST BIT(0)
+
+/* PMCR bits */
+#define PMCR_IFG_XMIT_96 BIT(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_BACK_PRES_EN BIT(9)
+#define PMCR_BACKOFF_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_FIXED_LINK (PMCR_IFG_XMIT_96 | PMCR_MAC_MODE | \
+ PMCR_FORCE_MODE | PMCR_TX_EN | PMCR_RX_EN | \
+ PMCR_BACK_PRES_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)
+
+/* TRGMII control registers */
+#define GSW_INTF_MODE 0x390
+#define GSW_TRGMII_TD0_ODT 0x354
+#define GSW_TRGMII_TD1_ODT 0x35c
+#define GSW_TRGMII_TD2_ODT 0x364
+#define GSW_TRGMII_TD3_ODT 0x36c
+#define GSW_TRGMII_TXCTL_ODT 0x374
+#define GSW_TRGMII_TCK_ODT 0x37c
+#define GSW_TRGMII_RCK_CTRL 0x300
+
+#define INTF_MODE_TRGMII BIT(1)
+#define TRGMII_RCK_CTRL_RX_RST BIT(31)
+
+
+/* possible XTAL speed */
+#define MT7623_XTAL_40 0
+#define MT7623_XTAL_20 1
+#define MT7623_XTAL_25 3
+
+/* GPIO port control registers */
+#define GPIO_OD33_CTRL8 0x4c0
+#define GPIO_BIAS_CTRL 0xed0
+#define GPIO_DRV_SEL10 0xf00
+
+/* on MT7620 the functio of port 4 can be software configured */
+enum {
+ PORT4_EPHY = 0,
+ PORT4_EXT,
+};
+
+/* struct mt7620_gsw - the structure that holds the SoC specific data
+ * @dev: The Device struct
+ * @base: The base address
+ * @piac_offset: The PIAC base may change depending on SoC
+ * @irq: The IRQ we are using
+ * @port4: The port4 mode on MT7620
+ * @autopoll: Is MDIO autopolling enabled
+ * @ethsys: The ethsys register map
+ * @pctl: The pin control register map
+ * @clk_trgpll: The trgmii pll clock
+ */
+struct mt7620_gsw {
+ struct mtk_eth *eth;
+ struct device *dev;
+ void __iomem *base;
+ u32 piac_offset;
+ int irq;
+ int port4;
+ unsigned long int autopoll;
+
+ struct regmap *ethsys;
+ struct regmap *pctl;
+
+ struct clk *clk_trgpll;
+
+ int trgmii_force;
+ bool wllll;
+};
+
+/* switch register I/O wrappers */
+void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg);
+u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg);
+
+/* the callback used by the driver core to bringup the switch */
+int mtk_gsw_init(struct mtk_eth *eth);
+
+/* MDIO access wrappers */
+int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val);
+int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg);
+void mt7620_mdio_link_adjust(struct mtk_eth *eth, int port);
+int mt7620_has_carrier(struct mtk_eth *eth);
+void mt7620_print_link_state(struct mtk_eth *eth, int port, int link,
+ int speed, int duplex);
+void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val);
+u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg);
+void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg);
+
+u32 _mtk_mdio_write(struct mtk_eth *eth, u32 phy_addr,
+ u32 phy_register, u32 write_data);
+u32 _mtk_mdio_read(struct mtk_eth *eth, int phy_addr, int phy_reg);
+void mt7620_handle_carrier(struct mtk_eth *eth);
+
+#endif
diff --git a/drivers/net/ethernet/mediatek/gsw_mt7623.c b/drivers/net/ethernet/mediatek/gsw_mt7623.c
new file mode 100644
index 0000000..28df15a
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/gsw_mt7623.c
@@ -0,0 +1,1081 @@
+/* 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; version 2 of the License
+ *
+ * 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) 2009-2016 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/of_mdio.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mii.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/dma-mapping.h>
+#include <linux/phy.h>
+#include <linux/ethtool.h>
+#include <linux/version.h>
+#include <linux/atomic.h>
+
+#include "mtk_eth_soc.h"
+#include "gsw_mt7620.h"
+#include "mt7530.h"
+
+#define ETHSYS_CLKCFG0 0x2c
+#define ETHSYS_TRGMII_CLK_SEL362_5 BIT(11)
+
+void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val)
+{
+ _mtk_mdio_write(gsw->eth, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
+ _mtk_mdio_write(gsw->eth, 0x1f, (reg >> 2) & 0xf, val & 0xffff);
+ _mtk_mdio_write(gsw->eth, 0x1f, 0x10, val >> 16);
+}
+
+u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg)
+{
+ u16 high, low;
+
+ _mtk_mdio_write(gsw->eth, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
+ low = _mtk_mdio_read(gsw->eth, 0x1f, (reg >> 2) & 0xf);
+ high = _mtk_mdio_read(gsw->eth, 0x1f, 0x10);
+
+ return (high << 16) | (low & 0xffff);
+}
+
+void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg)
+{
+ u32 val = mt7530_mdio_r32(gsw, reg);
+
+ val &= mask;
+ val |= set;
+ mt7530_mdio_w32(gsw, reg, val);
+}
+
+void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg)
+{
+ mtk_w32(gsw->eth, val, reg + 0x10000);
+}
+
+u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg)
+{
+ return mtk_r32(gsw->eth, reg + 0x10000);
+}
+
+void mtk_switch_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, unsigned reg)
+{
+ u32 val = mtk_switch_r32(gsw, reg);
+
+ val &= mask;
+ val |= set;
+
+ mtk_switch_w32(gsw, val, reg);
+}
+
+int mt7623_gsw_config(struct mtk_eth *eth)
+{
+ if (eth->mii_bus && eth->mii_bus->phy_map[0x1f])
+ mt7530_probe(eth->dev, NULL, eth->mii_bus, 1);
+
+ return 0;
+}
+
+static irqreturn_t gsw_interrupt_mt7623(int irq, void *_eth)
+{
+ struct mtk_eth *eth = (struct mtk_eth *)_eth;
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
+ u32 reg, i;
+
+ reg = mt7530_mdio_r32(gsw, 0x700c);
+
+ for (i = 0; i < 5; i++)
+ if (reg & BIT(i)) {
+ unsigned int link;
+
+ link = mt7530_mdio_r32(gsw,
+ 0x3008 + (i * 0x100)) & 0x1;
+
+ if (link)
+ dev_info(gsw->dev,
+ "port %d link up\n", i);
+ else
+ dev_info(gsw->dev,
+ "port %d link down\n", i);
+ }
+
+// mt7620_handle_carrier(eth);
+ mt7530_mdio_w32(gsw, 0x700c, 0x1f);
+
+ return IRQ_HANDLED;
+}
+
+static void wait_loop(struct mt7620_gsw *gsw)
+{
+ int i;
+ int read_data;
+
+ for (i = 0; i < 320; i = i + 1)
+ read_data = mtk_switch_r32(gsw, 0x610);
+}
+
+static void trgmii_calibration_7623(struct mt7620_gsw *gsw)
+{
+
+ unsigned int tap_a[5] = { 0, 0, 0, 0, 0 }; /* minumum delay for all correct */
+ unsigned int tap_b[5] = { 0, 0, 0, 0, 0 }; /* maximum delay for all correct */
+ unsigned int final_tap[5];
+ unsigned int rxc_step_size;
+ unsigned int rxd_step_size;
+ unsigned int read_data;
+ unsigned int tmp;
+ unsigned int rd_wd;
+ int i;
+ unsigned int err_cnt[5];
+ unsigned int init_toggle_data;
+ unsigned int err_flag[5];
+ unsigned int err_total_flag;
+ unsigned int training_word;
+ unsigned int rd_tap;
+ u32 val;
+
+ u32 TRGMII_7623_base;
+ u32 TRGMII_7623_RD_0;
+ u32 TRGMII_RCK_CTRL;
+
+ TRGMII_7623_base = 0x300; /* 0xFB110300 */
+ TRGMII_7623_RD_0 = TRGMII_7623_base + 0x10;
+ TRGMII_RCK_CTRL = TRGMII_7623_base;
+ rxd_step_size = 0x1;
+ rxc_step_size = 0x4;
+ init_toggle_data = 0x00000055;
+ training_word = 0x000000AC;
+
+ /* RX clock gating in MT7623 */
+ mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x04);
+
+ /* Assert RX reset in MT7623 */
+ mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_base + 0x00);
+
+ /* Set TX OE edge in MT7623 */
+ mtk_switch_m32(gsw, 0, 0x00002000, TRGMII_7623_base + 0x78);
+
+ /* Disable RX clock gating in MT7623 */
+ mtk_switch_m32(gsw, 0, 0xC0000000, TRGMII_7623_base + 0x04);
+
+ /* Release RX reset in MT7623 */
+ mtk_switch_m32(gsw, 0x7fffffff, 0, TRGMII_7623_base);
+
+ for (i = 0; i < 5; i++)
+ mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_RD_0 + i * 8);
+
+ pr_err("Enable Training Mode in MT7530\n");
+ read_data = mt7530_mdio_r32(gsw, 0x7A40);
+ read_data |= 0xC0000000;
+ mt7530_mdio_w32(gsw, 0x7A40, read_data); /* Enable Training Mode in MT7530 */
+ err_total_flag = 0;
+ pr_err("Adjust RXC delay in MT7623\n");
+ read_data = 0x0;
+ while (err_total_flag == 0 && read_data != 0x68) {
+ pr_err("2nd Enable EDGE CHK in MT7623\n");
+ /* Enable EDGE CHK in MT7623 */
+ for (i = 0; i < 5; i++)
+ mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
+
+ wait_loop(gsw);
+ err_total_flag = 1;
+ for (i = 0; i < 5; i++) {
+ err_cnt[i] =
+ mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) >> 8;
+ err_cnt[i] &= 0x0000000f;
+ rd_wd = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) >> 16;
+ rd_wd &= 0x000000ff;
+ val = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
+ pr_err("ERR_CNT = %d, RD_WD =%x, TRGMII_7623_RD_0=%x\n",
+ err_cnt[i], rd_wd, val);
+ if (err_cnt[i] != 0) {
+ err_flag[i] = 1;
+ } else if (rd_wd != 0x55) {
+ err_flag[i] = 1;
+ } else {
+ err_flag[i] = 0;
+ }
+ err_total_flag = err_flag[i] & err_total_flag;
+ }
+
+ pr_err("2nd Disable EDGE CHK in MT7623\n");
+ /* Disable EDGE CHK in MT7623 */
+ for (i = 0; i < 5; i++)
+ mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
+ wait_loop(gsw);
+ pr_err("2nd Disable EDGE CHK in MT7623\n");
+ /* Adjust RXC delay */
+ /* RX clock gating in MT7623 */
+ mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x04);
+ read_data = mtk_switch_r32(gsw, TRGMII_7623_base);
+ if (err_total_flag == 0) {
+ tmp = (read_data & 0x0000007f) + rxc_step_size;
+ pr_err(" RXC delay = %d\n", tmp);
+ read_data >>= 8;
+ read_data &= 0xffffff80;
+ read_data |= tmp;
+ read_data <<= 8;
+ read_data &= 0xffffff80;
+ read_data |= tmp;
+ mtk_switch_w32(gsw, read_data, TRGMII_7623_base);
+ } else {
+ tmp = (read_data & 0x0000007f) + 16;
+ pr_err(" RXC delay = %d\n", tmp);
+ read_data >>= 8;
+ read_data &= 0xffffff80;
+ read_data |= tmp;
+ read_data <<= 8;
+ read_data &= 0xffffff80;
+ read_data |= tmp;
+ mtk_switch_w32(gsw, read_data, TRGMII_7623_base);
+ }
+ read_data &= 0x000000ff;
+
+ /* Disable RX clock gating in MT7623 */
+ mtk_switch_m32(gsw, 0, 0xC0000000, TRGMII_7623_base + 0x04);
+ for (i = 0; i < 5; i++)
+ mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_RD_0 + i * 8);
+ }
+
+ /* Read RD_WD MT7623 */
+ for (i = 0; i < 5; i++) {
+ rd_tap = 0;
+ while (err_flag[i] != 0 && rd_tap != 128) {
+ /* Enable EDGE CHK in MT7623 */
+ mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
+ wait_loop(gsw);
+
+ read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
+ err_cnt[i] = (read_data >> 8) & 0x0000000f; /* Read MT7623 Errcnt */
+ rd_wd = (read_data >> 16) & 0x000000ff;
+ if (err_cnt[i] != 0 || rd_wd != 0x55) {
+ err_flag[i] = 1;
+ } else {
+ err_flag[i] = 0;
+ }
+ /* Disable EDGE CHK in MT7623 */
+ mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
+ wait_loop(gsw);
+ if (err_flag[i] != 0) {
+ rd_tap = (read_data & 0x0000007f) + rxd_step_size; /* Add RXD delay in MT7623 */
+ read_data = (read_data & 0xffffff80) | rd_tap;
+ mtk_switch_w32(gsw, read_data,
+ TRGMII_7623_RD_0 + i * 8);
+ tap_a[i] = rd_tap;
+ } else {
+ rd_tap = (read_data & 0x0000007f) + 48;
+ read_data = (read_data & 0xffffff80) | rd_tap;
+ mtk_switch_w32(gsw, read_data,
+ TRGMII_7623_RD_0 + i * 8);
+ }
+
+ }
+ pr_err("MT7623 %dth bit Tap_a = %d\n", i, tap_a[i]);
+ }
+ /* pr_err("Last While Loop\n"); */
+ for (i = 0; i < 5; i++) {
+ while ((err_flag[i] == 0) && (rd_tap != 128)) {
+ read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
+ rd_tap = (read_data & 0x0000007f) + rxd_step_size; /* Add RXD delay in MT7623 */
+ read_data = (read_data & 0xffffff80) | rd_tap;
+ mtk_switch_w32(gsw, read_data, TRGMII_7623_RD_0 + i * 8);
+ /* Enable EDGE CHK in MT7623 */
+ val =
+ mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) | 0x40000000;
+ val &= 0x4fffffff;
+ mtk_switch_w32(gsw, val, TRGMII_7623_RD_0 + i * 8);
+ wait_loop(gsw);
+ read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
+ err_cnt[i] = (read_data >> 8) & 0x0000000f; /* Read MT7623 Errcnt */
+ rd_wd = (read_data >> 16) & 0x000000ff;
+ if (err_cnt[i] != 0 || rd_wd != 0x55) {
+ err_flag[i] = 1;
+ } else {
+ err_flag[i] = 0;
+ }
+
+ /* Disable EDGE CHK in MT7623 */
+ mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
+ wait_loop(gsw);
+
+ }
+
+ tap_b[i] = rd_tap; /* -rxd_step_size; */
+ pr_err("MT7623 %dth bit Tap_b = %d\n", i, tap_b[i]);
+ final_tap[i] = (tap_a[i] + tap_b[i]) / 2; /* Calculate RXD delay = (TAP_A + TAP_B)/2 */
+ read_data = (read_data & 0xffffff80) | final_tap[i];
+ mtk_switch_w32(gsw, read_data, TRGMII_7623_RD_0 + i * 8);
+ }
+
+ read_data = mt7530_mdio_r32(gsw, 0x7A40);
+ read_data &= 0x3fffffff;
+ mt7530_mdio_w32(gsw, 0x7A40, read_data);
+}
+
+static void trgmii_calibration_7530(struct mt7620_gsw *gsw)
+{
+
+ unsigned int tap_a[5] = { 0, 0, 0, 0, 0 };
+ unsigned int tap_b[5] = { 0, 0, 0, 0, 0 };
+ unsigned int final_tap[5];
+ unsigned int rxc_step_size;
+ unsigned int rxd_step_size;
+ unsigned int read_data;
+ unsigned int tmp = 0;
+ int i;
+ unsigned int err_cnt[5];
+ unsigned int rd_wd;
+ unsigned int init_toggle_data;
+ unsigned int err_flag[5];
+ unsigned int err_total_flag;
+ unsigned int training_word;
+ unsigned int rd_tap;
+
+ u32 TRGMII_7623_base;
+ u32 TRGMII_7530_RD_0;
+ u32 TRGMII_RCK_CTRL;
+ u32 TRGMII_7530_base;
+ u32 TRGMII_7530_TX_base;
+ u32 val;
+
+ TRGMII_7623_base = 0x300;
+ TRGMII_7530_base = 0x7A00;
+ TRGMII_7530_RD_0 = TRGMII_7530_base + 0x10;
+ TRGMII_RCK_CTRL = TRGMII_7623_base;
+ rxd_step_size = 0x1;
+ rxc_step_size = 0x8;
+ init_toggle_data = 0x00000055;
+ training_word = 0x000000AC;
+
+ TRGMII_7530_TX_base = TRGMII_7530_base + 0x50;
+
+ /* pr_err("Calibration begin ........\n"); */
+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000;
+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
+ read_data = mt7530_mdio_r32(gsw, 0x7a10);
+ /* pr_err("TRGMII_7530_RD_0 is %x\n", read_data); */
+
+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
+ read_data &= 0x3fffffff;
+ mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data); /* RX clock gating in MT7530 */
+
+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x78);
+ read_data |= 0x00002000;
+ mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x78, read_data); /* Set TX OE edge in MT7530 */
+
+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
+ read_data |= 0x80000000;
+ mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data); /* Assert RX reset in MT7530 */
+
+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
+ read_data &= 0x7fffffff;
+ mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data); /* Release RX reset in MT7530 */
+
+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
+ read_data |= 0xC0000000;
+ mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data); /* Disable RX clock gating in MT7530 */
+
+ /* pr_err("Enable Training Mode in MT7623\n"); */
+ /*Enable Training Mode in MT7623 */
+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000;
+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
+ if (gsw->trgmii_force == 2000) {
+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0xC0000000;
+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
+ } else {
+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000;
+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
+ }
+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x078) & 0xfffff0ff;
+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x078);
+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x50) & 0xfffff0ff;
+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x50);
+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x58) & 0xfffff0ff;
+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x58);
+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x60) & 0xfffff0ff;
+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x60);
+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x68) & 0xfffff0ff;
+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x68);
+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x70) & 0xfffff0ff;
+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x70);
+ val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x78) & 0x00000800;
+ mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x78);
+ err_total_flag = 0;
+ /* pr_err("Adjust RXC delay in MT7530\n"); */
+ read_data = 0x0;
+ while (err_total_flag == 0 && (read_data != 0x68)) {
+ /* pr_err("2nd Enable EDGE CHK in MT7530\n"); */
+ /* Enable EDGE CHK in MT7530 */
+ for (i = 0; i < 5; i++) {
+ read_data =
+ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
+ read_data |= 0x40000000;
+ read_data &= 0x4fffffff;
+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
+ read_data);
+ wait_loop(gsw);
+ /* pr_err("2nd Disable EDGE CHK in MT7530\n"); */
+ err_cnt[i] =
+ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
+ /* pr_err("***** MT7530 %dth bit ERR_CNT =%x\n",i, err_cnt[i]); */
+ /* pr_err("MT7530 %dth bit ERR_CNT =%x\n",i, err_cnt[i]); */
+ err_cnt[i] >>= 8;
+ err_cnt[i] &= 0x0000ff0f;
+ rd_wd = err_cnt[i] >> 8;
+ rd_wd &= 0x000000ff;
+ err_cnt[i] &= 0x0000000f;
+ /* read_data = mt7530_mdio_r32(gsw,0x7a10,&read_data); */
+ if (err_cnt[i] != 0) {
+ err_flag[i] = 1;
+ } else if (rd_wd != 0x55) {
+ err_flag[i] = 1;
+ } else {
+ err_flag[i] = 0;
+ }
+ if (i == 0) {
+ err_total_flag = err_flag[i];
+ } else {
+ err_total_flag = err_flag[i] & err_total_flag;
+ }
+ /* Disable EDGE CHK in MT7530 */
+ read_data =
+ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
+ read_data |= 0x40000000;
+ read_data &= 0x4fffffff;
+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
+ read_data);
+ wait_loop(gsw);
+ }
+ /*Adjust RXC delay */
+ if (err_total_flag == 0) {
+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
+ read_data |= 0x80000000;
+ mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data); /* Assert RX reset in MT7530 */
+
+ read_data =
+ mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
+ read_data &= 0x3fffffff;
+ mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data); /* RX clock gating in MT7530 */
+
+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
+ tmp = read_data;
+ tmp &= 0x0000007f;
+ tmp += rxc_step_size;
+ /* pr_err("Current rxc delay = %d\n", tmp); */
+ read_data &= 0xffffff80;
+ read_data |= tmp;
+ mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data);
+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
+ /* pr_err("Current RXC delay = %x\n", read_data); */
+
+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
+ read_data &= 0x7fffffff;
+ mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data); /* Release RX reset in MT7530 */
+
+ read_data =
+ mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
+ read_data |= 0xc0000000;
+ mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data); /* Disable RX clock gating in MT7530 */
+ pr_err("####### MT7530 RXC delay is %d\n", tmp);
+ }
+ read_data = tmp;
+ }
+ pr_err("Finish RXC Adjustment while loop\n");
+
+ /* pr_err("Read RD_WD MT7530\n"); */
+ /* Read RD_WD MT7530 */
+ for (i = 0; i < 5; i++) {
+ rd_tap = 0;
+ while (err_flag[i] != 0 && rd_tap != 128) {
+ /* Enable EDGE CHK in MT7530 */
+ read_data =
+ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
+ read_data |= 0x40000000;
+ read_data &= 0x4fffffff;
+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
+ read_data);
+ wait_loop(gsw);
+ err_cnt[i] = (read_data >> 8) & 0x0000000f;
+ rd_wd = (read_data >> 16) & 0x000000ff;
+ if (err_cnt[i] != 0 || rd_wd != 0x55) {
+ err_flag[i] = 1;
+ } else {
+ err_flag[i] = 0;
+ }
+ if (err_flag[i] != 0) {
+ rd_tap = (read_data & 0x0000007f) + rxd_step_size; /* Add RXD delay in MT7530 */
+ read_data = (read_data & 0xffffff80) | rd_tap;
+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
+ read_data);
+ tap_a[i] = rd_tap;
+ } else {
+ tap_a[i] = (read_data & 0x0000007f); /* Record the min delay TAP_A */
+ rd_tap = tap_a[i] + 0x4;
+ read_data = (read_data & 0xffffff80) | rd_tap;
+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
+ read_data);
+ }
+
+ /* Disable EDGE CHK in MT7530 */
+ read_data =
+ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
+ read_data |= 0x40000000;
+ read_data &= 0x4fffffff;
+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
+ read_data);
+ wait_loop(gsw);
+
+ }
+ pr_err("MT7530 %dth bit Tap_a = %d\n", i, tap_a[i]);
+ }
+
+ /* pr_err("Last While Loop\n"); */
+ for (i = 0; i < 5; i++) {
+ rd_tap = 0;
+ while (err_flag[i] == 0 && (rd_tap != 128)) {
+ /* Enable EDGE CHK in MT7530 */
+ read_data = mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
+ read_data |= 0x40000000;
+ read_data &= 0x4fffffff;
+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
+ read_data);
+ wait_loop(gsw);
+ err_cnt[i] = (read_data >> 8) & 0x0000000f;
+ rd_wd = (read_data >> 16) & 0x000000ff;
+ if (err_cnt[i] != 0 || rd_wd != 0x55)
+ err_flag[i] = 1;
+ else
+ err_flag[i] = 0;
+
+ if (err_flag[i] == 0 && (rd_tap != 128)) {
+ /* Add RXD delay in MT7530 */
+ rd_tap = (read_data & 0x0000007f) + rxd_step_size;
+ read_data = (read_data & 0xffffff80) | rd_tap;
+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
+ read_data);
+ }
+ /* Disable EDGE CHK in MT7530 */
+ read_data =
+ mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
+ read_data |= 0x40000000;
+ read_data &= 0x4fffffff;
+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
+ read_data);
+ wait_loop(gsw);
+ }
+ tap_b[i] = rd_tap; /* - rxd_step_size; */
+ pr_err("MT7530 %dth bit Tap_b = %d\n", i, tap_b[i]);
+ /* Calculate RXD delay = (TAP_A + TAP_B)/2 */
+ final_tap[i] = (tap_a[i] + tap_b[i]) / 2;
+ /* pr_err("########****** MT7530 %dth bit Final Tap = %d\n", i, final_tap[i]); */
+
+ read_data = (read_data & 0xffffff80) | final_tap[i];
+ mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8, read_data);
+ }
+
+ if (gsw->trgmii_force == 2000)
+ mtk_switch_m32(gsw, 0x7fffffff, 0, TRGMII_7623_base + 0x40);
+ else
+ mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x40);
+
+}
+
+static void mt7530_trgmii_clock_setting(struct mt7620_gsw *gsw, u32 xtal_mode)
+{
+
+ u32 regValue;
+
+ /* TRGMII Clock */
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x410);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x1);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x404);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
+
+ if (xtal_mode == 1) {
+ /* 25MHz */
+ if (gsw->trgmii_force == 2600)
+ /* 325MHz */
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x1a00);
+ else if (gsw->trgmii_force == 2000)
+ /* 250MHz */
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x1400);
+ } else if (xtal_mode == 2) {
+ /* 40MHz */
+ if (gsw->trgmii_force == 2600)
+ /* 325MHz */
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x1040);
+ else if (gsw->trgmii_force == 2000)
+ /* 250MHz */
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x0c80);
+ }
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x405);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x0);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x409);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
+ if (xtal_mode == 1)
+ /* 25MHz */
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x0057);
+ else
+ /* 40MHz */
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x0087);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x40a);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
+ if (xtal_mode == 1)
+ /* 25MHz */
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x0057);
+ else
+ /* 40MHz */
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x0087);
+
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x403);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x1800);
+
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x403);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x1c00);
+
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x401);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0xc020);
+
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x406);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0xa030);
+
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x406);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0xa038);
+
+// udelay(120); /* for MT7623 bring up test */
+
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x410);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x3);
+
+ regValue = mt7530_mdio_r32(gsw, 0x7830);
+ regValue &= 0xFFFFFFFC;
+ regValue |= 0x00000001;
+ mt7530_mdio_w32(gsw, 0x7830, regValue);
+
+ regValue = mt7530_mdio_r32(gsw, 0x7a40);
+ regValue &= ~(0x1 << 30);
+ regValue &= ~(0x1 << 28);
+ mt7530_mdio_w32(gsw, 0x7a40, regValue);
+
+ mt7530_mdio_w32(gsw, 0x7a78, 0x55);
+// udelay(100); /* for mt7623 bring up test */
+
+ mtk_switch_m32(gsw, 0x7fffffff, 0, 0x300);
+
+ trgmii_calibration_7623(gsw);
+ trgmii_calibration_7530(gsw);
+
+ mtk_switch_m32(gsw, 0, 0x80000000, 0x300);
+ mtk_switch_m32(gsw, 0, 0x7fffffff, 0x300);
+
+ /*MT7530 RXC reset */
+ regValue = mt7530_mdio_r32(gsw, 0x7a00);
+ regValue |= (0x1 << 31);
+ mt7530_mdio_w32(gsw, 0x7a00, regValue);
+ mdelay(1);
+ regValue &= ~(0x1 << 31);
+ mt7530_mdio_w32(gsw, 0x7a00, regValue);
+ mdelay(100);
+}
+
+static void mt7623_hw_init(struct mtk_eth *eth, struct mt7620_gsw *gsw, struct device_node *np)
+{
+ u32 i;
+ u32 val;
+ u32 xtal_mode;
+
+ regmap_update_bits(gsw->ethsys, ETHSYS_CLKCFG0,
+ ETHSYS_TRGMII_CLK_SEL362_5,
+ ETHSYS_TRGMII_CLK_SEL362_5);
+
+ /* reset the TRGMII core */
+ mtk_switch_m32(gsw, 0, INTF_MODE_TRGMII, GSW_INTF_MODE);
+ /* Assert MT7623 RXC reset */
+ mtk_switch_m32(gsw, 0, TRGMII_RCK_CTRL_RX_RST, GSW_TRGMII_RCK_CTRL);
+
+ /* Hardware reset Switch */
+ device_reset(eth->dev);
+
+ /* Wait for Switch Reset Completed*/
+ for (i = 0; i < 100; i++) {
+ mdelay(10);
+ if (mt7530_mdio_r32(gsw, MT7530_HWTRAP))
+ break;
+ }
+
+ /* turn off all PHYs */
+ for (i = 0; i <= 4; i++) {
+ val = _mtk_mdio_read(gsw->eth, i, 0x0);
+ val |= BIT(11);
+ _mtk_mdio_write(gsw->eth, i, 0x0, val);
+ }
+
+ /* reset the switch */
+ mt7530_mdio_w32(gsw, MT7530_SYS_CTRL,
+ SYS_CTRL_SW_RST | SYS_CTRL_REG_RST);
+ udelay(100);
+
+ /* GE1, Force 1000M/FD, FC ON */
+ mt7530_mdio_w32(gsw, MT7530_PMCR_P(6), PMCR_FIXED_LINK_FC);
+
+ /* GE2, Force 1000M/FD, FC ON */
+ mt7530_mdio_w32(gsw, MT7530_PMCR_P(5), PMCR_FIXED_LINK_FC);
+
+ /* Enable Port 6, P5 as GMAC5, P5 disable */
+ val = mt7530_mdio_r32(gsw, MT7530_MHWTRAP);
+ if (gsw->eth->mac[0] &&
+ of_phy_is_fixed_link(gsw->eth->mac[0]->of_node))
+ /* Enable Port 6 */
+ val &= ~MHWTRAP_P6_DIS;
+ else
+ /* Disable Port 6 */
+ val |= MHWTRAP_P6_DIS;
+ if (gsw->eth->mac[1] &&
+ of_phy_is_fixed_link(gsw->eth->mac[1]->of_node)) {
+ /* Enable Port 5 */
+ val &= ~MHWTRAP_P5_DIS;
+ /* Port 5 as PHY */
+ val &= ~MHWTRAP_P5_MAC_SEL;
+ } else {
+ /* Disable Port 5 */
+ val |= MHWTRAP_P5_DIS;
+ /* Port 5 as GMAC */
+ val |= MHWTRAP_P5_MAC_SEL;
+ val |= BIT(7);
+ mt7530_mdio_w32(gsw, MT7530_PMCR_P(5), 0x8000);
+ }
+ /* gphy to port 0/4 */
+ if (gsw->wllll)
+ val |= BIT(20);
+ else
+ val &= ~BIT(20);
+
+ /* Set MT7530 phy direct access mode**/
+ val &= ~MHWTRAP_PHY_ACCESS;
+ /* manual override of HW-Trap */
+ val |= MHWTRAP_MANUAL;
+ mt7530_mdio_w32(gsw, MT7530_MHWTRAP, val);
+ dev_info(gsw->dev, "Setting MHWTRAP to 0x%08x\n", val);
+
+ val = mt7530_mdio_r32(gsw, 0x7800);
+ val = (val >> 9) & 0x3;
+ if (val == 0x3) {
+ xtal_mode = 1;
+ /* 25Mhz Xtal - do nothing */
+ } else if (val == 0x2) {
+ /* 40Mhz */
+ xtal_mode = 2;
+
+ /* disable MT7530 core clock */
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x410);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x0);
+
+ /* disable MT7530 PLL */
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x40d);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x2020);
+
+ /* for MT7530 core clock = 500Mhz */
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x40e);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x119);
+
+ /* enable MT7530 PLL */
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x40d);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x2820);
+
+ udelay(20);
+
+ /* enable MT7530 core clock */
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x410);
+ _mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
+ } else {
+ xtal_mode = 3;
+ /* 20Mhz Xtal - TODO */
+ }
+
+ /* RGMII */
+ _mtk_mdio_write(gsw->eth, 0, 14, 0x1);
+
+ /* set MT7530 central align */
+ val = mt7530_mdio_r32(gsw, 0x7830);
+ val &= ~1;
+ val |= 1<<1;
+ mt7530_mdio_w32(gsw, 0x7830, val);
+
+ val = mt7530_mdio_r32(gsw, 0x7a40);
+ val &= ~(1<<30);
+ mt7530_mdio_w32(gsw, 0x7a40, val);
+
+ mt7530_mdio_w32(gsw, 0x7a78, 0x855);
+
+ /* delay setting for 10/1000M */
+ mt7530_mdio_w32(gsw, 0x7b00, 0x104);
+ mt7530_mdio_w32(gsw, 0x7b04, 0x10);
+
+ /* lower Tx Driving */
+ mt7530_mdio_w32(gsw, 0x7a54, 0x88);
+ mt7530_mdio_w32(gsw, 0x7a5c, 0x88);
+ mt7530_mdio_w32(gsw, 0x7a64, 0x88);
+ mt7530_mdio_w32(gsw, 0x7a6c, 0x88);
+ mt7530_mdio_w32(gsw, 0x7a74, 0x88);
+ mt7530_mdio_w32(gsw, 0x7a7c, 0x88);
+ mt7530_mdio_w32(gsw, 0x7810, 0x11);
+
+ /* Set MT7623/MT7683 TX Driving */
+ mtk_switch_w32(gsw, 0x88, 0x354);
+ mtk_switch_w32(gsw, 0x88, 0x35c);
+ mtk_switch_w32(gsw, 0x88, 0x364);
+ mtk_switch_w32(gsw, 0x88, 0x36c);
+ mtk_switch_w32(gsw, 0x88, 0x374);
+ mtk_switch_w32(gsw, 0x88, 0x37c);
+
+ /* Set GE2 driving and slew rate */
+ regmap_write(gsw->pctl, 0xF00, 0xe00);
+ /* set GE2 TDSEL */
+ regmap_write(gsw->pctl, 0x4C0, 0x5);
+ /* set GE2 TUNE */
+ regmap_write(gsw->pctl, 0xED0, 0x0);
+
+ regmap_write(gsw->pctl, 0xb70, 0);
+ regmap_write(gsw->pctl, 0x250, 0xffff);
+ regmap_write(gsw->pctl, 0x260, 0xff);
+ regmap_write(gsw->pctl, 0x380, 0x37);
+ regmap_write(gsw->pctl, 0x390, 0x40);
+
+ mt7530_trgmii_clock_setting(gsw, xtal_mode);
+
+ //LANWANPartition(gsw);
+
+ /* disable EEE */
+ for (i = 0; i <= 4; i++) {
+ _mtk_mdio_write(gsw->eth, i, 13, 0x7);
+ _mtk_mdio_write(gsw->eth, i, 14, 0x3C);
+ _mtk_mdio_write(gsw->eth, i, 13, 0x4007);
+ _mtk_mdio_write(gsw->eth, i, 14, 0x0);
+
+ /* Increase SlvDPSready time */
+ _mtk_mdio_write(gsw->eth, i, 31, 0x52b5);
+ _mtk_mdio_write(gsw->eth, i, 16, 0xafae);
+ _mtk_mdio_write(gsw->eth, i, 18, 0x2f);
+ _mtk_mdio_write(gsw->eth, i, 16, 0x8fae);
+
+ /* Incease post_update_timer */
+ _mtk_mdio_write(gsw->eth, i, 31, 0x3);
+ _mtk_mdio_write(gsw->eth, i, 17, 0x4b);
+
+ /* Adjust 100_mse_threshold */
+ _mtk_mdio_write(gsw->eth, i, 13, 0x1e);
+ _mtk_mdio_write(gsw->eth, i, 14, 0x123);
+ _mtk_mdio_write(gsw->eth, i, 13, 0x401e);
+ _mtk_mdio_write(gsw->eth, i, 14, 0xffff);
+
+ /* Disable mcc */
+ _mtk_mdio_write(gsw->eth, i, 13, 0x1e);
+ _mtk_mdio_write(gsw->eth, i, 14, 0xa6);
+ _mtk_mdio_write(gsw->eth, i, 13, 0x401e);
+ _mtk_mdio_write(gsw->eth, i, 14, 0x300);
+
+ /* Disable HW auto downshift*/
+ _mtk_mdio_write(gsw->eth, i, 31, 0x1);
+ val = _mtk_mdio_read(gsw->eth, i, 0x14);
+ val &= ~(1<<4);
+ _mtk_mdio_write(gsw->eth, i, 0x14, val);
+ }
+
+ /* turn on all PHYs */
+ for (i = 0; i <= 4; i++) {
+ val = _mtk_mdio_read(gsw->eth, i, 0);
+ val &= ~BIT(11);
+ _mtk_mdio_write(gsw->eth, i, 0, val);
+ }
+
+ /* enable irq */
+ mt7530_mdio_m32(gsw, 0, TOP_SIG_CTRL_NORMAL, MT7530_TOP_SIG_CTRL);
+}
+
+static const struct of_device_id mediatek_gsw_match[] = {
+ { .compatible = "mediatek,mt7623-gsw" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mediatek_gsw_match);
+
+int mtk_gsw_init(struct mtk_eth *eth)
+{
+ struct device_node *np = eth->switch_np;
+ struct platform_device *pdev = of_find_device_by_node(np);
+ struct mt7620_gsw *gsw;
+
+ if (!pdev)
+ return -ENODEV;
+
+ if (!of_device_is_compatible(np, mediatek_gsw_match->compatible))
+ return -EINVAL;
+
+ gsw = platform_get_drvdata(pdev);
+ if (!gsw)
+ return -ENODEV;
+ gsw->eth = eth;
+ eth->sw_priv = gsw;
+
+ mt7623_hw_init(eth, gsw, np);
+
+ if (request_threaded_irq(gsw->irq, gsw_interrupt_mt7623, NULL, 0,
+ "gsw", eth))
+ pr_err("fail to request irq\n");
+ mt7530_mdio_w32(gsw, 0x7008, 0x1f);
+
+ return 0;
+}
+
+static int mt7623_gsw_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *pctl;
+ int reset_pin, ret;
+ struct mt7620_gsw *gsw;
+ struct regulator *supply;
+
+ gsw = devm_kzalloc(&pdev->dev, sizeof(struct mt7620_gsw), GFP_KERNEL);
+ if (!gsw)
+ return -ENOMEM;
+
+ gsw->dev = &pdev->dev;
+ gsw->trgmii_force = 2000;
+ gsw->irq = irq_of_parse_and_map(np, 0);
+ if (gsw->irq < 0)
+ return -EINVAL;
+
+ gsw->ethsys = syscon_regmap_lookup_by_phandle(np, "mediatek,ethsys");
+ if (IS_ERR(gsw->ethsys))
+ return PTR_ERR(gsw->ethsys);
+
+ reset_pin = of_get_named_gpio(np, "mediatek,reset-pin", 0);
+ if (reset_pin < 0)
+ return reset_pin;
+
+ pctl = of_parse_phandle(np, "mediatek,pctl-regmap", 0);
+ if (IS_ERR(pctl))
+ return PTR_ERR(pctl);
+
+ gsw->pctl = syscon_node_to_regmap(pctl);
+ if (IS_ERR(pctl))
+ return PTR_ERR(pctl);
+
+ ret = devm_gpio_request(&pdev->dev, reset_pin, "mt7530-reset");
+ if (ret)
+ return ret;
+
+ gsw->clk_trgpll = devm_clk_get(&pdev->dev, "trgpll");
+
+ if (IS_ERR(gsw->clk_trgpll))
+ return -ENODEV;
+
+ supply = devm_regulator_get(&pdev->dev, "mt7530");
+ if (IS_ERR(supply))
+ return PTR_ERR(supply);
+
+ regulator_set_voltage(supply, 1000000, 1000000);
+ ret = regulator_enable(supply);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to enable reg-7530: %d\n", ret);
+ return ret;
+ }
+
+ gsw->wllll = of_property_read_bool(np, "mediatek,wllll");
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+
+ ret = clk_set_rate(gsw->clk_trgpll, 500000000);
+ if (ret)
+ return ret;
+
+ clk_prepare_enable(gsw->clk_trgpll);
+
+ gpio_direction_output(reset_pin, 0);
+ udelay(1000);
+ gpio_set_value(reset_pin, 1);
+ mdelay(100);
+
+ platform_set_drvdata(pdev, gsw);
+
+ return 0;
+}
+
+static int mt7623_gsw_remove(struct platform_device *pdev)
+{
+ struct mt7620_gsw *gsw = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(gsw->clk_trgpll);
+
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver gsw_driver = {
+ .probe = mt7623_gsw_probe,
+ .remove = mt7623_gsw_remove,
+ .driver = {
+ .name = "mt7623-gsw",
+ .owner = THIS_MODULE,
+ .of_match_table = mediatek_gsw_match,
+ },
+};
+
+module_platform_driver(gsw_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+MODULE_DESCRIPTION("GBit switch driver for Mediatek MT7623 SoC");
diff --git a/drivers/net/ethernet/mediatek/mt7530.c b/drivers/net/ethernet/mediatek/mt7530.c
new file mode 100644
index 0000000..2e9d280
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/mt7530.c
@@ -0,0 +1,808 @@
+/*
+ * 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>
+ */
+
+#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;
+};
+
+#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
+
+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 *eth = container_of(dev, struct mt7530_priv, swdev);
+ int i;
+
+ memset(eth->port_entries, 0, sizeof(eth->port_entries));
+ memset(eth->vlan_entries, 0, sizeof(eth->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++) {
+ eth->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 *eth = container_of(dev, struct mt7530_priv, swdev);
+
+ val->value.i = eth->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 *eth = container_of(dev, struct mt7530_priv, swdev);
+
+ eth->global_vlan_enable = val->value.i != 0;
+
+ return 0;
+}
+
+static u32
+mt7530_r32(struct mt7530_priv *eth, u32 reg)
+{
+ u32 val;
+ if (eth->bus) {
+ u16 high, low;
+
+ mdiobus_write(eth->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
+ low = mdiobus_read(eth->bus, 0x1f, (reg >> 2) & 0xf);
+ high = mdiobus_read(eth->bus, 0x1f, 0x10);
+
+ return (high << 16) | (low & 0xffff);
+ }
+
+ val = ioread32(eth->base + reg);
+ pr_debug("MT7530 MDIO Read [%04x]=%08x\n", reg, val);
+
+ return val;
+}
+
+static void
+mt7530_w32(struct mt7530_priv *eth, u32 reg, u32 val)
+{
+ if (eth->bus) {
+ mdiobus_write(eth->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
+ mdiobus_write(eth->bus, 0x1f, (reg >> 2) & 0xf, val & 0xffff);
+ mdiobus_write(eth->bus, 0x1f, 0x10, val >> 16);
+ return;
+ }
+
+ pr_debug("MT7530 MDIO Write[%04x]=%08x\n", reg, val);
+ iowrite32(val, eth->base + reg);
+}
+
+static void
+mt7530_vtcr(struct mt7530_priv *eth, u32 cmd, u32 val)
+{
+ int i;
+
+ mt7530_w32(eth, REG_ESW_VLAN_VTCR, BIT(31) | (cmd << 12) | val);
+
+ for (i = 0; i < 20; i++) {
+ u32 val = mt7530_r32(eth, 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 *eth = container_of(dev, struct mt7530_priv, swdev);
+
+ if (port >= MT7530_NUM_PORTS)
+ return -EINVAL;
+
+ *val = mt7530_r32(eth, 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 *eth = 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;
+
+ eth->port_entries[port].pvid = pvid;
+
+ return 0;
+}
+
+static int
+mt7530_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+ struct mt7530_priv *eth = 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(eth, 0, val->port_vlan);
+
+ member = mt7530_r32(eth, REG_ESW_VLAN_VAWD1);
+ member >>= 16;
+ member &= 0xff;
+
+ etags = mt7530_r32(eth, 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 *eth = 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);
+ }
+ eth->vlan_entries[val->port_vlan].member = member;
+ eth->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 *eth = 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;
+
+ eth->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 *eth = container_of(dev, struct mt7530_priv, swdev);
+ u32 vid;
+ int vlan;
+
+ vlan = val->port_vlan;
+
+ vid = mt7530_r32(eth, 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 *eth = container_of(dev, struct mt7530_priv, swdev);
+ int i, j;
+ u8 tag_ports;
+ u8 untag_ports;
+
+ if (!eth->global_vlan_enable) {
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
+ mt7530_w32(eth, REG_ESW_PORT_PCR(i), 0x00ff0000);
+
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
+ mt7530_w32(eth, REG_ESW_PORT_PVC(i), 0x810000c0);
+
+ return 0;
+ }
+
+ /* set all ports as security mode */
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
+ mt7530_w32(eth, 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 = eth->vlan_entries[i].member;
+ u8 etags = eth->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(eth, REG_ESW_PORT_PVC(i), pvc_mode);
+ }
+
+ for (i = 0; i < MT7530_NUM_VLANS; i++) {
+ u16 vid = eth->vlan_entries[i].vid;
+ u8 member = eth->vlan_entries[i].member;
+ u8 etags = eth->vlan_entries[i].etags;
+ u32 val;
+
+ /* vid of vlan */
+ val = mt7530_r32(eth, REG_ESW_VLAN_VTIM(i));
+ if (i % 2 == 0) {
+ val &= 0xfff000;
+ val |= vid;
+ } else {
+ val &= 0xfff;
+ val |= (vid << 12);
+ }
+ mt7530_w32(eth, REG_ESW_VLAN_VTIM(i), val);
+
+ /* vlan port membership */
+ if (member)
+ mt7530_w32(eth, 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(eth, 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(eth, REG_ESW_VLAN_VAWD2, val);
+
+ /* write to vlan table */
+ mt7530_vtcr(eth, 1, i);
+ }
+
+ /* Port Default PVID */
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
+ u32 val;
+ val = mt7530_r32(eth, REG_ESW_PORT_PPBV1(i));
+ val &= ~0xfff;
+ val |= eth->port_entries[i].pvid;
+ mt7530_w32(eth, 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 *eth = container_of(dev, struct mt7530_priv, swdev);
+ u32 speed, pmsr;
+
+ if (port < 0 || port >= MT7530_NUM_PORTS)
+ return -EINVAL;
+
+ pmsr = mt7530_r32(eth, 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 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,
+ },
+};
+
+static u64 get_mib_counter(struct mt7530_priv *eth, int i, int port)
+{
+ unsigned int port_base;
+ u64 t;
+
+ port_base = MT7621_MIB_COUNTER_BASE +
+ MT7621_MIB_COUNTER_PORT_OFFSET * port;
+
+ t = mt7530_r32(eth, port_base + mt7621_mibs[i].offset);
+ if (mt7621_mibs[i].size == 2) {
+ u64 hi;
+
+ hi = mt7530_r32(eth, port_base + mt7621_mibs[i].offset + 4);
+ t |= hi << 32;
+ }
+
+ return t;
+}
+
+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 *eth = 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 < sizeof(mt7621_mibs) / sizeof(*mt7621_mibs); ++i) {
+ u64 counter;
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%-11s: ", mt7621_mibs[i].name);
+ counter = get_mib_counter(eth, 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 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[] = {
+};
+
+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_MACH_MT7623)) {
+ swdev->alias = "mt7623";
+ swdev->name = "mt7623";
+ } 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) || IS_ENABLED(CONFIG_MACH_MT7623))
+ 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;
+ }
+
+ mt7530_reset_switch(swdev);
+
+ 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) || IS_ENABLED(CONFIG_MACH_MT7623)) && 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;
+}
diff --git a/drivers/net/ethernet/mediatek/mt7530.h b/drivers/net/ethernet/mediatek/mt7530.h
new file mode 100644
index 0000000..1fc8c62
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/mt7530.h
@@ -0,0 +1,20 @@
+/*
+ * 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>
+ */
+
+#ifndef _MT7530_H__
+#define _MT7530_H__
+
+int mt7530_probe(struct device *dev, void __iomem *base, struct mii_bus *bus, int vlan);
+
+#endif
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index b5e364c..eee4324a 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -24,6 +24,9 @@
#include "mtk_eth_soc.h"
+/* the callback used by the driver core to bringup the switch */
+int mtk_gsw_init(struct mtk_eth *eth);
+
static int mtk_msg_level = -1;
module_param_named(msg_level, mtk_msg_level, int, 0);
MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)");
@@ -69,7 +72,7 @@ static int mtk_mdio_busy_wait(struct mtk_eth *eth)
return 0;
if (time_after(jiffies, t_start + PHY_IAC_TIMEOUT))
break;
- usleep_range(10, 20);
+// usleep_range(10, 20);
}
dev_err(eth->dev, "mdio: MDIO timeout\n");
@@ -1407,15 +1410,6 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
reset_control_deassert(eth->rstc);
usleep_range(10, 20);
- /* Set GE2 driving and slew rate */
- regmap_write(eth->pctl, GPIO_DRV_SEL10, 0xa00);
-
- /* set GE2 TDSEL */
- regmap_write(eth->pctl, GPIO_OD33_CTRL8, 0x5);
-
- /* set GE2 TUNE */
- regmap_write(eth->pctl, GPIO_BIAS_CTRL, 0x0);
-
/* GE1, Force 1000M/FD, FC ON */
mtk_w32(eth, MAC_MCR_FIXED_LINK, MTK_MAC_MCR(0));
@@ -1438,6 +1432,8 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
if (err)
return err;
+ mtk_gsw_init(eth);
+
/* disable delay and normal interrupt */
mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
mtk_irq_disable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
@@ -1465,6 +1461,8 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
}
+ mt7623_gsw_config(eth);
+
return 0;
}
@@ -1720,7 +1718,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
{
struct mtk_mac *mac;
const __be32 *_id = of_get_property(np, "reg", NULL);
- int id, err;
+ int id;
if (!_id) {
dev_err(eth->dev, "missing mac id\n");
@@ -1754,8 +1752,8 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
GFP_KERNEL);
if (!mac->hw_stats) {
dev_err(eth->dev, "failed to allocate counter memory\n");
- err = -ENOMEM;
- goto free_netdev;
+ free_netdev(eth->netdev[id]);
+ return -ENOMEM;
}
spin_lock_init(&mac->hw_stats->stats_lock);
mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET;
@@ -1769,21 +1767,9 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
eth->netdev[id]->features |= MTK_HW_FEATURES;
eth->netdev[id]->ethtool_ops = &mtk_ethtool_ops;
- err = register_netdev(eth->netdev[id]);
- if (err) {
- dev_err(eth->dev, "error bringing up device\n");
- goto free_netdev;
- }
eth->netdev[id]->irq = eth->irq[0];
- netif_info(eth, probe, eth->netdev[id],
- "mediatek frame engine at 0x%08lx, irq %d\n",
- eth->netdev[id]->base_addr, eth->irq[0]);
return 0;
-
-free_netdev:
- free_netdev(eth->netdev[id]);
- return err;
}
static int mtk_probe(struct platform_device *pdev)
@@ -1851,14 +1837,13 @@ static int mtk_probe(struct platform_device *pdev)
clk_prepare_enable(eth->clk_gp1);
clk_prepare_enable(eth->clk_gp2);
+ eth->switch_np = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,switch", 0);
+
eth->dev = &pdev->dev;
eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE);
INIT_WORK(&eth->pending_work, mtk_pending_work);
- err = mtk_hw_init(eth);
- if (err)
- return err;
-
for_each_child_of_node(pdev->dev.of_node, mac_np) {
if (!of_device_is_compatible(mac_np,
"mediatek,eth-mac"))
@@ -1872,6 +1857,22 @@ static int mtk_probe(struct platform_device *pdev)
goto err_free_dev;
}
+ err = mtk_hw_init(eth);
+ if (err)
+ return err;
+
+ for (i = 0; i < MTK_MAX_DEVS; i++) {
+ if (!eth->netdev[i])
+ continue;
+ err = register_netdev(eth->netdev[i]);
+ if (err)
+ dev_err(eth->dev, "error bringing up device\n");
+ else
+ netif_info(eth, probe, eth->netdev[i],
+ "mediatek frame engine at 0x%08lx, irq %d\n",
+ eth->netdev[i]->base_addr, eth->irq[0]);
+ }
+
/* we run 2 devices on the same DMA ring so we need a dummy device
* for NAPI to work
*/
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index bf158f8..5093518 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -406,6 +406,8 @@ struct mtk_eth {
struct mii_bus *mii_bus;
struct work_struct pending_work;
+ struct device_node *switch_np;
+ void *sw_priv;
};
/* struct mtk_mac - the structure that holds the info about the MACs of the
@@ -433,4 +435,6 @@ void mtk_stats_update_mac(struct mtk_mac *mac);
void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
+int mt7623_gsw_config(struct mtk_eth *eth);
+
#endif /* MTK_ETH_H */
--
1.7.10.4