diff --git a/drivers/net/phy/an8801.c b/drivers/net/phy/an8801.c index bb9c5b239c2b52..e713c2a842ddcc 100644 --- a/drivers/net/phy/an8801.c +++ b/drivers/net/phy/an8801.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "an8801.h" @@ -24,19 +25,59 @@ MODULE_DESCRIPTION("Airoha AN8801 PHY drivers"); MODULE_AUTHOR("Airoha"); MODULE_LICENSE("GPL"); -#define phydev_mdiobus(phy) ((phy)->mdio.bus) +#define phydev_mdiobus(_dev) ((_dev)->mdio.bus) +#define phydev_phy_addr(_dev) ((_dev)->mdio.addr) +#define phydev_dev(_dev) (&(_dev)->mdio.dev) + #define phydev_mdiobus_lock(phy) (phydev_mdiobus(phy)->mdio_lock) #define phydev_cfg(phy) ((struct an8801r_priv *)(phy)->priv) #define mdiobus_lock(phy) (mutex_lock(&phydev_mdiobus_lock(phy))) #define mdiobus_unlock(phy) (mutex_unlock(&phydev_mdiobus_lock(phy))) +#ifdef AN8801R_DEBUGFS +#define AN8801R_DEBUGFS_PBUS_HELP_STRING \ + "\nUsage: echo w [pbus_reg] [value] > /sys/" \ + "kernel/debug/mdio-bus\':[phy_addr]/pbus_reg_op" \ + "\n echo r [pbus_reg] > /sys/" \ + "kernel/debug/mdio-bus\':[phy_addr]/pbus_op" \ + "\nRead example: 0x10000054" \ + "\necho r 0x10000054> /sys/" \ + "kernel/debug/mdio-bus\':[phy_addr]/pbus_reg_op" \ + "\nWrite example: Register 0x10000054 0x0" \ + "\necho w 0x10000054 0x0> /sys/" \ + "kernel/debug/mdio-bus\':[phy_addr]/pbus_op" \ + "\n" +#define AN8801R_DEBUGFS_MDIO_HELP_STRING \ + "\nUsage: echo cl22 w [phy_reg] [value]> /sys/" \ + "kernel/debug/mdio-bus\':[phy_addr]/mdio" \ + "\n echo cl22 r [phy_reg] > /sys/" \ + "kernel/debug/mdio-bus\':[phy_addr]/mdio" \ + "\nUsage: echo cl45 w [devad] [phy_reg] [value]> /sys/" \ + "kernel/debug/mdio-bus\':[phy_addr]/mdio" \ + "\n echo cl45 r [devad] [phy_reg] > /sys/" \ + "kernel/debug/mdio-bus\':[phy_addr]/mdio" \ + "\n" +#endif + /* For reference only * GPIO1 <-> LED0, * GPIO2 <-> LED1, * GPIO3 <-> LED2, */ /* User-defined.B */ +#define R50_SHIFT (-7) +static const u16 r50ohm_table[] = { +127, 127, 127, 127, 127, 127, 127, 127, 127, 127, +127, 127, 127, 127, 127, 127, 127, 127, 127, 124, +120, 116, 112, 108, 104, 100, 96, 93, 90, 86, +84, 80, 77, 74, 72, 68, 65, 64, 61, 59, +56, 54, 52, 48, 48, 45, 43, 40, 39, 36, +35, 32, 32, 30, 28, 26, 24, 23, 21, 20, +18, 16, 16, 14 +}; + +static const u16 r50ohm_table_size = sizeof(r50ohm_table) / sizeof(u16); static const struct AIR_LED_CFG_T led_cfg_dlt[MAX_LED_SIZE] = { // LED Enable, GPIO, LED Polarity, LED ON, LED Blink /* LED0 */ @@ -168,76 +209,85 @@ static int air_buckpbus_reg_modify(struct phy_device *phydev, u32 addr, return err; } -static int __an8801r_cl45_write(struct phy_device *phydev, int devad, u16 reg, - u16 val) +static int __air_read_mmd(struct phy_device *phydev, int devad, u32 regnum) { - u32 addr = (AN8801R_EPHY_ADDR | AN8801R_CL22 | (devad << 18) | - (reg << 2)); + int val; + struct mii_bus *bus = phydev->mdio.bus; + int phy_addr = phydev->mdio.addr; - return __air_buckpbus_reg_write(phydev, addr, val); -} - -static int __an8801r_cl45_read(struct phy_device *phydev, int devad, u16 reg) -{ - u32 addr = (AN8801R_EPHY_ADDR | AN8801R_CL22 | (devad << 18) | - (reg << 2)); - u32 data = 0; - int err; + __mdiobus_write(bus, phy_addr, MII_MMD_CTRL, devad); + __mdiobus_write(bus, phy_addr, MII_MMD_DATA, regnum); + __mdiobus_write(bus, phy_addr, MII_MMD_CTRL, + devad | MII_MMD_CTRL_NOINCR); - err = __air_buckpbus_reg_read(phydev, addr, &data); + val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA); - return (err) ? -EINVAL : data; + return val; } -static int an8801r_cl45_write(struct phy_device *phydev, int devad, u16 reg, - u16 val) +static int __air_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) { - int err = 0; + int ret; + struct mii_bus *bus = phydev->mdio.bus; + int phy_addr = phydev->mdio.addr; - mdiobus_lock(phydev); - err = __an8801r_cl45_write(phydev, devad, reg, val); - mdiobus_unlock(phydev); + __mdiobus_write(bus, phy_addr, MII_MMD_CTRL, devad); + __mdiobus_write(bus, phy_addr, MII_MMD_DATA, regnum); + __mdiobus_write(bus, phy_addr, MII_MMD_CTRL, + devad | MII_MMD_CTRL_NOINCR); - return err; + __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val); + + ret = 0; + + return ret; } -static int an8801r_cl45_read(struct phy_device *phydev, int devad, u16 reg, - u16 *read_data) +static int __air_modify_mmd(struct phy_device *phydev, int devad, u32 regnum, + u16 mask, u16 set) { - int data = 0; + int new, ret; - mdiobus_lock(phydev); - data = __an8801r_cl45_read(phydev, devad, reg); - mdiobus_unlock(phydev); + ret = __air_read_mmd(phydev, devad, regnum); + if (ret < 0) + return ret; - if (data < 0) - return data; + new = (ret & ~mask) | set; + if (new == ret) + return 0; - *read_data = data; + ret = __air_write_mmd(phydev, devad, regnum, new); - return 0; + return ret < 0 ? ret : 0; } -static int air_sw_reset(struct phy_device *phydev) +static int air_efuse_read(struct phy_device *phydev, u32 addr, u32 *data) { - u32 reg_value; - u8 retry = MAX_RETRY; + int ret = 0; + + ret |= air_buckpbus_reg_write(phydev, 0x10004034, 0xde7502bc); + ret |= air_buckpbus_reg_write(phydev, 0x1000408c, 0x78d39bf1); + ret |= air_buckpbus_reg_write(phydev, 0x10004004, addr); + ret |= air_buckpbus_reg_write(phydev, 0x10004000, 1); + mdelay(1); + air_buckpbus_reg_read(phydev, 0x10004008, data); + air_buckpbus_reg_read(phydev, 0x10004014, data); + ret |= air_buckpbus_reg_write(phydev, 0x10004034, 0x0); + ret |= air_buckpbus_reg_write(phydev, 0x1000408c, 0x0); - /* Software Reset PHY */ - reg_value = phy_read(phydev, MII_BMCR); - reg_value |= BMCR_RESET; - phy_write(phydev, MII_BMCR, reg_value); - do { - mdelay(10); - reg_value = phy_read(phydev, MII_BMCR); - retry--; - if (retry == 0) { - phydev_err(phydev, "Reset fail !\n"); - return -1; - } - } while (reg_value & BMCR_RESET); + return ret; +} - return 0; +static int air_modify_mmd(struct phy_device *phydev, int devad, u32 regnum, + u16 mask, u16 set) +{ + int ret; + + mdiobus_lock(phydev); + ret = __air_modify_mmd(phydev, devad, regnum, mask, set); + mdiobus_unlock(phydev); + + return ret; } static int an8801r_led_set_usr_def(struct phy_device *phydev, u8 entity, @@ -252,63 +302,49 @@ static int an8801r_led_set_usr_def(struct phy_device *phydev, u8 entity, on_evt |= LED_ON_EN; - err = an8801r_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), on_evt); + err = phy_write_mmd(phydev, 0x1f, LED_ON_CTRL(entity), on_evt); if (err) return -1; - return an8801r_cl45_write(phydev, 0x1f, LED_BLK_CTRL(entity), blk_evt); + return phy_write_mmd(phydev, 0x1f, LED_BLK_CTRL(entity), blk_evt); } static int an8801r_led_set_blink(struct phy_device *phydev, u16 blink) { int err; - err = an8801r_cl45_write(phydev, 0x1f, LED_BLK_DUR, - LED_BLINK_DURATION(blink)); + err = phy_write_mmd(phydev, 0x1f, LED_BLK_DUR, + LED_BLINK_DURATION(blink)); if (err) return err; - return an8801r_cl45_write(phydev, 0x1f, LED_ON_DUR, - (LED_BLINK_DURATION(blink) >> 1)); + return phy_write_mmd(phydev, 0x1f, LED_ON_DUR, + (LED_BLINK_DURATION(blink) >> 1)); } static int an8801r_led_set_mode(struct phy_device *phydev, u8 mode) { - int err; - u16 data; - - err = an8801r_cl45_read(phydev, 0x1f, LED_BCR, &data); - if (err) - return -1; - switch (mode) { case AIR_LED_MODE_DISABLE: - data &= ~LED_BCR_EXT_CTRL; - data &= ~LED_BCR_MODE_MASK; - data |= LED_BCR_MODE_DISABLE; - break; + return air_modify_mmd(phydev, 0x1f, LED_BCR, + (LED_BCR_EXT_CTRL | LED_BCR_CLK_EN), + 0x0); case AIR_LED_MODE_USER_DEFINE: - data |= (LED_BCR_EXT_CTRL | LED_BCR_CLK_EN); + return air_modify_mmd(phydev, 0x1f, LED_BCR, + (LED_BCR_EXT_CTRL | LED_BCR_CLK_EN), + (LED_BCR_EXT_CTRL | LED_BCR_CLK_EN)); + default: break; } - return an8801r_cl45_write(phydev, 0x1f, LED_BCR, data); + dev_err(phydev_dev(phydev), + "LED mode %d is not supported\n", mode); + return -EINVAL; } static int an8801r_led_set_state(struct phy_device *phydev, u8 entity, u8 state) { - u16 data; - int err; - - err = an8801r_cl45_read(phydev, 0x1f, LED_ON_CTRL(entity), &data); - if (err) - return err; - - if (state) - data |= LED_ON_EN; - else - data &= ~LED_ON_EN; - - return an8801r_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), data); + return air_modify_mmd(phydev, 0x1f, LED_ON_CTRL(entity), LED_ON_EN, + (state) ? LED_ON_EN : 0x0); } static int an8801r_led_init(struct phy_device *phydev) @@ -325,16 +361,17 @@ static int an8801r_led_init(struct phy_device *phydev) ret = an8801r_led_set_mode(phydev, AIR_LED_MODE_USER_DEFINE); if (ret != 0) { - phydev_err(phydev, "LED fail to set mode, ret %d !\n", ret); + dev_err(phydev_dev(phydev), + "LED fail to set mode, ret %d !\n", ret); return ret; } for (led_id = AIR_LED0; led_id < MAX_LED_SIZE; led_id++) { ret = an8801r_led_set_state(phydev, led_id, led_cfg[led_id].en); if (ret != 0) { - phydev_err(phydev, - "LED fail to set LED(%d) state, ret %d !\n", - led_id, ret); + dev_err(phydev_dev(phydev), + "LED fail to set LED(%d) state, ret %d !\n", + led_id, ret); return ret; } if (led_cfg[led_id].en == LED_ENABLE) { @@ -352,14 +389,14 @@ static int an8801r_led_init(struct phy_device *phydev) led_cfg[led_id].on_cfg, led_cfg[led_id].blk_cfg); if (ret != 0) { - phydev_err(phydev, - "Fail to set LED(%d) usr def, ret %d !\n", - led_id, ret); + dev_err(phydev_dev(phydev), + "Fail to set LED(%d) usr def, ret %d !\n", + led_id, ret); return ret; } } } - phydev_info(phydev, "LED initialize OK !\n"); + dev_info(phydev_dev(phydev), "LED initialize OK !\n"); return 0; } @@ -368,14 +405,11 @@ static int an8801r_ack_interrupt(struct phy_device *phydev) u32 reg_val = 0; /* Reset WOL status */ - air_buckpbus_reg_write(phydev, 0x100050c4, 0x200000); - air_buckpbus_reg_write(phydev, 0x100050c4, 0x0); air_buckpbus_reg_write(phydev, 0x10285404, 0x102); air_buckpbus_reg_read(phydev, 0x10285400, ®_val); - if (reg_val & 0x1E) - air_buckpbus_reg_write(phydev, 0x10285404, 0x12); - else - air_buckpbus_reg_write(phydev, 0x10285404, 0x2); + air_buckpbus_reg_write(phydev, 0x10285400, 0x0); + air_buckpbus_reg_write(phydev, 0x10285400, reg_val | 0x10); + air_buckpbus_reg_write(phydev, 0x10285404, 0x12); /* Clear the interrupts by writing the reg */ air_buckpbus_reg_write(phydev, 0x10285704, 0x1f); return 0; @@ -384,10 +418,10 @@ static int an8801r_ack_interrupt(struct phy_device *phydev) static int an8801r_config_intr(struct phy_device *phydev) { if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { - air_buckpbus_reg_modify(phydev, 0x10285400, 0x10, 0x10); + air_buckpbus_reg_write(phydev, 0x1000007c, 0x10000); air_buckpbus_reg_modify(phydev, 0x10285700, 0x1, 0x1); } else { - air_buckpbus_reg_modify(phydev, 0x10285400, 0x10, 0); + air_buckpbus_reg_write(phydev, 0x1000007c, 0x0); air_buckpbus_reg_modify(phydev, 0x10285700, 0x1, 0); } an8801r_ack_interrupt(phydev); @@ -456,11 +490,9 @@ static int an8801r_set_wol(struct phy_device *phydev, reg_val = (attach_dev->dev_addr[0] << 8) | (attach_dev->dev_addr[1]); air_buckpbus_reg_write(phydev, 0x10285118, reg_val); - air_buckpbus_reg_write(phydev, 0x1000007c, 0x10000); air_buckpbus_reg_modify(phydev, 0x10285400, 0xE, 0xE); air_buckpbus_reg_modify(phydev, 0x10285700, 0x10, 0x10); } else { - air_buckpbus_reg_write(phydev, 0x1000007c, 0x0); air_buckpbus_reg_modify(phydev, 0x10285400, 0xE, 0x0); air_buckpbus_reg_modify(phydev, 0x10285700, 0x10, 0x0); } @@ -478,14 +510,14 @@ static int an8801r_of_init(struct phy_device *phydev) if (of_find_property(of_node, "airoha,rxclk-delay", NULL)) { if (of_property_read_u32(of_node, "airoha,rxclk-delay", &val) != 0) { - phydev_err(phydev, "airoha,rxclk-delay value is invalid."); + dev_err(phydev_dev(phydev), "airoha,rxclk-delay value is invalid."); return -1; } if (val < AIR_RGMII_DELAY_NOSTEP || val > AIR_RGMII_DELAY_STEP_7) { - phydev_err(phydev, - "airoha,rxclk-delay value %u out of range.", - val); + dev_err(phydev_dev(phydev), + "airoha,rxclk-delay value %u out of range.", + val); return -1; } priv->rxdelay_force = TRUE; @@ -497,21 +529,56 @@ static int an8801r_of_init(struct phy_device *phydev) if (of_find_property(of_node, "airoha,txclk-delay", NULL)) { if (of_property_read_u32(of_node, "airoha,txclk-delay", &val) != 0) { - phydev_err(phydev, - "airoha,txclk-delay value is invalid."); + dev_err(phydev_dev(phydev), + "airoha,txclk-delay value is invalid."); return -1; } if (val < AIR_RGMII_DELAY_NOSTEP || val > AIR_RGMII_DELAY_STEP_7) { - phydev_err(phydev, - "airoha,txclk-delay value %u out of range.", - val); + dev_err(phydev_dev(phydev), + "airoha,txclk-delay value %u out of range.", + val); return -1; } priv->txdelay_force = TRUE; priv->txdelay_step = val; } + if (of_find_property(of_node, "airoha,cko-output", NULL)) { + if (of_property_read_u32(of_node, "airoha,cko-output", + &val) != 0) { + dev_err(phydev_dev(phydev), + "airoha,cko-output value is invalid."); + return -1; + } + if (val < AIR_CKO_OUTPUT_RATE_25M || + val > AIR_CKO_OUTPUT_RATE_125M) { + dev_err(phydev_dev(phydev), + "airoha,cko-output value %u out of range.", + val); + return -1; + } + priv->cko_output_en = TRUE; + priv->cko_output_rate = val; + } + if (of_find_property(of_node, "airoha,surge", NULL)) { + if (of_property_read_u32(of_node, "airoha,surge", + &val) != 0) { + dev_err(phydev_dev(phydev), "airoha,surge value is invalid."); + return -1; + } + if (val < AIR_SURGE_0R || + val > AIR_SURGE_5R) { + dev_err(phydev_dev(phydev), + "airoha,surge value %u out of range.", + val); + return -1; + } + priv->surge = val; + } else { + priv->surge = AIR_SURGE_0R; + } + return 0; } @@ -522,13 +589,14 @@ static int an8801r_rgmii_rxdelay(struct phy_device *phydev, u16 delay, u8 align) /* align */ if (align) { reg_val |= RGMII_RXDELAY_ALIGN; - phydev_info(phydev, "Rxdelay align\n"); + dev_info(phydev_dev(phydev), "Rxdelay align\n"); } reg_val |= RGMII_RXDELAY_FORCE_MODE; air_buckpbus_reg_write(phydev, 0x1021C02C, reg_val); reg_val = 0; air_buckpbus_reg_read(phydev, 0x1021C02C, ®_val); - phydev_info(phydev, "Force rxdelay = %d(0x%x)\n", delay, reg_val); + dev_info(phydev_dev(phydev), + "Force rxdelay = %d(0x%x)\n", delay, reg_val); return 0; } @@ -540,7 +608,8 @@ static int an8801r_rgmii_txdelay(struct phy_device *phydev, u16 delay) air_buckpbus_reg_write(phydev, 0x1021C024, reg_val); reg_val = 0; air_buckpbus_reg_read(phydev, 0x1021C024, ®_val); - phydev_info(phydev, "Force txdelay = %d(0x%x)\n", delay, reg_val); + dev_info(phydev_dev(phydev), + "Force txdelay = %d(0x%x)\n", delay, reg_val); return 0; } @@ -571,21 +640,220 @@ static int an8801r_rgmii_delay_config(struct phy_device *phydev) return 0; } -static int an8801r_config_init(struct phy_device *phydev) +static int an8801r_cko_config(struct phy_device *phydev) { - int ret; + struct an8801r_priv *priv = phydev_cfg(phydev); - ret = an8801r_of_init(phydev); + if (priv->cko_output_en) { + if (priv->cko_output_rate == AIR_CKO_OUTPUT_RATE_125M) { + air_buckpbus_reg_write(phydev, 0x10000194, 0x80); + air_buckpbus_reg_write(phydev, 0x100001A4, (0x3 << 10) | + ((AIR_CKO_OUT_DRV & 0xF) << 4) | + (0x2 << 2)); + } else if (priv->cko_output_rate == AIR_CKO_OUTPUT_RATE_25M) { + air_buckpbus_reg_write(phydev, 0x10000194, 0x0); + air_buckpbus_reg_write(phydev, 0x100001A4, (0x0 << 10) | + ((AIR_CKO_OUT_DRV & 0xF) << 4) | + (0x2 << 2)); + } + } else { + air_buckpbus_reg_write(phydev, 0x100001A4, 0x3); + } + + return 0; +} + +static int findClosestNumber(const u16 *arr, u16 size, u16 target) +{ + int left = 0, right = size - 1; + + while (left <= right) { + int mid = left + ((right - left) >> 2); + + if (arr[mid] == target) + return mid; + + if (arr[mid] < target) + right = mid - 1; + else + left = mid + 1; + } + + if (left > size - 1) + return (size - 1); + else + return ((left - 1) >= 0 ? (left - 1) : 0); +} + +static int an8801r_i2mpb_config(struct phy_device *phydev) +{ + int ret = 0; + u32 efuse_data0 = 0, efuse_data1 = 0, efuse_data2 = 0; + u16 cl45_value = 0; + u16 mask = 0; + + air_efuse_read(phydev, 0, &efuse_data0); + air_efuse_read(phydev, 1, &efuse_data1); + air_efuse_read(phydev, 2, &efuse_data2); + dev_dbg(phydev_dev(phydev), "%s:%d efuse data0 0x%x!\n", __func__, __LINE__, efuse_data0); + dev_dbg(phydev_dev(phydev), "%s:%d efuse data1 0x%x!\n", __func__, __LINE__, efuse_data1); + dev_dbg(phydev_dev(phydev), "%s:%d efuse data2 0x%x!\n", __func__, __LINE__, efuse_data2); + + cl45_value = ((efuse_data0 & GENMASK(5, 0)) + 6) << 10; + dev_dbg(phydev_dev(phydev), "%s:%d cl45_value 0x%x!\n", __func__, __LINE__, cl45_value); + ret = phy_modify_mmd(phydev, 0x1e, 0x12, GENMASK(15, 10), cl45_value); + if (ret < 0) + return ret; + cl45_value = (efuse_data1 & GENMASK(5, 0)) + 6; + cl45_value = cl45_value | (((efuse_data2 & GENMASK(5, 0)) + 9) << 10); + dev_dbg(phydev_dev(phydev), "%s:%d cl45_value 0x%x!\n", __func__, __LINE__, cl45_value); + mask = GENMASK(15, 10) | GENMASK(5, 0); + ret = phy_modify_mmd(phydev, 0x1e, 0x16, mask, cl45_value); if (ret < 0) return ret; + cl45_value = (efuse_data0 & GENMASK(13, 8)) + (6 << 8); + dev_dbg(phydev_dev(phydev), "%s:%d cl45_value 0x%x!\n", __func__, __LINE__, cl45_value); + ret = phy_modify_mmd(phydev, 0x1e, 0x17, GENMASK(13, 8), cl45_value); + if (ret < 0) + return ret; + cl45_value = ((efuse_data1 & GENMASK(15, 10)) >> 10) + 6; + cl45_value = cl45_value | (((efuse_data2 & GENMASK(15, 10)) >> 2) + (9 << 8)); + dev_dbg(phydev_dev(phydev), "%s:%d cl45_value 0x%x!\n", __func__, __LINE__, cl45_value); + mask = GENMASK(13, 8) | GENMASK(5, 0); + ret = phy_modify_mmd(phydev, 0x1e, 0x18, mask, cl45_value); + if (ret < 0) + return ret; + cl45_value = ((efuse_data0 & GENMASK(21, 16)) >> 8) + (6 << 8); + dev_dbg(phydev_dev(phydev), "%s:%d cl45_value 0x%x!\n", __func__, __LINE__, cl45_value); + ret = phy_modify_mmd(phydev, 0x1e, 0x19, GENMASK(13, 8), cl45_value); + if (ret < 0) + return ret; + cl45_value = ((efuse_data1 & GENMASK(21, 16)) >> 16) + 6; + dev_dbg(phydev_dev(phydev), "%s:%d cl45_value 0x%x!\n", __func__, __LINE__, cl45_value); + ret = phy_modify_mmd(phydev, 0x1e, 0x20, GENMASK(5, 0), cl45_value); + if (ret < 0) + return ret; + cl45_value = ((efuse_data0 & GENMASK(29, 24)) >> 16) + (6 << 8); + dev_dbg(phydev_dev(phydev), "%s:%d cl45_value 0x%x!\n", __func__, __LINE__, cl45_value); + ret = phy_modify_mmd(phydev, 0x1e, 0x21, GENMASK(13, 8), cl45_value); + if (ret < 0) + return ret; + cl45_value = ((efuse_data1 & GENMASK(31, 26)) >> 26) + 6; + dev_dbg(phydev_dev(phydev), "%s:%d cl45_value 0x%x!\n", __func__, __LINE__, cl45_value); + ret = phy_modify_mmd(phydev, 0x1e, 0x22, GENMASK(5, 0), cl45_value); + if (ret < 0) + return ret; + ret = phy_write_mmd(phydev, 0x1e, 0x23, 0x883); + ret |= phy_write_mmd(phydev, 0x1e, 0x24, 0x883); + ret |= phy_write_mmd(phydev, 0x1e, 0x25, 0x883); + ret |= phy_write_mmd(phydev, 0x1e, 0x26, 0x883); + ret |= phy_write_mmd(phydev, 0x1e, 0x0, 0x100); + ret |= phy_write_mmd(phydev, 0x1e, 0x1, 0x1bc); + ret |= phy_write_mmd(phydev, 0x1e, 0x2, 0x1d0); + ret |= phy_write_mmd(phydev, 0x1e, 0x3, 0x186); + ret |= phy_write_mmd(phydev, 0x1e, 0x4, 0x202); + ret |= phy_write_mmd(phydev, 0x1e, 0x5, 0x20e); + ret |= phy_write_mmd(phydev, 0x1e, 0x6, 0x300); + ret |= phy_write_mmd(phydev, 0x1e, 0x7, 0x3c0); + ret |= phy_write_mmd(phydev, 0x1e, 0x8, 0x3d0); + ret |= phy_write_mmd(phydev, 0x1e, 0x9, 0x317); + ret |= phy_write_mmd(phydev, 0x1e, 0xa, 0x206); + ret |= phy_write_mmd(phydev, 0x1e, 0xb, 0xe); + if (ret < 0) + return ret; + + dev_info(phydev_dev(phydev), "I2MPB Initialize OK\n"); + return ret; +} + +void update_r50_value(struct phy_device *phydev, + u16 *cl45_value, int pos1, int pos2) +{ + *cl45_value &= ~(0x007f << 8); + *cl45_value |= ((r50ohm_table[pos1]) & 0x007f) << 8; + *cl45_value &= ~(0x007f); + *cl45_value |= (r50ohm_table[pos2]) & 0x007f; + dev_dbg(phydev_dev(phydev), "Read: r50ohm_tx_1=%d r50ohm_tx_2=%d\n", + r50ohm_table[pos1], r50ohm_table[pos2]); +} - ret = air_sw_reset(phydev); +int calculate_position(int pos, int shift, int table_size) +{ + if (shift > 0) + return (pos + shift < table_size) ? (pos + shift) : (table_size - 1); + else + return (pos + shift > 0) ? (pos + shift) : 0; +} + +int process_r50(struct phy_device *phydev, int reg, + u16 *cl45_value, u16 *r50ohm_tx_a, u16 *r50ohm_tx_b) +{ + int pos1 = findClosestNumber(r50ohm_table, r50ohm_table_size, *r50ohm_tx_a); + int pos2 = findClosestNumber(r50ohm_table, r50ohm_table_size, *r50ohm_tx_b); + + if (pos1 != -1 && pos2 != -1) { + pos1 = calculate_position(pos1, R50_SHIFT, r50ohm_table_size); + pos2 = calculate_position(pos2, R50_SHIFT, r50ohm_table_size); + + update_r50_value(phydev, cl45_value, pos1, pos2); + return phy_write_mmd(phydev, 0x1e, reg, *cl45_value); + } + return 0; +} + +int an8801r_surge_protect_cfg(struct phy_device *phydev) +{ + int ret = 0; + struct device *dev = phydev_dev(phydev); + struct an8801r_priv *priv = phydev->priv; + u16 r50ohm_tx_a = 0, r50ohm_tx_b = 0, r50ohm_tx_c = 0, r50ohm_tx_d = 0; + u16 cl45_value = 0; + u32 efuse_data4; + + if (priv->surge) { + air_efuse_read(phydev, 4, &efuse_data4); + dev_dbg(phydev_dev(phydev), "%s:%d efuse data4 0x%x!\n", + __func__, __LINE__, efuse_data4); + cl45_value = phy_read_mmd(phydev, 0x1e, 0x174); + r50ohm_tx_a = efuse_data4 & 0x007f; + r50ohm_tx_b = (efuse_data4 >> 8) & 0x007f; + dev_dbg(phydev_dev(phydev), "Read: (0x174) value=0x%04x r50ohm_tx_a=%d r50ohm_tx_b=%d\n", + cl45_value, r50ohm_tx_a, r50ohm_tx_b); + ret = process_r50(phydev, 0x174, &cl45_value, &r50ohm_tx_a, &r50ohm_tx_b); + if (ret < 0) + return ret; + cl45_value = phy_read_mmd(phydev, 0x1e, 0x175); + r50ohm_tx_c = (efuse_data4 >> 16) & 0x007f; + r50ohm_tx_d = (efuse_data4 >> 24) & 0x007f; + dev_dbg(phydev_dev(phydev), "Read: (0x175) value=0x%04x r50ohm_tx_c=%d r50ohm_tx_d=%d\n", + cl45_value, r50ohm_tx_c, r50ohm_tx_d); + ret = process_r50(phydev, 0x175, &cl45_value, &r50ohm_tx_c, &r50ohm_tx_d); + if (ret < 0) + return ret; + ret = an8801r_i2mpb_config(phydev); + if (ret < 0) { + dev_err(dev, "an8801r_i2mpb_config fail\n"); + return ret; + } + dev_info(dev, "surge protection mode - 5R\n"); + } else { + dev_info(dev, "surge protection mode - 0R\n"); + } + return ret; +} + +static int an8801r_config_init(struct phy_device *phydev) +{ + int ret; + + air_buckpbus_reg_write(phydev, 0x100000C8, 0x7); + ret = an8801r_of_init(phydev); if (ret < 0) return ret; - an8801r_cl45_write(phydev, 0x1f, 0x600, 0x1e); - an8801r_cl45_write(phydev, 0x1f, 0x601, 0x2); - an8801r_cl45_write(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0); + phy_write_mmd(phydev, 0x1f, 0x600, 0x1e); + phy_write_mmd(phydev, 0x1f, 0x601, 0x2); + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0); mdiobus_lock(phydev); __phy_write(phydev, 0x1f, 0x1); __phy_write(phydev, 0x14, 0x3a14); @@ -599,38 +867,344 @@ static int an8801r_config_init(struct phy_device *phydev) air_buckpbus_reg_write(phydev, 0x10270104, 0xff); air_buckpbus_reg_write(phydev, 0x10270204, 0xff); - air_buckpbus_reg_write(phydev, 0x100001A4, 0x3); - - an8801r_cl45_write(phydev, 0x1e, 0x13, 0x4040); - an8801r_cl45_write(phydev, 0x1e, 0xD8, 0x1010); - an8801r_cl45_write(phydev, 0x1e, 0xD9, 0x100); - an8801r_cl45_write(phydev, 0x1e, 0xDA, 0x100); + phy_write_mmd(phydev, 0x1e, 0x13, 0x4040); + phy_write_mmd(phydev, 0x1e, 0xD8, 0x1010); + phy_write_mmd(phydev, 0x1e, 0xD9, 0x100); + phy_write_mmd(phydev, 0x1e, 0xDA, 0x100); an8801r_rgmii_delay_config(phydev); + an8801r_cko_config(phydev); + ret = an8801r_surge_protect_cfg(phydev); + if (ret < 0) { + dev_err(phydev_dev(phydev), + "an8801r_surge_protect_cfg fail. (ret=%d)\n", ret); + return ret; + } ret = an8801r_led_init(phydev); if (ret != 0) { - phydev_err(phydev, "LED initialize fail, ret %d !\n", ret); + dev_err(phydev_dev(phydev), + "LED initialize fail, ret %d !\n", ret); + return ret; + } + dev_info(phydev_dev(phydev), "AN8801R Initialize OK ! (%s)\n", + AN8801R_DRIVER_VERSION); + return 0; +} + +#ifdef AN8801R_DEBUGFS +static ssize_t an8801r_mdio_write(struct file *file, const char __user *ptr, + size_t len, loff_t *off) +{ + struct phy_device *phydev = file->private_data; + char buf[64], param1[32], param2[32]; + int count = len, ret = 0; + unsigned int reg, devad, val; + u16 reg_val; + + memset(buf, 0, 64); + memset(param1, 0, 32); + memset(param2, 0, 32); + + if (count > sizeof(buf) - 1) + return -EINVAL; + if (copy_from_user(buf, ptr, len)) + return -EFAULT; + + ret = sscanf(buf, "%s %s", param1, param2); + if (ret < 0) + return ret; + + if (!strncmp("cl22", param1, strlen("cl22"))) { + if (!strncmp("w", param2, strlen("w"))) { + if (sscanf(buf, "cl22 w %x %x", ®, &val) == -1) + return -EFAULT; + pr_notice("\nphy=0x%x, reg=0x%x, val=0x%x\n", + phydev_phy_addr(phydev), reg, val); + + ret = phy_write(phydev, reg, val); + if (ret < 0) + return ret; + pr_notice("\nphy=0x%x, reg=0x%x, val=0x%x confirm..\n", + phydev_phy_addr(phydev), reg, + phy_read(phydev, reg)); + } else if (!strncmp("r", param2, strlen("r"))) { + if (sscanf(buf, "cl22 r %x", ®) == -1) + return -EFAULT; + pr_notice("\nphy=0x%x, reg=0x%x, val=0x%x\n", + phydev_phy_addr(phydev), reg, + phy_read(phydev, reg)); + } else { + pr_notice(AN8801R_DEBUGFS_MDIO_HELP_STRING); + return -EINVAL; + } + } else if (!strncmp("cl45", param1, strlen("cl45"))) { + if (!strncmp("w", param2, strlen("w"))) { + if (sscanf(buf, "cl45 w %x %x %x", &devad, ®, &val) == -1) + return -EFAULT; + pr_notice("\nphy=0x%x, devad=0x%x, reg=0x%x, val=0x%x\n", + phydev_phy_addr(phydev), devad, reg, val); + + ret = phy_write_mmd(phydev, devad, reg, val); + if (ret < 0) + return ret; + reg_val = phy_read_mmd(phydev, devad, reg); + pr_notice("\nphy=0x%x, devad=0x%x, reg=0x%x, val=0x%x confirm..\n", + phydev_phy_addr(phydev), devad, reg, reg_val); + } else if (!strncmp("r", param2, strlen("r"))) { + if (sscanf(buf, "cl45 r %x %x", &devad, ®) == -1) + return -EFAULT; + reg_val = phy_read_mmd(phydev, devad, reg); + pr_notice("\nphy=0x%x, devad=0x%x, reg=0x%x, val=0x%x\n", + phydev_phy_addr(phydev), devad, reg, reg_val); + } else { + pr_notice(AN8801R_DEBUGFS_MDIO_HELP_STRING); + return -EINVAL; + } + } else { + pr_notice(AN8801R_DEBUGFS_MDIO_HELP_STRING); + return -EINVAL; + } + + return count; +} + +static int an8801r_counter_show(struct seq_file *seq, void *v) +{ + struct phy_device *phydev = seq->private; + int ret = 0; + u32 pkt_cnt = 0; + + seq_puts(seq, "|\t<>\n"); + seq_puts(seq, "| Rx from Line side_S :"); + air_buckpbus_reg_read(phydev, 0x10270030, &pkt_cnt); + seq_printf(seq, "%010u |\n", pkt_cnt); + seq_puts(seq, "| Rx from Line side_E :"); + air_buckpbus_reg_read(phydev, 0x10270034, &pkt_cnt); + seq_printf(seq, "%010u |\n", pkt_cnt); + seq_puts(seq, "| Tx to System side_S :"); + air_buckpbus_reg_read(phydev, 0x10270038, &pkt_cnt); + seq_printf(seq, "%010u |\n", pkt_cnt); + seq_puts(seq, "| Tx to System side_E :"); + air_buckpbus_reg_read(phydev, 0x1027003C, &pkt_cnt); + seq_printf(seq, "%010u |\n", pkt_cnt); + seq_puts(seq, "| Rx from System side_S :"); + air_buckpbus_reg_read(phydev, 0x10270020, &pkt_cnt); + seq_printf(seq, "%010u |\n", pkt_cnt); + seq_puts(seq, "| Rx from System side_E :"); + air_buckpbus_reg_read(phydev, 0x10270024, &pkt_cnt); + seq_printf(seq, "%010u |\n", pkt_cnt); + seq_puts(seq, "| Tx to Line side_S :"); + air_buckpbus_reg_read(phydev, 0x10270028, &pkt_cnt); + seq_printf(seq, "%010u |\n", pkt_cnt); + seq_puts(seq, "| Tx to Line side_E :"); + air_buckpbus_reg_read(phydev, 0x1027002C, &pkt_cnt); + seq_printf(seq, "%010u |\n", pkt_cnt); + + ret = air_buckpbus_reg_write(phydev, 0x1027001C, 0x3); + if (ret < 0) + return ret; + + seq_puts(seq, "|\t<>\n"); + ret = phy_write(phydev, 0x1f, 1); + if (ret < 0) + return ret; + seq_puts(seq, "| Rx from Line side :"); + pkt_cnt = phy_read(phydev, 0x12) & 0x7fff; + seq_printf(seq, "%010u |\n", pkt_cnt); + seq_puts(seq, "| Rx Error from Line side :"); + pkt_cnt = phy_read(phydev, 0x17) & 0xff; + seq_printf(seq, "%010u |\n", pkt_cnt); + + ret = phy_write(phydev, 0x1f, 0); + if (ret < 0) return ret; + ret = phy_write(phydev, 0x1f, 0x52B5); + if (ret < 0) + return ret; + ret = phy_write(phydev, 0x10, 0xBF92); + if (ret < 0) + return ret; + + seq_puts(seq, "| Tx to Line side :"); + pkt_cnt = (phy_read(phydev, 0x11) & 0x7ffe) >> 1; + seq_printf(seq, "%010u |\n", pkt_cnt); + seq_puts(seq, "| Tx Error to Line side :"); + pkt_cnt = phy_read(phydev, 0x12); + pkt_cnt &= 0x7f; + seq_printf(seq, "%010u |\n\n", pkt_cnt); + ret = phy_write(phydev, 0x1f, 0); + if (ret < 0) + return ret; + + return ret; +} + +static int an8801r_counter_open(struct inode *inode, struct file *file) +{ + return single_open(file, an8801r_counter_show, inode->i_private); +} + +static int an8801r_debugfs_pbus_help(void) +{ + pr_notice(AN8801R_DEBUGFS_PBUS_HELP_STRING); + return 0; +} + +static ssize_t an8801r_debugfs_pbus(struct file *file, + const char __user *buffer, size_t count, + loff_t *data) +{ + struct phy_device *phydev = file->private_data; + char buf[64]; + int ret = 0; + unsigned int reg; + u32 val = 0; + + memset(buf, 0, 64); + + if (copy_from_user(buf, buffer, count)) + return -EFAULT; + + if (buf[0] == 'w') { + if (sscanf(buf, "w %x %x", ®, &val) == -1) + return -EFAULT; + + pr_notice("\nphy=0x%x, reg=0x%x, val=0x%x\n", + phydev_phy_addr(phydev), reg, val); + + ret = air_buckpbus_reg_write(phydev, reg, val); + if (ret < 0) + return ret; + + val = 0; + air_buckpbus_reg_read(phydev, reg, &val); + pr_notice("\nphy=%d, reg=0x%x, val=0x%x confirm..\n", + phydev_phy_addr(phydev), reg, val); + } else if (buf[0] == 'r') { + if (sscanf(buf, "r %x", ®) == -1) + return -EFAULT; + + air_buckpbus_reg_read(phydev, reg, &val); + pr_notice("\nphy=0x%x, reg=0x%x, val=0x%x\n", + phydev_phy_addr(phydev), reg, val); + } else if (buf[0] == 'h') { + an8801r_debugfs_pbus_help(); } - phydev_info(phydev, "Initialize OK ! (%s)\n", AN8801R_DRIVER_VERSION); + + return count; +} + +int an8801r_info_show(struct seq_file *seq, void *v) +{ + struct phy_device *phydev = seq->private; + u32 pbus_data = 0; + int reg = 0; + + seq_puts(seq, "\t<>\n"); + air_buckpbus_reg_read(phydev, 0x10005004, &pbus_data); + seq_printf(seq, "| Product Version : E%d\n", pbus_data); + seq_printf(seq, "| Driver Version : %s\n", AN8801R_DRIVER_VERSION); + air_buckpbus_reg_read(phydev, 0x10000094, &pbus_data); + seq_printf(seq, "| RG_HW_STRAP : 0x%08x\n", pbus_data); + 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_puts(seq, "\n"); return 0; } +static int an8801r_info_open(struct inode *inode, struct file *file) +{ + return single_open(file, an8801r_info_show, inode->i_private); +} + +static const struct file_operations an8801r_info_fops = { + .owner = THIS_MODULE, + .open = an8801r_info_open, + .read = seq_read, + .llseek = noop_llseek, + .release = single_release, +}; + +static const struct file_operations an8801r_counter_fops = { + .owner = THIS_MODULE, + .open = an8801r_counter_open, + .read = seq_read, + .llseek = noop_llseek, + .release = single_release, +}; + +static const struct file_operations an8801r_debugfs_pbus_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = an8801r_debugfs_pbus, + .llseek = noop_llseek, +}; + +static const struct file_operations an8801r_mdio_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = an8801r_mdio_write, + .llseek = noop_llseek, +}; + +int an8801r_debugfs_init(struct phy_device *phydev) +{ + int ret = 0; + struct an8801r_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_INFO, 0444, + priv->debugfs_root, phydev, + &an8801r_info_fops); + debugfs_create_file(DEBUGFS_COUNTER, 0644, + priv->debugfs_root, phydev, + &an8801r_counter_fops); + debugfs_create_file(DEBUGFS_PBUS_OP, S_IFREG | 0200, + priv->debugfs_root, phydev, + &an8801r_debugfs_pbus_fops); + debugfs_create_file(DEBUGFS_MDIO, S_IFREG | 0200, + priv->debugfs_root, phydev, + &an8801r_mdio_fops); + return ret; +} + +static void air_debugfs_remove(struct phy_device *phydev) +{ + struct an8801r_priv *priv = phydev->priv; + + debugfs_remove_recursive(priv->debugfs_root); + priv->debugfs_root = NULL; +} +#endif /*AN8801R_DEBUGFS*/ + static int an8801r_phy_probe(struct phy_device *phydev) { u32 reg_val, phy_id, led_id; struct device *dev = &phydev->mdio.dev; struct an8801r_priv *priv = NULL; +#ifdef AN8801R_DEBUGFS + int ret = 0; +#endif reg_val = phy_read(phydev, 2); phy_id = reg_val << 16; reg_val = phy_read(phydev, 3); phy_id |= reg_val; - phydev_info(phydev, "PHY-ID = %x\n", phy_id); + dev_info(phydev_dev(phydev), "PHY-ID = %x\n", phy_id); if (phy_id != AN8801R_PHY_ID) { - phydev_err(phydev, "AN8801R can't be detected.\n"); + dev_err(phydev_dev(phydev), + "AN8801R can't be detected.\n"); return -1; } @@ -649,6 +1223,16 @@ static int an8801r_phy_probe(struct phy_device *phydev) priv->txdelay_step = txdelay_step; phydev->priv = priv; + +#ifdef AN8801R_DEBUGFS + ret = an8801r_debugfs_init(phydev); + if (ret < 0) { + dev_info(phydev_dev(phydev), "AN8801R debugfs init failed\n"); + air_debugfs_remove(phydev); + kfree(priv); + return ret; + } +#endif return 0; } @@ -656,6 +1240,9 @@ static void an8801r_phy_remove(struct phy_device *phydev) { struct an8801r_priv *priv = (struct an8801r_priv *)phydev->priv; +#ifdef AN8801R_DEBUGFS + air_debugfs_remove(phydev); +#endif kfree(priv); phydev->priv = NULL; } @@ -671,7 +1258,7 @@ static int an8801r_read_status(struct phy_device *phydev) } if (prespeed != phydev->speed && phydev->link == LINK_UP) { prespeed = phydev->speed; - phydev_dbg(phydev, "SPEED %d\n", prespeed); + dev_dbg(phydev_dev(phydev), "AN8801R SPEED %d\n", prespeed); if (prespeed == SPEED_1000) air_buckpbus_reg_modify(phydev, 0x10005054, BIT(0), BIT(0)); else @@ -697,8 +1284,6 @@ static struct phy_driver airoha_driver[] = { .get_wol = an8801r_get_wol, .suspend = genphy_suspend, .resume = genphy_resume, - .read_mmd = __an8801r_cl45_read, - .write_mmd = __an8801r_cl45_write, } }; diff --git a/drivers/net/phy/an8801.h b/drivers/net/phy/an8801.h index ae54707b28341f..51cad20696c5b0 100644 --- a/drivers/net/phy/an8801.h +++ b/drivers/net/phy/an8801.h @@ -10,9 +10,13 @@ #ifndef __AN8801_H #define __AN8801_H -/* NAMING DECLARATIONS - */ -#define AN8801R_DRIVER_VERSION "1.0.15" +/* NAMING DECLARATIONS */ +#define AN8801R_DRIVER_VERSION "1.0.21" + +#define DEBUGFS_COUNTER "counter" +#define DEBUGFS_INFO "driver_info" +#define DEBUGFS_PBUS_OP "pbus_op" +#define DEBUGFS_MDIO "mdio" #define AN8801R_MDIO_PHY_ID 0x1 #define AN8801R_PHY_ID1 0xc0ff @@ -28,12 +32,11 @@ #define MAX_RETRY 5 -#define AN8801R_EPHY_ADDR 0x11000000 -#define AN8801R_CL22 0x00800000 - #define LED_ENABLE 1 #define LED_DISABLE 0 +#define AN8801R_DEBUGFS + #ifndef BIT #define BIT(nr) (1 << (nr)) #endif @@ -77,12 +80,13 @@ #define LED_BLK_EVT_1000M_RX BIT(1) #define LED_BLK_EVT_1000M_TX BIT(0) -#define UNIT_LED_BLINK_DURATION 1024 +#define UNIT_LED_BLINK_DURATION 780 #define RGMII_DELAY_STEP_MASK 0x7 #define RGMII_RXDELAY_ALIGN BIT(4) #define RGMII_RXDELAY_FORCE_MODE BIT(24) #define RGMII_TXDELAY_FORCE_MODE BIT(24) +#define AIR_CKO_OUT_DRV 0xF /* Available: 0x0~0xF (about 1.8V~2.5V) */ /* For reference only */ /* User-defined.B */ @@ -156,6 +160,17 @@ enum AIR_RGMII_DELAY_STEP_T { AIR_RGMII_DELAY_STEP_7 = 7, }; +enum AIR_CKO_OUTPUT_RATE_T { + AIR_CKO_OUTPUT_RATE_25M = 0, + AIR_CKO_OUTPUT_RATE_125M = 1, +}; + +enum air_surge { + AIR_SURGE_0R, + AIR_SURGE_5R, + AIR_SURGE_LAST = 0xff +}; + struct AIR_LED_CFG_T { u16 en; u16 gpio; @@ -172,6 +187,12 @@ struct an8801r_priv { u16 rxdelay_step; u8 rxdelay_align; u16 txdelay_step; + u8 cko_output_en; + u8 cko_output_rate; + u8 surge; +#ifdef AN8801R_DEBUGFS + struct dentry *debugfs_root; +#endif }; #endif /* End of __AN8801_H */