2 * Driver for Aquantia PHY
4 * Author: Shaohui Xie <Shaohui.Xie@freescale.com>
6 * Copyright 2015 Freescale Semiconductor, Inc.
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.
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>
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
28 #define PHY_AQUANTIA_FEATURES (SUPPORTED_10000baseT_Full | \
29 SUPPORTED_1000baseT_Full | \
30 SUPPORTED_100baseT_Full | \
33 static int aquantia_config_aneg(struct phy_device
*phydev
)
35 phydev
->supported
= PHY_AQUANTIA_FEATURES
;
36 phydev
->advertising
= phydev
->supported
;
41 static int aquantia_aneg_done(struct phy_device
*phydev
)
45 reg
= phy_read_mmd(phydev
, MDIO_MMD_AN
, MDIO_STAT1
);
46 return (reg
< 0) ? reg
: (reg
& BMSR_ANEGCOMPLETE
);
49 static int aquantia_config_intr(struct phy_device
*phydev
)
53 if (phydev
->interrupts
== PHY_INTERRUPT_ENABLED
) {
54 err
= phy_write_mmd(phydev
, MDIO_MMD_AN
, 0xd401, 1);
58 err
= phy_write_mmd(phydev
, MDIO_MMD_VEND1
, 0xff00, 1);
62 err
= phy_write_mmd(phydev
, MDIO_MMD_VEND1
, 0xff01, 0x1001);
64 err
= phy_write_mmd(phydev
, MDIO_MMD_AN
, 0xd401, 0);
68 err
= phy_write_mmd(phydev
, MDIO_MMD_VEND1
, 0xff00, 0);
72 err
= phy_write_mmd(phydev
, MDIO_MMD_VEND1
, 0xff01, 0);
78 static int aquantia_ack_interrupt(struct phy_device
*phydev
)
82 reg
= phy_read_mmd(phydev
, MDIO_MMD_AN
, 0xcc01);
83 return (reg
< 0) ? reg
: 0;
86 static int aquantia_read_status(struct phy_device
*phydev
)
90 reg
= phy_read_mmd(phydev
, MDIO_MMD_AN
, MDIO_STAT1
);
91 reg
= phy_read_mmd(phydev
, MDIO_MMD_AN
, MDIO_STAT1
);
92 if (reg
& MDIO_STAT1_LSTATUS
)
97 reg
= phy_read_mmd(phydev
, MDIO_MMD_AN
, 0xc800);
99 reg
= phy_read_mmd(phydev
, MDIO_MMD_AN
, 0xc800);
103 phydev
->speed
= SPEED_2500
;
106 phydev
->speed
= SPEED_1000
;
109 phydev
->speed
= SPEED_100
;
113 phydev
->speed
= SPEED_10000
;
116 phydev
->duplex
= DUPLEX_FULL
;
121 static struct phy_driver aquantia_driver
[] = {
123 .phy_id
= PHY_ID_AQ1202
,
124 .phy_id_mask
= 0xfffffff0,
125 .name
= "Aquantia AQ1202",
126 .features
= PHY_AQUANTIA_FEATURES
,
127 .flags
= PHY_HAS_INTERRUPT
,
128 .aneg_done
= aquantia_aneg_done
,
129 .config_aneg
= aquantia_config_aneg
,
130 .config_intr
= aquantia_config_intr
,
131 .ack_interrupt
= aquantia_ack_interrupt
,
132 .read_status
= aquantia_read_status
,
135 .phy_id
= PHY_ID_AQ2104
,
136 .phy_id_mask
= 0xfffffff0,
137 .name
= "Aquantia AQ2104",
138 .features
= PHY_AQUANTIA_FEATURES
,
139 .flags
= PHY_HAS_INTERRUPT
,
140 .aneg_done
= aquantia_aneg_done
,
141 .config_aneg
= aquantia_config_aneg
,
142 .config_intr
= aquantia_config_intr
,
143 .ack_interrupt
= aquantia_ack_interrupt
,
144 .read_status
= aquantia_read_status
,
147 .phy_id
= PHY_ID_AQR105
,
148 .phy_id_mask
= 0xfffffff0,
149 .name
= "Aquantia AQR105",
150 .features
= PHY_AQUANTIA_FEATURES
,
151 .flags
= PHY_HAS_INTERRUPT
,
152 .aneg_done
= aquantia_aneg_done
,
153 .config_aneg
= aquantia_config_aneg
,
154 .config_intr
= aquantia_config_intr
,
155 .ack_interrupt
= aquantia_ack_interrupt
,
156 .read_status
= aquantia_read_status
,
159 .phy_id
= PHY_ID_AQR106
,
160 .phy_id_mask
= 0xfffffff0,
161 .name
= "Aquantia AQR106",
162 .features
= PHY_AQUANTIA_FEATURES
,
163 .flags
= PHY_HAS_INTERRUPT
,
164 .aneg_done
= aquantia_aneg_done
,
165 .config_aneg
= aquantia_config_aneg
,
166 .config_intr
= aquantia_config_intr
,
167 .ack_interrupt
= aquantia_ack_interrupt
,
168 .read_status
= aquantia_read_status
,
171 .phy_id
= PHY_ID_AQR107
,
172 .phy_id_mask
= 0xfffffff0,
173 .name
= "Aquantia AQR107",
174 .features
= PHY_AQUANTIA_FEATURES
,
175 .flags
= PHY_HAS_INTERRUPT
,
176 .aneg_done
= aquantia_aneg_done
,
177 .config_aneg
= aquantia_config_aneg
,
178 .config_intr
= aquantia_config_intr
,
179 .ack_interrupt
= aquantia_ack_interrupt
,
180 .read_status
= aquantia_read_status
,
183 .phy_id
= PHY_ID_AQR405
,
184 .phy_id_mask
= 0xfffffff0,
185 .name
= "Aquantia AQR405",
186 .features
= PHY_AQUANTIA_FEATURES
,
187 .flags
= PHY_HAS_INTERRUPT
,
188 .aneg_done
= aquantia_aneg_done
,
189 .config_aneg
= aquantia_config_aneg
,
190 .config_intr
= aquantia_config_intr
,
191 .ack_interrupt
= aquantia_ack_interrupt
,
192 .read_status
= aquantia_read_status
,
196 module_phy_driver(aquantia_driver
);
198 static struct mdio_device_id __maybe_unused aquantia_tbl
[] = {
199 { PHY_ID_AQ1202
, 0xfffffff0 },
200 { PHY_ID_AQ2104
, 0xfffffff0 },
201 { PHY_ID_AQR105
, 0xfffffff0 },
202 { PHY_ID_AQR106
, 0xfffffff0 },
203 { PHY_ID_AQR107
, 0xfffffff0 },
204 { PHY_ID_AQR405
, 0xfffffff0 },
208 MODULE_DEVICE_TABLE(mdio
, aquantia_tbl
);
210 MODULE_DESCRIPTION("Aquantia PHY driver");
211 MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>");
212 MODULE_LICENSE("GPL v2");