| /* | 
 |  * Copyright (C) 2015 Broadcom Corporation | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU General Public License as | 
 |  * published by the Free Software Foundation version 2. | 
 |  * | 
 |  * This program is distributed "as is" WITHOUT ANY WARRANTY of any | 
 |  * kind, whether express or implied; without even the implied warranty | 
 |  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
 |  * GNU General Public License for more details. | 
 |  */ | 
 |  | 
 | /* Broadcom Cygnus SoC internal transceivers support. */ | 
 | #include "bcm-phy-lib.h" | 
 | #include <linux/brcmphy.h> | 
 | #include <linux/module.h> | 
 | #include <linux/netdevice.h> | 
 | #include <linux/phy.h> | 
 |  | 
 | /* Broadcom Cygnus Phy specific registers */ | 
 | #define MII_BCM_CYGNUS_AFE_VDAC_ICTRL_0  0x91E5 /* VDAL Control register */ | 
 |  | 
 | static int bcm_cygnus_afe_config(struct phy_device *phydev) | 
 | { | 
 | 	int rc; | 
 |  | 
 | 	/* ensure smdspclk is enabled */ | 
 | 	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 0x0c30); | 
 | 	if (rc < 0) | 
 | 		return rc; | 
 |  | 
 | 	/* AFE_VDAC_ICTRL_0 bit 7:4 Iq=1100 for 1g 10bt, normal modes */ | 
 | 	rc = bcm_phy_write_misc(phydev, 0x39, 0x01, 0xA7C8); | 
 | 	if (rc < 0) | 
 | 		return rc; | 
 |  | 
 | 	/* AFE_HPF_TRIM_OTHERS bit11=1, short cascode enable for all modes*/ | 
 | 	rc = bcm_phy_write_misc(phydev, 0x3A, 0x00, 0x0803); | 
 | 	if (rc < 0) | 
 | 		return rc; | 
 |  | 
 | 	/* AFE_TX_CONFIG_1 bit 7:4 Iq=1100 for test modes */ | 
 | 	rc = bcm_phy_write_misc(phydev, 0x3A, 0x01, 0xA740); | 
 | 	if (rc < 0) | 
 | 		return rc; | 
 |  | 
 | 	/* AFE TEMPSEN_OTHERS rcal_HT, rcal_LT 10000 */ | 
 | 	rc = bcm_phy_write_misc(phydev, 0x3A, 0x03, 0x8400); | 
 | 	if (rc < 0) | 
 | 		return rc; | 
 |  | 
 | 	/* AFE_FUTURE_RSV bit 2:0 rccal <2:0>=100 */ | 
 | 	rc = bcm_phy_write_misc(phydev, 0x3B, 0x00, 0x0004); | 
 | 	if (rc < 0) | 
 | 		return rc; | 
 |  | 
 | 	/* Adjust bias current trim to overcome digital offSet */ | 
 | 	rc = phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x02); | 
 | 	if (rc < 0) | 
 | 		return rc; | 
 |  | 
 | 	/* make rcal=100, since rdb default is 000 */ | 
 | 	rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB1, 0x10); | 
 | 	if (rc < 0) | 
 | 		return rc; | 
 |  | 
 | 	/* CORE_EXPB0, Reset R_CAL/RC_CAL Engine */ | 
 | 	rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB0, 0x10); | 
 | 	if (rc < 0) | 
 | 		return rc; | 
 |  | 
 | 	/* CORE_EXPB0, Disable Reset R_CAL/RC_CAL Engine */ | 
 | 	rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB0, 0x00); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int bcm_cygnus_config_init(struct phy_device *phydev) | 
 | { | 
 | 	int reg, rc; | 
 |  | 
 | 	reg = phy_read(phydev, MII_BCM54XX_ECR); | 
 | 	if (reg < 0) | 
 | 		return reg; | 
 |  | 
 | 	/* Mask interrupts globally. */ | 
 | 	reg |= MII_BCM54XX_ECR_IM; | 
 | 	rc = phy_write(phydev, MII_BCM54XX_ECR, reg); | 
 | 	if (rc) | 
 | 		return rc; | 
 |  | 
 | 	/* Unmask events of interest */ | 
 | 	reg = ~(MII_BCM54XX_INT_DUPLEX | | 
 | 		MII_BCM54XX_INT_SPEED | | 
 | 		MII_BCM54XX_INT_LINK); | 
 | 	rc = phy_write(phydev, MII_BCM54XX_IMR, reg); | 
 | 	if (rc) | 
 | 		return rc; | 
 |  | 
 | 	/* Apply AFE settings for the PHY */ | 
 | 	rc = bcm_cygnus_afe_config(phydev); | 
 | 	if (rc) | 
 | 		return rc; | 
 |  | 
 | 	/* Advertise EEE */ | 
 | 	rc = bcm_phy_set_eee(phydev, true); | 
 | 	if (rc) | 
 | 		return rc; | 
 |  | 
 | 	/* Enable APD */ | 
 | 	return bcm_phy_enable_apd(phydev, false); | 
 | } | 
 |  | 
 | static int bcm_cygnus_resume(struct phy_device *phydev) | 
 | { | 
 | 	int rc; | 
 |  | 
 | 	genphy_resume(phydev); | 
 |  | 
 | 	/* Re-initialize the PHY to apply AFE work-arounds and | 
 | 	 * configurations when coming out of suspend. | 
 | 	 */ | 
 | 	rc = bcm_cygnus_config_init(phydev); | 
 | 	if (rc) | 
 | 		return rc; | 
 |  | 
 | 	/* restart auto negotiation with the new settings */ | 
 | 	return genphy_config_aneg(phydev); | 
 | } | 
 |  | 
 | static struct phy_driver bcm_cygnus_phy_driver[] = { | 
 | { | 
 | 	.phy_id        = PHY_ID_BCM_CYGNUS, | 
 | 	.phy_id_mask   = 0xfffffff0, | 
 | 	.name          = "Broadcom Cygnus PHY", | 
 | 	.features      = PHY_GBIT_FEATURES, | 
 | 	.config_init   = bcm_cygnus_config_init, | 
 | 	.ack_interrupt = bcm_phy_ack_intr, | 
 | 	.config_intr   = bcm_phy_config_intr, | 
 | 	.suspend       = genphy_suspend, | 
 | 	.resume        = bcm_cygnus_resume, | 
 | } }; | 
 |  | 
 | static struct mdio_device_id __maybe_unused bcm_cygnus_phy_tbl[] = { | 
 | 	{ PHY_ID_BCM_CYGNUS, 0xfffffff0, }, | 
 | 	{ } | 
 | }; | 
 | MODULE_DEVICE_TABLE(mdio, bcm_cygnus_phy_tbl); | 
 |  | 
 | module_phy_driver(bcm_cygnus_phy_driver); | 
 |  | 
 | MODULE_DESCRIPTION("Broadcom Cygnus internal PHY driver"); | 
 | MODULE_LICENSE("GPL v2"); | 
 | MODULE_AUTHOR("Broadcom Corporation"); |