xref: /openbmc/linux/drivers/net/phy/smsc.c (revision 7e035230)
1 /*
2  * drivers/net/phy/smsc.c
3  *
4  * Driver for SMSC PHYs
5  *
6  * Author: Herbert Valerio Riedel
7  *
8  * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org>
9  *
10  * This program is free software; you can redistribute  it and/or modify it
11  * under  the terms of  the GNU General  Public License as published by the
12  * Free Software Foundation;  either version 2 of the  License, or (at your
13  * option) any later version.
14  *
15  * Support added for SMSC LAN8187 and LAN8700 by steve.glendinning@shawell.net
16  *
17  */
18 
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/mii.h>
22 #include <linux/ethtool.h>
23 #include <linux/phy.h>
24 #include <linux/netdevice.h>
25 #include <linux/smscphy.h>
26 
27 static int smsc_phy_config_intr(struct phy_device *phydev)
28 {
29 	int rc = phy_write (phydev, MII_LAN83C185_IM,
30 			((PHY_INTERRUPT_ENABLED == phydev->interrupts)
31 			? MII_LAN83C185_ISF_INT_PHYLIB_EVENTS
32 			: 0));
33 
34 	return rc < 0 ? rc : 0;
35 }
36 
37 static int smsc_phy_ack_interrupt(struct phy_device *phydev)
38 {
39 	int rc = phy_read (phydev, MII_LAN83C185_ISF);
40 
41 	return rc < 0 ? rc : 0;
42 }
43 
44 static int smsc_phy_config_init(struct phy_device *phydev)
45 {
46 	int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
47 	if (rc < 0)
48 		return rc;
49 
50 	/* Enable energy detect mode for this SMSC Transceivers */
51 	rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
52 		       rc | MII_LAN83C185_EDPWRDOWN);
53 	if (rc < 0)
54 		return rc;
55 
56 	return smsc_phy_ack_interrupt (phydev);
57 }
58 
59 static int lan911x_config_init(struct phy_device *phydev)
60 {
61 	return smsc_phy_ack_interrupt(phydev);
62 }
63 
64 static struct phy_driver smsc_phy_driver[] = {
65 {
66 	.phy_id		= 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */
67 	.phy_id_mask	= 0xfffffff0,
68 	.name		= "SMSC LAN83C185",
69 
70 	.features	= (PHY_BASIC_FEATURES | SUPPORTED_Pause
71 				| SUPPORTED_Asym_Pause),
72 	.flags		= PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
73 
74 	/* basic functions */
75 	.config_aneg	= genphy_config_aneg,
76 	.read_status	= genphy_read_status,
77 	.config_init	= smsc_phy_config_init,
78 
79 	/* IRQ related */
80 	.ack_interrupt	= smsc_phy_ack_interrupt,
81 	.config_intr	= smsc_phy_config_intr,
82 
83 	.suspend	= genphy_suspend,
84 	.resume		= genphy_resume,
85 
86 	.driver		= { .owner = THIS_MODULE, }
87 }, {
88 	.phy_id		= 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */
89 	.phy_id_mask	= 0xfffffff0,
90 	.name		= "SMSC LAN8187",
91 
92 	.features	= (PHY_BASIC_FEATURES | SUPPORTED_Pause
93 				| SUPPORTED_Asym_Pause),
94 	.flags		= PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
95 
96 	/* basic functions */
97 	.config_aneg	= genphy_config_aneg,
98 	.read_status	= genphy_read_status,
99 	.config_init	= smsc_phy_config_init,
100 
101 	/* IRQ related */
102 	.ack_interrupt	= smsc_phy_ack_interrupt,
103 	.config_intr	= smsc_phy_config_intr,
104 
105 	.suspend	= genphy_suspend,
106 	.resume		= genphy_resume,
107 
108 	.driver		= { .owner = THIS_MODULE, }
109 }, {
110 	.phy_id		= 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */
111 	.phy_id_mask	= 0xfffffff0,
112 	.name		= "SMSC LAN8700",
113 
114 	.features	= (PHY_BASIC_FEATURES | SUPPORTED_Pause
115 				| SUPPORTED_Asym_Pause),
116 	.flags		= PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
117 
118 	/* basic functions */
119 	.config_aneg	= genphy_config_aneg,
120 	.read_status	= genphy_read_status,
121 	.config_init	= smsc_phy_config_init,
122 
123 	/* IRQ related */
124 	.ack_interrupt	= smsc_phy_ack_interrupt,
125 	.config_intr	= smsc_phy_config_intr,
126 
127 	.suspend	= genphy_suspend,
128 	.resume		= genphy_resume,
129 
130 	.driver		= { .owner = THIS_MODULE, }
131 }, {
132 	.phy_id		= 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */
133 	.phy_id_mask	= 0xfffffff0,
134 	.name		= "SMSC LAN911x Internal PHY",
135 
136 	.features	= (PHY_BASIC_FEATURES | SUPPORTED_Pause
137 				| SUPPORTED_Asym_Pause),
138 	.flags		= PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
139 
140 	/* basic functions */
141 	.config_aneg	= genphy_config_aneg,
142 	.read_status	= genphy_read_status,
143 	.config_init	= lan911x_config_init,
144 
145 	/* IRQ related */
146 	.ack_interrupt	= smsc_phy_ack_interrupt,
147 	.config_intr	= smsc_phy_config_intr,
148 
149 	.suspend	= genphy_suspend,
150 	.resume		= genphy_resume,
151 
152 	.driver		= { .owner = THIS_MODULE, }
153 }, {
154 	.phy_id		= 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */
155 	.phy_id_mask	= 0xfffffff0,
156 	.name		= "SMSC LAN8710/LAN8720",
157 
158 	.features	= (PHY_BASIC_FEATURES | SUPPORTED_Pause
159 				| SUPPORTED_Asym_Pause),
160 	.flags		= PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
161 
162 	/* basic functions */
163 	.config_aneg	= genphy_config_aneg,
164 	.read_status	= genphy_read_status,
165 	.config_init	= smsc_phy_config_init,
166 
167 	/* IRQ related */
168 	.ack_interrupt	= smsc_phy_ack_interrupt,
169 	.config_intr	= smsc_phy_config_intr,
170 
171 	.suspend	= genphy_suspend,
172 	.resume		= genphy_resume,
173 
174 	.driver		= { .owner = THIS_MODULE, }
175 } };
176 
177 static int __init smsc_init(void)
178 {
179 	return phy_drivers_register(smsc_phy_driver,
180 		ARRAY_SIZE(smsc_phy_driver));
181 }
182 
183 static void __exit smsc_exit(void)
184 {
185 	return phy_drivers_unregister(smsc_phy_driver,
186 		ARRAY_SIZE(smsc_phy_driver));
187 }
188 
189 MODULE_DESCRIPTION("SMSC PHY driver");
190 MODULE_AUTHOR("Herbert Valerio Riedel");
191 MODULE_LICENSE("GPL");
192 
193 module_init(smsc_init);
194 module_exit(smsc_exit);
195 
196 static struct mdio_device_id __maybe_unused smsc_tbl[] = {
197 	{ 0x0007c0a0, 0xfffffff0 },
198 	{ 0x0007c0b0, 0xfffffff0 },
199 	{ 0x0007c0c0, 0xfffffff0 },
200 	{ 0x0007c0d0, 0xfffffff0 },
201 	{ 0x0007c0f0, 0xfffffff0 },
202 	{ }
203 };
204 
205 MODULE_DEVICE_TABLE(mdio, smsc_tbl);
206