// 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 #if (KERNEL_VERSION(4, 16, 0) > LINUX_VERSION_CODE) #include #include #endif #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)) #if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) #define phydev_mdio_bus(_dev) (_dev->bus) #define phydev_phy_addr(_dev) (_dev->addr) #define phydev_dev(_dev) (&_dev->dev) #define phydev_pbus_addr(dev) ((dev)->addr + 1) #else #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) #endif enum { PHY_STATE_DONE = 0, PHY_STATE_INIT = 1, PHY_STATE_PROCESS = 2, PHY_STATE_FAIL = 3, }; #define CONFIG_EN8801S_DEBUGFS struct en8801s_priv { bool first_init; u16 count; u16 pro_version; #if (KERNEL_VERSION(4, 16, 0) > LINUX_VERSION_CODE) struct gpio_desc *hw_reset; #endif #ifdef CONFIG_EN8801S_DEBUGFS struct dentry *debugfs_root; #endif }; /* 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 */ #ifdef CONFIG_EN8801S_DEBUGFS #define EN8801S_DEBUGFS_POLARITY_HELP_STRING \ "\nUsage: echo [tx_polarity] [rx_polarity] > /sys/" \ "kernel/debug/mdio-bus\':[phy_addr]/polarity" \ "\npolarity: tx_normal, tx_reverse, rx_normal, rx_reverse" \ "\ntx_normal is tx polarity is normal." \ "\ntx_reverse is tx polarity need to be swapped." \ "\nrx_normal is rx polarity is normal." \ "\nrx_reverse is rx polarity need to be swapped." \ "\nFor example tx polarity need to be swapped. " \ "But rx polarity is normal." \ "\necho tx_reverse rx_normal > /sys/" \ "kernel/debug/mdio-bus\':[phy_addr]/polarity" \ "\n" #define EN8801S_DEBUGFS_RX_ERROR_STRING \ "\nRx param is not correct." \ "\nrx_normal: rx polarity is normal." \ "rx_reverse: rx polarity is reverse.\n" #define EN8801S_DEBUGFS_TX_ERROR_STRING \ "\nTx param is not correct." \ "\ntx_normal: tx polarity is normal." \ "tx_reverse: tx polarity is reverse.\n" #define EN8801S_DEBUGFS_PBUS_HELP_STRING \ "\nUsage: echo w [pbus_addr] [pbus_reg] [value] > /sys/" \ "kernel/debug/mdio-bus\':[phy_addr]/pbus_reg_op" \ "\n echo r [pbus_addr] [pbus_reg] > /sys/" \ "kernel/debug/mdio-bus\':[phy_addr]/pbus_reg_op" \ "\nRead example: PBUS addr 0x19, Register 0x19a4" \ "\necho r 0x19 0x19a4 > /sys/" \ "kernel/debug/mdio-bus\':[phy_addr]/pbus_reg_op" \ "\nWrite example: PBUS addr 0x19, Register 0xcf8 0x1a01503" \ "\necho w 0x19 0xcf8 0x1a01503> /sys/" \ "kernel/debug/mdio-bus\':[phy_addr]/pbus_reg_op" \ "\n" #endif /************************************************************************ * F U N C T I O N S ************************************************************************/ static int en8801s_phase2_init(struct phy_device *phydev); static void air_debugfs_remove(struct phy_device *phydev); static int en8801s_debugfs_init(struct phy_device *phydev); static unsigned int airoha_cl22_read(struct mii_bus *ebus, int phy_addr, unsigned int phy_register, unsigned int *read_data) { *read_data = mdiobus_read(ebus, phy_addr, phy_register); return 0; } static int airoha_cl22_write(struct mii_bus *ebus, int phy_addr, unsigned int phy_register, unsigned int write_data) { int ret = 0; struct device *dev = &ebus->dev; ret = mdiobus_write(ebus, phy_addr, phy_register, write_data); if (ret < 0) { dev_err(dev, "%s fail. (ret=%d)\n", __func__, ret); return ret; } return ret; } 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; struct device *dev = &ebus->dev; 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; int phy_addr = EN8801S_PHY_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; #if (KERNEL_VERSION(4, 16, 0) > LINUX_VERSION_CODE) /* Deassert the reset signal */ if (priv->hw_reset) gpiod_set_value(priv->hw_reset, 0); #endif 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 mii_bus *mbus = phydev_mdio_bus(phydev); struct device *dev = &mbus->dev; int ret = 0; #if (KERNEL_VERSION(4, 16, 0) > LINUX_VERSION_CODE) struct gpio_desc *en8801s_reset; int err = 0; #else struct mdio_device *mdiodev = &phydev->mdio; #endif priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->count = 0; priv->first_init = true; phydev->priv = priv; #ifdef CONFIG_EN8801S_DEBUGFS ret = en8801s_debugfs_init(phydev); if (ret < 0) { air_debugfs_remove(phydev); kfree(priv); return ret; } #endif #if (KERNEL_VERSION(4, 16, 0) > LINUX_VERSION_CODE) /* Assert the optional reset signal */ en8801s_reset = gpiod_get_optional(&phydev->dev, "reset", GPIOD_OUT_HIGH); err = PTR_ERR_OR_ZERO(en8801s_reset); if (err) { dev_dbg(phydev_dev(phydev), "PHY %lx have no reset pin in device tree.\n", phy_addr); } else { dev_dbg(phydev_dev(phydev), "Assert PHY %lx HWRST until config_init\n", phy_addr); priv->hw_reset = en8801s_reset; } #else 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); } #endif phydev->priv = priv; return 0; } static const char * const tx_rx_string[32] = { "Tx Normal, Rx Reverse", "Tx Reverse, Rx Reverse", "Tx Normal, Rx Normal", "Tx Reverse, Rx Normal", }; static int en8801s_counter_show(struct seq_file *seq, void *v) { struct phy_device *phydev = seq->private; struct mii_bus *mbus = phydev_mdio_bus(phydev); int ret = 0; u32 pkt_cnt = 0; int pbus_addr = phydev_pbus_addr(phydev); seq_puts(seq, "==========AIR PHY COUNTER==========\n"); seq_puts(seq, "|\t<>\n"); seq_puts(seq, "| Rx from Line side_S :"); pkt_cnt = airoha_pbus_read(mbus, pbus_addr, 0x1490); seq_printf(seq, "%010u |\n", pkt_cnt); seq_puts(seq, "| Rx from Line side_T :"); pkt_cnt = airoha_pbus_read(mbus, pbus_addr, 0x1494); seq_printf(seq, "%010u |\n", pkt_cnt); seq_puts(seq, "| Tx to System side_S :"); pkt_cnt = airoha_pbus_read(mbus, pbus_addr, 0x149c); seq_printf(seq, "%010u |\n", pkt_cnt); seq_puts(seq, "| Tx to System side_T :"); pkt_cnt = airoha_pbus_read(mbus, pbus_addr, 0x14a0); seq_printf(seq, "%010u |\n", pkt_cnt); seq_puts(seq, "| Rx from System side_S :"); pkt_cnt = airoha_pbus_read(mbus, pbus_addr, 0x1478); seq_printf(seq, "%010u |\n", pkt_cnt); seq_puts(seq, "| Rx from System side_T :"); pkt_cnt = airoha_pbus_read(mbus, pbus_addr, 0x147c); seq_printf(seq, "%010u |\n", pkt_cnt); seq_puts(seq, "| Tx to Line side_S :"); pkt_cnt = airoha_pbus_read(mbus, pbus_addr, 0x1484); seq_printf(seq, "%010u |\n", pkt_cnt); seq_puts(seq, "| Tx to Line side_T :"); pkt_cnt = airoha_pbus_read(mbus, pbus_addr, 0x1488); seq_printf(seq, "%010u |\n", pkt_cnt); seq_puts(seq, "| Rx from Line side_PAUSE :"); pkt_cnt = airoha_pbus_read(mbus, pbus_addr, 0x1498); seq_printf(seq, "%010u |\n", pkt_cnt); seq_puts(seq, "| Tx to System side_PAUSE :"); pkt_cnt = airoha_pbus_read(mbus, pbus_addr, 0x14a4); seq_printf(seq, "%010u |\n", pkt_cnt); seq_puts(seq, "| Rx from System side_PAUSE :"); pkt_cnt = airoha_pbus_read(mbus, pbus_addr, 0x1480); seq_printf(seq, "%010u |\n", pkt_cnt); seq_puts(seq, "| Tx to Line side_PAUSE :"); pkt_cnt = airoha_pbus_read(mbus, pbus_addr, 0x148c); seq_printf(seq, "%010u |\n", pkt_cnt); ret = airoha_pbus_write(mbus, pbus_addr, 0x1474, 0x3); if (ret < 0) seq_puts(seq, "\nClear Counter fail\n"); else seq_puts(seq, "\nClear Counter!!\n"); return ret; } static int en8801s_counter_open(struct inode *inode, struct file *file) { return single_open(file, en8801s_counter_show, inode->i_private); } int en8801s_info_show(struct seq_file *seq, void *v) { struct phy_device *phydev = seq->private; struct mii_bus *mbus = phydev_mdio_bus(phydev); int pbus_addr = phydev_pbus_addr(phydev); unsigned int tx_rx = (airoha_pbus_read(mbus, pbus_addr, EN8801S_RG_LTR_CTL) & 0x3); unsigned long pbus_data = 0; seq_puts(seq, "<>\n"); pbus_data = airoha_pbus_read(mbus, pbus_addr, EN8801S_RG_PROD_VER); pbus_data &= 0xf; seq_printf(seq, "| Product Version : E%ld\n", pbus_data); seq_printf(seq, "| Driver Version : %s\n", EN8801S_DRIVER_VERSION); pbus_data = airoha_pbus_read(mbus, pbus_addr, 0xb04); seq_printf(seq, "| Serdes Status : Rx_Sync(%01ld), AN_Done(%01ld)\n", GET_BIT(pbus_data, 4), GET_BIT(pbus_data, 0)); seq_printf(seq, "| Tx, Rx Polarity : %s(%02d)\n", tx_rx_string[tx_rx], tx_rx); seq_puts(seq, "\n"); return 0; } static int en8801s_info_open(struct inode *inode, struct file *file) { return single_open(file, en8801s_info_show, inode->i_private); } static int en8801s_debugfs_pbus_help(void) { pr_notice(EN8801S_DEBUGFS_PBUS_HELP_STRING); return 0; } static ssize_t en8801s_debugfs_pbus(struct file *file, const char __user *buffer, size_t count, loff_t *data) { struct phy_device *phydev = file->private_data; struct mii_bus *mbus = phydev_mdio_bus(phydev); char buf[64]; int ret = 0; unsigned int reg, addr; unsigned long val; memset(buf, 0, 64); if (copy_from_user(buf, buffer, count)) return -EFAULT; if (buf[0] == 'w') { if (sscanf(buf, "w %x %x %lx", &addr, ®, &val) == -1) return -EFAULT; if (addr > 0 && addr < 32) { pr_notice("\nphy=0x%x, reg=0x%x, val=0x%lx\n", addr, reg, val); ret = airoha_pbus_write(mbus, addr, reg, val); if (ret < 0) return ret; pr_notice("\nphy=%d, reg=0x%x, val=0x%lx confirm..\n", addr, reg, airoha_pbus_read(mbus, addr, reg)); } else { pr_notice("addr is out of range(1~32)\n"); } } else if (buf[0] == 'r') { if (sscanf(buf, "r %x %x", &addr, ®) == -1) return -EFAULT; if (addr > 0 && addr < 32) { pr_notice("\nphy=0x%x, reg=0x%x, val=0x%lx\n", addr, reg, airoha_pbus_read(mbus, addr, reg)); } else { pr_notice("addr is out of range(1~32)\n"); } } else if (buf[0] == 'h') { en8801s_debugfs_pbus_help(); } return count; } static int dbg_regs_show(struct seq_file *seq, void *v) { struct phy_device *phydev = seq->private; struct mii_bus *mbus = phydev_mdio_bus(phydev); int pbus_addr = phydev_pbus_addr(phydev), reg; seq_puts(seq, "\t<>\n"); for (reg = MII_BMCR; reg <= MII_STAT1000; reg++) { if ((reg <= MII_LPA) || (reg >= MII_CTRL1000)) seq_printf(seq, "| RG_MII 0x%02x : 0x%08x\n", reg, phy_read(phydev, reg)); } seq_printf(seq, "| RG_SERDES_1 : 0x%08lx |\n", airoha_pbus_read(mbus, pbus_addr, 0x600)); seq_printf(seq, "| RG_SERDES_2 : 0x%08lx |\n", airoha_pbus_read(mbus, pbus_addr, 0x10)); seq_printf(seq, "| RG_SERDES_3 : 0x%08lx |\n", airoha_pbus_read(mbus, pbus_addr, 0x0)); seq_printf(seq, "| RG_SERDES_4 : 0x%08lx |\n", airoha_pbus_read(mbus, pbus_addr, 0xa14)); seq_printf(seq, "| RG_SERDES_5 : 0x%08lx |\n", airoha_pbus_read(mbus, pbus_addr, 0x1404)); seq_printf(seq, "| RG_SERDES_6 : 0x%08lx |\n", airoha_pbus_read(mbus, pbus_addr, 0x140c)); seq_printf(seq, "| RG_SERDES_7 : 0x%08lx |\n", airoha_pbus_read(mbus, pbus_addr, 0x1694)); return 0; } static int airphy_dbg_regs_show_open(struct inode *inode, struct file *file) { return single_open(file, dbg_regs_show, inode->i_private); } static const struct file_operations airphy_dbg_reg_show_fops = { .owner = THIS_MODULE, .open = airphy_dbg_regs_show_open, .read = seq_read, .llseek = noop_llseek, .release = single_release, }; static const struct file_operations en8801s_debugfs_pbus_fops = { .owner = THIS_MODULE, .open = simple_open, .write = en8801s_debugfs_pbus, .llseek = noop_llseek, }; static const struct file_operations en8801s_info_fops = { .owner = THIS_MODULE, .open = en8801s_info_open, .read = seq_read, .llseek = noop_llseek, .release = single_release, }; static const struct file_operations en8801s_counter_fops = { .owner = THIS_MODULE, .open = en8801s_counter_open, .read = seq_read, .llseek = noop_llseek, .release = single_release, }; static int en8801s_debugfs_init(struct phy_device *phydev) { int ret = 0; struct en8801s_priv *priv = phydev->priv; dev_info(phydev_dev(phydev), "Debugfs init start\n"); priv->debugfs_root = debugfs_create_dir(dev_name(phydev_dev(phydev)), NULL); if (!priv->debugfs_root) { dev_err(phydev_dev(phydev), "Debugfs init err\n"); ret = -ENOMEM; } debugfs_create_file(DEBUGFS_DRIVER_INFO, 0444, priv->debugfs_root, phydev, &en8801s_info_fops); debugfs_create_file(DEBUGFS_COUNTER, 0644, priv->debugfs_root, phydev, &en8801s_counter_fops); debugfs_create_file(DEBUGFS_PBUS_OP, S_IFREG | 0200, priv->debugfs_root, phydev, &en8801s_debugfs_pbus_fops); debugfs_create_file(DEBUGFS_DBG_REG_SHOW, S_IFREG | 0444, priv->debugfs_root, phydev, &airphy_dbg_reg_show_fops); return ret; } static void air_debugfs_remove(struct phy_device *phydev) { struct en8801s_priv *priv = phydev->priv; if (priv->debugfs_root != NULL) { debugfs_remove_recursive(priv->debugfs_root); priv->debugfs_root = NULL; } } static void en8801s_phy_remove(struct phy_device *phydev) { struct en8801s_priv *priv = (struct en8801s_priv *)phydev->priv; if (priv) { #ifdef CONFIG_EN8801S_DEBUGFS air_debugfs_remove(phydev); #endif kfree(priv); dev_info(phydev_dev(phydev), "EN8801S remove OK!\n"); } } #if (KERNEL_VERSION(4, 5, 0) < LINUX_VERSION_CODE) 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; } #endif 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, #if (KERNEL_VERSION(4, 5, 0) < LINUX_VERSION_CODE) .read_mmd = airoha_mmd_read, .write_mmd = airoha_mmd_write, #endif } }; module_phy_driver(Airoha_driver); static struct mdio_device_id __maybe_unused Airoha_tbl[] = { { EN8801SC_PHY_ID, 0x0ffffff0 }, { } }; MODULE_DEVICE_TABLE(mdio, Airoha_tbl);