| @@ -1,1400 +1,1389 @@ | | | @@ -1,1400 +1,1389 @@ |
1 | /* $NetBSD: if_axe.c,v 1.102.2.1 2019/09/01 13:00:36 martin Exp $ */ | | 1 | /* $NetBSD: if_axe.c,v 1.102.2.2 2019/09/01 14:03:02 martin Exp $ */ |
2 | /* $OpenBSD: if_axe.c,v 1.137 2016/04/13 11:03:37 mpi Exp $ */ | | 2 | /* $OpenBSD: if_axe.c,v 1.137 2016/04/13 11:03:37 mpi Exp $ */ |
3 | | | 3 | |
4 | /* | | 4 | /* |
5 | * Copyright (c) 2005, 2006, 2007 Jonathan Gray <jsg@openbsd.org> | | 5 | * Copyright (c) 2005, 2006, 2007 Jonathan Gray <jsg@openbsd.org> |
6 | * | | 6 | * |
7 | * Permission to use, copy, modify, and distribute this software for any | | 7 | * Permission to use, copy, modify, and distribute this software for any |
8 | * purpose with or without fee is hereby granted, provided that the above | | 8 | * purpose with or without fee is hereby granted, provided that the above |
9 | * copyright notice and this permission notice appear in all copies. | | 9 | * copyright notice and this permission notice appear in all copies. |
10 | * | | 10 | * |
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | | 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | | 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | | 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | | 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | | 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | | 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | | 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
18 | */ | | 18 | */ |
19 | | | 19 | |
20 | /* | | 20 | /* |
21 | * Copyright (c) 1997, 1998, 1999, 2000-2003 | | 21 | * Copyright (c) 1997, 1998, 1999, 2000-2003 |
22 | * Bill Paul <wpaul@windriver.com>. All rights reserved. | | 22 | * Bill Paul <wpaul@windriver.com>. All rights reserved. |
23 | * | | 23 | * |
24 | * Redistribution and use in source and binary forms, with or without | | 24 | * Redistribution and use in source and binary forms, with or without |
25 | * modification, are permitted provided that the following conditions | | 25 | * modification, are permitted provided that the following conditions |
26 | * are met: | | 26 | * are met: |
27 | * 1. Redistributions of source code must retain the above copyright | | 27 | * 1. Redistributions of source code must retain the above copyright |
28 | * notice, this list of conditions and the following disclaimer. | | 28 | * notice, this list of conditions and the following disclaimer. |
29 | * 2. Redistributions in binary form must reproduce the above copyright | | 29 | * 2. Redistributions in binary form must reproduce the above copyright |
30 | * notice, this list of conditions and the following disclaimer in the | | 30 | * notice, this list of conditions and the following disclaimer in the |
31 | * documentation and/or other materials provided with the distribution. | | 31 | * documentation and/or other materials provided with the distribution. |
32 | * 3. All advertising materials mentioning features or use of this software | | 32 | * 3. All advertising materials mentioning features or use of this software |
33 | * must display the following acknowledgement: | | 33 | * must display the following acknowledgement: |
34 | * This product includes software developed by Bill Paul. | | 34 | * This product includes software developed by Bill Paul. |
35 | * 4. Neither the name of the author nor the names of any co-contributors | | 35 | * 4. Neither the name of the author nor the names of any co-contributors |
36 | * may be used to endorse or promote products derived from this software | | 36 | * may be used to endorse or promote products derived from this software |
37 | * without specific prior written permission. | | 37 | * without specific prior written permission. |
38 | * | | 38 | * |
39 | * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND | | 39 | * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND |
40 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 40 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
41 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 41 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
42 | * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD | | 42 | * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD |
43 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | | 43 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
44 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | 44 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
45 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | 45 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
46 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | | 46 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
47 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 47 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
48 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | | 48 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
49 | * THE POSSIBILITY OF SUCH DAMAGE. | | 49 | * THE POSSIBILITY OF SUCH DAMAGE. |
50 | */ | | 50 | */ |
51 | | | 51 | |
52 | /* | | 52 | /* |
53 | * ASIX Electronics AX88172/AX88178/AX88778 USB 2.0 ethernet driver. | | 53 | * ASIX Electronics AX88172/AX88178/AX88778 USB 2.0 ethernet driver. |
54 | * Used in the LinkSys USB200M and various other adapters. | | 54 | * Used in the LinkSys USB200M and various other adapters. |
55 | * | | 55 | * |
56 | * Written by Bill Paul <wpaul@windriver.com> | | 56 | * Written by Bill Paul <wpaul@windriver.com> |
57 | * Senior Engineer | | 57 | * Senior Engineer |
58 | * Wind River Systems | | 58 | * Wind River Systems |
59 | */ | | 59 | */ |
60 | | | 60 | |
61 | /* | | 61 | /* |
62 | * The AX88172 provides USB ethernet supports at 10 and 100Mbps. | | 62 | * The AX88172 provides USB ethernet supports at 10 and 100Mbps. |
63 | * It uses an external PHY (reference designs use a RealTek chip), | | 63 | * It uses an external PHY (reference designs use a RealTek chip), |
64 | * and has a 64-bit multicast hash filter. There is some information | | 64 | * and has a 64-bit multicast hash filter. There is some information |
65 | * missing from the manual which one needs to know in order to make | | 65 | * missing from the manual which one needs to know in order to make |
66 | * the chip function: | | 66 | * the chip function: |
67 | * | | 67 | * |
68 | * - You must set bit 7 in the RX control register, otherwise the | | 68 | * - You must set bit 7 in the RX control register, otherwise the |
69 | * chip won't receive any packets. | | 69 | * chip won't receive any packets. |
70 | * - You must initialize all 3 IPG registers, or you won't be able | | 70 | * - You must initialize all 3 IPG registers, or you won't be able |
71 | * to send any packets. | | 71 | * to send any packets. |
72 | * | | 72 | * |
73 | * Note that this device appears to only support loading the station | | 73 | * Note that this device appears to only support loading the station |
74 | * address via autoload from the EEPROM (i.e. there's no way to manually | | 74 | * address via autoload from the EEPROM (i.e. there's no way to manually |
75 | * set it). | | 75 | * set it). |
76 | * | | 76 | * |
77 | * (Adam Weinberger wanted me to name this driver if_gir.c.) | | 77 | * (Adam Weinberger wanted me to name this driver if_gir.c.) |
78 | */ | | 78 | */ |
79 | | | 79 | |
80 | /* | | 80 | /* |
81 | * Ax88178 and Ax88772 support backported from the OpenBSD driver. | | 81 | * Ax88178 and Ax88772 support backported from the OpenBSD driver. |
82 | * 2007/02/12, J.R. Oldroyd, fbsd@opal.com | | 82 | * 2007/02/12, J.R. Oldroyd, fbsd@opal.com |
83 | * | | 83 | * |
84 | * Manual here: | | 84 | * Manual here: |
85 | * http://www.asix.com.tw/FrootAttach/datasheet/AX88178_datasheet_Rev10.pdf | | 85 | * http://www.asix.com.tw/FrootAttach/datasheet/AX88178_datasheet_Rev10.pdf |
86 | * http://www.asix.com.tw/FrootAttach/datasheet/AX88772_datasheet_Rev10.pdf | | 86 | * http://www.asix.com.tw/FrootAttach/datasheet/AX88772_datasheet_Rev10.pdf |
87 | */ | | 87 | */ |
88 | | | 88 | |
89 | #include <sys/cdefs.h> | | 89 | #include <sys/cdefs.h> |
90 | __KERNEL_RCSID(0, "$NetBSD: if_axe.c,v 1.102.2.1 2019/09/01 13:00:36 martin Exp $"); | | 90 | __KERNEL_RCSID(0, "$NetBSD: if_axe.c,v 1.102.2.2 2019/09/01 14:03:02 martin Exp $"); |
91 | | | 91 | |
92 | #ifdef _KERNEL_OPT | | 92 | #ifdef _KERNEL_OPT |
93 | #include "opt_usb.h" | | 93 | #include "opt_usb.h" |
94 | #include "opt_net_mpsafe.h" | | 94 | #include "opt_net_mpsafe.h" |
95 | #endif | | 95 | #endif |
96 | | | 96 | |
97 | #include <sys/param.h> | | 97 | #include <sys/param.h> |
98 | | | 98 | |
99 | #include <dev/usb/usbnet.h> | | 99 | #include <dev/usb/usbnet.h> |
100 | #include <dev/usb/usbhist.h> | | 100 | #include <dev/usb/usbhist.h> |
101 | #include <dev/usb/if_axereg.h> | | 101 | #include <dev/usb/if_axereg.h> |
102 | | | 102 | |
103 | struct axe_type { | | 103 | struct axe_type { |
104 | struct usb_devno axe_dev; | | 104 | struct usb_devno axe_dev; |
105 | uint16_t axe_flags; | | 105 | uint16_t axe_flags; |
106 | }; | | 106 | }; |
107 | | | 107 | |
108 | struct axe_softc { | | 108 | struct axe_softc { |
109 | struct usbnet axe_un; | | 109 | struct usbnet axe_un; |
110 | | | 110 | |
111 | /* usbnet:un_flags values */ | | 111 | /* usbnet:un_flags values */ |
112 | #define AX178 __BIT(0) /* AX88178 */ | | 112 | #define AX178 __BIT(0) /* AX88178 */ |
113 | #define AX772 __BIT(1) /* AX88772 */ | | 113 | #define AX772 __BIT(1) /* AX88772 */ |
114 | #define AX772A __BIT(2) /* AX88772A */ | | 114 | #define AX772A __BIT(2) /* AX88772A */ |
115 | #define AX772B __BIT(3) /* AX88772B */ | | 115 | #define AX772B __BIT(3) /* AX88772B */ |
116 | #define AXSTD_FRAME __BIT(12) | | 116 | #define AXSTD_FRAME __BIT(12) |
117 | #define AXCSUM_FRAME __BIT(13) | | 117 | #define AXCSUM_FRAME __BIT(13) |
118 | | | 118 | |
119 | uint8_t axe_ipgs[3]; | | 119 | uint8_t axe_ipgs[3]; |
120 | uint8_t axe_phyaddrs[2]; | | 120 | uint8_t axe_phyaddrs[2]; |
121 | uint16_t sc_pwrcfg; | | 121 | uint16_t sc_pwrcfg; |
122 | uint16_t sc_lenmask; | | 122 | uint16_t sc_lenmask; |
123 | | | 123 | |
124 | }; | | 124 | }; |
125 | | | 125 | |
126 | #define AXE_IS_178_FAMILY(un) \ | | 126 | #define AXE_IS_178_FAMILY(un) \ |
127 | ((un)->un_flags & (AX772 | AX772A | AX772B | AX178)) | | 127 | ((un)->un_flags & (AX772 | AX772A | AX772B | AX178)) |
128 | | | 128 | |
129 | #define AXE_IS_772(un) \ | | 129 | #define AXE_IS_772(un) \ |
130 | ((un)->un_flags & (AX772 | AX772A | AX772B)) | | 130 | ((un)->un_flags & (AX772 | AX772A | AX772B)) |
131 | | | 131 | |
132 | #define AX_RXCSUM \ | | 132 | #define AX_RXCSUM \ |
133 | (IFCAP_CSUM_IPv4_Rx | \ | | 133 | (IFCAP_CSUM_IPv4_Rx | \ |
134 | IFCAP_CSUM_TCPv4_Rx | IFCAP_CSUM_UDPv4_Rx | \ | | 134 | IFCAP_CSUM_TCPv4_Rx | IFCAP_CSUM_UDPv4_Rx | \ |
135 | IFCAP_CSUM_TCPv6_Rx | IFCAP_CSUM_UDPv6_Rx) | | 135 | IFCAP_CSUM_TCPv6_Rx | IFCAP_CSUM_UDPv6_Rx) |
136 | | | 136 | |
137 | #define AX_TXCSUM \ | | 137 | #define AX_TXCSUM \ |
138 | (IFCAP_CSUM_IPv4_Tx | \ | | 138 | (IFCAP_CSUM_IPv4_Tx | \ |
139 | IFCAP_CSUM_TCPv4_Tx | IFCAP_CSUM_UDPv4_Tx | \ | | 139 | IFCAP_CSUM_TCPv4_Tx | IFCAP_CSUM_UDPv4_Tx | \ |
140 | IFCAP_CSUM_TCPv6_Tx | IFCAP_CSUM_UDPv6_Tx) | | 140 | IFCAP_CSUM_TCPv6_Tx | IFCAP_CSUM_UDPv6_Tx) |
141 | | | 141 | |
142 | /* | | 142 | /* |
143 | * AXE_178_MAX_FRAME_BURST | | 143 | * AXE_178_MAX_FRAME_BURST |
144 | * max frame burst size for Ax88178 and Ax88772 | | 144 | * max frame burst size for Ax88178 and Ax88772 |
145 | * 0 2048 bytes | | 145 | * 0 2048 bytes |
146 | * 1 4096 bytes | | 146 | * 1 4096 bytes |
147 | * 2 8192 bytes | | 147 | * 2 8192 bytes |
148 | * 3 16384 bytes | | 148 | * 3 16384 bytes |
149 | * use the largest your system can handle without USB stalling. | | 149 | * use the largest your system can handle without USB stalling. |
150 | * | | 150 | * |
151 | * NB: 88772 parts appear to generate lots of input errors with | | 151 | * NB: 88772 parts appear to generate lots of input errors with |
152 | * a 2K rx buffer and 8K is only slightly faster than 4K on an | | 152 | * a 2K rx buffer and 8K is only slightly faster than 4K on an |
153 | * EHCI port on a T42 so change at your own risk. | | 153 | * EHCI port on a T42 so change at your own risk. |
154 | */ | | 154 | */ |
155 | #define AXE_178_MAX_FRAME_BURST 1 | | 155 | #define AXE_178_MAX_FRAME_BURST 1 |
156 | | | 156 | |
157 | | | 157 | |
158 | #ifdef USB_DEBUG | | 158 | #ifdef USB_DEBUG |
159 | #ifndef AXE_DEBUG | | 159 | #ifndef AXE_DEBUG |
160 | #define axedebug 0 | | 160 | #define axedebug 0 |
161 | #else | | 161 | #else |
162 | static int axedebug = 0; | | 162 | static int axedebug = 0; |
163 | | | 163 | |
164 | SYSCTL_SETUP(sysctl_hw_axe_setup, "sysctl hw.axe setup") | | 164 | SYSCTL_SETUP(sysctl_hw_axe_setup, "sysctl hw.axe setup") |
165 | { | | 165 | { |
166 | int err; | | 166 | int err; |
167 | const struct sysctlnode *rnode; | | 167 | const struct sysctlnode *rnode; |
168 | const struct sysctlnode *cnode; | | 168 | const struct sysctlnode *cnode; |
169 | | | 169 | |
170 | err = sysctl_createv(clog, 0, NULL, &rnode, | | 170 | err = sysctl_createv(clog, 0, NULL, &rnode, |
171 | CTLFLAG_PERMANENT, CTLTYPE_NODE, "axe", | | 171 | CTLFLAG_PERMANENT, CTLTYPE_NODE, "axe", |
172 | SYSCTL_DESCR("axe global controls"), | | 172 | SYSCTL_DESCR("axe global controls"), |
173 | NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL); | | 173 | NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL); |
174 | | | 174 | |
175 | if (err) | | 175 | if (err) |
176 | goto fail; | | 176 | goto fail; |
177 | | | 177 | |
178 | /* control debugging printfs */ | | 178 | /* control debugging printfs */ |
179 | err = sysctl_createv(clog, 0, &rnode, &cnode, | | 179 | err = sysctl_createv(clog, 0, &rnode, &cnode, |
180 | CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT, | | 180 | CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT, |
181 | "debug", SYSCTL_DESCR("Enable debugging output"), | | 181 | "debug", SYSCTL_DESCR("Enable debugging output"), |
182 | NULL, 0, &axedebug, sizeof(axedebug), CTL_CREATE, CTL_EOL); | | 182 | NULL, 0, &axedebug, sizeof(axedebug), CTL_CREATE, CTL_EOL); |
183 | if (err) | | 183 | if (err) |
184 | goto fail; | | 184 | goto fail; |
185 | | | 185 | |
186 | return; | | 186 | return; |
187 | fail: | | 187 | fail: |
188 | aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err); | | 188 | aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err); |
189 | } | | 189 | } |
190 | | | 190 | |
191 | #endif /* AXE_DEBUG */ | | 191 | #endif /* AXE_DEBUG */ |
192 | #endif /* USB_DEBUG */ | | 192 | #endif /* USB_DEBUG */ |
193 | | | 193 | |
194 | #define DPRINTF(FMT,A,B,C,D) USBHIST_LOGN(axedebug,1,FMT,A,B,C,D) | | 194 | #define DPRINTF(FMT,A,B,C,D) USBHIST_LOGN(axedebug,1,FMT,A,B,C,D) |
195 | #define DPRINTFN(N,FMT,A,B,C,D) USBHIST_LOGN(axedebug,N,FMT,A,B,C,D) | | 195 | #define DPRINTFN(N,FMT,A,B,C,D) USBHIST_LOGN(axedebug,N,FMT,A,B,C,D) |
196 | #define AXEHIST_FUNC() USBHIST_FUNC() | | 196 | #define AXEHIST_FUNC() USBHIST_FUNC() |
197 | #define AXEHIST_CALLED(name) USBHIST_CALLED(axedebug) | | 197 | #define AXEHIST_CALLED(name) USBHIST_CALLED(axedebug) |
198 | | | 198 | |
199 | /* | | 199 | /* |
200 | * Various supported device vendors/products. | | 200 | * Various supported device vendors/products. |
201 | */ | | 201 | */ |
202 | static const struct axe_type axe_devs[] = { | | 202 | static const struct axe_type axe_devs[] = { |
203 | { { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UFE2000}, 0 }, | | 203 | { { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UFE2000}, 0 }, |
204 | { { USB_VENDOR_ACERCM, USB_PRODUCT_ACERCM_EP1427X2}, 0 }, | | 204 | { { USB_VENDOR_ACERCM, USB_PRODUCT_ACERCM_EP1427X2}, 0 }, |
205 | { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_ETHERNET }, AX772 }, | | 205 | { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_ETHERNET }, AX772 }, |
206 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88172}, 0 }, | | 206 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88172}, 0 }, |
207 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772}, AX772 }, | | 207 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772}, AX772 }, |
208 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772A}, AX772 }, | | 208 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772A}, AX772 }, |
209 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772B}, AX772B }, | | 209 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772B}, AX772B }, |
210 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772B_1}, AX772B }, | | 210 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772B_1}, AX772B }, |
211 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88178}, AX178 }, | | 211 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88178}, AX178 }, |
212 | { { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC210T}, 0 }, | | 212 | { { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC210T}, 0 }, |
213 | { { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D5055 }, AX178 }, | | 213 | { { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D5055 }, AX178 }, |
214 | { { USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB2AR}, 0}, | | 214 | { { USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB2AR}, 0}, |
215 | { { USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_USB200MV2}, AX772A }, | | 215 | { { USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_USB200MV2}, AX772A }, |
216 | { { USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB2_TX }, 0}, | | 216 | { { USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB2_TX }, 0}, |
217 | { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100}, 0 }, | | 217 | { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100}, 0 }, |
218 | { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100B1 }, AX772 }, | | 218 | { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100B1 }, AX772 }, |
219 | { { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DUBE100B1 }, AX772 }, | | 219 | { { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DUBE100B1 }, AX772 }, |
220 | { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100C1 }, AX772B }, | | 220 | { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100C1 }, AX772B }, |
221 | { { USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_GWUSB2E}, 0 }, | | 221 | { { USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_GWUSB2E}, 0 }, |
222 | { { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_ETGUS2 }, AX178 }, | | 222 | { { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_ETGUS2 }, AX178 }, |
223 | { { USB_VENDOR_JVC, USB_PRODUCT_JVC_MP_PRX1}, 0 }, | | 223 | { { USB_VENDOR_JVC, USB_PRODUCT_JVC_MP_PRX1}, 0 }, |
224 | { { USB_VENDOR_LENOVO, USB_PRODUCT_LENOVO_ETHERNET }, AX772B }, | | 224 | { { USB_VENDOR_LENOVO, USB_PRODUCT_LENOVO_ETHERNET }, AX772B }, |
225 | { { USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_HG20F9}, AX772B }, | | 225 | { { USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_HG20F9}, AX772B }, |
226 | { { USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_USB200M}, 0 }, | | 226 | { { USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_USB200M}, 0 }, |
227 | { { USB_VENDOR_LINKSYS4, USB_PRODUCT_LINKSYS4_USB1000 }, AX178 }, | | 227 | { { USB_VENDOR_LINKSYS4, USB_PRODUCT_LINKSYS4_USB1000 }, AX178 }, |
228 | { { USB_VENDOR_LOGITEC, USB_PRODUCT_LOGITEC_LAN_GTJU2}, AX178 }, | | 228 | { { USB_VENDOR_LOGITEC, USB_PRODUCT_LOGITEC_LAN_GTJU2}, AX178 }, |
229 | { { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAU2GT}, AX178 }, | | 229 | { { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAU2GT}, AX178 }, |
230 | { { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAU2KTX}, 0 }, | | 230 | { { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAU2KTX}, 0 }, |
231 | { { USB_VENDOR_MSI, USB_PRODUCT_MSI_AX88772A}, AX772 }, | | 231 | { { USB_VENDOR_MSI, USB_PRODUCT_MSI_AX88772A}, AX772 }, |
232 | { { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA120}, 0 }, | | 232 | { { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA120}, 0 }, |
233 | { { USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01PLUS }, AX772 }, | | 233 | { { USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01PLUS }, AX772 }, |
234 | { { USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GU1000T }, AX178 }, | | 234 | { { USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GU1000T }, AX178 }, |
235 | { { USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_LN029}, 0 }, | | 235 | { { USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_LN029}, 0 }, |
236 | { { USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN028 }, AX178 }, | | 236 | { { USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN028 }, AX178 }, |
237 | { { USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN031 }, AX178 }, | | 237 | { { USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN031 }, AX178 }, |
238 | { { USB_VENDOR_SYSTEMTALKS, USB_PRODUCT_SYSTEMTALKS_SGCX2UL}, 0 }, | | 238 | { { USB_VENDOR_SYSTEMTALKS, USB_PRODUCT_SYSTEMTALKS_SGCX2UL}, 0 }, |
239 | }; | | 239 | }; |
240 | #define axe_lookup(v, p) ((const struct axe_type *)usb_lookup(axe_devs, v, p)) | | 240 | #define axe_lookup(v, p) ((const struct axe_type *)usb_lookup(axe_devs, v, p)) |
241 | | | 241 | |
242 | static const struct ax88772b_mfb ax88772b_mfb_table[] = { | | 242 | static const struct ax88772b_mfb ax88772b_mfb_table[] = { |
243 | { 0x8000, 0x8001, 2048 }, | | 243 | { 0x8000, 0x8001, 2048 }, |
244 | { 0x8100, 0x8147, 4096 }, | | 244 | { 0x8100, 0x8147, 4096 }, |
245 | { 0x8200, 0x81EB, 6144 }, | | 245 | { 0x8200, 0x81EB, 6144 }, |
246 | { 0x8300, 0x83D7, 8192 }, | | 246 | { 0x8300, 0x83D7, 8192 }, |
247 | { 0x8400, 0x851E, 16384 }, | | 247 | { 0x8400, 0x851E, 16384 }, |
248 | { 0x8500, 0x8666, 20480 }, | | 248 | { 0x8500, 0x8666, 20480 }, |
249 | { 0x8600, 0x87AE, 24576 }, | | 249 | { 0x8600, 0x87AE, 24576 }, |
250 | { 0x8700, 0x8A3D, 32768 } | | 250 | { 0x8700, 0x8A3D, 32768 } |
251 | }; | | 251 | }; |
252 | | | 252 | |
253 | int axe_match(device_t, cfdata_t, void *); | | 253 | int axe_match(device_t, cfdata_t, void *); |
254 | void axe_attach(device_t, device_t, void *); | | 254 | void axe_attach(device_t, device_t, void *); |
255 | | | 255 | |
256 | CFATTACH_DECL_NEW(axe, sizeof(struct axe_softc), | | 256 | CFATTACH_DECL_NEW(axe, sizeof(struct axe_softc), |
257 | axe_match, axe_attach, usbnet_detach, usbnet_activate); | | 257 | axe_match, axe_attach, usbnet_detach, usbnet_activate); |
258 | | | 258 | |
259 | static void axe_stop(struct ifnet *, int); | | 259 | static void axe_stop(struct ifnet *, int); |
260 | static int axe_ioctl(struct ifnet *, u_long, void *); | | 260 | static int axe_ioctl(struct ifnet *, u_long, void *); |
261 | static int axe_init(struct ifnet *); | | 261 | static int axe_init(struct ifnet *); |
262 | static int axe_mii_read_reg(struct usbnet *, int, int, uint16_t *); | | 262 | static int axe_mii_read_reg(struct usbnet *, int, int, uint16_t *); |
263 | static int axe_mii_write_reg(struct usbnet *, int, int, uint16_t); | | 263 | static int axe_mii_write_reg(struct usbnet *, int, int, uint16_t); |
264 | static void axe_mii_statchg(struct ifnet *); | | 264 | static void axe_mii_statchg(struct ifnet *); |
265 | static void axe_rx_loop(struct usbnet *, struct usbnet_chain *, uint32_t); | | 265 | static void axe_rx_loop(struct usbnet *, struct usbnet_chain *, uint32_t); |
266 | static unsigned axe_tx_prepare(struct usbnet *, struct mbuf *, | | 266 | static unsigned axe_tx_prepare(struct usbnet *, struct mbuf *, |
267 | struct usbnet_chain *); | | 267 | struct usbnet_chain *); |
268 | | | 268 | |
269 | static void axe_ax88178_init(struct axe_softc *); | | 269 | static void axe_ax88178_init(struct axe_softc *); |
270 | static void axe_ax88772_init(struct axe_softc *); | | 270 | static void axe_ax88772_init(struct axe_softc *); |
271 | static void axe_ax88772a_init(struct axe_softc *); | | 271 | static void axe_ax88772a_init(struct axe_softc *); |
272 | static void axe_ax88772b_init(struct axe_softc *); | | 272 | static void axe_ax88772b_init(struct axe_softc *); |
273 | | | 273 | |
274 | static struct usbnet_ops axe_ops = { | | 274 | static struct usbnet_ops axe_ops = { |
275 | .uno_stop = axe_stop, | | 275 | .uno_stop = axe_stop, |
276 | .uno_ioctl = axe_ioctl, | | 276 | .uno_ioctl = axe_ioctl, |
277 | .uno_read_reg = axe_mii_read_reg, | | 277 | .uno_read_reg = axe_mii_read_reg, |
278 | .uno_write_reg = axe_mii_write_reg, | | 278 | .uno_write_reg = axe_mii_write_reg, |
279 | .uno_statchg = axe_mii_statchg, | | 279 | .uno_statchg = axe_mii_statchg, |
280 | .uno_tx_prepare = axe_tx_prepare, | | 280 | .uno_tx_prepare = axe_tx_prepare, |
281 | .uno_rx_loop = axe_rx_loop, | | 281 | .uno_rx_loop = axe_rx_loop, |
282 | .uno_init = axe_init, | | 282 | .uno_init = axe_init, |
283 | }; | | 283 | }; |
284 | | | 284 | |
285 | static usbd_status | | 285 | static usbd_status |
286 | axe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf) | | 286 | axe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf) |
287 | { | | 287 | { |
288 | AXEHIST_FUNC(); AXEHIST_CALLED(); | | 288 | AXEHIST_FUNC(); AXEHIST_CALLED(); |
289 | struct usbnet * const un = &sc->axe_un; | | 289 | struct usbnet * const un = &sc->axe_un; |
290 | usb_device_request_t req; | | 290 | usb_device_request_t req; |
291 | usbd_status err; | | 291 | usbd_status err; |
292 | | | 292 | |
293 | usbnet_isowned_mii(un); | | 293 | usbnet_isowned_mii(un); |
294 | | | 294 | |
295 | if (usbnet_isdying(un)) | | 295 | if (usbnet_isdying(un)) |
296 | return -1; | | 296 | return -1; |
297 | | | 297 | |
298 | DPRINTFN(20, "cmd %#jx index %#jx val %#jx", cmd, index, val, 0); | | 298 | DPRINTFN(20, "cmd %#jx index %#jx val %#jx", cmd, index, val, 0); |
299 | | | 299 | |
300 | if (AXE_CMD_DIR(cmd)) | | 300 | if (AXE_CMD_DIR(cmd)) |
301 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE; | | 301 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE; |
302 | else | | 302 | else |
303 | req.bmRequestType = UT_READ_VENDOR_DEVICE; | | 303 | req.bmRequestType = UT_READ_VENDOR_DEVICE; |
304 | req.bRequest = AXE_CMD_CMD(cmd); | | 304 | req.bRequest = AXE_CMD_CMD(cmd); |
305 | USETW(req.wValue, val); | | 305 | USETW(req.wValue, val); |
306 | USETW(req.wIndex, index); | | 306 | USETW(req.wIndex, index); |
307 | USETW(req.wLength, AXE_CMD_LEN(cmd)); | | 307 | USETW(req.wLength, AXE_CMD_LEN(cmd)); |
308 | | | 308 | |
309 | err = usbd_do_request(un->un_udev, &req, buf); | | 309 | err = usbd_do_request(un->un_udev, &req, buf); |
310 | if (err) | | 310 | if (err) |
311 | DPRINTF("cmd %jd err %jd", cmd, err, 0, 0); | | 311 | DPRINTF("cmd %jd err %jd", cmd, err, 0, 0); |
312 | | | 312 | |
313 | return err; | | 313 | return err; |
314 | } | | 314 | } |
315 | | | 315 | |
316 | static int | | 316 | static int |
317 | axe_mii_read_reg(struct usbnet *un, int phy, int reg, uint16_t *val) | | 317 | axe_mii_read_reg(struct usbnet *un, int phy, int reg, uint16_t *val) |
318 | { | | 318 | { |
319 | AXEHIST_FUNC(); AXEHIST_CALLED(); | | 319 | AXEHIST_FUNC(); AXEHIST_CALLED(); |
320 | struct axe_softc * const sc = usbnet_softc(un); | | 320 | struct axe_softc * const sc = usbnet_softc(un); |
321 | usbd_status err; | | 321 | usbd_status err; |
322 | uint16_t data; | | 322 | uint16_t data; |
323 | | | 323 | |
324 | DPRINTFN(30, "phy 0x%jx reg 0x%jx\n", phy, reg, 0, 0); | | 324 | DPRINTFN(30, "phy 0x%jx reg 0x%jx\n", phy, reg, 0, 0); |
325 | | | 325 | |
326 | if (un->un_phyno != phy) | | 326 | if (un->un_phyno != phy) |
327 | return EINVAL; | | 327 | return EINVAL; |
328 | | | 328 | |
329 | axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); | | 329 | axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); |
330 | | | 330 | |
331 | err = axe_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, &data); | | 331 | err = axe_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, &data); |
332 | axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); | | 332 | axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); |
333 | | | 333 | |
334 | if (err) { | | 334 | if (err) { |
335 | aprint_error_dev(un->un_dev, "read PHY failed\n"); | | 335 | aprint_error_dev(un->un_dev, "read PHY failed\n"); |
336 | return EIO; | | 336 | return EIO; |
337 | } | | 337 | } |
338 | | | 338 | |
339 | *val = le16toh(data); | | 339 | *val = le16toh(data); |
340 | if (AXE_IS_772(un) && reg == MII_BMSR) { | | 340 | if (AXE_IS_772(un) && reg == MII_BMSR) { |
341 | /* | | 341 | /* |
342 | * BMSR of AX88772 indicates that it supports extended | | 342 | * BMSR of AX88772 indicates that it supports extended |
343 | * capability but the extended status register is | | 343 | * capability but the extended status register is |
344 | * reserved for embedded ethernet PHY. So clear the | | 344 | * reserved for embedded ethernet PHY. So clear the |
345 | * extended capability bit of BMSR. | | 345 | * extended capability bit of BMSR. |
346 | */ | | 346 | */ |
347 | *val &= ~BMSR_EXTCAP; | | 347 | *val &= ~BMSR_EXTCAP; |
348 | } | | 348 | } |
349 | | | 349 | |
350 | DPRINTFN(30, "phy 0x%jx reg 0x%jx val %#jx", phy, reg, *val, 0); | | 350 | DPRINTFN(30, "phy 0x%jx reg 0x%jx val %#jx", phy, reg, *val, 0); |
351 | | | 351 | |
352 | return 0; | | 352 | return 0; |
353 | } | | 353 | } |
354 | | | 354 | |
355 | static int | | 355 | static int |
356 | axe_mii_write_reg(struct usbnet *un, int phy, int reg, uint16_t val) | | 356 | axe_mii_write_reg(struct usbnet *un, int phy, int reg, uint16_t val) |
357 | { | | 357 | { |
358 | struct axe_softc * const sc = usbnet_softc(un); | | 358 | struct axe_softc * const sc = usbnet_softc(un); |
359 | usbd_status err; | | 359 | usbd_status err; |
360 | uint16_t aval; | | 360 | uint16_t aval; |
361 | | | 361 | |
362 | if (un->un_phyno != phy) | | 362 | if (un->un_phyno != phy) |
363 | return EINVAL; | | 363 | return EINVAL; |
364 | | | 364 | |
365 | aval = htole16(val); | | 365 | aval = htole16(val); |
366 | | | 366 | |
367 | axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); | | 367 | axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); |
368 | err = axe_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, &aval); | | 368 | err = axe_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, &aval); |
369 | axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); | | 369 | axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); |
370 | | | 370 | |
371 | if (err) | | 371 | if (err) |
372 | return EIO; | | 372 | return EIO; |
373 | return 0; | | 373 | return 0; |
374 | } | | 374 | } |
375 | | | 375 | |
376 | static void | | 376 | static void |
377 | axe_mii_statchg(struct ifnet *ifp) | | 377 | axe_mii_statchg(struct ifnet *ifp) |
378 | { | | 378 | { |
379 | AXEHIST_FUNC(); AXEHIST_CALLED(); | | 379 | AXEHIST_FUNC(); AXEHIST_CALLED(); |
380 | | | 380 | |
381 | struct usbnet * const un = ifp->if_softc; | | 381 | struct usbnet * const un = ifp->if_softc; |
382 | struct axe_softc * const sc = usbnet_softc(un); | | 382 | struct axe_softc * const sc = usbnet_softc(un); |
383 | struct mii_data *mii = usbnet_mii(un); | | 383 | struct mii_data *mii = usbnet_mii(un); |
384 | int val, err; | | 384 | int val, err; |
385 | | | 385 | |
386 | if (usbnet_isdying(un)) | | 386 | if (usbnet_isdying(un)) |
387 | return; | | 387 | return; |
388 | | | 388 | |
389 | val = 0; | | 389 | val = 0; |
390 | if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { | | 390 | if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { |
391 | val |= AXE_MEDIA_FULL_DUPLEX; | | 391 | val |= AXE_MEDIA_FULL_DUPLEX; |
392 | if (AXE_IS_178_FAMILY(un)) { | | 392 | if (AXE_IS_178_FAMILY(un)) { |
393 | if ((IFM_OPTIONS(mii->mii_media_active) & | | 393 | if ((IFM_OPTIONS(mii->mii_media_active) & |
394 | IFM_ETH_TXPAUSE) != 0) | | 394 | IFM_ETH_TXPAUSE) != 0) |
395 | val |= AXE_178_MEDIA_TXFLOW_CONTROL_EN; | | 395 | val |= AXE_178_MEDIA_TXFLOW_CONTROL_EN; |
396 | if ((IFM_OPTIONS(mii->mii_media_active) & | | 396 | if ((IFM_OPTIONS(mii->mii_media_active) & |
397 | IFM_ETH_RXPAUSE) != 0) | | 397 | IFM_ETH_RXPAUSE) != 0) |
398 | val |= AXE_178_MEDIA_RXFLOW_CONTROL_EN; | | 398 | val |= AXE_178_MEDIA_RXFLOW_CONTROL_EN; |
399 | } | | 399 | } |
400 | } | | 400 | } |
401 | if (AXE_IS_178_FAMILY(un)) { | | 401 | if (AXE_IS_178_FAMILY(un)) { |
402 | val |= AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC; | | 402 | val |= AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC; |
403 | if (un->un_flags & AX178) | | 403 | if (un->un_flags & AX178) |
404 | val |= AXE_178_MEDIA_ENCK; | | 404 | val |= AXE_178_MEDIA_ENCK; |
405 | switch (IFM_SUBTYPE(mii->mii_media_active)) { | | 405 | switch (IFM_SUBTYPE(mii->mii_media_active)) { |
406 | case IFM_1000_T: | | 406 | case IFM_1000_T: |
407 | val |= AXE_178_MEDIA_GMII | AXE_178_MEDIA_ENCK; | | 407 | val |= AXE_178_MEDIA_GMII | AXE_178_MEDIA_ENCK; |
408 | usbnet_set_link(un, true); | | 408 | usbnet_set_link(un, true); |
409 | break; | | 409 | break; |
410 | case IFM_100_TX: | | 410 | case IFM_100_TX: |
411 | val |= AXE_178_MEDIA_100TX; | | 411 | val |= AXE_178_MEDIA_100TX; |
412 | usbnet_set_link(un, true); | | 412 | usbnet_set_link(un, true); |
413 | break; | | 413 | break; |
414 | case IFM_10_T: | | 414 | case IFM_10_T: |
415 | usbnet_set_link(un, true); | | 415 | usbnet_set_link(un, true); |
416 | break; | | 416 | break; |
417 | } | | 417 | } |
418 | } | | 418 | } |
419 | | | 419 | |
420 | DPRINTF("val=0x%jx", val, 0, 0, 0); | | 420 | DPRINTF("val=0x%jx", val, 0, 0, 0); |
421 | usbnet_lock_mii(un); | | 421 | usbnet_lock_mii(un); |
422 | err = axe_cmd(sc, AXE_CMD_WRITE_MEDIA, 0, val, NULL); | | 422 | err = axe_cmd(sc, AXE_CMD_WRITE_MEDIA, 0, val, NULL); |
423 | usbnet_unlock_mii(un); | | 423 | usbnet_unlock_mii(un); |
424 | if (err) | | 424 | if (err) |
425 | aprint_error_dev(un->un_dev, "media change failed\n"); | | 425 | aprint_error_dev(un->un_dev, "media change failed\n"); |
426 | } | | 426 | } |
427 | | | 427 | |
428 | static void | | 428 | static void |
429 | axe_setiff_locked(struct usbnet *un) | | 429 | axe_setiff_locked(struct usbnet *un) |
430 | { | | 430 | { |
431 | AXEHIST_FUNC(); AXEHIST_CALLED(); | | 431 | AXEHIST_FUNC(); AXEHIST_CALLED(); |
432 | struct axe_softc * const sc = usbnet_softc(un); | | 432 | struct axe_softc * const sc = usbnet_softc(un); |
433 | struct ifnet * const ifp = usbnet_ifp(un); | | 433 | struct ifnet * const ifp = usbnet_ifp(un); |
434 | struct ethercom *ec = usbnet_ec(un); | | 434 | struct ethercom *ec = usbnet_ec(un); |
435 | struct ether_multi *enm; | | 435 | struct ether_multi *enm; |
436 | struct ether_multistep step; | | 436 | struct ether_multistep step; |
437 | uint32_t h = 0; | | 437 | uint32_t h = 0; |
438 | uint16_t rxmode; | | 438 | uint16_t rxmode; |
439 | uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; | | 439 | uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; |
440 | | | 440 | |
441 | usbnet_isowned_mii(un); | | 441 | usbnet_isowned_mii(un); |
442 | | | 442 | |
443 | if (usbnet_isdying(un)) | | 443 | if (usbnet_isdying(un)) |
444 | return; | | 444 | return; |
445 | | | 445 | |
446 | if (axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode)) { | | 446 | if (axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode)) { |
447 | aprint_error_dev(un->un_dev, "can't read rxmode"); | | 447 | aprint_error_dev(un->un_dev, "can't read rxmode"); |
448 | return; | | 448 | return; |
449 | } | | 449 | } |
450 | rxmode = le16toh(rxmode); | | 450 | rxmode = le16toh(rxmode); |
451 | | | 451 | |
452 | rxmode &= | | 452 | rxmode &= |
453 | ~(AXE_RXCMD_ALLMULTI | AXE_RXCMD_PROMISC | | | 453 | ~(AXE_RXCMD_ALLMULTI | AXE_RXCMD_PROMISC | |
454 | AXE_RXCMD_BROADCAST | AXE_RXCMD_MULTICAST); | | 454 | AXE_RXCMD_BROADCAST | AXE_RXCMD_MULTICAST); |
455 | | | 455 | |
456 | rxmode |= | | 456 | rxmode |= |
457 | (ifp->if_flags & IFF_BROADCAST) ? AXE_RXCMD_BROADCAST : 0; | | 457 | (ifp->if_flags & IFF_BROADCAST) ? AXE_RXCMD_BROADCAST : 0; |
458 | | | 458 | |
459 | if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) { | | 459 | if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) { |
460 | if (ifp->if_flags & IFF_PROMISC) | | 460 | if (ifp->if_flags & IFF_PROMISC) |
461 | rxmode |= AXE_RXCMD_PROMISC; | | 461 | rxmode |= AXE_RXCMD_PROMISC; |
462 | goto allmulti; | | 462 | goto allmulti; |
463 | } | | 463 | } |
464 | | | 464 | |
465 | /* Now program new ones */ | | 465 | /* Now program new ones */ |
466 | ETHER_LOCK(ec); | | 466 | ETHER_LOCK(ec); |
467 | ETHER_FIRST_MULTI(step, ec, enm); | | 467 | ETHER_FIRST_MULTI(step, ec, enm); |
468 | while (enm != NULL) { | | 468 | while (enm != NULL) { |
469 | if (memcmp(enm->enm_addrlo, enm->enm_addrhi, | | 469 | if (memcmp(enm->enm_addrlo, enm->enm_addrhi, |
470 | ETHER_ADDR_LEN) != 0) { | | 470 | ETHER_ADDR_LEN) != 0) { |
471 | ETHER_UNLOCK(ec); | | 471 | ETHER_UNLOCK(ec); |
472 | goto allmulti; | | 472 | goto allmulti; |
473 | } | | 473 | } |
474 | | | 474 | |
475 | h = ether_crc32_be(enm->enm_addrlo, ETHER_ADDR_LEN) >> 26; | | 475 | h = ether_crc32_be(enm->enm_addrlo, ETHER_ADDR_LEN) >> 26; |
476 | hashtbl[h >> 3] |= 1U << (h & 7); | | 476 | hashtbl[h >> 3] |= 1U << (h & 7); |
477 | ETHER_NEXT_MULTI(step, enm); | | 477 | ETHER_NEXT_MULTI(step, enm); |
478 | } | | 478 | } |
479 | ETHER_UNLOCK(ec); | | 479 | ETHER_UNLOCK(ec); |
480 | ifp->if_flags &= ~IFF_ALLMULTI; | | 480 | ifp->if_flags &= ~IFF_ALLMULTI; |
481 | rxmode |= AXE_RXCMD_MULTICAST; | | 481 | rxmode |= AXE_RXCMD_MULTICAST; |
482 | | | 482 | |
483 | axe_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, hashtbl); | | 483 | axe_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, hashtbl); |
484 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); | | 484 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); |
485 | return; | | 485 | return; |
486 | | | 486 | |
487 | allmulti: | | 487 | allmulti: |
488 | ifp->if_flags |= IFF_ALLMULTI; | | 488 | ifp->if_flags |= IFF_ALLMULTI; |
489 | rxmode |= AXE_RXCMD_ALLMULTI; | | 489 | rxmode |= AXE_RXCMD_ALLMULTI; |
490 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); | | 490 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); |
491 | } | | 491 | } |
492 | | | 492 | |
493 | static void | | 493 | static void |
494 | axe_setiff(struct usbnet *un) | | 494 | axe_setiff(struct usbnet *un) |
495 | { | | 495 | { |
496 | usbnet_lock_mii(un); | | 496 | usbnet_lock_mii(un); |
497 | axe_setiff_locked(un); | | 497 | axe_setiff_locked(un); |
498 | usbnet_unlock_mii(un); | | 498 | usbnet_unlock_mii(un); |
499 | } | | 499 | } |
500 | | | 500 | |
501 | static void | | 501 | static void |
502 | axe_ax_init(struct usbnet *un) | | 502 | axe_ax_init(struct usbnet *un) |
503 | { | | 503 | { |
504 | struct axe_softc * const sc = usbnet_softc(un); | | 504 | struct axe_softc * const sc = usbnet_softc(un); |
505 | | | 505 | |
506 | int cmd = AXE_178_CMD_READ_NODEID; | | 506 | int cmd = AXE_178_CMD_READ_NODEID; |
507 | | | 507 | |
508 | if (un->un_flags & AX178) { | | 508 | if (un->un_flags & AX178) { |
509 | axe_ax88178_init(sc); | | 509 | axe_ax88178_init(sc); |
510 | } else if (un->un_flags & AX772) { | | 510 | } else if (un->un_flags & AX772) { |
511 | axe_ax88772_init(sc); | | 511 | axe_ax88772_init(sc); |
512 | } else if (un->un_flags & AX772A) { | | 512 | } else if (un->un_flags & AX772A) { |
513 | axe_ax88772a_init(sc); | | 513 | axe_ax88772a_init(sc); |
514 | } else if (un->un_flags & AX772B) { | | 514 | } else if (un->un_flags & AX772B) { |
515 | axe_ax88772b_init(sc); | | 515 | axe_ax88772b_init(sc); |
516 | return; | | 516 | return; |
517 | } else { | | 517 | } else { |
518 | cmd = AXE_172_CMD_READ_NODEID; | | 518 | cmd = AXE_172_CMD_READ_NODEID; |
519 | } | | 519 | } |
520 | | | 520 | |
521 | if (axe_cmd(sc, cmd, 0, 0, un->un_eaddr)) { | | 521 | if (axe_cmd(sc, cmd, 0, 0, un->un_eaddr)) { |
522 | aprint_error_dev(un->un_dev, | | 522 | aprint_error_dev(un->un_dev, |
523 | "failed to read ethernet address\n"); | | 523 | "failed to read ethernet address\n"); |
524 | } | | 524 | } |
525 | } | | 525 | } |
526 | | | 526 | |
527 | | | 527 | |
528 | static void | | 528 | static void |
529 | axe_reset(struct usbnet *un) | | 529 | axe_reset(struct usbnet *un) |
530 | { | | 530 | { |
531 | | | 531 | |
532 | usbnet_isowned_mii(un); | | 532 | usbnet_isowned_mii(un); |
533 | | | 533 | |
534 | if (usbnet_isdying(un)) | | 534 | if (usbnet_isdying(un)) |
535 | return; | | 535 | return; |
536 | | | 536 | |
537 | /* | | 537 | /* |
538 | * softnet_lock can be taken when NET_MPAFE is not defined when calling | | 538 | * softnet_lock can be taken when NET_MPAFE is not defined when calling |
539 | * if_addr_init -> if_init. This doesn't mix well with the | | 539 | * if_addr_init -> if_init. This doesn't mix well with the |
540 | * usbd_delay_ms calls in the init routines as things like nd6_slowtimo | | 540 | * usbd_delay_ms calls in the init routines as things like nd6_slowtimo |
541 | * can fire during the wait and attempt to take softnet_lock and then | | 541 | * can fire during the wait and attempt to take softnet_lock and then |
542 | * block the softclk thread meaning the wait never ends. | | 542 | * block the softclk thread meaning the wait never ends. |
543 | */ | | 543 | */ |
544 | #ifndef NET_MPSAFE | | 544 | #ifndef NET_MPSAFE |
545 | /* XXX What to reset? */ | | 545 | /* XXX What to reset? */ |
546 | | | 546 | |
547 | /* Wait a little while for the chip to get its brains in order. */ | | 547 | /* Wait a little while for the chip to get its brains in order. */ |
548 | DELAY(1000); | | 548 | DELAY(1000); |
549 | #else | | 549 | #else |
550 | axe_ax_init(un); | | 550 | axe_ax_init(un); |
551 | #endif | | 551 | #endif |
552 | } | | 552 | } |
553 | | | 553 | |
554 | static int | | 554 | static int |
555 | axe_get_phyno(struct axe_softc *sc, int sel) | | 555 | axe_get_phyno(struct axe_softc *sc, int sel) |
556 | { | | 556 | { |
557 | int phyno; | | 557 | int phyno; |
558 | | | 558 | |
559 | switch (AXE_PHY_TYPE(sc->axe_phyaddrs[sel])) { | | 559 | switch (AXE_PHY_TYPE(sc->axe_phyaddrs[sel])) { |
560 | case PHY_TYPE_100_HOME: | | 560 | case PHY_TYPE_100_HOME: |
561 | /* FALLTHROUGH */ | | 561 | /* FALLTHROUGH */ |
562 | case PHY_TYPE_GIG: | | 562 | case PHY_TYPE_GIG: |
563 | phyno = AXE_PHY_NO(sc->axe_phyaddrs[sel]); | | 563 | phyno = AXE_PHY_NO(sc->axe_phyaddrs[sel]); |
564 | break; | | 564 | break; |
565 | case PHY_TYPE_SPECIAL: | | 565 | case PHY_TYPE_SPECIAL: |
566 | /* FALLTHROUGH */ | | 566 | /* FALLTHROUGH */ |
567 | case PHY_TYPE_RSVD: | | 567 | case PHY_TYPE_RSVD: |
568 | /* FALLTHROUGH */ | | 568 | /* FALLTHROUGH */ |
569 | case PHY_TYPE_NON_SUP: | | 569 | case PHY_TYPE_NON_SUP: |
570 | /* FALLTHROUGH */ | | 570 | /* FALLTHROUGH */ |
571 | default: | | 571 | default: |
572 | phyno = -1; | | 572 | phyno = -1; |
573 | break; | | 573 | break; |
574 | } | | 574 | } |
575 | | | 575 | |
576 | return phyno; | | 576 | return phyno; |
577 | } | | 577 | } |
578 | | | 578 | |
579 | #define AXE_GPIO_WRITE(x, y) do { \ | | 579 | #define AXE_GPIO_WRITE(x, y) do { \ |
580 | axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, (x), NULL); \ | | 580 | axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, (x), NULL); \ |
581 | usbd_delay_ms(sc->axe_un.un_udev, hztoms(y)); \ | | 581 | usbd_delay_ms(sc->axe_un.un_udev, hztoms(y)); \ |
582 | } while (0) | | 582 | } while (0) |
583 | | | 583 | |
584 | static void | | 584 | static void |
585 | axe_ax88178_init(struct axe_softc *sc) | | 585 | axe_ax88178_init(struct axe_softc *sc) |
586 | { | | 586 | { |
587 | AXEHIST_FUNC(); AXEHIST_CALLED(); | | 587 | AXEHIST_FUNC(); AXEHIST_CALLED(); |
588 | struct usbnet * const un = &sc->axe_un; | | 588 | struct usbnet * const un = &sc->axe_un; |
589 | int gpio0, ledmode, phymode; | | 589 | int gpio0, ledmode, phymode; |
590 | uint16_t eeprom, val; | | 590 | uint16_t eeprom, val; |
591 | | | 591 | |
592 | axe_cmd(sc, AXE_CMD_SROM_WR_ENABLE, 0, 0, NULL); | | 592 | axe_cmd(sc, AXE_CMD_SROM_WR_ENABLE, 0, 0, NULL); |
593 | /* XXX magic */ | | 593 | /* XXX magic */ |
594 | if (axe_cmd(sc, AXE_CMD_SROM_READ, 0, 0x0017, &eeprom) != 0) | | 594 | if (axe_cmd(sc, AXE_CMD_SROM_READ, 0, 0x0017, &eeprom) != 0) |
595 | eeprom = 0xffff; | | 595 | eeprom = 0xffff; |
596 | axe_cmd(sc, AXE_CMD_SROM_WR_DISABLE, 0, 0, NULL); | | 596 | axe_cmd(sc, AXE_CMD_SROM_WR_DISABLE, 0, 0, NULL); |
597 | | | 597 | |
598 | eeprom = le16toh(eeprom); | | 598 | eeprom = le16toh(eeprom); |
599 | | | 599 | |
600 | DPRINTF("EEPROM is 0x%jx", eeprom, 0, 0, 0); | | 600 | DPRINTF("EEPROM is 0x%jx", eeprom, 0, 0, 0); |
601 | | | 601 | |
602 | /* if EEPROM is invalid we have to use to GPIO0 */ | | 602 | /* if EEPROM is invalid we have to use to GPIO0 */ |
603 | if (eeprom == 0xffff) { | | 603 | if (eeprom == 0xffff) { |
604 | phymode = AXE_PHY_MODE_MARVELL; | | 604 | phymode = AXE_PHY_MODE_MARVELL; |
605 | gpio0 = 1; | | 605 | gpio0 = 1; |
606 | ledmode = 0; | | 606 | ledmode = 0; |
607 | } else { | | 607 | } else { |
608 | phymode = eeprom & 0x7f; | | 608 | phymode = eeprom & 0x7f; |
609 | gpio0 = (eeprom & 0x80) ? 0 : 1; | | 609 | gpio0 = (eeprom & 0x80) ? 0 : 1; |
610 | ledmode = eeprom >> 8; | | 610 | ledmode = eeprom >> 8; |
611 | } | | 611 | } |
612 | | | 612 | |
613 | DPRINTF("use gpio0: %jd, phymode %jd", gpio0, phymode, 0, 0); | | 613 | DPRINTF("use gpio0: %jd, phymode %jd", gpio0, phymode, 0, 0); |
614 | | | 614 | |
615 | /* Program GPIOs depending on PHY hardware. */ | | 615 | /* Program GPIOs depending on PHY hardware. */ |
616 | switch (phymode) { | | 616 | switch (phymode) { |
617 | case AXE_PHY_MODE_MARVELL: | | 617 | case AXE_PHY_MODE_MARVELL: |
618 | if (gpio0 == 1) { | | 618 | if (gpio0 == 1) { |
619 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO0_EN, | | 619 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO0_EN, |
620 | hz / 32); | | 620 | hz / 32); |
621 | AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2 | AXE_GPIO2_EN, | | 621 | AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2 | AXE_GPIO2_EN, |
622 | hz / 32); | | 622 | hz / 32); |
623 | AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2_EN, hz / 4); | | 623 | AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2_EN, hz / 4); |
624 | AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2 | AXE_GPIO2_EN, | | 624 | AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2 | AXE_GPIO2_EN, |
625 | hz / 32); | | 625 | hz / 32); |
626 | } else { | | 626 | } else { |
627 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | | | 627 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | |
628 | AXE_GPIO1_EN, hz / 3); | | 628 | AXE_GPIO1_EN, hz / 3); |
629 | if (ledmode == 1) { | | 629 | if (ledmode == 1) { |
630 | AXE_GPIO_WRITE(AXE_GPIO1_EN, hz / 3); | | 630 | AXE_GPIO_WRITE(AXE_GPIO1_EN, hz / 3); |
631 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN, | | 631 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN, |
632 | hz / 3); | | 632 | hz / 3); |
633 | } else { | | 633 | } else { |
634 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | | | 634 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | |
635 | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); | | 635 | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); |
636 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | | | 636 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | |
637 | AXE_GPIO2_EN, hz / 4); | | 637 | AXE_GPIO2_EN, hz / 4); |
638 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | | | 638 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | |
639 | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); | | 639 | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); |
640 | } | | 640 | } |
641 | } | | 641 | } |
642 | break; | | 642 | break; |
643 | case AXE_PHY_MODE_CICADA: | | 643 | case AXE_PHY_MODE_CICADA: |
644 | case AXE_PHY_MODE_CICADA_V2: | | 644 | case AXE_PHY_MODE_CICADA_V2: |
645 | case AXE_PHY_MODE_CICADA_V2_ASIX: | | 645 | case AXE_PHY_MODE_CICADA_V2_ASIX: |
646 | if (gpio0 == 1) | | 646 | if (gpio0 == 1) |
647 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO0 | | | 647 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO0 | |
648 | AXE_GPIO0_EN, hz / 32); | | 648 | AXE_GPIO0_EN, hz / 32); |
649 | else | | 649 | else |
650 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | | | 650 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | |
651 | AXE_GPIO1_EN, hz / 32); | | 651 | AXE_GPIO1_EN, hz / 32); |
652 | break; | | 652 | break; |
653 | case AXE_PHY_MODE_AGERE: | | 653 | case AXE_PHY_MODE_AGERE: |
654 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | | | 654 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | |
655 | AXE_GPIO1_EN, hz / 32); | | 655 | AXE_GPIO1_EN, hz / 32); |
656 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2 | | | 656 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2 | |
657 | AXE_GPIO2_EN, hz / 32); | | 657 | AXE_GPIO2_EN, hz / 32); |
658 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2_EN, hz / 4); | | 658 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2_EN, hz / 4); |
659 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2 | | | 659 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2 | |
660 | AXE_GPIO2_EN, hz / 32); | | 660 | AXE_GPIO2_EN, hz / 32); |
661 | break; | | 661 | break; |
662 | case AXE_PHY_MODE_REALTEK_8211CL: | | 662 | case AXE_PHY_MODE_REALTEK_8211CL: |
663 | case AXE_PHY_MODE_REALTEK_8211BN: | | 663 | case AXE_PHY_MODE_REALTEK_8211BN: |
664 | case AXE_PHY_MODE_REALTEK_8251CL: | | 664 | case AXE_PHY_MODE_REALTEK_8251CL: |
665 | val = gpio0 == 1 ? AXE_GPIO0 | AXE_GPIO0_EN : | | 665 | val = gpio0 == 1 ? AXE_GPIO0 | AXE_GPIO0_EN : |
666 | AXE_GPIO1 | AXE_GPIO1_EN; | | 666 | AXE_GPIO1 | AXE_GPIO1_EN; |
667 | AXE_GPIO_WRITE(val, hz / 32); | | 667 | AXE_GPIO_WRITE(val, hz / 32); |
668 | AXE_GPIO_WRITE(val | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); | | 668 | AXE_GPIO_WRITE(val | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); |
669 | AXE_GPIO_WRITE(val | AXE_GPIO2_EN, hz / 4); | | 669 | AXE_GPIO_WRITE(val | AXE_GPIO2_EN, hz / 4); |
670 | AXE_GPIO_WRITE(val | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); | | 670 | AXE_GPIO_WRITE(val | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); |
671 | if (phymode == AXE_PHY_MODE_REALTEK_8211CL) { | | 671 | if (phymode == AXE_PHY_MODE_REALTEK_8211CL) { |
672 | axe_mii_write_reg(un, un->un_phyno, 0x1F, 0x0005); | | 672 | axe_mii_write_reg(un, un->un_phyno, 0x1F, 0x0005); |
673 | axe_mii_write_reg(un, un->un_phyno, 0x0C, 0x0000); | | 673 | axe_mii_write_reg(un, un->un_phyno, 0x0C, 0x0000); |
674 | axe_mii_read_reg(un, un->un_phyno, 0x0001, &val); | | 674 | axe_mii_read_reg(un, un->un_phyno, 0x0001, &val); |
675 | axe_mii_write_reg(un, un->un_phyno, 0x01, val | 0x0080); | | 675 | axe_mii_write_reg(un, un->un_phyno, 0x01, val | 0x0080); |
676 | axe_mii_write_reg(un, un->un_phyno, 0x1F, 0x0000); | | 676 | axe_mii_write_reg(un, un->un_phyno, 0x1F, 0x0000); |
677 | } | | 677 | } |
678 | break; | | 678 | break; |
679 | default: | | 679 | default: |
680 | /* Unknown PHY model or no need to program GPIOs. */ | | 680 | /* Unknown PHY model or no need to program GPIOs. */ |
681 | break; | | 681 | break; |
682 | } | | 682 | } |
683 | | | 683 | |
684 | /* soft reset */ | | 684 | /* soft reset */ |
685 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); | | 685 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); |
686 | usbd_delay_ms(un->un_udev, 150); | | 686 | usbd_delay_ms(un->un_udev, 150); |
687 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, | | 687 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, |
688 | AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL); | | 688 | AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL); |
689 | usbd_delay_ms(un->un_udev, 150); | | 689 | usbd_delay_ms(un->un_udev, 150); |
690 | /* Enable MII/GMII/RGMII interface to work with external PHY. */ | | 690 | /* Enable MII/GMII/RGMII interface to work with external PHY. */ |
691 | axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0, NULL); | | 691 | axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0, NULL); |
692 | usbd_delay_ms(un->un_udev, 10); | | 692 | usbd_delay_ms(un->un_udev, 10); |
693 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); | | 693 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); |
694 | } | | 694 | } |
695 | | | 695 | |
696 | static void | | 696 | static void |
697 | axe_ax88772_init(struct axe_softc *sc) | | 697 | axe_ax88772_init(struct axe_softc *sc) |
698 | { | | 698 | { |
699 | AXEHIST_FUNC(); AXEHIST_CALLED(); | | 699 | AXEHIST_FUNC(); AXEHIST_CALLED(); |
700 | struct usbnet * const un = &sc->axe_un; | | 700 | struct usbnet * const un = &sc->axe_un; |
701 | | | 701 | |
702 | axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL); | | 702 | axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL); |
703 | usbd_delay_ms(un->un_udev, 40); | | 703 | usbd_delay_ms(un->un_udev, 40); |
704 | | | 704 | |
705 | if (un->un_phyno == AXE_772_PHY_NO_EPHY) { | | 705 | if (un->un_phyno == AXE_772_PHY_NO_EPHY) { |
706 | /* ask for the embedded PHY */ | | 706 | /* ask for the embedded PHY */ |
707 | axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, | | 707 | axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, |
708 | AXE_SW_PHY_SELECT_EMBEDDED, NULL); | | 708 | AXE_SW_PHY_SELECT_EMBEDDED, NULL); |
709 | usbd_delay_ms(un->un_udev, 10); | | 709 | usbd_delay_ms(un->un_udev, 10); |
710 | | | 710 | |
711 | /* power down and reset state, pin reset state */ | | 711 | /* power down and reset state, pin reset state */ |
712 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); | | 712 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); |
713 | usbd_delay_ms(un->un_udev, 60); | | 713 | usbd_delay_ms(un->un_udev, 60); |
714 | | | 714 | |
715 | /* power down/reset state, pin operating state */ | | 715 | /* power down/reset state, pin operating state */ |
716 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, | | 716 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, |
717 | AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); | | 717 | AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); |
718 | usbd_delay_ms(un->un_udev, 150); | | 718 | usbd_delay_ms(un->un_udev, 150); |
719 | | | 719 | |
720 | /* power up, reset */ | | 720 | /* power up, reset */ |
721 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL, NULL); | | 721 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL, NULL); |
722 | | | 722 | |
723 | /* power up, operating */ | | 723 | /* power up, operating */ |
724 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, | | 724 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, |
725 | AXE_SW_RESET_IPRL | AXE_SW_RESET_PRL, NULL); | | 725 | AXE_SW_RESET_IPRL | AXE_SW_RESET_PRL, NULL); |
726 | } else { | | 726 | } else { |
727 | /* ask for external PHY */ | | 727 | /* ask for external PHY */ |
728 | axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, AXE_SW_PHY_SELECT_EXT, | | 728 | axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, AXE_SW_PHY_SELECT_EXT, |
729 | NULL); | | 729 | NULL); |
730 | usbd_delay_ms(un->un_udev, 10); | | 730 | usbd_delay_ms(un->un_udev, 10); |
731 | | | 731 | |
732 | /* power down internal PHY */ | | 732 | /* power down internal PHY */ |
733 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, | | 733 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, |
734 | AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); | | 734 | AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); |
735 | } | | 735 | } |
736 | | | 736 | |
737 | usbd_delay_ms(un->un_udev, 150); | | 737 | usbd_delay_ms(un->un_udev, 150); |
738 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); | | 738 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); |
739 | } | | 739 | } |
740 | | | 740 | |
741 | static void | | 741 | static void |
742 | axe_ax88772_phywake(struct axe_softc *sc) | | 742 | axe_ax88772_phywake(struct axe_softc *sc) |
743 | { | | 743 | { |
744 | AXEHIST_FUNC(); AXEHIST_CALLED(); | | 744 | AXEHIST_FUNC(); AXEHIST_CALLED(); |
745 | struct usbnet * const un = &sc->axe_un; | | 745 | struct usbnet * const un = &sc->axe_un; |
746 | | | 746 | |
747 | if (un->un_phyno == AXE_772_PHY_NO_EPHY) { | | 747 | if (un->un_phyno == AXE_772_PHY_NO_EPHY) { |
748 | /* Manually select internal(embedded) PHY - MAC mode. */ | | 748 | /* Manually select internal(embedded) PHY - MAC mode. */ |
749 | axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, | | 749 | axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, |
750 | AXE_SW_PHY_SELECT_EMBEDDED, NULL); | | 750 | AXE_SW_PHY_SELECT_EMBEDDED, NULL); |
751 | usbd_delay_ms(un->un_udev, hztoms(hz / 32)); | | 751 | usbd_delay_ms(un->un_udev, hztoms(hz / 32)); |
752 | } else { | | 752 | } else { |
753 | /* | | 753 | /* |
754 | * Manually select external PHY - MAC mode. | | 754 | * Manually select external PHY - MAC mode. |
755 | * Reverse MII/RMII is for AX88772A PHY mode. | | 755 | * Reverse MII/RMII is for AX88772A PHY mode. |
756 | */ | | 756 | */ |
757 | axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, AXE_SW_PHY_SELECT_SS_ENB | | | 757 | axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, AXE_SW_PHY_SELECT_SS_ENB | |
758 | AXE_SW_PHY_SELECT_EXT | AXE_SW_PHY_SELECT_SS_MII, NULL); | | 758 | AXE_SW_PHY_SELECT_EXT | AXE_SW_PHY_SELECT_SS_MII, NULL); |
759 | usbd_delay_ms(un->un_udev, hztoms(hz / 32)); | | 759 | usbd_delay_ms(un->un_udev, hztoms(hz / 32)); |
760 | } | | 760 | } |
761 | | | 761 | |
762 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPPD | | | 762 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPPD | |
763 | AXE_SW_RESET_IPRL, NULL); | | 763 | AXE_SW_RESET_IPRL, NULL); |
764 | | | 764 | |
765 | /* T1 = min 500ns everywhere */ | | 765 | /* T1 = min 500ns everywhere */ |
766 | usbd_delay_ms(un->un_udev, 150); | | 766 | usbd_delay_ms(un->un_udev, 150); |
767 | | | 767 | |
768 | /* Take PHY out of power down. */ | | 768 | /* Take PHY out of power down. */ |
769 | if (un->un_phyno == AXE_772_PHY_NO_EPHY) { | | 769 | if (un->un_phyno == AXE_772_PHY_NO_EPHY) { |
770 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPRL, NULL); | | 770 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPRL, NULL); |
771 | } else { | | 771 | } else { |
772 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRTE, NULL); | | 772 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRTE, NULL); |
773 | } | | 773 | } |
774 | | | 774 | |
775 | /* 772 T2 is 60ms. 772A T2 is 160ms, 772B T2 is 600ms */ | | 775 | /* 772 T2 is 60ms. 772A T2 is 160ms, 772B T2 is 600ms */ |
776 | usbd_delay_ms(un->un_udev, 600); | | 776 | usbd_delay_ms(un->un_udev, 600); |
777 | | | 777 | |
778 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); | | 778 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); |
779 | | | 779 | |
780 | /* T3 = 500ns everywhere */ | | 780 | /* T3 = 500ns everywhere */ |
781 | usbd_delay_ms(un->un_udev, hztoms(hz / 32)); | | 781 | usbd_delay_ms(un->un_udev, hztoms(hz / 32)); |
782 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPRL, NULL); | | 782 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPRL, NULL); |
783 | usbd_delay_ms(un->un_udev, hztoms(hz / 32)); | | 783 | usbd_delay_ms(un->un_udev, hztoms(hz / 32)); |
784 | } | | 784 | } |
785 | | | 785 | |
786 | static void | | 786 | static void |
787 | axe_ax88772a_init(struct axe_softc *sc) | | 787 | axe_ax88772a_init(struct axe_softc *sc) |
788 | { | | 788 | { |
789 | AXEHIST_FUNC(); AXEHIST_CALLED(); | | 789 | AXEHIST_FUNC(); AXEHIST_CALLED(); |
790 | | | 790 | |
791 | /* Reload EEPROM. */ | | 791 | /* Reload EEPROM. */ |
792 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM, hz / 32); | | 792 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM, hz / 32); |
793 | axe_ax88772_phywake(sc); | | 793 | axe_ax88772_phywake(sc); |
794 | /* Stop MAC. */ | | 794 | /* Stop MAC. */ |
795 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); | | 795 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); |
796 | } | | 796 | } |
797 | | | 797 | |
798 | static void | | 798 | static void |
799 | axe_ax88772b_init(struct axe_softc *sc) | | 799 | axe_ax88772b_init(struct axe_softc *sc) |
800 | { | | 800 | { |
801 | AXEHIST_FUNC(); AXEHIST_CALLED(); | | 801 | AXEHIST_FUNC(); AXEHIST_CALLED(); |
802 | struct usbnet * const un = &sc->axe_un; | | 802 | struct usbnet * const un = &sc->axe_un; |
803 | uint16_t eeprom; | | 803 | uint16_t eeprom; |
804 | int i; | | 804 | int i; |
805 | | | 805 | |
806 | /* Reload EEPROM. */ | | 806 | /* Reload EEPROM. */ |
807 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM , hz / 32); | | 807 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM , hz / 32); |
808 | | | 808 | |
809 | /* | | 809 | /* |
810 | * Save PHY power saving configuration(high byte) and | | 810 | * Save PHY power saving configuration(high byte) and |
811 | * clear EEPROM checksum value(low byte). | | 811 | * clear EEPROM checksum value(low byte). |
812 | */ | | 812 | */ |
813 | if (axe_cmd(sc, AXE_CMD_SROM_READ, 0, AXE_EEPROM_772B_PHY_PWRCFG, | | 813 | if (axe_cmd(sc, AXE_CMD_SROM_READ, 0, AXE_EEPROM_772B_PHY_PWRCFG, |
814 | &eeprom)) { | | 814 | &eeprom)) { |
815 | aprint_error_dev(un->un_dev, "failed to read eeprom\n"); | | 815 | aprint_error_dev(un->un_dev, "failed to read eeprom\n"); |
816 | return; | | 816 | return; |
817 | } | | 817 | } |
818 | | | 818 | |
819 | sc->sc_pwrcfg = le16toh(eeprom) & 0xFF00; | | 819 | sc->sc_pwrcfg = le16toh(eeprom) & 0xFF00; |
820 | | | 820 | |
821 | /* | | 821 | /* |
822 | * Auto-loaded default station address from internal ROM is | | 822 | * Auto-loaded default station address from internal ROM is |
823 | * 00:00:00:00:00:00 such that an explicit access to EEPROM | | 823 | * 00:00:00:00:00:00 such that an explicit access to EEPROM |
824 | * is required to get real station address. | | 824 | * is required to get real station address. |
825 | */ | | 825 | */ |
826 | uint8_t *eaddr = un->un_eaddr; | | 826 | uint8_t *eaddr = un->un_eaddr; |
827 | for (i = 0; i < ETHER_ADDR_LEN / 2; i++) { | | 827 | for (i = 0; i < ETHER_ADDR_LEN / 2; i++) { |
828 | if (axe_cmd(sc, AXE_CMD_SROM_READ, 0, | | 828 | if (axe_cmd(sc, AXE_CMD_SROM_READ, 0, |
829 | AXE_EEPROM_772B_NODE_ID + i, &eeprom)) { | | 829 | AXE_EEPROM_772B_NODE_ID + i, &eeprom)) { |
830 | aprint_error_dev(un->un_dev, | | 830 | aprint_error_dev(un->un_dev, |
831 | "failed to read eeprom\n"); | | 831 | "failed to read eeprom\n"); |
832 | eeprom = 0; | | 832 | eeprom = 0; |
833 | } | | 833 | } |
834 | eeprom = le16toh(eeprom); | | 834 | eeprom = le16toh(eeprom); |
835 | *eaddr++ = (uint8_t)(eeprom & 0xFF); | | 835 | *eaddr++ = (uint8_t)(eeprom & 0xFF); |
836 | *eaddr++ = (uint8_t)((eeprom >> 8) & 0xFF); | | 836 | *eaddr++ = (uint8_t)((eeprom >> 8) & 0xFF); |
837 | } | | 837 | } |
838 | /* Wakeup PHY. */ | | 838 | /* Wakeup PHY. */ |
839 | axe_ax88772_phywake(sc); | | 839 | axe_ax88772_phywake(sc); |
840 | /* Stop MAC. */ | | 840 | /* Stop MAC. */ |
841 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); | | 841 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); |
842 | } | | 842 | } |
843 | | | 843 | |
844 | #undef AXE_GPIO_WRITE | | 844 | #undef AXE_GPIO_WRITE |
845 | | | 845 | |
846 | /* | | 846 | /* |
847 | * Probe for a AX88172 chip. | | 847 | * Probe for a AX88172 chip. |
848 | */ | | 848 | */ |
849 | int | | 849 | int |
850 | axe_match(device_t parent, cfdata_t match, void *aux) | | 850 | axe_match(device_t parent, cfdata_t match, void *aux) |
851 | { | | 851 | { |
852 | struct usb_attach_arg *uaa = aux; | | 852 | struct usb_attach_arg *uaa = aux; |
853 | | | 853 | |
854 | return axe_lookup(uaa->uaa_vendor, uaa->uaa_product) != NULL ? | | 854 | return axe_lookup(uaa->uaa_vendor, uaa->uaa_product) != NULL ? |
855 | UMATCH_VENDOR_PRODUCT : UMATCH_NONE; | | 855 | UMATCH_VENDOR_PRODUCT : UMATCH_NONE; |
856 | } | | 856 | } |
857 | | | 857 | |
858 | /* | | 858 | /* |
859 | * Attach the interface. Allocate softc structures, do ifmedia | | 859 | * Attach the interface. Allocate softc structures, do ifmedia |
860 | * setup and ethernet/BPF attach. | | 860 | * setup and ethernet/BPF attach. |
861 | */ | | 861 | */ |
862 | void | | 862 | void |
863 | axe_attach(device_t parent, device_t self, void *aux) | | 863 | axe_attach(device_t parent, device_t self, void *aux) |
864 | { | | 864 | { |
865 | AXEHIST_FUNC(); AXEHIST_CALLED(); | | 865 | AXEHIST_FUNC(); AXEHIST_CALLED(); |
866 | USBNET_MII_DECL_DEFAULT(unm); | | 866 | USBNET_MII_DECL_DEFAULT(unm); |
867 | struct axe_softc *sc = device_private(self); | | 867 | struct axe_softc *sc = device_private(self); |
868 | struct usbnet * const un = &sc->axe_un; | | 868 | struct usbnet * const un = &sc->axe_un; |
869 | struct usb_attach_arg *uaa = aux; | | 869 | struct usb_attach_arg *uaa = aux; |
870 | struct usbd_device *dev = uaa->uaa_device; | | 870 | struct usbd_device *dev = uaa->uaa_device; |
871 | usbd_status err; | | 871 | usbd_status err; |
872 | usb_interface_descriptor_t *id; | | 872 | usb_interface_descriptor_t *id; |
873 | usb_endpoint_descriptor_t *ed; | | 873 | usb_endpoint_descriptor_t *ed; |
874 | char *devinfop; | | 874 | char *devinfop; |
875 | unsigned bufsz; | | 875 | unsigned bufsz; |
876 | int i; | | 876 | int i; |
877 | | | 877 | |
878 | KASSERT((void *)sc == un); | | 878 | KASSERT((void *)sc == un); |
879 | | | 879 | |
880 | aprint_naive("\n"); | | 880 | aprint_naive("\n"); |
881 | aprint_normal("\n"); | | 881 | aprint_normal("\n"); |
882 | devinfop = usbd_devinfo_alloc(dev, 0); | | 882 | devinfop = usbd_devinfo_alloc(dev, 0); |
883 | aprint_normal_dev(self, "%s\n", devinfop); | | 883 | aprint_normal_dev(self, "%s\n", devinfop); |
884 | usbd_devinfo_free(devinfop); | | 884 | usbd_devinfo_free(devinfop); |
885 | | | 885 | |
886 | un->un_dev = self; | | 886 | un->un_dev = self; |
887 | un->un_udev = dev; | | 887 | un->un_udev = dev; |
888 | un->un_sc = sc; | | 888 | un->un_sc = sc; |
889 | un->un_ops = &axe_ops; | | 889 | un->un_ops = &axe_ops; |
890 | un->un_rx_xfer_flags = USBD_SHORT_XFER_OK; | | 890 | un->un_rx_xfer_flags = USBD_SHORT_XFER_OK; |
891 | un->un_tx_xfer_flags = USBD_FORCE_SHORT_XFER; | | 891 | un->un_tx_xfer_flags = USBD_FORCE_SHORT_XFER; |
892 | un->un_rx_list_cnt = AXE_RX_LIST_CNT; | | 892 | un->un_rx_list_cnt = AXE_RX_LIST_CNT; |
893 | un->un_tx_list_cnt = AXE_TX_LIST_CNT; | | 893 | un->un_tx_list_cnt = AXE_TX_LIST_CNT; |
894 | | | 894 | |
895 | err = usbd_set_config_no(dev, AXE_CONFIG_NO, 1); | | 895 | err = usbd_set_config_no(dev, AXE_CONFIG_NO, 1); |
896 | if (err) { | | 896 | if (err) { |
897 | aprint_error_dev(self, "failed to set configuration" | | 897 | aprint_error_dev(self, "failed to set configuration" |
898 | ", err=%s\n", usbd_errstr(err)); | | 898 | ", err=%s\n", usbd_errstr(err)); |
899 | return; | | 899 | return; |
900 | } | | 900 | } |
901 | | | 901 | |
902 | un->un_flags = axe_lookup(uaa->uaa_vendor, uaa->uaa_product)->axe_flags; | | 902 | un->un_flags = axe_lookup(uaa->uaa_vendor, uaa->uaa_product)->axe_flags; |
903 | | | 903 | |
904 | err = usbd_device2interface_handle(dev, AXE_IFACE_IDX, &un->un_iface); | | 904 | err = usbd_device2interface_handle(dev, AXE_IFACE_IDX, &un->un_iface); |
905 | if (err) { | | 905 | if (err) { |
906 | aprint_error_dev(self, "getting interface handle failed\n"); | | 906 | aprint_error_dev(self, "getting interface handle failed\n"); |
907 | return; | | 907 | return; |
908 | } | | 908 | } |
909 | | | 909 | |
910 | id = usbd_get_interface_descriptor(un->un_iface); | | 910 | id = usbd_get_interface_descriptor(un->un_iface); |
911 | | | 911 | |
912 | /* decide on what our bufsize will be */ | | 912 | /* decide on what our bufsize will be */ |
913 | if (AXE_IS_178_FAMILY(un)) | | 913 | if (AXE_IS_178_FAMILY(un)) |
914 | bufsz = (un->un_udev->ud_speed == USB_SPEED_HIGH) ? | | 914 | bufsz = (un->un_udev->ud_speed == USB_SPEED_HIGH) ? |
915 | AXE_178_MAX_BUFSZ : AXE_178_MIN_BUFSZ; | | 915 | AXE_178_MAX_BUFSZ : AXE_178_MIN_BUFSZ; |
916 | else | | 916 | else |
917 | bufsz = AXE_172_BUFSZ; | | 917 | bufsz = AXE_172_BUFSZ; |
918 | un->un_rx_bufsz = un->un_tx_bufsz = bufsz; | | 918 | un->un_rx_bufsz = un->un_tx_bufsz = bufsz; |
919 | | | 919 | |
920 | un->un_ed[USBNET_ENDPT_RX] = 0; | | 920 | un->un_ed[USBNET_ENDPT_RX] = 0; |
921 | un->un_ed[USBNET_ENDPT_TX] = 0; | | 921 | un->un_ed[USBNET_ENDPT_TX] = 0; |
922 | un->un_ed[USBNET_ENDPT_INTR] = 0; | | 922 | un->un_ed[USBNET_ENDPT_INTR] = 0; |
923 | | | 923 | |
924 | /* Find endpoints. */ | | 924 | /* Find endpoints. */ |
925 | for (i = 0; i < id->bNumEndpoints; i++) { | | 925 | for (i = 0; i < id->bNumEndpoints; i++) { |
926 | ed = usbd_interface2endpoint_descriptor(un->un_iface, i); | | 926 | ed = usbd_interface2endpoint_descriptor(un->un_iface, i); |
927 | if (ed == NULL) { | | 927 | if (ed == NULL) { |
928 | aprint_error_dev(self, "couldn't get ep %d\n", i); | | 928 | aprint_error_dev(self, "couldn't get ep %d\n", i); |
929 | return; | | 929 | return; |
930 | } | | 930 | } |
931 | const uint8_t xt = UE_GET_XFERTYPE(ed->bmAttributes); | | 931 | const uint8_t xt = UE_GET_XFERTYPE(ed->bmAttributes); |
932 | const uint8_t dir = UE_GET_DIR(ed->bEndpointAddress); | | 932 | const uint8_t dir = UE_GET_DIR(ed->bEndpointAddress); |
933 | | | 933 | |
934 | if (dir == UE_DIR_IN && xt == UE_BULK && | | 934 | if (dir == UE_DIR_IN && xt == UE_BULK && |
935 | un->un_ed[USBNET_ENDPT_RX] == 0) { | | 935 | un->un_ed[USBNET_ENDPT_RX] == 0) { |
936 | un->un_ed[USBNET_ENDPT_RX] = ed->bEndpointAddress; | | 936 | un->un_ed[USBNET_ENDPT_RX] = ed->bEndpointAddress; |
937 | } else if (dir == UE_DIR_OUT && xt == UE_BULK && | | 937 | } else if (dir == UE_DIR_OUT && xt == UE_BULK && |
938 | un->un_ed[USBNET_ENDPT_TX] == 0) { | | 938 | un->un_ed[USBNET_ENDPT_TX] == 0) { |
939 | un->un_ed[USBNET_ENDPT_TX] = ed->bEndpointAddress; | | 939 | un->un_ed[USBNET_ENDPT_TX] = ed->bEndpointAddress; |
940 | } else if (dir == UE_DIR_IN && xt == UE_INTERRUPT) { | | 940 | } else if (dir == UE_DIR_IN && xt == UE_INTERRUPT) { |
941 | un->un_ed[USBNET_ENDPT_INTR] = ed->bEndpointAddress; | | 941 | un->un_ed[USBNET_ENDPT_INTR] = ed->bEndpointAddress; |
942 | } | | 942 | } |
943 | } | | 943 | } |
944 | | | 944 | |
945 | /* Set these up now for axe_cmd(). */ | | 945 | /* Set these up now for axe_cmd(). */ |
946 | usbnet_attach(un, "axedet"); | | 946 | usbnet_attach(un, "axedet"); |
947 | | | 947 | |
948 | /* We need the PHYID for init dance in some cases */ | | 948 | /* We need the PHYID for init dance in some cases */ |
949 | usbnet_lock_mii(un); | | 949 | usbnet_lock_mii(un); |
950 | if (axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, &sc->axe_phyaddrs)) { | | 950 | if (axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, &sc->axe_phyaddrs)) { |
951 | aprint_error_dev(self, "failed to read phyaddrs\n"); | | 951 | aprint_error_dev(self, "failed to read phyaddrs\n"); |
952 | | | 952 | |
953 | return; | | 953 | return; |
954 | } | | 954 | } |
955 | | | 955 | |
956 | DPRINTF(" phyaddrs[0]: %jx phyaddrs[1]: %jx", | | 956 | DPRINTF(" phyaddrs[0]: %jx phyaddrs[1]: %jx", |
957 | sc->axe_phyaddrs[0], sc->axe_phyaddrs[1], 0, 0); | | 957 | sc->axe_phyaddrs[0], sc->axe_phyaddrs[1], 0, 0); |
958 | un->un_phyno = axe_get_phyno(sc, AXE_PHY_SEL_PRI); | | 958 | un->un_phyno = axe_get_phyno(sc, AXE_PHY_SEL_PRI); |
959 | if (un->un_phyno == -1) | | 959 | if (un->un_phyno == -1) |
960 | un->un_phyno = axe_get_phyno(sc, AXE_PHY_SEL_SEC); | | 960 | un->un_phyno = axe_get_phyno(sc, AXE_PHY_SEL_SEC); |
961 | if (un->un_phyno == -1) { | | 961 | if (un->un_phyno == -1) { |
962 | DPRINTF(" no valid PHY address found, assuming PHY address 0", | | 962 | DPRINTF(" no valid PHY address found, assuming PHY address 0", |
963 | 0, 0, 0, 0); | | 963 | 0, 0, 0, 0); |
964 | un->un_phyno = 0; | | 964 | un->un_phyno = 0; |
965 | } | | 965 | } |
966 | | | 966 | |
967 | /* Initialize controller and get station address. */ | | 967 | /* Initialize controller and get station address. */ |
968 | | | 968 | |
969 | axe_ax_init(un); | | 969 | axe_ax_init(un); |
970 | | | 970 | |
971 | /* | | 971 | /* |
972 | * Fetch IPG values. | | 972 | * Fetch IPG values. |
973 | */ | | 973 | */ |
974 | if (un->un_flags & (AX772A | AX772B)) { | | 974 | if (un->un_flags & (AX772A | AX772B)) { |
975 | /* Set IPG values. */ | | 975 | /* Set IPG values. */ |
976 | sc->axe_ipgs[0] = AXE_IPG0_DEFAULT; | | 976 | sc->axe_ipgs[0] = AXE_IPG0_DEFAULT; |
977 | sc->axe_ipgs[1] = AXE_IPG1_DEFAULT; | | 977 | sc->axe_ipgs[1] = AXE_IPG1_DEFAULT; |
978 | sc->axe_ipgs[2] = AXE_IPG2_DEFAULT; | | 978 | sc->axe_ipgs[2] = AXE_IPG2_DEFAULT; |
979 | } else { | | 979 | } else { |
980 | if (axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->axe_ipgs)) { | | 980 | if (axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->axe_ipgs)) { |
981 | aprint_error_dev(self, "failed to read ipg\n"); | | 981 | aprint_error_dev(self, "failed to read ipg\n"); |
982 | usbnet_unlock_mii(un); | | 982 | usbnet_unlock_mii(un); |
983 | return; | | 983 | return; |
984 | } | | 984 | } |
985 | } | | 985 | } |
986 | | | 986 | |
987 | usbnet_unlock_mii(un); | | 987 | usbnet_unlock_mii(un); |
988 | | | 988 | |
989 | if (AXE_IS_178_FAMILY(un)) | | 989 | if (AXE_IS_178_FAMILY(un)) |
990 | usbnet_ec(un)->ec_capabilities = ETHERCAP_VLAN_MTU; | | 990 | usbnet_ec(un)->ec_capabilities = ETHERCAP_VLAN_MTU; |
991 | if (un->un_flags & AX772B) { | | 991 | if (un->un_flags & AX772B) { |
992 | struct ifnet *ifp = usbnet_ifp(un); | | 992 | struct ifnet *ifp = usbnet_ifp(un); |
993 | | | 993 | |
994 | ifp->if_capabilities = | | 994 | ifp->if_capabilities = |
995 | IFCAP_CSUM_IPv4_Rx | | | 995 | IFCAP_CSUM_IPv4_Rx | |
996 | IFCAP_CSUM_TCPv4_Rx | IFCAP_CSUM_UDPv4_Rx | | | 996 | IFCAP_CSUM_TCPv4_Rx | IFCAP_CSUM_UDPv4_Rx | |
997 | IFCAP_CSUM_TCPv6_Rx | IFCAP_CSUM_UDPv6_Rx; | | 997 | IFCAP_CSUM_TCPv6_Rx | IFCAP_CSUM_UDPv6_Rx; |
998 | /* | | 998 | /* |
999 | * Checksum offloading of AX88772B also works with VLAN | | 999 | * Checksum offloading of AX88772B also works with VLAN |
1000 | * tagged frames but there is no way to take advantage | | 1000 | * tagged frames but there is no way to take advantage |
1001 | * of the feature because vlan(4) assumes | | 1001 | * of the feature because vlan(4) assumes |
1002 | * IFCAP_VLAN_HWTAGGING is prerequisite condition to | | 1002 | * IFCAP_VLAN_HWTAGGING is prerequisite condition to |
1003 | * support checksum offloading with VLAN. VLAN hardware | | 1003 | * support checksum offloading with VLAN. VLAN hardware |
1004 | * tagging support of AX88772B is very limited so it's | | 1004 | * tagging support of AX88772B is very limited so it's |
1005 | * not possible to announce IFCAP_VLAN_HWTAGGING. | | 1005 | * not possible to announce IFCAP_VLAN_HWTAGGING. |
1006 | */ | | 1006 | */ |
1007 | } | | 1007 | } |
1008 | u_int adv_pause; | | 1008 | u_int adv_pause; |
1009 | if (un->un_flags & (AX772A | AX772B | AX178)) | | 1009 | if (un->un_flags & (AX772A | AX772B | AX178)) |
1010 | adv_pause = MIIF_DOPAUSE; | | 1010 | adv_pause = MIIF_DOPAUSE; |
1011 | else | | 1011 | else |
1012 | adv_pause = 0; | | 1012 | adv_pause = 0; |
1013 | adv_pause = 0; | | 1013 | adv_pause = 0; |
1014 | | | 1014 | |
1015 | unm.un_mii_flags = adv_pause; | | 1015 | unm.un_mii_flags = adv_pause; |
1016 | usbnet_attach_ifp(un, IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST, | | 1016 | usbnet_attach_ifp(un, IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST, |
1017 | 0, &unm); | | 1017 | 0, &unm); |
1018 | } | | 1018 | } |
1019 | | | 1019 | |
1020 | static void | | 1020 | static void |
1021 | axe_rx_loop(struct usbnet * un, struct usbnet_chain *c, uint32_t total_len) | | 1021 | axe_rx_loop(struct usbnet * un, struct usbnet_chain *c, uint32_t total_len) |
1022 | { | | 1022 | { |
1023 | AXEHIST_FUNC(); AXEHIST_CALLED(); | | 1023 | AXEHIST_FUNC(); AXEHIST_CALLED(); |
1024 | struct axe_softc * const sc = usbnet_softc(un); | | 1024 | struct axe_softc * const sc = usbnet_softc(un); |
1025 | struct ifnet *ifp = usbnet_ifp(un); | | 1025 | struct ifnet *ifp = usbnet_ifp(un); |
1026 | uint8_t *buf = c->unc_buf; | | 1026 | uint8_t *buf = c->unc_buf; |
1027 | | | 1027 | |
1028 | do { | | 1028 | do { |
1029 | u_int pktlen = 0; | | 1029 | u_int pktlen = 0; |
1030 | u_int rxlen = 0; | | 1030 | u_int rxlen = 0; |
1031 | int flags = 0; | | 1031 | int flags = 0; |
1032 | | | 1032 | |
1033 | if ((un->un_flags & AXSTD_FRAME) != 0) { | | 1033 | if ((un->un_flags & AXSTD_FRAME) != 0) { |
1034 | struct axe_sframe_hdr hdr; | | 1034 | struct axe_sframe_hdr hdr; |
1035 | | | 1035 | |
1036 | if (total_len < sizeof(hdr)) { | | 1036 | if (total_len < sizeof(hdr)) { |
1037 | ifp->if_ierrors++; | | 1037 | ifp->if_ierrors++; |
1038 | break; | | 1038 | break; |
1039 | } | | 1039 | } |
1040 | | | 1040 | |
1041 | #if !defined(__NO_STRICT_ALIGNMENT) && __GNUC_PREREQ__(6, 1) | | | |
1042 | /* | | | |
1043 | * XXX hdr is 2-byte aligned in buf, not 4-byte. | | | |
1044 | * For some architectures, __builtin_memcpy() of | | | |
1045 | * GCC 6 attempts to copy sizeof(hdr) = 4 bytes | | | |
1046 | * at onece, which results in alignment error. | | | |
1047 | */ | | | |
1048 | hdr.len = *(uint16_t *)buf; | | | |
1049 | hdr.ilen = *(uint16_t *)(buf + sizeof(uint16_t)); | | | |
1050 | #else | | | |
1051 | memcpy(&hdr, buf, sizeof(hdr)); | | 1041 | memcpy(&hdr, buf, sizeof(hdr)); |
1052 | #endif | | | |
1053 | | | 1042 | |
1054 | DPRINTFN(20, "total_len %#jx len %jx ilen %#jx", | | 1043 | DPRINTFN(20, "total_len %#jx len %jx ilen %#jx", |
1055 | total_len, | | 1044 | total_len, |
1056 | (le16toh(hdr.len) & AXE_RH1M_RXLEN_MASK), | | 1045 | (le16toh(hdr.len) & AXE_RH1M_RXLEN_MASK), |
1057 | (le16toh(hdr.ilen) & AXE_RH1M_RXLEN_MASK), 0); | | 1046 | (le16toh(hdr.ilen) & AXE_RH1M_RXLEN_MASK), 0); |
1058 | | | 1047 | |
1059 | total_len -= sizeof(hdr); | | 1048 | total_len -= sizeof(hdr); |
1060 | buf += sizeof(hdr); | | 1049 | buf += sizeof(hdr); |
1061 | | | 1050 | |
1062 | if (((le16toh(hdr.len) & AXE_RH1M_RXLEN_MASK) ^ | | 1051 | if (((le16toh(hdr.len) & AXE_RH1M_RXLEN_MASK) ^ |
1063 | (le16toh(hdr.ilen) & AXE_RH1M_RXLEN_MASK)) != | | 1052 | (le16toh(hdr.ilen) & AXE_RH1M_RXLEN_MASK)) != |
1064 | AXE_RH1M_RXLEN_MASK) { | | 1053 | AXE_RH1M_RXLEN_MASK) { |
1065 | ifp->if_ierrors++; | | 1054 | ifp->if_ierrors++; |
1066 | break; | | 1055 | break; |
1067 | } | | 1056 | } |
1068 | | | 1057 | |
1069 | rxlen = le16toh(hdr.len) & AXE_RH1M_RXLEN_MASK; | | 1058 | rxlen = le16toh(hdr.len) & AXE_RH1M_RXLEN_MASK; |
1070 | if (total_len < rxlen) { | | 1059 | if (total_len < rxlen) { |
1071 | pktlen = total_len; | | 1060 | pktlen = total_len; |
1072 | total_len = 0; | | 1061 | total_len = 0; |
1073 | } else { | | 1062 | } else { |
1074 | pktlen = rxlen; | | 1063 | pktlen = rxlen; |
1075 | rxlen = roundup2(rxlen, 2); | | 1064 | rxlen = roundup2(rxlen, 2); |
1076 | total_len -= rxlen; | | 1065 | total_len -= rxlen; |
1077 | } | | 1066 | } |
1078 | | | 1067 | |
1079 | } else if ((un->un_flags & AXCSUM_FRAME) != 0) { | | 1068 | } else if ((un->un_flags & AXCSUM_FRAME) != 0) { |
1080 | struct axe_csum_hdr csum_hdr; | | 1069 | struct axe_csum_hdr csum_hdr; |
1081 | | | 1070 | |
1082 | if (total_len < sizeof(csum_hdr)) { | | 1071 | if (total_len < sizeof(csum_hdr)) { |
1083 | ifp->if_ierrors++; | | 1072 | ifp->if_ierrors++; |
1084 | break; | | 1073 | break; |
1085 | } | | 1074 | } |
1086 | | | 1075 | |
1087 | memcpy(&csum_hdr, buf, sizeof(csum_hdr)); | | 1076 | memcpy(&csum_hdr, buf, sizeof(csum_hdr)); |
1088 | | | 1077 | |
1089 | csum_hdr.len = le16toh(csum_hdr.len); | | 1078 | csum_hdr.len = le16toh(csum_hdr.len); |
1090 | csum_hdr.ilen = le16toh(csum_hdr.ilen); | | 1079 | csum_hdr.ilen = le16toh(csum_hdr.ilen); |
1091 | csum_hdr.cstatus = le16toh(csum_hdr.cstatus); | | 1080 | csum_hdr.cstatus = le16toh(csum_hdr.cstatus); |
1092 | | | 1081 | |
1093 | DPRINTFN(20, "total_len %#jx len %#jx ilen %#jx" | | 1082 | DPRINTFN(20, "total_len %#jx len %#jx ilen %#jx" |
1094 | " cstatus %#jx", total_len, | | 1083 | " cstatus %#jx", total_len, |
1095 | csum_hdr.len, csum_hdr.ilen, csum_hdr.cstatus); | | 1084 | csum_hdr.len, csum_hdr.ilen, csum_hdr.cstatus); |
1096 | | | 1085 | |
1097 | if ((AXE_CSUM_RXBYTES(csum_hdr.len) ^ | | 1086 | if ((AXE_CSUM_RXBYTES(csum_hdr.len) ^ |
1098 | AXE_CSUM_RXBYTES(csum_hdr.ilen)) != | | 1087 | AXE_CSUM_RXBYTES(csum_hdr.ilen)) != |
1099 | sc->sc_lenmask) { | | 1088 | sc->sc_lenmask) { |
1100 | /* we lost sync */ | | 1089 | /* we lost sync */ |
1101 | ifp->if_ierrors++; | | 1090 | ifp->if_ierrors++; |
1102 | DPRINTFN(20, "len %#jx ilen %#jx lenmask %#jx " | | 1091 | DPRINTFN(20, "len %#jx ilen %#jx lenmask %#jx " |
1103 | "err", | | 1092 | "err", |
1104 | AXE_CSUM_RXBYTES(csum_hdr.len), | | 1093 | AXE_CSUM_RXBYTES(csum_hdr.len), |
1105 | AXE_CSUM_RXBYTES(csum_hdr.ilen), | | 1094 | AXE_CSUM_RXBYTES(csum_hdr.ilen), |
1106 | sc->sc_lenmask, 0); | | 1095 | sc->sc_lenmask, 0); |
1107 | break; | | 1096 | break; |
1108 | } | | 1097 | } |
1109 | /* | | 1098 | /* |
1110 | * Get total transferred frame length including | | 1099 | * Get total transferred frame length including |
1111 | * checksum header. The length should be multiple | | 1100 | * checksum header. The length should be multiple |
1112 | * of 4. | | 1101 | * of 4. |
1113 | */ | | 1102 | */ |
1114 | pktlen = AXE_CSUM_RXBYTES(csum_hdr.len); | | 1103 | pktlen = AXE_CSUM_RXBYTES(csum_hdr.len); |
1115 | u_int len = sizeof(csum_hdr) + pktlen; | | 1104 | u_int len = sizeof(csum_hdr) + pktlen; |
1116 | len = (len + 3) & ~3; | | 1105 | len = (len + 3) & ~3; |
1117 | if (total_len < len) { | | 1106 | if (total_len < len) { |
1118 | DPRINTFN(20, "total_len %#jx < len %#jx", | | 1107 | DPRINTFN(20, "total_len %#jx < len %#jx", |
1119 | total_len, len, 0, 0); | | 1108 | total_len, len, 0, 0); |
1120 | /* invalid length */ | | 1109 | /* invalid length */ |
1121 | ifp->if_ierrors++; | | 1110 | ifp->if_ierrors++; |
1122 | break; | | 1111 | break; |
1123 | } | | 1112 | } |
1124 | buf += sizeof(csum_hdr); | | 1113 | buf += sizeof(csum_hdr); |
1125 | | | 1114 | |
1126 | const uint16_t cstatus = csum_hdr.cstatus; | | 1115 | const uint16_t cstatus = csum_hdr.cstatus; |
1127 | | | 1116 | |
1128 | if (cstatus & AXE_CSUM_HDR_L3_TYPE_IPV4) { | | 1117 | if (cstatus & AXE_CSUM_HDR_L3_TYPE_IPV4) { |
1129 | if (cstatus & AXE_CSUM_HDR_L4_CSUM_ERR) | | 1118 | if (cstatus & AXE_CSUM_HDR_L4_CSUM_ERR) |
1130 | flags |= M_CSUM_TCP_UDP_BAD; | | 1119 | flags |= M_CSUM_TCP_UDP_BAD; |
1131 | if (cstatus & AXE_CSUM_HDR_L3_CSUM_ERR) | | 1120 | if (cstatus & AXE_CSUM_HDR_L3_CSUM_ERR) |
1132 | flags |= M_CSUM_IPv4_BAD; | | 1121 | flags |= M_CSUM_IPv4_BAD; |
1133 | | | 1122 | |
1134 | const uint16_t l4type = | | 1123 | const uint16_t l4type = |
1135 | cstatus & AXE_CSUM_HDR_L4_TYPE_MASK; | | 1124 | cstatus & AXE_CSUM_HDR_L4_TYPE_MASK; |
1136 | | | 1125 | |
1137 | if (l4type == AXE_CSUM_HDR_L4_TYPE_TCP) | | 1126 | if (l4type == AXE_CSUM_HDR_L4_TYPE_TCP) |
1138 | flags |= M_CSUM_TCPv4; | | 1127 | flags |= M_CSUM_TCPv4; |
1139 | if (l4type == AXE_CSUM_HDR_L4_TYPE_UDP) | | 1128 | if (l4type == AXE_CSUM_HDR_L4_TYPE_UDP) |
1140 | flags |= M_CSUM_UDPv4; | | 1129 | flags |= M_CSUM_UDPv4; |
1141 | } | | 1130 | } |
1142 | if (total_len < len) { | | 1131 | if (total_len < len) { |
1143 | pktlen = total_len; | | 1132 | pktlen = total_len; |
1144 | total_len = 0; | | 1133 | total_len = 0; |
1145 | } else { | | 1134 | } else { |
1146 | total_len -= len; | | 1135 | total_len -= len; |
1147 | rxlen = len - sizeof(csum_hdr); | | 1136 | rxlen = len - sizeof(csum_hdr); |
1148 | } | | 1137 | } |
1149 | DPRINTFN(20, "total_len %#jx len %#jx pktlen %#jx" | | 1138 | DPRINTFN(20, "total_len %#jx len %#jx pktlen %#jx" |
1150 | " rxlen %#jx", total_len, len, pktlen, rxlen); | | 1139 | " rxlen %#jx", total_len, len, pktlen, rxlen); |
1151 | } else { /* AX172 */ | | 1140 | } else { /* AX172 */ |
1152 | pktlen = rxlen = total_len; | | 1141 | pktlen = rxlen = total_len; |
1153 | total_len = 0; | | 1142 | total_len = 0; |
1154 | } | | 1143 | } |
1155 | | | 1144 | |
1156 | usbnet_enqueue(un, buf, pktlen, flags, 0, 0); | | 1145 | usbnet_enqueue(un, buf, pktlen, flags, 0, 0); |
1157 | buf += rxlen; | | 1146 | buf += rxlen; |
1158 | | | 1147 | |
1159 | } while (total_len > 0); | | 1148 | } while (total_len > 0); |
1160 | | | 1149 | |
1161 | DPRINTFN(10, "start rx", 0, 0, 0, 0); | | 1150 | DPRINTFN(10, "start rx", 0, 0, 0, 0); |
1162 | } | | 1151 | } |
1163 | | | 1152 | |
1164 | static unsigned | | 1153 | static unsigned |
1165 | axe_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c) | | 1154 | axe_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c) |
1166 | { | | 1155 | { |
1167 | AXEHIST_FUNC(); AXEHIST_CALLED(); | | 1156 | AXEHIST_FUNC(); AXEHIST_CALLED(); |
1168 | struct axe_sframe_hdr hdr, tlr; | | 1157 | struct axe_sframe_hdr hdr, tlr; |
1169 | size_t hdr_len = 0, tlr_len = 0; | | 1158 | size_t hdr_len = 0, tlr_len = 0; |
1170 | int length, boundary; | | 1159 | int length, boundary; |
1171 | | | 1160 | |
1172 | usbnet_isowned_tx(un); | | 1161 | usbnet_isowned_tx(un); |
1173 | | | 1162 | |
1174 | if (AXE_IS_178_FAMILY(un)) { | | 1163 | if (AXE_IS_178_FAMILY(un)) { |
1175 | /* | | 1164 | /* |
1176 | * Copy the mbuf data into a contiguous buffer, leaving two | | 1165 | * Copy the mbuf data into a contiguous buffer, leaving two |
1177 | * bytes at the beginning to hold the frame length. | | 1166 | * bytes at the beginning to hold the frame length. |
1178 | */ | | 1167 | */ |
1179 | boundary = (un->un_udev->ud_speed == USB_SPEED_HIGH) ? 512 : 64; | | 1168 | boundary = (un->un_udev->ud_speed == USB_SPEED_HIGH) ? 512 : 64; |
1180 | | | 1169 | |
1181 | hdr.len = htole16(m->m_pkthdr.len); | | 1170 | hdr.len = htole16(m->m_pkthdr.len); |
1182 | hdr.ilen = ~hdr.len; | | 1171 | hdr.ilen = ~hdr.len; |
1183 | hdr_len = sizeof(hdr); | | 1172 | hdr_len = sizeof(hdr); |
1184 | | | 1173 | |
1185 | length = hdr_len + m->m_pkthdr.len; | | 1174 | length = hdr_len + m->m_pkthdr.len; |
1186 | | | 1175 | |
1187 | if ((length % boundary) == 0) { | | 1176 | if ((length % boundary) == 0) { |
1188 | tlr.len = 0x0000; | | 1177 | tlr.len = 0x0000; |
1189 | tlr.ilen = 0xffff; | | 1178 | tlr.ilen = 0xffff; |
1190 | tlr_len = sizeof(tlr); | | 1179 | tlr_len = sizeof(tlr); |
1191 | } | | 1180 | } |
1192 | DPRINTFN(20, "length %jx m_pkthdr.len %jx hdrsize %#jx", | | 1181 | DPRINTFN(20, "length %jx m_pkthdr.len %jx hdrsize %#jx", |
1193 | length, m->m_pkthdr.len, sizeof(hdr), 0); | | 1182 | length, m->m_pkthdr.len, sizeof(hdr), 0); |
1194 | } | | 1183 | } |
1195 | | | 1184 | |
1196 | if ((unsigned)m->m_pkthdr.len > un->un_tx_bufsz - hdr_len - tlr_len) | | 1185 | if ((unsigned)m->m_pkthdr.len > un->un_tx_bufsz - hdr_len - tlr_len) |
1197 | return 0; | | 1186 | return 0; |
1198 | length = hdr_len + m->m_pkthdr.len + tlr_len; | | 1187 | length = hdr_len + m->m_pkthdr.len + tlr_len; |
1199 | | | 1188 | |
1200 | if (hdr_len) | | 1189 | if (hdr_len) |
1201 | memcpy(c->unc_buf, &hdr, hdr_len); | | 1190 | memcpy(c->unc_buf, &hdr, hdr_len); |
1202 | m_copydata(m, 0, m->m_pkthdr.len, c->unc_buf + hdr_len); | | 1191 | m_copydata(m, 0, m->m_pkthdr.len, c->unc_buf + hdr_len); |
1203 | if (tlr_len) | | 1192 | if (tlr_len) |
1204 | memcpy(c->unc_buf + length - tlr_len, &tlr, tlr_len); | | 1193 | memcpy(c->unc_buf + length - tlr_len, &tlr, tlr_len); |
1205 | | | 1194 | |
1206 | return length; | | 1195 | return length; |
1207 | } | | 1196 | } |
1208 | | | 1197 | |
1209 | static void | | 1198 | static void |
1210 | axe_csum_cfg(struct axe_softc *sc) | | 1199 | axe_csum_cfg(struct axe_softc *sc) |
1211 | { | | 1200 | { |
1212 | struct usbnet * const un = &sc->axe_un; | | 1201 | struct usbnet * const un = &sc->axe_un; |
1213 | struct ifnet * const ifp = usbnet_ifp(un); | | 1202 | struct ifnet * const ifp = usbnet_ifp(un); |
1214 | uint16_t csum1, csum2; | | 1203 | uint16_t csum1, csum2; |
1215 | | | 1204 | |
1216 | if ((un->un_flags & AX772B) != 0) { | | 1205 | if ((un->un_flags & AX772B) != 0) { |
1217 | csum1 = 0; | | 1206 | csum1 = 0; |
1218 | csum2 = 0; | | 1207 | csum2 = 0; |
1219 | if ((ifp->if_capenable & IFCAP_CSUM_IPv4_Tx) != 0) | | 1208 | if ((ifp->if_capenable & IFCAP_CSUM_IPv4_Tx) != 0) |
1220 | csum1 |= AXE_TXCSUM_IP; | | 1209 | csum1 |= AXE_TXCSUM_IP; |
1221 | if ((ifp->if_capenable & IFCAP_CSUM_TCPv4_Tx) != 0) | | 1210 | if ((ifp->if_capenable & IFCAP_CSUM_TCPv4_Tx) != 0) |
1222 | csum1 |= AXE_TXCSUM_TCP; | | 1211 | csum1 |= AXE_TXCSUM_TCP; |
1223 | if ((ifp->if_capenable & IFCAP_CSUM_UDPv4_Tx) != 0) | | 1212 | if ((ifp->if_capenable & IFCAP_CSUM_UDPv4_Tx) != 0) |
1224 | csum1 |= AXE_TXCSUM_UDP; | | 1213 | csum1 |= AXE_TXCSUM_UDP; |
1225 | if ((ifp->if_capenable & IFCAP_CSUM_TCPv6_Tx) != 0) | | 1214 | if ((ifp->if_capenable & IFCAP_CSUM_TCPv6_Tx) != 0) |
1226 | csum1 |= AXE_TXCSUM_TCPV6; | | 1215 | csum1 |= AXE_TXCSUM_TCPV6; |
1227 | if ((ifp->if_capenable & IFCAP_CSUM_UDPv6_Tx) != 0) | | 1216 | if ((ifp->if_capenable & IFCAP_CSUM_UDPv6_Tx) != 0) |
1228 | csum1 |= AXE_TXCSUM_UDPV6; | | 1217 | csum1 |= AXE_TXCSUM_UDPV6; |
1229 | axe_cmd(sc, AXE_772B_CMD_WRITE_TXCSUM, csum2, csum1, NULL); | | 1218 | axe_cmd(sc, AXE_772B_CMD_WRITE_TXCSUM, csum2, csum1, NULL); |
1230 | csum1 = 0; | | 1219 | csum1 = 0; |
1231 | csum2 = 0; | | 1220 | csum2 = 0; |
1232 | | | 1221 | |
1233 | if ((ifp->if_capenable & IFCAP_CSUM_IPv4_Rx) != 0) | | 1222 | if ((ifp->if_capenable & IFCAP_CSUM_IPv4_Rx) != 0) |
1234 | csum1 |= AXE_RXCSUM_IP; | | 1223 | csum1 |= AXE_RXCSUM_IP; |
1235 | if ((ifp->if_capenable & IFCAP_CSUM_TCPv4_Rx) != 0) | | 1224 | if ((ifp->if_capenable & IFCAP_CSUM_TCPv4_Rx) != 0) |
1236 | csum1 |= AXE_RXCSUM_TCP; | | 1225 | csum1 |= AXE_RXCSUM_TCP; |
1237 | if ((ifp->if_capenable & IFCAP_CSUM_UDPv4_Rx) != 0) | | 1226 | if ((ifp->if_capenable & IFCAP_CSUM_UDPv4_Rx) != 0) |
1238 | csum1 |= AXE_RXCSUM_UDP; | | 1227 | csum1 |= AXE_RXCSUM_UDP; |
1239 | if ((ifp->if_capenable & IFCAP_CSUM_TCPv6_Rx) != 0) | | 1228 | if ((ifp->if_capenable & IFCAP_CSUM_TCPv6_Rx) != 0) |
1240 | csum1 |= AXE_RXCSUM_TCPV6; | | 1229 | csum1 |= AXE_RXCSUM_TCPV6; |
1241 | if ((ifp->if_capenable & IFCAP_CSUM_UDPv6_Rx) != 0) | | 1230 | if ((ifp->if_capenable & IFCAP_CSUM_UDPv6_Rx) != 0) |
1242 | csum1 |= AXE_RXCSUM_UDPV6; | | 1231 | csum1 |= AXE_RXCSUM_UDPV6; |
1243 | axe_cmd(sc, AXE_772B_CMD_WRITE_RXCSUM, csum2, csum1, NULL); | | 1232 | axe_cmd(sc, AXE_772B_CMD_WRITE_RXCSUM, csum2, csum1, NULL); |
1244 | } | | 1233 | } |
1245 | } | | 1234 | } |
1246 | | | 1235 | |
1247 | static int | | 1236 | static int |
1248 | axe_init_locked(struct ifnet *ifp) | | 1237 | axe_init_locked(struct ifnet *ifp) |
1249 | { | | 1238 | { |
1250 | AXEHIST_FUNC(); AXEHIST_CALLED(); | | 1239 | AXEHIST_FUNC(); AXEHIST_CALLED(); |
1251 | struct usbnet * const un = ifp->if_softc; | | 1240 | struct usbnet * const un = ifp->if_softc; |
1252 | struct axe_softc * const sc = usbnet_softc(un); | | 1241 | struct axe_softc * const sc = usbnet_softc(un); |
1253 | int rxmode; | | 1242 | int rxmode; |
1254 | | | 1243 | |
1255 | usbnet_isowned(un); | | 1244 | usbnet_isowned(un); |
1256 | | | 1245 | |
1257 | if (usbnet_isdying(un)) | | 1246 | if (usbnet_isdying(un)) |
1258 | return EIO; | | 1247 | return EIO; |
1259 | | | 1248 | |
1260 | /* Cancel pending I/O */ | | 1249 | /* Cancel pending I/O */ |
1261 | usbnet_stop(un, ifp, 1); | | 1250 | usbnet_stop(un, ifp, 1); |
1262 | | | 1251 | |
1263 | usbnet_lock_mii_un_locked(un); | | 1252 | usbnet_lock_mii_un_locked(un); |
1264 | | | 1253 | |
1265 | /* Reset the ethernet interface. */ | | 1254 | /* Reset the ethernet interface. */ |
1266 | axe_reset(un); | | 1255 | axe_reset(un); |
1267 | | | 1256 | |
1268 | #if 0 | | 1257 | #if 0 |
1269 | ret = asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_2 | | | 1258 | ret = asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_2 | |
1270 | AX_GPIO_GPO2EN, 5, in_pm); | | 1259 | AX_GPIO_GPO2EN, 5, in_pm); |
1271 | #endif | | 1260 | #endif |
1272 | /* Set MAC address and transmitter IPG values. */ | | 1261 | /* Set MAC address and transmitter IPG values. */ |
1273 | if (AXE_IS_178_FAMILY(un)) { | | 1262 | if (AXE_IS_178_FAMILY(un)) { |
1274 | axe_cmd(sc, AXE_178_CMD_WRITE_NODEID, 0, 0, un->un_eaddr); | | 1263 | axe_cmd(sc, AXE_178_CMD_WRITE_NODEID, 0, 0, un->un_eaddr); |
1275 | axe_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->axe_ipgs[2], | | 1264 | axe_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->axe_ipgs[2], |
1276 | (sc->axe_ipgs[1] << 8) | (sc->axe_ipgs[0]), NULL); | | 1265 | (sc->axe_ipgs[1] << 8) | (sc->axe_ipgs[0]), NULL); |
1277 | } else { | | 1266 | } else { |
1278 | axe_cmd(sc, AXE_172_CMD_WRITE_NODEID, 0, 0, un->un_eaddr); | | 1267 | axe_cmd(sc, AXE_172_CMD_WRITE_NODEID, 0, 0, un->un_eaddr); |
1279 | axe_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->axe_ipgs[0], NULL); | | 1268 | axe_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->axe_ipgs[0], NULL); |
1280 | axe_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->axe_ipgs[1], NULL); | | 1269 | axe_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->axe_ipgs[1], NULL); |
1281 | axe_cmd(sc, AXE_172_CMD_WRITE_IPG2, 0, sc->axe_ipgs[2], NULL); | | 1270 | axe_cmd(sc, AXE_172_CMD_WRITE_IPG2, 0, sc->axe_ipgs[2], NULL); |
1282 | } | | 1271 | } |
1283 | if (AXE_IS_178_FAMILY(un)) { | | 1272 | if (AXE_IS_178_FAMILY(un)) { |
1284 | un->un_flags &= ~(AXSTD_FRAME | AXCSUM_FRAME); | | 1273 | un->un_flags &= ~(AXSTD_FRAME | AXCSUM_FRAME); |
1285 | if ((un->un_flags & AX772B) != 0 && | | 1274 | if ((un->un_flags & AX772B) != 0 && |
1286 | (ifp->if_capenable & AX_RXCSUM) != 0) { | | 1275 | (ifp->if_capenable & AX_RXCSUM) != 0) { |
1287 | sc->sc_lenmask = AXE_CSUM_HDR_LEN_MASK; | | 1276 | sc->sc_lenmask = AXE_CSUM_HDR_LEN_MASK; |
1288 | un->un_flags |= AXCSUM_FRAME; | | 1277 | un->un_flags |= AXCSUM_FRAME; |
1289 | } else { | | 1278 | } else { |
1290 | sc->sc_lenmask = AXE_HDR_LEN_MASK; | | 1279 | sc->sc_lenmask = AXE_HDR_LEN_MASK; |
1291 | un->un_flags |= AXSTD_FRAME; | | 1280 | un->un_flags |= AXSTD_FRAME; |
1292 | } | | 1281 | } |
1293 | } | | 1282 | } |
1294 | | | 1283 | |
1295 | /* Configure TX/RX checksum offloading. */ | | 1284 | /* Configure TX/RX checksum offloading. */ |
1296 | axe_csum_cfg(sc); | | 1285 | axe_csum_cfg(sc); |
1297 | | | 1286 | |
1298 | if (un->un_flags & AX772B) { | | 1287 | if (un->un_flags & AX772B) { |
1299 | /* AX88772B uses different maximum frame burst configuration. */ | | 1288 | /* AX88772B uses different maximum frame burst configuration. */ |
1300 | axe_cmd(sc, AXE_772B_CMD_RXCTL_WRITE_CFG, | | 1289 | axe_cmd(sc, AXE_772B_CMD_RXCTL_WRITE_CFG, |
1301 | ax88772b_mfb_table[AX88772B_MFB_16K].threshold, | | 1290 | ax88772b_mfb_table[AX88772B_MFB_16K].threshold, |
1302 | ax88772b_mfb_table[AX88772B_MFB_16K].byte_cnt, NULL); | | 1291 | ax88772b_mfb_table[AX88772B_MFB_16K].byte_cnt, NULL); |
1303 | } | | 1292 | } |
1304 | /* Enable receiver, set RX mode */ | | 1293 | /* Enable receiver, set RX mode */ |
1305 | rxmode = (AXE_RXCMD_MULTICAST | AXE_RXCMD_ENABLE); | | 1294 | rxmode = (AXE_RXCMD_MULTICAST | AXE_RXCMD_ENABLE); |
1306 | if (AXE_IS_178_FAMILY(un)) { | | 1295 | if (AXE_IS_178_FAMILY(un)) { |
1307 | if (un->un_flags & AX772B) { | | 1296 | if (un->un_flags & AX772B) { |
1308 | /* | | 1297 | /* |
1309 | * Select RX header format type 1. Aligning IP | | 1298 | * Select RX header format type 1. Aligning IP |
1310 | * header on 4 byte boundary is not needed when | | 1299 | * header on 4 byte boundary is not needed when |
1311 | * checksum offloading feature is not used | | 1300 | * checksum offloading feature is not used |
1312 | * because we always copy the received frame in | | 1301 | * because we always copy the received frame in |
1313 | * RX handler. When RX checksum offloading is | | 1302 | * RX handler. When RX checksum offloading is |
1314 | * active, aligning IP header is required to | | 1303 | * active, aligning IP header is required to |
1315 | * reflect actual frame length including RX | | 1304 | * reflect actual frame length including RX |
1316 | * header size. | | 1305 | * header size. |
1317 | */ | | 1306 | */ |
1318 | rxmode |= AXE_772B_RXCMD_HDR_TYPE_1; | | 1307 | rxmode |= AXE_772B_RXCMD_HDR_TYPE_1; |
1319 | if (un->un_flags & AXCSUM_FRAME) | | 1308 | if (un->un_flags & AXCSUM_FRAME) |
1320 | rxmode |= AXE_772B_RXCMD_IPHDR_ALIGN; | | 1309 | rxmode |= AXE_772B_RXCMD_IPHDR_ALIGN; |
1321 | } else { | | 1310 | } else { |
1322 | /* | | 1311 | /* |
1323 | * Default Rx buffer size is too small to get | | 1312 | * Default Rx buffer size is too small to get |
1324 | * maximum performance. | | 1313 | * maximum performance. |
1325 | */ | | 1314 | */ |
1326 | #if 0 | | 1315 | #if 0 |
1327 | if (un->un_udev->ud_speed == USB_SPEED_HIGH) { | | 1316 | if (un->un_udev->ud_speed == USB_SPEED_HIGH) { |
1328 | /* Largest possible USB buffer size for AX88178 */ | | 1317 | /* Largest possible USB buffer size for AX88178 */ |
1329 | } | | 1318 | } |
1330 | #endif | | 1319 | #endif |
1331 | rxmode |= AXE_178_RXCMD_MFB_16384; | | 1320 | rxmode |= AXE_178_RXCMD_MFB_16384; |
1332 | } | | 1321 | } |
1333 | } else { | | 1322 | } else { |
1334 | rxmode |= AXE_172_RXCMD_UNICAST; | | 1323 | rxmode |= AXE_172_RXCMD_UNICAST; |
1335 | } | | 1324 | } |
1336 | | | 1325 | |
1337 | | | 1326 | |
1338 | /* If we want promiscuous mode, set the allframes bit. */ | | 1327 | /* If we want promiscuous mode, set the allframes bit. */ |
1339 | if (ifp->if_flags & IFF_PROMISC) | | 1328 | if (ifp->if_flags & IFF_PROMISC) |
1340 | rxmode |= AXE_RXCMD_PROMISC; | | 1329 | rxmode |= AXE_RXCMD_PROMISC; |
1341 | | | 1330 | |
1342 | if (ifp->if_flags & IFF_BROADCAST) | | 1331 | if (ifp->if_flags & IFF_BROADCAST) |
1343 | rxmode |= AXE_RXCMD_BROADCAST; | | 1332 | rxmode |= AXE_RXCMD_BROADCAST; |
1344 | | | 1333 | |
1345 | DPRINTF("rxmode 0x%#jx", rxmode, 0, 0, 0); | | 1334 | DPRINTF("rxmode 0x%#jx", rxmode, 0, 0, 0); |
1346 | | | 1335 | |
1347 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); | | 1336 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); |
1348 | | | 1337 | |
1349 | /* Load the multicast filter. */ | | 1338 | /* Load the multicast filter. */ |
1350 | axe_setiff_locked(un); | | 1339 | axe_setiff_locked(un); |
1351 | | | 1340 | |
1352 | usbnet_unlock_mii_un_locked(un); | | 1341 | usbnet_unlock_mii_un_locked(un); |
1353 | | | 1342 | |
1354 | return usbnet_init_rx_tx(un); | | 1343 | return usbnet_init_rx_tx(un); |
1355 | } | | 1344 | } |
1356 | | | 1345 | |
1357 | static int | | 1346 | static int |
1358 | axe_init(struct ifnet *ifp) | | 1347 | axe_init(struct ifnet *ifp) |
1359 | { | | 1348 | { |
1360 | struct usbnet * const un = ifp->if_softc; | | 1349 | struct usbnet * const un = ifp->if_softc; |
1361 | | | 1350 | |
1362 | usbnet_lock(un); | | 1351 | usbnet_lock(un); |
1363 | int ret = axe_init_locked(ifp); | | 1352 | int ret = axe_init_locked(ifp); |
1364 | usbnet_unlock(un); | | 1353 | usbnet_unlock(un); |
1365 | | | 1354 | |
1366 | return ret; | | 1355 | return ret; |
1367 | } | | 1356 | } |
1368 | | | 1357 | |
1369 | static int | | 1358 | static int |
1370 | axe_ioctl(struct ifnet *ifp, u_long cmd, void *data) | | 1359 | axe_ioctl(struct ifnet *ifp, u_long cmd, void *data) |
1371 | { | | 1360 | { |
1372 | struct usbnet * const un = ifp->if_softc; | | 1361 | struct usbnet * const un = ifp->if_softc; |
1373 | | | 1362 | |
1374 | switch (cmd) { | | 1363 | switch (cmd) { |
1375 | case SIOCADDMULTI: | | 1364 | case SIOCADDMULTI: |
1376 | case SIOCDELMULTI: | | 1365 | case SIOCDELMULTI: |
1377 | axe_setiff(un); | | 1366 | axe_setiff(un); |
1378 | break; | | 1367 | break; |
1379 | default: | | 1368 | default: |
1380 | break; | | 1369 | break; |
1381 | } | | 1370 | } |
1382 | | | 1371 | |
1383 | return 0; | | 1372 | return 0; |
1384 | } | | 1373 | } |
1385 | | | 1374 | |
1386 | static void | | 1375 | static void |
1387 | axe_stop(struct ifnet *ifp, int disable) | | 1376 | axe_stop(struct ifnet *ifp, int disable) |
1388 | { | | 1377 | { |
1389 | struct usbnet * const un = ifp->if_softc; | | 1378 | struct usbnet * const un = ifp->if_softc; |
1390 | | | 1379 | |
1391 | usbnet_lock_mii_un_locked(un); | | 1380 | usbnet_lock_mii_un_locked(un); |
1392 | axe_reset(un); | | 1381 | axe_reset(un); |
1393 | usbnet_unlock_mii_un_locked(un); | | 1382 | usbnet_unlock_mii_un_locked(un); |
1394 | } | | 1383 | } |
1395 | | | 1384 | |
1396 | #ifdef _MODULE | | 1385 | #ifdef _MODULE |
1397 | #include "ioconf.c" | | 1386 | #include "ioconf.c" |
1398 | #endif | | 1387 | #endif |
1399 | | | 1388 | |
1400 | USBNET_MODULE(axe) | | 1389 | USBNET_MODULE(axe) |