1a17c678eSDavid Greenman /* 2a17c678eSDavid Greenman * Copyright (c) 1995, David Greenman 3a17c678eSDavid Greenman * All rights reserved. 4a17c678eSDavid Greenman * 5ba8c6fd5SDavid Greenman * Modifications to support NetBSD and media selection: 6ba8c6fd5SDavid Greenman * Copyright (c) 1997 Jason R. Thorpe. All rights reserved. 7ba8c6fd5SDavid Greenman * 8a17c678eSDavid Greenman * Redistribution and use in source and binary forms, with or without 9a17c678eSDavid Greenman * modification, are permitted provided that the following conditions 10a17c678eSDavid Greenman * are met: 11a17c678eSDavid Greenman * 1. Redistributions of source code must retain the above copyright 12a17c678eSDavid Greenman * notice unmodified, this list of conditions, and the following 13a17c678eSDavid Greenman * disclaimer. 14a17c678eSDavid Greenman * 2. Redistributions in binary form must reproduce the above copyright 15a17c678eSDavid Greenman * notice, this list of conditions and the following disclaimer in the 16a17c678eSDavid Greenman * documentation and/or other materials provided with the distribution. 17a17c678eSDavid Greenman * 18a17c678eSDavid Greenman * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19a17c678eSDavid Greenman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20a17c678eSDavid Greenman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21a17c678eSDavid Greenman * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22a17c678eSDavid Greenman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23a17c678eSDavid Greenman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24a17c678eSDavid Greenman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25a17c678eSDavid Greenman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26a17c678eSDavid Greenman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27a17c678eSDavid Greenman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28a17c678eSDavid Greenman * SUCH DAMAGE. 29a17c678eSDavid Greenman * 30df373873SWes Peters * $Id: if_fxp.c,v 1.65 1999/03/17 16:44:53 luigi Exp $ 31a17c678eSDavid Greenman */ 32a17c678eSDavid Greenman 33a17c678eSDavid Greenman /* 34ae12cddaSDavid Greenman * Intel EtherExpress Pro/100B PCI Fast Ethernet driver 35a17c678eSDavid Greenman */ 36a17c678eSDavid Greenman 37a17c678eSDavid Greenman #include "bpfilter.h" 38a17c678eSDavid Greenman 39a17c678eSDavid Greenman #include <sys/param.h> 40a17c678eSDavid Greenman #include <sys/systm.h> 41a17c678eSDavid Greenman #include <sys/mbuf.h> 42a17c678eSDavid Greenman #include <sys/malloc.h> 43a17c678eSDavid Greenman #include <sys/kernel.h> 444458ac71SBruce Evans #include <sys/socket.h> 45a17c678eSDavid Greenman 46a17c678eSDavid Greenman #include <net/if.h> 47397f9dfeSDavid Greenman #include <net/if_dl.h> 48ba8c6fd5SDavid Greenman #include <net/if_media.h> 49a17c678eSDavid Greenman 50a17c678eSDavid Greenman #ifdef NS 51a17c678eSDavid Greenman #include <netns/ns.h> 52a17c678eSDavid Greenman #include <netns/ns_if.h> 53a17c678eSDavid Greenman #endif 54a17c678eSDavid Greenman 55a17c678eSDavid Greenman #if NBPFILTER > 0 56a17c678eSDavid Greenman #include <net/bpf.h> 57a17c678eSDavid Greenman #endif 58a17c678eSDavid Greenman 59ba8c6fd5SDavid Greenman #if defined(__NetBSD__) 60ba8c6fd5SDavid Greenman 61ba8c6fd5SDavid Greenman #include <sys/ioctl.h> 62ba8c6fd5SDavid Greenman #include <sys/errno.h> 63ba8c6fd5SDavid Greenman #include <sys/device.h> 64ba8c6fd5SDavid Greenman 65ba8c6fd5SDavid Greenman #include <net/if_dl.h> 66ba8c6fd5SDavid Greenman #include <net/if_ether.h> 67ba8c6fd5SDavid Greenman 68ba8c6fd5SDavid Greenman #include <netinet/if_inarp.h> 69ba8c6fd5SDavid Greenman 70ba8c6fd5SDavid Greenman #include <vm/vm.h> 71ba8c6fd5SDavid Greenman 72ba8c6fd5SDavid Greenman #include <machine/cpu.h> 73ba8c6fd5SDavid Greenman #include <machine/bus.h> 74ba8c6fd5SDavid Greenman #include <machine/intr.h> 75ba8c6fd5SDavid Greenman 76ba8c6fd5SDavid Greenman #include <dev/pci/if_fxpreg.h> 77ba8c6fd5SDavid Greenman #include <dev/pci/if_fxpvar.h> 78ba8c6fd5SDavid Greenman 79ba8c6fd5SDavid Greenman #include <dev/pci/pcivar.h> 80ba8c6fd5SDavid Greenman #include <dev/pci/pcireg.h> 81ba8c6fd5SDavid Greenman #include <dev/pci/pcidevs.h> 82ba8c6fd5SDavid Greenman 83ba8c6fd5SDavid Greenman #ifdef __alpha__ /* XXX */ 84ba8c6fd5SDavid Greenman /* XXX XXX NEED REAL DMA MAPPING SUPPORT XXX XXX */ 85ba8c6fd5SDavid Greenman #undef vtophys 86ba8c6fd5SDavid Greenman #define vtophys(va) alpha_XXX_dmamap((vm_offset_t)(va)) 87ba8c6fd5SDavid Greenman #endif /* __alpha__ */ 88ba8c6fd5SDavid Greenman 89ba8c6fd5SDavid Greenman #else /* __FreeBSD__ */ 90ba8c6fd5SDavid Greenman 91ba8c6fd5SDavid Greenman #include <sys/sockio.h> 92ba8c6fd5SDavid Greenman 931d5e9e22SEivind Eklund #include <net/ethernet.h> 941d5e9e22SEivind Eklund #include <net/if_arp.h> 95ba8c6fd5SDavid Greenman 96dfe61cf1SDavid Greenman #include <vm/vm.h> /* for vtophys */ 97efeaf95aSDavid Greenman #include <vm/pmap.h> /* for vtophys */ 98dfe61cf1SDavid Greenman #include <machine/clock.h> /* for DELAY */ 99a17c678eSDavid Greenman 100a17c678eSDavid Greenman #include <pci/pcivar.h> 101df373873SWes Peters #include <pci/pcireg.h> /* for PCIM_CMD_xxx */ 102a17c678eSDavid Greenman #include <pci/if_fxpreg.h> 103ba8c6fd5SDavid Greenman #include <pci/if_fxpvar.h> 104a17c678eSDavid Greenman 105ba8c6fd5SDavid Greenman #endif /* __NetBSD__ */ 106a17c678eSDavid Greenman 107ab090e5bSLuigi Rizzo #include "opt_bdg.h" 108ab090e5bSLuigi Rizzo #ifdef BRIDGE 109ab090e5bSLuigi Rizzo #include <net/if_types.h> 110ab090e5bSLuigi Rizzo #include <net/bridge.h> 111ab090e5bSLuigi Rizzo #endif 112ab090e5bSLuigi Rizzo 113ba8c6fd5SDavid Greenman /* 114ba8c6fd5SDavid Greenman * NOTE! On the Alpha, we have an alignment constraint. The 115ba8c6fd5SDavid Greenman * card DMAs the packet immediately following the RFA. However, 116ba8c6fd5SDavid Greenman * the first thing in the packet is a 14-byte Ethernet header. 117ba8c6fd5SDavid Greenman * This means that the packet is misaligned. To compensate, 118ba8c6fd5SDavid Greenman * we actually offset the RFA 2 bytes into the cluster. This 119ba8c6fd5SDavid Greenman * alignes the packet after the Ethernet header at a 32-bit 120ba8c6fd5SDavid Greenman * boundary. HOWEVER! This means that the RFA is misaligned! 121ba8c6fd5SDavid Greenman */ 122ba8c6fd5SDavid Greenman #define RFA_ALIGNMENT_FUDGE 2 123ba8c6fd5SDavid Greenman 124ba8c6fd5SDavid Greenman /* 125ba8c6fd5SDavid Greenman * Inline function to copy a 16-bit aligned 32-bit quantity. 126ba8c6fd5SDavid Greenman */ 127ba8c6fd5SDavid Greenman static __inline void fxp_lwcopy __P((volatile u_int32_t *, 128ba8c6fd5SDavid Greenman volatile u_int32_t *)); 129ba8c6fd5SDavid Greenman static __inline void 130ba8c6fd5SDavid Greenman fxp_lwcopy(src, dst) 131ba8c6fd5SDavid Greenman volatile u_int32_t *src, *dst; 132ba8c6fd5SDavid Greenman { 133fe08c21aSMatthew Dillon volatile u_int16_t *a = (volatile u_int16_t *)src; 134fe08c21aSMatthew Dillon volatile u_int16_t *b = (volatile u_int16_t *)dst; 135ba8c6fd5SDavid Greenman 136ba8c6fd5SDavid Greenman b[0] = a[0]; 137ba8c6fd5SDavid Greenman b[1] = a[1]; 138ba8c6fd5SDavid Greenman } 139a17c678eSDavid Greenman 140a17c678eSDavid Greenman /* 141a17c678eSDavid Greenman * Template for default configuration parameters. 142a17c678eSDavid Greenman * See struct fxp_cb_config for the bit definitions. 143a17c678eSDavid Greenman */ 144a17c678eSDavid Greenman static u_char fxp_cb_config_template[] = { 145a17c678eSDavid Greenman 0x0, 0x0, /* cb_status */ 146a17c678eSDavid Greenman 0x80, 0x2, /* cb_command */ 147a17c678eSDavid Greenman 0xff, 0xff, 0xff, 0xff, /* link_addr */ 148a17c678eSDavid Greenman 0x16, /* 0 */ 149a17c678eSDavid Greenman 0x8, /* 1 */ 150a17c678eSDavid Greenman 0x0, /* 2 */ 151a17c678eSDavid Greenman 0x0, /* 3 */ 152a17c678eSDavid Greenman 0x0, /* 4 */ 153a17c678eSDavid Greenman 0x80, /* 5 */ 154a17c678eSDavid Greenman 0xb2, /* 6 */ 155a17c678eSDavid Greenman 0x3, /* 7 */ 156a17c678eSDavid Greenman 0x1, /* 8 */ 157a17c678eSDavid Greenman 0x0, /* 9 */ 158a17c678eSDavid Greenman 0x26, /* 10 */ 159a17c678eSDavid Greenman 0x0, /* 11 */ 160a17c678eSDavid Greenman 0x60, /* 12 */ 161a17c678eSDavid Greenman 0x0, /* 13 */ 162a17c678eSDavid Greenman 0xf2, /* 14 */ 163a17c678eSDavid Greenman 0x48, /* 15 */ 164a17c678eSDavid Greenman 0x0, /* 16 */ 165a17c678eSDavid Greenman 0x40, /* 17 */ 166a17c678eSDavid Greenman 0xf3, /* 18 */ 167a17c678eSDavid Greenman 0x0, /* 19 */ 168a17c678eSDavid Greenman 0x3f, /* 20 */ 169397f9dfeSDavid Greenman 0x5 /* 21 */ 170a17c678eSDavid Greenman }; 171a17c678eSDavid Greenman 172ba8c6fd5SDavid Greenman /* Supported media types. */ 173ba8c6fd5SDavid Greenman struct fxp_supported_media { 174ba8c6fd5SDavid Greenman const int fsm_phy; /* PHY type */ 175ba8c6fd5SDavid Greenman const int *fsm_media; /* the media array */ 176ba8c6fd5SDavid Greenman const int fsm_nmedia; /* the number of supported media */ 177ba8c6fd5SDavid Greenman const int fsm_defmedia; /* default media for this PHY */ 178ba8c6fd5SDavid Greenman }; 179ba8c6fd5SDavid Greenman 180303b270bSEivind Eklund static const int fxp_media_standard[] = { 181ba8c6fd5SDavid Greenman IFM_ETHER|IFM_10_T, 182ba8c6fd5SDavid Greenman IFM_ETHER|IFM_10_T|IFM_FDX, 183ba8c6fd5SDavid Greenman IFM_ETHER|IFM_100_TX, 184ba8c6fd5SDavid Greenman IFM_ETHER|IFM_100_TX|IFM_FDX, 185ba8c6fd5SDavid Greenman IFM_ETHER|IFM_AUTO, 186ba8c6fd5SDavid Greenman }; 187ba8c6fd5SDavid Greenman #define FXP_MEDIA_STANDARD_DEFMEDIA (IFM_ETHER|IFM_AUTO) 188ba8c6fd5SDavid Greenman 189303b270bSEivind Eklund static const int fxp_media_default[] = { 190ba8c6fd5SDavid Greenman IFM_ETHER|IFM_MANUAL, /* XXX IFM_AUTO ? */ 191ba8c6fd5SDavid Greenman }; 192ba8c6fd5SDavid Greenman #define FXP_MEDIA_DEFAULT_DEFMEDIA (IFM_ETHER|IFM_MANUAL) 193ba8c6fd5SDavid Greenman 194303b270bSEivind Eklund static const struct fxp_supported_media fxp_media[] = { 195ba8c6fd5SDavid Greenman { FXP_PHY_DP83840, fxp_media_standard, 196ba8c6fd5SDavid Greenman sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), 197ba8c6fd5SDavid Greenman FXP_MEDIA_STANDARD_DEFMEDIA }, 198ba8c6fd5SDavid Greenman { FXP_PHY_DP83840A, fxp_media_standard, 199ba8c6fd5SDavid Greenman sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), 200ba8c6fd5SDavid Greenman FXP_MEDIA_STANDARD_DEFMEDIA }, 20192924291SDavid Greenman { FXP_PHY_82553A, fxp_media_standard, 20292924291SDavid Greenman sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), 20392924291SDavid Greenman FXP_MEDIA_STANDARD_DEFMEDIA }, 20492924291SDavid Greenman { FXP_PHY_82553C, fxp_media_standard, 20592924291SDavid Greenman sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), 20692924291SDavid Greenman FXP_MEDIA_STANDARD_DEFMEDIA }, 207ba8c6fd5SDavid Greenman { FXP_PHY_82555, fxp_media_standard, 208ba8c6fd5SDavid Greenman sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), 209ba8c6fd5SDavid Greenman FXP_MEDIA_STANDARD_DEFMEDIA }, 21092924291SDavid Greenman { FXP_PHY_82555B, fxp_media_standard, 21192924291SDavid Greenman sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), 21292924291SDavid Greenman FXP_MEDIA_STANDARD_DEFMEDIA }, 213ba8c6fd5SDavid Greenman { FXP_PHY_80C24, fxp_media_default, 214ba8c6fd5SDavid Greenman sizeof(fxp_media_default) / sizeof(fxp_media_default[0]), 215ba8c6fd5SDavid Greenman FXP_MEDIA_DEFAULT_DEFMEDIA }, 216ba8c6fd5SDavid Greenman }; 217ba8c6fd5SDavid Greenman #define NFXPMEDIA (sizeof(fxp_media) / sizeof(fxp_media[0])) 218ba8c6fd5SDavid Greenman 219ba8c6fd5SDavid Greenman static int fxp_mediachange __P((struct ifnet *)); 220ba8c6fd5SDavid Greenman static void fxp_mediastatus __P((struct ifnet *, struct ifmediareq *)); 221303b270bSEivind Eklund static void fxp_set_media __P((struct fxp_softc *, int)); 222c1087c13SBruce Evans static __inline void fxp_scb_wait __P((struct fxp_softc *)); 223ba8c6fd5SDavid Greenman static FXP_INTR_TYPE fxp_intr __P((void *)); 224a17c678eSDavid Greenman static void fxp_start __P((struct ifnet *)); 225ba8c6fd5SDavid Greenman static int fxp_ioctl __P((struct ifnet *, 226ba8c6fd5SDavid Greenman FXP_IOCTLCMD_TYPE, caddr_t)); 227fb583156SDavid Greenman static void fxp_init __P((void *)); 2284a5f1499SDavid Greenman static void fxp_stop __P((struct fxp_softc *)); 2294a5f1499SDavid Greenman static void fxp_watchdog __P((struct ifnet *)); 230a17c678eSDavid Greenman static int fxp_add_rfabuf __P((struct fxp_softc *, struct mbuf *)); 231ba8c6fd5SDavid Greenman static int fxp_mdi_read __P((struct fxp_softc *, int, int)); 232ba8c6fd5SDavid Greenman static void fxp_mdi_write __P((struct fxp_softc *, int, int, int)); 233ba8c6fd5SDavid Greenman static void fxp_read_eeprom __P((struct fxp_softc *, u_int16_t *, 234ba8c6fd5SDavid Greenman int, int)); 235ba8c6fd5SDavid Greenman static int fxp_attach_common __P((struct fxp_softc *, u_int8_t *)); 236303b270bSEivind Eklund static void fxp_stats_update __P((void *)); 237397f9dfeSDavid Greenman static void fxp_mc_setup __P((struct fxp_softc *)); 238a17c678eSDavid Greenman 239a17c678eSDavid Greenman /* 240f9be9005SDavid Greenman * Set initial transmit threshold at 64 (512 bytes). This is 241f9be9005SDavid Greenman * increased by 64 (512 bytes) at a time, to maximum of 192 242f9be9005SDavid Greenman * (1536 bytes), if an underrun occurs. 243f9be9005SDavid Greenman */ 244f9be9005SDavid Greenman static int tx_threshold = 64; 245f9be9005SDavid Greenman 246f9be9005SDavid Greenman /* 247a17c678eSDavid Greenman * Number of transmit control blocks. This determines the number 248a17c678eSDavid Greenman * of transmit buffers that can be chained in the CB list. 249a17c678eSDavid Greenman * This must be a power of two. 250a17c678eSDavid Greenman */ 2511cd443acSDavid Greenman #define FXP_NTXCB 128 252a17c678eSDavid Greenman 253a17c678eSDavid Greenman /* 2543114fdb4SDavid Greenman * Number of completed TX commands at which point an interrupt 2553114fdb4SDavid Greenman * will be generated to garbage collect the attached buffers. 2563114fdb4SDavid Greenman * Must be at least one less than FXP_NTXCB, and should be 2573114fdb4SDavid Greenman * enough less so that the transmitter doesn't becomes idle 2583114fdb4SDavid Greenman * during the buffer rundown (which would reduce performance). 2593114fdb4SDavid Greenman */ 2603114fdb4SDavid Greenman #define FXP_CXINT_THRESH 120 2613114fdb4SDavid Greenman 2623114fdb4SDavid Greenman /* 263a17c678eSDavid Greenman * TxCB list index mask. This is used to do list wrap-around. 264a17c678eSDavid Greenman */ 265a17c678eSDavid Greenman #define FXP_TXCB_MASK (FXP_NTXCB - 1) 266a17c678eSDavid Greenman 267a17c678eSDavid Greenman /* 268a17c678eSDavid Greenman * Number of receive frame area buffers. These are large so chose 269a17c678eSDavid Greenman * wisely. 270a17c678eSDavid Greenman */ 2716f5818b0SDavid Greenman #define FXP_NRFABUFS 64 272a17c678eSDavid Greenman 273dfe61cf1SDavid Greenman /* 274397f9dfeSDavid Greenman * Maximum number of seconds that the receiver can be idle before we 275397f9dfeSDavid Greenman * assume it's dead and attempt to reset it by reprogramming the 276397f9dfeSDavid Greenman * multicast filter. This is part of a work-around for a bug in the 277397f9dfeSDavid Greenman * NIC. See fxp_stats_update(). 278397f9dfeSDavid Greenman */ 279397f9dfeSDavid Greenman #define FXP_MAX_RX_IDLE 15 280397f9dfeSDavid Greenman 281397f9dfeSDavid Greenman /* 282dfe61cf1SDavid Greenman * Wait for the previous command to be accepted (but not necessarily 283dfe61cf1SDavid Greenman * completed). 284dfe61cf1SDavid Greenman */ 285c1087c13SBruce Evans static __inline void 286ba8c6fd5SDavid Greenman fxp_scb_wait(sc) 287ba8c6fd5SDavid Greenman struct fxp_softc *sc; 288a17c678eSDavid Greenman { 289a17c678eSDavid Greenman int i = 10000; 290a17c678eSDavid Greenman 291397f9dfeSDavid Greenman while (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) && --i); 292a17c678eSDavid Greenman } 293a17c678eSDavid Greenman 294ba8c6fd5SDavid Greenman /************************************************************* 295ba8c6fd5SDavid Greenman * Operating system-specific autoconfiguration glue 296ba8c6fd5SDavid Greenman *************************************************************/ 297ba8c6fd5SDavid Greenman 298ba8c6fd5SDavid Greenman #if defined(__NetBSD__) 299ba8c6fd5SDavid Greenman 300ba8c6fd5SDavid Greenman #ifdef __BROKEN_INDIRECT_CONFIG 301ba8c6fd5SDavid Greenman static int fxp_match __P((struct device *, void *, void *)); 302ba8c6fd5SDavid Greenman #else 303ba8c6fd5SDavid Greenman static int fxp_match __P((struct device *, struct cfdata *, void *)); 304ba8c6fd5SDavid Greenman #endif 305ba8c6fd5SDavid Greenman static void fxp_attach __P((struct device *, struct device *, void *)); 306ba8c6fd5SDavid Greenman 307ba8c6fd5SDavid Greenman static void fxp_shutdown __P((void *)); 308ba8c6fd5SDavid Greenman 309ba8c6fd5SDavid Greenman /* Compensate for lack of a generic ether_ioctl() */ 310ba8c6fd5SDavid Greenman static int fxp_ether_ioctl __P((struct ifnet *, 311ba8c6fd5SDavid Greenman FXP_IOCTLCMD_TYPE, caddr_t)); 312ba8c6fd5SDavid Greenman #define ether_ioctl fxp_ether_ioctl 313ba8c6fd5SDavid Greenman 314ba8c6fd5SDavid Greenman struct cfattach fxp_ca = { 315ba8c6fd5SDavid Greenman sizeof(struct fxp_softc), fxp_match, fxp_attach 316ba8c6fd5SDavid Greenman }; 317ba8c6fd5SDavid Greenman 318ba8c6fd5SDavid Greenman struct cfdriver fxp_cd = { 319ba8c6fd5SDavid Greenman NULL, "fxp", DV_IFNET 320ba8c6fd5SDavid Greenman }; 321ba8c6fd5SDavid Greenman 322ba8c6fd5SDavid Greenman /* 323ba8c6fd5SDavid Greenman * Check if a device is an 82557. 324ba8c6fd5SDavid Greenman */ 325ba8c6fd5SDavid Greenman static int 326ba8c6fd5SDavid Greenman fxp_match(parent, match, aux) 327ba8c6fd5SDavid Greenman struct device *parent; 328ba8c6fd5SDavid Greenman #ifdef __BROKEN_INDIRECT_CONFIG 329ba8c6fd5SDavid Greenman void *match; 330ba8c6fd5SDavid Greenman #else 331ba8c6fd5SDavid Greenman struct cfdata *match; 332ba8c6fd5SDavid Greenman #endif 333ba8c6fd5SDavid Greenman void *aux; 334ba8c6fd5SDavid Greenman { 335ba8c6fd5SDavid Greenman struct pci_attach_args *pa = aux; 336ba8c6fd5SDavid Greenman 337ba8c6fd5SDavid Greenman if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL) 338ba8c6fd5SDavid Greenman return (0); 339ba8c6fd5SDavid Greenman 340ba8c6fd5SDavid Greenman switch (PCI_PRODUCT(pa->pa_id)) { 341ba8c6fd5SDavid Greenman case PCI_PRODUCT_INTEL_82557: 342ba8c6fd5SDavid Greenman return (1); 343ba8c6fd5SDavid Greenman } 344ba8c6fd5SDavid Greenman 345ba8c6fd5SDavid Greenman return (0); 346ba8c6fd5SDavid Greenman } 347ba8c6fd5SDavid Greenman 348ba8c6fd5SDavid Greenman static void 349ba8c6fd5SDavid Greenman fxp_attach(parent, self, aux) 350ba8c6fd5SDavid Greenman struct device *parent, *self; 351ba8c6fd5SDavid Greenman void *aux; 352ba8c6fd5SDavid Greenman { 353ba8c6fd5SDavid Greenman struct fxp_softc *sc = (struct fxp_softc *)self; 354ba8c6fd5SDavid Greenman struct pci_attach_args *pa = aux; 355ba8c6fd5SDavid Greenman pci_chipset_tag_t pc = pa->pa_pc; 356ba8c6fd5SDavid Greenman pci_intr_handle_t ih; 357ba8c6fd5SDavid Greenman const char *intrstr = NULL; 358ba8c6fd5SDavid Greenman u_int8_t enaddr[6]; 359ba8c6fd5SDavid Greenman struct ifnet *ifp; 360ba8c6fd5SDavid Greenman 361ba8c6fd5SDavid Greenman /* 362ba8c6fd5SDavid Greenman * Map control/status registers. 363ba8c6fd5SDavid Greenman */ 364ba8c6fd5SDavid Greenman if (pci_mapreg_map(pa, FXP_PCI_MMBA, PCI_MAPREG_TYPE_MEM, 0, 365ba8c6fd5SDavid Greenman &sc->sc_st, &sc->sc_sh, NULL, NULL)) { 366ba8c6fd5SDavid Greenman printf(": can't map registers\n"); 367ba8c6fd5SDavid Greenman return; 368ba8c6fd5SDavid Greenman } 369ba8c6fd5SDavid Greenman printf(": Intel EtherExpress Pro 10/100B Ethernet\n"); 370ba8c6fd5SDavid Greenman 371ba8c6fd5SDavid Greenman /* 372ba8c6fd5SDavid Greenman * Allocate our interrupt. 373ba8c6fd5SDavid Greenman */ 374ba8c6fd5SDavid Greenman if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin, 375ba8c6fd5SDavid Greenman pa->pa_intrline, &ih)) { 376ba8c6fd5SDavid Greenman printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname); 377ba8c6fd5SDavid Greenman return; 378ba8c6fd5SDavid Greenman } 379ba8c6fd5SDavid Greenman intrstr = pci_intr_string(pc, ih); 380ba8c6fd5SDavid Greenman sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, fxp_intr, sc); 381ba8c6fd5SDavid Greenman if (sc->sc_ih == NULL) { 382ba8c6fd5SDavid Greenman printf("%s: couldn't establish interrupt", 383ba8c6fd5SDavid Greenman sc->sc_dev.dv_xname); 384ba8c6fd5SDavid Greenman if (intrstr != NULL) 385ba8c6fd5SDavid Greenman printf(" at %s", intrstr); 386ba8c6fd5SDavid Greenman printf("\n"); 387ba8c6fd5SDavid Greenman return; 388ba8c6fd5SDavid Greenman } 389ba8c6fd5SDavid Greenman printf("%s: interrupting at %s\n", sc->sc_dev.dv_xname, intrstr); 390ba8c6fd5SDavid Greenman 391ba8c6fd5SDavid Greenman /* Do generic parts of attach. */ 392ba8c6fd5SDavid Greenman if (fxp_attach_common(sc, enaddr)) { 393ba8c6fd5SDavid Greenman /* Failed! */ 394ba8c6fd5SDavid Greenman return; 395ba8c6fd5SDavid Greenman } 396ba8c6fd5SDavid Greenman 397ba8c6fd5SDavid Greenman printf("%s: Ethernet address %s%s\n", sc->sc_dev.dv_xname, 398ba8c6fd5SDavid Greenman ether_sprintf(enaddr), sc->phy_10Mbps_only ? ", 10Mbps" : ""); 399ba8c6fd5SDavid Greenman 400ba8c6fd5SDavid Greenman ifp = &sc->sc_ethercom.ec_if; 401ba8c6fd5SDavid Greenman bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); 402ba8c6fd5SDavid Greenman ifp->if_softc = sc; 403ba8c6fd5SDavid Greenman ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 404ba8c6fd5SDavid Greenman ifp->if_ioctl = fxp_ioctl; 405ba8c6fd5SDavid Greenman ifp->if_start = fxp_start; 406ba8c6fd5SDavid Greenman ifp->if_watchdog = fxp_watchdog; 407ba8c6fd5SDavid Greenman 408ba8c6fd5SDavid Greenman /* 409ba8c6fd5SDavid Greenman * Attach the interface. 410ba8c6fd5SDavid Greenman */ 411ba8c6fd5SDavid Greenman if_attach(ifp); 412483b9871SDavid Greenman /* 4133114fdb4SDavid Greenman * Let the system queue as many packets as we have available 4143114fdb4SDavid Greenman * TX descriptors. 415483b9871SDavid Greenman */ 4163114fdb4SDavid Greenman ifp->if_snd.ifq_maxlen = FXP_NTXCB - 1; 417ba8c6fd5SDavid Greenman ether_ifattach(ifp, enaddr); 418ba8c6fd5SDavid Greenman #if NBPFILTER > 0 419ba8c6fd5SDavid Greenman bpfattach(&sc->sc_ethercom.ec_if.if_bpf, ifp, DLT_EN10MB, 420ba8c6fd5SDavid Greenman sizeof(struct ether_header)); 421ba8c6fd5SDavid Greenman #endif 422ba8c6fd5SDavid Greenman 423ba8c6fd5SDavid Greenman /* 424ba8c6fd5SDavid Greenman * Add shutdown hook so that DMA is disabled prior to reboot. Not 425ba8c6fd5SDavid Greenman * doing do could allow DMA to corrupt kernel memory during the 426ba8c6fd5SDavid Greenman * reboot before the driver initializes. 427ba8c6fd5SDavid Greenman */ 428ba8c6fd5SDavid Greenman shutdownhook_establish(fxp_shutdown, sc); 429ba8c6fd5SDavid Greenman } 430ba8c6fd5SDavid Greenman 431ba8c6fd5SDavid Greenman /* 432ba8c6fd5SDavid Greenman * Device shutdown routine. Called at system shutdown after sync. The 433ba8c6fd5SDavid Greenman * main purpose of this routine is to shut off receiver DMA so that 434ba8c6fd5SDavid Greenman * kernel memory doesn't get clobbered during warmboot. 435ba8c6fd5SDavid Greenman */ 436ba8c6fd5SDavid Greenman static void 437ba8c6fd5SDavid Greenman fxp_shutdown(sc) 438ba8c6fd5SDavid Greenman void *sc; 439ba8c6fd5SDavid Greenman { 440ba8c6fd5SDavid Greenman fxp_stop((struct fxp_softc *) sc); 441ba8c6fd5SDavid Greenman } 442ba8c6fd5SDavid Greenman 443ba8c6fd5SDavid Greenman static int 444ba8c6fd5SDavid Greenman fxp_ether_ioctl(ifp, cmd, data) 445ba8c6fd5SDavid Greenman struct ifnet *ifp; 446ba8c6fd5SDavid Greenman FXP_IOCTLCMD_TYPE cmd; 447ba8c6fd5SDavid Greenman caddr_t data; 448ba8c6fd5SDavid Greenman { 449ba8c6fd5SDavid Greenman struct ifaddr *ifa = (struct ifaddr *) data; 450ba8c6fd5SDavid Greenman struct fxp_softc *sc = ifp->if_softc; 451ba8c6fd5SDavid Greenman 452ba8c6fd5SDavid Greenman switch (cmd) { 453ba8c6fd5SDavid Greenman case SIOCSIFADDR: 454ba8c6fd5SDavid Greenman ifp->if_flags |= IFF_UP; 455ba8c6fd5SDavid Greenman 456ba8c6fd5SDavid Greenman switch (ifa->ifa_addr->sa_family) { 457ba8c6fd5SDavid Greenman #ifdef INET 458ba8c6fd5SDavid Greenman case AF_INET: 459ba8c6fd5SDavid Greenman fxp_init(sc); 460ba8c6fd5SDavid Greenman arp_ifinit(ifp, ifa); 461ba8c6fd5SDavid Greenman break; 462ba8c6fd5SDavid Greenman #endif 463ba8c6fd5SDavid Greenman #ifdef NS 464ba8c6fd5SDavid Greenman case AF_NS: 465ba8c6fd5SDavid Greenman { 466ba8c6fd5SDavid Greenman register struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; 467ba8c6fd5SDavid Greenman 468ba8c6fd5SDavid Greenman if (ns_nullhost(*ina)) 469ba8c6fd5SDavid Greenman ina->x_host = *(union ns_host *) 470ba8c6fd5SDavid Greenman LLADDR(ifp->if_sadl); 471ba8c6fd5SDavid Greenman else 472ba8c6fd5SDavid Greenman bcopy(ina->x_host.c_host, LLADDR(ifp->if_sadl), 473ba8c6fd5SDavid Greenman ifp->if_addrlen); 474ba8c6fd5SDavid Greenman /* Set new address. */ 475ba8c6fd5SDavid Greenman fxp_init(sc); 476ba8c6fd5SDavid Greenman break; 477ba8c6fd5SDavid Greenman } 478ba8c6fd5SDavid Greenman #endif 479ba8c6fd5SDavid Greenman default: 480ba8c6fd5SDavid Greenman fxp_init(sc); 481ba8c6fd5SDavid Greenman break; 482ba8c6fd5SDavid Greenman } 483ba8c6fd5SDavid Greenman break; 484ba8c6fd5SDavid Greenman 485ba8c6fd5SDavid Greenman default: 486ba8c6fd5SDavid Greenman return (EINVAL); 487ba8c6fd5SDavid Greenman } 488ba8c6fd5SDavid Greenman 489ba8c6fd5SDavid Greenman return (0); 490ba8c6fd5SDavid Greenman } 491ba8c6fd5SDavid Greenman 492ba8c6fd5SDavid Greenman #else /* __FreeBSD__ */ 493ba8c6fd5SDavid Greenman 494ba8c6fd5SDavid Greenman static u_long fxp_count; 49535f069d0SMatthew Dillon static const char *fxp_probe __P((pcici_t, pcidi_t)); 496ba8c6fd5SDavid Greenman static void fxp_attach __P((pcici_t, int)); 497ba8c6fd5SDavid Greenman 498ba8c6fd5SDavid Greenman static void fxp_shutdown __P((int, void *)); 499ba8c6fd5SDavid Greenman 500ba8c6fd5SDavid Greenman static struct pci_device fxp_device = { 501ba8c6fd5SDavid Greenman "fxp", 502ba8c6fd5SDavid Greenman fxp_probe, 503ba8c6fd5SDavid Greenman fxp_attach, 504ba8c6fd5SDavid Greenman &fxp_count, 505ba8c6fd5SDavid Greenman NULL 506ba8c6fd5SDavid Greenman }; 507ba8c6fd5SDavid Greenman DATA_SET(pcidevice_set, fxp_device); 508ba8c6fd5SDavid Greenman 509dfe61cf1SDavid Greenman /* 510dfe61cf1SDavid Greenman * Return identification string if this is device is ours. 511dfe61cf1SDavid Greenman */ 51235f069d0SMatthew Dillon static const char * 513a17c678eSDavid Greenman fxp_probe(config_id, device_id) 514a17c678eSDavid Greenman pcici_t config_id; 515a17c678eSDavid Greenman pcidi_t device_id; 516a17c678eSDavid Greenman { 517a17c678eSDavid Greenman if (((device_id & 0xffff) == FXP_VENDORID_INTEL) && 518a17c678eSDavid Greenman ((device_id >> 16) & 0xffff) == FXP_DEVICEID_i82557) 519dccee1a1SDavid Greenman return ("Intel EtherExpress Pro 10/100B Ethernet"); 520a17c678eSDavid Greenman 521a17c678eSDavid Greenman return NULL; 522a17c678eSDavid Greenman } 523a17c678eSDavid Greenman 524a17c678eSDavid Greenman static void 525a17c678eSDavid Greenman fxp_attach(config_id, unit) 526a17c678eSDavid Greenman pcici_t config_id; 527a17c678eSDavid Greenman int unit; 528a17c678eSDavid Greenman { 529a17c678eSDavid Greenman struct fxp_softc *sc; 530a17c678eSDavid Greenman vm_offset_t pbase; 531ba8c6fd5SDavid Greenman struct ifnet *ifp; 532ba8c6fd5SDavid Greenman int s; 533df373873SWes Peters u_long val; 534a17c678eSDavid Greenman 535a17c678eSDavid Greenman sc = malloc(sizeof(struct fxp_softc), M_DEVBUF, M_NOWAIT); 536a17c678eSDavid Greenman if (sc == NULL) 537a17c678eSDavid Greenman return; 538a17c678eSDavid Greenman bzero(sc, sizeof(struct fxp_softc)); 5396c951b44SJustin T. Gibbs callout_handle_init(&sc->stat_ch); 540a17c678eSDavid Greenman 541a17c678eSDavid Greenman s = splimp(); 542a17c678eSDavid Greenman 543dfe61cf1SDavid Greenman /* 544df373873SWes Peters * Enable bus mastering. 545df373873SWes Peters */ 546df373873SWes Peters val = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG); 547df373873SWes Peters val |= (PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 548df373873SWes Peters pci_conf_write(config_id, PCI_COMMAND_STATUS_REG, val); 549df373873SWes Peters 550df373873SWes Peters /* 551dfe61cf1SDavid Greenman * Map control/status registers. 552dfe61cf1SDavid Greenman */ 553a17c678eSDavid Greenman if (!pci_map_mem(config_id, FXP_PCI_MMBA, 554a17c678eSDavid Greenman (vm_offset_t *)&sc->csr, &pbase)) { 555a17c678eSDavid Greenman printf("fxp%d: couldn't map memory\n", unit); 556a17c678eSDavid Greenman goto fail; 557a17c678eSDavid Greenman } 558a17c678eSDavid Greenman 559a17c678eSDavid Greenman /* 560dfe61cf1SDavid Greenman * Allocate our interrupt. 561dfe61cf1SDavid Greenman */ 562a17c678eSDavid Greenman if (!pci_map_int(config_id, fxp_intr, sc, &net_imask)) { 563a17c678eSDavid Greenman printf("fxp%d: couldn't map interrupt\n", unit); 564a17c678eSDavid Greenman goto fail; 565a17c678eSDavid Greenman } 566a17c678eSDavid Greenman 567ba8c6fd5SDavid Greenman /* Do generic parts of attach. */ 568ba8c6fd5SDavid Greenman if (fxp_attach_common(sc, sc->arpcom.ac_enaddr)) { 569ba8c6fd5SDavid Greenman /* Failed! */ 570ba8c6fd5SDavid Greenman (void) pci_unmap_int(config_id); 571ba8c6fd5SDavid Greenman goto fail; 572a17c678eSDavid Greenman } 573a17c678eSDavid Greenman 574ba8c6fd5SDavid Greenman printf("fxp%d: Ethernet address %6D%s\n", unit, 575ba8c6fd5SDavid Greenman sc->arpcom.ac_enaddr, ":", sc->phy_10Mbps_only ? ", 10Mbps" : ""); 576dccee1a1SDavid Greenman 577a17c678eSDavid Greenman ifp = &sc->arpcom.ac_if; 578a17c678eSDavid Greenman ifp->if_unit = unit; 579a17c678eSDavid Greenman ifp->if_name = "fxp"; 580a17c678eSDavid Greenman ifp->if_output = ether_output; 581a330e1f1SGary Palmer ifp->if_baudrate = 100000000; 582fb583156SDavid Greenman ifp->if_init = fxp_init; 583ba8c6fd5SDavid Greenman ifp->if_softc = sc; 584ba8c6fd5SDavid Greenman ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 585ba8c6fd5SDavid Greenman ifp->if_ioctl = fxp_ioctl; 586ba8c6fd5SDavid Greenman ifp->if_start = fxp_start; 587ba8c6fd5SDavid Greenman ifp->if_watchdog = fxp_watchdog; 588a17c678eSDavid Greenman 589dfe61cf1SDavid Greenman /* 590dfe61cf1SDavid Greenman * Attach the interface. 591dfe61cf1SDavid Greenman */ 592a17c678eSDavid Greenman if_attach(ifp); 593483b9871SDavid Greenman /* 5943114fdb4SDavid Greenman * Let the system queue as many packets as we have available 5953114fdb4SDavid Greenman * TX descriptors. 596483b9871SDavid Greenman */ 5973114fdb4SDavid Greenman ifp->if_snd.ifq_maxlen = FXP_NTXCB - 1; 5989b44ff22SGarrett Wollman ether_ifattach(ifp); 599a17c678eSDavid Greenman #if NBPFILTER > 0 6009b44ff22SGarrett Wollman bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 601a17c678eSDavid Greenman #endif 6024a684684SDavid Greenman 6034a684684SDavid Greenman /* 6044a684684SDavid Greenman * Add shutdown hook so that DMA is disabled prior to reboot. Not 6054a684684SDavid Greenman * doing do could allow DMA to corrupt kernel memory during the 6064a684684SDavid Greenman * reboot before the driver initializes. 6074a684684SDavid Greenman */ 6084a684684SDavid Greenman at_shutdown(fxp_shutdown, sc, SHUTDOWN_POST_SYNC); 6094a684684SDavid Greenman 610a17c678eSDavid Greenman splx(s); 611a17c678eSDavid Greenman return; 612a17c678eSDavid Greenman 613a17c678eSDavid Greenman fail: 614a17c678eSDavid Greenman free(sc, M_DEVBUF); 615a17c678eSDavid Greenman splx(s); 616a17c678eSDavid Greenman } 617a17c678eSDavid Greenman 618a17c678eSDavid Greenman /* 6194a684684SDavid Greenman * Device shutdown routine. Called at system shutdown after sync. The 620a17c678eSDavid Greenman * main purpose of this routine is to shut off receiver DMA so that 621a17c678eSDavid Greenman * kernel memory doesn't get clobbered during warmboot. 622a17c678eSDavid Greenman */ 6234a684684SDavid Greenman static void 6244a684684SDavid Greenman fxp_shutdown(howto, sc) 6254a684684SDavid Greenman int howto; 6264a684684SDavid Greenman void *sc; 627a17c678eSDavid Greenman { 6284a684684SDavid Greenman fxp_stop((struct fxp_softc *) sc); 629a17c678eSDavid Greenman } 630a17c678eSDavid Greenman 631ba8c6fd5SDavid Greenman #endif /* __NetBSD__ */ 632ba8c6fd5SDavid Greenman 633ba8c6fd5SDavid Greenman /************************************************************* 634ba8c6fd5SDavid Greenman * End of operating system-specific autoconfiguration glue 635ba8c6fd5SDavid Greenman *************************************************************/ 636ba8c6fd5SDavid Greenman 637ba8c6fd5SDavid Greenman /* 638ba8c6fd5SDavid Greenman * Do generic parts of attach. 639ba8c6fd5SDavid Greenman */ 640ba8c6fd5SDavid Greenman static int 641ba8c6fd5SDavid Greenman fxp_attach_common(sc, enaddr) 642ba8c6fd5SDavid Greenman struct fxp_softc *sc; 643ba8c6fd5SDavid Greenman u_int8_t *enaddr; 644ba8c6fd5SDavid Greenman { 645ba8c6fd5SDavid Greenman u_int16_t data; 646ba8c6fd5SDavid Greenman int i, nmedia, defmedia; 647ba8c6fd5SDavid Greenman const int *media; 648ba8c6fd5SDavid Greenman 649ba8c6fd5SDavid Greenman /* 650ba8c6fd5SDavid Greenman * Reset to a stable state. 651ba8c6fd5SDavid Greenman */ 652ba8c6fd5SDavid Greenman CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET); 653ba8c6fd5SDavid Greenman DELAY(10); 654ba8c6fd5SDavid Greenman 655ba8c6fd5SDavid Greenman sc->cbl_base = malloc(sizeof(struct fxp_cb_tx) * FXP_NTXCB, 656ba8c6fd5SDavid Greenman M_DEVBUF, M_NOWAIT); 657ba8c6fd5SDavid Greenman if (sc->cbl_base == NULL) 658ba8c6fd5SDavid Greenman goto fail; 65991aa9f90SDavid Greenman bzero(sc->cbl_base, sizeof(struct fxp_cb_tx) * FXP_NTXCB); 660ba8c6fd5SDavid Greenman 661ba8c6fd5SDavid Greenman sc->fxp_stats = malloc(sizeof(struct fxp_stats), M_DEVBUF, M_NOWAIT); 662ba8c6fd5SDavid Greenman if (sc->fxp_stats == NULL) 663ba8c6fd5SDavid Greenman goto fail; 664ba8c6fd5SDavid Greenman bzero(sc->fxp_stats, sizeof(struct fxp_stats)); 665ba8c6fd5SDavid Greenman 666397f9dfeSDavid Greenman sc->mcsp = malloc(sizeof(struct fxp_cb_mcs), M_DEVBUF, M_NOWAIT); 667397f9dfeSDavid Greenman if (sc->mcsp == NULL) 668397f9dfeSDavid Greenman goto fail; 669397f9dfeSDavid Greenman 670ba8c6fd5SDavid Greenman /* 671ba8c6fd5SDavid Greenman * Pre-allocate our receive buffers. 672ba8c6fd5SDavid Greenman */ 673ba8c6fd5SDavid Greenman for (i = 0; i < FXP_NRFABUFS; i++) { 674ba8c6fd5SDavid Greenman if (fxp_add_rfabuf(sc, NULL) != 0) { 675ba8c6fd5SDavid Greenman goto fail; 676ba8c6fd5SDavid Greenman } 677ba8c6fd5SDavid Greenman } 678ba8c6fd5SDavid Greenman 679ba8c6fd5SDavid Greenman /* 680ba8c6fd5SDavid Greenman * Get info about the primary PHY 681ba8c6fd5SDavid Greenman */ 682ba8c6fd5SDavid Greenman fxp_read_eeprom(sc, (u_int16_t *)&data, 6, 1); 683ba8c6fd5SDavid Greenman sc->phy_primary_addr = data & 0xff; 684ba8c6fd5SDavid Greenman sc->phy_primary_device = (data >> 8) & 0x3f; 685ba8c6fd5SDavid Greenman sc->phy_10Mbps_only = data >> 15; 686ba8c6fd5SDavid Greenman 687ba8c6fd5SDavid Greenman /* 688ba8c6fd5SDavid Greenman * Read MAC address. 689ba8c6fd5SDavid Greenman */ 690ba8c6fd5SDavid Greenman fxp_read_eeprom(sc, (u_int16_t *)enaddr, 0, 3); 691ba8c6fd5SDavid Greenman 692ba8c6fd5SDavid Greenman /* 693ba8c6fd5SDavid Greenman * Initialize the media structures. 694ba8c6fd5SDavid Greenman */ 695ba8c6fd5SDavid Greenman 696ba8c6fd5SDavid Greenman media = fxp_media_default; 697ba8c6fd5SDavid Greenman nmedia = sizeof(fxp_media_default) / sizeof(fxp_media_default[0]); 698ba8c6fd5SDavid Greenman defmedia = FXP_MEDIA_DEFAULT_DEFMEDIA; 699ba8c6fd5SDavid Greenman 700ba8c6fd5SDavid Greenman for (i = 0; i < NFXPMEDIA; i++) { 701ba8c6fd5SDavid Greenman if (sc->phy_primary_device == fxp_media[i].fsm_phy) { 702ba8c6fd5SDavid Greenman media = fxp_media[i].fsm_media; 703ba8c6fd5SDavid Greenman nmedia = fxp_media[i].fsm_nmedia; 704ba8c6fd5SDavid Greenman defmedia = fxp_media[i].fsm_defmedia; 705ba8c6fd5SDavid Greenman } 706ba8c6fd5SDavid Greenman } 707ba8c6fd5SDavid Greenman 708ba8c6fd5SDavid Greenman ifmedia_init(&sc->sc_media, 0, fxp_mediachange, fxp_mediastatus); 709ba8c6fd5SDavid Greenman for (i = 0; i < nmedia; i++) { 710ba8c6fd5SDavid Greenman if (IFM_SUBTYPE(media[i]) == IFM_100_TX && sc->phy_10Mbps_only) 711ba8c6fd5SDavid Greenman continue; 712ba8c6fd5SDavid Greenman ifmedia_add(&sc->sc_media, media[i], 0, NULL); 713ba8c6fd5SDavid Greenman } 714ba8c6fd5SDavid Greenman ifmedia_set(&sc->sc_media, defmedia); 715ba8c6fd5SDavid Greenman 716ba8c6fd5SDavid Greenman return (0); 717ba8c6fd5SDavid Greenman 718ba8c6fd5SDavid Greenman fail: 719ba8c6fd5SDavid Greenman printf(FXP_FORMAT ": Failed to malloc memory\n", FXP_ARGS(sc)); 720ba8c6fd5SDavid Greenman if (sc->cbl_base) 721ba8c6fd5SDavid Greenman free(sc->cbl_base, M_DEVBUF); 722ba8c6fd5SDavid Greenman if (sc->fxp_stats) 723ba8c6fd5SDavid Greenman free(sc->fxp_stats, M_DEVBUF); 724397f9dfeSDavid Greenman if (sc->mcsp) 725397f9dfeSDavid Greenman free(sc->mcsp, M_DEVBUF); 726ba8c6fd5SDavid Greenman /* frees entire chain */ 727ba8c6fd5SDavid Greenman if (sc->rfa_headm) 728ba8c6fd5SDavid Greenman m_freem(sc->rfa_headm); 729ba8c6fd5SDavid Greenman 730ba8c6fd5SDavid Greenman return (ENOMEM); 731ba8c6fd5SDavid Greenman } 732ba8c6fd5SDavid Greenman 733ba8c6fd5SDavid Greenman /* 734ba8c6fd5SDavid Greenman * Read from the serial EEPROM. Basically, you manually shift in 735ba8c6fd5SDavid Greenman * the read opcode (one bit at a time) and then shift in the address, 736ba8c6fd5SDavid Greenman * and then you shift out the data (all of this one bit at a time). 737ba8c6fd5SDavid Greenman * The word size is 16 bits, so you have to provide the address for 738ba8c6fd5SDavid Greenman * every 16 bits of data. 739ba8c6fd5SDavid Greenman */ 740ba8c6fd5SDavid Greenman static void 741ba8c6fd5SDavid Greenman fxp_read_eeprom(sc, data, offset, words) 742ba8c6fd5SDavid Greenman struct fxp_softc *sc; 743ba8c6fd5SDavid Greenman u_short *data; 744ba8c6fd5SDavid Greenman int offset; 745ba8c6fd5SDavid Greenman int words; 746ba8c6fd5SDavid Greenman { 747ba8c6fd5SDavid Greenman u_int16_t reg; 748ba8c6fd5SDavid Greenman int i, x; 749ba8c6fd5SDavid Greenman 750ba8c6fd5SDavid Greenman for (i = 0; i < words; i++) { 751ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); 752ba8c6fd5SDavid Greenman /* 753ba8c6fd5SDavid Greenman * Shift in read opcode. 754ba8c6fd5SDavid Greenman */ 755ba8c6fd5SDavid Greenman for (x = 3; x > 0; x--) { 756ba8c6fd5SDavid Greenman if (FXP_EEPROM_OPC_READ & (1 << (x - 1))) { 757ba8c6fd5SDavid Greenman reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; 758ba8c6fd5SDavid Greenman } else { 759ba8c6fd5SDavid Greenman reg = FXP_EEPROM_EECS; 760ba8c6fd5SDavid Greenman } 761ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); 762ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 763ba8c6fd5SDavid Greenman reg | FXP_EEPROM_EESK); 764ba8c6fd5SDavid Greenman DELAY(1); 765ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); 766ba8c6fd5SDavid Greenman DELAY(1); 767ba8c6fd5SDavid Greenman } 768ba8c6fd5SDavid Greenman /* 769ba8c6fd5SDavid Greenman * Shift in address. 770ba8c6fd5SDavid Greenman */ 771ba8c6fd5SDavid Greenman for (x = 6; x > 0; x--) { 772ba8c6fd5SDavid Greenman if ((i + offset) & (1 << (x - 1))) { 773ba8c6fd5SDavid Greenman reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; 774ba8c6fd5SDavid Greenman } else { 775ba8c6fd5SDavid Greenman reg = FXP_EEPROM_EECS; 776ba8c6fd5SDavid Greenman } 777ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); 778ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 779ba8c6fd5SDavid Greenman reg | FXP_EEPROM_EESK); 780ba8c6fd5SDavid Greenman DELAY(1); 781ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); 782ba8c6fd5SDavid Greenman DELAY(1); 783ba8c6fd5SDavid Greenman } 784ba8c6fd5SDavid Greenman reg = FXP_EEPROM_EECS; 785ba8c6fd5SDavid Greenman data[i] = 0; 786ba8c6fd5SDavid Greenman /* 787ba8c6fd5SDavid Greenman * Shift out data. 788ba8c6fd5SDavid Greenman */ 789ba8c6fd5SDavid Greenman for (x = 16; x > 0; x--) { 790ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 791ba8c6fd5SDavid Greenman reg | FXP_EEPROM_EESK); 792ba8c6fd5SDavid Greenman DELAY(1); 793ba8c6fd5SDavid Greenman if (CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & 794ba8c6fd5SDavid Greenman FXP_EEPROM_EEDO) 795ba8c6fd5SDavid Greenman data[i] |= (1 << (x - 1)); 796ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); 797ba8c6fd5SDavid Greenman DELAY(1); 798ba8c6fd5SDavid Greenman } 799ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0); 800ba8c6fd5SDavid Greenman DELAY(1); 801ba8c6fd5SDavid Greenman } 802ba8c6fd5SDavid Greenman } 803ba8c6fd5SDavid Greenman 804a17c678eSDavid Greenman /* 805a17c678eSDavid Greenman * Start packet transmission on the interface. 806a17c678eSDavid Greenman */ 807a17c678eSDavid Greenman static void 808a17c678eSDavid Greenman fxp_start(ifp) 809a17c678eSDavid Greenman struct ifnet *ifp; 810a17c678eSDavid Greenman { 8119b44ff22SGarrett Wollman struct fxp_softc *sc = ifp->if_softc; 812a17c678eSDavid Greenman struct fxp_cb_tx *txp; 813a17c678eSDavid Greenman 814a17c678eSDavid Greenman /* 815483b9871SDavid Greenman * See if we need to suspend xmit until the multicast filter 816483b9871SDavid Greenman * has been reprogrammed (which can only be done at the head 817483b9871SDavid Greenman * of the command chain). 818a17c678eSDavid Greenman */ 819483b9871SDavid Greenman if (sc->need_mcsetup) 820a17c678eSDavid Greenman return; 8211cd443acSDavid Greenman 822483b9871SDavid Greenman txp = NULL; 823483b9871SDavid Greenman 824483b9871SDavid Greenman /* 825483b9871SDavid Greenman * We're finished if there is nothing more to add to the list or if 826483b9871SDavid Greenman * we're all filled up with buffers to transmit. 8273114fdb4SDavid Greenman * NOTE: One TxCB is reserved to guarantee that fxp_mc_setup() can add 8283114fdb4SDavid Greenman * a NOP command when needed. 829483b9871SDavid Greenman */ 8303114fdb4SDavid Greenman while (ifp->if_snd.ifq_head != NULL && sc->tx_queued < FXP_NTXCB - 1) { 831483b9871SDavid Greenman struct mbuf *m, *mb_head; 832483b9871SDavid Greenman int segment; 833483b9871SDavid Greenman 834dfe61cf1SDavid Greenman /* 835dfe61cf1SDavid Greenman * Grab a packet to transmit. 836dfe61cf1SDavid Greenman */ 8376318197eSDavid Greenman IF_DEQUEUE(&ifp->if_snd, mb_head); 838a17c678eSDavid Greenman 839dfe61cf1SDavid Greenman /* 840483b9871SDavid Greenman * Get pointer to next available tx desc. 841dfe61cf1SDavid Greenman */ 842a17c678eSDavid Greenman txp = sc->cbl_last->next; 843a17c678eSDavid Greenman 844a17c678eSDavid Greenman /* 845a17c678eSDavid Greenman * Go through each of the mbufs in the chain and initialize 846483b9871SDavid Greenman * the transmit buffer descriptors with the physical address 847a17c678eSDavid Greenman * and size of the mbuf. 848a17c678eSDavid Greenman */ 84923a0ed7cSDavid Greenman tbdinit: 850a17c678eSDavid Greenman for (m = mb_head, segment = 0; m != NULL; m = m->m_next) { 851a17c678eSDavid Greenman if (m->m_len != 0) { 852a17c678eSDavid Greenman if (segment == FXP_NTXSEG) 853a17c678eSDavid Greenman break; 854a17c678eSDavid Greenman txp->tbd[segment].tb_addr = 855a17c678eSDavid Greenman vtophys(mtod(m, vm_offset_t)); 856a17c678eSDavid Greenman txp->tbd[segment].tb_size = m->m_len; 857a17c678eSDavid Greenman segment++; 858a17c678eSDavid Greenman } 859a17c678eSDavid Greenman } 860fb583156SDavid Greenman if (m != NULL) { 86123a0ed7cSDavid Greenman struct mbuf *mn; 86223a0ed7cSDavid Greenman 863a17c678eSDavid Greenman /* 864a17c678eSDavid Greenman * We ran out of segments. We have to recopy this mbuf 865483b9871SDavid Greenman * chain first. Bail out if we can't get the new buffers. 866a17c678eSDavid Greenman */ 86723a0ed7cSDavid Greenman MGETHDR(mn, M_DONTWAIT, MT_DATA); 86823a0ed7cSDavid Greenman if (mn == NULL) { 86923a0ed7cSDavid Greenman m_freem(mb_head); 870483b9871SDavid Greenman break; 871a17c678eSDavid Greenman } 87223a0ed7cSDavid Greenman if (mb_head->m_pkthdr.len > MHLEN) { 87323a0ed7cSDavid Greenman MCLGET(mn, M_DONTWAIT); 87423a0ed7cSDavid Greenman if ((mn->m_flags & M_EXT) == 0) { 87523a0ed7cSDavid Greenman m_freem(mn); 87623a0ed7cSDavid Greenman m_freem(mb_head); 877483b9871SDavid Greenman break; 87823a0ed7cSDavid Greenman } 87923a0ed7cSDavid Greenman } 880ba8c6fd5SDavid Greenman m_copydata(mb_head, 0, mb_head->m_pkthdr.len, 881ba8c6fd5SDavid Greenman mtod(mn, caddr_t)); 88223a0ed7cSDavid Greenman mn->m_pkthdr.len = mn->m_len = mb_head->m_pkthdr.len; 88323a0ed7cSDavid Greenman m_freem(mb_head); 88423a0ed7cSDavid Greenman mb_head = mn; 88523a0ed7cSDavid Greenman goto tbdinit; 88623a0ed7cSDavid Greenman } 88723a0ed7cSDavid Greenman 88823a0ed7cSDavid Greenman txp->tbd_number = segment; 8891cd443acSDavid Greenman txp->mb_head = mb_head; 890a17c678eSDavid Greenman txp->cb_status = 0; 8913114fdb4SDavid Greenman if (sc->tx_queued != FXP_CXINT_THRESH - 1) { 892a17c678eSDavid Greenman txp->cb_command = 893a17c678eSDavid Greenman FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S; 8943114fdb4SDavid Greenman } else { 8953114fdb4SDavid Greenman txp->cb_command = 8963114fdb4SDavid Greenman FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I; 8973114fdb4SDavid Greenman /* 8983114fdb4SDavid Greenman * Set a 5 second timer just in case we don't hear from the 8993114fdb4SDavid Greenman * card again. 9003114fdb4SDavid Greenman */ 9013114fdb4SDavid Greenman ifp->if_timer = 5; 9023114fdb4SDavid Greenman } 903f9be9005SDavid Greenman txp->tx_threshold = tx_threshold; 904a17c678eSDavid Greenman 905a17c678eSDavid Greenman /* 906483b9871SDavid Greenman * Advance the end of list forward. 907a17c678eSDavid Greenman */ 908a17c678eSDavid Greenman sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S; 909a17c678eSDavid Greenman sc->cbl_last = txp; 910a17c678eSDavid Greenman 911a17c678eSDavid Greenman /* 9121cd443acSDavid Greenman * Advance the beginning of the list forward if there are 9131cd443acSDavid Greenman * no other packets queued (when nothing is queued, cbl_first 914483b9871SDavid Greenman * sits on the last TxCB that was sent out). 915a17c678eSDavid Greenman */ 9161cd443acSDavid Greenman if (sc->tx_queued == 0) 917a17c678eSDavid Greenman sc->cbl_first = txp; 918a17c678eSDavid Greenman 9191cd443acSDavid Greenman sc->tx_queued++; 9201cd443acSDavid Greenman 921a17c678eSDavid Greenman #if NBPFILTER > 0 922a17c678eSDavid Greenman /* 923a17c678eSDavid Greenman * Pass packet to bpf if there is a listener. 924a17c678eSDavid Greenman */ 925fb583156SDavid Greenman if (ifp->if_bpf) 926ba8c6fd5SDavid Greenman bpf_mtap(FXP_BPFTAP_ARG(ifp), mb_head); 927a17c678eSDavid Greenman #endif 928483b9871SDavid Greenman } 929483b9871SDavid Greenman 930483b9871SDavid Greenman /* 931483b9871SDavid Greenman * We're finished. If we added to the list, issue a RESUME to get DMA 932483b9871SDavid Greenman * going again if suspended. 933483b9871SDavid Greenman */ 934483b9871SDavid Greenman if (txp != NULL) { 935483b9871SDavid Greenman fxp_scb_wait(sc); 936483b9871SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_RESUME); 937483b9871SDavid Greenman } 938a17c678eSDavid Greenman } 939a17c678eSDavid Greenman 940a17c678eSDavid Greenman /* 9419c7d2607SDavid Greenman * Process interface interrupts. 942a17c678eSDavid Greenman */ 943ba8c6fd5SDavid Greenman static FXP_INTR_TYPE 944a17c678eSDavid Greenman fxp_intr(arg) 945a17c678eSDavid Greenman void *arg; 946a17c678eSDavid Greenman { 947a17c678eSDavid Greenman struct fxp_softc *sc = arg; 948ba8c6fd5SDavid Greenman struct ifnet *ifp = &sc->sc_if; 9491cd443acSDavid Greenman u_int8_t statack; 950ba8c6fd5SDavid Greenman #if defined(__NetBSD__) 951ba8c6fd5SDavid Greenman int claimed = 0; 952ba8c6fd5SDavid Greenman #endif 953a17c678eSDavid Greenman 954ba8c6fd5SDavid Greenman while ((statack = CSR_READ_1(sc, FXP_CSR_SCB_STATACK)) != 0) { 955ba8c6fd5SDavid Greenman #if defined(__NetBSD__) 956ba8c6fd5SDavid Greenman claimed = 1; 957ba8c6fd5SDavid Greenman #endif 958a17c678eSDavid Greenman /* 959a17c678eSDavid Greenman * First ACK all the interrupts in this pass. 960a17c678eSDavid Greenman */ 961ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_STATACK, statack); 962a17c678eSDavid Greenman 963a17c678eSDavid Greenman /* 9643114fdb4SDavid Greenman * Free any finished transmit mbuf chains. 9653114fdb4SDavid Greenman */ 9663114fdb4SDavid Greenman if (statack & FXP_SCB_STATACK_CXTNO) { 9673114fdb4SDavid Greenman struct fxp_cb_tx *txp; 9683114fdb4SDavid Greenman 9693114fdb4SDavid Greenman for (txp = sc->cbl_first; sc->tx_queued && 9703114fdb4SDavid Greenman (txp->cb_status & FXP_CB_STATUS_C) != 0; 9713114fdb4SDavid Greenman txp = txp->next) { 9723114fdb4SDavid Greenman if (txp->mb_head != NULL) { 9733114fdb4SDavid Greenman m_freem(txp->mb_head); 9743114fdb4SDavid Greenman txp->mb_head = NULL; 9753114fdb4SDavid Greenman } 9763114fdb4SDavid Greenman sc->tx_queued--; 9773114fdb4SDavid Greenman } 9783114fdb4SDavid Greenman sc->cbl_first = txp; 9793114fdb4SDavid Greenman ifp->if_timer = 0; 9803114fdb4SDavid Greenman if (sc->tx_queued == 0) { 9813114fdb4SDavid Greenman if (sc->need_mcsetup) 9823114fdb4SDavid Greenman fxp_mc_setup(sc); 9833114fdb4SDavid Greenman } 9843114fdb4SDavid Greenman /* 9853114fdb4SDavid Greenman * Try to start more packets transmitting. 9863114fdb4SDavid Greenman */ 9873114fdb4SDavid Greenman if (ifp->if_snd.ifq_head != NULL) 9883114fdb4SDavid Greenman fxp_start(ifp); 9893114fdb4SDavid Greenman } 9903114fdb4SDavid Greenman /* 991a17c678eSDavid Greenman * Process receiver interrupts. If a no-resource (RNR) 992a17c678eSDavid Greenman * condition exists, get whatever packets we can and 993a17c678eSDavid Greenman * re-start the receiver. 994a17c678eSDavid Greenman */ 995a17c678eSDavid Greenman if (statack & (FXP_SCB_STATACK_FR | FXP_SCB_STATACK_RNR)) { 996a17c678eSDavid Greenman struct mbuf *m; 997a17c678eSDavid Greenman struct fxp_rfa *rfa; 998a17c678eSDavid Greenman rcvloop: 999a17c678eSDavid Greenman m = sc->rfa_headm; 1000ba8c6fd5SDavid Greenman rfa = (struct fxp_rfa *)(m->m_ext.ext_buf + 1001ba8c6fd5SDavid Greenman RFA_ALIGNMENT_FUDGE); 1002a17c678eSDavid Greenman 1003a17c678eSDavid Greenman if (rfa->rfa_status & FXP_RFA_STATUS_C) { 1004dfe61cf1SDavid Greenman /* 1005dfe61cf1SDavid Greenman * Remove first packet from the chain. 1006dfe61cf1SDavid Greenman */ 1007a17c678eSDavid Greenman sc->rfa_headm = m->m_next; 1008a17c678eSDavid Greenman m->m_next = NULL; 1009a17c678eSDavid Greenman 1010dfe61cf1SDavid Greenman /* 1011ba8c6fd5SDavid Greenman * Add a new buffer to the receive chain. 1012ba8c6fd5SDavid Greenman * If this fails, the old buffer is recycled 1013ba8c6fd5SDavid Greenman * instead. 1014dfe61cf1SDavid Greenman */ 1015a17c678eSDavid Greenman if (fxp_add_rfabuf(sc, m) == 0) { 1016a17c678eSDavid Greenman struct ether_header *eh; 1017ba8c6fd5SDavid Greenman u_int16_t total_len; 1018a17c678eSDavid Greenman 1019ba8c6fd5SDavid Greenman total_len = rfa->actual_size & 1020ba8c6fd5SDavid Greenman (MCLBYTES - 1); 1021ba8c6fd5SDavid Greenman if (total_len < 1022ba8c6fd5SDavid Greenman sizeof(struct ether_header)) { 102306339180SDavid Greenman m_freem(m); 102406339180SDavid Greenman goto rcvloop; 102506339180SDavid Greenman } 1026a17c678eSDavid Greenman m->m_pkthdr.rcvif = ifp; 1027ba8c6fd5SDavid Greenman m->m_pkthdr.len = m->m_len = 1028ab090e5bSLuigi Rizzo total_len ; 1029a17c678eSDavid Greenman eh = mtod(m, struct ether_header *); 1030a17c678eSDavid Greenman #if NBPFILTER > 0 1031ab090e5bSLuigi Rizzo if (ifp->if_bpf) 1032ba8c6fd5SDavid Greenman bpf_tap(FXP_BPFTAP_ARG(ifp), 1033ba8c6fd5SDavid Greenman mtod(m, caddr_t), 1034ba8c6fd5SDavid Greenman total_len); 1035ab090e5bSLuigi Rizzo #endif /* NBPFILTER > 0 */ 1036ab090e5bSLuigi Rizzo #ifdef BRIDGE 1037ab090e5bSLuigi Rizzo if (do_bridge) { 1038ab090e5bSLuigi Rizzo struct ifnet *bdg_ifp ; 1039ab090e5bSLuigi Rizzo bdg_ifp = bridge_in(m); 1040ab090e5bSLuigi Rizzo if (bdg_ifp == BDG_DROP) 1041ab090e5bSLuigi Rizzo goto dropit ; 1042ab090e5bSLuigi Rizzo if (bdg_ifp != BDG_LOCAL) 1043ab090e5bSLuigi Rizzo bdg_forward(&m, bdg_ifp); 1044ab090e5bSLuigi Rizzo if (bdg_ifp != BDG_LOCAL && 1045ab090e5bSLuigi Rizzo bdg_ifp != BDG_BCAST && 1046ab090e5bSLuigi Rizzo bdg_ifp != BDG_MCAST) 1047ab090e5bSLuigi Rizzo goto dropit ; 1048ab090e5bSLuigi Rizzo goto getit ; 1049ab090e5bSLuigi Rizzo } 1050ab090e5bSLuigi Rizzo #endif 1051a17c678eSDavid Greenman /* 1052ba8c6fd5SDavid Greenman * Only pass this packet up 1053ba8c6fd5SDavid Greenman * if it is for us. 1054a17c678eSDavid Greenman */ 1055ba8c6fd5SDavid Greenman if ((ifp->if_flags & 1056ba8c6fd5SDavid Greenman IFF_PROMISC) && 1057ba8c6fd5SDavid Greenman (rfa->rfa_status & 1058ba8c6fd5SDavid Greenman FXP_RFA_STATUS_IAMATCH) && 1059ba8c6fd5SDavid Greenman (eh->ether_dhost[0] & 1) 1060ba8c6fd5SDavid Greenman == 0) { 1061ab090e5bSLuigi Rizzo dropit: 1062ab090e5bSLuigi Rizzo if (m) 1063a17c678eSDavid Greenman m_freem(m); 1064a17c678eSDavid Greenman goto rcvloop; 1065a17c678eSDavid Greenman } 1066ab090e5bSLuigi Rizzo getit: 1067ba8c6fd5SDavid Greenman m->m_data += 1068ba8c6fd5SDavid Greenman sizeof(struct ether_header); 1069ab090e5bSLuigi Rizzo m->m_len -= 1070ab090e5bSLuigi Rizzo sizeof(struct ether_header); 1071ab090e5bSLuigi Rizzo m->m_pkthdr.len = m->m_len ; 1072a17c678eSDavid Greenman ether_input(ifp, eh, m); 1073a17c678eSDavid Greenman } 1074a17c678eSDavid Greenman goto rcvloop; 1075a17c678eSDavid Greenman } 1076a17c678eSDavid Greenman if (statack & FXP_SCB_STATACK_RNR) { 1077ba8c6fd5SDavid Greenman fxp_scb_wait(sc); 1078ba8c6fd5SDavid Greenman CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, 1079ba8c6fd5SDavid Greenman vtophys(sc->rfa_headm->m_ext.ext_buf) + 1080ba8c6fd5SDavid Greenman RFA_ALIGNMENT_FUDGE); 1081ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, 1082ba8c6fd5SDavid Greenman FXP_SCB_COMMAND_RU_START); 1083a17c678eSDavid Greenman } 1084a17c678eSDavid Greenman } 1085a17c678eSDavid Greenman } 1086ba8c6fd5SDavid Greenman #if defined(__NetBSD__) 1087ba8c6fd5SDavid Greenman return (claimed); 1088ba8c6fd5SDavid Greenman #endif 1089a17c678eSDavid Greenman } 1090a17c678eSDavid Greenman 1091dfe61cf1SDavid Greenman /* 1092dfe61cf1SDavid Greenman * Update packet in/out/collision statistics. The i82557 doesn't 1093dfe61cf1SDavid Greenman * allow you to access these counters without doing a fairly 1094dfe61cf1SDavid Greenman * expensive DMA to get _all_ of the statistics it maintains, so 1095dfe61cf1SDavid Greenman * we do this operation here only once per second. The statistics 1096dfe61cf1SDavid Greenman * counters in the kernel are updated from the previous dump-stats 1097dfe61cf1SDavid Greenman * DMA and then a new dump-stats DMA is started. The on-chip 1098dfe61cf1SDavid Greenman * counters are zeroed when the DMA completes. If we can't start 1099dfe61cf1SDavid Greenman * the DMA immediately, we don't wait - we just prepare to read 1100dfe61cf1SDavid Greenman * them again next time. 1101dfe61cf1SDavid Greenman */ 1102303b270bSEivind Eklund static void 1103a17c678eSDavid Greenman fxp_stats_update(arg) 1104a17c678eSDavid Greenman void *arg; 1105a17c678eSDavid Greenman { 1106a17c678eSDavid Greenman struct fxp_softc *sc = arg; 1107ba8c6fd5SDavid Greenman struct ifnet *ifp = &sc->sc_if; 1108a17c678eSDavid Greenman struct fxp_stats *sp = sc->fxp_stats; 1109c8cc6fcaSDavid Greenman struct fxp_cb_tx *txp; 1110397f9dfeSDavid Greenman int s; 1111a17c678eSDavid Greenman 1112a17c678eSDavid Greenman ifp->if_opackets += sp->tx_good; 1113a17c678eSDavid Greenman ifp->if_collisions += sp->tx_total_collisions; 1114397f9dfeSDavid Greenman if (sp->rx_good) { 1115397f9dfeSDavid Greenman ifp->if_ipackets += sp->rx_good; 1116397f9dfeSDavid Greenman sc->rx_idle_secs = 0; 1117397f9dfeSDavid Greenman } else { 1118c8cc6fcaSDavid Greenman /* 1119c8cc6fcaSDavid Greenman * Receiver's been idle for another second. 1120c8cc6fcaSDavid Greenman */ 1121397f9dfeSDavid Greenman sc->rx_idle_secs++; 1122397f9dfeSDavid Greenman } 11233ba65732SDavid Greenman ifp->if_ierrors += 11243ba65732SDavid Greenman sp->rx_crc_errors + 11253ba65732SDavid Greenman sp->rx_alignment_errors + 11263ba65732SDavid Greenman sp->rx_rnr_errors + 11276e39e599SDavid Greenman sp->rx_overrun_errors; 1128a17c678eSDavid Greenman /* 1129f9be9005SDavid Greenman * If any transmit underruns occured, bump up the transmit 1130f9be9005SDavid Greenman * threshold by another 512 bytes (64 * 8). 1131f9be9005SDavid Greenman */ 1132f9be9005SDavid Greenman if (sp->tx_underruns) { 1133f9be9005SDavid Greenman ifp->if_oerrors += sp->tx_underruns; 1134f9be9005SDavid Greenman if (tx_threshold < 192) 1135f9be9005SDavid Greenman tx_threshold += 64; 1136f9be9005SDavid Greenman } 1137397f9dfeSDavid Greenman s = splimp(); 1138397f9dfeSDavid Greenman /* 1139c8cc6fcaSDavid Greenman * Release any xmit buffers that have completed DMA. This isn't 1140c8cc6fcaSDavid Greenman * strictly necessary to do here, but it's advantagous for mbufs 1141c8cc6fcaSDavid Greenman * with external storage to be released in a timely manner rather 1142c8cc6fcaSDavid Greenman * than being defered for a potentially long time. This limits 1143c8cc6fcaSDavid Greenman * the delay to a maximum of one second. 1144c8cc6fcaSDavid Greenman */ 1145c8cc6fcaSDavid Greenman for (txp = sc->cbl_first; sc->tx_queued && 1146c8cc6fcaSDavid Greenman (txp->cb_status & FXP_CB_STATUS_C) != 0; 1147c8cc6fcaSDavid Greenman txp = txp->next) { 1148c8cc6fcaSDavid Greenman if (txp->mb_head != NULL) { 1149c8cc6fcaSDavid Greenman m_freem(txp->mb_head); 1150c8cc6fcaSDavid Greenman txp->mb_head = NULL; 1151c8cc6fcaSDavid Greenman } 1152c8cc6fcaSDavid Greenman sc->tx_queued--; 1153c8cc6fcaSDavid Greenman } 1154c8cc6fcaSDavid Greenman sc->cbl_first = txp; 1155c8cc6fcaSDavid Greenman /* 1156397f9dfeSDavid Greenman * If we haven't received any packets in FXP_MAC_RX_IDLE seconds, 1157397f9dfeSDavid Greenman * then assume the receiver has locked up and attempt to clear 1158397f9dfeSDavid Greenman * the condition by reprogramming the multicast filter. This is 1159397f9dfeSDavid Greenman * a work-around for a bug in the 82557 where the receiver locks 1160397f9dfeSDavid Greenman * up if it gets certain types of garbage in the syncronization 1161397f9dfeSDavid Greenman * bits prior to the packet header. This bug is supposed to only 1162397f9dfeSDavid Greenman * occur in 10Mbps mode, but has been seen to occur in 100Mbps 1163397f9dfeSDavid Greenman * mode as well (perhaps due to a 10/100 speed transition). 1164397f9dfeSDavid Greenman */ 1165397f9dfeSDavid Greenman if (sc->rx_idle_secs > FXP_MAX_RX_IDLE) { 1166397f9dfeSDavid Greenman sc->rx_idle_secs = 0; 1167397f9dfeSDavid Greenman fxp_mc_setup(sc); 1168397f9dfeSDavid Greenman } 1169f9be9005SDavid Greenman /* 11703ba65732SDavid Greenman * If there is no pending command, start another stats 11713ba65732SDavid Greenman * dump. Otherwise punt for now. 1172a17c678eSDavid Greenman */ 1173397f9dfeSDavid Greenman if (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) == 0) { 1174a17c678eSDavid Greenman /* 1175397f9dfeSDavid Greenman * Start another stats dump. 1176a17c678eSDavid Greenman */ 1177ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, 1178ba8c6fd5SDavid Greenman FXP_SCB_COMMAND_CU_DUMPRESET); 1179dfe61cf1SDavid Greenman } else { 1180dfe61cf1SDavid Greenman /* 1181dfe61cf1SDavid Greenman * A previous command is still waiting to be accepted. 1182dfe61cf1SDavid Greenman * Just zero our copy of the stats and wait for the 11833ba65732SDavid Greenman * next timer event to update them. 1184dfe61cf1SDavid Greenman */ 1185dfe61cf1SDavid Greenman sp->tx_good = 0; 1186f9be9005SDavid Greenman sp->tx_underruns = 0; 1187dfe61cf1SDavid Greenman sp->tx_total_collisions = 0; 11883ba65732SDavid Greenman 1189dfe61cf1SDavid Greenman sp->rx_good = 0; 11903ba65732SDavid Greenman sp->rx_crc_errors = 0; 11913ba65732SDavid Greenman sp->rx_alignment_errors = 0; 11923ba65732SDavid Greenman sp->rx_rnr_errors = 0; 11933ba65732SDavid Greenman sp->rx_overrun_errors = 0; 1194dfe61cf1SDavid Greenman } 1195397f9dfeSDavid Greenman splx(s); 1196a17c678eSDavid Greenman /* 1197a17c678eSDavid Greenman * Schedule another timeout one second from now. 1198a17c678eSDavid Greenman */ 1199397f9dfeSDavid Greenman sc->stat_ch = timeout(fxp_stats_update, sc, hz); 1200a17c678eSDavid Greenman } 1201a17c678eSDavid Greenman 1202a17c678eSDavid Greenman /* 1203a17c678eSDavid Greenman * Stop the interface. Cancels the statistics updater and resets 1204a17c678eSDavid Greenman * the interface. 1205a17c678eSDavid Greenman */ 1206a17c678eSDavid Greenman static void 12074a5f1499SDavid Greenman fxp_stop(sc) 12084a5f1499SDavid Greenman struct fxp_softc *sc; 1209a17c678eSDavid Greenman { 1210ba8c6fd5SDavid Greenman struct ifnet *ifp = &sc->sc_if; 12113ba65732SDavid Greenman struct fxp_cb_tx *txp; 12123ba65732SDavid Greenman int i; 1213a17c678eSDavid Greenman 1214a17c678eSDavid Greenman /* 1215a17c678eSDavid Greenman * Cancel stats updater. 1216a17c678eSDavid Greenman */ 12176c951b44SJustin T. Gibbs untimeout(fxp_stats_update, sc, sc->stat_ch); 12183ba65732SDavid Greenman 12193ba65732SDavid Greenman /* 12203ba65732SDavid Greenman * Issue software reset 12213ba65732SDavid Greenman */ 1222ba8c6fd5SDavid Greenman CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET); 1223a17c678eSDavid Greenman DELAY(10); 1224a17c678eSDavid Greenman 12253ba65732SDavid Greenman /* 12263ba65732SDavid Greenman * Release any xmit buffers. 12273ba65732SDavid Greenman */ 1228da91462dSDavid Greenman txp = sc->cbl_base; 1229da91462dSDavid Greenman if (txp != NULL) { 1230da91462dSDavid Greenman for (i = 0; i < FXP_NTXCB; i++) { 1231da91462dSDavid Greenman if (txp[i].mb_head != NULL) { 1232da91462dSDavid Greenman m_freem(txp[i].mb_head); 1233da91462dSDavid Greenman txp[i].mb_head = NULL; 1234da91462dSDavid Greenman } 1235da91462dSDavid Greenman } 12363ba65732SDavid Greenman } 12373ba65732SDavid Greenman sc->tx_queued = 0; 12383ba65732SDavid Greenman 12393ba65732SDavid Greenman /* 12403ba65732SDavid Greenman * Free all the receive buffers then reallocate/reinitialize 12413ba65732SDavid Greenman */ 12423ba65732SDavid Greenman if (sc->rfa_headm != NULL) 12433ba65732SDavid Greenman m_freem(sc->rfa_headm); 12443ba65732SDavid Greenman sc->rfa_headm = NULL; 12453ba65732SDavid Greenman sc->rfa_tailm = NULL; 12463ba65732SDavid Greenman for (i = 0; i < FXP_NRFABUFS; i++) { 12473ba65732SDavid Greenman if (fxp_add_rfabuf(sc, NULL) != 0) { 12483ba65732SDavid Greenman /* 12493ba65732SDavid Greenman * This "can't happen" - we're at splimp() 12503ba65732SDavid Greenman * and we just freed all the buffers we need 12513ba65732SDavid Greenman * above. 12523ba65732SDavid Greenman */ 12533ba65732SDavid Greenman panic("fxp_stop: no buffers!"); 12543ba65732SDavid Greenman } 12553ba65732SDavid Greenman } 12563ba65732SDavid Greenman 12573ba65732SDavid Greenman ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 12583ba65732SDavid Greenman ifp->if_timer = 0; 1259a17c678eSDavid Greenman } 1260a17c678eSDavid Greenman 1261a17c678eSDavid Greenman /* 1262a17c678eSDavid Greenman * Watchdog/transmission transmit timeout handler. Called when a 1263a17c678eSDavid Greenman * transmission is started on the interface, but no interrupt is 1264a17c678eSDavid Greenman * received before the timeout. This usually indicates that the 1265a17c678eSDavid Greenman * card has wedged for some reason. 1266a17c678eSDavid Greenman */ 1267a17c678eSDavid Greenman static void 12684a5f1499SDavid Greenman fxp_watchdog(ifp) 12694a5f1499SDavid Greenman struct ifnet *ifp; 1270a17c678eSDavid Greenman { 1271ba8c6fd5SDavid Greenman struct fxp_softc *sc = ifp->if_softc; 1272ba8c6fd5SDavid Greenman 1273397f9dfeSDavid Greenman printf(FXP_FORMAT ": device timeout\n", FXP_ARGS(sc)); 12744a5f1499SDavid Greenman ifp->if_oerrors++; 1275a17c678eSDavid Greenman 1276ba8c6fd5SDavid Greenman fxp_init(sc); 1277a17c678eSDavid Greenman } 1278a17c678eSDavid Greenman 1279a17c678eSDavid Greenman static void 1280fb583156SDavid Greenman fxp_init(xsc) 1281fb583156SDavid Greenman void *xsc; 1282a17c678eSDavid Greenman { 1283fb583156SDavid Greenman struct fxp_softc *sc = xsc; 1284ba8c6fd5SDavid Greenman struct ifnet *ifp = &sc->sc_if; 1285a17c678eSDavid Greenman struct fxp_cb_config *cbp; 1286a17c678eSDavid Greenman struct fxp_cb_ias *cb_ias; 1287a17c678eSDavid Greenman struct fxp_cb_tx *txp; 1288397f9dfeSDavid Greenman int i, s, prm; 1289a17c678eSDavid Greenman 1290a17c678eSDavid Greenman s = splimp(); 1291a17c678eSDavid Greenman /* 12923ba65732SDavid Greenman * Cancel any pending I/O 1293a17c678eSDavid Greenman */ 12943ba65732SDavid Greenman fxp_stop(sc); 1295a17c678eSDavid Greenman 1296a17c678eSDavid Greenman prm = (ifp->if_flags & IFF_PROMISC) ? 1 : 0; 1297a17c678eSDavid Greenman 1298a17c678eSDavid Greenman /* 1299a17c678eSDavid Greenman * Initialize base of CBL and RFA memory. Loading with zero 1300a17c678eSDavid Greenman * sets it up for regular linear addressing. 1301a17c678eSDavid Greenman */ 1302ba8c6fd5SDavid Greenman CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, 0); 1303ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_BASE); 1304a17c678eSDavid Greenman 1305ba8c6fd5SDavid Greenman fxp_scb_wait(sc); 1306ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_BASE); 1307a17c678eSDavid Greenman 1308a17c678eSDavid Greenman /* 1309a17c678eSDavid Greenman * Initialize base of dump-stats buffer. 1310a17c678eSDavid Greenman */ 1311ba8c6fd5SDavid Greenman fxp_scb_wait(sc); 1312ba8c6fd5SDavid Greenman CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(sc->fxp_stats)); 1313ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_DUMP_ADR); 1314a17c678eSDavid Greenman 1315a17c678eSDavid Greenman /* 1316a17c678eSDavid Greenman * We temporarily use memory that contains the TxCB list to 1317a17c678eSDavid Greenman * construct the config CB. The TxCB list memory is rebuilt 1318a17c678eSDavid Greenman * later. 1319a17c678eSDavid Greenman */ 1320a17c678eSDavid Greenman cbp = (struct fxp_cb_config *) sc->cbl_base; 1321a17c678eSDavid Greenman 1322a17c678eSDavid Greenman /* 1323a17c678eSDavid Greenman * This bcopy is kind of disgusting, but there are a bunch of must be 1324a17c678eSDavid Greenman * zero and must be one bits in this structure and this is the easiest 1325a17c678eSDavid Greenman * way to initialize them all to proper values. 1326a17c678eSDavid Greenman */ 1327697457a1SMatthew Dillon bcopy(fxp_cb_config_template, (volatile void *)&cbp->cb_status, 1328397f9dfeSDavid Greenman sizeof(fxp_cb_config_template)); 1329a17c678eSDavid Greenman 1330a17c678eSDavid Greenman cbp->cb_status = 0; 1331a17c678eSDavid Greenman cbp->cb_command = FXP_CB_COMMAND_CONFIG | FXP_CB_COMMAND_EL; 1332a17c678eSDavid Greenman cbp->link_addr = -1; /* (no) next command */ 1333a17c678eSDavid Greenman cbp->byte_count = 22; /* (22) bytes to config */ 1334001696daSDavid Greenman cbp->rx_fifo_limit = 8; /* rx fifo threshold (32 bytes) */ 1335001696daSDavid Greenman cbp->tx_fifo_limit = 0; /* tx fifo threshold (0 bytes) */ 1336a17c678eSDavid Greenman cbp->adaptive_ifs = 0; /* (no) adaptive interframe spacing */ 1337001696daSDavid Greenman cbp->rx_dma_bytecount = 0; /* (no) rx DMA max */ 1338001696daSDavid Greenman cbp->tx_dma_bytecount = 0; /* (no) tx DMA max */ 1339001696daSDavid Greenman cbp->dma_bce = 0; /* (disable) dma max counters */ 1340a17c678eSDavid Greenman cbp->late_scb = 0; /* (don't) defer SCB update */ 1341a17c678eSDavid Greenman cbp->tno_int = 0; /* (disable) tx not okay interrupt */ 13423114fdb4SDavid Greenman cbp->ci_int = 1; /* interrupt on CU idle */ 1343a17c678eSDavid Greenman cbp->save_bf = prm; /* save bad frames */ 1344a17c678eSDavid Greenman cbp->disc_short_rx = !prm; /* discard short packets */ 1345a17c678eSDavid Greenman cbp->underrun_retry = 1; /* retry mode (1) on DMA underrun */ 1346dccee1a1SDavid Greenman cbp->mediatype = !sc->phy_10Mbps_only; /* interface mode */ 1347a17c678eSDavid Greenman cbp->nsai = 1; /* (don't) disable source addr insert */ 1348a17c678eSDavid Greenman cbp->preamble_length = 2; /* (7 byte) preamble */ 1349a17c678eSDavid Greenman cbp->loopback = 0; /* (don't) loopback */ 1350a17c678eSDavid Greenman cbp->linear_priority = 0; /* (normal CSMA/CD operation) */ 1351a17c678eSDavid Greenman cbp->linear_pri_mode = 0; /* (wait after xmit only) */ 1352a17c678eSDavid Greenman cbp->interfrm_spacing = 6; /* (96 bits of) interframe spacing */ 1353a17c678eSDavid Greenman cbp->promiscuous = prm; /* promiscuous mode */ 1354a17c678eSDavid Greenman cbp->bcast_disable = 0; /* (don't) disable broadcasts */ 1355001696daSDavid Greenman cbp->crscdt = 0; /* (CRS only) */ 1356a17c678eSDavid Greenman cbp->stripping = !prm; /* truncate rx packet to byte count */ 1357a17c678eSDavid Greenman cbp->padding = 1; /* (do) pad short tx packets */ 1358a17c678eSDavid Greenman cbp->rcv_crc_xfer = 0; /* (don't) xfer CRC to host */ 1359a17c678eSDavid Greenman cbp->force_fdx = 0; /* (don't) force full duplex */ 13603ba65732SDavid Greenman cbp->fdx_pin_en = 1; /* (enable) FDX# pin */ 1361a17c678eSDavid Greenman cbp->multi_ia = 0; /* (don't) accept multiple IAs */ 1362397f9dfeSDavid Greenman cbp->mc_all = sc->all_mcasts;/* accept all multicasts */ 1363a17c678eSDavid Greenman 1364a17c678eSDavid Greenman /* 1365a17c678eSDavid Greenman * Start the config command/DMA. 1366a17c678eSDavid Greenman */ 1367ba8c6fd5SDavid Greenman fxp_scb_wait(sc); 1368397f9dfeSDavid Greenman CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&cbp->cb_status)); 1369ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); 1370a17c678eSDavid Greenman /* ...and wait for it to complete. */ 1371a17c678eSDavid Greenman while (!(cbp->cb_status & FXP_CB_STATUS_C)); 1372a17c678eSDavid Greenman 1373a17c678eSDavid Greenman /* 1374a17c678eSDavid Greenman * Now initialize the station address. Temporarily use the TxCB 1375a17c678eSDavid Greenman * memory area like we did above for the config CB. 1376a17c678eSDavid Greenman */ 1377a17c678eSDavid Greenman cb_ias = (struct fxp_cb_ias *) sc->cbl_base; 1378a17c678eSDavid Greenman cb_ias->cb_status = 0; 1379a17c678eSDavid Greenman cb_ias->cb_command = FXP_CB_COMMAND_IAS | FXP_CB_COMMAND_EL; 1380a17c678eSDavid Greenman cb_ias->link_addr = -1; 1381ba8c6fd5SDavid Greenman #if defined(__NetBSD__) 1382ba8c6fd5SDavid Greenman bcopy(LLADDR(ifp->if_sadl), (void *)cb_ias->macaddr, 6); 1383ba8c6fd5SDavid Greenman #else 13848aef1712SMatthew Dillon bcopy(sc->arpcom.ac_enaddr, (volatile void *)cb_ias->macaddr, 1385a17c678eSDavid Greenman sizeof(sc->arpcom.ac_enaddr)); 1386ba8c6fd5SDavid Greenman #endif /* __NetBSD__ */ 1387a17c678eSDavid Greenman 1388a17c678eSDavid Greenman /* 1389a17c678eSDavid Greenman * Start the IAS (Individual Address Setup) command/DMA. 1390a17c678eSDavid Greenman */ 1391ba8c6fd5SDavid Greenman fxp_scb_wait(sc); 1392ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); 1393a17c678eSDavid Greenman /* ...and wait for it to complete. */ 1394a17c678eSDavid Greenman while (!(cb_ias->cb_status & FXP_CB_STATUS_C)); 1395a17c678eSDavid Greenman 1396a17c678eSDavid Greenman /* 1397a17c678eSDavid Greenman * Initialize transmit control block (TxCB) list. 1398a17c678eSDavid Greenman */ 1399a17c678eSDavid Greenman 1400a17c678eSDavid Greenman txp = sc->cbl_base; 1401a17c678eSDavid Greenman bzero(txp, sizeof(struct fxp_cb_tx) * FXP_NTXCB); 1402a17c678eSDavid Greenman for (i = 0; i < FXP_NTXCB; i++) { 1403a17c678eSDavid Greenman txp[i].cb_status = FXP_CB_STATUS_C | FXP_CB_STATUS_OK; 1404a17c678eSDavid Greenman txp[i].cb_command = FXP_CB_COMMAND_NOP; 1405397f9dfeSDavid Greenman txp[i].link_addr = vtophys(&txp[(i + 1) & FXP_TXCB_MASK].cb_status); 1406a17c678eSDavid Greenman txp[i].tbd_array_addr = vtophys(&txp[i].tbd[0]); 1407a17c678eSDavid Greenman txp[i].next = &txp[(i + 1) & FXP_TXCB_MASK]; 1408a17c678eSDavid Greenman } 1409a17c678eSDavid Greenman /* 1410397f9dfeSDavid Greenman * Set the suspend flag on the first TxCB and start the control 1411a17c678eSDavid Greenman * unit. It will execute the NOP and then suspend. 1412a17c678eSDavid Greenman */ 1413a17c678eSDavid Greenman txp->cb_command = FXP_CB_COMMAND_NOP | FXP_CB_COMMAND_S; 1414a17c678eSDavid Greenman sc->cbl_first = sc->cbl_last = txp; 1415397f9dfeSDavid Greenman sc->tx_queued = 1; 1416a17c678eSDavid Greenman 1417ba8c6fd5SDavid Greenman fxp_scb_wait(sc); 1418ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); 1419a17c678eSDavid Greenman 1420a17c678eSDavid Greenman /* 1421a17c678eSDavid Greenman * Initialize receiver buffer area - RFA. 1422a17c678eSDavid Greenman */ 1423ba8c6fd5SDavid Greenman fxp_scb_wait(sc); 1424ba8c6fd5SDavid Greenman CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, 1425ba8c6fd5SDavid Greenman vtophys(sc->rfa_headm->m_ext.ext_buf) + RFA_ALIGNMENT_FUDGE); 1426ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_START); 1427a17c678eSDavid Greenman 1428dccee1a1SDavid Greenman /* 1429ba8c6fd5SDavid Greenman * Set current media. 1430dccee1a1SDavid Greenman */ 1431ba8c6fd5SDavid Greenman fxp_set_media(sc, sc->sc_media.ifm_cur->ifm_media); 1432dccee1a1SDavid Greenman 1433a17c678eSDavid Greenman ifp->if_flags |= IFF_RUNNING; 1434a17c678eSDavid Greenman ifp->if_flags &= ~IFF_OACTIVE; 1435a17c678eSDavid Greenman splx(s); 1436a17c678eSDavid Greenman 1437a17c678eSDavid Greenman /* 1438a17c678eSDavid Greenman * Start stats updater. 1439a17c678eSDavid Greenman */ 14406c951b44SJustin T. Gibbs sc->stat_ch = timeout(fxp_stats_update, sc, hz); 1441a17c678eSDavid Greenman } 1442a17c678eSDavid Greenman 1443303b270bSEivind Eklund static void 1444ba8c6fd5SDavid Greenman fxp_set_media(sc, media) 1445ba8c6fd5SDavid Greenman struct fxp_softc *sc; 1446ba8c6fd5SDavid Greenman int media; 1447ba8c6fd5SDavid Greenman { 1448ba8c6fd5SDavid Greenman 1449ba8c6fd5SDavid Greenman switch (sc->phy_primary_device) { 1450ba8c6fd5SDavid Greenman case FXP_PHY_DP83840: 1451ba8c6fd5SDavid Greenman case FXP_PHY_DP83840A: 1452ba8c6fd5SDavid Greenman fxp_mdi_write(sc, sc->phy_primary_addr, FXP_DP83840_PCR, 1453ba8c6fd5SDavid Greenman fxp_mdi_read(sc, sc->phy_primary_addr, FXP_DP83840_PCR) | 1454ba8c6fd5SDavid Greenman FXP_DP83840_PCR_LED4_MODE | /* LED4 always indicates duplex */ 1455ba8c6fd5SDavid Greenman FXP_DP83840_PCR_F_CONNECT | /* force link disconnect bypass */ 1456ba8c6fd5SDavid Greenman FXP_DP83840_PCR_BIT10); /* XXX I have no idea */ 1457ba8c6fd5SDavid Greenman /* fall through */ 145892924291SDavid Greenman case FXP_PHY_82553A: 145992924291SDavid Greenman case FXP_PHY_82553C: /* untested */ 1460ba8c6fd5SDavid Greenman case FXP_PHY_82555: 146192924291SDavid Greenman case FXP_PHY_82555B: 1462ba8c6fd5SDavid Greenman if (IFM_SUBTYPE(media) != IFM_AUTO) { 1463ba8c6fd5SDavid Greenman int flags; 1464ba8c6fd5SDavid Greenman 1465ba8c6fd5SDavid Greenman flags = (IFM_SUBTYPE(media) == IFM_100_TX) ? 1466ba8c6fd5SDavid Greenman FXP_PHY_BMCR_SPEED_100M : 0; 1467ba8c6fd5SDavid Greenman flags |= (media & IFM_FDX) ? 1468ba8c6fd5SDavid Greenman FXP_PHY_BMCR_FULLDUPLEX : 0; 1469ba8c6fd5SDavid Greenman fxp_mdi_write(sc, sc->phy_primary_addr, 1470ba8c6fd5SDavid Greenman FXP_PHY_BMCR, 1471ba8c6fd5SDavid Greenman (fxp_mdi_read(sc, sc->phy_primary_addr, 1472ba8c6fd5SDavid Greenman FXP_PHY_BMCR) & 1473ba8c6fd5SDavid Greenman ~(FXP_PHY_BMCR_AUTOEN | FXP_PHY_BMCR_SPEED_100M | 1474ba8c6fd5SDavid Greenman FXP_PHY_BMCR_FULLDUPLEX)) | flags); 1475ba8c6fd5SDavid Greenman } else { 1476ba8c6fd5SDavid Greenman fxp_mdi_write(sc, sc->phy_primary_addr, 1477ba8c6fd5SDavid Greenman FXP_PHY_BMCR, 1478ba8c6fd5SDavid Greenman (fxp_mdi_read(sc, sc->phy_primary_addr, 1479ba8c6fd5SDavid Greenman FXP_PHY_BMCR) | FXP_PHY_BMCR_AUTOEN)); 1480ba8c6fd5SDavid Greenman } 1481ba8c6fd5SDavid Greenman break; 1482ba8c6fd5SDavid Greenman /* 1483ba8c6fd5SDavid Greenman * The Seeq 80c24 doesn't have a PHY programming interface, so do 1484ba8c6fd5SDavid Greenman * nothing. 1485ba8c6fd5SDavid Greenman */ 1486ba8c6fd5SDavid Greenman case FXP_PHY_80C24: 1487ba8c6fd5SDavid Greenman break; 1488ba8c6fd5SDavid Greenman default: 1489ba8c6fd5SDavid Greenman printf(FXP_FORMAT 1490ba8c6fd5SDavid Greenman ": warning: unsupported PHY, type = %d, addr = %d\n", 1491ba8c6fd5SDavid Greenman FXP_ARGS(sc), sc->phy_primary_device, 1492ba8c6fd5SDavid Greenman sc->phy_primary_addr); 1493ba8c6fd5SDavid Greenman } 1494ba8c6fd5SDavid Greenman } 1495ba8c6fd5SDavid Greenman 1496ba8c6fd5SDavid Greenman /* 1497ba8c6fd5SDavid Greenman * Change media according to request. 1498ba8c6fd5SDavid Greenman */ 1499ba8c6fd5SDavid Greenman int 1500ba8c6fd5SDavid Greenman fxp_mediachange(ifp) 1501ba8c6fd5SDavid Greenman struct ifnet *ifp; 1502ba8c6fd5SDavid Greenman { 1503ba8c6fd5SDavid Greenman struct fxp_softc *sc = ifp->if_softc; 1504ba8c6fd5SDavid Greenman struct ifmedia *ifm = &sc->sc_media; 1505ba8c6fd5SDavid Greenman 1506ba8c6fd5SDavid Greenman if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) 1507ba8c6fd5SDavid Greenman return (EINVAL); 1508ba8c6fd5SDavid Greenman 1509ba8c6fd5SDavid Greenman fxp_set_media(sc, ifm->ifm_media); 1510ba8c6fd5SDavid Greenman return (0); 1511ba8c6fd5SDavid Greenman } 1512ba8c6fd5SDavid Greenman 1513ba8c6fd5SDavid Greenman /* 1514ba8c6fd5SDavid Greenman * Notify the world which media we're using. 1515ba8c6fd5SDavid Greenman */ 1516ba8c6fd5SDavid Greenman void 1517ba8c6fd5SDavid Greenman fxp_mediastatus(ifp, ifmr) 1518ba8c6fd5SDavid Greenman struct ifnet *ifp; 1519ba8c6fd5SDavid Greenman struct ifmediareq *ifmr; 1520ba8c6fd5SDavid Greenman { 1521ba8c6fd5SDavid Greenman struct fxp_softc *sc = ifp->if_softc; 1522a7280784SJulian Elischer int flags, stsflags; 1523ba8c6fd5SDavid Greenman 1524ba8c6fd5SDavid Greenman switch (sc->phy_primary_device) { 1525ba8c6fd5SDavid Greenman case FXP_PHY_82555: 152635517ab7SDavid Greenman case FXP_PHY_82555B: 1527a7280784SJulian Elischer case FXP_PHY_DP83840: 1528a7280784SJulian Elischer case FXP_PHY_DP83840A: 1529a7280784SJulian Elischer ifmr->ifm_status = IFM_AVALID; /* IFM_ACTIVE will be valid */ 1530ba8c6fd5SDavid Greenman ifmr->ifm_active = IFM_ETHER; 1531a7280784SJulian Elischer /* 1532a7280784SJulian Elischer * the following is not an error. 1533a7280784SJulian Elischer * You need to read this register twice to get current 1534a7280784SJulian Elischer * status. This is correct documented behaviour, the 1535a7280784SJulian Elischer * first read gets latched values. 1536a7280784SJulian Elischer */ 1537a7280784SJulian Elischer stsflags = fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_STS); 1538a7280784SJulian Elischer stsflags = fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_STS); 1539a7280784SJulian Elischer if (stsflags & FXP_PHY_STS_LINK_STS) 1540a7280784SJulian Elischer ifmr->ifm_status |= IFM_ACTIVE; 1541a7280784SJulian Elischer 1542a7280784SJulian Elischer /* 1543a7280784SJulian Elischer * If we are in auto mode, then try report the result. 1544a7280784SJulian Elischer */ 1545a7280784SJulian Elischer flags = fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_BMCR); 1546f1bf08c2SJulian Elischer if (flags & FXP_PHY_BMCR_AUTOEN) { 1547f1bf08c2SJulian Elischer ifmr->ifm_active |= IFM_AUTO; /* XXX presently 0 */ 1548a7280784SJulian Elischer if (stsflags & FXP_PHY_STS_AUTO_DONE) { 1549f1bf08c2SJulian Elischer /* 1550a7280784SJulian Elischer * Intel and National parts report 1551a7280784SJulian Elischer * differently on what they found. 1552f1bf08c2SJulian Elischer */ 1553f1bf08c2SJulian Elischer if ((sc->phy_primary_device == FXP_PHY_82555) 1554f1bf08c2SJulian Elischer || (sc->phy_primary_device == FXP_PHY_82555B)) { 1555f1bf08c2SJulian Elischer flags = fxp_mdi_read(sc, 1556a7280784SJulian Elischer sc->phy_primary_addr, 1557a7280784SJulian Elischer FXP_PHY_USC); 1558f1bf08c2SJulian Elischer 1559f1bf08c2SJulian Elischer if (flags & FXP_PHY_USC_SPEED) 1560f1bf08c2SJulian Elischer ifmr->ifm_active |= IFM_100_TX; 1561f1bf08c2SJulian Elischer else 1562f1bf08c2SJulian Elischer ifmr->ifm_active |= IFM_10_T; 1563f1bf08c2SJulian Elischer 1564f1bf08c2SJulian Elischer if (flags & FXP_PHY_USC_DUPLEX) 1565f1bf08c2SJulian Elischer ifmr->ifm_active |= IFM_FDX; 1566a7280784SJulian Elischer } else { /* it's National. only know speed */ 1567a7280784SJulian Elischer flags = fxp_mdi_read(sc, 1568a7280784SJulian Elischer sc->phy_primary_addr, 1569a7280784SJulian Elischer FXP_DP83840_PAR); 1570a7280784SJulian Elischer 1571a7280784SJulian Elischer if (flags & FXP_DP83840_PAR_SPEED_10) 1572a7280784SJulian Elischer ifmr->ifm_active |= IFM_10_T; 1573a7280784SJulian Elischer else 1574a7280784SJulian Elischer ifmr->ifm_active |= IFM_100_TX; 1575f1bf08c2SJulian Elischer } 1576a7280784SJulian Elischer } 1577a7280784SJulian Elischer } else { /* in manual mode.. just report what we were set to */ 1578ba8c6fd5SDavid Greenman if (flags & FXP_PHY_BMCR_SPEED_100M) 1579ba8c6fd5SDavid Greenman ifmr->ifm_active |= IFM_100_TX; 1580ba8c6fd5SDavid Greenman else 1581ba8c6fd5SDavid Greenman ifmr->ifm_active |= IFM_10_T; 1582ba8c6fd5SDavid Greenman 1583ba8c6fd5SDavid Greenman if (flags & FXP_PHY_BMCR_FULLDUPLEX) 1584ba8c6fd5SDavid Greenman ifmr->ifm_active |= IFM_FDX; 1585ba8c6fd5SDavid Greenman } 1586ba8c6fd5SDavid Greenman break; 1587ba8c6fd5SDavid Greenman 1588ba8c6fd5SDavid Greenman case FXP_PHY_80C24: 1589ba8c6fd5SDavid Greenman default: 1590ba8c6fd5SDavid Greenman ifmr->ifm_active = IFM_ETHER|IFM_MANUAL; /* XXX IFM_AUTO ? */ 1591ba8c6fd5SDavid Greenman } 1592ba8c6fd5SDavid Greenman } 1593ba8c6fd5SDavid Greenman 1594a17c678eSDavid Greenman /* 1595a17c678eSDavid Greenman * Add a buffer to the end of the RFA buffer list. 1596a17c678eSDavid Greenman * Return 0 if successful, 1 for failure. A failure results in 1597a17c678eSDavid Greenman * adding the 'oldm' (if non-NULL) on to the end of the list - 1598dc733423SDag-Erling Smørgrav * tossing out its old contents and recycling it. 1599a17c678eSDavid Greenman * The RFA struct is stuck at the beginning of mbuf cluster and the 1600a17c678eSDavid Greenman * data pointer is fixed up to point just past it. 1601a17c678eSDavid Greenman */ 1602a17c678eSDavid Greenman static int 1603a17c678eSDavid Greenman fxp_add_rfabuf(sc, oldm) 1604a17c678eSDavid Greenman struct fxp_softc *sc; 1605a17c678eSDavid Greenman struct mbuf *oldm; 1606a17c678eSDavid Greenman { 1607ba8c6fd5SDavid Greenman u_int32_t v; 1608a17c678eSDavid Greenman struct mbuf *m; 1609a17c678eSDavid Greenman struct fxp_rfa *rfa, *p_rfa; 1610a17c678eSDavid Greenman 1611a17c678eSDavid Greenman MGETHDR(m, M_DONTWAIT, MT_DATA); 1612a17c678eSDavid Greenman if (m != NULL) { 1613a17c678eSDavid Greenman MCLGET(m, M_DONTWAIT); 1614a17c678eSDavid Greenman if ((m->m_flags & M_EXT) == 0) { 1615a17c678eSDavid Greenman m_freem(m); 1616eadd5e3aSDavid Greenman if (oldm == NULL) 1617eadd5e3aSDavid Greenman return 1; 1618a17c678eSDavid Greenman m = oldm; 1619eadd5e3aSDavid Greenman m->m_data = m->m_ext.ext_buf; 1620a17c678eSDavid Greenman } 1621a17c678eSDavid Greenman } else { 1622eadd5e3aSDavid Greenman if (oldm == NULL) 1623a17c678eSDavid Greenman return 1; 1624eadd5e3aSDavid Greenman m = oldm; 1625eadd5e3aSDavid Greenman m->m_data = m->m_ext.ext_buf; 1626eadd5e3aSDavid Greenman } 1627ba8c6fd5SDavid Greenman 1628ba8c6fd5SDavid Greenman /* 1629ba8c6fd5SDavid Greenman * Move the data pointer up so that the incoming data packet 1630ba8c6fd5SDavid Greenman * will be 32-bit aligned. 1631ba8c6fd5SDavid Greenman */ 1632ba8c6fd5SDavid Greenman m->m_data += RFA_ALIGNMENT_FUDGE; 1633ba8c6fd5SDavid Greenman 1634eadd5e3aSDavid Greenman /* 1635eadd5e3aSDavid Greenman * Get a pointer to the base of the mbuf cluster and move 1636eadd5e3aSDavid Greenman * data start past it. 1637eadd5e3aSDavid Greenman */ 1638a17c678eSDavid Greenman rfa = mtod(m, struct fxp_rfa *); 1639eadd5e3aSDavid Greenman m->m_data += sizeof(struct fxp_rfa); 1640ba8c6fd5SDavid Greenman rfa->size = MCLBYTES - sizeof(struct fxp_rfa) - RFA_ALIGNMENT_FUDGE; 1641eadd5e3aSDavid Greenman 1642ba8c6fd5SDavid Greenman /* 1643ba8c6fd5SDavid Greenman * Initialize the rest of the RFA. Note that since the RFA 1644ba8c6fd5SDavid Greenman * is misaligned, we cannot store values directly. Instead, 1645ba8c6fd5SDavid Greenman * we use an optimized, inline copy. 1646ba8c6fd5SDavid Greenman */ 1647a17c678eSDavid Greenman rfa->rfa_status = 0; 1648a17c678eSDavid Greenman rfa->rfa_control = FXP_RFA_CONTROL_EL; 1649a17c678eSDavid Greenman rfa->actual_size = 0; 1650ba8c6fd5SDavid Greenman 1651ba8c6fd5SDavid Greenman v = -1; 1652ba8c6fd5SDavid Greenman fxp_lwcopy(&v, &rfa->link_addr); 1653ba8c6fd5SDavid Greenman fxp_lwcopy(&v, &rfa->rbd_addr); 1654ba8c6fd5SDavid Greenman 1655dfe61cf1SDavid Greenman /* 1656dfe61cf1SDavid Greenman * If there are other buffers already on the list, attach this 1657dfe61cf1SDavid Greenman * one to the end by fixing up the tail to point to this one. 1658dfe61cf1SDavid Greenman */ 1659a17c678eSDavid Greenman if (sc->rfa_headm != NULL) { 1660ba8c6fd5SDavid Greenman p_rfa = (struct fxp_rfa *) (sc->rfa_tailm->m_ext.ext_buf + 1661ba8c6fd5SDavid Greenman RFA_ALIGNMENT_FUDGE); 1662a17c678eSDavid Greenman sc->rfa_tailm->m_next = m; 1663ba8c6fd5SDavid Greenman v = vtophys(rfa); 1664ba8c6fd5SDavid Greenman fxp_lwcopy(&v, &p_rfa->link_addr); 1665a17c678eSDavid Greenman p_rfa->rfa_control &= ~FXP_RFA_CONTROL_EL; 1666a17c678eSDavid Greenman } else { 1667a17c678eSDavid Greenman sc->rfa_headm = m; 1668a17c678eSDavid Greenman } 1669a17c678eSDavid Greenman sc->rfa_tailm = m; 1670a17c678eSDavid Greenman 1671dfe61cf1SDavid Greenman return (m == oldm); 1672a17c678eSDavid Greenman } 1673a17c678eSDavid Greenman 16746ebc3153SDavid Greenman static volatile int 1675ba8c6fd5SDavid Greenman fxp_mdi_read(sc, phy, reg) 1676ba8c6fd5SDavid Greenman struct fxp_softc *sc; 1677dccee1a1SDavid Greenman int phy; 1678dccee1a1SDavid Greenman int reg; 1679dccee1a1SDavid Greenman { 1680dccee1a1SDavid Greenman int count = 10000; 16816ebc3153SDavid Greenman int value; 1682dccee1a1SDavid Greenman 1683ba8c6fd5SDavid Greenman CSR_WRITE_4(sc, FXP_CSR_MDICONTROL, 1684ba8c6fd5SDavid Greenman (FXP_MDI_READ << 26) | (reg << 16) | (phy << 21)); 1685dccee1a1SDavid Greenman 1686ba8c6fd5SDavid Greenman while (((value = CSR_READ_4(sc, FXP_CSR_MDICONTROL)) & 0x10000000) == 0 1687ba8c6fd5SDavid Greenman && count--) 16886ebc3153SDavid Greenman DELAY(10); 1689dccee1a1SDavid Greenman 1690dccee1a1SDavid Greenman if (count <= 0) 1691ba8c6fd5SDavid Greenman printf(FXP_FORMAT ": fxp_mdi_read: timed out\n", 1692ba8c6fd5SDavid Greenman FXP_ARGS(sc)); 1693dccee1a1SDavid Greenman 16946ebc3153SDavid Greenman return (value & 0xffff); 1695dccee1a1SDavid Greenman } 1696dccee1a1SDavid Greenman 1697dccee1a1SDavid Greenman static void 1698ba8c6fd5SDavid Greenman fxp_mdi_write(sc, phy, reg, value) 1699ba8c6fd5SDavid Greenman struct fxp_softc *sc; 1700dccee1a1SDavid Greenman int phy; 1701dccee1a1SDavid Greenman int reg; 1702dccee1a1SDavid Greenman int value; 1703dccee1a1SDavid Greenman { 1704dccee1a1SDavid Greenman int count = 10000; 1705dccee1a1SDavid Greenman 1706ba8c6fd5SDavid Greenman CSR_WRITE_4(sc, FXP_CSR_MDICONTROL, 1707ba8c6fd5SDavid Greenman (FXP_MDI_WRITE << 26) | (reg << 16) | (phy << 21) | 1708ba8c6fd5SDavid Greenman (value & 0xffff)); 1709dccee1a1SDavid Greenman 1710ba8c6fd5SDavid Greenman while((CSR_READ_4(sc, FXP_CSR_MDICONTROL) & 0x10000000) == 0 && 1711ba8c6fd5SDavid Greenman count--) 17126ebc3153SDavid Greenman DELAY(10); 1713dccee1a1SDavid Greenman 1714dccee1a1SDavid Greenman if (count <= 0) 1715ba8c6fd5SDavid Greenman printf(FXP_FORMAT ": fxp_mdi_write: timed out\n", 1716ba8c6fd5SDavid Greenman FXP_ARGS(sc)); 1717dccee1a1SDavid Greenman } 1718dccee1a1SDavid Greenman 1719dccee1a1SDavid Greenman static int 1720a17c678eSDavid Greenman fxp_ioctl(ifp, command, data) 1721a17c678eSDavid Greenman struct ifnet *ifp; 1722ba8c6fd5SDavid Greenman FXP_IOCTLCMD_TYPE command; 1723a17c678eSDavid Greenman caddr_t data; 1724a17c678eSDavid Greenman { 17259b44ff22SGarrett Wollman struct fxp_softc *sc = ifp->if_softc; 1726a17c678eSDavid Greenman struct ifreq *ifr = (struct ifreq *)data; 1727a17c678eSDavid Greenman int s, error = 0; 1728a17c678eSDavid Greenman 1729a17c678eSDavid Greenman s = splimp(); 1730a17c678eSDavid Greenman 1731a17c678eSDavid Greenman switch (command) { 1732a17c678eSDavid Greenman 1733a17c678eSDavid Greenman case SIOCSIFADDR: 1734ba8c6fd5SDavid Greenman #if !defined(__NetBSD__) 1735a17c678eSDavid Greenman case SIOCGIFADDR: 1736fb583156SDavid Greenman case SIOCSIFMTU: 1737ba8c6fd5SDavid Greenman #endif 1738fb583156SDavid Greenman error = ether_ioctl(ifp, command, data); 1739a17c678eSDavid Greenman break; 1740a17c678eSDavid Greenman 1741a17c678eSDavid Greenman case SIOCSIFFLAGS: 1742397f9dfeSDavid Greenman sc->all_mcasts = (ifp->if_flags & IFF_ALLMULTI) ? 1 : 0; 1743a17c678eSDavid Greenman 1744a17c678eSDavid Greenman /* 1745a17c678eSDavid Greenman * If interface is marked up and not running, then start it. 1746a17c678eSDavid Greenman * If it is marked down and running, stop it. 1747a17c678eSDavid Greenman * XXX If it's up then re-initialize it. This is so flags 1748a17c678eSDavid Greenman * such as IFF_PROMISC are handled. 1749a17c678eSDavid Greenman */ 1750a17c678eSDavid Greenman if (ifp->if_flags & IFF_UP) { 1751fb583156SDavid Greenman fxp_init(sc); 1752a17c678eSDavid Greenman } else { 1753a17c678eSDavid Greenman if (ifp->if_flags & IFF_RUNNING) 17544a5f1499SDavid Greenman fxp_stop(sc); 1755a17c678eSDavid Greenman } 1756a17c678eSDavid Greenman break; 1757a17c678eSDavid Greenman 1758a17c678eSDavid Greenman case SIOCADDMULTI: 1759a17c678eSDavid Greenman case SIOCDELMULTI: 1760397f9dfeSDavid Greenman sc->all_mcasts = (ifp->if_flags & IFF_ALLMULTI) ? 1 : 0; 1761ba8c6fd5SDavid Greenman #if defined(__NetBSD__) 1762ba8c6fd5SDavid Greenman error = (command == SIOCADDMULTI) ? 1763ba8c6fd5SDavid Greenman ether_addmulti(ifr, &sc->sc_ethercom) : 1764ba8c6fd5SDavid Greenman ether_delmulti(ifr, &sc->sc_ethercom); 1765ba8c6fd5SDavid Greenman 1766ba8c6fd5SDavid Greenman if (error == ENETRESET) { 1767ba8c6fd5SDavid Greenman /* 1768ba8c6fd5SDavid Greenman * Multicast list has changed; set the hardware 1769ba8c6fd5SDavid Greenman * filter accordingly. 1770ba8c6fd5SDavid Greenman */ 1771397f9dfeSDavid Greenman if (!sc->all_mcasts) 1772397f9dfeSDavid Greenman fxp_mc_setup(sc); 1773397f9dfeSDavid Greenman /* 1774397f9dfeSDavid Greenman * fxp_mc_setup() can turn on all_mcasts if we run 1775397f9dfeSDavid Greenman * out of space, so check it again rather than else {}. 1776397f9dfeSDavid Greenman */ 1777397f9dfeSDavid Greenman if (sc->all_mcasts) 1778ba8c6fd5SDavid Greenman fxp_init(sc); 1779ba8c6fd5SDavid Greenman error = 0; 1780ba8c6fd5SDavid Greenman } 1781ba8c6fd5SDavid Greenman #else /* __FreeBSD__ */ 1782a17c678eSDavid Greenman /* 1783a17c678eSDavid Greenman * Multicast list has changed; set the hardware filter 1784a17c678eSDavid Greenman * accordingly. 1785a17c678eSDavid Greenman */ 1786397f9dfeSDavid Greenman if (!sc->all_mcasts) 1787397f9dfeSDavid Greenman fxp_mc_setup(sc); 1788397f9dfeSDavid Greenman /* 1789397f9dfeSDavid Greenman * fxp_mc_setup() can turn on sc->all_mcasts, so check it 1790397f9dfeSDavid Greenman * again rather than else {}. 1791397f9dfeSDavid Greenman */ 1792397f9dfeSDavid Greenman if (sc->all_mcasts) 1793fb583156SDavid Greenman fxp_init(sc); 1794a17c678eSDavid Greenman error = 0; 1795ba8c6fd5SDavid Greenman #endif /* __NetBSD__ */ 1796ba8c6fd5SDavid Greenman break; 1797ba8c6fd5SDavid Greenman 1798ba8c6fd5SDavid Greenman case SIOCSIFMEDIA: 1799ba8c6fd5SDavid Greenman case SIOCGIFMEDIA: 1800ba8c6fd5SDavid Greenman error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, command); 1801a17c678eSDavid Greenman break; 1802a17c678eSDavid Greenman 1803a17c678eSDavid Greenman default: 1804a17c678eSDavid Greenman error = EINVAL; 1805a17c678eSDavid Greenman } 1806a17c678eSDavid Greenman (void) splx(s); 1807a17c678eSDavid Greenman return (error); 1808a17c678eSDavid Greenman } 1809397f9dfeSDavid Greenman 1810397f9dfeSDavid Greenman /* 1811397f9dfeSDavid Greenman * Program the multicast filter. 1812397f9dfeSDavid Greenman * 1813397f9dfeSDavid Greenman * We have an artificial restriction that the multicast setup command 1814397f9dfeSDavid Greenman * must be the first command in the chain, so we take steps to ensure 18153114fdb4SDavid Greenman * this. By requiring this, it allows us to keep up the performance of 1816397f9dfeSDavid Greenman * the pre-initialized command ring (esp. link pointers) by not actually 1817dc733423SDag-Erling Smørgrav * inserting the mcsetup command in the ring - i.e. its link pointer 1818397f9dfeSDavid Greenman * points to the TxCB ring, but the mcsetup descriptor itself is not part 1819397f9dfeSDavid Greenman * of it. We then can do 'CU_START' on the mcsetup descriptor and have it 1820397f9dfeSDavid Greenman * lead into the regular TxCB ring when it completes. 1821397f9dfeSDavid Greenman * 1822397f9dfeSDavid Greenman * This function must be called at splimp. 1823397f9dfeSDavid Greenman */ 1824397f9dfeSDavid Greenman static void 1825397f9dfeSDavid Greenman fxp_mc_setup(sc) 1826397f9dfeSDavid Greenman struct fxp_softc *sc; 1827397f9dfeSDavid Greenman { 1828397f9dfeSDavid Greenman struct fxp_cb_mcs *mcsp = sc->mcsp; 1829397f9dfeSDavid Greenman struct ifnet *ifp = &sc->sc_if; 1830397f9dfeSDavid Greenman struct ifmultiaddr *ifma; 1831397f9dfeSDavid Greenman int nmcasts; 1832397f9dfeSDavid Greenman 18333114fdb4SDavid Greenman /* 18343114fdb4SDavid Greenman * If there are queued commands, we must wait until they are all 18353114fdb4SDavid Greenman * completed. If we are already waiting, then add a NOP command 18363114fdb4SDavid Greenman * with interrupt option so that we're notified when all commands 18373114fdb4SDavid Greenman * have been completed - fxp_start() ensures that no additional 18383114fdb4SDavid Greenman * TX commands will be added when need_mcsetup is true. 18393114fdb4SDavid Greenman */ 1840397f9dfeSDavid Greenman if (sc->tx_queued) { 18413114fdb4SDavid Greenman struct fxp_cb_tx *txp; 18423114fdb4SDavid Greenman 18433114fdb4SDavid Greenman /* 18443114fdb4SDavid Greenman * need_mcsetup will be true if we are already waiting for the 18453114fdb4SDavid Greenman * NOP command to be completed (see below). In this case, bail. 18463114fdb4SDavid Greenman */ 18473114fdb4SDavid Greenman if (sc->need_mcsetup) 18483114fdb4SDavid Greenman return; 1849397f9dfeSDavid Greenman sc->need_mcsetup = 1; 18503114fdb4SDavid Greenman 18513114fdb4SDavid Greenman /* 18523114fdb4SDavid Greenman * Add a NOP command with interrupt so that we are notified when all 18533114fdb4SDavid Greenman * TX commands have been processed. 18543114fdb4SDavid Greenman */ 18553114fdb4SDavid Greenman txp = sc->cbl_last->next; 18563114fdb4SDavid Greenman txp->mb_head = NULL; 18573114fdb4SDavid Greenman txp->cb_status = 0; 18583114fdb4SDavid Greenman txp->cb_command = FXP_CB_COMMAND_NOP | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I; 18593114fdb4SDavid Greenman /* 18603114fdb4SDavid Greenman * Advance the end of list forward. 18613114fdb4SDavid Greenman */ 18623114fdb4SDavid Greenman sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S; 18633114fdb4SDavid Greenman sc->cbl_last = txp; 18643114fdb4SDavid Greenman sc->tx_queued++; 18653114fdb4SDavid Greenman /* 18663114fdb4SDavid Greenman * Issue a resume in case the CU has just suspended. 18673114fdb4SDavid Greenman */ 18683114fdb4SDavid Greenman fxp_scb_wait(sc); 18693114fdb4SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_RESUME); 18703114fdb4SDavid Greenman /* 18713114fdb4SDavid Greenman * Set a 5 second timer just in case we don't hear from the 18723114fdb4SDavid Greenman * card again. 18733114fdb4SDavid Greenman */ 18743114fdb4SDavid Greenman ifp->if_timer = 5; 18753114fdb4SDavid Greenman 1876397f9dfeSDavid Greenman return; 1877397f9dfeSDavid Greenman } 1878397f9dfeSDavid Greenman sc->need_mcsetup = 0; 1879397f9dfeSDavid Greenman 1880397f9dfeSDavid Greenman /* 1881397f9dfeSDavid Greenman * Initialize multicast setup descriptor. 1882397f9dfeSDavid Greenman */ 1883397f9dfeSDavid Greenman mcsp->next = sc->cbl_base; 1884397f9dfeSDavid Greenman mcsp->mb_head = NULL; 1885397f9dfeSDavid Greenman mcsp->cb_status = 0; 18863114fdb4SDavid Greenman mcsp->cb_command = FXP_CB_COMMAND_MCAS | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I; 1887397f9dfeSDavid Greenman mcsp->link_addr = vtophys(&sc->cbl_base->cb_status); 1888397f9dfeSDavid Greenman 1889397f9dfeSDavid Greenman nmcasts = 0; 1890397f9dfeSDavid Greenman if (!sc->all_mcasts) { 1891397f9dfeSDavid Greenman for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 1892397f9dfeSDavid Greenman ifma = ifma->ifma_link.le_next) { 1893397f9dfeSDavid Greenman if (ifma->ifma_addr->sa_family != AF_LINK) 1894397f9dfeSDavid Greenman continue; 1895397f9dfeSDavid Greenman if (nmcasts >= MAXMCADDR) { 1896397f9dfeSDavid Greenman sc->all_mcasts = 1; 1897397f9dfeSDavid Greenman nmcasts = 0; 1898397f9dfeSDavid Greenman break; 1899397f9dfeSDavid Greenman } 1900397f9dfeSDavid Greenman bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 19018aef1712SMatthew Dillon (volatile void *) &sc->mcsp->mc_addr[nmcasts][0], 6); 1902397f9dfeSDavid Greenman nmcasts++; 1903397f9dfeSDavid Greenman } 1904397f9dfeSDavid Greenman } 1905397f9dfeSDavid Greenman mcsp->mc_cnt = nmcasts * 6; 1906397f9dfeSDavid Greenman sc->cbl_first = sc->cbl_last = (struct fxp_cb_tx *) mcsp; 1907397f9dfeSDavid Greenman sc->tx_queued = 1; 1908397f9dfeSDavid Greenman 1909397f9dfeSDavid Greenman /* 1910397f9dfeSDavid Greenman * Wait until command unit is not active. This should never 1911397f9dfeSDavid Greenman * be the case when nothing is queued, but make sure anyway. 1912397f9dfeSDavid Greenman */ 1913397f9dfeSDavid Greenman while ((CSR_READ_1(sc, FXP_CSR_SCB_RUSCUS) >> 6) == 1914397f9dfeSDavid Greenman FXP_SCB_CUS_ACTIVE) ; 1915397f9dfeSDavid Greenman 1916397f9dfeSDavid Greenman /* 1917397f9dfeSDavid Greenman * Start the multicast setup command. 1918397f9dfeSDavid Greenman */ 1919397f9dfeSDavid Greenman fxp_scb_wait(sc); 1920397f9dfeSDavid Greenman CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&mcsp->cb_status)); 1921397f9dfeSDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); 1922397f9dfeSDavid Greenman 19233114fdb4SDavid Greenman ifp->if_timer = 2; 1924397f9dfeSDavid Greenman return; 1925397f9dfeSDavid Greenman } 1926