freebsd-dev/sys/dev/hme/if_hme.c

1704 lines
46 KiB
C
Raw Normal View History

/*-
* Copyright (c) 1999 The NetBSD Foundation, Inc.
* Copyright (c) 2001-2003 Thomas Moestl <tmm@FreeBSD.org>.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Paul Kranenburg.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
* from: NetBSD: hme.c,v 1.35 2003/02/27 14:58:22 pk Exp
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* HME Ethernet module driver.
*
* The HME is e.g. part of the PCIO PCI multi function device.
* It supports TX gathering and TX and RX checksum offloading.
* RX buffers must be aligned at a programmable offset modulo 16. We choose 2
* for this offset: mbuf clusters are usually on about 2^11 boundaries, 2 bytes
* are skipped to make sure the header after the ethernet header is aligned on a
* natural boundary, so this ensures minimal wastage in the most common case.
*
* Also, apparently, the buffers must extend to a DMA burst boundary beyond the
* maximum packet size (this is not verified). Buffers starting on odd
* boundaries must be mapped so that the burst can start on a natural boundary.
*
* STP2002QFP-UG says that Ethernet hardware supports TCP checksum offloading.
* In reality, we can do the same technique for UDP datagram too. However,
* the hardware doesn't compensate the checksum for UDP datagram which can yield
* to 0x0. As a safe guard, UDP checksum offload is disabled by default. It
* can be reactivated by setting special link option link0 with ifconfig(8).
*/
#define HME_CSUM_FEATURES (CSUM_TCP)
#define HMEDEBUG
#define KTR_HME KTR_CT2 /* XXX */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/endian.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/ktr.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <net/bpf.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/if_vlan_var.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include <machine/bus.h>
2003-08-23 05:51:03 +00:00
#include <dev/hme/if_hmereg.h>
#include <dev/hme/if_hmevar.h>
static void hme_start(struct ifnet *);
static void hme_start_locked(struct ifnet *);
static void hme_stop(struct hme_softc *);
static int hme_ioctl(struct ifnet *, u_long, caddr_t);
static void hme_tick(void *);
static void hme_watchdog(struct ifnet *);
static void hme_init(void *);
static void hme_init_locked(struct hme_softc *);
static int hme_add_rxbuf(struct hme_softc *, unsigned int, int);
static int hme_meminit(struct hme_softc *);
static int hme_mac_bitflip(struct hme_softc *, u_int32_t, u_int32_t,
u_int32_t, u_int32_t);
static void hme_mifinit(struct hme_softc *);
static void hme_setladrf(struct hme_softc *, int);
static int hme_mediachange(struct ifnet *);
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
static int hme_mediachange_locked(struct hme_softc *);
static void hme_mediastatus(struct ifnet *, struct ifmediareq *);
static int hme_load_txmbuf(struct hme_softc *, struct mbuf **);
static void hme_read(struct hme_softc *, int, int, u_int32_t);
static void hme_eint(struct hme_softc *, u_int);
static void hme_rint(struct hme_softc *);
static void hme_tint(struct hme_softc *);
static void hme_txcksum(struct mbuf *, u_int32_t *);
static void hme_rxcksum(struct mbuf *, u_int32_t);
static void hme_cdma_callback(void *, bus_dma_segment_t *, int, int);
devclass_t hme_devclass;
static int hme_nerr;
DRIVER_MODULE(miibus, hme, miibus_driver, miibus_devclass, 0, 0);
MODULE_DEPEND(hme, miibus, 1, 1, 1);
#define HME_SPC_READ_4(spc, sc, offs) \
bus_space_read_4((sc)->sc_ ## spc ## t, (sc)->sc_ ## spc ## h, \
(offs))
#define HME_SPC_WRITE_4(spc, sc, offs, v) \
bus_space_write_4((sc)->sc_ ## spc ## t, (sc)->sc_ ## spc ## h, \
(offs), (v))
#define HME_SEB_READ_4(sc, offs) HME_SPC_READ_4(seb, (sc), (offs))
#define HME_SEB_WRITE_4(sc, offs, v) HME_SPC_WRITE_4(seb, (sc), (offs), (v))
#define HME_ERX_READ_4(sc, offs) HME_SPC_READ_4(erx, (sc), (offs))
#define HME_ERX_WRITE_4(sc, offs, v) HME_SPC_WRITE_4(erx, (sc), (offs), (v))
#define HME_ETX_READ_4(sc, offs) HME_SPC_READ_4(etx, (sc), (offs))
#define HME_ETX_WRITE_4(sc, offs, v) HME_SPC_WRITE_4(etx, (sc), (offs), (v))
#define HME_MAC_READ_4(sc, offs) HME_SPC_READ_4(mac, (sc), (offs))
#define HME_MAC_WRITE_4(sc, offs, v) HME_SPC_WRITE_4(mac, (sc), (offs), (v))
#define HME_MIF_READ_4(sc, offs) HME_SPC_READ_4(mif, (sc), (offs))
#define HME_MIF_WRITE_4(sc, offs, v) HME_SPC_WRITE_4(mif, (sc), (offs), (v))
#define HME_MAXERR 5
#define HME_WHINE(dev, ...) do { \
if (hme_nerr++ < HME_MAXERR) \
device_printf(dev, __VA_ARGS__); \
if (hme_nerr == HME_MAXERR) { \
device_printf(dev, "too many errors; not reporting " \
"any more\n"); \
} \
} while(0)
/* Support oversized VLAN frames. */
#define HME_MAX_FRAMESIZE (ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN)
int
hme_config(struct hme_softc *sc)
{
struct ifnet *ifp;
struct mii_softc *child;
bus_size_t size;
int error, rdesc, tdesc, i;
ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
if (ifp == NULL)
return (ENOSPC);
/*
* HME common initialization.
*
* hme_softc fields that must be initialized by the front-end:
*
* the DMA bus tag:
* sc_dmatag
*
* the bus handles, tags and offsets (splitted for SBus compatability):
* sc_seb{t,h,o} (Shared Ethernet Block registers)
* sc_erx{t,h,o} (Receiver Unit registers)
* sc_etx{t,h,o} (Transmitter Unit registers)
* sc_mac{t,h,o} (MAC registers)
* sc_mif{t,h,o} (Management Interface registers)
*
* the maximum bus burst size:
* sc_burst
*
*/
callout_init_mtx(&sc->sc_tick_ch, &sc->sc_lock, 0);
/* Make sure the chip is stopped. */
HME_LOCK(sc);
hme_stop(sc);
HME_UNLOCK(sc);
/*
* Allocate DMA capable memory
* Buffer descriptors must be aligned on a 2048 byte boundary;
* take this into account when calculating the size. Note that
* the maximum number of descriptors (256) occupies 2048 bytes,
* so we allocate that much regardless of HME_N*DESC.
*/
size = 4096;
error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT,
BUS_SPACE_MAXADDR, NULL, NULL, size, HME_NTXDESC + HME_NRXDESC + 1,
BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &sc->sc_pdmatag);
if (error)
goto fail_ifnet;
error = bus_dma_tag_create(sc->sc_pdmatag, 2048, 0,
BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size,
1, BUS_SPACE_MAXSIZE_32BIT, BUS_DMA_ALLOCNOW, busdma_lock_mutex,
&sc->sc_lock, &sc->sc_cdmatag);
if (error)
goto fail_ptag;
error = bus_dma_tag_create(sc->sc_pdmatag, max(0x10, sc->sc_burst), 0,
BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES,
HME_NRXDESC, BUS_SPACE_MAXSIZE_32BIT, BUS_DMA_ALLOCNOW,
NULL, NULL, &sc->sc_rdmatag);
if (error)
goto fail_ctag;
error = bus_dma_tag_create(sc->sc_pdmatag, max(0x10, sc->sc_burst), 0,
BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES,
HME_NTXDESC, BUS_SPACE_MAXSIZE_32BIT, BUS_DMA_ALLOCNOW,
NULL, NULL, &sc->sc_tdmatag);
if (error)
goto fail_rtag;
/* Allocate control/TX DMA buffer */
error = bus_dmamem_alloc(sc->sc_cdmatag, (void **)&sc->sc_rb.rb_membase,
0, &sc->sc_cdmamap);
if (error != 0) {
device_printf(sc->sc_dev, "DMA buffer alloc error %d\n", error);
goto fail_ttag;
}
/* Load the buffer */
sc->sc_rb.rb_dmabase = 0;
if ((error = bus_dmamap_load(sc->sc_cdmatag, sc->sc_cdmamap,
sc->sc_rb.rb_membase, size, hme_cdma_callback, sc, 0)) != 0 ||
sc->sc_rb.rb_dmabase == 0) {
device_printf(sc->sc_dev, "DMA buffer map load error %d\n",
error);
goto fail_free;
}
CTR2(KTR_HME, "hme_config: dma va %p, pa %#lx", sc->sc_rb.rb_membase,
sc->sc_rb.rb_dmabase);
/*
* Prepare the RX descriptors. rdesc serves as marker for the last
* processed descriptor and may be used later on.
*/
for (rdesc = 0; rdesc < HME_NRXDESC; rdesc++) {
sc->sc_rb.rb_rxdesc[rdesc].hrx_m = NULL;
error = bus_dmamap_create(sc->sc_rdmatag, 0,
&sc->sc_rb.rb_rxdesc[rdesc].hrx_dmamap);
if (error != 0)
goto fail_rxdesc;
}
error = bus_dmamap_create(sc->sc_rdmatag, 0,
&sc->sc_rb.rb_spare_dmamap);
if (error != 0)
goto fail_rxdesc;
/* Same for the TX descs. */
for (tdesc = 0; tdesc < HME_NTXQ; tdesc++) {
sc->sc_rb.rb_txdesc[tdesc].htx_m = NULL;
error = bus_dmamap_create(sc->sc_tdmatag, 0,
&sc->sc_rb.rb_txdesc[tdesc].htx_dmamap);
if (error != 0)
goto fail_txdesc;
}
sc->sc_csum_features = HME_CSUM_FEATURES;
/* Initialize ifnet structure. */
ifp->if_softc = sc;
if_initname(ifp, device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev));
ifp->if_mtu = ETHERMTU;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifp->if_start = hme_start;
ifp->if_ioctl = hme_ioctl;
ifp->if_init = hme_init;
ifp->if_watchdog = hme_watchdog;
IFQ_SET_MAXLEN(&ifp->if_snd, HME_NTXQ);
ifp->if_snd.ifq_drv_maxlen = HME_NTXQ;
IFQ_SET_READY(&ifp->if_snd);
hme_mifinit(sc);
if ((error = mii_phy_probe(sc->sc_dev, &sc->sc_miibus, hme_mediachange,
hme_mediastatus)) != 0) {
device_printf(sc->sc_dev, "phy probe failed: %d\n", error);
goto fail_rxdesc;
}
sc->sc_mii = device_get_softc(sc->sc_miibus);
/*
* Walk along the list of attached MII devices and
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
* establish an `MII instance' to `PHY number'
* mapping. We'll use this mapping to enable the MII
* drivers of the external transceiver according to
* the currently selected media.
*/
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
sc->sc_phys[0] = sc->sc_phys[1] = -1;
LIST_FOREACH(child, &sc->sc_mii->mii_phys, mii_list) {
/*
* Note: we support just two PHYs: the built-in
* internal device and an external on the MII
* connector.
*/
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
if ((child->mii_phy != HME_PHYAD_EXTERNAL &&
child->mii_phy != HME_PHYAD_INTERNAL) ||
child->mii_inst > 1) {
device_printf(sc->sc_dev, "cannot accommodate "
"MII device %s at phy %d, instance %d\n",
device_get_name(child->mii_dev),
child->mii_phy, child->mii_inst);
continue;
}
sc->sc_phys[child->mii_inst] = child->mii_phy;
}
/* Attach the interface. */
ether_ifattach(ifp, sc->sc_enaddr);
/*
* Tell the upper layer(s) we support long frames/checksum offloads.
*/
ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_HWCSUM;
ifp->if_hwassist |= sc->sc_csum_features;
ifp->if_capenable |= IFCAP_VLAN_MTU | IFCAP_HWCSUM;
return (0);
fail_txdesc:
for (i = 0; i < tdesc; i++) {
bus_dmamap_destroy(sc->sc_tdmatag,
sc->sc_rb.rb_txdesc[i].htx_dmamap);
}
bus_dmamap_destroy(sc->sc_rdmatag, sc->sc_rb.rb_spare_dmamap);
fail_rxdesc:
for (i = 0; i < rdesc; i++) {
bus_dmamap_destroy(sc->sc_rdmatag,
sc->sc_rb.rb_rxdesc[i].hrx_dmamap);
}
bus_dmamap_unload(sc->sc_cdmatag, sc->sc_cdmamap);
fail_free:
bus_dmamem_free(sc->sc_cdmatag, sc->sc_rb.rb_membase, sc->sc_cdmamap);
fail_ttag:
bus_dma_tag_destroy(sc->sc_tdmatag);
fail_rtag:
bus_dma_tag_destroy(sc->sc_rdmatag);
fail_ctag:
bus_dma_tag_destroy(sc->sc_cdmatag);
fail_ptag:
bus_dma_tag_destroy(sc->sc_pdmatag);
fail_ifnet:
if_free(ifp);
return (error);
}
void
hme_detach(struct hme_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
int i;
HME_LOCK(sc);
hme_stop(sc);
HME_UNLOCK(sc);
callout_drain(&sc->sc_tick_ch);
ether_ifdetach(ifp);
if_free(ifp);
device_delete_child(sc->sc_dev, sc->sc_miibus);
for (i = 0; i < HME_NTXQ; i++) {
bus_dmamap_destroy(sc->sc_tdmatag,
sc->sc_rb.rb_txdesc[i].htx_dmamap);
}
bus_dmamap_destroy(sc->sc_rdmatag, sc->sc_rb.rb_spare_dmamap);
for (i = 0; i < HME_NRXDESC; i++) {
bus_dmamap_destroy(sc->sc_rdmatag,
sc->sc_rb.rb_rxdesc[i].hrx_dmamap);
}
bus_dmamap_sync(sc->sc_cdmatag, sc->sc_cdmamap, BUS_DMASYNC_POSTREAD);
bus_dmamap_sync(sc->sc_cdmatag, sc->sc_cdmamap, BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_cdmatag, sc->sc_cdmamap);
bus_dmamem_free(sc->sc_cdmatag, sc->sc_rb.rb_membase, sc->sc_cdmamap);
bus_dma_tag_destroy(sc->sc_tdmatag);
bus_dma_tag_destroy(sc->sc_rdmatag);
bus_dma_tag_destroy(sc->sc_cdmatag);
bus_dma_tag_destroy(sc->sc_pdmatag);
}
void
hme_suspend(struct hme_softc *sc)
{
HME_LOCK(sc);
hme_stop(sc);
HME_UNLOCK(sc);
}
void
hme_resume(struct hme_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
HME_LOCK(sc);
if ((ifp->if_flags & IFF_UP) != 0)
hme_init_locked(sc);
HME_UNLOCK(sc);
}
static void
hme_cdma_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
{
struct hme_softc *sc = (struct hme_softc *)xsc;
if (error != 0)
return;
KASSERT(nsegs == 1, ("hme_cdma_callback: bad dma segment count"));
sc->sc_rb.rb_dmabase = segs[0].ds_addr;
}
static void
hme_tick(void *arg)
{
struct hme_softc *sc = arg;
struct ifnet *ifp;
HME_LOCK_ASSERT(sc, MA_OWNED);
ifp = sc->sc_ifp;
/*
* Unload collision counters
*/
ifp->if_collisions +=
HME_MAC_READ_4(sc, HME_MACI_NCCNT) +
HME_MAC_READ_4(sc, HME_MACI_FCCNT) +
HME_MAC_READ_4(sc, HME_MACI_EXCNT) +
HME_MAC_READ_4(sc, HME_MACI_LTCNT);
/*
* then clear the hardware counters.
*/
HME_MAC_WRITE_4(sc, HME_MACI_NCCNT, 0);
HME_MAC_WRITE_4(sc, HME_MACI_FCCNT, 0);
HME_MAC_WRITE_4(sc, HME_MACI_EXCNT, 0);
HME_MAC_WRITE_4(sc, HME_MACI_LTCNT, 0);
mii_tick(sc->sc_mii);
callout_reset(&sc->sc_tick_ch, hz, hme_tick, sc);
}
static void
hme_stop(struct hme_softc *sc)
{
u_int32_t v;
int n;
callout_stop(&sc->sc_tick_ch);
sc->sc_ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
/* Mask all interrupts */
HME_SEB_WRITE_4(sc, HME_SEBI_IMASK, 0xffffffff);
/* Reset transmitter and receiver */
HME_SEB_WRITE_4(sc, HME_SEBI_RESET, HME_SEB_RESET_ETX |
HME_SEB_RESET_ERX);
for (n = 0; n < 20; n++) {
v = HME_SEB_READ_4(sc, HME_SEBI_RESET);
if ((v & (HME_SEB_RESET_ETX | HME_SEB_RESET_ERX)) == 0)
return;
DELAY(20);
}
device_printf(sc->sc_dev, "hme_stop: reset failed\n");
}
/*
* Discard the contents of an mbuf in the RX ring, freeing the buffer in the
* ring for subsequent use.
*/
static __inline void
hme_discard_rxbuf(struct hme_softc *sc, int ix)
{
/*
* Dropped a packet, reinitialize the descriptor and turn the
* ownership back to the hardware.
*/
HME_XD_SETFLAGS(sc->sc_pci, sc->sc_rb.rb_rxd, ix, HME_XD_OWN |
HME_XD_ENCODE_RSIZE(HME_DESC_RXLEN(sc, &sc->sc_rb.rb_rxdesc[ix])));
}
static int
hme_add_rxbuf(struct hme_softc *sc, unsigned int ri, int keepold)
{
struct hme_rxdesc *rd;
struct mbuf *m;
bus_dma_segment_t segs[1];
bus_dmamap_t map;
uintptr_t b;
int a, unmap, nsegs;
rd = &sc->sc_rb.rb_rxdesc[ri];
unmap = rd->hrx_m != NULL;
if (unmap && keepold) {
/*
* Reinitialize the descriptor flags, as they may have been
* altered by the hardware.
*/
hme_discard_rxbuf(sc, ri);
return (0);
}
if ((m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR)) == NULL)
return (ENOBUFS);
m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
b = mtod(m, uintptr_t);
/*
* Required alignment boundary. At least 16 is needed, but since
* the mapping must be done in a way that a burst can start on a
* natural boundary we might need to extend this.
*/
a = imax(HME_MINRXALIGN, sc->sc_burst);
/*
* Make sure the buffer suitably aligned. The 2 byte offset is removed
* when the mbuf is handed up. XXX: this ensures at least 16 byte
* alignment of the header adjacent to the ethernet header, which
* should be sufficient in all cases. Nevertheless, this second-guesses
* ALIGN().
*/
m_adj(m, roundup2(b, a) - b);
if (bus_dmamap_load_mbuf_sg(sc->sc_rdmatag, sc->sc_rb.rb_spare_dmamap,
m, segs, &nsegs, 0) != 0) {
m_freem(m);
return (ENOBUFS);
}
/* If nsegs is wrong then the stack is corrupt */
2005-01-16 02:39:18 +00:00
KASSERT(nsegs == 1, ("Too many segments returned!"));
if (unmap) {
bus_dmamap_sync(sc->sc_rdmatag, rd->hrx_dmamap,
BUS_DMASYNC_POSTREAD);
bus_dmamap_unload(sc->sc_rdmatag, rd->hrx_dmamap);
}
map = rd->hrx_dmamap;
rd->hrx_dmamap = sc->sc_rb.rb_spare_dmamap;
sc->sc_rb.rb_spare_dmamap = map;
bus_dmamap_sync(sc->sc_rdmatag, rd->hrx_dmamap, BUS_DMASYNC_PREREAD);
HME_XD_SETADDR(sc->sc_pci, sc->sc_rb.rb_rxd, ri, segs[0].ds_addr);
rd->hrx_m = m;
HME_XD_SETFLAGS(sc->sc_pci, sc->sc_rb.rb_rxd, ri, HME_XD_OWN |
HME_XD_ENCODE_RSIZE(HME_DESC_RXLEN(sc, rd)));
return (0);
}
static int
hme_meminit(struct hme_softc *sc)
{
struct hme_ring *hr = &sc->sc_rb;
struct hme_txdesc *td;
bus_addr_t dma;
caddr_t p;
unsigned int i;
int error;
p = hr->rb_membase;
dma = hr->rb_dmabase;
/*
* Allocate transmit descriptors
*/
hr->rb_txd = p;
hr->rb_txddma = dma;
p += HME_NTXDESC * HME_XD_SIZE;
dma += HME_NTXDESC * HME_XD_SIZE;
/* We have reserved descriptor space until the next 2048 byte boundary.*/
dma = (bus_addr_t)roundup((u_long)dma, 2048);
p = (caddr_t)roundup((u_long)p, 2048);
/*
* Allocate receive descriptors
*/
hr->rb_rxd = p;
hr->rb_rxddma = dma;
p += HME_NRXDESC * HME_XD_SIZE;
dma += HME_NRXDESC * HME_XD_SIZE;
/* Again move forward to the next 2048 byte boundary.*/
dma = (bus_addr_t)roundup((u_long)dma, 2048);
p = (caddr_t)roundup((u_long)p, 2048);
/*
* Initialize transmit buffer descriptors
*/
for (i = 0; i < HME_NTXDESC; i++) {
HME_XD_SETADDR(sc->sc_pci, hr->rb_txd, i, 0);
HME_XD_SETFLAGS(sc->sc_pci, hr->rb_txd, i, 0);
}
STAILQ_INIT(&sc->sc_rb.rb_txfreeq);
STAILQ_INIT(&sc->sc_rb.rb_txbusyq);
for (i = 0; i < HME_NTXQ; i++) {
td = &sc->sc_rb.rb_txdesc[i];
if (td->htx_m != NULL) {
bus_dmamap_sync(sc->sc_tdmatag, td->htx_dmamap,
BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_tdmatag, td->htx_dmamap);
m_freem(td->htx_m);
td->htx_m = NULL;
}
STAILQ_INSERT_TAIL(&sc->sc_rb.rb_txfreeq, td, htx_q);
}
/*
* Initialize receive buffer descriptors
*/
for (i = 0; i < HME_NRXDESC; i++) {
error = hme_add_rxbuf(sc, i, 1);
if (error != 0)
return (error);
}
bus_dmamap_sync(sc->sc_cdmatag, sc->sc_cdmamap, BUS_DMASYNC_PREREAD);
bus_dmamap_sync(sc->sc_cdmatag, sc->sc_cdmamap, BUS_DMASYNC_PREWRITE);
hr->rb_tdhead = hr->rb_tdtail = 0;
hr->rb_td_nbusy = 0;
hr->rb_rdtail = 0;
CTR2(KTR_HME, "hme_meminit: tx ring va %p, pa %#lx", hr->rb_txd,
hr->rb_txddma);
CTR2(KTR_HME, "hme_meminit: rx ring va %p, pa %#lx", hr->rb_rxd,
hr->rb_rxddma);
CTR2(KTR_HME, "rx entry 1: flags %x, address %x",
*(u_int32_t *)hr->rb_rxd, *(u_int32_t *)(hr->rb_rxd + 4));
CTR2(KTR_HME, "tx entry 1: flags %x, address %x",
*(u_int32_t *)hr->rb_txd, *(u_int32_t *)(hr->rb_txd + 4));
return (0);
}
static int
hme_mac_bitflip(struct hme_softc *sc, u_int32_t reg, u_int32_t val,
u_int32_t clr, u_int32_t set)
{
int i = 0;
val &= ~clr;
val |= set;
HME_MAC_WRITE_4(sc, reg, val);
if (clr == 0 && set == 0)
return (1); /* just write, no bits to wait for */
do {
DELAY(100);
i++;
val = HME_MAC_READ_4(sc, reg);
if (i > 40) {
/* After 3.5ms, we should have been done. */
device_printf(sc->sc_dev, "timeout while writing to "
"MAC configuration register\n");
return (0);
}
} while ((val & clr) != 0 && (val & set) != set);
return (1);
}
/*
* Initialization of interface; set up initialization block
* and transmit/receive descriptor rings.
*/
static void
hme_init(void *xsc)
{
struct hme_softc *sc = (struct hme_softc *)xsc;
HME_LOCK(sc);
hme_init_locked(sc);
HME_UNLOCK(sc);
}
static void
hme_init_locked(struct hme_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
u_int8_t *ea;
u_int32_t n, v;
HME_LOCK_ASSERT(sc, MA_OWNED);
/*
* Initialization sequence. The numbered steps below correspond
* to the sequence outlined in section 6.3.5.1 in the Ethernet
* Channel Engine manual (part of the PCIO manual).
* See also the STP2002-STQ document from Sun Microsystems.
*/
/* step 1 & 2. Reset the Ethernet Channel */
hme_stop(sc);
/* Re-initialize the MIF */
hme_mifinit(sc);
#if 0
/* Mask all MIF interrupts, just in case */
HME_MIF_WRITE_4(sc, HME_MIFI_IMASK, 0xffff);
#endif
/* step 3. Setup data structures in host memory */
if (hme_meminit(sc) != 0) {
device_printf(sc->sc_dev, "out of buffers; init aborted.");
return;
}
/* step 4. TX MAC registers & counters */
HME_MAC_WRITE_4(sc, HME_MACI_NCCNT, 0);
HME_MAC_WRITE_4(sc, HME_MACI_FCCNT, 0);
HME_MAC_WRITE_4(sc, HME_MACI_EXCNT, 0);
HME_MAC_WRITE_4(sc, HME_MACI_LTCNT, 0);
HME_MAC_WRITE_4(sc, HME_MACI_TXSIZE, HME_MAX_FRAMESIZE);
/* Load station MAC address */
ea = IF_LLADDR(sc->sc_ifp);
HME_MAC_WRITE_4(sc, HME_MACI_MACADDR0, (ea[0] << 8) | ea[1]);
HME_MAC_WRITE_4(sc, HME_MACI_MACADDR1, (ea[2] << 8) | ea[3]);
HME_MAC_WRITE_4(sc, HME_MACI_MACADDR2, (ea[4] << 8) | ea[5]);
/*
* Init seed for backoff
* (source suggested by manual: low 10 bits of MAC address)
*/
v = ((ea[4] << 8) | ea[5]) & 0x3fff;
HME_MAC_WRITE_4(sc, HME_MACI_RANDSEED, v);
/* Note: Accepting power-on default for other MAC registers here.. */
/* step 5. RX MAC registers & counters */
hme_setladrf(sc, 0);
/* step 6 & 7. Program Descriptor Ring Base Addresses */
HME_ETX_WRITE_4(sc, HME_ETXI_RING, sc->sc_rb.rb_txddma);
/* Transmit Descriptor ring size: in increments of 16 */
HME_ETX_WRITE_4(sc, HME_ETXI_RSIZE, HME_NTXDESC / 16 - 1);
HME_ERX_WRITE_4(sc, HME_ERXI_RING, sc->sc_rb.rb_rxddma);
HME_MAC_WRITE_4(sc, HME_MACI_RXSIZE, HME_MAX_FRAMESIZE);
/* step 8. Global Configuration & Interrupt Mask */
HME_SEB_WRITE_4(sc, HME_SEBI_IMASK,
~(/*HME_SEB_STAT_GOTFRAME | HME_SEB_STAT_SENTFRAME |*/
HME_SEB_STAT_HOSTTOTX |
HME_SEB_STAT_RXTOHOST |
HME_SEB_STAT_TXALL |
HME_SEB_STAT_TXPERR |
HME_SEB_STAT_RCNTEXP |
HME_SEB_STAT_ALL_ERRORS ));
switch (sc->sc_burst) {
default:
v = 0;
break;
case 16:
v = HME_SEB_CFG_BURST16;
break;
case 32:
v = HME_SEB_CFG_BURST32;
break;
case 64:
v = HME_SEB_CFG_BURST64;
break;
}
/*
* Blindly setting 64bit transfers may hang PCI cards(Cheerio?).
* Allowing 64bit transfers breaks TX checksum offload as well.
* Don't know this comes from hardware bug or driver's DMAing
* scheme.
*
* if (sc->sc_pci == 0)
* v |= HME_SEB_CFG_64BIT;
*/
HME_SEB_WRITE_4(sc, HME_SEBI_CFG, v);
/* step 9. ETX Configuration: use mostly default values */
/* Enable DMA */
v = HME_ETX_READ_4(sc, HME_ETXI_CFG);
v |= HME_ETX_CFG_DMAENABLE;
HME_ETX_WRITE_4(sc, HME_ETXI_CFG, v);
/* step 10. ERX Configuration */
v = HME_ERX_READ_4(sc, HME_ERXI_CFG);
/* Encode Receive Descriptor ring size: four possible values */
v &= ~HME_ERX_CFG_RINGSIZEMSK;
switch (HME_NRXDESC) {
case 32:
v |= HME_ERX_CFG_RINGSIZE32;
break;
case 64:
v |= HME_ERX_CFG_RINGSIZE64;
break;
case 128:
v |= HME_ERX_CFG_RINGSIZE128;
break;
case 256:
v |= HME_ERX_CFG_RINGSIZE256;
break;
default:
printf("hme: invalid Receive Descriptor ring size\n");
break;
}
/* Enable DMA, fix RX first byte offset. */
v &= ~HME_ERX_CFG_FBO_MASK;
v |= HME_ERX_CFG_DMAENABLE | (HME_RXOFFS << HME_ERX_CFG_FBO_SHIFT);
/* RX TCP/UDP checksum offset */
n = (ETHER_HDR_LEN + sizeof(struct ip)) / 2;
n = (n << HME_ERX_CFG_CSUMSTART_SHIFT) & HME_ERX_CFG_CSUMSTART_MASK;
v |= n;
CTR1(KTR_HME, "hme_init: programming ERX_CFG to %x", (u_int)v);
HME_ERX_WRITE_4(sc, HME_ERXI_CFG, v);
/* step 11. XIF Configuration */
v = HME_MAC_READ_4(sc, HME_MACI_XIF);
v |= HME_MAC_XIF_OE;
CTR1(KTR_HME, "hme_init: programming XIF to %x", (u_int)v);
HME_MAC_WRITE_4(sc, HME_MACI_XIF, v);
/* step 12. RX_MAC Configuration Register */
v = HME_MAC_READ_4(sc, HME_MACI_RXCFG);
v |= HME_MAC_RXCFG_ENABLE;
v &= ~(HME_MAC_RXCFG_DCRCS);
CTR1(KTR_HME, "hme_init: programming RX_MAC to %x", (u_int)v);
HME_MAC_WRITE_4(sc, HME_MACI_RXCFG, v);
/* step 13. TX_MAC Configuration Register */
v = HME_MAC_READ_4(sc, HME_MACI_TXCFG);
v |= (HME_MAC_TXCFG_ENABLE | HME_MAC_TXCFG_DGIVEUP);
CTR1(KTR_HME, "hme_init: programming TX_MAC to %x", (u_int)v);
HME_MAC_WRITE_4(sc, HME_MACI_TXCFG, v);
/* step 14. Issue Transmit Pending command */
#ifdef HMEDEBUG
/* Debug: double-check. */
CTR4(KTR_HME, "hme_init: tx ring %#x, rsz %#x, rx ring %#x, "
"rxsize %#x", HME_ETX_READ_4(sc, HME_ETXI_RING),
HME_ETX_READ_4(sc, HME_ETXI_RSIZE),
HME_ERX_READ_4(sc, HME_ERXI_RING),
HME_MAC_READ_4(sc, HME_MACI_RXSIZE));
CTR3(KTR_HME, "hme_init: intr mask %#x, erx cfg %#x, etx cfg %#x",
HME_SEB_READ_4(sc, HME_SEBI_IMASK),
HME_ERX_READ_4(sc, HME_ERXI_CFG),
HME_ETX_READ_4(sc, HME_ETXI_CFG));
CTR2(KTR_HME, "hme_init: mac rxcfg %#x, maci txcfg %#x",
HME_MAC_READ_4(sc, HME_MACI_RXCFG),
HME_MAC_READ_4(sc, HME_MACI_TXCFG));
#endif
/* Set the current media. */
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
hme_mediachange_locked(sc);
/* Start the one second timer. */
callout_reset(&sc->sc_tick_ch, hz, hme_tick, sc);
ifp->if_drv_flags |= IFF_DRV_RUNNING;
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
ifp->if_timer = 0;
hme_start_locked(ifp);
}
/* TX TCP/UDP checksum */
static void
hme_txcksum(struct mbuf *m, u_int32_t *cflags)
{
struct ip *ip;
u_int32_t offset, offset2;
caddr_t p;
for(; m && m->m_len == 0; m = m->m_next)
;
if (m == NULL || m->m_len < ETHER_HDR_LEN) {
printf("hme_txcksum: m_len < ETHER_HDR_LEN\n");
return; /* checksum will be corrupted */
}
if (m->m_len < ETHER_HDR_LEN + sizeof(u_int32_t)) {
if (m->m_len != ETHER_HDR_LEN) {
printf("hme_txcksum: m_len != ETHER_HDR_LEN\n");
return; /* checksum will be corrupted */
}
/* XXX */
for(m = m->m_next; m && m->m_len == 0; m = m->m_next)
;
if (m == NULL)
return; /* checksum will be corrupted */
ip = mtod(m, struct ip *);
} else {
p = mtod(m, caddr_t);
p += ETHER_HDR_LEN;
ip = (struct ip *)p;
}
offset2 = m->m_pkthdr.csum_data;
offset = (ip->ip_hl << 2) + ETHER_HDR_LEN;
*cflags = offset << HME_XD_TXCKSUM_SSHIFT;
*cflags |= ((offset + offset2) << HME_XD_TXCKSUM_OSHIFT);
*cflags |= HME_XD_TXCKSUM;
}
/*
* Routine to dma map an mbuf chain, set up the descriptor rings accordingly and
* start the transmission.
* Returns 0 on success, -1 if there were not enough free descriptors to map
* the packet, or an errno otherwise.
*
* XXX: this relies on the fact that segments returned by bus_dmamap_load_mbuf()
* are readable from the nearest burst boundary on (i.e. potentially before
* ds_addr) to the first boundary beyond the end. This is usually a safe
* assumption to make, but is not documented.
*/
static int
hme_load_txmbuf(struct hme_softc *sc, struct mbuf **m0)
{
struct hme_txdesc *htx;
caddr_t txd;
int i, pci, si, ri, nseg;
u_int32_t flags, cflags = 0;
int error = 0;
if ((htx = STAILQ_FIRST(&sc->sc_rb.rb_txfreeq)) == NULL)
return (-1);
error = bus_dmamap_load_mbuf_sg(sc->sc_tdmatag, htx->htx_dmamap,
*m0, sc->sc_rb.rb_txsegs, &nseg, 0);
if (error == EFBIG) {
struct mbuf *m;
m = m_defrag(*m0, M_DONTWAIT);
if (m == NULL) {
m_freem(*m0);
*m0 = NULL;
return (ENOMEM);
}
*m0 = m;
error = bus_dmamap_load_mbuf_sg(sc->sc_tdmatag, htx->htx_dmamap,
*m0, sc->sc_rb.rb_txsegs, &nseg, 0);
if (error != 0) {
m_freem(*m0);
*m0 = NULL;
return (error);
}
} else if (error != 0)
return (error);
if (nseg == 0) {
m_freem(*m0);
*m0 = NULL;
return (EIO);
}
if (sc->sc_rb.rb_td_nbusy + nseg >= HME_NTXDESC) {
bus_dmamap_unload(sc->sc_tdmatag, htx->htx_dmamap);
/* retry with m_defrag(9)? */
return (-2);
}
if (((*m0)->m_pkthdr.csum_flags & sc->sc_csum_features) != 0)
hme_txcksum(*m0, &cflags);
bus_dmamap_sync(sc->sc_tdmatag, htx->htx_dmamap, BUS_DMASYNC_PREWRITE);
si = ri = sc->sc_rb.rb_tdhead;
txd = sc->sc_rb.rb_txd;
pci = sc->sc_pci;
CTR2(KTR_HME, "hme_load_mbuf: next desc is %d (%#x)", ri,
HME_XD_GETFLAGS(pci, txd, ri));
for (i = 0; i < nseg; i++) {
/* Fill the ring entry. */
flags = HME_XD_ENCODE_TSIZE(sc->sc_rb.rb_txsegs[i].ds_len);
if (i == 0)
flags |= HME_XD_SOP | cflags;
else
flags |= HME_XD_OWN | cflags;
CTR3(KTR_HME, "hme_load_mbuf: activating ri %d, si %d (%#x)",
ri, si, flags);
HME_XD_SETADDR(pci, txd, ri, sc->sc_rb.rb_txsegs[i].ds_addr);
HME_XD_SETFLAGS(pci, txd, ri, flags);
sc->sc_rb.rb_td_nbusy++;
htx->htx_lastdesc = ri;
ri = (ri + 1) % HME_NTXDESC;
}
sc->sc_rb.rb_tdhead = ri;
/* set EOP on the last descriptor */
ri = (ri + HME_NTXDESC - 1) % HME_NTXDESC;
flags = HME_XD_GETFLAGS(pci, txd, ri);
flags |= HME_XD_EOP;
CTR3(KTR_HME, "hme_load_mbuf: setting EOP ri %d, si %d (%#x)", ri, si,
flags);
HME_XD_SETFLAGS(pci, txd, ri, flags);
/* Turn the first descriptor ownership to the hme */
flags = HME_XD_GETFLAGS(pci, txd, si);
flags |= HME_XD_OWN;
CTR2(KTR_HME, "hme_load_mbuf: setting OWN for 1st desc ri %d, (%#x)",
ri, flags);
HME_XD_SETFLAGS(pci, txd, si, flags);
STAILQ_REMOVE_HEAD(&sc->sc_rb.rb_txfreeq, htx_q);
STAILQ_INSERT_TAIL(&sc->sc_rb.rb_txbusyq, htx, htx_q);
htx->htx_m = *m0;
/* start the transmission. */
HME_ETX_WRITE_4(sc, HME_ETXI_PENDING, HME_ETX_TP_DMAWAKEUP);
return (0);
}
/*
* Pass a packet to the higher levels.
*/
static void
hme_read(struct hme_softc *sc, int ix, int len, u_int32_t flags)
{
struct ifnet *ifp = sc->sc_ifp;
struct mbuf *m;
if (len <= sizeof(struct ether_header) ||
len > HME_MAX_FRAMESIZE) {
#ifdef HMEDEBUG
HME_WHINE(sc->sc_dev, "invalid packet size %d; dropping\n",
len);
#endif
ifp->if_ierrors++;
hme_discard_rxbuf(sc, ix);
return;
}
m = sc->sc_rb.rb_rxdesc[ix].hrx_m;
CTR1(KTR_HME, "hme_read: len %d", len);
if (hme_add_rxbuf(sc, ix, 0) != 0) {
/*
* hme_add_rxbuf will leave the old buffer in the ring until
* it is sure that a new buffer can be mapped. If it can not,
* drop the packet, but leave the interface up.
*/
ifp->if_iqdrops++;
hme_discard_rxbuf(sc, ix);
return;
}
ifp->if_ipackets++;
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = m->m_len = len + HME_RXOFFS;
m_adj(m, HME_RXOFFS);
/* RX TCP/UDP checksum */
if (ifp->if_capenable & IFCAP_RXCSUM)
hme_rxcksum(m, flags);
/* Pass the packet up. */
HME_UNLOCK(sc);
(*ifp->if_input)(ifp, m);
HME_LOCK(sc);
}
static void
hme_start(struct ifnet *ifp)
{
struct hme_softc *sc = ifp->if_softc;
HME_LOCK(sc);
hme_start_locked(ifp);
HME_UNLOCK(sc);
}
static void
hme_start_locked(struct ifnet *ifp)
{
struct hme_softc *sc = (struct hme_softc *)ifp->if_softc;
struct mbuf *m;
int error, enq = 0;
if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
IFF_DRV_RUNNING)
return;
for (; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) &&
sc->sc_rb.rb_td_nbusy < HME_NTXDESC - 1;) {
IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
if (m == NULL)
break;
error = hme_load_txmbuf(sc, &m);
if (error != 0) {
if (m == NULL)
break;
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
IFQ_DRV_PREPEND(&ifp->if_snd, m);
break;
}
enq++;
BPF_MTAP(ifp, m);
}
/* Set watchdog timer if a packet was queued */
if (enq > 0) {
bus_dmamap_sync(sc->sc_cdmatag, sc->sc_cdmamap,
BUS_DMASYNC_PREWRITE);
ifp->if_timer = 5;
}
}
/*
* Transmit interrupt.
*/
static void
hme_tint(struct hme_softc *sc)
{
caddr_t txd;
struct ifnet *ifp = sc->sc_ifp;
struct hme_txdesc *htx;
unsigned int ri, txflags;
txd = sc->sc_rb.rb_txd;
htx = STAILQ_FIRST(&sc->sc_rb.rb_txbusyq);
bus_dmamap_sync(sc->sc_cdmatag, sc->sc_cdmamap, BUS_DMASYNC_POSTREAD);
/* Fetch current position in the transmit ring */
for (ri = sc->sc_rb.rb_tdtail;; ri = (ri + 1) % HME_NTXDESC) {
if (sc->sc_rb.rb_td_nbusy <= 0) {
CTR0(KTR_HME, "hme_tint: not busy!");
break;
}
txflags = HME_XD_GETFLAGS(sc->sc_pci, txd, ri);
CTR2(KTR_HME, "hme_tint: index %d, flags %#x", ri, txflags);
if ((txflags & HME_XD_OWN) != 0)
break;
CTR0(KTR_HME, "hme_tint: not owned");
--sc->sc_rb.rb_td_nbusy;
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
/* Complete packet transmitted? */
if ((txflags & HME_XD_EOP) == 0)
continue;
KASSERT(htx->htx_lastdesc == ri,
("hme_tint: ring indices skewed: %d != %d!",
htx->htx_lastdesc, ri));
bus_dmamap_sync(sc->sc_tdmatag, htx->htx_dmamap,
BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_tdmatag, htx->htx_dmamap);
ifp->if_opackets++;
m_freem(htx->htx_m);
htx->htx_m = NULL;
STAILQ_REMOVE_HEAD(&sc->sc_rb.rb_txbusyq, htx_q);
STAILQ_INSERT_TAIL(&sc->sc_rb.rb_txfreeq, htx, htx_q);
htx = STAILQ_FIRST(&sc->sc_rb.rb_txbusyq);
}
/* Turn off watchdog if hme(4) transmitted queued packet */
ifp->if_timer = sc->sc_rb.rb_td_nbusy > 0 ? 5 : 0;
/* Update ring */
sc->sc_rb.rb_tdtail = ri;
if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
hme_start_locked(ifp);
}
/*
* RX TCP/UDP checksum
*/
static void
hme_rxcksum(struct mbuf *m, u_int32_t flags)
{
struct ether_header *eh;
struct ip *ip;
struct udphdr *uh;
int32_t hlen, len, pktlen;
u_int16_t cksum, *opts;
u_int32_t temp32;
pktlen = m->m_pkthdr.len;
if (pktlen < sizeof(struct ether_header) + sizeof(struct ip))
return;
eh = mtod(m, struct ether_header *);
if (eh->ether_type != htons(ETHERTYPE_IP))
return;
ip = (struct ip *)(eh + 1);
if (ip->ip_v != IPVERSION)
return;
hlen = ip->ip_hl << 2;
pktlen -= sizeof(struct ether_header);
if (hlen < sizeof(struct ip))
return;
if (ntohs(ip->ip_len) < hlen)
return;
if (ntohs(ip->ip_len) != pktlen)
return;
if (ip->ip_off & htons(IP_MF | IP_OFFMASK))
return; /* can't handle fragmented packet */
switch (ip->ip_p) {
case IPPROTO_TCP:
if (pktlen < (hlen + sizeof(struct tcphdr)))
return;
break;
case IPPROTO_UDP:
if (pktlen < (hlen + sizeof(struct udphdr)))
return;
uh = (struct udphdr *)((caddr_t)ip + hlen);
if (uh->uh_sum == 0)
return; /* no checksum */
break;
default:
return;
}
cksum = ~(flags & HME_XD_RXCKSUM);
/* checksum fixup for IP options */
len = hlen - sizeof(struct ip);
if (len > 0) {
opts = (u_int16_t *)(ip + 1);
for (; len > 0; len -= sizeof(u_int16_t), opts++) {
temp32 = cksum - *opts;
temp32 = (temp32 >> 16) + (temp32 & 65535);
cksum = temp32 & 65535;
}
}
m->m_pkthdr.csum_flags |= CSUM_DATA_VALID;
m->m_pkthdr.csum_data = cksum;
}
/*
* Receive interrupt.
*/
static void
hme_rint(struct hme_softc *sc)
{
caddr_t xdr = sc->sc_rb.rb_rxd;
struct ifnet *ifp = sc->sc_ifp;
unsigned int ri, len;
int progress = 0;
u_int32_t flags;
/*
* Process all buffers with valid data.
*/
bus_dmamap_sync(sc->sc_cdmatag, sc->sc_cdmamap, BUS_DMASYNC_POSTREAD);
for (ri = sc->sc_rb.rb_rdtail;; ri = (ri + 1) % HME_NRXDESC) {
flags = HME_XD_GETFLAGS(sc->sc_pci, xdr, ri);
CTR2(KTR_HME, "hme_rint: index %d, flags %#x", ri, flags);
if ((flags & HME_XD_OWN) != 0)
break;
progress++;
if ((flags & HME_XD_OFL) != 0) {
device_printf(sc->sc_dev, "buffer overflow, ri=%d; "
"flags=0x%x\n", ri, flags);
ifp->if_ierrors++;
hme_discard_rxbuf(sc, ri);
} else {
len = HME_XD_DECODE_RSIZE(flags);
hme_read(sc, ri, len, flags);
}
}
if (progress) {
bus_dmamap_sync(sc->sc_cdmatag, sc->sc_cdmamap,
BUS_DMASYNC_PREWRITE);
}
sc->sc_rb.rb_rdtail = ri;
}
static void
hme_eint(struct hme_softc *sc, u_int status)
{
if ((status & HME_SEB_STAT_MIFIRQ) != 0) {
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
device_printf(sc->sc_dev, "XXXlink status changed: "
"cfg=%#x, stat=%#x, sm=%#x\n",
HME_MIF_READ_4(sc, HME_MIFI_CFG),
HME_MIF_READ_4(sc, HME_MIFI_STAT),
HME_MIF_READ_4(sc, HME_MIFI_SM));
return;
}
/* check for fatal errors that needs reset to unfreeze DMA engine */
if ((status & HME_SEB_STAT_FATAL_ERRORS) != 0) {
HME_WHINE(sc->sc_dev, "error signaled, status=%#x\n", status);
hme_init_locked(sc);
}
}
void
hme_intr(void *v)
{
struct hme_softc *sc = (struct hme_softc *)v;
u_int32_t status;
HME_LOCK(sc);
status = HME_SEB_READ_4(sc, HME_SEBI_STAT);
CTR1(KTR_HME, "hme_intr: status %#x", (u_int)status);
if ((status & HME_SEB_STAT_ALL_ERRORS) != 0)
hme_eint(sc, status);
if ((status & (HME_SEB_STAT_TXALL | HME_SEB_STAT_HOSTTOTX)) != 0)
hme_tint(sc);
if ((status & HME_SEB_STAT_RXTOHOST) != 0)
hme_rint(sc);
HME_UNLOCK(sc);
}
static void
hme_watchdog(struct ifnet *ifp)
{
struct hme_softc *sc = ifp->if_softc;
#ifdef HMEDEBUG
u_int32_t status;
#endif
HME_LOCK(sc);
#ifdef HMEDEBUG
status = HME_SEB_READ_4(sc, HME_SEBI_STAT);
CTR1(KTR_HME, "hme_watchdog: status %x", (u_int)status);
#endif
device_printf(sc->sc_dev, "device timeout\n");
++ifp->if_oerrors;
hme_init_locked(sc);
HME_UNLOCK(sc);
}
/*
* Initialize the MII Management Interface
*/
static void
hme_mifinit(struct hme_softc *sc)
{
u_int32_t v;
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
/*
* Configure the MIF in frame mode, polling disabled, internal PHY
* selected.
*/
HME_MIF_WRITE_4(sc, HME_MIFI_CFG, 0);
/*
* If the currently selected media uses the external transceiver,
* enable its MII drivers (which basically isolates the internal
* one and vice versa). In case the current media hasn't been set,
* yet, we default to the internal transceiver.
*/
v = HME_MAC_READ_4(sc, HME_MACI_XIF);
if (sc->sc_mii != NULL && sc->sc_mii->mii_media.ifm_cur != NULL &&
sc->sc_phys[IFM_INST(sc->sc_mii->mii_media.ifm_cur->ifm_media)] ==
HME_PHYAD_EXTERNAL)
v |= HME_MAC_XIF_MIIENABLE;
else
v &= ~HME_MAC_XIF_MIIENABLE;
HME_MAC_WRITE_4(sc, HME_MACI_XIF, v);
}
/*
* MII interface
*/
int
hme_mii_readreg(device_t dev, int phy, int reg)
{
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
struct hme_softc *sc;
int n;
u_int32_t v;
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
/* We can at most have two PHYs. */
if (phy != HME_PHYAD_EXTERNAL && phy != HME_PHYAD_INTERNAL)
return (0);
sc = device_get_softc(dev);
/* Select the desired PHY in the MIF configuration register */
v = HME_MIF_READ_4(sc, HME_MIFI_CFG);
if (phy == HME_PHYAD_EXTERNAL)
v |= HME_MIF_CFG_PHY;
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
else
v &= ~HME_MIF_CFG_PHY;
HME_MIF_WRITE_4(sc, HME_MIFI_CFG, v);
/* Construct the frame command */
v = (MII_COMMAND_START << HME_MIF_FO_ST_SHIFT) |
HME_MIF_FO_TAMSB |
(MII_COMMAND_READ << HME_MIF_FO_OPC_SHIFT) |
(phy << HME_MIF_FO_PHYAD_SHIFT) |
(reg << HME_MIF_FO_REGAD_SHIFT);
HME_MIF_WRITE_4(sc, HME_MIFI_FO, v);
for (n = 0; n < 100; n++) {
DELAY(1);
v = HME_MIF_READ_4(sc, HME_MIFI_FO);
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
if (v & HME_MIF_FO_TALSB)
return (v & HME_MIF_FO_DATA);
}
device_printf(sc->sc_dev, "mii_read timeout\n");
return (0);
}
int
hme_mii_writereg(device_t dev, int phy, int reg, int val)
{
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
struct hme_softc *sc;
int n;
u_int32_t v;
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
/* We can at most have two PHYs. */
if (phy != HME_PHYAD_EXTERNAL && phy != HME_PHYAD_INTERNAL)
return (0);
sc = device_get_softc(dev);
/* Select the desired PHY in the MIF configuration register */
v = HME_MIF_READ_4(sc, HME_MIFI_CFG);
if (phy == HME_PHYAD_EXTERNAL)
v |= HME_MIF_CFG_PHY;
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
else
v &= ~HME_MIF_CFG_PHY;
HME_MIF_WRITE_4(sc, HME_MIFI_CFG, v);
/* Construct the frame command */
v = (MII_COMMAND_START << HME_MIF_FO_ST_SHIFT) |
HME_MIF_FO_TAMSB |
(MII_COMMAND_WRITE << HME_MIF_FO_OPC_SHIFT) |
(phy << HME_MIF_FO_PHYAD_SHIFT) |
(reg << HME_MIF_FO_REGAD_SHIFT) |
(val & HME_MIF_FO_DATA);
HME_MIF_WRITE_4(sc, HME_MIFI_FO, v);
for (n = 0; n < 100; n++) {
DELAY(1);
v = HME_MIF_READ_4(sc, HME_MIFI_FO);
if (v & HME_MIF_FO_TALSB)
return (1);
}
device_printf(sc->sc_dev, "mii_write timeout\n");
return (0);
}
void
hme_mii_statchg(device_t dev)
{
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
struct hme_softc *sc;
u_int32_t v;
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
sc = device_get_softc(dev);
#ifdef HMEDEBUG
if (sc->sc_debug)
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
device_printf(sc->sc_dev, "hme_mii_statchg: status change\n");
#endif
/* Set the MAC Full Duplex bit appropriately */
v = HME_MAC_READ_4(sc, HME_MACI_TXCFG);
if (!hme_mac_bitflip(sc, HME_MACI_TXCFG, v, HME_MAC_TXCFG_ENABLE, 0))
return;
if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) != 0)
v |= HME_MAC_TXCFG_FULLDPLX;
else
v &= ~HME_MAC_TXCFG_FULLDPLX;
HME_MAC_WRITE_4(sc, HME_MACI_TXCFG, v);
if (!hme_mac_bitflip(sc, HME_MACI_TXCFG, v, 0, HME_MAC_TXCFG_ENABLE))
return;
}
static int
hme_mediachange(struct ifnet *ifp)
{
struct hme_softc *sc = ifp->if_softc;
int error;
HME_LOCK(sc);
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
error = hme_mediachange_locked(sc);
HME_UNLOCK(sc);
return (error);
}
- In hme_stop() mask all interrupts. - In hme_eint() print MIF register contents on MIF interrupts. - In hme_mifinit() don't bother to preserve the previous MIF config. This was mainly done in order to preserve the PHY select bit (external or internal PHY) but which only needs to be set as appropriate when reading from or writing to the desired PHY in hme_mii_{read,write}reg(). Similarly don't bother to set the PHY select bit in hme_mii_statchg(). - In hme_mii_{read,write}reg() ignore requests to PHYs other than the external and internal PHY one. - Move enabling/disabling the MII drivers of the external transceiver from hme_init_locked() and based on the sheer presence of an external to hme_mifinit() and based on the currently selected media, defaulting to the internal transceiver when the media hasn't been set, yet. Invoke hme_mifinit() from the newly added hme_mediachange_locked() so the setting of the MII drivers is updated when changing media. These changes keep the MII bus from wedging (which manifests in the HME and the PHYs no longer being able to communicate with each other) when the PHY device drivers isolate the unused PHY in two-PHY configurations as present in f.e. Netra t1 100 while changing media, either from hme_init_locked() (see also below) or via ifconfig(8). They also allow for using both transceivers/PHYs. - In the newly added hme_mediachange_locked() also reset the PHYs in two- PHY configurations before invoking mii_mediachg(). This is required for successfully unisolating the previously unused PHY when switching between PHYs. - Now that changing media should no longer cause problems back out rev. 1.27 and re-enable setting the current media in hme_init_locked() (see the commit message of rev. 1.23 for more info). These changes are roughly a merge of NetBSD gem.c rev. 1.32 - 1.35 (1.30 was already fixed differently in our 1.36; 1.31 and 1.32 were wrong) with some parts reworked and things that don't make sense like setting the MII drivers and restoring the previous MIF and XIF settings in hme_mii_{read, write}reg() omitted. MFC after: 2 weeks
2006-12-04 00:51:08 +00:00
static int
hme_mediachange_locked(struct hme_softc *sc)
{
struct mii_softc *child;
HME_LOCK_ASSERT(sc, MA_OWNED);
#ifdef HMEDEBUG
if (sc->sc_debug)
device_printf(sc->sc_dev, "hme_mediachange_locked");
#endif
hme_mifinit(sc);
/*
* If both PHYs are present reset them. This is required for
* unisolating the previously isolated PHY when switching PHYs.
* As the above hme_mifinit() call will set the MII drivers in
* the XIF configuration register accoring to the currently
* selected media, there should be no window during which the
* data paths of both transceivers are open at the same time,
* even if the PHY device drivers use MIIF_NOISOLATE.
*/
if (sc->sc_phys[0] != -1 && sc->sc_phys[1] != -1)
LIST_FOREACH(child, &sc->sc_mii->mii_phys, mii_list)
mii_phy_reset(child);
return (mii_mediachg(sc->sc_mii));
}
static void
hme_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
{
struct hme_softc *sc = ifp->if_softc;
HME_LOCK(sc);
if ((ifp->if_flags & IFF_UP) == 0) {
HME_UNLOCK(sc);
return;
}
mii_pollstat(sc->sc_mii);
ifmr->ifm_active = sc->sc_mii->mii_media_active;
ifmr->ifm_status = sc->sc_mii->mii_media_status;
HME_UNLOCK(sc);
}
/*
* Process an ioctl request.
*/
static int
hme_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct hme_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
int error = 0;
switch (cmd) {
case SIOCSIFFLAGS:
HME_LOCK(sc);
if ((ifp->if_flags & IFF_UP) == 0 &&
(ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
/*
* If interface is marked down and it is running, then
* stop it.
*/
hme_stop(sc);
} else if ((ifp->if_flags & IFF_UP) != 0 &&
(ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
/*
* If interface is marked up and it is stopped, then
* start it.
*/
hme_init_locked(sc);
} else if ((ifp->if_flags & IFF_UP) != 0) {
/*
* Reset the interface to pick up changes in any other
* flags that affect hardware registers.
*/
hme_init_locked(sc);
}
if ((ifp->if_flags & IFF_LINK0) != 0)
sc->sc_csum_features |= CSUM_UDP;
else
sc->sc_csum_features &= ~CSUM_UDP;
if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
ifp->if_hwassist = sc->sc_csum_features;
#ifdef HMEDEBUG
sc->sc_debug = (ifp->if_flags & IFF_DEBUG) != 0 ? 1 : 0;
#endif
HME_UNLOCK(sc);
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
HME_LOCK(sc);
hme_setladrf(sc, 1);
HME_UNLOCK(sc);
error = 0;
break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii->mii_media, cmd);
break;
case SIOCSIFCAP:
HME_LOCK(sc);
ifp->if_capenable = ifr->ifr_reqcap;
if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
ifp->if_hwassist = sc->sc_csum_features;
else
ifp->if_hwassist = 0;
HME_UNLOCK(sc);
break;
default:
error = ether_ioctl(ifp, cmd, data);
break;
}
return (error);
}
/*
* Set up the logical address filter.
*/
static void
hme_setladrf(struct hme_softc *sc, int reenable)
{
struct ifnet *ifp = sc->sc_ifp;
struct ifmultiaddr *inm;
u_int32_t crc;
u_int32_t hash[4];
u_int32_t macc;
HME_LOCK_ASSERT(sc, MA_OWNED);
/* Clear hash table */
hash[3] = hash[2] = hash[1] = hash[0] = 0;
/* Get current RX configuration */
macc = HME_MAC_READ_4(sc, HME_MACI_RXCFG);
/*
* Disable the receiver while changing it's state as the documentation
* mandates.
* We then must wait until the bit clears in the register. This should
* take at most 3.5ms.
*/
if (!hme_mac_bitflip(sc, HME_MACI_RXCFG, macc, HME_MAC_RXCFG_ENABLE, 0))
return;
/* Disable the hash filter before writing to the filter registers. */
if (!hme_mac_bitflip(sc, HME_MACI_RXCFG, macc,
HME_MAC_RXCFG_HENABLE, 0))
return;
/* make RXMAC really SIMPLEX */
macc |= HME_MAC_RXCFG_ME;
if (reenable)
macc |= HME_MAC_RXCFG_ENABLE;
else
macc &= ~HME_MAC_RXCFG_ENABLE;
if ((ifp->if_flags & IFF_PROMISC) != 0) {
/* Turn on promiscuous mode; turn off the hash filter */
macc |= HME_MAC_RXCFG_PMISC;
macc &= ~HME_MAC_RXCFG_HENABLE;
ifp->if_flags |= IFF_ALLMULTI;
goto chipit;
}
/* Turn off promiscuous mode; turn on the hash filter */
macc &= ~HME_MAC_RXCFG_PMISC;
macc |= HME_MAC_RXCFG_HENABLE;
/*
* Set up multicast address filter by passing all multicast addresses
* through a crc generator, and then using the high order 6 bits as an
* index into the 64 bit logical address filter. The high order bit
* selects the word, while the rest of the bits select the bit within
* the word.
*/
IF_ADDR_LOCK(sc->sc_ifp);
TAILQ_FOREACH(inm, &sc->sc_ifp->if_multiaddrs, ifma_link) {
if (inm->ifma_addr->sa_family != AF_LINK)
continue;
crc = ether_crc32_le(LLADDR((struct sockaddr_dl *)
inm->ifma_addr), ETHER_ADDR_LEN);
/* Just want the 6 most significant bits. */
crc >>= 26;
/* Set the corresponding bit in the filter. */
hash[crc >> 4] |= 1 << (crc & 0xf);
}
IF_ADDR_UNLOCK(sc->sc_ifp);
ifp->if_flags &= ~IFF_ALLMULTI;
chipit:
/* Now load the hash table into the chip */
HME_MAC_WRITE_4(sc, HME_MACI_HASHTAB0, hash[0]);
HME_MAC_WRITE_4(sc, HME_MACI_HASHTAB1, hash[1]);
HME_MAC_WRITE_4(sc, HME_MACI_HASHTAB2, hash[2]);
HME_MAC_WRITE_4(sc, HME_MACI_HASHTAB3, hash[3]);
hme_mac_bitflip(sc, HME_MACI_RXCFG, macc, 0,
macc & (HME_MAC_RXCFG_ENABLE | HME_MAC_RXCFG_HENABLE |
HME_MAC_RXCFG_ME));
}