| @@ -1,390 +1,390 @@ | | | @@ -1,390 +1,390 @@ |
1 | /* $NetBSD: atphy.c,v 1.5.2.2 2009/05/03 23:45:47 snj Exp $ */ | | 1 | /* $NetBSD: atphy.c,v 1.5.2.2.6.1 2015/11/07 20:37:09 snj Exp $ */ |
2 | /* $OpenBSD: atphy.c,v 1.1 2008/09/25 20:47:16 brad Exp $ */ | | 2 | /* $OpenBSD: atphy.c,v 1.1 2008/09/25 20:47:16 brad Exp $ */ |
3 | | | 3 | |
4 | /*- | | 4 | /*- |
5 | * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org> | | 5 | * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org> |
6 | * All rights reserved. | | 6 | * All rights reserved. |
7 | * | | 7 | * |
8 | * Redistribution and use in source and binary forms, with or without | | 8 | * Redistribution and use in source and binary forms, with or without |
9 | * modification, are permitted provided that the following conditions | | 9 | * modification, are permitted provided that the following conditions |
10 | * are met: | | 10 | * are met: |
11 | * 1. Redistributions of source code must retain the above copyright | | 11 | * 1. Redistributions of source code must retain the above copyright |
12 | * notice unmodified, this list of conditions, and the following | | 12 | * notice unmodified, this list of conditions, and the following |
13 | * disclaimer. | | 13 | * disclaimer. |
14 | * 2. Redistributions in binary form must reproduce the above copyright | | 14 | * 2. Redistributions in binary form must reproduce the above copyright |
15 | * notice, this list of conditions and the following disclaimer in the | | 15 | * notice, this list of conditions and the following disclaimer in the |
16 | * documentation and/or other materials provided with the distribution. | | 16 | * documentation and/or other materials provided with the distribution. |
17 | * | | 17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | | 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | | 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
28 | * SUCH DAMAGE. | | 28 | * SUCH DAMAGE. |
29 | */ | | 29 | */ |
30 | | | 30 | |
31 | /* | | 31 | /* |
32 | * Driver for the Attansic F1 10/100/1000 PHY. | | 32 | * Driver for the Attansic F1 10/100/1000 PHY. |
33 | */ | | 33 | */ |
34 | | | 34 | |
35 | #include <sys/cdefs.h> | | 35 | #include <sys/cdefs.h> |
36 | __KERNEL_RCSID(0, "$NetBSD: atphy.c,v 1.5.2.2 2009/05/03 23:45:47 snj Exp $"); | | 36 | __KERNEL_RCSID(0, "$NetBSD: atphy.c,v 1.5.2.2.6.1 2015/11/07 20:37:09 snj Exp $"); |
37 | | | 37 | |
38 | #include <sys/param.h> | | 38 | #include <sys/param.h> |
39 | #include <sys/systm.h> | | 39 | #include <sys/systm.h> |
40 | #include <sys/kernel.h> | | 40 | #include <sys/kernel.h> |
41 | #include <sys/device.h> | | 41 | #include <sys/device.h> |
42 | #include <sys/socket.h> | | 42 | #include <sys/socket.h> |
43 | | | 43 | |
44 | #include <net/if.h> | | 44 | #include <net/if.h> |
45 | #include <net/if_media.h> | | 45 | #include <net/if_media.h> |
46 | | | 46 | |
47 | #include <dev/mii/mii.h> | | 47 | #include <dev/mii/mii.h> |
48 | #include <dev/mii/miivar.h> | | 48 | #include <dev/mii/miivar.h> |
49 | #include <dev/mii/miidevs.h> | | 49 | #include <dev/mii/miidevs.h> |
50 | | | 50 | |
51 | /* Special Control Register */ | | 51 | /* Special Control Register */ |
52 | #define ATPHY_SCR 0x10 | | 52 | #define ATPHY_SCR 0x10 |
53 | #define ATPHY_SCR_JABBER_DISABLE 0x0001 | | 53 | #define ATPHY_SCR_JABBER_DISABLE 0x0001 |
54 | #define ATPHY_SCR_POLARITY_REVERSAL 0x0002 | | 54 | #define ATPHY_SCR_POLARITY_REVERSAL 0x0002 |
55 | #define ATPHY_SCR_SQE_TEST 0x0004 | | 55 | #define ATPHY_SCR_SQE_TEST 0x0004 |
56 | #define ATPHY_SCR_MAC_PDOWN 0x0008 | | 56 | #define ATPHY_SCR_MAC_PDOWN 0x0008 |
57 | #define ATPHY_SCR_CLK125_DISABLE 0x0010 | | 57 | #define ATPHY_SCR_CLK125_DISABLE 0x0010 |
58 | #define ATPHY_SCR_MDI_MANUAL_MODE 0x0000 | | 58 | #define ATPHY_SCR_MDI_MANUAL_MODE 0x0000 |
59 | #define ATPHY_SCR_MDIX_MANUAL_MODE 0x0020 | | 59 | #define ATPHY_SCR_MDIX_MANUAL_MODE 0x0020 |
60 | #define ATPHY_SCR_AUTO_X_1000T 0x0040 | | 60 | #define ATPHY_SCR_AUTO_X_1000T 0x0040 |
61 | #define ATPHY_SCR_AUTO_X_MODE 0x0060 | | 61 | #define ATPHY_SCR_AUTO_X_MODE 0x0060 |
62 | #define ATPHY_SCR_10BT_EXT_ENABLE 0x0080 | | 62 | #define ATPHY_SCR_10BT_EXT_ENABLE 0x0080 |
63 | #define ATPHY_SCR_MII_5BIT_ENABLE 0x0100 | | 63 | #define ATPHY_SCR_MII_5BIT_ENABLE 0x0100 |
64 | #define ATPHY_SCR_SCRAMBLER_DISABLE 0x0200 | | 64 | #define ATPHY_SCR_SCRAMBLER_DISABLE 0x0200 |
65 | #define ATPHY_SCR_FORCE_LINK_GOOD 0x0400 | | 65 | #define ATPHY_SCR_FORCE_LINK_GOOD 0x0400 |
66 | #define ATPHY_SCR_ASSERT_CRS_ON_TX 0x0800 | | 66 | #define ATPHY_SCR_ASSERT_CRS_ON_TX 0x0800 |
67 | | | 67 | |
68 | /* Special Status Register. */ | | 68 | /* Special Status Register. */ |
69 | #define ATPHY_SSR 0x11 | | 69 | #define ATPHY_SSR 0x11 |
70 | #define ATPHY_SSR_SPD_DPLX_RESOLVED 0x0800 | | 70 | #define ATPHY_SSR_SPD_DPLX_RESOLVED 0x0800 |
71 | #define ATPHY_SSR_DUPLEX 0x2000 | | 71 | #define ATPHY_SSR_DUPLEX 0x2000 |
72 | #define ATPHY_SSR_SPEED_MASK 0xC000 | | 72 | #define ATPHY_SSR_SPEED_MASK 0xC000 |
73 | #define ATPHY_SSR_10MBS 0x0000 | | 73 | #define ATPHY_SSR_10MBS 0x0000 |
74 | #define ATPHY_SSR_100MBS 0x4000 | | 74 | #define ATPHY_SSR_100MBS 0x4000 |
75 | #define ATPHY_SSR_1000MBS 0x8000 | | 75 | #define ATPHY_SSR_1000MBS 0x8000 |
76 | | | 76 | |
77 | static int atphy_match(device_t, cfdata_t, void *); | | 77 | static int atphy_match(device_t, cfdata_t, void *); |
78 | static void atphy_attach(device_t, device_t, void *); | | 78 | static void atphy_attach(device_t, device_t, void *); |
79 | | | 79 | |
80 | static int atphy_service(struct mii_softc *, struct mii_data *, int); | | 80 | static int atphy_service(struct mii_softc *, struct mii_data *, int); |
81 | static void atphy_reset(struct mii_softc *); | | 81 | static void atphy_reset(struct mii_softc *); |
82 | static void atphy_status(struct mii_softc *); | | 82 | static void atphy_status(struct mii_softc *); |
83 | static int atphy_mii_phy_auto(struct mii_softc *); | | 83 | static int atphy_mii_phy_auto(struct mii_softc *); |
84 | | | 84 | |
85 | CFATTACH_DECL_NEW(atphy, sizeof(struct mii_softc), | | 85 | CFATTACH_DECL_NEW(atphy, sizeof(struct mii_softc), |
86 | atphy_match, atphy_attach, mii_phy_detach, mii_phy_activate); | | 86 | atphy_match, atphy_attach, mii_phy_detach, mii_phy_activate); |
87 | | | 87 | |
88 | const struct mii_phy_funcs atphy_funcs = { | | 88 | const struct mii_phy_funcs atphy_funcs = { |
89 | atphy_service, atphy_status, atphy_reset, | | 89 | atphy_service, atphy_status, atphy_reset, |
90 | }; | | 90 | }; |
91 | | | 91 | |
92 | static const struct mii_phydesc etphys[] = { | | 92 | static const struct mii_phydesc etphys[] = { |
93 | { MII_OUI_ATHEROS, MII_MODEL_ATHEROS_F1, | | 93 | { MII_OUI_ATHEROS, MII_MODEL_ATHEROS_F1, |
94 | MII_STR_ATHEROS_F1 }, | | 94 | MII_STR_ATHEROS_F1 }, |
95 | { MII_OUI_ATTANSIC, MII_MODEL_ATTANSIC_L1, | | 95 | { MII_OUI_ATTANSIC, MII_MODEL_ATTANSIC_L1, |
96 | MII_STR_ATTANSIC_L1 }, | | 96 | MII_STR_ATTANSIC_L1 }, |
97 | { 0, 0, | | 97 | { 0, 0, |
98 | NULL }, | | 98 | NULL }, |
99 | }; | | 99 | }; |
100 | | | 100 | |
101 | static int | | 101 | static int |
102 | atphy_match(device_t parent, cfdata_t match, void *aux) | | 102 | atphy_match(device_t parent, cfdata_t match, void *aux) |
103 | { | | 103 | { |
104 | struct mii_attach_args *ma = aux; | | 104 | struct mii_attach_args *ma = aux; |
105 | | | 105 | |
106 | if (mii_phy_match(ma, etphys) != NULL) | | 106 | if (mii_phy_match(ma, etphys) != NULL) |
107 | return 10; | | 107 | return 10; |
108 | | | 108 | |
109 | return 0; | | 109 | return 0; |
110 | } | | 110 | } |
111 | | | 111 | |
112 | void | | 112 | void |
113 | atphy_attach(device_t parent, device_t self, void *aux) | | 113 | atphy_attach(device_t parent, device_t self, void *aux) |
114 | { | | 114 | { |
115 | struct mii_softc *sc = device_private(self); | | 115 | struct mii_softc *sc = device_private(self); |
116 | struct mii_attach_args *ma = aux; | | 116 | struct mii_attach_args *ma = aux; |
117 | struct mii_data *mii = ma->mii_data; | | 117 | struct mii_data *mii = ma->mii_data; |
118 | const struct mii_phydesc *mpd; | | 118 | const struct mii_phydesc *mpd; |
119 | | | 119 | |
120 | mpd = mii_phy_match(ma, etphys); | | 120 | mpd = mii_phy_match(ma, etphys); |
121 | aprint_naive(": Media interface\n"); | | 121 | aprint_naive(": Media interface\n"); |
122 | aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2)); | | 122 | aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2)); |
123 | | | 123 | |
124 | sc->mii_dev = self; | | 124 | sc->mii_dev = self; |
125 | sc->mii_inst = mii->mii_instance; | | 125 | sc->mii_inst = mii->mii_instance; |
126 | sc->mii_phy = ma->mii_phyno; | | 126 | sc->mii_phy = ma->mii_phyno; |
127 | sc->mii_funcs = &atphy_funcs; | | 127 | sc->mii_funcs = &atphy_funcs; |
128 | sc->mii_pdata = mii; | | 128 | sc->mii_pdata = mii; |
129 | sc->mii_flags = ma->mii_flags; | | 129 | sc->mii_flags = ma->mii_flags; |
130 | sc->mii_anegticks = MII_ANEGTICKS_GIGE; | | 130 | sc->mii_anegticks = MII_ANEGTICKS_GIGE; |
131 | | | 131 | |
132 | sc->mii_flags |= MIIF_NOLOOP; | | 132 | sc->mii_flags |= MIIF_NOLOOP; |
133 | | | 133 | |
134 | PHY_RESET(sc); | | 134 | PHY_RESET(sc); |
135 | | | 135 | |
136 | sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; | | 136 | sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; |
137 | if (sc->mii_capabilities & BMSR_EXTSTAT) | | 137 | if (sc->mii_capabilities & BMSR_EXTSTAT) |
138 | sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); | | 138 | sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); |
139 | | | 139 | |
140 | aprint_normal_dev(self, ""); | | 140 | aprint_normal_dev(self, ""); |
141 | mii_phy_add_media(sc); | | 141 | mii_phy_add_media(sc); |
142 | aprint_normal("\n"); | | 142 | aprint_normal("\n"); |
143 | } | | 143 | } |
144 | | | 144 | |
145 | int | | 145 | int |
146 | atphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) | | 146 | atphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) |
147 | { | | 147 | { |
148 | struct ifmedia_entry *ife = mii->mii_media.ifm_cur; | | 148 | struct ifmedia_entry *ife = mii->mii_media.ifm_cur; |
149 | uint16_t anar, bmcr, bmsr; | | 149 | uint16_t anar, bmcr, bmsr; |
150 | | | 150 | |
151 | switch (cmd) { | | 151 | switch (cmd) { |
152 | case MII_POLLSTAT: | | 152 | case MII_POLLSTAT: |
153 | /* | | 153 | /* |
154 | * If we're not polling our PHY instance, just return. | | 154 | * If we're not polling our PHY instance, just return. |
155 | */ | | 155 | */ |
156 | if (IFM_INST(ife->ifm_media) != sc->mii_inst) | | 156 | if (IFM_INST(ife->ifm_media) != sc->mii_inst) |
157 | return 0; | | 157 | return 0; |
158 | break; | | 158 | break; |
159 | | | 159 | |
160 | case MII_MEDIACHG: | | 160 | case MII_MEDIACHG: |
161 | /* | | 161 | /* |
162 | * If the media indicates a different PHY instance, | | 162 | * If the media indicates a different PHY instance, |
163 | * isolate ourselves. | | 163 | * isolate ourselves. |
164 | */ | | 164 | */ |
165 | if (IFM_INST(ife->ifm_media) != sc->mii_inst) { | | 165 | if (IFM_INST(ife->ifm_media) != sc->mii_inst) { |
166 | bmcr = PHY_READ(sc, MII_BMCR); | | 166 | bmcr = PHY_READ(sc, MII_BMCR); |
167 | PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO); | | 167 | PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO); |
168 | return 0; | | 168 | return 0; |
169 | } | | 169 | } |
170 | | | 170 | |
171 | /* | | 171 | /* |
172 | * If the interface is not up, don't do anything. | | 172 | * If the interface is not up, don't do anything. |
173 | */ | | 173 | */ |
174 | if ((mii->mii_ifp->if_flags & IFF_UP) == 0) | | 174 | if ((mii->mii_ifp->if_flags & IFF_UP) == 0) |
175 | break; | | 175 | break; |
176 | | | 176 | |
177 | bmcr = 0; | | 177 | bmcr = 0; |
178 | switch (IFM_SUBTYPE(ife->ifm_media)) { | | 178 | switch (IFM_SUBTYPE(ife->ifm_media)) { |
179 | case IFM_AUTO: | | 179 | case IFM_AUTO: |
180 | case IFM_1000_T: | | 180 | case IFM_1000_T: |
181 | atphy_mii_phy_auto(sc); | | 181 | atphy_mii_phy_auto(sc); |
182 | goto done; | | 182 | goto done; |
183 | case IFM_100_TX: | | 183 | case IFM_100_TX: |
184 | bmcr = BMCR_S100; | | 184 | bmcr = BMCR_S100; |
185 | break; | | 185 | break; |
186 | case IFM_10_T: | | 186 | case IFM_10_T: |
187 | bmcr = BMCR_S10; | | 187 | bmcr = BMCR_S10; |
188 | break; | | 188 | break; |
189 | case IFM_NONE: | | 189 | case IFM_NONE: |
190 | bmcr = PHY_READ(sc, MII_BMCR); | | 190 | bmcr = PHY_READ(sc, MII_BMCR); |
191 | /* | | 191 | /* |
192 | * XXX | | 192 | * XXX |
193 | * Due to an unknown reason powering down PHY resulted | | 193 | * Due to an unknown reason powering down PHY resulted |
194 | * in unexpected results such as inaccessbility of | | 194 | * in unexpected results such as inaccessbility of |
195 | * hardware of freshly rebooted system. Disable | | 195 | * hardware of freshly rebooted system. Disable |
196 | * powering down PHY until I got more information for | | 196 | * powering down PHY until I got more information for |
197 | * Attansic/Atheros PHY hardwares. | | 197 | * Attansic/Atheros PHY hardwares. |
198 | */ | | 198 | */ |
199 | PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO); | | 199 | PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO); |
200 | goto done; | | 200 | goto done; |
201 | default: | | 201 | default: |
202 | return EINVAL; | | 202 | return EINVAL; |
203 | } | | 203 | } |
204 | | | 204 | |
205 | anar = mii_anar(ife->ifm_media); | | 205 | anar = mii_anar(IFM_SUBTYPE(ife->ifm_media)); |
206 | if (((ife->ifm_media & IFM_GMASK) & IFM_FDX) != 0) { | | 206 | if (((ife->ifm_media & IFM_GMASK) & IFM_FDX) != 0) { |
207 | bmcr |= BMCR_FDX; | | 207 | bmcr |= BMCR_FDX; |
208 | /* Enable pause. */ | | 208 | /* Enable pause. */ |
209 | if (sc->mii_flags & MIIF_DOPAUSE) | | 209 | if (sc->mii_flags & MIIF_DOPAUSE) |
210 | anar |= (3 << 10); | | 210 | anar |= (3 << 10); |
211 | } | | 211 | } |
212 | | | 212 | |
213 | if ((sc->mii_extcapabilities & (EXTSR_1000TFDX | | | 213 | if ((sc->mii_extcapabilities & (EXTSR_1000TFDX | |
214 | EXTSR_1000THDX)) != 0) | | 214 | EXTSR_1000THDX)) != 0) |
215 | PHY_WRITE(sc, MII_100T2CR, 0); | | 215 | PHY_WRITE(sc, MII_100T2CR, 0); |
216 | PHY_WRITE(sc, MII_ANAR, anar); | | 216 | PHY_WRITE(sc, MII_ANAR, anar); |
217 | | | 217 | |
218 | /* | | 218 | /* |
219 | * Reset the PHY so all changes take effect. | | 219 | * Reset the PHY so all changes take effect. |
220 | */ | | 220 | */ |
221 | PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_RESET | BMCR_AUTOEN | | | 221 | PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_RESET | BMCR_AUTOEN | |
222 | BMCR_STARTNEG); | | 222 | BMCR_STARTNEG); |
223 | done: | | 223 | done: |
224 | break; | | 224 | break; |
225 | | | 225 | |
226 | case MII_TICK: | | 226 | case MII_TICK: |
227 | /* | | 227 | /* |
228 | * If we're not currently selected, just return. | | 228 | * If we're not currently selected, just return. |
229 | */ | | 229 | */ |
230 | if (IFM_INST(ife->ifm_media) != sc->mii_inst) | | 230 | if (IFM_INST(ife->ifm_media) != sc->mii_inst) |
231 | return 0; | | 231 | return 0; |
232 | | | 232 | |
233 | /* | | 233 | /* |
234 | * Is the interface even up? | | 234 | * Is the interface even up? |
235 | */ | | 235 | */ |
236 | if ((mii->mii_ifp->if_flags & IFF_UP) == 0) | | 236 | if ((mii->mii_ifp->if_flags & IFF_UP) == 0) |
237 | return 0; | | 237 | return 0; |
238 | | | 238 | |
239 | /* | | 239 | /* |
240 | * Only used for autonegotiation. | | 240 | * Only used for autonegotiation. |
241 | */ | | 241 | */ |
242 | if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { | | 242 | if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { |
243 | sc->mii_ticks = 0; | | 243 | sc->mii_ticks = 0; |
244 | break; | | 244 | break; |
245 | } | | 245 | } |
246 | | | 246 | |
247 | /* | | 247 | /* |
248 | * Check for link. | | 248 | * Check for link. |
249 | * Read the status register twice; BMSR_LINK is latch-low. | | 249 | * Read the status register twice; BMSR_LINK is latch-low. |
250 | */ | | 250 | */ |
251 | bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); | | 251 | bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); |
252 | if (bmsr & BMSR_LINK) { | | 252 | if (bmsr & BMSR_LINK) { |
253 | sc->mii_ticks = 0; | | 253 | sc->mii_ticks = 0; |
254 | break; | | 254 | break; |
255 | } | | 255 | } |
256 | | | 256 | |
257 | /* Announce link loss right after it happens. */ | | 257 | /* Announce link loss right after it happens. */ |
258 | if (sc->mii_ticks++ == 0) | | 258 | if (sc->mii_ticks++ == 0) |
259 | break; | | 259 | break; |
260 | | | 260 | |
261 | /* | | 261 | /* |
262 | * Only retry autonegotiation every mii_anegticks seconds. | | 262 | * Only retry autonegotiation every mii_anegticks seconds. |
263 | */ | | 263 | */ |
264 | if (sc->mii_ticks <= sc->mii_anegticks) | | 264 | if (sc->mii_ticks <= sc->mii_anegticks) |
265 | break; | | 265 | break; |
266 | | | 266 | |
267 | sc->mii_ticks = 0; | | 267 | sc->mii_ticks = 0; |
268 | atphy_mii_phy_auto(sc); | | 268 | atphy_mii_phy_auto(sc); |
269 | break; | | 269 | break; |
270 | } | | 270 | } |
271 | | | 271 | |
272 | /* Update the media status. */ | | 272 | /* Update the media status. */ |
273 | mii_phy_status(sc); | | 273 | mii_phy_status(sc); |
274 | | | 274 | |
275 | /* Callback if something changed. */ | | 275 | /* Callback if something changed. */ |
276 | mii_phy_update(sc, cmd); | | 276 | mii_phy_update(sc, cmd); |
277 | return 0; | | 277 | return 0; |
278 | } | | 278 | } |
279 | | | 279 | |
280 | static void | | 280 | static void |
281 | atphy_status(struct mii_softc *sc) | | 281 | atphy_status(struct mii_softc *sc) |
282 | { | | 282 | { |
283 | struct mii_data *mii = sc->mii_pdata; | | 283 | struct mii_data *mii = sc->mii_pdata; |
284 | uint32_t bmsr, bmcr, gsr, ssr; | | 284 | uint32_t bmsr, bmcr, gsr, ssr; |
285 | | | 285 | |
286 | mii->mii_media_status = IFM_AVALID; | | 286 | mii->mii_media_status = IFM_AVALID; |
287 | mii->mii_media_active = IFM_ETHER; | | 287 | mii->mii_media_active = IFM_ETHER; |
288 | | | 288 | |
289 | bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); | | 289 | bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); |
290 | if (bmsr & BMSR_LINK) | | 290 | if (bmsr & BMSR_LINK) |
291 | mii->mii_media_status |= IFM_ACTIVE; | | 291 | mii->mii_media_status |= IFM_ACTIVE; |
292 | | | 292 | |
293 | bmcr = PHY_READ(sc, MII_BMCR); | | 293 | bmcr = PHY_READ(sc, MII_BMCR); |
294 | if (bmcr & BMCR_ISO) { | | 294 | if (bmcr & BMCR_ISO) { |
295 | mii->mii_media_active |= IFM_NONE; | | 295 | mii->mii_media_active |= IFM_NONE; |
296 | mii->mii_media_status = 0; | | 296 | mii->mii_media_status = 0; |
297 | return; | | 297 | return; |
298 | } | | 298 | } |
299 | | | 299 | |
300 | if (bmcr & BMCR_LOOP) | | 300 | if (bmcr & BMCR_LOOP) |
301 | mii->mii_media_active |= IFM_LOOP; | | 301 | mii->mii_media_active |= IFM_LOOP; |
302 | | | 302 | |
303 | ssr = PHY_READ(sc, ATPHY_SSR); | | 303 | ssr = PHY_READ(sc, ATPHY_SSR); |
304 | if (!(ssr & ATPHY_SSR_SPD_DPLX_RESOLVED)) { | | 304 | if (!(ssr & ATPHY_SSR_SPD_DPLX_RESOLVED)) { |
305 | /* Erg, still trying, I guess... */ | | 305 | /* Erg, still trying, I guess... */ |
306 | mii->mii_media_active |= IFM_NONE; | | 306 | mii->mii_media_active |= IFM_NONE; |
307 | return; | | 307 | return; |
308 | } | | 308 | } |
309 | | | 309 | |
310 | switch (ssr & ATPHY_SSR_SPEED_MASK) { | | 310 | switch (ssr & ATPHY_SSR_SPEED_MASK) { |
311 | case ATPHY_SSR_1000MBS: | | 311 | case ATPHY_SSR_1000MBS: |
312 | mii->mii_media_active |= IFM_1000_T; | | 312 | mii->mii_media_active |= IFM_1000_T; |
313 | /* | | 313 | /* |
314 | * atphy(4) has a valid link so reset mii_ticks. | | 314 | * atphy(4) has a valid link so reset mii_ticks. |
315 | * Resetting mii_ticks is needed in order to | | 315 | * Resetting mii_ticks is needed in order to |
316 | * detect link loss after auto-negotiation. | | 316 | * detect link loss after auto-negotiation. |
317 | */ | | 317 | */ |
318 | sc->mii_ticks = 0; | | 318 | sc->mii_ticks = 0; |
319 | break; | | 319 | break; |
320 | case ATPHY_SSR_100MBS: | | 320 | case ATPHY_SSR_100MBS: |
321 | mii->mii_media_active |= IFM_100_TX; | | 321 | mii->mii_media_active |= IFM_100_TX; |
322 | sc->mii_ticks = 0; | | 322 | sc->mii_ticks = 0; |
323 | break; | | 323 | break; |
324 | case ATPHY_SSR_10MBS: | | 324 | case ATPHY_SSR_10MBS: |
325 | mii->mii_media_active |= IFM_10_T; | | 325 | mii->mii_media_active |= IFM_10_T; |
326 | sc->mii_ticks = 0; | | 326 | sc->mii_ticks = 0; |
327 | break; | | 327 | break; |
328 | default: | | 328 | default: |
329 | mii->mii_media_active |= IFM_NONE; | | 329 | mii->mii_media_active |= IFM_NONE; |
330 | return; | | 330 | return; |
331 | } | | 331 | } |
332 | | | 332 | |
333 | if (ssr & ATPHY_SSR_DUPLEX) | | 333 | if (ssr & ATPHY_SSR_DUPLEX) |
334 | mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc); | | 334 | mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc); |
335 | else | | 335 | else |
336 | mii->mii_media_active |= IFM_HDX; | | 336 | mii->mii_media_active |= IFM_HDX; |
337 | | | 337 | |
338 | gsr = PHY_READ(sc, MII_100T2SR); | | 338 | gsr = PHY_READ(sc, MII_100T2SR); |
339 | if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) && | | 339 | if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) && |
340 | gsr & GTSR_MS_RES) | | 340 | gsr & GTSR_MS_RES) |
341 | mii->mii_media_active |= IFM_ETH_MASTER; | | 341 | mii->mii_media_active |= IFM_ETH_MASTER; |
342 | } | | 342 | } |
343 | | | 343 | |
344 | static void | | 344 | static void |
345 | atphy_reset(struct mii_softc *sc) | | 345 | atphy_reset(struct mii_softc *sc) |
346 | { | | 346 | { |
347 | uint32_t reg; | | 347 | uint32_t reg; |
348 | int i; | | 348 | int i; |
349 | | | 349 | |
350 | /* Take PHY out of power down mode. */ | | 350 | /* Take PHY out of power down mode. */ |
351 | PHY_WRITE(sc, 29, 0x29); | | 351 | PHY_WRITE(sc, 29, 0x29); |
352 | PHY_WRITE(sc, 30, 0); | | 352 | PHY_WRITE(sc, 30, 0); |
353 | | | 353 | |
354 | reg = PHY_READ(sc, ATPHY_SCR); | | 354 | reg = PHY_READ(sc, ATPHY_SCR); |
355 | /* Enable automatic crossover. */ | | 355 | /* Enable automatic crossover. */ |
356 | reg |= ATPHY_SCR_AUTO_X_MODE; | | 356 | reg |= ATPHY_SCR_AUTO_X_MODE; |
357 | /* Disable power down. */ | | 357 | /* Disable power down. */ |
358 | reg &= ~ATPHY_SCR_MAC_PDOWN; | | 358 | reg &= ~ATPHY_SCR_MAC_PDOWN; |
359 | /* Enable CRS on Tx. */ | | 359 | /* Enable CRS on Tx. */ |
360 | reg |= ATPHY_SCR_ASSERT_CRS_ON_TX; | | 360 | reg |= ATPHY_SCR_ASSERT_CRS_ON_TX; |
361 | /* Auto correction for reversed cable polarity. */ | | 361 | /* Auto correction for reversed cable polarity. */ |
362 | reg |= ATPHY_SCR_POLARITY_REVERSAL; | | 362 | reg |= ATPHY_SCR_POLARITY_REVERSAL; |
363 | PHY_WRITE(sc, ATPHY_SCR, reg); | | 363 | PHY_WRITE(sc, ATPHY_SCR, reg); |
364 | | | 364 | |
365 | /* Workaround F1 bug to reset phy. */ | | 365 | /* Workaround F1 bug to reset phy. */ |
366 | atphy_mii_phy_auto(sc); | | 366 | atphy_mii_phy_auto(sc); |
367 | | | 367 | |
368 | for (i = 0; i < 1000; i++) { | | 368 | for (i = 0; i < 1000; i++) { |
369 | DELAY(1); | | 369 | DELAY(1); |
370 | if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0) | | 370 | if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0) |
371 | break; | | 371 | break; |
372 | } | | 372 | } |
373 | } | | 373 | } |
374 | | | 374 | |
375 | static int | | 375 | static int |
376 | atphy_mii_phy_auto(struct mii_softc *sc) | | 376 | atphy_mii_phy_auto(struct mii_softc *sc) |
377 | { | | 377 | { |
378 | uint16_t anar; | | 378 | uint16_t anar; |
379 | | | 379 | |
380 | anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA; | | 380 | anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA; |
381 | if (sc->mii_flags & MIIF_DOPAUSE) | | 381 | if (sc->mii_flags & MIIF_DOPAUSE) |
382 | anar |= (3 << 10); | | 382 | anar |= (3 << 10); |
383 | PHY_WRITE(sc, MII_ANAR, anar); | | 383 | PHY_WRITE(sc, MII_ANAR, anar); |
384 | if (sc->mii_extcapabilities & (EXTSR_1000TFDX | EXTSR_1000THDX)) | | 384 | if (sc->mii_extcapabilities & (EXTSR_1000TFDX | EXTSR_1000THDX)) |
385 | PHY_WRITE(sc, MII_100T2CR, GTCR_ADV_1000TFDX | | | 385 | PHY_WRITE(sc, MII_100T2CR, GTCR_ADV_1000TFDX | |
386 | GTCR_ADV_1000THDX); | | 386 | GTCR_ADV_1000THDX); |
387 | PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN | BMCR_STARTNEG); | | 387 | PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN | BMCR_STARTNEG); |
388 | | | 388 | |
389 | return EJUSTRETURN; | | 389 | return EJUSTRETURN; |
390 | } | | 390 | } |