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