18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Vitesse PHYs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Kriston Carson 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/mii.h> 118c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 128c2ecf20Sopenharmony_ci#include <linux/phy.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* Vitesse Extended Page Magic Register(s) */ 158c2ecf20Sopenharmony_ci#define MII_VSC82X4_EXT_PAGE_16E 0x10 168c2ecf20Sopenharmony_ci#define MII_VSC82X4_EXT_PAGE_17E 0x11 178c2ecf20Sopenharmony_ci#define MII_VSC82X4_EXT_PAGE_18E 0x12 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* Vitesse Extended Control Register 1 */ 208c2ecf20Sopenharmony_ci#define MII_VSC8244_EXT_CON1 0x17 218c2ecf20Sopenharmony_ci#define MII_VSC8244_EXTCON1_INIT 0x0000 228c2ecf20Sopenharmony_ci#define MII_VSC8244_EXTCON1_TX_SKEW_MASK 0x0c00 238c2ecf20Sopenharmony_ci#define MII_VSC8244_EXTCON1_RX_SKEW_MASK 0x0300 248c2ecf20Sopenharmony_ci#define MII_VSC8244_EXTCON1_TX_SKEW 0x0800 258c2ecf20Sopenharmony_ci#define MII_VSC8244_EXTCON1_RX_SKEW 0x0200 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* Vitesse Interrupt Mask Register */ 288c2ecf20Sopenharmony_ci#define MII_VSC8244_IMASK 0x19 298c2ecf20Sopenharmony_ci#define MII_VSC8244_IMASK_IEN 0x8000 308c2ecf20Sopenharmony_ci#define MII_VSC8244_IMASK_SPEED 0x4000 318c2ecf20Sopenharmony_ci#define MII_VSC8244_IMASK_LINK 0x2000 328c2ecf20Sopenharmony_ci#define MII_VSC8244_IMASK_DUPLEX 0x1000 338c2ecf20Sopenharmony_ci#define MII_VSC8244_IMASK_MASK 0xf000 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define MII_VSC8221_IMASK_MASK 0xa000 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Vitesse Interrupt Status Register */ 388c2ecf20Sopenharmony_ci#define MII_VSC8244_ISTAT 0x1a 398c2ecf20Sopenharmony_ci#define MII_VSC8244_ISTAT_STATUS 0x8000 408c2ecf20Sopenharmony_ci#define MII_VSC8244_ISTAT_SPEED 0x4000 418c2ecf20Sopenharmony_ci#define MII_VSC8244_ISTAT_LINK 0x2000 428c2ecf20Sopenharmony_ci#define MII_VSC8244_ISTAT_DUPLEX 0x1000 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* Vitesse Auxiliary Control/Status Register */ 458c2ecf20Sopenharmony_ci#define MII_VSC8244_AUX_CONSTAT 0x1c 468c2ecf20Sopenharmony_ci#define MII_VSC8244_AUXCONSTAT_INIT 0x0000 478c2ecf20Sopenharmony_ci#define MII_VSC8244_AUXCONSTAT_DUPLEX 0x0020 488c2ecf20Sopenharmony_ci#define MII_VSC8244_AUXCONSTAT_SPEED 0x0018 498c2ecf20Sopenharmony_ci#define MII_VSC8244_AUXCONSTAT_GBIT 0x0010 508c2ecf20Sopenharmony_ci#define MII_VSC8244_AUXCONSTAT_100 0x0008 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define MII_VSC8221_AUXCONSTAT_INIT 0x0004 /* need to set this bit? */ 538c2ecf20Sopenharmony_ci#define MII_VSC8221_AUXCONSTAT_RESERVED 0x0004 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* Vitesse Extended Page Access Register */ 568c2ecf20Sopenharmony_ci#define MII_VSC82X4_EXT_PAGE_ACCESS 0x1f 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* Vitesse VSC8601 Extended PHY Control Register 1 */ 598c2ecf20Sopenharmony_ci#define MII_VSC8601_EPHY_CTL 0x17 608c2ecf20Sopenharmony_ci#define MII_VSC8601_EPHY_CTL_RGMII_SKEW (1 << 8) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define PHY_ID_VSC8234 0x000fc620 638c2ecf20Sopenharmony_ci#define PHY_ID_VSC8244 0x000fc6c0 648c2ecf20Sopenharmony_ci#define PHY_ID_VSC8572 0x000704d0 658c2ecf20Sopenharmony_ci#define PHY_ID_VSC8601 0x00070420 668c2ecf20Sopenharmony_ci#define PHY_ID_VSC7385 0x00070450 678c2ecf20Sopenharmony_ci#define PHY_ID_VSC7388 0x00070480 688c2ecf20Sopenharmony_ci#define PHY_ID_VSC7395 0x00070550 698c2ecf20Sopenharmony_ci#define PHY_ID_VSC7398 0x00070580 708c2ecf20Sopenharmony_ci#define PHY_ID_VSC8662 0x00070660 718c2ecf20Sopenharmony_ci#define PHY_ID_VSC8221 0x000fc550 728c2ecf20Sopenharmony_ci#define PHY_ID_VSC8211 0x000fc4b0 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Vitesse PHY driver"); 758c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kriston Carson"); 768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int vsc824x_add_skew(struct phy_device *phydev) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci int err; 818c2ecf20Sopenharmony_ci int extcon; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci extcon = phy_read(phydev, MII_VSC8244_EXT_CON1); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (extcon < 0) 868c2ecf20Sopenharmony_ci return extcon; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci extcon &= ~(MII_VSC8244_EXTCON1_TX_SKEW_MASK | 898c2ecf20Sopenharmony_ci MII_VSC8244_EXTCON1_RX_SKEW_MASK); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci extcon |= (MII_VSC8244_EXTCON1_TX_SKEW | 928c2ecf20Sopenharmony_ci MII_VSC8244_EXTCON1_RX_SKEW); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci err = phy_write(phydev, MII_VSC8244_EXT_CON1, extcon); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return err; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int vsc824x_config_init(struct phy_device *phydev) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci int err; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT, 1048c2ecf20Sopenharmony_ci MII_VSC8244_AUXCONSTAT_INIT); 1058c2ecf20Sopenharmony_ci if (err < 0) 1068c2ecf20Sopenharmony_ci return err; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) 1098c2ecf20Sopenharmony_ci err = vsc824x_add_skew(phydev); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return err; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#define VSC73XX_EXT_PAGE_ACCESS 0x1f 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic int vsc73xx_read_page(struct phy_device *phydev) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci return __phy_read(phydev, VSC73XX_EXT_PAGE_ACCESS); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int vsc73xx_write_page(struct phy_device *phydev, int page) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci return __phy_write(phydev, VSC73XX_EXT_PAGE_ACCESS, page); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void vsc73xx_config_init(struct phy_device *phydev) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci /* Receiver init */ 1298c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 1308c2ecf20Sopenharmony_ci phy_modify(phydev, 0x0c, 0x0300, 0x0200); 1318c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x0000); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* Config LEDs 0x61 */ 1348c2ecf20Sopenharmony_ci phy_modify(phydev, MII_TPISTATUS, 0xff00, 0x0061); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int vsc738x_config_init(struct phy_device *phydev) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci u16 rev; 1408c2ecf20Sopenharmony_ci /* This magic sequence appear in the application note 1418c2ecf20Sopenharmony_ci * "VSC7385/7388 PHY Configuration". 1428c2ecf20Sopenharmony_ci * 1438c2ecf20Sopenharmony_ci * Maybe one day we will get to know what it all means. 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 1468c2ecf20Sopenharmony_ci phy_modify(phydev, 0x08, 0x0200, 0x0200); 1478c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x52b5); 1488c2ecf20Sopenharmony_ci phy_write(phydev, 0x10, 0xb68a); 1498c2ecf20Sopenharmony_ci phy_modify(phydev, 0x12, 0xff07, 0x0003); 1508c2ecf20Sopenharmony_ci phy_modify(phydev, 0x11, 0x00ff, 0x00a2); 1518c2ecf20Sopenharmony_ci phy_write(phydev, 0x10, 0x968a); 1528c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 1538c2ecf20Sopenharmony_ci phy_modify(phydev, 0x08, 0x0200, 0x0000); 1548c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x0000); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* Read revision */ 1578c2ecf20Sopenharmony_ci rev = phy_read(phydev, MII_PHYSID2); 1588c2ecf20Sopenharmony_ci rev &= 0x0f; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* Special quirk for revision 0 */ 1618c2ecf20Sopenharmony_ci if (rev == 0) { 1628c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 1638c2ecf20Sopenharmony_ci phy_modify(phydev, 0x08, 0x0200, 0x0200); 1648c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x52b5); 1658c2ecf20Sopenharmony_ci phy_write(phydev, 0x12, 0x0000); 1668c2ecf20Sopenharmony_ci phy_write(phydev, 0x11, 0x0689); 1678c2ecf20Sopenharmony_ci phy_write(phydev, 0x10, 0x8f92); 1688c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x52b5); 1698c2ecf20Sopenharmony_ci phy_write(phydev, 0x12, 0x0000); 1708c2ecf20Sopenharmony_ci phy_write(phydev, 0x11, 0x0e35); 1718c2ecf20Sopenharmony_ci phy_write(phydev, 0x10, 0x9786); 1728c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 1738c2ecf20Sopenharmony_ci phy_modify(phydev, 0x08, 0x0200, 0x0000); 1748c2ecf20Sopenharmony_ci phy_write(phydev, 0x17, 0xff80); 1758c2ecf20Sopenharmony_ci phy_write(phydev, 0x17, 0x0000); 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x0000); 1798c2ecf20Sopenharmony_ci phy_write(phydev, 0x12, 0x0048); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (rev == 0) { 1828c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 1838c2ecf20Sopenharmony_ci phy_write(phydev, 0x14, 0x6600); 1848c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x0000); 1858c2ecf20Sopenharmony_ci phy_write(phydev, 0x18, 0xa24e); 1868c2ecf20Sopenharmony_ci } else { 1878c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 1888c2ecf20Sopenharmony_ci phy_modify(phydev, 0x16, 0x0fc0, 0x0240); 1898c2ecf20Sopenharmony_ci phy_modify(phydev, 0x14, 0x6000, 0x4000); 1908c2ecf20Sopenharmony_ci /* bits 14-15 in extended register 0x14 controls DACG amplitude 1918c2ecf20Sopenharmony_ci * 6 = -8%, 2 is hardware default 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x0001); 1948c2ecf20Sopenharmony_ci phy_modify(phydev, 0x14, 0xe000, 0x6000); 1958c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x0000); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci vsc73xx_config_init(phydev); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int vsc739x_config_init(struct phy_device *phydev) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci /* This magic sequence appears in the VSC7395 SparX-G5e application 2068c2ecf20Sopenharmony_ci * note "VSC7395/VSC7398 PHY Configuration" 2078c2ecf20Sopenharmony_ci * 2088c2ecf20Sopenharmony_ci * Maybe one day we will get to know what it all means. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 2118c2ecf20Sopenharmony_ci phy_modify(phydev, 0x08, 0x0200, 0x0200); 2128c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x52b5); 2138c2ecf20Sopenharmony_ci phy_write(phydev, 0x10, 0xb68a); 2148c2ecf20Sopenharmony_ci phy_modify(phydev, 0x12, 0xff07, 0x0003); 2158c2ecf20Sopenharmony_ci phy_modify(phydev, 0x11, 0x00ff, 0x00a2); 2168c2ecf20Sopenharmony_ci phy_write(phydev, 0x10, 0x968a); 2178c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 2188c2ecf20Sopenharmony_ci phy_modify(phydev, 0x08, 0x0200, 0x0000); 2198c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x0000); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x0000); 2228c2ecf20Sopenharmony_ci phy_write(phydev, 0x12, 0x0048); 2238c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x2a30); 2248c2ecf20Sopenharmony_ci phy_modify(phydev, 0x16, 0x0fc0, 0x0240); 2258c2ecf20Sopenharmony_ci phy_modify(phydev, 0x14, 0x6000, 0x4000); 2268c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x0001); 2278c2ecf20Sopenharmony_ci phy_modify(phydev, 0x14, 0xe000, 0x6000); 2288c2ecf20Sopenharmony_ci phy_write(phydev, 0x1f, 0x0000); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci vsc73xx_config_init(phydev); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int vsc73xx_config_aneg(struct phy_device *phydev) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci /* The VSC73xx switches does not like to be instructed to 2388c2ecf20Sopenharmony_ci * do autonegotiation in any way, it prefers that you just go 2398c2ecf20Sopenharmony_ci * with the power-on/reset defaults. Writing some registers will 2408c2ecf20Sopenharmony_ci * just make autonegotiation permanently fail. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/* This adds a skew for both TX and RX clocks, so the skew should only be 2468c2ecf20Sopenharmony_ci * applied to "rgmii-id" interfaces. It may not work as expected 2478c2ecf20Sopenharmony_ci * on "rgmii-txid", "rgmii-rxid" or "rgmii" interfaces. */ 2488c2ecf20Sopenharmony_cistatic int vsc8601_add_skew(struct phy_device *phydev) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci int ret; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci ret = phy_read(phydev, MII_VSC8601_EPHY_CTL); 2538c2ecf20Sopenharmony_ci if (ret < 0) 2548c2ecf20Sopenharmony_ci return ret; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci ret |= MII_VSC8601_EPHY_CTL_RGMII_SKEW; 2578c2ecf20Sopenharmony_ci return phy_write(phydev, MII_VSC8601_EPHY_CTL, ret); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic int vsc8601_config_init(struct phy_device *phydev) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci int ret = 0; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) 2658c2ecf20Sopenharmony_ci ret = vsc8601_add_skew(phydev); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (ret < 0) 2688c2ecf20Sopenharmony_ci return ret; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic int vsc824x_ack_interrupt(struct phy_device *phydev) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci int err = 0; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* Don't bother to ACK the interrupts if interrupts 2788c2ecf20Sopenharmony_ci * are disabled. The 824x cannot clear the interrupts 2798c2ecf20Sopenharmony_ci * if they are disabled. 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 2828c2ecf20Sopenharmony_ci err = phy_read(phydev, MII_VSC8244_ISTAT); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return (err < 0) ? err : 0; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int vsc82xx_config_intr(struct phy_device *phydev) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci int err; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 2928c2ecf20Sopenharmony_ci err = phy_write(phydev, MII_VSC8244_IMASK, 2938c2ecf20Sopenharmony_ci (phydev->drv->phy_id == PHY_ID_VSC8234 || 2948c2ecf20Sopenharmony_ci phydev->drv->phy_id == PHY_ID_VSC8244 || 2958c2ecf20Sopenharmony_ci phydev->drv->phy_id == PHY_ID_VSC8572 || 2968c2ecf20Sopenharmony_ci phydev->drv->phy_id == PHY_ID_VSC8601) ? 2978c2ecf20Sopenharmony_ci MII_VSC8244_IMASK_MASK : 2988c2ecf20Sopenharmony_ci MII_VSC8221_IMASK_MASK); 2998c2ecf20Sopenharmony_ci else { 3008c2ecf20Sopenharmony_ci /* The Vitesse PHY cannot clear the interrupt 3018c2ecf20Sopenharmony_ci * once it has disabled them, so we clear them first 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_ci err = phy_read(phydev, MII_VSC8244_ISTAT); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (err < 0) 3068c2ecf20Sopenharmony_ci return err; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci err = phy_write(phydev, MII_VSC8244_IMASK, 0); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return err; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int vsc8221_config_init(struct phy_device *phydev) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci int err; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT, 3198c2ecf20Sopenharmony_ci MII_VSC8221_AUXCONSTAT_INIT); 3208c2ecf20Sopenharmony_ci return err; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* Perhaps we should set EXT_CON1 based on the interface? 3238c2ecf20Sopenharmony_ci * Options are 802.3Z SerDes or SGMII 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci/* vsc82x4_config_autocross_enable - Enable auto MDI/MDI-X for forced links 3288c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 3298c2ecf20Sopenharmony_ci * 3308c2ecf20Sopenharmony_ci * Enable auto MDI/MDI-X when in 10/100 forced link speeds by writing 3318c2ecf20Sopenharmony_ci * special values in the VSC8234/VSC8244 extended reserved registers 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_cistatic int vsc82x4_config_autocross_enable(struct phy_device *phydev) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci int ret; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed > SPEED_100) 3388c2ecf20Sopenharmony_ci return 0; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* map extended registers set 0x10 - 0x1e */ 3418c2ecf20Sopenharmony_ci ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x52b5); 3428c2ecf20Sopenharmony_ci if (ret >= 0) 3438c2ecf20Sopenharmony_ci ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_18E, 0x0012); 3448c2ecf20Sopenharmony_ci if (ret >= 0) 3458c2ecf20Sopenharmony_ci ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_17E, 0x2803); 3468c2ecf20Sopenharmony_ci if (ret >= 0) 3478c2ecf20Sopenharmony_ci ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_16E, 0x87fa); 3488c2ecf20Sopenharmony_ci /* map standard registers set 0x10 - 0x1e */ 3498c2ecf20Sopenharmony_ci if (ret >= 0) 3508c2ecf20Sopenharmony_ci ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x0000); 3518c2ecf20Sopenharmony_ci else 3528c2ecf20Sopenharmony_ci phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x0000); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return ret; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/* vsc82x4_config_aneg - restart auto-negotiation or write BMCR 3588c2ecf20Sopenharmony_ci * @phydev: target phy_device struct 3598c2ecf20Sopenharmony_ci * 3608c2ecf20Sopenharmony_ci * Description: If auto-negotiation is enabled, we configure the 3618c2ecf20Sopenharmony_ci * advertising, and then restart auto-negotiation. If it is not 3628c2ecf20Sopenharmony_ci * enabled, then we write the BMCR and also start the auto 3638c2ecf20Sopenharmony_ci * MDI/MDI-X feature 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_cistatic int vsc82x4_config_aneg(struct phy_device *phydev) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci int ret; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* Enable auto MDI/MDI-X when in 10/100 forced link speeds by 3708c2ecf20Sopenharmony_ci * writing special values in the VSC8234 extended reserved registers 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_ci if (phydev->autoneg != AUTONEG_ENABLE && phydev->speed <= SPEED_100) { 3738c2ecf20Sopenharmony_ci ret = genphy_setup_forced(phydev); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (ret < 0) /* error */ 3768c2ecf20Sopenharmony_ci return ret; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return vsc82x4_config_autocross_enable(phydev); 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return genphy_config_aneg(phydev); 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci/* Vitesse 82xx */ 3858c2ecf20Sopenharmony_cistatic struct phy_driver vsc82xx_driver[] = { 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci .phy_id = PHY_ID_VSC8234, 3888c2ecf20Sopenharmony_ci .name = "Vitesse VSC8234", 3898c2ecf20Sopenharmony_ci .phy_id_mask = 0x000ffff0, 3908c2ecf20Sopenharmony_ci /* PHY_GBIT_FEATURES */ 3918c2ecf20Sopenharmony_ci .config_init = &vsc824x_config_init, 3928c2ecf20Sopenharmony_ci .config_aneg = &vsc82x4_config_aneg, 3938c2ecf20Sopenharmony_ci .ack_interrupt = &vsc824x_ack_interrupt, 3948c2ecf20Sopenharmony_ci .config_intr = &vsc82xx_config_intr, 3958c2ecf20Sopenharmony_ci}, { 3968c2ecf20Sopenharmony_ci .phy_id = PHY_ID_VSC8244, 3978c2ecf20Sopenharmony_ci .name = "Vitesse VSC8244", 3988c2ecf20Sopenharmony_ci .phy_id_mask = 0x000fffc0, 3998c2ecf20Sopenharmony_ci /* PHY_GBIT_FEATURES */ 4008c2ecf20Sopenharmony_ci .config_init = &vsc824x_config_init, 4018c2ecf20Sopenharmony_ci .config_aneg = &vsc82x4_config_aneg, 4028c2ecf20Sopenharmony_ci .ack_interrupt = &vsc824x_ack_interrupt, 4038c2ecf20Sopenharmony_ci .config_intr = &vsc82xx_config_intr, 4048c2ecf20Sopenharmony_ci}, { 4058c2ecf20Sopenharmony_ci .phy_id = PHY_ID_VSC8572, 4068c2ecf20Sopenharmony_ci .name = "Vitesse VSC8572", 4078c2ecf20Sopenharmony_ci .phy_id_mask = 0x000ffff0, 4088c2ecf20Sopenharmony_ci /* PHY_GBIT_FEATURES */ 4098c2ecf20Sopenharmony_ci .config_init = &vsc824x_config_init, 4108c2ecf20Sopenharmony_ci .config_aneg = &vsc82x4_config_aneg, 4118c2ecf20Sopenharmony_ci .ack_interrupt = &vsc824x_ack_interrupt, 4128c2ecf20Sopenharmony_ci .config_intr = &vsc82xx_config_intr, 4138c2ecf20Sopenharmony_ci}, { 4148c2ecf20Sopenharmony_ci .phy_id = PHY_ID_VSC8601, 4158c2ecf20Sopenharmony_ci .name = "Vitesse VSC8601", 4168c2ecf20Sopenharmony_ci .phy_id_mask = 0x000ffff0, 4178c2ecf20Sopenharmony_ci /* PHY_GBIT_FEATURES */ 4188c2ecf20Sopenharmony_ci .config_init = &vsc8601_config_init, 4198c2ecf20Sopenharmony_ci .ack_interrupt = &vsc824x_ack_interrupt, 4208c2ecf20Sopenharmony_ci .config_intr = &vsc82xx_config_intr, 4218c2ecf20Sopenharmony_ci}, { 4228c2ecf20Sopenharmony_ci .phy_id = PHY_ID_VSC7385, 4238c2ecf20Sopenharmony_ci .name = "Vitesse VSC7385", 4248c2ecf20Sopenharmony_ci .phy_id_mask = 0x000ffff0, 4258c2ecf20Sopenharmony_ci /* PHY_GBIT_FEATURES */ 4268c2ecf20Sopenharmony_ci .config_init = vsc738x_config_init, 4278c2ecf20Sopenharmony_ci .config_aneg = vsc73xx_config_aneg, 4288c2ecf20Sopenharmony_ci .read_page = vsc73xx_read_page, 4298c2ecf20Sopenharmony_ci .write_page = vsc73xx_write_page, 4308c2ecf20Sopenharmony_ci}, { 4318c2ecf20Sopenharmony_ci .phy_id = PHY_ID_VSC7388, 4328c2ecf20Sopenharmony_ci .name = "Vitesse VSC7388", 4338c2ecf20Sopenharmony_ci .phy_id_mask = 0x000ffff0, 4348c2ecf20Sopenharmony_ci /* PHY_GBIT_FEATURES */ 4358c2ecf20Sopenharmony_ci .config_init = vsc738x_config_init, 4368c2ecf20Sopenharmony_ci .config_aneg = vsc73xx_config_aneg, 4378c2ecf20Sopenharmony_ci .read_page = vsc73xx_read_page, 4388c2ecf20Sopenharmony_ci .write_page = vsc73xx_write_page, 4398c2ecf20Sopenharmony_ci}, { 4408c2ecf20Sopenharmony_ci .phy_id = PHY_ID_VSC7395, 4418c2ecf20Sopenharmony_ci .name = "Vitesse VSC7395", 4428c2ecf20Sopenharmony_ci .phy_id_mask = 0x000ffff0, 4438c2ecf20Sopenharmony_ci /* PHY_GBIT_FEATURES */ 4448c2ecf20Sopenharmony_ci .config_init = vsc739x_config_init, 4458c2ecf20Sopenharmony_ci .config_aneg = vsc73xx_config_aneg, 4468c2ecf20Sopenharmony_ci .read_page = vsc73xx_read_page, 4478c2ecf20Sopenharmony_ci .write_page = vsc73xx_write_page, 4488c2ecf20Sopenharmony_ci}, { 4498c2ecf20Sopenharmony_ci .phy_id = PHY_ID_VSC7398, 4508c2ecf20Sopenharmony_ci .name = "Vitesse VSC7398", 4518c2ecf20Sopenharmony_ci .phy_id_mask = 0x000ffff0, 4528c2ecf20Sopenharmony_ci /* PHY_GBIT_FEATURES */ 4538c2ecf20Sopenharmony_ci .config_init = vsc739x_config_init, 4548c2ecf20Sopenharmony_ci .config_aneg = vsc73xx_config_aneg, 4558c2ecf20Sopenharmony_ci .read_page = vsc73xx_read_page, 4568c2ecf20Sopenharmony_ci .write_page = vsc73xx_write_page, 4578c2ecf20Sopenharmony_ci}, { 4588c2ecf20Sopenharmony_ci .phy_id = PHY_ID_VSC8662, 4598c2ecf20Sopenharmony_ci .name = "Vitesse VSC8662", 4608c2ecf20Sopenharmony_ci .phy_id_mask = 0x000ffff0, 4618c2ecf20Sopenharmony_ci /* PHY_GBIT_FEATURES */ 4628c2ecf20Sopenharmony_ci .config_init = &vsc824x_config_init, 4638c2ecf20Sopenharmony_ci .config_aneg = &vsc82x4_config_aneg, 4648c2ecf20Sopenharmony_ci .ack_interrupt = &vsc824x_ack_interrupt, 4658c2ecf20Sopenharmony_ci .config_intr = &vsc82xx_config_intr, 4668c2ecf20Sopenharmony_ci}, { 4678c2ecf20Sopenharmony_ci /* Vitesse 8221 */ 4688c2ecf20Sopenharmony_ci .phy_id = PHY_ID_VSC8221, 4698c2ecf20Sopenharmony_ci .phy_id_mask = 0x000ffff0, 4708c2ecf20Sopenharmony_ci .name = "Vitesse VSC8221", 4718c2ecf20Sopenharmony_ci /* PHY_GBIT_FEATURES */ 4728c2ecf20Sopenharmony_ci .config_init = &vsc8221_config_init, 4738c2ecf20Sopenharmony_ci .ack_interrupt = &vsc824x_ack_interrupt, 4748c2ecf20Sopenharmony_ci .config_intr = &vsc82xx_config_intr, 4758c2ecf20Sopenharmony_ci}, { 4768c2ecf20Sopenharmony_ci /* Vitesse 8211 */ 4778c2ecf20Sopenharmony_ci .phy_id = PHY_ID_VSC8211, 4788c2ecf20Sopenharmony_ci .phy_id_mask = 0x000ffff0, 4798c2ecf20Sopenharmony_ci .name = "Vitesse VSC8211", 4808c2ecf20Sopenharmony_ci /* PHY_GBIT_FEATURES */ 4818c2ecf20Sopenharmony_ci .config_init = &vsc8221_config_init, 4828c2ecf20Sopenharmony_ci .ack_interrupt = &vsc824x_ack_interrupt, 4838c2ecf20Sopenharmony_ci .config_intr = &vsc82xx_config_intr, 4848c2ecf20Sopenharmony_ci} }; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cimodule_phy_driver(vsc82xx_driver); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic struct mdio_device_id __maybe_unused vitesse_tbl[] = { 4898c2ecf20Sopenharmony_ci { PHY_ID_VSC8234, 0x000ffff0 }, 4908c2ecf20Sopenharmony_ci { PHY_ID_VSC8244, 0x000fffc0 }, 4918c2ecf20Sopenharmony_ci { PHY_ID_VSC8572, 0x000ffff0 }, 4928c2ecf20Sopenharmony_ci { PHY_ID_VSC7385, 0x000ffff0 }, 4938c2ecf20Sopenharmony_ci { PHY_ID_VSC7388, 0x000ffff0 }, 4948c2ecf20Sopenharmony_ci { PHY_ID_VSC7395, 0x000ffff0 }, 4958c2ecf20Sopenharmony_ci { PHY_ID_VSC7398, 0x000ffff0 }, 4968c2ecf20Sopenharmony_ci { PHY_ID_VSC8662, 0x000ffff0 }, 4978c2ecf20Sopenharmony_ci { PHY_ID_VSC8221, 0x000ffff0 }, 4988c2ecf20Sopenharmony_ci { PHY_ID_VSC8211, 0x000ffff0 }, 4998c2ecf20Sopenharmony_ci { } 5008c2ecf20Sopenharmony_ci}; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, vitesse_tbl); 503