Home | History | Annotate | Download | only in phy
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Broadcom PHY drivers
      4  *
      5  * Copyright 2010-2011 Freescale Semiconductor, Inc.
      6  * author Andy Fleming
      7  */
      8 #include <config.h>
      9 #include <common.h>
     10 #include <phy.h>
     11 
     12 /* Broadcom BCM54xx -- taken from linux sungem_phy */
     13 #define MIIM_BCM54xx_AUXCNTL			0x18
     14 #define MIIM_BCM54xx_AUXCNTL_ENCODE(val) (((val & 0x7) << 12)|(val & 0x7))
     15 #define MIIM_BCM54xx_AUXSTATUS			0x19
     16 #define MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK	0x0700
     17 #define MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT	8
     18 
     19 #define MIIM_BCM54XX_SHD			0x1c
     20 #define MIIM_BCM54XX_SHD_WRITE			0x8000
     21 #define MIIM_BCM54XX_SHD_VAL(x)			((x & 0x1f) << 10)
     22 #define MIIM_BCM54XX_SHD_DATA(x)		((x & 0x3ff) << 0)
     23 #define MIIM_BCM54XX_SHD_WR_ENCODE(val, data)	\
     24 	(MIIM_BCM54XX_SHD_WRITE | MIIM_BCM54XX_SHD_VAL(val) | \
     25 	 MIIM_BCM54XX_SHD_DATA(data))
     26 
     27 #define MIIM_BCM54XX_EXP_DATA		0x15	/* Expansion register data */
     28 #define MIIM_BCM54XX_EXP_SEL		0x17	/* Expansion register select */
     29 #define MIIM_BCM54XX_EXP_SEL_SSD	0x0e00	/* Secondary SerDes select */
     30 #define MIIM_BCM54XX_EXP_SEL_ER		0x0f00	/* Expansion register select */
     31 
     32 #define MIIM_BCM_AUXCNTL_SHDWSEL_MISC	0x0007
     33 #define MIIM_BCM_AUXCNTL_ACTL_SMDSP_EN	0x0800
     34 
     35 #define MIIM_BCM_CHANNEL_WIDTH    0x2000
     36 
     37 static void bcm_phy_write_misc(struct phy_device *phydev,
     38 			       u16 reg, u16 chl, u16 value)
     39 {
     40 	int reg_val;
     41 
     42 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL,
     43 		  MIIM_BCM_AUXCNTL_SHDWSEL_MISC);
     44 
     45 	reg_val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL);
     46 	reg_val |= MIIM_BCM_AUXCNTL_ACTL_SMDSP_EN;
     47 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, reg_val);
     48 
     49 	reg_val = (chl * MIIM_BCM_CHANNEL_WIDTH) | reg;
     50 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL, reg_val);
     51 
     52 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA, value);
     53 }
     54 
     55 /* Broadcom BCM5461S */
     56 static int bcm5461_config(struct phy_device *phydev)
     57 {
     58 	genphy_config_aneg(phydev);
     59 
     60 	phy_reset(phydev);
     61 
     62 	return 0;
     63 }
     64 
     65 static int bcm54xx_parse_status(struct phy_device *phydev)
     66 {
     67 	unsigned int mii_reg;
     68 
     69 	mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXSTATUS);
     70 
     71 	switch ((mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK) >>
     72 			MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT) {
     73 	case 1:
     74 		phydev->duplex = DUPLEX_HALF;
     75 		phydev->speed = SPEED_10;
     76 		break;
     77 	case 2:
     78 		phydev->duplex = DUPLEX_FULL;
     79 		phydev->speed = SPEED_10;
     80 		break;
     81 	case 3:
     82 		phydev->duplex = DUPLEX_HALF;
     83 		phydev->speed = SPEED_100;
     84 		break;
     85 	case 5:
     86 		phydev->duplex = DUPLEX_FULL;
     87 		phydev->speed = SPEED_100;
     88 		break;
     89 	case 6:
     90 		phydev->duplex = DUPLEX_HALF;
     91 		phydev->speed = SPEED_1000;
     92 		break;
     93 	case 7:
     94 		phydev->duplex = DUPLEX_FULL;
     95 		phydev->speed = SPEED_1000;
     96 		break;
     97 	default:
     98 		printf("Auto-neg error, defaulting to 10BT/HD\n");
     99 		phydev->duplex = DUPLEX_HALF;
    100 		phydev->speed = SPEED_10;
    101 		break;
    102 	}
    103 
    104 	return 0;
    105 }
    106 
    107 static int bcm54xx_startup(struct phy_device *phydev)
    108 {
    109 	int ret;
    110 
    111 	/* Read the Status (2x to make sure link is right) */
    112 	ret = genphy_update_link(phydev);
    113 	if (ret)
    114 		return ret;
    115 
    116 	return bcm54xx_parse_status(phydev);
    117 }
    118 
    119 /* Broadcom BCM5482S */
    120 /*
    121  * "Ethernet@Wirespeed" needs to be enabled to achieve link in certain
    122  * circumstances.  eg a gigabit TSEC connected to a gigabit switch with
    123  * a 4-wire ethernet cable.  Both ends advertise gigabit, but can't
    124  * link.  "Ethernet@Wirespeed" reduces advertised speed until link
    125  * can be achieved.
    126  */
    127 static u32 bcm5482_read_wirespeed(struct phy_device *phydev, u32 reg)
    128 {
    129 	return (phy_read(phydev, MDIO_DEVAD_NONE, reg) & 0x8FFF) | 0x8010;
    130 }
    131 
    132 static int bcm5482_config(struct phy_device *phydev)
    133 {
    134 	unsigned int reg;
    135 
    136 	/* reset the PHY */
    137 	reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
    138 	reg |= BMCR_RESET;
    139 	phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, reg);
    140 
    141 	/* Setup read from auxilary control shadow register 7 */
    142 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL,
    143 			MIIM_BCM54xx_AUXCNTL_ENCODE(7));
    144 	/* Read Misc Control register and or in Ethernet@Wirespeed */
    145 	reg = bcm5482_read_wirespeed(phydev, MIIM_BCM54xx_AUXCNTL);
    146 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, reg);
    147 
    148 	/* Initial config/enable of secondary SerDes interface */
    149 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD,
    150 			MIIM_BCM54XX_SHD_WR_ENCODE(0x14, 0xf));
    151 	/* Write intial value to secondary SerDes Contol */
    152 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
    153 			MIIM_BCM54XX_EXP_SEL_SSD | 0);
    154 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA,
    155 			BMCR_ANRESTART);
    156 	/* Enable copper/fiber auto-detect */
    157 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD,
    158 			MIIM_BCM54XX_SHD_WR_ENCODE(0x1e, 0x201));
    159 
    160 	genphy_config_aneg(phydev);
    161 
    162 	return 0;
    163 }
    164 
    165 static int bcm_cygnus_startup(struct phy_device *phydev)
    166 {
    167 	int ret;
    168 
    169 	/* Read the Status (2x to make sure link is right) */
    170 	ret = genphy_update_link(phydev);
    171 	if (ret)
    172 		return ret;
    173 
    174 	return genphy_parse_link(phydev);
    175 }
    176 
    177 static void bcm_cygnus_afe(struct phy_device *phydev)
    178 {
    179 	/* ensures smdspclk is enabled */
    180 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, 0x0c30);
    181 
    182 	/* AFE_VDAC_ICTRL_0 bit 7:4 Iq=1100 for 1g 10bt, normal modes */
    183 	bcm_phy_write_misc(phydev, 0x39, 0x01, 0xA7C8);
    184 
    185 	/* AFE_HPF_TRIM_OTHERS bit11=1, short cascode for all modes*/
    186 	bcm_phy_write_misc(phydev, 0x3A, 0x00, 0x0803);
    187 
    188 	/* AFE_TX_CONFIG_1 bit 7:4 Iq=1100 for test modes */
    189 	bcm_phy_write_misc(phydev, 0x3A, 0x01, 0xA740);
    190 
    191 	/* AFE TEMPSEN_OTHERS rcal_HT, rcal_LT 10000 */
    192 	bcm_phy_write_misc(phydev, 0x3A, 0x03, 0x8400);
    193 
    194 	/* AFE_FUTURE_RSV bit 2:0 rccal <2:0>=100 */
    195 	bcm_phy_write_misc(phydev, 0x3B, 0x00, 0x0004);
    196 
    197 	/* Adjust bias current trim to overcome digital offSet */
    198 	phy_write(phydev, MDIO_DEVAD_NONE, 0x1E, 0x02);
    199 
    200 	/* make rcal=100, since rdb default is 000 */
    201 	phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x00B1);
    202 	phy_write(phydev, MDIO_DEVAD_NONE, 0x15, 0x0010);
    203 
    204 	/* CORE_EXPB0, Reset R_CAL/RC_CAL Engine */
    205 	phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x00B0);
    206 	phy_write(phydev, MDIO_DEVAD_NONE, 0x15, 0x0010);
    207 
    208 	/* CORE_EXPB0, Disable Reset R_CAL/RC_CAL Engine */
    209 	phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x00B0);
    210 	phy_write(phydev, MDIO_DEVAD_NONE, 0x15, 0x0000);
    211 }
    212 
    213 static int bcm_cygnus_config(struct phy_device *phydev)
    214 {
    215 	genphy_config_aneg(phydev);
    216 	phy_reset(phydev);
    217 	/* AFE settings for PHY stability */
    218 	bcm_cygnus_afe(phydev);
    219 	/* Forcing aneg after applying the AFE settings */
    220 	genphy_restart_aneg(phydev);
    221 
    222 	return 0;
    223 }
    224 
    225 /*
    226  * Find out if PHY is in copper or serdes mode by looking at Expansion Reg
    227  * 0x42 - "Operating Mode Status Register"
    228  */
    229 static int bcm5482_is_serdes(struct phy_device *phydev)
    230 {
    231 	u16 val;
    232 	int serdes = 0;
    233 
    234 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
    235 			MIIM_BCM54XX_EXP_SEL_ER | 0x42);
    236 	val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA);
    237 
    238 	switch (val & 0x1f) {
    239 	case 0x0d:	/* RGMII-to-100Base-FX */
    240 	case 0x0e:	/* RGMII-to-SGMII */
    241 	case 0x0f:	/* RGMII-to-SerDes */
    242 	case 0x12:	/* SGMII-to-SerDes */
    243 	case 0x13:	/* SGMII-to-100Base-FX */
    244 	case 0x16:	/* SerDes-to-Serdes */
    245 		serdes = 1;
    246 		break;
    247 	case 0x6:	/* RGMII-to-Copper */
    248 	case 0x14:	/* SGMII-to-Copper */
    249 	case 0x17:	/* SerDes-to-Copper */
    250 		break;
    251 	default:
    252 		printf("ERROR, invalid PHY mode (0x%x\n)", val);
    253 		break;
    254 	}
    255 
    256 	return serdes;
    257 }
    258 
    259 /*
    260  * Determine SerDes link speed and duplex from Expansion reg 0x42 "Operating
    261  * Mode Status Register"
    262  */
    263 static u32 bcm5482_parse_serdes_sr(struct phy_device *phydev)
    264 {
    265 	u16 val;
    266 	int i = 0;
    267 
    268 	/* Wait 1s for link - Clause 37 autonegotiation happens very fast */
    269 	while (1) {
    270 		phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
    271 				MIIM_BCM54XX_EXP_SEL_ER | 0x42);
    272 		val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA);
    273 
    274 		if (val & 0x8000)
    275 			break;
    276 
    277 		if (i++ > 1000) {
    278 			phydev->link = 0;
    279 			return 1;
    280 		}
    281 
    282 		udelay(1000);	/* 1 ms */
    283 	}
    284 
    285 	phydev->link = 1;
    286 	switch ((val >> 13) & 0x3) {
    287 	case (0x00):
    288 		phydev->speed = 10;
    289 		break;
    290 	case (0x01):
    291 		phydev->speed = 100;
    292 		break;
    293 	case (0x02):
    294 		phydev->speed = 1000;
    295 		break;
    296 	}
    297 
    298 	phydev->duplex = (val & 0x1000) == 0x1000;
    299 
    300 	return 0;
    301 }
    302 
    303 /*
    304  * Figure out if BCM5482 is in serdes or copper mode and determine link
    305  * configuration accordingly
    306  */
    307 static int bcm5482_startup(struct phy_device *phydev)
    308 {
    309 	int ret;
    310 
    311 	if (bcm5482_is_serdes(phydev)) {
    312 		bcm5482_parse_serdes_sr(phydev);
    313 		phydev->port = PORT_FIBRE;
    314 		return 0;
    315 	}
    316 
    317 	/* Wait for auto-negotiation to complete or fail */
    318 	ret = genphy_update_link(phydev);
    319 	if (ret)
    320 		return ret;
    321 
    322 	/* Parse BCM54xx copper aux status register */
    323 	return bcm54xx_parse_status(phydev);
    324 }
    325 
    326 static struct phy_driver BCM5461S_driver = {
    327 	.name = "Broadcom BCM5461S",
    328 	.uid = 0x2060c0,
    329 	.mask = 0xfffff0,
    330 	.features = PHY_GBIT_FEATURES,
    331 	.config = &bcm5461_config,
    332 	.startup = &bcm54xx_startup,
    333 	.shutdown = &genphy_shutdown,
    334 };
    335 
    336 static struct phy_driver BCM5464S_driver = {
    337 	.name = "Broadcom BCM5464S",
    338 	.uid = 0x2060b0,
    339 	.mask = 0xfffff0,
    340 	.features = PHY_GBIT_FEATURES,
    341 	.config = &bcm5461_config,
    342 	.startup = &bcm54xx_startup,
    343 	.shutdown = &genphy_shutdown,
    344 };
    345 
    346 static struct phy_driver BCM5482S_driver = {
    347 	.name = "Broadcom BCM5482S",
    348 	.uid = 0x143bcb0,
    349 	.mask = 0xffffff0,
    350 	.features = PHY_GBIT_FEATURES,
    351 	.config = &bcm5482_config,
    352 	.startup = &bcm5482_startup,
    353 	.shutdown = &genphy_shutdown,
    354 };
    355 
    356 static struct phy_driver BCM_CYGNUS_driver = {
    357 	.name = "Broadcom CYGNUS GPHY",
    358 	.uid = 0xae025200,
    359 	.mask = 0xfffff0,
    360 	.features = PHY_GBIT_FEATURES,
    361 	.config = &bcm_cygnus_config,
    362 	.startup = &bcm_cygnus_startup,
    363 	.shutdown = &genphy_shutdown,
    364 };
    365 
    366 int phy_broadcom_init(void)
    367 {
    368 	phy_register(&BCM5482S_driver);
    369 	phy_register(&BCM5464S_driver);
    370 	phy_register(&BCM5461S_driver);
    371 	phy_register(&BCM_CYGNUS_driver);
    372 
    373 	return 0;
    374 }
    375