// SPDX-License-Identifier: GPL-2.0 /* FILE NAME: en8801sc.c * PURPOSE: * EN8801SC phy driver for Linux * NOTES: * */ /* INCLUDE FILE DECLARATIONS */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "en8801sc.h" MODULE_DESCRIPTION("Airoha EN8801S PHY drivers for MediaTek SoC"); MODULE_AUTHOR("Airoha"); MODULE_LICENSE("GPL"); #define airoha_mdio_lock(bus) mutex_lock(&((bus)->mdio_lock)) #define airoha_mdio_unlock(bus) mutex_unlock(&((bus)->mdio_lock)) #define phydev_mdio_bus(_dev) (_dev->mdio.bus) #define phydev_phy_addr(_dev) (_dev->mdio.addr) #define phydev_dev(_dev) (&_dev->mdio.dev) #define phydev_pbus_addr(dev) ((dev)->mdio.addr + 1) enum { PHY_STATE_DONE = 0, PHY_STATE_INIT = 1, PHY_STATE_PROCESS = 2, PHY_STATE_FAIL = 3, }; struct en8801s_priv { bool first_init; u16 count; u16 pro_version; }; /* The following led_cfg example is for reference only. LED5 1000M/LINK/ACT (GPIO5) <-> BASE_T_LED0, LED6 10/100M/LINK/ACT (GPIO9) <-> BASE_T_LED1, LED4 100M/LINK/ACT (GPIO8) <-> BASE_T_LED2, */ /* User-defined.B */ #define AIR_LED_SUPPORT #ifdef AIR_LED_SUPPORT static const struct AIR_BASE_T_LED_CFG_S led_cfg[4] = { /* * {LED Enable, GPIO, LED Polarity, LED ON, LED Blink} */ /* BASE-T LED0 */ {LED_ENABLE, 5, AIR_ACTIVE_LOW, BASE_T_LED0_ON_CFG, BASE_T_LED0_BLK_CFG}, /* BASE-T LED1 */ {LED_ENABLE, 9, AIR_ACTIVE_LOW, BASE_T_LED1_ON_CFG, BASE_T_LED1_BLK_CFG}, /* BASE-T LED2 */ {LED_ENABLE, 8, AIR_ACTIVE_LOW, BASE_T_LED2_ON_CFG, BASE_T_LED2_BLK_CFG}, /* BASE-T LED3 */ {LED_DISABLE, 1, AIR_ACTIVE_LOW, BASE_T_LED3_ON_CFG, BASE_T_LED3_BLK_CFG}, }; static const u16 led_dur = UNIT_LED_BLINK_DURATION << AIR_LED_BLK_DUR_64M; #endif /* User-defined.E */ /************************************************************************ * F U N C T I O N S ************************************************************************/ static int en8801s_phase2_init(struct phy_device *phydev); static int __airoha_cl45_write(struct mii_bus *bus, int port, u32 devad, u32 reg, u16 val) { int ret = 0; struct device *dev = &bus->dev; ret = __mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG, devad); if (ret < 0) { dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret); return ret; } ret = __mdiobus_write(bus, port, MII_MMD_ADDR_DATA_REG, reg); if (ret < 0) { dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret); return ret; } ret = __mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad); if (ret < 0) { dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret); return ret; } ret = __mdiobus_write(bus, port, MII_MMD_ADDR_DATA_REG, val); if (ret < 0) { dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret); return ret; } return ret; } static int __airoha_cl45_read(struct mii_bus *bus, int port, u32 devad, u32 reg, u16 *read_data) { int ret = 0; struct device *dev = &bus->dev; ret = __mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG, devad); if (ret < 0) { dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret); return ret; } ret = __mdiobus_write(bus, port, MII_MMD_ADDR_DATA_REG, reg); if (ret < 0) { dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret); return ret; } ret = __mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad); if (ret < 0) { dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret); return ret; } *read_data = __mdiobus_read(bus, port, MII_MMD_ADDR_DATA_REG); return ret; } static int airoha_cl45_write(struct mii_bus *bus, int port, u32 devad, u32 reg, u16 val) { int ret = 0; airoha_mdio_lock(bus); ret = __airoha_cl45_write(bus, port, devad, reg, val); airoha_mdio_unlock(bus); return ret; } static int airoha_cl45_read(struct mii_bus *bus, int port, u32 devad, u32 reg, u16 *read_data) { int ret = 0; airoha_mdio_lock(bus); ret = __airoha_cl45_read(bus, port, devad, reg, read_data); airoha_mdio_unlock(bus); return ret; } static int __airoha_pbus_write(struct mii_bus *ebus, int pbus_id, unsigned long pbus_address, unsigned long pbus_data) { int ret = 0; ret = __mdiobus_write(ebus, pbus_id, 0x1F, (unsigned int)(pbus_address >> 6)); if (ret < 0) return ret; ret = __mdiobus_write(ebus, pbus_id, (unsigned int)((pbus_address >> 2) & 0xf), (unsigned int)(pbus_data & 0xFFFF)); if (ret < 0) return ret; ret = __mdiobus_write(ebus, pbus_id, 0x10, (unsigned int)(pbus_data >> 16)); if (ret < 0) return ret; return ret; } static unsigned long __airoha_pbus_read(struct mii_bus *ebus, int pbus_id, unsigned long pbus_address) { unsigned long pbus_data; unsigned int pbus_data_low, pbus_data_high; int ret = 0; struct device *dev = &ebus->dev; ret = __mdiobus_write(ebus, pbus_id, 0x1F, (unsigned int)(pbus_address >> 6)); if (ret < 0) { dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret); return INVALID_DATA; } pbus_data_low = __mdiobus_read(ebus, pbus_id, (unsigned int)((pbus_address >> 2) & 0xf)); pbus_data_high = __mdiobus_read(ebus, pbus_id, 0x10); pbus_data = (pbus_data_high << 16) + pbus_data_low; return pbus_data; } static int airoha_pbus_write(struct mii_bus *ebus, int pbus_id, unsigned long pbus_address, unsigned long pbus_data) { int ret = 0; airoha_mdio_lock(ebus); ret = __airoha_pbus_write(ebus, pbus_id, pbus_address, pbus_data); airoha_mdio_unlock(ebus); return ret; } static unsigned long airoha_pbus_read(struct mii_bus *ebus, int pbus_id, unsigned long pbus_address) { unsigned long pbus_data; airoha_mdio_lock(ebus); pbus_data = __airoha_pbus_read(ebus, pbus_id, pbus_address); airoha_mdio_unlock(ebus); return pbus_data; } /* Airoha Token Ring Write function */ static int airoha_tr_reg_write(struct phy_device *phydev, unsigned long tr_address, unsigned long tr_data) { int ret = 0; int phy_addr = phydev_phy_addr(phydev); struct mii_bus *ebus = phydev_mdio_bus(phydev); airoha_mdio_lock(ebus); ret = __mdiobus_write(ebus, phy_addr, 0x1F, 0x52b5); /* page select */ ret = __mdiobus_write(ebus, phy_addr, 0x11, (unsigned int)(tr_data & 0xffff)); ret = __mdiobus_write(ebus, phy_addr, 0x12, (unsigned int)(tr_data >> 16)); ret = __mdiobus_write(ebus, phy_addr, 0x10, (unsigned int)(tr_address | TrReg_WR)); ret = __mdiobus_write(ebus, phy_addr, 0x1F, 0x0); /* page resetore */ airoha_mdio_unlock(ebus); return ret; } #ifdef AIR_LED_SUPPORT static int airoha_led_set_usr_def(struct phy_device *phydev, u8 entity, int polar, u16 on_evt, u16 blk_evt) { int ret = 0; int phy_addr = phydev_phy_addr(phydev); struct mii_bus *mbus = phydev_mdio_bus(phydev); if (polar == AIR_ACTIVE_HIGH) on_evt |= LED_ON_POL; else on_evt &= ~LED_ON_POL; ret = airoha_cl45_write(mbus, phy_addr, 0x1f, LED_ON_CTRL(entity), on_evt | LED_ON_EN); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1f, LED_BLK_CTRL(entity), blk_evt); if (ret < 0) return ret; return 0; } static int airoha_led_set_mode(struct phy_device *phydev, u8 mode) { u16 cl45_data; int err = 0; int phy_addr = phydev_phy_addr(phydev); struct mii_bus *mbus = phydev_mdio_bus(phydev); err = airoha_cl45_read(mbus, phy_addr, 0x1f, LED_BCR, &cl45_data); if (err < 0) return err; switch (mode) { case AIR_LED_MODE_DISABLE: cl45_data &= ~LED_BCR_EXT_CTRL; cl45_data &= ~LED_BCR_MODE_MASK; cl45_data |= LED_BCR_MODE_DISABLE; break; case AIR_LED_MODE_USER_DEFINE: cl45_data |= LED_BCR_EXT_CTRL; cl45_data |= LED_BCR_CLK_EN; break; default: return -EINVAL; } err = airoha_cl45_write(mbus, phy_addr, 0x1f, LED_BCR, cl45_data); if (err < 0) return err; return 0; } static int airoha_led_set_state(struct phy_device *phydev, u8 entity, u8 state) { u16 cl45_data; int err; int phy_addr = phydev_phy_addr(phydev); struct mii_bus *mbus = phydev_mdio_bus(phydev); err = airoha_cl45_read(mbus, phy_addr, 0x1f, LED_ON_CTRL(entity), &cl45_data); if (err < 0) return err; if (state == LED_ENABLE) cl45_data |= LED_ON_EN; else cl45_data &= ~LED_ON_EN; err = airoha_cl45_write(mbus, phy_addr, 0x1f, LED_ON_CTRL(entity), cl45_data); if (err < 0) return err; return 0; } static int en8801s_led_init(struct phy_device *phydev) { unsigned long led_gpio = 0, reg_value = 0; int ret = 0, led_id; struct mii_bus *mbus = phydev_mdio_bus(phydev); int gpio_led_rg[3] = {0x1870, 0x1874, 0x1878}; u16 cl45_data = led_dur; struct device *dev = phydev_dev(phydev); int phy_addr = phydev_phy_addr(phydev); int pbus_addr = phydev_pbus_addr(phydev); ret = airoha_cl45_write(mbus, phy_addr, 0x1f, LED_BLK_DUR, cl45_data); if (ret < 0) return ret; cl45_data >>= 1; ret = airoha_cl45_write(mbus, phy_addr, 0x1f, LED_ON_DUR, cl45_data); if (ret < 0) return ret; ret = airoha_led_set_mode(phydev, AIR_LED_MODE_USER_DEFINE); if (ret != 0) { dev_err(dev, "LED fail to set mode, ret %d !\n", ret); return ret; } for (led_id = 0; led_id < EN8801S_LED_COUNT; led_id++) { reg_value = 0; ret = airoha_led_set_state(phydev, led_id, led_cfg[led_id].en); if (ret != 0) { dev_err(dev, "LED fail to set state, ret %d !\n", ret); return ret; } if (led_cfg[led_id].en == LED_ENABLE) { if ((led_cfg[led_id].gpio < 0) || led_cfg[led_id].gpio > 9) { dev_err(dev, "GPIO%d is out of range!! GPIO number is 0~9.\n", led_cfg[led_id].gpio); return -EIO; } led_gpio |= BIT(led_cfg[led_id].gpio); reg_value = airoha_pbus_read(mbus, pbus_addr, gpio_led_rg[led_cfg[led_id].gpio / 4]); LED_SET_GPIO_SEL(led_cfg[led_id].gpio, led_id, reg_value); dev_dbg(dev, "[Airoha] gpio%d, reg_value 0x%lx\n", led_cfg[led_id].gpio, reg_value); ret = airoha_pbus_write(mbus, pbus_addr, gpio_led_rg[led_cfg[led_id].gpio / 4], reg_value); if (ret < 0) return ret; ret = airoha_led_set_usr_def(phydev, led_id, led_cfg[led_id].pol, led_cfg[led_id].on_cfg, led_cfg[led_id].blk_cfg); if (ret != 0) { dev_err(dev, "LED fail to set usr def, ret %d !\n", ret); return ret; } } } reg_value = (airoha_pbus_read(mbus, pbus_addr, 0x1880) & ~led_gpio); ret = airoha_pbus_write(mbus, pbus_addr, 0x1880, reg_value); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x186c, led_gpio); if (ret < 0) return ret; dev_info(dev, "LED initialize OK !\n"); return 0; } #endif static int en8801s_phy_process(struct phy_device *phydev) { struct mii_bus *mbus = phydev_mdio_bus(phydev); unsigned long reg_value = 0; int ret = 0; int pbus_addr = phydev_pbus_addr(phydev); reg_value = airoha_pbus_read(mbus, pbus_addr, 0x19e0); reg_value |= BIT(0); ret = airoha_pbus_write(mbus, pbus_addr, 0x19e0, reg_value); if (ret < 0) return ret; reg_value = airoha_pbus_read(mbus, pbus_addr, 0x19e0); reg_value &= ~BIT(0); ret = airoha_pbus_write(mbus, pbus_addr, 0x19e0, reg_value); if (ret < 0) return ret; return ret; } static int en8801s_phase1_init(struct phy_device *phydev) { unsigned long pbus_data; int pbus_addr = EN8801S_PBUS_DEFAULT_ADDR; u16 reg_value; int retry, ret = 0; struct mii_bus *mbus = phydev_mdio_bus(phydev); struct device *dev = phydev_dev(phydev); struct en8801s_priv *priv = phydev->priv; priv->count = 1; msleep(1000); retry = MAX_OUI_CHECK; while (1) { pbus_data = airoha_pbus_read(mbus, pbus_addr, EN8801S_RG_ETHER_PHY_OUI); /* PHY OUI */ if (pbus_data == EN8801S_PBUS_OUI) { dev_info(dev, "PBUS addr 0x%x: Start initialized.\n", pbus_addr); break; } pbus_addr = phydev_pbus_addr(phydev); if (0 == --retry) { dev_err(dev, "Probe fail !\n"); return 0; } } ret = airoha_pbus_write(mbus, pbus_addr, EN8801S_RG_BUCK_CTL, 0x03); if (ret < 0) return ret; pbus_data = airoha_pbus_read(mbus, pbus_addr, EN8801S_RG_PROD_VER); priv->pro_version = pbus_data & 0xf; dev_info(dev, "EN8801S Procduct Version :E%d\n", priv->pro_version); mdelay(10); pbus_data = (airoha_pbus_read(mbus, pbus_addr, EN8801S_RG_LTR_CTL) & 0xfffffffc) | BIT(2); ret = airoha_pbus_write(mbus, pbus_addr, EN8801S_RG_LTR_CTL, pbus_data); if (ret < 0) return ret; mdelay(500); pbus_data = (pbus_data & ~BIT(2)) | EN8801S_RX_POLARITY_NORMAL | EN8801S_TX_POLARITY_NORMAL; ret = airoha_pbus_write(mbus, pbus_addr, EN8801S_RG_LTR_CTL, pbus_data); if (ret < 0) return ret; mdelay(500); if (priv->pro_version == 4) { pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1900); dev_dbg(dev, "Before 0x1900 0x%lx\n", pbus_data); ret = airoha_pbus_write(mbus, pbus_addr, 0x1900, 0x101009f); if (ret < 0) return ret; pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1900); dev_dbg(dev, "After 0x1900 0x%lx\n", pbus_data); pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x19a8); dev_dbg(dev, "Before 19a8 0x%lx\n", pbus_data); ret = airoha_pbus_write(mbus, pbus_addr, 0x19a8, pbus_data & ~BIT(16)); if (ret < 0) return ret; pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x19a8); dev_dbg(dev, "After 19a8 0x%lx\n", pbus_data); } pbus_data = airoha_pbus_read(mbus, pbus_addr, EN8801S_RG_SMI_ADDR); /* SMI ADDR */ pbus_data = (pbus_data & 0xffff0000) | (unsigned long)(phydev_pbus_addr(phydev) << 8) | (unsigned long)(phydev_phy_addr(phydev)); dev_info(phydev_dev(phydev), "SMI_ADDR=%lx (renew)\n", pbus_data); ret = airoha_pbus_write(mbus, pbus_addr, EN8801S_RG_SMI_ADDR, pbus_data); mdelay(10); retry = MAX_RETRY; while (1) { mdelay(10); reg_value = phy_read(phydev, MII_PHYSID2); if (reg_value == EN8801S_PHY_ID2) break; /* wait GPHY ready */ retry--; if (retry == 0) { dev_err(dev, "Initialize fail !\n"); return 0; } } /* Software Reset PHY */ reg_value = phy_read(phydev, MII_BMCR); reg_value |= BMCR_RESET; ret = phy_write(phydev, MII_BMCR, reg_value); if (ret < 0) return ret; retry = MAX_RETRY; do { mdelay(10); reg_value = phy_read(phydev, MII_BMCR); retry--; if (retry == 0) { dev_err(dev, "Reset fail !\n"); return 0; } } while (reg_value & BMCR_RESET); phydev->dev_flags = PHY_STATE_INIT; dev_info(dev, "Phase1 initialize OK ! (%s)\n", EN8801S_DRIVER_VERSION); if (priv->pro_version == 4) { ret = en8801s_phase2_init(phydev); if (ret != 0) { dev_info(dev, "en8801_phase2_init failed\n"); phydev->dev_flags = PHY_STATE_FAIL; return 0; } phydev->dev_flags = PHY_STATE_PROCESS; } return 0; } static int en8801s_phase2_init(struct phy_device *phydev) { union gephy_all_REG_LpiReg1Ch GPHY_RG_LPI_1C; union gephy_all_REG_dev1Eh_reg324h GPHY_RG_1E_324; union gephy_all_REG_dev1Eh_reg012h GPHY_RG_1E_012; union gephy_all_REG_dev1Eh_reg017h GPHY_RG_1E_017; unsigned long pbus_data; int phy_addr = phydev_phy_addr(phydev); int pbus_addr = phydev_pbus_addr(phydev); u16 cl45_value; int retry, ret = 0; struct mii_bus *mbus = phydev_mdio_bus(phydev); struct device *dev = phydev_dev(phydev); struct en8801s_priv *priv = phydev->priv; pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1690); pbus_data |= BIT(31); ret = airoha_pbus_write(mbus, pbus_addr, 0x1690, pbus_data); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x0600, 0x0c000c00); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x10, 0xD801); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x0, 0x9140); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x0A14, 0x0003); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x0600, 0x0c000c00); if (ret < 0) return ret; /* Set FCM control */ ret = airoha_pbus_write(mbus, pbus_addr, 0x1404, 0x004b); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x140c, 0x0007); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x142c, 0x05050505); if (ret < 0) return ret; pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1440); ret = airoha_pbus_write(mbus, pbus_addr, 0x1440, pbus_data & ~BIT(11)); if (ret < 0) return ret; pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1408); ret = airoha_pbus_write(mbus, pbus_addr, 0x1408, pbus_data | BIT(5)); if (ret < 0) return ret; /* Set GPHY Perfomance*/ /* Token Ring */ ret = airoha_tr_reg_write(phydev, RgAddr_R1000DEC_15h, 0x0055A0); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_R1000DEC_17h, 0x07FF3F); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_PMA_00h, 0x00001E); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_PMA_01h, 0x6FB90A); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_PMA_17h, 0x060671); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_PMA_18h, 0x0E2F00); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_TR_26h, 0x444444); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_03h, 0x000000); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_06h, 0x2EBAEF); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_08h, 0x00000B); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_0Ch, 0x00504D); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_0Dh, 0x02314F); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_0Fh, 0x003028); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_10h, 0x005010); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_11h, 0x040001); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_13h, 0x018670); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_14h, 0x00024A); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_1Bh, 0x000072); if (ret < 0) return ret; ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_1Ch, 0x003210); if (ret < 0) return ret; /* CL22 & CL45 */ ret = phy_write(phydev, 0x1f, 0x03); if (ret < 0) return ret; GPHY_RG_LPI_1C.DATA = phy_read(phydev, RgAddr_LPI_1Ch); GPHY_RG_LPI_1C.DataBitField.smi_deton_th = 0x0C; ret = phy_write(phydev, RgAddr_LPI_1Ch, GPHY_RG_LPI_1C.DATA); if (ret < 0) return ret; ret = phy_write(phydev, RgAddr_LPI_1Ch, 0xC92); if (ret < 0) return ret; ret = phy_write(phydev, RgAddr_AUXILIARY_1Dh, 0x1); if (ret < 0) return ret; ret = phy_write(phydev, 0x1f, 0x0); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x120, 0x8014); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x122, 0xffff); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x123, 0xffff); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x144, 0x0200); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x14A, 0xEE20); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x189, 0x0110); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x19B, 0x0111); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x234, 0x0181); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x238, 0x0120); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x239, 0x0117); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x268, 0x07F4); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x2D1, 0x0733); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x323, 0x0011); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x324, 0x013F); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x326, 0x0037); if (ret < 0) return ret; ret = airoha_cl45_read(mbus, phy_addr, 0x1E, 0x324, &cl45_value); if (ret < 0) return ret; GPHY_RG_1E_324.DATA = cl45_value; GPHY_RG_1E_324.DataBitField.smi_det_deglitch_off = 0; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x324, GPHY_RG_1E_324.DATA); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x19E, 0xC2); if (ret < 0) return ret; ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x013, 0x0); if (ret < 0) return ret; /* EFUSE */ airoha_pbus_write(mbus, pbus_addr, 0x1C08, 0x40000040); retry = MAX_RETRY; while (retry != 0) { mdelay(1); pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1C08); if ((pbus_data & BIT(30)) == 0) break; retry--; } pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1C38); /* RAW#2 */ ret = airoha_cl45_read(mbus, phy_addr, 0x1E, 0x12, &cl45_value); if (ret < 0) return ret; GPHY_RG_1E_012.DATA = cl45_value; GPHY_RG_1E_012.DataBitField.da_tx_i2mpb_a_tbt = (u16)(pbus_data & 0x03f); ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x12, GPHY_RG_1E_012.DATA); if (ret < 0) return ret; ret = airoha_cl45_read(mbus, phy_addr, 0x1E, 0x17, &cl45_value); if (ret < 0) return ret; GPHY_RG_1E_017.DATA = cl45_value; GPHY_RG_1E_017.DataBitField.da_tx_i2mpb_b_tbt = (u16)((pbus_data >> 8) & 0x03f); ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x17, GPHY_RG_1E_017.DATA); if (ret < 0) return ret; airoha_pbus_write(mbus, pbus_addr, 0x1C08, 0x40400040); retry = MAX_RETRY; while (retry != 0) { mdelay(1); pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1C08); if ((pbus_data & BIT(30)) == 0) break; retry--; } pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1C30); /* RAW#16 */ GPHY_RG_1E_324.DataBitField.smi_det_deglitch_off = (u16)((pbus_data >> 12) & 0x01); ret = airoha_cl45_write(mbus, phy_addr, 0x1E, 0x324, GPHY_RG_1E_324.DATA); if (ret < 0) return ret; #ifdef AIR_LED_SUPPORT ret = en8801s_led_init(phydev); if (ret != 0) dev_err(dev, "en8801s_led_init fail (ret:%d) !\n", ret); #endif ret = airoha_cl45_read(mbus, phy_addr, MDIO_MMD_AN, MDIO_AN_EEE_ADV, &cl45_value); if (ret < 0) return ret; if (cl45_value == 0) { pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1960); if (0xA == ((pbus_data & 0x07c00000) >> 22)) { pbus_data = (pbus_data & 0xf83fffff) | (0xC << 22); ret = airoha_pbus_write(mbus, pbus_addr, 0x1960, pbus_data); if (ret < 0) return ret; mdelay(10); pbus_data = (pbus_data & 0xf83fffff) | (0xE << 22); ret = airoha_pbus_write(mbus, pbus_addr, 0x1960, pbus_data); if (ret < 0) return ret; mdelay(10); } } else { pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1960); if (0xE == ((pbus_data & 0x07c00000) >> 22)) { pbus_data = (pbus_data & 0xf83fffff) | (0xC << 22); ret = airoha_pbus_write(mbus, pbus_addr, 0x1960, pbus_data); if (ret < 0) return ret; mdelay(10); pbus_data = (pbus_data & 0xf83fffff) | (0xA << 22); ret = airoha_pbus_write(mbus, pbus_addr, 0x1960, pbus_data); if (ret < 0) return ret; mdelay(10); } } priv->first_init = false; dev_info(phydev_dev(phydev), "Phase2 initialize OK !\n"); return 0; } static int en8801s_read_status(struct phy_device *phydev) { int ret = 0, preSpeed = phydev->speed; struct mii_bus *mbus = phydev_mdio_bus(phydev); u32 reg_value; struct device *dev = phydev_dev(phydev); int pbus_addr = phydev_pbus_addr(phydev); struct en8801s_priv *priv = phydev->priv; ret = genphy_read_status(phydev); if (phydev->link == LINK_DOWN) preSpeed = phydev->speed = 0; if (phydev->dev_flags == PHY_STATE_PROCESS) { en8801s_phy_process(phydev); phydev->dev_flags = PHY_STATE_DONE; } if (phydev->dev_flags == PHY_STATE_INIT) { dev_dbg(dev, "phydev->link %d, count %d\n", phydev->link, priv->count); if ((phydev->link) || (priv->count == 5)) { if (priv->pro_version != 4) { ret = en8801s_phase2_init(phydev); if (ret != 0) { dev_info(dev, "en8801_phase2_init failed\n"); phydev->dev_flags = PHY_STATE_FAIL; return 0; } phydev->dev_flags = PHY_STATE_PROCESS; } } priv->count++; } if ((preSpeed != phydev->speed) && (phydev->link == LINK_UP)) { preSpeed = phydev->speed; if (preSpeed == SPEED_10) { reg_value = airoha_pbus_read(mbus, pbus_addr, 0x1694); reg_value |= BIT(31); ret = airoha_pbus_write(mbus, pbus_addr, 0x1694, reg_value); if (ret < 0) return ret; phydev->dev_flags = PHY_STATE_PROCESS; } else { reg_value = airoha_pbus_read(mbus, pbus_addr, 0x1694); reg_value &= ~BIT(31); ret = airoha_pbus_write(mbus, pbus_addr, 0x1694, reg_value); if (ret < 0) return ret; phydev->dev_flags = PHY_STATE_PROCESS; } airoha_pbus_write(mbus, pbus_addr, 0x0600, 0x0c000c00); if (preSpeed == SPEED_1000) { dev_dbg(dev, "SPEED_1000\n"); ret = airoha_pbus_write(mbus, pbus_addr, 0x10, 0xD801); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x0, 0x9140); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x0A14, 0x0003); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x0600, 0x0c000c00); if (ret < 0) return ret; mdelay(2); /* delay 2 ms */ ret = airoha_pbus_write(mbus, pbus_addr, 0x1404, 0x004b); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x140c, 0x0007); if (ret < 0) return ret; } else if (preSpeed == SPEED_100) { dev_dbg(dev, "SPEED_100\n"); ret = airoha_pbus_write(mbus, pbus_addr, 0x10, 0xD401); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x0, 0x9140); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x0A14, 0x0007); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x0600, 0x0c11); if (ret < 0) return ret; mdelay(2); /* delay 2 ms */ ret = airoha_pbus_write(mbus, pbus_addr, 0x1404, 0x0027); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x140c, 0x0007); if (ret < 0) return ret; } else if (preSpeed == SPEED_10) { dev_dbg(dev, "SPEED_10\n"); ret = airoha_pbus_write(mbus, pbus_addr, 0x10, 0xD001); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x0, 0x9140); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x0A14, 0x000b); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x0600, 0x0c11); if (ret < 0) return ret; mdelay(2); /* delay 2 ms */ ret = airoha_pbus_write(mbus, pbus_addr, 0x1404, 0x0027); if (ret < 0) return ret; ret = airoha_pbus_write(mbus, pbus_addr, 0x140c, 0x0007); if (ret < 0) return ret; } } return ret; } static int en8801s_probe(struct phy_device *phydev) { struct en8801s_priv *priv; unsigned long phy_addr = phydev_phy_addr(phydev); struct mdio_device *mdiodev = &phydev->mdio; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->count = 0; priv->first_init = true; if (mdiodev->reset_gpio) { dev_dbg(phydev_dev(phydev), "Assert PHY %lx HWRST until phy_init_hw\n", phy_addr); phy_device_reset(phydev, 1); } phydev->priv = priv; return 0; } static int airoha_mmd_read(struct phy_device *phydev, int devad, u16 reg) { struct mii_bus *mbus = phydev_mdio_bus(phydev); int phy_addr = phydev_phy_addr(phydev); int ret = 0; u16 cl45_value; ret = __airoha_cl45_read(mbus, phy_addr, devad, reg, &cl45_value); if (ret < 0) return ret; return cl45_value; } static int airoha_mmd_write(struct phy_device *phydev, int devad, u16 reg, u16 val) { struct mii_bus *mbus = phydev_mdio_bus(phydev); int phy_addr = phydev_phy_addr(phydev); int pbus_addr = phydev_pbus_addr(phydev); unsigned long pbus_data; int ret = 0; if (MDIO_MMD_AN == devad && MDIO_AN_EEE_ADV == reg) { if (val == 0) { pbus_data = __airoha_pbus_read(mbus, pbus_addr, 0x1960); if (0xA == ((pbus_data & 0x07c00000) >> 22)) { pbus_data = (pbus_data & 0xf83fffff) | (0xC << 22); __airoha_pbus_write(mbus, pbus_addr, 0x1960, pbus_data); mdelay(10); pbus_data = (pbus_data & 0xf83fffff) | (0xE << 22); __airoha_pbus_write(mbus, pbus_addr, 0x1960, pbus_data); mdelay(10); } } else { pbus_data = __airoha_pbus_read(mbus, pbus_addr, 0x1960); if (0xE == ((pbus_data & 0x07c00000) >> 22)) { pbus_data = (pbus_data & 0xf83fffff) | (0xC << 22); __airoha_pbus_write(mbus, pbus_addr, 0x1960, pbus_data); mdelay(10); pbus_data = (pbus_data & 0xf83fffff) | (0xA << 22); __airoha_pbus_write(mbus, pbus_addr, 0x1960, pbus_data); mdelay(10); } } } ret = __airoha_cl45_write(mbus, phy_addr, devad, reg, val); if (ret < 0) return ret; return 0; } static struct phy_driver Airoha_driver[] = { { .phy_id = EN8801SC_PHY_ID, .name = "Airoha EN8801SC", .phy_id_mask = 0x0ffffff0, .features = PHY_GBIT_FEATURES, .probe = en8801s_probe, .config_init = en8801s_phase1_init, .config_aneg = genphy_config_aneg, .read_status = en8801s_read_status, .suspend = genphy_suspend, .resume = genphy_resume, .read_mmd = airoha_mmd_read, .write_mmd = airoha_mmd_write, } }; module_phy_driver(Airoha_driver); static struct mdio_device_id __maybe_unused Airoha_tbl[] = { { EN8801SC_PHY_ID, 0x0ffffff0 }, { } }; MODULE_DEVICE_TABLE(mdio, Airoha_tbl);