1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2015 Broadcom Corporation 4 */ 5 6 /* Broadcom Cygnus SoC internal transceivers support. */ 7 #include "bcm-phy-lib.h" 8 #include <linux/brcmphy.h> 9 #include <linux/module.h> 10 #include <linux/netdevice.h> 11 #include <linux/phy.h> 12 13 /* Broadcom Cygnus Phy specific registers */ 14 #define MII_BCM_CYGNUS_AFE_VDAC_ICTRL_0 0x91E5 /* VDAL Control register */ 15 16 static int bcm_cygnus_afe_config(struct phy_device *phydev) 17 { 18 int rc; 19 20 /* ensure smdspclk is enabled */ 21 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 0x0c30); 22 if (rc < 0) 23 return rc; 24 25 /* AFE_VDAC_ICTRL_0 bit 7:4 Iq=1100 for 1g 10bt, normal modes */ 26 rc = bcm_phy_write_misc(phydev, 0x39, 0x01, 0xA7C8); 27 if (rc < 0) 28 return rc; 29 30 /* AFE_HPF_TRIM_OTHERS bit11=1, short cascode enable for all modes*/ 31 rc = bcm_phy_write_misc(phydev, 0x3A, 0x00, 0x0803); 32 if (rc < 0) 33 return rc; 34 35 /* AFE_TX_CONFIG_1 bit 7:4 Iq=1100 for test modes */ 36 rc = bcm_phy_write_misc(phydev, 0x3A, 0x01, 0xA740); 37 if (rc < 0) 38 return rc; 39 40 /* AFE TEMPSEN_OTHERS rcal_HT, rcal_LT 10000 */ 41 rc = bcm_phy_write_misc(phydev, 0x3A, 0x03, 0x8400); 42 if (rc < 0) 43 return rc; 44 45 /* AFE_FUTURE_RSV bit 2:0 rccal <2:0>=100 */ 46 rc = bcm_phy_write_misc(phydev, 0x3B, 0x00, 0x0004); 47 if (rc < 0) 48 return rc; 49 50 /* Adjust bias current trim to overcome digital offSet */ 51 rc = phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x02); 52 if (rc < 0) 53 return rc; 54 55 /* make rcal=100, since rdb default is 000 */ 56 rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB1, 0x10); 57 if (rc < 0) 58 return rc; 59 60 /* CORE_EXPB0, Reset R_CAL/RC_CAL Engine */ 61 rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB0, 0x10); 62 if (rc < 0) 63 return rc; 64 65 /* CORE_EXPB0, Disable Reset R_CAL/RC_CAL Engine */ 66 rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB0, 0x00); 67 68 return 0; 69 } 70 71 static int bcm_cygnus_config_init(struct phy_device *phydev) 72 { 73 int reg, rc; 74 75 reg = phy_read(phydev, MII_BCM54XX_ECR); 76 if (reg < 0) 77 return reg; 78 79 /* Mask interrupts globally. */ 80 reg |= MII_BCM54XX_ECR_IM; 81 rc = phy_write(phydev, MII_BCM54XX_ECR, reg); 82 if (rc) 83 return rc; 84 85 /* Unmask events of interest */ 86 reg = ~(MII_BCM54XX_INT_DUPLEX | 87 MII_BCM54XX_INT_SPEED | 88 MII_BCM54XX_INT_LINK); 89 rc = phy_write(phydev, MII_BCM54XX_IMR, reg); 90 if (rc) 91 return rc; 92 93 /* Apply AFE settings for the PHY */ 94 rc = bcm_cygnus_afe_config(phydev); 95 if (rc) 96 return rc; 97 98 /* Advertise EEE */ 99 rc = bcm_phy_set_eee(phydev, true); 100 if (rc) 101 return rc; 102 103 /* Enable APD */ 104 return bcm_phy_enable_apd(phydev, false); 105 } 106 107 static int bcm_cygnus_resume(struct phy_device *phydev) 108 { 109 int rc; 110 111 genphy_resume(phydev); 112 113 /* Re-initialize the PHY to apply AFE work-arounds and 114 * configurations when coming out of suspend. 115 */ 116 rc = bcm_cygnus_config_init(phydev); 117 if (rc) 118 return rc; 119 120 /* restart auto negotiation with the new settings */ 121 return genphy_config_aneg(phydev); 122 } 123 124 static struct phy_driver bcm_cygnus_phy_driver[] = { 125 { 126 .phy_id = PHY_ID_BCM_CYGNUS, 127 .phy_id_mask = 0xfffffff0, 128 .name = "Broadcom Cygnus PHY", 129 .features = PHY_GBIT_FEATURES, 130 .config_init = bcm_cygnus_config_init, 131 .ack_interrupt = bcm_phy_ack_intr, 132 .config_intr = bcm_phy_config_intr, 133 .suspend = genphy_suspend, 134 .resume = bcm_cygnus_resume, 135 } }; 136 137 static struct mdio_device_id __maybe_unused bcm_cygnus_phy_tbl[] = { 138 { PHY_ID_BCM_CYGNUS, 0xfffffff0, }, 139 { } 140 }; 141 MODULE_DEVICE_TABLE(mdio, bcm_cygnus_phy_tbl); 142 143 module_phy_driver(bcm_cygnus_phy_driver); 144 145 MODULE_DESCRIPTION("Broadcom Cygnus internal PHY driver"); 146 MODULE_LICENSE("GPL v2"); 147 MODULE_AUTHOR("Broadcom Corporation"); 148