mirror of https://github.com/hak5/openwrt.git
2323 lines
65 KiB
Diff
2323 lines
65 KiB
Diff
From 6b8a7257e7bcb56782c3f8048311670fe6a80209 Mon Sep 17 00:00:00 2001
|
|
From: John Crispin <blogic@openwrt.org>
|
|
Date: Mon, 11 Apr 2016 03:11:54 +0200
|
|
Subject: [PATCH 101/102] 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 | 1084 +++++++++++++++++++++++++++
|
|
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 | 5 +
|
|
7 files changed, 2199 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
|
|
|
|
--- 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
|
|
--- /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@nbd.name>
|
|
+ * 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
|
|
--- /dev/null
|
|
+++ b/drivers/net/ethernet/mediatek/gsw_mt7623.c
|
|
@@ -0,0 +1,1084 @@
|
|
+/* 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@nbd.name>
|
|
+ * 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 && mdiobus_get_phy(eth->mii_bus, 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;
|
|
+
|
|
+ regmap_write(gsw->ethsys, 0x34, 0x800000);
|
|
+ regmap_write(gsw->ethsys, 0x34, 0x0);
|
|
+
|
|
+ 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");
|
|
--- /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;
|
|
+}
|
|
--- /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
|
|
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
|
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
|
@@ -25,6 +25,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)");
|
|
@@ -74,14 +77,14 @@
|
|
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");
|
|
return -1;
|
|
}
|
|
|
|
-static u32 _mtk_mdio_write(struct mtk_eth *eth, u32 phy_addr,
|
|
+u32 _mtk_mdio_write(struct mtk_eth *eth, u32 phy_addr,
|
|
u32 phy_register, u32 write_data)
|
|
{
|
|
if (mtk_mdio_busy_wait(eth))
|
|
@@ -100,7 +103,7 @@
|
|
return 0;
|
|
}
|
|
|
|
-static u32 _mtk_mdio_read(struct mtk_eth *eth, int phy_addr, int phy_reg)
|
|
+u32 _mtk_mdio_read(struct mtk_eth *eth, int phy_addr, int phy_reg)
|
|
{
|
|
u32 d;
|
|
|
|
@@ -155,7 +158,7 @@
|
|
|
|
val = (speed == SPEED_1000) ?
|
|
RCK_CTRL_RGMII_1000 : RCK_CTRL_RGMII_10_100;
|
|
- mtk_w32(eth, val, TRGMII_RCK_CTRL);
|
|
+ mtk_w32(eth, val, _TRGMII_RCK_CTRL);
|
|
|
|
val = (speed == SPEED_1000) ?
|
|
TCK_CTRL_RGMII_1000 : TCK_CTRL_RGMII_10_100;
|
|
@@ -1833,15 +1836,6 @@
|
|
}
|
|
regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val);
|
|
|
|
- /* 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));
|
|
|
|
@@ -1851,6 +1845,8 @@
|
|
/* Enable RX VLan Offloading */
|
|
mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
|
|
|
|
+ mtk_gsw_init(eth);
|
|
+
|
|
/* disable delay and normal interrupt */
|
|
mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
|
|
mtk_w32(eth, 0, MTK_PDMA_DELAY_INT);
|
|
@@ -1879,6 +1875,8 @@
|
|
mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
|
|
}
|
|
|
|
+ mt7623_gsw_config(eth);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -2379,6 +2377,9 @@
|
|
if (!eth)
|
|
return -ENOMEM;
|
|
|
|
+ eth->switch_np = of_parse_phandle(pdev->dev.of_node,
|
|
+ "mediatek,switch", 0);
|
|
+
|
|
eth->dev = &pdev->dev;
|
|
eth->base = devm_ioremap_resource(&pdev->dev, res);
|
|
if (IS_ERR(eth->base))
|
|
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
|
|
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
|
|
@@ -314,7 +314,7 @@
|
|
MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_LINK)
|
|
|
|
/* TRGMII RXC control register */
|
|
-#define TRGMII_RCK_CTRL 0x10300
|
|
+#define _TRGMII_RCK_CTRL 0x10300
|
|
#define DQSI0(x) ((x << 0) & GENMASK(6, 0))
|
|
#define DQSI1(x) ((x << 8) & GENMASK(14, 8))
|
|
#define RXCTL_DMWTLAT(x) ((x << 16) & GENMASK(18, 16))
|
|
@@ -554,6 +554,9 @@
|
|
struct mii_bus *mii_bus;
|
|
struct work_struct pending_work;
|
|
unsigned long state;
|
|
+
|
|
+ struct device_node *switch_np;
|
|
+ void *sw_priv;
|
|
};
|
|
|
|
/* struct mtk_mac - the structure that holds the info about the MACs of the
|
|
@@ -586,4 +589,6 @@
|
|
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 */
|