From 42fa01d00a7d14b4db1ff1e5176469d349e03d8a Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 21 Mar 2016 16:36:22 +0100 Subject: [PATCH 74/78] net: mediatek: out of tree fixes Signed-off-by: John Crispin --- arch/arm/boot/dts/mt7623-evb.dts | 1 - arch/arm/boot/dts/mt7623.dtsi | 40 +- drivers/net/ethernet/mediatek/Makefile | 2 +- drivers/net/ethernet/mediatek/gsw_mt7620.h | 250 +++++++ drivers/net/ethernet/mediatek/gsw_mt7623.c | 1070 +++++++++++++++++++++++++++ drivers/net/ethernet/mediatek/mt7530.c | 808 ++++++++++++++++++++ drivers/net/ethernet/mediatek/mt7530.h | 20 + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 107 ++- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 5 + 9 files changed, 2230 insertions(+), 73 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/arch/arm/boot/dts/mt7623-evb.dts b/arch/arm/boot/dts/mt7623-evb.dts index 5e9381d..bc2b3f1 100644 --- a/arch/arm/boot/dts/mt7623-evb.dts +++ b/arch/arm/boot/dts/mt7623-evb.dts @@ -425,7 +425,6 @@ &usb1 { vusb33-supply = <&mt6323_vusb_reg>; vbus-supply = <&usb_p1_vbus>; -// mediatek,wakeup-src = <1>; status = "okay"; }; diff --git a/arch/arm/boot/dts/mt7623.dtsi b/arch/arm/boot/dts/mt7623.dtsi index c8c802d..f405ec7 100644 --- a/arch/arm/boot/dts/mt7623.dtsi +++ b/arch/arm/boot/dts/mt7623.dtsi @@ -453,25 +453,32 @@ }; ethsys: syscon@1b000000 { - #address-cells = <1>; - #size-cells = <1>; compatible = "mediatek,mt2701-ethsys", "syscon"; reg = <0 0x1b000000 0 0x1000>; + #reset-cells = <1>; #clock-cells = <1>; }; eth: ethernet@1b100000 { compatible = "mediatek,mt7623-eth"; - reg = <0 0x1b100000 0 0x10000>; + reg = <0 0x1b100000 0 0x20000>; - clocks = <&topckgen CLK_TOP_ETHIF_SEL>; - clock-names = "ethif"; + clocks = <&topckgen CLK_TOP_ETHIF_SEL>, + <ðsys CLK_ETHSYS_ESW>, + <ðsys CLK_ETHSYS_GP2>, + <ðsys CLK_ETHSYS_GP1>; + clock-names = "ethif", "esw", "gp2", "gp1"; interrupts = ; power-domains = <&scpsys MT2701_POWER_DOMAIN_ETH>; + resets = <ðsys 6>; + reset-names = "eth"; + mediatek,ethsys = <ðsys>; + mediatek,pctl = <&syscfg_pctl_a>; + mediatek,switch = <&gsw>; #address-cells = <1>; @@ -482,7 +489,7 @@ gmac1: mac@0 { compatible = "mediatek,eth-mac"; reg = <0>; - + status = "disabled"; }; @@ -490,6 +497,7 @@ compatible = "mediatek,eth-mac"; reg = <1>; + phy-handle = <&phy5>; status = "disabled"; }; @@ -497,6 +505,16 @@ #address-cells = <1>; #size-cells = <0>; + phy4: ethernet-phy@4 { + reg = <4>; + phy-mode = "rgmii"; + }; + + phy5: ethernet-phy@5 { + reg = <5>; + phy-mode = "rgmii"; + }; + phy1f: ethernet-phy@1f { reg = <0x1f>; phy-mode = "rgmii"; @@ -506,14 +524,12 @@ gsw: switch@1b100000 { compatible = "mediatek,mt7623-gsw"; - reg = <0 0x1b110000 0 0x300000>; interrupt-parent = <&pio>; interrupts = <168 IRQ_TYPE_EDGE_RISING>; - clocks = <&apmixedsys CLK_APMIXED_TRGPLL>, - <ðsys CLK_ETHSYS_ESW>, - <ðsys CLK_ETHSYS_GP2>, - <ðsys CLK_ETHSYS_GP1>; - clock-names = "trgpll", "esw", "gp2", "gp1"; + resets = <ðsys 2>; + reset-names = "eth"; + clocks = <&apmixedsys CLK_APMIXED_TRGPLL>; + clock-names = "trgpll"; mt7530-supply = <&mt6323_vpa_reg>; mediatek,pctl-regmap = <&syscfg_pctl_a>; mediatek,ethsys = <ðsys>; 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..7013803 --- /dev/null +++ b/drivers/net/ethernet/mediatek/gsw_mt7620.h @@ -0,0 +1,250 @@ +/* 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 + * Copyright (C) 2009-2016 Felix Fietkau + * Copyright (C) 2013-2016 Michael Lee + */ + +#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; +}; + +/* 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..873a525 --- /dev/null +++ b/drivers/net/ethernet/mediatek/gsw_mt7623.c @@ -0,0 +1,1070 @@ +/* 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 + * Copyright (C) 2009-2016 Felix Fietkau + * Copyright (C) 2013-2016 Michael Lee + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + 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); + /* Enable Port 6 */ + val &= ~MHWTRAP_P6_DIS; + /* Enable Port 5 */ + val &= ~MHWTRAP_P5_DIS; + /* Port 5 as GMAC */ + val |= MHWTRAP_P5_MAC_SEL; + /* Port 5 Interface mode */ + val |= MHWTRAP_P5_RGMII_MODE; + /* Set MT7530 phy direct access mode**/ + val &= ~MHWTRAP_PHY_ACCESS; + val |= MHWTRAP_PHY_ACCESS; + /* manual override of HW-Trap */ + val |= MHWTRAP_MANUAL; + mt7530_mdio_w32(gsw, MT7530_MHWTRAP, val); + + val = mt7530_mdio_r32(gsw, 0x7800); + val = (val >> 9) & 0x3; + pr_err("!!%s: Mhz value= %d\n", __func__, val); + 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); + + + 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); + + /* enable autopolling */ + if (gsw->eth->mac[1] && gsw->eth->mac[1]->phy_dev) { + val = mtk_switch_r32(gsw, 0); + val |= (1<<31); + val &= ~(0x1f); + val &= ~(0x1f<<8); + val |= 4; + val |= 5 << 8; + mtk_switch_w32(gsw, val, 0); + + val = _mtk_mdio_read(gsw->eth, 5, 4); + val |= BIT(10); + _mtk_mdio_write(gsw->eth, 5, 4, val); + val = _mtk_mdio_read(gsw->eth, 5, 0); + val |= BIT(9); + _mtk_mdio_write(gsw->eth, 5, 0, val); + } +} + +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); + + request_threaded_irq(gsw->irq, gsw_interrupt_mt7623, NULL, 0, + "gsw", eth); + 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; + } + 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 "); +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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + */ + +#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 2097ae1..ca7e961 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"); @@ -132,36 +135,8 @@ static int mtk_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg) static void mtk_phy_link_adjust(struct net_device *dev) { - struct mtk_mac *mac = netdev_priv(dev); - u32 mcr = MAC_MCR_MAX_RX_1536 | MAC_MCR_IPG_CFG | - MAC_MCR_FORCE_MODE | MAC_MCR_TX_EN | - MAC_MCR_RX_EN | MAC_MCR_BACKOFF_EN | - MAC_MCR_BACKPR_EN; - - switch (mac->phy_dev->speed) { - case SPEED_1000: - mcr |= MAC_MCR_SPEED_1000; - break; - case SPEED_100: - mcr |= MAC_MCR_SPEED_100; - break; - }; - - if (mac->phy_dev->link) - mcr |= MAC_MCR_FORCE_LINK; - - if (mac->phy_dev->duplex) - mcr |= MAC_MCR_FORCE_DPX; - - if (mac->phy_dev->pause) - mcr |= MAC_MCR_FORCE_RX_FC | MAC_MCR_FORCE_TX_FC; - - mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); - - if (mac->phy_dev->link) - netif_carrier_on(dev); - else - netif_carrier_off(dev); + netif_carrier_on(dev); + return; } static int mtk_phy_connect_node(struct mtk_eth *eth, struct mtk_mac *mac, @@ -193,7 +168,7 @@ static int mtk_phy_connect_node(struct mtk_eth *eth, struct mtk_mac *mac, dev_info(eth->dev, "connected mac %d to PHY at %s [uid=%08x, driver=%s]\n", - mac->id, phydev_name(phydev), phydev->phy_id, + mac->id, dev_name(&phydev->dev), phydev->phy_id, phydev->drv->name); mac->phy_dev = phydev; @@ -209,7 +184,7 @@ static int mtk_phy_connect(struct mtk_mac *mac) np = of_parse_phandle(mac->of_node, "phy-handle", 0); if (!np) - return -ENODEV; + return 0; switch (of_get_phy_mode(np)) { case PHY_INTERFACE_MODE_RGMII: @@ -239,7 +214,8 @@ static int mtk_phy_connect(struct mtk_mac *mac) mac->phy_dev->supported &= PHY_BASIC_FEATURES; mac->phy_dev->advertising = mac->phy_dev->supported | ADVERTISED_Autoneg; - phy_start_aneg(mac->phy_dev); + if (mac->phy_dev) + phy_start_aneg(mac->phy_dev); return 0; } @@ -626,7 +602,6 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev, WRITE_ONCE(itxd->txd3, (TX_DMA_SWC | TX_DMA_PLEN0(skb_headlen(skb)) | (!nr_frags * TX_DMA_LS0))); - netdev_sent_queue(dev, skb->len); skb_tx_timestamp(skb); ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2); @@ -906,7 +881,6 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget) for (i = 0; i < MTK_MAC_COUNT; i++) { if (!eth->netdev[i] || !done[i]) continue; - netdev_completed_queue(eth->netdev[i], done[i], bytes[i]); total += done[i]; } @@ -1284,9 +1258,12 @@ static int mtk_open(struct net_device *dev) } atomic_inc(ð->dma_refcnt); - phy_start(mac->phy_dev); + if (mac->phy_dev) + phy_start(mac->phy_dev); netif_start_queue(dev); + netif_carrier_on(dev); + return 0; } @@ -1319,8 +1296,10 @@ static int mtk_stop(struct net_device *dev) struct mtk_mac *mac = netdev_priv(dev); struct mtk_eth *eth = mac->hw; + netif_carrier_off(dev); netif_tx_disable(dev); - phy_stop(mac->phy_dev); + if (mac->phy_dev) + phy_stop(mac->phy_dev); /* only shutdown DMA if this is the last user */ if (!atomic_dec_and_test(ð->dma_refcnt)) @@ -1346,20 +1325,11 @@ 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)); + mtk_w32(eth, 0x0105e33b, MTK_MAC_MCR(0)); - /* GE2, Force 1000M/FD, FC ON */ - mtk_w32(eth, MAC_MCR_FIXED_LINK, MTK_MAC_MCR(1)); + /* GE2, use autopolling */ + mtk_w32(eth, 0x01056300, MTK_MAC_MCR(1)); /* Enable RX VLan Offloading */ mtk_w32(eth, 1, MTK_CDMP_EG_CTRL); @@ -1377,6 +1347,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); @@ -1404,6 +1376,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; } @@ -1433,7 +1407,8 @@ static void mtk_uninit(struct net_device *dev) struct mtk_mac *mac = netdev_priv(dev); struct mtk_eth *eth = mac->hw; - phy_disconnect(mac->phy_dev); + if (mac->phy_dev) + phy_disconnect(mac->phy_dev); mtk_mdio_cleanup(eth); mtk_irq_disable(eth, ~0); free_irq(eth->irq[0], dev); @@ -1445,7 +1420,7 @@ static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct mtk_mac *mac = netdev_priv(dev); - switch (cmd) { + if (mac->phy_dev) switch (cmd) { case SIOCGMIIPHY: case SIOCGMIIREG: case SIOCSMIIREG: @@ -1508,9 +1483,10 @@ static int mtk_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct mtk_mac *mac = netdev_priv(dev); - int err; + int err = -1; - err = phy_read_status(mac->phy_dev); + if (mac->phy_dev) + err = phy_read_status(mac->phy_dev); if (err) return -ENODEV; @@ -1522,11 +1498,16 @@ static int mtk_set_settings(struct net_device *dev, { struct mtk_mac *mac = netdev_priv(dev); - if (cmd->phy_address != mac->phy_dev->mdio.addr) { - mac->phy_dev = mdiobus_get_phy(mac->hw->mii_bus, - cmd->phy_address); - if (!mac->phy_dev) + if (!mac->phy_dev) + return -ENODEV; + + if (cmd->phy_address != mac->phy_dev->addr) { + if (mac->hw->mii_bus->phy_map[cmd->phy_address]) { + mac->phy_dev = + mac->hw->mii_bus->phy_map[cmd->phy_address]; + } else { return -ENODEV; + } } return phy_ethtool_sset(mac->phy_dev, cmd); @@ -1560,6 +1541,9 @@ static int mtk_nway_reset(struct net_device *dev) { struct mtk_mac *mac = netdev_priv(dev); + if (!mac->phy_dev) + return -ENODEV; + return genphy_restart_aneg(mac->phy_dev); } @@ -1568,6 +1552,9 @@ static u32 mtk_get_link(struct net_device *dev) struct mtk_mac *mac = netdev_priv(dev); int err; + if (!mac->phy_dev) + return -ENODEV; + err = genphy_update_link(mac->phy_dev); if (err) return ethtool_op_get_link(dev); @@ -1619,7 +1606,6 @@ static void mtk_get_ethtool_stats(struct net_device *dev, data_src = (u64*)hwstats; data_dst = data; start = u64_stats_fetch_begin_irq(&hwstats->syncp); - for (i = 0; i < ARRAY_SIZE(mtk_ethtool_stats); i++) *data_dst++ = *(data_src + mtk_ethtool_stats[i].offset); } while (u64_stats_fetch_retry_irq(&hwstats->syncp, start)); @@ -1789,6 +1775,9 @@ 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(ð->pending_work, mtk_pending_work); diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index 4cfb40c..bbe0346 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -401,6 +401,9 @@ struct mtk_eth { struct mii_bus *mii_bus; struct work_struct pending_work; struct tasklet_struct tx_clean_tasklet; + + struct device_node *switch_np; + void *sw_priv; }; /* struct mtk_mac - the structure that holds the info about the MACs of the @@ -428,4 +431,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