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 * 30c3aac50fSPeter Wemm * $FreeBSD$ 31a17c678eSDavid Greenman */ 32a17c678eSDavid Greenman 33a17c678eSDavid Greenman /* 34ae12cddaSDavid Greenman * Intel EtherExpress Pro/100B PCI Fast Ethernet driver 35a17c678eSDavid Greenman */ 36a17c678eSDavid Greenman 37a17c678eSDavid Greenman #include <sys/param.h> 38a17c678eSDavid Greenman #include <sys/systm.h> 39a17c678eSDavid Greenman #include <sys/mbuf.h> 40a17c678eSDavid Greenman #include <sys/malloc.h> 41a17c678eSDavid Greenman #include <sys/kernel.h> 424458ac71SBruce Evans #include <sys/socket.h> 43a17c678eSDavid Greenman 44a17c678eSDavid Greenman #include <net/if.h> 45397f9dfeSDavid Greenman #include <net/if_dl.h> 46ba8c6fd5SDavid Greenman #include <net/if_media.h> 47a17c678eSDavid Greenman 48a17c678eSDavid Greenman #ifdef NS 49a17c678eSDavid Greenman #include <netns/ns.h> 50a17c678eSDavid Greenman #include <netns/ns_if.h> 51a17c678eSDavid Greenman #endif 52a17c678eSDavid Greenman 53a17c678eSDavid Greenman #include <net/bpf.h> 54a17c678eSDavid Greenman 55ba8c6fd5SDavid Greenman #if defined(__NetBSD__) 56ba8c6fd5SDavid Greenman 57ba8c6fd5SDavid Greenman #include <sys/ioctl.h> 58ba8c6fd5SDavid Greenman #include <sys/errno.h> 59ba8c6fd5SDavid Greenman #include <sys/device.h> 60ba8c6fd5SDavid Greenman 61ba8c6fd5SDavid Greenman #include <net/if_dl.h> 62ba8c6fd5SDavid Greenman #include <net/if_ether.h> 63ba8c6fd5SDavid Greenman 64ba8c6fd5SDavid Greenman #include <netinet/if_inarp.h> 65ba8c6fd5SDavid Greenman 66ba8c6fd5SDavid Greenman #include <vm/vm.h> 67ba8c6fd5SDavid Greenman 68ba8c6fd5SDavid Greenman #include <machine/cpu.h> 69ba8c6fd5SDavid Greenman #include <machine/bus.h> 70ba8c6fd5SDavid Greenman #include <machine/intr.h> 71ba8c6fd5SDavid Greenman 72ba8c6fd5SDavid Greenman #include <dev/pci/if_fxpreg.h> 73ba8c6fd5SDavid Greenman #include <dev/pci/if_fxpvar.h> 74ba8c6fd5SDavid Greenman 75ba8c6fd5SDavid Greenman #include <dev/pci/pcivar.h> 76ba8c6fd5SDavid Greenman #include <dev/pci/pcireg.h> 77ba8c6fd5SDavid Greenman #include <dev/pci/pcidevs.h> 78ba8c6fd5SDavid Greenman 79ba8c6fd5SDavid Greenman 80ba8c6fd5SDavid Greenman #else /* __FreeBSD__ */ 81ba8c6fd5SDavid Greenman 82ba8c6fd5SDavid Greenman #include <sys/sockio.h> 836182fdbdSPeter Wemm #include <sys/bus.h> 846182fdbdSPeter Wemm #include <machine/bus.h> 856182fdbdSPeter Wemm #include <sys/rman.h> 866182fdbdSPeter Wemm #include <machine/resource.h> 87ba8c6fd5SDavid Greenman 881d5e9e22SEivind Eklund #include <net/ethernet.h> 891d5e9e22SEivind Eklund #include <net/if_arp.h> 90ba8c6fd5SDavid Greenman 91dfe61cf1SDavid Greenman #include <vm/vm.h> /* for vtophys */ 92efeaf95aSDavid Greenman #include <vm/pmap.h> /* for vtophys */ 93dfe61cf1SDavid Greenman #include <machine/clock.h> /* for DELAY */ 94a17c678eSDavid Greenman 95a17c678eSDavid Greenman #include <pci/pcivar.h> 96df373873SWes Peters #include <pci/pcireg.h> /* for PCIM_CMD_xxx */ 97a17c678eSDavid Greenman #include <pci/if_fxpreg.h> 98ba8c6fd5SDavid Greenman #include <pci/if_fxpvar.h> 99a17c678eSDavid Greenman 100ba8c6fd5SDavid Greenman #endif /* __NetBSD__ */ 101a17c678eSDavid Greenman 1024fc1dda9SAndrew Gallatin #ifdef __alpha__ /* XXX */ 1034fc1dda9SAndrew Gallatin /* XXX XXX NEED REAL DMA MAPPING SUPPORT XXX XXX */ 1044fc1dda9SAndrew Gallatin #undef vtophys 1054fc1dda9SAndrew Gallatin #define vtophys(va) alpha_XXX_dmamap((vm_offset_t)(va)) 1064fc1dda9SAndrew Gallatin #endif /* __alpha__ */ 1074fc1dda9SAndrew Gallatin 108ba8c6fd5SDavid Greenman /* 109ba8c6fd5SDavid Greenman * NOTE! On the Alpha, we have an alignment constraint. The 110ba8c6fd5SDavid Greenman * card DMAs the packet immediately following the RFA. However, 111ba8c6fd5SDavid Greenman * the first thing in the packet is a 14-byte Ethernet header. 112ba8c6fd5SDavid Greenman * This means that the packet is misaligned. To compensate, 113ba8c6fd5SDavid Greenman * we actually offset the RFA 2 bytes into the cluster. This 114ba8c6fd5SDavid Greenman * alignes the packet after the Ethernet header at a 32-bit 115ba8c6fd5SDavid Greenman * boundary. HOWEVER! This means that the RFA is misaligned! 116ba8c6fd5SDavid Greenman */ 117ba8c6fd5SDavid Greenman #define RFA_ALIGNMENT_FUDGE 2 118ba8c6fd5SDavid Greenman 119ba8c6fd5SDavid Greenman /* 120ba8c6fd5SDavid Greenman * Inline function to copy a 16-bit aligned 32-bit quantity. 121ba8c6fd5SDavid Greenman */ 122ba8c6fd5SDavid Greenman static __inline void fxp_lwcopy __P((volatile u_int32_t *, 123ba8c6fd5SDavid Greenman volatile u_int32_t *)); 124ba8c6fd5SDavid Greenman static __inline void 125ba8c6fd5SDavid Greenman fxp_lwcopy(src, dst) 126ba8c6fd5SDavid Greenman volatile u_int32_t *src, *dst; 127ba8c6fd5SDavid Greenman { 128fe08c21aSMatthew Dillon volatile u_int16_t *a = (volatile u_int16_t *)src; 129fe08c21aSMatthew Dillon volatile u_int16_t *b = (volatile u_int16_t *)dst; 130ba8c6fd5SDavid Greenman 131ba8c6fd5SDavid Greenman b[0] = a[0]; 132ba8c6fd5SDavid Greenman b[1] = a[1]; 133ba8c6fd5SDavid Greenman } 134a17c678eSDavid Greenman 135a17c678eSDavid Greenman /* 136a17c678eSDavid Greenman * Template for default configuration parameters. 137a17c678eSDavid Greenman * See struct fxp_cb_config for the bit definitions. 138a17c678eSDavid Greenman */ 139a17c678eSDavid Greenman static u_char fxp_cb_config_template[] = { 140a17c678eSDavid Greenman 0x0, 0x0, /* cb_status */ 141a17c678eSDavid Greenman 0x80, 0x2, /* cb_command */ 142a17c678eSDavid Greenman 0xff, 0xff, 0xff, 0xff, /* link_addr */ 143a17c678eSDavid Greenman 0x16, /* 0 */ 144a17c678eSDavid Greenman 0x8, /* 1 */ 145a17c678eSDavid Greenman 0x0, /* 2 */ 146a17c678eSDavid Greenman 0x0, /* 3 */ 147a17c678eSDavid Greenman 0x0, /* 4 */ 148a17c678eSDavid Greenman 0x80, /* 5 */ 149a17c678eSDavid Greenman 0xb2, /* 6 */ 150a17c678eSDavid Greenman 0x3, /* 7 */ 151a17c678eSDavid Greenman 0x1, /* 8 */ 152a17c678eSDavid Greenman 0x0, /* 9 */ 153a17c678eSDavid Greenman 0x26, /* 10 */ 154a17c678eSDavid Greenman 0x0, /* 11 */ 155a17c678eSDavid Greenman 0x60, /* 12 */ 156a17c678eSDavid Greenman 0x0, /* 13 */ 157a17c678eSDavid Greenman 0xf2, /* 14 */ 158a17c678eSDavid Greenman 0x48, /* 15 */ 159a17c678eSDavid Greenman 0x0, /* 16 */ 160a17c678eSDavid Greenman 0x40, /* 17 */ 161a17c678eSDavid Greenman 0xf3, /* 18 */ 162a17c678eSDavid Greenman 0x0, /* 19 */ 163a17c678eSDavid Greenman 0x3f, /* 20 */ 164397f9dfeSDavid Greenman 0x5 /* 21 */ 165a17c678eSDavid Greenman }; 166a17c678eSDavid Greenman 167ba8c6fd5SDavid Greenman /* Supported media types. */ 168ba8c6fd5SDavid Greenman struct fxp_supported_media { 169ba8c6fd5SDavid Greenman const int fsm_phy; /* PHY type */ 170ba8c6fd5SDavid Greenman const int *fsm_media; /* the media array */ 171ba8c6fd5SDavid Greenman const int fsm_nmedia; /* the number of supported media */ 172ba8c6fd5SDavid Greenman const int fsm_defmedia; /* default media for this PHY */ 173ba8c6fd5SDavid Greenman }; 174ba8c6fd5SDavid Greenman 175303b270bSEivind Eklund static const int fxp_media_standard[] = { 176ba8c6fd5SDavid Greenman IFM_ETHER|IFM_10_T, 177ba8c6fd5SDavid Greenman IFM_ETHER|IFM_10_T|IFM_FDX, 178ba8c6fd5SDavid Greenman IFM_ETHER|IFM_100_TX, 179ba8c6fd5SDavid Greenman IFM_ETHER|IFM_100_TX|IFM_FDX, 180ba8c6fd5SDavid Greenman IFM_ETHER|IFM_AUTO, 181ba8c6fd5SDavid Greenman }; 182ba8c6fd5SDavid Greenman #define FXP_MEDIA_STANDARD_DEFMEDIA (IFM_ETHER|IFM_AUTO) 183ba8c6fd5SDavid Greenman 184303b270bSEivind Eklund static const int fxp_media_default[] = { 185ba8c6fd5SDavid Greenman IFM_ETHER|IFM_MANUAL, /* XXX IFM_AUTO ? */ 186ba8c6fd5SDavid Greenman }; 187ba8c6fd5SDavid Greenman #define FXP_MEDIA_DEFAULT_DEFMEDIA (IFM_ETHER|IFM_MANUAL) 188ba8c6fd5SDavid Greenman 189303b270bSEivind Eklund static const struct fxp_supported_media fxp_media[] = { 190ba8c6fd5SDavid Greenman { FXP_PHY_DP83840, fxp_media_standard, 191ba8c6fd5SDavid Greenman sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), 192ba8c6fd5SDavid Greenman FXP_MEDIA_STANDARD_DEFMEDIA }, 193ba8c6fd5SDavid Greenman { FXP_PHY_DP83840A, fxp_media_standard, 194ba8c6fd5SDavid Greenman sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), 195ba8c6fd5SDavid Greenman FXP_MEDIA_STANDARD_DEFMEDIA }, 19692924291SDavid Greenman { FXP_PHY_82553A, fxp_media_standard, 19792924291SDavid Greenman sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), 19892924291SDavid Greenman FXP_MEDIA_STANDARD_DEFMEDIA }, 19992924291SDavid Greenman { FXP_PHY_82553C, fxp_media_standard, 20092924291SDavid Greenman sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), 20192924291SDavid Greenman FXP_MEDIA_STANDARD_DEFMEDIA }, 202ba8c6fd5SDavid Greenman { FXP_PHY_82555, fxp_media_standard, 203ba8c6fd5SDavid Greenman sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), 204ba8c6fd5SDavid Greenman FXP_MEDIA_STANDARD_DEFMEDIA }, 20592924291SDavid Greenman { FXP_PHY_82555B, fxp_media_standard, 20692924291SDavid Greenman sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), 20792924291SDavid Greenman FXP_MEDIA_STANDARD_DEFMEDIA }, 208ba8c6fd5SDavid Greenman { FXP_PHY_80C24, fxp_media_default, 209ba8c6fd5SDavid Greenman sizeof(fxp_media_default) / sizeof(fxp_media_default[0]), 210ba8c6fd5SDavid Greenman FXP_MEDIA_DEFAULT_DEFMEDIA }, 211ba8c6fd5SDavid Greenman }; 212ba8c6fd5SDavid Greenman #define NFXPMEDIA (sizeof(fxp_media) / sizeof(fxp_media[0])) 213ba8c6fd5SDavid Greenman 214ba8c6fd5SDavid Greenman static int fxp_mediachange __P((struct ifnet *)); 215ba8c6fd5SDavid Greenman static void fxp_mediastatus __P((struct ifnet *, struct ifmediareq *)); 216303b270bSEivind Eklund static void fxp_set_media __P((struct fxp_softc *, int)); 217c1087c13SBruce Evans static __inline void fxp_scb_wait __P((struct fxp_softc *)); 218ba8c6fd5SDavid Greenman static FXP_INTR_TYPE fxp_intr __P((void *)); 219a17c678eSDavid Greenman static void fxp_start __P((struct ifnet *)); 220ba8c6fd5SDavid Greenman static int fxp_ioctl __P((struct ifnet *, 221ba8c6fd5SDavid Greenman FXP_IOCTLCMD_TYPE, caddr_t)); 222fb583156SDavid Greenman static void fxp_init __P((void *)); 2234a5f1499SDavid Greenman static void fxp_stop __P((struct fxp_softc *)); 2244a5f1499SDavid Greenman static void fxp_watchdog __P((struct ifnet *)); 225a17c678eSDavid Greenman static int fxp_add_rfabuf __P((struct fxp_softc *, struct mbuf *)); 226ba8c6fd5SDavid Greenman static int fxp_mdi_read __P((struct fxp_softc *, int, int)); 227ba8c6fd5SDavid Greenman static void fxp_mdi_write __P((struct fxp_softc *, int, int, int)); 228e9bf2fa7SDavid Greenman static void fxp_autosize_eeprom __P((struct fxp_softc *)); 229ba8c6fd5SDavid Greenman static void fxp_read_eeprom __P((struct fxp_softc *, u_int16_t *, 230ba8c6fd5SDavid Greenman int, int)); 231ba8c6fd5SDavid Greenman static int fxp_attach_common __P((struct fxp_softc *, u_int8_t *)); 232303b270bSEivind Eklund static void fxp_stats_update __P((void *)); 233397f9dfeSDavid Greenman static void fxp_mc_setup __P((struct fxp_softc *)); 234a17c678eSDavid Greenman 235a17c678eSDavid Greenman /* 236f9be9005SDavid Greenman * Set initial transmit threshold at 64 (512 bytes). This is 237f9be9005SDavid Greenman * increased by 64 (512 bytes) at a time, to maximum of 192 238f9be9005SDavid Greenman * (1536 bytes), if an underrun occurs. 239f9be9005SDavid Greenman */ 240f9be9005SDavid Greenman static int tx_threshold = 64; 241f9be9005SDavid Greenman 242f9be9005SDavid Greenman /* 243a17c678eSDavid Greenman * Number of transmit control blocks. This determines the number 244a17c678eSDavid Greenman * of transmit buffers that can be chained in the CB list. 245a17c678eSDavid Greenman * This must be a power of two. 246a17c678eSDavid Greenman */ 2471cd443acSDavid Greenman #define FXP_NTXCB 128 248a17c678eSDavid Greenman 249a17c678eSDavid Greenman /* 2503114fdb4SDavid Greenman * Number of completed TX commands at which point an interrupt 2513114fdb4SDavid Greenman * will be generated to garbage collect the attached buffers. 2523114fdb4SDavid Greenman * Must be at least one less than FXP_NTXCB, and should be 2533114fdb4SDavid Greenman * enough less so that the transmitter doesn't becomes idle 2543114fdb4SDavid Greenman * during the buffer rundown (which would reduce performance). 2553114fdb4SDavid Greenman */ 2563114fdb4SDavid Greenman #define FXP_CXINT_THRESH 120 2573114fdb4SDavid Greenman 2583114fdb4SDavid Greenman /* 259a17c678eSDavid Greenman * TxCB list index mask. This is used to do list wrap-around. 260a17c678eSDavid Greenman */ 261a17c678eSDavid Greenman #define FXP_TXCB_MASK (FXP_NTXCB - 1) 262a17c678eSDavid Greenman 263a17c678eSDavid Greenman /* 264a17c678eSDavid Greenman * Number of receive frame area buffers. These are large so chose 265a17c678eSDavid Greenman * wisely. 266a17c678eSDavid Greenman */ 2676f5818b0SDavid Greenman #define FXP_NRFABUFS 64 268a17c678eSDavid Greenman 269dfe61cf1SDavid Greenman /* 270397f9dfeSDavid Greenman * Maximum number of seconds that the receiver can be idle before we 271397f9dfeSDavid Greenman * assume it's dead and attempt to reset it by reprogramming the 272397f9dfeSDavid Greenman * multicast filter. This is part of a work-around for a bug in the 273397f9dfeSDavid Greenman * NIC. See fxp_stats_update(). 274397f9dfeSDavid Greenman */ 275397f9dfeSDavid Greenman #define FXP_MAX_RX_IDLE 15 276397f9dfeSDavid Greenman 277397f9dfeSDavid Greenman /* 278dfe61cf1SDavid Greenman * Wait for the previous command to be accepted (but not necessarily 279dfe61cf1SDavid Greenman * completed). 280dfe61cf1SDavid Greenman */ 281c1087c13SBruce Evans static __inline void 282ba8c6fd5SDavid Greenman fxp_scb_wait(sc) 283ba8c6fd5SDavid Greenman struct fxp_softc *sc; 284a17c678eSDavid Greenman { 285a17c678eSDavid Greenman int i = 10000; 286a17c678eSDavid Greenman 287397f9dfeSDavid Greenman while (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) && --i); 288a17c678eSDavid Greenman } 289a17c678eSDavid Greenman 290ba8c6fd5SDavid Greenman /************************************************************* 291ba8c6fd5SDavid Greenman * Operating system-specific autoconfiguration glue 292ba8c6fd5SDavid Greenman *************************************************************/ 293ba8c6fd5SDavid Greenman 294ba8c6fd5SDavid Greenman #if defined(__NetBSD__) 295ba8c6fd5SDavid Greenman 296ba8c6fd5SDavid Greenman #ifdef __BROKEN_INDIRECT_CONFIG 297ba8c6fd5SDavid Greenman static int fxp_match __P((struct device *, void *, void *)); 298ba8c6fd5SDavid Greenman #else 299ba8c6fd5SDavid Greenman static int fxp_match __P((struct device *, struct cfdata *, void *)); 300ba8c6fd5SDavid Greenman #endif 301ba8c6fd5SDavid Greenman static void fxp_attach __P((struct device *, struct device *, void *)); 302ba8c6fd5SDavid Greenman 303ba8c6fd5SDavid Greenman static void fxp_shutdown __P((void *)); 304ba8c6fd5SDavid Greenman 305ba8c6fd5SDavid Greenman /* Compensate for lack of a generic ether_ioctl() */ 306ba8c6fd5SDavid Greenman static int fxp_ether_ioctl __P((struct ifnet *, 307ba8c6fd5SDavid Greenman FXP_IOCTLCMD_TYPE, caddr_t)); 308ba8c6fd5SDavid Greenman #define ether_ioctl fxp_ether_ioctl 309ba8c6fd5SDavid Greenman 310ba8c6fd5SDavid Greenman struct cfattach fxp_ca = { 311ba8c6fd5SDavid Greenman sizeof(struct fxp_softc), fxp_match, fxp_attach 312ba8c6fd5SDavid Greenman }; 313ba8c6fd5SDavid Greenman 314ba8c6fd5SDavid Greenman struct cfdriver fxp_cd = { 315ba8c6fd5SDavid Greenman NULL, "fxp", DV_IFNET 316ba8c6fd5SDavid Greenman }; 317ba8c6fd5SDavid Greenman 318ba8c6fd5SDavid Greenman /* 319ba8c6fd5SDavid Greenman * Check if a device is an 82557. 320ba8c6fd5SDavid Greenman */ 321ba8c6fd5SDavid Greenman static int 322ba8c6fd5SDavid Greenman fxp_match(parent, match, aux) 323ba8c6fd5SDavid Greenman struct device *parent; 324ba8c6fd5SDavid Greenman #ifdef __BROKEN_INDIRECT_CONFIG 325ba8c6fd5SDavid Greenman void *match; 326ba8c6fd5SDavid Greenman #else 327ba8c6fd5SDavid Greenman struct cfdata *match; 328ba8c6fd5SDavid Greenman #endif 329ba8c6fd5SDavid Greenman void *aux; 330ba8c6fd5SDavid Greenman { 331ba8c6fd5SDavid Greenman struct pci_attach_args *pa = aux; 332ba8c6fd5SDavid Greenman 333ba8c6fd5SDavid Greenman if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL) 334ba8c6fd5SDavid Greenman return (0); 335ba8c6fd5SDavid Greenman 336ba8c6fd5SDavid Greenman switch (PCI_PRODUCT(pa->pa_id)) { 337ba8c6fd5SDavid Greenman case PCI_PRODUCT_INTEL_82557: 338ba8c6fd5SDavid Greenman return (1); 339ba8c6fd5SDavid Greenman } 340ba8c6fd5SDavid Greenman 341ba8c6fd5SDavid Greenman return (0); 342ba8c6fd5SDavid Greenman } 343ba8c6fd5SDavid Greenman 344ba8c6fd5SDavid Greenman static void 345ba8c6fd5SDavid Greenman fxp_attach(parent, self, aux) 346ba8c6fd5SDavid Greenman struct device *parent, *self; 347ba8c6fd5SDavid Greenman void *aux; 348ba8c6fd5SDavid Greenman { 349ba8c6fd5SDavid Greenman struct fxp_softc *sc = (struct fxp_softc *)self; 350ba8c6fd5SDavid Greenman struct pci_attach_args *pa = aux; 351ba8c6fd5SDavid Greenman pci_chipset_tag_t pc = pa->pa_pc; 352ba8c6fd5SDavid Greenman pci_intr_handle_t ih; 353ba8c6fd5SDavid Greenman const char *intrstr = NULL; 354ba8c6fd5SDavid Greenman u_int8_t enaddr[6]; 355ba8c6fd5SDavid Greenman struct ifnet *ifp; 356ba8c6fd5SDavid Greenman 357ba8c6fd5SDavid Greenman /* 358ba8c6fd5SDavid Greenman * Map control/status registers. 359ba8c6fd5SDavid Greenman */ 360ba8c6fd5SDavid Greenman if (pci_mapreg_map(pa, FXP_PCI_MMBA, PCI_MAPREG_TYPE_MEM, 0, 361ba8c6fd5SDavid Greenman &sc->sc_st, &sc->sc_sh, NULL, NULL)) { 362ba8c6fd5SDavid Greenman printf(": can't map registers\n"); 363ba8c6fd5SDavid Greenman return; 364ba8c6fd5SDavid Greenman } 365ba8c6fd5SDavid Greenman printf(": Intel EtherExpress Pro 10/100B Ethernet\n"); 366ba8c6fd5SDavid Greenman 367ba8c6fd5SDavid Greenman /* 368ba8c6fd5SDavid Greenman * Allocate our interrupt. 369ba8c6fd5SDavid Greenman */ 370ba8c6fd5SDavid Greenman if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin, 371ba8c6fd5SDavid Greenman pa->pa_intrline, &ih)) { 372ba8c6fd5SDavid Greenman printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname); 373ba8c6fd5SDavid Greenman return; 374ba8c6fd5SDavid Greenman } 375ba8c6fd5SDavid Greenman intrstr = pci_intr_string(pc, ih); 376ba8c6fd5SDavid Greenman sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, fxp_intr, sc); 377ba8c6fd5SDavid Greenman if (sc->sc_ih == NULL) { 378ba8c6fd5SDavid Greenman printf("%s: couldn't establish interrupt", 379ba8c6fd5SDavid Greenman sc->sc_dev.dv_xname); 380ba8c6fd5SDavid Greenman if (intrstr != NULL) 381ba8c6fd5SDavid Greenman printf(" at %s", intrstr); 382ba8c6fd5SDavid Greenman printf("\n"); 383ba8c6fd5SDavid Greenman return; 384ba8c6fd5SDavid Greenman } 385ba8c6fd5SDavid Greenman printf("%s: interrupting at %s\n", sc->sc_dev.dv_xname, intrstr); 386ba8c6fd5SDavid Greenman 387ba8c6fd5SDavid Greenman /* Do generic parts of attach. */ 388ba8c6fd5SDavid Greenman if (fxp_attach_common(sc, enaddr)) { 389ba8c6fd5SDavid Greenman /* Failed! */ 390ba8c6fd5SDavid Greenman return; 391ba8c6fd5SDavid Greenman } 392ba8c6fd5SDavid Greenman 393ba8c6fd5SDavid Greenman printf("%s: Ethernet address %s%s\n", sc->sc_dev.dv_xname, 394ba8c6fd5SDavid Greenman ether_sprintf(enaddr), sc->phy_10Mbps_only ? ", 10Mbps" : ""); 395ba8c6fd5SDavid Greenman 396ba8c6fd5SDavid Greenman ifp = &sc->sc_ethercom.ec_if; 397ba8c6fd5SDavid Greenman bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); 398ba8c6fd5SDavid Greenman ifp->if_softc = sc; 399ba8c6fd5SDavid Greenman ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 400ba8c6fd5SDavid Greenman ifp->if_ioctl = fxp_ioctl; 401ba8c6fd5SDavid Greenman ifp->if_start = fxp_start; 402ba8c6fd5SDavid Greenman ifp->if_watchdog = fxp_watchdog; 403ba8c6fd5SDavid Greenman 404ba8c6fd5SDavid Greenman /* 405ba8c6fd5SDavid Greenman * Attach the interface. 406ba8c6fd5SDavid Greenman */ 407ba8c6fd5SDavid Greenman if_attach(ifp); 408483b9871SDavid Greenman /* 4093114fdb4SDavid Greenman * Let the system queue as many packets as we have available 4103114fdb4SDavid Greenman * TX descriptors. 411483b9871SDavid Greenman */ 4123114fdb4SDavid Greenman ifp->if_snd.ifq_maxlen = FXP_NTXCB - 1; 413ba8c6fd5SDavid Greenman ether_ifattach(ifp, enaddr); 414ba8c6fd5SDavid Greenman bpfattach(&sc->sc_ethercom.ec_if.if_bpf, ifp, DLT_EN10MB, 415ba8c6fd5SDavid Greenman sizeof(struct ether_header)); 416ba8c6fd5SDavid Greenman 417ba8c6fd5SDavid Greenman /* 418ba8c6fd5SDavid Greenman * Add shutdown hook so that DMA is disabled prior to reboot. Not 419ba8c6fd5SDavid Greenman * doing do could allow DMA to corrupt kernel memory during the 420ba8c6fd5SDavid Greenman * reboot before the driver initializes. 421ba8c6fd5SDavid Greenman */ 422ba8c6fd5SDavid Greenman shutdownhook_establish(fxp_shutdown, sc); 423ba8c6fd5SDavid Greenman } 424ba8c6fd5SDavid Greenman 425ba8c6fd5SDavid Greenman /* 426ba8c6fd5SDavid Greenman * Device shutdown routine. Called at system shutdown after sync. The 427ba8c6fd5SDavid Greenman * main purpose of this routine is to shut off receiver DMA so that 428ba8c6fd5SDavid Greenman * kernel memory doesn't get clobbered during warmboot. 429ba8c6fd5SDavid Greenman */ 430ba8c6fd5SDavid Greenman static void 431ba8c6fd5SDavid Greenman fxp_shutdown(sc) 432ba8c6fd5SDavid Greenman void *sc; 433ba8c6fd5SDavid Greenman { 434ba8c6fd5SDavid Greenman fxp_stop((struct fxp_softc *) sc); 435ba8c6fd5SDavid Greenman } 436ba8c6fd5SDavid Greenman 437ba8c6fd5SDavid Greenman static int 438ba8c6fd5SDavid Greenman fxp_ether_ioctl(ifp, cmd, data) 439ba8c6fd5SDavid Greenman struct ifnet *ifp; 440ba8c6fd5SDavid Greenman FXP_IOCTLCMD_TYPE cmd; 441ba8c6fd5SDavid Greenman caddr_t data; 442ba8c6fd5SDavid Greenman { 443ba8c6fd5SDavid Greenman struct ifaddr *ifa = (struct ifaddr *) data; 444ba8c6fd5SDavid Greenman struct fxp_softc *sc = ifp->if_softc; 445ba8c6fd5SDavid Greenman 446ba8c6fd5SDavid Greenman switch (cmd) { 447ba8c6fd5SDavid Greenman case SIOCSIFADDR: 448ba8c6fd5SDavid Greenman ifp->if_flags |= IFF_UP; 449ba8c6fd5SDavid Greenman 450ba8c6fd5SDavid Greenman switch (ifa->ifa_addr->sa_family) { 451ba8c6fd5SDavid Greenman #ifdef INET 452ba8c6fd5SDavid Greenman case AF_INET: 453ba8c6fd5SDavid Greenman fxp_init(sc); 454ba8c6fd5SDavid Greenman arp_ifinit(ifp, ifa); 455ba8c6fd5SDavid Greenman break; 456ba8c6fd5SDavid Greenman #endif 457ba8c6fd5SDavid Greenman #ifdef NS 458ba8c6fd5SDavid Greenman case AF_NS: 459ba8c6fd5SDavid Greenman { 460ba8c6fd5SDavid Greenman register struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; 461ba8c6fd5SDavid Greenman 462ba8c6fd5SDavid Greenman if (ns_nullhost(*ina)) 463ba8c6fd5SDavid Greenman ina->x_host = *(union ns_host *) 464ba8c6fd5SDavid Greenman LLADDR(ifp->if_sadl); 465ba8c6fd5SDavid Greenman else 466ba8c6fd5SDavid Greenman bcopy(ina->x_host.c_host, LLADDR(ifp->if_sadl), 467ba8c6fd5SDavid Greenman ifp->if_addrlen); 468ba8c6fd5SDavid Greenman /* Set new address. */ 469ba8c6fd5SDavid Greenman fxp_init(sc); 470ba8c6fd5SDavid Greenman break; 471ba8c6fd5SDavid Greenman } 472ba8c6fd5SDavid Greenman #endif 473ba8c6fd5SDavid Greenman default: 474ba8c6fd5SDavid Greenman fxp_init(sc); 475ba8c6fd5SDavid Greenman break; 476ba8c6fd5SDavid Greenman } 477ba8c6fd5SDavid Greenman break; 478ba8c6fd5SDavid Greenman 479ba8c6fd5SDavid Greenman default: 480ba8c6fd5SDavid Greenman return (EINVAL); 481ba8c6fd5SDavid Greenman } 482ba8c6fd5SDavid Greenman 483ba8c6fd5SDavid Greenman return (0); 484ba8c6fd5SDavid Greenman } 485ba8c6fd5SDavid Greenman 486ba8c6fd5SDavid Greenman #else /* __FreeBSD__ */ 487ba8c6fd5SDavid Greenman 488dfe61cf1SDavid Greenman /* 489dfe61cf1SDavid Greenman * Return identification string if this is device is ours. 490dfe61cf1SDavid Greenman */ 4916182fdbdSPeter Wemm static int 4926182fdbdSPeter Wemm fxp_probe(device_t dev) 493a17c678eSDavid Greenman { 4946182fdbdSPeter Wemm if ((pci_get_vendor(dev) == FXP_VENDORID_INTEL) && 4956182fdbdSPeter Wemm (pci_get_device(dev) == FXP_DEVICEID_i82557)) { 4966182fdbdSPeter Wemm device_set_desc(dev, "Intel EtherExpress Pro 10/100B Ethernet"); 4976182fdbdSPeter Wemm return 0; 498a17c678eSDavid Greenman } 499dd68ef16SPeter Wemm if ((pci_get_vendor(dev) == FXP_VENDORID_INTEL) && 500dd68ef16SPeter Wemm (pci_get_device(dev) == FXP_DEVICEID_i82559)) { 501dd68ef16SPeter Wemm device_set_desc(dev, "Intel InBusiness 10/100 Ethernet"); 502dd68ef16SPeter Wemm return 0; 503dd68ef16SPeter Wemm } 504a17c678eSDavid Greenman 5056182fdbdSPeter Wemm return ENXIO; 5066182fdbdSPeter Wemm } 5076182fdbdSPeter Wemm 5086182fdbdSPeter Wemm static int 5096182fdbdSPeter Wemm fxp_attach(device_t dev) 510a17c678eSDavid Greenman { 5116182fdbdSPeter Wemm int error = 0; 5126182fdbdSPeter Wemm struct fxp_softc *sc = device_get_softc(dev); 513ba8c6fd5SDavid Greenman struct ifnet *ifp; 514ba8c6fd5SDavid Greenman int s; 515df373873SWes Peters u_long val; 5166182fdbdSPeter Wemm int rid; 517a17c678eSDavid Greenman 5186c951b44SJustin T. Gibbs callout_handle_init(&sc->stat_ch); 519a17c678eSDavid Greenman 520a17c678eSDavid Greenman s = splimp(); 521a17c678eSDavid Greenman 522dfe61cf1SDavid Greenman /* 523df373873SWes Peters * Enable bus mastering. 524df373873SWes Peters */ 5256182fdbdSPeter Wemm val = pci_read_config(dev, PCIR_COMMAND, 2); 526df373873SWes Peters val |= (PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 5276182fdbdSPeter Wemm pci_write_config(dev, PCIR_COMMAND, val, 2); 528df373873SWes Peters 529df373873SWes Peters /* 530dfe61cf1SDavid Greenman * Map control/status registers. 531dfe61cf1SDavid Greenman */ 5326182fdbdSPeter Wemm rid = FXP_PCI_MMBA; 5336182fdbdSPeter Wemm sc->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 5346182fdbdSPeter Wemm 0, ~0, 1, RF_ACTIVE); 5356182fdbdSPeter Wemm if (!sc->mem) { 5366182fdbdSPeter Wemm device_printf(dev, "could not map memory\n"); 5376182fdbdSPeter Wemm error = ENXIO; 538a17c678eSDavid Greenman goto fail; 539a17c678eSDavid Greenman } 5404fc1dda9SAndrew Gallatin 5414fc1dda9SAndrew Gallatin sc->sc_st = rman_get_bustag(sc->mem); 5424fc1dda9SAndrew Gallatin sc->sc_sh = rman_get_bushandle(sc->mem); 543a17c678eSDavid Greenman 544a17c678eSDavid Greenman /* 545dfe61cf1SDavid Greenman * Allocate our interrupt. 546dfe61cf1SDavid Greenman */ 5476182fdbdSPeter Wemm rid = 0; 5486182fdbdSPeter Wemm sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 5496182fdbdSPeter Wemm RF_SHAREABLE | RF_ACTIVE); 5506182fdbdSPeter Wemm if (sc->irq == NULL) { 5516182fdbdSPeter Wemm device_printf(dev, "could not map interrupt\n"); 5526182fdbdSPeter Wemm error = ENXIO; 5536182fdbdSPeter Wemm goto fail; 5546182fdbdSPeter Wemm } 5556182fdbdSPeter Wemm 556566643e3SDoug Rabson error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, 557566643e3SDoug Rabson fxp_intr, sc, &sc->ih); 5586182fdbdSPeter Wemm if (error) { 5596182fdbdSPeter Wemm device_printf(dev, "could not setup irq\n"); 560a17c678eSDavid Greenman goto fail; 561a17c678eSDavid Greenman } 562a17c678eSDavid Greenman 563ba8c6fd5SDavid Greenman /* Do generic parts of attach. */ 564ba8c6fd5SDavid Greenman if (fxp_attach_common(sc, sc->arpcom.ac_enaddr)) { 565ba8c6fd5SDavid Greenman /* Failed! */ 5666182fdbdSPeter Wemm bus_teardown_intr(dev, sc->irq, sc->ih); 5676182fdbdSPeter Wemm bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); 5686182fdbdSPeter Wemm bus_release_resource(dev, SYS_RES_MEMORY, FXP_PCI_MMBA, sc->mem); 5696182fdbdSPeter Wemm error = ENXIO; 570ba8c6fd5SDavid Greenman goto fail; 571a17c678eSDavid Greenman } 572a17c678eSDavid Greenman 5736182fdbdSPeter Wemm device_printf(dev, "Ethernet address %6D%s\n", 574ba8c6fd5SDavid Greenman sc->arpcom.ac_enaddr, ":", sc->phy_10Mbps_only ? ", 10Mbps" : ""); 575dccee1a1SDavid Greenman 576a17c678eSDavid Greenman ifp = &sc->arpcom.ac_if; 5776182fdbdSPeter Wemm ifp->if_unit = device_get_unit(dev); 578a17c678eSDavid Greenman ifp->if_name = "fxp"; 579a17c678eSDavid Greenman ifp->if_output = ether_output; 580a330e1f1SGary Palmer ifp->if_baudrate = 100000000; 581fb583156SDavid Greenman ifp->if_init = fxp_init; 582ba8c6fd5SDavid Greenman ifp->if_softc = sc; 583ba8c6fd5SDavid Greenman ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 584ba8c6fd5SDavid Greenman ifp->if_ioctl = fxp_ioctl; 585ba8c6fd5SDavid Greenman ifp->if_start = fxp_start; 586ba8c6fd5SDavid Greenman ifp->if_watchdog = fxp_watchdog; 587a17c678eSDavid Greenman 588dfe61cf1SDavid Greenman /* 589dfe61cf1SDavid Greenman * Attach the interface. 590dfe61cf1SDavid Greenman */ 591a17c678eSDavid Greenman if_attach(ifp); 592483b9871SDavid Greenman /* 5933114fdb4SDavid Greenman * Let the system queue as many packets as we have available 5943114fdb4SDavid Greenman * TX descriptors. 595483b9871SDavid Greenman */ 5963114fdb4SDavid Greenman ifp->if_snd.ifq_maxlen = FXP_NTXCB - 1; 5979b44ff22SGarrett Wollman ether_ifattach(ifp); 5989b44ff22SGarrett Wollman bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 5994a684684SDavid Greenman 600a17c678eSDavid Greenman splx(s); 6016182fdbdSPeter Wemm return 0; 602a17c678eSDavid Greenman 603a17c678eSDavid Greenman fail: 604a17c678eSDavid Greenman splx(s); 6056182fdbdSPeter Wemm return error; 6066182fdbdSPeter Wemm } 6076182fdbdSPeter Wemm 6086182fdbdSPeter Wemm /* 6096182fdbdSPeter Wemm * Detach interface. 6106182fdbdSPeter Wemm */ 6116182fdbdSPeter Wemm static int 6126182fdbdSPeter Wemm fxp_detach(device_t dev) 6136182fdbdSPeter Wemm { 6146182fdbdSPeter Wemm struct fxp_softc *sc = device_get_softc(dev); 6156182fdbdSPeter Wemm int s; 6166182fdbdSPeter Wemm 6176182fdbdSPeter Wemm s = splimp(); 6186182fdbdSPeter Wemm 6196182fdbdSPeter Wemm /* 6206182fdbdSPeter Wemm * Close down routes etc. 6216182fdbdSPeter Wemm */ 6226182fdbdSPeter Wemm if_detach(&sc->arpcom.ac_if); 6236182fdbdSPeter Wemm 6246182fdbdSPeter Wemm /* 6256182fdbdSPeter Wemm * Stop DMA and drop transmit queue. 6266182fdbdSPeter Wemm */ 6276182fdbdSPeter Wemm fxp_stop(sc); 6286182fdbdSPeter Wemm 6296182fdbdSPeter Wemm /* 6306182fdbdSPeter Wemm * Deallocate resources. 6316182fdbdSPeter Wemm */ 6326182fdbdSPeter Wemm bus_teardown_intr(dev, sc->irq, sc->ih); 6336182fdbdSPeter Wemm bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); 6346182fdbdSPeter Wemm bus_release_resource(dev, SYS_RES_MEMORY, FXP_PCI_MMBA, sc->mem); 6356182fdbdSPeter Wemm 6366182fdbdSPeter Wemm /* 6376182fdbdSPeter Wemm * Free all the receive buffers. 6386182fdbdSPeter Wemm */ 6396182fdbdSPeter Wemm if (sc->rfa_headm != NULL) 6406182fdbdSPeter Wemm m_freem(sc->rfa_headm); 6416182fdbdSPeter Wemm 6426182fdbdSPeter Wemm /* 6436182fdbdSPeter Wemm * Free all media structures. 6446182fdbdSPeter Wemm */ 6456182fdbdSPeter Wemm ifmedia_removeall(&sc->sc_media); 6466182fdbdSPeter Wemm 6476182fdbdSPeter Wemm /* 6486182fdbdSPeter Wemm * Free anciliary structures. 6496182fdbdSPeter Wemm */ 6506182fdbdSPeter Wemm free(sc->cbl_base, M_DEVBUF); 6516182fdbdSPeter Wemm free(sc->fxp_stats, M_DEVBUF); 6526182fdbdSPeter Wemm free(sc->mcsp, M_DEVBUF); 6536182fdbdSPeter Wemm 6546182fdbdSPeter Wemm splx(s); 6556182fdbdSPeter Wemm 6566182fdbdSPeter Wemm return 0; 657a17c678eSDavid Greenman } 658a17c678eSDavid Greenman 659a17c678eSDavid Greenman /* 6604a684684SDavid Greenman * Device shutdown routine. Called at system shutdown after sync. The 661a17c678eSDavid Greenman * main purpose of this routine is to shut off receiver DMA so that 662a17c678eSDavid Greenman * kernel memory doesn't get clobbered during warmboot. 663a17c678eSDavid Greenman */ 6646182fdbdSPeter Wemm static int 6656182fdbdSPeter Wemm fxp_shutdown(device_t dev) 666a17c678eSDavid Greenman { 6676182fdbdSPeter Wemm /* 6686182fdbdSPeter Wemm * Make sure that DMA is disabled prior to reboot. Not doing 6696182fdbdSPeter Wemm * do could allow DMA to corrupt kernel memory during the 6706182fdbdSPeter Wemm * reboot before the driver initializes. 6716182fdbdSPeter Wemm */ 6726182fdbdSPeter Wemm fxp_stop((struct fxp_softc *) device_get_softc(dev)); 6736182fdbdSPeter Wemm return 0; 674a17c678eSDavid Greenman } 675a17c678eSDavid Greenman 6766182fdbdSPeter Wemm static device_method_t fxp_methods[] = { 6776182fdbdSPeter Wemm /* Device interface */ 6786182fdbdSPeter Wemm DEVMETHOD(device_probe, fxp_probe), 6796182fdbdSPeter Wemm DEVMETHOD(device_attach, fxp_attach), 6806182fdbdSPeter Wemm DEVMETHOD(device_detach, fxp_detach), 6816182fdbdSPeter Wemm DEVMETHOD(device_shutdown, fxp_shutdown), 6826182fdbdSPeter Wemm 6836182fdbdSPeter Wemm { 0, 0 } 6846182fdbdSPeter Wemm }; 6856182fdbdSPeter Wemm 6866182fdbdSPeter Wemm static driver_t fxp_driver = { 6876182fdbdSPeter Wemm "fxp", 6886182fdbdSPeter Wemm fxp_methods, 6896182fdbdSPeter Wemm sizeof(struct fxp_softc), 6906182fdbdSPeter Wemm }; 6916182fdbdSPeter Wemm 6926182fdbdSPeter Wemm static devclass_t fxp_devclass; 6936182fdbdSPeter Wemm 6949e4c647cSBill Paul DRIVER_MODULE(if_fxp, pci, fxp_driver, fxp_devclass, 0, 0); 6956182fdbdSPeter Wemm 696ba8c6fd5SDavid Greenman #endif /* __NetBSD__ */ 697ba8c6fd5SDavid Greenman 698ba8c6fd5SDavid Greenman /************************************************************* 699ba8c6fd5SDavid Greenman * End of operating system-specific autoconfiguration glue 700ba8c6fd5SDavid Greenman *************************************************************/ 701ba8c6fd5SDavid Greenman 702ba8c6fd5SDavid Greenman /* 703ba8c6fd5SDavid Greenman * Do generic parts of attach. 704ba8c6fd5SDavid Greenman */ 705ba8c6fd5SDavid Greenman static int 706ba8c6fd5SDavid Greenman fxp_attach_common(sc, enaddr) 707ba8c6fd5SDavid Greenman struct fxp_softc *sc; 708ba8c6fd5SDavid Greenman u_int8_t *enaddr; 709ba8c6fd5SDavid Greenman { 710ba8c6fd5SDavid Greenman u_int16_t data; 711ba8c6fd5SDavid Greenman int i, nmedia, defmedia; 712ba8c6fd5SDavid Greenman const int *media; 713ba8c6fd5SDavid Greenman 714ba8c6fd5SDavid Greenman /* 715ba8c6fd5SDavid Greenman * Reset to a stable state. 716ba8c6fd5SDavid Greenman */ 717ba8c6fd5SDavid Greenman CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET); 718ba8c6fd5SDavid Greenman DELAY(10); 719ba8c6fd5SDavid Greenman 720ba8c6fd5SDavid Greenman sc->cbl_base = malloc(sizeof(struct fxp_cb_tx) * FXP_NTXCB, 721ba8c6fd5SDavid Greenman M_DEVBUF, M_NOWAIT); 722ba8c6fd5SDavid Greenman if (sc->cbl_base == NULL) 723ba8c6fd5SDavid Greenman goto fail; 72491aa9f90SDavid Greenman bzero(sc->cbl_base, sizeof(struct fxp_cb_tx) * FXP_NTXCB); 725ba8c6fd5SDavid Greenman 726ba8c6fd5SDavid Greenman sc->fxp_stats = malloc(sizeof(struct fxp_stats), M_DEVBUF, M_NOWAIT); 727ba8c6fd5SDavid Greenman if (sc->fxp_stats == NULL) 728ba8c6fd5SDavid Greenman goto fail; 729ba8c6fd5SDavid Greenman bzero(sc->fxp_stats, sizeof(struct fxp_stats)); 730ba8c6fd5SDavid Greenman 731397f9dfeSDavid Greenman sc->mcsp = malloc(sizeof(struct fxp_cb_mcs), M_DEVBUF, M_NOWAIT); 732397f9dfeSDavid Greenman if (sc->mcsp == NULL) 733397f9dfeSDavid Greenman goto fail; 734397f9dfeSDavid Greenman 735ba8c6fd5SDavid Greenman /* 736ba8c6fd5SDavid Greenman * Pre-allocate our receive buffers. 737ba8c6fd5SDavid Greenman */ 738ba8c6fd5SDavid Greenman for (i = 0; i < FXP_NRFABUFS; i++) { 739ba8c6fd5SDavid Greenman if (fxp_add_rfabuf(sc, NULL) != 0) { 740ba8c6fd5SDavid Greenman goto fail; 741ba8c6fd5SDavid Greenman } 742ba8c6fd5SDavid Greenman } 743ba8c6fd5SDavid Greenman 744ba8c6fd5SDavid Greenman /* 745e9bf2fa7SDavid Greenman * Find out how large of an SEEPROM we have. 746e9bf2fa7SDavid Greenman */ 747e9bf2fa7SDavid Greenman fxp_autosize_eeprom(sc); 748e9bf2fa7SDavid Greenman 749e9bf2fa7SDavid Greenman /* 750ba8c6fd5SDavid Greenman * Get info about the primary PHY 751ba8c6fd5SDavid Greenman */ 752ba8c6fd5SDavid Greenman fxp_read_eeprom(sc, (u_int16_t *)&data, 6, 1); 753ba8c6fd5SDavid Greenman sc->phy_primary_addr = data & 0xff; 754ba8c6fd5SDavid Greenman sc->phy_primary_device = (data >> 8) & 0x3f; 755ba8c6fd5SDavid Greenman sc->phy_10Mbps_only = data >> 15; 756ba8c6fd5SDavid Greenman 757ba8c6fd5SDavid Greenman /* 758ba8c6fd5SDavid Greenman * Read MAC address. 759ba8c6fd5SDavid Greenman */ 760ba8c6fd5SDavid Greenman fxp_read_eeprom(sc, (u_int16_t *)enaddr, 0, 3); 761ba8c6fd5SDavid Greenman 762ba8c6fd5SDavid Greenman /* 763ba8c6fd5SDavid Greenman * Initialize the media structures. 764ba8c6fd5SDavid Greenman */ 765ba8c6fd5SDavid Greenman 766ba8c6fd5SDavid Greenman media = fxp_media_default; 767ba8c6fd5SDavid Greenman nmedia = sizeof(fxp_media_default) / sizeof(fxp_media_default[0]); 768ba8c6fd5SDavid Greenman defmedia = FXP_MEDIA_DEFAULT_DEFMEDIA; 769ba8c6fd5SDavid Greenman 770ba8c6fd5SDavid Greenman for (i = 0; i < NFXPMEDIA; i++) { 771ba8c6fd5SDavid Greenman if (sc->phy_primary_device == fxp_media[i].fsm_phy) { 772ba8c6fd5SDavid Greenman media = fxp_media[i].fsm_media; 773ba8c6fd5SDavid Greenman nmedia = fxp_media[i].fsm_nmedia; 774ba8c6fd5SDavid Greenman defmedia = fxp_media[i].fsm_defmedia; 775ba8c6fd5SDavid Greenman } 776ba8c6fd5SDavid Greenman } 777ba8c6fd5SDavid Greenman 778ba8c6fd5SDavid Greenman ifmedia_init(&sc->sc_media, 0, fxp_mediachange, fxp_mediastatus); 779ba8c6fd5SDavid Greenman for (i = 0; i < nmedia; i++) { 780ba8c6fd5SDavid Greenman if (IFM_SUBTYPE(media[i]) == IFM_100_TX && sc->phy_10Mbps_only) 781ba8c6fd5SDavid Greenman continue; 782ba8c6fd5SDavid Greenman ifmedia_add(&sc->sc_media, media[i], 0, NULL); 783ba8c6fd5SDavid Greenman } 784ba8c6fd5SDavid Greenman ifmedia_set(&sc->sc_media, defmedia); 785ba8c6fd5SDavid Greenman 786ba8c6fd5SDavid Greenman return (0); 787ba8c6fd5SDavid Greenman 788ba8c6fd5SDavid Greenman fail: 789ba8c6fd5SDavid Greenman printf(FXP_FORMAT ": Failed to malloc memory\n", FXP_ARGS(sc)); 790ba8c6fd5SDavid Greenman if (sc->cbl_base) 791ba8c6fd5SDavid Greenman free(sc->cbl_base, M_DEVBUF); 792ba8c6fd5SDavid Greenman if (sc->fxp_stats) 793ba8c6fd5SDavid Greenman free(sc->fxp_stats, M_DEVBUF); 794397f9dfeSDavid Greenman if (sc->mcsp) 795397f9dfeSDavid Greenman free(sc->mcsp, M_DEVBUF); 796ba8c6fd5SDavid Greenman /* frees entire chain */ 797ba8c6fd5SDavid Greenman if (sc->rfa_headm) 798ba8c6fd5SDavid Greenman m_freem(sc->rfa_headm); 799ba8c6fd5SDavid Greenman 800ba8c6fd5SDavid Greenman return (ENOMEM); 801ba8c6fd5SDavid Greenman } 802ba8c6fd5SDavid Greenman 803ba8c6fd5SDavid Greenman /* 804e9bf2fa7SDavid Greenman * From NetBSD: 805e9bf2fa7SDavid Greenman * 806e9bf2fa7SDavid Greenman * Figure out EEPROM size. 807e9bf2fa7SDavid Greenman * 808e9bf2fa7SDavid Greenman * 559's can have either 64-word or 256-word EEPROMs, the 558 809e9bf2fa7SDavid Greenman * datasheet only talks about 64-word EEPROMs, and the 557 datasheet 810e9bf2fa7SDavid Greenman * talks about the existance of 16 to 256 word EEPROMs. 811e9bf2fa7SDavid Greenman * 812e9bf2fa7SDavid Greenman * The only known sizes are 64 and 256, where the 256 version is used 813e9bf2fa7SDavid Greenman * by CardBus cards to store CIS information. 814e9bf2fa7SDavid Greenman * 815e9bf2fa7SDavid Greenman * The address is shifted in msb-to-lsb, and after the last 816e9bf2fa7SDavid Greenman * address-bit the EEPROM is supposed to output a `dummy zero' bit, 817e9bf2fa7SDavid Greenman * after which follows the actual data. We try to detect this zero, by 818e9bf2fa7SDavid Greenman * probing the data-out bit in the EEPROM control register just after 819e9bf2fa7SDavid Greenman * having shifted in a bit. If the bit is zero, we assume we've 820e9bf2fa7SDavid Greenman * shifted enough address bits. The data-out should be tri-state, 821e9bf2fa7SDavid Greenman * before this, which should translate to a logical one. 822e9bf2fa7SDavid Greenman * 823e9bf2fa7SDavid Greenman * Other ways to do this would be to try to read a register with known 824e9bf2fa7SDavid Greenman * contents with a varying number of address bits, but no such 825e9bf2fa7SDavid Greenman * register seem to be available. The high bits of register 10 are 01 826e9bf2fa7SDavid Greenman * on the 558 and 559, but apparently not on the 557. 827e9bf2fa7SDavid Greenman * 828e9bf2fa7SDavid Greenman * The Linux driver computes a checksum on the EEPROM data, but the 829e9bf2fa7SDavid Greenman * value of this checksum is not very well documented. 830e9bf2fa7SDavid Greenman */ 831e9bf2fa7SDavid Greenman static void 832e9bf2fa7SDavid Greenman fxp_autosize_eeprom(sc) 833e9bf2fa7SDavid Greenman struct fxp_softc *sc; 834e9bf2fa7SDavid Greenman { 835e9bf2fa7SDavid Greenman u_int16_t reg; 836e9bf2fa7SDavid Greenman int x; 837e9bf2fa7SDavid Greenman 838e9bf2fa7SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); 839e9bf2fa7SDavid Greenman /* 840e9bf2fa7SDavid Greenman * Shift in read opcode. 841e9bf2fa7SDavid Greenman */ 842e9bf2fa7SDavid Greenman for (x = 3; x > 0; x--) { 843e9bf2fa7SDavid Greenman if (FXP_EEPROM_OPC_READ & (1 << (x - 1))) { 844e9bf2fa7SDavid Greenman reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; 845e9bf2fa7SDavid Greenman } else { 846e9bf2fa7SDavid Greenman reg = FXP_EEPROM_EECS; 847e9bf2fa7SDavid Greenman } 848e9bf2fa7SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); 849e9bf2fa7SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 850e9bf2fa7SDavid Greenman reg | FXP_EEPROM_EESK); 851e9bf2fa7SDavid Greenman DELAY(1); 852e9bf2fa7SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); 853e9bf2fa7SDavid Greenman DELAY(1); 854e9bf2fa7SDavid Greenman } 855e9bf2fa7SDavid Greenman /* 856e9bf2fa7SDavid Greenman * Shift in address. 857e9bf2fa7SDavid Greenman * Wait for the dummy zero following a correct address shift. 858e9bf2fa7SDavid Greenman */ 859e9bf2fa7SDavid Greenman for (x = 1; x <= 8; x++) { 860e9bf2fa7SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); 861e9bf2fa7SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 862e9bf2fa7SDavid Greenman FXP_EEPROM_EECS | FXP_EEPROM_EESK); 863e9bf2fa7SDavid Greenman DELAY(1); 864e9bf2fa7SDavid Greenman if ((CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO) == 0) 865e9bf2fa7SDavid Greenman break; 866e9bf2fa7SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); 867e9bf2fa7SDavid Greenman DELAY(1); 868e9bf2fa7SDavid Greenman } 869e9bf2fa7SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0); 870e9bf2fa7SDavid Greenman DELAY(1); 871e9bf2fa7SDavid Greenman sc->eeprom_size = x; 872e9bf2fa7SDavid Greenman } 873e9bf2fa7SDavid Greenman /* 874ba8c6fd5SDavid Greenman * Read from the serial EEPROM. Basically, you manually shift in 875ba8c6fd5SDavid Greenman * the read opcode (one bit at a time) and then shift in the address, 876ba8c6fd5SDavid Greenman * and then you shift out the data (all of this one bit at a time). 877ba8c6fd5SDavid Greenman * The word size is 16 bits, so you have to provide the address for 878ba8c6fd5SDavid Greenman * every 16 bits of data. 879ba8c6fd5SDavid Greenman */ 880ba8c6fd5SDavid Greenman static void 881ba8c6fd5SDavid Greenman fxp_read_eeprom(sc, data, offset, words) 882ba8c6fd5SDavid Greenman struct fxp_softc *sc; 883ba8c6fd5SDavid Greenman u_short *data; 884ba8c6fd5SDavid Greenman int offset; 885ba8c6fd5SDavid Greenman int words; 886ba8c6fd5SDavid Greenman { 887ba8c6fd5SDavid Greenman u_int16_t reg; 888ba8c6fd5SDavid Greenman int i, x; 889ba8c6fd5SDavid Greenman 890ba8c6fd5SDavid Greenman for (i = 0; i < words; i++) { 891ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); 892ba8c6fd5SDavid Greenman /* 893ba8c6fd5SDavid Greenman * Shift in read opcode. 894ba8c6fd5SDavid Greenman */ 895ba8c6fd5SDavid Greenman for (x = 3; x > 0; x--) { 896ba8c6fd5SDavid Greenman if (FXP_EEPROM_OPC_READ & (1 << (x - 1))) { 897ba8c6fd5SDavid Greenman reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; 898ba8c6fd5SDavid Greenman } else { 899ba8c6fd5SDavid Greenman reg = FXP_EEPROM_EECS; 900ba8c6fd5SDavid Greenman } 901ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); 902ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 903ba8c6fd5SDavid Greenman reg | FXP_EEPROM_EESK); 904ba8c6fd5SDavid Greenman DELAY(1); 905ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); 906ba8c6fd5SDavid Greenman DELAY(1); 907ba8c6fd5SDavid Greenman } 908ba8c6fd5SDavid Greenman /* 909ba8c6fd5SDavid Greenman * Shift in address. 910ba8c6fd5SDavid Greenman */ 911e9bf2fa7SDavid Greenman for (x = sc->eeprom_size; x > 0; x--) { 912ba8c6fd5SDavid Greenman if ((i + offset) & (1 << (x - 1))) { 913ba8c6fd5SDavid Greenman reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; 914ba8c6fd5SDavid Greenman } else { 915ba8c6fd5SDavid Greenman reg = FXP_EEPROM_EECS; 916ba8c6fd5SDavid Greenman } 917ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); 918ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 919ba8c6fd5SDavid Greenman reg | FXP_EEPROM_EESK); 920ba8c6fd5SDavid Greenman DELAY(1); 921ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); 922ba8c6fd5SDavid Greenman DELAY(1); 923ba8c6fd5SDavid Greenman } 924ba8c6fd5SDavid Greenman reg = FXP_EEPROM_EECS; 925ba8c6fd5SDavid Greenman data[i] = 0; 926ba8c6fd5SDavid Greenman /* 927ba8c6fd5SDavid Greenman * Shift out data. 928ba8c6fd5SDavid Greenman */ 929ba8c6fd5SDavid Greenman for (x = 16; x > 0; x--) { 930ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 931ba8c6fd5SDavid Greenman reg | FXP_EEPROM_EESK); 932ba8c6fd5SDavid Greenman DELAY(1); 933ba8c6fd5SDavid Greenman if (CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & 934ba8c6fd5SDavid Greenman FXP_EEPROM_EEDO) 935ba8c6fd5SDavid Greenman data[i] |= (1 << (x - 1)); 936ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); 937ba8c6fd5SDavid Greenman DELAY(1); 938ba8c6fd5SDavid Greenman } 939ba8c6fd5SDavid Greenman CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0); 940ba8c6fd5SDavid Greenman DELAY(1); 941ba8c6fd5SDavid Greenman } 942ba8c6fd5SDavid Greenman } 943ba8c6fd5SDavid Greenman 944a17c678eSDavid Greenman /* 945a17c678eSDavid Greenman * Start packet transmission on the interface. 946a17c678eSDavid Greenman */ 947a17c678eSDavid Greenman static void 948a17c678eSDavid Greenman fxp_start(ifp) 949a17c678eSDavid Greenman struct ifnet *ifp; 950a17c678eSDavid Greenman { 9519b44ff22SGarrett Wollman struct fxp_softc *sc = ifp->if_softc; 952a17c678eSDavid Greenman struct fxp_cb_tx *txp; 953a17c678eSDavid Greenman 954a17c678eSDavid Greenman /* 955483b9871SDavid Greenman * See if we need to suspend xmit until the multicast filter 956483b9871SDavid Greenman * has been reprogrammed (which can only be done at the head 957483b9871SDavid Greenman * of the command chain). 958a17c678eSDavid Greenman */ 959483b9871SDavid Greenman if (sc->need_mcsetup) 960a17c678eSDavid Greenman return; 9611cd443acSDavid Greenman 962483b9871SDavid Greenman txp = NULL; 963483b9871SDavid Greenman 964483b9871SDavid Greenman /* 965483b9871SDavid Greenman * We're finished if there is nothing more to add to the list or if 966483b9871SDavid Greenman * we're all filled up with buffers to transmit. 9673114fdb4SDavid Greenman * NOTE: One TxCB is reserved to guarantee that fxp_mc_setup() can add 9683114fdb4SDavid Greenman * a NOP command when needed. 969483b9871SDavid Greenman */ 9703114fdb4SDavid Greenman while (ifp->if_snd.ifq_head != NULL && sc->tx_queued < FXP_NTXCB - 1) { 971483b9871SDavid Greenman struct mbuf *m, *mb_head; 972483b9871SDavid Greenman int segment; 973483b9871SDavid Greenman 974dfe61cf1SDavid Greenman /* 975dfe61cf1SDavid Greenman * Grab a packet to transmit. 976dfe61cf1SDavid Greenman */ 9776318197eSDavid Greenman IF_DEQUEUE(&ifp->if_snd, mb_head); 978a17c678eSDavid Greenman 979dfe61cf1SDavid Greenman /* 980483b9871SDavid Greenman * Get pointer to next available tx desc. 981dfe61cf1SDavid Greenman */ 982a17c678eSDavid Greenman txp = sc->cbl_last->next; 983a17c678eSDavid Greenman 984a17c678eSDavid Greenman /* 985a17c678eSDavid Greenman * Go through each of the mbufs in the chain and initialize 986483b9871SDavid Greenman * the transmit buffer descriptors with the physical address 987a17c678eSDavid Greenman * and size of the mbuf. 988a17c678eSDavid Greenman */ 98923a0ed7cSDavid Greenman tbdinit: 990a17c678eSDavid Greenman for (m = mb_head, segment = 0; m != NULL; m = m->m_next) { 991a17c678eSDavid Greenman if (m->m_len != 0) { 992a17c678eSDavid Greenman if (segment == FXP_NTXSEG) 993a17c678eSDavid Greenman break; 994a17c678eSDavid Greenman txp->tbd[segment].tb_addr = 995a17c678eSDavid Greenman vtophys(mtod(m, vm_offset_t)); 996a17c678eSDavid Greenman txp->tbd[segment].tb_size = m->m_len; 997a17c678eSDavid Greenman segment++; 998a17c678eSDavid Greenman } 999a17c678eSDavid Greenman } 1000fb583156SDavid Greenman if (m != NULL) { 100123a0ed7cSDavid Greenman struct mbuf *mn; 100223a0ed7cSDavid Greenman 1003a17c678eSDavid Greenman /* 1004a17c678eSDavid Greenman * We ran out of segments. We have to recopy this mbuf 1005483b9871SDavid Greenman * chain first. Bail out if we can't get the new buffers. 1006a17c678eSDavid Greenman */ 100723a0ed7cSDavid Greenman MGETHDR(mn, M_DONTWAIT, MT_DATA); 100823a0ed7cSDavid Greenman if (mn == NULL) { 100923a0ed7cSDavid Greenman m_freem(mb_head); 1010483b9871SDavid Greenman break; 1011a17c678eSDavid Greenman } 101223a0ed7cSDavid Greenman if (mb_head->m_pkthdr.len > MHLEN) { 101323a0ed7cSDavid Greenman MCLGET(mn, M_DONTWAIT); 101423a0ed7cSDavid Greenman if ((mn->m_flags & M_EXT) == 0) { 101523a0ed7cSDavid Greenman m_freem(mn); 101623a0ed7cSDavid Greenman m_freem(mb_head); 1017483b9871SDavid Greenman break; 101823a0ed7cSDavid Greenman } 101923a0ed7cSDavid Greenman } 1020ba8c6fd5SDavid Greenman m_copydata(mb_head, 0, mb_head->m_pkthdr.len, 1021ba8c6fd5SDavid Greenman mtod(mn, caddr_t)); 102223a0ed7cSDavid Greenman mn->m_pkthdr.len = mn->m_len = mb_head->m_pkthdr.len; 102323a0ed7cSDavid Greenman m_freem(mb_head); 102423a0ed7cSDavid Greenman mb_head = mn; 102523a0ed7cSDavid Greenman goto tbdinit; 102623a0ed7cSDavid Greenman } 102723a0ed7cSDavid Greenman 102823a0ed7cSDavid Greenman txp->tbd_number = segment; 10291cd443acSDavid Greenman txp->mb_head = mb_head; 1030a17c678eSDavid Greenman txp->cb_status = 0; 10313114fdb4SDavid Greenman if (sc->tx_queued != FXP_CXINT_THRESH - 1) { 1032a17c678eSDavid Greenman txp->cb_command = 1033a17c678eSDavid Greenman FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S; 10343114fdb4SDavid Greenman } else { 10353114fdb4SDavid Greenman txp->cb_command = 10363114fdb4SDavid Greenman FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I; 10373114fdb4SDavid Greenman /* 10383114fdb4SDavid Greenman * Set a 5 second timer just in case we don't hear from the 10393114fdb4SDavid Greenman * card again. 10403114fdb4SDavid Greenman */ 10413114fdb4SDavid Greenman ifp->if_timer = 5; 10423114fdb4SDavid Greenman } 1043f9be9005SDavid Greenman txp->tx_threshold = tx_threshold; 1044a17c678eSDavid Greenman 1045a17c678eSDavid Greenman /* 1046483b9871SDavid Greenman * Advance the end of list forward. 1047a17c678eSDavid Greenman */ 1048a17c678eSDavid Greenman sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S; 1049a17c678eSDavid Greenman sc->cbl_last = txp; 1050a17c678eSDavid Greenman 1051a17c678eSDavid Greenman /* 10521cd443acSDavid Greenman * Advance the beginning of the list forward if there are 10531cd443acSDavid Greenman * no other packets queued (when nothing is queued, cbl_first 1054483b9871SDavid Greenman * sits on the last TxCB that was sent out). 1055a17c678eSDavid Greenman */ 10561cd443acSDavid Greenman if (sc->tx_queued == 0) 1057a17c678eSDavid Greenman sc->cbl_first = txp; 1058a17c678eSDavid Greenman 10591cd443acSDavid Greenman sc->tx_queued++; 10601cd443acSDavid Greenman 1061a17c678eSDavid Greenman /* 1062a17c678eSDavid Greenman * Pass packet to bpf if there is a listener. 1063a17c678eSDavid Greenman */ 1064fb583156SDavid Greenman if (ifp->if_bpf) 1065ba8c6fd5SDavid Greenman bpf_mtap(FXP_BPFTAP_ARG(ifp), mb_head); 1066483b9871SDavid Greenman } 1067483b9871SDavid Greenman 1068483b9871SDavid Greenman /* 1069483b9871SDavid Greenman * We're finished. If we added to the list, issue a RESUME to get DMA 1070483b9871SDavid Greenman * going again if suspended. 1071483b9871SDavid Greenman */ 1072483b9871SDavid Greenman if (txp != NULL) { 1073483b9871SDavid Greenman fxp_scb_wait(sc); 1074483b9871SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_RESUME); 1075483b9871SDavid Greenman } 1076a17c678eSDavid Greenman } 1077a17c678eSDavid Greenman 1078a17c678eSDavid Greenman /* 10799c7d2607SDavid Greenman * Process interface interrupts. 1080a17c678eSDavid Greenman */ 1081ba8c6fd5SDavid Greenman static FXP_INTR_TYPE 1082a17c678eSDavid Greenman fxp_intr(arg) 1083a17c678eSDavid Greenman void *arg; 1084a17c678eSDavid Greenman { 1085a17c678eSDavid Greenman struct fxp_softc *sc = arg; 1086ba8c6fd5SDavid Greenman struct ifnet *ifp = &sc->sc_if; 10871cd443acSDavid Greenman u_int8_t statack; 1088ba8c6fd5SDavid Greenman #if defined(__NetBSD__) 1089ba8c6fd5SDavid Greenman int claimed = 0; 1090ba8c6fd5SDavid Greenman #endif 1091a17c678eSDavid Greenman 1092ba8c6fd5SDavid Greenman while ((statack = CSR_READ_1(sc, FXP_CSR_SCB_STATACK)) != 0) { 1093ba8c6fd5SDavid Greenman #if defined(__NetBSD__) 1094ba8c6fd5SDavid Greenman claimed = 1; 1095ba8c6fd5SDavid Greenman #endif 1096a17c678eSDavid Greenman /* 1097a17c678eSDavid Greenman * First ACK all the interrupts in this pass. 1098a17c678eSDavid Greenman */ 1099ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_STATACK, statack); 1100a17c678eSDavid Greenman 1101a17c678eSDavid Greenman /* 11023114fdb4SDavid Greenman * Free any finished transmit mbuf chains. 11033114fdb4SDavid Greenman */ 11043114fdb4SDavid Greenman if (statack & FXP_SCB_STATACK_CXTNO) { 11053114fdb4SDavid Greenman struct fxp_cb_tx *txp; 11063114fdb4SDavid Greenman 11073114fdb4SDavid Greenman for (txp = sc->cbl_first; sc->tx_queued && 11083114fdb4SDavid Greenman (txp->cb_status & FXP_CB_STATUS_C) != 0; 11093114fdb4SDavid Greenman txp = txp->next) { 11103114fdb4SDavid Greenman if (txp->mb_head != NULL) { 11113114fdb4SDavid Greenman m_freem(txp->mb_head); 11123114fdb4SDavid Greenman txp->mb_head = NULL; 11133114fdb4SDavid Greenman } 11143114fdb4SDavid Greenman sc->tx_queued--; 11153114fdb4SDavid Greenman } 11163114fdb4SDavid Greenman sc->cbl_first = txp; 11173114fdb4SDavid Greenman ifp->if_timer = 0; 11183114fdb4SDavid Greenman if (sc->tx_queued == 0) { 11193114fdb4SDavid Greenman if (sc->need_mcsetup) 11203114fdb4SDavid Greenman fxp_mc_setup(sc); 11213114fdb4SDavid Greenman } 11223114fdb4SDavid Greenman /* 11233114fdb4SDavid Greenman * Try to start more packets transmitting. 11243114fdb4SDavid Greenman */ 11253114fdb4SDavid Greenman if (ifp->if_snd.ifq_head != NULL) 11263114fdb4SDavid Greenman fxp_start(ifp); 11273114fdb4SDavid Greenman } 11283114fdb4SDavid Greenman /* 1129a17c678eSDavid Greenman * Process receiver interrupts. If a no-resource (RNR) 1130a17c678eSDavid Greenman * condition exists, get whatever packets we can and 1131a17c678eSDavid Greenman * re-start the receiver. 1132a17c678eSDavid Greenman */ 1133a17c678eSDavid Greenman if (statack & (FXP_SCB_STATACK_FR | FXP_SCB_STATACK_RNR)) { 1134a17c678eSDavid Greenman struct mbuf *m; 1135a17c678eSDavid Greenman struct fxp_rfa *rfa; 1136a17c678eSDavid Greenman rcvloop: 1137a17c678eSDavid Greenman m = sc->rfa_headm; 1138ba8c6fd5SDavid Greenman rfa = (struct fxp_rfa *)(m->m_ext.ext_buf + 1139ba8c6fd5SDavid Greenman RFA_ALIGNMENT_FUDGE); 1140a17c678eSDavid Greenman 1141a17c678eSDavid Greenman if (rfa->rfa_status & FXP_RFA_STATUS_C) { 1142dfe61cf1SDavid Greenman /* 1143dfe61cf1SDavid Greenman * Remove first packet from the chain. 1144dfe61cf1SDavid Greenman */ 1145a17c678eSDavid Greenman sc->rfa_headm = m->m_next; 1146a17c678eSDavid Greenman m->m_next = NULL; 1147a17c678eSDavid Greenman 1148dfe61cf1SDavid Greenman /* 1149ba8c6fd5SDavid Greenman * Add a new buffer to the receive chain. 1150ba8c6fd5SDavid Greenman * If this fails, the old buffer is recycled 1151ba8c6fd5SDavid Greenman * instead. 1152dfe61cf1SDavid Greenman */ 1153a17c678eSDavid Greenman if (fxp_add_rfabuf(sc, m) == 0) { 1154a17c678eSDavid Greenman struct ether_header *eh; 1155ba8c6fd5SDavid Greenman u_int16_t total_len; 1156a17c678eSDavid Greenman 1157ba8c6fd5SDavid Greenman total_len = rfa->actual_size & 1158ba8c6fd5SDavid Greenman (MCLBYTES - 1); 1159ba8c6fd5SDavid Greenman if (total_len < 1160ba8c6fd5SDavid Greenman sizeof(struct ether_header)) { 116106339180SDavid Greenman m_freem(m); 116206339180SDavid Greenman goto rcvloop; 116306339180SDavid Greenman } 1164a17c678eSDavid Greenman m->m_pkthdr.rcvif = ifp; 11652e2de7f2SArchie Cobbs m->m_pkthdr.len = m->m_len = total_len; 1166a17c678eSDavid Greenman eh = mtod(m, struct ether_header *); 1167ba8c6fd5SDavid Greenman m->m_data += 1168ba8c6fd5SDavid Greenman sizeof(struct ether_header); 1169ab090e5bSLuigi Rizzo m->m_len -= 1170ab090e5bSLuigi Rizzo sizeof(struct ether_header); 1171ab090e5bSLuigi Rizzo m->m_pkthdr.len = m->m_len; 1172a17c678eSDavid Greenman ether_input(ifp, eh, m); 1173a17c678eSDavid Greenman } 1174a17c678eSDavid Greenman goto rcvloop; 1175a17c678eSDavid Greenman } 1176a17c678eSDavid Greenman if (statack & FXP_SCB_STATACK_RNR) { 1177ba8c6fd5SDavid Greenman fxp_scb_wait(sc); 1178ba8c6fd5SDavid Greenman CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, 1179ba8c6fd5SDavid Greenman vtophys(sc->rfa_headm->m_ext.ext_buf) + 1180ba8c6fd5SDavid Greenman RFA_ALIGNMENT_FUDGE); 1181ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, 1182ba8c6fd5SDavid Greenman FXP_SCB_COMMAND_RU_START); 1183a17c678eSDavid Greenman } 1184a17c678eSDavid Greenman } 1185a17c678eSDavid Greenman } 1186ba8c6fd5SDavid Greenman #if defined(__NetBSD__) 1187ba8c6fd5SDavid Greenman return (claimed); 1188ba8c6fd5SDavid Greenman #endif 1189a17c678eSDavid Greenman } 1190a17c678eSDavid Greenman 1191dfe61cf1SDavid Greenman /* 1192dfe61cf1SDavid Greenman * Update packet in/out/collision statistics. The i82557 doesn't 1193dfe61cf1SDavid Greenman * allow you to access these counters without doing a fairly 1194dfe61cf1SDavid Greenman * expensive DMA to get _all_ of the statistics it maintains, so 1195dfe61cf1SDavid Greenman * we do this operation here only once per second. The statistics 1196dfe61cf1SDavid Greenman * counters in the kernel are updated from the previous dump-stats 1197dfe61cf1SDavid Greenman * DMA and then a new dump-stats DMA is started. The on-chip 1198dfe61cf1SDavid Greenman * counters are zeroed when the DMA completes. If we can't start 1199dfe61cf1SDavid Greenman * the DMA immediately, we don't wait - we just prepare to read 1200dfe61cf1SDavid Greenman * them again next time. 1201dfe61cf1SDavid Greenman */ 1202303b270bSEivind Eklund static void 1203a17c678eSDavid Greenman fxp_stats_update(arg) 1204a17c678eSDavid Greenman void *arg; 1205a17c678eSDavid Greenman { 1206a17c678eSDavid Greenman struct fxp_softc *sc = arg; 1207ba8c6fd5SDavid Greenman struct ifnet *ifp = &sc->sc_if; 1208a17c678eSDavid Greenman struct fxp_stats *sp = sc->fxp_stats; 1209c8cc6fcaSDavid Greenman struct fxp_cb_tx *txp; 1210397f9dfeSDavid Greenman int s; 1211a17c678eSDavid Greenman 1212a17c678eSDavid Greenman ifp->if_opackets += sp->tx_good; 1213a17c678eSDavid Greenman ifp->if_collisions += sp->tx_total_collisions; 1214397f9dfeSDavid Greenman if (sp->rx_good) { 1215397f9dfeSDavid Greenman ifp->if_ipackets += sp->rx_good; 1216397f9dfeSDavid Greenman sc->rx_idle_secs = 0; 1217397f9dfeSDavid Greenman } else { 1218c8cc6fcaSDavid Greenman /* 1219c8cc6fcaSDavid Greenman * Receiver's been idle for another second. 1220c8cc6fcaSDavid Greenman */ 1221397f9dfeSDavid Greenman sc->rx_idle_secs++; 1222397f9dfeSDavid Greenman } 12233ba65732SDavid Greenman ifp->if_ierrors += 12243ba65732SDavid Greenman sp->rx_crc_errors + 12253ba65732SDavid Greenman sp->rx_alignment_errors + 12263ba65732SDavid Greenman sp->rx_rnr_errors + 12276e39e599SDavid Greenman sp->rx_overrun_errors; 1228a17c678eSDavid Greenman /* 1229f9be9005SDavid Greenman * If any transmit underruns occured, bump up the transmit 1230f9be9005SDavid Greenman * threshold by another 512 bytes (64 * 8). 1231f9be9005SDavid Greenman */ 1232f9be9005SDavid Greenman if (sp->tx_underruns) { 1233f9be9005SDavid Greenman ifp->if_oerrors += sp->tx_underruns; 1234f9be9005SDavid Greenman if (tx_threshold < 192) 1235f9be9005SDavid Greenman tx_threshold += 64; 1236f9be9005SDavid Greenman } 1237397f9dfeSDavid Greenman s = splimp(); 1238397f9dfeSDavid Greenman /* 1239c8cc6fcaSDavid Greenman * Release any xmit buffers that have completed DMA. This isn't 1240c8cc6fcaSDavid Greenman * strictly necessary to do here, but it's advantagous for mbufs 1241c8cc6fcaSDavid Greenman * with external storage to be released in a timely manner rather 1242c8cc6fcaSDavid Greenman * than being defered for a potentially long time. This limits 1243c8cc6fcaSDavid Greenman * the delay to a maximum of one second. 1244c8cc6fcaSDavid Greenman */ 1245c8cc6fcaSDavid Greenman for (txp = sc->cbl_first; sc->tx_queued && 1246c8cc6fcaSDavid Greenman (txp->cb_status & FXP_CB_STATUS_C) != 0; 1247c8cc6fcaSDavid Greenman txp = txp->next) { 1248c8cc6fcaSDavid Greenman if (txp->mb_head != NULL) { 1249c8cc6fcaSDavid Greenman m_freem(txp->mb_head); 1250c8cc6fcaSDavid Greenman txp->mb_head = NULL; 1251c8cc6fcaSDavid Greenman } 1252c8cc6fcaSDavid Greenman sc->tx_queued--; 1253c8cc6fcaSDavid Greenman } 1254c8cc6fcaSDavid Greenman sc->cbl_first = txp; 1255c8cc6fcaSDavid Greenman /* 1256397f9dfeSDavid Greenman * If we haven't received any packets in FXP_MAC_RX_IDLE seconds, 1257397f9dfeSDavid Greenman * then assume the receiver has locked up and attempt to clear 1258397f9dfeSDavid Greenman * the condition by reprogramming the multicast filter. This is 1259397f9dfeSDavid Greenman * a work-around for a bug in the 82557 where the receiver locks 1260397f9dfeSDavid Greenman * up if it gets certain types of garbage in the syncronization 1261397f9dfeSDavid Greenman * bits prior to the packet header. This bug is supposed to only 1262397f9dfeSDavid Greenman * occur in 10Mbps mode, but has been seen to occur in 100Mbps 1263397f9dfeSDavid Greenman * mode as well (perhaps due to a 10/100 speed transition). 1264397f9dfeSDavid Greenman */ 1265397f9dfeSDavid Greenman if (sc->rx_idle_secs > FXP_MAX_RX_IDLE) { 1266397f9dfeSDavid Greenman sc->rx_idle_secs = 0; 1267397f9dfeSDavid Greenman fxp_mc_setup(sc); 1268397f9dfeSDavid Greenman } 1269f9be9005SDavid Greenman /* 12703ba65732SDavid Greenman * If there is no pending command, start another stats 12713ba65732SDavid Greenman * dump. Otherwise punt for now. 1272a17c678eSDavid Greenman */ 1273397f9dfeSDavid Greenman if (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) == 0) { 1274a17c678eSDavid Greenman /* 1275397f9dfeSDavid Greenman * Start another stats dump. 1276a17c678eSDavid Greenman */ 1277ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, 1278ba8c6fd5SDavid Greenman FXP_SCB_COMMAND_CU_DUMPRESET); 1279dfe61cf1SDavid Greenman } else { 1280dfe61cf1SDavid Greenman /* 1281dfe61cf1SDavid Greenman * A previous command is still waiting to be accepted. 1282dfe61cf1SDavid Greenman * Just zero our copy of the stats and wait for the 12833ba65732SDavid Greenman * next timer event to update them. 1284dfe61cf1SDavid Greenman */ 1285dfe61cf1SDavid Greenman sp->tx_good = 0; 1286f9be9005SDavid Greenman sp->tx_underruns = 0; 1287dfe61cf1SDavid Greenman sp->tx_total_collisions = 0; 12883ba65732SDavid Greenman 1289dfe61cf1SDavid Greenman sp->rx_good = 0; 12903ba65732SDavid Greenman sp->rx_crc_errors = 0; 12913ba65732SDavid Greenman sp->rx_alignment_errors = 0; 12923ba65732SDavid Greenman sp->rx_rnr_errors = 0; 12933ba65732SDavid Greenman sp->rx_overrun_errors = 0; 1294dfe61cf1SDavid Greenman } 1295397f9dfeSDavid Greenman splx(s); 1296a17c678eSDavid Greenman /* 1297a17c678eSDavid Greenman * Schedule another timeout one second from now. 1298a17c678eSDavid Greenman */ 1299397f9dfeSDavid Greenman sc->stat_ch = timeout(fxp_stats_update, sc, hz); 1300a17c678eSDavid Greenman } 1301a17c678eSDavid Greenman 1302a17c678eSDavid Greenman /* 1303a17c678eSDavid Greenman * Stop the interface. Cancels the statistics updater and resets 1304a17c678eSDavid Greenman * the interface. 1305a17c678eSDavid Greenman */ 1306a17c678eSDavid Greenman static void 13074a5f1499SDavid Greenman fxp_stop(sc) 13084a5f1499SDavid Greenman struct fxp_softc *sc; 1309a17c678eSDavid Greenman { 1310ba8c6fd5SDavid Greenman struct ifnet *ifp = &sc->sc_if; 13113ba65732SDavid Greenman struct fxp_cb_tx *txp; 13123ba65732SDavid Greenman int i; 1313a17c678eSDavid Greenman 1314a17c678eSDavid Greenman /* 1315a17c678eSDavid Greenman * Cancel stats updater. 1316a17c678eSDavid Greenman */ 13176c951b44SJustin T. Gibbs untimeout(fxp_stats_update, sc, sc->stat_ch); 13183ba65732SDavid Greenman 13193ba65732SDavid Greenman /* 13203ba65732SDavid Greenman * Issue software reset 13213ba65732SDavid Greenman */ 1322ba8c6fd5SDavid Greenman CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET); 1323a17c678eSDavid Greenman DELAY(10); 1324a17c678eSDavid Greenman 13253ba65732SDavid Greenman /* 13263ba65732SDavid Greenman * Release any xmit buffers. 13273ba65732SDavid Greenman */ 1328da91462dSDavid Greenman txp = sc->cbl_base; 1329da91462dSDavid Greenman if (txp != NULL) { 1330da91462dSDavid Greenman for (i = 0; i < FXP_NTXCB; i++) { 1331da91462dSDavid Greenman if (txp[i].mb_head != NULL) { 1332da91462dSDavid Greenman m_freem(txp[i].mb_head); 1333da91462dSDavid Greenman txp[i].mb_head = NULL; 1334da91462dSDavid Greenman } 1335da91462dSDavid Greenman } 13363ba65732SDavid Greenman } 13373ba65732SDavid Greenman sc->tx_queued = 0; 13383ba65732SDavid Greenman 13393ba65732SDavid Greenman /* 13403ba65732SDavid Greenman * Free all the receive buffers then reallocate/reinitialize 13413ba65732SDavid Greenman */ 13423ba65732SDavid Greenman if (sc->rfa_headm != NULL) 13433ba65732SDavid Greenman m_freem(sc->rfa_headm); 13443ba65732SDavid Greenman sc->rfa_headm = NULL; 13453ba65732SDavid Greenman sc->rfa_tailm = NULL; 13463ba65732SDavid Greenman for (i = 0; i < FXP_NRFABUFS; i++) { 13473ba65732SDavid Greenman if (fxp_add_rfabuf(sc, NULL) != 0) { 13483ba65732SDavid Greenman /* 13493ba65732SDavid Greenman * This "can't happen" - we're at splimp() 13503ba65732SDavid Greenman * and we just freed all the buffers we need 13513ba65732SDavid Greenman * above. 13523ba65732SDavid Greenman */ 13533ba65732SDavid Greenman panic("fxp_stop: no buffers!"); 13543ba65732SDavid Greenman } 13553ba65732SDavid Greenman } 13563ba65732SDavid Greenman 13573ba65732SDavid Greenman ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 13583ba65732SDavid Greenman ifp->if_timer = 0; 1359a17c678eSDavid Greenman } 1360a17c678eSDavid Greenman 1361a17c678eSDavid Greenman /* 1362a17c678eSDavid Greenman * Watchdog/transmission transmit timeout handler. Called when a 1363a17c678eSDavid Greenman * transmission is started on the interface, but no interrupt is 1364a17c678eSDavid Greenman * received before the timeout. This usually indicates that the 1365a17c678eSDavid Greenman * card has wedged for some reason. 1366a17c678eSDavid Greenman */ 1367a17c678eSDavid Greenman static void 13684a5f1499SDavid Greenman fxp_watchdog(ifp) 13694a5f1499SDavid Greenman struct ifnet *ifp; 1370a17c678eSDavid Greenman { 1371ba8c6fd5SDavid Greenman struct fxp_softc *sc = ifp->if_softc; 1372ba8c6fd5SDavid Greenman 1373397f9dfeSDavid Greenman printf(FXP_FORMAT ": device timeout\n", FXP_ARGS(sc)); 13744a5f1499SDavid Greenman ifp->if_oerrors++; 1375a17c678eSDavid Greenman 1376ba8c6fd5SDavid Greenman fxp_init(sc); 1377a17c678eSDavid Greenman } 1378a17c678eSDavid Greenman 1379a17c678eSDavid Greenman static void 1380fb583156SDavid Greenman fxp_init(xsc) 1381fb583156SDavid Greenman void *xsc; 1382a17c678eSDavid Greenman { 1383fb583156SDavid Greenman struct fxp_softc *sc = xsc; 1384ba8c6fd5SDavid Greenman struct ifnet *ifp = &sc->sc_if; 1385a17c678eSDavid Greenman struct fxp_cb_config *cbp; 1386a17c678eSDavid Greenman struct fxp_cb_ias *cb_ias; 1387a17c678eSDavid Greenman struct fxp_cb_tx *txp; 1388397f9dfeSDavid Greenman int i, s, prm; 1389a17c678eSDavid Greenman 1390a17c678eSDavid Greenman s = splimp(); 1391a17c678eSDavid Greenman /* 13923ba65732SDavid Greenman * Cancel any pending I/O 1393a17c678eSDavid Greenman */ 13943ba65732SDavid Greenman fxp_stop(sc); 1395a17c678eSDavid Greenman 1396a17c678eSDavid Greenman prm = (ifp->if_flags & IFF_PROMISC) ? 1 : 0; 1397a17c678eSDavid Greenman 1398a17c678eSDavid Greenman /* 1399a17c678eSDavid Greenman * Initialize base of CBL and RFA memory. Loading with zero 1400a17c678eSDavid Greenman * sets it up for regular linear addressing. 1401a17c678eSDavid Greenman */ 1402ba8c6fd5SDavid Greenman CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, 0); 1403ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_BASE); 1404a17c678eSDavid Greenman 1405ba8c6fd5SDavid Greenman fxp_scb_wait(sc); 1406ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_BASE); 1407a17c678eSDavid Greenman 1408a17c678eSDavid Greenman /* 1409a17c678eSDavid Greenman * Initialize base of dump-stats buffer. 1410a17c678eSDavid Greenman */ 1411ba8c6fd5SDavid Greenman fxp_scb_wait(sc); 1412ba8c6fd5SDavid Greenman CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(sc->fxp_stats)); 1413ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_DUMP_ADR); 1414a17c678eSDavid Greenman 1415a17c678eSDavid Greenman /* 1416a17c678eSDavid Greenman * We temporarily use memory that contains the TxCB list to 1417a17c678eSDavid Greenman * construct the config CB. The TxCB list memory is rebuilt 1418a17c678eSDavid Greenman * later. 1419a17c678eSDavid Greenman */ 1420a17c678eSDavid Greenman cbp = (struct fxp_cb_config *) sc->cbl_base; 1421a17c678eSDavid Greenman 1422a17c678eSDavid Greenman /* 1423a17c678eSDavid Greenman * This bcopy is kind of disgusting, but there are a bunch of must be 1424a17c678eSDavid Greenman * zero and must be one bits in this structure and this is the easiest 1425a17c678eSDavid Greenman * way to initialize them all to proper values. 1426a17c678eSDavid Greenman */ 1427697457a1SMatthew Dillon bcopy(fxp_cb_config_template, (volatile void *)&cbp->cb_status, 1428397f9dfeSDavid Greenman sizeof(fxp_cb_config_template)); 1429a17c678eSDavid Greenman 1430a17c678eSDavid Greenman cbp->cb_status = 0; 1431a17c678eSDavid Greenman cbp->cb_command = FXP_CB_COMMAND_CONFIG | FXP_CB_COMMAND_EL; 1432a17c678eSDavid Greenman cbp->link_addr = -1; /* (no) next command */ 1433a17c678eSDavid Greenman cbp->byte_count = 22; /* (22) bytes to config */ 1434001696daSDavid Greenman cbp->rx_fifo_limit = 8; /* rx fifo threshold (32 bytes) */ 1435001696daSDavid Greenman cbp->tx_fifo_limit = 0; /* tx fifo threshold (0 bytes) */ 1436a17c678eSDavid Greenman cbp->adaptive_ifs = 0; /* (no) adaptive interframe spacing */ 1437001696daSDavid Greenman cbp->rx_dma_bytecount = 0; /* (no) rx DMA max */ 1438001696daSDavid Greenman cbp->tx_dma_bytecount = 0; /* (no) tx DMA max */ 1439001696daSDavid Greenman cbp->dma_bce = 0; /* (disable) dma max counters */ 1440a17c678eSDavid Greenman cbp->late_scb = 0; /* (don't) defer SCB update */ 1441a17c678eSDavid Greenman cbp->tno_int = 0; /* (disable) tx not okay interrupt */ 14423114fdb4SDavid Greenman cbp->ci_int = 1; /* interrupt on CU idle */ 1443a17c678eSDavid Greenman cbp->save_bf = prm; /* save bad frames */ 1444a17c678eSDavid Greenman cbp->disc_short_rx = !prm; /* discard short packets */ 1445a17c678eSDavid Greenman cbp->underrun_retry = 1; /* retry mode (1) on DMA underrun */ 1446dccee1a1SDavid Greenman cbp->mediatype = !sc->phy_10Mbps_only; /* interface mode */ 1447a17c678eSDavid Greenman cbp->nsai = 1; /* (don't) disable source addr insert */ 1448a17c678eSDavid Greenman cbp->preamble_length = 2; /* (7 byte) preamble */ 1449a17c678eSDavid Greenman cbp->loopback = 0; /* (don't) loopback */ 1450a17c678eSDavid Greenman cbp->linear_priority = 0; /* (normal CSMA/CD operation) */ 1451a17c678eSDavid Greenman cbp->linear_pri_mode = 0; /* (wait after xmit only) */ 1452a17c678eSDavid Greenman cbp->interfrm_spacing = 6; /* (96 bits of) interframe spacing */ 1453a17c678eSDavid Greenman cbp->promiscuous = prm; /* promiscuous mode */ 1454a17c678eSDavid Greenman cbp->bcast_disable = 0; /* (don't) disable broadcasts */ 1455001696daSDavid Greenman cbp->crscdt = 0; /* (CRS only) */ 1456a17c678eSDavid Greenman cbp->stripping = !prm; /* truncate rx packet to byte count */ 1457a17c678eSDavid Greenman cbp->padding = 1; /* (do) pad short tx packets */ 1458a17c678eSDavid Greenman cbp->rcv_crc_xfer = 0; /* (don't) xfer CRC to host */ 1459a17c678eSDavid Greenman cbp->force_fdx = 0; /* (don't) force full duplex */ 14603ba65732SDavid Greenman cbp->fdx_pin_en = 1; /* (enable) FDX# pin */ 1461a17c678eSDavid Greenman cbp->multi_ia = 0; /* (don't) accept multiple IAs */ 1462397f9dfeSDavid Greenman cbp->mc_all = sc->all_mcasts;/* accept all multicasts */ 1463a17c678eSDavid Greenman 1464a17c678eSDavid Greenman /* 1465a17c678eSDavid Greenman * Start the config command/DMA. 1466a17c678eSDavid Greenman */ 1467ba8c6fd5SDavid Greenman fxp_scb_wait(sc); 1468397f9dfeSDavid Greenman CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&cbp->cb_status)); 1469ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); 1470a17c678eSDavid Greenman /* ...and wait for it to complete. */ 1471a17c678eSDavid Greenman while (!(cbp->cb_status & FXP_CB_STATUS_C)); 1472a17c678eSDavid Greenman 1473a17c678eSDavid Greenman /* 1474a17c678eSDavid Greenman * Now initialize the station address. Temporarily use the TxCB 1475a17c678eSDavid Greenman * memory area like we did above for the config CB. 1476a17c678eSDavid Greenman */ 1477a17c678eSDavid Greenman cb_ias = (struct fxp_cb_ias *) sc->cbl_base; 1478a17c678eSDavid Greenman cb_ias->cb_status = 0; 1479a17c678eSDavid Greenman cb_ias->cb_command = FXP_CB_COMMAND_IAS | FXP_CB_COMMAND_EL; 1480a17c678eSDavid Greenman cb_ias->link_addr = -1; 1481ba8c6fd5SDavid Greenman #if defined(__NetBSD__) 1482ba8c6fd5SDavid Greenman bcopy(LLADDR(ifp->if_sadl), (void *)cb_ias->macaddr, 6); 1483ba8c6fd5SDavid Greenman #else 14848aef1712SMatthew Dillon bcopy(sc->arpcom.ac_enaddr, (volatile void *)cb_ias->macaddr, 1485a17c678eSDavid Greenman sizeof(sc->arpcom.ac_enaddr)); 1486ba8c6fd5SDavid Greenman #endif /* __NetBSD__ */ 1487a17c678eSDavid Greenman 1488a17c678eSDavid Greenman /* 1489a17c678eSDavid Greenman * Start the IAS (Individual Address Setup) command/DMA. 1490a17c678eSDavid Greenman */ 1491ba8c6fd5SDavid Greenman fxp_scb_wait(sc); 1492ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); 1493a17c678eSDavid Greenman /* ...and wait for it to complete. */ 1494a17c678eSDavid Greenman while (!(cb_ias->cb_status & FXP_CB_STATUS_C)); 1495a17c678eSDavid Greenman 1496a17c678eSDavid Greenman /* 1497a17c678eSDavid Greenman * Initialize transmit control block (TxCB) list. 1498a17c678eSDavid Greenman */ 1499a17c678eSDavid Greenman 1500a17c678eSDavid Greenman txp = sc->cbl_base; 1501a17c678eSDavid Greenman bzero(txp, sizeof(struct fxp_cb_tx) * FXP_NTXCB); 1502a17c678eSDavid Greenman for (i = 0; i < FXP_NTXCB; i++) { 1503a17c678eSDavid Greenman txp[i].cb_status = FXP_CB_STATUS_C | FXP_CB_STATUS_OK; 1504a17c678eSDavid Greenman txp[i].cb_command = FXP_CB_COMMAND_NOP; 1505397f9dfeSDavid Greenman txp[i].link_addr = vtophys(&txp[(i + 1) & FXP_TXCB_MASK].cb_status); 1506a17c678eSDavid Greenman txp[i].tbd_array_addr = vtophys(&txp[i].tbd[0]); 1507a17c678eSDavid Greenman txp[i].next = &txp[(i + 1) & FXP_TXCB_MASK]; 1508a17c678eSDavid Greenman } 1509a17c678eSDavid Greenman /* 1510397f9dfeSDavid Greenman * Set the suspend flag on the first TxCB and start the control 1511a17c678eSDavid Greenman * unit. It will execute the NOP and then suspend. 1512a17c678eSDavid Greenman */ 1513a17c678eSDavid Greenman txp->cb_command = FXP_CB_COMMAND_NOP | FXP_CB_COMMAND_S; 1514a17c678eSDavid Greenman sc->cbl_first = sc->cbl_last = txp; 1515397f9dfeSDavid Greenman sc->tx_queued = 1; 1516a17c678eSDavid Greenman 1517ba8c6fd5SDavid Greenman fxp_scb_wait(sc); 1518ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); 1519a17c678eSDavid Greenman 1520a17c678eSDavid Greenman /* 1521a17c678eSDavid Greenman * Initialize receiver buffer area - RFA. 1522a17c678eSDavid Greenman */ 1523ba8c6fd5SDavid Greenman fxp_scb_wait(sc); 1524ba8c6fd5SDavid Greenman CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, 1525ba8c6fd5SDavid Greenman vtophys(sc->rfa_headm->m_ext.ext_buf) + RFA_ALIGNMENT_FUDGE); 1526ba8c6fd5SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_START); 1527a17c678eSDavid Greenman 1528dccee1a1SDavid Greenman /* 1529ba8c6fd5SDavid Greenman * Set current media. 1530dccee1a1SDavid Greenman */ 1531ba8c6fd5SDavid Greenman fxp_set_media(sc, sc->sc_media.ifm_cur->ifm_media); 1532dccee1a1SDavid Greenman 1533a17c678eSDavid Greenman ifp->if_flags |= IFF_RUNNING; 1534a17c678eSDavid Greenman ifp->if_flags &= ~IFF_OACTIVE; 1535a17c678eSDavid Greenman splx(s); 1536a17c678eSDavid Greenman 1537a17c678eSDavid Greenman /* 1538a17c678eSDavid Greenman * Start stats updater. 1539a17c678eSDavid Greenman */ 15406c951b44SJustin T. Gibbs sc->stat_ch = timeout(fxp_stats_update, sc, hz); 1541a17c678eSDavid Greenman } 1542a17c678eSDavid Greenman 1543303b270bSEivind Eklund static void 1544ba8c6fd5SDavid Greenman fxp_set_media(sc, media) 1545ba8c6fd5SDavid Greenman struct fxp_softc *sc; 1546ba8c6fd5SDavid Greenman int media; 1547ba8c6fd5SDavid Greenman { 1548ba8c6fd5SDavid Greenman 1549ba8c6fd5SDavid Greenman switch (sc->phy_primary_device) { 1550ba8c6fd5SDavid Greenman case FXP_PHY_DP83840: 1551ba8c6fd5SDavid Greenman case FXP_PHY_DP83840A: 1552ba8c6fd5SDavid Greenman fxp_mdi_write(sc, sc->phy_primary_addr, FXP_DP83840_PCR, 1553ba8c6fd5SDavid Greenman fxp_mdi_read(sc, sc->phy_primary_addr, FXP_DP83840_PCR) | 1554ba8c6fd5SDavid Greenman FXP_DP83840_PCR_LED4_MODE | /* LED4 always indicates duplex */ 1555ba8c6fd5SDavid Greenman FXP_DP83840_PCR_F_CONNECT | /* force link disconnect bypass */ 1556ba8c6fd5SDavid Greenman FXP_DP83840_PCR_BIT10); /* XXX I have no idea */ 1557ba8c6fd5SDavid Greenman /* fall through */ 155892924291SDavid Greenman case FXP_PHY_82553A: 155992924291SDavid Greenman case FXP_PHY_82553C: /* untested */ 1560ba8c6fd5SDavid Greenman case FXP_PHY_82555: 156192924291SDavid Greenman case FXP_PHY_82555B: 1562ba8c6fd5SDavid Greenman if (IFM_SUBTYPE(media) != IFM_AUTO) { 1563ba8c6fd5SDavid Greenman int flags; 1564ba8c6fd5SDavid Greenman 1565ba8c6fd5SDavid Greenman flags = (IFM_SUBTYPE(media) == IFM_100_TX) ? 1566ba8c6fd5SDavid Greenman FXP_PHY_BMCR_SPEED_100M : 0; 1567ba8c6fd5SDavid Greenman flags |= (media & IFM_FDX) ? 1568ba8c6fd5SDavid Greenman FXP_PHY_BMCR_FULLDUPLEX : 0; 1569ba8c6fd5SDavid Greenman fxp_mdi_write(sc, sc->phy_primary_addr, 1570ba8c6fd5SDavid Greenman FXP_PHY_BMCR, 1571ba8c6fd5SDavid Greenman (fxp_mdi_read(sc, sc->phy_primary_addr, 1572ba8c6fd5SDavid Greenman FXP_PHY_BMCR) & 1573ba8c6fd5SDavid Greenman ~(FXP_PHY_BMCR_AUTOEN | FXP_PHY_BMCR_SPEED_100M | 1574ba8c6fd5SDavid Greenman FXP_PHY_BMCR_FULLDUPLEX)) | flags); 1575ba8c6fd5SDavid Greenman } else { 1576ba8c6fd5SDavid Greenman fxp_mdi_write(sc, sc->phy_primary_addr, 1577ba8c6fd5SDavid Greenman FXP_PHY_BMCR, 1578ba8c6fd5SDavid Greenman (fxp_mdi_read(sc, sc->phy_primary_addr, 1579ba8c6fd5SDavid Greenman FXP_PHY_BMCR) | FXP_PHY_BMCR_AUTOEN)); 1580ba8c6fd5SDavid Greenman } 1581ba8c6fd5SDavid Greenman break; 1582ba8c6fd5SDavid Greenman /* 1583ba8c6fd5SDavid Greenman * The Seeq 80c24 doesn't have a PHY programming interface, so do 1584ba8c6fd5SDavid Greenman * nothing. 1585ba8c6fd5SDavid Greenman */ 1586ba8c6fd5SDavid Greenman case FXP_PHY_80C24: 1587ba8c6fd5SDavid Greenman break; 1588ba8c6fd5SDavid Greenman default: 1589ba8c6fd5SDavid Greenman printf(FXP_FORMAT 1590ba8c6fd5SDavid Greenman ": warning: unsupported PHY, type = %d, addr = %d\n", 1591ba8c6fd5SDavid Greenman FXP_ARGS(sc), sc->phy_primary_device, 1592ba8c6fd5SDavid Greenman sc->phy_primary_addr); 1593ba8c6fd5SDavid Greenman } 1594ba8c6fd5SDavid Greenman } 1595ba8c6fd5SDavid Greenman 1596ba8c6fd5SDavid Greenman /* 1597ba8c6fd5SDavid Greenman * Change media according to request. 1598ba8c6fd5SDavid Greenman */ 1599ba8c6fd5SDavid Greenman int 1600ba8c6fd5SDavid Greenman fxp_mediachange(ifp) 1601ba8c6fd5SDavid Greenman struct ifnet *ifp; 1602ba8c6fd5SDavid Greenman { 1603ba8c6fd5SDavid Greenman struct fxp_softc *sc = ifp->if_softc; 1604ba8c6fd5SDavid Greenman struct ifmedia *ifm = &sc->sc_media; 1605ba8c6fd5SDavid Greenman 1606ba8c6fd5SDavid Greenman if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) 1607ba8c6fd5SDavid Greenman return (EINVAL); 1608ba8c6fd5SDavid Greenman 1609ba8c6fd5SDavid Greenman fxp_set_media(sc, ifm->ifm_media); 1610ba8c6fd5SDavid Greenman return (0); 1611ba8c6fd5SDavid Greenman } 1612ba8c6fd5SDavid Greenman 1613ba8c6fd5SDavid Greenman /* 1614ba8c6fd5SDavid Greenman * Notify the world which media we're using. 1615ba8c6fd5SDavid Greenman */ 1616ba8c6fd5SDavid Greenman void 1617ba8c6fd5SDavid Greenman fxp_mediastatus(ifp, ifmr) 1618ba8c6fd5SDavid Greenman struct ifnet *ifp; 1619ba8c6fd5SDavid Greenman struct ifmediareq *ifmr; 1620ba8c6fd5SDavid Greenman { 1621ba8c6fd5SDavid Greenman struct fxp_softc *sc = ifp->if_softc; 1622a7280784SJulian Elischer int flags, stsflags; 1623ba8c6fd5SDavid Greenman 1624ba8c6fd5SDavid Greenman switch (sc->phy_primary_device) { 1625ba8c6fd5SDavid Greenman case FXP_PHY_82555: 162635517ab7SDavid Greenman case FXP_PHY_82555B: 1627a7280784SJulian Elischer case FXP_PHY_DP83840: 1628a7280784SJulian Elischer case FXP_PHY_DP83840A: 1629a7280784SJulian Elischer ifmr->ifm_status = IFM_AVALID; /* IFM_ACTIVE will be valid */ 1630ba8c6fd5SDavid Greenman ifmr->ifm_active = IFM_ETHER; 1631a7280784SJulian Elischer /* 1632a7280784SJulian Elischer * the following is not an error. 1633a7280784SJulian Elischer * You need to read this register twice to get current 1634a7280784SJulian Elischer * status. This is correct documented behaviour, the 1635a7280784SJulian Elischer * first read gets latched values. 1636a7280784SJulian Elischer */ 1637a7280784SJulian Elischer stsflags = fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_STS); 1638a7280784SJulian Elischer stsflags = fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_STS); 1639a7280784SJulian Elischer if (stsflags & FXP_PHY_STS_LINK_STS) 1640a7280784SJulian Elischer ifmr->ifm_status |= IFM_ACTIVE; 1641a7280784SJulian Elischer 1642a7280784SJulian Elischer /* 1643a7280784SJulian Elischer * If we are in auto mode, then try report the result. 1644a7280784SJulian Elischer */ 1645a7280784SJulian Elischer flags = fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_BMCR); 1646f1bf08c2SJulian Elischer if (flags & FXP_PHY_BMCR_AUTOEN) { 1647f1bf08c2SJulian Elischer ifmr->ifm_active |= IFM_AUTO; /* XXX presently 0 */ 1648a7280784SJulian Elischer if (stsflags & FXP_PHY_STS_AUTO_DONE) { 1649f1bf08c2SJulian Elischer /* 1650a7280784SJulian Elischer * Intel and National parts report 1651a7280784SJulian Elischer * differently on what they found. 1652f1bf08c2SJulian Elischer */ 1653f1bf08c2SJulian Elischer if ((sc->phy_primary_device == FXP_PHY_82555) 1654f1bf08c2SJulian Elischer || (sc->phy_primary_device == FXP_PHY_82555B)) { 1655f1bf08c2SJulian Elischer flags = fxp_mdi_read(sc, 1656a7280784SJulian Elischer sc->phy_primary_addr, 1657a7280784SJulian Elischer FXP_PHY_USC); 1658f1bf08c2SJulian Elischer 1659f1bf08c2SJulian Elischer if (flags & FXP_PHY_USC_SPEED) 1660f1bf08c2SJulian Elischer ifmr->ifm_active |= IFM_100_TX; 1661f1bf08c2SJulian Elischer else 1662f1bf08c2SJulian Elischer ifmr->ifm_active |= IFM_10_T; 1663f1bf08c2SJulian Elischer 1664f1bf08c2SJulian Elischer if (flags & FXP_PHY_USC_DUPLEX) 1665f1bf08c2SJulian Elischer ifmr->ifm_active |= IFM_FDX; 1666a7280784SJulian Elischer } else { /* it's National. only know speed */ 1667a7280784SJulian Elischer flags = fxp_mdi_read(sc, 1668a7280784SJulian Elischer sc->phy_primary_addr, 1669a7280784SJulian Elischer FXP_DP83840_PAR); 1670a7280784SJulian Elischer 1671a7280784SJulian Elischer if (flags & FXP_DP83840_PAR_SPEED_10) 1672a7280784SJulian Elischer ifmr->ifm_active |= IFM_10_T; 1673a7280784SJulian Elischer else 1674a7280784SJulian Elischer ifmr->ifm_active |= IFM_100_TX; 1675f1bf08c2SJulian Elischer } 1676a7280784SJulian Elischer } 1677a7280784SJulian Elischer } else { /* in manual mode.. just report what we were set to */ 1678ba8c6fd5SDavid Greenman if (flags & FXP_PHY_BMCR_SPEED_100M) 1679ba8c6fd5SDavid Greenman ifmr->ifm_active |= IFM_100_TX; 1680ba8c6fd5SDavid Greenman else 1681ba8c6fd5SDavid Greenman ifmr->ifm_active |= IFM_10_T; 1682ba8c6fd5SDavid Greenman 1683ba8c6fd5SDavid Greenman if (flags & FXP_PHY_BMCR_FULLDUPLEX) 1684ba8c6fd5SDavid Greenman ifmr->ifm_active |= IFM_FDX; 1685ba8c6fd5SDavid Greenman } 1686ba8c6fd5SDavid Greenman break; 1687ba8c6fd5SDavid Greenman 1688ba8c6fd5SDavid Greenman case FXP_PHY_80C24: 1689ba8c6fd5SDavid Greenman default: 1690ba8c6fd5SDavid Greenman ifmr->ifm_active = IFM_ETHER|IFM_MANUAL; /* XXX IFM_AUTO ? */ 1691ba8c6fd5SDavid Greenman } 1692ba8c6fd5SDavid Greenman } 1693ba8c6fd5SDavid Greenman 1694a17c678eSDavid Greenman /* 1695a17c678eSDavid Greenman * Add a buffer to the end of the RFA buffer list. 1696a17c678eSDavid Greenman * Return 0 if successful, 1 for failure. A failure results in 1697a17c678eSDavid Greenman * adding the 'oldm' (if non-NULL) on to the end of the list - 1698dc733423SDag-Erling Smørgrav * tossing out its old contents and recycling it. 1699a17c678eSDavid Greenman * The RFA struct is stuck at the beginning of mbuf cluster and the 1700a17c678eSDavid Greenman * data pointer is fixed up to point just past it. 1701a17c678eSDavid Greenman */ 1702a17c678eSDavid Greenman static int 1703a17c678eSDavid Greenman fxp_add_rfabuf(sc, oldm) 1704a17c678eSDavid Greenman struct fxp_softc *sc; 1705a17c678eSDavid Greenman struct mbuf *oldm; 1706a17c678eSDavid Greenman { 1707ba8c6fd5SDavid Greenman u_int32_t v; 1708a17c678eSDavid Greenman struct mbuf *m; 1709a17c678eSDavid Greenman struct fxp_rfa *rfa, *p_rfa; 1710a17c678eSDavid Greenman 1711a17c678eSDavid Greenman MGETHDR(m, M_DONTWAIT, MT_DATA); 1712a17c678eSDavid Greenman if (m != NULL) { 1713a17c678eSDavid Greenman MCLGET(m, M_DONTWAIT); 1714a17c678eSDavid Greenman if ((m->m_flags & M_EXT) == 0) { 1715a17c678eSDavid Greenman m_freem(m); 1716eadd5e3aSDavid Greenman if (oldm == NULL) 1717eadd5e3aSDavid Greenman return 1; 1718a17c678eSDavid Greenman m = oldm; 1719eadd5e3aSDavid Greenman m->m_data = m->m_ext.ext_buf; 1720a17c678eSDavid Greenman } 1721a17c678eSDavid Greenman } else { 1722eadd5e3aSDavid Greenman if (oldm == NULL) 1723a17c678eSDavid Greenman return 1; 1724eadd5e3aSDavid Greenman m = oldm; 1725eadd5e3aSDavid Greenman m->m_data = m->m_ext.ext_buf; 1726eadd5e3aSDavid Greenman } 1727ba8c6fd5SDavid Greenman 1728ba8c6fd5SDavid Greenman /* 1729ba8c6fd5SDavid Greenman * Move the data pointer up so that the incoming data packet 1730ba8c6fd5SDavid Greenman * will be 32-bit aligned. 1731ba8c6fd5SDavid Greenman */ 1732ba8c6fd5SDavid Greenman m->m_data += RFA_ALIGNMENT_FUDGE; 1733ba8c6fd5SDavid Greenman 1734eadd5e3aSDavid Greenman /* 1735eadd5e3aSDavid Greenman * Get a pointer to the base of the mbuf cluster and move 1736eadd5e3aSDavid Greenman * data start past it. 1737eadd5e3aSDavid Greenman */ 1738a17c678eSDavid Greenman rfa = mtod(m, struct fxp_rfa *); 1739eadd5e3aSDavid Greenman m->m_data += sizeof(struct fxp_rfa); 17404fc1dda9SAndrew Gallatin rfa->size = (u_int16_t)(MCLBYTES - sizeof(struct fxp_rfa) - RFA_ALIGNMENT_FUDGE); 1741eadd5e3aSDavid Greenman 1742ba8c6fd5SDavid Greenman /* 1743ba8c6fd5SDavid Greenman * Initialize the rest of the RFA. Note that since the RFA 1744ba8c6fd5SDavid Greenman * is misaligned, we cannot store values directly. Instead, 1745ba8c6fd5SDavid Greenman * we use an optimized, inline copy. 1746ba8c6fd5SDavid Greenman */ 17474fc1dda9SAndrew Gallatin 1748a17c678eSDavid Greenman rfa->rfa_status = 0; 1749a17c678eSDavid Greenman rfa->rfa_control = FXP_RFA_CONTROL_EL; 1750a17c678eSDavid Greenman rfa->actual_size = 0; 1751ba8c6fd5SDavid Greenman 1752ba8c6fd5SDavid Greenman v = -1; 17534fc1dda9SAndrew Gallatin fxp_lwcopy(&v, (volatile u_int32_t *) rfa->link_addr); 17544fc1dda9SAndrew Gallatin fxp_lwcopy(&v, (volatile u_int32_t *) rfa->rbd_addr); 1755ba8c6fd5SDavid Greenman 1756dfe61cf1SDavid Greenman /* 1757dfe61cf1SDavid Greenman * If there are other buffers already on the list, attach this 1758dfe61cf1SDavid Greenman * one to the end by fixing up the tail to point to this one. 1759dfe61cf1SDavid Greenman */ 1760a17c678eSDavid Greenman if (sc->rfa_headm != NULL) { 1761ba8c6fd5SDavid Greenman p_rfa = (struct fxp_rfa *) (sc->rfa_tailm->m_ext.ext_buf + 1762ba8c6fd5SDavid Greenman RFA_ALIGNMENT_FUDGE); 1763a17c678eSDavid Greenman sc->rfa_tailm->m_next = m; 1764ba8c6fd5SDavid Greenman v = vtophys(rfa); 17654fc1dda9SAndrew Gallatin fxp_lwcopy(&v, (volatile u_int32_t *) p_rfa->link_addr); 1766a17c678eSDavid Greenman p_rfa->rfa_control &= ~FXP_RFA_CONTROL_EL; 1767a17c678eSDavid Greenman } else { 1768a17c678eSDavid Greenman sc->rfa_headm = m; 1769a17c678eSDavid Greenman } 1770a17c678eSDavid Greenman sc->rfa_tailm = m; 1771a17c678eSDavid Greenman 1772dfe61cf1SDavid Greenman return (m == oldm); 1773a17c678eSDavid Greenman } 1774a17c678eSDavid Greenman 17756ebc3153SDavid Greenman static volatile int 1776ba8c6fd5SDavid Greenman fxp_mdi_read(sc, phy, reg) 1777ba8c6fd5SDavid Greenman struct fxp_softc *sc; 1778dccee1a1SDavid Greenman int phy; 1779dccee1a1SDavid Greenman int reg; 1780dccee1a1SDavid Greenman { 1781dccee1a1SDavid Greenman int count = 10000; 17826ebc3153SDavid Greenman int value; 1783dccee1a1SDavid Greenman 1784ba8c6fd5SDavid Greenman CSR_WRITE_4(sc, FXP_CSR_MDICONTROL, 1785ba8c6fd5SDavid Greenman (FXP_MDI_READ << 26) | (reg << 16) | (phy << 21)); 1786dccee1a1SDavid Greenman 1787ba8c6fd5SDavid Greenman while (((value = CSR_READ_4(sc, FXP_CSR_MDICONTROL)) & 0x10000000) == 0 1788ba8c6fd5SDavid Greenman && count--) 17896ebc3153SDavid Greenman DELAY(10); 1790dccee1a1SDavid Greenman 1791dccee1a1SDavid Greenman if (count <= 0) 1792ba8c6fd5SDavid Greenman printf(FXP_FORMAT ": fxp_mdi_read: timed out\n", 1793ba8c6fd5SDavid Greenman FXP_ARGS(sc)); 1794dccee1a1SDavid Greenman 17956ebc3153SDavid Greenman return (value & 0xffff); 1796dccee1a1SDavid Greenman } 1797dccee1a1SDavid Greenman 1798dccee1a1SDavid Greenman static void 1799ba8c6fd5SDavid Greenman fxp_mdi_write(sc, phy, reg, value) 1800ba8c6fd5SDavid Greenman struct fxp_softc *sc; 1801dccee1a1SDavid Greenman int phy; 1802dccee1a1SDavid Greenman int reg; 1803dccee1a1SDavid Greenman int value; 1804dccee1a1SDavid Greenman { 1805dccee1a1SDavid Greenman int count = 10000; 1806dccee1a1SDavid Greenman 1807ba8c6fd5SDavid Greenman CSR_WRITE_4(sc, FXP_CSR_MDICONTROL, 1808ba8c6fd5SDavid Greenman (FXP_MDI_WRITE << 26) | (reg << 16) | (phy << 21) | 1809ba8c6fd5SDavid Greenman (value & 0xffff)); 1810dccee1a1SDavid Greenman 1811ba8c6fd5SDavid Greenman while((CSR_READ_4(sc, FXP_CSR_MDICONTROL) & 0x10000000) == 0 && 1812ba8c6fd5SDavid Greenman count--) 18136ebc3153SDavid Greenman DELAY(10); 1814dccee1a1SDavid Greenman 1815dccee1a1SDavid Greenman if (count <= 0) 1816ba8c6fd5SDavid Greenman printf(FXP_FORMAT ": fxp_mdi_write: timed out\n", 1817ba8c6fd5SDavid Greenman FXP_ARGS(sc)); 1818dccee1a1SDavid Greenman } 1819dccee1a1SDavid Greenman 1820dccee1a1SDavid Greenman static int 1821a17c678eSDavid Greenman fxp_ioctl(ifp, command, data) 1822a17c678eSDavid Greenman struct ifnet *ifp; 1823ba8c6fd5SDavid Greenman FXP_IOCTLCMD_TYPE command; 1824a17c678eSDavid Greenman caddr_t data; 1825a17c678eSDavid Greenman { 18269b44ff22SGarrett Wollman struct fxp_softc *sc = ifp->if_softc; 1827a17c678eSDavid Greenman struct ifreq *ifr = (struct ifreq *)data; 1828a17c678eSDavid Greenman int s, error = 0; 1829a17c678eSDavid Greenman 1830a17c678eSDavid Greenman s = splimp(); 1831a17c678eSDavid Greenman 1832a17c678eSDavid Greenman switch (command) { 1833a17c678eSDavid Greenman 1834a17c678eSDavid Greenman case SIOCSIFADDR: 1835ba8c6fd5SDavid Greenman #if !defined(__NetBSD__) 1836a17c678eSDavid Greenman case SIOCGIFADDR: 1837fb583156SDavid Greenman case SIOCSIFMTU: 1838ba8c6fd5SDavid Greenman #endif 1839fb583156SDavid Greenman error = ether_ioctl(ifp, command, data); 1840a17c678eSDavid Greenman break; 1841a17c678eSDavid Greenman 1842a17c678eSDavid Greenman case SIOCSIFFLAGS: 1843397f9dfeSDavid Greenman sc->all_mcasts = (ifp->if_flags & IFF_ALLMULTI) ? 1 : 0; 1844a17c678eSDavid Greenman 1845a17c678eSDavid Greenman /* 1846a17c678eSDavid Greenman * If interface is marked up and not running, then start it. 1847a17c678eSDavid Greenman * If it is marked down and running, stop it. 1848a17c678eSDavid Greenman * XXX If it's up then re-initialize it. This is so flags 1849a17c678eSDavid Greenman * such as IFF_PROMISC are handled. 1850a17c678eSDavid Greenman */ 1851a17c678eSDavid Greenman if (ifp->if_flags & IFF_UP) { 1852fb583156SDavid Greenman fxp_init(sc); 1853a17c678eSDavid Greenman } else { 1854a17c678eSDavid Greenman if (ifp->if_flags & IFF_RUNNING) 18554a5f1499SDavid Greenman fxp_stop(sc); 1856a17c678eSDavid Greenman } 1857a17c678eSDavid Greenman break; 1858a17c678eSDavid Greenman 1859a17c678eSDavid Greenman case SIOCADDMULTI: 1860a17c678eSDavid Greenman case SIOCDELMULTI: 1861397f9dfeSDavid Greenman sc->all_mcasts = (ifp->if_flags & IFF_ALLMULTI) ? 1 : 0; 1862ba8c6fd5SDavid Greenman #if defined(__NetBSD__) 1863ba8c6fd5SDavid Greenman error = (command == SIOCADDMULTI) ? 1864ba8c6fd5SDavid Greenman ether_addmulti(ifr, &sc->sc_ethercom) : 1865ba8c6fd5SDavid Greenman ether_delmulti(ifr, &sc->sc_ethercom); 1866ba8c6fd5SDavid Greenman 1867ba8c6fd5SDavid Greenman if (error == ENETRESET) { 1868ba8c6fd5SDavid Greenman /* 1869ba8c6fd5SDavid Greenman * Multicast list has changed; set the hardware 1870ba8c6fd5SDavid Greenman * filter accordingly. 1871ba8c6fd5SDavid Greenman */ 1872397f9dfeSDavid Greenman if (!sc->all_mcasts) 1873397f9dfeSDavid Greenman fxp_mc_setup(sc); 1874397f9dfeSDavid Greenman /* 1875397f9dfeSDavid Greenman * fxp_mc_setup() can turn on all_mcasts if we run 1876397f9dfeSDavid Greenman * out of space, so check it again rather than else {}. 1877397f9dfeSDavid Greenman */ 1878397f9dfeSDavid Greenman if (sc->all_mcasts) 1879ba8c6fd5SDavid Greenman fxp_init(sc); 1880ba8c6fd5SDavid Greenman error = 0; 1881ba8c6fd5SDavid Greenman } 1882ba8c6fd5SDavid Greenman #else /* __FreeBSD__ */ 1883a17c678eSDavid Greenman /* 1884a17c678eSDavid Greenman * Multicast list has changed; set the hardware filter 1885a17c678eSDavid Greenman * accordingly. 1886a17c678eSDavid Greenman */ 1887397f9dfeSDavid Greenman if (!sc->all_mcasts) 1888397f9dfeSDavid Greenman fxp_mc_setup(sc); 1889397f9dfeSDavid Greenman /* 1890397f9dfeSDavid Greenman * fxp_mc_setup() can turn on sc->all_mcasts, so check it 1891397f9dfeSDavid Greenman * again rather than else {}. 1892397f9dfeSDavid Greenman */ 1893397f9dfeSDavid Greenman if (sc->all_mcasts) 1894fb583156SDavid Greenman fxp_init(sc); 1895a17c678eSDavid Greenman error = 0; 1896ba8c6fd5SDavid Greenman #endif /* __NetBSD__ */ 1897ba8c6fd5SDavid Greenman break; 1898ba8c6fd5SDavid Greenman 1899ba8c6fd5SDavid Greenman case SIOCSIFMEDIA: 1900ba8c6fd5SDavid Greenman case SIOCGIFMEDIA: 1901ba8c6fd5SDavid Greenman error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, command); 1902a17c678eSDavid Greenman break; 1903a17c678eSDavid Greenman 1904a17c678eSDavid Greenman default: 1905a17c678eSDavid Greenman error = EINVAL; 1906a17c678eSDavid Greenman } 1907a17c678eSDavid Greenman (void) splx(s); 1908a17c678eSDavid Greenman return (error); 1909a17c678eSDavid Greenman } 1910397f9dfeSDavid Greenman 1911397f9dfeSDavid Greenman /* 1912397f9dfeSDavid Greenman * Program the multicast filter. 1913397f9dfeSDavid Greenman * 1914397f9dfeSDavid Greenman * We have an artificial restriction that the multicast setup command 1915397f9dfeSDavid Greenman * must be the first command in the chain, so we take steps to ensure 19163114fdb4SDavid Greenman * this. By requiring this, it allows us to keep up the performance of 1917397f9dfeSDavid Greenman * the pre-initialized command ring (esp. link pointers) by not actually 1918dc733423SDag-Erling Smørgrav * inserting the mcsetup command in the ring - i.e. its link pointer 1919397f9dfeSDavid Greenman * points to the TxCB ring, but the mcsetup descriptor itself is not part 1920397f9dfeSDavid Greenman * of it. We then can do 'CU_START' on the mcsetup descriptor and have it 1921397f9dfeSDavid Greenman * lead into the regular TxCB ring when it completes. 1922397f9dfeSDavid Greenman * 1923397f9dfeSDavid Greenman * This function must be called at splimp. 1924397f9dfeSDavid Greenman */ 1925397f9dfeSDavid Greenman static void 1926397f9dfeSDavid Greenman fxp_mc_setup(sc) 1927397f9dfeSDavid Greenman struct fxp_softc *sc; 1928397f9dfeSDavid Greenman { 1929397f9dfeSDavid Greenman struct fxp_cb_mcs *mcsp = sc->mcsp; 1930397f9dfeSDavid Greenman struct ifnet *ifp = &sc->sc_if; 1931397f9dfeSDavid Greenman struct ifmultiaddr *ifma; 1932397f9dfeSDavid Greenman int nmcasts; 1933397f9dfeSDavid Greenman 19343114fdb4SDavid Greenman /* 19353114fdb4SDavid Greenman * If there are queued commands, we must wait until they are all 19363114fdb4SDavid Greenman * completed. If we are already waiting, then add a NOP command 19373114fdb4SDavid Greenman * with interrupt option so that we're notified when all commands 19383114fdb4SDavid Greenman * have been completed - fxp_start() ensures that no additional 19393114fdb4SDavid Greenman * TX commands will be added when need_mcsetup is true. 19403114fdb4SDavid Greenman */ 1941397f9dfeSDavid Greenman if (sc->tx_queued) { 19423114fdb4SDavid Greenman struct fxp_cb_tx *txp; 19433114fdb4SDavid Greenman 19443114fdb4SDavid Greenman /* 19453114fdb4SDavid Greenman * need_mcsetup will be true if we are already waiting for the 19463114fdb4SDavid Greenman * NOP command to be completed (see below). In this case, bail. 19473114fdb4SDavid Greenman */ 19483114fdb4SDavid Greenman if (sc->need_mcsetup) 19493114fdb4SDavid Greenman return; 1950397f9dfeSDavid Greenman sc->need_mcsetup = 1; 19513114fdb4SDavid Greenman 19523114fdb4SDavid Greenman /* 19533114fdb4SDavid Greenman * Add a NOP command with interrupt so that we are notified when all 19543114fdb4SDavid Greenman * TX commands have been processed. 19553114fdb4SDavid Greenman */ 19563114fdb4SDavid Greenman txp = sc->cbl_last->next; 19573114fdb4SDavid Greenman txp->mb_head = NULL; 19583114fdb4SDavid Greenman txp->cb_status = 0; 19593114fdb4SDavid Greenman txp->cb_command = FXP_CB_COMMAND_NOP | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I; 19603114fdb4SDavid Greenman /* 19613114fdb4SDavid Greenman * Advance the end of list forward. 19623114fdb4SDavid Greenman */ 19633114fdb4SDavid Greenman sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S; 19643114fdb4SDavid Greenman sc->cbl_last = txp; 19653114fdb4SDavid Greenman sc->tx_queued++; 19663114fdb4SDavid Greenman /* 19673114fdb4SDavid Greenman * Issue a resume in case the CU has just suspended. 19683114fdb4SDavid Greenman */ 19693114fdb4SDavid Greenman fxp_scb_wait(sc); 19703114fdb4SDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_RESUME); 19713114fdb4SDavid Greenman /* 19723114fdb4SDavid Greenman * Set a 5 second timer just in case we don't hear from the 19733114fdb4SDavid Greenman * card again. 19743114fdb4SDavid Greenman */ 19753114fdb4SDavid Greenman ifp->if_timer = 5; 19763114fdb4SDavid Greenman 1977397f9dfeSDavid Greenman return; 1978397f9dfeSDavid Greenman } 1979397f9dfeSDavid Greenman sc->need_mcsetup = 0; 1980397f9dfeSDavid Greenman 1981397f9dfeSDavid Greenman /* 1982397f9dfeSDavid Greenman * Initialize multicast setup descriptor. 1983397f9dfeSDavid Greenman */ 1984397f9dfeSDavid Greenman mcsp->next = sc->cbl_base; 1985397f9dfeSDavid Greenman mcsp->mb_head = NULL; 1986397f9dfeSDavid Greenman mcsp->cb_status = 0; 19873114fdb4SDavid Greenman mcsp->cb_command = FXP_CB_COMMAND_MCAS | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I; 1988397f9dfeSDavid Greenman mcsp->link_addr = vtophys(&sc->cbl_base->cb_status); 1989397f9dfeSDavid Greenman 1990397f9dfeSDavid Greenman nmcasts = 0; 1991397f9dfeSDavid Greenman if (!sc->all_mcasts) { 1992397f9dfeSDavid Greenman for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 1993397f9dfeSDavid Greenman ifma = ifma->ifma_link.le_next) { 1994397f9dfeSDavid Greenman if (ifma->ifma_addr->sa_family != AF_LINK) 1995397f9dfeSDavid Greenman continue; 1996397f9dfeSDavid Greenman if (nmcasts >= MAXMCADDR) { 1997397f9dfeSDavid Greenman sc->all_mcasts = 1; 1998397f9dfeSDavid Greenman nmcasts = 0; 1999397f9dfeSDavid Greenman break; 2000397f9dfeSDavid Greenman } 2001397f9dfeSDavid Greenman bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 20028aef1712SMatthew Dillon (volatile void *) &sc->mcsp->mc_addr[nmcasts][0], 6); 2003397f9dfeSDavid Greenman nmcasts++; 2004397f9dfeSDavid Greenman } 2005397f9dfeSDavid Greenman } 2006397f9dfeSDavid Greenman mcsp->mc_cnt = nmcasts * 6; 2007397f9dfeSDavid Greenman sc->cbl_first = sc->cbl_last = (struct fxp_cb_tx *) mcsp; 2008397f9dfeSDavid Greenman sc->tx_queued = 1; 2009397f9dfeSDavid Greenman 2010397f9dfeSDavid Greenman /* 2011397f9dfeSDavid Greenman * Wait until command unit is not active. This should never 2012397f9dfeSDavid Greenman * be the case when nothing is queued, but make sure anyway. 2013397f9dfeSDavid Greenman */ 2014397f9dfeSDavid Greenman while ((CSR_READ_1(sc, FXP_CSR_SCB_RUSCUS) >> 6) == 2015397f9dfeSDavid Greenman FXP_SCB_CUS_ACTIVE) ; 2016397f9dfeSDavid Greenman 2017397f9dfeSDavid Greenman /* 2018397f9dfeSDavid Greenman * Start the multicast setup command. 2019397f9dfeSDavid Greenman */ 2020397f9dfeSDavid Greenman fxp_scb_wait(sc); 2021397f9dfeSDavid Greenman CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&mcsp->cb_status)); 2022397f9dfeSDavid Greenman CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); 2023397f9dfeSDavid Greenman 20243114fdb4SDavid Greenman ifp->if_timer = 2; 2025397f9dfeSDavid Greenman return; 2026397f9dfeSDavid Greenman } 2027