1 /*
2  * Driver for Aquantia PHY
3  *
4  * Author: Shaohui Xie <Shaohui.Xie@freescale.com>
5  *
6  * Copyright 2015 Freescale Semiconductor, Inc.
7  *
8  * This file is licensed under the terms of the GNU General Public License
9  * version 2.  This program is licensed "as is" without any warranty of any
10  * kind, whether express or implied.
11  */
12 
13 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include <linux/delay.h>
16 #include <linux/mii.h>
17 #include <linux/ethtool.h>
18 #include <linux/phy.h>
19 #include <linux/mdio.h>
20 
21 #define PHY_ID_AQ1202	0x03a1b445
22 #define PHY_ID_AQ2104	0x03a1b460
23 #define PHY_ID_AQR105	0x03a1b4a2
24 #define PHY_ID_AQR106	0x03a1b4d0
25 #define PHY_ID_AQR107	0x03a1b4e0
26 #define PHY_ID_AQR405	0x03a1b4b0
27 
28 #define PHY_AQUANTIA_FEATURES	(SUPPORTED_10000baseT_Full | \
29 				 SUPPORTED_1000baseT_Full | \
30 				 SUPPORTED_100baseT_Full | \
31 				 PHY_DEFAULT_FEATURES)
32 
aquantia_config_aneg(struct phy_device * phydev)33 static int aquantia_config_aneg(struct phy_device *phydev)
34 {
35 	phydev->supported = PHY_AQUANTIA_FEATURES;
36 	phydev->advertising = phydev->supported;
37 
38 	return 0;
39 }
40 
aquantia_config_intr(struct phy_device * phydev)41 static int aquantia_config_intr(struct phy_device *phydev)
42 {
43 	int err;
44 
45 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
46 		err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 1);
47 		if (err < 0)
48 			return err;
49 
50 		err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 1);
51 		if (err < 0)
52 			return err;
53 
54 		err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0x1001);
55 	} else {
56 		err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 0);
57 		if (err < 0)
58 			return err;
59 
60 		err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 0);
61 		if (err < 0)
62 			return err;
63 
64 		err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0);
65 	}
66 
67 	return err;
68 }
69 
aquantia_ack_interrupt(struct phy_device * phydev)70 static int aquantia_ack_interrupt(struct phy_device *phydev)
71 {
72 	int reg;
73 
74 	reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xcc01);
75 	return (reg < 0) ? reg : 0;
76 }
77 
aquantia_read_status(struct phy_device * phydev)78 static int aquantia_read_status(struct phy_device *phydev)
79 {
80 	int reg;
81 
82 	reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
83 	reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
84 	if (reg & MDIO_STAT1_LSTATUS)
85 		phydev->link = 1;
86 	else
87 		phydev->link = 0;
88 
89 	reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800);
90 	mdelay(10);
91 	reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800);
92 
93 	switch (reg) {
94 	case 0x9:
95 		phydev->speed = SPEED_2500;
96 		break;
97 	case 0x5:
98 		phydev->speed = SPEED_1000;
99 		break;
100 	case 0x3:
101 		phydev->speed = SPEED_100;
102 		break;
103 	case 0x7:
104 	default:
105 		phydev->speed = SPEED_10000;
106 		break;
107 	}
108 	phydev->duplex = DUPLEX_FULL;
109 
110 	return 0;
111 }
112 
113 static struct phy_driver aquantia_driver[] = {
114 {
115 	.phy_id		= PHY_ID_AQ1202,
116 	.phy_id_mask	= 0xfffffff0,
117 	.name		= "Aquantia AQ1202",
118 	.features	= PHY_AQUANTIA_FEATURES,
119 	.flags		= PHY_HAS_INTERRUPT,
120 	.aneg_done	= genphy_c45_aneg_done,
121 	.config_aneg    = aquantia_config_aneg,
122 	.config_intr	= aquantia_config_intr,
123 	.ack_interrupt	= aquantia_ack_interrupt,
124 	.read_status	= aquantia_read_status,
125 },
126 {
127 	.phy_id		= PHY_ID_AQ2104,
128 	.phy_id_mask	= 0xfffffff0,
129 	.name		= "Aquantia AQ2104",
130 	.features	= PHY_AQUANTIA_FEATURES,
131 	.flags		= PHY_HAS_INTERRUPT,
132 	.aneg_done	= genphy_c45_aneg_done,
133 	.config_aneg    = aquantia_config_aneg,
134 	.config_intr	= aquantia_config_intr,
135 	.ack_interrupt	= aquantia_ack_interrupt,
136 	.read_status	= aquantia_read_status,
137 },
138 {
139 	.phy_id		= PHY_ID_AQR105,
140 	.phy_id_mask	= 0xfffffff0,
141 	.name		= "Aquantia AQR105",
142 	.features	= PHY_AQUANTIA_FEATURES,
143 	.flags		= PHY_HAS_INTERRUPT,
144 	.aneg_done	= genphy_c45_aneg_done,
145 	.config_aneg    = aquantia_config_aneg,
146 	.config_intr	= aquantia_config_intr,
147 	.ack_interrupt	= aquantia_ack_interrupt,
148 	.read_status	= aquantia_read_status,
149 },
150 {
151 	.phy_id		= PHY_ID_AQR106,
152 	.phy_id_mask	= 0xfffffff0,
153 	.name		= "Aquantia AQR106",
154 	.features	= PHY_AQUANTIA_FEATURES,
155 	.flags		= PHY_HAS_INTERRUPT,
156 	.aneg_done	= genphy_c45_aneg_done,
157 	.config_aneg    = aquantia_config_aneg,
158 	.config_intr	= aquantia_config_intr,
159 	.ack_interrupt	= aquantia_ack_interrupt,
160 	.read_status	= aquantia_read_status,
161 },
162 {
163 	.phy_id		= PHY_ID_AQR107,
164 	.phy_id_mask	= 0xfffffff0,
165 	.name		= "Aquantia AQR107",
166 	.features	= PHY_AQUANTIA_FEATURES,
167 	.flags		= PHY_HAS_INTERRUPT,
168 	.aneg_done	= genphy_c45_aneg_done,
169 	.config_aneg    = aquantia_config_aneg,
170 	.config_intr	= aquantia_config_intr,
171 	.ack_interrupt	= aquantia_ack_interrupt,
172 	.read_status	= aquantia_read_status,
173 },
174 {
175 	.phy_id		= PHY_ID_AQR405,
176 	.phy_id_mask	= 0xfffffff0,
177 	.name		= "Aquantia AQR405",
178 	.features	= PHY_AQUANTIA_FEATURES,
179 	.flags		= PHY_HAS_INTERRUPT,
180 	.aneg_done	= genphy_c45_aneg_done,
181 	.config_aneg    = aquantia_config_aneg,
182 	.config_intr	= aquantia_config_intr,
183 	.ack_interrupt	= aquantia_ack_interrupt,
184 	.read_status	= aquantia_read_status,
185 },
186 };
187 
188 module_phy_driver(aquantia_driver);
189 
190 static struct mdio_device_id __maybe_unused aquantia_tbl[] = {
191 	{ PHY_ID_AQ1202, 0xfffffff0 },
192 	{ PHY_ID_AQ2104, 0xfffffff0 },
193 	{ PHY_ID_AQR105, 0xfffffff0 },
194 	{ PHY_ID_AQR106, 0xfffffff0 },
195 	{ PHY_ID_AQR107, 0xfffffff0 },
196 	{ PHY_ID_AQR405, 0xfffffff0 },
197 	{ }
198 };
199 
200 MODULE_DEVICE_TABLE(mdio, aquantia_tbl);
201 
202 MODULE_DESCRIPTION("Aquantia PHY driver");
203 MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>");
204 MODULE_LICENSE("GPL v2");
205