162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Copyright (C) 2018 Microchip Technology 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/kernel.h> 562306a36Sopenharmony_ci#include <linux/module.h> 662306a36Sopenharmony_ci#include <linux/delay.h> 762306a36Sopenharmony_ci#include <linux/mii.h> 862306a36Sopenharmony_ci#include <linux/phy.h> 962306a36Sopenharmony_ci#include <linux/ethtool.h> 1062306a36Sopenharmony_ci#include <linux/ethtool_netlink.h> 1162306a36Sopenharmony_ci#include <linux/bitfield.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define PHY_ID_LAN87XX 0x0007c150 1462306a36Sopenharmony_ci#define PHY_ID_LAN937X 0x0007c180 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* External Register Control Register */ 1762306a36Sopenharmony_ci#define LAN87XX_EXT_REG_CTL (0x14) 1862306a36Sopenharmony_ci#define LAN87XX_EXT_REG_CTL_RD_CTL (0x1000) 1962306a36Sopenharmony_ci#define LAN87XX_EXT_REG_CTL_WR_CTL (0x0800) 2062306a36Sopenharmony_ci#define LAN87XX_REG_BANK_SEL_MASK GENMASK(10, 8) 2162306a36Sopenharmony_ci#define LAN87XX_REG_ADDR_MASK GENMASK(7, 0) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* External Register Read Data Register */ 2462306a36Sopenharmony_ci#define LAN87XX_EXT_REG_RD_DATA (0x15) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* External Register Write Data Register */ 2762306a36Sopenharmony_ci#define LAN87XX_EXT_REG_WR_DATA (0x16) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Interrupt Source Register */ 3062306a36Sopenharmony_ci#define LAN87XX_INTERRUPT_SOURCE (0x18) 3162306a36Sopenharmony_ci#define LAN87XX_INTERRUPT_SOURCE_2 (0x08) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* Interrupt Mask Register */ 3462306a36Sopenharmony_ci#define LAN87XX_INTERRUPT_MASK (0x19) 3562306a36Sopenharmony_ci#define LAN87XX_MASK_LINK_UP (0x0004) 3662306a36Sopenharmony_ci#define LAN87XX_MASK_LINK_DOWN (0x0002) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define LAN87XX_INTERRUPT_MASK_2 (0x09) 3962306a36Sopenharmony_ci#define LAN87XX_MASK_COMM_RDY BIT(10) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* MISC Control 1 Register */ 4262306a36Sopenharmony_ci#define LAN87XX_CTRL_1 (0x11) 4362306a36Sopenharmony_ci#define LAN87XX_MASK_RGMII_TXC_DLY_EN (0x4000) 4462306a36Sopenharmony_ci#define LAN87XX_MASK_RGMII_RXC_DLY_EN (0x2000) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* phyaccess nested types */ 4762306a36Sopenharmony_ci#define PHYACC_ATTR_MODE_READ 0 4862306a36Sopenharmony_ci#define PHYACC_ATTR_MODE_WRITE 1 4962306a36Sopenharmony_ci#define PHYACC_ATTR_MODE_MODIFY 2 5062306a36Sopenharmony_ci#define PHYACC_ATTR_MODE_POLL 3 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define PHYACC_ATTR_BANK_SMI 0 5362306a36Sopenharmony_ci#define PHYACC_ATTR_BANK_MISC 1 5462306a36Sopenharmony_ci#define PHYACC_ATTR_BANK_PCS 2 5562306a36Sopenharmony_ci#define PHYACC_ATTR_BANK_AFE 3 5662306a36Sopenharmony_ci#define PHYACC_ATTR_BANK_DSP 4 5762306a36Sopenharmony_ci#define PHYACC_ATTR_BANK_MAX 7 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* measurement defines */ 6062306a36Sopenharmony_ci#define LAN87XX_CABLE_TEST_OK 0 6162306a36Sopenharmony_ci#define LAN87XX_CABLE_TEST_OPEN 1 6262306a36Sopenharmony_ci#define LAN87XX_CABLE_TEST_SAME_SHORT 2 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* T1 Registers */ 6562306a36Sopenharmony_ci#define T1_AFE_PORT_CFG1_REG 0x0B 6662306a36Sopenharmony_ci#define T1_POWER_DOWN_CONTROL_REG 0x1A 6762306a36Sopenharmony_ci#define T1_SLV_FD_MULT_CFG_REG 0x18 6862306a36Sopenharmony_ci#define T1_CDR_CFG_PRE_LOCK_REG 0x05 6962306a36Sopenharmony_ci#define T1_CDR_CFG_POST_LOCK_REG 0x06 7062306a36Sopenharmony_ci#define T1_LCK_STG2_MUFACT_CFG_REG 0x1A 7162306a36Sopenharmony_ci#define T1_LCK_STG3_MUFACT_CFG_REG 0x1B 7262306a36Sopenharmony_ci#define T1_POST_LCK_MUFACT_CFG_REG 0x1C 7362306a36Sopenharmony_ci#define T1_TX_RX_FIFO_CFG_REG 0x02 7462306a36Sopenharmony_ci#define T1_TX_LPF_FIR_CFG_REG 0x55 7562306a36Sopenharmony_ci#define T1_COEF_CLK_PWR_DN_CFG 0x04 7662306a36Sopenharmony_ci#define T1_COEF_RW_CTL_CFG 0x0D 7762306a36Sopenharmony_ci#define T1_SQI_CONFIG_REG 0x2E 7862306a36Sopenharmony_ci#define T1_SQI_CONFIG2_REG 0x4A 7962306a36Sopenharmony_ci#define T1_DCQ_SQI_REG 0xC3 8062306a36Sopenharmony_ci#define T1_DCQ_SQI_MSK GENMASK(3, 1) 8162306a36Sopenharmony_ci#define T1_MDIO_CONTROL2_REG 0x10 8262306a36Sopenharmony_ci#define T1_INTERRUPT_SOURCE_REG 0x18 8362306a36Sopenharmony_ci#define T1_INTERRUPT2_SOURCE_REG 0x08 8462306a36Sopenharmony_ci#define T1_EQ_FD_STG1_FRZ_CFG 0x69 8562306a36Sopenharmony_ci#define T1_EQ_FD_STG2_FRZ_CFG 0x6A 8662306a36Sopenharmony_ci#define T1_EQ_FD_STG3_FRZ_CFG 0x6B 8762306a36Sopenharmony_ci#define T1_EQ_FD_STG4_FRZ_CFG 0x6C 8862306a36Sopenharmony_ci#define T1_EQ_WT_FD_LCK_FRZ_CFG 0x6D 8962306a36Sopenharmony_ci#define T1_PST_EQ_LCK_STG1_FRZ_CFG 0x6E 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define T1_MODE_STAT_REG 0x11 9262306a36Sopenharmony_ci#define T1_LINK_UP_MSK BIT(0) 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* SQI defines */ 9562306a36Sopenharmony_ci#define LAN87XX_MAX_SQI 0x07 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define DRIVER_AUTHOR "Nisar Sayed <nisar.sayed@microchip.com>" 9862306a36Sopenharmony_ci#define DRIVER_DESC "Microchip LAN87XX/LAN937x T1 PHY driver" 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistruct access_ereg_val { 10162306a36Sopenharmony_ci u8 mode; 10262306a36Sopenharmony_ci u8 bank; 10362306a36Sopenharmony_ci u8 offset; 10462306a36Sopenharmony_ci u16 val; 10562306a36Sopenharmony_ci u16 mask; 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int lan937x_dsp_workaround(struct phy_device *phydev, u16 ereg, u8 bank) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci u8 prev_bank; 11162306a36Sopenharmony_ci int rc = 0; 11262306a36Sopenharmony_ci u16 val; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci mutex_lock(&phydev->lock); 11562306a36Sopenharmony_ci /* Read previous selected bank */ 11662306a36Sopenharmony_ci rc = phy_read(phydev, LAN87XX_EXT_REG_CTL); 11762306a36Sopenharmony_ci if (rc < 0) 11862306a36Sopenharmony_ci goto out_unlock; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* store the prev_bank */ 12162306a36Sopenharmony_ci prev_bank = FIELD_GET(LAN87XX_REG_BANK_SEL_MASK, rc); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (bank != prev_bank && bank == PHYACC_ATTR_BANK_DSP) { 12462306a36Sopenharmony_ci val = ereg & ~LAN87XX_REG_ADDR_MASK; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci val &= ~LAN87XX_EXT_REG_CTL_WR_CTL; 12762306a36Sopenharmony_ci val |= LAN87XX_EXT_REG_CTL_RD_CTL; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* access twice for DSP bank change,dummy access */ 13062306a36Sopenharmony_ci rc = phy_write(phydev, LAN87XX_EXT_REG_CTL, val); 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ciout_unlock: 13462306a36Sopenharmony_ci mutex_unlock(&phydev->lock); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return rc; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic int access_ereg(struct phy_device *phydev, u8 mode, u8 bank, 14062306a36Sopenharmony_ci u8 offset, u16 val) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci u16 ereg = 0; 14362306a36Sopenharmony_ci int rc = 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (mode > PHYACC_ATTR_MODE_WRITE || bank > PHYACC_ATTR_BANK_MAX) 14662306a36Sopenharmony_ci return -EINVAL; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (bank == PHYACC_ATTR_BANK_SMI) { 14962306a36Sopenharmony_ci if (mode == PHYACC_ATTR_MODE_WRITE) 15062306a36Sopenharmony_ci rc = phy_write(phydev, offset, val); 15162306a36Sopenharmony_ci else 15262306a36Sopenharmony_ci rc = phy_read(phydev, offset); 15362306a36Sopenharmony_ci return rc; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (mode == PHYACC_ATTR_MODE_WRITE) { 15762306a36Sopenharmony_ci ereg = LAN87XX_EXT_REG_CTL_WR_CTL; 15862306a36Sopenharmony_ci rc = phy_write(phydev, LAN87XX_EXT_REG_WR_DATA, val); 15962306a36Sopenharmony_ci if (rc < 0) 16062306a36Sopenharmony_ci return rc; 16162306a36Sopenharmony_ci } else { 16262306a36Sopenharmony_ci ereg = LAN87XX_EXT_REG_CTL_RD_CTL; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci ereg |= (bank << 8) | offset; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* DSP bank access workaround for lan937x */ 16862306a36Sopenharmony_ci if (phydev->phy_id == PHY_ID_LAN937X) { 16962306a36Sopenharmony_ci rc = lan937x_dsp_workaround(phydev, ereg, bank); 17062306a36Sopenharmony_ci if (rc < 0) 17162306a36Sopenharmony_ci return rc; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci rc = phy_write(phydev, LAN87XX_EXT_REG_CTL, ereg); 17562306a36Sopenharmony_ci if (rc < 0) 17662306a36Sopenharmony_ci return rc; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (mode == PHYACC_ATTR_MODE_READ) 17962306a36Sopenharmony_ci rc = phy_read(phydev, LAN87XX_EXT_REG_RD_DATA); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return rc; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int access_ereg_modify_changed(struct phy_device *phydev, 18562306a36Sopenharmony_ci u8 bank, u8 offset, u16 val, u16 mask) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci int new = 0, rc = 0; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (bank > PHYACC_ATTR_BANK_MAX) 19062306a36Sopenharmony_ci return -EINVAL; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, bank, offset, val); 19362306a36Sopenharmony_ci if (rc < 0) 19462306a36Sopenharmony_ci return rc; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci new = val | (rc & (mask ^ 0xFFFF)); 19762306a36Sopenharmony_ci rc = access_ereg(phydev, PHYACC_ATTR_MODE_WRITE, bank, offset, new); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return rc; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int access_smi_poll_timeout(struct phy_device *phydev, 20362306a36Sopenharmony_ci u8 offset, u16 mask, u16 clr) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci int val; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return phy_read_poll_timeout(phydev, offset, val, (val & mask) == clr, 20862306a36Sopenharmony_ci 150, 30000, true); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int lan87xx_config_rgmii_delay(struct phy_device *phydev) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci int rc; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (!phy_interface_is_rgmii(phydev)) 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, 21962306a36Sopenharmony_ci PHYACC_ATTR_BANK_MISC, LAN87XX_CTRL_1, 0); 22062306a36Sopenharmony_ci if (rc < 0) 22162306a36Sopenharmony_ci return rc; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci switch (phydev->interface) { 22462306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII: 22562306a36Sopenharmony_ci rc &= ~LAN87XX_MASK_RGMII_TXC_DLY_EN; 22662306a36Sopenharmony_ci rc &= ~LAN87XX_MASK_RGMII_RXC_DLY_EN; 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_ID: 22962306a36Sopenharmony_ci rc |= LAN87XX_MASK_RGMII_TXC_DLY_EN; 23062306a36Sopenharmony_ci rc |= LAN87XX_MASK_RGMII_RXC_DLY_EN; 23162306a36Sopenharmony_ci break; 23262306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_RXID: 23362306a36Sopenharmony_ci rc &= ~LAN87XX_MASK_RGMII_TXC_DLY_EN; 23462306a36Sopenharmony_ci rc |= LAN87XX_MASK_RGMII_RXC_DLY_EN; 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_TXID: 23762306a36Sopenharmony_ci rc |= LAN87XX_MASK_RGMII_TXC_DLY_EN; 23862306a36Sopenharmony_ci rc &= ~LAN87XX_MASK_RGMII_RXC_DLY_EN; 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci default: 24162306a36Sopenharmony_ci return 0; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return access_ereg(phydev, PHYACC_ATTR_MODE_WRITE, 24562306a36Sopenharmony_ci PHYACC_ATTR_BANK_MISC, LAN87XX_CTRL_1, rc); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int lan87xx_phy_init_cmd(struct phy_device *phydev, 24962306a36Sopenharmony_ci const struct access_ereg_val *cmd_seq, int cnt) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci int ret, i; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci for (i = 0; i < cnt; i++) { 25462306a36Sopenharmony_ci if (cmd_seq[i].mode == PHYACC_ATTR_MODE_POLL && 25562306a36Sopenharmony_ci cmd_seq[i].bank == PHYACC_ATTR_BANK_SMI) { 25662306a36Sopenharmony_ci ret = access_smi_poll_timeout(phydev, 25762306a36Sopenharmony_ci cmd_seq[i].offset, 25862306a36Sopenharmony_ci cmd_seq[i].val, 25962306a36Sopenharmony_ci cmd_seq[i].mask); 26062306a36Sopenharmony_ci } else { 26162306a36Sopenharmony_ci ret = access_ereg(phydev, cmd_seq[i].mode, 26262306a36Sopenharmony_ci cmd_seq[i].bank, cmd_seq[i].offset, 26362306a36Sopenharmony_ci cmd_seq[i].val); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci if (ret < 0) 26662306a36Sopenharmony_ci return ret; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return ret; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic int lan87xx_phy_init(struct phy_device *phydev) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci static const struct access_ereg_val hw_init[] = { 27562306a36Sopenharmony_ci /* TXPD/TXAMP6 Configs */ 27662306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_AFE, 27762306a36Sopenharmony_ci T1_AFE_PORT_CFG1_REG, 0x002D, 0 }, 27862306a36Sopenharmony_ci /* HW_Init Hi and Force_ED */ 27962306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI, 28062306a36Sopenharmony_ci T1_POWER_DOWN_CONTROL_REG, 0x0308, 0 }, 28162306a36Sopenharmony_ci }; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci static const struct access_ereg_val slave_init[] = { 28462306a36Sopenharmony_ci /* Equalizer Full Duplex Freeze - T1 Slave */ 28562306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 28662306a36Sopenharmony_ci T1_EQ_FD_STG1_FRZ_CFG, 0x0002, 0 }, 28762306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 28862306a36Sopenharmony_ci T1_EQ_FD_STG2_FRZ_CFG, 0x0002, 0 }, 28962306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 29062306a36Sopenharmony_ci T1_EQ_FD_STG3_FRZ_CFG, 0x0002, 0 }, 29162306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 29262306a36Sopenharmony_ci T1_EQ_FD_STG4_FRZ_CFG, 0x0002, 0 }, 29362306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 29462306a36Sopenharmony_ci T1_EQ_WT_FD_LCK_FRZ_CFG, 0x0002, 0 }, 29562306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 29662306a36Sopenharmony_ci T1_PST_EQ_LCK_STG1_FRZ_CFG, 0x0002, 0 }, 29762306a36Sopenharmony_ci }; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci static const struct access_ereg_val phy_init[] = { 30062306a36Sopenharmony_ci /* Slave Full Duplex Multi Configs */ 30162306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 30262306a36Sopenharmony_ci T1_SLV_FD_MULT_CFG_REG, 0x0D53, 0 }, 30362306a36Sopenharmony_ci /* CDR Pre and Post Lock Configs */ 30462306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 30562306a36Sopenharmony_ci T1_CDR_CFG_PRE_LOCK_REG, 0x0AB2, 0 }, 30662306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 30762306a36Sopenharmony_ci T1_CDR_CFG_POST_LOCK_REG, 0x0AB3, 0 }, 30862306a36Sopenharmony_ci /* Lock Stage 2-3 Multi Factor Config */ 30962306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 31062306a36Sopenharmony_ci T1_LCK_STG2_MUFACT_CFG_REG, 0x0AEA, 0 }, 31162306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 31262306a36Sopenharmony_ci T1_LCK_STG3_MUFACT_CFG_REG, 0x0AEB, 0 }, 31362306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 31462306a36Sopenharmony_ci T1_POST_LCK_MUFACT_CFG_REG, 0x0AEB, 0 }, 31562306a36Sopenharmony_ci /* Pointer delay */ 31662306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 31762306a36Sopenharmony_ci T1_TX_RX_FIFO_CFG_REG, 0x1C00, 0 }, 31862306a36Sopenharmony_ci /* Tx iir edits */ 31962306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 32062306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1000, 0 }, 32162306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 32262306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1861, 0 }, 32362306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 32462306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1061, 0 }, 32562306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 32662306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1922, 0 }, 32762306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 32862306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1122, 0 }, 32962306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 33062306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1983, 0 }, 33162306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 33262306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1183, 0 }, 33362306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 33462306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1944, 0 }, 33562306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 33662306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1144, 0 }, 33762306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 33862306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x18c5, 0 }, 33962306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 34062306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x10c5, 0 }, 34162306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 34262306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1846, 0 }, 34362306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 34462306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1046, 0 }, 34562306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 34662306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1807, 0 }, 34762306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 34862306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1007, 0 }, 34962306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 35062306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1808, 0 }, 35162306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 35262306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1008, 0 }, 35362306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 35462306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1809, 0 }, 35562306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 35662306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1009, 0 }, 35762306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 35862306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x180A, 0 }, 35962306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 36062306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x100A, 0 }, 36162306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 36262306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x180B, 0 }, 36362306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 36462306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x100B, 0 }, 36562306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 36662306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x180C, 0 }, 36762306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 36862306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x100C, 0 }, 36962306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 37062306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x180D, 0 }, 37162306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 37262306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x100D, 0 }, 37362306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 37462306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x180E, 0 }, 37562306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 37662306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x100E, 0 }, 37762306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 37862306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x180F, 0 }, 37962306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 38062306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x100F, 0 }, 38162306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 38262306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1810, 0 }, 38362306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 38462306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1010, 0 }, 38562306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 38662306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1811, 0 }, 38762306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 38862306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1011, 0 }, 38962306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 39062306a36Sopenharmony_ci T1_TX_LPF_FIR_CFG_REG, 0x1000, 0 }, 39162306a36Sopenharmony_ci /* Setup SQI measurement */ 39262306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 39362306a36Sopenharmony_ci T1_COEF_CLK_PWR_DN_CFG, 0x16d6, 0 }, 39462306a36Sopenharmony_ci /* SQI enable */ 39562306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 39662306a36Sopenharmony_ci T1_SQI_CONFIG_REG, 0x9572, 0 }, 39762306a36Sopenharmony_ci /* SQI select mode 5 */ 39862306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 39962306a36Sopenharmony_ci T1_SQI_CONFIG2_REG, 0x0001, 0 }, 40062306a36Sopenharmony_ci /* Throws the first SQI reading */ 40162306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 40262306a36Sopenharmony_ci T1_COEF_RW_CTL_CFG, 0x0301, 0 }, 40362306a36Sopenharmony_ci { PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_DSP, 40462306a36Sopenharmony_ci T1_DCQ_SQI_REG, 0, 0 }, 40562306a36Sopenharmony_ci /* Flag LPS and WUR as idle errors */ 40662306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI, 40762306a36Sopenharmony_ci T1_MDIO_CONTROL2_REG, 0x0014, 0 }, 40862306a36Sopenharmony_ci /* HW_Init toggle, undo force ED, TXPD off */ 40962306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI, 41062306a36Sopenharmony_ci T1_POWER_DOWN_CONTROL_REG, 0x0200, 0 }, 41162306a36Sopenharmony_ci /* Reset PCS to trigger hardware initialization */ 41262306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI, 41362306a36Sopenharmony_ci T1_MDIO_CONTROL2_REG, 0x0094, 0 }, 41462306a36Sopenharmony_ci /* Poll till Hardware is initialized */ 41562306a36Sopenharmony_ci { PHYACC_ATTR_MODE_POLL, PHYACC_ATTR_BANK_SMI, 41662306a36Sopenharmony_ci T1_MDIO_CONTROL2_REG, 0x0080, 0 }, 41762306a36Sopenharmony_ci /* Tx AMP - 0x06 */ 41862306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_AFE, 41962306a36Sopenharmony_ci T1_AFE_PORT_CFG1_REG, 0x000C, 0 }, 42062306a36Sopenharmony_ci /* Read INTERRUPT_SOURCE Register */ 42162306a36Sopenharmony_ci { PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_SMI, 42262306a36Sopenharmony_ci T1_INTERRUPT_SOURCE_REG, 0, 0 }, 42362306a36Sopenharmony_ci /* Read INTERRUPT_SOURCE Register */ 42462306a36Sopenharmony_ci { PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_MISC, 42562306a36Sopenharmony_ci T1_INTERRUPT2_SOURCE_REG, 0, 0 }, 42662306a36Sopenharmony_ci /* HW_Init Hi */ 42762306a36Sopenharmony_ci { PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI, 42862306a36Sopenharmony_ci T1_POWER_DOWN_CONTROL_REG, 0x0300, 0 }, 42962306a36Sopenharmony_ci }; 43062306a36Sopenharmony_ci int rc; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* phy Soft reset */ 43362306a36Sopenharmony_ci rc = genphy_soft_reset(phydev); 43462306a36Sopenharmony_ci if (rc < 0) 43562306a36Sopenharmony_ci return rc; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* PHY Initialization */ 43862306a36Sopenharmony_ci rc = lan87xx_phy_init_cmd(phydev, hw_init, ARRAY_SIZE(hw_init)); 43962306a36Sopenharmony_ci if (rc < 0) 44062306a36Sopenharmony_ci return rc; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci rc = genphy_read_master_slave(phydev); 44362306a36Sopenharmony_ci if (rc) 44462306a36Sopenharmony_ci return rc; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* The following squence needs to run only if phydev is in 44762306a36Sopenharmony_ci * slave mode. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_ci if (phydev->master_slave_state == MASTER_SLAVE_STATE_SLAVE) { 45062306a36Sopenharmony_ci rc = lan87xx_phy_init_cmd(phydev, slave_init, 45162306a36Sopenharmony_ci ARRAY_SIZE(slave_init)); 45262306a36Sopenharmony_ci if (rc < 0) 45362306a36Sopenharmony_ci return rc; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci rc = lan87xx_phy_init_cmd(phydev, phy_init, ARRAY_SIZE(phy_init)); 45762306a36Sopenharmony_ci if (rc < 0) 45862306a36Sopenharmony_ci return rc; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci return lan87xx_config_rgmii_delay(phydev); 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic int lan87xx_phy_config_intr(struct phy_device *phydev) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci int rc, val = 0; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 46862306a36Sopenharmony_ci /* clear all interrupt */ 46962306a36Sopenharmony_ci rc = phy_write(phydev, LAN87XX_INTERRUPT_MASK, val); 47062306a36Sopenharmony_ci if (rc < 0) 47162306a36Sopenharmony_ci return rc; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci rc = phy_read(phydev, LAN87XX_INTERRUPT_SOURCE); 47462306a36Sopenharmony_ci if (rc < 0) 47562306a36Sopenharmony_ci return rc; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci rc = access_ereg(phydev, PHYACC_ATTR_MODE_WRITE, 47862306a36Sopenharmony_ci PHYACC_ATTR_BANK_MISC, 47962306a36Sopenharmony_ci LAN87XX_INTERRUPT_MASK_2, val); 48062306a36Sopenharmony_ci if (rc < 0) 48162306a36Sopenharmony_ci return rc; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, 48462306a36Sopenharmony_ci PHYACC_ATTR_BANK_MISC, 48562306a36Sopenharmony_ci LAN87XX_INTERRUPT_SOURCE_2, 0); 48662306a36Sopenharmony_ci if (rc < 0) 48762306a36Sopenharmony_ci return rc; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* enable link down and comm ready interrupt */ 49062306a36Sopenharmony_ci val = LAN87XX_MASK_LINK_DOWN; 49162306a36Sopenharmony_ci rc = phy_write(phydev, LAN87XX_INTERRUPT_MASK, val); 49262306a36Sopenharmony_ci if (rc < 0) 49362306a36Sopenharmony_ci return rc; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci val = LAN87XX_MASK_COMM_RDY; 49662306a36Sopenharmony_ci rc = access_ereg(phydev, PHYACC_ATTR_MODE_WRITE, 49762306a36Sopenharmony_ci PHYACC_ATTR_BANK_MISC, 49862306a36Sopenharmony_ci LAN87XX_INTERRUPT_MASK_2, val); 49962306a36Sopenharmony_ci } else { 50062306a36Sopenharmony_ci rc = phy_write(phydev, LAN87XX_INTERRUPT_MASK, val); 50162306a36Sopenharmony_ci if (rc < 0) 50262306a36Sopenharmony_ci return rc; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci rc = phy_read(phydev, LAN87XX_INTERRUPT_SOURCE); 50562306a36Sopenharmony_ci if (rc < 0) 50662306a36Sopenharmony_ci return rc; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci rc = access_ereg(phydev, PHYACC_ATTR_MODE_WRITE, 50962306a36Sopenharmony_ci PHYACC_ATTR_BANK_MISC, 51062306a36Sopenharmony_ci LAN87XX_INTERRUPT_MASK_2, val); 51162306a36Sopenharmony_ci if (rc < 0) 51262306a36Sopenharmony_ci return rc; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, 51562306a36Sopenharmony_ci PHYACC_ATTR_BANK_MISC, 51662306a36Sopenharmony_ci LAN87XX_INTERRUPT_SOURCE_2, 0); 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci return rc < 0 ? rc : 0; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic irqreturn_t lan87xx_handle_interrupt(struct phy_device *phydev) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci int irq_status; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci irq_status = access_ereg(phydev, PHYACC_ATTR_MODE_READ, 52762306a36Sopenharmony_ci PHYACC_ATTR_BANK_MISC, 52862306a36Sopenharmony_ci LAN87XX_INTERRUPT_SOURCE_2, 0); 52962306a36Sopenharmony_ci if (irq_status < 0) { 53062306a36Sopenharmony_ci phy_error(phydev); 53162306a36Sopenharmony_ci return IRQ_NONE; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci irq_status = phy_read(phydev, LAN87XX_INTERRUPT_SOURCE); 53562306a36Sopenharmony_ci if (irq_status < 0) { 53662306a36Sopenharmony_ci phy_error(phydev); 53762306a36Sopenharmony_ci return IRQ_NONE; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (irq_status == 0) 54162306a36Sopenharmony_ci return IRQ_NONE; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci phy_trigger_machine(phydev); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci return IRQ_HANDLED; 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic int lan87xx_config_init(struct phy_device *phydev) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci int rc = lan87xx_phy_init(phydev); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return rc < 0 ? rc : 0; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic int microchip_cable_test_start_common(struct phy_device *phydev) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci int bmcr, bmsr, ret; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* If auto-negotiation is enabled, but not complete, the cable 56062306a36Sopenharmony_ci * test never completes. So disable auto-neg. 56162306a36Sopenharmony_ci */ 56262306a36Sopenharmony_ci bmcr = phy_read(phydev, MII_BMCR); 56362306a36Sopenharmony_ci if (bmcr < 0) 56462306a36Sopenharmony_ci return bmcr; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci bmsr = phy_read(phydev, MII_BMSR); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (bmsr < 0) 56962306a36Sopenharmony_ci return bmsr; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (bmcr & BMCR_ANENABLE) { 57262306a36Sopenharmony_ci ret = phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0); 57362306a36Sopenharmony_ci if (ret < 0) 57462306a36Sopenharmony_ci return ret; 57562306a36Sopenharmony_ci ret = genphy_soft_reset(phydev); 57662306a36Sopenharmony_ci if (ret < 0) 57762306a36Sopenharmony_ci return ret; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* If the link is up, allow it some time to go down */ 58162306a36Sopenharmony_ci if (bmsr & BMSR_LSTATUS) 58262306a36Sopenharmony_ci msleep(1500); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci return 0; 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic int lan87xx_cable_test_start(struct phy_device *phydev) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci static const struct access_ereg_val cable_test[] = { 59062306a36Sopenharmony_ci /* min wait */ 59162306a36Sopenharmony_ci {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 93, 59262306a36Sopenharmony_ci 0, 0}, 59362306a36Sopenharmony_ci /* max wait */ 59462306a36Sopenharmony_ci {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 94, 59562306a36Sopenharmony_ci 10, 0}, 59662306a36Sopenharmony_ci /* pulse cycle */ 59762306a36Sopenharmony_ci {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 95, 59862306a36Sopenharmony_ci 90, 0}, 59962306a36Sopenharmony_ci /* cable diag thresh */ 60062306a36Sopenharmony_ci {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 92, 60162306a36Sopenharmony_ci 60, 0}, 60262306a36Sopenharmony_ci /* max gain */ 60362306a36Sopenharmony_ci {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 79, 60462306a36Sopenharmony_ci 31, 0}, 60562306a36Sopenharmony_ci /* clock align for each iteration */ 60662306a36Sopenharmony_ci {PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_DSP, 55, 60762306a36Sopenharmony_ci 0, 0x0038}, 60862306a36Sopenharmony_ci /* max cycle wait config */ 60962306a36Sopenharmony_ci {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 94, 61062306a36Sopenharmony_ci 70, 0}, 61162306a36Sopenharmony_ci /* start cable diag*/ 61262306a36Sopenharmony_ci {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 90, 61362306a36Sopenharmony_ci 1, 0}, 61462306a36Sopenharmony_ci }; 61562306a36Sopenharmony_ci int rc, i; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci rc = microchip_cable_test_start_common(phydev); 61862306a36Sopenharmony_ci if (rc < 0) 61962306a36Sopenharmony_ci return rc; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* start cable diag */ 62262306a36Sopenharmony_ci /* check if part is alive - if not, return diagnostic error */ 62362306a36Sopenharmony_ci rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_SMI, 62462306a36Sopenharmony_ci 0x00, 0); 62562306a36Sopenharmony_ci if (rc < 0) 62662306a36Sopenharmony_ci return rc; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* master/slave specific configs */ 62962306a36Sopenharmony_ci rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_SMI, 63062306a36Sopenharmony_ci 0x0A, 0); 63162306a36Sopenharmony_ci if (rc < 0) 63262306a36Sopenharmony_ci return rc; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if ((rc & 0x4000) != 0x4000) { 63562306a36Sopenharmony_ci /* DUT is Slave */ 63662306a36Sopenharmony_ci rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_AFE, 63762306a36Sopenharmony_ci 0x0E, 0x5, 0x7); 63862306a36Sopenharmony_ci if (rc < 0) 63962306a36Sopenharmony_ci return rc; 64062306a36Sopenharmony_ci rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI, 64162306a36Sopenharmony_ci 0x1A, 0x8, 0x8); 64262306a36Sopenharmony_ci if (rc < 0) 64362306a36Sopenharmony_ci return rc; 64462306a36Sopenharmony_ci } else { 64562306a36Sopenharmony_ci /* DUT is Master */ 64662306a36Sopenharmony_ci rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI, 64762306a36Sopenharmony_ci 0x10, 0x8, 0x40); 64862306a36Sopenharmony_ci if (rc < 0) 64962306a36Sopenharmony_ci return rc; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cable_test); i++) { 65362306a36Sopenharmony_ci if (cable_test[i].mode == PHYACC_ATTR_MODE_MODIFY) { 65462306a36Sopenharmony_ci rc = access_ereg_modify_changed(phydev, 65562306a36Sopenharmony_ci cable_test[i].bank, 65662306a36Sopenharmony_ci cable_test[i].offset, 65762306a36Sopenharmony_ci cable_test[i].val, 65862306a36Sopenharmony_ci cable_test[i].mask); 65962306a36Sopenharmony_ci /* wait 50ms */ 66062306a36Sopenharmony_ci msleep(50); 66162306a36Sopenharmony_ci } else { 66262306a36Sopenharmony_ci rc = access_ereg(phydev, cable_test[i].mode, 66362306a36Sopenharmony_ci cable_test[i].bank, 66462306a36Sopenharmony_ci cable_test[i].offset, 66562306a36Sopenharmony_ci cable_test[i].val); 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci if (rc < 0) 66862306a36Sopenharmony_ci return rc; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci /* cable diag started */ 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci return 0; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic int lan87xx_cable_test_report_trans(u32 result) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci switch (result) { 67862306a36Sopenharmony_ci case LAN87XX_CABLE_TEST_OK: 67962306a36Sopenharmony_ci return ETHTOOL_A_CABLE_RESULT_CODE_OK; 68062306a36Sopenharmony_ci case LAN87XX_CABLE_TEST_OPEN: 68162306a36Sopenharmony_ci return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; 68262306a36Sopenharmony_ci case LAN87XX_CABLE_TEST_SAME_SHORT: 68362306a36Sopenharmony_ci return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; 68462306a36Sopenharmony_ci default: 68562306a36Sopenharmony_ci /* DIAGNOSTIC_ERROR */ 68662306a36Sopenharmony_ci return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic int lan87xx_cable_test_report(struct phy_device *phydev) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci int pos_peak_cycle = 0, pos_peak_in_phases = 0, pos_peak_phase = 0; 69362306a36Sopenharmony_ci int neg_peak_cycle = 0, neg_peak_in_phases = 0, neg_peak_phase = 0; 69462306a36Sopenharmony_ci int noise_margin = 20, time_margin = 89, jitter_var = 30; 69562306a36Sopenharmony_ci int min_time_diff = 96, max_time_diff = 96 + time_margin; 69662306a36Sopenharmony_ci bool fault = false, check_a = false, check_b = false; 69762306a36Sopenharmony_ci int gain_idx = 0, pos_peak = 0, neg_peak = 0; 69862306a36Sopenharmony_ci int pos_peak_time = 0, neg_peak_time = 0; 69962306a36Sopenharmony_ci int pos_peak_in_phases_hybrid = 0; 70062306a36Sopenharmony_ci int detect = -1; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci gain_idx = access_ereg(phydev, PHYACC_ATTR_MODE_READ, 70362306a36Sopenharmony_ci PHYACC_ATTR_BANK_DSP, 151, 0); 70462306a36Sopenharmony_ci /* read non-hybrid results */ 70562306a36Sopenharmony_ci pos_peak = access_ereg(phydev, PHYACC_ATTR_MODE_READ, 70662306a36Sopenharmony_ci PHYACC_ATTR_BANK_DSP, 153, 0); 70762306a36Sopenharmony_ci neg_peak = access_ereg(phydev, PHYACC_ATTR_MODE_READ, 70862306a36Sopenharmony_ci PHYACC_ATTR_BANK_DSP, 154, 0); 70962306a36Sopenharmony_ci pos_peak_time = access_ereg(phydev, PHYACC_ATTR_MODE_READ, 71062306a36Sopenharmony_ci PHYACC_ATTR_BANK_DSP, 156, 0); 71162306a36Sopenharmony_ci neg_peak_time = access_ereg(phydev, PHYACC_ATTR_MODE_READ, 71262306a36Sopenharmony_ci PHYACC_ATTR_BANK_DSP, 157, 0); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci pos_peak_cycle = (pos_peak_time >> 7) & 0x7F; 71562306a36Sopenharmony_ci /* calculate non-hybrid values */ 71662306a36Sopenharmony_ci pos_peak_phase = pos_peak_time & 0x7F; 71762306a36Sopenharmony_ci pos_peak_in_phases = (pos_peak_cycle * 96) + pos_peak_phase; 71862306a36Sopenharmony_ci neg_peak_cycle = (neg_peak_time >> 7) & 0x7F; 71962306a36Sopenharmony_ci neg_peak_phase = neg_peak_time & 0x7F; 72062306a36Sopenharmony_ci neg_peak_in_phases = (neg_peak_cycle * 96) + neg_peak_phase; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* process values */ 72362306a36Sopenharmony_ci check_a = 72462306a36Sopenharmony_ci ((pos_peak_in_phases - neg_peak_in_phases) >= min_time_diff) && 72562306a36Sopenharmony_ci ((pos_peak_in_phases - neg_peak_in_phases) < max_time_diff) && 72662306a36Sopenharmony_ci pos_peak_in_phases_hybrid < pos_peak_in_phases && 72762306a36Sopenharmony_ci (pos_peak_in_phases_hybrid < (neg_peak_in_phases + jitter_var)); 72862306a36Sopenharmony_ci check_b = 72962306a36Sopenharmony_ci ((neg_peak_in_phases - pos_peak_in_phases) >= min_time_diff) && 73062306a36Sopenharmony_ci ((neg_peak_in_phases - pos_peak_in_phases) < max_time_diff) && 73162306a36Sopenharmony_ci pos_peak_in_phases_hybrid < neg_peak_in_phases && 73262306a36Sopenharmony_ci (pos_peak_in_phases_hybrid < (pos_peak_in_phases + jitter_var)); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (pos_peak_in_phases > neg_peak_in_phases && check_a) 73562306a36Sopenharmony_ci detect = 2; 73662306a36Sopenharmony_ci else if ((neg_peak_in_phases > pos_peak_in_phases) && check_b) 73762306a36Sopenharmony_ci detect = 1; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (pos_peak > noise_margin && neg_peak > noise_margin && 74062306a36Sopenharmony_ci gain_idx >= 0) { 74162306a36Sopenharmony_ci if (detect == 1 || detect == 2) 74262306a36Sopenharmony_ci fault = true; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci if (!fault) 74662306a36Sopenharmony_ci detect = 0; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, 74962306a36Sopenharmony_ci lan87xx_cable_test_report_trans(detect)); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci return 0; 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic int lan87xx_cable_test_get_status(struct phy_device *phydev, 75562306a36Sopenharmony_ci bool *finished) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci int rc = 0; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci *finished = false; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci /* check if cable diag was finished */ 76262306a36Sopenharmony_ci rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_DSP, 76362306a36Sopenharmony_ci 90, 0); 76462306a36Sopenharmony_ci if (rc < 0) 76562306a36Sopenharmony_ci return rc; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if ((rc & 2) == 2) { 76862306a36Sopenharmony_ci /* stop cable diag*/ 76962306a36Sopenharmony_ci rc = access_ereg(phydev, PHYACC_ATTR_MODE_WRITE, 77062306a36Sopenharmony_ci PHYACC_ATTR_BANK_DSP, 77162306a36Sopenharmony_ci 90, 0); 77262306a36Sopenharmony_ci if (rc < 0) 77362306a36Sopenharmony_ci return rc; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci *finished = true; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci return lan87xx_cable_test_report(phydev); 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci return 0; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic int lan87xx_read_status(struct phy_device *phydev) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci int rc = 0; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci rc = phy_read(phydev, T1_MODE_STAT_REG); 78862306a36Sopenharmony_ci if (rc < 0) 78962306a36Sopenharmony_ci return rc; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (rc & T1_LINK_UP_MSK) 79262306a36Sopenharmony_ci phydev->link = 1; 79362306a36Sopenharmony_ci else 79462306a36Sopenharmony_ci phydev->link = 0; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci phydev->speed = SPEED_UNKNOWN; 79762306a36Sopenharmony_ci phydev->duplex = DUPLEX_UNKNOWN; 79862306a36Sopenharmony_ci phydev->pause = 0; 79962306a36Sopenharmony_ci phydev->asym_pause = 0; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci rc = genphy_read_master_slave(phydev); 80262306a36Sopenharmony_ci if (rc < 0) 80362306a36Sopenharmony_ci return rc; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci rc = genphy_read_status_fixed(phydev); 80662306a36Sopenharmony_ci if (rc < 0) 80762306a36Sopenharmony_ci return rc; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci return rc; 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic int lan87xx_config_aneg(struct phy_device *phydev) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci u16 ctl = 0; 81562306a36Sopenharmony_ci int ret; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci switch (phydev->master_slave_set) { 81862306a36Sopenharmony_ci case MASTER_SLAVE_CFG_MASTER_FORCE: 81962306a36Sopenharmony_ci ctl |= CTL1000_AS_MASTER; 82062306a36Sopenharmony_ci break; 82162306a36Sopenharmony_ci case MASTER_SLAVE_CFG_SLAVE_FORCE: 82262306a36Sopenharmony_ci break; 82362306a36Sopenharmony_ci case MASTER_SLAVE_CFG_UNKNOWN: 82462306a36Sopenharmony_ci case MASTER_SLAVE_CFG_UNSUPPORTED: 82562306a36Sopenharmony_ci return 0; 82662306a36Sopenharmony_ci default: 82762306a36Sopenharmony_ci phydev_warn(phydev, "Unsupported Master/Slave mode\n"); 82862306a36Sopenharmony_ci return -EOPNOTSUPP; 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci ret = phy_modify_changed(phydev, MII_CTRL1000, CTL1000_AS_MASTER, ctl); 83262306a36Sopenharmony_ci if (ret == 1) 83362306a36Sopenharmony_ci return phy_init_hw(phydev); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci return ret; 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cistatic int lan87xx_get_sqi(struct phy_device *phydev) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci u8 sqi_value = 0; 84162306a36Sopenharmony_ci int rc; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci rc = access_ereg(phydev, PHYACC_ATTR_MODE_WRITE, 84462306a36Sopenharmony_ci PHYACC_ATTR_BANK_DSP, T1_COEF_RW_CTL_CFG, 0x0301); 84562306a36Sopenharmony_ci if (rc < 0) 84662306a36Sopenharmony_ci return rc; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, 84962306a36Sopenharmony_ci PHYACC_ATTR_BANK_DSP, T1_DCQ_SQI_REG, 0x0); 85062306a36Sopenharmony_ci if (rc < 0) 85162306a36Sopenharmony_ci return rc; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci sqi_value = FIELD_GET(T1_DCQ_SQI_MSK, rc); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci return sqi_value; 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic int lan87xx_get_sqi_max(struct phy_device *phydev) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci return LAN87XX_MAX_SQI; 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_cistatic struct phy_driver microchip_t1_phy_driver[] = { 86462306a36Sopenharmony_ci { 86562306a36Sopenharmony_ci PHY_ID_MATCH_MODEL(PHY_ID_LAN87XX), 86662306a36Sopenharmony_ci .name = "Microchip LAN87xx T1", 86762306a36Sopenharmony_ci .flags = PHY_POLL_CABLE_TEST, 86862306a36Sopenharmony_ci .features = PHY_BASIC_T1_FEATURES, 86962306a36Sopenharmony_ci .config_init = lan87xx_config_init, 87062306a36Sopenharmony_ci .config_intr = lan87xx_phy_config_intr, 87162306a36Sopenharmony_ci .handle_interrupt = lan87xx_handle_interrupt, 87262306a36Sopenharmony_ci .suspend = genphy_suspend, 87362306a36Sopenharmony_ci .resume = genphy_resume, 87462306a36Sopenharmony_ci .config_aneg = lan87xx_config_aneg, 87562306a36Sopenharmony_ci .read_status = lan87xx_read_status, 87662306a36Sopenharmony_ci .get_sqi = lan87xx_get_sqi, 87762306a36Sopenharmony_ci .get_sqi_max = lan87xx_get_sqi_max, 87862306a36Sopenharmony_ci .cable_test_start = lan87xx_cable_test_start, 87962306a36Sopenharmony_ci .cable_test_get_status = lan87xx_cable_test_get_status, 88062306a36Sopenharmony_ci }, 88162306a36Sopenharmony_ci { 88262306a36Sopenharmony_ci PHY_ID_MATCH_MODEL(PHY_ID_LAN937X), 88362306a36Sopenharmony_ci .name = "Microchip LAN937x T1", 88462306a36Sopenharmony_ci .flags = PHY_POLL_CABLE_TEST, 88562306a36Sopenharmony_ci .features = PHY_BASIC_T1_FEATURES, 88662306a36Sopenharmony_ci .config_init = lan87xx_config_init, 88762306a36Sopenharmony_ci .config_intr = lan87xx_phy_config_intr, 88862306a36Sopenharmony_ci .handle_interrupt = lan87xx_handle_interrupt, 88962306a36Sopenharmony_ci .suspend = genphy_suspend, 89062306a36Sopenharmony_ci .resume = genphy_resume, 89162306a36Sopenharmony_ci .config_aneg = lan87xx_config_aneg, 89262306a36Sopenharmony_ci .read_status = lan87xx_read_status, 89362306a36Sopenharmony_ci .get_sqi = lan87xx_get_sqi, 89462306a36Sopenharmony_ci .get_sqi_max = lan87xx_get_sqi_max, 89562306a36Sopenharmony_ci .cable_test_start = lan87xx_cable_test_start, 89662306a36Sopenharmony_ci .cable_test_get_status = lan87xx_cable_test_get_status, 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci}; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cimodule_phy_driver(microchip_t1_phy_driver); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused microchip_t1_tbl[] = { 90362306a36Sopenharmony_ci { PHY_ID_MATCH_MODEL(PHY_ID_LAN87XX) }, 90462306a36Sopenharmony_ci { PHY_ID_MATCH_MODEL(PHY_ID_LAN937X) }, 90562306a36Sopenharmony_ci { } 90662306a36Sopenharmony_ci}; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, microchip_t1_tbl); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 91162306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 91262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 913