Commit | Line | Data |
---|---|---|
bee8259d SX |
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_AQR405 0x03a1b4b0 | |
25 | ||
26 | #define PHY_AQUANTIA_FEATURES (SUPPORTED_10000baseT_Full | \ | |
27 | SUPPORTED_1000baseT_Full | \ | |
28 | SUPPORTED_100baseT_Full | \ | |
29 | PHY_DEFAULT_FEATURES) | |
30 | ||
31 | static int aquantia_config_aneg(struct phy_device *phydev) | |
32 | { | |
33 | phydev->supported = PHY_AQUANTIA_FEATURES; | |
34 | phydev->advertising = phydev->supported; | |
35 | ||
36 | return 0; | |
37 | } | |
38 | ||
39 | static int aquantia_aneg_done(struct phy_device *phydev) | |
40 | { | |
41 | int reg; | |
42 | ||
43 | reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); | |
44 | return (reg < 0) ? reg : (reg & BMSR_ANEGCOMPLETE); | |
45 | } | |
46 | ||
54cf7be9 SX |
47 | static int aquantia_config_intr(struct phy_device *phydev) |
48 | { | |
49 | int err; | |
50 | ||
51 | if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { | |
52 | err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 1); | |
53 | if (err < 0) | |
54 | return err; | |
55 | ||
56 | err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 1); | |
57 | if (err < 0) | |
58 | return err; | |
59 | ||
60 | err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0x1001); | |
61 | } else { | |
62 | err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 0); | |
63 | if (err < 0) | |
64 | return err; | |
65 | ||
66 | err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 0); | |
67 | if (err < 0) | |
68 | return err; | |
69 | ||
70 | err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0); | |
71 | } | |
72 | ||
73 | return err; | |
74 | } | |
75 | ||
76 | static int aquantia_ack_interrupt(struct phy_device *phydev) | |
77 | { | |
78 | int reg; | |
79 | ||
80 | reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xcc01); | |
81 | return (reg < 0) ? reg : 0; | |
82 | } | |
83 | ||
bee8259d SX |
84 | static int aquantia_read_status(struct phy_device *phydev) |
85 | { | |
86 | int reg; | |
87 | ||
88 | reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); | |
89 | reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); | |
90 | if (reg & MDIO_STAT1_LSTATUS) | |
91 | phydev->link = 1; | |
92 | else | |
93 | phydev->link = 0; | |
94 | ||
95 | reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800); | |
96 | mdelay(10); | |
97 | reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800); | |
98 | ||
99 | switch (reg) { | |
100 | case 0x9: | |
101 | phydev->speed = SPEED_2500; | |
102 | break; | |
103 | case 0x5: | |
104 | phydev->speed = SPEED_1000; | |
105 | break; | |
106 | case 0x3: | |
107 | phydev->speed = SPEED_100; | |
108 | break; | |
109 | case 0x7: | |
110 | default: | |
111 | phydev->speed = SPEED_10000; | |
112 | break; | |
113 | } | |
114 | phydev->duplex = DUPLEX_FULL; | |
115 | ||
116 | return 0; | |
117 | } | |
118 | ||
119 | static struct phy_driver aquantia_driver[] = { | |
120 | { | |
121 | .phy_id = PHY_ID_AQ1202, | |
122 | .phy_id_mask = 0xfffffff0, | |
123 | .name = "Aquantia AQ1202", | |
124 | .features = PHY_AQUANTIA_FEATURES, | |
54cf7be9 | 125 | .flags = PHY_HAS_INTERRUPT, |
bee8259d SX |
126 | .aneg_done = aquantia_aneg_done, |
127 | .config_aneg = aquantia_config_aneg, | |
54cf7be9 SX |
128 | .config_intr = aquantia_config_intr, |
129 | .ack_interrupt = aquantia_ack_interrupt, | |
bee8259d | 130 | .read_status = aquantia_read_status, |
bee8259d SX |
131 | }, |
132 | { | |
133 | .phy_id = PHY_ID_AQ2104, | |
134 | .phy_id_mask = 0xfffffff0, | |
135 | .name = "Aquantia AQ2104", | |
136 | .features = PHY_AQUANTIA_FEATURES, | |
54cf7be9 | 137 | .flags = PHY_HAS_INTERRUPT, |
bee8259d SX |
138 | .aneg_done = aquantia_aneg_done, |
139 | .config_aneg = aquantia_config_aneg, | |
54cf7be9 SX |
140 | .config_intr = aquantia_config_intr, |
141 | .ack_interrupt = aquantia_ack_interrupt, | |
bee8259d | 142 | .read_status = aquantia_read_status, |
bee8259d SX |
143 | }, |
144 | { | |
145 | .phy_id = PHY_ID_AQR105, | |
146 | .phy_id_mask = 0xfffffff0, | |
147 | .name = "Aquantia AQR105", | |
148 | .features = PHY_AQUANTIA_FEATURES, | |
54cf7be9 | 149 | .flags = PHY_HAS_INTERRUPT, |
bee8259d SX |
150 | .aneg_done = aquantia_aneg_done, |
151 | .config_aneg = aquantia_config_aneg, | |
54cf7be9 SX |
152 | .config_intr = aquantia_config_intr, |
153 | .ack_interrupt = aquantia_ack_interrupt, | |
bee8259d | 154 | .read_status = aquantia_read_status, |
bee8259d SX |
155 | }, |
156 | { | |
157 | .phy_id = PHY_ID_AQR405, | |
158 | .phy_id_mask = 0xfffffff0, | |
159 | .name = "Aquantia AQR405", | |
160 | .features = PHY_AQUANTIA_FEATURES, | |
54cf7be9 | 161 | .flags = PHY_HAS_INTERRUPT, |
bee8259d SX |
162 | .aneg_done = aquantia_aneg_done, |
163 | .config_aneg = aquantia_config_aneg, | |
54cf7be9 SX |
164 | .config_intr = aquantia_config_intr, |
165 | .ack_interrupt = aquantia_ack_interrupt, | |
bee8259d | 166 | .read_status = aquantia_read_status, |
bee8259d SX |
167 | }, |
168 | }; | |
169 | ||
fb0801dc | 170 | module_phy_driver(aquantia_driver); |
bee8259d SX |
171 | |
172 | static struct mdio_device_id __maybe_unused aquantia_tbl[] = { | |
173 | { PHY_ID_AQ1202, 0xfffffff0 }, | |
174 | { PHY_ID_AQ2104, 0xfffffff0 }, | |
175 | { PHY_ID_AQR105, 0xfffffff0 }, | |
176 | { PHY_ID_AQR405, 0xfffffff0 }, | |
177 | { } | |
178 | }; | |
179 | ||
180 | MODULE_DEVICE_TABLE(mdio, aquantia_tbl); | |
181 | ||
182 | MODULE_DESCRIPTION("Aquantia PHY driver"); | |
183 | MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>"); | |
184 | MODULE_LICENSE("GPL v2"); |