diff options
| author | Mark Brown <[email protected]> | 2015-10-12 18:09:27 +0100 | 
|---|---|---|
| committer | Mark Brown <[email protected]> | 2015-10-12 18:09:27 +0100 | 
| commit | 79828b4fa835f73cdaf4bffa48696abdcbea9d02 (patch) | |
| tree | 5e0fa7156acb75ba603022bc807df8f2fedb97a8 /drivers/net/phy/microchip.c | |
| parent | 721b51fcf91898299d96f4b72cb9434cda29dce6 (diff) | |
| parent | 8c1a9d6323abf0fb1e5dad96cf3f1c783505ea5a (diff) | |
Merge remote-tracking branch 'asoc/fix/rt5645' into asoc-fix-rt5645
Diffstat (limited to 'drivers/net/phy/microchip.c')
| -rw-r--r-- | drivers/net/phy/microchip.c | 148 | 
1 files changed, 148 insertions, 0 deletions
diff --git a/drivers/net/phy/microchip.c b/drivers/net/phy/microchip.c new file mode 100644 index 000000000000..c0a20ebd083b --- /dev/null +++ b/drivers/net/phy/microchip.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2015 Microchip Technology + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mii.h> +#include <linux/ethtool.h> +#include <linux/phy.h> +#include <linux/microchipphy.h> + +#define DRIVER_AUTHOR	"WOOJUNG HUH <[email protected]>" +#define DRIVER_DESC	"Microchip LAN88XX PHY driver" + +struct lan88xx_priv { +	int	chip_id; +	int	chip_rev; +	__u32	wolopts; +}; + +static int lan88xx_phy_config_intr(struct phy_device *phydev) +{ +	int rc; + +	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { +		/* unmask all source and clear them before enable */ +		rc = phy_write(phydev, LAN88XX_INT_MASK, 0x7FFF); +		rc = phy_read(phydev, LAN88XX_INT_STS); +		rc = phy_write(phydev, LAN88XX_INT_MASK, +			       LAN88XX_INT_MASK_MDINTPIN_EN_ | +			       LAN88XX_INT_MASK_LINK_CHANGE_); +	} else { +		rc = phy_write(phydev, LAN88XX_INT_MASK, 0); +	} + +	return rc < 0 ? rc : 0; +} + +static int lan88xx_phy_ack_interrupt(struct phy_device *phydev) +{ +	int rc = phy_read(phydev, LAN88XX_INT_STS); + +	return rc < 0 ? rc : 0; +} + +int lan88xx_suspend(struct phy_device *phydev) +{ +	struct lan88xx_priv *priv = phydev->priv; + +	/* do not power down PHY when WOL is enabled */ +	if (!priv->wolopts) +		genphy_suspend(phydev); + +	return 0; +} + +static int lan88xx_probe(struct phy_device *phydev) +{ +	struct device *dev = &phydev->dev; +	struct lan88xx_priv *priv; + +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +	if (!priv) +		return -ENOMEM; + +	priv->wolopts = 0; + +	/* these values can be used to identify internal PHY */ +	priv->chip_id = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_ID, +					      3, phydev->addr); +	priv->chip_rev = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_REV, +					       3, phydev->addr); + +	phydev->priv = priv; + +	return 0; +} + +static void lan88xx_remove(struct phy_device *phydev) +{ +	struct device *dev = &phydev->dev; +	struct lan88xx_priv *priv = phydev->priv; + +	if (priv) +		devm_kfree(dev, priv); +} + +static int lan88xx_set_wol(struct phy_device *phydev, +			   struct ethtool_wolinfo *wol) +{ +	struct lan88xx_priv *priv = phydev->priv; + +	priv->wolopts = wol->wolopts; + +	return 0; +} + +static struct phy_driver microchip_phy_driver[] = { +{ +	.phy_id		= 0x0007c130, +	.phy_id_mask	= 0xfffffff0, +	.name		= "Microchip LAN88xx", + +	.features	= (PHY_GBIT_FEATURES | +			   SUPPORTED_Pause | SUPPORTED_Asym_Pause), +	.flags		= PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, + +	.probe		= lan88xx_probe, +	.remove		= lan88xx_remove, + +	.config_init	= genphy_config_init, +	.config_aneg	= genphy_config_aneg, +	.read_status	= genphy_read_status, + +	.ack_interrupt	= lan88xx_phy_ack_interrupt, +	.config_intr	= lan88xx_phy_config_intr, + +	.suspend	= lan88xx_suspend, +	.resume		= genphy_resume, +	.set_wol	= lan88xx_set_wol, + +	.driver		= { .owner = THIS_MODULE, } +} }; + +module_phy_driver(microchip_phy_driver); + +static struct mdio_device_id __maybe_unused microchip_tbl[] = { +	{ 0x0007c130, 0xfffffff0 }, +	{ } +}; + +MODULE_DEVICE_TABLE(mdio, microchip_tbl); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL");  |