1b011f8c4SOleksandr Tymoshenko /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni *
4b011f8c4SOleksandr Tymoshenko * Copyright (c) 2012
5b011f8c4SOleksandr Tymoshenko * Ben Gray <bgray@freebsd.org>.
6b011f8c4SOleksandr Tymoshenko * All rights reserved.
7b011f8c4SOleksandr Tymoshenko *
8b011f8c4SOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without
9b011f8c4SOleksandr Tymoshenko * modification, are permitted provided that the following conditions
10b011f8c4SOleksandr Tymoshenko * are met:
11b011f8c4SOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright
12b011f8c4SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer.
13b011f8c4SOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright
14b011f8c4SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the
15b011f8c4SOleksandr Tymoshenko * documentation and/or other materials provided with the distribution.
16b011f8c4SOleksandr Tymoshenko *
17b011f8c4SOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18b011f8c4SOleksandr Tymoshenko * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19b011f8c4SOleksandr Tymoshenko * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20b011f8c4SOleksandr Tymoshenko * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21b011f8c4SOleksandr Tymoshenko * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22b011f8c4SOleksandr Tymoshenko * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23b011f8c4SOleksandr Tymoshenko * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24b011f8c4SOleksandr Tymoshenko * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25b011f8c4SOleksandr Tymoshenko * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26b011f8c4SOleksandr Tymoshenko * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27b011f8c4SOleksandr Tymoshenko */
28b011f8c4SOleksandr Tymoshenko
29b011f8c4SOleksandr Tymoshenko #include <sys/cdefs.h>
30b011f8c4SOleksandr Tymoshenko /*
31*7ef6ce51SEd Maste * Microchip LAN9xxx devices (https://www.microchip.com/en-us/product/lan9500a)
32b011f8c4SOleksandr Tymoshenko *
33b011f8c4SOleksandr Tymoshenko * The LAN9500 & LAN9500A devices are stand-alone USB to Ethernet chips that
34b011f8c4SOleksandr Tymoshenko * support USB 2.0 and 10/100 Mbps Ethernet.
35b011f8c4SOleksandr Tymoshenko *
36b011f8c4SOleksandr Tymoshenko * The LAN951x devices are an integrated USB hub and USB to Ethernet adapter.
37b011f8c4SOleksandr Tymoshenko * The driver only covers the Ethernet part, the standard USB hub driver
38b011f8c4SOleksandr Tymoshenko * supports the hub part.
39b011f8c4SOleksandr Tymoshenko *
40b011f8c4SOleksandr Tymoshenko * This driver is closely modelled on the Linux driver written and copyrighted
41*7ef6ce51SEd Maste * by SMSC (later acquired by Microchip).
42b011f8c4SOleksandr Tymoshenko *
43b011f8c4SOleksandr Tymoshenko *
44b011f8c4SOleksandr Tymoshenko *
45b011f8c4SOleksandr Tymoshenko *
46b011f8c4SOleksandr Tymoshenko * H/W TCP & UDP Checksum Offloading
47b011f8c4SOleksandr Tymoshenko * ---------------------------------
48b011f8c4SOleksandr Tymoshenko * The chip supports both tx and rx offloading of UDP & TCP checksums, this
49b011f8c4SOleksandr Tymoshenko * feature can be dynamically enabled/disabled.
50b011f8c4SOleksandr Tymoshenko *
51b011f8c4SOleksandr Tymoshenko * RX checksuming is performed across bytes after the IPv4 header to the end of
52b011f8c4SOleksandr Tymoshenko * the Ethernet frame, this means if the frame is padded with non-zero values
53b011f8c4SOleksandr Tymoshenko * the H/W checksum will be incorrect, however the rx code compensates for this.
54b011f8c4SOleksandr Tymoshenko *
55b011f8c4SOleksandr Tymoshenko * TX checksuming is more complicated, the device requires a special header to
56b011f8c4SOleksandr Tymoshenko * be prefixed onto the start of the frame which indicates the start and end
57b011f8c4SOleksandr Tymoshenko * positions of the UDP or TCP frame. This requires the driver to manually
58b011f8c4SOleksandr Tymoshenko * go through the packet data and decode the headers prior to sending.
59b011f8c4SOleksandr Tymoshenko * On Linux they generally provide cues to the location of the csum and the
60b011f8c4SOleksandr Tymoshenko * area to calculate it over, on FreeBSD we seem to have to do it all ourselves,
61b011f8c4SOleksandr Tymoshenko * hence this is not as optimal and therefore h/w tX checksum is currently not
62b011f8c4SOleksandr Tymoshenko * implemented.
63b011f8c4SOleksandr Tymoshenko *
64b011f8c4SOleksandr Tymoshenko */
65b011f8c4SOleksandr Tymoshenko #include <sys/stdint.h>
66b011f8c4SOleksandr Tymoshenko #include <sys/stddef.h>
67b011f8c4SOleksandr Tymoshenko #include <sys/param.h>
68b011f8c4SOleksandr Tymoshenko #include <sys/queue.h>
69b011f8c4SOleksandr Tymoshenko #include <sys/types.h>
70b011f8c4SOleksandr Tymoshenko #include <sys/systm.h>
71b011f8c4SOleksandr Tymoshenko #include <sys/kernel.h>
72b011f8c4SOleksandr Tymoshenko #include <sys/bus.h>
73b011f8c4SOleksandr Tymoshenko #include <sys/module.h>
74b011f8c4SOleksandr Tymoshenko #include <sys/lock.h>
75b011f8c4SOleksandr Tymoshenko #include <sys/mutex.h>
76b011f8c4SOleksandr Tymoshenko #include <sys/condvar.h>
77c3322cb9SGleb Smirnoff #include <sys/socket.h>
78b011f8c4SOleksandr Tymoshenko #include <sys/sysctl.h>
79b011f8c4SOleksandr Tymoshenko #include <sys/sx.h>
80b011f8c4SOleksandr Tymoshenko #include <sys/unistd.h>
81b011f8c4SOleksandr Tymoshenko #include <sys/callout.h>
82b011f8c4SOleksandr Tymoshenko #include <sys/malloc.h>
83b011f8c4SOleksandr Tymoshenko #include <sys/priv.h>
84b011f8c4SOleksandr Tymoshenko #include <sys/random.h>
85b011f8c4SOleksandr Tymoshenko
86c3322cb9SGleb Smirnoff #include <net/if.h>
87c3322cb9SGleb Smirnoff #include <net/if_var.h>
8831c484adSJustin Hibbits #include <net/if_media.h>
8931c484adSJustin Hibbits
9031c484adSJustin Hibbits #include <dev/mii/mii.h>
9131c484adSJustin Hibbits #include <dev/mii/miivar.h>
92c3322cb9SGleb Smirnoff
935a1a4f75SMichael Tuexen #include <netinet/in.h>
945a1a4f75SMichael Tuexen #include <netinet/ip.h>
955a1a4f75SMichael Tuexen
9682b036b9SOleksandr Tymoshenko #include "opt_platform.h"
9782b036b9SOleksandr Tymoshenko
9882b036b9SOleksandr Tymoshenko #ifdef FDT
9982b036b9SOleksandr Tymoshenko #include <dev/fdt/fdt_common.h>
10082b036b9SOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h>
10182b036b9SOleksandr Tymoshenko #include <dev/ofw/ofw_bus_subr.h>
102bac5ec96SIan Lepore #include <dev/usb/usb_fdt_support.h>
10382b036b9SOleksandr Tymoshenko #endif
10482b036b9SOleksandr Tymoshenko
105b011f8c4SOleksandr Tymoshenko #include <dev/usb/usb.h>
106b011f8c4SOleksandr Tymoshenko #include <dev/usb/usbdi.h>
107b011f8c4SOleksandr Tymoshenko #include <dev/usb/usbdi_util.h>
108b011f8c4SOleksandr Tymoshenko #include "usbdevs.h"
109b011f8c4SOleksandr Tymoshenko
110b011f8c4SOleksandr Tymoshenko #define USB_DEBUG_VAR smsc_debug
111b011f8c4SOleksandr Tymoshenko #include <dev/usb/usb_debug.h>
112b011f8c4SOleksandr Tymoshenko #include <dev/usb/usb_process.h>
113b011f8c4SOleksandr Tymoshenko
114b011f8c4SOleksandr Tymoshenko #include <dev/usb/net/usb_ethernet.h>
11574b50dd9SHans Petter Selasky
11674b50dd9SHans Petter Selasky #include <dev/usb/net/if_smscreg.h>
117b011f8c4SOleksandr Tymoshenko
11831c484adSJustin Hibbits #include "miibus_if.h"
11931c484adSJustin Hibbits
1205a3426f4SHans Petter Selasky SYSCTL_NODE(_hw_usb, OID_AUTO, smsc, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
1215a3426f4SHans Petter Selasky "USB smsc");
1225a3426f4SHans Petter Selasky
1235a3426f4SHans Petter Selasky static bool smsc_rx_packet_batching = 1;
1245a3426f4SHans Petter Selasky
1255a3426f4SHans Petter Selasky SYSCTL_BOOL(_hw_usb_smsc, OID_AUTO, smsc_rx_packet_batching, CTLFLAG_RDTUN,
1265a3426f4SHans Petter Selasky &smsc_rx_packet_batching, 0,
1275a3426f4SHans Petter Selasky "If set, allows packet batching to increase throughput and latency. "
1285a3426f4SHans Petter Selasky "Else throughput and latency is decreased.");
1295a3426f4SHans Petter Selasky
130b011f8c4SOleksandr Tymoshenko #ifdef USB_DEBUG
131b011f8c4SOleksandr Tymoshenko static int smsc_debug = 0;
132b011f8c4SOleksandr Tymoshenko
133ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_smsc, OID_AUTO, debug, CTLFLAG_RWTUN, &smsc_debug, 0,
134b011f8c4SOleksandr Tymoshenko "Debug level");
135b011f8c4SOleksandr Tymoshenko #endif
136b011f8c4SOleksandr Tymoshenko
137b011f8c4SOleksandr Tymoshenko /*
138b011f8c4SOleksandr Tymoshenko * Various supported device vendors/products.
139b011f8c4SOleksandr Tymoshenko */
140b011f8c4SOleksandr Tymoshenko static const struct usb_device_id smsc_devs[] = {
141b011f8c4SOleksandr Tymoshenko #define SMSC_DEV(p,i) { USB_VPI(USB_VENDOR_SMC2, USB_PRODUCT_SMC2_##p, i) }
142cd8d304eSGavin Atkinson SMSC_DEV(LAN89530_ETH, 0),
143cd8d304eSGavin Atkinson SMSC_DEV(LAN9500_ETH, 0),
144cd8d304eSGavin Atkinson SMSC_DEV(LAN9500_ETH_2, 0),
145cd8d304eSGavin Atkinson SMSC_DEV(LAN9500A_ETH, 0),
146cd8d304eSGavin Atkinson SMSC_DEV(LAN9500A_ETH_2, 0),
147cd8d304eSGavin Atkinson SMSC_DEV(LAN9505_ETH, 0),
148cd8d304eSGavin Atkinson SMSC_DEV(LAN9505A_ETH, 0),
149b011f8c4SOleksandr Tymoshenko SMSC_DEV(LAN9514_ETH, 0),
150cd8d304eSGavin Atkinson SMSC_DEV(LAN9514_ETH_2, 0),
151cd8d304eSGavin Atkinson SMSC_DEV(LAN9530_ETH, 0),
152cd8d304eSGavin Atkinson SMSC_DEV(LAN9730_ETH, 0),
153cd8d304eSGavin Atkinson SMSC_DEV(LAN9500_SAL10, 0),
154cd8d304eSGavin Atkinson SMSC_DEV(LAN9505_SAL10, 0),
155cd8d304eSGavin Atkinson SMSC_DEV(LAN9500A_SAL10, 0),
156cd8d304eSGavin Atkinson SMSC_DEV(LAN9505A_SAL10, 0),
157cd8d304eSGavin Atkinson SMSC_DEV(LAN9514_SAL10, 0),
158cd8d304eSGavin Atkinson SMSC_DEV(LAN9500A_HAL, 0),
159cd8d304eSGavin Atkinson SMSC_DEV(LAN9505A_HAL, 0),
160b011f8c4SOleksandr Tymoshenko #undef SMSC_DEV
161b011f8c4SOleksandr Tymoshenko };
162b011f8c4SOleksandr Tymoshenko
163b011f8c4SOleksandr Tymoshenko #ifdef USB_DEBUG
164b011f8c4SOleksandr Tymoshenko #define smsc_dbg_printf(sc, fmt, args...) \
165b011f8c4SOleksandr Tymoshenko do { \
166b011f8c4SOleksandr Tymoshenko if (smsc_debug > 0) \
167b011f8c4SOleksandr Tymoshenko device_printf((sc)->sc_ue.ue_dev, "debug: " fmt, ##args); \
168b011f8c4SOleksandr Tymoshenko } while(0)
169b011f8c4SOleksandr Tymoshenko #else
17033299e3dSHans Petter Selasky #define smsc_dbg_printf(sc, fmt, args...) do { } while (0)
171b011f8c4SOleksandr Tymoshenko #endif
172b011f8c4SOleksandr Tymoshenko
173b011f8c4SOleksandr Tymoshenko #define smsc_warn_printf(sc, fmt, args...) \
174b011f8c4SOleksandr Tymoshenko device_printf((sc)->sc_ue.ue_dev, "warning: " fmt, ##args)
175b011f8c4SOleksandr Tymoshenko
176b011f8c4SOleksandr Tymoshenko #define smsc_err_printf(sc, fmt, args...) \
177b011f8c4SOleksandr Tymoshenko device_printf((sc)->sc_ue.ue_dev, "error: " fmt, ##args)
178b011f8c4SOleksandr Tymoshenko
179b011f8c4SOleksandr Tymoshenko #define ETHER_IS_VALID(addr) \
180b011f8c4SOleksandr Tymoshenko (!ETHER_IS_MULTICAST(addr) && !ETHER_IS_ZERO(addr))
181b011f8c4SOleksandr Tymoshenko
1823878bbf1SRonald Klop #define BOOTARGS_SMSC95XX "smsc95xx.macaddr"
1833878bbf1SRonald Klop
184b011f8c4SOleksandr Tymoshenko static device_probe_t smsc_probe;
185b011f8c4SOleksandr Tymoshenko static device_attach_t smsc_attach;
186b011f8c4SOleksandr Tymoshenko static device_detach_t smsc_detach;
187b011f8c4SOleksandr Tymoshenko
188b011f8c4SOleksandr Tymoshenko static usb_callback_t smsc_bulk_read_callback;
189b011f8c4SOleksandr Tymoshenko static usb_callback_t smsc_bulk_write_callback;
190b011f8c4SOleksandr Tymoshenko
191b011f8c4SOleksandr Tymoshenko static miibus_readreg_t smsc_miibus_readreg;
192b011f8c4SOleksandr Tymoshenko static miibus_writereg_t smsc_miibus_writereg;
193b011f8c4SOleksandr Tymoshenko static miibus_statchg_t smsc_miibus_statchg;
194b011f8c4SOleksandr Tymoshenko
195b011f8c4SOleksandr Tymoshenko static int smsc_attach_post_sub(struct usb_ether *ue);
196b011f8c4SOleksandr Tymoshenko static uether_fn_t smsc_attach_post;
197b011f8c4SOleksandr Tymoshenko static uether_fn_t smsc_init;
198b011f8c4SOleksandr Tymoshenko static uether_fn_t smsc_stop;
199b011f8c4SOleksandr Tymoshenko static uether_fn_t smsc_start;
200b011f8c4SOleksandr Tymoshenko static uether_fn_t smsc_tick;
201b011f8c4SOleksandr Tymoshenko static uether_fn_t smsc_setmulti;
202b011f8c4SOleksandr Tymoshenko static uether_fn_t smsc_setpromisc;
203b011f8c4SOleksandr Tymoshenko
204935b194dSJustin Hibbits static int smsc_ifmedia_upd(if_t);
205935b194dSJustin Hibbits static void smsc_ifmedia_sts(if_t, struct ifmediareq *);
206b011f8c4SOleksandr Tymoshenko
207b011f8c4SOleksandr Tymoshenko static int smsc_chip_init(struct smsc_softc *sc);
208935b194dSJustin Hibbits static int smsc_ioctl(if_t ifp, u_long cmd, caddr_t data);
209b011f8c4SOleksandr Tymoshenko
210b011f8c4SOleksandr Tymoshenko static const struct usb_config smsc_config[SMSC_N_TRANSFER] = {
211b011f8c4SOleksandr Tymoshenko [SMSC_BULK_DT_WR] = {
212b011f8c4SOleksandr Tymoshenko .type = UE_BULK,
213b011f8c4SOleksandr Tymoshenko .endpoint = UE_ADDR_ANY,
214b011f8c4SOleksandr Tymoshenko .direction = UE_DIR_OUT,
215b011f8c4SOleksandr Tymoshenko .frames = 16,
216b011f8c4SOleksandr Tymoshenko .bufsize = 16 * (MCLBYTES + 16),
217b011f8c4SOleksandr Tymoshenko .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
218b011f8c4SOleksandr Tymoshenko .callback = smsc_bulk_write_callback,
219b011f8c4SOleksandr Tymoshenko .timeout = 10000, /* 10 seconds */
220b011f8c4SOleksandr Tymoshenko },
221b011f8c4SOleksandr Tymoshenko
222b011f8c4SOleksandr Tymoshenko [SMSC_BULK_DT_RD] = {
223b011f8c4SOleksandr Tymoshenko .type = UE_BULK,
224b011f8c4SOleksandr Tymoshenko .endpoint = UE_ADDR_ANY,
225b011f8c4SOleksandr Tymoshenko .direction = UE_DIR_IN,
226b011f8c4SOleksandr Tymoshenko .bufsize = 20480, /* bytes */
227b011f8c4SOleksandr Tymoshenko .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
228b011f8c4SOleksandr Tymoshenko .callback = smsc_bulk_read_callback,
229b011f8c4SOleksandr Tymoshenko .timeout = 0, /* no timeout */
230b011f8c4SOleksandr Tymoshenko },
231b011f8c4SOleksandr Tymoshenko
232b011f8c4SOleksandr Tymoshenko /* The SMSC chip supports an interrupt endpoints, however they aren't
233b011f8c4SOleksandr Tymoshenko * needed as we poll on the MII status.
234b011f8c4SOleksandr Tymoshenko */
235b011f8c4SOleksandr Tymoshenko };
236b011f8c4SOleksandr Tymoshenko
237b011f8c4SOleksandr Tymoshenko static const struct usb_ether_methods smsc_ue_methods = {
238b011f8c4SOleksandr Tymoshenko .ue_attach_post = smsc_attach_post,
239b011f8c4SOleksandr Tymoshenko .ue_attach_post_sub = smsc_attach_post_sub,
240b011f8c4SOleksandr Tymoshenko .ue_start = smsc_start,
241b011f8c4SOleksandr Tymoshenko .ue_ioctl = smsc_ioctl,
242b011f8c4SOleksandr Tymoshenko .ue_init = smsc_init,
243b011f8c4SOleksandr Tymoshenko .ue_stop = smsc_stop,
244b011f8c4SOleksandr Tymoshenko .ue_tick = smsc_tick,
245b011f8c4SOleksandr Tymoshenko .ue_setmulti = smsc_setmulti,
246b011f8c4SOleksandr Tymoshenko .ue_setpromisc = smsc_setpromisc,
247b011f8c4SOleksandr Tymoshenko .ue_mii_upd = smsc_ifmedia_upd,
248b011f8c4SOleksandr Tymoshenko .ue_mii_sts = smsc_ifmedia_sts,
249b011f8c4SOleksandr Tymoshenko };
250b011f8c4SOleksandr Tymoshenko
251b011f8c4SOleksandr Tymoshenko /**
252b011f8c4SOleksandr Tymoshenko * smsc_read_reg - Reads a 32-bit register on the device
253b011f8c4SOleksandr Tymoshenko * @sc: driver soft context
254b011f8c4SOleksandr Tymoshenko * @off: offset of the register
255b011f8c4SOleksandr Tymoshenko * @data: pointer a value that will be populated with the register value
256b011f8c4SOleksandr Tymoshenko *
257b011f8c4SOleksandr Tymoshenko * LOCKING:
258b011f8c4SOleksandr Tymoshenko * The device lock must be held before calling this function.
259b011f8c4SOleksandr Tymoshenko *
260b011f8c4SOleksandr Tymoshenko * RETURNS:
261b011f8c4SOleksandr Tymoshenko * 0 on success, a USB_ERR_?? error code on failure.
262b011f8c4SOleksandr Tymoshenko */
263b011f8c4SOleksandr Tymoshenko static int
smsc_read_reg(struct smsc_softc * sc,uint32_t off,uint32_t * data)264b011f8c4SOleksandr Tymoshenko smsc_read_reg(struct smsc_softc *sc, uint32_t off, uint32_t *data)
265b011f8c4SOleksandr Tymoshenko {
266b011f8c4SOleksandr Tymoshenko struct usb_device_request req;
267b011f8c4SOleksandr Tymoshenko uint32_t buf;
268b011f8c4SOleksandr Tymoshenko usb_error_t err;
269b011f8c4SOleksandr Tymoshenko
270b011f8c4SOleksandr Tymoshenko SMSC_LOCK_ASSERT(sc, MA_OWNED);
271b011f8c4SOleksandr Tymoshenko
272b011f8c4SOleksandr Tymoshenko req.bmRequestType = UT_READ_VENDOR_DEVICE;
273b011f8c4SOleksandr Tymoshenko req.bRequest = SMSC_UR_READ_REG;
274b011f8c4SOleksandr Tymoshenko USETW(req.wValue, 0);
275b011f8c4SOleksandr Tymoshenko USETW(req.wIndex, off);
276b011f8c4SOleksandr Tymoshenko USETW(req.wLength, 4);
277b011f8c4SOleksandr Tymoshenko
278b011f8c4SOleksandr Tymoshenko err = uether_do_request(&sc->sc_ue, &req, &buf, 1000);
279b011f8c4SOleksandr Tymoshenko if (err != 0)
280b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "Failed to read register 0x%0x\n", off);
281b011f8c4SOleksandr Tymoshenko
282b011f8c4SOleksandr Tymoshenko *data = le32toh(buf);
283b011f8c4SOleksandr Tymoshenko
284b011f8c4SOleksandr Tymoshenko return (err);
285b011f8c4SOleksandr Tymoshenko }
286b011f8c4SOleksandr Tymoshenko
287b011f8c4SOleksandr Tymoshenko /**
288b011f8c4SOleksandr Tymoshenko * smsc_write_reg - Writes a 32-bit register on the device
289b011f8c4SOleksandr Tymoshenko * @sc: driver soft context
290b011f8c4SOleksandr Tymoshenko * @off: offset of the register
291b011f8c4SOleksandr Tymoshenko * @data: the 32-bit value to write into the register
292b011f8c4SOleksandr Tymoshenko *
293b011f8c4SOleksandr Tymoshenko * LOCKING:
294b011f8c4SOleksandr Tymoshenko * The device lock must be held before calling this function.
295b011f8c4SOleksandr Tymoshenko *
296b011f8c4SOleksandr Tymoshenko * RETURNS:
297b011f8c4SOleksandr Tymoshenko * 0 on success, a USB_ERR_?? error code on failure.
298b011f8c4SOleksandr Tymoshenko */
299b011f8c4SOleksandr Tymoshenko static int
smsc_write_reg(struct smsc_softc * sc,uint32_t off,uint32_t data)300b011f8c4SOleksandr Tymoshenko smsc_write_reg(struct smsc_softc *sc, uint32_t off, uint32_t data)
301b011f8c4SOleksandr Tymoshenko {
302b011f8c4SOleksandr Tymoshenko struct usb_device_request req;
303b011f8c4SOleksandr Tymoshenko uint32_t buf;
304b011f8c4SOleksandr Tymoshenko usb_error_t err;
305b011f8c4SOleksandr Tymoshenko
306b011f8c4SOleksandr Tymoshenko SMSC_LOCK_ASSERT(sc, MA_OWNED);
307b011f8c4SOleksandr Tymoshenko
308b011f8c4SOleksandr Tymoshenko buf = htole32(data);
309b011f8c4SOleksandr Tymoshenko
310b011f8c4SOleksandr Tymoshenko req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
311b011f8c4SOleksandr Tymoshenko req.bRequest = SMSC_UR_WRITE_REG;
312b011f8c4SOleksandr Tymoshenko USETW(req.wValue, 0);
313b011f8c4SOleksandr Tymoshenko USETW(req.wIndex, off);
314b011f8c4SOleksandr Tymoshenko USETW(req.wLength, 4);
315b011f8c4SOleksandr Tymoshenko
316b011f8c4SOleksandr Tymoshenko err = uether_do_request(&sc->sc_ue, &req, &buf, 1000);
317b011f8c4SOleksandr Tymoshenko if (err != 0)
318b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "Failed to write register 0x%0x\n", off);
319b011f8c4SOleksandr Tymoshenko
320b011f8c4SOleksandr Tymoshenko return (err);
321b011f8c4SOleksandr Tymoshenko }
322b011f8c4SOleksandr Tymoshenko
323b011f8c4SOleksandr Tymoshenko /**
324b011f8c4SOleksandr Tymoshenko * smsc_wait_for_bits - Polls on a register value until bits are cleared
325b011f8c4SOleksandr Tymoshenko * @sc: soft context
326b011f8c4SOleksandr Tymoshenko * @reg: offset of the register
327b011f8c4SOleksandr Tymoshenko * @bits: if the bits are clear the function returns
328b011f8c4SOleksandr Tymoshenko *
329b011f8c4SOleksandr Tymoshenko * LOCKING:
330b011f8c4SOleksandr Tymoshenko * The device lock must be held before calling this function.
331b011f8c4SOleksandr Tymoshenko *
332b011f8c4SOleksandr Tymoshenko * RETURNS:
333b011f8c4SOleksandr Tymoshenko * 0 on success, or a USB_ERR_?? error code on failure.
334b011f8c4SOleksandr Tymoshenko */
335b011f8c4SOleksandr Tymoshenko static int
smsc_wait_for_bits(struct smsc_softc * sc,uint32_t reg,uint32_t bits)336b011f8c4SOleksandr Tymoshenko smsc_wait_for_bits(struct smsc_softc *sc, uint32_t reg, uint32_t bits)
337b011f8c4SOleksandr Tymoshenko {
338b011f8c4SOleksandr Tymoshenko usb_ticks_t start_ticks;
3398692ca36SHans Petter Selasky const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000);
340b011f8c4SOleksandr Tymoshenko uint32_t val;
341b011f8c4SOleksandr Tymoshenko int err;
342b011f8c4SOleksandr Tymoshenko
343b011f8c4SOleksandr Tymoshenko SMSC_LOCK_ASSERT(sc, MA_OWNED);
344b011f8c4SOleksandr Tymoshenko
345b011f8c4SOleksandr Tymoshenko start_ticks = (usb_ticks_t)ticks;
346b011f8c4SOleksandr Tymoshenko do {
347b011f8c4SOleksandr Tymoshenko if ((err = smsc_read_reg(sc, reg, &val)) != 0)
348b011f8c4SOleksandr Tymoshenko return (err);
349b011f8c4SOleksandr Tymoshenko if (!(val & bits))
350b011f8c4SOleksandr Tymoshenko return (0);
351b011f8c4SOleksandr Tymoshenko
352b011f8c4SOleksandr Tymoshenko uether_pause(&sc->sc_ue, hz / 100);
3538692ca36SHans Petter Selasky } while (((usb_ticks_t)(ticks - start_ticks)) < max_ticks);
354b011f8c4SOleksandr Tymoshenko
355b011f8c4SOleksandr Tymoshenko return (USB_ERR_TIMEOUT);
356b011f8c4SOleksandr Tymoshenko }
357b011f8c4SOleksandr Tymoshenko
358b011f8c4SOleksandr Tymoshenko /**
359b011f8c4SOleksandr Tymoshenko * smsc_eeprom_read - Reads the attached EEPROM
360b011f8c4SOleksandr Tymoshenko * @sc: soft context
361b011f8c4SOleksandr Tymoshenko * @off: the eeprom address offset
362b011f8c4SOleksandr Tymoshenko * @buf: stores the bytes
363b011f8c4SOleksandr Tymoshenko * @buflen: the number of bytes to read
364b011f8c4SOleksandr Tymoshenko *
365b011f8c4SOleksandr Tymoshenko * Simply reads bytes from an attached eeprom.
366b011f8c4SOleksandr Tymoshenko *
367b011f8c4SOleksandr Tymoshenko * LOCKING:
368b011f8c4SOleksandr Tymoshenko * The function takes and releases the device lock if it is not already held.
369b011f8c4SOleksandr Tymoshenko *
370b011f8c4SOleksandr Tymoshenko * RETURNS:
371b011f8c4SOleksandr Tymoshenko * 0 on success, or a USB_ERR_?? error code on failure.
372b011f8c4SOleksandr Tymoshenko */
373b011f8c4SOleksandr Tymoshenko static int
smsc_eeprom_read(struct smsc_softc * sc,uint16_t off,uint8_t * buf,uint16_t buflen)374b011f8c4SOleksandr Tymoshenko smsc_eeprom_read(struct smsc_softc *sc, uint16_t off, uint8_t *buf, uint16_t buflen)
375b011f8c4SOleksandr Tymoshenko {
376b011f8c4SOleksandr Tymoshenko usb_ticks_t start_ticks;
3779b42038bSHans Petter Selasky const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000);
378b011f8c4SOleksandr Tymoshenko int err;
379b011f8c4SOleksandr Tymoshenko int locked;
380b011f8c4SOleksandr Tymoshenko uint32_t val;
381b011f8c4SOleksandr Tymoshenko uint16_t i;
382b011f8c4SOleksandr Tymoshenko
383b011f8c4SOleksandr Tymoshenko locked = mtx_owned(&sc->sc_mtx);
384b011f8c4SOleksandr Tymoshenko if (!locked)
385b011f8c4SOleksandr Tymoshenko SMSC_LOCK(sc);
386b011f8c4SOleksandr Tymoshenko
387b011f8c4SOleksandr Tymoshenko err = smsc_wait_for_bits(sc, SMSC_EEPROM_CMD, SMSC_EEPROM_CMD_BUSY);
388b011f8c4SOleksandr Tymoshenko if (err != 0) {
389b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "eeprom busy, failed to read data\n");
390b011f8c4SOleksandr Tymoshenko goto done;
391b011f8c4SOleksandr Tymoshenko }
392b011f8c4SOleksandr Tymoshenko
393b011f8c4SOleksandr Tymoshenko /* start reading the bytes, one at a time */
394b011f8c4SOleksandr Tymoshenko for (i = 0; i < buflen; i++) {
395b011f8c4SOleksandr Tymoshenko val = SMSC_EEPROM_CMD_BUSY | (SMSC_EEPROM_CMD_ADDR_MASK & (off + i));
396b011f8c4SOleksandr Tymoshenko if ((err = smsc_write_reg(sc, SMSC_EEPROM_CMD, val)) != 0)
397b011f8c4SOleksandr Tymoshenko goto done;
398b011f8c4SOleksandr Tymoshenko
399b011f8c4SOleksandr Tymoshenko start_ticks = (usb_ticks_t)ticks;
400b011f8c4SOleksandr Tymoshenko do {
401b011f8c4SOleksandr Tymoshenko if ((err = smsc_read_reg(sc, SMSC_EEPROM_CMD, &val)) != 0)
402b011f8c4SOleksandr Tymoshenko goto done;
403b011f8c4SOleksandr Tymoshenko if (!(val & SMSC_EEPROM_CMD_BUSY) || (val & SMSC_EEPROM_CMD_TIMEOUT))
404b011f8c4SOleksandr Tymoshenko break;
405b011f8c4SOleksandr Tymoshenko
406b011f8c4SOleksandr Tymoshenko uether_pause(&sc->sc_ue, hz / 100);
4079b42038bSHans Petter Selasky } while (((usb_ticks_t)(ticks - start_ticks)) < max_ticks);
408b011f8c4SOleksandr Tymoshenko
409b011f8c4SOleksandr Tymoshenko if (val & (SMSC_EEPROM_CMD_BUSY | SMSC_EEPROM_CMD_TIMEOUT)) {
410b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "eeprom command failed\n");
411b011f8c4SOleksandr Tymoshenko err = USB_ERR_IOERROR;
412b011f8c4SOleksandr Tymoshenko break;
413b011f8c4SOleksandr Tymoshenko }
414b011f8c4SOleksandr Tymoshenko
415b011f8c4SOleksandr Tymoshenko if ((err = smsc_read_reg(sc, SMSC_EEPROM_DATA, &val)) != 0)
416b011f8c4SOleksandr Tymoshenko goto done;
417b011f8c4SOleksandr Tymoshenko
418b011f8c4SOleksandr Tymoshenko buf[i] = (val & 0xff);
419b011f8c4SOleksandr Tymoshenko }
420b011f8c4SOleksandr Tymoshenko
421b011f8c4SOleksandr Tymoshenko done:
422b011f8c4SOleksandr Tymoshenko if (!locked)
423b011f8c4SOleksandr Tymoshenko SMSC_UNLOCK(sc);
424b011f8c4SOleksandr Tymoshenko
425b011f8c4SOleksandr Tymoshenko return (err);
426b011f8c4SOleksandr Tymoshenko }
427b011f8c4SOleksandr Tymoshenko
428b011f8c4SOleksandr Tymoshenko /**
429b011f8c4SOleksandr Tymoshenko * smsc_miibus_readreg - Reads a MII/MDIO register
430b011f8c4SOleksandr Tymoshenko * @dev: usb ether device
431b011f8c4SOleksandr Tymoshenko * @phy: the number of phy reading from
432b011f8c4SOleksandr Tymoshenko * @reg: the register address
433b011f8c4SOleksandr Tymoshenko *
434b011f8c4SOleksandr Tymoshenko * Attempts to read a phy register over the MII bus.
435b011f8c4SOleksandr Tymoshenko *
436b011f8c4SOleksandr Tymoshenko * LOCKING:
437b011f8c4SOleksandr Tymoshenko * Takes and releases the device mutex lock if not already held.
438b011f8c4SOleksandr Tymoshenko *
439b011f8c4SOleksandr Tymoshenko * RETURNS:
440b011f8c4SOleksandr Tymoshenko * Returns the 16-bits read from the MII register, if this function fails 0
441b011f8c4SOleksandr Tymoshenko * is returned.
442b011f8c4SOleksandr Tymoshenko */
443b011f8c4SOleksandr Tymoshenko static int
smsc_miibus_readreg(device_t dev,int phy,int reg)444b011f8c4SOleksandr Tymoshenko smsc_miibus_readreg(device_t dev, int phy, int reg)
445b011f8c4SOleksandr Tymoshenko {
446b011f8c4SOleksandr Tymoshenko struct smsc_softc *sc = device_get_softc(dev);
447b011f8c4SOleksandr Tymoshenko int locked;
448b011f8c4SOleksandr Tymoshenko uint32_t addr;
449b011f8c4SOleksandr Tymoshenko uint32_t val = 0;
450b011f8c4SOleksandr Tymoshenko
451b011f8c4SOleksandr Tymoshenko locked = mtx_owned(&sc->sc_mtx);
452b011f8c4SOleksandr Tymoshenko if (!locked)
453b011f8c4SOleksandr Tymoshenko SMSC_LOCK(sc);
454b011f8c4SOleksandr Tymoshenko
455b011f8c4SOleksandr Tymoshenko if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) {
456b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "MII is busy\n");
457b011f8c4SOleksandr Tymoshenko goto done;
458b011f8c4SOleksandr Tymoshenko }
459b011f8c4SOleksandr Tymoshenko
460361558a6SEd Maste addr = (phy << 11) | (reg << 6) | SMSC_MII_READ | SMSC_MII_BUSY;
461b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_MII_ADDR, addr);
462b011f8c4SOleksandr Tymoshenko
463b011f8c4SOleksandr Tymoshenko if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0)
464b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "MII read timeout\n");
465b011f8c4SOleksandr Tymoshenko
466b011f8c4SOleksandr Tymoshenko smsc_read_reg(sc, SMSC_MII_DATA, &val);
467b011f8c4SOleksandr Tymoshenko val = le32toh(val);
468b011f8c4SOleksandr Tymoshenko
469b011f8c4SOleksandr Tymoshenko done:
470b011f8c4SOleksandr Tymoshenko if (!locked)
471b011f8c4SOleksandr Tymoshenko SMSC_UNLOCK(sc);
472b011f8c4SOleksandr Tymoshenko
473b011f8c4SOleksandr Tymoshenko return (val & 0xFFFF);
474b011f8c4SOleksandr Tymoshenko }
475b011f8c4SOleksandr Tymoshenko
476b011f8c4SOleksandr Tymoshenko /**
477b011f8c4SOleksandr Tymoshenko * smsc_miibus_writereg - Writes a MII/MDIO register
478b011f8c4SOleksandr Tymoshenko * @dev: usb ether device
479b011f8c4SOleksandr Tymoshenko * @phy: the number of phy writing to
480b011f8c4SOleksandr Tymoshenko * @reg: the register address
481b011f8c4SOleksandr Tymoshenko * @val: the value to write
482b011f8c4SOleksandr Tymoshenko *
483b011f8c4SOleksandr Tymoshenko * Attempts to write a phy register over the MII bus.
484b011f8c4SOleksandr Tymoshenko *
485b011f8c4SOleksandr Tymoshenko * LOCKING:
486b011f8c4SOleksandr Tymoshenko * Takes and releases the device mutex lock if not already held.
487b011f8c4SOleksandr Tymoshenko *
488b011f8c4SOleksandr Tymoshenko * RETURNS:
489b011f8c4SOleksandr Tymoshenko * Always returns 0 regardless of success or failure.
490b011f8c4SOleksandr Tymoshenko */
491b011f8c4SOleksandr Tymoshenko static int
smsc_miibus_writereg(device_t dev,int phy,int reg,int val)492b011f8c4SOleksandr Tymoshenko smsc_miibus_writereg(device_t dev, int phy, int reg, int val)
493b011f8c4SOleksandr Tymoshenko {
494b011f8c4SOleksandr Tymoshenko struct smsc_softc *sc = device_get_softc(dev);
495b011f8c4SOleksandr Tymoshenko int locked;
496b011f8c4SOleksandr Tymoshenko uint32_t addr;
497b011f8c4SOleksandr Tymoshenko
498b011f8c4SOleksandr Tymoshenko if (sc->sc_phyno != phy)
499b011f8c4SOleksandr Tymoshenko return (0);
500b011f8c4SOleksandr Tymoshenko
501b011f8c4SOleksandr Tymoshenko locked = mtx_owned(&sc->sc_mtx);
502b011f8c4SOleksandr Tymoshenko if (!locked)
503b011f8c4SOleksandr Tymoshenko SMSC_LOCK(sc);
504b011f8c4SOleksandr Tymoshenko
505b011f8c4SOleksandr Tymoshenko if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) {
506b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "MII is busy\n");
507b011f8c4SOleksandr Tymoshenko goto done;
508b011f8c4SOleksandr Tymoshenko }
509b011f8c4SOleksandr Tymoshenko
510b011f8c4SOleksandr Tymoshenko val = htole32(val);
511b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_MII_DATA, val);
512b011f8c4SOleksandr Tymoshenko
513361558a6SEd Maste addr = (phy << 11) | (reg << 6) | SMSC_MII_WRITE | SMSC_MII_BUSY;
514b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_MII_ADDR, addr);
515b011f8c4SOleksandr Tymoshenko
516b011f8c4SOleksandr Tymoshenko if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0)
517b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "MII write timeout\n");
518b011f8c4SOleksandr Tymoshenko
519b011f8c4SOleksandr Tymoshenko done:
520b011f8c4SOleksandr Tymoshenko if (!locked)
521b011f8c4SOleksandr Tymoshenko SMSC_UNLOCK(sc);
522b011f8c4SOleksandr Tymoshenko return (0);
523b011f8c4SOleksandr Tymoshenko }
524b011f8c4SOleksandr Tymoshenko
525b011f8c4SOleksandr Tymoshenko /**
526b011f8c4SOleksandr Tymoshenko * smsc_miibus_statchg - Called to detect phy status change
527b011f8c4SOleksandr Tymoshenko * @dev: usb ether device
528b011f8c4SOleksandr Tymoshenko *
529b011f8c4SOleksandr Tymoshenko * This function is called periodically by the system to poll for status
530b011f8c4SOleksandr Tymoshenko * changes of the link.
531b011f8c4SOleksandr Tymoshenko *
532b011f8c4SOleksandr Tymoshenko * LOCKING:
533b011f8c4SOleksandr Tymoshenko * Takes and releases the device mutex lock if not already held.
534b011f8c4SOleksandr Tymoshenko */
535b011f8c4SOleksandr Tymoshenko static void
smsc_miibus_statchg(device_t dev)536b011f8c4SOleksandr Tymoshenko smsc_miibus_statchg(device_t dev)
537b011f8c4SOleksandr Tymoshenko {
538b011f8c4SOleksandr Tymoshenko struct smsc_softc *sc = device_get_softc(dev);
539b011f8c4SOleksandr Tymoshenko struct mii_data *mii = uether_getmii(&sc->sc_ue);
540935b194dSJustin Hibbits if_t ifp;
541b011f8c4SOleksandr Tymoshenko int locked;
542b011f8c4SOleksandr Tymoshenko int err;
543b011f8c4SOleksandr Tymoshenko uint32_t flow;
544b011f8c4SOleksandr Tymoshenko uint32_t afc_cfg;
545b011f8c4SOleksandr Tymoshenko
546b011f8c4SOleksandr Tymoshenko locked = mtx_owned(&sc->sc_mtx);
547b011f8c4SOleksandr Tymoshenko if (!locked)
548b011f8c4SOleksandr Tymoshenko SMSC_LOCK(sc);
549b011f8c4SOleksandr Tymoshenko
550b011f8c4SOleksandr Tymoshenko ifp = uether_getifp(&sc->sc_ue);
551b011f8c4SOleksandr Tymoshenko if (mii == NULL || ifp == NULL ||
552935b194dSJustin Hibbits (if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
553b011f8c4SOleksandr Tymoshenko goto done;
554b011f8c4SOleksandr Tymoshenko
555b011f8c4SOleksandr Tymoshenko /* Use the MII status to determine link status */
556b011f8c4SOleksandr Tymoshenko sc->sc_flags &= ~SMSC_FLAG_LINK;
557b011f8c4SOleksandr Tymoshenko if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
558b011f8c4SOleksandr Tymoshenko (IFM_ACTIVE | IFM_AVALID)) {
559b011f8c4SOleksandr Tymoshenko switch (IFM_SUBTYPE(mii->mii_media_active)) {
560b011f8c4SOleksandr Tymoshenko case IFM_10_T:
561b011f8c4SOleksandr Tymoshenko case IFM_100_TX:
562b011f8c4SOleksandr Tymoshenko sc->sc_flags |= SMSC_FLAG_LINK;
563b011f8c4SOleksandr Tymoshenko break;
564b011f8c4SOleksandr Tymoshenko case IFM_1000_T:
565b011f8c4SOleksandr Tymoshenko /* Gigabit ethernet not supported by chipset */
566b011f8c4SOleksandr Tymoshenko break;
567b011f8c4SOleksandr Tymoshenko default:
568b011f8c4SOleksandr Tymoshenko break;
569b011f8c4SOleksandr Tymoshenko }
570b011f8c4SOleksandr Tymoshenko }
571b011f8c4SOleksandr Tymoshenko
572b011f8c4SOleksandr Tymoshenko /* Lost link, do nothing. */
573b011f8c4SOleksandr Tymoshenko if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) {
574b011f8c4SOleksandr Tymoshenko smsc_dbg_printf(sc, "link flag not set\n");
575b011f8c4SOleksandr Tymoshenko goto done;
576b011f8c4SOleksandr Tymoshenko }
577b011f8c4SOleksandr Tymoshenko
578b011f8c4SOleksandr Tymoshenko err = smsc_read_reg(sc, SMSC_AFC_CFG, &afc_cfg);
579b011f8c4SOleksandr Tymoshenko if (err) {
580b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "failed to read initial AFC_CFG, error %d\n", err);
581b011f8c4SOleksandr Tymoshenko goto done;
582b011f8c4SOleksandr Tymoshenko }
583b011f8c4SOleksandr Tymoshenko
584b011f8c4SOleksandr Tymoshenko /* Enable/disable full duplex operation and TX/RX pause */
585b011f8c4SOleksandr Tymoshenko if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
586b011f8c4SOleksandr Tymoshenko smsc_dbg_printf(sc, "full duplex operation\n");
587b011f8c4SOleksandr Tymoshenko sc->sc_mac_csr &= ~SMSC_MAC_CSR_RCVOWN;
588b011f8c4SOleksandr Tymoshenko sc->sc_mac_csr |= SMSC_MAC_CSR_FDPX;
589b011f8c4SOleksandr Tymoshenko
590b011f8c4SOleksandr Tymoshenko if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0)
591b011f8c4SOleksandr Tymoshenko flow = 0xffff0002;
592b011f8c4SOleksandr Tymoshenko else
593b011f8c4SOleksandr Tymoshenko flow = 0;
594b011f8c4SOleksandr Tymoshenko
595b011f8c4SOleksandr Tymoshenko if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0)
596b011f8c4SOleksandr Tymoshenko afc_cfg |= 0xf;
597b011f8c4SOleksandr Tymoshenko else
598b011f8c4SOleksandr Tymoshenko afc_cfg &= ~0xf;
599b011f8c4SOleksandr Tymoshenko
600b011f8c4SOleksandr Tymoshenko } else {
601b011f8c4SOleksandr Tymoshenko smsc_dbg_printf(sc, "half duplex operation\n");
602b011f8c4SOleksandr Tymoshenko sc->sc_mac_csr &= ~SMSC_MAC_CSR_FDPX;
603b011f8c4SOleksandr Tymoshenko sc->sc_mac_csr |= SMSC_MAC_CSR_RCVOWN;
604b011f8c4SOleksandr Tymoshenko
605b011f8c4SOleksandr Tymoshenko flow = 0;
606b011f8c4SOleksandr Tymoshenko afc_cfg |= 0xf;
607b011f8c4SOleksandr Tymoshenko }
608b011f8c4SOleksandr Tymoshenko
609b011f8c4SOleksandr Tymoshenko err = smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
610b011f8c4SOleksandr Tymoshenko err += smsc_write_reg(sc, SMSC_FLOW, flow);
611b011f8c4SOleksandr Tymoshenko err += smsc_write_reg(sc, SMSC_AFC_CFG, afc_cfg);
612b011f8c4SOleksandr Tymoshenko if (err)
613b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "media change failed, error %d\n", err);
614b011f8c4SOleksandr Tymoshenko
615b011f8c4SOleksandr Tymoshenko done:
616b011f8c4SOleksandr Tymoshenko if (!locked)
617b011f8c4SOleksandr Tymoshenko SMSC_UNLOCK(sc);
618b011f8c4SOleksandr Tymoshenko }
619b011f8c4SOleksandr Tymoshenko
620b011f8c4SOleksandr Tymoshenko /**
621b011f8c4SOleksandr Tymoshenko * smsc_ifmedia_upd - Set media options
622b011f8c4SOleksandr Tymoshenko * @ifp: interface pointer
623b011f8c4SOleksandr Tymoshenko *
624b011f8c4SOleksandr Tymoshenko * Basically boilerplate code that simply calls the mii functions to set the
625b011f8c4SOleksandr Tymoshenko * media options.
626b011f8c4SOleksandr Tymoshenko *
627b011f8c4SOleksandr Tymoshenko * LOCKING:
628b011f8c4SOleksandr Tymoshenko * The device lock must be held before this function is called.
629b011f8c4SOleksandr Tymoshenko *
630b011f8c4SOleksandr Tymoshenko * RETURNS:
631b011f8c4SOleksandr Tymoshenko * Returns 0 on success or a negative error code.
632b011f8c4SOleksandr Tymoshenko */
633b011f8c4SOleksandr Tymoshenko static int
smsc_ifmedia_upd(if_t ifp)634935b194dSJustin Hibbits smsc_ifmedia_upd(if_t ifp)
635b011f8c4SOleksandr Tymoshenko {
636935b194dSJustin Hibbits struct smsc_softc *sc = if_getsoftc(ifp);
637b011f8c4SOleksandr Tymoshenko struct mii_data *mii = uether_getmii(&sc->sc_ue);
638ebe01023SKevin Lo struct mii_softc *miisc;
639b011f8c4SOleksandr Tymoshenko int err;
640b011f8c4SOleksandr Tymoshenko
641b011f8c4SOleksandr Tymoshenko SMSC_LOCK_ASSERT(sc, MA_OWNED);
642b011f8c4SOleksandr Tymoshenko
643b011f8c4SOleksandr Tymoshenko LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
644ebe01023SKevin Lo PHY_RESET(miisc);
645b011f8c4SOleksandr Tymoshenko err = mii_mediachg(mii);
646b011f8c4SOleksandr Tymoshenko return (err);
647b011f8c4SOleksandr Tymoshenko }
648b011f8c4SOleksandr Tymoshenko
649b011f8c4SOleksandr Tymoshenko /**
650b011f8c4SOleksandr Tymoshenko * smsc_ifmedia_sts - Report current media status
651b011f8c4SOleksandr Tymoshenko * @ifp: inet interface pointer
652b011f8c4SOleksandr Tymoshenko * @ifmr: interface media request
653b011f8c4SOleksandr Tymoshenko *
654b011f8c4SOleksandr Tymoshenko * Basically boilerplate code that simply calls the mii functions to get the
655b011f8c4SOleksandr Tymoshenko * media status.
656b011f8c4SOleksandr Tymoshenko *
657b011f8c4SOleksandr Tymoshenko * LOCKING:
658b011f8c4SOleksandr Tymoshenko * Internally takes and releases the device lock.
659b011f8c4SOleksandr Tymoshenko */
660b011f8c4SOleksandr Tymoshenko static void
smsc_ifmedia_sts(if_t ifp,struct ifmediareq * ifmr)661935b194dSJustin Hibbits smsc_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
662b011f8c4SOleksandr Tymoshenko {
663935b194dSJustin Hibbits struct smsc_softc *sc = if_getsoftc(ifp);
664b011f8c4SOleksandr Tymoshenko struct mii_data *mii = uether_getmii(&sc->sc_ue);
665b011f8c4SOleksandr Tymoshenko
666b011f8c4SOleksandr Tymoshenko SMSC_LOCK(sc);
667b011f8c4SOleksandr Tymoshenko mii_pollstat(mii);
668b011f8c4SOleksandr Tymoshenko ifmr->ifm_active = mii->mii_media_active;
669b011f8c4SOleksandr Tymoshenko ifmr->ifm_status = mii->mii_media_status;
670ebe01023SKevin Lo SMSC_UNLOCK(sc);
671b011f8c4SOleksandr Tymoshenko }
672b011f8c4SOleksandr Tymoshenko
673b011f8c4SOleksandr Tymoshenko /**
674b011f8c4SOleksandr Tymoshenko * smsc_hash - Calculate the hash of a mac address
675b011f8c4SOleksandr Tymoshenko * @addr: The mac address to calculate the hash on
676b011f8c4SOleksandr Tymoshenko *
677b011f8c4SOleksandr Tymoshenko * This function is used when configuring a range of m'cast mac addresses to
678b011f8c4SOleksandr Tymoshenko * filter on. The hash of the mac address is put in the device's mac hash
679b011f8c4SOleksandr Tymoshenko * table.
680b011f8c4SOleksandr Tymoshenko *
681b011f8c4SOleksandr Tymoshenko * RETURNS:
682b011f8c4SOleksandr Tymoshenko * Returns a value from 0-63 value which is the hash of the mac address.
683b011f8c4SOleksandr Tymoshenko */
684b011f8c4SOleksandr Tymoshenko static inline uint32_t
smsc_hash(uint8_t addr[ETHER_ADDR_LEN])685b011f8c4SOleksandr Tymoshenko smsc_hash(uint8_t addr[ETHER_ADDR_LEN])
686b011f8c4SOleksandr Tymoshenko {
687b011f8c4SOleksandr Tymoshenko return (ether_crc32_be(addr, ETHER_ADDR_LEN) >> 26) & 0x3f;
688b011f8c4SOleksandr Tymoshenko }
689b011f8c4SOleksandr Tymoshenko
6900a738e4dSGleb Smirnoff static u_int
smsc_hash_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)6910a738e4dSGleb Smirnoff smsc_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
6920a738e4dSGleb Smirnoff {
6930a738e4dSGleb Smirnoff uint32_t hash, *hashtbl = arg;
6940a738e4dSGleb Smirnoff
6950a738e4dSGleb Smirnoff hash = smsc_hash(LLADDR(sdl));
6960a738e4dSGleb Smirnoff hashtbl[hash >> 5] |= 1 << (hash & 0x1F);
6970a738e4dSGleb Smirnoff
6980a738e4dSGleb Smirnoff return (1);
6990a738e4dSGleb Smirnoff }
7000a738e4dSGleb Smirnoff
701b011f8c4SOleksandr Tymoshenko /**
702b011f8c4SOleksandr Tymoshenko * smsc_setmulti - Setup multicast
703b011f8c4SOleksandr Tymoshenko * @ue: usb ethernet device context
704b011f8c4SOleksandr Tymoshenko *
705b011f8c4SOleksandr Tymoshenko * Tells the device to either accept frames with a multicast mac address, a
706b011f8c4SOleksandr Tymoshenko * select group of m'cast mac addresses or just the devices mac address.
707b011f8c4SOleksandr Tymoshenko *
708b011f8c4SOleksandr Tymoshenko * LOCKING:
709b011f8c4SOleksandr Tymoshenko * Should be called with the SMSC lock held.
710b011f8c4SOleksandr Tymoshenko */
711b011f8c4SOleksandr Tymoshenko static void
smsc_setmulti(struct usb_ether * ue)712b011f8c4SOleksandr Tymoshenko smsc_setmulti(struct usb_ether *ue)
713b011f8c4SOleksandr Tymoshenko {
714b011f8c4SOleksandr Tymoshenko struct smsc_softc *sc = uether_getsc(ue);
715935b194dSJustin Hibbits if_t ifp = uether_getifp(ue);
716b011f8c4SOleksandr Tymoshenko uint32_t hashtbl[2] = { 0, 0 };
717b011f8c4SOleksandr Tymoshenko
718b011f8c4SOleksandr Tymoshenko SMSC_LOCK_ASSERT(sc, MA_OWNED);
719b011f8c4SOleksandr Tymoshenko
720935b194dSJustin Hibbits if (if_getflags(ifp) & (IFF_ALLMULTI | IFF_PROMISC)) {
721b011f8c4SOleksandr Tymoshenko smsc_dbg_printf(sc, "receive all multicast enabled\n");
722b011f8c4SOleksandr Tymoshenko sc->sc_mac_csr |= SMSC_MAC_CSR_MCPAS;
723b011f8c4SOleksandr Tymoshenko sc->sc_mac_csr &= ~SMSC_MAC_CSR_HPFILT;
724b011f8c4SOleksandr Tymoshenko
725b011f8c4SOleksandr Tymoshenko } else {
7260a738e4dSGleb Smirnoff if (if_foreach_llmaddr(ifp, smsc_hash_maddr, &hashtbl) > 0) {
7270a738e4dSGleb Smirnoff /* We are filtering on a set of address so calculate
7280a738e4dSGleb Smirnoff * hashes of each of the address and set the
7290a738e4dSGleb Smirnoff * corresponding bits in the register.
730b011f8c4SOleksandr Tymoshenko */
731b011f8c4SOleksandr Tymoshenko sc->sc_mac_csr |= SMSC_MAC_CSR_HPFILT;
732b011f8c4SOleksandr Tymoshenko sc->sc_mac_csr &= ~(SMSC_MAC_CSR_PRMS | SMSC_MAC_CSR_MCPAS);
733b011f8c4SOleksandr Tymoshenko } else {
7340a738e4dSGleb Smirnoff /* Only receive packets with destination set to
7350a738e4dSGleb Smirnoff * our mac address
7360a738e4dSGleb Smirnoff */
737b011f8c4SOleksandr Tymoshenko sc->sc_mac_csr &= ~(SMSC_MAC_CSR_MCPAS | SMSC_MAC_CSR_HPFILT);
738b011f8c4SOleksandr Tymoshenko }
739b011f8c4SOleksandr Tymoshenko
740b011f8c4SOleksandr Tymoshenko /* Debug */
741b011f8c4SOleksandr Tymoshenko if (sc->sc_mac_csr & SMSC_MAC_CSR_HPFILT)
742b011f8c4SOleksandr Tymoshenko smsc_dbg_printf(sc, "receive select group of macs\n");
743b011f8c4SOleksandr Tymoshenko else
744b011f8c4SOleksandr Tymoshenko smsc_dbg_printf(sc, "receive own packets only\n");
745b011f8c4SOleksandr Tymoshenko }
746b011f8c4SOleksandr Tymoshenko
747b011f8c4SOleksandr Tymoshenko /* Write the hash table and mac control registers */
748b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_HASHH, hashtbl[1]);
749b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_HASHL, hashtbl[0]);
750b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
751b011f8c4SOleksandr Tymoshenko }
752b011f8c4SOleksandr Tymoshenko
753b011f8c4SOleksandr Tymoshenko /**
754b011f8c4SOleksandr Tymoshenko * smsc_setpromisc - Enables/disables promiscuous mode
755b011f8c4SOleksandr Tymoshenko * @ue: usb ethernet device context
756b011f8c4SOleksandr Tymoshenko *
757b011f8c4SOleksandr Tymoshenko * LOCKING:
758b011f8c4SOleksandr Tymoshenko * Should be called with the SMSC lock held.
759b011f8c4SOleksandr Tymoshenko */
760b011f8c4SOleksandr Tymoshenko static void
smsc_setpromisc(struct usb_ether * ue)761b011f8c4SOleksandr Tymoshenko smsc_setpromisc(struct usb_ether *ue)
762b011f8c4SOleksandr Tymoshenko {
763b011f8c4SOleksandr Tymoshenko struct smsc_softc *sc = uether_getsc(ue);
764935b194dSJustin Hibbits if_t ifp = uether_getifp(ue);
765b011f8c4SOleksandr Tymoshenko
766b011f8c4SOleksandr Tymoshenko smsc_dbg_printf(sc, "promiscuous mode %sabled\n",
767935b194dSJustin Hibbits (if_getflags(ifp) & IFF_PROMISC) ? "en" : "dis");
768b011f8c4SOleksandr Tymoshenko
769b011f8c4SOleksandr Tymoshenko SMSC_LOCK_ASSERT(sc, MA_OWNED);
770b011f8c4SOleksandr Tymoshenko
771935b194dSJustin Hibbits if (if_getflags(ifp) & IFF_PROMISC)
772b011f8c4SOleksandr Tymoshenko sc->sc_mac_csr |= SMSC_MAC_CSR_PRMS;
773b011f8c4SOleksandr Tymoshenko else
774b011f8c4SOleksandr Tymoshenko sc->sc_mac_csr &= ~SMSC_MAC_CSR_PRMS;
775b011f8c4SOleksandr Tymoshenko
776b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
777b011f8c4SOleksandr Tymoshenko }
778b011f8c4SOleksandr Tymoshenko
779b011f8c4SOleksandr Tymoshenko /**
780b011f8c4SOleksandr Tymoshenko * smsc_sethwcsum - Enable or disable H/W UDP and TCP checksumming
781b011f8c4SOleksandr Tymoshenko * @sc: driver soft context
782b011f8c4SOleksandr Tymoshenko *
783b011f8c4SOleksandr Tymoshenko * LOCKING:
784b011f8c4SOleksandr Tymoshenko * Should be called with the SMSC lock held.
785b011f8c4SOleksandr Tymoshenko *
786b011f8c4SOleksandr Tymoshenko * RETURNS:
787b011f8c4SOleksandr Tymoshenko * Returns 0 on success or a negative error code.
788b011f8c4SOleksandr Tymoshenko */
smsc_sethwcsum(struct smsc_softc * sc)789b011f8c4SOleksandr Tymoshenko static int smsc_sethwcsum(struct smsc_softc *sc)
790b011f8c4SOleksandr Tymoshenko {
791935b194dSJustin Hibbits if_t ifp = uether_getifp(&sc->sc_ue);
792b011f8c4SOleksandr Tymoshenko uint32_t val;
793b011f8c4SOleksandr Tymoshenko int err;
794b011f8c4SOleksandr Tymoshenko
795b011f8c4SOleksandr Tymoshenko if (!ifp)
796b011f8c4SOleksandr Tymoshenko return (-EIO);
797b011f8c4SOleksandr Tymoshenko
798b011f8c4SOleksandr Tymoshenko SMSC_LOCK_ASSERT(sc, MA_OWNED);
799b011f8c4SOleksandr Tymoshenko
800b011f8c4SOleksandr Tymoshenko err = smsc_read_reg(sc, SMSC_COE_CTRL, &val);
801b011f8c4SOleksandr Tymoshenko if (err != 0) {
802b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "failed to read SMSC_COE_CTRL (err=%d)\n", err);
803b011f8c4SOleksandr Tymoshenko return (err);
804b011f8c4SOleksandr Tymoshenko }
805b011f8c4SOleksandr Tymoshenko
806b011f8c4SOleksandr Tymoshenko /* Enable/disable the Rx checksum */
807935b194dSJustin Hibbits if ((if_getcapabilities(ifp) & if_getcapenable(ifp)) & IFCAP_RXCSUM)
808b011f8c4SOleksandr Tymoshenko val |= SMSC_COE_CTRL_RX_EN;
809b011f8c4SOleksandr Tymoshenko else
810b011f8c4SOleksandr Tymoshenko val &= ~SMSC_COE_CTRL_RX_EN;
811b011f8c4SOleksandr Tymoshenko
812b011f8c4SOleksandr Tymoshenko /* Enable/disable the Tx checksum (currently not supported) */
813935b194dSJustin Hibbits if ((if_getcapabilities(ifp) & if_getcapenable(ifp)) & IFCAP_TXCSUM)
814b011f8c4SOleksandr Tymoshenko val |= SMSC_COE_CTRL_TX_EN;
815b011f8c4SOleksandr Tymoshenko else
816b011f8c4SOleksandr Tymoshenko val &= ~SMSC_COE_CTRL_TX_EN;
817b011f8c4SOleksandr Tymoshenko
818b011f8c4SOleksandr Tymoshenko err = smsc_write_reg(sc, SMSC_COE_CTRL, val);
819b011f8c4SOleksandr Tymoshenko if (err != 0) {
820b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "failed to write SMSC_COE_CTRL (err=%d)\n", err);
821b011f8c4SOleksandr Tymoshenko return (err);
822b011f8c4SOleksandr Tymoshenko }
823b011f8c4SOleksandr Tymoshenko
824b011f8c4SOleksandr Tymoshenko return (0);
825b011f8c4SOleksandr Tymoshenko }
826b011f8c4SOleksandr Tymoshenko
827b011f8c4SOleksandr Tymoshenko /**
828b011f8c4SOleksandr Tymoshenko * smsc_setmacaddress - Sets the mac address in the device
829b011f8c4SOleksandr Tymoshenko * @sc: driver soft context
830b011f8c4SOleksandr Tymoshenko * @addr: pointer to array contain at least 6 bytes of the mac
831b011f8c4SOleksandr Tymoshenko *
832b011f8c4SOleksandr Tymoshenko * Writes the MAC address into the device, usually the MAC is programmed with
833b011f8c4SOleksandr Tymoshenko * values from the EEPROM.
834b011f8c4SOleksandr Tymoshenko *
835b011f8c4SOleksandr Tymoshenko * LOCKING:
836b011f8c4SOleksandr Tymoshenko * Should be called with the SMSC lock held.
837b011f8c4SOleksandr Tymoshenko *
838b011f8c4SOleksandr Tymoshenko * RETURNS:
839b011f8c4SOleksandr Tymoshenko * Returns 0 on success or a negative error code.
840b011f8c4SOleksandr Tymoshenko */
841b011f8c4SOleksandr Tymoshenko static int
smsc_setmacaddress(struct smsc_softc * sc,const uint8_t * addr)842b011f8c4SOleksandr Tymoshenko smsc_setmacaddress(struct smsc_softc *sc, const uint8_t *addr)
843b011f8c4SOleksandr Tymoshenko {
844b011f8c4SOleksandr Tymoshenko int err;
845b011f8c4SOleksandr Tymoshenko uint32_t val;
846b011f8c4SOleksandr Tymoshenko
847b011f8c4SOleksandr Tymoshenko smsc_dbg_printf(sc, "setting mac address to %02x:%02x:%02x:%02x:%02x:%02x\n",
848b011f8c4SOleksandr Tymoshenko addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
849b011f8c4SOleksandr Tymoshenko
850b011f8c4SOleksandr Tymoshenko SMSC_LOCK_ASSERT(sc, MA_OWNED);
851b011f8c4SOleksandr Tymoshenko
852b011f8c4SOleksandr Tymoshenko val = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
853b011f8c4SOleksandr Tymoshenko if ((err = smsc_write_reg(sc, SMSC_MAC_ADDRL, val)) != 0)
854b011f8c4SOleksandr Tymoshenko goto done;
855b011f8c4SOleksandr Tymoshenko
856b011f8c4SOleksandr Tymoshenko val = (addr[5] << 8) | addr[4];
857b011f8c4SOleksandr Tymoshenko err = smsc_write_reg(sc, SMSC_MAC_ADDRH, val);
858b011f8c4SOleksandr Tymoshenko
859b011f8c4SOleksandr Tymoshenko done:
860b011f8c4SOleksandr Tymoshenko return (err);
861b011f8c4SOleksandr Tymoshenko }
862b011f8c4SOleksandr Tymoshenko
863b011f8c4SOleksandr Tymoshenko /**
864b011f8c4SOleksandr Tymoshenko * smsc_reset - Reset the SMSC chip
865b011f8c4SOleksandr Tymoshenko * @sc: device soft context
866b011f8c4SOleksandr Tymoshenko *
867b011f8c4SOleksandr Tymoshenko * LOCKING:
868b011f8c4SOleksandr Tymoshenko * Should be called with the SMSC lock held.
869b011f8c4SOleksandr Tymoshenko */
870b011f8c4SOleksandr Tymoshenko static void
smsc_reset(struct smsc_softc * sc)871b011f8c4SOleksandr Tymoshenko smsc_reset(struct smsc_softc *sc)
872b011f8c4SOleksandr Tymoshenko {
873b011f8c4SOleksandr Tymoshenko struct usb_config_descriptor *cd;
874b011f8c4SOleksandr Tymoshenko usb_error_t err;
875b011f8c4SOleksandr Tymoshenko
876b011f8c4SOleksandr Tymoshenko cd = usbd_get_config_descriptor(sc->sc_ue.ue_udev);
877b011f8c4SOleksandr Tymoshenko
878b011f8c4SOleksandr Tymoshenko err = usbd_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx,
879b011f8c4SOleksandr Tymoshenko cd->bConfigurationValue);
880b011f8c4SOleksandr Tymoshenko if (err)
881b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "reset failed (ignored)\n");
882b011f8c4SOleksandr Tymoshenko
883b011f8c4SOleksandr Tymoshenko /* Wait a little while for the chip to get its brains in order. */
884b011f8c4SOleksandr Tymoshenko uether_pause(&sc->sc_ue, hz / 100);
885b011f8c4SOleksandr Tymoshenko
886b011f8c4SOleksandr Tymoshenko /* Reinitialize controller to achieve full reset. */
887b011f8c4SOleksandr Tymoshenko smsc_chip_init(sc);
888b011f8c4SOleksandr Tymoshenko }
889b011f8c4SOleksandr Tymoshenko
890b011f8c4SOleksandr Tymoshenko /**
891b011f8c4SOleksandr Tymoshenko * smsc_init - Initialises the LAN95xx chip
892b011f8c4SOleksandr Tymoshenko * @ue: USB ether interface
893b011f8c4SOleksandr Tymoshenko *
894b011f8c4SOleksandr Tymoshenko * Called when the interface is brought up (i.e. ifconfig ue0 up), this
895b011f8c4SOleksandr Tymoshenko * initialise the interface and the rx/tx pipes.
896b011f8c4SOleksandr Tymoshenko *
897b011f8c4SOleksandr Tymoshenko * LOCKING:
898b011f8c4SOleksandr Tymoshenko * Should be called with the SMSC lock held.
899b011f8c4SOleksandr Tymoshenko */
900b011f8c4SOleksandr Tymoshenko static void
smsc_init(struct usb_ether * ue)901b011f8c4SOleksandr Tymoshenko smsc_init(struct usb_ether *ue)
902b011f8c4SOleksandr Tymoshenko {
903b011f8c4SOleksandr Tymoshenko struct smsc_softc *sc = uether_getsc(ue);
904935b194dSJustin Hibbits if_t ifp = uether_getifp(ue);
905b011f8c4SOleksandr Tymoshenko
906b011f8c4SOleksandr Tymoshenko SMSC_LOCK_ASSERT(sc, MA_OWNED);
907b011f8c4SOleksandr Tymoshenko
908935b194dSJustin Hibbits if (smsc_setmacaddress(sc, if_getlladdr(ifp)))
90933299e3dSHans Petter Selasky smsc_dbg_printf(sc, "setting MAC address failed\n");
91033299e3dSHans Petter Selasky
911935b194dSJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
912b011f8c4SOleksandr Tymoshenko return;
913b011f8c4SOleksandr Tymoshenko
914b011f8c4SOleksandr Tymoshenko /* Cancel pending I/O */
915b011f8c4SOleksandr Tymoshenko smsc_stop(ue);
916b011f8c4SOleksandr Tymoshenko
917b011f8c4SOleksandr Tymoshenko /* Reset the ethernet interface. */
918b011f8c4SOleksandr Tymoshenko smsc_reset(sc);
919b011f8c4SOleksandr Tymoshenko
920b011f8c4SOleksandr Tymoshenko /* Load the multicast filter. */
921b011f8c4SOleksandr Tymoshenko smsc_setmulti(ue);
922b011f8c4SOleksandr Tymoshenko
923b011f8c4SOleksandr Tymoshenko /* TCP/UDP checksum offload engines. */
924b011f8c4SOleksandr Tymoshenko smsc_sethwcsum(sc);
925b011f8c4SOleksandr Tymoshenko
926b011f8c4SOleksandr Tymoshenko usbd_xfer_set_stall(sc->sc_xfer[SMSC_BULK_DT_WR]);
927b011f8c4SOleksandr Tymoshenko
928b011f8c4SOleksandr Tymoshenko /* Indicate we are up and running. */
929935b194dSJustin Hibbits if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
930b011f8c4SOleksandr Tymoshenko
931b011f8c4SOleksandr Tymoshenko /* Switch to selected media. */
932b011f8c4SOleksandr Tymoshenko smsc_ifmedia_upd(ifp);
933b011f8c4SOleksandr Tymoshenko smsc_start(ue);
934b011f8c4SOleksandr Tymoshenko }
935b011f8c4SOleksandr Tymoshenko
936b011f8c4SOleksandr Tymoshenko /**
937b011f8c4SOleksandr Tymoshenko * smsc_bulk_read_callback - Read callback used to process the USB URB
938b011f8c4SOleksandr Tymoshenko * @xfer: the USB transfer
939b011f8c4SOleksandr Tymoshenko * @error:
940b011f8c4SOleksandr Tymoshenko *
941b011f8c4SOleksandr Tymoshenko * Reads the URB data which can contain one or more ethernet frames, the
942b011f8c4SOleksandr Tymoshenko * frames are copyed into a mbuf and given to the system.
943b011f8c4SOleksandr Tymoshenko *
944b011f8c4SOleksandr Tymoshenko * LOCKING:
945b011f8c4SOleksandr Tymoshenko * No locking required, doesn't access internal driver settings.
946b011f8c4SOleksandr Tymoshenko */
947b011f8c4SOleksandr Tymoshenko static void
smsc_bulk_read_callback(struct usb_xfer * xfer,usb_error_t error)948b011f8c4SOleksandr Tymoshenko smsc_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
949b011f8c4SOleksandr Tymoshenko {
950b011f8c4SOleksandr Tymoshenko struct smsc_softc *sc = usbd_xfer_softc(xfer);
951b011f8c4SOleksandr Tymoshenko struct usb_ether *ue = &sc->sc_ue;
952935b194dSJustin Hibbits if_t ifp = uether_getifp(ue);
953b011f8c4SOleksandr Tymoshenko struct mbuf *m;
954b011f8c4SOleksandr Tymoshenko struct usb_page_cache *pc;
955b011f8c4SOleksandr Tymoshenko uint32_t rxhdr;
9569c847ffdSHans Petter Selasky int pktlen;
957b011f8c4SOleksandr Tymoshenko int off;
958b011f8c4SOleksandr Tymoshenko int actlen;
959b011f8c4SOleksandr Tymoshenko
960b011f8c4SOleksandr Tymoshenko usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
961b011f8c4SOleksandr Tymoshenko smsc_dbg_printf(sc, "rx : actlen %d\n", actlen);
962b011f8c4SOleksandr Tymoshenko
963b011f8c4SOleksandr Tymoshenko switch (USB_GET_STATE(xfer)) {
964b011f8c4SOleksandr Tymoshenko case USB_ST_TRANSFERRED:
965b011f8c4SOleksandr Tymoshenko
966b011f8c4SOleksandr Tymoshenko /* There is always a zero length frame after bringing the IF up */
967b011f8c4SOleksandr Tymoshenko if (actlen < (sizeof(rxhdr) + ETHER_CRC_LEN))
968b011f8c4SOleksandr Tymoshenko goto tr_setup;
969b011f8c4SOleksandr Tymoshenko
970b011f8c4SOleksandr Tymoshenko /* There maybe multiple packets in the USB frame, each will have a
971b011f8c4SOleksandr Tymoshenko * header and each needs to have it's own mbuf allocated and populated
972b011f8c4SOleksandr Tymoshenko * for it.
973b011f8c4SOleksandr Tymoshenko */
974b011f8c4SOleksandr Tymoshenko pc = usbd_xfer_get_frame(xfer, 0);
975b011f8c4SOleksandr Tymoshenko off = 0;
976b011f8c4SOleksandr Tymoshenko
977b011f8c4SOleksandr Tymoshenko while (off < actlen) {
978b011f8c4SOleksandr Tymoshenko
979b011f8c4SOleksandr Tymoshenko /* The frame header is always aligned on a 4 byte boundary */
980b011f8c4SOleksandr Tymoshenko off = ((off + 0x3) & ~0x3);
981b011f8c4SOleksandr Tymoshenko
9829c847ffdSHans Petter Selasky if ((off + sizeof(rxhdr)) > actlen)
9839c847ffdSHans Petter Selasky goto tr_setup;
9849c847ffdSHans Petter Selasky
985b011f8c4SOleksandr Tymoshenko usbd_copy_out(pc, off, &rxhdr, sizeof(rxhdr));
986b011f8c4SOleksandr Tymoshenko off += (sizeof(rxhdr) + ETHER_ALIGN);
987b011f8c4SOleksandr Tymoshenko rxhdr = le32toh(rxhdr);
988b011f8c4SOleksandr Tymoshenko
989b011f8c4SOleksandr Tymoshenko pktlen = (uint16_t)SMSC_RX_STAT_FRM_LENGTH(rxhdr);
990b011f8c4SOleksandr Tymoshenko
991b011f8c4SOleksandr Tymoshenko smsc_dbg_printf(sc, "rx : rxhdr 0x%08x : pktlen %d : actlen %d : "
992b011f8c4SOleksandr Tymoshenko "off %d\n", rxhdr, pktlen, actlen, off);
993b011f8c4SOleksandr Tymoshenko
994b011f8c4SOleksandr Tymoshenko
995b011f8c4SOleksandr Tymoshenko if (rxhdr & SMSC_RX_STAT_ERROR) {
996b011f8c4SOleksandr Tymoshenko smsc_dbg_printf(sc, "rx error (hdr 0x%08x)\n", rxhdr);
997ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
998b011f8c4SOleksandr Tymoshenko if (rxhdr & SMSC_RX_STAT_COLLISION)
999ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1);
1000b011f8c4SOleksandr Tymoshenko } else {
1001b011f8c4SOleksandr Tymoshenko /* Check if the ethernet frame is too big or too small */
1002b011f8c4SOleksandr Tymoshenko if ((pktlen < ETHER_HDR_LEN) || (pktlen > (actlen - off)))
1003b011f8c4SOleksandr Tymoshenko goto tr_setup;
1004b011f8c4SOleksandr Tymoshenko
1005b011f8c4SOleksandr Tymoshenko /* Create a new mbuf to store the packet in */
1006b011f8c4SOleksandr Tymoshenko m = uether_newbuf();
1007b011f8c4SOleksandr Tymoshenko if (m == NULL) {
1008b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "failed to create new mbuf\n");
1009ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
1010b011f8c4SOleksandr Tymoshenko goto tr_setup;
1011b011f8c4SOleksandr Tymoshenko }
10129c847ffdSHans Petter Selasky if (pktlen > m->m_len) {
10139c847ffdSHans Petter Selasky smsc_dbg_printf(sc, "buffer too small %d vs %d bytes",
10149c847ffdSHans Petter Selasky pktlen, m->m_len);
10159c847ffdSHans Petter Selasky if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
10169c847ffdSHans Petter Selasky m_freem(m);
10179c847ffdSHans Petter Selasky goto tr_setup;
10189c847ffdSHans Petter Selasky }
1019b011f8c4SOleksandr Tymoshenko usbd_copy_out(pc, off, mtod(m, uint8_t *), pktlen);
1020b011f8c4SOleksandr Tymoshenko
1021b011f8c4SOleksandr Tymoshenko /* Check if RX TCP/UDP checksumming is being offloaded */
1022935b194dSJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0) {
1023664413a1SHans Petter Selasky struct ether_header *eh;
1024664413a1SHans Petter Selasky
1025664413a1SHans Petter Selasky eh = mtod(m, struct ether_header *);
1026664413a1SHans Petter Selasky
1027b011f8c4SOleksandr Tymoshenko /* Remove the extra 2 bytes of the csum */
1028b011f8c4SOleksandr Tymoshenko pktlen -= 2;
1029b011f8c4SOleksandr Tymoshenko
1030b011f8c4SOleksandr Tymoshenko /* The checksum appears to be simplistically calculated
1031b011f8c4SOleksandr Tymoshenko * over the udp/tcp header and data up to the end of the
1032b011f8c4SOleksandr Tymoshenko * eth frame. Which means if the eth frame is padded
1033b011f8c4SOleksandr Tymoshenko * the csum calculation is incorrectly performed over
1034b011f8c4SOleksandr Tymoshenko * the padding bytes as well. Therefore to be safe we
1035b011f8c4SOleksandr Tymoshenko * ignore the H/W csum on frames less than or equal to
1036b011f8c4SOleksandr Tymoshenko * 64 bytes.
1037664413a1SHans Petter Selasky *
1038664413a1SHans Petter Selasky * Ignore H/W csum for non-IPv4 packets.
1039b011f8c4SOleksandr Tymoshenko */
10405a1a4f75SMichael Tuexen if ((be16toh(eh->ether_type) == ETHERTYPE_IP) &&
10415a1a4f75SMichael Tuexen (pktlen > ETHER_MIN_LEN)) {
10425a1a4f75SMichael Tuexen struct ip *ip;
1043b011f8c4SOleksandr Tymoshenko
10445a1a4f75SMichael Tuexen ip = (struct ip *)(eh + 1);
10455a1a4f75SMichael Tuexen if ((ip->ip_v == IPVERSION) &&
10465a1a4f75SMichael Tuexen ((ip->ip_p == IPPROTO_TCP) ||
10475a1a4f75SMichael Tuexen (ip->ip_p == IPPROTO_UDP))) {
1048b011f8c4SOleksandr Tymoshenko /* Indicate the UDP/TCP csum has been calculated */
1049b011f8c4SOleksandr Tymoshenko m->m_pkthdr.csum_flags |= CSUM_DATA_VALID;
1050b011f8c4SOleksandr Tymoshenko
1051b011f8c4SOleksandr Tymoshenko /* Copy the TCP/UDP checksum from the last 2 bytes
1052b011f8c4SOleksandr Tymoshenko * of the transfer and put in the csum_data field.
1053b011f8c4SOleksandr Tymoshenko */
1054b011f8c4SOleksandr Tymoshenko usbd_copy_out(pc, (off + pktlen),
1055b011f8c4SOleksandr Tymoshenko &m->m_pkthdr.csum_data, 2);
1056b011f8c4SOleksandr Tymoshenko
1057b011f8c4SOleksandr Tymoshenko /* The data is copied in network order, but the
1058b011f8c4SOleksandr Tymoshenko * csum algorithm in the kernel expects it to be
1059b011f8c4SOleksandr Tymoshenko * in host network order.
1060b011f8c4SOleksandr Tymoshenko */
1061b011f8c4SOleksandr Tymoshenko m->m_pkthdr.csum_data = ntohs(m->m_pkthdr.csum_data);
1062b011f8c4SOleksandr Tymoshenko
1063b011f8c4SOleksandr Tymoshenko smsc_dbg_printf(sc, "RX checksum offloaded (0x%04x)\n",
1064b011f8c4SOleksandr Tymoshenko m->m_pkthdr.csum_data);
1065b011f8c4SOleksandr Tymoshenko }
10665a1a4f75SMichael Tuexen }
1067b011f8c4SOleksandr Tymoshenko
1068b011f8c4SOleksandr Tymoshenko /* Need to adjust the offset as well or we'll be off
1069b011f8c4SOleksandr Tymoshenko * by 2 because the csum is removed from the packet
1070b011f8c4SOleksandr Tymoshenko * length.
1071b011f8c4SOleksandr Tymoshenko */
1072b011f8c4SOleksandr Tymoshenko off += 2;
1073b011f8c4SOleksandr Tymoshenko }
1074b011f8c4SOleksandr Tymoshenko
1075b011f8c4SOleksandr Tymoshenko /* Finally enqueue the mbuf on the receive queue */
107666249c7cSHans Petter Selasky /* Remove 4 trailing bytes */
10772196d98eSHans Petter Selasky if (pktlen < (4 + ETHER_HDR_LEN)) {
10782196d98eSHans Petter Selasky m_freem(m);
10792196d98eSHans Petter Selasky goto tr_setup;
10802196d98eSHans Petter Selasky }
108166249c7cSHans Petter Selasky uether_rxmbuf(ue, m, pktlen - 4);
1082b011f8c4SOleksandr Tymoshenko }
1083b011f8c4SOleksandr Tymoshenko
1084b011f8c4SOleksandr Tymoshenko /* Update the offset to move to the next potential packet */
1085b011f8c4SOleksandr Tymoshenko off += pktlen;
1086b011f8c4SOleksandr Tymoshenko }
1087b011f8c4SOleksandr Tymoshenko
1088b011f8c4SOleksandr Tymoshenko /* FALLTHROUGH */
1089b011f8c4SOleksandr Tymoshenko
1090b011f8c4SOleksandr Tymoshenko case USB_ST_SETUP:
1091b011f8c4SOleksandr Tymoshenko tr_setup:
1092b011f8c4SOleksandr Tymoshenko usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
1093b011f8c4SOleksandr Tymoshenko usbd_transfer_submit(xfer);
1094b011f8c4SOleksandr Tymoshenko uether_rxflush(ue);
1095b011f8c4SOleksandr Tymoshenko return;
1096b011f8c4SOleksandr Tymoshenko
1097b011f8c4SOleksandr Tymoshenko default:
1098b011f8c4SOleksandr Tymoshenko if (error != USB_ERR_CANCELLED) {
1099b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "bulk read error, %s\n", usbd_errstr(error));
1100b011f8c4SOleksandr Tymoshenko usbd_xfer_set_stall(xfer);
1101b011f8c4SOleksandr Tymoshenko goto tr_setup;
1102b011f8c4SOleksandr Tymoshenko }
1103b011f8c4SOleksandr Tymoshenko return;
1104b011f8c4SOleksandr Tymoshenko }
1105b011f8c4SOleksandr Tymoshenko }
1106b011f8c4SOleksandr Tymoshenko
1107b011f8c4SOleksandr Tymoshenko /**
1108b011f8c4SOleksandr Tymoshenko * smsc_bulk_write_callback - Write callback used to send ethernet frame(s)
1109b011f8c4SOleksandr Tymoshenko * @xfer: the USB transfer
1110b011f8c4SOleksandr Tymoshenko * @error: error code if the transfers is in an errored state
1111b011f8c4SOleksandr Tymoshenko *
1112b011f8c4SOleksandr Tymoshenko * The main write function that pulls ethernet frames off the queue and sends
1113b011f8c4SOleksandr Tymoshenko * them out.
1114b011f8c4SOleksandr Tymoshenko *
1115b011f8c4SOleksandr Tymoshenko * LOCKING:
1116b011f8c4SOleksandr Tymoshenko *
1117b011f8c4SOleksandr Tymoshenko */
1118b011f8c4SOleksandr Tymoshenko static void
smsc_bulk_write_callback(struct usb_xfer * xfer,usb_error_t error)1119b011f8c4SOleksandr Tymoshenko smsc_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
1120b011f8c4SOleksandr Tymoshenko {
1121b011f8c4SOleksandr Tymoshenko struct smsc_softc *sc = usbd_xfer_softc(xfer);
1122935b194dSJustin Hibbits if_t ifp = uether_getifp(&sc->sc_ue);
1123b011f8c4SOleksandr Tymoshenko struct usb_page_cache *pc;
1124b011f8c4SOleksandr Tymoshenko struct mbuf *m;
1125b011f8c4SOleksandr Tymoshenko uint32_t txhdr;
1126b011f8c4SOleksandr Tymoshenko uint32_t frm_len = 0;
1127b011f8c4SOleksandr Tymoshenko int nframes;
1128b011f8c4SOleksandr Tymoshenko
1129b011f8c4SOleksandr Tymoshenko switch (USB_GET_STATE(xfer)) {
1130b011f8c4SOleksandr Tymoshenko case USB_ST_TRANSFERRED:
1131935b194dSJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
1132b011f8c4SOleksandr Tymoshenko /* FALLTHROUGH */
1133b011f8c4SOleksandr Tymoshenko
1134b011f8c4SOleksandr Tymoshenko case USB_ST_SETUP:
1135b011f8c4SOleksandr Tymoshenko tr_setup:
1136b011f8c4SOleksandr Tymoshenko if ((sc->sc_flags & SMSC_FLAG_LINK) == 0 ||
1137935b194dSJustin Hibbits (if_getdrvflags(ifp) & IFF_DRV_OACTIVE) != 0) {
1138b011f8c4SOleksandr Tymoshenko /* Don't send anything if there is no link or controller is busy. */
1139b011f8c4SOleksandr Tymoshenko return;
1140b011f8c4SOleksandr Tymoshenko }
1141b011f8c4SOleksandr Tymoshenko
1142b011f8c4SOleksandr Tymoshenko for (nframes = 0; nframes < 16 &&
1143935b194dSJustin Hibbits !if_sendq_empty(ifp); nframes++) {
1144935b194dSJustin Hibbits m = if_dequeue(ifp);
1145b011f8c4SOleksandr Tymoshenko if (m == NULL)
1146b011f8c4SOleksandr Tymoshenko break;
1147b011f8c4SOleksandr Tymoshenko usbd_xfer_set_frame_offset(xfer, nframes * MCLBYTES,
1148b011f8c4SOleksandr Tymoshenko nframes);
1149b011f8c4SOleksandr Tymoshenko frm_len = 0;
1150b011f8c4SOleksandr Tymoshenko pc = usbd_xfer_get_frame(xfer, nframes);
1151b011f8c4SOleksandr Tymoshenko
1152b011f8c4SOleksandr Tymoshenko /* Each frame is prefixed with two 32-bit values describing the
1153b011f8c4SOleksandr Tymoshenko * length of the packet and buffer.
1154b011f8c4SOleksandr Tymoshenko */
1155b011f8c4SOleksandr Tymoshenko txhdr = SMSC_TX_CTRL_0_BUF_SIZE(m->m_pkthdr.len) |
1156b011f8c4SOleksandr Tymoshenko SMSC_TX_CTRL_0_FIRST_SEG | SMSC_TX_CTRL_0_LAST_SEG;
1157b011f8c4SOleksandr Tymoshenko txhdr = htole32(txhdr);
1158b011f8c4SOleksandr Tymoshenko usbd_copy_in(pc, 0, &txhdr, sizeof(txhdr));
1159b011f8c4SOleksandr Tymoshenko
1160b011f8c4SOleksandr Tymoshenko txhdr = SMSC_TX_CTRL_1_PKT_LENGTH(m->m_pkthdr.len);
1161b011f8c4SOleksandr Tymoshenko txhdr = htole32(txhdr);
1162b011f8c4SOleksandr Tymoshenko usbd_copy_in(pc, 4, &txhdr, sizeof(txhdr));
1163b011f8c4SOleksandr Tymoshenko
1164b011f8c4SOleksandr Tymoshenko frm_len += 8;
1165b011f8c4SOleksandr Tymoshenko
1166b011f8c4SOleksandr Tymoshenko /* Next copy in the actual packet */
1167b011f8c4SOleksandr Tymoshenko usbd_m_copy_in(pc, frm_len, m, 0, m->m_pkthdr.len);
1168b011f8c4SOleksandr Tymoshenko frm_len += m->m_pkthdr.len;
1169b011f8c4SOleksandr Tymoshenko
1170ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
1171b011f8c4SOleksandr Tymoshenko
1172b011f8c4SOleksandr Tymoshenko /* If there's a BPF listener, bounce a copy of this frame to him */
1173b011f8c4SOleksandr Tymoshenko BPF_MTAP(ifp, m);
1174b011f8c4SOleksandr Tymoshenko
1175b011f8c4SOleksandr Tymoshenko m_freem(m);
1176b011f8c4SOleksandr Tymoshenko
1177b011f8c4SOleksandr Tymoshenko /* Set frame length. */
1178b011f8c4SOleksandr Tymoshenko usbd_xfer_set_frame_len(xfer, nframes, frm_len);
1179b011f8c4SOleksandr Tymoshenko }
1180b011f8c4SOleksandr Tymoshenko if (nframes != 0) {
1181b011f8c4SOleksandr Tymoshenko usbd_xfer_set_frames(xfer, nframes);
1182b011f8c4SOleksandr Tymoshenko usbd_transfer_submit(xfer);
1183935b194dSJustin Hibbits if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
1184b011f8c4SOleksandr Tymoshenko }
1185b011f8c4SOleksandr Tymoshenko return;
1186b011f8c4SOleksandr Tymoshenko
1187b011f8c4SOleksandr Tymoshenko default:
1188ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
1189935b194dSJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
1190b011f8c4SOleksandr Tymoshenko
1191b011f8c4SOleksandr Tymoshenko if (error != USB_ERR_CANCELLED) {
1192b011f8c4SOleksandr Tymoshenko smsc_err_printf(sc, "usb error on tx: %s\n", usbd_errstr(error));
1193b011f8c4SOleksandr Tymoshenko usbd_xfer_set_stall(xfer);
1194b011f8c4SOleksandr Tymoshenko goto tr_setup;
1195b011f8c4SOleksandr Tymoshenko }
1196b011f8c4SOleksandr Tymoshenko return;
1197b011f8c4SOleksandr Tymoshenko }
1198b011f8c4SOleksandr Tymoshenko }
1199b011f8c4SOleksandr Tymoshenko
1200b011f8c4SOleksandr Tymoshenko /**
1201b011f8c4SOleksandr Tymoshenko * smsc_tick - Called periodically to monitor the state of the LAN95xx chip
1202b011f8c4SOleksandr Tymoshenko * @ue: USB ether interface
1203b011f8c4SOleksandr Tymoshenko *
1204b011f8c4SOleksandr Tymoshenko * Simply calls the mii status functions to check the state of the link.
1205b011f8c4SOleksandr Tymoshenko *
1206b011f8c4SOleksandr Tymoshenko * LOCKING:
1207b011f8c4SOleksandr Tymoshenko * Should be called with the SMSC lock held.
1208b011f8c4SOleksandr Tymoshenko */
1209b011f8c4SOleksandr Tymoshenko static void
smsc_tick(struct usb_ether * ue)1210b011f8c4SOleksandr Tymoshenko smsc_tick(struct usb_ether *ue)
1211b011f8c4SOleksandr Tymoshenko {
1212b011f8c4SOleksandr Tymoshenko struct smsc_softc *sc = uether_getsc(ue);
1213db702c59SEitan Adler struct mii_data *mii = uether_getmii(&sc->sc_ue);
1214b011f8c4SOleksandr Tymoshenko
1215b011f8c4SOleksandr Tymoshenko SMSC_LOCK_ASSERT(sc, MA_OWNED);
1216b011f8c4SOleksandr Tymoshenko
1217b011f8c4SOleksandr Tymoshenko mii_tick(mii);
1218b011f8c4SOleksandr Tymoshenko if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) {
1219b011f8c4SOleksandr Tymoshenko smsc_miibus_statchg(ue->ue_dev);
1220b011f8c4SOleksandr Tymoshenko if ((sc->sc_flags & SMSC_FLAG_LINK) != 0)
1221b011f8c4SOleksandr Tymoshenko smsc_start(ue);
1222b011f8c4SOleksandr Tymoshenko }
1223b011f8c4SOleksandr Tymoshenko }
1224b011f8c4SOleksandr Tymoshenko
1225b011f8c4SOleksandr Tymoshenko /**
1226b011f8c4SOleksandr Tymoshenko * smsc_start - Starts communication with the LAN95xx chip
1227b011f8c4SOleksandr Tymoshenko * @ue: USB ether interface
1228b011f8c4SOleksandr Tymoshenko *
1229b011f8c4SOleksandr Tymoshenko *
1230b011f8c4SOleksandr Tymoshenko *
1231b011f8c4SOleksandr Tymoshenko */
1232b011f8c4SOleksandr Tymoshenko static void
smsc_start(struct usb_ether * ue)1233b011f8c4SOleksandr Tymoshenko smsc_start(struct usb_ether *ue)
1234b011f8c4SOleksandr Tymoshenko {
1235b011f8c4SOleksandr Tymoshenko struct smsc_softc *sc = uether_getsc(ue);
1236b011f8c4SOleksandr Tymoshenko
1237b011f8c4SOleksandr Tymoshenko /*
1238b011f8c4SOleksandr Tymoshenko * start the USB transfers, if not already started:
1239b011f8c4SOleksandr Tymoshenko */
1240b011f8c4SOleksandr Tymoshenko usbd_transfer_start(sc->sc_xfer[SMSC_BULK_DT_RD]);
1241b011f8c4SOleksandr Tymoshenko usbd_transfer_start(sc->sc_xfer[SMSC_BULK_DT_WR]);
1242b011f8c4SOleksandr Tymoshenko }
1243b011f8c4SOleksandr Tymoshenko
1244b011f8c4SOleksandr Tymoshenko /**
1245b011f8c4SOleksandr Tymoshenko * smsc_stop - Stops communication with the LAN95xx chip
1246b011f8c4SOleksandr Tymoshenko * @ue: USB ether interface
1247b011f8c4SOleksandr Tymoshenko *
1248b011f8c4SOleksandr Tymoshenko *
1249b011f8c4SOleksandr Tymoshenko *
1250b011f8c4SOleksandr Tymoshenko */
1251b011f8c4SOleksandr Tymoshenko static void
smsc_stop(struct usb_ether * ue)1252b011f8c4SOleksandr Tymoshenko smsc_stop(struct usb_ether *ue)
1253b011f8c4SOleksandr Tymoshenko {
1254b011f8c4SOleksandr Tymoshenko struct smsc_softc *sc = uether_getsc(ue);
1255935b194dSJustin Hibbits if_t ifp = uether_getifp(ue);
1256b011f8c4SOleksandr Tymoshenko
1257b011f8c4SOleksandr Tymoshenko SMSC_LOCK_ASSERT(sc, MA_OWNED);
1258b011f8c4SOleksandr Tymoshenko
1259935b194dSJustin Hibbits if_setdrvflagbits(ifp, 0, (IFF_DRV_RUNNING | IFF_DRV_OACTIVE));
1260b011f8c4SOleksandr Tymoshenko sc->sc_flags &= ~SMSC_FLAG_LINK;
1261b011f8c4SOleksandr Tymoshenko
1262b011f8c4SOleksandr Tymoshenko /*
1263b011f8c4SOleksandr Tymoshenko * stop all the transfers, if not already stopped:
1264b011f8c4SOleksandr Tymoshenko */
1265b011f8c4SOleksandr Tymoshenko usbd_transfer_stop(sc->sc_xfer[SMSC_BULK_DT_WR]);
1266b011f8c4SOleksandr Tymoshenko usbd_transfer_stop(sc->sc_xfer[SMSC_BULK_DT_RD]);
1267b011f8c4SOleksandr Tymoshenko }
1268b011f8c4SOleksandr Tymoshenko
1269b011f8c4SOleksandr Tymoshenko /**
1270b011f8c4SOleksandr Tymoshenko * smsc_phy_init - Initialises the in-built SMSC phy
1271b011f8c4SOleksandr Tymoshenko * @sc: driver soft context
1272b011f8c4SOleksandr Tymoshenko *
1273b011f8c4SOleksandr Tymoshenko * Resets the PHY part of the chip and then initialises it to default
1274b011f8c4SOleksandr Tymoshenko * values. The 'link down' and 'auto-negotiation complete' interrupts
1275b011f8c4SOleksandr Tymoshenko * from the PHY are also enabled, however we don't monitor the interrupt
1276b011f8c4SOleksandr Tymoshenko * endpoints for the moment.
1277b011f8c4SOleksandr Tymoshenko *
1278b011f8c4SOleksandr Tymoshenko * RETURNS:
1279b011f8c4SOleksandr Tymoshenko * Returns 0 on success or EIO if failed to reset the PHY.
1280b011f8c4SOleksandr Tymoshenko */
1281b011f8c4SOleksandr Tymoshenko static int
smsc_phy_init(struct smsc_softc * sc)1282b011f8c4SOleksandr Tymoshenko smsc_phy_init(struct smsc_softc *sc)
1283b011f8c4SOleksandr Tymoshenko {
1284b011f8c4SOleksandr Tymoshenko int bmcr;
1285b011f8c4SOleksandr Tymoshenko usb_ticks_t start_ticks;
12869b42038bSHans Petter Selasky const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000);
1287b011f8c4SOleksandr Tymoshenko
1288b011f8c4SOleksandr Tymoshenko SMSC_LOCK_ASSERT(sc, MA_OWNED);
1289b011f8c4SOleksandr Tymoshenko
1290b011f8c4SOleksandr Tymoshenko /* Reset phy and wait for reset to complete */
1291b011f8c4SOleksandr Tymoshenko smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR, BMCR_RESET);
1292b011f8c4SOleksandr Tymoshenko
1293b011f8c4SOleksandr Tymoshenko start_ticks = ticks;
1294b011f8c4SOleksandr Tymoshenko do {
1295b011f8c4SOleksandr Tymoshenko uether_pause(&sc->sc_ue, hz / 100);
1296b011f8c4SOleksandr Tymoshenko bmcr = smsc_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR);
12978c9b26c3SEd Maste } while ((bmcr & BMCR_RESET) && ((ticks - start_ticks) < max_ticks));
1298b011f8c4SOleksandr Tymoshenko
12999b42038bSHans Petter Selasky if (((usb_ticks_t)(ticks - start_ticks)) >= max_ticks) {
1300b011f8c4SOleksandr Tymoshenko smsc_err_printf(sc, "PHY reset timed-out");
1301b011f8c4SOleksandr Tymoshenko return (EIO);
1302b011f8c4SOleksandr Tymoshenko }
1303b011f8c4SOleksandr Tymoshenko
1304b011f8c4SOleksandr Tymoshenko smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_ANAR,
1305b011f8c4SOleksandr Tymoshenko ANAR_10 | ANAR_10_FD | ANAR_TX | ANAR_TX_FD | /* all modes */
1306b011f8c4SOleksandr Tymoshenko ANAR_CSMA |
1307b011f8c4SOleksandr Tymoshenko ANAR_FC |
1308b011f8c4SOleksandr Tymoshenko ANAR_PAUSE_ASYM);
1309b011f8c4SOleksandr Tymoshenko
1310b011f8c4SOleksandr Tymoshenko /* Setup the phy to interrupt when the link goes down or autoneg completes */
1311b011f8c4SOleksandr Tymoshenko smsc_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, SMSC_PHY_INTR_STAT);
1312b011f8c4SOleksandr Tymoshenko smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, SMSC_PHY_INTR_MASK,
1313b011f8c4SOleksandr Tymoshenko (SMSC_PHY_INTR_ANEG_COMP | SMSC_PHY_INTR_LINK_DOWN));
1314b011f8c4SOleksandr Tymoshenko
1315c8c1c23aSGordon Bergling /* Restart auto-negotiation */
1316b011f8c4SOleksandr Tymoshenko bmcr = smsc_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR);
1317b011f8c4SOleksandr Tymoshenko bmcr |= BMCR_STARTNEG;
1318b011f8c4SOleksandr Tymoshenko smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR, bmcr);
1319b011f8c4SOleksandr Tymoshenko
1320b011f8c4SOleksandr Tymoshenko return (0);
1321b011f8c4SOleksandr Tymoshenko }
1322b011f8c4SOleksandr Tymoshenko
1323b011f8c4SOleksandr Tymoshenko /**
1324b011f8c4SOleksandr Tymoshenko * smsc_chip_init - Initialises the chip after power on
1325b011f8c4SOleksandr Tymoshenko * @sc: driver soft context
1326b011f8c4SOleksandr Tymoshenko *
1327b011f8c4SOleksandr Tymoshenko * This initialisation sequence is modelled on the procedure in the Linux
1328b011f8c4SOleksandr Tymoshenko * driver.
1329b011f8c4SOleksandr Tymoshenko *
1330b011f8c4SOleksandr Tymoshenko * RETURNS:
1331b011f8c4SOleksandr Tymoshenko * Returns 0 on success or an error code on failure.
1332b011f8c4SOleksandr Tymoshenko */
1333b011f8c4SOleksandr Tymoshenko static int
smsc_chip_init(struct smsc_softc * sc)1334b011f8c4SOleksandr Tymoshenko smsc_chip_init(struct smsc_softc *sc)
1335b011f8c4SOleksandr Tymoshenko {
1336b011f8c4SOleksandr Tymoshenko int err;
1337b011f8c4SOleksandr Tymoshenko int locked;
1338b011f8c4SOleksandr Tymoshenko uint32_t reg_val;
1339b011f8c4SOleksandr Tymoshenko int burst_cap;
1340b011f8c4SOleksandr Tymoshenko
1341b011f8c4SOleksandr Tymoshenko locked = mtx_owned(&sc->sc_mtx);
1342b011f8c4SOleksandr Tymoshenko if (!locked)
1343b011f8c4SOleksandr Tymoshenko SMSC_LOCK(sc);
1344b011f8c4SOleksandr Tymoshenko
1345b011f8c4SOleksandr Tymoshenko /* Enter H/W config mode */
1346b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_HW_CFG, SMSC_HW_CFG_LRST);
1347b011f8c4SOleksandr Tymoshenko
1348b011f8c4SOleksandr Tymoshenko if ((err = smsc_wait_for_bits(sc, SMSC_HW_CFG, SMSC_HW_CFG_LRST)) != 0) {
1349b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "timed-out waiting for reset to complete\n");
1350b011f8c4SOleksandr Tymoshenko goto init_failed;
1351b011f8c4SOleksandr Tymoshenko }
1352b011f8c4SOleksandr Tymoshenko
1353b011f8c4SOleksandr Tymoshenko /* Reset the PHY */
1354b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_PM_CTRL, SMSC_PM_CTRL_PHY_RST);
1355b011f8c4SOleksandr Tymoshenko
13568cb15594SHans Petter Selasky if ((err = smsc_wait_for_bits(sc, SMSC_PM_CTRL, SMSC_PM_CTRL_PHY_RST)) != 0) {
1357b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "timed-out waiting for phy reset to complete\n");
1358b011f8c4SOleksandr Tymoshenko goto init_failed;
1359b011f8c4SOleksandr Tymoshenko }
1360b011f8c4SOleksandr Tymoshenko
1361b011f8c4SOleksandr Tymoshenko /* Set the mac address */
1362b011f8c4SOleksandr Tymoshenko if ((err = smsc_setmacaddress(sc, sc->sc_ue.ue_eaddr)) != 0) {
1363b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "failed to set the MAC address\n");
1364b011f8c4SOleksandr Tymoshenko goto init_failed;
1365b011f8c4SOleksandr Tymoshenko }
1366b011f8c4SOleksandr Tymoshenko
1367b011f8c4SOleksandr Tymoshenko /* Don't know what the HW_CFG_BIR bit is, but following the reset sequence
1368b011f8c4SOleksandr Tymoshenko * as used in the Linux driver.
1369b011f8c4SOleksandr Tymoshenko */
1370b011f8c4SOleksandr Tymoshenko if ((err = smsc_read_reg(sc, SMSC_HW_CFG, ®_val)) != 0) {
1371b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "failed to read HW_CFG: %d\n", err);
1372b011f8c4SOleksandr Tymoshenko goto init_failed;
1373b011f8c4SOleksandr Tymoshenko }
1374b011f8c4SOleksandr Tymoshenko reg_val |= SMSC_HW_CFG_BIR;
1375b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_HW_CFG, reg_val);
1376b011f8c4SOleksandr Tymoshenko
1377b011f8c4SOleksandr Tymoshenko /* There is a so called 'turbo mode' that the linux driver supports, it
1378b011f8c4SOleksandr Tymoshenko * seems to allow you to jam multiple frames per Rx transaction. By default
1379b011f8c4SOleksandr Tymoshenko * this driver supports that and therefore allows multiple frames per URB.
1380b011f8c4SOleksandr Tymoshenko *
1381b011f8c4SOleksandr Tymoshenko * The xfer buffer size needs to reflect this as well, therefore based on
1382b011f8c4SOleksandr Tymoshenko * the calculations in the Linux driver the RX bufsize is set to 18944,
1383b011f8c4SOleksandr Tymoshenko * bufsz = (16 * 1024 + 5 * 512)
1384b011f8c4SOleksandr Tymoshenko *
1385b011f8c4SOleksandr Tymoshenko * Burst capability is the number of URBs that can be in a burst of data/
1386b011f8c4SOleksandr Tymoshenko * ethernet frames.
1387b011f8c4SOleksandr Tymoshenko */
13885a3426f4SHans Petter Selasky if (!smsc_rx_packet_batching)
13895a3426f4SHans Petter Selasky burst_cap = 0;
13905a3426f4SHans Petter Selasky else if (usbd_get_speed(sc->sc_ue.ue_udev) == USB_SPEED_HIGH)
1391b011f8c4SOleksandr Tymoshenko burst_cap = 37;
1392b011f8c4SOleksandr Tymoshenko else
1393b011f8c4SOleksandr Tymoshenko burst_cap = 128;
1394b011f8c4SOleksandr Tymoshenko
1395b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_BURST_CAP, burst_cap);
1396b011f8c4SOleksandr Tymoshenko
1397b011f8c4SOleksandr Tymoshenko /* Set the default bulk in delay (magic value from Linux driver) */
1398b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_BULK_IN_DLY, 0x00002000);
1399b011f8c4SOleksandr Tymoshenko
1400b011f8c4SOleksandr Tymoshenko /*
1401b011f8c4SOleksandr Tymoshenko * Initialise the RX interface
1402b011f8c4SOleksandr Tymoshenko */
1403b011f8c4SOleksandr Tymoshenko if ((err = smsc_read_reg(sc, SMSC_HW_CFG, ®_val)) < 0) {
1404b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "failed to read HW_CFG: (err = %d)\n", err);
1405b011f8c4SOleksandr Tymoshenko goto init_failed;
1406b011f8c4SOleksandr Tymoshenko }
1407b011f8c4SOleksandr Tymoshenko
1408b011f8c4SOleksandr Tymoshenko /* Adjust the packet offset in the buffer (designed to try and align IP
1409b011f8c4SOleksandr Tymoshenko * header on 4 byte boundary)
1410b011f8c4SOleksandr Tymoshenko */
1411b011f8c4SOleksandr Tymoshenko reg_val &= ~SMSC_HW_CFG_RXDOFF;
1412b011f8c4SOleksandr Tymoshenko reg_val |= (ETHER_ALIGN << 9) & SMSC_HW_CFG_RXDOFF;
1413b011f8c4SOleksandr Tymoshenko
14149599d814SGordon Bergling /* The following settings are used for 'turbo mode', a.k.a multiple frames
1415b011f8c4SOleksandr Tymoshenko * per Rx transaction (again info taken form Linux driver).
1416b011f8c4SOleksandr Tymoshenko */
14175a3426f4SHans Petter Selasky if (smsc_rx_packet_batching)
1418b011f8c4SOleksandr Tymoshenko reg_val |= (SMSC_HW_CFG_MEF | SMSC_HW_CFG_BCE);
1419b011f8c4SOleksandr Tymoshenko
1420b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_HW_CFG, reg_val);
1421b011f8c4SOleksandr Tymoshenko
1422b011f8c4SOleksandr Tymoshenko /* Clear the status register ? */
1423b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_INTR_STATUS, 0xffffffff);
1424b011f8c4SOleksandr Tymoshenko
1425b011f8c4SOleksandr Tymoshenko /* Read and display the revision register */
1426b011f8c4SOleksandr Tymoshenko if ((err = smsc_read_reg(sc, SMSC_ID_REV, &sc->sc_rev_id)) < 0) {
1427b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "failed to read ID_REV (err = %d)\n", err);
1428b011f8c4SOleksandr Tymoshenko goto init_failed;
1429b011f8c4SOleksandr Tymoshenko }
1430b011f8c4SOleksandr Tymoshenko
1431b011f8c4SOleksandr Tymoshenko device_printf(sc->sc_ue.ue_dev, "chip 0x%04lx, rev. %04lx\n",
1432b011f8c4SOleksandr Tymoshenko (sc->sc_rev_id & SMSC_ID_REV_CHIP_ID_MASK) >> 16,
1433b011f8c4SOleksandr Tymoshenko (sc->sc_rev_id & SMSC_ID_REV_CHIP_REV_MASK));
1434b011f8c4SOleksandr Tymoshenko
1435b011f8c4SOleksandr Tymoshenko /* GPIO/LED setup */
1436b011f8c4SOleksandr Tymoshenko reg_val = SMSC_LED_GPIO_CFG_SPD_LED | SMSC_LED_GPIO_CFG_LNK_LED |
1437b011f8c4SOleksandr Tymoshenko SMSC_LED_GPIO_CFG_FDX_LED;
1438b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_LED_GPIO_CFG, reg_val);
1439b011f8c4SOleksandr Tymoshenko
1440b011f8c4SOleksandr Tymoshenko /*
1441b011f8c4SOleksandr Tymoshenko * Initialise the TX interface
1442b011f8c4SOleksandr Tymoshenko */
1443b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_FLOW, 0);
1444b011f8c4SOleksandr Tymoshenko
1445b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_AFC_CFG, AFC_CFG_DEFAULT);
1446b011f8c4SOleksandr Tymoshenko
1447b011f8c4SOleksandr Tymoshenko /* Read the current MAC configuration */
1448b011f8c4SOleksandr Tymoshenko if ((err = smsc_read_reg(sc, SMSC_MAC_CSR, &sc->sc_mac_csr)) < 0) {
1449b011f8c4SOleksandr Tymoshenko smsc_warn_printf(sc, "failed to read MAC_CSR (err=%d)\n", err);
1450b011f8c4SOleksandr Tymoshenko goto init_failed;
1451b011f8c4SOleksandr Tymoshenko }
1452b011f8c4SOleksandr Tymoshenko
1453b011f8c4SOleksandr Tymoshenko /* Vlan */
1454b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_VLAN1, (uint32_t)ETHERTYPE_VLAN);
1455b011f8c4SOleksandr Tymoshenko
1456b011f8c4SOleksandr Tymoshenko /*
1457b011f8c4SOleksandr Tymoshenko * Initialise the PHY
1458b011f8c4SOleksandr Tymoshenko */
1459b011f8c4SOleksandr Tymoshenko if ((err = smsc_phy_init(sc)) != 0)
1460b011f8c4SOleksandr Tymoshenko goto init_failed;
1461b011f8c4SOleksandr Tymoshenko
1462b011f8c4SOleksandr Tymoshenko /*
1463b011f8c4SOleksandr Tymoshenko * Start TX
1464b011f8c4SOleksandr Tymoshenko */
1465b011f8c4SOleksandr Tymoshenko sc->sc_mac_csr |= SMSC_MAC_CSR_TXEN;
1466b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
1467b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_TX_CFG, SMSC_TX_CFG_ON);
1468b011f8c4SOleksandr Tymoshenko
1469b011f8c4SOleksandr Tymoshenko /*
1470b011f8c4SOleksandr Tymoshenko * Start RX
1471b011f8c4SOleksandr Tymoshenko */
1472b011f8c4SOleksandr Tymoshenko sc->sc_mac_csr |= SMSC_MAC_CSR_RXEN;
1473b011f8c4SOleksandr Tymoshenko smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
1474b011f8c4SOleksandr Tymoshenko
1475b011f8c4SOleksandr Tymoshenko if (!locked)
1476b011f8c4SOleksandr Tymoshenko SMSC_UNLOCK(sc);
1477b011f8c4SOleksandr Tymoshenko
1478b011f8c4SOleksandr Tymoshenko return (0);
1479b011f8c4SOleksandr Tymoshenko
1480b011f8c4SOleksandr Tymoshenko init_failed:
1481b011f8c4SOleksandr Tymoshenko if (!locked)
1482b011f8c4SOleksandr Tymoshenko SMSC_UNLOCK(sc);
1483b011f8c4SOleksandr Tymoshenko
1484b011f8c4SOleksandr Tymoshenko smsc_err_printf(sc, "smsc_chip_init failed (err=%d)\n", err);
1485b011f8c4SOleksandr Tymoshenko return (err);
1486b011f8c4SOleksandr Tymoshenko }
1487b011f8c4SOleksandr Tymoshenko
1488b011f8c4SOleksandr Tymoshenko /**
1489b011f8c4SOleksandr Tymoshenko * smsc_ioctl - ioctl function for the device
1490b011f8c4SOleksandr Tymoshenko * @ifp: interface pointer
1491b011f8c4SOleksandr Tymoshenko * @cmd: the ioctl command
1492b011f8c4SOleksandr Tymoshenko * @data: data passed in the ioctl call, typically a pointer to struct ifreq.
1493b011f8c4SOleksandr Tymoshenko *
1494b011f8c4SOleksandr Tymoshenko * The ioctl routine is overridden to detect change requests for the H/W
1495b011f8c4SOleksandr Tymoshenko * checksum capabilities.
1496b011f8c4SOleksandr Tymoshenko *
1497b011f8c4SOleksandr Tymoshenko * RETURNS:
1498b011f8c4SOleksandr Tymoshenko * 0 on success and an error code on failure.
1499b011f8c4SOleksandr Tymoshenko */
1500b011f8c4SOleksandr Tymoshenko static int
smsc_ioctl(if_t ifp,u_long cmd,caddr_t data)1501935b194dSJustin Hibbits smsc_ioctl(if_t ifp, u_long cmd, caddr_t data)
1502b011f8c4SOleksandr Tymoshenko {
1503935b194dSJustin Hibbits struct usb_ether *ue = if_getsoftc(ifp);
1504b011f8c4SOleksandr Tymoshenko struct smsc_softc *sc;
1505b011f8c4SOleksandr Tymoshenko struct ifreq *ifr;
1506b011f8c4SOleksandr Tymoshenko int rc;
1507b011f8c4SOleksandr Tymoshenko int mask;
1508b011f8c4SOleksandr Tymoshenko int reinit;
1509b011f8c4SOleksandr Tymoshenko
1510b011f8c4SOleksandr Tymoshenko if (cmd == SIOCSIFCAP) {
1511b011f8c4SOleksandr Tymoshenko sc = uether_getsc(ue);
1512b011f8c4SOleksandr Tymoshenko ifr = (struct ifreq *)data;
1513b011f8c4SOleksandr Tymoshenko
1514b011f8c4SOleksandr Tymoshenko SMSC_LOCK(sc);
1515b011f8c4SOleksandr Tymoshenko
1516b011f8c4SOleksandr Tymoshenko rc = 0;
1517b011f8c4SOleksandr Tymoshenko reinit = 0;
1518b011f8c4SOleksandr Tymoshenko
1519935b194dSJustin Hibbits mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
1520b011f8c4SOleksandr Tymoshenko
1521b011f8c4SOleksandr Tymoshenko /* Modify the RX CSUM enable bits */
1522b011f8c4SOleksandr Tymoshenko if ((mask & IFCAP_RXCSUM) != 0 &&
1523935b194dSJustin Hibbits (if_getcapabilities(ifp) & IFCAP_RXCSUM) != 0) {
1524935b194dSJustin Hibbits if_togglecapenable(ifp, IFCAP_RXCSUM);
1525b011f8c4SOleksandr Tymoshenko
1526935b194dSJustin Hibbits if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
1527935b194dSJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
1528b011f8c4SOleksandr Tymoshenko reinit = 1;
1529b011f8c4SOleksandr Tymoshenko }
1530b011f8c4SOleksandr Tymoshenko }
1531b011f8c4SOleksandr Tymoshenko
1532b011f8c4SOleksandr Tymoshenko SMSC_UNLOCK(sc);
1533b011f8c4SOleksandr Tymoshenko if (reinit)
1534b011f8c4SOleksandr Tymoshenko uether_init(ue);
1535b011f8c4SOleksandr Tymoshenko
1536b011f8c4SOleksandr Tymoshenko } else {
1537b011f8c4SOleksandr Tymoshenko rc = uether_ioctl(ifp, cmd, data);
1538b011f8c4SOleksandr Tymoshenko }
1539b011f8c4SOleksandr Tymoshenko
1540b011f8c4SOleksandr Tymoshenko return (rc);
1541b011f8c4SOleksandr Tymoshenko }
1542b011f8c4SOleksandr Tymoshenko
15433878bbf1SRonald Klop #ifdef FDT
15443878bbf1SRonald Klop static bool
smsc_get_smsc95xx_macaddr(char * bootargs,size_t len,struct usb_ether * ue)15453878bbf1SRonald Klop smsc_get_smsc95xx_macaddr(char* bootargs, size_t len, struct usb_ether *ue)
15463878bbf1SRonald Klop {
15473878bbf1SRonald Klop int values[6];
15483878bbf1SRonald Klop int i;
15493878bbf1SRonald Klop char* p;
15503878bbf1SRonald Klop
15513878bbf1SRonald Klop p = strnstr(bootargs, BOOTARGS_SMSC95XX, len);
15523878bbf1SRonald Klop if (p == NULL)
15533878bbf1SRonald Klop return (false);
15543878bbf1SRonald Klop
15553878bbf1SRonald Klop if (sscanf(p, BOOTARGS_SMSC95XX "=%x:%x:%x:%x:%x:%x%*c",
15563878bbf1SRonald Klop &values[0], &values[1], &values[2],
15573878bbf1SRonald Klop &values[3], &values[4], &values[5]) != 6) {
15583878bbf1SRonald Klop smsc_warn_printf((struct smsc_softc *)ue->ue_sc,
15593878bbf1SRonald Klop "invalid mac from bootargs '%s'.\n", p);
15603878bbf1SRonald Klop return (false);
15613878bbf1SRonald Klop }
15623878bbf1SRonald Klop
15633878bbf1SRonald Klop for (i = 0; i < ETHER_ADDR_LEN; ++i)
15643878bbf1SRonald Klop ue->ue_eaddr[i] = values[i];
15653878bbf1SRonald Klop
15663878bbf1SRonald Klop smsc_dbg_printf((struct smsc_softc *)ue->ue_sc,
15673878bbf1SRonald Klop "bootargs mac=%6D.\n", ue->ue_eaddr, ":");
15683878bbf1SRonald Klop return (true);
15693878bbf1SRonald Klop }
15703878bbf1SRonald Klop
15713878bbf1SRonald Klop /**
15723878bbf1SRonald Klop * Raspberry Pi is known to pass smsc95xx.macaddr=XX:XX:XX:XX:XX:XX via
15733878bbf1SRonald Klop * bootargs.
15743878bbf1SRonald Klop */
15753878bbf1SRonald Klop static bool
smsc_bootargs_get_mac_addr(device_t dev,struct usb_ether * ue)15763878bbf1SRonald Klop smsc_bootargs_get_mac_addr(device_t dev, struct usb_ether *ue)
15773878bbf1SRonald Klop {
15783878bbf1SRonald Klop char *bootargs;
15793878bbf1SRonald Klop ssize_t len;
15803878bbf1SRonald Klop phandle_t node;
15813878bbf1SRonald Klop
15823878bbf1SRonald Klop /* only use bootargs for the first device
15833878bbf1SRonald Klop * to prevent duplicate mac addresses */
15843878bbf1SRonald Klop if (device_get_unit(dev) != 0)
15853878bbf1SRonald Klop return (false);
15863878bbf1SRonald Klop node = OF_finddevice("/chosen");
15873878bbf1SRonald Klop if (node == -1)
15883878bbf1SRonald Klop return (false);
15893878bbf1SRonald Klop if (OF_hasprop(node, "bootargs") == 0) {
15903878bbf1SRonald Klop smsc_dbg_printf((struct smsc_softc *)ue->ue_sc,
15913878bbf1SRonald Klop "bootargs not found");
15923878bbf1SRonald Klop return (false);
15933878bbf1SRonald Klop }
15943878bbf1SRonald Klop len = OF_getprop_alloc(node, "bootargs", (void **)&bootargs);
15953878bbf1SRonald Klop if (len == -1 || bootargs == NULL) {
15963878bbf1SRonald Klop smsc_warn_printf((struct smsc_softc *)ue->ue_sc,
15978a0ee306SRonald Klop "failed alloc for bootargs (%zd)", len);
15983878bbf1SRonald Klop return (false);
15993878bbf1SRonald Klop }
16003878bbf1SRonald Klop smsc_dbg_printf((struct smsc_softc *)ue->ue_sc, "bootargs: %s.\n",
16013878bbf1SRonald Klop bootargs);
16023878bbf1SRonald Klop if (!smsc_get_smsc95xx_macaddr(bootargs, len, ue)) {
16033878bbf1SRonald Klop OF_prop_free(bootargs);
16043878bbf1SRonald Klop return (false);
16053878bbf1SRonald Klop }
16063878bbf1SRonald Klop OF_prop_free(bootargs);
16073878bbf1SRonald Klop device_printf(dev, "MAC address found in bootargs %6D.\n",
16083878bbf1SRonald Klop ue->ue_eaddr, ":");
16093878bbf1SRonald Klop return (true);
16103878bbf1SRonald Klop }
16113878bbf1SRonald Klop #endif
16123878bbf1SRonald Klop
1613b011f8c4SOleksandr Tymoshenko /**
1614b011f8c4SOleksandr Tymoshenko * smsc_attach_post - Called after the driver attached to the USB interface
1615b011f8c4SOleksandr Tymoshenko * @ue: the USB ethernet device
1616b011f8c4SOleksandr Tymoshenko *
1617b011f8c4SOleksandr Tymoshenko * This is where the chip is intialised for the first time. This is different
161894466c43SGordon Bergling * from the smsc_init() function in that that one is designed to setup the
1619b011f8c4SOleksandr Tymoshenko * H/W to match the UE settings and can be called after a reset.
1620b011f8c4SOleksandr Tymoshenko *
1621b011f8c4SOleksandr Tymoshenko *
1622b011f8c4SOleksandr Tymoshenko */
1623b011f8c4SOleksandr Tymoshenko static void
smsc_attach_post(struct usb_ether * ue)1624b011f8c4SOleksandr Tymoshenko smsc_attach_post(struct usb_ether *ue)
1625b011f8c4SOleksandr Tymoshenko {
1626b011f8c4SOleksandr Tymoshenko struct smsc_softc *sc = uether_getsc(ue);
16273878bbf1SRonald Klop struct ether_addr eaddr;
1628b011f8c4SOleksandr Tymoshenko uint32_t mac_h, mac_l;
1629b011f8c4SOleksandr Tymoshenko int err;
16303878bbf1SRonald Klop int i;
1631b011f8c4SOleksandr Tymoshenko
1632b011f8c4SOleksandr Tymoshenko smsc_dbg_printf(sc, "smsc_attach_post\n");
1633b011f8c4SOleksandr Tymoshenko
1634b011f8c4SOleksandr Tymoshenko /* Setup some of the basics */
1635b011f8c4SOleksandr Tymoshenko sc->sc_phyno = 1;
1636b011f8c4SOleksandr Tymoshenko
1637b011f8c4SOleksandr Tymoshenko /* Attempt to get the mac address, if an EEPROM is not attached this
1638b011f8c4SOleksandr Tymoshenko * will just return FF:FF:FF:FF:FF:FF, so in such cases we invent a MAC
1639b011f8c4SOleksandr Tymoshenko * address based on urandom.
1640b011f8c4SOleksandr Tymoshenko */
1641b011f8c4SOleksandr Tymoshenko memset(sc->sc_ue.ue_eaddr, 0xff, ETHER_ADDR_LEN);
1642b011f8c4SOleksandr Tymoshenko
1643b011f8c4SOleksandr Tymoshenko /* Check if there is already a MAC address in the register */
1644b011f8c4SOleksandr Tymoshenko if ((smsc_read_reg(sc, SMSC_MAC_ADDRL, &mac_l) == 0) &&
1645b011f8c4SOleksandr Tymoshenko (smsc_read_reg(sc, SMSC_MAC_ADDRH, &mac_h) == 0)) {
1646b011f8c4SOleksandr Tymoshenko sc->sc_ue.ue_eaddr[5] = (uint8_t)((mac_h >> 8) & 0xff);
1647b011f8c4SOleksandr Tymoshenko sc->sc_ue.ue_eaddr[4] = (uint8_t)((mac_h) & 0xff);
1648b011f8c4SOleksandr Tymoshenko sc->sc_ue.ue_eaddr[3] = (uint8_t)((mac_l >> 24) & 0xff);
1649b011f8c4SOleksandr Tymoshenko sc->sc_ue.ue_eaddr[2] = (uint8_t)((mac_l >> 16) & 0xff);
1650b011f8c4SOleksandr Tymoshenko sc->sc_ue.ue_eaddr[1] = (uint8_t)((mac_l >> 8) & 0xff);
1651b011f8c4SOleksandr Tymoshenko sc->sc_ue.ue_eaddr[0] = (uint8_t)((mac_l) & 0xff);
1652b011f8c4SOleksandr Tymoshenko }
1653b011f8c4SOleksandr Tymoshenko
1654b011f8c4SOleksandr Tymoshenko /* MAC address is not set so try to read from EEPROM, if that fails generate
1655b011f8c4SOleksandr Tymoshenko * a random MAC address.
1656b011f8c4SOleksandr Tymoshenko */
1657b011f8c4SOleksandr Tymoshenko if (!ETHER_IS_VALID(sc->sc_ue.ue_eaddr)) {
1658b011f8c4SOleksandr Tymoshenko err = smsc_eeprom_read(sc, 0x01, sc->sc_ue.ue_eaddr, ETHER_ADDR_LEN);
165982b036b9SOleksandr Tymoshenko #ifdef FDT
166082b036b9SOleksandr Tymoshenko if ((err != 0) || (!ETHER_IS_VALID(sc->sc_ue.ue_eaddr)))
1661bac5ec96SIan Lepore err = usb_fdt_get_mac_addr(sc->sc_ue.ue_dev, &sc->sc_ue);
16623878bbf1SRonald Klop if ((err != 0) || (!ETHER_IS_VALID(sc->sc_ue.ue_eaddr)))
16633878bbf1SRonald Klop err = smsc_bootargs_get_mac_addr(sc->sc_ue.ue_dev,
16643878bbf1SRonald Klop &sc->sc_ue) ? (0) : (1);
166582b036b9SOleksandr Tymoshenko #endif
1666b011f8c4SOleksandr Tymoshenko if ((err != 0) || (!ETHER_IS_VALID(sc->sc_ue.ue_eaddr))) {
16673878bbf1SRonald Klop smsc_dbg_printf(sc, "No MAC address found."
16683878bbf1SRonald Klop " Using ether_gen_addr().\n");
16693878bbf1SRonald Klop ether_gen_addr_byname(device_get_nameunit(ue->ue_dev),
16703878bbf1SRonald Klop &eaddr);
16713878bbf1SRonald Klop for (i = 0; i < ETHER_ADDR_LEN; i++)
16723878bbf1SRonald Klop sc->sc_ue.ue_eaddr[i] = eaddr.octet[i];
1673b011f8c4SOleksandr Tymoshenko }
1674b011f8c4SOleksandr Tymoshenko }
1675b011f8c4SOleksandr Tymoshenko
1676b011f8c4SOleksandr Tymoshenko /* Initialise the chip for the first time */
1677b011f8c4SOleksandr Tymoshenko smsc_chip_init(sc);
1678b011f8c4SOleksandr Tymoshenko }
1679b011f8c4SOleksandr Tymoshenko
1680b011f8c4SOleksandr Tymoshenko /**
1681b011f8c4SOleksandr Tymoshenko * smsc_attach_post_sub - Called after the driver attached to the USB interface
1682b011f8c4SOleksandr Tymoshenko * @ue: the USB ethernet device
1683b011f8c4SOleksandr Tymoshenko *
1684b011f8c4SOleksandr Tymoshenko * Most of this is boilerplate code and copied from the base USB ethernet
1685d0ddb5aaSGordon Bergling * driver. It has been overridden so that we can indicate to the system that
1686b011f8c4SOleksandr Tymoshenko * the chip supports H/W checksumming.
1687b011f8c4SOleksandr Tymoshenko *
1688b011f8c4SOleksandr Tymoshenko * RETURNS:
1689b011f8c4SOleksandr Tymoshenko * Returns 0 on success or a negative error code.
1690b011f8c4SOleksandr Tymoshenko */
1691b011f8c4SOleksandr Tymoshenko static int
smsc_attach_post_sub(struct usb_ether * ue)1692b011f8c4SOleksandr Tymoshenko smsc_attach_post_sub(struct usb_ether *ue)
1693b011f8c4SOleksandr Tymoshenko {
1694b011f8c4SOleksandr Tymoshenko struct smsc_softc *sc;
1695935b194dSJustin Hibbits if_t ifp;
1696b011f8c4SOleksandr Tymoshenko int error;
1697b011f8c4SOleksandr Tymoshenko
1698b011f8c4SOleksandr Tymoshenko sc = uether_getsc(ue);
1699b011f8c4SOleksandr Tymoshenko ifp = ue->ue_ifp;
1700935b194dSJustin Hibbits if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
1701935b194dSJustin Hibbits if_setstartfn(ifp, uether_start);
1702935b194dSJustin Hibbits if_setioctlfn(ifp, smsc_ioctl);
1703935b194dSJustin Hibbits if_setinitfn(ifp, uether_init);
1704935b194dSJustin Hibbits if_setsendqlen(ifp, ifqmaxlen);
1705935b194dSJustin Hibbits if_setsendqready(ifp);
1706b011f8c4SOleksandr Tymoshenko
1707b011f8c4SOleksandr Tymoshenko /* The chip supports TCP/UDP checksum offloading on TX and RX paths, however
1708b011f8c4SOleksandr Tymoshenko * currently only RX checksum is supported in the driver (see top of file).
1709b011f8c4SOleksandr Tymoshenko */
1710935b194dSJustin Hibbits if_setcapabilitiesbit(ifp, IFCAP_RXCSUM | IFCAP_VLAN_MTU, 0);
1711935b194dSJustin Hibbits if_sethwassist(ifp, 0);
1712b011f8c4SOleksandr Tymoshenko
1713b011f8c4SOleksandr Tymoshenko /* TX checksuming is disabled (for now?)
1714935b194dSJustin Hibbits if_setcapabilitiesbit(ifp, IFCAP_TXCSUM, 0);
1715935b194dSJustin Hibbits if_setcapenablebit(ifp, IFCAP_TXCSUM, 0);
1716935b194dSJustin Hibbits if_sethwassist(ifp, CSUM_TCP | CSUM_UDP);
1717b011f8c4SOleksandr Tymoshenko */
1718b011f8c4SOleksandr Tymoshenko
1719935b194dSJustin Hibbits if_setcapenable(ifp, if_getcapabilities(ifp));
1720b011f8c4SOleksandr Tymoshenko
1721c6df6f53SWarner Losh bus_topo_lock();
1722b011f8c4SOleksandr Tymoshenko error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp,
1723b011f8c4SOleksandr Tymoshenko uether_ifmedia_upd, ue->ue_methods->ue_mii_sts,
1724b011f8c4SOleksandr Tymoshenko BMSR_DEFCAPMASK, sc->sc_phyno, MII_OFFSET_ANY, 0);
1725c6df6f53SWarner Losh bus_topo_unlock();
1726b011f8c4SOleksandr Tymoshenko
1727b011f8c4SOleksandr Tymoshenko return (error);
1728b011f8c4SOleksandr Tymoshenko }
1729b011f8c4SOleksandr Tymoshenko
1730b011f8c4SOleksandr Tymoshenko /**
1731b011f8c4SOleksandr Tymoshenko * smsc_probe - Probe the interface.
1732b011f8c4SOleksandr Tymoshenko * @dev: smsc device handle
1733b011f8c4SOleksandr Tymoshenko *
1734b011f8c4SOleksandr Tymoshenko * Checks if the device is a match for this driver.
1735b011f8c4SOleksandr Tymoshenko *
1736b011f8c4SOleksandr Tymoshenko * RETURNS:
1737b011f8c4SOleksandr Tymoshenko * Returns 0 on success or an error code on failure.
1738b011f8c4SOleksandr Tymoshenko */
1739b011f8c4SOleksandr Tymoshenko static int
smsc_probe(device_t dev)1740b011f8c4SOleksandr Tymoshenko smsc_probe(device_t dev)
1741b011f8c4SOleksandr Tymoshenko {
1742b011f8c4SOleksandr Tymoshenko struct usb_attach_arg *uaa = device_get_ivars(dev);
1743b011f8c4SOleksandr Tymoshenko
1744b011f8c4SOleksandr Tymoshenko if (uaa->usb_mode != USB_MODE_HOST)
1745b011f8c4SOleksandr Tymoshenko return (ENXIO);
1746b011f8c4SOleksandr Tymoshenko if (uaa->info.bConfigIndex != SMSC_CONFIG_INDEX)
1747b011f8c4SOleksandr Tymoshenko return (ENXIO);
1748b011f8c4SOleksandr Tymoshenko if (uaa->info.bIfaceIndex != SMSC_IFACE_IDX)
1749b011f8c4SOleksandr Tymoshenko return (ENXIO);
1750b011f8c4SOleksandr Tymoshenko
1751b011f8c4SOleksandr Tymoshenko return (usbd_lookup_id_by_uaa(smsc_devs, sizeof(smsc_devs), uaa));
1752b011f8c4SOleksandr Tymoshenko }
1753b011f8c4SOleksandr Tymoshenko
1754b011f8c4SOleksandr Tymoshenko /**
1755b011f8c4SOleksandr Tymoshenko * smsc_attach - Attach the interface.
1756b011f8c4SOleksandr Tymoshenko * @dev: smsc device handle
1757b011f8c4SOleksandr Tymoshenko *
1758b011f8c4SOleksandr Tymoshenko * Allocate softc structures, do ifmedia setup and ethernet/BPF attach.
1759b011f8c4SOleksandr Tymoshenko *
1760b011f8c4SOleksandr Tymoshenko * RETURNS:
1761b011f8c4SOleksandr Tymoshenko * Returns 0 on success or a negative error code.
1762b011f8c4SOleksandr Tymoshenko */
1763b011f8c4SOleksandr Tymoshenko static int
smsc_attach(device_t dev)1764b011f8c4SOleksandr Tymoshenko smsc_attach(device_t dev)
1765b011f8c4SOleksandr Tymoshenko {
1766b011f8c4SOleksandr Tymoshenko struct usb_attach_arg *uaa = device_get_ivars(dev);
1767b011f8c4SOleksandr Tymoshenko struct smsc_softc *sc = device_get_softc(dev);
1768b011f8c4SOleksandr Tymoshenko struct usb_ether *ue = &sc->sc_ue;
1769b011f8c4SOleksandr Tymoshenko uint8_t iface_index;
1770b011f8c4SOleksandr Tymoshenko int err;
1771b011f8c4SOleksandr Tymoshenko
1772b011f8c4SOleksandr Tymoshenko sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
1773b011f8c4SOleksandr Tymoshenko
1774b011f8c4SOleksandr Tymoshenko device_set_usb_desc(dev);
1775b011f8c4SOleksandr Tymoshenko
1776b011f8c4SOleksandr Tymoshenko mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
1777b011f8c4SOleksandr Tymoshenko
1778b011f8c4SOleksandr Tymoshenko /* Setup the endpoints for the SMSC LAN95xx device(s) */
1779b011f8c4SOleksandr Tymoshenko iface_index = SMSC_IFACE_IDX;
1780b011f8c4SOleksandr Tymoshenko err = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
1781b011f8c4SOleksandr Tymoshenko smsc_config, SMSC_N_TRANSFER, sc, &sc->sc_mtx);
1782b011f8c4SOleksandr Tymoshenko if (err) {
1783b011f8c4SOleksandr Tymoshenko device_printf(dev, "error: allocating USB transfers failed\n");
1784b011f8c4SOleksandr Tymoshenko goto detach;
1785b011f8c4SOleksandr Tymoshenko }
1786b011f8c4SOleksandr Tymoshenko
1787b011f8c4SOleksandr Tymoshenko ue->ue_sc = sc;
1788b011f8c4SOleksandr Tymoshenko ue->ue_dev = dev;
1789b011f8c4SOleksandr Tymoshenko ue->ue_udev = uaa->device;
1790b011f8c4SOleksandr Tymoshenko ue->ue_mtx = &sc->sc_mtx;
1791b011f8c4SOleksandr Tymoshenko ue->ue_methods = &smsc_ue_methods;
1792b011f8c4SOleksandr Tymoshenko
1793b011f8c4SOleksandr Tymoshenko err = uether_ifattach(ue);
1794b011f8c4SOleksandr Tymoshenko if (err) {
1795b011f8c4SOleksandr Tymoshenko device_printf(dev, "error: could not attach interface\n");
1796b011f8c4SOleksandr Tymoshenko goto detach;
1797b011f8c4SOleksandr Tymoshenko }
1798b011f8c4SOleksandr Tymoshenko return (0); /* success */
1799b011f8c4SOleksandr Tymoshenko
1800b011f8c4SOleksandr Tymoshenko detach:
1801b011f8c4SOleksandr Tymoshenko smsc_detach(dev);
1802b011f8c4SOleksandr Tymoshenko return (ENXIO); /* failure */
1803b011f8c4SOleksandr Tymoshenko }
1804b011f8c4SOleksandr Tymoshenko
1805b011f8c4SOleksandr Tymoshenko /**
1806b011f8c4SOleksandr Tymoshenko * smsc_detach - Detach the interface.
1807b011f8c4SOleksandr Tymoshenko * @dev: smsc device handle
1808b011f8c4SOleksandr Tymoshenko *
1809b011f8c4SOleksandr Tymoshenko * RETURNS:
1810b011f8c4SOleksandr Tymoshenko * Returns 0.
1811b011f8c4SOleksandr Tymoshenko */
1812b011f8c4SOleksandr Tymoshenko static int
smsc_detach(device_t dev)1813b011f8c4SOleksandr Tymoshenko smsc_detach(device_t dev)
1814b011f8c4SOleksandr Tymoshenko {
1815b011f8c4SOleksandr Tymoshenko struct smsc_softc *sc = device_get_softc(dev);
1816b011f8c4SOleksandr Tymoshenko struct usb_ether *ue = &sc->sc_ue;
1817b011f8c4SOleksandr Tymoshenko
1818b011f8c4SOleksandr Tymoshenko usbd_transfer_unsetup(sc->sc_xfer, SMSC_N_TRANSFER);
1819b011f8c4SOleksandr Tymoshenko uether_ifdetach(ue);
1820b011f8c4SOleksandr Tymoshenko mtx_destroy(&sc->sc_mtx);
1821b011f8c4SOleksandr Tymoshenko
1822b011f8c4SOleksandr Tymoshenko return (0);
1823b011f8c4SOleksandr Tymoshenko }
1824b011f8c4SOleksandr Tymoshenko
1825b011f8c4SOleksandr Tymoshenko static device_method_t smsc_methods[] = {
1826b011f8c4SOleksandr Tymoshenko /* Device interface */
1827b011f8c4SOleksandr Tymoshenko DEVMETHOD(device_probe, smsc_probe),
1828b011f8c4SOleksandr Tymoshenko DEVMETHOD(device_attach, smsc_attach),
1829b011f8c4SOleksandr Tymoshenko DEVMETHOD(device_detach, smsc_detach),
1830b011f8c4SOleksandr Tymoshenko
1831b011f8c4SOleksandr Tymoshenko /* bus interface */
1832b011f8c4SOleksandr Tymoshenko DEVMETHOD(bus_print_child, bus_generic_print_child),
1833b011f8c4SOleksandr Tymoshenko DEVMETHOD(bus_driver_added, bus_generic_driver_added),
1834b011f8c4SOleksandr Tymoshenko
1835b011f8c4SOleksandr Tymoshenko /* MII interface */
1836b011f8c4SOleksandr Tymoshenko DEVMETHOD(miibus_readreg, smsc_miibus_readreg),
1837b011f8c4SOleksandr Tymoshenko DEVMETHOD(miibus_writereg, smsc_miibus_writereg),
1838b011f8c4SOleksandr Tymoshenko DEVMETHOD(miibus_statchg, smsc_miibus_statchg),
1839b011f8c4SOleksandr Tymoshenko
184061bfd867SSofian Brabez DEVMETHOD_END
1841b011f8c4SOleksandr Tymoshenko };
1842b011f8c4SOleksandr Tymoshenko
1843b011f8c4SOleksandr Tymoshenko static driver_t smsc_driver = {
1844b011f8c4SOleksandr Tymoshenko .name = "smsc",
1845b011f8c4SOleksandr Tymoshenko .methods = smsc_methods,
1846b011f8c4SOleksandr Tymoshenko .size = sizeof(struct smsc_softc),
1847b011f8c4SOleksandr Tymoshenko };
1848b011f8c4SOleksandr Tymoshenko
1849bc9372d7SJohn Baldwin DRIVER_MODULE(smsc, uhub, smsc_driver, NULL, NULL);
18503e38757dSJohn Baldwin DRIVER_MODULE(miibus, smsc, miibus_driver, 0, 0);
1851b011f8c4SOleksandr Tymoshenko MODULE_DEPEND(smsc, uether, 1, 1, 1);
1852b011f8c4SOleksandr Tymoshenko MODULE_DEPEND(smsc, usb, 1, 1, 1);
1853b011f8c4SOleksandr Tymoshenko MODULE_DEPEND(smsc, ether, 1, 1, 1);
1854b011f8c4SOleksandr Tymoshenko MODULE_DEPEND(smsc, miibus, 1, 1, 1);
1855b011f8c4SOleksandr Tymoshenko MODULE_VERSION(smsc, 1);
1856f809f280SWarner Losh USB_PNP_HOST_INFO(smsc_devs);
1857