xref: /openbmc/linux/drivers/net/phy/microchip.c (revision e5a03bfd)
1792aec47SWoojung.Huh@microchip.com /*
2792aec47SWoojung.Huh@microchip.com  * Copyright (C) 2015 Microchip Technology
3792aec47SWoojung.Huh@microchip.com  *
4792aec47SWoojung.Huh@microchip.com  * This program is free software; you can redistribute it and/or
5792aec47SWoojung.Huh@microchip.com  * modify it under the terms of the GNU General Public License
6792aec47SWoojung.Huh@microchip.com  * as published by the Free Software Foundation; either version 2
7792aec47SWoojung.Huh@microchip.com  * of the License, or (at your option) any later version.
8792aec47SWoojung.Huh@microchip.com  *
9792aec47SWoojung.Huh@microchip.com  * This program is distributed in the hope that it will be useful,
10792aec47SWoojung.Huh@microchip.com  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11792aec47SWoojung.Huh@microchip.com  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12792aec47SWoojung.Huh@microchip.com  * GNU General Public License for more details.
13792aec47SWoojung.Huh@microchip.com  *
14792aec47SWoojung.Huh@microchip.com  * You should have received a copy of the GNU General Public License
15792aec47SWoojung.Huh@microchip.com  * along with this program; if not, see <http://www.gnu.org/licenses/>.
16792aec47SWoojung.Huh@microchip.com  */
17792aec47SWoojung.Huh@microchip.com #include <linux/kernel.h>
18792aec47SWoojung.Huh@microchip.com #include <linux/module.h>
19792aec47SWoojung.Huh@microchip.com #include <linux/mii.h>
20792aec47SWoojung.Huh@microchip.com #include <linux/ethtool.h>
21792aec47SWoojung.Huh@microchip.com #include <linux/phy.h>
22792aec47SWoojung.Huh@microchip.com #include <linux/microchipphy.h>
23792aec47SWoojung.Huh@microchip.com 
24792aec47SWoojung.Huh@microchip.com #define DRIVER_AUTHOR	"WOOJUNG HUH <woojung.huh@microchip.com>"
25792aec47SWoojung.Huh@microchip.com #define DRIVER_DESC	"Microchip LAN88XX PHY driver"
26792aec47SWoojung.Huh@microchip.com 
27792aec47SWoojung.Huh@microchip.com struct lan88xx_priv {
28792aec47SWoojung.Huh@microchip.com 	int	chip_id;
29792aec47SWoojung.Huh@microchip.com 	int	chip_rev;
30792aec47SWoojung.Huh@microchip.com 	__u32	wolopts;
31792aec47SWoojung.Huh@microchip.com };
32792aec47SWoojung.Huh@microchip.com 
33792aec47SWoojung.Huh@microchip.com static int lan88xx_phy_config_intr(struct phy_device *phydev)
34792aec47SWoojung.Huh@microchip.com {
35792aec47SWoojung.Huh@microchip.com 	int rc;
36792aec47SWoojung.Huh@microchip.com 
37792aec47SWoojung.Huh@microchip.com 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
38792aec47SWoojung.Huh@microchip.com 		/* unmask all source and clear them before enable */
39792aec47SWoojung.Huh@microchip.com 		rc = phy_write(phydev, LAN88XX_INT_MASK, 0x7FFF);
40792aec47SWoojung.Huh@microchip.com 		rc = phy_read(phydev, LAN88XX_INT_STS);
41792aec47SWoojung.Huh@microchip.com 		rc = phy_write(phydev, LAN88XX_INT_MASK,
42792aec47SWoojung.Huh@microchip.com 			       LAN88XX_INT_MASK_MDINTPIN_EN_ |
43792aec47SWoojung.Huh@microchip.com 			       LAN88XX_INT_MASK_LINK_CHANGE_);
44792aec47SWoojung.Huh@microchip.com 	} else {
45792aec47SWoojung.Huh@microchip.com 		rc = phy_write(phydev, LAN88XX_INT_MASK, 0);
46792aec47SWoojung.Huh@microchip.com 	}
47792aec47SWoojung.Huh@microchip.com 
48792aec47SWoojung.Huh@microchip.com 	return rc < 0 ? rc : 0;
49792aec47SWoojung.Huh@microchip.com }
50792aec47SWoojung.Huh@microchip.com 
51792aec47SWoojung.Huh@microchip.com static int lan88xx_phy_ack_interrupt(struct phy_device *phydev)
52792aec47SWoojung.Huh@microchip.com {
53792aec47SWoojung.Huh@microchip.com 	int rc = phy_read(phydev, LAN88XX_INT_STS);
54792aec47SWoojung.Huh@microchip.com 
55792aec47SWoojung.Huh@microchip.com 	return rc < 0 ? rc : 0;
56792aec47SWoojung.Huh@microchip.com }
57792aec47SWoojung.Huh@microchip.com 
58792aec47SWoojung.Huh@microchip.com int lan88xx_suspend(struct phy_device *phydev)
59792aec47SWoojung.Huh@microchip.com {
60792aec47SWoojung.Huh@microchip.com 	struct lan88xx_priv *priv = phydev->priv;
61792aec47SWoojung.Huh@microchip.com 
62792aec47SWoojung.Huh@microchip.com 	/* do not power down PHY when WOL is enabled */
63792aec47SWoojung.Huh@microchip.com 	if (!priv->wolopts)
64792aec47SWoojung.Huh@microchip.com 		genphy_suspend(phydev);
65792aec47SWoojung.Huh@microchip.com 
66792aec47SWoojung.Huh@microchip.com 	return 0;
67792aec47SWoojung.Huh@microchip.com }
68792aec47SWoojung.Huh@microchip.com 
69792aec47SWoojung.Huh@microchip.com static int lan88xx_probe(struct phy_device *phydev)
70792aec47SWoojung.Huh@microchip.com {
71e5a03bfdSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
72792aec47SWoojung.Huh@microchip.com 	struct lan88xx_priv *priv;
73792aec47SWoojung.Huh@microchip.com 
74792aec47SWoojung.Huh@microchip.com 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
75792aec47SWoojung.Huh@microchip.com 	if (!priv)
76792aec47SWoojung.Huh@microchip.com 		return -ENOMEM;
77792aec47SWoojung.Huh@microchip.com 
78792aec47SWoojung.Huh@microchip.com 	priv->wolopts = 0;
79792aec47SWoojung.Huh@microchip.com 
80792aec47SWoojung.Huh@microchip.com 	/* these values can be used to identify internal PHY */
81053e7e16SAndrew Lunn 	priv->chip_id = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_ID, 3);
82792aec47SWoojung.Huh@microchip.com 	priv->chip_rev = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_REV,
83053e7e16SAndrew Lunn 					       3);
84792aec47SWoojung.Huh@microchip.com 
85792aec47SWoojung.Huh@microchip.com 	phydev->priv = priv;
86792aec47SWoojung.Huh@microchip.com 
87792aec47SWoojung.Huh@microchip.com 	return 0;
88792aec47SWoojung.Huh@microchip.com }
89792aec47SWoojung.Huh@microchip.com 
90792aec47SWoojung.Huh@microchip.com static void lan88xx_remove(struct phy_device *phydev)
91792aec47SWoojung.Huh@microchip.com {
92e5a03bfdSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
93792aec47SWoojung.Huh@microchip.com 	struct lan88xx_priv *priv = phydev->priv;
94792aec47SWoojung.Huh@microchip.com 
95792aec47SWoojung.Huh@microchip.com 	if (priv)
96792aec47SWoojung.Huh@microchip.com 		devm_kfree(dev, priv);
97792aec47SWoojung.Huh@microchip.com }
98792aec47SWoojung.Huh@microchip.com 
99792aec47SWoojung.Huh@microchip.com static int lan88xx_set_wol(struct phy_device *phydev,
100792aec47SWoojung.Huh@microchip.com 			   struct ethtool_wolinfo *wol)
101792aec47SWoojung.Huh@microchip.com {
102792aec47SWoojung.Huh@microchip.com 	struct lan88xx_priv *priv = phydev->priv;
103792aec47SWoojung.Huh@microchip.com 
104792aec47SWoojung.Huh@microchip.com 	priv->wolopts = wol->wolopts;
105792aec47SWoojung.Huh@microchip.com 
106792aec47SWoojung.Huh@microchip.com 	return 0;
107792aec47SWoojung.Huh@microchip.com }
108792aec47SWoojung.Huh@microchip.com 
109792aec47SWoojung.Huh@microchip.com static struct phy_driver microchip_phy_driver[] = {
110792aec47SWoojung.Huh@microchip.com {
111792aec47SWoojung.Huh@microchip.com 	.phy_id		= 0x0007c130,
112792aec47SWoojung.Huh@microchip.com 	.phy_id_mask	= 0xfffffff0,
113792aec47SWoojung.Huh@microchip.com 	.name		= "Microchip LAN88xx",
114792aec47SWoojung.Huh@microchip.com 
115792aec47SWoojung.Huh@microchip.com 	.features	= (PHY_GBIT_FEATURES |
116792aec47SWoojung.Huh@microchip.com 			   SUPPORTED_Pause | SUPPORTED_Asym_Pause),
117792aec47SWoojung.Huh@microchip.com 	.flags		= PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
118792aec47SWoojung.Huh@microchip.com 
119792aec47SWoojung.Huh@microchip.com 	.probe		= lan88xx_probe,
120792aec47SWoojung.Huh@microchip.com 	.remove		= lan88xx_remove,
121792aec47SWoojung.Huh@microchip.com 
122792aec47SWoojung.Huh@microchip.com 	.config_init	= genphy_config_init,
123792aec47SWoojung.Huh@microchip.com 	.config_aneg	= genphy_config_aneg,
124792aec47SWoojung.Huh@microchip.com 	.read_status	= genphy_read_status,
125792aec47SWoojung.Huh@microchip.com 
126792aec47SWoojung.Huh@microchip.com 	.ack_interrupt	= lan88xx_phy_ack_interrupt,
127792aec47SWoojung.Huh@microchip.com 	.config_intr	= lan88xx_phy_config_intr,
128792aec47SWoojung.Huh@microchip.com 
129792aec47SWoojung.Huh@microchip.com 	.suspend	= lan88xx_suspend,
130792aec47SWoojung.Huh@microchip.com 	.resume		= genphy_resume,
131792aec47SWoojung.Huh@microchip.com 	.set_wol	= lan88xx_set_wol,
132792aec47SWoojung.Huh@microchip.com 
133792aec47SWoojung.Huh@microchip.com 	.driver		= { .owner = THIS_MODULE, }
134792aec47SWoojung.Huh@microchip.com } };
135792aec47SWoojung.Huh@microchip.com 
136792aec47SWoojung.Huh@microchip.com module_phy_driver(microchip_phy_driver);
137792aec47SWoojung.Huh@microchip.com 
138792aec47SWoojung.Huh@microchip.com static struct mdio_device_id __maybe_unused microchip_tbl[] = {
139792aec47SWoojung.Huh@microchip.com 	{ 0x0007c130, 0xfffffff0 },
140792aec47SWoojung.Huh@microchip.com 	{ }
141792aec47SWoojung.Huh@microchip.com };
142792aec47SWoojung.Huh@microchip.com 
143792aec47SWoojung.Huh@microchip.com MODULE_DEVICE_TABLE(mdio, microchip_tbl);
144792aec47SWoojung.Huh@microchip.com 
145792aec47SWoojung.Huh@microchip.com MODULE_AUTHOR(DRIVER_AUTHOR);
146792aec47SWoojung.Huh@microchip.com MODULE_DESCRIPTION(DRIVER_DESC);
147792aec47SWoojung.Huh@microchip.com MODULE_LICENSE("GPL");
148