From 4c23129f65d9bac10d9cf88c45fced58597400e6 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 11 Apr 2016 03:11:54 +0200 Subject: [PATCH 89/91] net: mediatek add gsw/mt7530 driver Signed-off-by: John Crispin --- drivers/net/ethernet/mediatek/Makefile | 2 +- drivers/net/ethernet/mediatek/gsw_mt7620.h | 251 +++++++ drivers/net/ethernet/mediatek/gsw_mt7623.c | 1081 +++++++++++++++++++++++++++ drivers/net/ethernet/mediatek/mt7530.c | 808 ++++++++++++++++++++ drivers/net/ethernet/mediatek/mt7530.h | 20 + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 59 +- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 4 + 7 files changed, 2195 insertions(+), 30 deletions(-) create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7620.h create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7623.c create mode 100644 drivers/net/ethernet/mediatek/mt7530.c create mode 100644 drivers/net/ethernet/mediatek/mt7530.h diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile index aa3f1c8..82001c4 100644 --- a/drivers/net/ethernet/mediatek/Makefile +++ b/drivers/net/ethernet/mediatek/Makefile @@ -2,4 +2,4 @@ # Makefile for the Mediatek SoCs built-in ethernet macs # -obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth_soc.o +obj-$(CONFIG_NET_MEDIATEK_SOC) += mt7530.o gsw_mt7623.o mtk_eth_soc.o diff --git a/drivers/net/ethernet/mediatek/gsw_mt7620.h b/drivers/net/ethernet/mediatek/gsw_mt7620.h new file mode 100644 index 0000000..6fca8f2 --- /dev/null +++ b/drivers/net/ethernet/mediatek/gsw_mt7620.h @@ -0,0 +1,251 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2009-2016 John Crispin + * 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; + bool wllll; +}; + +/* switch register I/O wrappers */ +void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg); +u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg); + +/* the callback used by the driver core to bringup the switch */ +int mtk_gsw_init(struct mtk_eth *eth); + +/* MDIO access wrappers */ +int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val); +int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg); +void mt7620_mdio_link_adjust(struct mtk_eth *eth, int port); +int mt7620_has_carrier(struct mtk_eth *eth); +void mt7620_print_link_state(struct mtk_eth *eth, int port, int link, + int speed, int duplex); +void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val); +u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg); +void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg); + +u32 _mtk_mdio_write(struct mtk_eth *eth, u32 phy_addr, + u32 phy_register, u32 write_data); +u32 _mtk_mdio_read(struct mtk_eth *eth, int phy_addr, int phy_reg); +void mt7620_handle_carrier(struct mtk_eth *eth); + +#endif diff --git a/drivers/net/ethernet/mediatek/gsw_mt7623.c b/drivers/net/ethernet/mediatek/gsw_mt7623.c new file mode 100644 index 0000000..28df15a --- /dev/null +++ b/drivers/net/ethernet/mediatek/gsw_mt7623.c @@ -0,0 +1,1081 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2009-2016 John Crispin + * 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 + +#include "mtk_eth_soc.h" +#include "gsw_mt7620.h" +#include "mt7530.h" + +#define ETHSYS_CLKCFG0 0x2c +#define ETHSYS_TRGMII_CLK_SEL362_5 BIT(11) + +void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val) +{ + _mtk_mdio_write(gsw->eth, 0x1f, 0x1f, (reg >> 6) & 0x3ff); + _mtk_mdio_write(gsw->eth, 0x1f, (reg >> 2) & 0xf, val & 0xffff); + _mtk_mdio_write(gsw->eth, 0x1f, 0x10, val >> 16); +} + +u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg) +{ + u16 high, low; + + _mtk_mdio_write(gsw->eth, 0x1f, 0x1f, (reg >> 6) & 0x3ff); + low = _mtk_mdio_read(gsw->eth, 0x1f, (reg >> 2) & 0xf); + high = _mtk_mdio_read(gsw->eth, 0x1f, 0x10); + + return (high << 16) | (low & 0xffff); +} + +void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg) +{ + u32 val = mt7530_mdio_r32(gsw, reg); + + val &= mask; + val |= set; + mt7530_mdio_w32(gsw, reg, val); +} + +void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg) +{ + mtk_w32(gsw->eth, val, reg + 0x10000); +} + +u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg) +{ + return mtk_r32(gsw->eth, reg + 0x10000); +} + +void mtk_switch_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, unsigned reg) +{ + u32 val = mtk_switch_r32(gsw, reg); + + val &= mask; + val |= set; + + mtk_switch_w32(gsw, val, reg); +} + +int mt7623_gsw_config(struct mtk_eth *eth) +{ + if (eth->mii_bus && eth->mii_bus->phy_map[0x1f]) + mt7530_probe(eth->dev, NULL, eth->mii_bus, 1); + + return 0; +} + +static irqreturn_t gsw_interrupt_mt7623(int irq, void *_eth) +{ + struct mtk_eth *eth = (struct mtk_eth *)_eth; + struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv; + u32 reg, i; + + reg = mt7530_mdio_r32(gsw, 0x700c); + + for (i = 0; i < 5; i++) + if (reg & BIT(i)) { + unsigned int link; + + link = mt7530_mdio_r32(gsw, + 0x3008 + (i * 0x100)) & 0x1; + + if (link) + dev_info(gsw->dev, + "port %d link up\n", i); + else + dev_info(gsw->dev, + "port %d link down\n", i); + } + +// mt7620_handle_carrier(eth); + mt7530_mdio_w32(gsw, 0x700c, 0x1f); + + return IRQ_HANDLED; +} + +static void wait_loop(struct mt7620_gsw *gsw) +{ + int i; + int read_data; + + for (i = 0; i < 320; i = i + 1) + read_data = mtk_switch_r32(gsw, 0x610); +} + +static void trgmii_calibration_7623(struct mt7620_gsw *gsw) +{ + + unsigned int tap_a[5] = { 0, 0, 0, 0, 0 }; /* minumum delay for all correct */ + unsigned int tap_b[5] = { 0, 0, 0, 0, 0 }; /* maximum delay for all correct */ + unsigned int final_tap[5]; + unsigned int rxc_step_size; + unsigned int rxd_step_size; + unsigned int read_data; + unsigned int tmp; + unsigned int rd_wd; + int i; + unsigned int err_cnt[5]; + unsigned int init_toggle_data; + unsigned int err_flag[5]; + unsigned int err_total_flag; + unsigned int training_word; + unsigned int rd_tap; + u32 val; + + u32 TRGMII_7623_base; + u32 TRGMII_7623_RD_0; + u32 TRGMII_RCK_CTRL; + + TRGMII_7623_base = 0x300; /* 0xFB110300 */ + TRGMII_7623_RD_0 = TRGMII_7623_base + 0x10; + TRGMII_RCK_CTRL = TRGMII_7623_base; + rxd_step_size = 0x1; + rxc_step_size = 0x4; + init_toggle_data = 0x00000055; + training_word = 0x000000AC; + + /* RX clock gating in MT7623 */ + mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x04); + + /* Assert RX reset in MT7623 */ + mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_base + 0x00); + + /* Set TX OE edge in MT7623 */ + mtk_switch_m32(gsw, 0, 0x00002000, TRGMII_7623_base + 0x78); + + /* Disable RX clock gating in MT7623 */ + mtk_switch_m32(gsw, 0, 0xC0000000, TRGMII_7623_base + 0x04); + + /* Release RX reset in MT7623 */ + mtk_switch_m32(gsw, 0x7fffffff, 0, TRGMII_7623_base); + + for (i = 0; i < 5; i++) + mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_RD_0 + i * 8); + + pr_err("Enable Training Mode in MT7530\n"); + read_data = mt7530_mdio_r32(gsw, 0x7A40); + read_data |= 0xC0000000; + mt7530_mdio_w32(gsw, 0x7A40, read_data); /* Enable Training Mode in MT7530 */ + err_total_flag = 0; + pr_err("Adjust RXC delay in MT7623\n"); + read_data = 0x0; + while (err_total_flag == 0 && read_data != 0x68) { + pr_err("2nd Enable EDGE CHK in MT7623\n"); + /* Enable EDGE CHK in MT7623 */ + for (i = 0; i < 5; i++) + mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8); + + wait_loop(gsw); + err_total_flag = 1; + for (i = 0; i < 5; i++) { + err_cnt[i] = + mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) >> 8; + err_cnt[i] &= 0x0000000f; + rd_wd = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) >> 16; + rd_wd &= 0x000000ff; + val = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8); + pr_err("ERR_CNT = %d, RD_WD =%x, TRGMII_7623_RD_0=%x\n", + err_cnt[i], rd_wd, val); + if (err_cnt[i] != 0) { + err_flag[i] = 1; + } else if (rd_wd != 0x55) { + err_flag[i] = 1; + } else { + err_flag[i] = 0; + } + err_total_flag = err_flag[i] & err_total_flag; + } + + pr_err("2nd Disable EDGE CHK in MT7623\n"); + /* Disable EDGE CHK in MT7623 */ + for (i = 0; i < 5; i++) + mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8); + wait_loop(gsw); + pr_err("2nd Disable EDGE CHK in MT7623\n"); + /* Adjust RXC delay */ + /* RX clock gating in MT7623 */ + mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x04); + read_data = mtk_switch_r32(gsw, TRGMII_7623_base); + if (err_total_flag == 0) { + tmp = (read_data & 0x0000007f) + rxc_step_size; + pr_err(" RXC delay = %d\n", tmp); + read_data >>= 8; + read_data &= 0xffffff80; + read_data |= tmp; + read_data <<= 8; + read_data &= 0xffffff80; + read_data |= tmp; + mtk_switch_w32(gsw, read_data, TRGMII_7623_base); + } else { + tmp = (read_data & 0x0000007f) + 16; + pr_err(" RXC delay = %d\n", tmp); + read_data >>= 8; + read_data &= 0xffffff80; + read_data |= tmp; + read_data <<= 8; + read_data &= 0xffffff80; + read_data |= tmp; + mtk_switch_w32(gsw, read_data, TRGMII_7623_base); + } + read_data &= 0x000000ff; + + /* Disable RX clock gating in MT7623 */ + mtk_switch_m32(gsw, 0, 0xC0000000, TRGMII_7623_base + 0x04); + for (i = 0; i < 5; i++) + mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_RD_0 + i * 8); + } + + /* Read RD_WD MT7623 */ + for (i = 0; i < 5; i++) { + rd_tap = 0; + while (err_flag[i] != 0 && rd_tap != 128) { + /* Enable EDGE CHK in MT7623 */ + mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8); + wait_loop(gsw); + + read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8); + err_cnt[i] = (read_data >> 8) & 0x0000000f; /* Read MT7623 Errcnt */ + rd_wd = (read_data >> 16) & 0x000000ff; + if (err_cnt[i] != 0 || rd_wd != 0x55) { + err_flag[i] = 1; + } else { + err_flag[i] = 0; + } + /* Disable EDGE CHK in MT7623 */ + mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8); + wait_loop(gsw); + if (err_flag[i] != 0) { + rd_tap = (read_data & 0x0000007f) + rxd_step_size; /* Add RXD delay in MT7623 */ + read_data = (read_data & 0xffffff80) | rd_tap; + mtk_switch_w32(gsw, read_data, + TRGMII_7623_RD_0 + i * 8); + tap_a[i] = rd_tap; + } else { + rd_tap = (read_data & 0x0000007f) + 48; + read_data = (read_data & 0xffffff80) | rd_tap; + mtk_switch_w32(gsw, read_data, + TRGMII_7623_RD_0 + i * 8); + } + + } + pr_err("MT7623 %dth bit Tap_a = %d\n", i, tap_a[i]); + } + /* pr_err("Last While Loop\n"); */ + for (i = 0; i < 5; i++) { + while ((err_flag[i] == 0) && (rd_tap != 128)) { + read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8); + rd_tap = (read_data & 0x0000007f) + rxd_step_size; /* Add RXD delay in MT7623 */ + read_data = (read_data & 0xffffff80) | rd_tap; + mtk_switch_w32(gsw, read_data, TRGMII_7623_RD_0 + i * 8); + /* Enable EDGE CHK in MT7623 */ + val = + mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) | 0x40000000; + val &= 0x4fffffff; + mtk_switch_w32(gsw, val, TRGMII_7623_RD_0 + i * 8); + wait_loop(gsw); + read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8); + err_cnt[i] = (read_data >> 8) & 0x0000000f; /* Read MT7623 Errcnt */ + rd_wd = (read_data >> 16) & 0x000000ff; + if (err_cnt[i] != 0 || rd_wd != 0x55) { + err_flag[i] = 1; + } else { + err_flag[i] = 0; + } + + /* Disable EDGE CHK in MT7623 */ + mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8); + wait_loop(gsw); + + } + + tap_b[i] = rd_tap; /* -rxd_step_size; */ + pr_err("MT7623 %dth bit Tap_b = %d\n", i, tap_b[i]); + final_tap[i] = (tap_a[i] + tap_b[i]) / 2; /* Calculate RXD delay = (TAP_A + TAP_B)/2 */ + read_data = (read_data & 0xffffff80) | final_tap[i]; + mtk_switch_w32(gsw, read_data, TRGMII_7623_RD_0 + i * 8); + } + + read_data = mt7530_mdio_r32(gsw, 0x7A40); + read_data &= 0x3fffffff; + mt7530_mdio_w32(gsw, 0x7A40, read_data); +} + +static void trgmii_calibration_7530(struct mt7620_gsw *gsw) +{ + + unsigned int tap_a[5] = { 0, 0, 0, 0, 0 }; + unsigned int tap_b[5] = { 0, 0, 0, 0, 0 }; + unsigned int final_tap[5]; + unsigned int rxc_step_size; + unsigned int rxd_step_size; + unsigned int read_data; + unsigned int tmp = 0; + int i; + unsigned int err_cnt[5]; + unsigned int rd_wd; + unsigned int init_toggle_data; + unsigned int err_flag[5]; + unsigned int err_total_flag; + unsigned int training_word; + unsigned int rd_tap; + + u32 TRGMII_7623_base; + u32 TRGMII_7530_RD_0; + u32 TRGMII_RCK_CTRL; + u32 TRGMII_7530_base; + u32 TRGMII_7530_TX_base; + u32 val; + + TRGMII_7623_base = 0x300; + TRGMII_7530_base = 0x7A00; + TRGMII_7530_RD_0 = TRGMII_7530_base + 0x10; + TRGMII_RCK_CTRL = TRGMII_7623_base; + rxd_step_size = 0x1; + rxc_step_size = 0x8; + init_toggle_data = 0x00000055; + training_word = 0x000000AC; + + TRGMII_7530_TX_base = TRGMII_7530_base + 0x50; + + /* pr_err("Calibration begin ........\n"); */ + val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000; + mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40); + read_data = mt7530_mdio_r32(gsw, 0x7a10); + /* pr_err("TRGMII_7530_RD_0 is %x\n", read_data); */ + + read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04); + read_data &= 0x3fffffff; + mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data); /* RX clock gating in MT7530 */ + + read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x78); + read_data |= 0x00002000; + mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x78, read_data); /* Set TX OE edge in MT7530 */ + + read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base); + read_data |= 0x80000000; + mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data); /* Assert RX reset in MT7530 */ + + read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base); + read_data &= 0x7fffffff; + mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data); /* Release RX reset in MT7530 */ + + read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04); + read_data |= 0xC0000000; + mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data); /* Disable RX clock gating in MT7530 */ + + /* pr_err("Enable Training Mode in MT7623\n"); */ + /*Enable Training Mode in MT7623 */ + val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000; + mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40); + if (gsw->trgmii_force == 2000) { + val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0xC0000000; + mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40); + } else { + val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000; + mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40); + } + val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x078) & 0xfffff0ff; + mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x078); + val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x50) & 0xfffff0ff; + mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x50); + val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x58) & 0xfffff0ff; + mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x58); + val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x60) & 0xfffff0ff; + mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x60); + val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x68) & 0xfffff0ff; + mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x68); + val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x70) & 0xfffff0ff; + mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x70); + val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x78) & 0x00000800; + mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x78); + err_total_flag = 0; + /* pr_err("Adjust RXC delay in MT7530\n"); */ + read_data = 0x0; + while (err_total_flag == 0 && (read_data != 0x68)) { + /* pr_err("2nd Enable EDGE CHK in MT7530\n"); */ + /* Enable EDGE CHK in MT7530 */ + for (i = 0; i < 5; i++) { + read_data = + mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8); + read_data |= 0x40000000; + read_data &= 0x4fffffff; + mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8, + read_data); + wait_loop(gsw); + /* pr_err("2nd Disable EDGE CHK in MT7530\n"); */ + err_cnt[i] = + mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8); + /* pr_err("***** MT7530 %dth bit ERR_CNT =%x\n",i, err_cnt[i]); */ + /* pr_err("MT7530 %dth bit ERR_CNT =%x\n",i, err_cnt[i]); */ + err_cnt[i] >>= 8; + err_cnt[i] &= 0x0000ff0f; + rd_wd = err_cnt[i] >> 8; + rd_wd &= 0x000000ff; + err_cnt[i] &= 0x0000000f; + /* read_data = mt7530_mdio_r32(gsw,0x7a10,&read_data); */ + if (err_cnt[i] != 0) { + err_flag[i] = 1; + } else if (rd_wd != 0x55) { + err_flag[i] = 1; + } else { + err_flag[i] = 0; + } + if (i == 0) { + err_total_flag = err_flag[i]; + } else { + err_total_flag = err_flag[i] & err_total_flag; + } + /* Disable EDGE CHK in MT7530 */ + read_data = + mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8); + read_data |= 0x40000000; + read_data &= 0x4fffffff; + mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8, + read_data); + wait_loop(gsw); + } + /*Adjust RXC delay */ + if (err_total_flag == 0) { + read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base); + read_data |= 0x80000000; + mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data); /* Assert RX reset in MT7530 */ + + read_data = + mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04); + read_data &= 0x3fffffff; + mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data); /* RX clock gating in MT7530 */ + + read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base); + tmp = read_data; + tmp &= 0x0000007f; + tmp += rxc_step_size; + /* pr_err("Current rxc delay = %d\n", tmp); */ + read_data &= 0xffffff80; + read_data |= tmp; + mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data); + read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base); + /* pr_err("Current RXC delay = %x\n", read_data); */ + + read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base); + read_data &= 0x7fffffff; + mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data); /* Release RX reset in MT7530 */ + + read_data = + mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04); + read_data |= 0xc0000000; + mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data); /* Disable RX clock gating in MT7530 */ + pr_err("####### MT7530 RXC delay is %d\n", tmp); + } + read_data = tmp; + } + pr_err("Finish RXC Adjustment while loop\n"); + + /* pr_err("Read RD_WD MT7530\n"); */ + /* Read RD_WD MT7530 */ + for (i = 0; i < 5; i++) { + rd_tap = 0; + while (err_flag[i] != 0 && rd_tap != 128) { + /* Enable EDGE CHK in MT7530 */ + read_data = + mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8); + read_data |= 0x40000000; + read_data &= 0x4fffffff; + mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8, + read_data); + wait_loop(gsw); + err_cnt[i] = (read_data >> 8) & 0x0000000f; + rd_wd = (read_data >> 16) & 0x000000ff; + if (err_cnt[i] != 0 || rd_wd != 0x55) { + err_flag[i] = 1; + } else { + err_flag[i] = 0; + } + if (err_flag[i] != 0) { + rd_tap = (read_data & 0x0000007f) + rxd_step_size; /* Add RXD delay in MT7530 */ + read_data = (read_data & 0xffffff80) | rd_tap; + mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8, + read_data); + tap_a[i] = rd_tap; + } else { + tap_a[i] = (read_data & 0x0000007f); /* Record the min delay TAP_A */ + rd_tap = tap_a[i] + 0x4; + read_data = (read_data & 0xffffff80) | rd_tap; + mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8, + read_data); + } + + /* Disable EDGE CHK in MT7530 */ + read_data = + mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8); + read_data |= 0x40000000; + read_data &= 0x4fffffff; + mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8, + read_data); + wait_loop(gsw); + + } + pr_err("MT7530 %dth bit Tap_a = %d\n", i, tap_a[i]); + } + + /* pr_err("Last While Loop\n"); */ + for (i = 0; i < 5; i++) { + rd_tap = 0; + while (err_flag[i] == 0 && (rd_tap != 128)) { + /* Enable EDGE CHK in MT7530 */ + read_data = mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8); + read_data |= 0x40000000; + read_data &= 0x4fffffff; + mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8, + read_data); + wait_loop(gsw); + err_cnt[i] = (read_data >> 8) & 0x0000000f; + rd_wd = (read_data >> 16) & 0x000000ff; + if (err_cnt[i] != 0 || rd_wd != 0x55) + err_flag[i] = 1; + else + err_flag[i] = 0; + + if (err_flag[i] == 0 && (rd_tap != 128)) { + /* Add RXD delay in MT7530 */ + rd_tap = (read_data & 0x0000007f) + rxd_step_size; + read_data = (read_data & 0xffffff80) | rd_tap; + mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8, + read_data); + } + /* Disable EDGE CHK in MT7530 */ + read_data = + mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8); + read_data |= 0x40000000; + read_data &= 0x4fffffff; + mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8, + read_data); + wait_loop(gsw); + } + tap_b[i] = rd_tap; /* - rxd_step_size; */ + pr_err("MT7530 %dth bit Tap_b = %d\n", i, tap_b[i]); + /* Calculate RXD delay = (TAP_A + TAP_B)/2 */ + final_tap[i] = (tap_a[i] + tap_b[i]) / 2; + /* pr_err("########****** MT7530 %dth bit Final Tap = %d\n", i, final_tap[i]); */ + + read_data = (read_data & 0xffffff80) | final_tap[i]; + mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8, read_data); + } + + if (gsw->trgmii_force == 2000) + mtk_switch_m32(gsw, 0x7fffffff, 0, TRGMII_7623_base + 0x40); + else + mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x40); + +} + +static void mt7530_trgmii_clock_setting(struct mt7620_gsw *gsw, u32 xtal_mode) +{ + + u32 regValue; + + /* TRGMII Clock */ + _mtk_mdio_write(gsw->eth, 0, 13, 0x1f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x410); + _mtk_mdio_write(gsw->eth, 0, 13, 0x401f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x1); + _mtk_mdio_write(gsw->eth, 0, 13, 0x1f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x404); + _mtk_mdio_write(gsw->eth, 0, 13, 0x401f); + + if (xtal_mode == 1) { + /* 25MHz */ + if (gsw->trgmii_force == 2600) + /* 325MHz */ + _mtk_mdio_write(gsw->eth, 0, 14, 0x1a00); + else if (gsw->trgmii_force == 2000) + /* 250MHz */ + _mtk_mdio_write(gsw->eth, 0, 14, 0x1400); + } else if (xtal_mode == 2) { + /* 40MHz */ + if (gsw->trgmii_force == 2600) + /* 325MHz */ + _mtk_mdio_write(gsw->eth, 0, 14, 0x1040); + else if (gsw->trgmii_force == 2000) + /* 250MHz */ + _mtk_mdio_write(gsw->eth, 0, 14, 0x0c80); + } + _mtk_mdio_write(gsw->eth, 0, 13, 0x1f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x405); + _mtk_mdio_write(gsw->eth, 0, 13, 0x401f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x0); + _mtk_mdio_write(gsw->eth, 0, 13, 0x1f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x409); + _mtk_mdio_write(gsw->eth, 0, 13, 0x401f); + if (xtal_mode == 1) + /* 25MHz */ + _mtk_mdio_write(gsw->eth, 0, 14, 0x0057); + else + /* 40MHz */ + _mtk_mdio_write(gsw->eth, 0, 14, 0x0087); + _mtk_mdio_write(gsw->eth, 0, 13, 0x1f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x40a); + _mtk_mdio_write(gsw->eth, 0, 13, 0x401f); + if (xtal_mode == 1) + /* 25MHz */ + _mtk_mdio_write(gsw->eth, 0, 14, 0x0057); + else + /* 40MHz */ + _mtk_mdio_write(gsw->eth, 0, 14, 0x0087); + + _mtk_mdio_write(gsw->eth, 0, 13, 0x1f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x403); + _mtk_mdio_write(gsw->eth, 0, 13, 0x401f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x1800); + + _mtk_mdio_write(gsw->eth, 0, 13, 0x1f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x403); + _mtk_mdio_write(gsw->eth, 0, 13, 0x401f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x1c00); + + _mtk_mdio_write(gsw->eth, 0, 13, 0x1f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x401); + _mtk_mdio_write(gsw->eth, 0, 13, 0x401f); + _mtk_mdio_write(gsw->eth, 0, 14, 0xc020); + + _mtk_mdio_write(gsw->eth, 0, 13, 0x1f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x406); + _mtk_mdio_write(gsw->eth, 0, 13, 0x401f); + _mtk_mdio_write(gsw->eth, 0, 14, 0xa030); + + _mtk_mdio_write(gsw->eth, 0, 13, 0x1f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x406); + _mtk_mdio_write(gsw->eth, 0, 13, 0x401f); + _mtk_mdio_write(gsw->eth, 0, 14, 0xa038); + +// udelay(120); /* for MT7623 bring up test */ + + _mtk_mdio_write(gsw->eth, 0, 13, 0x1f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x410); + _mtk_mdio_write(gsw->eth, 0, 13, 0x401f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x3); + + regValue = mt7530_mdio_r32(gsw, 0x7830); + regValue &= 0xFFFFFFFC; + regValue |= 0x00000001; + mt7530_mdio_w32(gsw, 0x7830, regValue); + + regValue = mt7530_mdio_r32(gsw, 0x7a40); + regValue &= ~(0x1 << 30); + regValue &= ~(0x1 << 28); + mt7530_mdio_w32(gsw, 0x7a40, regValue); + + mt7530_mdio_w32(gsw, 0x7a78, 0x55); +// udelay(100); /* for mt7623 bring up test */ + + mtk_switch_m32(gsw, 0x7fffffff, 0, 0x300); + + trgmii_calibration_7623(gsw); + trgmii_calibration_7530(gsw); + + mtk_switch_m32(gsw, 0, 0x80000000, 0x300); + mtk_switch_m32(gsw, 0, 0x7fffffff, 0x300); + + /*MT7530 RXC reset */ + regValue = mt7530_mdio_r32(gsw, 0x7a00); + regValue |= (0x1 << 31); + mt7530_mdio_w32(gsw, 0x7a00, regValue); + mdelay(1); + regValue &= ~(0x1 << 31); + mt7530_mdio_w32(gsw, 0x7a00, regValue); + mdelay(100); +} + +static void mt7623_hw_init(struct mtk_eth *eth, struct mt7620_gsw *gsw, struct device_node *np) +{ + u32 i; + u32 val; + u32 xtal_mode; + + regmap_update_bits(gsw->ethsys, ETHSYS_CLKCFG0, + ETHSYS_TRGMII_CLK_SEL362_5, + ETHSYS_TRGMII_CLK_SEL362_5); + + /* reset the TRGMII core */ + mtk_switch_m32(gsw, 0, INTF_MODE_TRGMII, GSW_INTF_MODE); + /* Assert MT7623 RXC reset */ + mtk_switch_m32(gsw, 0, TRGMII_RCK_CTRL_RX_RST, GSW_TRGMII_RCK_CTRL); + + /* Hardware reset Switch */ + device_reset(eth->dev); + + /* Wait for Switch Reset Completed*/ + for (i = 0; i < 100; i++) { + mdelay(10); + if (mt7530_mdio_r32(gsw, MT7530_HWTRAP)) + break; + } + + /* turn off all PHYs */ + for (i = 0; i <= 4; i++) { + val = _mtk_mdio_read(gsw->eth, i, 0x0); + val |= BIT(11); + _mtk_mdio_write(gsw->eth, i, 0x0, val); + } + + /* reset the switch */ + mt7530_mdio_w32(gsw, MT7530_SYS_CTRL, + SYS_CTRL_SW_RST | SYS_CTRL_REG_RST); + udelay(100); + + /* GE1, Force 1000M/FD, FC ON */ + mt7530_mdio_w32(gsw, MT7530_PMCR_P(6), PMCR_FIXED_LINK_FC); + + /* GE2, Force 1000M/FD, FC ON */ + mt7530_mdio_w32(gsw, MT7530_PMCR_P(5), PMCR_FIXED_LINK_FC); + + /* Enable Port 6, P5 as GMAC5, P5 disable */ + val = mt7530_mdio_r32(gsw, MT7530_MHWTRAP); + if (gsw->eth->mac[0] && + of_phy_is_fixed_link(gsw->eth->mac[0]->of_node)) + /* Enable Port 6 */ + val &= ~MHWTRAP_P6_DIS; + else + /* Disable Port 6 */ + val |= MHWTRAP_P6_DIS; + if (gsw->eth->mac[1] && + of_phy_is_fixed_link(gsw->eth->mac[1]->of_node)) { + /* Enable Port 5 */ + val &= ~MHWTRAP_P5_DIS; + /* Port 5 as PHY */ + val &= ~MHWTRAP_P5_MAC_SEL; + } else { + /* Disable Port 5 */ + val |= MHWTRAP_P5_DIS; + /* Port 5 as GMAC */ + val |= MHWTRAP_P5_MAC_SEL; + val |= BIT(7); + mt7530_mdio_w32(gsw, MT7530_PMCR_P(5), 0x8000); + } + /* gphy to port 0/4 */ + if (gsw->wllll) + val |= BIT(20); + else + val &= ~BIT(20); + + /* Set MT7530 phy direct access mode**/ + val &= ~MHWTRAP_PHY_ACCESS; + /* manual override of HW-Trap */ + val |= MHWTRAP_MANUAL; + mt7530_mdio_w32(gsw, MT7530_MHWTRAP, val); + dev_info(gsw->dev, "Setting MHWTRAP to 0x%08x\n", val); + + val = mt7530_mdio_r32(gsw, 0x7800); + val = (val >> 9) & 0x3; + if (val == 0x3) { + xtal_mode = 1; + /* 25Mhz Xtal - do nothing */ + } else if (val == 0x2) { + /* 40Mhz */ + xtal_mode = 2; + + /* disable MT7530 core clock */ + _mtk_mdio_write(gsw->eth, 0, 13, 0x1f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x410); + _mtk_mdio_write(gsw->eth, 0, 13, 0x401f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x0); + + /* disable MT7530 PLL */ + _mtk_mdio_write(gsw->eth, 0, 13, 0x1f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x40d); + _mtk_mdio_write(gsw->eth, 0, 13, 0x401f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x2020); + + /* for MT7530 core clock = 500Mhz */ + _mtk_mdio_write(gsw->eth, 0, 13, 0x1f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x40e); + _mtk_mdio_write(gsw->eth, 0, 13, 0x401f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x119); + + /* enable MT7530 PLL */ + _mtk_mdio_write(gsw->eth, 0, 13, 0x1f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x40d); + _mtk_mdio_write(gsw->eth, 0, 13, 0x401f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x2820); + + udelay(20); + + /* enable MT7530 core clock */ + _mtk_mdio_write(gsw->eth, 0, 13, 0x1f); + _mtk_mdio_write(gsw->eth, 0, 14, 0x410); + _mtk_mdio_write(gsw->eth, 0, 13, 0x401f); + } else { + xtal_mode = 3; + /* 20Mhz Xtal - TODO */ + } + + /* RGMII */ + _mtk_mdio_write(gsw->eth, 0, 14, 0x1); + + /* set MT7530 central align */ + val = mt7530_mdio_r32(gsw, 0x7830); + val &= ~1; + val |= 1<<1; + mt7530_mdio_w32(gsw, 0x7830, val); + + val = mt7530_mdio_r32(gsw, 0x7a40); + val &= ~(1<<30); + mt7530_mdio_w32(gsw, 0x7a40, val); + + mt7530_mdio_w32(gsw, 0x7a78, 0x855); + + /* delay setting for 10/1000M */ + mt7530_mdio_w32(gsw, 0x7b00, 0x104); + mt7530_mdio_w32(gsw, 0x7b04, 0x10); + + /* lower Tx Driving */ + mt7530_mdio_w32(gsw, 0x7a54, 0x88); + mt7530_mdio_w32(gsw, 0x7a5c, 0x88); + mt7530_mdio_w32(gsw, 0x7a64, 0x88); + mt7530_mdio_w32(gsw, 0x7a6c, 0x88); + mt7530_mdio_w32(gsw, 0x7a74, 0x88); + mt7530_mdio_w32(gsw, 0x7a7c, 0x88); + mt7530_mdio_w32(gsw, 0x7810, 0x11); + + /* Set MT7623/MT7683 TX Driving */ + mtk_switch_w32(gsw, 0x88, 0x354); + mtk_switch_w32(gsw, 0x88, 0x35c); + mtk_switch_w32(gsw, 0x88, 0x364); + mtk_switch_w32(gsw, 0x88, 0x36c); + mtk_switch_w32(gsw, 0x88, 0x374); + mtk_switch_w32(gsw, 0x88, 0x37c); + + /* Set GE2 driving and slew rate */ + regmap_write(gsw->pctl, 0xF00, 0xe00); + /* set GE2 TDSEL */ + regmap_write(gsw->pctl, 0x4C0, 0x5); + /* set GE2 TUNE */ + regmap_write(gsw->pctl, 0xED0, 0x0); + + regmap_write(gsw->pctl, 0xb70, 0); + regmap_write(gsw->pctl, 0x250, 0xffff); + regmap_write(gsw->pctl, 0x260, 0xff); + regmap_write(gsw->pctl, 0x380, 0x37); + regmap_write(gsw->pctl, 0x390, 0x40); + + mt7530_trgmii_clock_setting(gsw, xtal_mode); + + //LANWANPartition(gsw); + + /* disable EEE */ + for (i = 0; i <= 4; i++) { + _mtk_mdio_write(gsw->eth, i, 13, 0x7); + _mtk_mdio_write(gsw->eth, i, 14, 0x3C); + _mtk_mdio_write(gsw->eth, i, 13, 0x4007); + _mtk_mdio_write(gsw->eth, i, 14, 0x0); + + /* Increase SlvDPSready time */ + _mtk_mdio_write(gsw->eth, i, 31, 0x52b5); + _mtk_mdio_write(gsw->eth, i, 16, 0xafae); + _mtk_mdio_write(gsw->eth, i, 18, 0x2f); + _mtk_mdio_write(gsw->eth, i, 16, 0x8fae); + + /* Incease post_update_timer */ + _mtk_mdio_write(gsw->eth, i, 31, 0x3); + _mtk_mdio_write(gsw->eth, i, 17, 0x4b); + + /* Adjust 100_mse_threshold */ + _mtk_mdio_write(gsw->eth, i, 13, 0x1e); + _mtk_mdio_write(gsw->eth, i, 14, 0x123); + _mtk_mdio_write(gsw->eth, i, 13, 0x401e); + _mtk_mdio_write(gsw->eth, i, 14, 0xffff); + + /* Disable mcc */ + _mtk_mdio_write(gsw->eth, i, 13, 0x1e); + _mtk_mdio_write(gsw->eth, i, 14, 0xa6); + _mtk_mdio_write(gsw->eth, i, 13, 0x401e); + _mtk_mdio_write(gsw->eth, i, 14, 0x300); + + /* Disable HW auto downshift*/ + _mtk_mdio_write(gsw->eth, i, 31, 0x1); + val = _mtk_mdio_read(gsw->eth, i, 0x14); + val &= ~(1<<4); + _mtk_mdio_write(gsw->eth, i, 0x14, val); + } + + /* turn on all PHYs */ + for (i = 0; i <= 4; i++) { + val = _mtk_mdio_read(gsw->eth, i, 0); + val &= ~BIT(11); + _mtk_mdio_write(gsw->eth, i, 0, val); + } + + /* enable irq */ + mt7530_mdio_m32(gsw, 0, TOP_SIG_CTRL_NORMAL, MT7530_TOP_SIG_CTRL); +} + +static const struct of_device_id mediatek_gsw_match[] = { + { .compatible = "mediatek,mt7623-gsw" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mediatek_gsw_match); + +int mtk_gsw_init(struct mtk_eth *eth) +{ + struct device_node *np = eth->switch_np; + struct platform_device *pdev = of_find_device_by_node(np); + struct mt7620_gsw *gsw; + + if (!pdev) + return -ENODEV; + + if (!of_device_is_compatible(np, mediatek_gsw_match->compatible)) + return -EINVAL; + + gsw = platform_get_drvdata(pdev); + if (!gsw) + return -ENODEV; + gsw->eth = eth; + eth->sw_priv = gsw; + + mt7623_hw_init(eth, gsw, np); + + if (request_threaded_irq(gsw->irq, gsw_interrupt_mt7623, NULL, 0, + "gsw", eth)) + pr_err("fail to request irq\n"); + mt7530_mdio_w32(gsw, 0x7008, 0x1f); + + return 0; +} + +static int mt7623_gsw_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *pctl; + int reset_pin, ret; + struct mt7620_gsw *gsw; + struct regulator *supply; + + gsw = devm_kzalloc(&pdev->dev, sizeof(struct mt7620_gsw), GFP_KERNEL); + if (!gsw) + return -ENOMEM; + + gsw->dev = &pdev->dev; + gsw->trgmii_force = 2000; + gsw->irq = irq_of_parse_and_map(np, 0); + if (gsw->irq < 0) + return -EINVAL; + + gsw->ethsys = syscon_regmap_lookup_by_phandle(np, "mediatek,ethsys"); + if (IS_ERR(gsw->ethsys)) + return PTR_ERR(gsw->ethsys); + + reset_pin = of_get_named_gpio(np, "mediatek,reset-pin", 0); + if (reset_pin < 0) + return reset_pin; + + pctl = of_parse_phandle(np, "mediatek,pctl-regmap", 0); + if (IS_ERR(pctl)) + return PTR_ERR(pctl); + + gsw->pctl = syscon_node_to_regmap(pctl); + if (IS_ERR(pctl)) + return PTR_ERR(pctl); + + ret = devm_gpio_request(&pdev->dev, reset_pin, "mt7530-reset"); + if (ret) + return ret; + + gsw->clk_trgpll = devm_clk_get(&pdev->dev, "trgpll"); + + if (IS_ERR(gsw->clk_trgpll)) + return -ENODEV; + + supply = devm_regulator_get(&pdev->dev, "mt7530"); + if (IS_ERR(supply)) + return PTR_ERR(supply); + + regulator_set_voltage(supply, 1000000, 1000000); + ret = regulator_enable(supply); + if (ret) { + dev_err(&pdev->dev, "Failed to enable reg-7530: %d\n", ret); + return ret; + } + + gsw->wllll = of_property_read_bool(np, "mediatek,wllll"); + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + ret = clk_set_rate(gsw->clk_trgpll, 500000000); + if (ret) + return ret; + + clk_prepare_enable(gsw->clk_trgpll); + + gpio_direction_output(reset_pin, 0); + udelay(1000); + gpio_set_value(reset_pin, 1); + mdelay(100); + + platform_set_drvdata(pdev, gsw); + + return 0; +} + +static int mt7623_gsw_remove(struct platform_device *pdev) +{ + struct mt7620_gsw *gsw = platform_get_drvdata(pdev); + + clk_disable_unprepare(gsw->clk_trgpll); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver gsw_driver = { + .probe = mt7623_gsw_probe, + .remove = mt7623_gsw_remove, + .driver = { + .name = "mt7623-gsw", + .owner = THIS_MODULE, + .of_match_table = mediatek_gsw_match, + }, +}; + +module_platform_driver(gsw_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("John Crispin "); +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 b5e364c..eee4324a 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -24,6 +24,9 @@ #include "mtk_eth_soc.h" +/* the callback used by the driver core to bringup the switch */ +int mtk_gsw_init(struct mtk_eth *eth); + static int mtk_msg_level = -1; module_param_named(msg_level, mtk_msg_level, int, 0); MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)"); @@ -69,7 +72,7 @@ static int mtk_mdio_busy_wait(struct mtk_eth *eth) return 0; if (time_after(jiffies, t_start + PHY_IAC_TIMEOUT)) break; - usleep_range(10, 20); +// usleep_range(10, 20); } dev_err(eth->dev, "mdio: MDIO timeout\n"); @@ -1407,15 +1410,6 @@ static int __init mtk_hw_init(struct mtk_eth *eth) reset_control_deassert(eth->rstc); usleep_range(10, 20); - /* Set GE2 driving and slew rate */ - regmap_write(eth->pctl, GPIO_DRV_SEL10, 0xa00); - - /* set GE2 TDSEL */ - regmap_write(eth->pctl, GPIO_OD33_CTRL8, 0x5); - - /* set GE2 TUNE */ - regmap_write(eth->pctl, GPIO_BIAS_CTRL, 0x0); - /* GE1, Force 1000M/FD, FC ON */ mtk_w32(eth, MAC_MCR_FIXED_LINK, MTK_MAC_MCR(0)); @@ -1438,6 +1432,8 @@ static int __init mtk_hw_init(struct mtk_eth *eth) if (err) return err; + mtk_gsw_init(eth); + /* disable delay and normal interrupt */ mtk_w32(eth, 0, MTK_QDMA_DELAY_INT); mtk_irq_disable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT); @@ -1465,6 +1461,8 @@ static int __init mtk_hw_init(struct mtk_eth *eth) mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i)); } + mt7623_gsw_config(eth); + return 0; } @@ -1720,7 +1718,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) { struct mtk_mac *mac; const __be32 *_id = of_get_property(np, "reg", NULL); - int id, err; + int id; if (!_id) { dev_err(eth->dev, "missing mac id\n"); @@ -1754,8 +1752,8 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) GFP_KERNEL); if (!mac->hw_stats) { dev_err(eth->dev, "failed to allocate counter memory\n"); - err = -ENOMEM; - goto free_netdev; + free_netdev(eth->netdev[id]); + return -ENOMEM; } spin_lock_init(&mac->hw_stats->stats_lock); mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET; @@ -1769,21 +1767,9 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) eth->netdev[id]->features |= MTK_HW_FEATURES; eth->netdev[id]->ethtool_ops = &mtk_ethtool_ops; - err = register_netdev(eth->netdev[id]); - if (err) { - dev_err(eth->dev, "error bringing up device\n"); - goto free_netdev; - } eth->netdev[id]->irq = eth->irq[0]; - netif_info(eth, probe, eth->netdev[id], - "mediatek frame engine at 0x%08lx, irq %d\n", - eth->netdev[id]->base_addr, eth->irq[0]); return 0; - -free_netdev: - free_netdev(eth->netdev[id]); - return err; } static int mtk_probe(struct platform_device *pdev) @@ -1851,14 +1837,13 @@ static int mtk_probe(struct platform_device *pdev) clk_prepare_enable(eth->clk_gp1); clk_prepare_enable(eth->clk_gp2); + eth->switch_np = of_parse_phandle(pdev->dev.of_node, + "mediatek,switch", 0); + eth->dev = &pdev->dev; eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE); INIT_WORK(ð->pending_work, mtk_pending_work); - err = mtk_hw_init(eth); - if (err) - return err; - for_each_child_of_node(pdev->dev.of_node, mac_np) { if (!of_device_is_compatible(mac_np, "mediatek,eth-mac")) @@ -1872,6 +1857,22 @@ static int mtk_probe(struct platform_device *pdev) goto err_free_dev; } + err = mtk_hw_init(eth); + if (err) + return err; + + for (i = 0; i < MTK_MAX_DEVS; i++) { + if (!eth->netdev[i]) + continue; + err = register_netdev(eth->netdev[i]); + if (err) + dev_err(eth->dev, "error bringing up device\n"); + else + netif_info(eth, probe, eth->netdev[i], + "mediatek frame engine at 0x%08lx, irq %d\n", + eth->netdev[i]->base_addr, eth->irq[0]); + } + /* we run 2 devices on the same DMA ring so we need a dummy device * for NAPI to work */ diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index bf158f8..5093518 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -406,6 +406,8 @@ struct mtk_eth { struct mii_bus *mii_bus; struct work_struct pending_work; + struct device_node *switch_np; + void *sw_priv; }; /* struct mtk_mac - the structure that holds the info about the MACs of the @@ -433,4 +435,6 @@ void mtk_stats_update_mac(struct mtk_mac *mac); void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg); u32 mtk_r32(struct mtk_eth *eth, unsigned reg); +int mt7623_gsw_config(struct mtk_eth *eth); + #endif /* MTK_ETH_H */ -- 1.7.10.4