Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * Copyright (C) 2015 Microchip Technology
  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
  6 * as published by the Free Software Foundation; either version 2
  7 * of the License, or (at your option) any later version.
  8 *
  9 * This program is distributed in the hope that it will be useful,
 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12 * GNU General Public License for more details.
 13 *
 14 * You should have received a copy of the GNU General Public License
 15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 16 */
 17#include <linux/kernel.h>
 18#include <linux/module.h>
 19#include <linux/mii.h>
 20#include <linux/ethtool.h>
 21#include <linux/phy.h>
 22#include <linux/microchipphy.h>
 23
 24#define DRIVER_AUTHOR	"WOOJUNG HUH <woojung.huh@microchip.com>"
 25#define DRIVER_DESC	"Microchip LAN88XX PHY driver"
 26
 27struct lan88xx_priv {
 28	int	chip_id;
 29	int	chip_rev;
 30	__u32	wolopts;
 31};
 32
 33static int lan88xx_phy_config_intr(struct phy_device *phydev)
 34{
 35	int rc;
 36
 37	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
 38		/* unmask all source and clear them before enable */
 39		rc = phy_write(phydev, LAN88XX_INT_MASK, 0x7FFF);
 40		rc = phy_read(phydev, LAN88XX_INT_STS);
 41		rc = phy_write(phydev, LAN88XX_INT_MASK,
 42			       LAN88XX_INT_MASK_MDINTPIN_EN_ |
 43			       LAN88XX_INT_MASK_LINK_CHANGE_);
 44	} else {
 45		rc = phy_write(phydev, LAN88XX_INT_MASK, 0);
 46	}
 47
 48	return rc < 0 ? rc : 0;
 49}
 50
 51static int lan88xx_phy_ack_interrupt(struct phy_device *phydev)
 52{
 53	int rc = phy_read(phydev, LAN88XX_INT_STS);
 54
 55	return rc < 0 ? rc : 0;
 56}
 57
 58int lan88xx_suspend(struct phy_device *phydev)
 59{
 60	struct lan88xx_priv *priv = phydev->priv;
 61
 62	/* do not power down PHY when WOL is enabled */
 63	if (!priv->wolopts)
 64		genphy_suspend(phydev);
 65
 66	return 0;
 67}
 68
 69static int lan88xx_probe(struct phy_device *phydev)
 70{
 71	struct device *dev = &phydev->mdio.dev;
 72	struct lan88xx_priv *priv;
 73
 74	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 75	if (!priv)
 76		return -ENOMEM;
 77
 78	priv->wolopts = 0;
 79
 80	/* these values can be used to identify internal PHY */
 81	priv->chip_id = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_ID, 3);
 82	priv->chip_rev = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_REV,
 83					       3);
 84
 85	phydev->priv = priv;
 86
 87	return 0;
 88}
 89
 90static void lan88xx_remove(struct phy_device *phydev)
 91{
 92	struct device *dev = &phydev->mdio.dev;
 93	struct lan88xx_priv *priv = phydev->priv;
 94
 95	if (priv)
 96		devm_kfree(dev, priv);
 97}
 98
 99static int lan88xx_set_wol(struct phy_device *phydev,
100			   struct ethtool_wolinfo *wol)
101{
102	struct lan88xx_priv *priv = phydev->priv;
103
104	priv->wolopts = wol->wolopts;
105
106	return 0;
107}
108
109static struct phy_driver microchip_phy_driver[] = {
110{
111	.phy_id		= 0x0007c130,
112	.phy_id_mask	= 0xfffffff0,
113	.name		= "Microchip LAN88xx",
114
115	.features	= (PHY_GBIT_FEATURES |
116			   SUPPORTED_Pause | SUPPORTED_Asym_Pause),
117	.flags		= PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
118
119	.probe		= lan88xx_probe,
120	.remove		= lan88xx_remove,
121
122	.config_init	= genphy_config_init,
123	.config_aneg	= genphy_config_aneg,
124	.read_status	= genphy_read_status,
125
126	.ack_interrupt	= lan88xx_phy_ack_interrupt,
127	.config_intr	= lan88xx_phy_config_intr,
128
129	.suspend	= lan88xx_suspend,
130	.resume		= genphy_resume,
131	.set_wol	= lan88xx_set_wol,
132} };
133
134module_phy_driver(microchip_phy_driver);
135
136static struct mdio_device_id __maybe_unused microchip_tbl[] = {
137	{ 0x0007c130, 0xfffffff0 },
138	{ }
139};
140
141MODULE_DEVICE_TABLE(mdio, microchip_tbl);
142
143MODULE_AUTHOR(DRIVER_AUTHOR);
144MODULE_DESCRIPTION(DRIVER_DESC);
145MODULE_LICENSE("GPL");