// SPDX-License-Identifier: GPL-2.0
/*
 * 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

/* Top single control CR define */
#define TOP_SIG_CTRL			0x7808

/* TOP_SIG_CTRL Register bitmap of define */
#define OUTPUT_INTR_S			16
#define OUTPUT_INTR_M			0x30000

#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);

	/* Output INTR selected */
	val = mt753x_reg_read(gsw, TOP_SIG_CTRL);
	val &= ~OUTPUT_INTR_M;
	val |= (3 << OUTPUT_INTR_S);
	mt753x_reg_write(gsw, TOP_SIG_CTRL, val);

	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
};
