xref: /openbmc/linux/drivers/net/phy/microchip.c (revision a6d99fcd)
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 
5899408030SBaoyou Xie static 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 */
81a6d99fcdSRussell King 	priv->chip_id = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_ID);
82a6d99fcdSRussell King 	priv->chip_rev = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_REV);
83792aec47SWoojung.Huh@microchip.com 
84792aec47SWoojung.Huh@microchip.com 	phydev->priv = priv;
85792aec47SWoojung.Huh@microchip.com 
86792aec47SWoojung.Huh@microchip.com 	return 0;
87792aec47SWoojung.Huh@microchip.com }
88792aec47SWoojung.Huh@microchip.com 
89792aec47SWoojung.Huh@microchip.com static void lan88xx_remove(struct phy_device *phydev)
90792aec47SWoojung.Huh@microchip.com {
91e5a03bfdSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
92792aec47SWoojung.Huh@microchip.com 	struct lan88xx_priv *priv = phydev->priv;
93792aec47SWoojung.Huh@microchip.com 
94792aec47SWoojung.Huh@microchip.com 	if (priv)
95792aec47SWoojung.Huh@microchip.com 		devm_kfree(dev, priv);
96792aec47SWoojung.Huh@microchip.com }
97792aec47SWoojung.Huh@microchip.com 
98792aec47SWoojung.Huh@microchip.com static int lan88xx_set_wol(struct phy_device *phydev,
99792aec47SWoojung.Huh@microchip.com 			   struct ethtool_wolinfo *wol)
100792aec47SWoojung.Huh@microchip.com {
101792aec47SWoojung.Huh@microchip.com 	struct lan88xx_priv *priv = phydev->priv;
102792aec47SWoojung.Huh@microchip.com 
103792aec47SWoojung.Huh@microchip.com 	priv->wolopts = wol->wolopts;
104792aec47SWoojung.Huh@microchip.com 
105792aec47SWoojung.Huh@microchip.com 	return 0;
106792aec47SWoojung.Huh@microchip.com }
107792aec47SWoojung.Huh@microchip.com 
108f6e3ef3eSWoojung Huh static void lan88xx_set_mdix(struct phy_device *phydev)
109f6e3ef3eSWoojung Huh {
110f6e3ef3eSWoojung Huh 	int buf;
111f6e3ef3eSWoojung Huh 	int val;
112f6e3ef3eSWoojung Huh 
1134e26c5c3SRaju Lakkaraju 	switch (phydev->mdix_ctrl) {
114f6e3ef3eSWoojung Huh 	case ETH_TP_MDI:
115f6e3ef3eSWoojung Huh 		val = LAN88XX_EXT_MODE_CTRL_MDI_;
116f6e3ef3eSWoojung Huh 		break;
117f6e3ef3eSWoojung Huh 	case ETH_TP_MDI_X:
118f6e3ef3eSWoojung Huh 		val = LAN88XX_EXT_MODE_CTRL_MDI_X_;
119f6e3ef3eSWoojung Huh 		break;
120f6e3ef3eSWoojung Huh 	case ETH_TP_MDI_AUTO:
121f6e3ef3eSWoojung Huh 		val = LAN88XX_EXT_MODE_CTRL_AUTO_MDIX_;
122f6e3ef3eSWoojung Huh 		break;
123f6e3ef3eSWoojung Huh 	default:
124f6e3ef3eSWoojung Huh 		return;
125f6e3ef3eSWoojung Huh 	}
126f6e3ef3eSWoojung Huh 
127f6e3ef3eSWoojung Huh 	phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_1);
128f6e3ef3eSWoojung Huh 	buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL);
129f6e3ef3eSWoojung Huh 	buf &= ~LAN88XX_EXT_MODE_CTRL_MDIX_MASK_;
130f6e3ef3eSWoojung Huh 	buf |= val;
131f6e3ef3eSWoojung Huh 	phy_write(phydev, LAN88XX_EXT_MODE_CTRL, buf);
132f6e3ef3eSWoojung Huh 	phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_0);
133f6e3ef3eSWoojung Huh }
134f6e3ef3eSWoojung Huh 
135f6e3ef3eSWoojung Huh static int lan88xx_config_aneg(struct phy_device *phydev)
136f6e3ef3eSWoojung Huh {
137f6e3ef3eSWoojung Huh 	lan88xx_set_mdix(phydev);
138f6e3ef3eSWoojung Huh 
139f6e3ef3eSWoojung Huh 	return genphy_config_aneg(phydev);
140f6e3ef3eSWoojung Huh }
141f6e3ef3eSWoojung Huh 
142792aec47SWoojung.Huh@microchip.com static struct phy_driver microchip_phy_driver[] = {
143792aec47SWoojung.Huh@microchip.com {
144792aec47SWoojung.Huh@microchip.com 	.phy_id		= 0x0007c130,
145792aec47SWoojung.Huh@microchip.com 	.phy_id_mask	= 0xfffffff0,
146792aec47SWoojung.Huh@microchip.com 	.name		= "Microchip LAN88xx",
147792aec47SWoojung.Huh@microchip.com 
148529ed127STimur Tabi 	.features	= PHY_GBIT_FEATURES,
149792aec47SWoojung.Huh@microchip.com 	.flags		= PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
150792aec47SWoojung.Huh@microchip.com 
151792aec47SWoojung.Huh@microchip.com 	.probe		= lan88xx_probe,
152792aec47SWoojung.Huh@microchip.com 	.remove		= lan88xx_remove,
153792aec47SWoojung.Huh@microchip.com 
154792aec47SWoojung.Huh@microchip.com 	.config_init	= genphy_config_init,
155f6e3ef3eSWoojung Huh 	.config_aneg	= lan88xx_config_aneg,
156792aec47SWoojung.Huh@microchip.com 	.read_status	= genphy_read_status,
157792aec47SWoojung.Huh@microchip.com 
158792aec47SWoojung.Huh@microchip.com 	.ack_interrupt	= lan88xx_phy_ack_interrupt,
159792aec47SWoojung.Huh@microchip.com 	.config_intr	= lan88xx_phy_config_intr,
160792aec47SWoojung.Huh@microchip.com 
161792aec47SWoojung.Huh@microchip.com 	.suspend	= lan88xx_suspend,
162792aec47SWoojung.Huh@microchip.com 	.resume		= genphy_resume,
163792aec47SWoojung.Huh@microchip.com 	.set_wol	= lan88xx_set_wol,
164792aec47SWoojung.Huh@microchip.com } };
165792aec47SWoojung.Huh@microchip.com 
166792aec47SWoojung.Huh@microchip.com module_phy_driver(microchip_phy_driver);
167792aec47SWoojung.Huh@microchip.com 
168792aec47SWoojung.Huh@microchip.com static struct mdio_device_id __maybe_unused microchip_tbl[] = {
169792aec47SWoojung.Huh@microchip.com 	{ 0x0007c130, 0xfffffff0 },
170792aec47SWoojung.Huh@microchip.com 	{ }
171792aec47SWoojung.Huh@microchip.com };
172792aec47SWoojung.Huh@microchip.com 
173792aec47SWoojung.Huh@microchip.com MODULE_DEVICE_TABLE(mdio, microchip_tbl);
174792aec47SWoojung.Huh@microchip.com 
175792aec47SWoojung.Huh@microchip.com MODULE_AUTHOR(DRIVER_AUTHOR);
176792aec47SWoojung.Huh@microchip.com MODULE_DESCRIPTION(DRIVER_DESC);
177792aec47SWoojung.Huh@microchip.com MODULE_LICENSE("GPL");
178