162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for Vitesse PHYs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Kriston Carson 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/mii.h> 1162306a36Sopenharmony_ci#include <linux/ethtool.h> 1262306a36Sopenharmony_ci#include <linux/phy.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* Vitesse Extended Page Magic Register(s) */ 1562306a36Sopenharmony_ci#define MII_VSC82X4_EXT_PAGE_16E 0x10 1662306a36Sopenharmony_ci#define MII_VSC82X4_EXT_PAGE_17E 0x11 1762306a36Sopenharmony_ci#define MII_VSC82X4_EXT_PAGE_18E 0x12 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* Vitesse Extended Control Register 1 */ 2062306a36Sopenharmony_ci#define MII_VSC8244_EXT_CON1 0x17 2162306a36Sopenharmony_ci#define MII_VSC8244_EXTCON1_INIT 0x0000 2262306a36Sopenharmony_ci#define MII_VSC8244_EXTCON1_TX_SKEW_MASK 0x0c00 2362306a36Sopenharmony_ci#define MII_VSC8244_EXTCON1_RX_SKEW_MASK 0x0300 2462306a36Sopenharmony_ci#define MII_VSC8244_EXTCON1_TX_SKEW 0x0800 2562306a36Sopenharmony_ci#define MII_VSC8244_EXTCON1_RX_SKEW 0x0200 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* Vitesse Interrupt Mask Register */ 2862306a36Sopenharmony_ci#define MII_VSC8244_IMASK 0x19 2962306a36Sopenharmony_ci#define MII_VSC8244_IMASK_IEN 0x8000 3062306a36Sopenharmony_ci#define MII_VSC8244_IMASK_SPEED 0x4000 3162306a36Sopenharmony_ci#define MII_VSC8244_IMASK_LINK 0x2000 3262306a36Sopenharmony_ci#define MII_VSC8244_IMASK_DUPLEX 0x1000 3362306a36Sopenharmony_ci#define MII_VSC8244_IMASK_MASK 0xf000 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define MII_VSC8221_IMASK_MASK 0xa000 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* Vitesse Interrupt Status Register */ 3862306a36Sopenharmony_ci#define MII_VSC8244_ISTAT 0x1a 3962306a36Sopenharmony_ci#define MII_VSC8244_ISTAT_STATUS 0x8000 4062306a36Sopenharmony_ci#define MII_VSC8244_ISTAT_SPEED 0x4000 4162306a36Sopenharmony_ci#define MII_VSC8244_ISTAT_LINK 0x2000 4262306a36Sopenharmony_ci#define MII_VSC8244_ISTAT_DUPLEX 0x1000 4362306a36Sopenharmony_ci#define MII_VSC8244_ISTAT_MASK (MII_VSC8244_ISTAT_SPEED | \ 4462306a36Sopenharmony_ci MII_VSC8244_ISTAT_LINK | \ 4562306a36Sopenharmony_ci MII_VSC8244_ISTAT_DUPLEX) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define MII_VSC8221_ISTAT_MASK MII_VSC8244_ISTAT_LINK 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* Vitesse Auxiliary Control/Status Register */ 5062306a36Sopenharmony_ci#define MII_VSC8244_AUX_CONSTAT 0x1c 5162306a36Sopenharmony_ci#define MII_VSC8244_AUXCONSTAT_INIT 0x0000 5262306a36Sopenharmony_ci#define MII_VSC8244_AUXCONSTAT_DUPLEX 0x0020 5362306a36Sopenharmony_ci#define MII_VSC8244_AUXCONSTAT_SPEED 0x0018 5462306a36Sopenharmony_ci#define MII_VSC8244_AUXCONSTAT_GBIT 0x0010 5562306a36Sopenharmony_ci#define MII_VSC8244_AUXCONSTAT_100 0x0008 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define MII_VSC8221_AUXCONSTAT_INIT 0x0004 /* need to set this bit? */ 5862306a36Sopenharmony_ci#define MII_VSC8221_AUXCONSTAT_RESERVED 0x0004 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* Vitesse Extended Page Access Register */ 6162306a36Sopenharmony_ci#define MII_VSC82X4_EXT_PAGE_ACCESS 0x1f 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* Vitesse VSC8601 Extended PHY Control Register 1 */ 6462306a36Sopenharmony_ci#define MII_VSC8601_EPHY_CTL 0x17 6562306a36Sopenharmony_ci#define MII_VSC8601_EPHY_CTL_RGMII_SKEW (1 << 8) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define PHY_ID_VSC8234 0x000fc620 6862306a36Sopenharmony_ci#define PHY_ID_VSC8244 0x000fc6c0 6962306a36Sopenharmony_ci#define PHY_ID_VSC8572 0x000704d0 7062306a36Sopenharmony_ci#define PHY_ID_VSC8601 0x00070420 7162306a36Sopenharmony_ci#define PHY_ID_VSC7385 0x00070450 7262306a36Sopenharmony_ci#define PHY_ID_VSC7388 0x00070480 7362306a36Sopenharmony_ci#define PHY_ID_VSC7395 0x00070550 7462306a36Sopenharmony_ci#define PHY_ID_VSC7398 0x00070580 7562306a36Sopenharmony_ci#define PHY_ID_VSC8662 0x00070660 7662306a36Sopenharmony_ci#define PHY_ID_VSC8221 0x000fc550 7762306a36Sopenharmony_ci#define PHY_ID_VSC8211 0x000fc4b0 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ciMODULE_DESCRIPTION("Vitesse PHY driver"); 8062306a36Sopenharmony_ciMODULE_AUTHOR("Kriston Carson"); 8162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int vsc824x_add_skew(struct phy_device *phydev) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci int err; 8662306a36Sopenharmony_ci int extcon; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci extcon = phy_read(phydev, MII_VSC8244_EXT_CON1); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (extcon < 0) 9162306a36Sopenharmony_ci return extcon; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci extcon &= ~(MII_VSC8244_EXTCON1_TX_SKEW_MASK | 9462306a36Sopenharmony_ci MII_VSC8244_EXTCON1_RX_SKEW_MASK); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci extcon |= (MII_VSC8244_EXTCON1_TX_SKEW | 9762306a36Sopenharmony_ci MII_VSC8244_EXTCON1_RX_SKEW); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci err = phy_write(phydev, MII_VSC8244_EXT_CON1, extcon); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return err; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int vsc824x_config_init(struct phy_device *phydev) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci int err; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT, 10962306a36Sopenharmony_ci MII_VSC8244_AUXCONSTAT_INIT); 11062306a36Sopenharmony_ci if (err < 0) 11162306a36Sopenharmony_ci return err; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) 11462306a36Sopenharmony_ci err = vsc824x_add_skew(phydev); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return err; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#define VSC73XX_EXT_PAGE_ACCESS 0x1f 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int vsc73xx_read_page(struct phy_device *phydev) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci return __phy_read(phydev, VSC73XX_EXT_PAGE_ACCESS); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int vsc73xx_write_page(struct phy_device *phydev, int page) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci return __phy_write(phydev, VSC73XX_EXT_PAGE_ACCESS, page); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void vsc73xx_config_init(struct phy_device *phydev) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci /* Receiver init */ 13462306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 13562306a36Sopenharmony_ci phy_modify(phydev, 0x0c, 0x0300, 0x0200); 13662306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x0000); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* Config LEDs 0x61 */ 13962306a36Sopenharmony_ci phy_modify(phydev, MII_TPISTATUS, 0xff00, 0x0061); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic int vsc738x_config_init(struct phy_device *phydev) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci u16 rev; 14562306a36Sopenharmony_ci /* This magic sequence appear in the application note 14662306a36Sopenharmony_ci * "VSC7385/7388 PHY Configuration". 14762306a36Sopenharmony_ci * 14862306a36Sopenharmony_ci * Maybe one day we will get to know what it all means. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 15162306a36Sopenharmony_ci phy_modify(phydev, 0x08, 0x0200, 0x0200); 15262306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x52b5); 15362306a36Sopenharmony_ci phy_write(phydev, 0x10, 0xb68a); 15462306a36Sopenharmony_ci phy_modify(phydev, 0x12, 0xff07, 0x0003); 15562306a36Sopenharmony_ci phy_modify(phydev, 0x11, 0x00ff, 0x00a2); 15662306a36Sopenharmony_ci phy_write(phydev, 0x10, 0x968a); 15762306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 15862306a36Sopenharmony_ci phy_modify(phydev, 0x08, 0x0200, 0x0000); 15962306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x0000); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Read revision */ 16262306a36Sopenharmony_ci rev = phy_read(phydev, MII_PHYSID2); 16362306a36Sopenharmony_ci rev &= 0x0f; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* Special quirk for revision 0 */ 16662306a36Sopenharmony_ci if (rev == 0) { 16762306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 16862306a36Sopenharmony_ci phy_modify(phydev, 0x08, 0x0200, 0x0200); 16962306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x52b5); 17062306a36Sopenharmony_ci phy_write(phydev, 0x12, 0x0000); 17162306a36Sopenharmony_ci phy_write(phydev, 0x11, 0x0689); 17262306a36Sopenharmony_ci phy_write(phydev, 0x10, 0x8f92); 17362306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x52b5); 17462306a36Sopenharmony_ci phy_write(phydev, 0x12, 0x0000); 17562306a36Sopenharmony_ci phy_write(phydev, 0x11, 0x0e35); 17662306a36Sopenharmony_ci phy_write(phydev, 0x10, 0x9786); 17762306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 17862306a36Sopenharmony_ci phy_modify(phydev, 0x08, 0x0200, 0x0000); 17962306a36Sopenharmony_ci phy_write(phydev, 0x17, 0xff80); 18062306a36Sopenharmony_ci phy_write(phydev, 0x17, 0x0000); 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x0000); 18462306a36Sopenharmony_ci phy_write(phydev, 0x12, 0x0048); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (rev == 0) { 18762306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 18862306a36Sopenharmony_ci phy_write(phydev, 0x14, 0x6600); 18962306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x0000); 19062306a36Sopenharmony_ci phy_write(phydev, 0x18, 0xa24e); 19162306a36Sopenharmony_ci } else { 19262306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 19362306a36Sopenharmony_ci phy_modify(phydev, 0x16, 0x0fc0, 0x0240); 19462306a36Sopenharmony_ci phy_modify(phydev, 0x14, 0x6000, 0x4000); 19562306a36Sopenharmony_ci /* bits 14-15 in extended register 0x14 controls DACG amplitude 19662306a36Sopenharmony_ci * 6 = -8%, 2 is hardware default 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x0001); 19962306a36Sopenharmony_ci phy_modify(phydev, 0x14, 0xe000, 0x6000); 20062306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x0000); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci vsc73xx_config_init(phydev); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int vsc739x_config_init(struct phy_device *phydev) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci /* This magic sequence appears in the VSC7395 SparX-G5e application 21162306a36Sopenharmony_ci * note "VSC7395/VSC7398 PHY Configuration" 21262306a36Sopenharmony_ci * 21362306a36Sopenharmony_ci * Maybe one day we will get to know what it all means. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 21662306a36Sopenharmony_ci phy_modify(phydev, 0x08, 0x0200, 0x0200); 21762306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x52b5); 21862306a36Sopenharmony_ci phy_write(phydev, 0x10, 0xb68a); 21962306a36Sopenharmony_ci phy_modify(phydev, 0x12, 0xff07, 0x0003); 22062306a36Sopenharmony_ci phy_modify(phydev, 0x11, 0x00ff, 0x00a2); 22162306a36Sopenharmony_ci phy_write(phydev, 0x10, 0x968a); 22262306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 22362306a36Sopenharmony_ci phy_modify(phydev, 0x08, 0x0200, 0x0000); 22462306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x0000); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x0000); 22762306a36Sopenharmony_ci phy_write(phydev, 0x12, 0x0048); 22862306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 22962306a36Sopenharmony_ci phy_modify(phydev, 0x16, 0x0fc0, 0x0240); 23062306a36Sopenharmony_ci phy_modify(phydev, 0x14, 0x6000, 0x4000); 23162306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x0001); 23262306a36Sopenharmony_ci phy_modify(phydev, 0x14, 0xe000, 0x6000); 23362306a36Sopenharmony_ci phy_write(phydev, 0x1f, 0x0000); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci vsc73xx_config_init(phydev); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci return 0; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic int vsc73xx_config_aneg(struct phy_device *phydev) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci /* The VSC73xx switches does not like to be instructed to 24362306a36Sopenharmony_ci * do autonegotiation in any way, it prefers that you just go 24462306a36Sopenharmony_ci * with the power-on/reset defaults. Writing some registers will 24562306a36Sopenharmony_ci * just make autonegotiation permanently fail. 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/* This adds a skew for both TX and RX clocks, so the skew should only be 25162306a36Sopenharmony_ci * applied to "rgmii-id" interfaces. It may not work as expected 25262306a36Sopenharmony_ci * on "rgmii-txid", "rgmii-rxid" or "rgmii" interfaces. 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_cistatic int vsc8601_add_skew(struct phy_device *phydev) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci int ret; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci ret = phy_read(phydev, MII_VSC8601_EPHY_CTL); 25962306a36Sopenharmony_ci if (ret < 0) 26062306a36Sopenharmony_ci return ret; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci ret |= MII_VSC8601_EPHY_CTL_RGMII_SKEW; 26362306a36Sopenharmony_ci return phy_write(phydev, MII_VSC8601_EPHY_CTL, ret); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic int vsc8601_config_init(struct phy_device *phydev) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci int ret = 0; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) 27162306a36Sopenharmony_ci ret = vsc8601_add_skew(phydev); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (ret < 0) 27462306a36Sopenharmony_ci return ret; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return 0; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int vsc82xx_config_intr(struct phy_device *phydev) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci int err; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 28462306a36Sopenharmony_ci /* Don't bother to ACK the interrupts since the 824x cannot 28562306a36Sopenharmony_ci * clear the interrupts if they are disabled. 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_ci err = phy_write(phydev, MII_VSC8244_IMASK, 28862306a36Sopenharmony_ci (phydev->drv->phy_id == PHY_ID_VSC8234 || 28962306a36Sopenharmony_ci phydev->drv->phy_id == PHY_ID_VSC8244 || 29062306a36Sopenharmony_ci phydev->drv->phy_id == PHY_ID_VSC8572 || 29162306a36Sopenharmony_ci phydev->drv->phy_id == PHY_ID_VSC8601) ? 29262306a36Sopenharmony_ci MII_VSC8244_IMASK_MASK : 29362306a36Sopenharmony_ci MII_VSC8221_IMASK_MASK); 29462306a36Sopenharmony_ci else { 29562306a36Sopenharmony_ci /* The Vitesse PHY cannot clear the interrupt 29662306a36Sopenharmony_ci * once it has disabled them, so we clear them first 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ci err = phy_read(phydev, MII_VSC8244_ISTAT); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (err < 0) 30162306a36Sopenharmony_ci return err; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci err = phy_write(phydev, MII_VSC8244_IMASK, 0); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return err; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic irqreturn_t vsc82xx_handle_interrupt(struct phy_device *phydev) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci int irq_status, irq_mask; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (phydev->drv->phy_id == PHY_ID_VSC8244 || 31462306a36Sopenharmony_ci phydev->drv->phy_id == PHY_ID_VSC8572 || 31562306a36Sopenharmony_ci phydev->drv->phy_id == PHY_ID_VSC8601) 31662306a36Sopenharmony_ci irq_mask = MII_VSC8244_ISTAT_MASK; 31762306a36Sopenharmony_ci else 31862306a36Sopenharmony_ci irq_mask = MII_VSC8221_ISTAT_MASK; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci irq_status = phy_read(phydev, MII_VSC8244_ISTAT); 32162306a36Sopenharmony_ci if (irq_status < 0) { 32262306a36Sopenharmony_ci phy_error(phydev); 32362306a36Sopenharmony_ci return IRQ_NONE; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (!(irq_status & irq_mask)) 32762306a36Sopenharmony_ci return IRQ_NONE; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci phy_trigger_machine(phydev); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return IRQ_HANDLED; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int vsc8221_config_init(struct phy_device *phydev) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci int err; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT, 33962306a36Sopenharmony_ci MII_VSC8221_AUXCONSTAT_INIT); 34062306a36Sopenharmony_ci return err; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* Perhaps we should set EXT_CON1 based on the interface? 34362306a36Sopenharmony_ci * Options are 802.3Z SerDes or SGMII 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci/* vsc82x4_config_autocross_enable - Enable auto MDI/MDI-X for forced links 34862306a36Sopenharmony_ci * @phydev: target phy_device struct 34962306a36Sopenharmony_ci * 35062306a36Sopenharmony_ci * Enable auto MDI/MDI-X when in 10/100 forced link speeds by writing 35162306a36Sopenharmony_ci * special values in the VSC8234/VSC8244 extended reserved registers 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_cistatic int vsc82x4_config_autocross_enable(struct phy_device *phydev) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci int ret; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed > SPEED_100) 35862306a36Sopenharmony_ci return 0; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* map extended registers set 0x10 - 0x1e */ 36162306a36Sopenharmony_ci ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x52b5); 36262306a36Sopenharmony_ci if (ret >= 0) 36362306a36Sopenharmony_ci ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_18E, 0x0012); 36462306a36Sopenharmony_ci if (ret >= 0) 36562306a36Sopenharmony_ci ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_17E, 0x2803); 36662306a36Sopenharmony_ci if (ret >= 0) 36762306a36Sopenharmony_ci ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_16E, 0x87fa); 36862306a36Sopenharmony_ci /* map standard registers set 0x10 - 0x1e */ 36962306a36Sopenharmony_ci if (ret >= 0) 37062306a36Sopenharmony_ci ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x0000); 37162306a36Sopenharmony_ci else 37262306a36Sopenharmony_ci phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x0000); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return ret; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci/* vsc82x4_config_aneg - restart auto-negotiation or write BMCR 37862306a36Sopenharmony_ci * @phydev: target phy_device struct 37962306a36Sopenharmony_ci * 38062306a36Sopenharmony_ci * Description: If auto-negotiation is enabled, we configure the 38162306a36Sopenharmony_ci * advertising, and then restart auto-negotiation. If it is not 38262306a36Sopenharmony_ci * enabled, then we write the BMCR and also start the auto 38362306a36Sopenharmony_ci * MDI/MDI-X feature 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_cistatic int vsc82x4_config_aneg(struct phy_device *phydev) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci int ret; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* Enable auto MDI/MDI-X when in 10/100 forced link speeds by 39062306a36Sopenharmony_ci * writing special values in the VSC8234 extended reserved registers 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_ci if (phydev->autoneg != AUTONEG_ENABLE && phydev->speed <= SPEED_100) { 39362306a36Sopenharmony_ci ret = genphy_setup_forced(phydev); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (ret < 0) /* error */ 39662306a36Sopenharmony_ci return ret; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return vsc82x4_config_autocross_enable(phydev); 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci return genphy_config_aneg(phydev); 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci/* Vitesse 82xx */ 40562306a36Sopenharmony_cistatic struct phy_driver vsc82xx_driver[] = { 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci .phy_id = PHY_ID_VSC8234, 40862306a36Sopenharmony_ci .name = "Vitesse VSC8234", 40962306a36Sopenharmony_ci .phy_id_mask = 0x000ffff0, 41062306a36Sopenharmony_ci /* PHY_GBIT_FEATURES */ 41162306a36Sopenharmony_ci .config_init = &vsc824x_config_init, 41262306a36Sopenharmony_ci .config_aneg = &vsc82x4_config_aneg, 41362306a36Sopenharmony_ci .config_intr = &vsc82xx_config_intr, 41462306a36Sopenharmony_ci .handle_interrupt = &vsc82xx_handle_interrupt, 41562306a36Sopenharmony_ci}, { 41662306a36Sopenharmony_ci .phy_id = PHY_ID_VSC8244, 41762306a36Sopenharmony_ci .name = "Vitesse VSC8244", 41862306a36Sopenharmony_ci .phy_id_mask = 0x000fffc0, 41962306a36Sopenharmony_ci /* PHY_GBIT_FEATURES */ 42062306a36Sopenharmony_ci .config_init = &vsc824x_config_init, 42162306a36Sopenharmony_ci .config_aneg = &vsc82x4_config_aneg, 42262306a36Sopenharmony_ci .config_intr = &vsc82xx_config_intr, 42362306a36Sopenharmony_ci .handle_interrupt = &vsc82xx_handle_interrupt, 42462306a36Sopenharmony_ci}, { 42562306a36Sopenharmony_ci .phy_id = PHY_ID_VSC8572, 42662306a36Sopenharmony_ci .name = "Vitesse VSC8572", 42762306a36Sopenharmony_ci .phy_id_mask = 0x000ffff0, 42862306a36Sopenharmony_ci /* PHY_GBIT_FEATURES */ 42962306a36Sopenharmony_ci .config_init = &vsc824x_config_init, 43062306a36Sopenharmony_ci .config_aneg = &vsc82x4_config_aneg, 43162306a36Sopenharmony_ci .config_intr = &vsc82xx_config_intr, 43262306a36Sopenharmony_ci .handle_interrupt = &vsc82xx_handle_interrupt, 43362306a36Sopenharmony_ci}, { 43462306a36Sopenharmony_ci .phy_id = PHY_ID_VSC8601, 43562306a36Sopenharmony_ci .name = "Vitesse VSC8601", 43662306a36Sopenharmony_ci .phy_id_mask = 0x000ffff0, 43762306a36Sopenharmony_ci /* PHY_GBIT_FEATURES */ 43862306a36Sopenharmony_ci .config_init = &vsc8601_config_init, 43962306a36Sopenharmony_ci .config_intr = &vsc82xx_config_intr, 44062306a36Sopenharmony_ci .handle_interrupt = &vsc82xx_handle_interrupt, 44162306a36Sopenharmony_ci}, { 44262306a36Sopenharmony_ci .phy_id = PHY_ID_VSC7385, 44362306a36Sopenharmony_ci .name = "Vitesse VSC7385", 44462306a36Sopenharmony_ci .phy_id_mask = 0x000ffff0, 44562306a36Sopenharmony_ci /* PHY_GBIT_FEATURES */ 44662306a36Sopenharmony_ci .config_init = vsc738x_config_init, 44762306a36Sopenharmony_ci .config_aneg = vsc73xx_config_aneg, 44862306a36Sopenharmony_ci .read_page = vsc73xx_read_page, 44962306a36Sopenharmony_ci .write_page = vsc73xx_write_page, 45062306a36Sopenharmony_ci}, { 45162306a36Sopenharmony_ci .phy_id = PHY_ID_VSC7388, 45262306a36Sopenharmony_ci .name = "Vitesse VSC7388", 45362306a36Sopenharmony_ci .phy_id_mask = 0x000ffff0, 45462306a36Sopenharmony_ci /* PHY_GBIT_FEATURES */ 45562306a36Sopenharmony_ci .config_init = vsc738x_config_init, 45662306a36Sopenharmony_ci .config_aneg = vsc73xx_config_aneg, 45762306a36Sopenharmony_ci .read_page = vsc73xx_read_page, 45862306a36Sopenharmony_ci .write_page = vsc73xx_write_page, 45962306a36Sopenharmony_ci}, { 46062306a36Sopenharmony_ci .phy_id = PHY_ID_VSC7395, 46162306a36Sopenharmony_ci .name = "Vitesse VSC7395", 46262306a36Sopenharmony_ci .phy_id_mask = 0x000ffff0, 46362306a36Sopenharmony_ci /* PHY_GBIT_FEATURES */ 46462306a36Sopenharmony_ci .config_init = vsc739x_config_init, 46562306a36Sopenharmony_ci .config_aneg = vsc73xx_config_aneg, 46662306a36Sopenharmony_ci .read_page = vsc73xx_read_page, 46762306a36Sopenharmony_ci .write_page = vsc73xx_write_page, 46862306a36Sopenharmony_ci}, { 46962306a36Sopenharmony_ci .phy_id = PHY_ID_VSC7398, 47062306a36Sopenharmony_ci .name = "Vitesse VSC7398", 47162306a36Sopenharmony_ci .phy_id_mask = 0x000ffff0, 47262306a36Sopenharmony_ci /* PHY_GBIT_FEATURES */ 47362306a36Sopenharmony_ci .config_init = vsc739x_config_init, 47462306a36Sopenharmony_ci .config_aneg = vsc73xx_config_aneg, 47562306a36Sopenharmony_ci .read_page = vsc73xx_read_page, 47662306a36Sopenharmony_ci .write_page = vsc73xx_write_page, 47762306a36Sopenharmony_ci}, { 47862306a36Sopenharmony_ci .phy_id = PHY_ID_VSC8662, 47962306a36Sopenharmony_ci .name = "Vitesse VSC8662", 48062306a36Sopenharmony_ci .phy_id_mask = 0x000ffff0, 48162306a36Sopenharmony_ci /* PHY_GBIT_FEATURES */ 48262306a36Sopenharmony_ci .config_init = &vsc824x_config_init, 48362306a36Sopenharmony_ci .config_aneg = &vsc82x4_config_aneg, 48462306a36Sopenharmony_ci .config_intr = &vsc82xx_config_intr, 48562306a36Sopenharmony_ci .handle_interrupt = &vsc82xx_handle_interrupt, 48662306a36Sopenharmony_ci}, { 48762306a36Sopenharmony_ci /* Vitesse 8221 */ 48862306a36Sopenharmony_ci .phy_id = PHY_ID_VSC8221, 48962306a36Sopenharmony_ci .phy_id_mask = 0x000ffff0, 49062306a36Sopenharmony_ci .name = "Vitesse VSC8221", 49162306a36Sopenharmony_ci /* PHY_GBIT_FEATURES */ 49262306a36Sopenharmony_ci .config_init = &vsc8221_config_init, 49362306a36Sopenharmony_ci .config_intr = &vsc82xx_config_intr, 49462306a36Sopenharmony_ci .handle_interrupt = &vsc82xx_handle_interrupt, 49562306a36Sopenharmony_ci}, { 49662306a36Sopenharmony_ci /* Vitesse 8211 */ 49762306a36Sopenharmony_ci .phy_id = PHY_ID_VSC8211, 49862306a36Sopenharmony_ci .phy_id_mask = 0x000ffff0, 49962306a36Sopenharmony_ci .name = "Vitesse VSC8211", 50062306a36Sopenharmony_ci /* PHY_GBIT_FEATURES */ 50162306a36Sopenharmony_ci .config_init = &vsc8221_config_init, 50262306a36Sopenharmony_ci .config_intr = &vsc82xx_config_intr, 50362306a36Sopenharmony_ci .handle_interrupt = &vsc82xx_handle_interrupt, 50462306a36Sopenharmony_ci} }; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cimodule_phy_driver(vsc82xx_driver); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused vitesse_tbl[] = { 50962306a36Sopenharmony_ci { PHY_ID_VSC8234, 0x000ffff0 }, 51062306a36Sopenharmony_ci { PHY_ID_VSC8244, 0x000fffc0 }, 51162306a36Sopenharmony_ci { PHY_ID_VSC8572, 0x000ffff0 }, 51262306a36Sopenharmony_ci { PHY_ID_VSC7385, 0x000ffff0 }, 51362306a36Sopenharmony_ci { PHY_ID_VSC7388, 0x000ffff0 }, 51462306a36Sopenharmony_ci { PHY_ID_VSC7395, 0x000ffff0 }, 51562306a36Sopenharmony_ci { PHY_ID_VSC7398, 0x000ffff0 }, 51662306a36Sopenharmony_ci { PHY_ID_VSC8662, 0x000ffff0 }, 51762306a36Sopenharmony_ci { PHY_ID_VSC8221, 0x000ffff0 }, 51862306a36Sopenharmony_ci { PHY_ID_VSC8211, 0x000ffff0 }, 51962306a36Sopenharmony_ci { } 52062306a36Sopenharmony_ci}; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, vitesse_tbl); 523