From 58fcb06c24acca6395fda6651d49f95b9b2ebe7d Mon Sep 17 00:00:00 2001 From: Nathan Whitehorn Date: Thu, 30 Sep 2004 21:39:31 +0000 Subject: [PATCH] Added the Broadcom 440x driver under the SubIncludeGPL rule. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@9127 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/drivers/network/Jamfile | 2 + .../kernel/drivers/network/bcm440x/Jamfile | 51 + .../kernel/drivers/network/bcm440x/README | 11 + .../kernel/drivers/network/bcm440x/b44.h | 1130 +++++++++ .../kernel/drivers/network/bcm440x/b44lm.c | 2097 +++++++++++++++++ .../kernel/drivers/network/bcm440x/b44lm.h | 411 ++++ .../kernel/drivers/network/bcm440x/b44mm.h | 132 ++ .../kernel/drivers/network/bcm440x/b44queue.h | 342 +++ .../kernel/drivers/network/bcm440x/b44um.c | 489 ++++ .../kernel/drivers/network/bcm440x/mempool.c | 104 + .../kernel/drivers/network/bcm440x/mempool.h | 35 + 11 files changed, 4804 insertions(+) create mode 100644 src/add-ons/kernel/drivers/network/bcm440x/Jamfile create mode 100644 src/add-ons/kernel/drivers/network/bcm440x/README create mode 100644 src/add-ons/kernel/drivers/network/bcm440x/b44.h create mode 100644 src/add-ons/kernel/drivers/network/bcm440x/b44lm.c create mode 100644 src/add-ons/kernel/drivers/network/bcm440x/b44lm.h create mode 100644 src/add-ons/kernel/drivers/network/bcm440x/b44mm.h create mode 100644 src/add-ons/kernel/drivers/network/bcm440x/b44queue.h create mode 100644 src/add-ons/kernel/drivers/network/bcm440x/b44um.c create mode 100644 src/add-ons/kernel/drivers/network/bcm440x/mempool.c create mode 100644 src/add-ons/kernel/drivers/network/bcm440x/mempool.h diff --git a/src/add-ons/kernel/drivers/network/Jamfile b/src/add-ons/kernel/drivers/network/Jamfile index 20f732d0d7..d95f99c8c1 100644 --- a/src/add-ons/kernel/drivers/network/Jamfile +++ b/src/add-ons/kernel/drivers/network/Jamfile @@ -7,3 +7,5 @@ SubInclude OBOS_TOP src add-ons kernel drivers network wb840 ; SubInclude OBOS_TOP src add-ons kernel drivers network via-rhine ; SubInclude OBOS_TOP src add-ons kernel drivers network rtl8169 ; SubInclude OBOS_TOP src add-ons kernel drivers network ipro1000 ; + +SubIncludeGPL OBOS_TOP src add-ons kernel drivers network bcm440x ; \ No newline at end of file diff --git a/src/add-ons/kernel/drivers/network/bcm440x/Jamfile b/src/add-ons/kernel/drivers/network/bcm440x/Jamfile new file mode 100644 index 0000000000..e3cb54ac36 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/bcm440x/Jamfile @@ -0,0 +1,51 @@ +SubDir OBOS_TOP src add-ons kernel drivers network bcm440x ; + +# For ether_driver.h +UsePrivateHeaders net ; + +R5KernelAddon bcm440x : kernel drivers bin : + b44lm.c + b44um.c + mempool.c + ; + +Package haiku-networkingkit-cvs : + bcm440x : + boot home config add-ons kernel drivers bin ; +Package haiku-networkingkit-cvs : + bcm440x : + boot home config add-ons kernel drivers dev net ; + +# Link driver(s) to kernel/drivers/dev/net +{ + local dir = [ FDirName $(OBOS_ADDON_DIR) kernel drivers dev net ] ; + + local instDriver = bcm440x ; + MakeLocate $(instDriver) : $(dir) ; + RelSymLink $(instDriver) : bcm440x ; +} + + +rule InstallSiS900 +{ + Depends $(<) : $(>) ; +} + +actions ignore InstallSiS900 +{ + cp $(>) /boot/home/config/add-ons/kernel/drivers/bin/ +} + +#InstallSiS900 install : sis900 ; + +# Installation + +OBOSInstall install-networking : /boot/home/config/add-ons/kernel/drivers/bin : + sis900 +; + +OBOSInstallRelSymLink install-networking : /boot/home/config/add-ons/kernel/drivers/dev/net : + sis900 : + installed-symlink +; + diff --git a/src/add-ons/kernel/drivers/network/bcm440x/README b/src/add-ons/kernel/drivers/network/bcm440x/README new file mode 100644 index 0000000000..d8563507e7 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/bcm440x/README @@ -0,0 +1,11 @@ +Broadcom 440x 10/100 Ethernet Driver + +Copyright 2003-4 Nathan Whitehorn, portions copyright 2000-2004 Broadcom Corporation + +To install, double-click the file appropriately named "Double Click to Install". This has not been tested with net_server, but it should work. + +The driver is based on version 3.0.7 of the Linux drivers provided by Broadcom corporation, which can be obtained from their website, and is licensed under the GNU General Public License, which can be obtained from the Free Software Foundation. + +Questions and support requests should be directed to me at nathanw@uchicago.edu + +Many thanks to Broadcom for not being nVidia, and not only publishing source to their driver, but making it quite portable, even if the source was almost uncommented. Thanks as well to Stephen Flood, for allowing me to abuse his laptop in porting this driver. \ No newline at end of file diff --git a/src/add-ons/kernel/drivers/network/bcm440x/b44.h b/src/add-ons/kernel/drivers/network/bcm440x/b44.h new file mode 100644 index 0000000000..d144fa92f2 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/bcm440x/b44.h @@ -0,0 +1,1130 @@ + +/******************************************************************************/ +/* */ +/* Broadcom BCM4400 Linux Network Driver, Copyright (c) 2002 Broadcom */ +/* Corporation. */ +/* All rights reserved. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, located in the file LICENSE. */ +/* */ +/* History: */ +/* */ +/******************************************************************************/ + +#ifndef B44_H +#define B44_H + +#include "b44lm.h" + + + +/******************************************************************************/ +/* Constants. */ +/******************************************************************************/ + +/* Maxim number of packet descriptors used for sending packets. */ +#define MAX_TX_PACKET_DESC_COUNT 512 +#define DEFAULT_TX_PACKET_DESC_COUNT 64 + +#define MAX_RX_PACKET_DESC_COUNT 512 +#define DEFAULT_RX_PACKET_DESC_COUNT 64 + +#define BIT_0 0x01 +#define BIT_1 0x02 +#define BIT_2 0x04 +#define BIT_3 0x08 +#define BIT_4 0x10 +#define BIT_5 0x20 +#define BIT_6 0x40 +#define BIT_7 0x80 +#define BIT_8 0x100 +#define BIT_9 0x200 +#define BIT_10 0x400 +#define BIT_11 0x800 +#define BIT_12 0x1000 +#define BIT_13 0x2000 +#define BIT_14 0x4000 +#define BIT_15 0x8000 +#define BIT_16 0x10000 +#define BIT_17 0x20000 +#define BIT_18 0x40000 +#define BIT_19 0x80000 +#define BIT_20 0x100000 +#define BIT_21 0x200000 +#define BIT_22 0x400000 +#define BIT_23 0x800000 +#define BIT_24 0x1000000 +#define BIT_25 0x2000000 +#define BIT_26 0x4000000 +#define BIT_27 0x8000000 +#define BIT_28 0x10000000 +#define BIT_29 0x20000000 +#define BIT_30 0x40000000 +#define BIT_31 0x80000000 + +#define ROUNDUP(x, y) ((((LM_UINT32)(x)+((y)-1))/(y))*(y)) + +/******************************************************************************/ +/* MII registers. */ +/******************************************************************************/ + +/* Control register. */ +#define PHY_CTRL_REG 0x00 + +#define PHY_CTRL_SPEED_MASK (BIT_6 | BIT_13) +#define PHY_CTRL_SPEED_SELECT_10MBPS BIT_NONE +#define PHY_CTRL_SPEED_SELECT_100MBPS BIT_13 +#define PHY_CTRL_SPEED_SELECT_1000MBPS BIT_6 +#define PHY_CTRL_COLLISION_TEST_ENABLE BIT_7 +#define PHY_CTRL_FULL_DUPLEX_MODE BIT_8 +#define PHY_CTRL_RESTART_AUTO_NEG BIT_9 +#define PHY_CTRL_ISOLATE_PHY BIT_10 +#define PHY_CTRL_LOWER_POWER_MODE BIT_11 +#define PHY_CTRL_AUTO_NEG_ENABLE BIT_12 +#define PHY_CTRL_LOOPBACK_MODE BIT_14 +#define PHY_CTRL_PHY_RESET BIT_15 + + +/* Status register. */ +#define PHY_STATUS_REG 0x01 + +#define PHY_STATUS_LINK_PASS BIT_2 +#define PHY_STATUS_AUTO_NEG_COMPLETE BIT_5 + + +/* Phy Id registers. */ +#define PHY_ID1_REG 0x02 +#define PHY_ID1_OUI_MASK 0xffff + +#define PHY_ID2_REG 0x03 +#define PHY_ID2_REV_MASK 0x000f +#define PHY_ID2_MODEL_MASK 0x03f0 +#define PHY_ID2_OUI_MASK 0xfc00 + + +/* Auto-negotiation advertisement register. */ +#define PHY_AN_AD_REG 0x04 + +#define PHY_AN_AD_ASYM_PAUSE BIT_11 +#define PHY_AN_AD_PAUSE_CAPABLE BIT_10 +#define PHY_AN_AD_10BASET_HALF BIT_5 +#define PHY_AN_AD_10BASET_FULL BIT_6 +#define PHY_AN_AD_100BASETX_HALF BIT_7 +#define PHY_AN_AD_100BASETX_FULL BIT_8 +#define PHY_AN_AD_PROTOCOL_802_3_CSMA_CD 0x01 + +#define PHY_AN_AD_ALL_SPEEDS (PHY_AN_AD_10BASET_HALF | \ + PHY_AN_AD_10BASET_FULL | PHY_AN_AD_100BASETX_HALF | \ + PHY_AN_AD_100BASETX_FULL) + +/* Auto-negotiation Link Partner Ability register. */ +#define PHY_LINK_PARTNER_ABILITY_REG 0x05 + +#define PHY_LINK_PARTNER_ASYM_PAUSE BIT_11 +#define PHY_LINK_PARTNER_PAUSE_CAPABLE BIT_10 + + +#define STAT_REMFAULT (1 << 4) /* remote fault */ +#define STAT_LINK (1 << 2) /* link status */ +#define STAT_JAB (1 << 1) /* jabber detected */ +#define AUX_FORCED (1 << 2) /* forced 10/100 */ +#define AUX_SPEED (1 << 1) /* speed 0=10mbps 1=100mbps */ +#define AUX_DUPLEX (1 << 0) /* duplex 0=half 1=full */ + + +/******************************************************************************/ +/* Register definitions. */ +/******************************************************************************/ + +typedef volatile LM_UINT8 T3_8BIT_REGISTER, *PT3_8BIT_REGISTER; +typedef volatile LM_UINT16 T3_16BIT_REGISTER, *PT3_16BIT_REGISTER; +typedef volatile LM_UINT32 T3_32BIT_REGISTER, *PT3_32BIT_REGISTER; + +/* + * Each DMA processor consists of a transmit channel and a receive channel. + */ +typedef volatile struct { + /* transmit channel */ + LM_UINT32 xmtcontrol; /* enable, et al */ + LM_UINT32 xmtaddr; /* descriptor ring base address (4K aligned) */ + LM_UINT32 xmtptr; /* last descriptor posted to chip */ + LM_UINT32 xmtstatus; /* current active descriptor, et al */ + + /* receive channel */ + LM_UINT32 rcvcontrol; /* enable, et al */ + LM_UINT32 rcvaddr; /* descriptor ring base address (4K aligned) */ + LM_UINT32 rcvptr; /* last descriptor posted to chip */ + LM_UINT32 rcvstatus; /* current active descriptor, et al */ + + /* diag access */ + LM_UINT32 fifoaddr; /* diag address */ + LM_UINT32 fifodatalow; /* low 32bits of data */ + LM_UINT32 fifodatahigh; /* high 32bits of data */ + LM_UINT32 pad; /* reserved */ +} dmaregs_t; + +/* transmit channel control */ +#define XC_XE ((LM_UINT32)1 << 0) /* transmit enable */ +#define XC_SE ((LM_UINT32)1 << 1) /* transmit suspend request */ +#define XC_LE ((LM_UINT32)1 << 2) /* loopback enable */ +#define XC_FPRI ((LM_UINT32)1 << 3) /* fair priority */ +#define XC_FL ((LM_UINT32)1 << 4) /* flush request */ + +/* transmit descriptor table pointer */ +#define XP_LD_MASK 0xfff /* last valid descriptor */ + +/* transmit channel status */ +#define XS_CD_MASK 0x0fff /* current descriptor pointer */ +#define XS_XS_MASK 0xf000 /* transmit state */ +#define XS_XS_SHIFT 12 +#define XS_XS_DISABLED 0x0000 /* disabled */ +#define XS_XS_ACTIVE 0x1000 /* active */ +#define XS_XS_IDLE 0x2000 /* idle wait */ +#define XS_XS_STOPPED 0x3000 /* stopped */ +#define XS_XS_SUSP 0x4000 /* suspend pending */ +#define XS_XE_MASK 0xf0000 /* transmit errors */ +#define XS_XE_SHIFT 16 +#define XS_XE_NOERR 0x00000 /* no error */ +#define XS_XE_DPE 0x10000 /* descriptor protocol error */ +#define XS_XE_DFU 0x20000 /* data fifo underrun */ +#define XS_XE_BEBR 0x30000 /* bus error on buffer read */ +#define XS_XE_BEDA 0x40000 /* bus error on descriptor access */ +#define XS_FL ((LM_UINT32)1 << 20) /* flushed */ + +/* receive channel control */ +#define RC_RE ((LM_UINT32)1 << 0) /* receive enable */ +#define RC_RO_MASK 0xfe /* receive frame offset */ +#define RC_RO_SHIFT 1 + +/* receive descriptor table pointer */ +#define RP_LD_MASK 0xfff /* last valid descriptor */ + +/* receive channel status */ +#define RS_CD_MASK 0x0fff /* current descriptor pointer */ +#define RS_RS_MASK 0xf000 /* receive state */ +#define RS_RS_SHIFT 12 +#define RS_RS_DISABLED 0x0000 /* disabled */ +#define RS_RS_ACTIVE 0x1000 /* active */ +#define RS_RS_IDLE 0x2000 /* idle wait */ +#define RS_RS_STOPPED 0x3000 /* reserved */ +#define RS_RE_MASK 0xf0000 /* receive errors */ +#define RS_RE_SHIFT 16 +#define RS_RE_NOERR 0x00000 /* no error */ +#define RS_RE_DPE 0x10000 /* descriptor protocol error */ +#define RS_RE_DFO 0x20000 /* data fifo overflow */ +#define RS_RE_BEBW 0x30000 /* bus error on buffer write */ +#define RS_RE_BEDA 0x40000 /* bus error on descriptor access */ + +/* fifoaddr */ +#define FA_OFF_MASK 0xffff /* offset */ +#define FA_SEL_MASK 0xf0000 /* select */ +#define FA_SEL_SHIFT 16 +#define FA_SEL_XDD 0x00000 /* transmit dma data */ +#define FA_SEL_XDP 0x10000 /* transmit dma pointers */ +#define FA_SEL_RDD 0x40000 /* receive dma data */ +#define FA_SEL_RDP 0x50000 /* receive dma pointers */ +#define FA_SEL_XFD 0x80000 /* transmit fifo data */ +#define FA_SEL_XFP 0x90000 /* transmit fifo pointers */ +#define FA_SEL_RFD 0xc0000 /* receive fifo data */ +#define FA_SEL_RFP 0xd0000 /* receive fifo pointers */ + +/* + * DMA Descriptor + * Descriptors are only read by the hardware, never written back. + */ +typedef volatile struct { + LM_UINT32 ctrl; /* misc control bits & bufcount */ + LM_UINT32 addr; /* data buffer address */ +} dmadd_t; + +/* + * Each descriptor ring must be 4096byte aligned + * and fit within a single 4096byte page. + */ +#define DMAMAXRINGSZ 4096 +#define DMARINGALIGN 4096 + +/* control flags */ +#define CTRL_BC_MASK 0x1fff /* buffer byte count */ +#define CTRL_EOT ((LM_UINT32)1 << 28) /* end of descriptor table */ +#define CTRL_IOC ((LM_UINT32)1 << 29) /* interrupt on completion */ +#define CTRL_EOF ((LM_UINT32)1 << 30) /* end of frame */ +#define CTRL_SOF ((LM_UINT32)1 << 31) /* start of frame */ + +/* control flags in the range [27:20] are core-specific and not defined here */ +#define CTRL_CORE_MASK 0x0ff00000 + + +#define BCMENET_NFILTERS 64 /* # ethernet address filter entries */ +#define BCMENET_MCHASHBASE 0x200 /* multicast hash filter base address */ +#define BCMENET_MCHASHSIZE 256 /* multicast hash filter size in bytes */ +#define BCMENET_MAX_DMA 4096 /* chip has 12 bits of DMA addressing */ + +/* power management event wakeup pattern constants */ +#define BCMENET_NPMP 4 /* chip supports 4 wakeup patterns */ +#define BCMENET_PMPBASE 0x400 /* wakeup pattern base address */ +#define BCMENET_PMPSIZE 0x80 /* 128bytes each pattern */ +#define BCMENET_PMMBASE 0x600 /* wakeup mask base address */ +#define BCMENET_PMMSIZE 0x10 /* 128bits each mask */ + +/* PCI config space "back door" access registers */ +#define BCMENET_BACK_DOOR_ADDR 0xa0 +#define BCMENET_BACK_DOOR_DATA 0xa4 + +#define BCMENET_PMC 0x42 +#define BCMENET_PMCSR 0x44 +#define ENABLE_PCICONFIG_PME 0x8100 + +/* cpp contortions to concatenate w/arg prescan */ +#ifndef PAD +#define _PADLINE(line) pad ## line +#define _XSTR(line) _PADLINE(line) +#define PAD _XSTR(__LINE__) +#endif /* PAD */ + +/* + * EMAC MIB Registers + */ +typedef volatile struct { + LM_UINT32 tx_good_octets; + LM_UINT32 tx_good_pkts; + LM_UINT32 tx_octets; + LM_UINT32 tx_pkts; + LM_UINT32 tx_broadcast_pkts; + LM_UINT32 tx_multicast_pkts; + LM_UINT32 tx_len_64; + LM_UINT32 tx_len_65_to_127; + LM_UINT32 tx_len_128_to_255; + LM_UINT32 tx_len_256_to_511; + LM_UINT32 tx_len_512_to_1023; + LM_UINT32 tx_len_1024_to_max; + LM_UINT32 tx_jabber_pkts; + LM_UINT32 tx_oversize_pkts; + LM_UINT32 tx_fragment_pkts; + LM_UINT32 tx_underruns; + LM_UINT32 tx_total_cols; + LM_UINT32 tx_single_cols; + LM_UINT32 tx_multiple_cols; + LM_UINT32 tx_excessive_cols; + LM_UINT32 tx_late_cols; + LM_UINT32 tx_defered; + LM_UINT32 tx_carrier_lost; + LM_UINT32 tx_pause_pkts; + LM_UINT32 PAD[8]; + + LM_UINT32 rx_good_octets; + LM_UINT32 rx_good_pkts; + LM_UINT32 rx_octets; + LM_UINT32 rx_pkts; + LM_UINT32 rx_broadcast_pkts; + LM_UINT32 rx_multicast_pkts; + LM_UINT32 rx_len_64; + LM_UINT32 rx_len_65_to_127; + LM_UINT32 rx_len_128_to_255; + LM_UINT32 rx_len_256_to_511; + LM_UINT32 rx_len_512_to_1023; + LM_UINT32 rx_len_1024_to_max; + LM_UINT32 rx_jabber_pkts; + LM_UINT32 rx_oversize_pkts; + LM_UINT32 rx_fragment_pkts; + LM_UINT32 rx_missed_pkts; + LM_UINT32 rx_crc_align_errs; + LM_UINT32 rx_undersize; + LM_UINT32 rx_crc_errs; + LM_UINT32 rx_align_errs; + LM_UINT32 rx_symbol_errs; + LM_UINT32 rx_pause_pkts; + LM_UINT32 rx_nonpause_pkts; +} bcmenetmib_t; + +#define SB_ENUM_BASE 0x18000000 +#define SB_CORE_SIZE 0x1000 +#define SBCONFIGOFF 0xf00 /* core register space offset in bytes */ +#define SBCONFIGSIZE 256 /* size in bytes */ + +/* + * Sonics Configuration Space Registers. + */ +typedef volatile struct _sbconfig { + LM_UINT32 PAD[2]; + LM_UINT32 sbipsflag; /* initiator port ocp slave flag */ + LM_UINT32 PAD[3]; + LM_UINT32 sbtpsflag; /* target port ocp slave flag */ + LM_UINT32 PAD[17]; + LM_UINT32 sbadmatch3; /* address match3 */ + LM_UINT32 PAD; + LM_UINT32 sbadmatch2; /* address match2 */ + LM_UINT32 PAD; + LM_UINT32 sbadmatch1; /* address match1 */ + LM_UINT32 PAD[7]; + LM_UINT32 sbimstate; /* initiator agent state */ + LM_UINT32 sbintvec; /* interrupt mask */ + LM_UINT32 sbtmstatelow; /* target state */ + LM_UINT32 sbtmstatehigh; /* target state */ + LM_UINT32 sbbwa0; /* bandwidth allocation table0 */ + LM_UINT32 PAD; + LM_UINT32 sbimconfiglow; /* initiator configuration */ + LM_UINT32 sbimconfighigh; /* initiator configuration */ + LM_UINT32 sbadmatch0; /* address match0 */ + LM_UINT32 PAD; + LM_UINT32 sbtmconfiglow; /* target configuration */ + LM_UINT32 sbtmconfighigh; /* target configuration */ + LM_UINT32 sbbconfig; /* broadcast configuration */ + LM_UINT32 PAD; + LM_UINT32 sbbstate; /* broadcast state */ + LM_UINT32 PAD[3]; + LM_UINT32 sbactcnfg; /* activate configuration */ + LM_UINT32 PAD[3]; + LM_UINT32 sbflagst; /* current sbflags */ + LM_UINT32 PAD[3]; + LM_UINT32 sbidlow; /* identification */ + LM_UINT32 sbidhigh; /* identification */ +} sbconfig_t; + +/* XXX 4710-specific and should be deleted since they can be read from sbtpsflag */ +/* interrupt sbFlags */ +#define SBFLAG_PCI 0 +#define SBFLAG_ENET0 1 +#define SBFLAG_ILINE20 2 +#define SBFLAG_CODEC 3 +#define SBFLAG_USB 4 +#define SBFLAG_EXTIF 5 +#define SBFLAG_ENET1 6 + +/* sbipsflag */ +#define SBIPSFLAG 0x08 /* offset */ +#define SBIPS_INT1_MASK 0x3f /* which sbflags get routed to mips interrupt 1 */ +#define SBIPS_INT1_SHIFT 0 +#define SBIPS_INT2_MASK 0x3f00 /* which sbflags get routed to mips interrupt 2 */ +#define SBIPS_INT2_SHIFT 8 +#define SBIPS_INT3_MASK 0x3f0000 /* which sbflags get routed to mips interrupt 3 */ +#define SBIPS_INT3_SHIFT 16 +#define SBIPS_INT4_MASK 0x3f000000 /* which sbflags get routed to mips interrupt 4 */ +#define SBIPS_INT4_SHIFT 24 + +/* sbtpsflag */ +#define SBTPSFLAG 0x18 /* offset */ +#define SBTPS_NUM0_MASK 0x3f /* interrupt sbFlag # generated by this core */ +#define SBTPS_F0EN0 0x40 /* interrupt is always sent on the backplane */ + +/* sbadmatch */ +#define SBADMATCH3 0x60 /* offset */ +#define SBADMATCH2 0x68 /* offset */ +#define SBADMATCH1 0x70 /* offset */ + +/* sbimstate */ +#define SBIMSTATE 0x90 /* offset */ +#define SBIM_PC 0xf /* pipecount */ +#define SBIM_AP_MASK 0x30 /* arbitration policy */ +#define SBIM_AP_BOTH 0x00 /* use both timeslaces and token */ +#define SBIM_AP_TS 0x10 /* use timesliaces only */ +#define SBIM_AP_TK 0x20 /* use token only */ +#define SBIM_AP_RSV 0x30 /* reserved */ +#define SBIM_IBE 0x20000 /* inbanderror */ +#define SBIM_TO 0x40000 /* timeout */ + +/* sbintvec */ +#define SBINTVEC 0x94 /* offset */ +#define SBIV_PCI 0x1 /* enable interrupts for pci */ +#define SBIV_ENET0 0x2 /* enable interrupts for enet 0 */ +#define SBIV_ILINE20 0x4 /* enable interrupts for iline20 */ +#define SBIV_CODEC 0x8 /* enable interrupts for v90 codec */ +#define SBIV_USB 0x10 /* enable interrupts for usb */ +#define SBIV_EXTIF 0x20 /* enable interrupts for external i/f */ +#define SBIV_ENET1 0x40 /* enable interrupts for enet 1 */ + +/* sbtmstatelow */ +#define SBTMSTATELOW 0x98 /* offset */ +#define SBTML_RESET 0x1 /* reset */ +#define SBTML_REJ 0x2 /* reject */ +#define SBTML_CLK 0x10000 /* clock enable */ +#define SBTML_FGC 0x20000 /* force gated clocks on */ +#define SBTML_PE 0x40000000 /* pme enable */ +#define SBTML_BE 0x80000000 /* bist enable */ + +/* sbtmstatehigh */ +#define SBTMSTATEHIGH 0x9C /* offset */ +#define SBTMH_SERR 0x1 /* serror */ +#define SBTMH_INT 0x2 /* interrupt */ +#define SBTMH_BUSY 0x4 /* busy */ +#define SBTMH_GCR 0x20000000 /* gated clock request */ +#define SBTMH_BISTF 0x40000000 /* bist failed */ +#define SBTMH_BISTD 0x80000000 /* bist done */ + +/* sbbwa0 */ +#define SBBWA0 0xA0 /* offset */ +#define SBBWA_TAB0_MASK 0xffff /* lookup table 0 */ +#define SBBWA_TAB1_MASK 0xffff /* lookup table 1 */ +#define SBBWA_TAB1_SHIFT 16 + +/* sbimconfiglow */ +#define SBIMCONFIGLOW 0xA8 /* offset */ +#define SBIMCL_STO_MASK 0x3 /* service timeout */ +#define SBIMCL_RTO_MASK 0x30 /* request timeout */ +#define SBIMCL_RTO_SHIFT 4 +#define SBIMCL_CID_MASK 0xff0000 /* connection id */ +#define SBIMCL_CID_SHIFT 16 + +/* sbimconfighigh */ +#define SBIMCONFIGHIGH 0xAC /* offset */ +#define SBIMCH_IEM_MASK 0xc /* inband error mode */ +#define SBIMCH_TEM_MASK 0x30 /* timeout error mode */ +#define SBIMCH_TEM_SHIFT 4 +#define SBIMCH_BEM_MASK 0xc0 /* bus error mode */ +#define SBIMCH_BEM_SHIFT 6 + +/* sbadmatch0 */ +#define SBADMATCH0 0xB0 /* offset */ +#define SBAM_TYPE_MASK 0x3 /* address type */ +#define SBAM_AD64 0x4 /* reserved */ +#define SBAM_ADINT0_MASK 0xf8 /* type0 size */ +#define SBAM_ADINT0_SHIFT 3 +#define SBAM_ADINT1_MASK 0x1f8 /* type1 size */ +#define SBAM_ADINT1_SHIFT 3 +#define SBAM_ADINT2_MASK 0x1f8 /* type2 size */ +#define SBAM_ADINT2_SHIFT 3 +#define SBAM_ADEN 0x400 /* enable */ +#define SBAM_ADNEG 0x800 /* negative decode */ +#define SBAM_BASE0_MASK 0xffffff00 /* type0 base address */ +#define SBAM_BASE0_SHIFT 8 +#define SBAM_BASE1_MASK 0xfffff000 /* type1 base address for the core */ +#define SBAM_BASE1_SHIFT 12 +#define SBAM_BASE2_MASK 0xffff0000 /* type2 base address for the core */ +#define SBAM_BASE2_SHIFT 16 + +/* sbtmconfiglow */ +#define SBTMCONFIGLOW 0xB8 /* offset */ +#define SBTMCL_CD_MASK 0xff /* clock divide */ +#define SBTMCL_CO_MASK 0xf800 /* clock offset */ +#define SBTMCL_CO_SHIFT 11 +#define SBTMCL_IF_MASK 0xfc0000 /* interrupt flags */ +#define SBTMCL_IF_SHIFT 18 +#define SBTMCL_IM_MASK 0x3000000 /* interrupt mode */ +#define SBTMCL_IM_SHIFT 24 + +/* sbtmconfighigh */ +#define SBTMCONFIGHIGH 0xBC /* offset */ +#define SBTMCH_BM_MASK 0x3 /* busy mode */ +#define SBTMCH_RM_MASK 0x3 /* retry mode */ +#define SBTMCH_RM_SHIFT 2 +#define SBTMCH_SM_MASK 0x30 /* stop mode */ +#define SBTMCH_SM_SHIFT 4 +#define SBTMCH_EM_MASK 0x300 /* sb error mode */ +#define SBTMCH_EM_SHIFT 8 +#define SBTMCH_IM_MASK 0xc00 /* int mode */ +#define SBTMCH_IM_SHIFT 10 + +/* sbbconfig */ +#define SBBCONFIG 0xC0 /* offset */ +#define SBBC_LAT_MASK 0x3 /* sb latency */ +#define SBBC_MAX0_MASK 0xf0000 /* maxccntr0 */ +#define SBBC_MAX0_SHIFT 16 +#define SBBC_MAX1_MASK 0xf00000 /* maxccntr1 */ +#define SBBC_MAX1_SHIFT 20 + +/* sbbstate */ +#define SBBSTATE 0xC8 /* offset */ +#define SBBS_SRD 0x1 /* st reg disable */ +#define SBBS_HRD 0x2 /* hold reg disable */ + +/* sbactcnfg */ +#define SBACTCNFG 0xD8 /* offset */ + +/* sbflagst */ +#define SBFLAGST 0xE8 /* offset */ + +/* sbidlow */ +#define SBIDLOW 0xF8 /* offset */ +#define SBIDL_CS_MASK 0x3 /* config space */ +#define SBIDL_AR_MASK 0x38 /* # address ranges supported */ +#define SBIDL_AR_SHIFT 3 +#define SBIDL_SYNCH 0x40 /* sync */ +#define SBIDL_INIT 0x80 /* initiator */ +#define SBIDL_MINLAT_MASK 0xf00 /* minimum backplane latency */ +#define SBIDL_MINLAT_SHIFT 8 +#define SBIDL_MAXLAT 0xf000 /* maximum backplane latency */ +#define SBIDL_MAXLAT_SHIFT 12 +#define SBIDL_FIRST 0x10000 /* this initiator is first */ +#define SBIDL_CW_MASK 0xc0000 /* cycle counter width */ +#define SBIDL_CW_SHIFT 18 +#define SBIDL_TP_MASK 0xf00000 /* target ports */ +#define SBIDL_TP_SHIFT 20 +#define SBIDL_IP_MASK 0xf000000 /* initiator ports */ +#define SBIDL_IP_SHIFT 24 + +/* sbidhigh */ +#define SBIDHIGH 0xFC /* offset */ +#define SBIDH_RC_MASK 0xf /* revision code*/ +#define SBIDH_CC_MASK 0xfff0 /* core code */ +#define SBIDH_CC_SHIFT 4 +#define SBIDH_VC_MASK 0xffff0000 /* vendor code */ +#define SBIDH_VC_SHIFT 16 + +/* core codes */ +#define SB_ILINE20 0x801 /* iline20 core */ +#define SB_SDRAM 0x803 /* sdram core */ +#define SB_PCI 0x804 /* pci core */ +#define SB_MIPS 0x805 /* mips core */ +#define SB_ENET 0x806 /* enet mac core */ +#define SB_CODEC 0x807 /* v90 codec core */ +#define SB_USB 0x808 /* usb core */ +#define SB_ILINE100 0x80a /* iline100 core */ +#define SB_EXTIF 0x811 /* external interface core */ + +struct sbmap { + LM_UINT32 id; + LM_UINT32 coreunit; + LM_UINT32 sbaddr; +}; + +#define SBID_SDRAM 0 +#define SBID_PCI_MEM 1 +#define SBID_PCI_CFG 2 +#define SBID_PCI_DMA 3 +#define SBID_SDRAM_SWAPPED 4 +#define SBID_ENUM 5 +#define SBID_REG_SDRAM 6 +#define SBID_REG_ILINE20 7 +#define SBID_REG_EMAC 8 +#define SBID_REG_CODEC 9 +#define SBID_REG_USB 10 +#define SBID_REG_PCI 11 +#define SBID_REG_MIPS 12 +#define SBID_REG_EXTIF 13 +#define SBID_EXTIF 14 +#define SBID_EJTAG 15 +#define SBID_MAX 16 + +/* + * Host Interface Registers + */ +typedef volatile struct _bcmenettregs { + /* Device and Power Control */ + LM_UINT32 devcontrol; + LM_UINT32 PAD[2]; + LM_UINT32 biststatus; + LM_UINT32 wakeuplength; +#define DISABLE_32_PATMATCH 0x80800000 +#define DISABLE_3210_PATMATCH 0x80808080 + LM_UINT32 PAD[3]; + + /* Interrupt Control */ + LM_UINT32 intstatus; + LM_UINT32 intmask; + LM_UINT32 gptimer; + LM_UINT32 PAD[23]; + + /* Ethernet MAC Address Filtering Control */ + LM_UINT32 enetaddrlo; /* added in B0 */ + LM_UINT32 enetaddrhi; /* added in B0 */ + LM_UINT32 enetftaddr; + LM_UINT32 enetftdata; + LM_UINT32 PAD[2]; + + /* Ethernet MAC Control */ + LM_UINT32 emactxmaxburstlen; + LM_UINT32 emacrxmaxburstlen; + LM_UINT32 emaccontrol; + LM_UINT32 emacflowcontrol; + + LM_UINT32 PAD[20]; + + /* DMA Lazy Interrupt Control */ + LM_UINT32 intrecvlazy; + LM_UINT32 PAD[63]; + + /* DMA engine */ + dmaregs_t dmaregs; + LM_UINT32 PAD[116]; + + /* EMAC Registers */ + LM_UINT32 rxconfig; + LM_UINT32 rxmaxlength; + LM_UINT32 txmaxlength; + LM_UINT32 PAD; + LM_UINT32 mdiocontrol; + LM_UINT32 mdiodata; + LM_UINT32 emacintmask; + LM_UINT32 emacintstatus; + LM_UINT32 camdatalo; + LM_UINT32 camdatahi; + LM_UINT32 camcontrol; + LM_UINT32 enetcontrol; + LM_UINT32 txcontrol; + LM_UINT32 txwatermark; + LM_UINT32 mibcontrol; + LM_UINT32 PAD[49]; + + /* EMAC MIB counters */ + bcmenetmib_t mib; + + LM_UINT32 PAD[585]; + + /* Sonics SiliconBackplane config registers */ + sbconfig_t sbconfig; +} bcmenetregs_t; + +/* device control */ +#define DC_MPM ((LM_UINT32)1 << 6) /* Magic Packet PME enable(B0)*/ +#define DC_PM ((LM_UINT32)1 << 7) /* pattern filtering enable */ +#define DC_IP ((LM_UINT32)1 << 10) /* internal ephy present (rev >= 1) */ +#define DC_ER ((LM_UINT32)1 << 15) /* ephy reset */ +#define DC_MP ((LM_UINT32)1 << 16) /* mii phy mode enable */ +#define DC_CO ((LM_UINT32)1 << 17) /* mii phy mode: enable clocks */ +#define DC_PA_MASK 0x7c0000 /* mii phy mode: mdc/mdio phy address */ +#define DC_PA_SHIFT 18 + +/* wakeup length */ +#define WL_P0_MASK 0x7f /* pattern 0 */ +#define WL_D0 ((LM_UINT32)1 << 7) +#define WL_P1_MASK 0x7f00 /* pattern 1 */ +#define WL_P1_SHIFT 8 +#define WL_D1 ((LM_UINT32)1 << 15) +#define WL_P2_MASK 0x7f0000 /* pattern 2 */ +#define WL_P2_SHIFT 16 +#define WL_D2 ((LM_UINT32)1 << 23) +#define WL_P3_MASK 0x7f000000 /* pattern 3 */ +#define WL_P3_SHIFT 24 +#define WL_D3 ((LM_UINT32)1 << 31) + +/* intstatus and intmask */ +#define I_LS ((LM_UINT32)1 << 5) /* link change (new in B0) */ +#define I_PME ((LM_UINT32)1 << 6) /* power management event */ +#define I_TO ((LM_UINT32)1 << 7) /* general purpose timeout */ +#define I_PC ((LM_UINT32)1 << 10) /* descriptor error */ +#define I_PD ((LM_UINT32)1 << 11) /* data error */ +#define I_DE ((LM_UINT32)1 << 12) /* descriptor protocol error */ +#define I_RU ((LM_UINT32)1 << 13) /* receive descriptor underflow */ +#define I_RO ((LM_UINT32)1 << 14) /* receive fifo overflow */ +#define I_XU ((LM_UINT32)1 << 15) /* transmit fifo underflow */ +#define I_RI ((LM_UINT32)1 << 16) /* receive interrupt */ +#define I_XI ((LM_UINT32)1 << 24) /* transmit interrupt */ +#define I_EM ((LM_UINT32)1 << 26) /* emac interrupt */ +#define I_MW ((LM_UINT32)1 << 27) /* mii write */ +#define I_MR ((LM_UINT32)1 << 28) /* mii read */ + +#define I_ERRORS (I_PC | I_PD | I_DE | I_RU | I_RO | I_XU) +#define DEF_INTMASK (I_TO | I_XI | I_RI | I_ERRORS) + +/* emaccontrol */ +#define EMC_CG ((LM_UINT32)1 << 0) /* crc32 generation enable */ +#define EMC_EP ((LM_UINT32)1 << 2) /* onchip ephy: powerdown (rev >= 1) */ +#define EMC_ED ((LM_UINT32)1 << 3) /* onchip ephy: energy detected (rev >= 1) */ +#define EMC_LC_MASK 0xe0 /* onchip ephy: led control (rev >= 1) */ +#define EMC_LC_SHIFT 5 + +/* emacflowcontrol */ +#define EMF_RFH_MASK 0xff /* rx fifo hi water mark */ +#define EMF_PG ((LM_UINT32)1 << 15) /* enable pause frame generation */ + +/* interrupt receive lazy */ +#define IRL_TO_MASK 0x00ffffff /* timeout */ +#define IRL_FC_MASK 0xff000000 /* frame count */ +#define IRL_FC_SHIFT 24 /* frame count */ + +/* emac receive config */ +#define ERC_DB ((LM_UINT32)1 << 0) /* disable broadcast */ +#define ERC_AM ((LM_UINT32)1 << 1) /* accept all multicast */ +#define ERC_RDT ((LM_UINT32)1 << 2) /* receive disable while transmitting */ +#define ERC_PE ((LM_UINT32)1 << 3) /* promiscuous enable */ +#define ERC_LE ((LM_UINT32)1 << 4) /* loopback enable */ +#define ERC_EF ((LM_UINT32)1 << 5) /* enable flow control */ +#define ERC_UF ((LM_UINT32)1 << 6) /* accept unicast flow control frame */ +#define ERC_RF ((LM_UINT32)1 << 7) /* reject filter */ + +/* emac mdio control */ +#define MC_MF_MASK 0x7f /* mdc frequency */ +#define MC_PE ((LM_UINT32)1 << 7) /* mii preamble enable */ + +/* emac mdio data */ +#define MD_DATA_MASK 0xffffL /* r/w data */ +#define MD_TA_MASK 0x30000L /* turnaround value */ +#define MD_TA_SHIFT 16 +#define MD_TA_VALID (2L << MD_TA_SHIFT) /* valid ta */ +#define MD_RA_MASK 0x7c0000L /* register address */ +#define MD_RA_SHIFT 18 +#define MD_PMD_MASK 0xf800000L /* physical media device */ +#define MD_PMD_SHIFT 23 +#define MD_OP_MASK 0x30000000L /* opcode */ +#define MD_OP_SHIFT 28 +#define MD_OP_WRITE (1L << MD_OP_SHIFT) /* write op */ +#define MD_OP_READ (2L << MD_OP_SHIFT) /* read op */ +#define MD_SB_MASK 0xc0000000L /* start bits */ +#define MD_SB_SHIFT 30 +#define MD_SB_START (0x1L << MD_SB_SHIFT) /* start of frame */ + +/* emac intstatus and intmask */ +#define EI_MII ((LM_UINT32)1 << 0) /* mii mdio interrupt */ +#define EI_MIB ((LM_UINT32)1 << 1) /* mib interrupt */ +#define EI_FLOW ((LM_UINT32)1 << 2) /* flow control interrupt */ + +/* emac cam data high */ +#define CD_V ((LM_UINT32)1 << 16) /* valid bit */ + +/* emac cam control */ +#define CC_CE ((LM_UINT32)1 << 0) /* cam enable */ +#define CC_MS ((LM_UINT32)1 << 1) /* mask select */ +#define CC_RD ((LM_UINT32)1 << 2) /* read */ +#define CC_WR ((LM_UINT32)1 << 3) /* write */ +#define CC_INDEX_MASK 0x3f0000 /* index */ +#define CC_INDEX_SHIFT 16 +#define CC_CB ((LM_UINT32)1 << 31) /* cam busy */ + +/* emac ethernet control */ +#define EC_EE ((LM_UINT32)1 << 0) /* emac enable */ +#define EC_ED ((LM_UINT32)1 << 1) /* emac disable */ +#define EC_ES ((LM_UINT32)1 << 2) /* emac soft reset */ +#define EC_EP ((LM_UINT32)1 << 3) /* external phy select */ + +/* emac transmit control */ +#define EXC_FD ((LM_UINT32)1 << 0) /* full duplex */ +#define EXC_FM ((LM_UINT32)1 << 1) /* flowmode */ +#define EXC_SB ((LM_UINT32)1 << 2) /* single backoff enable */ +#define EXC_SS ((LM_UINT32)1 << 3) /* small slottime */ + +/* emac mib control */ +#define EMC_RZ ((LM_UINT32)1 << 0) /* autoclear on read */ + +/* + * The Ethernet MAC core returns an 8-byte Receive Frame Data Header + * with every frame consisting of + * 16bits of frame length, followed by + * 16bits of EMAC rx descriptor info, followed by 32bits of undefined. + */ +typedef volatile struct { + LM_UINT16 len; + LM_UINT16 flags; + LM_UINT16 pad[12]; +} bcmenetrxh_t; + +#define RXHDR_LEN 28 + +#define RXF_L ((LM_UINT16)1 << 11) /* last buffer in a frame */ +#define RXF_MISS ((LM_UINT16)1 << 7) /* received due to promisc mode */ +#define RXF_BRDCAST ((LM_UINT16)1 << 6) /* dest is broadcast address */ +#define RXF_MULT ((LM_UINT16)1 << 5) /* dest is multicast address */ +#define RXF_LG ((LM_UINT16)1 << 4) /* frame length > rxmaxlength */ +#define RXF_NO ((LM_UINT16)1 << 3) /* odd number of nibbles */ +#define RXF_RXER ((LM_UINT16)1 << 2) /* receive symbol error */ +#define RXF_CRC ((LM_UINT16)1 << 1) /* crc error */ +#define RXF_OV ((LM_UINT16)1 << 0) /* fifo overflow */ + +#define RXF_ERRORS (RXF_NO | RXF_CRC | RXF_OV) + +/* Sonics side: PCI core and host control registers */ +typedef struct sbpciregs { + LM_UINT32 control; /* PCI control */ + LM_UINT32 PAD[3]; + LM_UINT32 arbcontrol; /* PCI arbiter control */ + LM_UINT32 PAD[3]; + LM_UINT32 intstatus; /* Interrupt status */ + LM_UINT32 intmask; /* Interrupt mask */ + LM_UINT32 sbtopcimailbox; /* Sonics to PCI mailbox */ + LM_UINT32 PAD[9]; + LM_UINT32 bcastaddr; /* Sonics broadcast address */ + LM_UINT32 bcastdata; /* Sonics broadcast data */ + LM_UINT32 PAD[42]; + LM_UINT32 sbtopci0; /* Sonics to PCI translation 0 */ + LM_UINT32 sbtopci1; /* Sonics to PCI translation 1 */ + LM_UINT32 sbtopci2; /* Sonics to PCI translation 2 */ + LM_UINT32 PAD[445]; + LM_UINT16 sprom[36]; /* SPROM shadow Area */ + LM_UINT32 PAD[46]; +} sbpciregs_t; + +/* PCI control */ +#define PCI_RST_OE 0x01 /* When set, drives PCI_RESET out to pin */ +#define PCI_RST 0x02 /* Value driven out to pin */ +#define PCI_CLK_OE 0x04 /* When set, drives clock as gated by PCI_CLK out to pin */ +#define PCI_CLK 0x08 /* Gate for clock driven out to pin */ + +/* PCI arbiter control */ +#define PCI_INT_ARB 0x01 /* When set, use an internal arbiter */ +#define PCI_EXT_ARB 0x02 /* When set, use an external arbiter */ +#define PCI_PARKID_MASK 0x06 /* Selects which agent is parked on an idle bus */ +#define PCI_PARKID_SHIFT 1 +#define PCI_PARKID_LAST 0 /* Last requestor */ +#define PCI_PARKID_4710 1 /* 4710 */ +#define PCI_PARKID_EXTREQ0 2 /* External requestor 0 */ +#define PCI_PARKID_EXTREQ1 3 /* External requestor 1 */ + +/* Interrupt status/mask */ +#define PCI_INTA 0x01 /* PCI INTA# is asserted */ +#define PCI_INTB 0x02 /* PCI INTB# is asserted */ +#define PCI_SERR 0x04 /* PCI SERR# has been asserted (write one to clear) */ +#define PCI_PERR 0x08 /* PCI PERR# has been asserted (write one to clear) */ +#define PCI_PME 0x10 /* PCI PME# is asserted */ + +/* (General) PCI/SB mailbox interrupts, two bits per pci function */ +#define MAILBOX_F0_0 0x100 /* function 0, int 0 */ +#define MAILBOX_F0_1 0x200 /* function 0, int 1 */ +#define MAILBOX_F1_0 0x400 /* function 1, int 0 */ +#define MAILBOX_F1_1 0x800 /* function 1, int 1 */ +#define MAILBOX_F2_0 0x1000 /* function 2, int 0 */ +#define MAILBOX_F2_1 0x2000 /* function 2, int 1 */ +#define MAILBOX_F3_0 0x4000 /* function 3, int 0 */ +#define MAILBOX_F3_1 0x8000 /* function 3, int 1 */ + +/* Sonics broadcast address */ +#define BCAST_ADDR_MASK 0xff /* Broadcast register address */ + +/* Sonics to PCI translation types */ +#define SBTOPCI0_MASK 0xfc000000 +#define SBTOPCI1_MASK 0xfc000000 +#define SBTOPCI2_MASK 0xc0000000 +#define SBTOPCI_MEM 0 +#define SBTOPCI_IO 1 +#define SBTOPCI_CFG0 2 +#define SBTOPCI_CFG1 3 +#define SBTOPCI_PREF 0x4 /* prefetch enable */ +#define SBTOPCI_BURST 0x8 /* burst enable */ + +/* PCI side: Reserved PCI configuration registers (see pcicfg.h) */ +#define cap_list rsvd_a[0] +#define bar0_window dev_dep[0x80 - 0x40] +#define bar1_window dev_dep[0x84 - 0x40] +#define sprom_control dev_dep[0x88 - 0x40] + +#define PCI_BAR0_WIN 0x80 +#define PCI_BAR1_WIN 0x84 +#define PCI_SPROM_CONTROL 0x88 +#define PCI_BAR1_CONTROL 0x8c + +#define PCI_BAR0_SPROM_OFFSET 4096 /* top 4K of bar0 accesses external sprom */ + +/* PCI clock must be active and stable to read SPROM */ +#ifdef MIPSEL +#define pci_host(sprom) ((sprom[0] == 1) && (sprom[2] == 0x4710) && (sprom[8] == 0xf)) +#else +#define pci_host(sprom) ((sprom[1] == 1) && (sprom[3] == 0x4710) && (sprom[9] == 0xf)) +#endif + +DECLARE_QUEUE_TYPE(LM_RX_PACKET_Q, MAX_RX_PACKET_DESC_COUNT); +DECLARE_QUEUE_TYPE(LM_TX_PACKET_Q, MAX_TX_PACKET_DESC_COUNT); + +typedef struct _LM_PACKET { + /* Set in LM. */ + LM_STATUS PacketStatus; + + /* Set in LM for Rx, in UM for Tx. */ + LM_UINT32 PacketSize; + + union { + /* Send info. */ + struct { + /* Set up by UM. */ + LM_UINT32 FragCount; + + } Tx; + + /* Receive info. */ + struct { + /* Receive buffer size */ + LM_UINT32 RxBufferSize; + + /* Virtual and physical address of the receive buffer. */ + LM_UINT8 *pRxBufferVirt; + LM_PHYSICAL_ADDRESS RxBufferPhy; + + } Rx; + } u; +} LM_PACKET; + +#ifdef BCM_WOL + +#define BCMENET_PMPSIZE 0x80 +#define BCMENET_PMMSIZE 0x10 + +#endif + +typedef struct _LM_DEVICE_BLOCK +{ + /* Memory view. */ + bcmenetregs_t *pMemView; + LM_UINT8 *pMappedMemBase; + + PLM_VOID pPacketDescBase; + + LM_UINT32 RxPacketDescCnt; + LM_UINT32 MaxRxPacketDescCnt; + LM_UINT32 TxPacketDescCnt; + LM_UINT32 MaxTxPacketDescCnt; + + LM_RX_PACKET_Q RxPacketFreeQ; + LM_RX_PACKET_Q RxPacketReceivedQ; + LM_TX_PACKET_Q TxPacketFreeQ; + LM_TX_PACKET_Q TxPacketXmittedQ; + + LM_PACKET *RxPacketArr[DMAMAXRINGSZ / sizeof(dmadd_t)]; + LM_PACKET *TxPacketArr[DMAMAXRINGSZ / sizeof(dmadd_t)]; + + MM_ATOMIC_T SendDescLeft; + + /* Current node address. */ + LM_UINT8 NodeAddress[6]; + + /* The adapter's node address. */ + LM_UINT8 PermanentNodeAddress[6]; + + /* Multicast address list. */ + LM_UINT32 McEntryCount; + LM_UINT8 McTable[LM_MAX_MC_TABLE_SIZE][LM_MC_ENTRY_SIZE]; + + LM_UINT16 PciVendorId; + LM_UINT16 PciDeviceId; + LM_UINT16 PciSubvendorId; + LM_UINT16 PciSubsystemId; + LM_UINT8 PciRevId; + LM_UINT8 Reserved1[3]; + + LM_UINT32 corerev; + LM_UINT32 coreunit; + LM_UINT32 pcirev; + + LM_UINT32 PhyAddr; + LM_UINT32 MdcPort; + + dmadd_t *pRxDesc; + LM_PHYSICAL_ADDRESS RxDescPhy; + + dmadd_t *pTxDesc; + LM_PHYSICAL_ADDRESS TxDescPhy; + + LM_UINT32 ddoffset; + LM_UINT32 dataoffset; + LM_UINT32 rxoffset; + + LM_UINT32 rxin; + LM_UINT32 rxout; + + LM_UINT32 txin; + LM_UINT32 txout; + + LM_UINT32 lazyrxfc; + LM_UINT32 lazyrxmult; + LM_UINT32 lazytxfc; + LM_UINT32 lazytxmult; + + struct sbmap *sbmap; + + LM_LINE_SPEED RequestedLineSpeed; + LM_DUPLEX_MODE RequestedDuplexMode; + + LM_LINE_SPEED LineSpeed; + LM_DUPLEX_MODE DuplexMode; + + LM_FLOW_CONTROL FlowControlCap; + LM_FLOW_CONTROL FlowControl; + + LM_UINT32 Advertising; + + LM_UINT32 DisableAutoNeg; + + LM_STATUS LinkStatus; + + LM_UINT32 ReceiveMask; + + LM_BOOL InitDone; + + LM_BOOL InReset; + + LM_BOOL mibgood; + + LM_BOOL ShuttingDown; + + LM_BOOL QueueRxPackets; + + LM_UINT32 intstatus; + LM_UINT32 intmask; + + LM_COUNTER tx_good_octets; + LM_COUNTER tx_good_pkts; + LM_COUNTER tx_octets; + LM_COUNTER tx_pkts; + LM_COUNTER tx_broadcast_pkts; + LM_COUNTER tx_multicast_pkts; + LM_COUNTER tx_len_64; + LM_COUNTER tx_len_65_to_127; + LM_COUNTER tx_len_128_to_255; + LM_COUNTER tx_len_256_to_511; + LM_COUNTER tx_len_512_to_1023; + LM_COUNTER tx_len_1024_to_max; + LM_COUNTER tx_jabber_pkts; + LM_COUNTER tx_oversize_pkts; + LM_COUNTER tx_fragment_pkts; + LM_COUNTER tx_underruns; + LM_COUNTER tx_total_cols; + LM_COUNTER tx_single_cols; + LM_COUNTER tx_multiple_cols; + LM_COUNTER tx_excessive_cols; + LM_COUNTER tx_late_cols; + LM_COUNTER tx_defered; + LM_COUNTER tx_carrier_lost; + LM_COUNTER tx_pause_pkts; + + LM_COUNTER rx_good_octets; + LM_COUNTER rx_good_pkts; + LM_COUNTER rx_octets; + LM_COUNTER rx_pkts; + LM_COUNTER rx_broadcast_pkts; + LM_COUNTER rx_multicast_pkts; + LM_COUNTER rx_len_64; + LM_COUNTER rx_len_65_to_127; + LM_COUNTER rx_len_128_to_255; + LM_COUNTER rx_len_256_to_511; + LM_COUNTER rx_len_512_to_1023; + LM_COUNTER rx_len_1024_to_max; + LM_COUNTER rx_jabber_pkts; + LM_COUNTER rx_oversize_pkts; + LM_COUNTER rx_fragment_pkts; + LM_COUNTER rx_missed_pkts; + LM_COUNTER rx_crc_align_errs; + LM_COUNTER rx_undersize; + LM_COUNTER rx_crc_errs; + LM_COUNTER rx_align_errs; + LM_COUNTER rx_symbol_errs; + LM_COUNTER rx_pause_pkts; + LM_COUNTER rx_nonpause_pkts; + +#ifdef BCM_WOL + LM_WAKE_UP_MODE WakeUpMode; +#endif +#ifdef BCM_NAPI_RXPOLL + LM_UINT32 RxPoll; +#endif +} LM_DEVICE_BLOCK; + +/******************************************************************************/ +/* NIC register read/write macros. */ +/******************************************************************************/ + +#define REG_RD(pDevice, OffsetName) \ + MM_MEMREADL(&((pDevice)->pMemView->OffsetName)) + +#define REG_WR(pDevice, OffsetName, Value32) \ + (void) MM_MEMWRITEL(&((pDevice)->pMemView->OffsetName), Value32) + +#define REG_RD_OFFSET(pDevice, Offset) \ + MM_MEMREADL(((LM_UINT8 *) (pDevice)->pMemView + Offset)) + +#define REG_WR_OFFSET(pDevice, Offset, Value32) \ + MM_MEMWRITEL(((LM_UINT8 *) (pDevice)->pMemView + Offset), Value32) + +#define REG_OR(pDevice, OffsetName, Value32) \ + REG_WR(pDevice, OffsetName, REG_RD(pDevice, OffsetName) | Value32) + +#define REG_AND(pDevice, OffsetName, Value32) \ + REG_WR(pDevice, OffsetName, REG_RD(pDevice, OffsetName) & Value32) + +#define SPINWAIT(exp, us) { \ + LM_UINT32 countdown = (us) + 9; \ + while ((exp) && (countdown >= 10)) {\ + b44_MM_Wait(10); \ + countdown -= 10; \ + } \ +} + +#endif /* B44_H */ + diff --git a/src/add-ons/kernel/drivers/network/bcm440x/b44lm.c b/src/add-ons/kernel/drivers/network/bcm440x/b44lm.c new file mode 100644 index 0000000000..6c5012434f --- /dev/null +++ b/src/add-ons/kernel/drivers/network/bcm440x/b44lm.c @@ -0,0 +1,2097 @@ +/******************************************************************************/ +/* */ +/* Broadcom BCM4400 Linux Network Driver, Copyright (c) 2000 Broadcom */ +/* Corporation. */ +/* All rights reserved. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, located in the file LICENSE. */ +/* */ +/* History: */ +/******************************************************************************/ + +#include "b44mm.h" + + + +/******************************************************************************/ +/* Local functions. */ +/******************************************************************************/ + +LM_STATUS b44_LM_Abort(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_LM_QueueRxPackets(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_LM_SetFlowControl(PLM_DEVICE_BLOCK pDevice, + LM_UINT32 LocalPhyAd, LM_UINT32 RemotePhyAd); +static LM_UINT32 b44_GetPhyAdFlowCntrlSettings(PLM_DEVICE_BLOCK pDevice); + +STATIC LM_STATUS b44_LM_ResetChip(PLM_DEVICE_BLOCK pDevice); +STATIC LM_STATUS b44_LM_DisableChip(PLM_DEVICE_BLOCK pDevice); +void b44_LM_ClearStats(LM_DEVICE_BLOCK *pDevice); +void b44_LM_WriteCam(LM_DEVICE_BLOCK *pDevice, LM_UINT8 *ea, + LM_UINT32 camindex); + +LM_UINT32 b44_LM_getsbaddr(LM_DEVICE_BLOCK *pDevice, LM_UINT32 id, + LM_UINT32 coreunit); +void b44_LM_sb_core_disable(LM_DEVICE_BLOCK *pDevice); +LM_UINT32 b44_LM_sb_pci_setup(LM_DEVICE_BLOCK *pDevice, LM_UINT32 cores); +LM_UINT32 b44_LM_sb_coreunit(LM_DEVICE_BLOCK *pDevice); +void b44_LM_sb_core_reset(LM_DEVICE_BLOCK *pDevice); +LM_UINT32 b44_LM_sb_coreid(LM_DEVICE_BLOCK *pDevice); +LM_UINT32 b44_LM_sb_corerev(LM_DEVICE_BLOCK *pDevice); +LM_UINT32 b44_LM_sb_iscoreup(LM_DEVICE_BLOCK *pDevice); +#ifdef BCM_WOL +static void b44_LM_ftwrite(LM_DEVICE_BLOCK *pDevice, LM_UINT32 *b, + LM_UINT32 nbytes, LM_UINT32 ftaddr); +#endif + +#define BCM4710_PCI_DMA 0x40000000 /* Client Mode PCI memory access space (1 GB) */ +#define BCM4710_ENUM 0x18000000 /* Beginning of core enumeration space */ + +struct sbmap bcm4402[] = { + {SBID_PCI_DMA, 0, BCM4710_PCI_DMA}, + {SBID_ENUM, 0, BCM4710_ENUM}, + {SBID_REG_EMAC, 0, 0x18000000}, + {SBID_REG_CODEC, 0, 0x18001000}, + {SBID_REG_PCI, 0, 0x18002000} +}; + +#ifdef B44_DEBUG +int b44_reset_count = 0; +#endif + +/******************************************************************************/ +/* External functions. */ +/******************************************************************************/ + +LM_UINT32 +b44_LM_ByteSwap(LM_UINT32 Value32) +{ + return ((Value32 & 0xff) << 24)| ((Value32 & 0xff00) << 8)| + ((Value32 & 0xff0000) >> 8) | ((Value32 >> 24) & 0xff); +} + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_STATUS +b44_LM_QueueRxPackets(PLM_DEVICE_BLOCK pDevice) +{ + PLM_PACKET pPacket; + LM_PHYSICAL_ADDRESS pa; + LM_UINT32 rxout = pDevice->rxout; + LM_UINT32 ctrl; + + pPacket = (PLM_PACKET) QQ_PopHead(&pDevice->RxPacketFreeQ.Container); + while(pPacket) { + + /* Initialize the receive buffer pointer */ + b44_MM_MapRxDma(pDevice, pPacket, &pa); + + *((LM_UINT32 *) pPacket->u.Rx.pRxBufferVirt) = 0; + + /* prep the descriptor control value */ + ctrl = pPacket->u.Rx.RxBufferSize; + if (rxout == (pDevice->MaxRxPacketDescCnt - 1)) + ctrl |= CTRL_EOT; + + /* init the rx descriptor */ + pDevice->pRxDesc[rxout].ctrl = ctrl; + pDevice->pRxDesc[rxout].addr = pa + pDevice->dataoffset; + + pDevice->RxPacketArr[rxout] = pPacket; + rxout = (rxout + 1) & (pDevice->MaxRxPacketDescCnt - 1); + + pPacket = (PLM_PACKET) + QQ_PopHead(&pDevice->RxPacketFreeQ.Container); + } /* while */ + + pDevice->rxout = rxout; + MM_WMB(); + + REG_WR(pDevice, dmaregs.rcvptr, rxout * sizeof(dmadd_t)); + return LM_STATUS_SUCCESS; +} /* LM_QueueRxPackets */ + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +STATIC LM_STATUS +b44_LM_EepromReadBlock(PLM_DEVICE_BLOCK pDevice, + LM_UINT32 offset, LM_UINT32 *pData, LM_UINT32 size) +{ + int off, nw; +// LM_UINT8 chk8; + int i; + LM_UINT32 *buf; + + off = offset; + nw = ROUNDUP(size, 4); + buf = (LM_UINT32 *) pData; + + /* read the sprom */ + for (i = 0; i < nw; i += 4) + buf[i/4] = REG_RD_OFFSET(pDevice, 4096 + off + i); + + return LM_STATUS_SUCCESS; +} /* b44_LM_EepromRead */ + + +/******************************************************************************/ +/* Description: */ +/* This routine initializes default parameters and reads the PCI */ +/* configurations. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS +b44_LM_GetAdapterInfo( +PLM_DEVICE_BLOCK pDevice) +{ + LM_UINT32 eprom_dw[32]; + LM_UINT8 *eprom = (LM_UINT8 *) eprom_dw; + LM_STATUS Status; + LM_UINT32 Value32; + + /* Get Device Id and Vendor Id */ + Status = b44_MM_ReadConfig32(pDevice, PCI_VENDOR_ID_REG, &Value32); + if(Status != LM_STATUS_SUCCESS) + { + return Status; + } + pDevice->PciVendorId = (LM_UINT16) Value32; + pDevice->PciDeviceId = (LM_UINT16) (Value32 >> 16); + + Status = b44_MM_ReadConfig32(pDevice, PCI_REV_ID_REG, &Value32); + if(Status != LM_STATUS_SUCCESS) + { + return Status; + } + pDevice->PciRevId = (LM_UINT8) Value32; + + /* Get subsystem vendor. */ + Status = b44_MM_ReadConfig32(pDevice, PCI_SUBSYSTEM_VENDOR_ID_REG, &Value32); + if(Status != LM_STATUS_SUCCESS) + { + return Status; + } + pDevice->PciSubvendorId = (LM_UINT16) Value32; + + /* Get PCI subsystem id. */ + pDevice->PciSubsystemId = (LM_UINT16) (Value32 >> 16); + + Status = b44_MM_MapMemBase(pDevice); + if(Status != LM_STATUS_SUCCESS) + { + return Status; + } + /* Initialize the memory view pointer. */ + pDevice->pMemView = (bcmenetregs_t *) pDevice->pMappedMemBase; + + b44_LM_EepromReadBlock(pDevice, 0, eprom_dw, sizeof(eprom_dw)); + + /* check sprom version */ + if ((eprom[126] != 1) && (eprom[126] != 0x10)) + return LM_STATUS_FAILURE; + + pDevice->PermanentNodeAddress[0] = eprom[79]; + pDevice->PermanentNodeAddress[1] = eprom[78]; + pDevice->PermanentNodeAddress[2] = eprom[81]; + pDevice->PermanentNodeAddress[3] = eprom[80]; + pDevice->PermanentNodeAddress[4] = eprom[83]; + pDevice->PermanentNodeAddress[5] = eprom[82]; + + memcpy(pDevice->NodeAddress, pDevice->PermanentNodeAddress, 6); + + pDevice->PhyAddr = eprom[90] & 0x1f; + pDevice->MdcPort = (eprom[90] >> 14) & 0x1; + + /* Initialize the default values. */ + pDevice->TxPacketDescCnt = DEFAULT_TX_PACKET_DESC_COUNT; + pDevice->RxPacketDescCnt = DEFAULT_RX_PACKET_DESC_COUNT; + pDevice->MaxRxPacketDescCnt = DMAMAXRINGSZ / sizeof(dmadd_t); + pDevice->MaxTxPacketDescCnt = DMAMAXRINGSZ / sizeof(dmadd_t); + pDevice->rxoffset = 30; + pDevice->lazyrxfc = 1; + pDevice->lazyrxmult = 0; + pDevice->lazytxfc = 0; + pDevice->lazytxmult = 0; + pDevice->intmask = DEF_INTMASK; + pDevice->LinkStatus = LM_STATUS_LINK_DOWN; + +#ifdef BCM_WOL + pDevice->WakeUpMode = LM_WAKE_UP_MODE_NONE; +#endif + + /* Change driver parameters. */ + Status = b44_MM_GetConfig(pDevice); + if(Status != LM_STATUS_SUCCESS) + { + return Status; + } + +#if 0 + /* Calling SetupPhy will cause target aborts if the chip has not */ + /* been reset */ + b44_LM_SetupPhy(pDevice); +#endif + ASSERT(b44_LM_sb_coreid(pDevice) == SB_ENET); + + pDevice->corerev = b44_LM_sb_corerev(pDevice); + + pDevice->sbmap = bcm4402; + + pDevice->coreunit = b44_LM_sb_coreunit(pDevice); + + ASSERT((pDevice->coreunit == 0) || (pDevice->coreunit == 1)); + + /* supports link change interrupt */ + if (pDevice->corerev >= 7) + pDevice->intmask |= I_LS; + + return LM_STATUS_SUCCESS; +} /* LM_GetAdapterInfo */ + + +/******************************************************************************/ +/* Description: */ +/* This routine sets up receive/transmit buffer descriptions queues. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS +b44_LM_InitializeAdapter(PLM_DEVICE_BLOCK pDevice) +{ + LM_PHYSICAL_ADDRESS MemPhy, MemBasePhy; + LM_UINT8 *pMemVirt, *pMemBase; + PLM_PACKET pPacket; + LM_STATUS Status; + LM_UINT32 Size; + LM_UINT32 align, j; + + /* Intialize the queues. */ + QQ_InitQueue(&pDevice->RxPacketReceivedQ.Container, + MAX_RX_PACKET_DESC_COUNT); + QQ_InitQueue(&pDevice->RxPacketFreeQ.Container, + MAX_RX_PACKET_DESC_COUNT); + + QQ_InitQueue(&pDevice->TxPacketFreeQ.Container,MAX_TX_PACKET_DESC_COUNT); + QQ_InitQueue(&pDevice->TxPacketXmittedQ.Container,MAX_TX_PACKET_DESC_COUNT); + + /* Allocate memory for packet descriptors. */ + Size = (pDevice->RxPacketDescCnt + + pDevice->TxPacketDescCnt) * B44_MM_PACKET_DESC_SIZE; + Status = b44_MM_AllocateMemory(pDevice, Size, (PLM_VOID *) &pPacket); + if(Status != LM_STATUS_SUCCESS) + { + return Status; + } + + for(j = 0; j < pDevice->TxPacketDescCnt; j++) + { + QQ_PushTail(&pDevice->TxPacketFreeQ.Container, pPacket); + + pPacket = (PLM_PACKET) ((PLM_UINT8) pPacket + B44_MM_PACKET_DESC_SIZE); + } /* for(j.. */ + + for(j = 0; j < pDevice->RxPacketDescCnt; j++) + { + /* Receive buffer size. */ + pPacket->u.Rx.RxBufferSize = 1522 + pDevice->rxoffset; + + /* Add the descriptor to RxPacketFreeQ. */ + QQ_PushTail(&pDevice->RxPacketFreeQ.Container, pPacket); + + pPacket = (PLM_PACKET) ((PLM_UINT8) pPacket + B44_MM_PACKET_DESC_SIZE); + } /* for */ + + /* Initialize the rest of the packet descriptors. */ + Status = b44_MM_InitializeUmPackets(pDevice); + if(Status != LM_STATUS_SUCCESS) + { + return Status; + } /* if */ + + /* Make the Tx ring size power of 2 */ + pDevice->MaxTxPacketDescCnt = DMAMAXRINGSZ / sizeof(dmadd_t); + while ((pDevice->MaxTxPacketDescCnt >> 1) > pDevice->TxPacketDescCnt) + pDevice->MaxTxPacketDescCnt >>= 1; + + Size = (pDevice->MaxRxPacketDescCnt + pDevice->MaxTxPacketDescCnt) * + sizeof(dmadd_t) + DMARINGALIGN; + + Status = b44_MM_AllocateSharedMemory(pDevice, Size, (PLM_VOID) &pMemBase, &MemBasePhy); + if(Status != LM_STATUS_SUCCESS) + { + return Status; + } + + MemPhy = (MemBasePhy + (DMARINGALIGN - 1)) & ~(DMARINGALIGN - 1); + align = MemPhy - MemBasePhy; + pMemVirt = pMemBase + align; + + pDevice->pRxDesc = (dmadd_t *) pMemVirt; + pDevice->RxDescPhy = MemPhy; + + pMemVirt += pDevice->MaxRxPacketDescCnt * sizeof(dmadd_t); + MemPhy += pDevice->MaxRxPacketDescCnt * sizeof(dmadd_t); + + pDevice->pTxDesc = (dmadd_t *) pMemVirt; + pDevice->TxDescPhy = MemPhy; + + /* Initialize the hardware. */ + Status = b44_LM_ResetAdapter(pDevice, TRUE); + if(Status != LM_STATUS_SUCCESS) + { + return Status; + } + + /* We are done with initialization. */ + pDevice->InitDone = TRUE; + + return LM_STATUS_SUCCESS; +} /* LM_InitializeAdapter */ + + +LM_STATUS +b44_LM_DisableChip(PLM_DEVICE_BLOCK pDevice) +{ + + /* disable emac */ + REG_WR(pDevice, enetcontrol, EC_ED); + SPINWAIT((REG_RD(pDevice, enetcontrol) & EC_ED), 200); + + REG_WR(pDevice, dmaregs.xmtcontrol, 0); + REG_WR(pDevice, dmaregs.rcvcontrol, 0); + b44_MM_Wait(10); + + return LM_STATUS_SUCCESS; +} + +/******************************************************************************/ +/* Description: */ +/* This function reinitializes the adapter. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS +b44_LM_ResetAdapter(PLM_DEVICE_BLOCK pDevice, LM_BOOL full) +{ + + /* Disable interrupt. */ + if (pDevice->InitDone) + { + b44_LM_DisableInterrupt(pDevice); + } + + /* Disable transmit and receive DMA engines. Abort all pending requests. */ + b44_LM_Abort(pDevice); + + pDevice->ShuttingDown = FALSE; + + /* enable pci interrupts, bursts, and prefetch */ + pDevice->pcirev = b44_LM_sb_pci_setup(pDevice, + ((pDevice->coreunit == 0)? SBIV_ENET0: SBIV_ENET1)); + + pDevice->ddoffset = pDevice->dataoffset = + b44_LM_getsbaddr(pDevice, SBID_PCI_DMA, 0); + + b44_LM_ResetChip(pDevice); + +#if 1 + if (pDevice->InitDone != TRUE) { + if (pDevice->MdcPort == pDevice->coreunit) { + b44_LM_ResetPhy(pDevice); + b44_LM_SetupPhy(pDevice); + } + } +#endif + + b44_LM_SetMacAddress(pDevice, pDevice->NodeAddress); + + /* enable crc32 generation and set proper LED modes */ + REG_WR(pDevice, emaccontrol, EMC_CG | (0x7 << EMC_LC_SHIFT)); + + REG_WR(pDevice, intrecvlazy, (pDevice->lazyrxfc << IRL_FC_SHIFT)); + if (pDevice->lazyrxfc > 1) + { + REG_OR(pDevice, intrecvlazy, (pDevice->lazyrxmult * pDevice->lazyrxfc)); + } + + /* enable 802.3x tx flow control (honor received PAUSE frames) */ +// REG_WR(pDevice, rxconfig, ERC_FE | ERC_UF); + + b44_LM_SetReceiveMask(pDevice, pDevice->ReceiveMask); + + /* set max frame lengths - account for possible vlan tag */ + REG_WR(pDevice, rxmaxlength, MAX_ETHERNET_PACKET_SIZE + 32); + REG_WR(pDevice, txmaxlength, MAX_ETHERNET_PACKET_SIZE + 32); + + /* set tx watermark */ + REG_WR(pDevice, txwatermark, 56); + + if (full) + { + /* initialize the tx and rx dma channels */ + /* clear tx descriptor ring */ + memset((void*)pDevice->pTxDesc, 0, (pDevice->MaxTxPacketDescCnt * + sizeof(dmadd_t))); + + REG_WR(pDevice, dmaregs.xmtcontrol, XC_XE); + REG_WR(pDevice, dmaregs.xmtaddr, (pDevice->TxDescPhy + + pDevice->ddoffset)); + + /* clear rx descriptor ring */ + memset((void*)pDevice->pRxDesc, 0, (pDevice->MaxRxPacketDescCnt * + sizeof(dmadd_t))); + + REG_WR(pDevice, dmaregs.rcvcontrol, ((pDevice->rxoffset << + RC_RO_SHIFT) | RC_RE)); + + REG_WR(pDevice, dmaregs.rcvaddr, (pDevice->RxDescPhy + + pDevice->ddoffset)); + + /* Queue Rx packet buffers. */ + b44_LM_QueueRxPackets(pDevice); + + MM_ATOMIC_SET(&pDevice->SendDescLeft, pDevice->TxPacketDescCnt - 1); + } + else + { + REG_WR(pDevice, dmaregs.rcvcontrol, ((pDevice->rxoffset << + RC_RO_SHIFT) | RC_RE)); + } + + /* turn on the emac */ + REG_OR(pDevice, enetcontrol, EC_EE); + + return LM_STATUS_SUCCESS; +} /* LM_ResetAdapter */ + + +/******************************************************************************/ +/* Description: */ +/* This routine disables the adapter from generating interrupts. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS +b44_LM_DisableInterrupt( + PLM_DEVICE_BLOCK pDevice) +{ + REG_WR(pDevice, intmask, 0); + (void) REG_RD(pDevice, intmask); /* sync readback */ + return LM_STATUS_SUCCESS; +} + + + +/******************************************************************************/ +/* Description: */ +/* This routine enables the adapter to generate interrupts. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS +b44_LM_EnableInterrupt( + PLM_DEVICE_BLOCK pDevice) +{ + REG_WR(pDevice, intmask, pDevice->intmask); + return LM_STATUS_SUCCESS; +} + + + +/******************************************************************************/ +/* Description: */ +/* This routine puts a packet on the wire if there is a transmit DMA */ +/* descriptor available; otherwise the packet is queued for later */ +/* transmission. If the second argue is NULL, this routine will put */ +/* the queued packet on the wire if possible. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS +b44_LM_SendPacket(PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket) +{ + LM_UINT32 fragcount; + LM_UINT32 len, txout, ctrl; + LM_PHYSICAL_ADDRESS pa; + int first, next; + + txout = pDevice->txout; + + pDevice->TxPacketArr[txout] = pPacket; + for(fragcount = 0, first = 1, next = 1; next; + first = 0, fragcount++) { + + ctrl = 0; + b44_MM_MapTxDma(pDevice, pPacket, &pa, &len, fragcount); + ctrl = len & CTRL_BC_MASK; + + if (first) + ctrl |= CTRL_SOF; + if (fragcount == (pPacket->u.Tx.FragCount - 1)) { + ctrl |= CTRL_EOF; + next = 0; + } + if (txout == (pDevice->MaxTxPacketDescCnt - 1)) { + ctrl |= CTRL_EOT; + } + ctrl |= CTRL_IOC; + + /* init the tx descriptor */ + pDevice->pTxDesc[txout].ctrl = ctrl; + pDevice->pTxDesc[txout].addr = pa + pDevice->dataoffset; + + txout = (txout + 1) & (pDevice->MaxTxPacketDescCnt - 1); + + } + + MM_ATOMIC_SUB(&pDevice->SendDescLeft, pPacket->u.Tx.FragCount); + + pDevice->txout = txout; + + MM_WMB(); + + REG_WR(pDevice, dmaregs.xmtptr, (txout * sizeof(dmadd_t))); + + return LM_STATUS_SUCCESS; +} + + +/******************************************************************************/ +/* Description: */ +/* This routine sets the receive control register according to ReceiveMask */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS +b44_LM_SetReceiveMask(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Mask) +{ + LM_UINT32 ReceiveMask; + LM_UINT32 j; + LM_UINT32 idx = 0; + LM_UINT8 zero[6] = {0,0,0,0,0,0}; + + ReceiveMask = Mask; + + if(Mask & LM_ACCEPT_UNICAST) + { + Mask &= ~LM_ACCEPT_UNICAST; + } + + if(Mask & LM_ACCEPT_MULTICAST) + { + Mask &= ~LM_ACCEPT_MULTICAST; + } + + if(Mask & LM_ACCEPT_ALL_MULTICAST) + { + Mask &= ~LM_ACCEPT_ALL_MULTICAST; + } + + if(Mask & LM_ACCEPT_BROADCAST) + { + Mask &= ~LM_ACCEPT_BROADCAST; + } + + if(Mask & LM_PROMISCUOUS_MODE) + { + Mask &= ~LM_PROMISCUOUS_MODE; + } + + /* Make sure all the bits are valid before committing changes. */ + if(Mask) + { + return LM_STATUS_FAILURE; + } + + if (ReceiveMask & LM_PROMISCUOUS_MODE) + REG_OR(pDevice, rxconfig, ERC_PE); + else { + REG_WR(pDevice, camcontrol, 0); + /* our local address */ + b44_LM_WriteCam(pDevice, pDevice->NodeAddress, idx++); + + /* allmulti or a list of discrete multicast addresses */ + if (ReceiveMask & LM_ACCEPT_ALL_MULTICAST) + REG_OR(pDevice, rxconfig, ERC_AM); + else if (ReceiveMask & LM_ACCEPT_MULTICAST) { + for(j = 0; j < pDevice->McEntryCount; j++) { + b44_LM_WriteCam(pDevice, pDevice->McTable[j], + idx++); + } + } + + for (; idx < 64; idx++) { + b44_LM_WriteCam(pDevice, zero, idx); + } + + /* enable cam */ + REG_OR(pDevice, camcontrol, CC_CE); + } + + return LM_STATUS_SUCCESS; +} /* LM_SetReceiveMask */ + + + +/******************************************************************************/ +/* Description: */ +/* Disable the interrupt and put the transmitter and receiver engines in */ +/* an idle state. Also aborts all pending send requests and receive */ +/* buffers. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS +b44_LM_Abort( +PLM_DEVICE_BLOCK pDevice) +{ + PLM_PACKET pPacket; + LM_UINT32 rxin, txin, txdmask, txcurr; + + if (!pDevice->InitDone) + { + return LM_STATUS_SUCCESS; + } + + b44_LM_DisableInterrupt(pDevice); + + txcurr = (REG_RD(pDevice, dmaregs.xmtstatus) & XS_CD_MASK); + txcurr = txcurr / sizeof(dmadd_t); + /* Allow tx packets to drain */ + if (pDevice->txout != txcurr) + { + b44_MM_Wait(20); + } + REG_WR(pDevice, dmaregs.xmtcontrol, 0); + b44_MM_Wait(120); + + b44_LM_DisableChip(pDevice); + + txdmask = pDevice->MaxTxPacketDescCnt - 1; + for (txin = pDevice->txin; txin != pDevice->txout; + txin = (txin + 1) & txdmask) + { + if ((pPacket = pDevice->TxPacketArr[txin])) { + QQ_PushTail(&pDevice->TxPacketXmittedQ.Container, pPacket); + pDevice->TxPacketArr[txin] = 0; + } + } + + if(!pDevice->ShuttingDown) + { + /* Indicate packets to the protocol. */ + b44_MM_IndicateTxPackets(pDevice); + + /* Indicate received packets to the protocols. */ + b44_MM_IndicateRxPackets(pDevice); + } + else + { + /* Move the receive packet descriptors in the ReceivedQ to the */ + /* free queue. */ + for(; ;) + { + pPacket = (PLM_PACKET) QQ_PopHead( + &pDevice->RxPacketReceivedQ.Container); + if(pPacket == NULL) + { + break; + } + QQ_PushTail(&pDevice->RxPacketFreeQ.Container, pPacket); + } + } + + /* Clean up the Receive desc ring. */ + + rxin = pDevice->rxin; + while(rxin != pDevice->rxout) { + pPacket = pDevice->RxPacketArr[rxin]; + + QQ_PushTail(&pDevice->RxPacketFreeQ.Container, pPacket); + + rxin = (rxin + 1) & (pDevice->MaxRxPacketDescCnt - 1); + } /* while */ + + pDevice->rxin = rxin; + return LM_STATUS_SUCCESS; +} /* LM_Abort */ + + + +/******************************************************************************/ +/* Description: */ +/* Disable the interrupt and put the transmitter and receiver engines in */ +/* an idle state. Aborts all pending send requests and receive buffers. */ +/* Also free all the receive buffers. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS +b44_LM_Halt(PLM_DEVICE_BLOCK pDevice) +{ + PLM_PACKET pPacket; + LM_UINT32 EntryCnt; + + b44_LM_Abort(pDevice); + + /* Get the number of entries in the queue. */ + EntryCnt = QQ_GetEntryCnt(&pDevice->RxPacketFreeQ.Container); + + /* Make sure all the packets have been accounted for. */ + for(EntryCnt = 0; EntryCnt < pDevice->RxPacketDescCnt; EntryCnt++) + { + pPacket = (PLM_PACKET) QQ_PopHead(&pDevice->RxPacketFreeQ.Container); + if (pPacket == 0) + break; + + b44_MM_FreeRxBuffer(pDevice, pPacket); + + QQ_PushTail(&pDevice->RxPacketFreeQ.Container, pPacket); + } + + b44_LM_ResetChip(pDevice); + + /* Reprogram the MAC address. */ + b44_LM_SetMacAddress(pDevice, pDevice->NodeAddress); + + return LM_STATUS_SUCCESS; +} /* LM_Halt */ + + +STATIC LM_STATUS +b44_LM_ResetChip(LM_DEVICE_BLOCK *pDevice) +{ + if (!b44_LM_sb_iscoreup(pDevice)) { + b44_LM_sb_pci_setup(pDevice, + ((pDevice->coreunit == 0)? SBIV_ENET0: SBIV_ENET1)); + /* power on reset: reset the enet core */ + b44_LM_sb_core_reset(pDevice); + + goto chipinreset; + } + + /* read counters before resetting the chip */ + if (pDevice->mibgood) + b44_LM_StatsUpdate(pDevice); + + REG_WR(pDevice, intrecvlazy, 0); + + /* disable emac */ + REG_WR(pDevice, enetcontrol, EC_ED); + SPINWAIT((REG_RD(pDevice, enetcontrol) & EC_ED), 200); + + /* reset the dma engines */ + REG_WR(pDevice, dmaregs.xmtcontrol, 0); + pDevice->txin = pDevice->txout = 0; + + if (REG_RD(pDevice, dmaregs.rcvstatus) & RS_RE_MASK) { + /* wait until channel is idle or stopped */ + SPINWAIT(!(REG_RD(pDevice, dmaregs.rcvstatus) & RS_RS_IDLE), + 100); + } + + REG_WR(pDevice, dmaregs.rcvcontrol, 0); + pDevice->rxin = pDevice->rxout = 0; + + REG_WR(pDevice, enetcontrol, EC_ES); + + b44_LM_sb_core_reset(pDevice); + +chipinreset: + if (pDevice->InitDone == FALSE) + b44_LM_ClearStats(pDevice); + + /* + * We want the phy registers to be accessible even when + * the driver is "downed" so initialize MDC preamble, frequency, + * and whether internal or external phy here. + */ + /* 4402 has 62.5Mhz SB clock and internal phy */ + REG_WR(pDevice, mdiocontrol, 0x8d); + + /* some chips have internal phy, some don't */ + if (!(REG_RD(pDevice, devcontrol) & DC_IP)) { + REG_WR(pDevice, enetcontrol, EC_EP); + } else if (REG_RD(pDevice, devcontrol) & DC_ER) { + REG_AND(pDevice, devcontrol, ~DC_ER); + + b44_MM_Wait(100); + } + + /* clear persistent sw intstatus */ + pDevice->intstatus = 0; + return LM_STATUS_SUCCESS; +} + +void +b44_LM_ClearStats(LM_DEVICE_BLOCK *pDevice) +{ + /* must clear mib registers by hand */ + REG_WR(pDevice, mibcontrol, EMC_RZ); + (void) REG_RD(pDevice, mib.tx_good_octets); + (void) REG_RD(pDevice, mib.tx_good_pkts); + (void) REG_RD(pDevice, mib.tx_octets); + (void) REG_RD(pDevice, mib.tx_pkts); + (void) REG_RD(pDevice, mib.tx_broadcast_pkts); + (void) REG_RD(pDevice, mib.tx_multicast_pkts); + (void) REG_RD(pDevice, mib.tx_len_64); + (void) REG_RD(pDevice, mib.tx_len_65_to_127); + (void) REG_RD(pDevice, mib.tx_len_128_to_255); + (void) REG_RD(pDevice, mib.tx_len_256_to_511); + (void) REG_RD(pDevice, mib.tx_len_512_to_1023); + (void) REG_RD(pDevice, mib.tx_len_1024_to_max); + (void) REG_RD(pDevice, mib.tx_jabber_pkts); + (void) REG_RD(pDevice, mib.tx_oversize_pkts); + (void) REG_RD(pDevice, mib.tx_fragment_pkts); + (void) REG_RD(pDevice, mib.tx_underruns); + (void) REG_RD(pDevice, mib.tx_total_cols); + (void) REG_RD(pDevice, mib.tx_single_cols); + (void) REG_RD(pDevice, mib.tx_multiple_cols); + (void) REG_RD(pDevice, mib.tx_excessive_cols); + (void) REG_RD(pDevice, mib.tx_late_cols); + (void) REG_RD(pDevice, mib.tx_defered); + (void) REG_RD(pDevice, mib.tx_carrier_lost); + (void) REG_RD(pDevice, mib.tx_pause_pkts); + (void) REG_RD(pDevice, mib.rx_good_octets); + (void) REG_RD(pDevice, mib.rx_good_pkts); + (void) REG_RD(pDevice, mib.rx_octets); + (void) REG_RD(pDevice, mib.rx_pkts); + (void) REG_RD(pDevice, mib.rx_broadcast_pkts); + (void) REG_RD(pDevice, mib.rx_multicast_pkts); + (void) REG_RD(pDevice, mib.rx_len_64); + (void) REG_RD(pDevice, mib.rx_len_65_to_127); + (void) REG_RD(pDevice, mib.rx_len_128_to_255); + (void) REG_RD(pDevice, mib.rx_len_256_to_511); + (void) REG_RD(pDevice, mib.rx_len_512_to_1023); + (void) REG_RD(pDevice, mib.rx_len_1024_to_max); + (void) REG_RD(pDevice, mib.rx_jabber_pkts); + (void) REG_RD(pDevice, mib.rx_oversize_pkts); + (void) REG_RD(pDevice, mib.rx_fragment_pkts); + (void) REG_RD(pDevice, mib.rx_missed_pkts); + (void) REG_RD(pDevice, mib.rx_crc_align_errs); + (void) REG_RD(pDevice, mib.rx_undersize); + (void) REG_RD(pDevice, mib.rx_crc_errs); + (void) REG_RD(pDevice, mib.rx_align_errs); + (void) REG_RD(pDevice, mib.rx_symbol_errs); + (void) REG_RD(pDevice, mib.rx_pause_pkts); + (void) REG_RD(pDevice, mib.rx_nonpause_pkts); + pDevice->mibgood = TRUE; +} + +#ifdef BCM_NAPI_RXPOLL +int +b44_LM_ServiceRxPoll(PLM_DEVICE_BLOCK pDevice, int limit) +{ + LM_UINT32 rxin, curr, rxdmask; + unsigned int len; + int skiplen = 0; + bcmenetrxh_t *rxh; + LM_PACKET *pPacket; + int received = 0; + + curr = (REG_RD(pDevice, dmaregs.rcvstatus) & RS_CD_MASK); + curr = curr / sizeof(dmadd_t); + rxdmask = pDevice->MaxRxPacketDescCnt - 1; + for (rxin = pDevice->rxin; rxin != curr; rxin = (rxin + 1) & rxdmask) + { + pPacket = pDevice->RxPacketArr[rxin]; + if (skiplen > 0) { + pPacket->PacketStatus = LM_STATUS_FAILURE; + skiplen -= pPacket->u.Rx.RxBufferSize; + if (skiplen < 0) + skiplen = 0; + goto rx_err; + } + rxh = (bcmenetrxh_t *) pPacket->u.Rx.pRxBufferVirt; + len = MM_SWAP_LE16(rxh->len); + if (len > (pPacket->u.Rx.RxBufferSize - pDevice->rxoffset)) { + pPacket->PacketStatus = LM_STATUS_FAILURE; + skiplen = len - (pPacket->u.Rx.RxBufferSize - + pDevice->rxoffset); + } + else { + int i = 0; + + if (len == 0) { + while ((len == 0) && (i < 5)) { + b44_MM_Wait(2); + len = MM_SWAP_LE16(rxh->len); + i++; + } + if (len == 0) { + pPacket->PacketStatus = + LM_STATUS_FAILURE; + goto rx_err; + } + } + if (MM_SWAP_LE16(rxh->flags) & RXF_ERRORS) { + pPacket->PacketStatus = LM_STATUS_FAILURE; + } + else { + pPacket->PacketStatus = LM_STATUS_SUCCESS; + } + pPacket->PacketSize = len - 4; + } +rx_err: + QQ_PushTail(&pDevice->RxPacketReceivedQ.Container, + pPacket); + + if (++received >= limit) + { + rxin = (rxin + 1) & rxdmask; + break; + } + curr = (REG_RD(pDevice, dmaregs.rcvstatus) & RS_CD_MASK) / + sizeof(dmadd_t); + } + pDevice->rxin = rxin; + return received; +} +#endif + +void +b44_LM_ServiceRxInterrupt(LM_DEVICE_BLOCK *pDevice) +{ + LM_UINT32 curr; +#ifndef BCM_NAPI_RXPOLL + LM_UINT32 rxin, rxdmask; + unsigned int len; + int skiplen = 0; + bcmenetrxh_t *rxh; + LM_PACKET *pPacket; +#endif + + curr = (REG_RD(pDevice, dmaregs.rcvstatus) & RS_CD_MASK); + curr = curr / sizeof(dmadd_t); +#ifdef BCM_NAPI_RXPOLL + if (!pDevice->RxPoll) + { + if (pDevice->rxin != curr) + { + if (b44_MM_ScheduleRxPoll(pDevice) == LM_STATUS_SUCCESS) + { + pDevice->RxPoll = TRUE; + pDevice->intmask &= ~(I_RI | I_RU | I_RO); + REG_WR(pDevice, intmask, pDevice->intmask); + } + } + } +#else + rxdmask = pDevice->MaxRxPacketDescCnt - 1; + for (rxin = pDevice->rxin; rxin != curr; rxin = (rxin + 1) & rxdmask) + { + pPacket = pDevice->RxPacketArr[rxin]; + if (skiplen > 0) { + pPacket->PacketStatus = LM_STATUS_FAILURE; + skiplen -= pPacket->u.Rx.RxBufferSize; + if (skiplen < 0) + skiplen = 0; + goto rx_err; + } + rxh = (bcmenetrxh_t *) pPacket->u.Rx.pRxBufferVirt; + len = MM_SWAP_LE16(rxh->len); + if (len > (pPacket->u.Rx.RxBufferSize - pDevice->rxoffset)) { + pPacket->PacketStatus = LM_STATUS_FAILURE; + skiplen = len - (pPacket->u.Rx.RxBufferSize - + pDevice->rxoffset); + } + else { + int i = 0; + + if (len == 0) { + while ((len == 0) && (i < 5)) { + b44_MM_Wait(2); + len = MM_SWAP_LE16(rxh->len); + i++; + } + if (len == 0) { + pPacket->PacketStatus = + LM_STATUS_FAILURE; + goto rx_err; + } + } + if (MM_SWAP_LE16(rxh->flags) & RXF_ERRORS) { + pPacket->PacketStatus = LM_STATUS_FAILURE; + } + else { + pPacket->PacketStatus = LM_STATUS_SUCCESS; + } + pPacket->PacketSize = len - 4; + } +rx_err: + QQ_PushTail(&pDevice->RxPacketReceivedQ.Container, + pPacket); + curr = (REG_RD(pDevice, dmaregs.rcvstatus) & RS_CD_MASK) / + sizeof(dmadd_t); + } + pDevice->rxin = curr; +#endif +} + +void +b44_LM_ServiceTxInterrupt(LM_DEVICE_BLOCK *pDevice) +{ + LM_UINT32 txin, curr, txdmask; + LM_PACKET *pPacket; + + curr = (REG_RD(pDevice, dmaregs.xmtstatus) & XS_CD_MASK); + curr = curr / sizeof(dmadd_t); + txdmask = pDevice->MaxTxPacketDescCnt - 1; + for (txin = pDevice->txin; txin != curr; txin = (txin + 1) & txdmask) + { + if ((pPacket = pDevice->TxPacketArr[txin])) { + QQ_PushTail(&pDevice->TxPacketXmittedQ.Container, + pPacket); + pDevice->TxPacketArr[txin] = 0; + MM_ATOMIC_ADD(&pDevice->SendDescLeft, + pPacket->u.Tx.FragCount); + } + } + pDevice->txin = curr; +} + +/******************************************************************************/ +/* Description: */ +/* This is the interrupt event handler routine. It acknowledges all */ +/* pending interrupts and process all pending events. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS +b44_LM_ServiceInterrupts(PLM_DEVICE_BLOCK pDevice) +{ + LM_UINT32 intstatus, intmask; + LM_UINT32 count = 0; + + while (1) { + intstatus = REG_RD(pDevice, intstatus); + intmask = REG_RD(pDevice, intmask); + count++; + + /* defer unsolicited interrupts */ + intstatus &= intmask; + + /* if not for us */ + if (intstatus == 0) + if (count > 0) + return LM_STATUS_SUCCESS; + else + return 12; + + /* clear the interrupt */ + REG_WR(pDevice, intstatus, intstatus); + + if (intstatus & I_LS) { + b44_LM_PollLink(pDevice); + } + if (intstatus & I_RI) { + b44_LM_ServiceRxInterrupt(pDevice); + } + + if (intstatus & (I_XI | I_TO)) { + b44_LM_ServiceTxInterrupt(pDevice); + REG_WR(pDevice, gptimer, 0); + } + if (intstatus & I_ERRORS) { +#ifdef B44_DEBUG + b44_reset_count++; +#endif + pDevice->InReset = TRUE; + b44_LM_ResetAdapter(pDevice, TRUE); + pDevice->InReset = FALSE; + b44_LM_EnableInterrupt(pDevice); + } +#ifndef BCM_NAPI_RXPOLL + if (!QQ_Empty(&pDevice->RxPacketReceivedQ.Container)) { + b44_MM_IndicateRxPackets(pDevice); + } +#endif + if (!QQ_Empty(&pDevice->TxPacketXmittedQ.Container)) { + b44_MM_IndicateTxPackets(pDevice); + } + } + return LM_STATUS_SUCCESS; +} /* LM_ServiceInterrupts */ + + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_STATUS +b44_LM_MulticastAdd( +PLM_DEVICE_BLOCK pDevice, +PLM_UINT8 pMcAddress) { + PLM_UINT8 pEntry; + LM_UINT32 j; + + pEntry = pDevice->McTable[0]; + for(j = 0; j < pDevice->McEntryCount; j++) + { + if(IS_ETH_ADDRESS_EQUAL(pEntry, pMcAddress)) + { + /* Found a match, increment the instance count. */ + pEntry[LM_MC_INSTANCE_COUNT_INDEX] += 1; + + return LM_STATUS_SUCCESS; + } + + pEntry += LM_MC_ENTRY_SIZE; + } + + if(pDevice->McEntryCount >= LM_MAX_MC_TABLE_SIZE) + { + return LM_STATUS_FAILURE; + } + + pEntry = pDevice->McTable[pDevice->McEntryCount]; + + COPY_ETH_ADDRESS(pMcAddress, pEntry); + pEntry[LM_MC_INSTANCE_COUNT_INDEX] = 1; + + pDevice->McEntryCount++; + + b44_LM_SetReceiveMask(pDevice, pDevice->ReceiveMask | LM_ACCEPT_MULTICAST); + + return LM_STATUS_SUCCESS; +} /* b44_LM_MulticastAdd */ + + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_STATUS +b44_LM_MulticastDel(PLM_DEVICE_BLOCK pDevice, PLM_UINT8 pMcAddress) +{ + PLM_UINT8 pEntry; + LM_UINT32 j; + + pEntry = pDevice->McTable[0]; + for(j = 0; j < pDevice->McEntryCount; j++) + { + if(IS_ETH_ADDRESS_EQUAL(pEntry, pMcAddress)) + { + /* Found a match, decrement the instance count. */ + pEntry[LM_MC_INSTANCE_COUNT_INDEX] -= 1; + + /* No more instance left, remove the address from the table. */ + /* Move the last entry in the table to the delete slot. */ + if(pEntry[LM_MC_INSTANCE_COUNT_INDEX] == 0 && + pDevice->McEntryCount > 1) + { + + COPY_ETH_ADDRESS( + pDevice->McTable[pDevice->McEntryCount-1], pEntry); + pEntry[LM_MC_INSTANCE_COUNT_INDEX] = + pDevice->McTable[pDevice->McEntryCount-1] + [LM_MC_INSTANCE_COUNT_INDEX]; + } + pDevice->McEntryCount--; + + /* Update the receive mask if the table is empty. */ + if(pDevice->McEntryCount == 0) + { + b44_LM_SetReceiveMask(pDevice, + pDevice->ReceiveMask & ~LM_ACCEPT_MULTICAST); + } + + return LM_STATUS_SUCCESS; + } + + pEntry += LM_MC_ENTRY_SIZE; + } + + return LM_STATUS_FAILURE; +} /* b44_LM_MulticastDel */ + + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_STATUS +b44_LM_MulticastClear( +PLM_DEVICE_BLOCK pDevice) { + pDevice->McEntryCount = 0; + + b44_LM_SetReceiveMask(pDevice, pDevice->ReceiveMask & ~LM_ACCEPT_MULTICAST); + + return LM_STATUS_SUCCESS; +} /* b44_LM_MulticastClear */ + + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_STATUS +b44_LM_SetMacAddress(PLM_DEVICE_BLOCK pDevice, PLM_UINT8 pMacAddress) +{ + return LM_STATUS_SUCCESS; +} + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_STATUS +b44_LM_SetFlowControl( + PLM_DEVICE_BLOCK pDevice, + LM_UINT32 LocalPhyAd, + LM_UINT32 RemotePhyAd) +{ + LM_FLOW_CONTROL FlowCap; + + /* Resolve flow control. */ + FlowCap = LM_FLOW_CONTROL_NONE; + + /* See Table 28B-3 of 802.3ab-1999 spec. */ + if(pDevice->FlowControlCap & LM_FLOW_CONTROL_AUTO_PAUSE) + { + if(LocalPhyAd & PHY_AN_AD_PAUSE_CAPABLE) + { + if(LocalPhyAd & PHY_AN_AD_ASYM_PAUSE) + { + if(RemotePhyAd & PHY_LINK_PARTNER_PAUSE_CAPABLE) + { + FlowCap = LM_FLOW_CONTROL_TRANSMIT_PAUSE | + LM_FLOW_CONTROL_RECEIVE_PAUSE; + } + else if(RemotePhyAd & PHY_LINK_PARTNER_ASYM_PAUSE) + { + FlowCap = LM_FLOW_CONTROL_RECEIVE_PAUSE; + } + } + else + { + if(RemotePhyAd & PHY_LINK_PARTNER_PAUSE_CAPABLE) + { + FlowCap = LM_FLOW_CONTROL_TRANSMIT_PAUSE | + LM_FLOW_CONTROL_RECEIVE_PAUSE; + } + } + } + else if(LocalPhyAd & PHY_AN_AD_ASYM_PAUSE) + { + if((RemotePhyAd & PHY_LINK_PARTNER_PAUSE_CAPABLE) && + (RemotePhyAd & PHY_LINK_PARTNER_ASYM_PAUSE)) + { + FlowCap = LM_FLOW_CONTROL_TRANSMIT_PAUSE; + } + } + } + else + { + FlowCap = pDevice->FlowControlCap; + } + + /* Enable/disable rx PAUSE. */ + if(FlowCap & LM_FLOW_CONTROL_RECEIVE_PAUSE && + (pDevice->FlowControlCap == LM_FLOW_CONTROL_AUTO_PAUSE || + pDevice->FlowControlCap & LM_FLOW_CONTROL_RECEIVE_PAUSE)) + { + pDevice->FlowControl |= LM_FLOW_CONTROL_RECEIVE_PAUSE; + REG_WR(pDevice, rxconfig, ERC_EF); + + } + + /* Enable/disable tx PAUSE. */ + if(FlowCap & LM_FLOW_CONTROL_TRANSMIT_PAUSE && + (pDevice->FlowControlCap == LM_FLOW_CONTROL_AUTO_PAUSE || + pDevice->FlowControlCap & LM_FLOW_CONTROL_TRANSMIT_PAUSE)) + { + pDevice->FlowControl |= LM_FLOW_CONTROL_TRANSMIT_PAUSE; + REG_WR(pDevice, emacflowcontrol, EMF_PG | (0xc0 & EMF_RFH_MASK)); + + } + + return LM_STATUS_SUCCESS; +} + + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_STATUS +b44_LM_SetupPhy( + PLM_DEVICE_BLOCK pDevice) +{ + LM_UINT32 Value32; + LM_UINT32 Adv, FCAdv, Ctrl, NewCtrl; + int RestartAuto = 0; + int i; + + /* enable activity led */ + b44_LM_ReadPhy(pDevice, 26, &Value32); + b44_LM_WritePhy(pDevice, 26, Value32 & 0x7fff); + + /* enable traffic meter led mode */ + b44_LM_ReadPhy(pDevice, 27, &Value32); + b44_LM_WritePhy(pDevice, 27, Value32 | (1 << 6)); + if (!pDevice->DisableAutoNeg) { + if (pDevice->RequestedLineSpeed == LM_LINE_SPEED_AUTO) { + Adv = PHY_AN_AD_ALL_SPEEDS; + } + else if (pDevice->RequestedLineSpeed == LM_LINE_SPEED_10MBPS) { + if (pDevice->RequestedDuplexMode == + LM_DUPLEX_MODE_FULL) { + Adv = PHY_AN_AD_10BASET_FULL; + } + else { + Adv = PHY_AN_AD_10BASET_HALF; + } + } + else if (pDevice->RequestedLineSpeed == LM_LINE_SPEED_100MBPS) { + if (pDevice->RequestedDuplexMode == + LM_DUPLEX_MODE_FULL) { + Adv = PHY_AN_AD_100BASETX_FULL; + } + else { + Adv = PHY_AN_AD_100BASETX_HALF; + } + } + else { + Adv = PHY_AN_AD_ALL_SPEEDS; + } + + if ((pDevice->RequestedLineSpeed == LM_LINE_SPEED_AUTO) || + (pDevice->RequestedDuplexMode == LM_DUPLEX_MODE_FULL)) { + FCAdv = b44_GetPhyAdFlowCntrlSettings(pDevice); + Value32 &= PHY_AN_AD_ASYM_PAUSE | + PHY_AN_AD_PAUSE_CAPABLE; + if (FCAdv != Value32) { + RestartAuto = 1; + Adv |= FCAdv; + goto restart_auto_neg; + } + } + + b44_LM_ReadPhy(pDevice, PHY_CTRL_REG, &Ctrl); + if (!(Ctrl & PHY_CTRL_AUTO_NEG_ENABLE)) { + RestartAuto = 1; + goto restart_auto_neg; + } + b44_LM_ReadPhy(pDevice, PHY_AN_AD_REG, &Value32); + if ((Value32 & PHY_AN_AD_ALL_SPEEDS) != Adv) { + RestartAuto = 1; + } +restart_auto_neg: + if (RestartAuto) { + Adv |= PHY_AN_AD_PROTOCOL_802_3_CSMA_CD; + b44_LM_WritePhy(pDevice, PHY_AN_AD_REG, Adv); + b44_LM_WritePhy(pDevice, PHY_CTRL_REG, + PHY_CTRL_AUTO_NEG_ENABLE | + PHY_CTRL_RESTART_AUTO_NEG); + } + pDevice->Advertising = Adv; + } + else { + b44_LM_ReadPhy(pDevice, PHY_CTRL_REG, &Ctrl); + NewCtrl = Ctrl & (~(PHY_CTRL_SPEED_SELECT_100MBPS | + PHY_CTRL_FULL_DUPLEX_MODE | PHY_CTRL_AUTO_NEG_ENABLE)); + if (pDevice->RequestedLineSpeed == LM_LINE_SPEED_100MBPS) { + NewCtrl |= PHY_CTRL_SPEED_SELECT_100MBPS; + } + if (pDevice->RequestedDuplexMode == LM_DUPLEX_MODE_FULL) { + NewCtrl |= PHY_CTRL_FULL_DUPLEX_MODE; + REG_OR(pDevice, txcontrol, EXC_FD); + } + else { + REG_AND(pDevice, txcontrol, ~EXC_FD); + } + if (NewCtrl != Ctrl) { + /* force a link down */ + b44_LM_WritePhy(pDevice, PHY_CTRL_REG, + PHY_CTRL_LOOPBACK_MODE); + i = 0; + do { + b44_LM_ReadPhy(pDevice, PHY_STATUS_REG, + &Value32); + b44_MM_Wait(100); + i++; + } while ((Value32 & PHY_STATUS_LINK_PASS) && (i < 800)); + b44_LM_ResetPhy(pDevice); + b44_MM_Wait(100); + b44_LM_WritePhy(pDevice, PHY_CTRL_REG, NewCtrl); + b44_LM_ReadPhy(pDevice, 26, &Value32); + b44_LM_WritePhy(pDevice, 26, Value32 & 0x7fff); + } + if (pDevice->RequestedDuplexMode == LM_DUPLEX_MODE_FULL) { + pDevice->FlowControlCap &= ~LM_FLOW_CONTROL_AUTO_PAUSE; + b44_LM_SetFlowControl(pDevice, 0, 0); + } + } + return LM_STATUS_SUCCESS; +} + +LM_STATUS +b44_LM_ResetPhy(LM_DEVICE_BLOCK *pDevice) +{ + LM_UINT32 value32; + + b44_LM_WritePhy(pDevice, 0, PHY_CTRL_PHY_RESET); + b44_MM_Wait(100); + b44_LM_ReadPhy(pDevice, 0, &value32); + if (value32 & PHY_CTRL_PHY_RESET) { + printf("Phy reset not complete\n"); + } + return LM_STATUS_SUCCESS; +} + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_VOID +b44_LM_ReadPhy( +PLM_DEVICE_BLOCK pDevice, LM_UINT32 PhyReg, LM_UINT32 *pData32) +{ + /* clear mii_int */ + REG_WR(pDevice, emacintstatus, EI_MII); + + /* issue the read */ + REG_WR(pDevice, mdiodata, (MD_SB_START | MD_OP_READ | + (pDevice->PhyAddr << MD_PMD_SHIFT) + | (PhyReg << MD_RA_SHIFT) | MD_TA_VALID)); + + /* wait for it to complete */ + SPINWAIT(((REG_RD(pDevice, emacintstatus) & EI_MII) == 0), 100); + if ((REG_RD(pDevice, emacintstatus) & EI_MII) == 0) { + printf("LM_ReadPhy: did not complete\n"); + } + + *pData32 = REG_RD(pDevice, mdiodata) & MD_DATA_MASK; +} /* LM_ReadPhy */ + + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_VOID +b44_LM_WritePhy( +PLM_DEVICE_BLOCK pDevice, LM_UINT32 PhyReg, LM_UINT32 Data32) +{ + /* clear mii_int */ + REG_WR(pDevice, emacintstatus, EI_MII); + ASSERT((REG_RD(pDevice, emacintstatus) & EI_MII) == 0); + + /* issue the write */ + REG_WR(pDevice, mdiodata, (MD_SB_START | MD_OP_WRITE | + (pDevice->PhyAddr << MD_PMD_SHIFT) + | (PhyReg << MD_RA_SHIFT) | MD_TA_VALID | Data32)); + + /* wait for it to complete */ + SPINWAIT(((REG_RD(pDevice, emacintstatus) & EI_MII) == 0), 100); + if ((REG_RD(pDevice, emacintstatus) & EI_MII) == 0) { + printf("b44_LM_WritePhy: did not complete\n"); + } +} /* LM_WritePhy */ + + +LM_STATUS +b44_LM_StatsUpdate(LM_DEVICE_BLOCK *pDevice) +{ + pDevice->tx_good_octets += REG_RD(pDevice, mib.tx_good_octets); + pDevice->tx_good_pkts += REG_RD(pDevice, mib.tx_good_pkts); + pDevice->tx_octets += REG_RD(pDevice, mib.tx_octets); + pDevice->tx_pkts += REG_RD(pDevice, mib.tx_pkts); + pDevice->tx_broadcast_pkts += REG_RD(pDevice, mib.tx_broadcast_pkts); + pDevice->tx_multicast_pkts += REG_RD(pDevice, mib.tx_multicast_pkts); + pDevice->tx_len_64 += REG_RD(pDevice, mib.tx_len_64); + pDevice->tx_len_65_to_127 += REG_RD(pDevice, mib.tx_len_65_to_127); + pDevice->tx_len_128_to_255 += REG_RD(pDevice, mib.tx_len_128_to_255); + pDevice->tx_len_256_to_511 += REG_RD(pDevice, mib.tx_len_256_to_511); + pDevice->tx_len_512_to_1023 += REG_RD(pDevice, mib.tx_len_512_to_1023); + pDevice->tx_len_1024_to_max += REG_RD(pDevice, mib.tx_len_1024_to_max); + pDevice->tx_jabber_pkts += REG_RD(pDevice, mib.tx_jabber_pkts); + pDevice->tx_oversize_pkts += REG_RD(pDevice, mib.tx_oversize_pkts); + pDevice->tx_fragment_pkts += REG_RD(pDevice, mib.tx_fragment_pkts); + pDevice->tx_underruns += REG_RD(pDevice, mib.tx_underruns); + pDevice->tx_total_cols += REG_RD(pDevice, mib.tx_total_cols); + pDevice->tx_single_cols += REG_RD(pDevice, mib.tx_single_cols); + pDevice->tx_multiple_cols += REG_RD(pDevice, mib.tx_multiple_cols); + pDevice->tx_excessive_cols += REG_RD(pDevice, mib.tx_excessive_cols); + pDevice->tx_late_cols += REG_RD(pDevice, mib.tx_late_cols); + pDevice->tx_defered += REG_RD(pDevice, mib.tx_defered); +/* pDevice->tx_carrier_lost += REG_RD(pDevice, mib.tx_carrier_lost);*/ + /* carrier counter is sometimes bogus, so disable it for now */ + REG_RD(pDevice, mib.tx_carrier_lost); + pDevice->tx_pause_pkts += REG_RD(pDevice, mib.tx_pause_pkts); + + pDevice->rx_good_octets += REG_RD(pDevice, mib.rx_good_octets); + pDevice->rx_good_pkts += REG_RD(pDevice, mib.rx_good_pkts); + pDevice->rx_octets += REG_RD(pDevice, mib.rx_octets); + pDevice->rx_pkts += REG_RD(pDevice, mib.rx_pkts); + pDevice->rx_broadcast_pkts += REG_RD(pDevice, mib.rx_broadcast_pkts); + pDevice->rx_multicast_pkts += REG_RD(pDevice, mib.rx_multicast_pkts); + pDevice->rx_len_64 += REG_RD(pDevice, mib.rx_len_64); + pDevice->rx_len_65_to_127 += REG_RD(pDevice, mib.rx_len_65_to_127); + pDevice->rx_len_128_to_255 += REG_RD(pDevice, mib.rx_len_128_to_255); + pDevice->rx_len_256_to_511 += REG_RD(pDevice, mib.rx_len_256_to_511); + pDevice->rx_len_512_to_1023 += REG_RD(pDevice, mib.rx_len_512_to_1023); + pDevice->rx_len_1024_to_max += REG_RD(pDevice, mib.rx_len_1024_to_max); + pDevice->rx_jabber_pkts += REG_RD(pDevice, mib.rx_jabber_pkts); + pDevice->rx_oversize_pkts += REG_RD(pDevice, mib.rx_oversize_pkts); + pDevice->rx_fragment_pkts += REG_RD(pDevice, mib.rx_fragment_pkts); + pDevice->rx_missed_pkts += REG_RD(pDevice, mib.rx_missed_pkts); + pDevice->rx_crc_align_errs += REG_RD(pDevice, mib.rx_crc_align_errs); + pDevice->rx_undersize += REG_RD(pDevice, mib.rx_undersize); + pDevice->rx_crc_errs += REG_RD(pDevice, mib.rx_crc_errs); + pDevice->rx_align_errs += REG_RD(pDevice, mib.rx_align_errs); + pDevice->rx_symbol_errs += REG_RD(pDevice, mib.rx_symbol_errs); + pDevice->rx_pause_pkts += REG_RD(pDevice, mib.rx_pause_pkts); + pDevice->rx_nonpause_pkts += REG_RD(pDevice, mib.rx_nonpause_pkts); + + return LM_STATUS_SUCCESS; +} + +void +b44_LM_WriteCam(LM_DEVICE_BLOCK *pDevice, LM_UINT8 *ea, LM_UINT32 camindex) +{ + LM_UINT32 w; + + w = ((LM_UINT32)ea[2] << 24) | ((LM_UINT32)ea[3] << 16) | + ((LM_UINT32) ea[4] << 8) | ea[5]; + REG_WR(pDevice, camdatalo, w); + w = CD_V | ((LM_UINT32)ea[0] << 8) | ea[1]; + REG_WR(pDevice, camdatahi, w); + REG_WR(pDevice, camcontrol, (((LM_UINT32) camindex << CC_INDEX_SHIFT) | + CC_WR)); + + /* spin until done */ + SPINWAIT((REG_RD(pDevice, camcontrol) & CC_CB), 100); +} + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +static LM_UINT32 +b44_GetPhyAdFlowCntrlSettings( + PLM_DEVICE_BLOCK pDevice) +{ + LM_UINT32 Value32; + + Value32 = 0; + + /* Auto negotiation flow control only when autonegotiation is enabled. */ + if(pDevice->DisableAutoNeg == FALSE || + pDevice->RequestedLineSpeed == LM_LINE_SPEED_AUTO) + { + /* Please refer to Table 28B-3 of the 802.3ab-1999 spec. */ + if((pDevice->FlowControlCap == LM_FLOW_CONTROL_AUTO_PAUSE) || + ((pDevice->FlowControlCap & LM_FLOW_CONTROL_RECEIVE_PAUSE) && + (pDevice->FlowControlCap & LM_FLOW_CONTROL_TRANSMIT_PAUSE))) + { + Value32 |= PHY_AN_AD_PAUSE_CAPABLE; + } + else if(pDevice->FlowControlCap & LM_FLOW_CONTROL_TRANSMIT_PAUSE) + { + Value32 |= PHY_AN_AD_ASYM_PAUSE; + } + else if(pDevice->FlowControlCap & LM_FLOW_CONTROL_RECEIVE_PAUSE) + { + Value32 |= PHY_AN_AD_PAUSE_CAPABLE | PHY_AN_AD_ASYM_PAUSE; + } + } + + return Value32; +} + + +LM_STATUS +b44_LM_GetStats(PLM_DEVICE_BLOCK pDevice) +{ + return LM_STATUS_SUCCESS; +} + +void +b44_LM_PollLink(LM_DEVICE_BLOCK *pDevice) +{ + LM_UINT32 status, aux; + LM_UINT32 txcontrol; + LM_UINT32 LocalAdv, RemoteAdv; + + b44_LM_ReadPhy(pDevice, 1, &status); + b44_LM_ReadPhy(pDevice, 1, &status); + + b44_LM_ReadPhy(pDevice, 24, &aux); + + /* check for bad mdio read */ + if (status == 0xffff) { + return; + } + + /* update current speed and duplex */ + if (aux & AUX_SPEED) + pDevice->LineSpeed = LM_LINE_SPEED_100MBPS; + else + pDevice->LineSpeed = LM_LINE_SPEED_10MBPS; + if (aux & AUX_DUPLEX) + pDevice->DuplexMode = LM_DUPLEX_MODE_FULL; + else + pDevice->DuplexMode = LM_DUPLEX_MODE_HALF; + + /* monitor link state */ + if ((pDevice->LinkStatus == LM_STATUS_LINK_DOWN) && + (status & STAT_LINK)) { + + /* keep emac txcontrol duplex bit consistent with current */ + /* phy duplex */ + txcontrol = REG_RD(pDevice, txcontrol); + if ((pDevice->DuplexMode == LM_DUPLEX_MODE_FULL) && + !(txcontrol & EXC_FD)) { + + REG_OR(pDevice, txcontrol, EXC_FD); + } + else if ((pDevice->DuplexMode == LM_DUPLEX_MODE_HALF) && + (txcontrol & EXC_FD)) { + + REG_AND(pDevice, txcontrol, ~EXC_FD); + } + if (!pDevice->DisableAutoNeg && (pDevice->DuplexMode == + LM_DUPLEX_MODE_FULL)) { + + b44_LM_ReadPhy(pDevice, PHY_AN_AD_REG, &LocalAdv); + b44_LM_ReadPhy(pDevice, PHY_LINK_PARTNER_ABILITY_REG, + &RemoteAdv); + b44_LM_SetFlowControl(pDevice, LocalAdv, RemoteAdv); + } + + pDevice->LinkStatus = LM_STATUS_LINK_ACTIVE; + b44_MM_IndicateStatus(pDevice, LM_STATUS_LINK_ACTIVE); + } + else if ((pDevice->LinkStatus == LM_STATUS_LINK_ACTIVE) && + !(status & STAT_LINK)) { + + pDevice->LinkStatus = LM_STATUS_LINK_DOWN; + b44_MM_IndicateStatus(pDevice, LM_STATUS_LINK_DOWN); + } + + + /* check for remote fault error */ + if (status & STAT_REMFAULT) { + printf("remote fault\n"); + } + + /* check for jabber error */ + if (status & STAT_JAB) { + printf("jabber\n"); + } +} + +/* reset and re-enable a core */ +void +b44_LM_sb_core_reset(LM_DEVICE_BLOCK *pDevice) +{ + volatile LM_UINT32 dummy; + + /* + * Must do the disable sequence first to work for arbitrary current core state. + */ + b44_LM_sb_core_disable(pDevice); + + /* + * Now do the initialization sequence. + */ + + /* set reset while enabling the clock and forcing them on throughout the core */ + REG_WR(pDevice, sbconfig.sbtmstatelow, + (SBTML_FGC | SBTML_CLK | SBTML_RESET)); + + dummy = REG_RD(pDevice, sbconfig.sbtmstatelow); + b44_MM_Wait(1); + + /* PR3158 workaround - not fixed in any chip yet */ + if (REG_RD(pDevice, sbconfig.sbtmstatehigh) & SBTMH_SERR) { + printf("SBTMH_SERR; clearing...\n"); + REG_WR(pDevice, sbconfig.sbtmstatehigh, 0); + ASSERT(0); + } + if ((dummy = REG_RD(pDevice, sbconfig.sbimstate)) & + (SBIM_IBE | SBIM_TO)) { + + REG_AND(pDevice, sbconfig.sbimstate, ~(SBIM_IBE | SBIM_TO)); + ASSERT(0); + } + + /* clear reset and allow it to propagate throughout the core */ + REG_WR(pDevice, sbconfig.sbtmstatelow, (SBTML_FGC | SBTML_CLK)); + dummy = REG_RD(pDevice, sbconfig.sbtmstatelow); + b44_MM_Wait(1); + + /* leave clock enabled */ + REG_WR(pDevice, sbconfig.sbtmstatelow, SBTML_CLK); + dummy = REG_RD(pDevice, sbconfig.sbtmstatelow); + b44_MM_Wait(1); +} + +void +b44_LM_sb_core_disable(LM_DEVICE_BLOCK *pDevice) +{ + volatile LM_UINT32 dummy; + + /* must return if core is already in reset */ + if (REG_RD(pDevice, sbconfig.sbtmstatelow) & SBTML_RESET) + return; + + /* set the reject bit */ + REG_WR(pDevice, sbconfig.sbtmstatelow, (SBTML_CLK | SBTML_REJ)); + + /* spin until reject is set */ + while ((REG_RD(pDevice, sbconfig.sbtmstatelow) & SBTML_REJ) == 0) + b44_MM_Wait(1); + + /* spin until sbtmstatehigh.busy is clear */ + while (REG_RD(pDevice, sbconfig.sbtmstatehigh) & SBTMH_BUSY) + b44_MM_Wait(1); + + /* set reset and reject while enabling the clocks */ + REG_WR(pDevice, sbconfig.sbtmstatelow, + (SBTML_FGC | SBTML_CLK | SBTML_REJ | SBTML_RESET)); + + dummy = REG_RD(pDevice, sbconfig.sbtmstatelow); + b44_MM_Wait(10); + + /* leave reset and reject asserted */ + REG_WR(pDevice, sbconfig.sbtmstatelow, (SBTML_REJ | SBTML_RESET)); + b44_MM_Wait(1); +} + +/* + * Configure the pci core for pci client (NIC) action + * and return the pci core revision. + */ +LM_UINT32 +b44_LM_sb_pci_setup(LM_DEVICE_BLOCK *pDevice, LM_UINT32 cores) +{ + LM_UINT32 bar0window; + sbpciregs_t *pciregs; + LM_UINT32 pcirev; + + pciregs = (sbpciregs_t *) pDevice->pMemView; + + /* save bar0window */ + b44_MM_ReadConfig32(pDevice, PCI_BAR0_WIN, &bar0window); + /* point bar0 at pci core registers */ + b44_MM_WriteConfig32(pDevice, PCI_BAR0_WIN, b44_LM_getsbaddr(pDevice, + SBID_REG_PCI, 0)); + + ASSERT(b44_LM_sb_coreid(pDevice) == SB_PCI); + + pcirev = b44_LM_sb_corerev(pDevice); + + /* enable sb->pci interrupts */ + REG_OR(pDevice, sbconfig.sbintvec, cores); + + /* enable prefetch and bursts for sonics-to-pci translation 2 */ + REG_WR_OFFSET(pDevice, OFFSETOF(sbpciregs_t, sbtopci2), + REG_RD_OFFSET(pDevice, OFFSETOF(sbpciregs_t, sbtopci2)) | + (SBTOPCI_PREF|SBTOPCI_BURST)); + + /* restore bar0window */ + b44_MM_WriteConfig32(pDevice, PCI_BAR0_WIN, bar0window); + + return (pcirev); +} + +/* + * Return the SB address corresponding to core instance . + * Provide a layer of indirection between SB address map elements + * and the individual chip maps. + */ +LM_UINT32 +b44_LM_getsbaddr(LM_DEVICE_BLOCK *pDevice, LM_UINT32 id, LM_UINT32 coreunit) +{ + struct sbmap *sbmap; + int i; + + sbmap = pDevice->sbmap; + ASSERT(sbmap); + + for (i = 0; i < SBID_MAX; i++) + if ((id == sbmap[i].id) && (coreunit == sbmap[i].coreunit)) + return (sbmap[i].sbaddr); + + ASSERT(0); + return (0xdeadbeef); +} + +LM_UINT32 +b44_LM_sb_base(LM_UINT32 admatch) +{ + LM_UINT32 base; + LM_UINT32 type; + + type = admatch & SBAM_TYPE_MASK; + ASSERT(type < 3); + + base = 0; + + if (type == 0) { + base = admatch & SBAM_BASE0_MASK; + } else if (type == 1) { + ASSERT(admatch & SBAM_ADEN); + ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ + base = admatch & SBAM_BASE1_MASK; + } else if (type == 2) { + ASSERT(admatch & SBAM_ADEN); + ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ + base = admatch & SBAM_BASE2_MASK; + } + + return (base); +} + +LM_UINT32 +b44_LM_sb_size(LM_UINT32 admatch) +{ + LM_UINT32 size; + LM_UINT32 type; + + type = admatch & SBAM_TYPE_MASK; + ASSERT(type < 3); + + size = 0; + + if (type == 0) { + size = 1 << (((admatch & SBAM_ADINT0_MASK) >> SBAM_ADINT0_SHIFT) + 1); + } else if (type == 1) { + ASSERT(admatch & SBAM_ADEN); + ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ + size = 1 << (((admatch & SBAM_ADINT1_MASK) >> SBAM_ADINT1_SHIFT) + 1); + } else if (type == 2) { + ASSERT(admatch & SBAM_ADEN); + ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ + size = 1 << (((admatch & SBAM_ADINT2_MASK) >> SBAM_ADINT2_SHIFT) + 1); + } + + return (size); +} + +/* return the core instance number of this core */ +LM_UINT32 +b44_LM_sb_coreunit(LM_DEVICE_BLOCK *pDevice) +{ + struct sbmap *sbmap; + LM_UINT32 base; + int i; + + sbmap = pDevice->sbmap; + ASSERT(sbmap); + + base = b44_LM_sb_base(REG_RD(pDevice, sbconfig.sbadmatch0)); + + for (i = 0; i < SBID_MAX; i++) + if (base == sbmap[i].sbaddr) + return (sbmap[i].coreunit); + + ASSERT(0); + return (0xdeadbeef); +} + + +LM_UINT32 +b44_LM_sb_clock(LM_DEVICE_BLOCK *pDevice, LM_UINT32 extifva) +{ + ASSERT(0); /* XXX TBD */ + return (0); +} + +LM_UINT32 +b44_LM_sb_coreid(LM_DEVICE_BLOCK *pDevice) +{ + return ((REG_RD(pDevice, sbconfig.sbidhigh) & + SBIDH_CC_MASK) >> SBIDH_CC_SHIFT); +} + +LM_UINT32 +b44_LM_sb_corerev(LM_DEVICE_BLOCK *pDevice) +{ + return (REG_RD(pDevice, sbconfig.sbidhigh) & SBIDH_RC_MASK); +} + +LM_UINT32 +b44_LM_sb_iscoreup(LM_DEVICE_BLOCK *pDevice) +{ + return ((REG_RD(pDevice, sbconfig.sbtmstatelow) & + (SBTML_RESET | SBTML_REJ | SBTML_CLK)) == SBTML_CLK); +} + +LM_VOID +b44_LM_PowerDownPhy(LM_DEVICE_BLOCK *pDevice) +{ + REG_WR(pDevice, emaccontrol, EMC_EP); +} + +#ifdef BCM_WOL + +/* Program patterns on the chip */ +static void +b44_LM_pmprog(LM_DEVICE_BLOCK *pDevice) +{ + LM_UINT32 wfl; + int plen0, plen1, max, i, j; + LM_UINT8 wol_pattern[BCMENET_PMPSIZE]; + LM_UINT8 wol_mask[BCMENET_PMMSIZE]; + + /* program the chip with wakeup patterns, masks, and lengths */ + + if (pDevice->WakeUpMode == LM_WAKE_UP_MODE_NONE) { + wfl = DISABLE_3210_PATMATCH; + REG_WR(pDevice, wakeuplength, wfl); + } + else if (pDevice->WakeUpMode == LM_WAKE_UP_MODE_MAGIC_PACKET) { + /* allow multicast magic packet */ + REG_OR(pDevice, rxconfig, ERC_AM); + + if (pDevice->corerev >= 7) { + LM_UINT32 addr; + + REG_WR(pDevice, wakeuplength, DISABLE_3210_PATMATCH); + + addr = (pDevice->NodeAddress[2] << 24) | + (pDevice->NodeAddress[3] << 16) | + (pDevice->NodeAddress[4] << 8) | + pDevice->NodeAddress[5]; + REG_WR(pDevice, enetaddrlo, addr); + + addr = (pDevice->NodeAddress[0] << 8) | + pDevice->NodeAddress[1]; + REG_WR(pDevice, enetaddrhi, addr); + + REG_OR(pDevice, devcontrol, DC_MPM | DC_PM); + return; + } + /* older chip */ + /* UDP magic packet pattern */ + memset(wol_pattern, 0, BCMENET_PMPSIZE); + memset(wol_pattern + 42, 0xff, 6); /* sync pattern */ + max = ETHERNET_ADDRESS_SIZE; + for (i = 0; i < 14; ++i) { + if (i == 13) + max = 2; + for (j = 0; j < max; ++j) { + wol_pattern[42 + 6 + + (i * ETHERNET_ADDRESS_SIZE) + j] = + pDevice->NodeAddress[j]; + } + } + memset(wol_mask, 0, BCMENET_PMMSIZE); + wol_mask[5] = 0xfc; + memset(wol_mask + 6, 0xff, 10); + plen0 = BCMENET_PMPSIZE - 1; + + b44_LM_ftwrite(pDevice, (LM_UINT32 *)wol_pattern, + BCMENET_PMPSIZE, BCMENET_PMPBASE); + + b44_LM_ftwrite(pDevice, (LM_UINT32 *)wol_mask, BCMENET_PMMSIZE, + BCMENET_PMMBASE); + + /* raw ethernet II magic packet pattern */ + memset(wol_pattern, 0, BCMENET_PMPSIZE); + memset(wol_pattern + 14, 0xff, 6); /* sync pattern */ + max = ETHERNET_ADDRESS_SIZE; + for (i = 0; i < 16; ++i) { + for (j = 0; j < max; ++j) { + wol_pattern[14 + 6 + + (i * ETHERNET_ADDRESS_SIZE) + j] = + pDevice->NodeAddress[j]; + } + } + memset(wol_mask, 0, BCMENET_PMMSIZE); + wol_mask[2] = 0xf0; + memset(wol_mask + 3, 0xff, 11); + wol_mask[14] = 0xf; + plen1 = 14 + 6 + 96 - 1; + + b44_LM_ftwrite(pDevice, (LM_UINT32 *)wol_pattern, + BCMENET_PMPSIZE, BCMENET_PMPBASE + BCMENET_PMPSIZE); + + b44_LM_ftwrite(pDevice, (LM_UINT32 *)wol_mask, BCMENET_PMMSIZE, + BCMENET_PMMBASE + BCMENET_PMMSIZE); + + /* set this pattern's length: one less than the real length */ + wfl = plen0 | (plen1 << 8) | DISABLE_32_PATMATCH; + + REG_WR(pDevice, wakeuplength, wfl); + + /* enable chip wakeup pattern matching */ + REG_OR(pDevice, devcontrol, DC_PM); + } + +} + +LM_VOID +b44_LM_pmset(LM_DEVICE_BLOCK *pDevice) +{ + LM_UINT16 Value16; + + b44_LM_Halt(pDevice); + + /* now turn on just enough of the chip to receive and match patterns */ + b44_LM_ResetAdapter(pDevice, FALSE); + + /* program patterns */ + b44_LM_pmprog(pDevice); + + /* enable sonics bus PME */ + REG_OR(pDevice, sbconfig.sbtmstatelow, SBTML_PE); + + b44_MM_ReadConfig16(pDevice, BCMENET_PMCSR, &Value16); + b44_MM_WriteConfig16(pDevice, BCMENET_PMCSR, + Value16 | ENABLE_PCICONFIG_PME); +} + + +static void +b44_LM_ftwrite(LM_DEVICE_BLOCK *pDevice, LM_UINT32 *b, LM_UINT32 nbytes, + LM_UINT32 ftaddr) +{ + LM_UINT32 i; + + for (i = 0; i < nbytes; i += sizeof(LM_UINT32)) { + REG_WR(pDevice, enetftaddr, ftaddr + i); + REG_WR(pDevice, enetftdata, b[i / sizeof(LM_UINT32)]); + } +} + +#endif /* BCM_WOL */ diff --git a/src/add-ons/kernel/drivers/network/bcm440x/b44lm.h b/src/add-ons/kernel/drivers/network/bcm440x/b44lm.h new file mode 100644 index 0000000000..eb1268203c --- /dev/null +++ b/src/add-ons/kernel/drivers/network/bcm440x/b44lm.h @@ -0,0 +1,411 @@ + +/******************************************************************************/ +/* */ +/* Broadcom BCM4400 Linux Network Driver, Copyright (c) 2002 Broadcom */ +/* Corporation. */ +/* All rights reserved. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, located in the file LICENSE. */ +/* */ +/* History: */ +/* 02/25/00 Hav Khauv Initial version. */ +/******************************************************************************/ + +#ifndef B44LM_H +#define B44LM_H + +#include "b44queue.h" + + + +/******************************************************************************/ +/* Basic types. */ +/******************************************************************************/ + +typedef char LM_CHAR, *PLM_CHAR; +typedef unsigned int LM_UINT, *PLM_UINT; +typedef unsigned char LM_UINT8, *PLM_UINT8; +typedef unsigned short LM_UINT16, *PLM_UINT16; +typedef unsigned int LM_UINT32, *PLM_UINT32; +typedef unsigned long LM_COUNTER, *PLM_COUNTER; +typedef void LM_VOID, *PLM_VOID; +typedef char LM_BOOL, *PLM_BOOL; + +typedef LM_UINT32 LM_PHYSICAL_ADDRESS, *PLM_PHYSICAL_ADDRESS; + +#ifndef TRUE +#define TRUE 1 +#endif /* TRUE */ + +#ifndef FALSE +#define FALSE 0 +#endif /* FALSE */ + +#ifndef NULL +#define NULL ((void *) 0) +#endif /* NULL */ + +#ifndef OFFSETOF +#define OFFSETOF(_s, _m) (B44_MM_PTR(&(((_s *) 0)->_m))) +#endif /* OFFSETOF */ + + + +/******************************************************************************/ +/* Simple macros. */ +/******************************************************************************/ + +#define IS_ETH_BROADCAST(_pEthAddr) \ + (((unsigned char *) (_pEthAddr))[0] == ((unsigned char) 0xff)) + +#define IS_ETH_MULTICAST(_pEthAddr) \ + (((unsigned char *) (_pEthAddr))[0] & ((unsigned char) 0x01)) + +#define IS_ETH_ADDRESS_EQUAL(_pEtherAddr1, _pEtherAddr2) \ + ((((unsigned char *) (_pEtherAddr1))[0] == \ + ((unsigned char *) (_pEtherAddr2))[0]) && \ + (((unsigned char *) (_pEtherAddr1))[1] == \ + ((unsigned char *) (_pEtherAddr2))[1]) && \ + (((unsigned char *) (_pEtherAddr1))[2] == \ + ((unsigned char *) (_pEtherAddr2))[2]) && \ + (((unsigned char *) (_pEtherAddr1))[3] == \ + ((unsigned char *) (_pEtherAddr2))[3]) && \ + (((unsigned char *) (_pEtherAddr1))[4] == \ + ((unsigned char *) (_pEtherAddr2))[4]) && \ + (((unsigned char *) (_pEtherAddr1))[5] == \ + ((unsigned char *) (_pEtherAddr2))[5])) + +#define COPY_ETH_ADDRESS(_Src, _Dst) \ + ((unsigned char *) (_Dst))[0] = ((unsigned char *) (_Src))[0]; \ + ((unsigned char *) (_Dst))[1] = ((unsigned char *) (_Src))[1]; \ + ((unsigned char *) (_Dst))[2] = ((unsigned char *) (_Src))[2]; \ + ((unsigned char *) (_Dst))[3] = ((unsigned char *) (_Src))[3]; \ + ((unsigned char *) (_Dst))[4] = ((unsigned char *) (_Src))[4]; \ + ((unsigned char *) (_Dst))[5] = ((unsigned char *) (_Src))[5]; + + + +/******************************************************************************/ +/* Constants. */ +/******************************************************************************/ + +#define ETHERNET_ADDRESS_SIZE 6 +#define ETHERNET_PACKET_HEADER_SIZE 14 +#define MIN_ETHERNET_PACKET_SIZE 64 /* with 4 byte crc. */ +#define MAX_ETHERNET_PACKET_SIZE 1518 /* with 4 byte crc. */ +#define MIN_ETHERNET_PACKET_SIZE_NO_CRC 60 +#define MAX_ETHERNET_PACKET_SIZE_NO_CRC 1514 +#define MAX_ETHERNET_PACKET_BUFFER_SIZE 1536 /* A nice even number. */ + +#ifndef LM_MAX_MC_TABLE_SIZE +#define LM_MAX_MC_TABLE_SIZE 63 +#endif /* LM_MAX_MC_TABLE_SIZE */ +#define LM_MC_ENTRY_SIZE (ETHERNET_ADDRESS_SIZE+1) +#define LM_MC_INSTANCE_COUNT_INDEX (LM_MC_ENTRY_SIZE-1) + + +/* Receive filter masks. */ +#define LM_ACCEPT_UNICAST 0x0001 +#define LM_ACCEPT_MULTICAST 0x0002 +#define LM_ACCEPT_ALL_MULTICAST 0x0004 +#define LM_ACCEPT_BROADCAST 0x0008 +#define LM_ACCEPT_ERROR_PACKET 0x0010 + +#define LM_PROMISCUOUS_MODE 0x10000 + + + +/******************************************************************************/ +/* PCI registers. */ +/******************************************************************************/ + +#define PCI_VENDOR_ID_REG 0x00 +#define PCI_DEVICE_ID_REG 0x02 + +#define PCI_COMMAND_REG 0x04 +#define PCI_IO_SPACE_ENABLE 0x0001 +#define PCI_MEM_SPACE_ENABLE 0x0002 +#define PCI_BUSMASTER_ENABLE 0x0004 +#define PCI_MEMORY_WRITE_INVALIDATE 0x0010 +#define PCI_PARITY_ERROR_ENABLE 0x0040 +#define PCI_SYSTEM_ERROR_ENABLE 0x0100 +#define PCI_FAST_BACK_TO_BACK_ENABLE 0x0200 + +#define PCI_STATUS_REG 0x06 +#define PCI_REV_ID_REG 0x08 + +#define PCI_CACHE_LINE_SIZE_REG 0x0c + +#define PCI_IO_BASE_ADDR_REG 0x10 +#define PCI_IO_BASE_ADDR_MASK 0xfffffff0 + +#define PCI_MEM_BASE_ADDR_LOW 0x10 +#define PCI_MEM_BASE_ADDR_HIGH 0x14 + +#define PCI_SUBSYSTEM_VENDOR_ID_REG 0x2c +#define PCI_SUBSYSTEM_ID_REG 0x2e +#define PCI_INT_LINE_REG 0x3c + +#define PCIX_CAP_REG 0x40 +#define PCIX_ENABLE_RELAXED_ORDERING BIT_17 + +/******************************************************************************/ +/* Fragment structure. */ +/******************************************************************************/ + +typedef struct { + LM_UINT32 FragSize; + LM_PHYSICAL_ADDRESS FragBuf; +} LM_FRAG, *PLM_FRAG; + +typedef struct { + /* FragCount is initialized for the caller to the maximum array size, on */ + /* return FragCount is the number of the actual fragments in the array. */ + LM_UINT32 FragCount; + + /* Total buffer size. */ + LM_UINT32 TotalSize; + + /* Fragment array buffer. */ + LM_FRAG Fragments[1]; +} LM_FRAG_LIST, *PLM_FRAG_LIST; + +#define DECLARE_FRAG_LIST_BUFFER_TYPE(_FRAG_LIST_TYPE_NAME, _MAX_FRAG_COUNT) \ + typedef struct { \ + LM_FRAG_LIST FragList; \ + LM_FRAG FragListBuffer[_MAX_FRAG_COUNT-1]; \ + } _FRAG_LIST_TYPE_NAME, *P##_FRAG_LIST_TYPE_NAME + + + +/******************************************************************************/ +/* Status codes. */ +/******************************************************************************/ + +#define LM_STATUS_SUCCESS 0 +#define LM_STATUS_FAILURE 1 + +#define LM_STATUS_INTERRUPT_ACTIVE 2 +#define LM_STATUS_INTERRUPT_NOT_ACTIVE 3 + +#define LM_STATUS_LINK_ACTIVE 4 +#define LM_STATUS_LINK_DOWN 5 +#define LM_STATUS_LINK_SETTING_MISMATCH 6 + +#define LM_STATUS_TOO_MANY_FRAGMENTS 7 +#define LM_STATUS_TRANSMIT_ABORTED 8 +#define LM_STATUS_TRANSMIT_ERROR 9 +#define LM_STATUS_RECEIVE_ABORTED 10 +#define LM_STATUS_RECEIVE_ERROR 11 +#define LM_STATUS_INVALID_PACKET_SIZE 12 +#define LM_STATUS_OUT_OF_MAP_REGISTERS 13 +#define LM_STATUS_UNKNOWN_ADAPTER 14 + +typedef LM_UINT LM_STATUS, *PLM_STATUS; + + +/******************************************************************************/ +/* Line speed. */ +/******************************************************************************/ + +#define LM_LINE_SPEED_UNKNOWN 0 +#define LM_LINE_SPEED_AUTO LM_LINE_SPEED_UNKNOWN +#define LM_LINE_SPEED_10MBPS 10 +#define LM_LINE_SPEED_100MBPS 100 + +typedef LM_UINT32 LM_LINE_SPEED, *PLM_LINE_SPEED; + + + +/******************************************************************************/ +/* Duplex mode. */ +/******************************************************************************/ + +#define LM_DUPLEX_MODE_UNKNOWN 0 +#define LM_DUPLEX_MODE_HALF 1 +#define LM_DUPLEX_MODE_FULL 2 + +typedef LM_UINT32 LM_DUPLEX_MODE, *PLM_DUPLEX_MODE; + + + +/******************************************************************************/ +/* Power state. */ +/******************************************************************************/ + +#define LM_POWER_STATE_D0 0 +#define LM_POWER_STATE_D1 1 +#define LM_POWER_STATE_D2 2 +#define LM_POWER_STATE_D3 3 + +typedef LM_UINT32 LM_POWER_STATE, *PLM_POWER_STATE; + + + +/******************************************************************************/ +/* Task offloading. */ +/******************************************************************************/ + +#define LM_TASK_OFFLOAD_NONE 0x0000 +#define LM_TASK_OFFLOAD_TX_IP_CHECKSUM 0x0001 +#define LM_TASK_OFFLOAD_RX_IP_CHECKSUM 0x0002 +#define LM_TASK_OFFLOAD_TX_TCP_CHECKSUM 0x0004 +#define LM_TASK_OFFLOAD_RX_TCP_CHECKSUM 0x0008 +#define LM_TASK_OFFLOAD_TX_UDP_CHECKSUM 0x0010 +#define LM_TASK_OFFLOAD_RX_UDP_CHECKSUM 0x0020 +#define LM_TASK_OFFLOAD_TCP_SEGMENTATION 0x0040 + +typedef LM_UINT32 LM_TASK_OFFLOAD, *PLM_TASK_OFFLOAD; + + + +/******************************************************************************/ +/* Flow control. */ +/******************************************************************************/ + +#define LM_FLOW_CONTROL_NONE 0x00 +#define LM_FLOW_CONTROL_RECEIVE_PAUSE 0x01 +#define LM_FLOW_CONTROL_TRANSMIT_PAUSE 0x02 +#define LM_FLOW_CONTROL_RX_TX_PAUSE (LM_FLOW_CONTROL_RECEIVE_PAUSE | \ + LM_FLOW_CONTROL_TRANSMIT_PAUSE) + +/* This value can be or-ed with RECEIVE_PAUSE and TRANSMIT_PAUSE. If the */ +/* auto-negotiation is disabled and the RECEIVE_PAUSE and TRANSMIT_PAUSE */ +/* bits are set, then flow control is enabled regardless of link partner's */ +/* flow control capability. */ +#define LM_FLOW_CONTROL_AUTO_PAUSE 0x80000000 + +typedef LM_UINT32 LM_FLOW_CONTROL, *PLM_FLOW_CONTROL; + + + +/******************************************************************************/ +/* Wake up mode. */ +/******************************************************************************/ + +#define LM_WAKE_UP_MODE_NONE 0 +#define LM_WAKE_UP_MODE_MAGIC_PACKET 1 +#define LM_WAKE_UP_MODE_NWUF 2 +#define LM_WAKE_UP_MODE_LINK_CHANGE 4 + +typedef LM_UINT32 LM_WAKE_UP_MODE, *PLM_WAKE_UP_MODE; + + + +/******************************************************************************/ +/* Counters. */ +/******************************************************************************/ + +#define LM_COUNTER_FRAMES_XMITTED_OK 0 +#define LM_COUNTER_FRAMES_RECEIVED_OK 1 +#define LM_COUNTER_ERRORED_TRANSMIT_COUNT 2 +#define LM_COUNTER_ERRORED_RECEIVE_COUNT 3 +#define LM_COUNTER_RCV_CRC_ERROR 4 +#define LM_COUNTER_ALIGNMENT_ERROR 5 +#define LM_COUNTER_SINGLE_COLLISION_FRAMES 6 +#define LM_COUNTER_MULTIPLE_COLLISION_FRAMES 7 +#define LM_COUNTER_FRAMES_DEFERRED 8 +#define LM_COUNTER_MAX_COLLISIONS 9 +#define LM_COUNTER_RCV_OVERRUN 10 +#define LM_COUNTER_XMIT_UNDERRUN 11 +#define LM_COUNTER_UNICAST_FRAMES_XMIT 12 +#define LM_COUNTER_MULTICAST_FRAMES_XMIT 13 +#define LM_COUNTER_BROADCAST_FRAMES_XMIT 14 +#define LM_COUNTER_UNICAST_FRAMES_RCV 15 +#define LM_COUNTER_MULTICAST_FRAMES_RCV 16 +#define LM_COUNTER_BROADCAST_FRAMES_RCV 17 + +typedef LM_UINT32 LM_COUNTER_TYPE, *PLM_COUNTER_TYPE; + + + +/******************************************************************************/ +/* Forward definition. */ +/******************************************************************************/ + +typedef struct _LM_DEVICE_BLOCK *PLM_DEVICE_BLOCK; +typedef struct _LM_PACKET *PLM_PACKET; + + + +/******************************************************************************/ +/* Function prototypes. */ +/******************************************************************************/ + +LM_STATUS b44_LM_GetAdapterInfo(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_LM_InitializeAdapter(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_LM_ResetAdapter(PLM_DEVICE_BLOCK pDevice, LM_BOOL full); +LM_STATUS b44_LM_DisableInterrupt(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_LM_EnableInterrupt(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_LM_SendPacket(PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket); +LM_STATUS b44_LM_ServiceInterrupts(PLM_DEVICE_BLOCK pDevice); +#ifdef BCM_NAPI_RXPOLL +int b44_LM_ServiceRxPoll(PLM_DEVICE_BLOCK pDevice, int limit); +#endif +LM_STATUS b44_LM_QueueRxPackets(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_LM_SetReceiveMask(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Mask); +LM_STATUS b44_LM_Halt(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_LM_Abort(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_LM_MulticastAdd(PLM_DEVICE_BLOCK pDevice, PLM_UINT8 pMcAddress); +LM_STATUS b44_LM_MulticastDel(PLM_DEVICE_BLOCK pDevice, PLM_UINT8 pMcAddress); +LM_STATUS b44_LM_MulticastClear(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_LM_SetMacAddress(PLM_DEVICE_BLOCK pDevice, PLM_UINT8 pMacAddress); +LM_STATUS b44_LM_LoopbackAddress(PLM_DEVICE_BLOCK pDevice, PLM_UINT8 pAddress); + +LM_WAKE_UP_MODE b44_LM_PMCapabilities(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_LM_SetPowerState(PLM_DEVICE_BLOCK pDevice, LM_POWER_STATE PowerLevel); + +LM_VOID b44_LM_ReadPhy(PLM_DEVICE_BLOCK pDevice, LM_UINT32 PhyReg, + PLM_UINT32 pData32); +LM_VOID b44_LM_WritePhy(PLM_DEVICE_BLOCK pDevice, LM_UINT32 PhyReg, + LM_UINT32 Data32); + +LM_STATUS b44_LM_SetupPhy(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_LM_ResetPhy(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_LM_GetStats(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_LM_NvramRead(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT32 *pData); +LM_STATUS b44_LM_NvramWriteBlock(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT32 *pData, LM_UINT32 Size); +void b44_LM_PollLink(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_LM_StatsUpdate(PLM_DEVICE_BLOCK pDevice); +#ifdef BCM_WOL +LM_VOID b44_LM_pmset(PLM_DEVICE_BLOCK pDevice); +#endif +LM_VOID b44_LM_PowerDownPhy(PLM_DEVICE_BLOCK pDevice); + +/******************************************************************************/ +/* These are the OS specific functions called by LMAC. */ +/******************************************************************************/ + +LM_STATUS b44_MM_ReadConfig16(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT16 *pValue16); +LM_STATUS b44_MM_WriteConfig16(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT16 Value16); +LM_STATUS b44_MM_ReadConfig32(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT32 *pValue32); +LM_STATUS b44_MM_WriteConfig32(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT32 Value32); +LM_STATUS b44_MM_MapMemBase(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_MM_MapIoBase(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_MM_IndicateRxPackets(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_MM_IndicateTxPackets(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_MM_StartTxDma(PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket); +LM_STATUS b44_MM_CompleteTxDma(PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket); +LM_STATUS b44_MM_AllocateMemory(PLM_DEVICE_BLOCK pDevice, LM_UINT32 BlockSize, + PLM_VOID *pMemoryBlockVirt); +LM_STATUS b44_MM_AllocateSharedMemory(PLM_DEVICE_BLOCK pDevice, LM_UINT32 BlockSize, + PLM_VOID *pMemoryBlockVirt, PLM_PHYSICAL_ADDRESS pMemoryBlockPhy); +LM_STATUS b44_MM_GetConfig(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_MM_IndicateStatus(PLM_DEVICE_BLOCK pDevice, LM_STATUS Status); +LM_STATUS b44_MM_InitializeUmPackets(PLM_DEVICE_BLOCK pDevice); +LM_STATUS b44_MM_FreeRxBuffer(PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket); +#ifdef BCM_NAPI_RXPOLL +LM_STATUS b44_MM_ScheduleRxPoll(PLM_DEVICE_BLOCK pDevice); +#endif + + +#endif /* B44LM_H */ + diff --git a/src/add-ons/kernel/drivers/network/bcm440x/b44mm.h b/src/add-ons/kernel/drivers/network/bcm440x/b44mm.h new file mode 100644 index 0000000000..785cfcbf8d --- /dev/null +++ b/src/add-ons/kernel/drivers/network/bcm440x/b44mm.h @@ -0,0 +1,132 @@ +#ifndef B44MM_H +#define B44MM_H + +#include +#include +#include +#include +#include + +typedef vint32 MM_ATOMIC_T; + +//#define MM_SWAP_LE16(x) B_SWAP_INT16(x) +#define MM_SWAP_LE16(x) x + +/*#define MM_ATOMIC_SET(ptr, val) atomic_and(ptr, 0); atomic_add(ptr,val) +#define MM_ATOMIC_READ(ptr) atomic_add(ptr,0) +#define MM_ATOMIC_INC(ptr) atomic_add(ptr,1) +#define MM_ATOMIC_ADD(ptr, val) atomic_add(ptr,val) +#define MM_ATOMIC_DEC(ptr) atomic_add(ptr,-1) +#define MM_ATOMIC_SUB(ptr, val) atomic_add(ptr,0-val)*/ + +#define MM_ATOMIC_SET(ptr, val) *(ptr)=val +#define MM_ATOMIC_READ(ptr) *(ptr) +#define MM_ATOMIC_INC(ptr) (*(ptr))++ +#define MM_ATOMIC_ADD(ptr, val) *(ptr)+=val +#define MM_ATOMIC_DEC(ptr) (*(ptr))-- +#define MM_ATOMIC_SUB(ptr, val) *(ptr)-=val + +/* All critical sections are protected by locking mechanisms already */ + +#define __io_virt(x) ((void *)(x)) +#define readl(addr) (*(volatile unsigned int *) __io_virt(addr)) +#define writel(b,addr) (*(volatile unsigned int *) __io_virt(addr) = (b)) +#define __raw_readl readl +#define __raw_writel writel + +#define udelay spin + +#define MM_MEMWRITEL(ptr, val) __raw_writel(val, ptr) +#define MM_MEMREADL(ptr) __raw_readl(ptr) + +#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory") +#define wmb() mb() + +#define readl(addr) (*(volatile unsigned int *) __io_virt(addr)) + +#define MM_MB() mb() +#define MM_WMB() wmb() + +#define STATIC static + +extern int b44_Packet_Desc_Size; + +#define B44_MM_PACKET_DESC_SIZE b44_Packet_Desc_Size + +#include "b44lm.h" +#include "b44queue.h" +#include "b44.h" + +struct be_b44_dev { + LM_DEVICE_BLOCK lm_dev; + + struct pci_info pci_data; + + sem_id packet_release_sem; + //sem_id interrupt_sem; + //thread_id interrupt_handler; + + LM_RX_PACKET_Q RxPacketReadQ; + + void *mem_list[16]; + int mem_list_num; + + area_id lockmem_list[16]; + int lockmem_list_num; + + area_id mem_base; + + vint32 opened; + + int block; + spinlock lock; +}; + +struct B_UM_PACKET { + struct _LM_PACKET pkt; + + void *data; + size_t size; +}; + +static inline void b44_MM_MapRxDma(PLM_DEVICE_BLOCK pDevice, + struct _LM_PACKET *pPacket, + LM_UINT32 *paddr) +{ + physical_entry entry; + + get_memory_map(pPacket->u.Rx.pRxBufferVirt,pPacket->u.Rx.RxBufferSize,&entry,1); + *paddr = (LM_UINT32) entry.address; +} + +static inline void b44_MM_MapTxDma(PLM_DEVICE_BLOCK pDevice, + struct _LM_PACKET *pPacket, + LM_UINT32 *paddr, LM_UINT32 *len, int frag) +{ + struct B_UM_PACKET *pkt = (struct B_UM_PACKET *)pPacket; + physical_entry entry; + + get_memory_map(pkt->data,pkt->size,&entry,1); + *paddr = (LM_UINT32) entry.address; + *len = pPacket->PacketSize; +} + +#if (BITS_PER_LONG == 64) +#define B44_MM_GETSTATS(_Ctr) \ + (unsigned long) (_Ctr).Low + ((unsigned long) (_Ctr).High << 32) +#else +#define B44_MM_GETSTATS(_Ctr) \ + (unsigned long) (_Ctr).Low +#endif + +#define B44_MM_PTR(_ptr) ((unsigned long) (_ptr)) +#define printf(fmt, args...) dprintf(fmt, ##args) +#define DbgPrint(fmt, arg...) dprintf(fmt, ##arg) +#define DbgBreakPoint() +#define b44_MM_Wait(time) udelay(time) +#define ASSERT(expr) \ + if (!(expr)) { \ + dprintf("ASSERT failed: %s\n", #expr); \ + } + +#endif diff --git a/src/add-ons/kernel/drivers/network/bcm440x/b44queue.h b/src/add-ons/kernel/drivers/network/bcm440x/b44queue.h new file mode 100644 index 0000000000..d27b14c8c1 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/bcm440x/b44queue.h @@ -0,0 +1,342 @@ + +/******************************************************************************/ +/* */ +/* Broadcom BCM4400 Linux Network Driver, Copyright (c) 2002 Broadcom */ +/* Corporation. */ +/* All rights reserved. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, located in the file LICENSE. */ +/* */ +/* Queue functions. */ +/* void QQ_InitQueue(PQQ_CONTAINER pQueue) */ +/* char QQ_Full(PQQ_CONTAINER pQueue) */ +/* char QQ_Empty(PQQ_CONTAINER pQueue) */ +/* unsigned int QQ_GetSize(PQQ_CONTAINER pQueue) */ +/* unsigned int QQ_GetEntryCnt(PQQ_CONTAINER pQueue) */ +/* char QQ_PushHead(PQQ_CONTAINER pQueue, PQQ_ENTRY pEntry) */ +/* char QQ_PushTail(PQQ_CONTAINER pQueue, PQQ_ENTRY pEntry) */ +/* PQQ_ENTRY QQ_PopHead(PQQ_CONTAINER pQueue) */ +/* PQQ_ENTRY QQ_PopTail(PQQ_CONTAINER pQueue) */ +/* PQQ_ENTRY QQ_GetHead(PQQ_CONTAINER pQueue, unsigned int Idx) */ +/* PQQ_ENTRY QQ_GetTail(PQQ_CONTAINER pQueue, unsigned int Idx) */ +/* */ +/* */ +/* History: */ +/* 02/25/00 Hav Khauv Initial version. */ +/******************************************************************************/ + +#ifndef BCM_QUEUE_H +#define BCM_QUEUE_H + + + +/******************************************************************************/ +/* Queue definitions. */ +/******************************************************************************/ + +/* Entry for queueing. */ +typedef void *PQQ_ENTRY; + + +/* Queue header -- base type. */ +typedef struct { + unsigned int Head; + unsigned int Tail; + unsigned int Size; + MM_ATOMIC_T EntryCnt; + PQQ_ENTRY Array[1]; +} QQ_CONTAINER, *PQQ_CONTAINER; + + +/* Declare queue type macro. */ +#define DECLARE_QUEUE_TYPE(_QUEUE_TYPE, _QUEUE_SIZE) \ + \ + typedef struct { \ + QQ_CONTAINER Container; \ + PQQ_ENTRY EntryBuffer[_QUEUE_SIZE]; \ + } _QUEUE_TYPE, *P##_QUEUE_TYPE + + + +/******************************************************************************/ +/* Compilation switches. */ +/******************************************************************************/ + +#if DBG +#undef QQ_NO_OVERFLOW_CHECK +#undef QQ_NO_UNDERFLOW_CHECK +#endif /* DBG */ + +#ifdef QQ_USE_MACROS +/* notdone */ +#else + +#ifdef QQ_NO_INLINE +#define __inline +#endif /* QQ_NO_INLINE */ + + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +__inline static void +QQ_InitQueue( +PQQ_CONTAINER pQueue, +unsigned int QueueSize) { + pQueue->Head = 0; + pQueue->Tail = 0; + pQueue->Size = QueueSize+1; + MM_ATOMIC_SET(&pQueue->EntryCnt, 0); +} /* QQ_InitQueue */ + + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +__inline static char +QQ_Full( +PQQ_CONTAINER pQueue) { + unsigned int NewHead; + + NewHead = (pQueue->Head + 1) % pQueue->Size; + + return(NewHead == pQueue->Tail); +} /* QQ_Full */ + + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +__inline static char +QQ_Empty( +PQQ_CONTAINER pQueue) { + return(pQueue->Head == pQueue->Tail); +} /* QQ_Empty */ + + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +__inline static unsigned int +QQ_GetSize( +PQQ_CONTAINER pQueue) { + return pQueue->Size; +} /* QQ_GetSize */ + + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +__inline static unsigned int +QQ_GetEntryCnt( +PQQ_CONTAINER pQueue) { + return MM_ATOMIC_READ(&pQueue->EntryCnt); +} /* QQ_GetEntryCnt */ + + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/* TRUE entry was added successfully. */ +/* FALSE queue is full. */ +/******************************************************************************/ +__inline static char +QQ_PushHead( +PQQ_CONTAINER pQueue, +PQQ_ENTRY pEntry) { + unsigned int Head; + + Head = (pQueue->Head + 1) % pQueue->Size; + +#if !defined(QQ_NO_OVERFLOW_CHECK) + if(Head == pQueue->Tail) { + return 0; + } /* if */ +#endif /* QQ_NO_OVERFLOW_CHECK */ + + pQueue->Array[pQueue->Head] = pEntry; + MM_WMB(); + pQueue->Head = Head; + MM_ATOMIC_INC(&pQueue->EntryCnt); + + return -1; +} /* QQ_PushHead */ + + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/* TRUE entry was added successfully. */ +/* FALSE queue is full. */ +/******************************************************************************/ +__inline static char +QQ_PushTail( +PQQ_CONTAINER pQueue, +PQQ_ENTRY pEntry) { + unsigned int Tail; + + Tail = pQueue->Tail; + if(Tail == 0) { + Tail = pQueue->Size; + } /* if */ + Tail--; + +#if !defined(QQ_NO_OVERFLOW_CHECK) + if(Tail == pQueue->Head) { + return 0; + } /* if */ +#endif /* QQ_NO_OVERFLOW_CHECK */ + + pQueue->Array[Tail] = pEntry; + MM_WMB(); + pQueue->Tail = Tail; + MM_ATOMIC_INC(&pQueue->EntryCnt); + + return -1; +} /* QQ_PushTail */ + + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +__inline static PQQ_ENTRY +QQ_PopHead( +PQQ_CONTAINER pQueue) { + unsigned int Head; + PQQ_ENTRY Entry; + + Head = pQueue->Head; + +#if !defined(QQ_NO_UNDERFLOW_CHECK) + if(Head == pQueue->Tail) { + return (PQQ_ENTRY) 0; + } /* if */ +#endif /* QQ_NO_UNDERFLOW_CHECK */ + + if(Head == 0) { + Head = pQueue->Size; + } /* if */ + Head--; + + Entry = pQueue->Array[Head]; + MM_MB(); + pQueue->Head = Head; + MM_ATOMIC_DEC(&pQueue->EntryCnt); + + return Entry; +} /* QQ_PopHead */ + + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +__inline static PQQ_ENTRY +QQ_PopTail( +PQQ_CONTAINER pQueue) { + unsigned int Tail; + PQQ_ENTRY Entry; + + Tail = pQueue->Tail; + +#if !defined(QQ_NO_UNDERFLOW_CHECK) + if(Tail == pQueue->Head) { + return (PQQ_ENTRY) 0; + } /* if */ +#endif /* QQ_NO_UNDERFLOW_CHECK */ + + Entry = pQueue->Array[Tail]; + MM_MB(); + pQueue->Tail = (Tail + 1) % pQueue->Size; + MM_ATOMIC_DEC(&pQueue->EntryCnt); + + return Entry; +} /* QQ_PopTail */ + + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +__inline static PQQ_ENTRY +QQ_GetHead( + PQQ_CONTAINER pQueue, + unsigned int Idx) +{ + if(Idx >= MM_ATOMIC_READ(&pQueue->EntryCnt)) + { + return (PQQ_ENTRY) 0; + } + + if(pQueue->Head > Idx) + { + Idx = pQueue->Head - Idx; + } + else + { + Idx = pQueue->Size - (Idx - pQueue->Head); + } + Idx--; + + return pQueue->Array[Idx]; +} + + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +__inline static PQQ_ENTRY +QQ_GetTail( + PQQ_CONTAINER pQueue, + unsigned int Idx) +{ + if(Idx >= MM_ATOMIC_READ(&pQueue->EntryCnt)) + { + return (PQQ_ENTRY) 0; + } + + Idx += pQueue->Tail; + if(Idx >= pQueue->Size) + { + Idx = Idx - pQueue->Size; + } + + return pQueue->Array[Idx]; +} + +#endif /* QQ_USE_MACROS */ + + + +#endif /* QUEUE_H */ diff --git a/src/add-ons/kernel/drivers/network/bcm440x/b44um.c b/src/add-ons/kernel/drivers/network/bcm440x/b44um.c new file mode 100644 index 0000000000..41a604e914 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/bcm440x/b44um.c @@ -0,0 +1,489 @@ +#include +#include +#include +#include +#include +#include + +#include "b44mm.h" +#include "b44lm.h" +#include "mempool.h" + +struct pci_module_info *pci = NULL; + +const char *dev_list[11]; +struct be_b44_dev be_b44_dev_cards[10]; +int cards_found = 0; + +int b44_Packet_Desc_Size = sizeof(struct B_UM_PACKET); + +#define ROUND_UP_TO_PAGE(size) ((size % 4096 != 0) ? 4096 - (size % 4096) + size : size) + +/* -------- BeOS Driver Hooks ------------ */ + +status_t b44_open(const char *name, uint32 flags, void **cookie); +status_t b44_close(void *cookie); +status_t b44_free(void *cookie); +status_t b44_ioctl(void *cookie,uint32 op,void *data,size_t len); +status_t b44_read(void *cookie,off_t pos,void *data,size_t *numBytes); +status_t b44_write(void *cookie,off_t pos,const void *data,size_t *numBytes); +int32 b44_interrupt(void *cookie); +int32 tx_cleanup_thread(void *us); + +device_hooks b44_hooks = {b44_open,b44_close,b44_free,b44_ioctl,b44_read,b44_write,NULL,NULL,NULL,NULL}; + +status_t init_hardware(void) { + return B_OK; +} + +const char **publish_devices() { + return dev_list; +} + +device_hooks *find_device(const char *name) { + return &b44_hooks; +} + +status_t init_driver(void) { + int i = 0; + pci_info dev_info; + + if (pci == NULL) + get_module(B_PCI_MODULE_NAME,(module_info **)&pci); + + while (pci->get_nth_pci_info(i++,&dev_info) == 0) { + if (!((dev_info.class_base == PCI_network) && (dev_info.class_sub == PCI_ethernet) + && (dev_info.vendor_id == 0x14e4) && (dev_info.device_id == 0x4401))) + continue; + + if (cards_found >= 10) + break; + + dev_list[cards_found] = (char *)malloc(16 /* net/bcm440x/xx */); + sprintf(dev_list[cards_found],"net/bcm440x/%d",cards_found); + be_b44_dev_cards[cards_found].pci_data = dev_info; + be_b44_dev_cards[cards_found].packet_release_sem = create_sem(0,dev_list[cards_found]); + be_b44_dev_cards[cards_found].mem_list_num = 0; + be_b44_dev_cards[cards_found].lockmem_list_num = 0; + be_b44_dev_cards[cards_found].opened = 0; + be_b44_dev_cards[cards_found].block = 1; + be_b44_dev_cards[cards_found].lock = 0; + + if (b44_LM_GetAdapterInfo(&be_b44_dev_cards[cards_found].lm_dev) != LM_STATUS_SUCCESS) + return ENODEV; + + QQ_InitQueue(&be_b44_dev_cards[cards_found].RxPacketReadQ.Container,MAX_RX_PACKET_DESC_COUNT); + + cards_found++; + } + + mempool_init((MAX_RX_PACKET_DESC_COUNT+10) * cards_found); + + dev_list[cards_found] = NULL; + + return B_OK; +} + +void uninit_driver(void) { + int i,j; + struct be_b44_dev *pUmDevice; + + for (j = 0; j < cards_found; j++) { + pUmDevice = &be_b44_dev_cards[j]; + for (i = 0; i < pUmDevice->mem_list_num; i++) + free(pUmDevice->mem_list[i]); + for (i = 0; i < pUmDevice->lockmem_list_num; i++) + delete_area(pUmDevice->lockmem_list[i]); + + delete_area(pUmDevice->mem_base); + + free(dev_list[j]); + } + + mempool_exit(); +} + +status_t b44_open(const char *name, uint32 flags, void **cookie) { + struct be_b44_dev *pDevice = NULL; + int i; + + *cookie = NULL; + for (i = 0; i < cards_found; i++) { + if (strcmp(dev_list[i],name) == 0) { + *cookie = pDevice = &be_b44_dev_cards[i]; + break; + } + } + + if (*cookie == NULL) + return B_FILE_NOT_FOUND; + + if (atomic_or(&pDevice->opened,1)) { + *cookie = pDevice = NULL; + return B_BUSY; + } + + install_io_interrupt_handler(pDevice->pci_data.u.h0.interrupt_line,b44_interrupt,*cookie,0); + if (b44_LM_InitializeAdapter(&pDevice->lm_dev) != LM_STATUS_SUCCESS) { + atomic_and(&pDevice->opened,0); + remove_io_interrupt_handler(pDevice->pci_data.u.h0.interrupt_line,b44_interrupt,*cookie); + *cookie = NULL; + return B_ERROR; + } + + /*QQ_InitQueue(&pDevice->rx_out_of_buf_q.Container, + MAX_RX_PACKET_DESC_COUNT);*/ + + b44_LM_EnableInterrupt(&pDevice->lm_dev); + + return B_OK; +} + +status_t b44_close(void *cookie) { + struct be_b44_dev *pUmDevice = (struct be_b44_dev *)(cookie); + + if (cookie == NULL) + return B_OK; + + atomic_and(&pUmDevice->opened,0); + b44_LM_Halt(&pUmDevice->lm_dev); + + return B_OK; +} + +status_t b44_free(void *cookie) { + struct be_b44_dev *pUmDevice = (struct be_b44_dev *)(cookie); + + if (cookie == NULL) + return B_OK; + + remove_io_interrupt_handler(pUmDevice->pci_data.u.h0.interrupt_line,b44_interrupt,cookie); + return B_OK; +} + +status_t b44_ioctl(void *cookie,uint32 op,void *data,size_t len) { + struct be_b44_dev *pUmDevice = (struct be_b44_dev *)(cookie); + + switch (op) { + case ETHER_INIT: + return B_OK; + case ETHER_GETADDR: + if (data == NULL) + return B_ERROR; + + memcpy(data,pUmDevice->lm_dev.NodeAddress,6); + return B_OK; + case ETHER_NONBLOCK: + pUmDevice->block = *((uint8 *)(data)); + return B_OK; + case ETHER_ADDMULTI: + return (b44_LM_MulticastAdd(&pUmDevice->lm_dev,(PLM_UINT8)(data)) == LM_STATUS_SUCCESS) ? B_OK : B_ERROR; + case ETHER_REMMULTI: + return (b44_LM_MulticastDel(&pUmDevice->lm_dev,(PLM_UINT8)(data)) == LM_STATUS_SUCCESS) ? B_OK : B_ERROR; + case ETHER_SETPROMISC: + if (*((uint8 *)(data))) + b44_LM_SetReceiveMask(&pUmDevice->lm_dev, + pUmDevice->lm_dev.ReceiveMask | LM_PROMISCUOUS_MODE); + else + b44_LM_SetReceiveMask(&pUmDevice->lm_dev, + pUmDevice->lm_dev.ReceiveMask & ~LM_PROMISCUOUS_MODE); + return B_OK; + } + return B_ERROR; +} + +int32 b44_interrupt(void *cookie) { + struct be_b44_dev *pUmDevice = (struct be_b44_dev *)cookie; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + + + if (!pDevice->InitDone) + return B_UNHANDLED_INTERRUPT; + + if (b44_LM_ServiceInterrupts(pDevice) == 12) + return B_UNHANDLED_INTERRUPT; + + if (QQ_GetEntryCnt(&pDevice->RxPacketFreeQ.Container)) { + b44_LM_QueueRxPackets(pDevice); + return B_INVOKE_SCHEDULER; + } + + return B_HANDLED_INTERRUPT; +} + +status_t b44_read(void *cookie,off_t pos,void *data,size_t *numBytes) { + struct be_b44_dev *pUmDevice = (struct be_b44_dev *)cookie; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + PLM_PACKET pPacket; + struct B_UM_PACKET *pUmPacket; + cpu_status cpu; + + if (pUmDevice->block) + acquire_sem(pUmDevice->packet_release_sem); + + cpu = disable_interrupts(); + acquire_spinlock(&pUmDevice->lock); + + pPacket = (PLM_PACKET) + QQ_PopHead(&pUmDevice->RxPacketReadQ.Container); + + release_spinlock(&pUmDevice->lock); + restore_interrupts(cpu); + + if (pPacket == 0) + return B_ERROR; + + pUmPacket = (struct B_UM_PACKET *) pPacket; + if ((pPacket->PacketStatus != LM_STATUS_SUCCESS) || + ((pPacket->PacketSize) > 1518)) { + + cpu = disable_interrupts(); + acquire_spinlock(&pUmDevice->lock); + + QQ_PushTail(&pDevice->RxPacketFreeQ.Container, pPacket); + + release_spinlock(&pUmDevice->lock); + restore_interrupts(cpu); + *numBytes = -1; + return B_ERROR; + } + + if ((pPacket->PacketSize/*-pDevice->rxoffset*/) < *numBytes) + *numBytes = pPacket->PacketSize/*-pDevice->rxoffset*/; + + memcpy(data,pUmPacket->data+pDevice->rxoffset,*numBytes); + cpu = disable_interrupts(); + acquire_spinlock(&pUmDevice->lock); + + QQ_PushTail(&pDevice->RxPacketFreeQ.Container, pPacket); + + release_spinlock(&pUmDevice->lock); + restore_interrupts(cpu); + + return B_OK; +} + +status_t b44_write(void *cookie,off_t pos,const void *data,size_t *numBytes) { + struct be_b44_dev *pUmDevice = (struct be_b44_dev *)cookie; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + PLM_PACKET pPacket; + struct B_UM_PACKET *pUmPacket; + + /*if ((pDevice->LinkStatus == LM_STATUS_LINK_DOWN) || !pDevice->InitDone) + { + return ENETDOWN; + }*/ + + pPacket = (PLM_PACKET) + QQ_PopHead(&pDevice->TxPacketFreeQ.Container); + if (pPacket == 0) + return B_ERROR; + + pUmPacket = (struct B_UM_PACKET *) pPacket; + pUmPacket->data = chunk_pool_get(); + + memcpy(pUmPacket->data/*+pDevice->dataoffset*/,data,*numBytes); /* no guarantee data is contiguous, so we have to copy */ + pPacket->PacketSize = pUmPacket->size = *numBytes/*+pDevice->rxoffset*/; + + pPacket->u.Tx.FragCount = 1; + + tx_cleanup_thread(pUmDevice); + + b44_LM_SendPacket(pDevice, pPacket); + + return B_OK; +} + +/* -------- Broadcom MM hooks ----------- */ + +LM_STATUS b44_MM_ReadConfig16(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT16 *pValue16) { + if (pci == NULL) + get_module(B_PCI_MODULE_NAME,(module_info **)&pci); + + *pValue16 = (LM_UINT16)pci->read_pci_config(((struct be_b44_dev *)(pDevice))->pci_data.bus,((struct be_b44_dev *)(pDevice))->pci_data.device,((struct be_b44_dev *)(pDevice))->pci_data.function,(uchar)Offset,sizeof(LM_UINT16)); + return LM_STATUS_SUCCESS; +} + +LM_STATUS b44_MM_WriteConfig16(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT16 Value16) { + if (pci == NULL) + get_module(B_PCI_MODULE_NAME,(module_info **)&pci); + + pci->write_pci_config(((struct be_b44_dev *)(pDevice))->pci_data.bus,((struct be_b44_dev *)(pDevice))->pci_data.device,((struct be_b44_dev *)(pDevice))->pci_data.function,(uchar)Offset,sizeof(LM_UINT16),(uint32)Value16); + return LM_STATUS_SUCCESS; +} + +LM_STATUS b44_MM_ReadConfig32(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT32 *pValue32) { + if (pci == NULL) + get_module(B_PCI_MODULE_NAME,(module_info **)&pci); + + *pValue32 = (LM_UINT32)pci->read_pci_config(((struct be_b44_dev *)(pDevice))->pci_data.bus,((struct be_b44_dev *)(pDevice))->pci_data.device,((struct be_b44_dev *)(pDevice))->pci_data.function,(uchar)Offset,sizeof(LM_UINT32)); + return LM_STATUS_SUCCESS; +} + +LM_STATUS b44_MM_WriteConfig32(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT32 Value32){ + if (pci == NULL) + get_module(B_PCI_MODULE_NAME,(module_info **)&pci); + + pci->write_pci_config(((struct be_b44_dev *)(pDevice))->pci_data.bus,((struct be_b44_dev *)(pDevice))->pci_data.device,((struct be_b44_dev *)(pDevice))->pci_data.function,(uchar)Offset,sizeof(LM_UINT32),(uint32)Value32); + return LM_STATUS_SUCCESS; +} + +LM_STATUS b44_MM_MapMemBase(PLM_DEVICE_BLOCK pDevice) { + struct be_b44_dev *pUmDevice = (struct be_b44_dev *)(pDevice); + size_t size = pUmDevice->pci_data.u.h0.base_register_sizes[0]; + + if (pci == NULL) + get_module(B_PCI_MODULE_NAME,(module_info **)&pci); + + size = ROUNDUP(size,B_PAGE_SIZE); + pUmDevice->mem_base = map_physical_memory("bcm440x_regs",(void *)(pUmDevice->pci_data.u.h0.base_registers[0]),size,B_ANY_KERNEL_BLOCK_ADDRESS,B_READ_AREA | B_WRITE_AREA,(void **)(&pDevice->pMappedMemBase)); + + return LM_STATUS_SUCCESS; +} + +/*LM_STATUS b44_MM_MapIoBase(PLM_DEVICE_BLOCK pDevice) { + if (pci == NULL) + get_module(B_PCI_MODULE_NAME,(module_info **)&pci); + + pDevice->pMappedMemBase = pci->ram_address(((struct be_b44_dev *)(pDevice))->pci_data.memory_base); + return LM_STATUS_SUCCESS; +}*/ + +LM_STATUS b44_MM_IndicateRxPackets(PLM_DEVICE_BLOCK pDevice) { + struct be_b44_dev *dev = (struct be_b44_dev *)pDevice; + PLM_PACKET pPacket; + + while (1) { + pPacket = (PLM_PACKET) + QQ_PopHead(&pDevice->RxPacketReceivedQ.Container); + if (pPacket == 0) + break; + + acquire_spinlock(&dev->lock); + release_sem_etc(dev->packet_release_sem,1,B_DO_NOT_RESCHEDULE); + release_spinlock(&dev->lock); + QQ_PushTail(&dev->RxPacketReadQ.Container, pPacket); + } + + return LM_STATUS_SUCCESS; +} + +LM_STATUS b44_MM_IndicateTxPackets(PLM_DEVICE_BLOCK pDevice) { + return LM_STATUS_SUCCESS; +} + +int32 tx_cleanup_thread(void *us) { + PLM_PACKET pPacket; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK)(us); + struct be_b44_dev *pUmDevice = (struct be_b44_dev *)(us); + struct B_UM_PACKET *pUmPacket; + cpu_status cpu; + + while (1) { + cpu = disable_interrupts(); + acquire_spinlock(&pUmDevice->lock); + + pPacket = (PLM_PACKET) + QQ_PopHead(&pDevice->TxPacketXmittedQ.Container); + + release_spinlock(&pUmDevice->lock); + restore_interrupts(cpu); + if (pPacket == 0) + break; + pUmPacket = (struct B_UM_PACKET *)(pPacket); + chunk_pool_put(pUmPacket->data); + pUmPacket->data = NULL; + + cpu = disable_interrupts(); + acquire_spinlock(&pUmDevice->lock); + QQ_PushTail(&pDevice->TxPacketFreeQ.Container, pPacket); + release_spinlock(&pUmDevice->lock); + restore_interrupts(cpu); + } + return LM_STATUS_SUCCESS; +} + +/*LM_STATUS b44_MM_StartTxDma(PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket); +LM_STATUS b44_MM_CompleteTxDma(PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket);*/ + +LM_STATUS b44_MM_AllocateMemory(PLM_DEVICE_BLOCK pDevice, LM_UINT32 BlockSize, + PLM_VOID *pMemoryBlockVirt) { + struct be_b44_dev *dev = (struct be_b44_dev *)(pDevice); + + if (dev->mem_list_num == 16) + return LM_STATUS_FAILURE; + + *pMemoryBlockVirt = dev->mem_list[(dev->mem_list_num)++] = (void *)malloc(BlockSize); + return LM_STATUS_SUCCESS; + } + +LM_STATUS b44_MM_AllocateSharedMemory(PLM_DEVICE_BLOCK pDevice, LM_UINT32 BlockSize, + PLM_VOID *pMemoryBlockVirt, PLM_PHYSICAL_ADDRESS pMemoryBlockPhy) { + struct be_b44_dev *dev; + void *pvirt = NULL; + area_id area_desc; + physical_entry entry; + + dev = (struct be_b44_dev *)(pDevice); + area_desc = dev->lockmem_list[dev->lockmem_list_num++] = create_area("broadcom_shared_mem",&pvirt,B_ANY_KERNEL_ADDRESS,ROUND_UP_TO_PAGE(BlockSize),/*B_CONTIGUOUS | B_FULL_LOCK*/ B_LOMEM,B_READ_AREA | B_WRITE_AREA); + + if (pvirt == NULL) + return LM_STATUS_FAILURE; + + memset(pvirt, 0, BlockSize); + *pMemoryBlockVirt = (PLM_VOID) pvirt; + + get_memory_map(pvirt,BlockSize,&entry,1); + *pMemoryBlockPhy = (LM_PHYSICAL_ADDRESS) entry.address; + + return LM_STATUS_SUCCESS; +} + +LM_STATUS b44_MM_GetConfig(PLM_DEVICE_BLOCK pDevice){ + pDevice->DisableAutoNeg = FALSE; + pDevice->RequestedLineSpeed = LM_LINE_SPEED_AUTO; + pDevice->RequestedDuplexMode = LM_DUPLEX_MODE_FULL; + pDevice->FlowControlCap |= LM_FLOW_CONTROL_AUTO_PAUSE; + //pDevice->TxPacketDescCnt = tx_pkt_desc_cnt[DEFAULT_TX_PACKET_DESC_COUNT]; + pDevice->RxPacketDescCnt = DEFAULT_RX_PACKET_DESC_COUNT; + + return LM_STATUS_SUCCESS; +} + +LM_STATUS b44_MM_IndicateStatus(PLM_DEVICE_BLOCK pDevice, LM_STATUS Status) { + return LM_STATUS_SUCCESS; +} + +LM_STATUS +b44_MM_InitializeUmPackets(PLM_DEVICE_BLOCK pDevice) +{ + int i; + struct B_UM_PACKET *pUmPacket; + PLM_PACKET pPacket; + + for (i = 0; i < pDevice->RxPacketDescCnt; i++) { + pPacket = QQ_PopHead(&pDevice->RxPacketFreeQ.Container); + pUmPacket = (struct B_UM_PACKET *) pPacket; + pUmPacket->data = chunk_pool_get(); + /*if (pUmPacket->data == 0) { + QQ_PushTail(&pUmDevice->rx_out_of_buf_q.Container, pPacket); + continue; + }*/ + pPacket->u.Rx.pRxBufferVirt = pUmPacket->data; + QQ_PushTail(&pDevice->RxPacketFreeQ.Container, pPacket); + } + + return LM_STATUS_SUCCESS; +} + +LM_STATUS b44_MM_FreeRxBuffer(PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket) { + struct B_UM_PACKET *pUmPacket; + pUmPacket = (struct B_UM_PACKET *) pPacket; + chunk_pool_put(pUmPacket->data); + pUmPacket->data = NULL; + return LM_STATUS_SUCCESS; +} diff --git a/src/add-ons/kernel/drivers/network/bcm440x/mempool.c b/src/add-ons/kernel/drivers/network/bcm440x/mempool.c new file mode 100644 index 0000000000..02d571bb6b --- /dev/null +++ b/src/add-ons/kernel/drivers/network/bcm440x/mempool.c @@ -0,0 +1,104 @@ +/* Intel PRO/1000 Family Driver + * Copyright (C) 2004 Marcus Overhagen . All rights reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies, and that both the + * copyright notice and this permission notice appear in supporting documentation. + * + * Marcus Overhagen makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * + * MARCUS OVERHAGEN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL MARCUS + * OVERHAGEN BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include "mempool.h" + +spinlock chunk_pool_lock = 0; +void *chunk_pool = 0; +void *chunk_head = 0; + +void * +area_malloc(size_t size) +{ + void *p; + size = ROUNDUP(size, B_PAGE_SIZE); + + if (create_area("area_malloc", &p, B_ANY_KERNEL_ADDRESS, size, B_FULL_LOCK, 0) < 0) + return 0; + return p; +} + +void +area_free(void *p) +{ + delete_area(area_for(p)); +} + +int +mempool_init(int count) +{ + int chunk_size = 2048; + + int chunk_alloc_size = chunk_size * (count + 1); + + char *chunk_base; + + int i; + + chunk_pool = area_malloc(chunk_alloc_size); + if (!chunk_pool) { + return -1; + } + + chunk_base = (char *)ROUNDUP((unsigned long)chunk_pool, 2048); + + for (i = 0; i < count; i++) { + chunk_pool_put(chunk_base + i * chunk_size); + } + + return 0; +} + +void +mempool_exit(void) +{ + area_free(chunk_pool); +} + +void * +chunk_pool_get(void) +{ + void *p; + cpu_status s; + s = disable_interrupts(); + acquire_spinlock(&chunk_pool_lock); + + p = chunk_head; + if (p) + chunk_head = *(void **)p; + + release_spinlock(&chunk_pool_lock); + restore_interrupts(s); + return p; +} + +void +chunk_pool_put(void *p) +{ + cpu_status s; + s = disable_interrupts(); + acquire_spinlock(&chunk_pool_lock); + + *(void **)p = chunk_head; + chunk_head = p; + + release_spinlock(&chunk_pool_lock); + restore_interrupts(s); +} diff --git a/src/add-ons/kernel/drivers/network/bcm440x/mempool.h b/src/add-ons/kernel/drivers/network/bcm440x/mempool.h new file mode 100644 index 0000000000..c3f78e3f7e --- /dev/null +++ b/src/add-ons/kernel/drivers/network/bcm440x/mempool.h @@ -0,0 +1,35 @@ +/* Intel PRO/1000 Family Driver + * Copyright (C) 2004 Marcus Overhagen . All rights reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies, and that both the + * copyright notice and this permission notice appear in supporting documentation. + * + * Marcus Overhagen makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * + * MARCUS OVERHAGEN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL MARCUS + * OVERHAGEN BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __MEMPOOL_H +#define __MEMPOOL_H + +int mempool_init(int count); +void mempool_exit(void); + +void *chunk_pool_get(void); +void chunk_pool_put(void *p); + +void * area_malloc(size_t size); +void area_free(void *p); + +#ifndef ROUNDUP + #define ROUNDUP(size, blocksize) (((size) + (blocksize) - 1) & ~((blocksize) - 1)) +#endif + +#endif