From f48fab0efed0fe088248815f138d529781c3a104 Mon Sep 17 00:00:00 2001 From: Luo Jie Date: Wed, 22 Mar 2023 18:55:42 +0800 Subject: [PATCH 201/281] net: mdio-ipq4019: Add the qca8386 initialization. The clock fixup of qca8386 is necessary before the MDIO bus registered, which makes the embedded PHY probed correctly. Change-Id: I8e3f66c39c8362eb0333d96a4df17439dd713404 Signed-off-by: Luo Jie --- drivers/net/mdio/mdio-ipq4019.c | 187 ++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) diff --git a/drivers/net/mdio/mdio-ipq4019.c b/drivers/net/mdio/mdio-ipq4019.c index 2a9869151cb9..c555ee6d0fcd 100644 --- a/drivers/net/mdio/mdio-ipq4019.c +++ b/drivers/net/mdio/mdio-ipq4019.c @@ -44,6 +44,27 @@ #define PHY_ADDR_NUM 4 #define UNIPHY_ADDR_NUM 3 +#define EPHY_CFG 0xC90F018 +#define GEPHY0_TX_CBCR 0xC800058 +#define SRDS0_SYS_CBCR 0xC8001A8 +#define SRDS1_SYS_CBCR 0xC8001AC +#define EPHY0_SYS_CBCR 0xC8001B0 +#define EPHY1_SYS_CBCR 0xC8001B4 +#define EPHY2_SYS_CBCR 0xC8001B8 +#define EPHY3_SYS_CBCR 0xC8001BC +#define GCC_GEPHY_MISC 0xC800304 +#define QFPROM_RAW_PTE_ROW2_MSB 0xC900014 +#define QFPROM_RAW_CALIBRATION_ROW4_LSB 0xC900048 +#define QFPROM_RAW_CALIBRATION_ROW7_LSB 0xC900060 +#define QFPROM_RAW_CALIBRATION_ROW8_LSB 0xC900068 +#define QFPROM_RAW_CALIBRATION_ROW6_MSB 0xC90005C +#define PHY_DEBUG_PORT_ADDR 0x1d +#define PHY_DEBUG_PORT_DATA 0x1e +#define PHY_LDO_EFUSE_REG 0x180 +#define PHY_ICC_EFUSE_REG 0x280 +#define PHY_10BT_SG_THRESH_REG 0x3380 +#define PHY_MMD1_CTRL2ANA_OPTION2_REG 0x40018102 + struct ipq4019_mdio_data { void __iomem *membase; void __iomem *eth_ldo_rdy; @@ -239,6 +260,54 @@ int ipq_mii_write(struct mii_bus *bus, unsigned int reg, unsigned int val) return 0; }; +static inline void ipq_qca8386_clk_enable(struct mii_bus *mii_bus, u32 reg) +{ + u32 val; + + val = ipq_mii_read(mii_bus, reg); + val |= BIT(0); + ipq_mii_write(mii_bus, reg, val); +} + +static inline void ipq_qca8386_clk_disable(struct mii_bus *mii_bus, u32 reg) +{ + u32 val; + + val = ipq_mii_read(mii_bus, reg); + val &= ~BIT(0); + ipq_mii_write(mii_bus, reg, val); +} + +static inline void ipq_qca8386_clk_reset(struct mii_bus *mii_bus, u32 reg) +{ + u32 val; + + val = ipq_mii_read(mii_bus, reg); + val |= BIT(2); + ipq_mii_write(mii_bus, reg, val); + + usleep_range(20000, 21000); + + val &= ~BIT(2); + ipq_mii_write(mii_bus, reg, val); +} + +static u16 ipq_phy_debug_read(struct mii_bus *mii_bus, u32 phy_addr, u32 reg_id) +{ + mii_bus->write(mii_bus, phy_addr, PHY_DEBUG_PORT_ADDR, reg_id); + + return mii_bus->read(mii_bus, phy_addr, PHY_DEBUG_PORT_DATA); +} + +static void ipq_phy_debug_write(struct mii_bus *mii_bus, u32 phy_addr, u32 reg_id, u16 reg_val) +{ + + mii_bus->write(mii_bus, phy_addr, PHY_DEBUG_PORT_ADDR, reg_id); + + mii_bus->write(mii_bus, phy_addr, PHY_DEBUG_PORT_DATA, reg_val); +} + + static void ipq_phy_addr_fixup(struct mii_bus *bus, struct device_node *np) { void __iomem *ephy_cfg_base; @@ -344,6 +413,121 @@ static void ipq_phy_addr_fixup(struct mii_bus *bus, struct device_node *np) } } +static void ipq_qca8386_efuse_loading(struct mii_bus *mii_bus, u8 ethphy) +{ + u32 val = 0, ldo_efuse = 0, icc_efuse = 0, phy_addr = 0; + u16 reg_val = 0; + + phy_addr = ipq_mii_read(mii_bus, EPHY_CFG); + phy_addr = (phy_addr >> (ethphy * PHY_ADDR_LENGTH)) & GENMASK(4, 0); + + switch(ethphy) { + case 0: + val = ipq_mii_read(mii_bus, QFPROM_RAW_CALIBRATION_ROW4_LSB); + ldo_efuse = FIELD_GET(GENMASK(21, 18), val); + icc_efuse = FIELD_GET(GENMASK(26, 22), val); + break; + case 1: + val = ipq_mii_read(mii_bus, QFPROM_RAW_CALIBRATION_ROW7_LSB); + ldo_efuse = FIELD_GET(GENMASK(26, 23), val); + icc_efuse = FIELD_GET(GENMASK(31, 27), val); + break; + case 2: + val = ipq_mii_read(mii_bus, QFPROM_RAW_CALIBRATION_ROW8_LSB); + ldo_efuse = FIELD_GET(GENMASK(26, 23), val); + icc_efuse = FIELD_GET(GENMASK(31, 27), val); + break; + case 3: + val = ipq_mii_read(mii_bus, QFPROM_RAW_CALIBRATION_ROW6_MSB); + ldo_efuse = FIELD_GET(GENMASK(17, 14), val); + icc_efuse = FIELD_GET(GENMASK(22, 18), val); + break; + } + reg_val = ipq_phy_debug_read(mii_bus, phy_addr, PHY_LDO_EFUSE_REG); + reg_val &= ~GENMASK(7, 4); + reg_val |= FIELD_PREP(GENMASK(7, 4), ldo_efuse); + ipq_phy_debug_write(mii_bus, phy_addr, PHY_LDO_EFUSE_REG, reg_val); + + reg_val = ipq_phy_debug_read(mii_bus, phy_addr, PHY_ICC_EFUSE_REG); + reg_val &= ~GENMASK(4, 0); + reg_val |= FIELD_PREP(GENMASK(4, 0), icc_efuse); + ipq_phy_debug_write(mii_bus, phy_addr, PHY_ICC_EFUSE_REG, reg_val); +} + +static void ipq_qca8386_ethphy_ana_fixup(struct mii_bus *mii_bus, u8 ethphy) +{ + u32 phy_addr = 0; + u16 reg_val = 0; + + phy_addr = ipq_mii_read(mii_bus, EPHY_CFG); + phy_addr = (phy_addr >> (ethphy * PHY_ADDR_LENGTH)) & GENMASK(4, 0); + + /*increase 100BT tx amplitude*/ + reg_val = mii_bus->read(mii_bus, phy_addr, PHY_MMD1_CTRL2ANA_OPTION2_REG); + mii_bus->write(mii_bus, phy_addr, PHY_MMD1_CTRL2ANA_OPTION2_REG, reg_val | 0x7f); + + /*increase 10BT signal detect threshold*/ + reg_val = ipq_phy_debug_read(mii_bus, phy_addr, PHY_10BT_SG_THRESH_REG); + ipq_phy_debug_write(mii_bus, phy_addr, PHY_10BT_SG_THRESH_REG, reg_val | 0x1); +} + +static void ipq_qca8386_clock_init(struct mii_bus *mii_bus) +{ + u32 val = 0; + int i; + + /* Enable serdes */ + ipq_qca8386_clk_enable(mii_bus, SRDS0_SYS_CBCR); + ipq_qca8386_clk_enable(mii_bus, SRDS1_SYS_CBCR); + + /* Reset serdes */ + ipq_qca8386_clk_reset(mii_bus, SRDS0_SYS_CBCR); + ipq_qca8386_clk_reset(mii_bus, SRDS1_SYS_CBCR); + + /* Disable EPHY GMII clock */ + i = 0; + while (i < 2 * PHY_ADDR_NUM) { + ipq_qca8386_clk_disable(mii_bus, GEPHY0_TX_CBCR + i*0x20); + i++; + } + + /* Enable ephy */ + ipq_qca8386_clk_enable(mii_bus, EPHY0_SYS_CBCR); + ipq_qca8386_clk_enable(mii_bus, EPHY1_SYS_CBCR); + ipq_qca8386_clk_enable(mii_bus, EPHY2_SYS_CBCR); + ipq_qca8386_clk_enable(mii_bus, EPHY3_SYS_CBCR); + + /* Reset ephy */ + ipq_qca8386_clk_reset(mii_bus, EPHY0_SYS_CBCR); + ipq_qca8386_clk_reset(mii_bus, EPHY1_SYS_CBCR); + ipq_qca8386_clk_reset(mii_bus, EPHY2_SYS_CBCR); + ipq_qca8386_clk_reset(mii_bus, EPHY3_SYS_CBCR); + + /* Deassert EPHY DSP */ + val = ipq_mii_read(mii_bus, GCC_GEPHY_MISC); + val &= ~GENMASK(4, 0); + ipq_mii_write(mii_bus, GCC_GEPHY_MISC, val); + /*for ES chips, need to load efuse manually*/ + val = ipq_mii_read(mii_bus, QFPROM_RAW_PTE_ROW2_MSB); + val = FIELD_GET(GENMASK(23, 16), val); + if(val == 1 || val == 2) { + for(i = 0; i < 4; i++) + ipq_qca8386_efuse_loading(mii_bus, i); + } + /*fix 100BT template issue and 10BT threshold issue*/ + for(i = 0; i < 4; i++) + ipq_qca8386_ethphy_ana_fixup(mii_bus, i); + /* Enable efuse loading into analog circuit */ + val = ipq_mii_read(mii_bus, EPHY_CFG); + /* BIT20 for PHY0 and PHY1, BIT21 for PHY2 and PHY3 */ + val &= ~GENMASK(21, 20); + ipq_mii_write(mii_bus, EPHY_CFG, val); + + /* Sleep 10ms */ + usleep_range(10000, 11000); +} + + void ipq_mii_preinit(struct mii_bus *bus) { struct device_node *np = bus->parent->of_node; @@ -351,6 +535,9 @@ void ipq_mii_preinit(struct mii_bus *bus) return; ipq_phy_addr_fixup(bus, np); + if (of_property_read_bool(np, "mdio_clk_fixup")) + ipq_qca8386_clock_init(bus); + return; } EXPORT_SYMBOL_GPL(ipq_mii_preinit); -- 2.17.1