diff options
Diffstat (limited to 'drivers/net/phy/micrel.c')
-rw-r--r-- | drivers/net/phy/micrel.c | 224 |
1 files changed, 182 insertions, 42 deletions
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 827db6a6ff..4b22bb6393 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -114,12 +114,25 @@ #define LAN8814_INTR_CTRL_REG_POLARITY BIT(1) #define LAN8814_INTR_CTRL_REG_INTR_ENABLE BIT(0) +#define LAN8814_EEE_STATE 0x38 +#define LAN8814_EEE_STATE_MASK2P5P BIT(10) + +#define LAN8814_PD_CONTROLS 0x9d +#define LAN8814_PD_CONTROLS_PD_MEAS_TIME_MASK GENMASK(3, 0) +#define LAN8814_PD_CONTROLS_PD_MEAS_TIME_VAL 0xb + /* Represents 1ppm adjustment in 2^32 format with * each nsec contains 4 clock cycles. * The value is calculated as following: (1/1000000)/((2^-32)/4) */ #define LAN8814_1PPM_FORMAT 17179 +/* Represents 1ppm adjustment in 2^32 format with + * each nsec contains 8 clock cycles. + * The value is calculated as following: (1/1000000)/((2^-32)/8) + */ +#define LAN8841_1PPM_FORMAT 34360 + #define PTP_RX_VERSION 0x0248 #define PTP_TX_VERSION 0x0288 #define PTP_MAX_VERSION(x) (((x) & GENMASK(7, 0)) << 8) @@ -154,11 +167,13 @@ #define PTP_CMD_CTL_PTP_LTC_STEP_SEC_ BIT(5) #define PTP_CMD_CTL_PTP_LTC_STEP_NSEC_ BIT(6) +#define PTP_CLOCK_SET_SEC_HI 0x0205 #define PTP_CLOCK_SET_SEC_MID 0x0206 #define PTP_CLOCK_SET_SEC_LO 0x0207 #define PTP_CLOCK_SET_NS_HI 0x0208 #define PTP_CLOCK_SET_NS_LO 0x0209 +#define PTP_CLOCK_READ_SEC_HI 0x0229 #define PTP_CLOCK_READ_SEC_MID 0x022A #define PTP_CLOCK_READ_SEC_LO 0x022B #define PTP_CLOCK_READ_NS_HI 0x022C @@ -770,6 +785,17 @@ static int ksz8061_config_init(struct phy_device *phydev) { int ret; + /* Chip can be powered down by the bootstrap code. */ + ret = phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + if (ret & BMCR_PDOWN) { + ret = phy_write(phydev, MII_BMCR, ret & ~BMCR_PDOWN); + if (ret < 0) + return ret; + usleep_range(1000, 2000); + } + ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_DEVID1, 0xB61A); if (ret) return ret; @@ -1843,7 +1869,7 @@ static const struct ksz9477_errata_write ksz9477_errata_writes[] = { {0x1c, 0x20, 0xeeee}, }; -static int ksz9477_config_init(struct phy_device *phydev) +static int ksz9477_phy_errata(struct phy_device *phydev) { int err; int i; @@ -1871,16 +1897,30 @@ static int ksz9477_config_init(struct phy_device *phydev) return err; } + err = genphy_restart_aneg(phydev); + if (err) + return err; + + return err; +} + +static int ksz9477_config_init(struct phy_device *phydev) +{ + int err; + + /* Only KSZ9897 family of switches needs this fix. */ + if ((phydev->phy_id & 0xf) == 1) { + err = ksz9477_phy_errata(phydev); + if (err) + return err; + } + /* According to KSZ9477 Errata DS80000754C (Module 4) all EEE modes * in this switch shall be regarded as broken. */ if (phydev->dev_flags & MICREL_NO_EEE) phydev->eee_broken_modes = -1; - err = genphy_restart_aneg(phydev); - if (err) - return err; - return kszphy_config_init(phydev); } @@ -1989,6 +2029,71 @@ static int kszphy_resume(struct phy_device *phydev) return 0; } +static int ksz9477_resume(struct phy_device *phydev) +{ + int ret; + + /* No need to initialize registers if not powered down. */ + ret = phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + if (!(ret & BMCR_PDOWN)) + return 0; + + genphy_resume(phydev); + + /* After switching from power-down to normal mode, an internal global + * reset is automatically generated. Wait a minimum of 1 ms before + * read/write access to the PHY registers. + */ + usleep_range(1000, 2000); + + /* Only KSZ9897 family of switches needs this fix. */ + if ((phydev->phy_id & 0xf) == 1) { + ret = ksz9477_phy_errata(phydev); + if (ret) + return ret; + } + + /* Enable PHY Interrupts */ + if (phy_interrupt_is_valid(phydev)) { + phydev->interrupts = PHY_INTERRUPT_ENABLED; + if (phydev->drv->config_intr) + phydev->drv->config_intr(phydev); + } + + return 0; +} + +static int ksz8061_resume(struct phy_device *phydev) +{ + int ret; + + /* This function can be called twice when the Ethernet device is on. */ + ret = phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + if (!(ret & BMCR_PDOWN)) + return 0; + + genphy_resume(phydev); + usleep_range(1000, 2000); + + /* Re-program the value after chip is reset. */ + ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_DEVID1, 0xB61A); + if (ret) + return ret; + + /* Enable PHY Interrupts */ + if (phy_interrupt_is_valid(phydev)) { + phydev->interrupts = PHY_INTERRUPT_ENABLED; + if (phydev->drv->config_intr) + phydev->drv->config_intr(phydev); + } + + return 0; +} + static int kszphy_probe(struct phy_device *phydev) { const struct kszphy_type *type = phydev->drv->driver_data; @@ -2603,35 +2708,31 @@ static bool lan8814_rxtstamp(struct mii_timestamper *mii_ts, struct sk_buff *skb } static void lan8814_ptp_clock_set(struct phy_device *phydev, - u32 seconds, u32 nano_seconds) + time64_t sec, u32 nsec) { - u32 sec_low, sec_high, nsec_low, nsec_high; - - sec_low = seconds & 0xffff; - sec_high = (seconds >> 16) & 0xffff; - nsec_low = nano_seconds & 0xffff; - nsec_high = (nano_seconds >> 16) & 0x3fff; - - lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_LO, sec_low); - lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_MID, sec_high); - lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_NS_LO, nsec_low); - lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_NS_HI, nsec_high); + lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_LO, lower_16_bits(sec)); + lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_MID, upper_16_bits(sec)); + lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_HI, upper_32_bits(sec)); + lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_NS_LO, lower_16_bits(nsec)); + lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_NS_HI, upper_16_bits(nsec)); lanphy_write_page_reg(phydev, 4, PTP_CMD_CTL, PTP_CMD_CTL_PTP_CLOCK_LOAD_); } static void lan8814_ptp_clock_get(struct phy_device *phydev, - u32 *seconds, u32 *nano_seconds) + time64_t *sec, u32 *nsec) { lanphy_write_page_reg(phydev, 4, PTP_CMD_CTL, PTP_CMD_CTL_PTP_CLOCK_READ_); - *seconds = lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_MID); - *seconds = (*seconds << 16) | - lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_LO); + *sec = lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_HI); + *sec <<= 16; + *sec |= lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_MID); + *sec <<= 16; + *sec |= lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_LO); - *nano_seconds = lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_NS_HI); - *nano_seconds = ((*nano_seconds & 0x3fff) << 16) | - lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_NS_LO); + *nsec = lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_NS_HI); + *nsec <<= 16; + *nsec |= lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_NS_LO); } static int lan8814_ptpci_gettime64(struct ptp_clock_info *ptpci, @@ -2641,7 +2742,7 @@ static int lan8814_ptpci_gettime64(struct ptp_clock_info *ptpci, ptp_clock_info); struct phy_device *phydev = shared->phydev; u32 nano_seconds; - u32 seconds; + time64_t seconds; mutex_lock(&shared->shared_lock); lan8814_ptp_clock_get(phydev, &seconds, &nano_seconds); @@ -2671,38 +2772,37 @@ static void lan8814_ptp_clock_step(struct phy_device *phydev, { u32 nano_seconds_step; u64 abs_time_step_ns; - u32 unsigned_seconds; + time64_t set_seconds; u32 nano_seconds; u32 remainder; s32 seconds; if (time_step_ns > 15000000000LL) { /* convert to clock set */ - lan8814_ptp_clock_get(phydev, &unsigned_seconds, &nano_seconds); - unsigned_seconds += div_u64_rem(time_step_ns, 1000000000LL, - &remainder); + lan8814_ptp_clock_get(phydev, &set_seconds, &nano_seconds); + set_seconds += div_u64_rem(time_step_ns, 1000000000LL, + &remainder); nano_seconds += remainder; if (nano_seconds >= 1000000000) { - unsigned_seconds++; + set_seconds++; nano_seconds -= 1000000000; } - lan8814_ptp_clock_set(phydev, unsigned_seconds, nano_seconds); + lan8814_ptp_clock_set(phydev, set_seconds, nano_seconds); return; } else if (time_step_ns < -15000000000LL) { /* convert to clock set */ time_step_ns = -time_step_ns; - lan8814_ptp_clock_get(phydev, &unsigned_seconds, &nano_seconds); - unsigned_seconds -= div_u64_rem(time_step_ns, 1000000000LL, - &remainder); + lan8814_ptp_clock_get(phydev, &set_seconds, &nano_seconds); + set_seconds -= div_u64_rem(time_step_ns, 1000000000LL, + &remainder); nano_seconds_step = remainder; if (nano_seconds < nano_seconds_step) { - unsigned_seconds--; + set_seconds--; nano_seconds += 1000000000; } nano_seconds -= nano_seconds_step; - lan8814_ptp_clock_set(phydev, unsigned_seconds, - nano_seconds); + lan8814_ptp_clock_set(phydev, set_seconds, nano_seconds); return; } @@ -3302,6 +3402,33 @@ static int lan8814_release_coma_mode(struct phy_device *phydev) return 0; } +static void lan8814_clear_2psp_bit(struct phy_device *phydev) +{ + u16 val; + + /* It was noticed that when traffic is passing through the PHY and the + * cable is removed then the LED was still one even though there is no + * link + */ + val = lanphy_read_page_reg(phydev, 2, LAN8814_EEE_STATE); + val &= ~LAN8814_EEE_STATE_MASK2P5P; + lanphy_write_page_reg(phydev, 2, LAN8814_EEE_STATE, val); +} + +static void lan8814_update_meas_time(struct phy_device *phydev) +{ + u16 val; + + /* By setting the measure time to a value of 0xb this will allow cables + * longer than 100m to be used. This configuration can be used + * regardless of the mode of operation of the PHY + */ + val = lanphy_read_page_reg(phydev, 1, LAN8814_PD_CONTROLS); + val &= ~LAN8814_PD_CONTROLS_PD_MEAS_TIME_MASK; + val |= LAN8814_PD_CONTROLS_PD_MEAS_TIME_VAL; + lanphy_write_page_reg(phydev, 1, LAN8814_PD_CONTROLS, val); +} + static int lan8814_probe(struct phy_device *phydev) { const struct kszphy_type *type = phydev->drv->driver_data; @@ -3338,6 +3465,10 @@ static int lan8814_probe(struct phy_device *phydev) lan8814_ptp_init(phydev); + /* Errata workarounds */ + lan8814_clear_2psp_bit(phydev); + lan8814_update_meas_time(phydev); + return 0; } @@ -3475,7 +3606,7 @@ static int lan8841_config_intr(struct phy_device *phydev) if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { err = phy_read(phydev, LAN8814_INTS); - if (err) + if (err < 0) return err; /* Enable / disable interrupts. It is OK to enable PTP interrupt @@ -3491,6 +3622,14 @@ static int lan8841_config_intr(struct phy_device *phydev) return err; err = phy_read(phydev, LAN8814_INTS); + if (err < 0) + return err; + + /* Getting a positive value doesn't mean that is an error, it + * just indicates what was the status. Therefore make sure to + * clear the value and say that there is no error. + */ + err = 0; } return err; @@ -4135,8 +4274,8 @@ static int lan8841_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) faster = false; } - rate = LAN8814_1PPM_FORMAT * (upper_16_bits(scaled_ppm)); - rate += (LAN8814_1PPM_FORMAT * (lower_16_bits(scaled_ppm))) >> 16; + rate = LAN8841_1PPM_FORMAT * (upper_16_bits(scaled_ppm)); + rate += (LAN8841_1PPM_FORMAT * (lower_16_bits(scaled_ppm))) >> 16; mutex_lock(&ptp_priv->ptp_lock); phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_RATE_ADJ_HI, @@ -4773,10 +4912,11 @@ static struct phy_driver ksphy_driver[] = { /* PHY_BASIC_FEATURES */ .probe = kszphy_probe, .config_init = ksz8061_config_init, + .soft_reset = genphy_soft_reset, .config_intr = kszphy_config_intr, .handle_interrupt = kszphy_handle_interrupt, .suspend = kszphy_suspend, - .resume = kszphy_resume, + .resume = ksz8061_resume, }, { .phy_id = PHY_ID_KSZ9021, .phy_id_mask = 0x000ffffe, @@ -4930,7 +5070,7 @@ static struct phy_driver ksphy_driver[] = { .config_intr = kszphy_config_intr, .handle_interrupt = kszphy_handle_interrupt, .suspend = genphy_suspend, - .resume = genphy_resume, + .resume = ksz9477_resume, .get_features = ksz9477_get_features, } }; |