mediatek: remove mt753x driver
It is unused Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
		| @@ -1,3 +0,0 @@ | |||||||
|  |  | ||||||
| config MT753X_GSW |  | ||||||
| 	tristate "Driver for the MediaTek MT753x switch" |  | ||||||
| @@ -1,11 +0,0 @@ | |||||||
| # |  | ||||||
| # Makefile for MediaTek MT753x gigabit switch |  | ||||||
| # |  | ||||||
|  |  | ||||||
| obj-$(CONFIG_MT753X_GSW)	+= mt753x.o |  | ||||||
|  |  | ||||||
| mt753x-$(CONFIG_SWCONFIG)	+= mt753x_swconfig.o |  | ||||||
|  |  | ||||||
| mt753x-y			+= mt753x_mdio.o mt7530.o mt7531.o \ |  | ||||||
| 					mt753x_common.o mt753x_vlan.o \ |  | ||||||
| 					mt753x_nl.o |  | ||||||
| @@ -1,631 +0,0 @@ | |||||||
| // SPDX-License-Identifier: GPL-2.0-only |  | ||||||
| /* |  | ||||||
|  * Copyright (c) 2018 MediaTek Inc. |  | ||||||
|  * Author: Weijie Gao <weijie.gao@mediatek.com> |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include <linux/kernel.h> |  | ||||||
| #include <linux/delay.h> |  | ||||||
|  |  | ||||||
| #include "mt753x.h" |  | ||||||
| #include "mt753x_regs.h" |  | ||||||
|  |  | ||||||
| /* MT7530 registers */ |  | ||||||
|  |  | ||||||
| /* Unique fields of PMCR for MT7530 */ |  | ||||||
| #define FORCE_MODE			BIT(15) |  | ||||||
|  |  | ||||||
| /* Unique fields of GMACCR for MT7530 */ |  | ||||||
| #define VLAN_SUPT_NO_S			14 |  | ||||||
| #define VLAN_SUPT_NO_M			0x1c000 |  | ||||||
| #define LATE_COL_DROP			BIT(13) |  | ||||||
|  |  | ||||||
| /* Unique fields of (M)HWSTRAP for MT7530 */ |  | ||||||
| #define BOND_OPTION			BIT(24) |  | ||||||
| #define P5_PHY0_SEL			BIT(20) |  | ||||||
| #define CHG_TRAP			BIT(16) |  | ||||||
| #define LOOPDET_DIS			BIT(14) |  | ||||||
| #define P5_INTF_SEL_GMAC5		BIT(13) |  | ||||||
| #define SMI_ADDR_S			11 |  | ||||||
| #define SMI_ADDR_M			0x1800 |  | ||||||
| #define XTAL_FSEL_S			9 |  | ||||||
| #define XTAL_FSEL_M			0x600 |  | ||||||
| #define P6_INTF_DIS			BIT(8) |  | ||||||
| #define P5_INTF_MODE_RGMII		BIT(7) |  | ||||||
| #define P5_INTF_DIS_S			BIT(6) |  | ||||||
| #define C_MDIO_BPS_S			BIT(5) |  | ||||||
| #define EEPROM_EN_S			BIT(4) |  | ||||||
|  |  | ||||||
| /* PHY EEE Register bitmap of define */ |  | ||||||
| #define PHY_DEV07			0x07 |  | ||||||
| #define PHY_DEV07_REG_03C		0x3c |  | ||||||
|  |  | ||||||
| /* PHY Extend Register 0x14 bitmap of define */ |  | ||||||
| #define PHY_EXT_REG_14			0x14 |  | ||||||
|  |  | ||||||
| /* Fields of PHY_EXT_REG_14 */ |  | ||||||
| #define PHY_EN_DOWN_SHFIT		BIT(4) |  | ||||||
|  |  | ||||||
| /* PHY Token Ring Register 0x10 bitmap of define */ |  | ||||||
| #define PHY_TR_REG_10			0x10 |  | ||||||
|  |  | ||||||
| /* PHY Token Ring Register 0x12 bitmap of define */ |  | ||||||
| #define PHY_TR_REG_12			0x12 |  | ||||||
|  |  | ||||||
| /* PHY LPI PCS/DSP Control Register bitmap of define */ |  | ||||||
| #define PHY_LPI_REG_11			0x11 |  | ||||||
|  |  | ||||||
| /* PHY DEV 0x1e Register bitmap of define */ |  | ||||||
| #define PHY_DEV1E			0x1e |  | ||||||
| #define PHY_DEV1E_REG_123		0x123 |  | ||||||
| #define PHY_DEV1E_REG_A6		0xa6 |  | ||||||
|  |  | ||||||
| /* Values of XTAL_FSEL */ |  | ||||||
| #define XTAL_20MHZ			1 |  | ||||||
| #define XTAL_40MHZ			2 |  | ||||||
| #define XTAL_25MHZ			3 |  | ||||||
|  |  | ||||||
| #define P6ECR				0x7830 |  | ||||||
| #define P6_INTF_MODE_TRGMII		BIT(0) |  | ||||||
|  |  | ||||||
| #define TRGMII_TXCTRL			0x7a40 |  | ||||||
| #define TRAIN_TXEN			BIT(31) |  | ||||||
| #define TXC_INV				BIT(30) |  | ||||||
| #define TX_DOEO				BIT(29) |  | ||||||
| #define TX_RST				BIT(28) |  | ||||||
|  |  | ||||||
| #define TRGMII_TD0_CTRL			0x7a50 |  | ||||||
| #define TRGMII_TD1_CTRL			0x7a58 |  | ||||||
| #define TRGMII_TD2_CTRL			0x7a60 |  | ||||||
| #define TRGMII_TD3_CTRL			0x7a68 |  | ||||||
| #define TRGMII_TXCTL_CTRL		0x7a70 |  | ||||||
| #define TRGMII_TCK_CTRL			0x7a78 |  | ||||||
| #define TRGMII_TD_CTRL(n)		(0x7a50 + (n) * 8) |  | ||||||
| #define NUM_TRGMII_CTRL			6 |  | ||||||
| #define TX_DMPEDRV			BIT(31) |  | ||||||
| #define TX_DM_SR			BIT(15) |  | ||||||
| #define TX_DMERODT			BIT(14) |  | ||||||
| #define TX_DMOECTL			BIT(13) |  | ||||||
| #define TX_TAP_S			8 |  | ||||||
| #define TX_TAP_M			0xf00 |  | ||||||
| #define TX_TRAIN_WD_S			0 |  | ||||||
| #define TX_TRAIN_WD_M			0xff |  | ||||||
|  |  | ||||||
| #define TRGMII_TD0_ODT			0x7a54 |  | ||||||
| #define TRGMII_TD1_ODT			0x7a5c |  | ||||||
| #define TRGMII_TD2_ODT			0x7a64 |  | ||||||
| #define TRGMII_TD3_ODT			0x7a6c |  | ||||||
| #define TRGMII_TXCTL_ODT		0x7574 |  | ||||||
| #define TRGMII_TCK_ODT			0x757c |  | ||||||
| #define TRGMII_TD_ODT(n)		(0x7a54 + (n) * 8) |  | ||||||
| #define NUM_TRGMII_ODT			6 |  | ||||||
| #define TX_DM_DRVN_PRE_S		30 |  | ||||||
| #define TX_DM_DRVN_PRE_M		0xc0000000 |  | ||||||
| #define TX_DM_DRVP_PRE_S		28 |  | ||||||
| #define TX_DM_DRVP_PRE_M		0x30000000 |  | ||||||
| #define TX_DM_TDSEL_S			24 |  | ||||||
| #define TX_DM_TDSEL_M			0xf000000 |  | ||||||
| #define TX_ODTEN			BIT(23) |  | ||||||
| #define TX_DME_PRE			BIT(20) |  | ||||||
| #define TX_DM_DRVNT0			BIT(19) |  | ||||||
| #define TX_DM_DRVPT0			BIT(18) |  | ||||||
| #define TX_DM_DRVNTE			BIT(17) |  | ||||||
| #define TX_DM_DRVPTE			BIT(16) |  | ||||||
| #define TX_DM_ODTN_S			12 |  | ||||||
| #define TX_DM_ODTN_M			0x7000 |  | ||||||
| #define TX_DM_ODTP_S			8 |  | ||||||
| #define TX_DM_ODTP_M			0x700 |  | ||||||
| #define TX_DM_DRVN_S			4 |  | ||||||
| #define TX_DM_DRVN_M			0xf0 |  | ||||||
| #define TX_DM_DRVP_S			0 |  | ||||||
| #define TX_DM_DRVP_M			0x0f |  | ||||||
|  |  | ||||||
| #define P5RGMIIRXCR			0x7b00 |  | ||||||
| #define CSR_RGMII_RCTL_CFG_S		24 |  | ||||||
| #define CSR_RGMII_RCTL_CFG_M		0x7000000 |  | ||||||
| #define CSR_RGMII_RXD_CFG_S		16 |  | ||||||
| #define CSR_RGMII_RXD_CFG_M		0x70000 |  | ||||||
| #define CSR_RGMII_EDGE_ALIGN		BIT(8) |  | ||||||
| #define CSR_RGMII_RXC_90DEG_CFG_S	4 |  | ||||||
| #define CSR_RGMII_RXC_90DEG_CFG_M	0xf0 |  | ||||||
| #define CSR_RGMII_RXC_0DEG_CFG_S	0 |  | ||||||
| #define CSR_RGMII_RXC_0DEG_CFG_M	0x0f |  | ||||||
|  |  | ||||||
| #define P5RGMIITXCR			0x7b04 |  | ||||||
| #define CSR_RGMII_TXEN_CFG_S		16 |  | ||||||
| #define CSR_RGMII_TXEN_CFG_M		0x70000 |  | ||||||
| #define CSR_RGMII_TXD_CFG_S		8 |  | ||||||
| #define CSR_RGMII_TXD_CFG_M		0x700 |  | ||||||
| #define CSR_RGMII_TXC_CFG_S		0 |  | ||||||
| #define CSR_RGMII_TXC_CFG_M		0x1f |  | ||||||
|  |  | ||||||
| #define CHIP_REV			0x7ffc |  | ||||||
| #define CHIP_NAME_S			16 |  | ||||||
| #define CHIP_NAME_M			0xffff0000 |  | ||||||
| #define CHIP_REV_S			0 |  | ||||||
| #define CHIP_REV_M			0x0f |  | ||||||
|  |  | ||||||
| /* MMD registers */ |  | ||||||
| #define CORE_PLL_GROUP2			0x401 |  | ||||||
| #define RG_SYSPLL_EN_NORMAL		BIT(15) |  | ||||||
| #define RG_SYSPLL_VODEN			BIT(14) |  | ||||||
| #define RG_SYSPLL_POSDIV_S		5 |  | ||||||
| #define RG_SYSPLL_POSDIV_M		0x60 |  | ||||||
|  |  | ||||||
| #define CORE_PLL_GROUP4			0x403 |  | ||||||
| #define RG_SYSPLL_DDSFBK_EN		BIT(12) |  | ||||||
| #define RG_SYSPLL_BIAS_EN		BIT(11) |  | ||||||
| #define RG_SYSPLL_BIAS_LPF_EN		BIT(10) |  | ||||||
|  |  | ||||||
| #define CORE_PLL_GROUP5			0x404 |  | ||||||
| #define RG_LCDDS_PCW_NCPO1_S		0 |  | ||||||
| #define RG_LCDDS_PCW_NCPO1_M		0xffff |  | ||||||
|  |  | ||||||
| #define CORE_PLL_GROUP6			0x405 |  | ||||||
| #define RG_LCDDS_PCW_NCPO0_S		0 |  | ||||||
| #define RG_LCDDS_PCW_NCPO0_M		0xffff |  | ||||||
|  |  | ||||||
| #define CORE_PLL_GROUP7			0x406 |  | ||||||
| #define RG_LCDDS_PWDB			BIT(15) |  | ||||||
| #define RG_LCDDS_ISO_EN			BIT(13) |  | ||||||
| #define RG_LCCDS_C_S			4 |  | ||||||
| #define RG_LCCDS_C_M			0x70 |  | ||||||
| #define RG_LCDDS_PCW_NCPO_CHG		BIT(3) |  | ||||||
|  |  | ||||||
| #define CORE_PLL_GROUP10		0x409 |  | ||||||
| #define RG_LCDDS_SSC_DELTA_S		0 |  | ||||||
| #define RG_LCDDS_SSC_DELTA_M		0xfff |  | ||||||
|  |  | ||||||
| #define CORE_PLL_GROUP11		0x40a |  | ||||||
| #define RG_LCDDS_SSC_DELTA1_S		0 |  | ||||||
| #define RG_LCDDS_SSC_DELTA1_M		0xfff |  | ||||||
|  |  | ||||||
| #define CORE_GSWPLL_GCR_1		0x040d |  | ||||||
| #define GSWPLL_PREDIV_S			14 |  | ||||||
| #define GSWPLL_PREDIV_M			0xc000 |  | ||||||
| #define GSWPLL_POSTDIV_200M_S		12 |  | ||||||
| #define GSWPLL_POSTDIV_200M_M		0x3000 |  | ||||||
| #define GSWPLL_EN_PRE			BIT(11) |  | ||||||
| #define GSWPLL_FBKSEL			BIT(10) |  | ||||||
| #define GSWPLL_BP			BIT(9) |  | ||||||
| #define GSWPLL_BR			BIT(8) |  | ||||||
| #define GSWPLL_FBKDIV_200M_S		0 |  | ||||||
| #define GSWPLL_FBKDIV_200M_M		0xff |  | ||||||
|  |  | ||||||
| #define CORE_GSWPLL_GCR_2		0x040e |  | ||||||
| #define GSWPLL_POSTDIV_500M_S		8 |  | ||||||
| #define GSWPLL_POSTDIV_500M_M		0x300 |  | ||||||
| #define GSWPLL_FBKDIV_500M_S		0 |  | ||||||
| #define GSWPLL_FBKDIV_500M_M		0xff |  | ||||||
|  |  | ||||||
| #define TRGMII_GSW_CLK_CG		0x0410 |  | ||||||
| #define TRGMIICK_EN			BIT(1) |  | ||||||
| #define GSWCK_EN			BIT(0) |  | ||||||
|  |  | ||||||
| static int mt7530_mii_read(struct gsw_mt753x *gsw, int phy, int reg) |  | ||||||
| { |  | ||||||
| 	if (phy < MT753X_NUM_PHYS) |  | ||||||
| 		phy = (gsw->phy_base + phy) & MT753X_SMI_ADDR_MASK; |  | ||||||
|  |  | ||||||
| 	return mdiobus_read(gsw->host_bus, phy, reg); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void mt7530_mii_write(struct gsw_mt753x *gsw, int phy, int reg, u16 val) |  | ||||||
| { |  | ||||||
| 	if (phy < MT753X_NUM_PHYS) |  | ||||||
| 		phy = (gsw->phy_base + phy) & MT753X_SMI_ADDR_MASK; |  | ||||||
|  |  | ||||||
| 	mdiobus_write(gsw->host_bus, phy, reg, val); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt7530_mmd_read(struct gsw_mt753x *gsw, int addr, int devad, u16 reg) |  | ||||||
| { |  | ||||||
| 	u16 val; |  | ||||||
|  |  | ||||||
| 	if (addr < MT753X_NUM_PHYS) |  | ||||||
| 		addr = (gsw->phy_base + addr) & MT753X_SMI_ADDR_MASK; |  | ||||||
|  |  | ||||||
| 	mutex_lock(&gsw->host_bus->mdio_lock); |  | ||||||
|  |  | ||||||
| 	gsw->host_bus->write(gsw->host_bus, addr, MII_MMD_ACC_CTL_REG, |  | ||||||
| 			     (MMD_ADDR << MMD_CMD_S) | |  | ||||||
| 			     ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); |  | ||||||
|  |  | ||||||
| 	gsw->host_bus->write(gsw->host_bus, addr, MII_MMD_ADDR_DATA_REG, reg); |  | ||||||
|  |  | ||||||
| 	gsw->host_bus->write(gsw->host_bus, addr, MII_MMD_ACC_CTL_REG, |  | ||||||
| 			     (MMD_DATA << MMD_CMD_S) | |  | ||||||
| 			     ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); |  | ||||||
|  |  | ||||||
| 	val = gsw->host_bus->read(gsw->host_bus, addr, MII_MMD_ADDR_DATA_REG); |  | ||||||
|  |  | ||||||
| 	mutex_unlock(&gsw->host_bus->mdio_lock); |  | ||||||
|  |  | ||||||
| 	return val; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void mt7530_mmd_write(struct gsw_mt753x *gsw, int addr, int devad, |  | ||||||
| 			     u16 reg, u16 val) |  | ||||||
| { |  | ||||||
| 	if (addr < MT753X_NUM_PHYS) |  | ||||||
| 		addr = (gsw->phy_base + addr) & MT753X_SMI_ADDR_MASK; |  | ||||||
|  |  | ||||||
| 	mutex_lock(&gsw->host_bus->mdio_lock); |  | ||||||
|  |  | ||||||
| 	gsw->host_bus->write(gsw->host_bus, addr, MII_MMD_ACC_CTL_REG, |  | ||||||
| 		      (MMD_ADDR << MMD_CMD_S) | |  | ||||||
| 		      ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); |  | ||||||
|  |  | ||||||
| 	gsw->host_bus->write(gsw->host_bus, addr, MII_MMD_ADDR_DATA_REG, reg); |  | ||||||
|  |  | ||||||
| 	gsw->host_bus->write(gsw->host_bus, addr, MII_MMD_ACC_CTL_REG, |  | ||||||
| 		      (MMD_DATA << MMD_CMD_S) | |  | ||||||
| 		      ((devad << MMD_DEVAD_S) & MMD_DEVAD_M)); |  | ||||||
|  |  | ||||||
| 	gsw->host_bus->write(gsw->host_bus, addr, MII_MMD_ADDR_DATA_REG, val); |  | ||||||
|  |  | ||||||
| 	mutex_unlock(&gsw->host_bus->mdio_lock); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void mt7530_core_reg_write(struct gsw_mt753x *gsw, u32 reg, u32 val) |  | ||||||
| { |  | ||||||
| 	gsw->mmd_write(gsw, 0, 0x1f, reg, val); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void mt7530_trgmii_setting(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	u16 i; |  | ||||||
|  |  | ||||||
| 	mt7530_core_reg_write(gsw, CORE_PLL_GROUP5, 0x0780); |  | ||||||
| 	mdelay(1); |  | ||||||
| 	mt7530_core_reg_write(gsw, CORE_PLL_GROUP6, 0); |  | ||||||
| 	mt7530_core_reg_write(gsw, CORE_PLL_GROUP10, 0x87); |  | ||||||
| 	mdelay(1); |  | ||||||
| 	mt7530_core_reg_write(gsw, CORE_PLL_GROUP11, 0x87); |  | ||||||
|  |  | ||||||
| 	/* PLL BIAS enable */ |  | ||||||
| 	mt7530_core_reg_write(gsw, CORE_PLL_GROUP4, |  | ||||||
| 			      RG_SYSPLL_DDSFBK_EN | RG_SYSPLL_BIAS_EN); |  | ||||||
| 	mdelay(1); |  | ||||||
|  |  | ||||||
| 	/* PLL LPF enable */ |  | ||||||
| 	mt7530_core_reg_write(gsw, CORE_PLL_GROUP4, |  | ||||||
| 			      RG_SYSPLL_DDSFBK_EN | |  | ||||||
| 			      RG_SYSPLL_BIAS_EN | RG_SYSPLL_BIAS_LPF_EN); |  | ||||||
|  |  | ||||||
| 	/* sys PLL enable */ |  | ||||||
| 	mt7530_core_reg_write(gsw, CORE_PLL_GROUP2, |  | ||||||
| 			      RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN | |  | ||||||
| 			      (1 << RG_SYSPLL_POSDIV_S)); |  | ||||||
|  |  | ||||||
| 	/* LCDDDS PWDS */ |  | ||||||
| 	mt7530_core_reg_write(gsw, CORE_PLL_GROUP7, |  | ||||||
| 			      (3 << RG_LCCDS_C_S) | |  | ||||||
| 			      RG_LCDDS_PWDB | RG_LCDDS_ISO_EN); |  | ||||||
| 	mdelay(1); |  | ||||||
|  |  | ||||||
| 	/* Enable MT7530 TRGMII clock */ |  | ||||||
| 	mt7530_core_reg_write(gsw, TRGMII_GSW_CLK_CG, GSWCK_EN | TRGMIICK_EN); |  | ||||||
|  |  | ||||||
| 	/* lower Tx Driving */ |  | ||||||
| 	for (i = 0 ; i < NUM_TRGMII_ODT; i++) |  | ||||||
| 		mt753x_reg_write(gsw, TRGMII_TD_ODT(i), |  | ||||||
| 				 (4 << TX_DM_DRVP_S) | (4 << TX_DM_DRVN_S)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void mt7530_rgmii_setting(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	u32 val; |  | ||||||
|  |  | ||||||
| 	mt7530_core_reg_write(gsw, CORE_PLL_GROUP5, 0x0c80); |  | ||||||
| 	mdelay(1); |  | ||||||
| 	mt7530_core_reg_write(gsw, CORE_PLL_GROUP6, 0); |  | ||||||
| 	mt7530_core_reg_write(gsw, CORE_PLL_GROUP10, 0x87); |  | ||||||
| 	mdelay(1); |  | ||||||
| 	mt7530_core_reg_write(gsw, CORE_PLL_GROUP11, 0x87); |  | ||||||
|  |  | ||||||
| 	val = mt753x_reg_read(gsw, TRGMII_TXCTRL); |  | ||||||
| 	val &= ~TXC_INV; |  | ||||||
| 	mt753x_reg_write(gsw, TRGMII_TXCTRL, val); |  | ||||||
|  |  | ||||||
| 	mt753x_reg_write(gsw, TRGMII_TCK_CTRL, |  | ||||||
| 			 (8 << TX_TAP_S) | (0x55 << TX_TRAIN_WD_S)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt7530_mac_port_setup(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	u32 hwstrap, p6ecr = 0, p5mcr, p6mcr, phyad; |  | ||||||
|  |  | ||||||
| 	hwstrap = mt753x_reg_read(gsw, MHWSTRAP); |  | ||||||
| 	hwstrap &= ~(P6_INTF_DIS | P5_INTF_MODE_RGMII | P5_INTF_DIS_S); |  | ||||||
| 	hwstrap |= P5_INTF_SEL_GMAC5; |  | ||||||
| 	if (!gsw->port5_cfg.enabled) { |  | ||||||
| 		p5mcr = FORCE_MODE; |  | ||||||
| 		hwstrap |= P5_INTF_DIS_S; |  | ||||||
| 	} else { |  | ||||||
| 		p5mcr = (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) | |  | ||||||
| 			MAC_MODE | MAC_TX_EN | MAC_RX_EN | |  | ||||||
| 			BKOFF_EN | BACKPR_EN; |  | ||||||
|  |  | ||||||
| 		if (gsw->port5_cfg.force_link) { |  | ||||||
| 			p5mcr |= FORCE_MODE | FORCE_LINK | FORCE_RX_FC | |  | ||||||
| 				 FORCE_TX_FC; |  | ||||||
| 			p5mcr |= gsw->port5_cfg.speed << FORCE_SPD_S; |  | ||||||
|  |  | ||||||
| 			if (gsw->port5_cfg.duplex) |  | ||||||
| 				p5mcr |= FORCE_DPX; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		switch (gsw->port5_cfg.phy_mode) { |  | ||||||
| 		case PHY_INTERFACE_MODE_MII: |  | ||||||
| 		case PHY_INTERFACE_MODE_GMII: |  | ||||||
| 			break; |  | ||||||
| 		case PHY_INTERFACE_MODE_RGMII: |  | ||||||
| 			hwstrap |= P5_INTF_MODE_RGMII; |  | ||||||
| 			break; |  | ||||||
| 		default: |  | ||||||
| 			dev_info(gsw->dev, "%s is not supported by port5\n", |  | ||||||
| 				 phy_modes(gsw->port5_cfg.phy_mode)); |  | ||||||
| 			p5mcr = FORCE_MODE; |  | ||||||
| 			hwstrap |= P5_INTF_DIS_S; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		/* Port5 to PHY direct mode */ |  | ||||||
| 		if (of_property_read_u32(gsw->port5_cfg.np, "phy-address", |  | ||||||
| 					 &phyad)) |  | ||||||
| 			goto parse_p6; |  | ||||||
|  |  | ||||||
| 		if (phyad != 0 && phyad != 4) { |  | ||||||
| 			dev_info(gsw->dev, |  | ||||||
| 				 "Only PHY 0/4 can be connected to Port 5\n"); |  | ||||||
| 			goto parse_p6; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		hwstrap &= ~P5_INTF_SEL_GMAC5; |  | ||||||
| 		if (phyad == 0) |  | ||||||
| 			hwstrap |= P5_PHY0_SEL; |  | ||||||
| 		else |  | ||||||
| 			hwstrap &= ~P5_PHY0_SEL; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| parse_p6: |  | ||||||
| 	if (!gsw->port6_cfg.enabled) { |  | ||||||
| 		p6mcr = FORCE_MODE; |  | ||||||
| 		hwstrap |= P6_INTF_DIS; |  | ||||||
| 	} else { |  | ||||||
| 		p6mcr = (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) | |  | ||||||
| 			MAC_MODE | MAC_TX_EN | MAC_RX_EN | |  | ||||||
| 			BKOFF_EN | BACKPR_EN; |  | ||||||
|  |  | ||||||
| 		if (gsw->port6_cfg.force_link) { |  | ||||||
| 			p6mcr |= FORCE_MODE | FORCE_LINK | FORCE_RX_FC | |  | ||||||
| 				 FORCE_TX_FC; |  | ||||||
| 			p6mcr |= gsw->port6_cfg.speed << FORCE_SPD_S; |  | ||||||
|  |  | ||||||
| 			if (gsw->port6_cfg.duplex) |  | ||||||
| 				p6mcr |= FORCE_DPX; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		switch (gsw->port6_cfg.phy_mode) { |  | ||||||
| 		case PHY_INTERFACE_MODE_RGMII: |  | ||||||
| 			p6ecr = BIT(1); |  | ||||||
| 			break; |  | ||||||
| 		case PHY_INTERFACE_MODE_TRGMII: |  | ||||||
| 			/* set MT7530 central align */ |  | ||||||
| 			p6ecr = BIT(0); |  | ||||||
| 			break; |  | ||||||
| 		default: |  | ||||||
| 			dev_info(gsw->dev, "%s is not supported by port6\n", |  | ||||||
| 				 phy_modes(gsw->port6_cfg.phy_mode)); |  | ||||||
| 			p6mcr = FORCE_MODE; |  | ||||||
| 			hwstrap |= P6_INTF_DIS; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	mt753x_reg_write(gsw, MHWSTRAP, hwstrap); |  | ||||||
| 	mt753x_reg_write(gsw, P6ECR, p6ecr); |  | ||||||
|  |  | ||||||
| 	mt753x_reg_write(gsw, PMCR(5), p5mcr); |  | ||||||
| 	mt753x_reg_write(gsw, PMCR(6), p6mcr); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void mt7530_core_pll_setup(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	u32 hwstrap; |  | ||||||
|  |  | ||||||
| 	hwstrap = mt753x_reg_read(gsw, HWSTRAP); |  | ||||||
|  |  | ||||||
| 	switch ((hwstrap & XTAL_FSEL_M) >> XTAL_FSEL_S) { |  | ||||||
| 	case XTAL_40MHZ: |  | ||||||
| 		/* Disable MT7530 core clock */ |  | ||||||
| 		mt7530_core_reg_write(gsw, TRGMII_GSW_CLK_CG, 0); |  | ||||||
|  |  | ||||||
| 		/* disable MT7530 PLL */ |  | ||||||
| 		mt7530_core_reg_write(gsw, CORE_GSWPLL_GCR_1, |  | ||||||
| 				      (2 << GSWPLL_POSTDIV_200M_S) | |  | ||||||
| 				      (32 << GSWPLL_FBKDIV_200M_S)); |  | ||||||
|  |  | ||||||
| 		/* For MT7530 core clock = 500Mhz */ |  | ||||||
| 		mt7530_core_reg_write(gsw, CORE_GSWPLL_GCR_2, |  | ||||||
| 				      (1 << GSWPLL_POSTDIV_500M_S) | |  | ||||||
| 				      (25 << GSWPLL_FBKDIV_500M_S)); |  | ||||||
|  |  | ||||||
| 		/* Enable MT7530 PLL */ |  | ||||||
| 		mt7530_core_reg_write(gsw, CORE_GSWPLL_GCR_1, |  | ||||||
| 				      (2 << GSWPLL_POSTDIV_200M_S) | |  | ||||||
| 				      (32 << GSWPLL_FBKDIV_200M_S) | |  | ||||||
| 				      GSWPLL_EN_PRE); |  | ||||||
|  |  | ||||||
| 		usleep_range(20, 40); |  | ||||||
|  |  | ||||||
| 		/* Enable MT7530 core clock */ |  | ||||||
| 		mt7530_core_reg_write(gsw, TRGMII_GSW_CLK_CG, GSWCK_EN); |  | ||||||
| 		break; |  | ||||||
| 	default: |  | ||||||
| 		/* TODO: PLL settings for 20/25MHz */ |  | ||||||
| 		break; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	hwstrap = mt753x_reg_read(gsw, HWSTRAP); |  | ||||||
| 	hwstrap |= CHG_TRAP; |  | ||||||
| 	if (gsw->direct_phy_access) |  | ||||||
| 		hwstrap &= ~C_MDIO_BPS_S; |  | ||||||
| 	else |  | ||||||
| 		hwstrap |= C_MDIO_BPS_S; |  | ||||||
|  |  | ||||||
| 	mt753x_reg_write(gsw, MHWSTRAP, hwstrap); |  | ||||||
|  |  | ||||||
| 	if (gsw->port6_cfg.enabled && |  | ||||||
| 	    gsw->port6_cfg.phy_mode == PHY_INTERFACE_MODE_TRGMII) { |  | ||||||
| 		mt7530_trgmii_setting(gsw); |  | ||||||
| 	} else { |  | ||||||
| 		/* RGMII */ |  | ||||||
| 		mt7530_rgmii_setting(gsw); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* delay setting for 10/1000M */ |  | ||||||
| 	mt753x_reg_write(gsw, P5RGMIIRXCR, |  | ||||||
| 			 CSR_RGMII_EDGE_ALIGN | |  | ||||||
| 			 (2 << CSR_RGMII_RXC_0DEG_CFG_S)); |  | ||||||
| 	mt753x_reg_write(gsw, P5RGMIITXCR, 0x14 << CSR_RGMII_TXC_CFG_S); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt7530_sw_detect(struct gsw_mt753x *gsw, struct chip_rev *crev) |  | ||||||
| { |  | ||||||
| 	u32 rev; |  | ||||||
|  |  | ||||||
| 	rev = mt753x_reg_read(gsw, CHIP_REV); |  | ||||||
|  |  | ||||||
| 	if (((rev & CHIP_NAME_M) >> CHIP_NAME_S) == MT7530) { |  | ||||||
| 		if (crev) { |  | ||||||
| 			crev->rev = rev & CHIP_REV_M; |  | ||||||
| 			crev->name = "MT7530"; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return -ENODEV; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void mt7530_phy_setting(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	int i; |  | ||||||
| 	u32 val; |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < MT753X_NUM_PHYS; i++) { |  | ||||||
| 		/* Disable EEE */ |  | ||||||
| 		gsw->mmd_write(gsw, i, PHY_DEV07, PHY_DEV07_REG_03C, 0); |  | ||||||
|  |  | ||||||
| 		/* Enable HW auto downshift */ |  | ||||||
| 		gsw->mii_write(gsw, i, 0x1f, 0x1); |  | ||||||
| 		val = gsw->mii_read(gsw, i, PHY_EXT_REG_14); |  | ||||||
| 		val |= PHY_EN_DOWN_SHFIT; |  | ||||||
| 		gsw->mii_write(gsw, i, PHY_EXT_REG_14, val); |  | ||||||
|  |  | ||||||
| 		/* Increase SlvDPSready time */ |  | ||||||
| 		gsw->mii_write(gsw, i, 0x1f, 0x52b5); |  | ||||||
| 		gsw->mii_write(gsw, i, PHY_TR_REG_10, 0xafae); |  | ||||||
| 		gsw->mii_write(gsw, i, PHY_TR_REG_12, 0x2f); |  | ||||||
| 		gsw->mii_write(gsw, i, PHY_TR_REG_10, 0x8fae); |  | ||||||
|  |  | ||||||
| 		/* Increase post_update_timer */ |  | ||||||
| 		gsw->mii_write(gsw, i, 0x1f, 0x3); |  | ||||||
| 		gsw->mii_write(gsw, i, PHY_LPI_REG_11, 0x4b); |  | ||||||
| 		gsw->mii_write(gsw, i, 0x1f, 0); |  | ||||||
|  |  | ||||||
| 		/* Adjust 100_mse_threshold */ |  | ||||||
| 		gsw->mmd_write(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_123, 0xffff); |  | ||||||
|  |  | ||||||
| 		/* Disable mcc */ |  | ||||||
| 		gsw->mmd_write(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_A6, 0x300); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline bool get_phy_access_mode(const struct device_node *np) |  | ||||||
| { |  | ||||||
| 	return of_property_read_bool(np, "mt7530,direct-phy-access"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt7530_sw_init(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	int i; |  | ||||||
| 	u32 val; |  | ||||||
|  |  | ||||||
| 	gsw->direct_phy_access = get_phy_access_mode(gsw->dev->of_node); |  | ||||||
|  |  | ||||||
| 	/* Force MT7530 to use (in)direct PHY access */ |  | ||||||
| 	val = mt753x_reg_read(gsw, HWSTRAP); |  | ||||||
| 	val |= CHG_TRAP; |  | ||||||
| 	if (gsw->direct_phy_access) |  | ||||||
| 		val &= ~C_MDIO_BPS_S; |  | ||||||
| 	else |  | ||||||
| 		val |= C_MDIO_BPS_S; |  | ||||||
| 	mt753x_reg_write(gsw, MHWSTRAP, val); |  | ||||||
|  |  | ||||||
| 	/* Read PHY address base from HWSTRAP */ |  | ||||||
| 	gsw->phy_base  = (((val & SMI_ADDR_M) >> SMI_ADDR_S) << 3) + 8; |  | ||||||
| 	gsw->phy_base &= MT753X_SMI_ADDR_MASK; |  | ||||||
|  |  | ||||||
| 	if (gsw->direct_phy_access) { |  | ||||||
| 		gsw->mii_read = mt7530_mii_read; |  | ||||||
| 		gsw->mii_write = mt7530_mii_write; |  | ||||||
| 		gsw->mmd_read = mt7530_mmd_read; |  | ||||||
| 		gsw->mmd_write = mt7530_mmd_write; |  | ||||||
| 	} else { |  | ||||||
| 		gsw->mii_read = mt753x_mii_read; |  | ||||||
| 		gsw->mii_write = mt753x_mii_write; |  | ||||||
| 		gsw->mmd_read = mt753x_mmd_ind_read; |  | ||||||
| 		gsw->mmd_write = mt753x_mmd_ind_write; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < MT753X_NUM_PHYS; i++) { |  | ||||||
| 		val = gsw->mii_read(gsw, i, MII_BMCR); |  | ||||||
| 		val |= BMCR_PDOWN; |  | ||||||
| 		gsw->mii_write(gsw, i, MII_BMCR, val); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* Force MAC link down before reset */ |  | ||||||
| 	mt753x_reg_write(gsw, PMCR(5), FORCE_MODE); |  | ||||||
| 	mt753x_reg_write(gsw, PMCR(6), FORCE_MODE); |  | ||||||
|  |  | ||||||
| 	/* Switch soft reset */ |  | ||||||
| 	/* BUG: sw reset causes gsw int flooding */ |  | ||||||
| 	mt753x_reg_write(gsw, SYS_CTRL, SW_PHY_RST | SW_SYS_RST | SW_REG_RST); |  | ||||||
| 	usleep_range(10, 20); |  | ||||||
|  |  | ||||||
| 	/* global mac control settings configuration */ |  | ||||||
| 	mt753x_reg_write(gsw, GMACCR, |  | ||||||
| 			 LATE_COL_DROP | (15 << MTCC_LMT_S) | |  | ||||||
| 			 (2 << MAX_RX_JUMBO_S) | RX_PKT_LEN_MAX_JUMBO); |  | ||||||
|  |  | ||||||
| 	mt7530_core_pll_setup(gsw); |  | ||||||
| 	mt7530_mac_port_setup(gsw); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt7530_sw_post_init(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	int i; |  | ||||||
| 	u32 val; |  | ||||||
|  |  | ||||||
| 	mt7530_phy_setting(gsw); |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < MT753X_NUM_PHYS; i++) { |  | ||||||
| 		val = gsw->mii_read(gsw, i, MII_BMCR); |  | ||||||
| 		val &= ~BMCR_PDOWN; |  | ||||||
| 		gsw->mii_write(gsw, i, MII_BMCR, val); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct mt753x_sw_id mt7530_id = { |  | ||||||
| 	.model = MT7530, |  | ||||||
| 	.detect = mt7530_sw_detect, |  | ||||||
| 	.init = mt7530_sw_init, |  | ||||||
| 	.post_init = mt7530_sw_post_init |  | ||||||
| }; |  | ||||||
| @@ -1,13 +0,0 @@ | |||||||
| /* SPDX-License-Identifier: GPL-2.0-only */ |  | ||||||
| /* |  | ||||||
|  * Copyright (c) 2018 MediaTek Inc. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifndef _MT7530_H_ |  | ||||||
| #define _MT7530_H_ |  | ||||||
|  |  | ||||||
| #include "mt753x.h" |  | ||||||
|  |  | ||||||
| extern struct mt753x_sw_id mt7530_id; |  | ||||||
|  |  | ||||||
| #endif /* _MT7530_H_ */ |  | ||||||
| @@ -1,918 +0,0 @@ | |||||||
| // SPDX-License-Identifier: GPL-2.0-only |  | ||||||
| /* |  | ||||||
|  * Copyright (c) 2018 MediaTek Inc. |  | ||||||
|  * Author: Zhanguo Ju <zhanguo.ju@mediatek.com> |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include <linux/kernel.h> |  | ||||||
| #include <linux/delay.h> |  | ||||||
| #include <linux/hrtimer.h> |  | ||||||
|  |  | ||||||
| #include "mt753x.h" |  | ||||||
| #include "mt753x_regs.h" |  | ||||||
|  |  | ||||||
| /* MT7531 registers */ |  | ||||||
| #define SGMII_REG_BASE			0x5000 |  | ||||||
| #define SGMII_REG_PORT_BASE		0x1000 |  | ||||||
| #define SGMII_REG(p, r)			(SGMII_REG_BASE + \ |  | ||||||
| 					(p) * SGMII_REG_PORT_BASE + (r)) |  | ||||||
| #define PCS_CONTROL_1(p)		SGMII_REG(p, 0x00) |  | ||||||
| #define SGMII_MODE(p)			SGMII_REG(p, 0x20) |  | ||||||
| #define QPHY_PWR_STATE_CTRL(p)		SGMII_REG(p, 0xe8) |  | ||||||
| #define PHYA_CTRL_SIGNAL3(p)		SGMII_REG(p, 0x128) |  | ||||||
|  |  | ||||||
| /* Fields of PCS_CONTROL_1 */ |  | ||||||
| #define SGMII_LINK_STATUS		BIT(18) |  | ||||||
| #define SGMII_AN_ENABLE			BIT(12) |  | ||||||
| #define SGMII_AN_RESTART		BIT(9) |  | ||||||
|  |  | ||||||
| /* Fields of SGMII_MODE */ |  | ||||||
| #define SGMII_REMOTE_FAULT_DIS		BIT(8) |  | ||||||
| #define SGMII_IF_MODE_FORCE_DUPLEX	BIT(4) |  | ||||||
| #define SGMII_IF_MODE_FORCE_SPEED_S	0x2 |  | ||||||
| #define SGMII_IF_MODE_FORCE_SPEED_M	0x0c |  | ||||||
| #define SGMII_IF_MODE_ADVERT_AN		BIT(1) |  | ||||||
|  |  | ||||||
| /* Values of SGMII_IF_MODE_FORCE_SPEED */ |  | ||||||
| #define SGMII_IF_MODE_FORCE_SPEED_10	0 |  | ||||||
| #define SGMII_IF_MODE_FORCE_SPEED_100	1 |  | ||||||
| #define SGMII_IF_MODE_FORCE_SPEED_1000	2 |  | ||||||
|  |  | ||||||
| /* Fields of QPHY_PWR_STATE_CTRL */ |  | ||||||
| #define PHYA_PWD			BIT(4) |  | ||||||
|  |  | ||||||
| /* Fields of PHYA_CTRL_SIGNAL3 */ |  | ||||||
| #define RG_TPHY_SPEED_S			2 |  | ||||||
| #define RG_TPHY_SPEED_M			0x0c |  | ||||||
|  |  | ||||||
| /* Values of RG_TPHY_SPEED */ |  | ||||||
| #define RG_TPHY_SPEED_1000		0 |  | ||||||
| #define RG_TPHY_SPEED_2500		1 |  | ||||||
|  |  | ||||||
| /* Unique fields of (M)HWSTRAP for MT7531 */ |  | ||||||
| #define XTAL_FSEL_S			7 |  | ||||||
| #define XTAL_FSEL_M			BIT(7) |  | ||||||
| #define PHY_EN				BIT(6) |  | ||||||
| #define CHG_STRAP			BIT(8) |  | ||||||
|  |  | ||||||
| /* Efuse Register Define */ |  | ||||||
| #define GBE_EFUSE			0x7bc8 |  | ||||||
| #define GBE_SEL_EFUSE_EN		BIT(0) |  | ||||||
|  |  | ||||||
| /* PHY ENABLE Register bitmap define */ |  | ||||||
| #define PHY_DEV1F			0x1f |  | ||||||
| #define PHY_DEV1F_REG_44		0x44 |  | ||||||
| #define PHY_DEV1F_REG_104		0x104 |  | ||||||
| #define PHY_DEV1F_REG_10A		0x10a |  | ||||||
| #define PHY_DEV1F_REG_10B		0x10b |  | ||||||
| #define PHY_DEV1F_REG_10C		0x10c |  | ||||||
| #define PHY_DEV1F_REG_10D		0x10d |  | ||||||
| #define PHY_DEV1F_REG_268		0x268 |  | ||||||
| #define PHY_DEV1F_REG_269		0x269 |  | ||||||
| #define PHY_DEV1F_REG_403		0x403 |  | ||||||
|  |  | ||||||
| /* Fields of PHY_DEV1F_REG_403 */ |  | ||||||
| #define GBE_EFUSE_SETTING		BIT(3) |  | ||||||
| #define PHY_EN_BYPASS_MODE		BIT(4) |  | ||||||
| #define POWER_ON_OFF			BIT(5) |  | ||||||
| #define PHY_PLL_M			GENMASK(9, 8) |  | ||||||
| #define PHY_PLL_SEL(x)			(((x) << 8) & GENMASK(9, 8)) |  | ||||||
|  |  | ||||||
| /* PHY EEE Register bitmap of define */ |  | ||||||
| #define PHY_DEV07			0x07 |  | ||||||
| #define PHY_DEV07_REG_03C		0x3c |  | ||||||
|  |  | ||||||
| /* PHY Extend Register 0x14 bitmap of define */ |  | ||||||
| #define PHY_EXT_REG_14			0x14 |  | ||||||
|  |  | ||||||
| /* Fields of PHY_EXT_REG_14 */ |  | ||||||
| #define PHY_EN_DOWN_SHFIT		BIT(4) |  | ||||||
|  |  | ||||||
| /* PHY Extend Register 0x17 bitmap of define */ |  | ||||||
| #define PHY_EXT_REG_17			0x17 |  | ||||||
|  |  | ||||||
| /* Fields of PHY_EXT_REG_17 */ |  | ||||||
| #define PHY_LINKDOWN_POWER_SAVING_EN	BIT(4) |  | ||||||
|  |  | ||||||
| /* PHY Token Ring Register 0x10 bitmap of define */ |  | ||||||
| #define PHY_TR_REG_10			0x10 |  | ||||||
|  |  | ||||||
| /* PHY Token Ring Register 0x12 bitmap of define */ |  | ||||||
| #define PHY_TR_REG_12			0x12 |  | ||||||
|  |  | ||||||
| /* PHY DEV 0x1e Register bitmap of define */ |  | ||||||
| #define PHY_DEV1E			0x1e |  | ||||||
| #define PHY_DEV1E_REG_13		0x13 |  | ||||||
| #define PHY_DEV1E_REG_14		0x14 |  | ||||||
| #define PHY_DEV1E_REG_41		0x41 |  | ||||||
| #define PHY_DEV1E_REG_A6		0xa6 |  | ||||||
| #define PHY_DEV1E_REG_0C6		0x0c6 |  | ||||||
| #define PHY_DEV1E_REG_0FE		0x0fe |  | ||||||
| #define PHY_DEV1E_REG_123		0x123 |  | ||||||
| #define PHY_DEV1E_REG_189		0x189 |  | ||||||
|  |  | ||||||
| /* Fields of PHY_DEV1E_REG_0C6 */ |  | ||||||
| #define PHY_POWER_SAVING_S		8 |  | ||||||
| #define PHY_POWER_SAVING_M		0x300 |  | ||||||
| #define PHY_POWER_SAVING_TX		0x0 |  | ||||||
|  |  | ||||||
| /* Fields of PHY_DEV1E_REG_189 */ |  | ||||||
| #define DESCRAMBLER_CLEAR_EN		0x1 |  | ||||||
|  |  | ||||||
| /* Values of XTAL_FSEL_S */ |  | ||||||
| #define XTAL_40MHZ			0 |  | ||||||
| #define XTAL_25MHZ			1 |  | ||||||
|  |  | ||||||
| #define PLLGP_EN			0x7820 |  | ||||||
| #define EN_COREPLL			BIT(2) |  | ||||||
| #define SW_CLKSW			BIT(1) |  | ||||||
| #define SW_PLLGP			BIT(0) |  | ||||||
|  |  | ||||||
| #define PLLGP_CR0			0x78a8 |  | ||||||
| #define RG_COREPLL_EN			BIT(22) |  | ||||||
| #define RG_COREPLL_POSDIV_S		23 |  | ||||||
| #define RG_COREPLL_POSDIV_M		0x3800000 |  | ||||||
| #define RG_COREPLL_SDM_PCW_S		1 |  | ||||||
| #define RG_COREPLL_SDM_PCW_M		0x3ffffe |  | ||||||
| #define RG_COREPLL_SDM_PCW_CHG		BIT(0) |  | ||||||
|  |  | ||||||
| /* TOP Signals Status Register */ |  | ||||||
| #define TOP_SIG_SR			0x780c |  | ||||||
| #define PAD_DUAL_SGMII_EN		BIT(1) |  | ||||||
|  |  | ||||||
| /* RGMII and SGMII PLL clock */ |  | ||||||
| #define ANA_PLLGP_CR2			0x78b0 |  | ||||||
| #define ANA_PLLGP_CR5			0x78bc |  | ||||||
|  |  | ||||||
| /* GPIO mode define */ |  | ||||||
| #define GPIO_MODE_REGS(x)		(0x7c0c + (((x) / 8) * 4)) |  | ||||||
| #define GPIO_MODE_S			4 |  | ||||||
|  |  | ||||||
| /* GPIO GROUP IOLB SMT0 Control */ |  | ||||||
| #define SMT0_IOLB			0x7f04 |  | ||||||
| #define SMT_IOLB_5_SMI_MDC_EN		BIT(5) |  | ||||||
|  |  | ||||||
| /* Unique fields of PMCR for MT7531 */ |  | ||||||
| #define FORCE_MODE_EEE1G		BIT(25) |  | ||||||
| #define FORCE_MODE_EEE100		BIT(26) |  | ||||||
| #define FORCE_MODE_TX_FC		BIT(27) |  | ||||||
| #define FORCE_MODE_RX_FC		BIT(28) |  | ||||||
| #define FORCE_MODE_DPX			BIT(29) |  | ||||||
| #define FORCE_MODE_SPD			BIT(30) |  | ||||||
| #define FORCE_MODE_LNK			BIT(31) |  | ||||||
| #define FORCE_MODE			BIT(15) |  | ||||||
|  |  | ||||||
| #define CHIP_REV			0x781C |  | ||||||
| #define CHIP_NAME_S			16 |  | ||||||
| #define CHIP_NAME_M			0xffff0000 |  | ||||||
| #define CHIP_REV_S			0 |  | ||||||
| #define CHIP_REV_M			0x0f |  | ||||||
| #define CHIP_REV_E1			0x0 |  | ||||||
|  |  | ||||||
| #define CLKGEN_CTRL			0x7500 |  | ||||||
| #define CLK_SKEW_OUT_S			8 |  | ||||||
| #define CLK_SKEW_OUT_M			0x300 |  | ||||||
| #define CLK_SKEW_IN_S			6 |  | ||||||
| #define CLK_SKEW_IN_M			0xc0 |  | ||||||
| #define RXCLK_NO_DELAY			BIT(5) |  | ||||||
| #define TXCLK_NO_REVERSE		BIT(4) |  | ||||||
| #define GP_MODE_S			1 |  | ||||||
| #define GP_MODE_M			0x06 |  | ||||||
| #define GP_CLK_EN			BIT(0) |  | ||||||
|  |  | ||||||
| /* Values of GP_MODE */ |  | ||||||
| #define GP_MODE_RGMII			0 |  | ||||||
| #define GP_MODE_MII			1 |  | ||||||
| #define GP_MODE_REV_MII			2 |  | ||||||
|  |  | ||||||
| /* Values of CLK_SKEW_IN */ |  | ||||||
| #define CLK_SKEW_IN_NO_CHANGE		0 |  | ||||||
| #define CLK_SKEW_IN_DELAY_100PPS	1 |  | ||||||
| #define CLK_SKEW_IN_DELAY_200PPS	2 |  | ||||||
| #define CLK_SKEW_IN_REVERSE		3 |  | ||||||
|  |  | ||||||
| /* Values of CLK_SKEW_OUT */ |  | ||||||
| #define CLK_SKEW_OUT_NO_CHANGE		0 |  | ||||||
| #define CLK_SKEW_OUT_DELAY_100PPS	1 |  | ||||||
| #define CLK_SKEW_OUT_DELAY_200PPS	2 |  | ||||||
| #define CLK_SKEW_OUT_REVERSE		3 |  | ||||||
|  |  | ||||||
| /* Proprietory Control Register of Internal Phy device 0x1e */ |  | ||||||
| #define RXADC_CONTROL_3			0xc2 |  | ||||||
| #define RXADC_LDO_CONTROL_2		0xd3 |  | ||||||
|  |  | ||||||
| /* Proprietory Control Register of Internal Phy device 0x1f */ |  | ||||||
| #define TXVLD_DA_271			0x271 |  | ||||||
| #define TXVLD_DA_272			0x272 |  | ||||||
| #define TXVLD_DA_273			0x273 |  | ||||||
|  |  | ||||||
| /* DSP Channel and NOD_ADDR*/ |  | ||||||
| #define DSP_CH				0x2 |  | ||||||
| #define DSP_NOD_ADDR			0xD |  | ||||||
|  |  | ||||||
| /* gpio pinmux pins and functions define */ |  | ||||||
| static int gpio_int_pins[] = {0}; |  | ||||||
| static int gpio_int_funcs[] = {1}; |  | ||||||
| static int gpio_mdc_pins[] = {11, 20}; |  | ||||||
| static int gpio_mdc_funcs[] = {2, 2}; |  | ||||||
| static int gpio_mdio_pins[] = {12, 21}; |  | ||||||
| static int gpio_mdio_funcs[] = {2, 2}; |  | ||||||
|  |  | ||||||
| static int mt7531_set_port_sgmii_force_mode(struct gsw_mt753x *gsw, u32 port, |  | ||||||
| 					    struct mt753x_port_cfg *port_cfg) |  | ||||||
| { |  | ||||||
| 	u32 speed, port_base, val; |  | ||||||
| 	ktime_t timeout; |  | ||||||
| 	u32 timeout_us; |  | ||||||
|  |  | ||||||
| 	if (port < 5 || port >= MT753X_NUM_PORTS) { |  | ||||||
| 		dev_info(gsw->dev, "port %d is not a SGMII port\n", port); |  | ||||||
| 		return -EINVAL; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	port_base = port - 5; |  | ||||||
|  |  | ||||||
| 	switch (port_cfg->speed) { |  | ||||||
| 	case MAC_SPD_1000: |  | ||||||
| 		speed = RG_TPHY_SPEED_1000; |  | ||||||
| 		break; |  | ||||||
| 	case MAC_SPD_2500: |  | ||||||
| 		speed = RG_TPHY_SPEED_2500; |  | ||||||
| 		break; |  | ||||||
| 	default: |  | ||||||
| 		dev_info(gsw->dev, "invalid SGMII speed idx %d for port %d\n", |  | ||||||
| 			 port_cfg->speed, port); |  | ||||||
|  |  | ||||||
| 		speed = RG_TPHY_SPEED_1000; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* Step 1: Speed select register setting */ |  | ||||||
| 	val = mt753x_reg_read(gsw, PHYA_CTRL_SIGNAL3(port_base)); |  | ||||||
| 	val &= ~RG_TPHY_SPEED_M; |  | ||||||
| 	val |= speed << RG_TPHY_SPEED_S; |  | ||||||
| 	mt753x_reg_write(gsw, PHYA_CTRL_SIGNAL3(port_base), val); |  | ||||||
|  |  | ||||||
| 	/* Step 2 : Disable AN */ |  | ||||||
| 	val = mt753x_reg_read(gsw, PCS_CONTROL_1(port_base)); |  | ||||||
| 	val &= ~SGMII_AN_ENABLE; |  | ||||||
| 	mt753x_reg_write(gsw, PCS_CONTROL_1(port_base), val); |  | ||||||
|  |  | ||||||
| 	/* Step 3: SGMII force mode setting */ |  | ||||||
| 	val = mt753x_reg_read(gsw, SGMII_MODE(port_base)); |  | ||||||
| 	val &= ~SGMII_IF_MODE_ADVERT_AN; |  | ||||||
| 	val &= ~SGMII_IF_MODE_FORCE_SPEED_M; |  | ||||||
| 	val |= SGMII_IF_MODE_FORCE_SPEED_1000 << SGMII_IF_MODE_FORCE_SPEED_S; |  | ||||||
| 	val |= SGMII_IF_MODE_FORCE_DUPLEX; |  | ||||||
| 	/* For sgmii force mode, 0 is full duplex and 1 is half duplex */ |  | ||||||
| 	if (port_cfg->duplex) |  | ||||||
| 		val &= ~SGMII_IF_MODE_FORCE_DUPLEX; |  | ||||||
|  |  | ||||||
| 	mt753x_reg_write(gsw, SGMII_MODE(port_base), val); |  | ||||||
|  |  | ||||||
| 	/* Step 4: XXX: Disable Link partner's AN and set force mode */ |  | ||||||
|  |  | ||||||
| 	/* Step 5: XXX: Special setting for PHYA ==> reserved for flexible */ |  | ||||||
|  |  | ||||||
| 	/* Step 6 : Release PHYA power down state */ |  | ||||||
| 	val = mt753x_reg_read(gsw, QPHY_PWR_STATE_CTRL(port_base)); |  | ||||||
| 	val &= ~PHYA_PWD; |  | ||||||
| 	mt753x_reg_write(gsw, QPHY_PWR_STATE_CTRL(port_base), val); |  | ||||||
|  |  | ||||||
| 	/* Step 7 : Polling SGMII_LINK_STATUS */ |  | ||||||
| 	timeout_us = 2000000; |  | ||||||
| 	timeout = ktime_add_us(ktime_get(), timeout_us); |  | ||||||
| 	while (1) { |  | ||||||
| 		val = mt753x_reg_read(gsw, PCS_CONTROL_1(port_base)); |  | ||||||
| 		val &= SGMII_LINK_STATUS; |  | ||||||
|  |  | ||||||
| 		if (val) |  | ||||||
| 			break; |  | ||||||
|  |  | ||||||
| 		if (ktime_compare(ktime_get(), timeout) > 0) |  | ||||||
| 			return -ETIMEDOUT; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt7531_set_port_sgmii_an_mode(struct gsw_mt753x *gsw, u32 port, |  | ||||||
| 					 struct mt753x_port_cfg *port_cfg) |  | ||||||
| { |  | ||||||
| 	u32 speed, port_base, val; |  | ||||||
| 	ktime_t timeout; |  | ||||||
| 	u32 timeout_us; |  | ||||||
|  |  | ||||||
| 	if (port < 5 || port >= MT753X_NUM_PORTS) { |  | ||||||
| 		dev_info(gsw->dev, "port %d is not a SGMII port\n", port); |  | ||||||
| 		return -EINVAL; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	port_base = port - 5; |  | ||||||
|  |  | ||||||
| 	switch (port_cfg->speed) { |  | ||||||
| 	case MAC_SPD_1000: |  | ||||||
| 		speed = RG_TPHY_SPEED_1000; |  | ||||||
| 		break; |  | ||||||
| 	case MAC_SPD_2500: |  | ||||||
| 		speed = RG_TPHY_SPEED_2500; |  | ||||||
| 		break; |  | ||||||
| 	default: |  | ||||||
| 		dev_info(gsw->dev, "invalid SGMII speed idx %d for port %d\n", |  | ||||||
| 			 port_cfg->speed, port); |  | ||||||
|  |  | ||||||
| 		speed = RG_TPHY_SPEED_1000; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* Step 1: Speed select register setting */ |  | ||||||
| 	val = mt753x_reg_read(gsw, PHYA_CTRL_SIGNAL3(port_base)); |  | ||||||
| 	val &= ~RG_TPHY_SPEED_M; |  | ||||||
| 	val |= speed << RG_TPHY_SPEED_S; |  | ||||||
| 	mt753x_reg_write(gsw, PHYA_CTRL_SIGNAL3(port_base), val); |  | ||||||
|  |  | ||||||
| 	/* Step 2: Remote fault disable */ |  | ||||||
| 	val = mt753x_reg_read(gsw, SGMII_MODE(port)); |  | ||||||
| 	val |= SGMII_REMOTE_FAULT_DIS; |  | ||||||
| 	mt753x_reg_write(gsw, SGMII_MODE(port), val); |  | ||||||
|  |  | ||||||
| 	/* Step 3: Setting Link partner's AN enable = 1 */ |  | ||||||
|  |  | ||||||
| 	/* Step 4: Setting Link partner's device ability for speed/duplex */ |  | ||||||
|  |  | ||||||
| 	/* Step 5: AN re-start */ |  | ||||||
| 	val = mt753x_reg_read(gsw, PCS_CONTROL_1(port)); |  | ||||||
| 	val |= SGMII_AN_RESTART; |  | ||||||
| 	mt753x_reg_write(gsw, PCS_CONTROL_1(port), val); |  | ||||||
|  |  | ||||||
| 	/* Step 6: Special setting for PHYA ==> reserved for flexible */ |  | ||||||
|  |  | ||||||
| 	/* Step 7 : Polling SGMII_LINK_STATUS */ |  | ||||||
| 	timeout_us = 2000000; |  | ||||||
| 	timeout = ktime_add_us(ktime_get(), timeout_us); |  | ||||||
| 	while (1) { |  | ||||||
| 		val = mt753x_reg_read(gsw, PCS_CONTROL_1(port_base)); |  | ||||||
| 		val &= SGMII_LINK_STATUS; |  | ||||||
|  |  | ||||||
| 		if (val) |  | ||||||
| 			break; |  | ||||||
|  |  | ||||||
| 		if (ktime_compare(ktime_get(), timeout) > 0) |  | ||||||
| 			return -ETIMEDOUT; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt7531_set_port_rgmii(struct gsw_mt753x *gsw, u32 port) |  | ||||||
| { |  | ||||||
| 	u32 val; |  | ||||||
|  |  | ||||||
| 	if (port != 5) { |  | ||||||
| 		dev_info(gsw->dev, "RGMII mode is not available for port %d\n", |  | ||||||
| 			 port); |  | ||||||
| 		return -EINVAL; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	val = mt753x_reg_read(gsw, CLKGEN_CTRL); |  | ||||||
| 	val |= GP_CLK_EN; |  | ||||||
| 	val &= ~GP_MODE_M; |  | ||||||
| 	val |= GP_MODE_RGMII << GP_MODE_S; |  | ||||||
| 	val |= TXCLK_NO_REVERSE; |  | ||||||
| 	val |= RXCLK_NO_DELAY; |  | ||||||
| 	val &= ~CLK_SKEW_IN_M; |  | ||||||
| 	val |= CLK_SKEW_IN_NO_CHANGE << CLK_SKEW_IN_S; |  | ||||||
| 	val &= ~CLK_SKEW_OUT_M; |  | ||||||
| 	val |= CLK_SKEW_OUT_NO_CHANGE << CLK_SKEW_OUT_S; |  | ||||||
| 	mt753x_reg_write(gsw, CLKGEN_CTRL, val); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt7531_mac_port_setup(struct gsw_mt753x *gsw, u32 port, |  | ||||||
| 				 struct mt753x_port_cfg *port_cfg) |  | ||||||
| { |  | ||||||
| 	u32 pmcr; |  | ||||||
| 	u32 speed; |  | ||||||
|  |  | ||||||
| 	if (port < 5 || port >= MT753X_NUM_PORTS) { |  | ||||||
| 		dev_info(gsw->dev, "port %d is not a MAC port\n", port); |  | ||||||
| 		return -EINVAL; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (port_cfg->enabled) { |  | ||||||
| 		pmcr = (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) | |  | ||||||
| 		       MAC_MODE | MAC_TX_EN | MAC_RX_EN | |  | ||||||
| 		       BKOFF_EN | BACKPR_EN; |  | ||||||
|  |  | ||||||
| 		if (port_cfg->force_link) { |  | ||||||
| 			/* PMCR's speed field 0x11 is reserved, |  | ||||||
| 			 * sw should set 0x10 |  | ||||||
| 			 */ |  | ||||||
| 			speed = port_cfg->speed; |  | ||||||
| 			if (port_cfg->speed == MAC_SPD_2500) |  | ||||||
| 				speed = MAC_SPD_1000; |  | ||||||
|  |  | ||||||
| 			pmcr |= FORCE_MODE_LNK | FORCE_LINK | |  | ||||||
| 				FORCE_MODE_SPD | FORCE_MODE_DPX | |  | ||||||
| 				FORCE_MODE_RX_FC | FORCE_MODE_TX_FC | |  | ||||||
| 				FORCE_RX_FC | FORCE_TX_FC | |  | ||||||
| 				(speed << FORCE_SPD_S); |  | ||||||
|  |  | ||||||
| 			if (port_cfg->duplex) |  | ||||||
| 				pmcr |= FORCE_DPX; |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		pmcr = FORCE_MODE_LNK; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	switch (port_cfg->phy_mode) { |  | ||||||
| 	case PHY_INTERFACE_MODE_RGMII: |  | ||||||
| 		mt7531_set_port_rgmii(gsw, port); |  | ||||||
| 		break; |  | ||||||
| 	case PHY_INTERFACE_MODE_SGMII: |  | ||||||
| 		if (port_cfg->force_link) |  | ||||||
| 			mt7531_set_port_sgmii_force_mode(gsw, port, port_cfg); |  | ||||||
| 		else |  | ||||||
| 			mt7531_set_port_sgmii_an_mode(gsw, port, port_cfg); |  | ||||||
| 		break; |  | ||||||
| 	default: |  | ||||||
| 		if (port_cfg->enabled) |  | ||||||
| 			dev_info(gsw->dev, "%s is not supported by port %d\n", |  | ||||||
| 				 phy_modes(port_cfg->phy_mode), port); |  | ||||||
|  |  | ||||||
| 		pmcr = FORCE_MODE_LNK; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	mt753x_reg_write(gsw, PMCR(port), pmcr); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void mt7531_core_pll_setup(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	u32 hwstrap; |  | ||||||
| 	u32 val; |  | ||||||
|  |  | ||||||
| 	val = mt753x_reg_read(gsw, TOP_SIG_SR); |  | ||||||
| 	if (val & PAD_DUAL_SGMII_EN) |  | ||||||
| 		return; |  | ||||||
|  |  | ||||||
| 	hwstrap = mt753x_reg_read(gsw, HWSTRAP); |  | ||||||
|  |  | ||||||
| 	switch ((hwstrap & XTAL_FSEL_M) >> XTAL_FSEL_S) { |  | ||||||
| 	case XTAL_25MHZ: |  | ||||||
| 		/* Step 1 : Disable MT7531 COREPLL */ |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_EN); |  | ||||||
| 		val &= ~EN_COREPLL; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_EN, val); |  | ||||||
|  |  | ||||||
| 		/* Step 2: switch to XTAL output */ |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_EN); |  | ||||||
| 		val |= SW_CLKSW; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_EN, val); |  | ||||||
|  |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_CR0); |  | ||||||
| 		val &= ~RG_COREPLL_EN; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_CR0, val); |  | ||||||
|  |  | ||||||
| 		/* Step 3: disable PLLGP and enable program PLLGP */ |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_EN); |  | ||||||
| 		val |= SW_PLLGP; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_EN, val); |  | ||||||
|  |  | ||||||
| 		/* Step 4: program COREPLL output frequency to 500MHz */ |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_CR0); |  | ||||||
| 		val &= ~RG_COREPLL_POSDIV_M; |  | ||||||
| 		val |= 2 << RG_COREPLL_POSDIV_S; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_CR0, val); |  | ||||||
| 		usleep_range(25, 35); |  | ||||||
|  |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_CR0); |  | ||||||
| 		val &= ~RG_COREPLL_SDM_PCW_M; |  | ||||||
| 		val |= 0x140000 << RG_COREPLL_SDM_PCW_S; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_CR0, val); |  | ||||||
|  |  | ||||||
| 		/* Set feedback divide ratio update signal to high */ |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_CR0); |  | ||||||
| 		val |= RG_COREPLL_SDM_PCW_CHG; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_CR0, val); |  | ||||||
| 		/* Wait for at least 16 XTAL clocks */ |  | ||||||
| 		usleep_range(10, 20); |  | ||||||
|  |  | ||||||
| 		/* Step 5: set feedback divide ratio update signal to low */ |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_CR0); |  | ||||||
| 		val &= ~RG_COREPLL_SDM_PCW_CHG; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_CR0, val); |  | ||||||
|  |  | ||||||
| 		/* Enable 325M clock for SGMII */ |  | ||||||
| 		mt753x_reg_write(gsw, ANA_PLLGP_CR5, 0xad0000); |  | ||||||
|  |  | ||||||
| 		/* Enable 250SSC clock for RGMII */ |  | ||||||
| 		mt753x_reg_write(gsw, ANA_PLLGP_CR2, 0x4f40000); |  | ||||||
|  |  | ||||||
| 		/* Step 6: Enable MT7531 PLL */ |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_CR0); |  | ||||||
| 		val |= RG_COREPLL_EN; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_CR0, val); |  | ||||||
|  |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_EN); |  | ||||||
| 		val |= EN_COREPLL; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_EN, val); |  | ||||||
| 		usleep_range(25, 35); |  | ||||||
|  |  | ||||||
| 		break; |  | ||||||
| 	case XTAL_40MHZ: |  | ||||||
| 		/* Step 1 : Disable MT7531 COREPLL */ |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_EN); |  | ||||||
| 		val &= ~EN_COREPLL; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_EN, val); |  | ||||||
|  |  | ||||||
| 		/* Step 2: switch to XTAL output */ |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_EN); |  | ||||||
| 		val |= SW_CLKSW; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_EN, val); |  | ||||||
|  |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_CR0); |  | ||||||
| 		val &= ~RG_COREPLL_EN; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_CR0, val); |  | ||||||
|  |  | ||||||
| 		/* Step 3: disable PLLGP and enable program PLLGP */ |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_EN); |  | ||||||
| 		val |= SW_PLLGP; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_EN, val); |  | ||||||
|  |  | ||||||
| 		/* Step 4: program COREPLL output frequency to 500MHz */ |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_CR0); |  | ||||||
| 		val &= ~RG_COREPLL_POSDIV_M; |  | ||||||
| 		val |= 2 << RG_COREPLL_POSDIV_S; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_CR0, val); |  | ||||||
| 		usleep_range(25, 35); |  | ||||||
|  |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_CR0); |  | ||||||
| 		val &= ~RG_COREPLL_SDM_PCW_M; |  | ||||||
| 		val |= 0x190000 << RG_COREPLL_SDM_PCW_S; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_CR0, val); |  | ||||||
|  |  | ||||||
| 		/* Set feedback divide ratio update signal to high */ |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_CR0); |  | ||||||
| 		val |= RG_COREPLL_SDM_PCW_CHG; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_CR0, val); |  | ||||||
| 		/* Wait for at least 16 XTAL clocks */ |  | ||||||
| 		usleep_range(10, 20); |  | ||||||
|  |  | ||||||
| 		/* Step 5: set feedback divide ratio update signal to low */ |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_CR0); |  | ||||||
| 		val &= ~RG_COREPLL_SDM_PCW_CHG; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_CR0, val); |  | ||||||
|  |  | ||||||
| 		/* Enable 325M clock for SGMII */ |  | ||||||
| 		mt753x_reg_write(gsw, ANA_PLLGP_CR5, 0xad0000); |  | ||||||
|  |  | ||||||
| 		/* Enable 250SSC clock for RGMII */ |  | ||||||
| 		mt753x_reg_write(gsw, ANA_PLLGP_CR2, 0x4f40000); |  | ||||||
|  |  | ||||||
| 		/* Step 6: Enable MT7531 PLL */ |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_CR0); |  | ||||||
| 		val |= RG_COREPLL_EN; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_CR0, val); |  | ||||||
|  |  | ||||||
| 		val = mt753x_reg_read(gsw, PLLGP_EN); |  | ||||||
| 		val |= EN_COREPLL; |  | ||||||
| 		mt753x_reg_write(gsw, PLLGP_EN, val); |  | ||||||
| 		usleep_range(25, 35); |  | ||||||
| 		break; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt7531_internal_phy_calibration(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt7531_sw_detect(struct gsw_mt753x *gsw, struct chip_rev *crev) |  | ||||||
| { |  | ||||||
| 	u32 rev, topsig; |  | ||||||
|  |  | ||||||
| 	rev = mt753x_reg_read(gsw, CHIP_REV); |  | ||||||
|  |  | ||||||
| 	if (((rev & CHIP_NAME_M) >> CHIP_NAME_S) == MT7531) { |  | ||||||
| 		if (crev) { |  | ||||||
| 			topsig = mt753x_reg_read(gsw, TOP_SIG_SR); |  | ||||||
|  |  | ||||||
| 			crev->rev = rev & CHIP_REV_M; |  | ||||||
| 			crev->name = topsig & PAD_DUAL_SGMII_EN ? |  | ||||||
| 				     "MT7531AE" : "MT7531BE"; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return -ENODEV; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void pinmux_set_mux_7531(struct gsw_mt753x *gsw, u32 pin, u32 mode) |  | ||||||
| { |  | ||||||
| 	u32 val; |  | ||||||
|  |  | ||||||
| 	val = mt753x_reg_read(gsw, GPIO_MODE_REGS(pin)); |  | ||||||
| 	val &= ~(0xf << (pin & 7) * GPIO_MODE_S); |  | ||||||
| 	val |= mode << (pin & 7) * GPIO_MODE_S; |  | ||||||
| 	mt753x_reg_write(gsw, GPIO_MODE_REGS(pin), val); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt7531_set_gpio_pinmux(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	u32 group = 0; |  | ||||||
| 	struct device_node *np = gsw->dev->of_node; |  | ||||||
|  |  | ||||||
| 	/* Set GPIO 0 interrupt mode */ |  | ||||||
| 	pinmux_set_mux_7531(gsw, gpio_int_pins[0], gpio_int_funcs[0]); |  | ||||||
|  |  | ||||||
| 	of_property_read_u32(np, "mediatek,mdio_master_pinmux", &group); |  | ||||||
|  |  | ||||||
| 	/* group = 0: do nothing, 1: 1st group (AE), 2: 2nd group (BE) */ |  | ||||||
| 	if (group > 0 && group <= 2) { |  | ||||||
| 		group--; |  | ||||||
| 		pinmux_set_mux_7531(gsw, gpio_mdc_pins[group], |  | ||||||
| 				    gpio_mdc_funcs[group]); |  | ||||||
| 		pinmux_set_mux_7531(gsw, gpio_mdio_pins[group], |  | ||||||
| 				    gpio_mdio_funcs[group]); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void mt7531_phy_pll_setup(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	u32 hwstrap; |  | ||||||
| 	u32 val; |  | ||||||
|  |  | ||||||
| 	hwstrap = mt753x_reg_read(gsw, HWSTRAP); |  | ||||||
|  |  | ||||||
| 	switch ((hwstrap & XTAL_FSEL_M) >> XTAL_FSEL_S) { |  | ||||||
| 	case XTAL_25MHZ: |  | ||||||
| 		/* disable pll auto calibration */ |  | ||||||
| 		gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_104, 0x608); |  | ||||||
|  |  | ||||||
| 		/* change pll sel */ |  | ||||||
| 		val = gsw->mmd_read(gsw, 0, PHY_DEV1F, |  | ||||||
| 				     PHY_DEV1F_REG_403); |  | ||||||
| 		val &= ~(PHY_PLL_M); |  | ||||||
| 		val |= PHY_PLL_SEL(3); |  | ||||||
| 		gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_403, val); |  | ||||||
|  |  | ||||||
| 		/* set divider ratio */ |  | ||||||
| 		gsw->mmd_write(gsw, 0, PHY_DEV1F, |  | ||||||
| 			       PHY_DEV1F_REG_10A, 0x1009); |  | ||||||
|  |  | ||||||
| 		/* set divider ratio */ |  | ||||||
| 		gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_10B, 0x7c6); |  | ||||||
|  |  | ||||||
| 		/* capacitance and resistance adjustment */ |  | ||||||
| 		gsw->mmd_write(gsw, 0, PHY_DEV1F, |  | ||||||
| 			       PHY_DEV1F_REG_10C, 0xa8be); |  | ||||||
|  |  | ||||||
| 		break; |  | ||||||
| 	case XTAL_40MHZ: |  | ||||||
| 		/* disable pll auto calibration */ |  | ||||||
| 		gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_104, 0x608); |  | ||||||
|  |  | ||||||
| 		/* change pll sel */ |  | ||||||
| 		val = gsw->mmd_read(gsw, 0, PHY_DEV1F, |  | ||||||
| 				     PHY_DEV1F_REG_403); |  | ||||||
| 		val &= ~(PHY_PLL_M); |  | ||||||
| 		val |= PHY_PLL_SEL(3); |  | ||||||
| 		gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_403, val); |  | ||||||
|  |  | ||||||
| 		/* set divider ratio */ |  | ||||||
| 		gsw->mmd_write(gsw, 0, PHY_DEV1F, |  | ||||||
| 			       PHY_DEV1F_REG_10A, 0x1018); |  | ||||||
|  |  | ||||||
| 		/* set divider ratio */ |  | ||||||
| 		gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_10B, 0xc676); |  | ||||||
|  |  | ||||||
| 		/* capacitance and resistance adjustment */ |  | ||||||
| 		gsw->mmd_write(gsw, 0, PHY_DEV1F, |  | ||||||
| 			       PHY_DEV1F_REG_10C, 0xd8be); |  | ||||||
| 		break; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* power down pll. additional delay is not required via mdio access */ |  | ||||||
| 	gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_10D, 0x10); |  | ||||||
|  |  | ||||||
| 	/* power up pll */ |  | ||||||
| 	gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_10D, 0x14); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void mt7531_phy_setting(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	int i; |  | ||||||
| 	u32 val; |  | ||||||
|  |  | ||||||
| 	/* Adjust DAC TX Delay */ |  | ||||||
| 	gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_44, 0xc0); |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < MT753X_NUM_PHYS; i++) { |  | ||||||
| 		/* Disable EEE */ |  | ||||||
| 		gsw->mmd_write(gsw, i, PHY_DEV07, PHY_DEV07_REG_03C, 0); |  | ||||||
|  |  | ||||||
| 		/* Enable HW auto downshift */ |  | ||||||
| 		gsw->mii_write(gsw, i, 0x1f, 0x1); |  | ||||||
| 		val = gsw->mii_read(gsw, i, PHY_EXT_REG_14); |  | ||||||
| 		val |= PHY_EN_DOWN_SHFIT; |  | ||||||
| 		gsw->mii_write(gsw, i, PHY_EXT_REG_14, val); |  | ||||||
|  |  | ||||||
| 		/* Increase SlvDPSready time */ |  | ||||||
| 		gsw->mii_write(gsw, i, 0x1f, 0x52b5); |  | ||||||
| 		gsw->mii_write(gsw, i, PHY_TR_REG_10, 0xafae); |  | ||||||
| 		gsw->mii_write(gsw, i, PHY_TR_REG_12, 0x2f); |  | ||||||
| 		gsw->mii_write(gsw, i, PHY_TR_REG_10, 0x8fae); |  | ||||||
| 		gsw->mii_write(gsw, i, 0x1f, 0); |  | ||||||
|  |  | ||||||
| 		/* Adjust 100_mse_threshold */ |  | ||||||
| 		gsw->mmd_write(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_123, 0xffff); |  | ||||||
|  |  | ||||||
| 		/* Disable mcc */ |  | ||||||
| 		gsw->mmd_write(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_A6, 0x300); |  | ||||||
|  |  | ||||||
| 		/* PHY link down power saving enable */ |  | ||||||
| 		val = gsw->mii_read(gsw, i, PHY_EXT_REG_17); |  | ||||||
| 		val |= PHY_LINKDOWN_POWER_SAVING_EN; |  | ||||||
| 		gsw->mii_write(gsw, i, PHY_EXT_REG_17, val); |  | ||||||
|  |  | ||||||
| 		val = gsw->mmd_read(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_0C6); |  | ||||||
| 		val &= ~PHY_POWER_SAVING_M; |  | ||||||
| 		val |= PHY_POWER_SAVING_TX << PHY_POWER_SAVING_S; |  | ||||||
| 		gsw->mmd_write(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_0C6, val); |  | ||||||
|  |  | ||||||
| 		/* Set TX Pair delay selection */ |  | ||||||
| 		gsw->mmd_write(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_13, 0x404); |  | ||||||
| 		gsw->mmd_write(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_14, 0x404); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void mt7531_adjust_line_driving(struct gsw_mt753x *gsw, u32 port) |  | ||||||
| { |  | ||||||
| 	/* For ADC timing margin window for LDO calibration */ |  | ||||||
| 	gsw->mmd_write(gsw, port, PHY_DEV1E, RXADC_LDO_CONTROL_2, 0x2222); |  | ||||||
|  |  | ||||||
| 	/* Adjust AD sample timing */ |  | ||||||
| 	gsw->mmd_write(gsw, port, PHY_DEV1E, RXADC_CONTROL_3, 0x4444); |  | ||||||
|  |  | ||||||
| 	/* Adjust Line driver current for different mode */ |  | ||||||
| 	gsw->mmd_write(gsw, port, PHY_DEV1F, TXVLD_DA_271, 0x2ca5); |  | ||||||
|  |  | ||||||
| 	/* Adjust Line driver current for different mode */ |  | ||||||
| 	gsw->mmd_write(gsw, port, PHY_DEV1F, TXVLD_DA_272, 0xc6b); |  | ||||||
|  |  | ||||||
| 	/* Adjust Line driver amplitude for 10BT */ |  | ||||||
| 	gsw->mmd_write(gsw, port, PHY_DEV1F, TXVLD_DA_273, 0x3000); |  | ||||||
|  |  | ||||||
| 	/* Adjust RX Echo path filter */ |  | ||||||
| 	gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_DEV1E_REG_0FE, 0x2); |  | ||||||
|  |  | ||||||
| 	/* Adjust RX HVGA bias current */ |  | ||||||
| 	gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_DEV1E_REG_41, 0x3333); |  | ||||||
|  |  | ||||||
| 	/* Adjust TX class AB driver 1 */ |  | ||||||
| 	gsw->mmd_write(gsw, port, PHY_DEV1F, PHY_DEV1F_REG_268, 0x388); |  | ||||||
|  |  | ||||||
| 	/* Adjust TX class AB driver 2 */ |  | ||||||
| 	gsw->mmd_write(gsw, port, PHY_DEV1F, PHY_DEV1F_REG_269, 0x4448); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void mt7531_eee_setting(struct gsw_mt753x *gsw, u32 port) |  | ||||||
| { |  | ||||||
| 	u32 tr_reg_control; |  | ||||||
| 	u32 val; |  | ||||||
|  |  | ||||||
| 	/* Disable generate signal to clear the scramble_lock when lpi mode */ |  | ||||||
| 	val = gsw->mmd_read(gsw, port, PHY_DEV1E, PHY_DEV1E_REG_189); |  | ||||||
| 	val &= ~DESCRAMBLER_CLEAR_EN; |  | ||||||
| 	gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_DEV1E_REG_189, val); |  | ||||||
|  |  | ||||||
| 	/* roll back CR*/ |  | ||||||
| 	gsw->mii_write(gsw, port, 0x1f, 0x52b5); |  | ||||||
| 	gsw->mmd_write(gsw, port, 0x1e, 0x2d1, 0); |  | ||||||
| 	tr_reg_control = (1 << 15) | (0 << 13) | (DSP_CH << 11) | |  | ||||||
| 			 (DSP_NOD_ADDR << 7) | (0x8 << 1); |  | ||||||
| 	gsw->mii_write(gsw, port, 17, 0x1b); |  | ||||||
| 	gsw->mii_write(gsw, port, 18, 0); |  | ||||||
| 	gsw->mii_write(gsw, port, 16, tr_reg_control); |  | ||||||
| 	tr_reg_control = (1 << 15) | (0 << 13) | (DSP_CH << 11) | |  | ||||||
| 			 (DSP_NOD_ADDR << 7) | (0xf << 1); |  | ||||||
| 	gsw->mii_write(gsw, port, 17, 0); |  | ||||||
| 	gsw->mii_write(gsw, port, 18, 0); |  | ||||||
| 	gsw->mii_write(gsw, port, 16, tr_reg_control); |  | ||||||
|  |  | ||||||
| 	tr_reg_control = (1 << 15) | (0 << 13) | (DSP_CH << 11) | |  | ||||||
| 			 (DSP_NOD_ADDR << 7) | (0x10 << 1); |  | ||||||
| 	gsw->mii_write(gsw, port, 17, 0x500); |  | ||||||
| 	gsw->mii_write(gsw, port, 18, 0); |  | ||||||
| 	gsw->mii_write(gsw, port, 16, tr_reg_control); |  | ||||||
| 	gsw->mii_write(gsw, port, 0x1f, 0); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt7531_sw_init(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	int i; |  | ||||||
| 	u32 val; |  | ||||||
|  |  | ||||||
| 	gsw->phy_base = (gsw->smi_addr + 1) & MT753X_SMI_ADDR_MASK; |  | ||||||
|  |  | ||||||
| 	gsw->mii_read = mt753x_mii_read; |  | ||||||
| 	gsw->mii_write = mt753x_mii_write; |  | ||||||
| 	gsw->mmd_read = mt753x_mmd_read; |  | ||||||
| 	gsw->mmd_write = mt753x_mmd_write; |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < MT753X_NUM_PHYS; i++) { |  | ||||||
| 		val = gsw->mii_read(gsw, i, MII_BMCR); |  | ||||||
| 		val |= BMCR_ISOLATE; |  | ||||||
| 		gsw->mii_write(gsw, i, MII_BMCR, val); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* Force MAC link down before reset */ |  | ||||||
| 	mt753x_reg_write(gsw, PMCR(5), FORCE_MODE_LNK); |  | ||||||
| 	mt753x_reg_write(gsw, PMCR(6), FORCE_MODE_LNK); |  | ||||||
|  |  | ||||||
| 	/* Switch soft reset */ |  | ||||||
| 	mt753x_reg_write(gsw, SYS_CTRL, SW_SYS_RST | SW_REG_RST); |  | ||||||
| 	usleep_range(10, 20); |  | ||||||
|  |  | ||||||
| 	/* Enable MDC input Schmitt Trigger */ |  | ||||||
| 	val = mt753x_reg_read(gsw, SMT0_IOLB); |  | ||||||
| 	mt753x_reg_write(gsw, SMT0_IOLB, val | SMT_IOLB_5_SMI_MDC_EN); |  | ||||||
|  |  | ||||||
| 	/* Set 7531 gpio pinmux */ |  | ||||||
| 	mt7531_set_gpio_pinmux(gsw); |  | ||||||
|  |  | ||||||
| 	/* Global mac control settings */ |  | ||||||
| 	mt753x_reg_write(gsw, GMACCR, |  | ||||||
| 			 (15 << MTCC_LMT_S) | (11 << MAX_RX_JUMBO_S) | |  | ||||||
| 			 RX_PKT_LEN_MAX_JUMBO); |  | ||||||
|  |  | ||||||
| 	mt7531_core_pll_setup(gsw); |  | ||||||
| 	mt7531_mac_port_setup(gsw, 5, &gsw->port5_cfg); |  | ||||||
| 	mt7531_mac_port_setup(gsw, 6, &gsw->port6_cfg); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt7531_sw_post_init(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	int i; |  | ||||||
| 	u32 val; |  | ||||||
|  |  | ||||||
| 	mt7531_phy_pll_setup(gsw); |  | ||||||
|  |  | ||||||
| 	/* Internal PHYs are disabled by default. SW should enable them. |  | ||||||
| 	 * Note that this may already be enabled in bootloader stage. |  | ||||||
| 	 */ |  | ||||||
| 	val = gsw->mmd_read(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_403); |  | ||||||
| 	val |= PHY_EN_BYPASS_MODE; |  | ||||||
| 	val &= ~POWER_ON_OFF; |  | ||||||
| 	gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_403, val); |  | ||||||
|  |  | ||||||
| 	mt7531_phy_setting(gsw); |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < MT753X_NUM_PHYS; i++) { |  | ||||||
| 		val = gsw->mii_read(gsw, i, MII_BMCR); |  | ||||||
| 		val &= ~BMCR_ISOLATE; |  | ||||||
| 		gsw->mii_write(gsw, i, MII_BMCR, val); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < MT753X_NUM_PHYS; i++) |  | ||||||
| 		mt7531_adjust_line_driving(gsw, i); |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < MT753X_NUM_PHYS; i++) |  | ||||||
| 		mt7531_eee_setting(gsw, i); |  | ||||||
|  |  | ||||||
| 	val = mt753x_reg_read(gsw, CHIP_REV); |  | ||||||
| 	val &= CHIP_REV_M; |  | ||||||
| 	if (val == CHIP_REV_E1) { |  | ||||||
| 		mt7531_internal_phy_calibration(gsw); |  | ||||||
| 	} else { |  | ||||||
| 		val = mt753x_reg_read(gsw, GBE_EFUSE); |  | ||||||
| 		if (val & GBE_SEL_EFUSE_EN) { |  | ||||||
| 			val = gsw->mmd_read(gsw, 0, PHY_DEV1F, |  | ||||||
| 					    PHY_DEV1F_REG_403); |  | ||||||
| 			val &= ~GBE_EFUSE_SETTING; |  | ||||||
| 			gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_403, |  | ||||||
| 				       val); |  | ||||||
| 		} else { |  | ||||||
| 			mt7531_internal_phy_calibration(gsw); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct mt753x_sw_id mt7531_id = { |  | ||||||
| 	.model = MT7531, |  | ||||||
| 	.detect = mt7531_sw_detect, |  | ||||||
| 	.init = mt7531_sw_init, |  | ||||||
| 	.post_init = mt7531_sw_post_init |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| MODULE_LICENSE("GPL"); |  | ||||||
| MODULE_AUTHOR("Zhanguo Ju <zhanguo.ju@mediatek.com>"); |  | ||||||
| MODULE_DESCRIPTION("Driver for MediaTek MT753x Gigabit Switch"); |  | ||||||
| @@ -1,13 +0,0 @@ | |||||||
| /* SPDX-License-Identifier: GPL-2.0-only */ |  | ||||||
| /* |  | ||||||
|  * Copyright (c) 2018 MediaTek Inc. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifndef _MT7531_H_ |  | ||||||
| #define _MT7531_H_ |  | ||||||
|  |  | ||||||
| #include "mt753x.h" |  | ||||||
|  |  | ||||||
| extern struct mt753x_sw_id mt7531_id; |  | ||||||
|  |  | ||||||
| #endif /* _MT7531_H_ */ |  | ||||||
| @@ -1,213 +0,0 @@ | |||||||
| /* SPDX-License-Identifier: GPL-2.0-only */ |  | ||||||
| /* |  | ||||||
|  * Copyright (c) 2018 MediaTek Inc. |  | ||||||
|  * Author: Weijie Gao <weijie.gao@mediatek.com> |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifndef _MT753X_H_ |  | ||||||
| #define _MT753X_H_ |  | ||||||
|  |  | ||||||
| #include <linux/list.h> |  | ||||||
| #include <linux/mutex.h> |  | ||||||
| #include <linux/netdevice.h> |  | ||||||
| #include <linux/of_mdio.h> |  | ||||||
| #include <linux/workqueue.h> |  | ||||||
| #include <linux/gpio/consumer.h> |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_SWCONFIG |  | ||||||
| #include <linux/switch.h> |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #include "mt753x_vlan.h" |  | ||||||
|  |  | ||||||
| #define MT753X_DFL_CPU_PORT	6 |  | ||||||
| #define MT753X_NUM_PHYS		5 |  | ||||||
|  |  | ||||||
| #define MT753X_DFL_SMI_ADDR	0x1f |  | ||||||
| #define MT753X_SMI_ADDR_MASK	0x1f |  | ||||||
|  |  | ||||||
| struct gsw_mt753x; |  | ||||||
|  |  | ||||||
| enum mt753x_model { |  | ||||||
| 	MT7530 = 0x7530, |  | ||||||
| 	MT7531 = 0x7531 |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct mt753x_port_cfg { |  | ||||||
| 	struct device_node *np; |  | ||||||
| 	phy_interface_t phy_mode; |  | ||||||
| 	u32 enabled: 1; |  | ||||||
| 	u32 force_link: 1; |  | ||||||
| 	u32 speed: 2; |  | ||||||
| 	u32 duplex: 1; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct mt753x_phy { |  | ||||||
| 	struct gsw_mt753x *gsw; |  | ||||||
| 	struct net_device netdev; |  | ||||||
| 	struct phy_device *phydev; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct gsw_mt753x { |  | ||||||
| 	u32 id; |  | ||||||
|  |  | ||||||
| 	struct device *dev; |  | ||||||
| 	struct mii_bus *host_bus; |  | ||||||
| 	struct mii_bus *gphy_bus; |  | ||||||
| 	struct mutex mii_lock;	/* MII access lock */ |  | ||||||
| 	u32 smi_addr; |  | ||||||
| 	u32 phy_base; |  | ||||||
| 	int direct_phy_access; |  | ||||||
|  |  | ||||||
| 	enum mt753x_model model; |  | ||||||
| 	const char *name; |  | ||||||
|  |  | ||||||
| 	struct mt753x_port_cfg port5_cfg; |  | ||||||
| 	struct mt753x_port_cfg port6_cfg; |  | ||||||
|  |  | ||||||
| 	int phy_status_poll; |  | ||||||
| 	struct mt753x_phy phys[MT753X_NUM_PHYS]; |  | ||||||
|  |  | ||||||
| 	int phy_link_sts; |  | ||||||
|  |  | ||||||
| 	int irq; |  | ||||||
| 	int reset_pin; |  | ||||||
| 	struct work_struct irq_worker; |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_SWCONFIG |  | ||||||
| 	struct switch_dev swdev; |  | ||||||
| 	u32 cpu_port; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| 	int global_vlan_enable; |  | ||||||
| 	struct mt753x_vlan_entry vlan_entries[MT753X_NUM_VLANS]; |  | ||||||
| 	struct mt753x_port_entry port_entries[MT753X_NUM_PORTS]; |  | ||||||
|  |  | ||||||
| 	int (*mii_read)(struct gsw_mt753x *gsw, int phy, int reg); |  | ||||||
| 	void (*mii_write)(struct gsw_mt753x *gsw, int phy, int reg, u16 val); |  | ||||||
|  |  | ||||||
| 	int (*mmd_read)(struct gsw_mt753x *gsw, int addr, int devad, u16 reg); |  | ||||||
| 	void (*mmd_write)(struct gsw_mt753x *gsw, int addr, int devad, u16 reg, |  | ||||||
| 			  u16 val); |  | ||||||
|  |  | ||||||
| 	struct list_head list; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct chip_rev { |  | ||||||
| 	const char *name; |  | ||||||
| 	u32 rev; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct mt753x_sw_id { |  | ||||||
| 	enum mt753x_model model; |  | ||||||
| 	int (*detect)(struct gsw_mt753x *gsw, struct chip_rev *crev); |  | ||||||
| 	int (*init)(struct gsw_mt753x *gsw); |  | ||||||
| 	int (*post_init)(struct gsw_mt753x *gsw); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| extern struct list_head mt753x_devs; |  | ||||||
|  |  | ||||||
| struct gsw_mt753x *mt753x_get_gsw(u32 id); |  | ||||||
| struct gsw_mt753x *mt753x_get_first_gsw(void); |  | ||||||
| void mt753x_put_gsw(void); |  | ||||||
| void mt753x_lock_gsw(void); |  | ||||||
|  |  | ||||||
| u32 mt753x_reg_read(struct gsw_mt753x *gsw, u32 reg); |  | ||||||
| void mt753x_reg_write(struct gsw_mt753x *gsw, u32 reg, u32 val); |  | ||||||
|  |  | ||||||
| int mt753x_mii_read(struct gsw_mt753x *gsw, int phy, int reg); |  | ||||||
| void mt753x_mii_write(struct gsw_mt753x *gsw, int phy, int reg, u16 val); |  | ||||||
|  |  | ||||||
| int mt753x_mmd_read(struct gsw_mt753x *gsw, int addr, int devad, u16 reg); |  | ||||||
| void mt753x_mmd_write(struct gsw_mt753x *gsw, int addr, int devad, u16 reg, |  | ||||||
| 		      u16 val); |  | ||||||
|  |  | ||||||
| int mt753x_mmd_ind_read(struct gsw_mt753x *gsw, int addr, int devad, u16 reg); |  | ||||||
| void mt753x_mmd_ind_write(struct gsw_mt753x *gsw, int addr, int devad, u16 reg, |  | ||||||
| 			  u16 val); |  | ||||||
|  |  | ||||||
| void mt753x_irq_worker(struct work_struct *work); |  | ||||||
| void mt753x_irq_enable(struct gsw_mt753x *gsw); |  | ||||||
|  |  | ||||||
| /* MDIO Indirect Access Registers */ |  | ||||||
| #define MII_MMD_ACC_CTL_REG		0x0d |  | ||||||
| #define MMD_CMD_S			14 |  | ||||||
| #define MMD_CMD_M			0xc000 |  | ||||||
| #define MMD_DEVAD_S			0 |  | ||||||
| #define MMD_DEVAD_M			0x1f |  | ||||||
|  |  | ||||||
| /* MMD_CMD: MMD commands */ |  | ||||||
| #define MMD_ADDR			0 |  | ||||||
| #define MMD_DATA			1 |  | ||||||
|  |  | ||||||
| #define MII_MMD_ADDR_DATA_REG		0x0e |  | ||||||
|  |  | ||||||
| /* Procedure of MT753x Internal Register Access |  | ||||||
|  * |  | ||||||
|  * 1. Internal Register Address |  | ||||||
|  * |  | ||||||
|  *    The MT753x has a 16-bit register address and each register is 32-bit. |  | ||||||
|  *    This means the lowest two bits are not used as the register address is |  | ||||||
|  *    4-byte aligned. |  | ||||||
|  * |  | ||||||
|  *    Rest of the valid bits are divided into two parts: |  | ||||||
|  *      Bit 15..6 is the Page address |  | ||||||
|  *      Bit 5..2 is the low address |  | ||||||
|  * |  | ||||||
|  *    ------------------------------------------------------------------- |  | ||||||
|  *    | 15  14  13  12  11  10   9   8   7   6 | 5   4   3   2 | 1   0  | |  | ||||||
|  *    |----------------------------------------|---------------|--------| |  | ||||||
|  *    |              Page Address              |    Address    | Unused | |  | ||||||
|  *    ------------------------------------------------------------------- |  | ||||||
|  * |  | ||||||
|  * 2. MDIO access timing |  | ||||||
|  * |  | ||||||
|  *    The MT753x uses the following MDIO timing for a single register read |  | ||||||
|  * |  | ||||||
|  *      Phase 1: Write Page Address |  | ||||||
|  *    ------------------------------------------------------------------- |  | ||||||
|  *    | ST | OP | PHY_ADDR | TYPE | RSVD | TA |  RSVD |    PAGE_ADDR    | |  | ||||||
|  *    ------------------------------------------------------------------- |  | ||||||
|  *    | 01 | 01 |   11111  |   1  | 1111 | xx | 00000 | REG_ADDR[15..6] | |  | ||||||
|  *    ------------------------------------------------------------------- |  | ||||||
|  * |  | ||||||
|  *      Phase 2: Write low Address & Read low word |  | ||||||
|  *    ------------------------------------------------------------------- |  | ||||||
|  *    | ST | OP | PHY_ADDR | TYPE |    LOW_ADDR    | TA |      DATA     | |  | ||||||
|  *    ------------------------------------------------------------------- |  | ||||||
|  *    | 01 | 10 |   11111  |   0  | REG_ADDR[5..2] | xx |  DATA[15..0]  | |  | ||||||
|  *    ------------------------------------------------------------------- |  | ||||||
|  * |  | ||||||
|  *      Phase 3: Read high word |  | ||||||
|  *    ------------------------------------------------------------------- |  | ||||||
|  *    | ST | OP | PHY_ADDR | TYPE | RSVD | TA |           DATA          | |  | ||||||
|  *    ------------------------------------------------------------------- |  | ||||||
|  *    | 01 | 10 |   11111  |   1  | 0000 | xx |       DATA[31..16]      | |  | ||||||
|  *    ------------------------------------------------------------------- |  | ||||||
|  * |  | ||||||
|  *    The MT753x uses the following MDIO timing for a single register write |  | ||||||
|  * |  | ||||||
|  *      Phase 1: Write Page Address (The same as read) |  | ||||||
|  * |  | ||||||
|  *      Phase 2: Write low Address and low word |  | ||||||
|  *    ------------------------------------------------------------------- |  | ||||||
|  *    | ST | OP | PHY_ADDR | TYPE |    LOW_ADDR    | TA |      DATA     | |  | ||||||
|  *    ------------------------------------------------------------------- |  | ||||||
|  *    | 01 | 01 |   11111  |   0  | REG_ADDR[5..2] | xx |  DATA[15..0]  | |  | ||||||
|  *    ------------------------------------------------------------------- |  | ||||||
|  * |  | ||||||
|  *      Phase 3: write high word |  | ||||||
|  *    ------------------------------------------------------------------- |  | ||||||
|  *    | ST | OP | PHY_ADDR | TYPE | RSVD | TA |           DATA          | |  | ||||||
|  *    ------------------------------------------------------------------- |  | ||||||
|  *    | 01 | 01 |   11111  |   1  | 0000 | xx |       DATA[31..16]      | |  | ||||||
|  *    ------------------------------------------------------------------- |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| /* Internal Register Address fields */ |  | ||||||
| #define MT753X_REG_PAGE_ADDR_S		6 |  | ||||||
| #define MT753X_REG_PAGE_ADDR_M		0xffc0 |  | ||||||
| #define MT753X_REG_ADDR_S		2 |  | ||||||
| #define MT753X_REG_ADDR_M		0x3c |  | ||||||
| #endif /* _MT753X_H_ */ |  | ||||||
| @@ -1,90 +0,0 @@ | |||||||
| // SPDX-License-Identifier: GPL-2.0-only |  | ||||||
| /* |  | ||||||
|  * Copyright (c) 2018 MediaTek Inc. |  | ||||||
|  * Author: Weijie Gao <weijie.gao@mediatek.com> |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include <linux/kernel.h> |  | ||||||
| #include <linux/delay.h> |  | ||||||
|  |  | ||||||
| #include "mt753x.h" |  | ||||||
| #include "mt753x_regs.h" |  | ||||||
|  |  | ||||||
| void mt753x_irq_enable(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	u32 val; |  | ||||||
| 	int i; |  | ||||||
|  |  | ||||||
| 	/* Record initial PHY link status */ |  | ||||||
| 	for (i = 0; i < MT753X_NUM_PHYS; i++) { |  | ||||||
| 		val = gsw->mii_read(gsw, i, MII_BMSR); |  | ||||||
| 		if (val & BMSR_LSTATUS) |  | ||||||
| 			gsw->phy_link_sts |= BIT(i); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	val = BIT(MT753X_NUM_PHYS) - 1; |  | ||||||
|  |  | ||||||
| 	mt753x_reg_write(gsw, SYS_INT_EN, val); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void display_port_link_status(struct gsw_mt753x *gsw, u32 port) |  | ||||||
| { |  | ||||||
| 	u32 pmsr, speed_bits; |  | ||||||
| 	const char *speed; |  | ||||||
|  |  | ||||||
| 	pmsr = mt753x_reg_read(gsw, PMSR(port)); |  | ||||||
|  |  | ||||||
| 	speed_bits = (pmsr & MAC_SPD_STS_M) >> MAC_SPD_STS_S; |  | ||||||
|  |  | ||||||
| 	switch (speed_bits) { |  | ||||||
| 	case MAC_SPD_10: |  | ||||||
| 		speed = "10Mbps"; |  | ||||||
| 		break; |  | ||||||
| 	case MAC_SPD_100: |  | ||||||
| 		speed = "100Mbps"; |  | ||||||
| 		break; |  | ||||||
| 	case MAC_SPD_1000: |  | ||||||
| 		speed = "1Gbps"; |  | ||||||
| 		break; |  | ||||||
| 	case MAC_SPD_2500: |  | ||||||
| 		speed = "2.5Gbps"; |  | ||||||
| 		break; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (pmsr & MAC_LNK_STS) { |  | ||||||
| 		dev_info(gsw->dev, "Port %d Link is Up - %s/%s\n", |  | ||||||
| 			 port, speed, (pmsr & MAC_DPX_STS) ? "Full" : "Half"); |  | ||||||
| 	} else { |  | ||||||
| 		dev_info(gsw->dev, "Port %d Link is Down\n", port); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void mt753x_irq_worker(struct work_struct *work) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw; |  | ||||||
| 	u32 sts, physts, laststs; |  | ||||||
| 	int i; |  | ||||||
|  |  | ||||||
| 	gsw = container_of(work, struct gsw_mt753x, irq_worker); |  | ||||||
|  |  | ||||||
| 	sts = mt753x_reg_read(gsw, SYS_INT_STS); |  | ||||||
|  |  | ||||||
| 	/* Check for changed PHY link status */ |  | ||||||
| 	for (i = 0; i < MT753X_NUM_PHYS; i++) { |  | ||||||
| 		if (!(sts & PHY_LC_INT(i))) |  | ||||||
| 			continue; |  | ||||||
|  |  | ||||||
| 		laststs = gsw->phy_link_sts & BIT(i); |  | ||||||
| 		physts = !!(gsw->mii_read(gsw, i, MII_BMSR) & BMSR_LSTATUS); |  | ||||||
| 		physts <<= i; |  | ||||||
|  |  | ||||||
| 		if (physts ^ laststs) { |  | ||||||
| 			gsw->phy_link_sts ^= BIT(i); |  | ||||||
| 			display_port_link_status(gsw, i); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	mt753x_reg_write(gsw, SYS_INT_STS, sts); |  | ||||||
|  |  | ||||||
| 	enable_irq(gsw->irq); |  | ||||||
| } |  | ||||||
| @@ -1,597 +0,0 @@ | |||||||
| // SPDX-License-Identifier: GPL-2.0-only |  | ||||||
| /* |  | ||||||
|  * Copyright (c) 2018 MediaTek Inc. |  | ||||||
|  * Author: Weijie Gao <weijie.gao@mediatek.com> |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include <linux/kernel.h> |  | ||||||
| #include <linux/module.h> |  | ||||||
| #include <linux/init.h> |  | ||||||
| #include <linux/device.h> |  | ||||||
| #include <linux/delay.h> |  | ||||||
| #include <linux/reset.h> |  | ||||||
| #include <linux/hrtimer.h> |  | ||||||
| #include <linux/mii.h> |  | ||||||
| #include <linux/of_mdio.h> |  | ||||||
| #include <linux/of_platform.h> |  | ||||||
| #include <linux/of_gpio.h> |  | ||||||
| #include <linux/of_net.h> |  | ||||||
| #include <linux/of_irq.h> |  | ||||||
| #include <linux/phy.h> |  | ||||||
|  |  | ||||||
| #include "mt753x.h" |  | ||||||
| #include "mt753x_swconfig.h" |  | ||||||
| #include "mt753x_regs.h" |  | ||||||
| #include "mt753x_nl.h" |  | ||||||
| #include "mt7530.h" |  | ||||||
| #include "mt7531.h" |  | ||||||
|  |  | ||||||
| static u32 mt753x_id; |  | ||||||
| struct list_head mt753x_devs; |  | ||||||
| static DEFINE_MUTEX(mt753x_devs_lock); |  | ||||||
|  |  | ||||||
| static struct mt753x_sw_id *mt753x_sw_ids[] = { |  | ||||||
| 	&mt7530_id, |  | ||||||
| 	&mt7531_id, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| u32 mt753x_reg_read(struct gsw_mt753x *gsw, u32 reg) |  | ||||||
| { |  | ||||||
| 	u32 high, low; |  | ||||||
|  |  | ||||||
| 	mutex_lock(&gsw->host_bus->mdio_lock); |  | ||||||
|  |  | ||||||
| 	gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x1f, |  | ||||||
| 		(reg & MT753X_REG_PAGE_ADDR_M) >> MT753X_REG_PAGE_ADDR_S); |  | ||||||
|  |  | ||||||
| 	low = gsw->host_bus->read(gsw->host_bus, gsw->smi_addr, |  | ||||||
| 		(reg & MT753X_REG_ADDR_M) >> MT753X_REG_ADDR_S); |  | ||||||
|  |  | ||||||
| 	high = gsw->host_bus->read(gsw->host_bus, gsw->smi_addr, 0x10); |  | ||||||
|  |  | ||||||
| 	mutex_unlock(&gsw->host_bus->mdio_lock); |  | ||||||
|  |  | ||||||
| 	return (high << 16) | (low & 0xffff); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void mt753x_reg_write(struct gsw_mt753x *gsw, u32 reg, u32 val) |  | ||||||
| { |  | ||||||
| 	mutex_lock(&gsw->host_bus->mdio_lock); |  | ||||||
|  |  | ||||||
| 	gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x1f, |  | ||||||
| 		(reg & MT753X_REG_PAGE_ADDR_M) >> MT753X_REG_PAGE_ADDR_S); |  | ||||||
|  |  | ||||||
| 	gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, |  | ||||||
| 		(reg & MT753X_REG_ADDR_M) >> MT753X_REG_ADDR_S, val & 0xffff); |  | ||||||
|  |  | ||||||
| 	gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x10, val >> 16); |  | ||||||
|  |  | ||||||
| 	mutex_unlock(&gsw->host_bus->mdio_lock); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Indirect MDIO clause 22/45 access */ |  | ||||||
| static int mt753x_mii_rw(struct gsw_mt753x *gsw, int phy, int reg, u16 data, |  | ||||||
| 			 u32 cmd, u32 st) |  | ||||||
| { |  | ||||||
| 	ktime_t timeout; |  | ||||||
| 	u32 val, timeout_us; |  | ||||||
| 	int ret = 0; |  | ||||||
|  |  | ||||||
| 	timeout_us = 100000; |  | ||||||
| 	timeout = ktime_add_us(ktime_get(), timeout_us); |  | ||||||
| 	while (1) { |  | ||||||
| 		val = mt753x_reg_read(gsw, PHY_IAC); |  | ||||||
|  |  | ||||||
| 		if ((val & PHY_ACS_ST) == 0) |  | ||||||
| 			break; |  | ||||||
|  |  | ||||||
| 		if (ktime_compare(ktime_get(), timeout) > 0) |  | ||||||
| 			return -ETIMEDOUT; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	val = (st << MDIO_ST_S) | |  | ||||||
| 	      ((cmd << MDIO_CMD_S) & MDIO_CMD_M) | |  | ||||||
| 	      ((phy << MDIO_PHY_ADDR_S) & MDIO_PHY_ADDR_M) | |  | ||||||
| 	      ((reg << MDIO_REG_ADDR_S) & MDIO_REG_ADDR_M); |  | ||||||
|  |  | ||||||
| 	if (cmd == MDIO_CMD_WRITE || cmd == MDIO_CMD_ADDR) |  | ||||||
| 		val |= data & MDIO_RW_DATA_M; |  | ||||||
|  |  | ||||||
| 	mt753x_reg_write(gsw, PHY_IAC, val | PHY_ACS_ST); |  | ||||||
|  |  | ||||||
| 	timeout_us = 100000; |  | ||||||
| 	timeout = ktime_add_us(ktime_get(), timeout_us); |  | ||||||
| 	while (1) { |  | ||||||
| 		val = mt753x_reg_read(gsw, PHY_IAC); |  | ||||||
|  |  | ||||||
| 		if ((val & PHY_ACS_ST) == 0) |  | ||||||
| 			break; |  | ||||||
|  |  | ||||||
| 		if (ktime_compare(ktime_get(), timeout) > 0) |  | ||||||
| 			return -ETIMEDOUT; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (cmd == MDIO_CMD_READ || cmd == MDIO_CMD_READ_C45) { |  | ||||||
| 		val = mt753x_reg_read(gsw, PHY_IAC); |  | ||||||
| 		ret = val & MDIO_RW_DATA_M; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int mt753x_mii_read(struct gsw_mt753x *gsw, int phy, int reg) |  | ||||||
| { |  | ||||||
| 	int val; |  | ||||||
|  |  | ||||||
| 	if (phy < MT753X_NUM_PHYS) |  | ||||||
| 		phy = (gsw->phy_base + phy) & MT753X_SMI_ADDR_MASK; |  | ||||||
|  |  | ||||||
| 	mutex_lock(&gsw->mii_lock); |  | ||||||
| 	val = mt753x_mii_rw(gsw, phy, reg, 0, MDIO_CMD_READ, MDIO_ST_C22); |  | ||||||
| 	mutex_unlock(&gsw->mii_lock); |  | ||||||
|  |  | ||||||
| 	return val; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void mt753x_mii_write(struct gsw_mt753x *gsw, int phy, int reg, u16 val) |  | ||||||
| { |  | ||||||
| 	if (phy < MT753X_NUM_PHYS) |  | ||||||
| 		phy = (gsw->phy_base + phy) & MT753X_SMI_ADDR_MASK; |  | ||||||
|  |  | ||||||
| 	mutex_lock(&gsw->mii_lock); |  | ||||||
| 	mt753x_mii_rw(gsw, phy, reg, val, MDIO_CMD_WRITE, MDIO_ST_C22); |  | ||||||
| 	mutex_unlock(&gsw->mii_lock); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int mt753x_mmd_read(struct gsw_mt753x *gsw, int addr, int devad, u16 reg) |  | ||||||
| { |  | ||||||
| 	int val; |  | ||||||
|  |  | ||||||
| 	if (addr < MT753X_NUM_PHYS) |  | ||||||
| 		addr = (gsw->phy_base + addr) & MT753X_SMI_ADDR_MASK; |  | ||||||
|  |  | ||||||
| 	mutex_lock(&gsw->mii_lock); |  | ||||||
| 	mt753x_mii_rw(gsw, addr, devad, reg, MDIO_CMD_ADDR, MDIO_ST_C45); |  | ||||||
| 	val = mt753x_mii_rw(gsw, addr, devad, 0, MDIO_CMD_READ_C45, |  | ||||||
| 			    MDIO_ST_C45); |  | ||||||
| 	mutex_unlock(&gsw->mii_lock); |  | ||||||
|  |  | ||||||
| 	return val; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void mt753x_mmd_write(struct gsw_mt753x *gsw, int addr, int devad, u16 reg, |  | ||||||
| 		      u16 val) |  | ||||||
| { |  | ||||||
| 	if (addr < MT753X_NUM_PHYS) |  | ||||||
| 		addr = (gsw->phy_base + addr) & MT753X_SMI_ADDR_MASK; |  | ||||||
|  |  | ||||||
| 	mutex_lock(&gsw->mii_lock); |  | ||||||
| 	mt753x_mii_rw(gsw, addr, devad, reg, MDIO_CMD_ADDR, MDIO_ST_C45); |  | ||||||
| 	mt753x_mii_rw(gsw, addr, devad, val, MDIO_CMD_WRITE, MDIO_ST_C45); |  | ||||||
| 	mutex_unlock(&gsw->mii_lock); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int mt753x_mmd_ind_read(struct gsw_mt753x *gsw, int addr, int devad, u16 reg) |  | ||||||
| { |  | ||||||
| 	u16 val; |  | ||||||
|  |  | ||||||
| 	if (addr < MT753X_NUM_PHYS) |  | ||||||
| 		addr = (gsw->phy_base + addr) & MT753X_SMI_ADDR_MASK; |  | ||||||
|  |  | ||||||
| 	mutex_lock(&gsw->mii_lock); |  | ||||||
|  |  | ||||||
| 	mt753x_mii_rw(gsw, addr, MII_MMD_ACC_CTL_REG, |  | ||||||
| 		      (MMD_ADDR << MMD_CMD_S) | |  | ||||||
| 		      ((devad << MMD_DEVAD_S) & MMD_DEVAD_M), |  | ||||||
| 		      MDIO_CMD_WRITE, MDIO_ST_C22); |  | ||||||
|  |  | ||||||
| 	mt753x_mii_rw(gsw, addr, MII_MMD_ADDR_DATA_REG, reg, |  | ||||||
| 		      MDIO_CMD_WRITE, MDIO_ST_C22); |  | ||||||
|  |  | ||||||
| 	mt753x_mii_rw(gsw, addr, MII_MMD_ACC_CTL_REG, |  | ||||||
| 		      (MMD_DATA << MMD_CMD_S) | |  | ||||||
| 		      ((devad << MMD_DEVAD_S) & MMD_DEVAD_M), |  | ||||||
| 		      MDIO_CMD_WRITE, MDIO_ST_C22); |  | ||||||
|  |  | ||||||
| 	val = mt753x_mii_rw(gsw, addr, MII_MMD_ADDR_DATA_REG, 0, |  | ||||||
| 			    MDIO_CMD_READ, MDIO_ST_C22); |  | ||||||
|  |  | ||||||
| 	mutex_unlock(&gsw->mii_lock); |  | ||||||
|  |  | ||||||
| 	return val; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void mt753x_mmd_ind_write(struct gsw_mt753x *gsw, int addr, int devad, u16 reg, |  | ||||||
| 			  u16 val) |  | ||||||
| { |  | ||||||
| 	if (addr < MT753X_NUM_PHYS) |  | ||||||
| 		addr = (gsw->phy_base + addr) & MT753X_SMI_ADDR_MASK; |  | ||||||
|  |  | ||||||
| 	mutex_lock(&gsw->mii_lock); |  | ||||||
|  |  | ||||||
| 	mt753x_mii_rw(gsw, addr, MII_MMD_ACC_CTL_REG, |  | ||||||
| 		      (MMD_ADDR << MMD_CMD_S) | |  | ||||||
| 		      ((devad << MMD_DEVAD_S) & MMD_DEVAD_M), |  | ||||||
| 		      MDIO_CMD_WRITE, MDIO_ST_C22); |  | ||||||
|  |  | ||||||
| 	mt753x_mii_rw(gsw, addr, MII_MMD_ADDR_DATA_REG, reg, |  | ||||||
| 		      MDIO_CMD_WRITE, MDIO_ST_C22); |  | ||||||
|  |  | ||||||
| 	mt753x_mii_rw(gsw, addr, MII_MMD_ACC_CTL_REG, |  | ||||||
| 		      (MMD_DATA << MMD_CMD_S) | |  | ||||||
| 		      ((devad << MMD_DEVAD_S) & MMD_DEVAD_M), |  | ||||||
| 		      MDIO_CMD_WRITE, MDIO_ST_C22); |  | ||||||
|  |  | ||||||
| 	mt753x_mii_rw(gsw, addr, MII_MMD_ADDR_DATA_REG, val, |  | ||||||
| 		      MDIO_CMD_WRITE, MDIO_ST_C22); |  | ||||||
|  |  | ||||||
| 	mutex_unlock(&gsw->mii_lock); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline int mt753x_get_duplex(const struct device_node *np) |  | ||||||
| { |  | ||||||
| 	return of_property_read_bool(np, "full-duplex"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void mt753x_load_port_cfg(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	struct device_node *port_np; |  | ||||||
| 	struct device_node *fixed_link_node; |  | ||||||
| 	struct mt753x_port_cfg *port_cfg; |  | ||||||
| 	u32 port; |  | ||||||
|  |  | ||||||
| 	for_each_child_of_node(gsw->dev->of_node, port_np) { |  | ||||||
| 		if (!of_device_is_compatible(port_np, "mediatek,mt753x-port")) |  | ||||||
| 			continue; |  | ||||||
|  |  | ||||||
| 		if (!of_device_is_available(port_np)) |  | ||||||
| 			continue; |  | ||||||
|  |  | ||||||
| 		if (of_property_read_u32(port_np, "reg", &port)) |  | ||||||
| 			continue; |  | ||||||
|  |  | ||||||
| 		switch (port) { |  | ||||||
| 		case 5: |  | ||||||
| 			port_cfg = &gsw->port5_cfg; |  | ||||||
| 			break; |  | ||||||
| 		case 6: |  | ||||||
| 			port_cfg = &gsw->port6_cfg; |  | ||||||
| 			break; |  | ||||||
| 		default: |  | ||||||
| 			continue; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if (port_cfg->enabled) { |  | ||||||
| 			dev_info(gsw->dev, "duplicated node for port%d\n", |  | ||||||
| 				 port_cfg->phy_mode); |  | ||||||
| 			continue; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		port_cfg->np = port_np; |  | ||||||
|  |  | ||||||
| 		if (of_get_phy_mode(port_np, &port_cfg->phy_mode) < 0) { |  | ||||||
| 			dev_info(gsw->dev, "incorrect phy-mode %d\n", port); |  | ||||||
| 			continue; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		fixed_link_node = of_get_child_by_name(port_np, "fixed-link"); |  | ||||||
| 		if (fixed_link_node) { |  | ||||||
| 			u32 speed; |  | ||||||
|  |  | ||||||
| 			port_cfg->force_link = 1; |  | ||||||
| 			port_cfg->duplex = mt753x_get_duplex(fixed_link_node); |  | ||||||
|  |  | ||||||
| 			if (of_property_read_u32(fixed_link_node, "speed", |  | ||||||
| 						 &speed)) { |  | ||||||
| 				speed = 0; |  | ||||||
| 				continue; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			of_node_put(fixed_link_node); |  | ||||||
|  |  | ||||||
| 			switch (speed) { |  | ||||||
| 			case 10: |  | ||||||
| 				port_cfg->speed = MAC_SPD_10; |  | ||||||
| 				break; |  | ||||||
| 			case 100: |  | ||||||
| 				port_cfg->speed = MAC_SPD_100; |  | ||||||
| 				break; |  | ||||||
| 			case 1000: |  | ||||||
| 				port_cfg->speed = MAC_SPD_1000; |  | ||||||
| 				break; |  | ||||||
| 			case 2500: |  | ||||||
| 				port_cfg->speed = MAC_SPD_2500; |  | ||||||
| 				break; |  | ||||||
| 			default: |  | ||||||
| 				dev_info(gsw->dev, "incorrect speed %d\n", |  | ||||||
| 					 speed); |  | ||||||
| 				continue; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		port_cfg->enabled = 1; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void mt753x_add_gsw(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	mutex_lock(&mt753x_devs_lock); |  | ||||||
| 	gsw->id = mt753x_id++; |  | ||||||
| 	INIT_LIST_HEAD(&gsw->list); |  | ||||||
| 	list_add_tail(&gsw->list, &mt753x_devs); |  | ||||||
| 	mutex_unlock(&mt753x_devs_lock); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void mt753x_remove_gsw(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	mutex_lock(&mt753x_devs_lock); |  | ||||||
| 	list_del(&gsw->list); |  | ||||||
| 	mutex_unlock(&mt753x_devs_lock); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| struct gsw_mt753x *mt753x_get_gsw(u32 id) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *dev; |  | ||||||
|  |  | ||||||
| 	mutex_lock(&mt753x_devs_lock); |  | ||||||
|  |  | ||||||
| 	list_for_each_entry(dev, &mt753x_devs, list) { |  | ||||||
| 		if (dev->id == id) |  | ||||||
| 			return dev; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	mutex_unlock(&mt753x_devs_lock); |  | ||||||
|  |  | ||||||
| 	return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct gsw_mt753x *mt753x_get_first_gsw(void) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *dev; |  | ||||||
|  |  | ||||||
| 	mutex_lock(&mt753x_devs_lock); |  | ||||||
|  |  | ||||||
| 	list_for_each_entry(dev, &mt753x_devs, list) |  | ||||||
| 		return dev; |  | ||||||
|  |  | ||||||
| 	mutex_unlock(&mt753x_devs_lock); |  | ||||||
|  |  | ||||||
| 	return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void mt753x_put_gsw(void) |  | ||||||
| { |  | ||||||
| 	mutex_unlock(&mt753x_devs_lock); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void mt753x_lock_gsw(void) |  | ||||||
| { |  | ||||||
| 	mutex_lock(&mt753x_devs_lock); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_hw_reset(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	struct device_node *np = gsw->dev->of_node; |  | ||||||
| 	struct reset_control *rstc; |  | ||||||
| 	int mcm; |  | ||||||
| 	int ret = -EINVAL; |  | ||||||
|  |  | ||||||
| 	mcm = of_property_read_bool(np, "mediatek,mcm"); |  | ||||||
| 	if (mcm) { |  | ||||||
| 		rstc = devm_reset_control_get(gsw->dev, "mcm"); |  | ||||||
| 		ret = IS_ERR(rstc); |  | ||||||
| 		if (IS_ERR(rstc)) { |  | ||||||
| 			dev_err(gsw->dev, "Missing reset ctrl of switch\n"); |  | ||||||
| 			return ret; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		reset_control_assert(rstc); |  | ||||||
| 		msleep(30); |  | ||||||
| 		reset_control_deassert(rstc); |  | ||||||
|  |  | ||||||
| 		gsw->reset_pin = -1; |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	gsw->reset_pin = of_get_named_gpio(np, "reset-gpios", 0); |  | ||||||
| 	if (gsw->reset_pin < 0) { |  | ||||||
| 		dev_err(gsw->dev, "Missing reset pin of switch\n"); |  | ||||||
| 		return ret; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ret = devm_gpio_request(gsw->dev, gsw->reset_pin, "mt753x-reset"); |  | ||||||
| 	if (ret) { |  | ||||||
| 		dev_info(gsw->dev, "Failed to request gpio %d\n", |  | ||||||
| 			 gsw->reset_pin); |  | ||||||
| 		return ret; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	gpio_direction_output(gsw->reset_pin, 0); |  | ||||||
| 	msleep(30); |  | ||||||
| 	gpio_set_value(gsw->reset_pin, 1); |  | ||||||
| 	msleep(500); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static irqreturn_t mt753x_irq_handler(int irq, void *dev) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw = dev; |  | ||||||
|  |  | ||||||
| 	disable_irq_nosync(gsw->irq); |  | ||||||
|  |  | ||||||
| 	schedule_work(&gsw->irq_worker); |  | ||||||
|  |  | ||||||
| 	return IRQ_HANDLED; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_probe(struct platform_device *pdev) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw; |  | ||||||
| 	struct mt753x_sw_id *sw; |  | ||||||
| 	struct device_node *np = pdev->dev.of_node; |  | ||||||
| 	struct device_node *mdio; |  | ||||||
| 	struct mii_bus *mdio_bus; |  | ||||||
| 	int ret = -EINVAL; |  | ||||||
| 	struct chip_rev rev; |  | ||||||
| 	struct mt753x_mapping *map; |  | ||||||
| 	int i; |  | ||||||
|  |  | ||||||
| 	mdio = of_parse_phandle(np, "mediatek,mdio", 0); |  | ||||||
| 	if (!mdio) |  | ||||||
| 		return -EINVAL; |  | ||||||
|  |  | ||||||
| 	mdio_bus = of_mdio_find_bus(mdio); |  | ||||||
| 	if (!mdio_bus) |  | ||||||
| 		return -EPROBE_DEFER; |  | ||||||
|  |  | ||||||
| 	gsw = devm_kzalloc(&pdev->dev, sizeof(struct gsw_mt753x), GFP_KERNEL); |  | ||||||
| 	if (!gsw) |  | ||||||
| 		return -ENOMEM; |  | ||||||
|  |  | ||||||
| 	gsw->host_bus = mdio_bus; |  | ||||||
| 	gsw->dev = &pdev->dev; |  | ||||||
| 	mutex_init(&gsw->mii_lock); |  | ||||||
|  |  | ||||||
| 	/* Switch hard reset */ |  | ||||||
| 	if (mt753x_hw_reset(gsw)) |  | ||||||
| 		goto fail; |  | ||||||
|  |  | ||||||
| 	/* Fetch the SMI address dirst */ |  | ||||||
| 	if (of_property_read_u32(np, "mediatek,smi-addr", &gsw->smi_addr)) |  | ||||||
| 		gsw->smi_addr = MT753X_DFL_SMI_ADDR; |  | ||||||
|  |  | ||||||
| 	/* Get LAN/WAN port mapping */ |  | ||||||
| 	map = mt753x_find_mapping(np); |  | ||||||
| 	if (map) { |  | ||||||
| 		mt753x_apply_mapping(gsw, map); |  | ||||||
| 		gsw->global_vlan_enable = 1; |  | ||||||
| 		dev_info(gsw->dev, "LAN/WAN VLAN setting=%s\n", map->name); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* Load MAC port configurations */ |  | ||||||
| 	mt753x_load_port_cfg(gsw); |  | ||||||
|  |  | ||||||
| 	/* Check for valid switch and then initialize */ |  | ||||||
| 	for (i = 0; i < ARRAY_SIZE(mt753x_sw_ids); i++) { |  | ||||||
| 		if (!mt753x_sw_ids[i]->detect(gsw, &rev)) { |  | ||||||
| 			sw = mt753x_sw_ids[i]; |  | ||||||
|  |  | ||||||
| 			gsw->name = rev.name; |  | ||||||
| 			gsw->model = sw->model; |  | ||||||
|  |  | ||||||
| 			dev_info(gsw->dev, "Switch is MediaTek %s rev %d", |  | ||||||
| 				 gsw->name, rev.rev); |  | ||||||
|  |  | ||||||
| 			/* Initialize the switch */ |  | ||||||
| 			ret = sw->init(gsw); |  | ||||||
| 			if (ret) |  | ||||||
| 				goto fail; |  | ||||||
|  |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (i >= ARRAY_SIZE(mt753x_sw_ids)) { |  | ||||||
| 		dev_err(gsw->dev, "No mt753x switch found\n"); |  | ||||||
| 		goto fail; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	gsw->irq = platform_get_irq(pdev, 0); |  | ||||||
| 	if (gsw->irq >= 0) { |  | ||||||
| 		ret = devm_request_irq(gsw->dev, gsw->irq, mt753x_irq_handler, |  | ||||||
| 				       0, dev_name(gsw->dev), gsw); |  | ||||||
| 		if (ret) { |  | ||||||
| 			dev_err(gsw->dev, "Failed to request irq %d\n", |  | ||||||
| 				gsw->irq); |  | ||||||
| 			goto fail; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		INIT_WORK(&gsw->irq_worker, mt753x_irq_worker); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	platform_set_drvdata(pdev, gsw); |  | ||||||
|  |  | ||||||
| 	gsw->phy_status_poll = of_property_read_bool(gsw->dev->of_node, |  | ||||||
| 						     "mediatek,phy-poll"); |  | ||||||
|  |  | ||||||
| 	mt753x_add_gsw(gsw); |  | ||||||
|  |  | ||||||
| 	mt753x_swconfig_init(gsw); |  | ||||||
|  |  | ||||||
| 	if (sw->post_init) |  | ||||||
| 		sw->post_init(gsw); |  | ||||||
|  |  | ||||||
| 	if (gsw->irq >= 0) |  | ||||||
| 		mt753x_irq_enable(gsw); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
|  |  | ||||||
| fail: |  | ||||||
| 	devm_kfree(&pdev->dev, gsw); |  | ||||||
|  |  | ||||||
| 	return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_remove(struct platform_device *pdev) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw = platform_get_drvdata(pdev); |  | ||||||
|  |  | ||||||
| 	if (gsw->irq >= 0) |  | ||||||
| 		cancel_work_sync(&gsw->irq_worker); |  | ||||||
|  |  | ||||||
| 	if (gsw->reset_pin >= 0) |  | ||||||
| 		devm_gpio_free(&pdev->dev, gsw->reset_pin); |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_SWCONFIG |  | ||||||
| 	mt753x_swconfig_destroy(gsw); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| 	mt753x_remove_gsw(gsw); |  | ||||||
|  |  | ||||||
| 	platform_set_drvdata(pdev, NULL); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const struct of_device_id mt753x_ids[] = { |  | ||||||
| 	{ .compatible = "mediatek,mt753x" }, |  | ||||||
| 	{ }, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| MODULE_DEVICE_TABLE(of, mt753x_ids); |  | ||||||
|  |  | ||||||
| static struct platform_driver mt753x_driver = { |  | ||||||
| 	.probe = mt753x_probe, |  | ||||||
| 	.remove = mt753x_remove, |  | ||||||
| 	.driver = { |  | ||||||
| 		.name = "mt753x", |  | ||||||
| 		.of_match_table = mt753x_ids, |  | ||||||
| 	}, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static int __init mt753x_init(void) |  | ||||||
| { |  | ||||||
| 	int ret; |  | ||||||
|  |  | ||||||
| 	INIT_LIST_HEAD(&mt753x_devs); |  | ||||||
| 	ret = platform_driver_register(&mt753x_driver); |  | ||||||
|  |  | ||||||
| 	mt753x_nl_init(); |  | ||||||
|  |  | ||||||
| 	return ret; |  | ||||||
| } |  | ||||||
| module_init(mt753x_init); |  | ||||||
|  |  | ||||||
| static void __exit mt753x_exit(void) |  | ||||||
| { |  | ||||||
| 	mt753x_nl_exit(); |  | ||||||
|  |  | ||||||
| 	platform_driver_unregister(&mt753x_driver); |  | ||||||
| } |  | ||||||
| module_exit(mt753x_exit); |  | ||||||
|  |  | ||||||
| MODULE_LICENSE("GPL"); |  | ||||||
| MODULE_AUTHOR("Weijie Gao <weijie.gao@mediatek.com>"); |  | ||||||
| MODULE_DESCRIPTION("Driver for MediaTek MT753x Gigabit Switch"); |  | ||||||
| @@ -1,382 +0,0 @@ | |||||||
| // SPDX-License-Identifier: GPL-2.0-only |  | ||||||
| /* |  | ||||||
|  * Copyright (c) 2018 MediaTek Inc. |  | ||||||
|  * Author: Sirui Zhao <Sirui.Zhao@mediatek.com> |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include <linux/types.h> |  | ||||||
| #include <linux/kernel.h> |  | ||||||
| #include <linux/module.h> |  | ||||||
| #include <linux/init.h> |  | ||||||
| #include <net/genetlink.h> |  | ||||||
|  |  | ||||||
| #include "mt753x.h" |  | ||||||
| #include "mt753x_nl.h" |  | ||||||
|  |  | ||||||
| struct mt753x_nl_cmd_item { |  | ||||||
| 	enum mt753x_cmd cmd; |  | ||||||
| 	bool require_dev; |  | ||||||
| 	int (*process)(struct genl_info *info, struct gsw_mt753x *gsw); |  | ||||||
| 	u32 nr_required_attrs; |  | ||||||
| 	const enum mt753x_attr *required_attrs; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static int mt753x_nl_response(struct sk_buff *skb, struct genl_info *info); |  | ||||||
|  |  | ||||||
| /* |  | ||||||
| static const struct nla_policy mt753x_nl_cmd_policy[] = { |  | ||||||
| 	[MT753X_ATTR_TYPE_MESG] = { .type = NLA_STRING }, |  | ||||||
| 	[MT753X_ATTR_TYPE_PHY] = { .type = NLA_S32 }, |  | ||||||
| 	[MT753X_ATTR_TYPE_REG] = { .type = NLA_S32 }, |  | ||||||
| 	[MT753X_ATTR_TYPE_VAL] = { .type = NLA_S32 }, |  | ||||||
| 	[MT753X_ATTR_TYPE_DEV_NAME] = { .type = NLA_S32 }, |  | ||||||
| 	[MT753X_ATTR_TYPE_DEV_ID] = { .type = NLA_S32 }, |  | ||||||
| 	[MT753X_ATTR_TYPE_DEVAD] = { .type = NLA_S32 }, |  | ||||||
| }; |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| static const struct genl_ops mt753x_nl_ops[] = { |  | ||||||
| 	{ |  | ||||||
| 		.cmd = MT753X_CMD_REQUEST, |  | ||||||
| 		.doit = mt753x_nl_response, |  | ||||||
| //		.policy = mt753x_nl_cmd_policy, |  | ||||||
| 		.flags = GENL_ADMIN_PERM, |  | ||||||
| 	}, { |  | ||||||
| 		.cmd = MT753X_CMD_READ, |  | ||||||
| 		.doit = mt753x_nl_response, |  | ||||||
| //		.policy = mt753x_nl_cmd_policy, |  | ||||||
| 		.flags = GENL_ADMIN_PERM, |  | ||||||
| 	}, { |  | ||||||
| 		.cmd = MT753X_CMD_WRITE, |  | ||||||
| 		.doit = mt753x_nl_response, |  | ||||||
| //		.policy = mt753x_nl_cmd_policy, |  | ||||||
| 		.flags = GENL_ADMIN_PERM, |  | ||||||
| 	}, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static struct genl_family mt753x_nl_family = { |  | ||||||
| 	.name =		MT753X_GENL_NAME, |  | ||||||
| 	.version =	MT753X_GENL_VERSION, |  | ||||||
| 	.maxattr =	MT753X_NR_ATTR_TYPE, |  | ||||||
| 	.ops =		mt753x_nl_ops, |  | ||||||
| 	.n_ops =	ARRAY_SIZE(mt753x_nl_ops), |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static int mt753x_nl_list_devs(char *buff, int size) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw; |  | ||||||
| 	int len, total = 0; |  | ||||||
| 	char buf[80]; |  | ||||||
|  |  | ||||||
| 	memset(buff, 0, size); |  | ||||||
|  |  | ||||||
| 	mt753x_lock_gsw(); |  | ||||||
|  |  | ||||||
| 	list_for_each_entry(gsw, &mt753x_devs, list) { |  | ||||||
| 		len = snprintf(buf, sizeof(buf), |  | ||||||
| 			       "id: %d, model: %s, node: %s\n", |  | ||||||
| 			       gsw->id, gsw->name, gsw->dev->of_node->name); |  | ||||||
| 		strncat(buff, buf, size - total); |  | ||||||
| 		total += len; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	mt753x_put_gsw(); |  | ||||||
|  |  | ||||||
| 	return total; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_nl_prepare_reply(struct genl_info *info, u8 cmd, |  | ||||||
| 				   struct sk_buff **skbp) |  | ||||||
| { |  | ||||||
| 	struct sk_buff *msg; |  | ||||||
| 	void *reply; |  | ||||||
|  |  | ||||||
| 	if (!info) |  | ||||||
| 		return -EINVAL; |  | ||||||
|  |  | ||||||
| 	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); |  | ||||||
| 	if (!msg) |  | ||||||
| 		return -ENOMEM; |  | ||||||
|  |  | ||||||
| 	/* Construct send-back message header */ |  | ||||||
| 	reply = genlmsg_put(msg, info->snd_portid, info->snd_seq, |  | ||||||
| 			    &mt753x_nl_family, 0, cmd); |  | ||||||
| 	if (!reply) { |  | ||||||
| 		nlmsg_free(msg); |  | ||||||
| 		return -EINVAL; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	*skbp = msg; |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_nl_send_reply(struct sk_buff *skb, struct genl_info *info) |  | ||||||
| { |  | ||||||
| 	struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb)); |  | ||||||
| 	void *reply = genlmsg_data(genlhdr); |  | ||||||
|  |  | ||||||
| 	/* Finalize a generic netlink message (update message header) */ |  | ||||||
| 	genlmsg_end(skb, reply); |  | ||||||
|  |  | ||||||
| 	/* reply to a request */ |  | ||||||
| 	return genlmsg_reply(skb, info); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static s32 mt753x_nl_get_s32(struct genl_info *info, enum mt753x_attr attr, |  | ||||||
| 			     s32 defval) |  | ||||||
| { |  | ||||||
| 	struct nlattr *na; |  | ||||||
|  |  | ||||||
| 	na = info->attrs[attr]; |  | ||||||
| 	if (na) |  | ||||||
| 		return nla_get_s32(na); |  | ||||||
|  |  | ||||||
| 	return defval; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_nl_get_u32(struct genl_info *info, enum mt753x_attr attr, |  | ||||||
| 			     u32 *val) |  | ||||||
| { |  | ||||||
| 	struct nlattr *na; |  | ||||||
|  |  | ||||||
| 	na = info->attrs[attr]; |  | ||||||
| 	if (na) { |  | ||||||
| 		*val = nla_get_u32(na); |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static struct gsw_mt753x *mt753x_nl_parse_find_gsw(struct genl_info *info) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw; |  | ||||||
| 	struct nlattr *na; |  | ||||||
| 	int gsw_id; |  | ||||||
|  |  | ||||||
| 	na = info->attrs[MT753X_ATTR_TYPE_DEV_ID]; |  | ||||||
| 	if (na) { |  | ||||||
| 		gsw_id = nla_get_s32(na); |  | ||||||
| 		if (gsw_id >= 0) |  | ||||||
| 			gsw = mt753x_get_gsw(gsw_id); |  | ||||||
| 		else |  | ||||||
| 			gsw = mt753x_get_first_gsw(); |  | ||||||
| 	} else { |  | ||||||
| 		gsw = mt753x_get_first_gsw(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return gsw; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_nl_get_swdevs(struct genl_info *info, struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	struct sk_buff *rep_skb = NULL; |  | ||||||
| 	char dev_info[512]; |  | ||||||
| 	int ret; |  | ||||||
|  |  | ||||||
| 	ret = mt753x_nl_list_devs(dev_info, sizeof(dev_info)); |  | ||||||
| 	if (!ret) { |  | ||||||
| 		pr_info("No switch registered\n"); |  | ||||||
| 		return -EINVAL; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ret = mt753x_nl_prepare_reply(info, MT753X_CMD_REPLY, &rep_skb); |  | ||||||
| 	if (ret < 0) |  | ||||||
| 		goto err; |  | ||||||
|  |  | ||||||
| 	ret = nla_put_string(rep_skb, MT753X_ATTR_TYPE_MESG, dev_info); |  | ||||||
| 	if (ret < 0) |  | ||||||
| 		goto err; |  | ||||||
|  |  | ||||||
| 	return mt753x_nl_send_reply(rep_skb, info); |  | ||||||
|  |  | ||||||
| err: |  | ||||||
| 	if (rep_skb) |  | ||||||
| 		nlmsg_free(rep_skb); |  | ||||||
|  |  | ||||||
| 	return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_nl_reply_read(struct genl_info *info, struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	struct sk_buff *rep_skb = NULL; |  | ||||||
| 	s32 phy, devad, reg; |  | ||||||
| 	int value; |  | ||||||
| 	int ret = 0; |  | ||||||
|  |  | ||||||
| 	phy = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_PHY, -1); |  | ||||||
| 	devad = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_DEVAD, -1); |  | ||||||
| 	reg = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_REG, -1); |  | ||||||
|  |  | ||||||
| 	if (reg < 0) |  | ||||||
| 		goto err; |  | ||||||
|  |  | ||||||
| 	ret = mt753x_nl_prepare_reply(info, MT753X_CMD_READ, &rep_skb); |  | ||||||
| 	if (ret < 0) |  | ||||||
| 		goto err; |  | ||||||
|  |  | ||||||
| 	if (phy >= 0) { |  | ||||||
| 		if (devad < 0) |  | ||||||
| 			value = gsw->mii_read(gsw, phy, reg); |  | ||||||
| 		else |  | ||||||
| 			value = gsw->mmd_read(gsw, phy, devad, reg); |  | ||||||
| 	} else { |  | ||||||
| 		value = mt753x_reg_read(gsw, reg); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ret = nla_put_s32(rep_skb, MT753X_ATTR_TYPE_REG, reg); |  | ||||||
| 	if (ret < 0) |  | ||||||
| 		goto err; |  | ||||||
|  |  | ||||||
| 	ret = nla_put_s32(rep_skb, MT753X_ATTR_TYPE_VAL, value); |  | ||||||
| 	if (ret < 0) |  | ||||||
| 		goto err; |  | ||||||
|  |  | ||||||
| 	return mt753x_nl_send_reply(rep_skb, info); |  | ||||||
|  |  | ||||||
| err: |  | ||||||
| 	if (rep_skb) |  | ||||||
| 		nlmsg_free(rep_skb); |  | ||||||
|  |  | ||||||
| 	return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_nl_reply_write(struct genl_info *info, struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	struct sk_buff *rep_skb = NULL; |  | ||||||
| 	s32 phy, devad, reg; |  | ||||||
| 	u32 value; |  | ||||||
| 	int ret = 0; |  | ||||||
|  |  | ||||||
| 	phy = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_PHY, -1); |  | ||||||
| 	devad = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_DEVAD, -1); |  | ||||||
| 	reg = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_REG, -1); |  | ||||||
|  |  | ||||||
| 	if (mt753x_nl_get_u32(info, MT753X_ATTR_TYPE_VAL, &value)) |  | ||||||
| 		goto err; |  | ||||||
|  |  | ||||||
| 	if (reg < 0) |  | ||||||
| 		goto err; |  | ||||||
|  |  | ||||||
| 	ret = mt753x_nl_prepare_reply(info, MT753X_CMD_WRITE, &rep_skb); |  | ||||||
| 	if (ret < 0) |  | ||||||
| 		goto err; |  | ||||||
|  |  | ||||||
| 	if (phy >= 0) { |  | ||||||
| 		if (devad < 0) |  | ||||||
| 			gsw->mii_write(gsw, phy, reg, value); |  | ||||||
| 		else |  | ||||||
| 			gsw->mmd_write(gsw, phy, devad, reg, value); |  | ||||||
| 	} else { |  | ||||||
| 		mt753x_reg_write(gsw, reg, value); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ret = nla_put_s32(rep_skb, MT753X_ATTR_TYPE_REG, reg); |  | ||||||
| 	if (ret < 0) |  | ||||||
| 		goto err; |  | ||||||
|  |  | ||||||
| 	ret = nla_put_s32(rep_skb, MT753X_ATTR_TYPE_VAL, value); |  | ||||||
| 	if (ret < 0) |  | ||||||
| 		goto err; |  | ||||||
|  |  | ||||||
| 	return mt753x_nl_send_reply(rep_skb, info); |  | ||||||
|  |  | ||||||
| err: |  | ||||||
| 	if (rep_skb) |  | ||||||
| 		nlmsg_free(rep_skb); |  | ||||||
|  |  | ||||||
| 	return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const enum mt753x_attr mt753x_nl_cmd_read_attrs[] = { |  | ||||||
| 	MT753X_ATTR_TYPE_REG |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static const enum mt753x_attr mt753x_nl_cmd_write_attrs[] = { |  | ||||||
| 	MT753X_ATTR_TYPE_REG, |  | ||||||
| 	MT753X_ATTR_TYPE_VAL |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static const struct mt753x_nl_cmd_item mt753x_nl_cmds[] = { |  | ||||||
| 	{ |  | ||||||
| 		.cmd = MT753X_CMD_REQUEST, |  | ||||||
| 		.require_dev = false, |  | ||||||
| 		.process = mt753x_nl_get_swdevs |  | ||||||
| 	}, { |  | ||||||
| 		.cmd = MT753X_CMD_READ, |  | ||||||
| 		.require_dev = true, |  | ||||||
| 		.process = mt753x_nl_reply_read, |  | ||||||
| 		.required_attrs = mt753x_nl_cmd_read_attrs, |  | ||||||
| 		.nr_required_attrs = ARRAY_SIZE(mt753x_nl_cmd_read_attrs), |  | ||||||
| 	}, { |  | ||||||
| 		.cmd = MT753X_CMD_WRITE, |  | ||||||
| 		.require_dev = true, |  | ||||||
| 		.process = mt753x_nl_reply_write, |  | ||||||
| 		.required_attrs = mt753x_nl_cmd_write_attrs, |  | ||||||
| 		.nr_required_attrs = ARRAY_SIZE(mt753x_nl_cmd_write_attrs), |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static int mt753x_nl_response(struct sk_buff *skb, struct genl_info *info) |  | ||||||
| { |  | ||||||
| 	struct genlmsghdr *hdr = nlmsg_data(info->nlhdr); |  | ||||||
| 	const struct mt753x_nl_cmd_item *cmditem = NULL; |  | ||||||
| 	struct gsw_mt753x *gsw = NULL; |  | ||||||
| 	u32 sat_req_attrs = 0; |  | ||||||
| 	int i, ret; |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < ARRAY_SIZE(mt753x_nl_cmds); i++) { |  | ||||||
| 		if (hdr->cmd == mt753x_nl_cmds[i].cmd) { |  | ||||||
| 			cmditem = &mt753x_nl_cmds[i]; |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (!cmditem) { |  | ||||||
| 		pr_info("mt753x-nl: unknown cmd %u\n", hdr->cmd); |  | ||||||
| 		return -EINVAL; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < cmditem->nr_required_attrs; i++) { |  | ||||||
| 		if (info->attrs[cmditem->required_attrs[i]]) |  | ||||||
| 			sat_req_attrs++; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (sat_req_attrs != cmditem->nr_required_attrs) { |  | ||||||
| 		pr_info("mt753x-nl: missing required attr(s) for cmd %u\n", |  | ||||||
| 			hdr->cmd); |  | ||||||
| 		return -EINVAL; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (cmditem->require_dev) { |  | ||||||
| 		gsw = mt753x_nl_parse_find_gsw(info); |  | ||||||
| 		if (!gsw) { |  | ||||||
| 			pr_info("mt753x-nl: failed to find switch dev\n"); |  | ||||||
| 			return -EINVAL; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ret = cmditem->process(info, gsw); |  | ||||||
|  |  | ||||||
| 	mt753x_put_gsw(); |  | ||||||
|  |  | ||||||
| 	return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int __init mt753x_nl_init(void) |  | ||||||
| { |  | ||||||
| 	int ret; |  | ||||||
|  |  | ||||||
| 	ret = genl_register_family(&mt753x_nl_family); |  | ||||||
| 	if (ret) { |  | ||||||
| 		pr_info("mt753x-nl: genl_register_family_with_ops failed\n"); |  | ||||||
| 		return ret; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void __exit mt753x_nl_exit(void) |  | ||||||
| { |  | ||||||
| 	genl_unregister_family(&mt753x_nl_family); |  | ||||||
| } |  | ||||||
| @@ -1,43 +0,0 @@ | |||||||
| /* SPDX-License-Identifier: GPL-2.0-only */ |  | ||||||
| /* |  | ||||||
|  * Copyright (c) 2018 MediaTek Inc. |  | ||||||
|  * Author: Sirui Zhao <Sirui.Zhao@mediatek.com> |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifndef _MT753X_NL_H_ |  | ||||||
| #define _MT753X_NL_H_ |  | ||||||
|  |  | ||||||
| #define MT753X_GENL_NAME		"mt753x" |  | ||||||
| #define MT753X_GENL_VERSION		0x1 |  | ||||||
|  |  | ||||||
| enum mt753x_cmd { |  | ||||||
| 	MT753X_CMD_UNSPEC = 0, |  | ||||||
| 	MT753X_CMD_REQUEST, |  | ||||||
| 	MT753X_CMD_REPLY, |  | ||||||
| 	MT753X_CMD_READ, |  | ||||||
| 	MT753X_CMD_WRITE, |  | ||||||
|  |  | ||||||
| 	__MT753X_CMD_MAX, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| enum mt753x_attr { |  | ||||||
| 	MT753X_ATTR_TYPE_UNSPEC = 0, |  | ||||||
| 	MT753X_ATTR_TYPE_MESG, |  | ||||||
| 	MT753X_ATTR_TYPE_PHY, |  | ||||||
| 	MT753X_ATTR_TYPE_DEVAD, |  | ||||||
| 	MT753X_ATTR_TYPE_REG, |  | ||||||
| 	MT753X_ATTR_TYPE_VAL, |  | ||||||
| 	MT753X_ATTR_TYPE_DEV_NAME, |  | ||||||
| 	MT753X_ATTR_TYPE_DEV_ID, |  | ||||||
|  |  | ||||||
| 	__MT753X_ATTR_TYPE_MAX, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #define MT753X_NR_ATTR_TYPE		(__MT753X_ATTR_TYPE_MAX - 1) |  | ||||||
|  |  | ||||||
| #ifdef __KERNEL__ |  | ||||||
| int __init mt753x_nl_init(void); |  | ||||||
| void __exit mt753x_nl_exit(void); |  | ||||||
| #endif /* __KERNEL__ */ |  | ||||||
|  |  | ||||||
| #endif /* _MT753X_NL_H_ */ |  | ||||||
| @@ -1,294 +0,0 @@ | |||||||
| /* SPDX-License-Identifier: GPL-2.0-only */ |  | ||||||
| /* |  | ||||||
|  * Copyright (c) 2018 MediaTek Inc. |  | ||||||
|  * Author: Weijie Gao <weijie.gao@mediatek.com> |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifndef _MT753X_REGS_H_ |  | ||||||
| #define _MT753X_REGS_H_ |  | ||||||
|  |  | ||||||
| #include <linux/bitops.h> |  | ||||||
|  |  | ||||||
| /* Values of Egress TAG Control */ |  | ||||||
| #define ETAG_CTRL_UNTAG			0 |  | ||||||
| #define ETAG_CTRL_TAG			2 |  | ||||||
| #define ETAG_CTRL_SWAP			1 |  | ||||||
| #define ETAG_CTRL_STACK			3 |  | ||||||
|  |  | ||||||
| #define VTCR				0x90 |  | ||||||
| #define VAWD1				0x94 |  | ||||||
| #define VAWD2				0x98 |  | ||||||
|  |  | ||||||
| /* Fields of VTCR */ |  | ||||||
| #define VTCR_BUSY			BIT(31) |  | ||||||
| #define IDX_INVLD			BIT(16) |  | ||||||
| #define VTCR_FUNC_S			12 |  | ||||||
| #define VTCR_FUNC_M			0xf000 |  | ||||||
| #define VTCR_VID_S			0 |  | ||||||
| #define VTCR_VID_M			0xfff |  | ||||||
|  |  | ||||||
| /* Values of VTCR_FUNC */ |  | ||||||
| #define VTCR_READ_VLAN_ENTRY		0 |  | ||||||
| #define VTCR_WRITE_VLAN_ENTRY		1 |  | ||||||
| #define VTCR_INVD_VLAN_ENTRY		2 |  | ||||||
| #define VTCR_ENABLE_VLAN_ENTRY		3 |  | ||||||
| #define VTCR_READ_ACL_ENTRY		4 |  | ||||||
| #define VTCR_WRITE_ACL_ENTRY		5 |  | ||||||
| #define VTCR_READ_TRTCM_TABLE		6 |  | ||||||
| #define VTCR_WRITE_TRTCM_TABLE		7 |  | ||||||
| #define VTCR_READ_ACL_MASK_ENTRY	8 |  | ||||||
| #define VTCR_WRITE_ACL_MASK_ENTRY	9 |  | ||||||
| #define VTCR_READ_ACL_RULE_ENTRY	10 |  | ||||||
| #define VTCR_WRITE_ACL_RULE_ENTRY	11 |  | ||||||
| #define VTCR_READ_ACL_RATE_ENTRY	12 |  | ||||||
| #define VTCR_WRITE_ACL_RATE_ENTRY	13 |  | ||||||
|  |  | ||||||
| /* VLAN entry fields */ |  | ||||||
| /* VAWD1 */ |  | ||||||
| #define PORT_STAG			BIT(31) |  | ||||||
| #define IVL_MAC				BIT(30) |  | ||||||
| #define EG_CON				BIT(29) |  | ||||||
| #define VTAG_EN				BIT(28) |  | ||||||
| #define COPY_PRI			BIT(27) |  | ||||||
| #define USER_PRI_S			24 |  | ||||||
| #define USER_PRI_M			0x7000000 |  | ||||||
| #define PORT_MEM_S			16 |  | ||||||
| #define PORT_MEM_M			0xff0000 |  | ||||||
| #define S_TAG1_S			4 |  | ||||||
| #define S_TAG1_M			0xfff0 |  | ||||||
| #define FID_S				1 |  | ||||||
| #define FID_M				0x0e |  | ||||||
| #define VENTRY_VALID			BIT(0) |  | ||||||
|  |  | ||||||
| /* VAWD2 */ |  | ||||||
| #define S_TAG2_S			16 |  | ||||||
| #define S_TAG2_M			0xffff0000 |  | ||||||
| #define PORT_ETAG_S(p)			((p) * 2) |  | ||||||
| #define PORT_ETAG_M			0x03 |  | ||||||
|  |  | ||||||
| #define PORT_CTRL_BASE			0x2000 |  | ||||||
| #define PORT_CTRL_PORT_OFFSET		0x100 |  | ||||||
| #define PORT_CTRL_REG(p, r)		(PORT_CTRL_BASE + \ |  | ||||||
| 					(p) * PORT_CTRL_PORT_OFFSET +  (r)) |  | ||||||
| #define CKGCR(p)			PORT_CTRL_REG(p, 0x00) |  | ||||||
| #define PCR(p)				PORT_CTRL_REG(p, 0x04) |  | ||||||
| #define PIC(p)				PORT_CTRL_REG(p, 0x08) |  | ||||||
| #define PSC(p)				PORT_CTRL_REG(p, 0x0c) |  | ||||||
| #define PVC(p)				PORT_CTRL_REG(p, 0x10) |  | ||||||
| #define PPBV1(p)			PORT_CTRL_REG(p, 0x14) |  | ||||||
| #define PPBV2(p)			PORT_CTRL_REG(p, 0x18) |  | ||||||
| #define BSR(p)				PORT_CTRL_REG(p, 0x1c) |  | ||||||
| #define STAG01				PORT_CTRL_REG(p, 0x20) |  | ||||||
| #define STAG23				PORT_CTRL_REG(p, 0x24) |  | ||||||
| #define STAG45				PORT_CTRL_REG(p, 0x28) |  | ||||||
| #define STAG67				PORT_CTRL_REG(p, 0x2c) |  | ||||||
|  |  | ||||||
| #define PPBV(p, g)			(PPBV1(p) + ((g) / 2) * 4) |  | ||||||
|  |  | ||||||
| /* Fields of PCR */ |  | ||||||
| #define MLDV2_EN			BIT(30) |  | ||||||
| #define EG_TAG_S			28 |  | ||||||
| #define EG_TAG_M			0x30000000 |  | ||||||
| #define PORT_PRI_S			24 |  | ||||||
| #define PORT_PRI_M			0x7000000 |  | ||||||
| #define PORT_MATRIX_S			16 |  | ||||||
| #define PORT_MATRIX_M			0xff0000 |  | ||||||
| #define UP2DSCP_EN			BIT(12) |  | ||||||
| #define UP2TAG_EN			BIT(11) |  | ||||||
| #define ACL_EN				BIT(10) |  | ||||||
| #define PORT_TX_MIR			BIT(9) |  | ||||||
| #define PORT_RX_MIR			BIT(8) |  | ||||||
| #define ACL_MIR				BIT(7) |  | ||||||
| #define MIS_PORT_FW_S			4 |  | ||||||
| #define MIS_PORT_FW_M			0x70 |  | ||||||
| #define VLAN_MIS			BIT(2) |  | ||||||
| #define PORT_VLAN_S			0 |  | ||||||
| #define PORT_VLAN_M			0x03 |  | ||||||
|  |  | ||||||
| /* Values of PORT_VLAN */ |  | ||||||
| #define PORT_MATRIX_MODE		0 |  | ||||||
| #define FALLBACK_MODE			1 |  | ||||||
| #define CHECK_MODE			2 |  | ||||||
| #define SECURITY_MODE			3 |  | ||||||
|  |  | ||||||
| /* Fields of PVC */ |  | ||||||
| #define STAG_VPID_S			16 |  | ||||||
| #define STAG_VPID_M			0xffff0000 |  | ||||||
| #define DIS_PVID			BIT(15) |  | ||||||
| #define FORCE_PVID			BIT(14) |  | ||||||
| #define PT_VPM				BIT(12) |  | ||||||
| #define PT_OPTION			BIT(11) |  | ||||||
| #define PVC_EG_TAG_S			8 |  | ||||||
| #define PVC_EG_TAG_M			0x700 |  | ||||||
| #define VLAN_ATTR_S			6 |  | ||||||
| #define VLAN_ATTR_M			0xc0 |  | ||||||
| #define PVC_PORT_STAG			BIT(5) |  | ||||||
| #define BC_LKYV_EN			BIT(4) |  | ||||||
| #define MC_LKYV_EN			BIT(3) |  | ||||||
| #define UC_LKYV_EN			BIT(2) |  | ||||||
| #define ACC_FRM_S			0 |  | ||||||
| #define ACC_FRM_M			0x03 |  | ||||||
|  |  | ||||||
| /* Values of VLAN_ATTR */ |  | ||||||
| #define VA_USER_PORT			0 |  | ||||||
| #define VA_STACK_PORT			1 |  | ||||||
| #define VA_TRANSLATION_PORT		2 |  | ||||||
| #define VA_TRANSPARENT_PORT		3 |  | ||||||
|  |  | ||||||
| /* Fields of PPBV */ |  | ||||||
| #define GRP_PORT_PRI_S(g)		(((g) % 2) * 16 + 13) |  | ||||||
| #define GRP_PORT_PRI_M			0x07 |  | ||||||
| #define GRP_PORT_VID_S(g)		(((g) % 2) * 16) |  | ||||||
| #define GRP_PORT_VID_M			0xfff |  | ||||||
|  |  | ||||||
| #define PORT_MAC_CTRL_BASE		0x3000 |  | ||||||
| #define PORT_MAC_CTRL_PORT_OFFSET	0x100 |  | ||||||
| #define PORT_MAC_CTRL_REG(p, r)		(PORT_MAC_CTRL_BASE + \ |  | ||||||
| 					(p) * PORT_MAC_CTRL_PORT_OFFSET + (r)) |  | ||||||
| #define PMCR(p)				PORT_MAC_CTRL_REG(p, 0x00) |  | ||||||
| #define PMEEECR(p)			PORT_MAC_CTRL_REG(p, 0x04) |  | ||||||
| #define PMSR(p)				PORT_MAC_CTRL_REG(p, 0x08) |  | ||||||
| #define PINT_EN(p)			PORT_MAC_CTRL_REG(p, 0x10) |  | ||||||
| #define PINT_STS(p)			PORT_MAC_CTRL_REG(p, 0x14) |  | ||||||
|  |  | ||||||
| #define GMACCR				(PORT_MAC_CTRL_BASE + 0xe0) |  | ||||||
| #define TXCRC_EN			BIT(19) |  | ||||||
| #define RXCRC_EN			BIT(18) |  | ||||||
| #define PRMBL_LMT_EN			BIT(17) |  | ||||||
| #define MTCC_LMT_S			9 |  | ||||||
| #define MTCC_LMT_M			0x1e00 |  | ||||||
| #define MAX_RX_JUMBO_S			2 |  | ||||||
| #define MAX_RX_JUMBO_M			0x3c |  | ||||||
| #define MAX_RX_PKT_LEN_S		0 |  | ||||||
| #define MAX_RX_PKT_LEN_M		0x3 |  | ||||||
|  |  | ||||||
| /* Values of MAX_RX_PKT_LEN */ |  | ||||||
| #define RX_PKT_LEN_1518			0 |  | ||||||
| #define RX_PKT_LEN_1536			1 |  | ||||||
| #define RX_PKT_LEN_1522			2 |  | ||||||
| #define RX_PKT_LEN_MAX_JUMBO		3 |  | ||||||
|  |  | ||||||
| /* Fields of PMCR */ |  | ||||||
| #define IPG_CFG_S			18 |  | ||||||
| #define IPG_CFG_M			0xc0000 |  | ||||||
| #define EXT_PHY				BIT(17) |  | ||||||
| #define MAC_MODE			BIT(16) |  | ||||||
| #define MAC_TX_EN			BIT(14) |  | ||||||
| #define MAC_RX_EN			BIT(13) |  | ||||||
| #define MAC_PRE				BIT(11) |  | ||||||
| #define BKOFF_EN			BIT(9) |  | ||||||
| #define BACKPR_EN			BIT(8) |  | ||||||
| #define FORCE_EEE1G			BIT(7) |  | ||||||
| #define FORCE_EEE1000			BIT(6) |  | ||||||
| #define FORCE_RX_FC			BIT(5) |  | ||||||
| #define FORCE_TX_FC			BIT(4) |  | ||||||
| #define FORCE_SPD_S			2 |  | ||||||
| #define FORCE_SPD_M			0x0c |  | ||||||
| #define FORCE_DPX			BIT(1) |  | ||||||
| #define FORCE_LINK			BIT(0) |  | ||||||
|  |  | ||||||
| /* Fields of PMSR */ |  | ||||||
| #define EEE1G_STS			BIT(7) |  | ||||||
| #define EEE100_STS			BIT(6) |  | ||||||
| #define RX_FC_STS			BIT(5) |  | ||||||
| #define TX_FC_STS			BIT(4) |  | ||||||
| #define MAC_SPD_STS_S			2 |  | ||||||
| #define MAC_SPD_STS_M			0x0c |  | ||||||
| #define MAC_DPX_STS			BIT(1) |  | ||||||
| #define MAC_LNK_STS			BIT(0) |  | ||||||
|  |  | ||||||
| /* Values of MAC_SPD_STS */ |  | ||||||
| #define MAC_SPD_10			0 |  | ||||||
| #define MAC_SPD_100			1 |  | ||||||
| #define MAC_SPD_1000			2 |  | ||||||
| #define MAC_SPD_2500			3 |  | ||||||
|  |  | ||||||
| /* Values of IPG_CFG */ |  | ||||||
| #define IPG_96BIT			0 |  | ||||||
| #define IPG_96BIT_WITH_SHORT_IPG	1 |  | ||||||
| #define IPG_64BIT			2 |  | ||||||
|  |  | ||||||
| #define MIB_COUNTER_BASE		0x4000 |  | ||||||
| #define MIB_COUNTER_PORT_OFFSET		0x100 |  | ||||||
| #define MIB_COUNTER_REG(p, r)		(MIB_COUNTER_BASE + \ |  | ||||||
| 					(p) * MIB_COUNTER_PORT_OFFSET + (r)) |  | ||||||
| #define STATS_TDPC			0x00 |  | ||||||
| #define STATS_TCRC			0x04 |  | ||||||
| #define STATS_TUPC			0x08 |  | ||||||
| #define STATS_TMPC			0x0C |  | ||||||
| #define STATS_TBPC			0x10 |  | ||||||
| #define STATS_TCEC			0x14 |  | ||||||
| #define STATS_TSCEC			0x18 |  | ||||||
| #define STATS_TMCEC			0x1C |  | ||||||
| #define STATS_TDEC			0x20 |  | ||||||
| #define STATS_TLCEC			0x24 |  | ||||||
| #define STATS_TXCEC			0x28 |  | ||||||
| #define STATS_TPPC			0x2C |  | ||||||
| #define STATS_TL64PC			0x30 |  | ||||||
| #define STATS_TL65PC			0x34 |  | ||||||
| #define STATS_TL128PC			0x38 |  | ||||||
| #define STATS_TL256PC			0x3C |  | ||||||
| #define STATS_TL512PC			0x40 |  | ||||||
| #define STATS_TL1024PC			0x44 |  | ||||||
| #define STATS_TOC			0x48 |  | ||||||
| #define STATS_RDPC			0x60 |  | ||||||
| #define STATS_RFPC			0x64 |  | ||||||
| #define STATS_RUPC			0x68 |  | ||||||
| #define STATS_RMPC			0x6C |  | ||||||
| #define STATS_RBPC			0x70 |  | ||||||
| #define STATS_RAEPC			0x74 |  | ||||||
| #define STATS_RCEPC			0x78 |  | ||||||
| #define STATS_RUSPC			0x7C |  | ||||||
| #define STATS_RFEPC			0x80 |  | ||||||
| #define STATS_ROSPC			0x84 |  | ||||||
| #define STATS_RJEPC			0x88 |  | ||||||
| #define STATS_RPPC			0x8C |  | ||||||
| #define STATS_RL64PC			0x90 |  | ||||||
| #define STATS_RL65PC			0x94 |  | ||||||
| #define STATS_RL128PC			0x98 |  | ||||||
| #define STATS_RL256PC			0x9C |  | ||||||
| #define STATS_RL512PC			0xA0 |  | ||||||
| #define STATS_RL1024PC			0xA4 |  | ||||||
| #define STATS_ROC			0xA8 |  | ||||||
| #define STATS_RDPC_CTRL			0xB0 |  | ||||||
| #define STATS_RDPC_ING			0xB4 |  | ||||||
| #define STATS_RDPC_ARL			0xB8 |  | ||||||
|  |  | ||||||
| #define SYS_CTRL			0x7000 |  | ||||||
| #define SW_PHY_RST			BIT(2) |  | ||||||
| #define SW_SYS_RST			BIT(1) |  | ||||||
| #define SW_REG_RST			BIT(0) |  | ||||||
|  |  | ||||||
| #define SYS_INT_EN			0x7008 |  | ||||||
| #define SYS_INT_STS			0x700c |  | ||||||
| #define MAC_PC_INT			BIT(16) |  | ||||||
| #define PHY_INT(p)			BIT((p) + 8) |  | ||||||
| #define PHY_LC_INT(p)			BIT(p) |  | ||||||
|  |  | ||||||
| #define PHY_IAC				0x701c |  | ||||||
| #define PHY_ACS_ST			BIT(31) |  | ||||||
| #define MDIO_REG_ADDR_S			25 |  | ||||||
| #define MDIO_REG_ADDR_M			0x3e000000 |  | ||||||
| #define MDIO_PHY_ADDR_S			20 |  | ||||||
| #define MDIO_PHY_ADDR_M			0x1f00000 |  | ||||||
| #define MDIO_CMD_S			18 |  | ||||||
| #define MDIO_CMD_M			0xc0000 |  | ||||||
| #define MDIO_ST_S			16 |  | ||||||
| #define MDIO_ST_M			0x30000 |  | ||||||
| #define MDIO_RW_DATA_S			0 |  | ||||||
| #define MDIO_RW_DATA_M			0xffff |  | ||||||
|  |  | ||||||
| /* MDIO_CMD: MDIO commands */ |  | ||||||
| #define MDIO_CMD_ADDR			0 |  | ||||||
| #define MDIO_CMD_WRITE			1 |  | ||||||
| #define MDIO_CMD_READ			2 |  | ||||||
| #define MDIO_CMD_READ_C45		3 |  | ||||||
|  |  | ||||||
| /* MDIO_ST: MDIO start field */ |  | ||||||
| #define MDIO_ST_C45			0 |  | ||||||
| #define MDIO_ST_C22			1 |  | ||||||
|  |  | ||||||
| #define HWSTRAP				0x7800 |  | ||||||
| #define MHWSTRAP			0x7804 |  | ||||||
|  |  | ||||||
| #endif /* _MT753X_REGS_H_ */ |  | ||||||
| @@ -1,510 +0,0 @@ | |||||||
| // SPDX-License-Identifier: GPL-2.0-only |  | ||||||
| /* |  | ||||||
|  * Copyright (c) 2018 MediaTek Inc. |  | ||||||
|  * Author: Weijie Gao <weijie.gao@mediatek.com> |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include <linux/if.h> |  | ||||||
| #include <linux/list.h> |  | ||||||
| #include <linux/if_ether.h> |  | ||||||
| #include <linux/skbuff.h> |  | ||||||
| #include <linux/netdevice.h> |  | ||||||
| #include <linux/netlink.h> |  | ||||||
| #include <linux/bitops.h> |  | ||||||
| #include <net/genetlink.h> |  | ||||||
| #include <linux/delay.h> |  | ||||||
| #include <linux/phy.h> |  | ||||||
| #include <linux/netdevice.h> |  | ||||||
| #include <linux/etherdevice.h> |  | ||||||
| #include <linux/lockdep.h> |  | ||||||
| #include <linux/workqueue.h> |  | ||||||
| #include <linux/of_device.h> |  | ||||||
|  |  | ||||||
| #include "mt753x.h" |  | ||||||
| #include "mt753x_swconfig.h" |  | ||||||
| #include "mt753x_regs.h" |  | ||||||
|  |  | ||||||
| #define MT753X_PORT_MIB_TXB_ID	18	/* TxByte */ |  | ||||||
| #define MT753X_PORT_MIB_RXB_ID	37	/* RxByte */ |  | ||||||
|  |  | ||||||
| #define MIB_DESC(_s, _o, _n)   \ |  | ||||||
| 	{                       \ |  | ||||||
| 		.size = (_s),   \ |  | ||||||
| 		.offset = (_o), \ |  | ||||||
| 		.name = (_n),   \ |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| struct mt753x_mib_desc { |  | ||||||
| 	unsigned int size; |  | ||||||
| 	unsigned int offset; |  | ||||||
| 	const char *name; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static const struct mt753x_mib_desc mt753x_mibs[] = { |  | ||||||
| 	MIB_DESC(1, STATS_TDPC, "TxDrop"), |  | ||||||
| 	MIB_DESC(1, STATS_TCRC, "TxCRC"), |  | ||||||
| 	MIB_DESC(1, STATS_TUPC, "TxUni"), |  | ||||||
| 	MIB_DESC(1, STATS_TMPC, "TxMulti"), |  | ||||||
| 	MIB_DESC(1, STATS_TBPC, "TxBroad"), |  | ||||||
| 	MIB_DESC(1, STATS_TCEC, "TxCollision"), |  | ||||||
| 	MIB_DESC(1, STATS_TSCEC, "TxSingleCol"), |  | ||||||
| 	MIB_DESC(1, STATS_TMCEC, "TxMultiCol"), |  | ||||||
| 	MIB_DESC(1, STATS_TDEC, "TxDefer"), |  | ||||||
| 	MIB_DESC(1, STATS_TLCEC, "TxLateCol"), |  | ||||||
| 	MIB_DESC(1, STATS_TXCEC, "TxExcCol"), |  | ||||||
| 	MIB_DESC(1, STATS_TPPC, "TxPause"), |  | ||||||
| 	MIB_DESC(1, STATS_TL64PC, "Tx64Byte"), |  | ||||||
| 	MIB_DESC(1, STATS_TL65PC, "Tx65Byte"), |  | ||||||
| 	MIB_DESC(1, STATS_TL128PC, "Tx128Byte"), |  | ||||||
| 	MIB_DESC(1, STATS_TL256PC, "Tx256Byte"), |  | ||||||
| 	MIB_DESC(1, STATS_TL512PC, "Tx512Byte"), |  | ||||||
| 	MIB_DESC(1, STATS_TL1024PC, "Tx1024Byte"), |  | ||||||
| 	MIB_DESC(2, STATS_TOC, "TxByte"), |  | ||||||
| 	MIB_DESC(1, STATS_RDPC, "RxDrop"), |  | ||||||
| 	MIB_DESC(1, STATS_RFPC, "RxFiltered"), |  | ||||||
| 	MIB_DESC(1, STATS_RUPC, "RxUni"), |  | ||||||
| 	MIB_DESC(1, STATS_RMPC, "RxMulti"), |  | ||||||
| 	MIB_DESC(1, STATS_RBPC, "RxBroad"), |  | ||||||
| 	MIB_DESC(1, STATS_RAEPC, "RxAlignErr"), |  | ||||||
| 	MIB_DESC(1, STATS_RCEPC, "RxCRC"), |  | ||||||
| 	MIB_DESC(1, STATS_RUSPC, "RxUnderSize"), |  | ||||||
| 	MIB_DESC(1, STATS_RFEPC, "RxFragment"), |  | ||||||
| 	MIB_DESC(1, STATS_ROSPC, "RxOverSize"), |  | ||||||
| 	MIB_DESC(1, STATS_RJEPC, "RxJabber"), |  | ||||||
| 	MIB_DESC(1, STATS_RPPC, "RxPause"), |  | ||||||
| 	MIB_DESC(1, STATS_RL64PC, "Rx64Byte"), |  | ||||||
| 	MIB_DESC(1, STATS_RL65PC, "Rx65Byte"), |  | ||||||
| 	MIB_DESC(1, STATS_RL128PC, "Rx128Byte"), |  | ||||||
| 	MIB_DESC(1, STATS_RL256PC, "Rx256Byte"), |  | ||||||
| 	MIB_DESC(1, STATS_RL512PC, "Rx512Byte"), |  | ||||||
| 	MIB_DESC(1, STATS_RL1024PC, "Rx1024Byte"), |  | ||||||
| 	MIB_DESC(2, STATS_ROC, "RxByte"), |  | ||||||
| 	MIB_DESC(1, STATS_RDPC_CTRL, "RxCtrlDrop"), |  | ||||||
| 	MIB_DESC(1, STATS_RDPC_ING, "RxIngDrop"), |  | ||||||
| 	MIB_DESC(1, STATS_RDPC_ARL, "RxARLDrop") |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| enum { |  | ||||||
| 	/* Global attributes. */ |  | ||||||
| 	MT753X_ATTR_ENABLE_VLAN, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static int mt753x_get_vlan_enable(struct switch_dev *dev, |  | ||||||
| 				  const struct switch_attr *attr, |  | ||||||
| 				  struct switch_val *val) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw = container_of(dev, struct gsw_mt753x, swdev); |  | ||||||
|  |  | ||||||
| 	val->value.i = gsw->global_vlan_enable; |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_set_vlan_enable(struct switch_dev *dev, |  | ||||||
| 				  const struct switch_attr *attr, |  | ||||||
| 				  struct switch_val *val) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw = container_of(dev, struct gsw_mt753x, swdev); |  | ||||||
|  |  | ||||||
| 	gsw->global_vlan_enable = val->value.i != 0; |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_get_port_pvid(struct switch_dev *dev, int port, int *val) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw = container_of(dev, struct gsw_mt753x, swdev); |  | ||||||
|  |  | ||||||
| 	if (port >= MT753X_NUM_PORTS) |  | ||||||
| 		return -EINVAL; |  | ||||||
|  |  | ||||||
| 	*val = mt753x_reg_read(gsw, PPBV1(port)); |  | ||||||
| 	*val &= GRP_PORT_VID_M; |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_set_port_pvid(struct switch_dev *dev, int port, int pvid) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw = container_of(dev, struct gsw_mt753x, swdev); |  | ||||||
|  |  | ||||||
| 	if (port >= MT753X_NUM_PORTS) |  | ||||||
| 		return -EINVAL; |  | ||||||
|  |  | ||||||
| 	if (pvid < MT753X_MIN_VID || pvid > MT753X_MAX_VID) |  | ||||||
| 		return -EINVAL; |  | ||||||
|  |  | ||||||
| 	gsw->port_entries[port].pvid = pvid; |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_get_vlan_ports(struct switch_dev *dev, struct switch_val *val) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw = container_of(dev, struct gsw_mt753x, swdev); |  | ||||||
| 	u32 member; |  | ||||||
| 	u32 etags; |  | ||||||
| 	int i; |  | ||||||
|  |  | ||||||
| 	val->len = 0; |  | ||||||
|  |  | ||||||
| 	if (val->port_vlan < 0 || val->port_vlan >= MT753X_NUM_VLANS) |  | ||||||
| 		return -EINVAL; |  | ||||||
|  |  | ||||||
| 	mt753x_vlan_ctrl(gsw, VTCR_READ_VLAN_ENTRY, val->port_vlan); |  | ||||||
|  |  | ||||||
| 	member = mt753x_reg_read(gsw, VAWD1); |  | ||||||
| 	member &= PORT_MEM_M; |  | ||||||
| 	member >>= PORT_MEM_S; |  | ||||||
|  |  | ||||||
| 	etags = mt753x_reg_read(gsw, VAWD2); |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < MT753X_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 >> PORT_ETAG_S(i)) & PORT_ETAG_M; |  | ||||||
|  |  | ||||||
| 		if (etag == ETAG_CTRL_TAG) |  | ||||||
| 			p->flags |= BIT(SWITCH_PORT_FLAG_TAGGED); |  | ||||||
| 		else if (etag != ETAG_CTRL_UNTAG) |  | ||||||
| 			dev_info(gsw->dev, |  | ||||||
| 				 "vlan egress tag control neither untag nor tag.\n"); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_set_vlan_ports(struct switch_dev *dev, struct switch_val *val) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw = container_of(dev, struct gsw_mt753x, swdev); |  | ||||||
| 	u8 member = 0; |  | ||||||
| 	u8 etags = 0; |  | ||||||
| 	int i; |  | ||||||
|  |  | ||||||
| 	if (val->port_vlan < 0 || val->port_vlan >= MT753X_NUM_VLANS || |  | ||||||
| 	    val->len > MT753X_NUM_PORTS) |  | ||||||
| 		return -EINVAL; |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < val->len; i++) { |  | ||||||
| 		struct switch_port *p = &val->value.ports[i]; |  | ||||||
|  |  | ||||||
| 		if (p->id >= MT753X_NUM_PORTS) |  | ||||||
| 			return -EINVAL; |  | ||||||
|  |  | ||||||
| 		member |= BIT(p->id); |  | ||||||
|  |  | ||||||
| 		if (p->flags & BIT(SWITCH_PORT_FLAG_TAGGED)) |  | ||||||
| 			etags |= BIT(p->id); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	gsw->vlan_entries[val->port_vlan].member = member; |  | ||||||
| 	gsw->vlan_entries[val->port_vlan].etags = etags; |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_set_vid(struct switch_dev *dev, |  | ||||||
| 			  const struct switch_attr *attr, |  | ||||||
| 			  struct switch_val *val) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw = container_of(dev, struct gsw_mt753x, swdev); |  | ||||||
| 	int vlan; |  | ||||||
| 	u16 vid; |  | ||||||
|  |  | ||||||
| 	vlan = val->port_vlan; |  | ||||||
| 	vid = (u16)val->value.i; |  | ||||||
|  |  | ||||||
| 	if (vlan < 0 || vlan >= MT753X_NUM_VLANS) |  | ||||||
| 		return -EINVAL; |  | ||||||
|  |  | ||||||
| 	if (vid < MT753X_MIN_VID || vid > MT753X_MAX_VID) |  | ||||||
| 		return -EINVAL; |  | ||||||
|  |  | ||||||
| 	gsw->vlan_entries[vlan].vid = vid; |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_get_vid(struct switch_dev *dev, |  | ||||||
| 			  const struct switch_attr *attr, |  | ||||||
| 			  struct switch_val *val) |  | ||||||
| { |  | ||||||
| 	val->value.i = val->port_vlan; |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_get_port_link(struct switch_dev *dev, int port, |  | ||||||
| 				struct switch_port_link *link) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw = container_of(dev, struct gsw_mt753x, swdev); |  | ||||||
| 	u32 speed, pmsr; |  | ||||||
|  |  | ||||||
| 	if (port < 0 || port >= MT753X_NUM_PORTS) |  | ||||||
| 		return -EINVAL; |  | ||||||
|  |  | ||||||
| 	pmsr = mt753x_reg_read(gsw, PMSR(port)); |  | ||||||
|  |  | ||||||
| 	link->link = pmsr & MAC_LNK_STS; |  | ||||||
| 	link->duplex = pmsr & MAC_DPX_STS; |  | ||||||
| 	speed = (pmsr & MAC_SPD_STS_M) >> MAC_SPD_STS_S; |  | ||||||
|  |  | ||||||
| 	switch (speed) { |  | ||||||
| 	case MAC_SPD_10: |  | ||||||
| 		link->speed = SWITCH_PORT_SPEED_10; |  | ||||||
| 		break; |  | ||||||
| 	case MAC_SPD_100: |  | ||||||
| 		link->speed = SWITCH_PORT_SPEED_100; |  | ||||||
| 		break; |  | ||||||
| 	case MAC_SPD_1000: |  | ||||||
| 		link->speed = SWITCH_PORT_SPEED_1000; |  | ||||||
| 		break; |  | ||||||
| 	case MAC_SPD_2500: |  | ||||||
| 		/* TODO: swconfig has no support for 2500 now */ |  | ||||||
| 		link->speed = SWITCH_PORT_SPEED_UNKNOWN; |  | ||||||
| 		break; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_set_port_link(struct switch_dev *dev, int port, |  | ||||||
| 				struct switch_port_link *link) |  | ||||||
| { |  | ||||||
| #ifndef MODULE |  | ||||||
| 	if (port >= MT753X_NUM_PHYS) |  | ||||||
| 		return -EINVAL; |  | ||||||
|  |  | ||||||
| 	return switch_generic_set_link(dev, port, link); |  | ||||||
| #else |  | ||||||
| 	return -ENOTSUPP; |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static u64 get_mib_counter(struct gsw_mt753x *gsw, int i, int port) |  | ||||||
| { |  | ||||||
| 	unsigned int offset; |  | ||||||
| 	u64 lo, hi, hi2; |  | ||||||
|  |  | ||||||
| 	offset = mt753x_mibs[i].offset; |  | ||||||
|  |  | ||||||
| 	if (mt753x_mibs[i].size == 1) |  | ||||||
| 		return mt753x_reg_read(gsw, MIB_COUNTER_REG(port, offset)); |  | ||||||
|  |  | ||||||
| 	do { |  | ||||||
| 		hi = mt753x_reg_read(gsw, MIB_COUNTER_REG(port, offset + 4)); |  | ||||||
| 		lo = mt753x_reg_read(gsw, MIB_COUNTER_REG(port, offset)); |  | ||||||
| 		hi2 = mt753x_reg_read(gsw, MIB_COUNTER_REG(port, offset + 4)); |  | ||||||
| 	} while (hi2 != hi); |  | ||||||
|  |  | ||||||
| 	return (hi << 32) | lo; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_get_port_mib(struct switch_dev *dev, |  | ||||||
| 			       const struct switch_attr *attr, |  | ||||||
| 			       struct switch_val *val) |  | ||||||
| { |  | ||||||
| 	static char buf[4096]; |  | ||||||
| 	struct gsw_mt753x *gsw = container_of(dev, struct gsw_mt753x, swdev); |  | ||||||
| 	int i, len = 0; |  | ||||||
|  |  | ||||||
| 	if (val->port_vlan >= MT753X_NUM_PORTS) |  | ||||||
| 		return -EINVAL; |  | ||||||
|  |  | ||||||
| 	len += snprintf(buf + len, sizeof(buf) - len, |  | ||||||
| 			"Port %d MIB counters\n", val->port_vlan); |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < ARRAY_SIZE(mt753x_mibs); ++i) { |  | ||||||
| 		u64 counter; |  | ||||||
|  |  | ||||||
| 		len += snprintf(buf + len, sizeof(buf) - len, |  | ||||||
| 				"%-11s: ", mt753x_mibs[i].name); |  | ||||||
| 		counter = get_mib_counter(gsw, i, val->port_vlan); |  | ||||||
| 		len += snprintf(buf + len, sizeof(buf) - len, "%llu\n", |  | ||||||
| 				counter); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	val->value.s = buf; |  | ||||||
| 	val->len = len; |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_get_port_stats(struct switch_dev *dev, int port, |  | ||||||
| 				 struct switch_port_stats *stats) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw = container_of(dev, struct gsw_mt753x, swdev); |  | ||||||
|  |  | ||||||
| 	if (port < 0 || port >= MT753X_NUM_PORTS) |  | ||||||
| 		return -EINVAL; |  | ||||||
|  |  | ||||||
| 	stats->tx_bytes = get_mib_counter(gsw, MT753X_PORT_MIB_TXB_ID, port); |  | ||||||
| 	stats->rx_bytes = get_mib_counter(gsw, MT753X_PORT_MIB_RXB_ID, port); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void mt753x_port_isolation(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	int i; |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < MT753X_NUM_PORTS; i++) |  | ||||||
| 		mt753x_reg_write(gsw, PCR(i), |  | ||||||
| 				 BIT(gsw->cpu_port) << PORT_MATRIX_S); |  | ||||||
|  |  | ||||||
| 	mt753x_reg_write(gsw, PCR(gsw->cpu_port), PORT_MATRIX_M); |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < MT753X_NUM_PORTS; i++) |  | ||||||
| 		mt753x_reg_write(gsw, PVC(i), |  | ||||||
| 				 (0x8100 << STAG_VPID_S) | |  | ||||||
| 				 (VA_TRANSPARENT_PORT << VLAN_ATTR_S)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_apply_config(struct switch_dev *dev) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw = container_of(dev, struct gsw_mt753x, swdev); |  | ||||||
|  |  | ||||||
| 	if (!gsw->global_vlan_enable) { |  | ||||||
| 		mt753x_port_isolation(gsw); |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	mt753x_apply_vlan_config(gsw); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_reset_switch(struct switch_dev *dev) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw = container_of(dev, struct gsw_mt753x, swdev); |  | ||||||
| 	int i; |  | ||||||
|  |  | ||||||
| 	memset(gsw->port_entries, 0, sizeof(gsw->port_entries)); |  | ||||||
| 	memset(gsw->vlan_entries, 0, sizeof(gsw->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 < MT753X_NUM_VLANS; i++) |  | ||||||
| 		gsw->vlan_entries[i].vid = i; |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_phy_read16(struct switch_dev *dev, int addr, u8 reg, |  | ||||||
| 			     u16 *value) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw = container_of(dev, struct gsw_mt753x, swdev); |  | ||||||
|  |  | ||||||
| 	*value = gsw->mii_read(gsw, addr, reg); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int mt753x_phy_write16(struct switch_dev *dev, int addr, u8 reg, |  | ||||||
| 			      u16 value) |  | ||||||
| { |  | ||||||
| 	struct gsw_mt753x *gsw = container_of(dev, struct gsw_mt753x, swdev); |  | ||||||
|  |  | ||||||
| 	gsw->mii_write(gsw, addr, reg, value); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const struct switch_attr mt753x_global[] = { |  | ||||||
| 	{ |  | ||||||
| 		.type = SWITCH_TYPE_INT, |  | ||||||
| 		.name = "enable_vlan", |  | ||||||
| 		.description = "VLAN mode (1:enabled)", |  | ||||||
| 		.max = 1, |  | ||||||
| 		.id = MT753X_ATTR_ENABLE_VLAN, |  | ||||||
| 		.get = mt753x_get_vlan_enable, |  | ||||||
| 		.set = mt753x_set_vlan_enable, |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static const struct switch_attr mt753x_port[] = { |  | ||||||
| 	{ |  | ||||||
| 		.type = SWITCH_TYPE_STRING, |  | ||||||
| 		.name = "mib", |  | ||||||
| 		.description = "Get MIB counters for port", |  | ||||||
| 		.get = mt753x_get_port_mib, |  | ||||||
| 		.set = NULL, |  | ||||||
| 	}, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static const struct switch_attr mt753x_vlan[] = { |  | ||||||
| 	{ |  | ||||||
| 		.type = SWITCH_TYPE_INT, |  | ||||||
| 		.name = "vid", |  | ||||||
| 		.description = "VLAN ID (0-4094)", |  | ||||||
| 		.set = mt753x_set_vid, |  | ||||||
| 		.get = mt753x_get_vid, |  | ||||||
| 		.max = 4094, |  | ||||||
| 	}, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static const struct switch_dev_ops mt753x_swdev_ops = { |  | ||||||
| 	.attr_global = { |  | ||||||
| 		.attr = mt753x_global, |  | ||||||
| 		.n_attr = ARRAY_SIZE(mt753x_global), |  | ||||||
| 	}, |  | ||||||
| 	.attr_port = { |  | ||||||
| 		.attr = mt753x_port, |  | ||||||
| 		.n_attr = ARRAY_SIZE(mt753x_port), |  | ||||||
| 	}, |  | ||||||
| 	.attr_vlan = { |  | ||||||
| 		.attr = mt753x_vlan, |  | ||||||
| 		.n_attr = ARRAY_SIZE(mt753x_vlan), |  | ||||||
| 	}, |  | ||||||
| 	.get_vlan_ports = mt753x_get_vlan_ports, |  | ||||||
| 	.set_vlan_ports = mt753x_set_vlan_ports, |  | ||||||
| 	.get_port_pvid = mt753x_get_port_pvid, |  | ||||||
| 	.set_port_pvid = mt753x_set_port_pvid, |  | ||||||
| 	.get_port_link = mt753x_get_port_link, |  | ||||||
| 	.set_port_link = mt753x_set_port_link, |  | ||||||
| 	.get_port_stats = mt753x_get_port_stats, |  | ||||||
| 	.apply_config = mt753x_apply_config, |  | ||||||
| 	.reset_switch = mt753x_reset_switch, |  | ||||||
| 	.phy_read16 = mt753x_phy_read16, |  | ||||||
| 	.phy_write16 = mt753x_phy_write16, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| int mt753x_swconfig_init(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	struct device_node *np = gsw->dev->of_node; |  | ||||||
| 	struct switch_dev *swdev; |  | ||||||
| 	int ret; |  | ||||||
|  |  | ||||||
| 	if (of_property_read_u32(np, "mediatek,cpuport", &gsw->cpu_port)) |  | ||||||
| 		gsw->cpu_port = MT753X_DFL_CPU_PORT; |  | ||||||
|  |  | ||||||
| 	swdev = &gsw->swdev; |  | ||||||
|  |  | ||||||
| 	swdev->name = gsw->name; |  | ||||||
| 	swdev->alias = gsw->name; |  | ||||||
| 	swdev->cpu_port = gsw->cpu_port; |  | ||||||
| 	swdev->ports = MT753X_NUM_PORTS; |  | ||||||
| 	swdev->vlans = MT753X_NUM_VLANS; |  | ||||||
| 	swdev->ops = &mt753x_swdev_ops; |  | ||||||
|  |  | ||||||
| 	ret = register_switch(swdev, NULL); |  | ||||||
| 	if (ret) { |  | ||||||
| 		dev_notice(gsw->dev, "Failed to register switch %s\n", |  | ||||||
| 			   swdev->name); |  | ||||||
| 		return ret; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	mt753x_apply_config(swdev); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void mt753x_swconfig_destroy(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	unregister_switch(&gsw->swdev); |  | ||||||
| } |  | ||||||
| @@ -1,29 +0,0 @@ | |||||||
| /* SPDX-License-Identifier: GPL-2.0-only */ |  | ||||||
| /* |  | ||||||
|  * Copyright (c) 2018 MediaTek Inc. |  | ||||||
|  * Author: Weijie Gao <weijie.gao@mediatek.com> |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifndef _MT753X_SWCONFIG_H_ |  | ||||||
| #define _MT753X_SWCONFIG_H_ |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_SWCONFIG |  | ||||||
| #include <linux/switch.h> |  | ||||||
| #include "mt753x.h" |  | ||||||
|  |  | ||||||
| int mt753x_swconfig_init(struct gsw_mt753x *gsw); |  | ||||||
| void mt753x_swconfig_destroy(struct gsw_mt753x *gsw); |  | ||||||
| #else |  | ||||||
| static inline int mt753x_swconfig_init(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	mt753x_apply_vlan_config(gsw); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline void mt753x_swconfig_destroy(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #endif /* _MT753X_SWCONFIG_H_ */ |  | ||||||
| @@ -1,183 +0,0 @@ | |||||||
| // SPDX-License-Identifier: GPL-2.0-only |  | ||||||
| /* |  | ||||||
|  * Copyright (c) 2018 MediaTek Inc. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "mt753x.h" |  | ||||||
| #include "mt753x_regs.h" |  | ||||||
|  |  | ||||||
| struct mt753x_mapping mt753x_def_mapping[] = { |  | ||||||
| 	{ |  | ||||||
| 		.name = "llllw", |  | ||||||
| 		.pvids = { 1, 1, 1, 1, 2, 2, 1 }, |  | ||||||
| 		.members = { 0, 0x4f, 0x30 }, |  | ||||||
| 		.etags = { 0, 0, 0 }, |  | ||||||
| 		.vids = { 0, 1, 2 }, |  | ||||||
| 	}, { |  | ||||||
| 		.name = "wllll", |  | ||||||
| 		.pvids = { 2, 1, 1, 1, 1, 2, 1 }, |  | ||||||
| 		.members = { 0, 0x5e, 0x21 }, |  | ||||||
| 		.etags = { 0, 0, 0 }, |  | ||||||
| 		.vids = { 0, 1, 2 }, |  | ||||||
| 	}, { |  | ||||||
| 		.name = "lwlll", |  | ||||||
| 		.pvids = { 1, 2, 1, 1, 1, 2, 1 }, |  | ||||||
| 		.members = { 0, 0x5d, 0x22 }, |  | ||||||
| 		.etags = { 0, 0, 0 }, |  | ||||||
| 		.vids = { 0, 1, 2 }, |  | ||||||
| 	}, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| void mt753x_vlan_ctrl(struct gsw_mt753x *gsw, u32 cmd, u32 val) |  | ||||||
| { |  | ||||||
| 	int i; |  | ||||||
|  |  | ||||||
| 	mt753x_reg_write(gsw, VTCR, |  | ||||||
| 			 VTCR_BUSY | ((cmd << VTCR_FUNC_S) & VTCR_FUNC_M) | |  | ||||||
| 			 (val & VTCR_VID_M)); |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < 300; i++) { |  | ||||||
| 		u32 val = mt753x_reg_read(gsw, VTCR); |  | ||||||
|  |  | ||||||
| 		if ((val & VTCR_BUSY) == 0) |  | ||||||
| 			break; |  | ||||||
|  |  | ||||||
| 		usleep_range(1000, 1100); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (i == 300) |  | ||||||
| 		dev_info(gsw->dev, "vtcr timeout\n"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void mt753x_write_vlan_entry(struct gsw_mt753x *gsw, int vlan, u16 vid, |  | ||||||
| 				    u8 ports, u8 etags) |  | ||||||
| { |  | ||||||
| 	int port; |  | ||||||
| 	u32 val; |  | ||||||
|  |  | ||||||
| 	/* vlan port membership */ |  | ||||||
| 	if (ports) |  | ||||||
| 		mt753x_reg_write(gsw, VAWD1, |  | ||||||
| 				 IVL_MAC | VTAG_EN | VENTRY_VALID | |  | ||||||
| 				 ((ports << PORT_MEM_S) & PORT_MEM_M)); |  | ||||||
| 	else |  | ||||||
| 		mt753x_reg_write(gsw, VAWD1, 0); |  | ||||||
|  |  | ||||||
| 	/* egress mode */ |  | ||||||
| 	val = 0; |  | ||||||
| 	for (port = 0; port < MT753X_NUM_PORTS; port++) { |  | ||||||
| 		if (etags & BIT(port)) |  | ||||||
| 			val |= ETAG_CTRL_TAG << PORT_ETAG_S(port); |  | ||||||
| 		else |  | ||||||
| 			val |= ETAG_CTRL_UNTAG << PORT_ETAG_S(port); |  | ||||||
| 	} |  | ||||||
| 	mt753x_reg_write(gsw, VAWD2, val); |  | ||||||
|  |  | ||||||
| 	/* write to vlan table */ |  | ||||||
| 	mt753x_vlan_ctrl(gsw, VTCR_WRITE_VLAN_ENTRY, vid); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void mt753x_apply_vlan_config(struct gsw_mt753x *gsw) |  | ||||||
| { |  | ||||||
| 	int i, j; |  | ||||||
| 	u8 tag_ports; |  | ||||||
| 	u8 untag_ports; |  | ||||||
|  |  | ||||||
| 	/* set all ports as security mode */ |  | ||||||
| 	for (i = 0; i < MT753X_NUM_PORTS; i++) |  | ||||||
| 		mt753x_reg_write(gsw, PCR(i), |  | ||||||
| 				 PORT_MATRIX_M | SECURITY_MODE); |  | ||||||
|  |  | ||||||
| 	/* check if a port is used in tag/untag vlan egress mode */ |  | ||||||
| 	tag_ports = 0; |  | ||||||
| 	untag_ports = 0; |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < MT753X_NUM_VLANS; i++) { |  | ||||||
| 		u8 member = gsw->vlan_entries[i].member; |  | ||||||
| 		u8 etags = gsw->vlan_entries[i].etags; |  | ||||||
|  |  | ||||||
| 		if (!member) |  | ||||||
| 			continue; |  | ||||||
|  |  | ||||||
| 		for (j = 0; j < MT753X_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 < MT753X_NUM_PORTS; i++) { |  | ||||||
| 		u32 pvc_mode = 0x8100 << STAG_VPID_S; |  | ||||||
|  |  | ||||||
| 		if (untag_ports & BIT(i) && !(tag_ports & BIT(i))) |  | ||||||
| 			pvc_mode = (0x8100 << STAG_VPID_S) | |  | ||||||
| 				(VA_TRANSPARENT_PORT << VLAN_ATTR_S); |  | ||||||
|  |  | ||||||
| 		mt753x_reg_write(gsw, PVC(i), pvc_mode); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* first clear the switch vlan table */ |  | ||||||
| 	for (i = 0; i < MT753X_NUM_VLANS; i++) |  | ||||||
| 		mt753x_write_vlan_entry(gsw, i, i, 0, 0); |  | ||||||
|  |  | ||||||
| 	/* now program only vlans with members to avoid |  | ||||||
| 	 * clobbering remapped entries in later iterations |  | ||||||
| 	 */ |  | ||||||
| 	for (i = 0; i < MT753X_NUM_VLANS; i++) { |  | ||||||
| 		u16 vid = gsw->vlan_entries[i].vid; |  | ||||||
| 		u8 member = gsw->vlan_entries[i].member; |  | ||||||
| 		u8 etags = gsw->vlan_entries[i].etags; |  | ||||||
|  |  | ||||||
| 		if (member) |  | ||||||
| 			mt753x_write_vlan_entry(gsw, i, vid, member, etags); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* Port Default PVID */ |  | ||||||
| 	for (i = 0; i < MT753X_NUM_PORTS; i++) { |  | ||||||
| 		int vlan = gsw->port_entries[i].pvid; |  | ||||||
| 		u16 pvid = 0; |  | ||||||
| 		u32 val; |  | ||||||
|  |  | ||||||
| 		if (vlan < MT753X_NUM_VLANS && gsw->vlan_entries[vlan].member) |  | ||||||
| 			pvid = gsw->vlan_entries[vlan].vid; |  | ||||||
|  |  | ||||||
| 		val = mt753x_reg_read(gsw, PPBV1(i)); |  | ||||||
| 		val &= ~GRP_PORT_VID_M; |  | ||||||
| 		val |= pvid; |  | ||||||
| 		mt753x_reg_write(gsw, PPBV1(i), val); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct mt753x_mapping *mt753x_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(mt753x_def_mapping); i++) |  | ||||||
| 		if (!strcmp(map, mt753x_def_mapping[i].name)) |  | ||||||
| 			return &mt753x_def_mapping[i]; |  | ||||||
|  |  | ||||||
| 	return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void mt753x_apply_mapping(struct gsw_mt753x *gsw, struct mt753x_mapping *map) |  | ||||||
| { |  | ||||||
| 	int i = 0; |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < MT753X_NUM_PORTS; i++) |  | ||||||
| 		gsw->port_entries[i].pvid = map->pvids[i]; |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < MT753X_NUM_VLANS; i++) { |  | ||||||
| 		gsw->vlan_entries[i].member = map->members[i]; |  | ||||||
| 		gsw->vlan_entries[i].etags = map->etags[i]; |  | ||||||
| 		gsw->vlan_entries[i].vid = map->vids[i]; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,40 +0,0 @@ | |||||||
| /* SPDX-License-Identifier: GPL-2.0-only */ |  | ||||||
| /* |  | ||||||
|  * Copyright (c) 2018 MediaTek Inc. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifndef _MT753X_VLAN_H_ |  | ||||||
| #define _MT753X_VLAN_H_ |  | ||||||
|  |  | ||||||
| #define MT753X_NUM_PORTS	7 |  | ||||||
| #define MT753X_NUM_VLANS	4095 |  | ||||||
| #define MT753X_MAX_VID		4095 |  | ||||||
| #define MT753X_MIN_VID		0 |  | ||||||
|  |  | ||||||
| struct gsw_mt753x; |  | ||||||
|  |  | ||||||
| struct mt753x_port_entry { |  | ||||||
| 	u16	pvid; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct mt753x_vlan_entry { |  | ||||||
| 	u16	vid; |  | ||||||
| 	u8	member; |  | ||||||
| 	u8	etags; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct mt753x_mapping { |  | ||||||
| 	char	*name; |  | ||||||
| 	u16	pvids[MT753X_NUM_PORTS]; |  | ||||||
| 	u8	members[MT753X_NUM_VLANS]; |  | ||||||
| 	u8	etags[MT753X_NUM_VLANS]; |  | ||||||
| 	u16	vids[MT753X_NUM_VLANS]; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| extern struct mt753x_mapping mt753x_defaults[]; |  | ||||||
|  |  | ||||||
| void mt753x_vlan_ctrl(struct gsw_mt753x *gsw, u32 cmd, u32 val); |  | ||||||
| void mt753x_apply_vlan_config(struct gsw_mt753x *gsw); |  | ||||||
| struct mt753x_mapping *mt753x_find_mapping(struct device_node *np); |  | ||||||
| void mt753x_apply_mapping(struct gsw_mt753x *gsw, struct mt753x_mapping *map); |  | ||||||
| #endif /* _MT753X_VLAN_H_ */ |  | ||||||
		Reference in New Issue
	
	Block a user
	 Felix Fietkau
					Felix Fietkau