diff options
Diffstat (limited to 'cfe/cfe/dev')
-rw-r--r-- | cfe/cfe/dev/SIBYTE_PRIVATE_FILES | 6 | ||||
-rw-r--r-- | cfe/cfe/dev/bcm5700.h | 1007 | ||||
-rw-r--r-- | cfe/cfe/dev/bcm5821.h | 278 | ||||
-rw-r--r-- | cfe/cfe/dev/dc21143.h | 588 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_atapi.c | 222 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_bcm1250.c | 275 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_bcm5700.c | 2557 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_bcm5821.c | 1592 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_dc21143.c | 2388 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_dp83815.c | 2290 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_ds17887clock.c | 422 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_flash.c | 1367 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_ht7520.c | 167 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_ide_common.c | 1249 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_ide_pci.c | 358 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_newflash.c | 1428 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_ns16550.c | 255 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_ns16550_pci.c | 114 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_null.c | 142 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_promice.c | 402 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_sp1011.c | 151 | ||||
-rw-r--r-- | cfe/cfe/dev/dev_tulip.c | 2985 | ||||
-rw-r--r-- | cfe/cfe/dev/dp83815.h | 559 | ||||
-rw-r--r-- | cfe/cfe/dev/ns16550.h | 143 | ||||
-rw-r--r-- | cfe/cfe/dev/pci_devs.c | 79 |
25 files changed, 21024 insertions, 0 deletions
diff --git a/cfe/cfe/dev/SIBYTE_PRIVATE_FILES b/cfe/cfe/dev/SIBYTE_PRIVATE_FILES new file mode 100644 index 0000000..eae0411 --- /dev/null +++ b/cfe/cfe/dev/SIBYTE_PRIVATE_FILES @@ -0,0 +1,6 @@ +dev_ptflash.c +bcm5700.h +dev_bcm5700.c +bcm5821.h +dev_bcm5821.c + diff --git a/cfe/cfe/dev/bcm5700.h b/cfe/cfe/dev/bcm5700.h new file mode 100644 index 0000000..76aa3ea --- /dev/null +++ b/cfe/cfe/dev/bcm5700.h @@ -0,0 +1,1007 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * BCM5700 (10/100/1K EthernetMAC) registers File: bcm5700.c + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + +#ifndef _BCM5700_H_ +#define _BCM5700_H_ + +/* + * Register and bit definitions for the Broadcom BCM570X family (aka + * Tigon3) of Integrated MACs. + * + * References: + * + * Host Programmer Interface Specification for the BCM570X Family + * of Highly-Integrated Media Access Controllers, 570X-PG106-R. + * Broadcom Corp., 16215 Alton Parkway, Irvine CA, 09/27/02 + * + * Simplified Programmer Interface Specification for the BCM570X Family + * of Highly Integrated Media Access Controllers, 570X-PG202-R. + * Broadcom Corp., 16215 Alton Parkway, Irvine CA, 10/14/02 + */ + +#define K_PCI_VENDOR_BROADCOM 0x14e4 +#define K_PCI_ID_BCM5700 0x1644 +#define K_PCI_ID_BCM5701 0x1645 +#define K_PCI_ID_BCM5702 0x16A6 +#define K_PCI_ID_BCM5703 0x16A7 +#define K_PCI_ID_BCM5704C 0x1648 +#define K_PCI_ID_BCM5704S 0x16A8 +#define K_PCI_ID_BCM5705 0x1653 + +#define _DD_MAKEMASK1(n) (1 << (n)) +#define _DD_MAKEMASK(v,n) ((((1)<<(v))-1) << (n)) +#define _DD_MAKEVALUE(v,n) ((v) << (n)) +#define _DD_GETVALUE(v,n,m) (((v) & (m)) >> (n)) + + +/* Registers 0x0000 - 0x00FF are PCI Configuration registers (shadow) */ + +#define PCI_PCIX_CMD_REG 0x40 +#define PCI_PCIX_STAT_REG 0x44 + +#define PCI_PMC_REG 0x48 +#define PCI_PMCSR_REG 0x4C + +#define R_MISC_HOST_CTRL 0x0068 +#define R_DMA_RW_CTRL 0x006C +#define R_PCI_STATE 0x0070 +#define R_PCI_CLK_CTRL 0x0074 +#define R_REG_BASE_ADDR 0x0078 +#define R_MEMWIN_BASE_ADDR 0x007C +#define R_REG_DATA 0x0080 +#define R_MEMWIN_DATA 0x0084 +/* For 5700 and 5701, 0x0088-0x0090 shadow 0x6800-0x6808 */ +#define R_INT_MBOX0 0x0B0 /* 8 bytes, shadows R_INT_MBOX(0) */ + +/* Registers 0x0200 - 0x03FF are High Priority Mailbox registers */ + +#define R_INT_MBOX(n) (0x0200 + 8*(n)) /* 0 <= n < 4 */ +#define R_GEN_MBOX(n) (0x0220 + 8*(n)-8) /* 1 <= n <= 8 */ +#define R_RELOAD_STATS_MBOX 0x0260 +#define R_RCV_BD_STD_PI 0x0268 +#define R_RCV_BD_JUMBO_PI 0x0270 +#define R_RCV_BD_MINI_PI 0x0278 +#define R_RCV_BD_RTN_CI(n) (0x0280 + 8*(n)-8) /* 1 <= n <= 16 */ +#define R_SND_BD_PI(n) (0x0300 + 8*(n)-8) /* 1 <= n <= 16 */ +#define R_SND_BD_NIC_PI(n) (0x0380 + 8*(n)-8) /* 1 <= n <= 16 */ + +/* Registers 0x0400 - 0x0BFF are MAC Control registers */ + +#define R_MAC_MODE 0x0400 +#define R_MAC_STATUS 0x0404 +#define R_MAC_EVENT_ENB 0x0408 +#define R_MAC_LED_CTRL 0x040C + +#define R_MAC_ADDR1_HIGH 0x0410 +#define R_MAC_ADDR1_LOW 0x0414 +#define R_MAC_ADDR2_HIGH 0x0418 +#define R_MAC_ADDR2_LOW 0x041C +#define R_MAC_ADDR3_HIGH 0x0420 +#define R_MAC_ADDR3_LOW 0x0424 +#define R_MAC_ADDR4_HIGH 0x0428 +#define R_MAC_ADDR4_LOW 0x042C + +#define R_WOL_PATTERN_PTR 0x0430 +#define R_WOL_PATTERN_CFG 0x0434 +#define R_TX_BACKOFF 0x0438 +#define R_RX_MTU 0x043C +#define R_PCS_TEST 0x0440 +#define R_TX_AUTONEG 0x0444 +#define R_RX_AUTONEG 0x0448 + +#define R_MI_COMM 0x044C +#define R_MI_STATUS 0x0450 +#define R_MI_MODE 0x0454 + +#define R_AUTOPOLL_STAT 0x0458 +#define R_TX_MODE 0x045C +#define R_TX_STAT 0x0460 +#define R_TX_LENS 0x0464 +#define R_RX_MODE 0x0468 +#define R_RX_STAT 0x046C + +#define R_MAC_HASH(n) (0x0470 + 4*(n)) /* 0 <= n < 4 */ + +#define R_RX_BD_RULES_CTRL(n) (0x0480 + 8*(n)) /* 0 <= n < 16 */ +#define R_RX_BD_RULES_MASK(n) (0x0484 + 8*(n)) + +#define R_RX_RULES_CFG 0x0500 +#define R_RX_FRAMES_LOW 0x0504 +#define R_MAC_HASH_EXT(n) (0x0520 + 4*(n)) /* 0 <= n < 4 */ +#define R_MAC_ADDR_EXT(n) (0x0530 + 8*(n)) /* 0 <= n < 12 */ +#define R_SERDES_CTRL 0x0590 +#define R_SERDES_STAT 0x0594 +#define R_RX_STATS_MEM 0x0800 +#define R_TX_STATS_MEM 0x0880 + + +/* + * Note on Buffer Descriptor (BD) Ring indices: + * Numbering follows Broadcom literature, which uses indices 1-16. + * PI = producer index, CI = consumer index. + */ + +/* Registers 0x0C00 - 0x0FFF are Send Data Initiator Control registers */ + +#define R_SND_DATA_MODE 0x0C00 +#define R_SND_DATA_STAT 0x0C04 +#define R_SND_DATA_STATS_CTRL 0x0C08 +#define R_SND_DATA_STATS_ENB 0x0C0C +#define R_SND_DATA_STATS_INCR 0x0C10 + +#define R_STATS_CTR_SND_COS(n) (0x0C80 + 4*(n)-4) /* 1 <= n <= 16 */ +#define R_STATS_DMA_RDQ_FULL 0x0CC0 +#define R_STATS_DMA_HP_RDQ_FULL 0x0CC4 +#define R_STATS_SDCQ_FULL 0x0CC8 +#define R_STATS_NIC_SET_SND_PI 0x0CCC +#define R_STATS_STAT_UPDATED 0x0CD0 +#define R_STATS_IRQS 0x0CD4 +#define R_STATS_IRQS_AVOIDED 0x0CD8 +#define R_STATS_SND_THRSH_HIT 0x0CDC + +/* Registers 0x1000 - 0x13FF are Send Data Completion Control registers */ + +#define R_SND_DATA_COMP_MODE 0x1000 + +/* Registers 0x1400 - 0x17FF are Send BD Ring Selection Control registers */ + +#define R_SND_BD_SEL_MODE 0x1400 +#define R_SND_BD_SEL_STAT 0x1404 +#define R_SND_BD_DIAG 0x1408 +#define R_SND_BD_SEL_CI(n) (0x1440 + 4*(n)-4) /* 1 <= n <= 16 */ + +/* Registers 0x1800 - 0x1BFF are Send BD Initiator Control registers */ + +#define R_SND_BD_INIT_MODE 0x1800 +#define R_SND_BD_INIT_STAT 0x1804 +#define R_SND_BD_INIT_PI(n) (0x1808 + 4*(n)-4) /* 1 <= n <= 16 */ + +/* Registers 0x1C00 - 0x1FFF are Send BD Completion Control registers */ + +#define R_SND_BD_COMP_MODE 0x1C00 + +/* Registers 0x2000 - 0x23FF are Receive List Placement Control registers */ + +#define R_RCV_LIST_MODE 0x2000 +#define R_RCV_LIST_STAT 0x2004 +#define R_RCV_LIST_LOCK 0x2008 +#define R_RCV_NONEMPTY_BITS 0x200C +#define R_RCV_LIST_CFG 0x2010 +#define R_RCV_LIST_STATS_CTRL 0x2014 +#define R_RCV_LIST_STATS_ENB 0x2018 +#define R_RCV_LIST_STATS_INC 0x201C +#define R_RCV_LIST_HEAD(n) (0x2100 + 16*(n)-16) /* 1 <= n <= 16 */ +#define R_RCV_LIST_TAIL(n) (0x2104 + 16*(n)-16) +#define R_RCV_LIST_CNT(n) (0x2108 + 16*(n)-16) +#define R_STATS_CTR_RCV_COS(n) (0x2200 + 4*(n)-4) /* 1 <= n <= 16 */ +#define R_STATS_FILT_DROP 0x2240 +#define R_STATS_DMA_WRQ_FULL 0x2244 +#define R_STATS_DMA_HP_WRQ_FULL 0x2248 +#define R_STATS_NO_RCV_BDS 0x224C +#define R_STATS_IN_DISCARDS 0x2250 +#define R_STATS_IN_ERRORS 0x2254 +#define R_STATS_RCV_THRSH_HIT 0x2258 + +/* Registers 0x2400 - 0x27FF are Receive Data/BD Initiator Control registers */ + +#define R_RCV_DATA_INIT_MODE 0x2400 +#define R_RCV_DATA_INIT_STAT 0x2404 +#define R_JUMBO_RCV_BD_RCB 0x2440 /* 16 bytes */ +#define R_STD_RCV_BD_RCB 0x2450 /* 16 bytes */ +#define R_MINI_RCV_BD_RCB 0x2460 /* 16 bytes */ +#define R_RCV_BD_INIT_JUMBO_CI 0x2470 +#define R_RCV_BD_INIT_STD_CI 0x2474 +#define R_RCV_BD_INIT_MINI_CI 0x2478 +#define R_RCV_BD_INIT_RTN_PI(n) (0x2480 + 4*(n)-4) /* 1 <= n <= 16 */ +#define R_RCV_BD_INIT_DIAG 0x24C0 + +/* Registers 0x2800 - 0x2BFF are Receive Data Completion Control registers */ + +#define R_RCV_COMP_MODE 0x2800 + +/* Registers 0x2C00 - 0x2FFF are Receive BD Initiator Control registers */ + +#define R_RCV_BD_INIT_MODE 0x2C00 +#define R_RCV_BD_INIT_STAT 0x2C04 +#define R_RCV_BD_INIT_JUMBO_PI 0x2C08 +#define R_RCV_BD_INIT_STD_PI 0x2C0C +#define R_RCV_BD_INIT_MINI_PI 0x2C10 +#define R_MINI_RCV_BD_THRESH 0x2C14 +#define R_STD_RCV_BD_THRESH 0x2C18 +#define R_JUMBO_RCV_BD_THRESH 0x2C1C + +/* Registers 0x3000 - 0x33FF are Receive BD Completion Control registers */ + +#define R_RCV_BD_COMP_MODE 0x3000 +#define R_RCV_BD_COMP_STAT 0x3004 +#define R_NIC_JUMBO_RCV_BD_PI 0x3008 +#define R_NIC_STD_RCV_BD_PI 0x300C +#define R_NIC_MINI_RCV_BD_PI 0x3010 + +/* Registers 0x3400 - 0x37FF are Receive List Selector Control registers */ + +#define R_RCV_LIST_SEL_MODE 0x3400 +#define R_RCV_LIST_SEL_STATUS 0x3404 + +/* Registers 0x3800 - 0x3BFF are Mbuf Cluster Free registers */ + +#define R_MBUF_FREE_MODE 0x3800 +#define R_MBUF_FREE_STATUS 0x3804 + +/* Registers 0x3C00 - 0x3FFF are Host Coalescing Control registers */ + +#define R_HOST_COAL_MODE 0x3C00 +#define R_HOST_COAL_STAT 0x3C04 +#define R_RCV_COAL_TICKS 0x3C08 +#define R_SND_COAL_TICKS 0x3C0C +#define R_RCV_COAL_MAX_CNT 0x3C10 +#define R_SND_COAL_MAX_CNT 0x3C14 +#define R_RCV_COAL_INT_TICKS 0x3C18 +#define R_SND_COAL_INT_TICKS 0x3C1C +#define R_RCV_COAL_INT_CNT 0x3C20 +#define R_SND_COAL_INT_CNT 0x3C24 +#define R_STATS_TICKS 0x3C28 +#define R_STATS_HOST_ADDR 0x3C30 /* 8 bytes */ +#define R_STATUS_HOST_ADDR 0x3C38 /* 8 bytes */ +#define R_STATS_BASE_ADDR 0x3C40 +#define R_STATUS_BASE_ADDR 0x3C44 +#define R_FLOW_ATTN 0x3C48 +#define R_NIC_JUMBO_RCV_BD_CI 0x3C50 +#define R_NIC_STD_RCV_BD_CI 0x3C54 +#define R_NIC_MINI_RCV_BD_CI 0x3C58 +#define R_NIC_RTN_PI(n) (0x3C80 + 4*(n)-4) /* 1 <= n <= 16 */ +#define R_NIC_SND_BD_CD(n) (0x3CC0 + 4*(n)-4) /* 1 <= n <= 16 */ + +/* Registers 0x4000 - 0x43FF are Memory Arbiter registers */ + +#define R_MEM_MODE 0x4000 +#define R_MEM_STATUS 0x4004 +#define R_MEM_TRAP_LOW 0x4008 +#define R_MEM_TRAP_HIGH 0x400C + + +/* Registers 0x4400 - 0x47FF are Buffer Manager Control registers */ + +#define R_BMGR_MODE 0x4400 +#define R_BMGR_STATUS 0x4404 +#define R_BMGR_MBUF_BASE 0x4408 +#define R_BMGR_MBUF_LEN 0x440C +#define R_BMGR_MBUF_DMA_LOW 0x4410 +#define R_BMGR_MBUF_RX_LOW 0x4414 +#define R_BMGR_MBUF_HIGH 0x4418 + +#define R_BMGR_DMA_BASE 0x442C +#define R_BMGR_DMA_LEN 0x4430 +#define R_BMGR_DMA_LOW 0x4434 +#define R_BMGR_DMA_HIGH 0x4438 + +#define R_BMGR_DIAG1 0x444C +#define R_BMGR_DIAG2 0x4450 +#define R_BMGR_DIAG3 0x4454 +#define R_BMGR_RCV_FLOW_THRESH 0x4458 + +/* Registers 0x4800 - 0x4BFF are Read DMA Control registers */ + +#define R_RD_DMA_MODE 0x4800 +#define R_RD_DMA_STAT 0x4804 + +/* Registers 0x4C00 - 0x4FFF are Write DMA Control registers */ + +#define R_WR_DMA_MODE 0x4C00 +#define R_WR_DMA_STAT 0x4C04 + +/* Registers 0x5000 - 0x53FF are RX RISC registers */ + +#define R_RX_RISC_MODE 0x5000 +#define R_RX_RISC_STATE 0x5004 +#define R_RX_RISC_PC 0x501C + +/* Registers 0x5400 - 0x57FF are TX RISC registers */ + +#define R_TX_RISC_MODE 0x5400 +#define R_TX_RISC_STATE 0x5404 +#define R_TX_RISC_PC 0x541C + +/* Registers 0x5800 - 0x5BFF are Low Priority Mailbox registers (8 bytes) */ + +#define R_LP_INT_MBOX(n) (0x5800 + 8*(n)) /* 0 <= n < 4 */ +#define R_LP_GEN_MBOX(n) (0x5820 + 8*(n)-8) /* 1 <= n <= 8 */ +#define R_LP_RELOAD_STATS_MBOX 0x5860 +#define R_LP_RCV_BD_STD_PI 0x5868 +#define R_LP_RCV_BD_JUMBO_PI 0x5870 +#define R_LP_RCV_BD_MINI_PI 0x5878 +#define R_LP_RCV_BD_RTN_CI(n) (0x5880 + 8*(n)-8) /* 1 <= n <= 16 */ +#define R_LP_SND_BD_PI(n) (0x5900 + 8*(n)-8) /* 1 <= n <= 16 */ + +/* Registers 0x5C00 - 0x5C03 are Flow Through Queues */ + +#define R_FTQ_RESET 0x5C00 + +/* Registers 0x6000 - 0x63FF are Message Signaled Interrupt registers */ + +/* Registers 0x6400 - 0x67FF are DMA Completion registers */ + +#define R_DMA_COMP_MODE 0x6400 + +/* Registers 0x6800 - 0x68ff are General Control registers */ + +#define R_MODE_CTRL 0x6800 +#define R_MISC_CFG 0x6804 +#define R_MISC_LOCAL_CTRL 0x6808 +#define R_TIMER 0x680C +#define R_MEM_PWRUP 0x6030 /* 8 bytes */ +#define R_EEPROM_ADDR 0x6838 +#define R_EEPROM_DATA 0x683C +#define R_EEPROM_CTRL 0x6840 +#define R_MDI_CTRL 0x6844 +#define R_EEPROM_DELAY 0x6848 + +/* Registers 0x6C00 - 0x6CFF are ASF Support registers (NYI) */ + +/* Registers 0x7000 - 0x7024 are NVM Interface registers (NYI) */ + + +/* PCI Capability registers (should be moved to PCI headers) */ + +/* PCI-X Capability and Command Register (0x40) */ + +#define PCIX_CMD_DPREC_ENABLE 0x00010000 +#define PCIX_CMD_RLXORDER_ENABLE 0x00020000 +#define PCIX_CMD_RD_CNT_SHIFT 18 +#define PCIX_CMD_RD_CNT_MASK 0x000C0000 +#define PCIX_CMD_MAX_SPLIT_SHIFT 20 +#define PCIX_CMD_MAX_SPLIT_MASK 0x00700000 + +/* PCI-X Status Register (0x44) */ + + +/* Generic bit fields shared by most MODE and STATUS registers */ + +#define M_MODE_RESET _DD_MAKEMASK1(0) +#define M_MODE_ENABLE _DD_MAKEMASK1(1) +#define M_MODE_ATTNENABLE _DD_MAKEMASK1(2) + +#define M_STAT_ERROR _DD_MAKEMASK1(1) + +/* Generic bit fields shared by STATS_CTRL registers */ + +#define M_STATS_ENABLE _DD_MAKEMASK1(0) +#define M_STATS_FASTUPDATE _DD_MAKEMASK1(1) +#define M_STATS_CLEAR _DD_MAKEMASK1(2) +#define M_STATS_FLUSH _DD_MAKEMASK1(3) +#define M_STATS_ZERO _DD_MAKEMASK1(4) + +/* Private PCI Configuration registers (p 335) */ + +/* MHC: Miscellaneous Host Control Register (0x68) */ + +#define M_MHC_CLEARINTA _DD_MAKEMASK1(0) +#define M_MHC_MASKPCIINT _DD_MAKEMASK1(1) +#define M_MHC_ENBYTESWAP _DD_MAKEMASK1(2) +#define M_MHC_ENWORDSWAP _DD_MAKEMASK1(3) +#define M_MHC_ENPCISTATERW _DD_MAKEMASK1(4) +#define M_MHC_ENCLKCTRLRW _DD_MAKEMASK1(5) +#define M_MHC_ENREGWORDSWAP _DD_MAKEMASK1(6) +#define M_MHC_ENINDIRECT _DD_MAKEMASK1(7) +#define S_MHC_ASICREV 16 +#define M_MHC_ASICREV _DD_MAKEMASK(16,S_MHC_ASICREV) +#define G_MHC_ASICREV(x) _DD_GETVALUE(x,S_MHC_ASICREV,M_MHC_ASICREV) + +/* DMAC: DMA Read/Write Control Register (0x6c) */ + +#define S_DMAC_MINDMA 0 +#define M_DMAC_MINDMA _DD_MAKEMASK(8,S_DMAC_MINDMA) +#define V_DMAC_MINDMA(x) _DD_MAKEVALUE(x,S_DMAC_MINDMA) +#define G_DMAC_MINDMA(x) _DD_GETVALUE(x,S_DMAC_MINDMA,M_DMAC_MINDMA) + +#define M_DMAC_MEMRDMULT _DD_MAKEMASK1(22) /* 570{0,1} only */ +#define M_DMAC_BEALL _DD_MAKEMASK1(23) /* 570{0,1} only */ + +#define S_DMAC_RDCMD 24 /* 570{0,1} only */ +#define M_DMAC_RDCMD _DD_MAKEMASK(4,S_DMAC_RDCMD) +#define V_DMAC_RDCMD(x) _DD_MAKEVALUE(x,S_DMAC_RDCMD) +#define G_DMAC_RDCMD(x) _DD_GETVALUE(x,S_DMAC_RDCMD,M_DMAC_RDCMD) +#define K_PCI_MEMRD 0x6 + +#define S_DMAC_WRCMD 28 +#define M_DMAC_WRCMD _DD_MAKEMASK(4,S_DMAC_WRCMD) +#define V_DMAC_WRCMD(x) _DD_MAKEVALUE(x,S_DMAC_WRCMD) +#define G_DMAC_WRCMD(x) _DD_GETVALUE(x,S_DMAC_WRCMD,M_DMAC_WRCMD) +#define K_PCI_MEMWR 0x7 + +/* PCIS: PCI State Register (0x70) */ + +#define M_PCIS_RESET _DD_MAKEMASK1(0) +#define M_PCIS_INT _DD_MAKEMASK1(1) +#define M_PCIS_MODE _DD_MAKEMASK1(2) +#define M_PCIS_33MHZ _DD_MAKEMASK1(3) +#define M_PCIS_32BIT _DD_MAKEMASK1(4) +#define M_PCIS_ROMEN _DD_MAKEMASK1(5) +#define M_PCIS_ROMRETRY _DD_MAKEMASK1(6) +#define M_PCIS_FLATVIEW _DD_MAKEMASK1(8) +/* ... more ... */ + +/* PCI Clock Control Register (0x74) */ + +/* Register Base Address Register (0x78) */ + +/* Memory Window Base Address Register (0x7c) */ + + +/* High Priority Mailboxes (p 323) */ + + +/* Ethernet MAC Control registers (p 358) */ + +/* MACM: Ethernet MAC Mode Register (0x400) */ + +#define M_MACM_GLBRESET _DD_MAKEMASK1(0) +#define M_MACM_HALFDUPLEX _DD_MAKEMASK1(1) + +#define S_MACM_PORTMODE 2 +#define M_MACM_PORTMODE _DD_MAKEMASK(2,S_MACM_PORTMODE) +#define V_MACM_PORTMODE(x) _DD_MAKEVALUE(x,S_MACM_PORTMODE) +#define G_MACM_PORTMODE(x) _DD_GETVALUE(x,S_MACM_PORTMODE,M_MACM_PORTMODE) +#define K_MACM_PORTMODE_NONE 0x0 +#define K_MACM_PORTMODE_MII 0x1 +#define K_MACM_PORTMODE_GMII 0x2 +#define K_MACM_PORTMODE_TBI 0x3 + +#define M_MACM_LOOPBACK _DD_MAKEMASK1(4) +#define M_MACM_TAGGEDMAC _DD_MAKEMASK1(7) +#define M_MACM_TXBURST _DD_MAKEMASK1(8) +#define M_MACM_MAXDEFER _DD_MAKEMASK1(9) +#define M_MACM_LINKPOLARITY _DD_MAKEMASK1(10) +#define M_MACM_RXSTATSENB _DD_MAKEMASK1(11) +#define M_MACM_RXSTATSCLR _DD_MAKEMASK1(12) +#define M_MACM_RXSTATSFLUSH _DD_MAKEMASK1(13) +#define M_MACM_TXSTATSENB _DD_MAKEMASK1(14) +#define M_MACM_TXSTATSCLR _DD_MAKEMASK1(15) +#define M_MACM_TXSTATSFLUSH _DD_MAKEMASK1(16) +#define M_MACM_SENDCFGS _DD_MAKEMASK1(17) +#define M_MACM_MAGICPKT _DD_MAKEMASK1(18) +#define M_MACM_ACPI _DD_MAKEMASK1(19) +#define M_MACM_MIPENB _DD_MAKEMASK1(20) +#define M_MACM_TDEENB _DD_MAKEMASK1(21) +#define M_MACM_RDEENB _DD_MAKEMASK1(22) +#define M_MACM_FHDEENB _DD_MAKEMASK1(23) + +/* MACSTAT: Ethernet MAC Status Register (0x404) */ +/* MACEVNT: Ethernet MAC Event Enable Register (0x408) */ + +/* Status Register only */ +#define M_MACSTAT_PCSSYNC _DD_MAKEMASK1(0) +#define M_MACSTAT_SIGDET _DD_MAKEMASK1(1) +#define M_MACSTAT_RCVCFG _DD_MAKEMASK1(2) +#define M_MACSTAT_CFGCHNG _DD_MAKEMASK1(3) +#define M_MACSTAT_SYNCCHNG _DD_MAKEMASK1(4) +/* Status and Enable Registers */ +#define M_EVT_PORTERR _DD_MAKEMASK1(10) +#define M_EVT_LINKCHNG _DD_MAKEMASK1(12) +#define M_EVT_MICOMPLETE _DD_MAKEMASK1(22) +#define M_EVT_MIINT _DD_MAKEMASK1(23) +#define M_EVT_APERR _DD_MAKEMASK1(24) +#define M_EVT_ODIERR _DD_MAKEMASK1(25) +#define M_EVT_RXSTATOVRUN _DD_MAKEMASK1(26) +#define M_EVT_TXSTATOVRUN _DD_MAKEMASK1(27) + +/* MICOMM: MI Communication Register (0x44c) */ + +#define S_MICOMM_DATA 0 +#define M_MICOMM_DATA _DD_MAKEMASK(16,S_MICOMM_DATA) +#define V_MICOMM_DATA(x) _DD_MAKEVALUE(x,S_MICOMM_DATA) +#define G_MICOMM_DATA(x) _DD_GETVALUE(x,S_MICOMM_DATA,M_MICOMM_DATA) + +#define S_MICOMM_REG 16 +#define M_MICOMM_REG _DD_MAKEMASK(5,S_MICOMM_REG) +#define V_MICOMM_REG(x) _DD_MAKEVALUE(x,S_MICOMM_REG) +#define G_MICOMM_REG(x) _DD_GETVALUE(x,S_MICOMM_REG,M_MICOMM_REG) + +#define S_MICOMM_PHY 21 +#define M_MICOMM_PHY _DD_MAKEMASK(5,S_MICOMM_PHY) +#define V_MICOMM_PHY(x) _DD_MAKEVALUE(x,S_MICOMM_PHY) +#define G_MICOMM_PHY(x) _DD_GETVALUE(x,S_MICOMM_PHY,M_MICOMM_PHY) + +#define S_MICOMM_CMD 26 +#define M_MICOMM_CMD _DD_MAKEMASK(2,S_MICOMM_CMD) +#define V_MICOMM_CMD(x) _DD_MAKEVALUE(x,S_MICOMM_CMD) +#define G_MICOMM_CMD(x) _DD_GETVALUE(x,S_MICOMM_CMD,M_MICOMM_CMD) +#define K_MICOMM_CMD_WR 0x1 +#define K_MICOMM_CMD_RD 0x2 +#define V_MICOMM_CMD_WR V_MICOMM_CMD(K_MICOMM_CMD_WR) +#define V_MICOMM_CMD_RD V_MICOMM_CMD(K_MICOMM_CMD_RD) + +#define M_MICOMM_RDFAIL _DD_MAKEMASK1(28) +#define M_MICOMM_BUSY _DD_MAKEMASK1(29) + +/* MISTAT: MI Status Register (0x450) */ + +#define M_MISTAT_LINKED _DD_MAKEMASK1(0) +#define M_MISTAT_10MBPS _DD_MAKEMASK1(1) + +/* MIMODE: MI Mode Register (0x454) */ + +#define M_MIMODE_SHORTPREAMBLE _DD_MAKEMASK1(1) +#define M_MIMODE_POLLING _DD_MAKEMASK1(4) + +#define S_MIMODE_CLKCNT 16 +#define M_MIMODE_CLKCNT _DD_MAKEMASK(5,S_MIMODE_CLKCNT) +#define V_MIMODE_CLKCNT(x) _DD_MAKEVALUE(x,S_MIMODE_CLKCNT) +#define G_MIMODE_CLKCNT(x) _DD_GETVALUE(x,S_MIMODE_CLKCNT,M_MIMODE_CLKCNT) + +/* TXLEN: Transmit MAC Lengths Register (0x464) */ + +#define S_TXLEN_SLOT 0 +#define M_TXLEN_SLOT _DD_MAKEMASK(8,S_TXLEN_SLOT) +#define V_TXLEN_SLOT(x) _DD_MAKEVALUE(x,S_TXLEN_SLOT) +#define G_TXLEN_SLOT(x) _DD_GETVALUE(x,S_TXLEN_SLOT,M_TXLEN_SLOT) + +#define S_TXLEN_IPG 8 +#define M_TXLEN_IPG _DD_MAKEMASK(4,S_TXLEN_IPG) +#define V_TXLEN_IPG(x) _DD_MAKEVALUE(x,S_TXLEN_IPG) +#define G_TXLEN_IPG(x) _DD_GETVALUE(x,S_TXLEN_IPG,M_TXLEN_IPG) + +#define S_TXLEN_IPGCRS 12 +#define M_TXLEN_IPGCRS _DD_MAKEMASK(2,S_TXLEN_IPGCRS) +#define V_TXLEN_IPGCRS(x) _DD_MAKEVALUE(x,S_TXLEN_IPGCRS) +#define G_TXLEN_IPGCRS(x) _DD_GETVALUE(x,S_TXLEN_IPGCRS,M_TXLEN_IPGCRS) + +/* RULESCFG: Receive Rules Configuration Register (0x500) */ + +#define S_RULESCFG_DEFAULT 3 +#define M_RULESCFG_DEFAULT _DD_MAKEMASK(5,S_RULESCFG_DEFAULT) +#define V_RULESCFG_DEFAULT(x) _DD_MAKEVALUE(x,S_RULESCFG_DEFAULT) +#define G_RULESCFG_DEFAULT(x) _DD_GETVALUE(x,S_RULESCFG_DEFAULT,M_RULESCFG_DEFAULT) + + +/* Send Data Initiator Control Registers (p 383) */ +/* Send BD Ring Selector Control Registers (p 387) */ +/* Send BD Initiator Control Registers (p 389) */ + + +/* Receive List Placement Control Registers (p 392) */ + +/* LISTCFG: Receive List Placement Configuration Register (0x2010) */ + +#define S_LISTCFG_GROUP 0 +#define M_LISTCFG_GROUP _DD_MAKEMASK(3,S_LISTCFG_GROUP) +#define V_LISTCFG_GROUP(x) _DD_MAKEVALUE(x,S_LISTCFG_GROUP) +#define G_LISTCFG_GROUP(x) _DD_GETVALUE(x,S_LISTCFG_GROUP,M_LISTCFG_GROUP) + +#define S_LISTCFG_ACTIVE 3 +#define M_LISTCFG_ACTIVE _DD_MAKEMASK(5,S_LISTCFG_ACTIVE) +#define V_LISTCFG_ACTIVE(x) _DD_MAKEVALUE(x,S_LISTCFG_ACTIVE) +#define G_LISTCFG_ACTIVE(x) _DD_GETVALUE(x,S_LISTCFG_ACTIVE,M_LISTCFG_ACTIVE) + +#define S_LISTCFG_BAD 8 +#define M_LISTCFG_BAD _DD_MAKEMASK(5,S_LISTCFG_BAD) +#define V_LISTCFG_BAD(x) _DD_MAKEVALUE(x,S_LISTCFG_BAD) +#define G_LISTCFG_BAD(x) _DD_GETVALUE(x,S_LISTCFG_BAD,M_LISTCFG_BAD) + +#define S_LISTCFG_DEFAULT 13 +#define M_LISTCFG_DEFAULT _DD_MAKEMASK(2,S_LISTCFG_DEFAULT) +#define V_LISTCFG_DEFAULT(x) _DD_MAKEVALUE(x,S_LISTCFG_DEFAULT) +#define G_LISTCFG_DEFAULT(x) _DD_GETVALUE(x,S_LISTCFG_DEFAULT,M_LISTCFG_DEFAULT) + + +/* Receive Data and Receive BD Initiator Control Registers (p 399) */ + +/* RCVINITMODE: Receive Data and Receive BD Initiator Mode Register (0x2400) */ + +#define M_RCVINITMODE_JUMBO _DD_MAKEMASK1(2) +#define M_RCVINITMODE_FRMSIZE _DD_MAKEMASK1(3) +#define M_RCVINITMODE_RTNSIZE _DD_MAKEMASK1(4) + + +/* Receive Initiator Control Registers (p 404) */ +/* Receive BD Completion Control Registers (p 406) */ +/* Receive List Selector Control Registers (p 408) */ +/* Mbuf Cluster Free Registers (p 409) */ + + +/* Host Coalescing Control registers (p 410) */ + +/* HCM: Host Coalescing Mode Register (0x3C00) */ + +#define M_HCM_RESET _DD_MAKEMASK1(0) +#define M_HCM_ENABLE _DD_MAKEMASK1(1) +#define M_HCM_ATTN _DD_MAKEMASK1(2) +#define M_HCM_COAL_NOW _DD_MAKEMASK1(3) + +#define S_HCM_MSIBITS 4 +#define M_HCM_MSIBITS _DD_MAKEMASK(3,S_HCM_MSIBITS) +#define V_HCM_MSIBITS(x) _DD_MAKEVALUE(x,S_HCM_MSIBITS) +#define G_HCM_MSIBITS _DD_GETVALUE(x,S_HCM_MSIBITS,M_HCM_MSIBITS) + +#define S_HCM_SBSIZE 7 +#define M_HCM_SBSIZE _DD_MAKEMASK(2,S_HCM_SBSIZE) +#define V_HCM_SBSIZE(x) _DD_MAKEVALUE(x,S_HCM_SBSIZE) +#define G_HCM_SBSIZE _DD_GETVALUE(x,S_HCM_SBSIZE,M_HCM_SBSIZE) +#define K_HCM_SBSIZE_80 0x0 +#define K_HCM_SBSIZE_64 0x1 +#define K_HCM_SBSIZE_32 0x2 +/* ... more ... */ + + +/* Memory Arbiter Registers (p 420) */ + +/* MAM: Memory Arbiter Mode Register (0x4000) */ + +#define M_MAM_RESET _DD_MAKEMASK1(0) +#define M_MAM_ENABLE _DD_MAKEMASK1(1) + +/* Memory Arbiter Status Register (0x4004) */ + +/* Memory Arbiter Trap Low and Trap High Registers (0x4008, 0x400C) */ + + +/* Buffer Manager Control Registers (p 424) */ + +/* BMODE: Buffer Manager Control Register (0x4400) */ + +#define M_BMODE_RESET _DD_MAKEMASK1(0) +#define M_BMODE_ENABLE _DD_MAKEMASK1(1) +#define M_BMODE_ATTN _DD_MAKEMASK1(2) +#define M_BMODE_TEST _DD_MAKEMASK1(3) +#define M_BMODE_MBUFLOWATTN _DD_MAKEMASK1(4) + + +/* Read DMA Control Registers (p 428) */ +/* Write DMA Control Registers (p 431) */ + +/* Bit fields shared by DMA_MODE and DMA_STATUS registers */ + +#define M_ATTN_TGTABORT _DD_MAKEMASK1(2) +#define M_ATTN_MSTRABORT _DD_MAKEMASK1(3) +#define M_ATTN_PERR _DD_MAKEMASK1(4) +#define M_ATTN_ADDROVFL _DD_MAKEMASK1(5) +#define M_ATTN_FIFOOVFL _DD_MAKEMASK1(6) +#define M_ATTN_FIFOUNFL _DD_MAKEMASK1(7) +#define M_ATTN_FIFOREAD _DD_MAKEMASK1(8) +#define M_ATTN_LENERR _DD_MAKEMASK1(9) +#define M_ATTN_ALL (M_ATTN_TGTABORT | M_ATTN_MSTRABORT | \ + M_ATTN_PERR | M_ATTN_ADDROVFL | \ + M_ATTN_FIFOOVFL | M_ATTN_FIFOUNFL | \ + M_ATTN_FIFOREAD | M_ATTN_LENERR) + +/* Read DMA Mode Register (0x4800) */ +/* Write DMA Mode Register (0x4C00) */ + +/* Read DMA Status Register (0x4804) */ +/* Write DMA Status Register (0x4C04) */ + + +/* RX RISC Registers (p 433) */ +/* TX RISC Registers (p 437) */ +/* Low Priority Mailboxes (p 441) */ +/* Flow Through Queues (p 445) */ +/* Message Signaled Interrupt Registers (p 447) */ +/* DMA Completion Registers (p 449) */ + + +/* General Control registers (p 450) */ + +/* MCTL: Miscellaneous Host Control Register (0x6800) */ + +#define M_MCTL_UPDATE _DD_MAKEMASK1(0) +#define M_MCTL_BSWAPCTRL _DD_MAKEMASK1(1) +#define M_MCTL_WSWAPCTRL _DD_MAKEMASK1(2) +#define M_MCTL_BSWAPDATA _DD_MAKEMASK1(4) +#define M_MCTL_WSWAPDATA _DD_MAKEMASK1(5) +#define M_MCTL_NOCRACK _DD_MAKEMASK1(9) +#define M_MCTL_NOCRC _DD_MAKEMASK1(10) +#define M_MCTL_ACCEPTBAD _DD_MAKEMASK1(11) +#define M_MCTL_NOTXINT _DD_MAKEMASK1(13) +#define M_MCTL_NORTRNINT _DD_MAKEMASK1(14) +#define M_MCTL_PCI32 _DD_MAKEMASK1(15) +#define M_MCTL_HOSTUP _DD_MAKEMASK1(16) +#define M_MCTL_HOSTBDS _DD_MAKEMASK1(17) +#define M_MCTL_NOTXPHSUM _DD_MAKEMASK1(20) +#define M_MCTL_NORXPHSUM _DD_MAKEMASK1(23) +#define M_MCTL_TXINT _DD_MAKEMASK1(24) +#define M_MCTL_RXINT _DD_MAKEMASK1(25) +#define M_MCTL_MACINT _DD_MAKEMASK1(26) +#define M_MCTL_DMAINT _DD_MAKEMASK1(27) +#define M_MCTL_FLOWINT _DD_MAKEMASK1(28) +#define M_MCTL_4XRINGS _DD_MAKEMASK1(29) +#define M_MCTL_MCASTEN _DD_MAKEMASK1(30) + +/* MCFG: Miscellaneous Configuration Register (0x6804) */ + +#define M_MCFG_CORERESET _DD_MAKEMASK1(0) +#define S_MCFG_PRESCALER 1 +#define M_MCFG_PRESCALER _DD_MAKEMASK(7,S_MCFG_PRESCALER) +#define V_MCFG_PRESCALER(x) _DD_MAKEVALUE(x,S_MCFG_PRESCALER) +#define G_MCFG_PRESCALER(x) _DD_GETVALUE(x,S_MCFG_PRESCALER,M_MCFG_PRESCALER) + +/* MLCTL: Miscellaneous Local Control Register (0x6808) */ + +#define M_MLCTL_INTSTATE _DD_MAKEMASK1(0) +#define M_MLCTL_INTCLR _DD_MAKEMASK1(1) +#define M_MLCTL_INTSET _DD_MAKEMASK1(2) +#define M_MLCTL_INTATTN _DD_MAKEMASK1(3) +/* ... */ +#define M_MLCTL_EPAUTOACCESS _DD_MAKEMASK1(24) + +/* EPADDR: Serial EEPROM Address Register (0x6838) */ + +#define S_EPADDR_ADDR 0 +#define M_EPADDR_ADDR (_DD_MAKEMASK(16,S_EPADDR_ADDR) & ~3) +#define V_EPADDR_ADDR(x) _DD_MAKEVALUE(x,S_EPADDR_ADDR) +#define G_EPADDR_ADDR(x) _DD_GETVALUE(x,S_EPADDR_ADDR,M_EPADDR_ADDR) + +#define S_EPADDR_HPERIOD 16 +#define M_EPADDR_HPERIOD _DD_MAKEMASK(9,S_EPADDR_HPERIOD) +#define V_EPADDR_HPERIOD(x) _DD_MAKEVALUE(x,S_EPADDR_HPERIOD) +#define G_EPADDR_HPERIOD(x) _DD_GETVALUE(x,S_EPADDR_HPERIOD,M_EPADDR_HPERIOD) + +#define M_EPADDR_START _DD_MAKEMASK1(25) + +#define S_EPADDR_DEVID 26 +#define M_EPADDR_DEVID _DD_MAKEMASK(3,S_EPADDR_DEVID) +#define V_EPADDR_DEVID(x) _DD_MAKEVALUE(x,S_EPADDR_DEVID) +#define G_EPADDR_DEVID(x) _DD_GETVALUE(x,S_EPADDR_DEVID,M_EPADDR_DEVID) + +#define M_EPADDR_RESET _DD_MAKEMASK1(29) +#define M_EPADDR_COMPLETE _DD_MAKEMASK1(30) +#define M_EPADDR_RW _DD_MAKEMASK1(31) + +/* EPDATA: Serial EEPROM Data Register (0x683C) */ + +/* EPCTL: Serial EEPROM Control Register (0x6840) */ + +#define M_EPCTL_CLOCKTS0 _DD_MAKEMASK1(0) +#define M_EPCTL_CLOCKO _DD_MAKEMASK1(1) +#define M_EPCTL_CLOCKI _DD_MAKEMASK1(2) +#define M_EPCTL_DATATSO _DD_MAKEMASK1(3) +#define M_EPCTL_DATAO _DD_MAKEMASK1(4) +#define M_EPCTL_DATAI _DD_MAKEMASK1(5) + +/* MDCTL: MDI Control Register (0x6844) */ + +#define M_MDCTL_DATA _DD_MAKEMASK1(0) +#define M_MDCTL_ENABLE _DD_MAKEMASK1(1) +#define M_MDCTL_SELECT _DD_MAKEMASK1(2) +#define M_MDCTL_CLOCK _DD_MAKEMASK1(3) + + +/* Ring Control Blocks (p 97) */ + +#define RCB_HOST_ADDR_HIGH 0x0 +#define RCB_HOST_ADDR_LOW 0x4 +#define RCB_CTRL 0x8 +#define RCB_NIC_ADDR 0xC + +#define RCB_SIZE 0x10 + +#define RCB_FLAG_USE_EXT_RCV_BD _DD_MAKEMASK1(0) +#define RCB_FLAG_RING_DISABLED _DD_MAKEMASK1(1) + +#define S_RCB_MAXLEN 16 +#define M_RCB_MAXLEN _DD_MAKEMASK(16,S_RCB_MAXLEN) +#define V_RCB_MAXLEN(x) _DD_MAKEVALUE(x,S_RCB_MAXLEN) +#define G_RCB_MAXLEN(x) _DD_GETVALUE(x,S_RCB_MAXLEN,M_RCB_MAXLEN) + + +/* On-chip Memory Map (Tables 70 and 71, pp 178-179) This is the map + for the 5700 with no external SRAM, the 5701, 5702 and 5703. The + 5705 does not fully implement some ranges and maps the buffer pool + differently. */ + +/* Locations 0x0000 - 0x00FF are Page Zero */ + +/* Locations 0x0100 - 0x01FF are Send Producer Ring RCBs */ + +#define A_SND_RCBS 0x0100 +#define L_SND_RCBS (16*RCB_SIZE) +#define A_SND_RCB(n) (A_SND_RCBS + ((n)-1)*RCB_SIZE) + +/* Locations 0x0200 - 0x02FF are Receive Return Ring RCBs */ + +#define A_RTN_RCBS 0x0200 +#define L_RTN_RCBS (16*RCB_SIZE) +#define A_RTN_RCB(n) (A_RTN_RCBS + ((n)-1)*RCB_SIZE) + +/* Locations 0x0300 - 0x0AFF are Statistics Block */ + +#define A_MAC_STATS 0x0300 +#define L_MAC_STATS (0x0B00-A_MAC_STATS) + +/* Locations 0x0B00 - 0x0B4F are Status Block */ + +#define A_MAC_STATUS 0x0B00 +#define L_MAC_STATUS (0x0B50-A_MAC_STATUS) + +/* Locations 0x0B50 - 0x0FFF are Software General Communication */ + +#define A_PXE_MAILBOX 0x0B50 +#define T3_MAGIC_NUMBER 0x4B657654 + +/* Locations 0x1000 - 0x1FFF are unmapped */ + +/* Locations 0x2000 - 0x3FFF are DMA Descriptors */ + +#define A_DMA_DESCS 0x2000 +#define L_DMA_DESCS (0x4000-A_DMA_DESCS) + +/* Locations 0x4000 - 0x5FFF are Send Rings 1-4 */ + +#define A_SND_RINGS 0x4000 +#define L_SND_RINGS (0x6000-A_SND_RINGS) + +/* Locations 0x6000 - 0x6FFF are Standard Receive Rings */ + +#define A_STD_RCV_RINGS 0x6000 +#define L_STD_RCV_RINGS (0x7000-A_STD_RCV_RINGS) + +/* Locations 0x7000 - 0x7FFF are Jumbo Receive Rings */ + +#define A_JUMBO_RCV_RINGS 0x7000 +#define L_JUMBO_RCV_RINGS (0x8000-A_JUMBO_RCV_RINGS) + +/* Locations 0x08000 - 0x0FFFF are Buffer Pool 1 */ +/* Locations 0x10000 - 0x17FFF are Buffer Pool 2 */ +/* Locations 0x18000 - 0x1FFFF are Buffer Pool 3 */ + +#define A_BUFFER_POOL 0x08000 +#define L_BUFFER_POOL (0x20000-A_BUFFER_POOL) + +/* Locations 0x08000 - 0x09FFF are TXMBUF (5705) */ +/* Locations 0x10000 - 0x1DFFF are RXMBUF (5705) */ + +#define A_TXMBUF 0x080000 +#define L_TXMBUF (0x0A000-A_TXMBUF) +#define A_RXMBUF 0x100000 +#define L_RXMBUF (0x1E000-A_RXMBUF) + + +/* Indices of (8-byte) counters in the Statistics Block. */ + +#define ifHCInOctets 32 +#define etherStatsFragments 34 +#define ifHCInUcastPkts 35 +#define ifHCInMulticastPkts 36 +#define ifHCInBroadcastPkts 37 +#define dot3StatsFCSErrors 38 +#define dot3StatsAlignmentErrors 39 +#define xonPauseFramesReceived 40 +#define xoffPauseFramesReceived 41 +#define macControlFramesReceived 42 +#define xoffSateEntered 43 +#define dot3StatsFrameTooLongs 44 +#define etherStatsJabbers 45 +#define etherStatsUndersizePkts 46 +#define inRangeLengthError 47 +#define outRangeLengthError 48 +#define etherStatsPkts64Octets 49 +#define etherStatsPkts65to127Octets 50 +#define etherStatsPkts128to255Octets 51 +#define etherStatsPkts256to511Octets 52 +#define etherStatsPkts512to1023Octets 53 +#define etherStatsPkts1024to1522Octets 54 +#define etherStatsPkts1523to2047Octets 55 +#define etherStatsPkts2048to4095Octets 56 +#define etherStatsPkts4096to8191Octets 57 +#define etherStatsPkts8192to9022Octets 58 + +#define ifHCOutOctets 96 +#define etherStatsCollisions 98 +#define outXonSent 99 +#define outXoffSent 100 +#define flowControlDone 101 +#define dot3StatsInternalMacTransmitErrors 102 +#define dot3StatsSingleCollisionFrames 103 +#define dot3StatsMultipleCollisionFrames 104 +#define dot3StatsDeferredTransmissions 105 +#define dot3StatsExcessiveCollisions 107 +#define dot3StatsLateCollisions 108 +#define dot3Collided2Times 109 +#define dot3Collided3Times 110 +#define dot3Collided4Times 111 +#define dot3Collided5Times 112 +#define dot3Collided6Times 113 +#define dot3Collided7Times 114 +#define dot3Collided8Times 115 +#define dot3Collided9Times 116 +#define dot3Collided10Times 117 +#define dot3Collided11Times 118 +#define dot3Collided12Times 119 +#define dot3Collided13Times 120 +#define dot3Collided14Times 121 +#define dot3Collided15Times 122 +#define ifHCOutUcastPkts 123 +#define ifHCOutMulticastPkts 124 +#define ifHCOutBroadcastPkts 125 +#define dot3StatsCarrierSenseErrors 126 +#define ifOutDiscards 127 +#define ifOutErrors 128 + +#define COSifHCInPkts1 160 +#define COSifHCInPkts2 161 +#define COSifHCInPkts3 162 +#define COSifHCInPkts4 163 +#define COSifHCInPkts5 164 +#define COSifHCInPkts6 165 +#define COSifHCInPkts7 166 +#define COSifHCInPkts8 167 +#define COSifHCInPkts9 168 +#define COSifHCInPkts10 169 +#define COSifHCInPkts11 170 +#define COSifHCInPkts12 171 +#define COSifHCInPkts13 172 +#define COSifHCInPkts14 173 +#define COSifHCInPkts15 174 +#define COSifHCInPkts16 175 +#define COSFramesDroppedDueToFilters 176 +#define nicDmaWriteQueueFull 177 +#define nicDmaWriteHighPriQueueFull 178 +#define nicNoMoreRxBDs 179 +#define ifInDiscards 180 +#define ifInErrors 181 +#define nicRecvThresholdHit 182 + +#define COSifHCOutPkts1 192 +#define COSifHCOutPkts2 193 +#define COSifHCOutPkts3 194 +#define COSifHCOutPkts4 195 +#define COSifHCOutPkts5 196 +#define COSifHCOutPkts6 197 +#define COSifHCOutPkts7 198 +#define COSifHCOutPkts8 199 +#define COSifHCOutPkts9 200 +#define COSifHCOutPkts10 201 +#define COSifHCOutPkts11 202 +#define COSifHCOutPkts12 203 +#define COSifHCOutPkts13 204 +#define COSifHCOutPkts14 205 +#define COSifHCOutPkts15 206 +#define COSifHCOutPkts16 207 +#define nicDmaReadQueueFull 208 +#define nicDmaReadHighPriQueueFull 209 +#define nicSendDataCompQueueFull 210 +#define nicRingSetSendProdIndex 211 +#define nicRingStatusUpdate 212 +#define nicInterrupts 213 +#define nicAvoidedInterrupts 214 +#define nicSendThresholdHit 215 + +#endif /* _BCM_5700_H_ */ diff --git a/cfe/cfe/dev/bcm5821.h b/cfe/cfe/dev/bcm5821.h new file mode 100644 index 0000000..29ece60 --- /dev/null +++ b/cfe/cfe/dev/bcm5821.h @@ -0,0 +1,278 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * BCM5821 cryptoaccelerator File: bcm5821.h + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + +#ifndef _BCM5821_H_ +#define _BCM5821_H_ + +/* Register and field definitions for the Broadcom BCM5821 crypto + accelerator. The BCM5820 implements a compatible (modulo bugs) + subset of the BCM5821. */ + +#define K_PCI_VENDOR_BROADCOM 0x14e4 +#define K_PCI_ID_BCM5820 0x5820 +#define K_PCI_ID_BCM5821 0x5821 + +#define _DD_MAKEMASK1(n) (1 << (n)) +#define _DD_MAKEMASK(v,n) ((((1)<<(v))-1) << (n)) +#define _DD_MAKEVALUE(v,n) ((v) << (n)) +#define _DD_GETVALUE(v,n,m) (((v) & (m)) >> (n)) + + +/* DMA Control and Status Register offsets */ + +#define R_MCR1 0x00 +#define R_DMA_CTRL 0x04 +#define R_DMA_STAT 0x08 +#define R_DMA_ERR 0x0C +#define R_MCR2 0x10 + + +/* 0x00 MCR1@: Master Command Record 1 Address */ +/* 0x10 MCR2@: Master Command Record 2 Address */ + + +/* 0x04 DMA Control */ + +#define M_DMA_CTRL_WR_BURST _DD_MAKEMASK1(16) /* Not 5820 */ + +#define K_DMA_WR_BURST_128 0 +#define K_DMA_WR_BURST_240 M_DMA_CRTL_WR_BURST + +#define M_DMA_CTRL_MOD_NORM _DD_MAKEMASK1(22) +#define M_DMA_CTRL_RNG_MODE _DD_MAKEMASK1(23) +#define M_DMA_CTRL_DMAERR_EN _DD_MAKEMASK1(25) +#define M_DMA_CTRL_NORM_PCI _DD_MAKEMASK1(26) /* Not 5820 */ +#define M_DMA_CTRL_LE_CRYPTO _DD_MAKEMASK1(27) /* Not 5820 */ +#define M_DMA_CTRL_MCR1_INT_EN _DD_MAKEMASK1(29) +#define M_DMA_CTRL_MCR2_INT_EN _DD_MAKEMASK1(30) +#define M_DMA_CTRL_RESET _DD_MAKEMASK1(31) + + +/* 0x08 DMA Status */ + +#define M_DMA_STAT_MCR2_EMPTY _DD_MAKEMASK1(24) /* Not 5820 */ +#define M_DMA_STAT_MCR1_EMPTY _DD_MAKEMASK1(25) /* Not 5820 */ +#define M_DMA_STAT_MCR2_INTR _DD_MAKEMASK1(26) +#define M_DMA_STAT_MCR2_FULL _DD_MAKEMASK1(27) +#define M_DMA_STAT_DMAERR_INTR _DD_MAKEMASK1(28) +#define M_DMA_STAT_MCR1_INTR _DD_MAKEMASK1(29) +#define M_DMA_STAT_MCR1_FULL _DD_MAKEMASK1(30) +#define M_DMA_STAT_MSTR_ACCESS _DD_MAKEMASK1(31) + +/* 0x0C DMA Error Address */ + +#define M_DMA_ERR_RD_FAULT _DD_MAKEMASK1(1) +#define M_DMA_ERR_ADDR 0xFFFFFFFC + + +/* Master Command Record Header Format */ + +#define S_MCR_NUM_PACKETS 0 +#define M_MCR_NUM_PACKETS _DD_MAKEMASK(16,S_MCR_NUM_PACKETS) +#define V_MCR_NUM_PACKETS(x) _DD_MAKEVALUE(x,S_MCR_NUM_PACKETS) +#define G_MCR_NUM_PACKETS(x) _DD_GETVALUE(x,S_MCR_NUM_PACKETS,M_MCR_NUM_PACKETS) + +/* Input flags */ + +#define M_MCR_SUPPRESS_INTR _DD_MAKEMASK1(31) + +/* Output flags */ + +#define M_MCR_DONE _DD_MAKEMASK1(16) +#define M_MCR_ERROR _DD_MAKEMASK1(17) + +#define S_MCR_ERROR_CODE 24 +#define M_MCR_ERROR_CODE _DD_MAKEMASK(8,S_MCR_ERROR_CODE) +#define V_MCR_ERROR_CODE(x) _DD_MAKEVALUE(x,S_MCR_ERROR_CODE) +#define G_MCR_ERROR_CODE(x) _DD_GETVALUE(x,S_MCR_ERROR_CODE,M_MCR_ERROR_CODE) +#define K_MCR_ERROR_OK 0 +#define K_MCR_ERROR_UNKNOWN_OP 1 +#define K_MCR_ERROR_DSA_SHORT 2 +#define K_MCR_ERROR_PKI_SHORT 3 +#define K_MCR_ERROR_PKO_SHORT 4 /* Not 5820 */ +#define K_MCR_ERROR_CHAIN_SHORT 5 /* Not 5820 */ +#define K_MCR_ERROR_FIFO 6 /* Not 5820 */ + +/* In both cases, the header word is followed by an array of N PD entries: + commandContext[0] + dataBuffer[0] + pktLen[0] + outputBuffer[0] + ... + commandContext[n-1] + dataBuffer[n-1] + pktLen[n-1] + outputBuffer[n-1] +*/ + +#define MCR_WORDS(n) (1+8*(n)) +#define MCR_BYTES(n) ((1+8*(n))*4) + + +/* Data Buffer Chain Entry Offsets */ + +#define DBC_ADDR 0 +#define DBC_NEXT 4 +#define DBC_LEN 8 + +#define CHAIN_WORDS 3 + +#define S_DBC_DATA_LEN 0 +#define M_DBC_DATA_LEN _DD_MAKEMASK(16,S_DBC_DATA_LEN) +#define V_DBC_DATA_LEN(x) _DD_MAKEVALUE(x,S_DBC_DATA_LEN) +#define G_DBC_DATA_LEN(x) _DD_GETVALUE(x,S_DBC_DATA_LEN,M_DBC_DATA_LEN) + +/* Packet Descriptor Offsets */ + +#define PD_CC_ADDR 0 +#define PD_INPUT_FRAG 4 +#define PD_INPUT_FRAG_ADDR (PD_INPUT_FRAG+DBC_ADDR) +#define PD_INPUT_FRAG_NEXT (PD_INPUT_FRAG+DBC_NEXT) +#define PD_INPUT_FRAG_LEN (PD_INPUT_FRAG+DBC_LEN) +#define PD_PKT_LEN 16 +#define PD_OUTPUT_FRAG 20 +#define PD_OUTPUT_FRAG_ADDR (PD_OUTPUT_FRAG+DBC_ADDR) +#define PD_OUTPUT_FRAG_NEXT (PD_OUTPUT_FRAG+DBC_NEXT) +#define PD_OUTPUT_FRAG_LEN (PD_OUTPUT_FRAG+DBC_LEN) + +#define PD_SIZE 32 + +#define S_PD_PKT_LEN 16 +#define M_PD_PKT_LEN _DD_MAKEMASK(16,S_PD_PKT_LEN) +#define V_PD_PKT_LEN(x) _DD_MAKEVALUE(x,S_PD_PKT_LEN) +#define G_PD_PKT_LEN(x) _DD_GETVALUE(x,S_PD_PKT_LEN,M_PD_PKT_LEN) + + +/* Crypotographic Operations */ + +/* MCR1 only (symmetric) */ +#define K_IPSEC_3DES 0x0000 /* Not 5820 */ +#define K_SSL_MAC 0x0001 +#define K_TLS_HMAC 0x0002 +#define K_SSL_3DES 0x0003 +#define K_ARC4 0x0004 +#define K_HASH 0x0005 + +/* MCR2 only (asymmetric) */ +#define K_DH_PK_GEN 0x0001 +#define K_DH_SK_GEN 0x0002 +#define K_RSA_PK_OP 0x0003 +#define K_RSA_SK_OP 0x0004 +#define K_DSA_SIGN 0x0005 +#define K_DSA_VERIF 0x0006 +#define K_RNG_DIRECT 0x0041 +#define K_RNG_SHA1 0x0042 +#define K_MOD_ADD 0x0043 +#define K_MOD_SUB 0x0044 +#define K_MOD_MUL 0x0045 +#define K_MOD_REDUCE 0x0046 +#define K_MOD_EXP 0x0047 +#define K_MOD_INV 0x0048 /* Not 5821 */ +#define K_MOD_2EXP 0x0049 /* Not 5820 */ + + +/* Command Context Header */ + +/* Word 0 */ + +#define S_CC_OPCODE 16 +#define M_CC_OPCODE _DD_MAKEMASK(16,S_CC_OPCODE) +#define V_CC_OPCODE(x) _DD_MAKEVALUE(x,S_CC_OPCODE) +#define G_CC_OPCODE(x) _DD_GETVALUE(x,S_CC_OPCODE,M_CC_OPCODE) + +#define S_CC_LEN 0 +#define M_CC_LEN _DD_MAKEMASK(16,S_CC_LEN) +#define V_CC_LEN(x) _DD_MAKEVALUE(x,S_CC_LEN) +#define G_CC_LEN(x) _DD_GETVALUE(x,S_CC_LEN,M_CC_LEN) + +/* Word 1 */ + +#define S_CC_FLAGS 12 +#define M_CC_FLAGS _DD_MAKEMASK(4,S_CC_FLAGS) +#define V_CC_FLAGS(x) _DD_MAKEVALUE(x,S_CC_FLAGS) +#define G_CC_FLAGS(x) _DD_GETVALUE(x,S_CC_OPCODE,M_CC_OPCODE) + +/* The remaining command context fields depend on the opcode. */ + +/* IPSEC 3DES (K_IPSEC_3DES) */ + +/* SSL MAC (K_SSL_MAC) */ +/* TLS HMAC (K_TLS_HMAC) */ +/* Pure MD5/SHA-1 Hash (K_HASH) */ + +#define K_HASH_FLAGS_MD5 1 +#define K_HASH_FLAGS_SHA1 2 + +/* SSL MAC (K_SSL_MAC) */ + +#define SSL_MAC_CMD_WORDS 22 + +/* TLS HMAC (K_TLS_HMAC) */ + +#define TLS_HMAC_CMD_WORDS 16 + +/* Pure MD5/SHA-1 Hash (K_HASH) */ + +/* SSL/TLS DES/3DES (K_SSL_3DES) */ + +/* ARCFOUR (K_ARC4) */ + +#define ARC4_STATE_WORDS (1 + 256/4) +#define ARC4_CMD_WORDS (2 + ARC4_STATE_WORDS) + +#define M_ARC4_FLAGS_KEY _DD_MAKEMASK1(10) +#define M_ARC4_FLAGS_WRITEBACK _DD_MAKEMASK1(11) +#define M_ARC4_FLAGS_NULLDATA _DD_MAKEMASK1(12) + + +/* Random number generation (K_RNG_DIRECT, K_RNG_SHA1) */ + +/* Modular arithmetic (K_MOD_ADD, K_MOD_SUB, K_MOD_MUL) */ + +/* Modular Remainder (K_MOD_REDUCE) */ + +/* Modular Exponentiation (K_MOD_EXP) */ + +/* Double Modular Exponentiation (K_MOD_2EXP) */ + + +#endif /* _BCM_5821_H_ */ diff --git a/cfe/cfe/dev/dc21143.h b/cfe/cfe/dev/dc21143.h new file mode 100644 index 0000000..65f3255 --- /dev/null +++ b/cfe/cfe/dev/dc21143.h @@ -0,0 +1,588 @@ +/* + * Register and bit definitions for the DEC/Intel 21143 Ethernet controller, + * part of the Tulip family of 10 and 10/100 controllers. + * Reference: + * 21143 PCI/CardBus 10/100 Mb/s Ethernet LAN Controller, + * Hardware Reference Manual, Revision 1.0. + * Document No. 278074-001 + * Intel Corp., October 1998 + * Includes extensions/alternatives for the DEC 21040, 21041 and 21140(A) + * Ethernet controllers. + */ +#ifndef _DC21143_H_ +#define _DC21143_H_ + +#define _DD_MAKEMASK1(n) (1 << (n)) +#define _DD_MAKEMASK(v,n) ((((1)<<(v))-1) << (n)) +#define _DD_MAKEVALUE(v,n) ((v) << (n)) +#define _DD_GETVALUE(v,n,m) (((v) & (m)) >> (n)) + + +/* ********************************************************************* + * PCI Configuration Register offsets (Tulip nomenclature) + ********************************************************************* */ + +#define R_CFG_CFID PCI_ID_REG +#define K_PCI_VENDOR_DEC 0x1011 +#define K_PCI_ID_DC21040 0x0002 +#define K_PCI_ID_DC21041 0x0014 +#define K_PCI_ID_DC21140 0x0009 +#define K_PCI_ID_DC21143 0x0019 + +#define R_CFG_CFRV PCI_CLASS_REG + +#define R_CFG_CBIO PCI_MAPREG(0) +#define R_CFG_CBMA PCI_MAPREG(1) + +#define R_CFG_CFIT PCI_BPARAM_INTERRUPT_REG + +/* Tulip extensions */ +#define R_CFG_CFDD 0x40 + +#define M_CFDD_SLEEP __DD_MAKEMASK1(31) +#define M_CFDD_SNOOZE __DD_MAKEMASK1(30) + +#define R_CFG_CPMS 0xE0 + + +/* ********************************************************************* + * CSRs: offsets + ********************************************************************* */ + +#define R_CSR_BUSMODE 0x00 +#define R_CSR_TXPOLL 0x08 +#define R_CSR_RXPOLL 0x10 +#define R_CSR_RXRING 0x18 +#define R_CSR_TXRING 0x20 +#define R_CSR_STATUS 0x28 +#define R_CSR_OPMODE 0x30 +#define R_CSR_INTMASK 0x38 +#define R_CSR_MISSEDFRAME 0x40 +#define R_CSR_ROM_MII 0x48 +#define R_CSR_BOOTROM_ADRR 0x50 +#define R_CSR_GENTIMER 0x58 + +/* The following registers are specific to the 21040 */ + +#define R_CSR_FDUPLEX 0x58 + +/* The following registers are specific to the 21040/21041 and 21142/21143 */ + +#define R_CSR_SIASTATUS 0x60 +#define R_CSR_SIAMODE0 0x68 +#define R_CSR_SIAMODE1 0x70 +#define R_CSR_SIAMODE2 0x78 + +/* The following registers are specific to the 21140/21140A */ + +#define R_CSR_GENPORT 0x60 +#define R_CSR_WATCHDOG_TIMER 0x78 + + +/* CSR0: Bus Mode register */ + +#define M_CSR0_SWRESET _DD_MAKEMASK1(0) +#define M_CSR0_BUSARB _DD_MAKEMASK1(1) + +#define S_CSR0_SKIPLEN 2 +#define M_CSR0_SKIPLEN _DD_MAKEMASK(5,S_CSR0_SKIPLEN) +#define V_CSR0_SKIPLEN(x) _DD_MAKEVALUE(x,S_CSR0_SKIPLEN) +#define G_CSR0_SKIPLEN(x) _DD_GETVALUE(x,S_CSR0_SKIPLEN,M_CSR0_SKIPLEN) + +#define M_CSR0_BIGENDIAN _DD_MAKEMASK1(7) + +#define S_CSR0_BURSTLEN 8 +#define M_CSR0_BURSTLEN _DD_MAKEMASK(6,S_CSR0_BURSTLEN) +#define V_CSR0_BURSTLEN(x) _DD_MAKEVALUE(x,S_CSR0_BURSTLEN) +#define G_CSR0_BURSTLEN(x) _DD_GETVALUE(x,S_CSR0_BURSTLEN,M_CSR0_BURSTLEN) + +#define S_CSR0_CACHEALIGN 14 +#define M_CSR0_CACHEALIGN _DD_MAKEMASK(2,S_CSR0_CACHEALIGN) +#define V_CSR0_CACHEALIGN(x) _DD_MAKEVALUE(x,S_CSR0_CACHEALIGN) +#define G_CSR0_CACHEALIGN(x) _DD_GETVALUE(x,S_CSR0_CACHEALIGN,M_CSR0_CACHEALIGN) + +#define S_CSR0_TXAUTOPOLL 17 +#define M_CSR0_TXAUTOPOLL _DD_MAKEMASK(3,S_CSR0_AUTOPOLL) +#define V_CSR0_TXAUTOPOLL(x) _DD_MAKEVALUE(x,S_CSR0_TXAUTOPOLL) +#define G_CSR0_TXAUTOPOLL(x) _DD_GETVALUE(x,S_CSR0_TXAUTOPOLL,M_CSR0_TXAUTOPOLL) + +#define M_CSR0_DESCBYTEORDER _DD_MAKEMASK1(20) /* not 21040 */ +#define M_CSR0_READMULTENAB _DD_MAKEMASK1(21) /* not 2104{0,1} */ +#define M_CSR0_READLINEENAB _DD_MAKEMASK1(23) /* not 2104{0,1} */ +#define M_CSR0_WRITEINVALENAB _DD_MAKEMASK1(24) /* not 2104{0,1} */ + +#define K_CSR0_TAPDISABLED 0x00 +#define K_CSR0_TAP200US 0x01 +#define K_CSR0_TAP800US 0x02 +#define K_CSR0_TAP1600US 0x03 + +#define K_CSR0_ALIGNNONE 0 +#define K_CSR0_ALIGN32 1 +#define K_CSR0_ALIGN64 2 +#define K_CSR0_ALIGN128 3 + +#define K_CSR0_BURST32 32 +#define K_CSR0_BURST16 16 +#define K_CSR0_BURST8 8 +#define K_CSR0_BURST4 4 +#define K_CSR0_BURST2 2 +#define K_CSR0_BURST1 1 + + +#define M_CSR3_RXDSCRADDR 0xFFFFFFFC +#define M_CSR4_TXDSCRADDR 0xFFFFFFFC + + +/* CSR5: Status register */ + +#define M_CSR5_TXINT _DD_MAKEMASK1(0) +#define M_CSR5_TXSTOP _DD_MAKEMASK1(1) +#define M_CSR5_TXBUFUNAVAIL _DD_MAKEMASK1(2) +#define M_CSR5_TXJABTIMEOUT _DD_MAKEMASK1(3) +#define M_CSR5_LINKPASS _DD_MAKEMASK1(4) /* not 21040 */ +#define M_CSR5_TXUNDERFLOW _DD_MAKEMASK1(5) +#define M_CSR5_RXINT _DD_MAKEMASK1(6) +#define M_CSR5_RXBUFUNAVAIL _DD_MAKEMASK1(7) +#define M_CSR5_RXSTOPPED _DD_MAKEMASK1(8) +#define M_CSR5_RXWDOGTIMEOUT _DD_MAKEMASK1(9) +#define M_CSR5_AUITPPIN _DD_MAKEMASK1(10) /* 21040 only */ +#define M_CSR5_TXEARLYINT _DD_MAKEMASK1(10) /* not 2104{0,1} */ +#define M_CSR5_FDSHORTFRAME _DD_MAKEMASK1(11) /* 21040 only */ +#define M_CSR5_GPTIMEREXPIRE _DD_MAKEMASK1(11) /* not 21040 */ +#define M_CSR5_LINKFAIL _DD_MAKEMASK1(12) +#define M_CSR5_FATALBUSERROR _DD_MAKEMASK1(13) +#define M_CSR5_RXEARLYINT _DD_MAKEMASK1(14) /* not 21040 */ +#define M_CSR5_ABNORMALINT _DD_MAKEMASK1(15) +#define M_CSR5_NORMALINT _DD_MAKEMASK1(16) + +#define S_CSR5_RXPROCSTATE 17 +#define M_CSR5_RXPROCSTATE _DD_MAKEMASK(3,S_CSR5_RXPROCSTATE) +#define V_CSR5_RXPROCSTATE(x) _DD_MAKEVALUE(x,S_CSR5_RXPROCSTATE) +#define G_CSR5_RXPROCSTATE(x) _DD_GETVALUE(x,S_CSR5_RXPROCSTATE,M_CSR5_RXPROCSTATE) + +#define K_CSR5_RXSTOPPED 0x00 /* RESET or STOP command */ +#define K_CSR5_RXFETCH 0x01 /* fetching rx desc */ +#define K_CSR5_RXCHECK 0x02 /* checking end of rx pkt */ +#define K_CSR5_RXWAIT 0x03 /* waiting for rx pkt */ +#define K_CSR5_RXSUSPEND 0x04 /* unavailable rx buffer */ +#define K_CSR5_RXCLOSE 0x05 /* closing rx desc */ +#define K_CSR5_RXFLUSH 0x06 /* flushing rx frame */ +#define K_CSR5_RXQUEUE 0x07 /* reading rx frame from FIFO */ + +#define S_CSR5_TXPROCSTATE 20 +#define M_CSR5_TXPROCSTATE _DD_MAKEMASK(3,S_CSR5_TXPROCSTATE) +#define V_CSR5_TXPROCSTATE(x) _DD_MAKEVALUE(x,S_CSR5_TXPROCSTATE) +#define G_CSR5_TXPROCSTATE(x) _DD_GETVALUE(x,S_CSR5_TXPROCSTATE,M_CSR5_TXPROCSTATE) + +#define K_CSR5_TXSTOPPED 0x00 /* RESET or STOP command */ +#define K_CSR5_TXFETCH 0x01 /* fetching tx desc */ +#define K_CSR5_TXWAIT 0x02 /* waiting for end of tx */ +#define K_CSR5_TXREAD 0x03 /* reading buffer into FIFO */ +#define K_CSR5_TXSETUP 0x05 /* setup packet */ +#define K_CSR5_TXSUSPEND 0x06 /* tx underflow or no tx desc */ +#define K_CSR5_TXCLOSE 0x07 /* closing tx desc */ + +#define S_CSR5_ERRORBITS 23 +#define M_CSR5_ERRORBITS _DD_MAKEMASK(3,S_CSR5_ERRORBITS) +#define V_CSR5_ERRORBITS(x) _DD_MAKEVALUE(x,S_CSR5_ERRORBITS) +#define G_CSR5_ERRORBITS(x) _DD_GETVALUE(x,S_CSR5_ERRORBITS,M_CSR5_ERRORBITS) + +#define K_CSR5_FBE_PARITY 0x00 +#define K_CSR5_FBE_MABORT 0x01 +#define K_CSR5_FBE_TABORT 0x02 + +#define M_CSR5_GPPORTINT _DD_MAKEMASK1(26) /* not 2104{0,1} */ +#define M_CSR5_LINKCHANGED _DD_MAKEMASK1(27) /* not 2104{0,1} */ + + +/* CSR6: Operating Mode register */ + +#define M_CSR6_RXHASHFILT _DD_MAKEMASK1(0) +#define M_CSR6_RXSTART _DD_MAKEMASK1(1) +#define M_CSR6_HASHONLY _DD_MAKEMASK1(2) +#define M_CSR6_PASSBADFRAMES _DD_MAKEMASK1(3) +#define M_CSR6_INVERSEFILT _DD_MAKEMASK1(4) +#define M_CSR6_STOPBACKOFF _DD_MAKEMASK1(5) +#define M_CSR6_PROMISCUOUS _DD_MAKEMASK1(6) +#define M_CSR6_PASSALLMULTI _DD_MAKEMASK1(7) +#define M_CSR6_FULLDUPLEX _DD_MAKEMASK1(9) + +#define M_CSR6_INTLOOPBACK _DD_MAKEMASK1(10) +#define M_CSR6_EXTLOOPBACK _DD_MAKEMASK1(11) + +#define S_CSR6_OPMODE 10 +#define M_CSR6_OPMODE _DD_MAKEMASK(2,S_CSR6_OPMODE) +#define V_CSR6_OPMODE(x) _DD_MAKEVALUE(x,S_CSR6_OPMODE) +#define G_CSR6_OPMODE(x) _DD_GETVALUE(x,S_CSR6_OPMODE,M_CSR6_OPMODE) + +#define M_CSR6_FORCECOLL _DD_MAKEMASK1(12) +#define M_CSR6_TXSTART _DD_MAKEMASK1(13) + +#define S_CSR6_THRESHCONTROL 14 +#define M_CSR6_THRESHCONTROL _DD_MAKEMASK(2,S_CSR6_THRESHCONTROL) +#define V_CSR6_THRESHCONTROL(x) _DD_MAKEVALUE(x,S_CSR6_THRESHCONTROL) +#define G_CSR6_THRESHCONTROL(x) _DD_GETVALUE(x,S_CSR6_THRESHCONTROL,M_CSR6_THRESHCONTROL) + +#define M_CSR6_BACKPRESSURE _DD_MAKEMASK1(16) /* 21040 only */ +#define M_CSR6_CAPTUREEFFECT _DD_MAKEMASK1(17) + +#define M_CSR6_PORTSEL _DD_MAKEMASK1(18) /* not 2104{0,1} */ +#define M_CSR6_HBDISABLE _DD_MAKEMASK1(19) /* not 2104{0,1} */ +#define M_CSR6_STOREFWD _DD_MAKEMASK1(21) /* not 2104{0,1} */ +#define M_CSR6_TXTHRESH _DD_MAKEMASK1(22) /* not 2104{0,1} */ +#define M_CSR6_PCSFUNC _DD_MAKEMASK1(23) /* not 2104{0,1} */ +#define M_CSR6_SCRAMMODE _DD_MAKEMASK1(24) /* not 2104{0,1} */ +#define M_CSR6_MBO _DD_MAKEMASK1(25) /* not 2104{0,1} */ +#define M_CSR6_RXALL _DD_MAKEMASK1(30) /* not 2104{0,1} */ + +#define M_CSR6_SPECCAP _DD_MAKEMASK1(31) /* not 21040 */ + +#define K_CSR6_TXTHRES_128_72 0x00 +#define K_CSR6_TXTHRES_256_96 0x01 +#define K_CSR6_TXTHRES_512_128 0x02 +#define K_CSR6_TXTHRES_1024_160 0x03 + + +#define M_CSR6_SPEED_10 (M_CSR6_TXTHRESH) + +#define M_CSR6_SPEED_100 (M_CSR6_HBDISABLE | \ + M_CSR6_SCRAMMODE | \ + M_CSR6_PCSFUNC | \ + M_CSR6_PORTSEL) + +#define M_CSR6_SPEED_10_MII (M_CSR6_TXTHRESH | \ + M_CSR6_PORTSEL) + +#define M_CSR6_SPEED_100_MII (M_CSR6_HBDISABLE | \ + M_CSR6_PORTSEL) + + +/* CSR7: Interrupt mask register */ + +#define M_CSR7_TXINT _DD_MAKEMASK1(0) +#define M_CSR7_TXSTOP _DD_MAKEMASK1(1) +#define M_CSR7_TXBUFUNAVAIL _DD_MAKEMASK1(2) +#define M_CSR7_TXJABTIMEOUT _DD_MAKEMASK1(3) +#define M_CSR7_LINKPASS _DD_MAKEMASK1(4) /* not 21040 */ +#define M_CSR7_TXUNDERFLOW _DD_MAKEMASK1(5) +#define M_CSR7_RXINT _DD_MAKEMASK1(6) +#define M_CSR7_RXBUFUNAVAIL _DD_MAKEMASK1(7) +#define M_CSR7_RXSTOPPED _DD_MAKEMASK1(8) +#define M_CSR7_RXWDOGTIMEOUT _DD_MAKEMASK1(9) +#define M_CSR7_AUITPSW _DD_MAKEMASK1(10) /* 21040 only */ +#define M_CSR7_TXEARLY _DD_MAKEMASK1(10) /* not 2104{0,1} */ +#define M_CSR7_FD _DD_MAKEMASK1(11) /* 21040 only */ +#define M_CSR7_GPTIMER _DD_MAKEMASK1(11) /* not 21040 */ +#define M_CSR7_LINKFAIL _DD_MAKEMASK1(12) +#define M_CSR7_FATALBUSERROR _DD_MAKEMASK1(13) +#define M_CSR7_RXEARLY _DD_MAKEMASK1(14) /* not 21040 */ +#define M_CSR7_ABNORMALINT _DD_MAKEMASK1(15) +#define M_CSR7_NORMALINT _DD_MAKEMASK1(16) +#define M_CSR7_GPPORT _DD_MAKEMASK1(26) /* not 2104{0,1} */ +#define M_CSR7_LINKCHANGED _DD_MAKEMASK1(27) /* not 2104{0,1} */ + + +/* CSR8: Missed Frame register */ + +#define M_CSR8_RXOVER_WRAP _DD_MAKEMASK1(28) /* not 2104{0,1} */ +#define S_CSR8_RXOVER 17 +#define M_CSR8_RXOVER _DD_MAKEMASK(11,S_CSR8_RXOVER) /* not 2104{0,1} */ +#define V_CSR8_RXOVER(x) _DD_MAKEVALUE(x,S_CSR8_RXOVER) +#define G_CSR8_RXOVER(x) _DD_GETVALUE(x,S_CSR8_RXOVER,M_CSR8_RXOVER) + +#define M_CSR8_MISSEDWRAP _DD_MAKEMASK1(16) +#define S_CSR8_MISSED 0 +#define M_CSR8_MISSED _DD_MAKEMASK(16,S_CSR8_MISSED) +#define V_CSR8_MISSED(x) _DD_MAKEVALUE(x,S_CSR8_MISSED) +#define G_CSR8_MISSED(x) _DD_GETVALUE(x,S_CSR8_MISSED,M_CSR8_MISSED) + + +/* CSR9: ROM and MII register */ + +#define S_CSR9_ROMDATA 0 +#define M_CSR9_ROMDATA _DD_MAKEMASK(8,S_CSR9_ROMDATA) +#define V_CSR9_ROMDATA(x) _DD_MAKEVALUE(x,S_CSR9_ROMDATA) +#define G_CSR9_ROMDATA(x) _DD_GETVALUE(x,S_CSR9_ROMDATA,M_CSR9_ROMDATA) + +#define M_CSR9_SROMCHIPSEL _DD_MAKEMASK1(0) /* not 21040 */ +#define M_CSR9_SROMCLOCK _DD_MAKEMASK1(1) /* not 21040 */ +#define M_CSR9_SROMDATAIN _DD_MAKEMASK1(2) /* not 21040 */ +#define M_CSR9_SROMDATAOUT _DD_MAKEMASK1(3) /* not 21040 */ + +#define M_CSR9_REGSELECT _DD_MAKEMASK1(10) /* not 21040 */ +#define M_CSR9_SERROMSEL _DD_MAKEMASK1(11) /* not 21040 */ +#define M_CSR9_ROMSEL _DD_MAKEMASK1(12) /* not 21040 */ +#define M_CSR9_ROMWRITE _DD_MAKEMASK1(13) /* not 21040 */ +#define M_CSR9_ROMREAD _DD_MAKEMASK1(14) /* not 21040 */ +#define M_CSR9_MODESEL _DD_MAKEMASK1(15) /* 21041 only */ +#define M_CSR9_MDC _DD_MAKEMASK1(16) /* not 2104{0,1} */ +#define M_CSR9_MDO _DD_MAKEMASK1(17) /* not 2104{0,1} */ +#define M_CSR9_MIIMODE _DD_MAKEMASK1(18) /* not 2104{0,1} */ +#define M_CSR9_MDI _DD_MAKEMASK1(19) /* not 2104{0,1} */ + +#define M_CSR9_DATANOTVALID _DD_MAKEMASK1(31) /* 21040 only */ + +#define M_CSR10_BOOTROMADDR _DD_MAKEMASK(18,0) /* not 21040 */ + + +/* CSR11 General Purpose Timer register */ + +#define S_CSR11_GPTIMER 0 /* not 21040 */ +#define M_CSR11_GPTIMER _DD_MAKEMASK(16,S_CSR11_GPTIMER) +#define V_CSR11_GPTIMER(x) _DD_MAKEVALUE(x,S_CSR11_GPTIMER) +#define G_CSR11_GPTIMER(x) _DD_GETVALUE(x,S_CSR11_GPTIMER,M_CSR11_GPTIMER) + + +#define M_CSR11_GPTIMERCONT _DD_MAKEMASK1(16) /* not 21040 */ + +#define S_CSR11_FDAUTOCONF 0 /* 21040 only */ +#define M_CSR11_FDAUTOCONF _DD_MAKEMASK(16,S_CSR11_FDAUTOCONF) +#define V_CSR11_FDAUTOCONF(x) _DD_MAKEVALUE(x,S_CSR11_FDAUTOCONF) +#define G_CSR11_FRAUTOCONF(x) _DD_GETVALUE(x,S_CSR11_FDAUTOCONF,M_CSR11_AUTOCONF) + + +/* CSR12: SIA Status register (21143) */ + +#define M_CSR12_MIIRPA _DD_MAKEMASK1(0) +#define M_CSR12_100MBLINK _DD_MAKEMASK1(1) +#define M_CSR12_10MBLINK _DD_MAKEMASK1(2) +#define M_CSR12_AUTOPOLSTATE _DD_MAKEMASK1(3) + +#define M_CSR12_RXAUIACT _DD_MAKEMASK1(8) +#define M_CSR12_RX10BASETACT _DD_MAKEMASK1(9) +#define M_CSR12_NLPDETECT _DD_MAKEMASK1(10) +#define M_CSR12_TXREMFAULT _DD_MAKEMASK1(11) + +#define S_CSR12_AUTONEGARBIT 12 +#define M_CSR12_AUTONEGARBIT _DD_MAKEMASK(3,S_CSR12_AUTONEGARBIT) +#define V_CSR12_AUTONEGARBIT(x) _DD_MAKEVALUE(x,S_CSR12_AUTONEGARBIT) +#define G_CSR12_AUTONEGARBIT(x) _DD_GETVALUE(x,S_CSR12_AUTONEGARBIT,M_CSR12_AUTONEGARBIT) + +#define M_CSR12_LINKPARTNEG _DD_MAKEMASK1(15) + +#define S_CSR12_LINKPARTCODE 16 +#define M_CSR12_LINKPARTCODE _DD_MAKEMASK(16,S_CSR12_LINKPARTCODE) +#define V_CSR12_LINKPARTCODE(x) _DD_MAKEVALUE(x,S_CSR12_LINKPARTCODE) +#define G_CSR12_LINKPARTCODE(x) _DD_GETVALUE(x,S_CSR12_LINKPARTCODE,M_CSR12_LINKPARTCODE) + + +/* CSR12: SIA Status register (21041, also 31:12, 3:3 as for 21143) */ + +#define M_CSR12_NETCONNERR _DD_MAKEMASK1(1) +#define M_CSR12_LINKFAIL _DD_MAKEMASK1(2) +#define M_CSR12_SELPORTACT _DD_MAKEMASK1(8) +#define M_CSR12_NONSELPORTACT _DD_MAKEMASK1(9) +#define M_CSR12_AUTONEGRESTART _DD_MAKEMASK1(10) +#define M_CSR12_UNSTABLENLP _DD_MAKEMASK1(11) + + +/* CSR12: General Purpose Port register (21140) */ + +#define S_CSR12_DATA 0 +#define M_CSR12_DATA _DD_MAKEMASK(8,S_CSR12_DATA) +#define V_CSR12_DATA _DD_MAKEVALUE(x,S_CSR12_DATA,M_CSR12_DATA) +#define G_CSR12_DATA(x) _DD_GETVALUE(x,S_CSR12_DATA,M_CSR12_DATA) + +#define M_CSR12_CONTROL _DD_MAKEMASK1(8) + + +/* CSR13: SIA Mode 0 register (21143 and 21041) */ + +#define M_CSR13_CONN_NOT_RESET _DD_MAKEMASK1(0) +#define M_CSR13_CONN_CSR_AUTO _DD_MAKEMAKS1(2) /* 21041 only */ +#define M_CSR13_CONN_AUI_10BT _DD_MAKEMASK1(3) + + +/* CSR14: SIA Mode 1 register (21143 and 21041) */ + +#define M_CSR14_ENCODER _DD_MAKEMASK1(0) +#define M_CSR14_LOOPBACK _DD_MAKEMASK1(1) +#define M_CSR14_DRIVER _DD_MAKEMASK1(2) +#define M_CSR14_LINKPULSE _DD_MAKEMASK1(3) + +#define S_CSR14_COMPENSATE 4 +#define M_CSR14_COMPENSATE _DD_MAKEMASK(2,S_CSR14_COMPENSATE) +#define V_CSR15_COMPENSATE(x) _DD_MAKEVALUE(x,S_CSR15_COMPENSATE) +#define G_CSR15_COMPENSATE(x) _DD_GETVALUE(x,S_CSR15_COMPENSATE,M_CSR15_COMPENSATE) + +#define M_CSR14_HALFDUPLEX10BASET _DD_MAKEMASK1(6) +#define M_CSR14_AUTONEGOTIATE _DD_MAKEMASK1(7) +#define M_CSR14_RXSQUELCH _DD_MAKEMASK1(8) +#define M_CSR14_COLLSQUELCH _DD_MAKEMASK1(9) +#define M_CSR14_COLLDETECT _DD_MAKEMASK1(10) +#define M_CSR14_SIGQUALGEN _DD_MAKEMASK1(11) +#define M_CSR14_LINKTEST _DD_MAKEMASK1(12) +#define M_CSR14_AUTOPOLARITY _DD_MAKEMASK1(13) +#define M_CSR14_SETPOLARITY _DD_MAKEMASK1(14) +#define M_CSR14_10BASETAUIAUTO _DD_MAKEMASK1(15) +#define M_CSR14_100BASETHALFDUP _DD_MAKEMASK1(16) /* not 21041 */ +#define M_CSR14_100BASETFULLDUP _DD_MAKEMASK1(17) /* not 21041 */ +#define M_CSR14_100BASET4 _DD_MAKEMASK1(18) /* not 21041 */ + +#define M_CSR14_10BT_HD 0x7F3F +#define M_CSR14_10BT_FD 0x7F3D + + +/* CSR15: SIA Mode 2 register (21143 and 21041) */ + +#define M_CSR15_GP_JABBERDIS _DD_MAKEMASK1(0) /* 21041 only */ +#define M_CSR15_GP_HOSTUNJAB _DD_MAKEMASK1(1) +#define M_CSR15_GP_JABBERCLK _DD_MAKEMASK1(2) +#define M_CSR15_GP_AUIBNC _DD_MAKEMASK1(3) +#define M_CSR15_GP_RXWATCHDIS _DD_MAKEMASK1(4) +#define M_CSR15_GP_RXWATCHREL _DD_MAKEMASK1(5) + +/* (CSR15: 21143 only) */ + +#define S_CSR15_GP_GPDATA 16 +#define M_CSR15_GP_GPDATA _DD_MAKEMASK(4,S_CSR15_GP_GPDATA) +#define V_CSR15_GP_GPDATA(x) _DD_MAKEVALUE(x,S_CSR15_GP_GPDATA) +#define G_CSR15_GP_GPDATA(x) _DD_GETVALUE(x,S_CSR15_GP_GPDATA,M_CSR15_GP_GPDATA) + +#define M_CSR15_GP_LED0 _DD_MAKEMASK1(20) +#define M_CSR15_GP_LED1 _DD_MAKEMASK1(21) +#define M_CSR15_GP_LED2 _DD_MAKEMASK1(22) +#define M_CSR15_GP_LED3 _DD_MAKEMASK1(23) +#define M_CSR15_GP_INTPORT0 _DD_MAKEMASK1(24) +#define M_CSR15_GP_INTPORT1 _DD_MAKEMASK1(25) +#define M_CSR15_GP_RXMATCH _DD_MAKEMASK1(26) +#define M_CSR15_GP_CONTROLWRITE _DD_MAKEMASK1(27) +#define M_CSR15_GP_GPINT0 _DD_MAKEMASK1(28) +#define M_CSR15_GP_GPINT1 _DD_MAKEMASK1(29) +#define M_CSR15_GP_RXMATCHINT _DD_MAKEMASK1(30) + +#define M_CSR15_DEFAULT_VALUE 0x00050008 +#define M_CSR15_CONFIG_GEPS_LEDS 0x08af0000 + +/* (CSR15: 21041 only) */ + +#define M_CSR15_GP_LED1ENB _DD_MAKEMASK1(6) +#define M_CSR15_GP_LED1VALUE _DD_MAKEMASK1(7) +#define M_CSR15_GP_TSTCLK _DD_MAKEMASK1(8) +#define M_CSR15_GP_FORCEUNSQ _DD_MAKEMASK1(9) +#define M_CSR15_GP_FORCEFAIL _DD_MAKEMASK1(10) +#define M_CSR15_GP_LEDSTRDIS _DD_MAKEMASK1(11) +#define M_CSR15_GP_PLLTEST _DD_MAKEMASK1(12) +#define M_CSR15_GP_FORCERXLOW _DD_MAKEMASK1(13) +#define M_CSR15_GP_LED2ENB _DD_MAKEMASK1(14) +#define M_CSR15_GP_LED2VALUE _DD_MAKEMASK1(15) + + +/* CSR15: Watchdog Timer register (21140) */ + +#define M_CSR15_WT_JABBER _DD_MAKEMASK1(0) +#define M_CSR15_WT_HOSTUNJAB _DD_MAKEMASK1(1) +#define M_CSR15_WT_JABBERCLK _DD_MAKEMASK1(2) +#define M_CSR15_WT_RXWATCHDIS _DD_MAKEMASK1(4) +#define M_CSR15_WT_RXWATCHREL _DD_MAKEMASK1(5) + + +/* ********************************************************************* + * Receive Descriptors + ********************************************************************* */ + +#define M_RDES0_OWNSYS 0 +#define M_RDES0_OWNADAP _DD_MAKEMASK1(31) + +#define S_RDES0_FRAMELEN 16 +#define M_RDES0_FRAMELEN _DD_MAKEMASK(14,S_RDES0_FRAMELEN) +#define V_RDES0_FRAMELEN(x) _DD_MAKEVALUE(x,S_RDES0_FRAMELEN) +#define G_RDES0_FRAMELEN(x) _DD_GETVALUE(x,S_RDES0_FRAMELEN,M_RDES0_FRAMELEN) + +#define M_RDES0_ZERO _DD_MAKEMASK1(0) +#define M_RDES0_OVFL _DD_MAKEMAKS1(0) /* 21041 only */ +#define M_RDES0_CRCERR _DD_MAKEMASK1(1) +#define M_RDES0_DRIBBLE _DD_MAKEMASK1(2) +#define M_RDES0_MIIERROR _DD_MAKEMASK1(3) /* not 21041 */ +#define M_RDES0_WDOGTIMER _DD_MAKEMASK1(4) +#define M_RDES0_FRAMETYPE _DD_MAKEMASK1(5) +#define M_RDES0_COLLSEEN _DD_MAKEMASK1(6) +#define M_RDES0_FRAMETOOLONG _DD_MAKEMASK1(7) +#define M_RDES0_LASTDES _DD_MAKEMASK1(8) +#define M_RDES0_FIRSTDES _DD_MAKEMASK1(9) +#define M_RDES0_MCASTFRAME _DD_MAKEMASK1(10) +#define M_RDES0_RUNTFRAME _DD_MAKEMASK1(11) + +#define S_RDES0_DATATYPE 12 +#define M_RDES0_DATATYPE _DD_MAKEMASK(2,S_RDES0_DATATYPE) +#define V_RDES0_DATATYPE(x) _DD_MAKEVALUE(x,S_RDES0_DATATYPE) +#define G_RDES0_DATATYPE(x) _DD_GETVALUE(x,S_RDES0_DATATYPE,M_RDES0_DATATYPE) + +#define M_RDES0_ERROR _DD_MAKEMASK1(14) +#define M_RDES0_ERRORSUM _DD_MAKEMASK1(15) +#define M_RDES0_FILTFAIL _DD_MAKEMASK1(30) /* not 21041 */ + +#define S_RDES1_BUF1SIZE 0 +#define M_RDES1_BUF1SIZE _DD_MAKEMASK(11,S_TDES1_BUF1SIZE) +#define V_RDES1_BUF1SIZE(x) _DD_MAKEVALUE(x,S_RDES1_BUF1SIZE) +#define G_RDES1_BUF1SIZE(x) _DD_GETVALUE(x,S_RDES1_BUF1SIZE,M_RDES1_BUF1SIZE) + +#define S_RDES1_BUF2SIZE 11 +#define M_RDES1_BUF2SIZE _DD_MAKEMASK(11,S_TDES2_BUF2SIZE) +#define V_RDES1_BUF2SIZE(x) _DD_MAKEVALUE(x,S_RDES1_BUF2SIZE) +#define G_RDES1_BUF2SIZE(x) _DD_GETVALUE(x,S_RDES1_BUF2SIZE,M_RDES1_BUF2SIZE) + +#define M_RDES1_CHAINED _DD_MAKEMASK1(24) +#define M_RDES1_ENDOFRING _DD_MAKEMASK1(25) + +#define M_RDES2_BUFADDR 0xFFFFFFFF +#define M_RDES3_BUFADDR 0xFFFFFFFF + +/* ********************************************************************* + * Transmit Descriptors + ********************************************************************* */ + +#define M_TDES0_OWNSYS 0 +#define M_TDES0_OWNADAP _DD_MAKEMASK1(31) + +#define M_TDES0_DEFERRED _DD_MAKEMASK1(0) +#define M_TDES0_UNDERFLOW _DD_MAKEMASK1(1) +#define M_TDES0_LINK_FAIL _DD_MAKEMASK1(2) + +#define S_TDES0_COLLCOUNT 3 +#define M_TDES0_COLLCOUNT _DD_MAKEMASK(4,S_TDES0_COLLCOUNT) +#define V_TDES0_COLLCOUNT(x) _DD_MAKEVALUE(x,S_TDES0_COLLCOUNT) +#define G_TDES0_COLLCOUNT(x) _DD_GETVALUE(x,S_TDES0_COLLCOUNT,M_TDES0_COLLCOUNT) + +#define M_TDES0_HEARTBEAT_FAIL _DD_MAKEMASK1(7) +#define M_TDES0_EXCESSIVE_COLLISIONS _DD_MAKEMASK1(8) +#define M_TDES0_LATE_COLLISION _DD_MAKEMASK1(9) +#define M_TDES0_NO_CARRIER _DD_MAKEMASK1(10) +#define M_TDES0_LOSS_OF_CARRIER _DD_MAKEMASK1(11) +#define M_TDES0_TX_JABBER_TIMEOUT _DD_MAKEMASK1(14) +#define M_TDES0_ERROR_SUMMARY _DD_MAKEMASK1(15) +#define M_TDES0_OWN_BIT _DD_MAKEMASK1(31) + +#define S_TDES1_BUF1SIZE 0 +#define M_TDES1_BUF1SIZE _DD_MAKEMASK(11,S_TDES1_BUF1SIZE) +#define V_TDES1_BUF1SIZE(x) _DD_MAKEVALUE(x,S_TDES1_BUF1SIZE) +#define G_TDES1_BUF1SIZE(x) _DD_GETVALUE(x,S_TDES1_BUF1SIZE,M_TDES1_BUF1SIZE) + +#define S_TDES1_BUF2SIZE 11 +#define M_TDES1_BUF2SIZE _DD_MAKEMASK(11,S_TDES2_BUF2SIZE) +#define V_TDES1_BUF2SIZE(x) _DD_MAKEVALUE(x,S_TDES1_BUF2SIZE) +#define G_TDES1_BUF2SIZE(x) _DD_GETVALUE(x,S_TDES1_BUF2SIZE,M_TDES1_BUF2SIZE) + +#define M_TDES1_FT0 _DD_MAKEMASK1(22) +#define M_TDES1_NOPADDING _DD_MAKEMASK1(23) +#define M_TDES1_CHAINED _DD_MAKEMASK1(24) +#define M_TDES1_ENDOFRING _DD_MAKEMASK1(25) +#define M_TDES1_NOADDCRC _DD_MAKEMASK1(26) +#define M_TDES1_SETUP _DD_MAKEMASK1(27) +#define M_TDES1_FT1 _DD_MAKEMASK1(28) +#define M_TDES1_FIRSTSEG _DD_MAKEMASK1(29) +#define M_TDES1_LASTSEG _DD_MAKEMASK1(30) +#define M_TDES1_INTERRUPT _DD_MAKEMASK1(31) + +#define M_TDES2_BUFADDR 0xFFFFFFFF +#define M_TDES3_BUFADDR 0xFFFFFFFF + + +/* CAM */ + +#define CAM_HASH_THRESHOLD 14 +#define CAM_PERFECT_ENTRIES 16 + +#define CAM_SETUP_BUFFER_SIZE 192 + +#endif /* _DC21143_H_ */ diff --git a/cfe/cfe/dev/dev_atapi.c b/cfe/cfe/dev/dev_atapi.c new file mode 100644 index 0000000..b5f4d9a --- /dev/null +++ b/cfe/cfe/dev/dev_atapi.c @@ -0,0 +1,222 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * ATAPI device driver File: dev_atapi.c + * + * This is a simple driver for ATAPI devices. The disks + * are expected to be connected to the generic bus (this + * driver doesn't support PCI). + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "sbmips.h" +#include "lib_types.h" +#include "lib_malloc.h" +#include "lib_printf.h" +#include "lib_string.h" +#include "cfe_timer.h" +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_ioctl.h" + +#include "dev_ide_common.h" + +/* ********************************************************************* + * Macros + ********************************************************************* */ + +#define GETWORD_LE(buf,wordidx) (((unsigned int) (buf)[(wordidx)*2]) + \ + (((unsigned int) (buf)[(wordidx)*2+1]) << 8)) + + +/* ********************************************************************* + * Forward declarations + ********************************************************************* */ + +extern void _wbflush(void); +static void atapidrv_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + + +const static cfe_devdisp_t atapidrv_dispatch = { + idecommon_open, + idecommon_read, + idecommon_inpstat, + idecommon_write, + idecommon_ioctl, + idecommon_close, + NULL, + NULL +}; + +const cfe_driver_t atapidrv = { + "ATAPI device", + "atapi", + CFE_DEV_DISK, + &atapidrv_dispatch, + atapidrv_probe +}; + + + +/* ********************************************************************* + * Port I/O routines + * + * These routines are called back from the common code to do + * I/O cycles to the IDE disk. We provide routines for + * reading and writing bytes, words, and strings of words. + ********************************************************************* */ + +static uint8_t atapidrv_inb(idecommon_dispatch_t *disp,uint32_t reg) +{ + return *((volatile uint8_t *) PHYS_TO_K1(reg+disp->baseaddr)); +} + +static uint16_t atapidrv_inw(idecommon_dispatch_t *disp,uint32_t reg) +{ + return *((volatile uint16_t *) PHYS_TO_K1((reg+disp->baseaddr))); +} + +static void atapidrv_ins(idecommon_dispatch_t *disp,uint32_t reg,uint8_t *buf,int len) +{ + uint16_t data; + + while (len > 0) { + data = *((volatile uint16_t *) PHYS_TO_K1(reg+disp->baseaddr)); + +#ifdef _BYTESWAP_ + *buf++ = (data >> 8) & 0xFF; + *buf++ = (data & 0xFF); +#else + *buf++ = (data & 0xFF); + *buf++ = (data >> 8) & 0xFF; +#endif + len--; + len--; + } + +} + +static void atapidrv_outb(idecommon_dispatch_t *disp,uint32_t reg,uint8_t val) +{ + *((volatile uint8_t *) PHYS_TO_K1(reg+disp->baseaddr)) = val; + _wbflush(); +} + +static void atapidrv_outw(idecommon_dispatch_t *disp,uint32_t reg,uint16_t val) +{ + *((volatile uint16_t *) PHYS_TO_K1(reg+disp->baseaddr)) = val; + _wbflush(); +} + +static void atapidrv_outs(idecommon_dispatch_t *disp,uint32_t reg,uint8_t *buf,int len) +{ + uint16_t data; + + while (len > 0) { +#ifdef _BYTESWAP_ + data = (uint16_t) buf[1] + ((uint16_t) buf[0] << 8); +#else + data = (uint16_t) buf[0] + ((uint16_t) buf[1] << 8); +#endif + + *((volatile uint16_t *) PHYS_TO_K1(reg+disp->baseaddr)) = data; + _wbflush(); + + buf++; + buf++; + len--; + len--; + } +} + + + +static void atapidrv_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + idecommon_t *softc; + idecommon_dispatch_t *disp; + char descr[80]; + char unitstr[50]; + int res; + + /* + * probe_a is the IDE base address + * probe_b is the unit number and other flags + * probe_ptr is unused. + */ + + softc = (idecommon_t *) KMALLOC(sizeof(idecommon_t),0); + disp = (idecommon_dispatch_t *) KMALLOC(sizeof(idecommon_dispatch_t),0); + + if (softc && disp) { + softc->idecommon_addr = probe_a; + softc->idecommon_unit = probe_b; + + disp->ref = softc; + disp->baseaddr = softc->idecommon_addr; + softc->idecommon_dispatch = disp; + + disp->outb = atapidrv_outb; + disp->outw = atapidrv_outw; + disp->outs = atapidrv_outs; + + disp->inb = atapidrv_inb; + disp->inw = atapidrv_inw; + disp->ins = atapidrv_ins; + + res = idecommon_devprobe(softc); + if (res < 0) { + KFREE(softc); + KFREE(disp); + return; + } + + xsprintf(descr,"%s unit %d at %08X",drv->drv_description,probe_b,probe_a); + xsprintf(unitstr,"%d",probe_b); + cfe_attach(drv,softc,unitstr,descr); + } +} + + diff --git a/cfe/cfe/dev/dev_bcm1250.c b/cfe/cfe/dev/dev_bcm1250.c new file mode 100644 index 0000000..8343a46 --- /dev/null +++ b/cfe/cfe/dev/dev_bcm1250.c @@ -0,0 +1,275 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * BCM1250 (BCM1250 as PCI device) driver File: dev_bcm1250.c + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "sbmips.h" + +#ifndef _SB_MAKE64 +#define _SB_MAKE64(x) ((uint64_t)(x)) +#endif +#ifndef _SB_MAKEMASK1 +#define _SB_MAKEMASK1(n) (_SB_MAKE64(1) << _SB_MAKE64(n)) +#endif + +#include "lib_types.h" +#include "lib_hssubr.h" +#include "lib_malloc.h" +#include "lib_printf.h" + +#include "cfe_iocb.h" +#include "cfe_error.h" +#include "cfe_device.h" + +#include "pcivar.h" +#include "pcireg.h" + +#include "bsp_config.h" + +/* Note that PHYSADDR only works with 32-bit addresses */ +#define PHYSADDR(x) (K0_TO_PHYS((uint32_t)(uintptr_t)(x))) + + +static void bcm1250_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + +static int bcm1250_open(cfe_devctx_t *ctx); +static int bcm1250_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int bcm1250_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); +static int bcm1250_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int bcm1250_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int bcm1250_close(cfe_devctx_t *ctx); + +const static cfe_devdisp_t bcm1250_dispatch = { + bcm1250_open, + bcm1250_read, + bcm1250_inpstat, + bcm1250_write, + bcm1250_ioctl, + bcm1250_close, + NULL, + NULL +}; + +const cfe_driver_t bcm1250drv = { + "BCM1250", + "widget", + CFE_DEV_OTHER, + &bcm1250_dispatch, + bcm1250_probe +}; + + +typedef struct bcm1250_s { + uint64_t mailbox; + uint64_t mem_base; + uint8_t irq; /* interrupt mapping */ + pcitag_t tag; /* tag for configuration register */ + + int downloaded; /* code has already been downloaded. */ +} bcm1250_t; + + +/* + * BCM1250_PROBE + * probe_a, probe_b and probe_ptr all unused + */ + +static void +bcm1250_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + int index; + + index = 0; + for (;;) { + pcitag_t tag; + + if (pci_find_device(0x166d, 0x0001, index, &tag) != 0) + break; + + if (tag != 0x00000000) { /* don't configure ourselves */ + bcm1250_t *softc; + char descr[80]; + phys_addr_t pa; + + softc = (bcm1250_t *) KMALLOC(sizeof(bcm1250_t), 0); + if (softc == NULL) { + xprintf("BCM1250: No memory to complete probe\n"); + break; + } + + softc->tag = tag; + + pci_map_mem(tag, PCI_MAPREG(0), PCI_MATCH_BYTES, &pa); + xsprintf(descr, "%s at 0x%X", drv->drv_description, (uint32_t)pa); + softc->mem_base = PHYS_TO_XKSEG_UNCACHED(pa); + + /* Map the CPU0 mailbox registers of the device 1250. + Note that our BAR2 space maps to its "alias" mailbox + registers. Set bit 3 for mbox_set; clear bit 3 for + reading. Address bits 15-4 are don't cares. */ + pci_map_mem(tag, PCI_MAPREG(2), PCI_MATCH_BYTES, &pa); + softc->mailbox = PHYS_TO_XKSEG_UNCACHED(pa); + + softc->downloaded = 0; + + cfe_attach(drv, softc, NULL, descr); + } + index++; + } +} + + +#include "elf.h" + +static int +elf_header (const uint8_t *hdr) +{ + return (hdr[EI_MAG0] == ELFMAG0 && + hdr[EI_MAG1] == ELFMAG1 && + hdr[EI_MAG2] == ELFMAG2 && + hdr[EI_MAG3] == ELFMAG3); +} + + +#include "cfe_timer.h" + +typedef struct { + uint32_t addr; /* source address, in device's PCI space */ + uint32_t len; /* length of this chunk */ +} chunk_desc; + + +#define MBOX_SET_BIT 0x8 + +extern void download_start(void), download_end(void); + +static int +bcm1250_open(cfe_devctx_t *ctx) +{ + bcm1250_t *softc = ctx->dev_softc; + uint64_t cmd_p = softc->mailbox + 4; + + if (softc->downloaded) { + xprintf("bcm1250_open: Warning: Device previously downloaded\n"); + softc->downloaded = 0; + } + + if (hs_read32(cmd_p) != 0) { + xprintf("bcm1250_open: Device not in initial state\n"); + return -1; + } + + return 0; +} + +static int +bcm1250_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + return -1; +} + +static int +bcm1250_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat) +{ + return -1; +} + +static int +bcm1250_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + bcm1250_t *softc = ctx->dev_softc; + uint64_t arg_p = softc->mailbox + 0; + uint64_t cmd_p = softc->mailbox + 4; + chunk_desc code; + uint32_t cmd; + int64_t timer; + int res; + + /* Note: This code assumes that PHYSADDR gives a PCI memory space + address that is accessible via our BAR4 or BAR5 */ + + code.addr = PHYSADDR((uint8_t *)buffer->buf_ptr); + code.len = buffer->buf_length; + + cmd = 0x1; /* load */ + if (!elf_header((uint8_t *)buffer->buf_ptr)) { + /* No recognizable elf seal, so assume compressed. */ + cmd |= 0x2; + } + + hs_write32(arg_p | MBOX_SET_BIT, PHYSADDR(&code)); + hs_write32(cmd_p | MBOX_SET_BIT, cmd); /* load */ + + /* Wait for handshake */ + + res = CFE_ERR_TIMEOUT; + TIMER_SET(timer, 5*CFE_HZ); + while (!TIMER_EXPIRED(timer)) { + if ((hs_read32(cmd_p) & 0x3) == 0) { + softc->downloaded = 1; + buffer->buf_retlen = 0; /* XXX check this */ + /* Note that the result code need not be translated only + because we are assuming a CFE in the device that is + compatible with us. */ + res = (int)hs_read32(arg_p); + break; + } + POLL(); + } + + return res; +} + +static int +bcm1250_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + return -1; +} + +static int +bcm1250_close(cfe_devctx_t *ctx) +{ + return 0; +} diff --git a/cfe/cfe/dev/dev_bcm5700.c b/cfe/cfe/dev/dev_bcm5700.c new file mode 100644 index 0000000..a7bef89 --- /dev/null +++ b/cfe/cfe/dev/dev_bcm5700.c @@ -0,0 +1,2557 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * BCM5700/Tigon3 (10/100/1000 EthernetMAC) driver File: dev_bcm5700.c + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + +#include "sbmips.h" + +#ifndef _SB_MAKE64 +#define _SB_MAKE64(x) ((uint64_t)(x)) +#endif +#ifndef _SB_MAKEMASK1 +#define _SB_MAKEMASK1(n) (_SB_MAKE64(1) << _SB_MAKE64(n)) +#endif + +#include "lib_types.h" +#include "lib_hssubr.h" +#include "lib_malloc.h" +#include "lib_string.h" +#define blockcopy memcpy +#include "lib_printf.h" +#include "lib_queue.h" + +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_ioctl.h" +#include "cfe_timer.h" +#include "cfe_error.h" +#include "cfe_irq.h" + +#include "pcivar.h" +#include "pcireg.h" + +#include "bcm5700.h" +#include "mii.h" + +/* This is a driver for the Broadcom 570x ("Tigon 3") 10/100/1000 MAC. + Currently, only the 5700, 5701 and 5705 have been tested. The 5704 + dual MAC is not supported, nor is any device with a SerDes PHY. + + Reference: + Host Programmer Interface Specification for the BCM570X Family + of Highly-Integrated Media Access Controllers, 570X-PG106-R. + Broadcom Corp., 16215 Alton Parkway, Irvine CA, 09/27/02 + + This BCM1250 version takes advantage of DMA coherence and uses + "preserve bit lanes" addresses for all accesses that cross the + ZBbus-PCI bridge. + + Note that the 5705 does not fully map all address ranges. Per + the manual, reads and writes of the unmapped regions are permitted + and do not fault; however, it apparently has some poisoned registers, + at least in early revs, that should not be touched. See the + conditionals in the code. */ + +/* PIOSWAP controls whether word-swapping takes place for transactions + in which the 570x is the target device. In theory, either value + should work (with access macros adjusted as below) and it should be + set to be consistent with the settings for 570x as initiator. + Empirically, however, some combinations work with the bit clear: + + SWAP=0 SWAP=1 + 5700 32 PCI OK OK + 5700 64 Sturgeon OK OK + 5701-32 32 PCI OK OK + 5701-32 64 Sturgeon OK OK + 5701-32 64 Golem OK OK + 5701-64 64 Sturgeon OK OK + 5701-64 64 Golem OK FAIL + 5705 32 PCI OK OK + 5705 64 Sturgeon (OK)* FAIL + 5705 64 Golem OK OK + + * PCI status/interrupt ordering problem under load. */ + +#define PIOSWAP 0 + +#ifndef T3_DEBUG +#define T3_DEBUG 0 +#endif + +#ifndef T3_BRINGUP +#define T3_BRINGUP 0 +#endif + +/* Broadcom recommends using PHY interrupts instead of autopolling, + but I haven't made it work yet. */ +#define T3_AUTOPOLL 1 + +/* Set IPOLL to drive processing through the pseudo-interrupt + dispatcher. Set XPOLL to drive processing by an external polling + agent. One must be set; setting both is ok. */ + +#ifndef IPOLL +#define IPOLL 0 +#endif +#ifndef XPOLL +#define XPOLL 1 +#endif + +#define ENET_ADDR_LEN 6 /* size of an ethernet address */ +#define MIN_ETHER_PACK 64 /* min size of a packet */ +#define MAX_ETHER_PACK 1518 /* max size of a packet */ +#define VLAN_TAG_LEN 4 /* VLAN type plus tag */ +#define CRC_SIZE 4 /* size of CRC field */ + +/* Packet buffers. For the Tigon 3, packet buffer alignment is + arbitrary and can be to any byte boundary. We would like it + aligned to a cache line boundary for performance, although there is + a trade-off with IP/TCP header alignment. */ + +#define ETH_PKTBUF_LEN (((MAX_ETHER_PACK+31)/32)*32) + +#if __long64 +typedef struct eth_pkt_s { + queue_t next; /* 16 */ + uint8_t *buffer; /* 8 */ + uint32_t flags; /* 4 */ + int32_t length; /* 4 */ + uint8_t data[ETH_PKTBUF_LEN]; +} eth_pkt_t; +#else +typedef struct eth_pkt_s { + queue_t next; /* 8 */ + uint8_t *buffer; /* 4 */ + uint32_t flags; /* 4 */ + int32_t length; /* 4 */ + uint32_t unused[3]; /* 12 */ + uint8_t data[ETH_PKTBUF_LEN]; +} eth_pkt_t; +#endif + +#define CACHE_ALIGN 32 +#define ETH_PKTBUF_LINES ((sizeof(eth_pkt_t) + (CACHE_ALIGN-1))/CACHE_ALIGN) +#define ETH_PKTBUF_SIZE (ETH_PKTBUF_LINES*CACHE_ALIGN) +#define ETH_PKTBUF_OFFSET (offsetof(eth_pkt_t, data)) + +#define ETH_PKT_BASE(data) ((eth_pkt_t *)((data) - ETH_PKTBUF_OFFSET)) + +static void +show_packet(char c, eth_pkt_t *pkt) +{ + int i; + int n = (pkt->length < 32 ? pkt->length : 32); + + xprintf("%c[%4d]:", c, pkt->length); + for (i = 0; i < n; i++) { + if (i % 4 == 0) + xprintf(" "); + xprintf("%02x", pkt->buffer[i]); + } + xprintf("\n"); +} + + +static void t3_ether_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + + +/* BCM570X Hardware Common Data Structures + XXX These work for 1250 big endian. Need endian testing. + XXX Should they move to the header file? */ + +/* Chip documentation numbers the rings with 1-origin. */ + +#define RI(n) ((n)-1) + +/* BCM570x Ring Sizes (no external memory). Pages 97-98 */ + +#define TXP_MAX_RINGS 16 +#define TXP_INTERNAL_RINGS 4 +#define TXP_RING_ENTRIES 512 + +#define RXP_STD_ENTRIES 512 + +#define RXR_MAX_RINGS 16 +#define RXR_RING_ENTRIES 1024 + +#define RXR_MAX_RINGS_05 1 +#define RXR_RING_ENTRIES_05 512 + + +/* BCM570x Send Buffer Descriptors as a struct. Pages 100-101 */ + +typedef struct t3_snd_bd_s { + uint32_t bufptr_hi; + uint32_t bufptr_lo; +#ifdef __MIPSEB + uint16_t length; + uint16_t flags; + uint16_t pad; + uint16_t vlan_tag; +#elif __MIPSEL + uint16_t flags; + uint16_t length; + uint16_t vlan_tag; + uint16_t pad; +#else +#error "bcm5700: endian not set" +#endif +} t3_snd_bd_t; + +#define SND_BD_SIZE 16 + +#define TX_FLAG_TCP_CKSUM 0x0001 +#define TX_FLAG_IP_CKSUM 0x0002 +#define TX_FLAG_PACKET_END 0x0004 +#define TX_FLAG_IP_FRAG 0x0008 +#define TX_FLAG_IP_FRAG_END 0x0010 +#define TX_FLAG_VLAN_TAG 0x0040 +#define TX_FLAG_COAL_NOW 0x0080 +#define TX_FLAG_CPU_PRE_DMA 0x0100 +#define TX_FLAG_CPU_POST_DMA 0x0200 +#define TX_FLAG_ADD_SRC 0x1000 +#define TX_FLAG_SRC_ADDR_SEL 0x6000 +#define TX_FLAG_NO_CRC 0x8000 + +/* BCM570x Receive Buffer Descriptors as a struct. Pages 105-107 */ + +typedef struct t3_rcv_bd_s { + uint32_t bufptr_hi; + uint32_t bufptr_lo; +#ifdef __MIPSEB + uint16_t index; + uint16_t length; + uint16_t type; + uint16_t flags; + uint16_t ip_cksum; + uint16_t tcp_cksum; + uint16_t error_flag; + uint16_t vlan_tag; +#elif __MIPSEL + uint16_t length; + uint16_t index; + uint16_t flags; + uint16_t type; + uint16_t tcp_cksum; + uint16_t ip_cksum; + uint16_t vlan_tag; + uint16_t error_flag; +#else +#error "bcm5700: endian not set" +#endif + uint32_t pad; + uint32_t opaque; +} t3_rcv_bd_t; + +#define RCV_BD_SIZE 32 + +#define RX_FLAG_PACKET_END 0x0004 +#define RX_FLAG_JUMBO_RING 0x0020 +#define RX_FLAG_VLAN_TAG 0x0040 +#define RX_FLAG_ERROR 0x0400 +#define RX_FLAG_MINI_RING 0x0800 +#define RX_FLAG_IP_CKSUM 0x1000 +#define RX_FLAG_TCP_CKSUM 0x2000 +#define RX_FLAG_IS_TCP 0x4000 + +#define RX_ERR_BAD_CRC 0x0001 +#define RX_ERR_COLL_DETECT 0x0002 +#define RX_ERR_LINK_LOST 0x0004 +#define RX_ERR_PHY_DECODE 0x0008 +#define RX_ERR_DRIBBLE 0x0010 +#define RX_ERR_MAC_ABORT 0x0020 +#define RX_ERR_SHORT_PKT 0x0040 +#define RX_ERR_TRUNC_NO_RES 0x0080 +#define RX_ERR_GIANT_PKT 0x0100 + +/* BCM570x Status Block format as a struct (not BCM5705). Pages 110-111. */ + +typedef struct t3_status_s { + uint32_t status; + uint32_t tag; +#ifdef __MIPSEB + uint16_t rxc_std_index; + uint16_t rxc_jumbo_index; + uint16_t reserved2; + uint16_t rxc_mini_index; + struct { + uint16_t send_c; + uint16_t return_p; + } index [16]; +#elif __MIPSEL + uint16_t rxc_jumbo_index; + uint16_t rxc_std_index; + uint16_t rxc_mini_index; + uint16_t reserved2; + struct { + uint16_t return_p; + uint16_t send_c; + } index [16]; +#else +#error "bcm5700: endian not set" +#endif +} t3_status_t; + +#define M_STATUS_UPDATED 0x00000001 +#define M_STATUS_LINKCHNG 0x00000002 +#define M_STATUS_ERROR 0x00000004 + +/* BCM570x Statistics Block format as a struct. Pages 112-120 */ + +typedef struct t3_stats_s { + uint64_t stats[L_MAC_STATS/sizeof(uint64_t)]; +} t3_stats_t; + +/* End of 570X defined data structures */ + + +typedef enum { + eth_state_uninit, + eth_state_off, + eth_state_on, +} eth_state_t; + +typedef struct t3_ether_s { + /* status block */ + volatile t3_status_t *status; /* should be cache-aligned */ + + /* PCI access information */ + uint32_t regbase; + uint32_t membase; + uint8_t irq; + pcitag_t tag; /* tag for configuration registers */ + + uint8_t hwaddr[6]; + uint16_t device; /* chip device code */ + uint8_t revision; /* chip revision */ + + eth_state_t state; /* current state */ + uint32_t intmask; /* interrupt mask */ + + /* packet lists */ + queue_t freelist; + uint8_t *pktpool; + queue_t rxqueue; + + /* rings */ + /* For now, support only the standard Rx Producer Ring */ + t3_rcv_bd_t *rxp_std; /* Standard Rx Producer Ring */ + uint32_t rxp_std_index; + uint32_t prev_rxp_std_index; + + /* For now, support only 1 priority */ + uint32_t rxr_entries; + t3_rcv_bd_t *rxr_1; /* Rx Return Ring 1 */ + uint32_t rxr_1_index; + t3_snd_bd_t *txp_1; /* Send Ring 1 */ + uint32_t txp_1_index; + uint32_t txc_1_index; + + cfe_devctx_t *devctx; + + /* PHY access */ + int phy_addr; + uint16_t phy_status; + uint16_t phy_ability; + uint16_t phy_xability; + + /* MII polling control */ + int phy_change; + int mii_polling; + + /* statistics block */ + t3_stats_t *stats; /* should be cache-aligned */ + + /* additional driver statistics */ + uint32_t rx_interrupts; + uint32_t tx_interrupts; + uint32_t bogus_interrupts; +} t3_ether_t; + + +/* Address mapping macros */ + +#define PTR_TO_PHYS(x) (K0_TO_PHYS((uintptr_t)(x))) +#define PHYS_TO_PTR(a) ((uint8_t *)PHYS_TO_K0(a)) + +/* All mappings through the PCI host bridge use match bits mode. */ +#define PHYS_TO_PCI(a) ((uint32_t) (a) | 0x20000000) +#define PCI_TO_PHYS(a) ((uint32_t) (a) & 0x1FFFFFFF) + +#define PCI_TO_PTR(a) (PHYS_TO_PTR(PCI_TO_PHYS(a))) +#define PTR_TO_PCI(x) (PHYS_TO_PCI(PTR_TO_PHYS(x))) + + +/* Chip access macros */ + +/* These macros attempt to be compatible with match-bits mode, + which may put the data and byte masks into the wrong 32-bit word + for 64-bit accesses. See the comment above on PIOSWAP. + Externally mastered DMA (control and data) uses match-bits and does + specify word-swaps when operating big endian. */ + +/* Most registers are 32 bits wide and are accessed by 32-bit + transactions. The mailbox registers and on-chip RAM are 64-bits + wide but are generally accessed by 32-bit transactions. + Furthermore, the documentation is ambiguous about which 32-bits of + the mailbox is significant. To localize the potential confusions, + we define macros for the 3 different cases. */ + +#if __long64 +#define READCSR(sc,csr) \ + (*((volatile uint32_t *) \ + (PHYS_TO_XKSEG_UNCACHED((sc)->regbase+(csr))))) + +#define WRITECSR(sc,csr,val) \ + (*((volatile uint32_t *) \ + (PHYS_TO_XKSEG_UNCACHED((sc)->regbase+(csr)))) = (val)) + +#if PIOSWAP +#define READMBOX(sc,csr) \ + (*((volatile uint32_t *) \ + (PHYS_TO_XKSEG_UNCACHED((sc)->regbase+((csr)+4))))) + +#define WRITEMBOX(sc,csr,val) \ + (*((volatile uint32_t *) \ + (PHYS_TO_XKSEG_UNCACHED((sc)->regbase+((csr)+4)))) = (val)) + +#define READMEM(sc,csr) \ + (*((volatile uint32_t *) \ + (PHYS_TO_XKSEG_UNCACHED((sc)->membase+(csr))))) + +#define WRITEMEM(sc,csr,val) \ + (*((volatile uint32_t *) \ + (PHYS_TO_XKSEG_UNCACHED((sc)->membase+(csr)))) = (val)) + +#else +#define READMBOX(sc,csr) \ + (*((volatile uint32_t *) \ + (PHYS_TO_XKSEG_UNCACHED((sc)->regbase+(csr))))) + +#define WRITEMBOX(sc,csr,val) \ + (*((volatile uint32_t *) \ + (PHYS_TO_XKSEG_UNCACHED((sc)->regbase+(csr)))) = (val)) + +#define READMEM(sc,csr) \ + (*((volatile uint32_t *) \ + (PHYS_TO_XKSEG_UNCACHED((sc)->membase+((csr) ^ 4))))) + +#define WRITEMEM(sc,csr,val) \ + (*((volatile uint32_t *) \ + (PHYS_TO_XKSEG_UNCACHED((sc)->membase+((csr) ^ 4)))) = (val)) + +#endif +#else +#define READCSR(sc,csr) \ + (hs_read32(PHYS_TO_XKSEG_UNCACHED((sc)->regbase+(csr)))) + +#define WRITECSR(sc,csr,val) \ + (hs_write32(PHYS_TO_XKSEG_UNCACHED((sc)->regbase+(csr), (val)))) + +#define READMBOX(sc,csr) \ + (hs_read32(PHYS_TO_XKSEG_UNCACHED((sc)->regbase+(csr))))) + +#define WRITEMBOX(sc,csr,val) \ + (hs_write32(PHYS_TO_XKSEG_UNCACHED((sc)->regbase+(csr)))) = (val)) + +#define READMEM(sc,csr) \ + (hs_read32(PHYS_TO_XKSEG_UNCACHED((sc)->membase+(csr)))) + +#define WRITEMEM(sc,csr,val) \ + (hs_write32(PHYS_TO_XKSEG_UNCACHED((sc)->membase+(csr), (val)))) + +#endif + + +/* Entry to and exit from critical sections (currently relative to + interrupts only, not SMP) */ + +#if CFG_INTERRUPTS +#define CS_ENTER(sc) cfe_disable_irq(sc->irq) +#define CS_EXIT(sc) cfe_enable_irq(sc->irq) +#else +#define CS_ENTER(sc) ((void)0) +#define CS_EXIT(sc) ((void)0) +#endif + + +static void +dumpseq(t3_ether_t *sc, int start, int next) +{ + int offset, i, j; + int columns = 4; + int lines = (((next - start)/4 + 1) + 3)/columns; + int step = lines*4; + + offset = start; + for (i = 0; i < lines; i++) { + xprintf("\nCSR"); + for (j = 0; j < columns; j++) { + if (offset + j*step < next) + xprintf(" %04X: %08X ", + offset+j*step, READCSR(sc, offset+j*step)); + } + offset += 4; + } + xprintf("\n"); +} + +static void +dumpcsrs(t3_ether_t *sc, const char *legend) +{ + xprintf("%s:\n", legend); + + /* Some device-specific PCI configuration registers */ + xprintf("-----PCI-----"); + dumpseq(sc, 0x68, 0x78); + + /* Some general control registers */ + xprintf("---General---"); + dumpseq(sc, 0x6800, 0x6810); + + xprintf("-------------\n"); +} + + +/* Packet management */ + +#define ETH_PKTPOOL_SIZE 64 +#define MIN_RXP_STD_BDS 32 + + +static eth_pkt_t * +eth_alloc_pkt(t3_ether_t *sc) +{ + eth_pkt_t *pkt; + + CS_ENTER(sc); + pkt = (eth_pkt_t *) q_deqnext(&sc->freelist); + CS_EXIT(sc); + if (!pkt) return NULL; + + pkt->buffer = pkt->data; + pkt->length = ETH_PKTBUF_LEN; + pkt->flags = 0; + + return pkt; +} + + +static void +eth_free_pkt(t3_ether_t *sc, eth_pkt_t *pkt) +{ + CS_ENTER(sc); + q_enqueue(&sc->freelist, &pkt->next); + CS_EXIT(sc); +} + +static void +eth_initfreelist(t3_ether_t *sc) +{ + int idx; + uint8_t *ptr; + eth_pkt_t *pkt; + + q_init(&sc->freelist); + + ptr = sc->pktpool; + for (idx = 0; idx < ETH_PKTPOOL_SIZE; idx++) { + pkt = (eth_pkt_t *) ptr; + eth_free_pkt(sc, pkt); + ptr += ETH_PKTBUF_SIZE; + } +} + + +/* Utilities */ + +static const char * +t3_devname(t3_ether_t *sc) +{ + return (sc->devctx != NULL ? cfe_device_name(sc->devctx) : "eth?"); +} + + +/* CRCs */ + +#define IEEE_CRC32_POLY 0xEDB88320UL /* CRC-32 Poly -- either endian */ + +uint32_t eth_crc32(const uint8_t *databuf, unsigned int datalen); +/*static*/ uint32_t +eth_crc32(const uint8_t *databuf, unsigned int datalen) +{ + unsigned int idx, bit, data; + uint32_t crc; + + crc = 0xFFFFFFFFUL; + for (idx = 0; idx < datalen; idx++) + for (data = *databuf++, bit = 0; bit < 8; bit++, data >>= 1) + crc = (crc >> 1) ^ (((crc ^ data) & 1) ? IEEE_CRC32_POLY : 0); + return crc; +} + + +/* Descriptor ring management */ + +static int +t3_add_rcvbuf(t3_ether_t *sc, eth_pkt_t *pkt) +{ + t3_rcv_bd_t *rxp; + + rxp = &(sc->rxp_std[sc->rxp_std_index]); + rxp->bufptr_lo = PTR_TO_PCI(pkt->buffer); + rxp->length = ETH_PKTBUF_LEN; + sc->rxp_std_index++; + if (sc->rxp_std_index == RXP_STD_ENTRIES) + sc->rxp_std_index = 0; + return 0; +} + +static void +t3_fillrxring(t3_ether_t *sc) +{ + eth_pkt_t *pkt; + unsigned rxp_ci, rxp_onring; + + rxp_ci = sc->status->rxc_std_index; /* Get a snapshot */ + + if (sc->rxp_std_index >= rxp_ci) + rxp_onring = sc->rxp_std_index - rxp_ci; + else + rxp_onring = (sc->rxp_std_index + RXP_STD_ENTRIES) - rxp_ci; + + while (rxp_onring < MIN_RXP_STD_BDS) { + pkt = eth_alloc_pkt(sc); + if (pkt == NULL) { + /* could not allocate a buffer */ + break; + } + if (t3_add_rcvbuf(sc, pkt) != 0) { + /* could not add buffer to ring */ + eth_free_pkt(sc, pkt); + break; + } + rxp_onring++; + } +} + +static void +t3_rx_callback(t3_ether_t *sc, eth_pkt_t *pkt) +{ + if (T3_DEBUG) show_packet('>', pkt); /* debug */ + + CS_ENTER(sc); + q_enqueue(&sc->rxqueue, &pkt->next); + CS_EXIT(sc); +} + +static void +t3_procrxring(t3_ether_t *sc) +{ + eth_pkt_t *pkt; + t3_rcv_bd_t *rxc; + volatile t3_status_t *status = sc->status; + + rxc = &(sc->rxr_1[sc->rxr_1_index]); + do { + pkt = ETH_PKT_BASE(PCI_TO_PTR(rxc->bufptr_lo)); + pkt->length = rxc->length; + if ((rxc->flags & RX_FLAG_ERROR) == 0) + t3_rx_callback(sc, pkt); + else { +#if T3_BRINGUP + xprintf("%s: rx error %04X\n", t3_devname(sc), rxc->error_flag); +#endif + eth_free_pkt(sc, pkt); /* Could optimize */ + } + sc->rxr_1_index++; + rxc++; + if (sc->rxr_1_index == sc->rxr_entries) { + sc->rxr_1_index = 0; + rxc = &(sc->rxr_1[0]); + } + } while (status->index[RI(1)].return_p != sc->rxr_1_index); + + /* Update the return ring */ + WRITEMBOX(sc, R_RCV_BD_RTN_CI(1), sc->rxr_1_index); + + /* Refill the producer ring */ + t3_fillrxring(sc); +} + + +static int +t3_transmit(t3_ether_t *sc, eth_pkt_t *pkt) +{ + t3_snd_bd_t *txp; + + if (T3_DEBUG) show_packet('<', pkt); /* debug */ + + txp = &(sc->txp_1[sc->txp_1_index]); + txp->bufptr_hi = 0; + txp->bufptr_lo = PTR_TO_PCI(pkt->buffer); + txp->length = pkt->length; + txp->flags = TX_FLAG_PACKET_END; + + sc->txp_1_index++; + if (sc->txp_1_index == TXP_RING_ENTRIES) + sc->txp_1_index = 0; + + WRITEMBOX(sc, R_SND_BD_PI(1), sc->txp_1_index); + + return 0; +} + + +static void +t3_proctxring(t3_ether_t *sc) +{ + eth_pkt_t *pkt; + t3_snd_bd_t *txc; + volatile t3_status_t *status = sc->status; + + txc = &(sc->txp_1[sc->txc_1_index]); + do { + pkt = ETH_PKT_BASE(PCI_TO_PTR(txc->bufptr_lo)); + eth_free_pkt(sc, pkt); + sc->txc_1_index++; + txc++; + if (sc->txc_1_index == TXP_RING_ENTRIES) { + sc->txc_1_index = 0; + txc = &(sc->txp_1[0]); + } + } while (status->index[RI(1)].send_c != sc->txc_1_index); +} + + +static void +t3_initrings(t3_ether_t *sc) +{ + int i; + t3_rcv_bd_t *rxp; + volatile t3_status_t *status = sc->status; + + /* Clear all Producer BDs */ + rxp = &(sc->rxp_std[0]); + for (i = 0; i < RXP_STD_ENTRIES; i++) { + rxp->bufptr_hi = rxp->bufptr_lo = 0; + rxp->length = 0; + rxp->index = i; + rxp->flags = 0; + rxp->type = 0; + rxp->ip_cksum = rxp->tcp_cksum = 0; + rxp++; + } + + /* Init the ring pointers */ + + sc->rxp_std_index = 0; status->rxc_std_index = 0; + sc->rxr_1_index = 0; status->index[RI(1)].return_p = 0; + sc->txp_1_index = 0; status->index[RI(1)].send_c = 0; + + /* Allocate some initial buffers for the Producer BD ring */ + sc->prev_rxp_std_index = 0; + t3_fillrxring(sc); + + /* Nothing consumed yet */ + sc->txc_1_index = 0; +} + +static void +t3_init(t3_ether_t *sc) +{ + /* Allocate buffer pool */ + sc->pktpool = KMALLOC(ETH_PKTPOOL_SIZE*ETH_PKTBUF_SIZE, CACHE_ALIGN); + eth_initfreelist(sc); + q_init(&sc->rxqueue); + + t3_initrings(sc); +} + +static void +t3_reinit(t3_ether_t *sc) +{ + eth_initfreelist(sc); + q_init(&sc->rxqueue); + + t3_initrings(sc); +} + + +/* Byte swap utilities. */ + +#define SWAP4(x) \ + ((((x) & 0x00FF) << 24) | \ + (((x) & 0xFF00) << 8) | \ + (((x) >> 8) & 0xFF00) | \ + (((x) >> 24) & 0x00FF)) + +static uint32_t +swap4(uint32_t x) +{ + uint32_t t; + + t = ((x & 0xFF00FF00) >> 8) | ((x & 0x00FF00FF) << 8); + return (t >> 16) | ((t & 0xFFFF) << 16); +} + + +/* EEPROM access functions (BCM5700 and BCM5701 version) */ + +/* The 570x chips support multiple access methods. We use "Auto Access", + which requires that + Miscellaneous_Local_Control.Auto_SEEPROM_Access be set, + Serial_EEprom.Address.HalfClock be programmed for <= 400 Hz. + (both done by initialization code) */ + +#define EP_MAX_RETRIES 500 +#define EP_DEVICE_ID 0x00 /* default ATMEL device ID */ + +static void +eeprom_access_init(t3_ether_t *sc) +{ + uint32_t mlctl; + + WRITECSR(sc, R_EEPROM_ADDR, M_EPADDR_RESET | V_EPADDR_HPERIOD(0x60)); + + mlctl = READCSR(sc, R_MISC_LOCAL_CTRL); + mlctl |= M_MLCTL_EPAUTOACCESS; + WRITECSR(sc, R_MISC_LOCAL_CTRL, mlctl); +} + + +static uint32_t +eeprom_read_word(t3_ether_t *sc, unsigned int offset) +{ + /* Assumes that SEEPROM is already set up for auto access. */ + uint32_t epaddr, epdata; + volatile uint32_t temp; + int i; + + epaddr = READCSR(sc, R_EEPROM_ADDR); + epaddr &= M_EPADDR_HPERIOD; + epaddr |= (V_EPADDR_ADDR(offset) | V_EPADDR_DEVID(EP_DEVICE_ID) + | M_EPADDR_RW | M_EPADDR_START | M_EPADDR_COMPLETE); + WRITECSR(sc, R_EEPROM_ADDR, epaddr); + temp = READCSR(sc, R_EEPROM_ADDR); /* push */ + + for (i = 0; i < EP_MAX_RETRIES; i++) { + temp = READCSR(sc, R_EEPROM_ADDR); + if ((temp & M_EPADDR_COMPLETE) != 0) + break; + cfe_usleep(10); + } + if (i == EP_MAX_RETRIES) + xprintf("%s: eeprom_read_word: no SEEPROM response @ %x\n", + t3_devname(sc), offset); + + epdata = READCSR(sc, R_EEPROM_DATA); /* little endian */ +#ifdef __MIPSEB + return swap4(epdata); +#else + return epdata; +#endif +} + +static int +eeprom_read_range(t3_ether_t *sc, unsigned int offset, unsigned int len, + uint32_t buf[]) +{ + int index; + + offset &= ~3; len &= ~3; /* 4-byte words only */ + index = 0; + + while (len > 0) { + buf[index++] = eeprom_read_word(sc, offset); + offset += 4; len -= 4; + } + + return index; +} + +static void +eeprom_dump_range(const char *label, + uint32_t buf[], unsigned int offset, unsigned int len) +{ + int index; + + xprintf("EEPROM: %s", label); + + offset &= ~3; len &= ~3; /* 4-byte words only */ + index = 0; + + for (index = 0; len > 0; index++) { + if (index % 8 == 0) + xprintf("\n %04x: ", offset); + xprintf(" %08x", buf[offset/4]); + offset += 4; len -= 4; + } + xprintf("\n"); +} + + +/* MII access functions. */ + +/* BCM5401 device specific registers */ + +#define MII_ISR 0x1A /* Interrupt Status Register */ +#define MII_IMR 0x1B /* Interrupt Mask Register */ + +#define M_INT_LINKCHNG 0x0002 + + +/* The 570x chips support multiple access methods. We use "Auto + Access", which requires that MDI_Control_Register.MDI_Select be + clear (done by initialization code) */ + +#define MII_MAX_RETRIES 5000 + +static void +mii_access_init(t3_ether_t *sc) +{ + WRITECSR(sc, R_MDI_CTRL, 0); /* here for now */ +#if !T3_AUTOPOLL + WRITECSR(sc, R_MI_MODE, V_MIMODE_CLKCNT(0x1F)); /* max divider */ +#endif +} + +/* XXX Autopolling should be disabled during reads and writes per the + manual, but doing so currently generates recurvise LINKCHNG + attentions. */ + +static uint16_t +mii_read_register(t3_ether_t *sc, int phy, int index) +{ + uint32_t mode; + uint32_t comm, val; + int i; + + mode = READCSR(sc, R_MI_MODE); +#if 0 /* for now */ + if (mode & M_MIMODE_POLLING) { + WRITECSR(sc, R_MI_MODE, mode & ~M_MIMODE_POLLING); + cfe_usleep(40); + } +#endif + + comm = (V_MICOMM_CMD_RD | V_MICOMM_PHY(phy) | V_MICOMM_REG(index) + | M_MICOMM_BUSY); + WRITECSR(sc, R_MI_COMM, comm); + + for (i = 0; i < MII_MAX_RETRIES; i++) { + val = READCSR(sc, R_MI_COMM); + if ((val & M_MICOMM_BUSY) == 0) + break; + } + if (i == MII_MAX_RETRIES) + xprintf("%s: mii_read_register: MII always busy\n", t3_devname(sc)); + +#if 0 + if (mode & M_MIMODE_POLLING) + WRITECSR(sc, R_MI_MODE, mode); +#endif + + return G_MICOMM_DATA(val); +} + +/* Register reads occasionally return spurious 0's. Verify a zero by + doing a second read, or spinning when a zero is "impossible". */ +static uint16_t +mii_read_register_v(t3_ether_t *sc, int phy, int index, int spin) +{ + uint32_t val; + + val = mii_read_register(sc, phy, index); + if (val == 0) { + do { + val = mii_read_register(sc, phy, index); + } while (spin && val == 0); + } + return val; +} + +static void +mii_write_register(t3_ether_t *sc, int phy, int index, uint16_t value) +{ + uint32_t mode; + uint32_t comm, val; + int i; + + mode = READCSR(sc, R_MI_MODE); +#if 0 /* for now */ + if (mode & M_MIMODE_POLLING) { + WRITECSR(sc, R_MI_MODE, mode & ~M_MIMODE_POLLING); + cfe_usleep(40); + } +#endif + + comm = (V_MICOMM_CMD_WR | V_MICOMM_PHY(phy) | V_MICOMM_REG(index) + | V_MICOMM_DATA(value) | M_MICOMM_BUSY); + WRITECSR(sc, R_MI_COMM, comm); + + for (i = 0; i < MII_MAX_RETRIES; i++) { + val = READCSR(sc, R_MI_COMM); + if ((val & M_MICOMM_BUSY) == 0) + break; + } + if (i == MII_MAX_RETRIES) + xprintf("%s: mii_write_register: MII always busy\n", t3_devname(sc)); + +#if 0 + if (mode & M_MIMODE_POLLING) + WRITECSR(sc, R_MI_MODE, mode); +#endif +} + +static int +mii_probe(t3_ether_t *sc) +{ +#if T3_AUTOPOLL /* With autopolling, the code below is not reliable. */ + return 1; /* Guaranteed for integrated PHYs */ +#else + int i; + uint16_t id1, id2; + + for (i = 0; i < 32; i++) { + id1 = mii_read_register(sc, i, MII_PHYIDR1); + id2 = mii_read_register(sc, i, MII_PHYIDR2); + if ((id1 != 0x0000 && id1 != 0xFFFF) || + (id2 != 0x0000 && id2 != 0xFFFF)) { + if (id1 != id2) return i; + } + } + return -1; +#endif +} + +#if T3_DEBUG +#define OUI_BCM 0x001018 +#define IDR_BCM 0x000818 +/* 5400: 4, 5401: 5, 5411: 6, 5421: e, 5701: 11 */ + +static void +mii_dump(t3_ether_t *sc, const char *label) +{ + int i; + uint16_t r; + uint32_t idr, part; + + xprintf("%s, MII:\n", label); + idr = part = 0; + + /* Required registers */ + for (i = 0x0; i <= 0x6; ++i) { + r = mii_read_register(sc, sc->phy_addr, i); + xprintf(" REG%02X: %04X", i, r); + if (i == 3 || i == 6) + xprintf("\n"); + if (i == MII_PHYIDR1) { + idr |= r << 6; + } + else if (i == MII_PHYIDR2) { + idr |= (r >> 10) & 0x3F; + part = (r >> 4) & 0x3F; + } + } + + /* GMII extensions */ + for (i = 0x9; i <= 0xA; ++i) { + r = mii_read_register(sc, sc->phy_addr, i); + xprintf(" REG%02X: %04X", i, r); + } + r = mii_read_register(sc, sc->phy_addr, 0xF); + xprintf(" REG%02X: %04X\n", 0xF, r); + + /* Broadcom extensions (54xx family) */ + if (idr == IDR_BCM) { + for (i = 0x10; i <= 0x14; i++) { + r = mii_read_register(sc, sc->phy_addr, i); + xprintf(" REG%02X: %04X", i, r); + } + xprintf("\n"); + for (i = 0x18; i <= 0x1A; i++) { + r = mii_read_register(sc, sc->phy_addr, i); + xprintf(" REG%02X: %04X", i, r); + } + xprintf("\n"); + } +} +#else +#define mii_dump(sc,label) +#endif + +static void +mii_enable_interrupts(t3_ether_t *sc) +{ + mii_write_register(sc, sc->phy_addr, MII_IMR, ~M_INT_LINKCHNG); +} + + +/* For 5700/5701, LINKCHNG is read-only in the status register and + cleared by writing to CFGCHNG | SYNCCHNG. For the 5705 + (empirically), LINKCHNG is cleared by writing a one, while CFGCHNG + and SYNCCHNG are unimplemented. Thus we can safely clear the + interrupt by writing ones to all the above bits. */ + +#define M_LINKCHNG_CLR \ + (M_EVT_LINKCHNG | M_MACSTAT_CFGCHNG | M_MACSTAT_SYNCCHNG) + +static int +mii_poll(t3_ether_t *sc) +{ + uint32_t macstat; + uint16_t status, ability, xability; + uint16_t isr; + + macstat = READCSR(sc, R_MAC_STATUS); + if ((macstat & (M_EVT_LINKCHNG | M_EVT_MIINT)) != 0) + WRITECSR(sc, R_MAC_STATUS, M_LINKCHNG_CLR); + + /* BMSR has read-to-clear bits; read twice. */ + + status = mii_read_register(sc, sc->phy_addr, MII_BMSR); + status = mii_read_register_v(sc, sc->phy_addr, MII_BMSR, 1); + ability = mii_read_register_v(sc, sc->phy_addr, MII_ANLPAR, 0); + if (status & BMSR_1000BT_XSR) + xability = mii_read_register_v(sc, sc->phy_addr, MII_K1STSR, 0); + else + xability = 0; + isr = mii_read_register(sc, sc->phy_addr, MII_ISR); + + if (status != sc->phy_status + || ability != sc->phy_ability || xability != sc->phy_xability) { +#if T3_DEBUG + xprintf("[%04x]", isr); + xprintf((macstat & (M_EVT_LINKCHNG | M_EVT_MIINT)) != 0 ? "+" : "-"); + + if (status != sc->phy_status) + xprintf(" ST: %04x %04x", sc->phy_status, status); + if (ability != sc->phy_ability) + xprintf(" AB: %04x %04x", sc->phy_ability, ability); + if (xability != sc->phy_xability) + xprintf(" XA: %04x %04x", sc->phy_xability, xability); + xprintf("\n"); +#endif + sc->phy_status = status; + sc->phy_ability = ability; + sc->phy_xability = xability; + return 1; + } + else if ((macstat & (M_EVT_LINKCHNG | M_EVT_MIINT)) != 0) { + isr = mii_read_register(sc, sc->phy_addr, MII_ISR); + } + return 0; +} + +static void +mii_set_speed(t3_ether_t *sc, int speed) +{ + uint16_t control; + + control = mii_read_register(sc, sc->phy_addr, MII_BMCR); + + control &= ~(BMCR_ANENABLE | BMCR_RESTARTAN); + mii_write_register(sc, sc->phy_addr, MII_BMCR, control); + control &= ~(BMCR_SPEED0 | BMCR_SPEED1 | BMCR_DUPLEX); + + switch (speed) { + case ETHER_SPEED_10HDX: + default: + break; + case ETHER_SPEED_10FDX: + control |= BMCR_DUPLEX; + break; + case ETHER_SPEED_100HDX: + control |= BMCR_SPEED100; + break; + case ETHER_SPEED_100FDX: + control |= BMCR_SPEED100 | BMCR_DUPLEX ; + break; + } + + mii_write_register(sc, sc->phy_addr, MII_BMCR, control); +} + +static void +mii_autonegotiate(t3_ether_t *sc) +{ + uint16_t control, status, remote, xremote; + unsigned int timeout; + int linkspeed; + uint32_t mode; + + linkspeed = ETHER_SPEED_UNKNOWN; + + /* Read twice to clear latching bits */ + status = mii_read_register(sc, sc->phy_addr, MII_BMSR); + status = mii_read_register_v(sc, sc->phy_addr, MII_BMSR, 1); + mii_dump(sc, "query PHY"); + + if ((status & (BMSR_AUTONEG | BMSR_LINKSTAT)) == + (BMSR_AUTONEG | BMSR_LINKSTAT)) + control = mii_read_register(sc, sc->phy_addr, MII_BMCR); + else { + for (timeout = 4*CFE_HZ; timeout > 0; timeout -= CFE_HZ/2) { + status = mii_read_register(sc, sc->phy_addr, MII_BMSR); + if ((status & BMSR_ANCOMPLETE) != 0 || timeout <= 0) + break; + cfe_sleep(CFE_HZ/2); + } + } + + remote = mii_read_register_v(sc, sc->phy_addr, MII_ANLPAR, 0); + + /* XXX Empirically, it appears best to set/keep PortMode non-null to + get STATUS_LINKCHNG assertions. */ + mode = READCSR(sc, R_MAC_MODE); + + xprintf("%s: Link speed: ", t3_devname(sc)); + if ((status & BMSR_ANCOMPLETE) != 0) { + /* A link partner was negogiated... */ + + if (status & BMSR_1000BT_XSR) + xremote = mii_read_register_v(sc, sc->phy_addr, MII_K1STSR, 0); + else + xremote = 0; + + mode &= ~(M_MACM_PORTMODE | M_MACM_HALFDUPLEX); + + if ((xremote & K1STSR_LP1KFD) != 0) { + xprintf("1000BaseT FDX\n"); + linkspeed = ETHER_SPEED_1000FDX; + mode |= V_MACM_PORTMODE(K_MACM_PORTMODE_GMII); + } + else if ((xremote & K1STSR_LP1KHD) != 0) { + xprintf("1000BaseT HDX\n"); + linkspeed = ETHER_SPEED_1000HDX; + mode |= V_MACM_PORTMODE(K_MACM_PORTMODE_GMII) | M_MACM_HALFDUPLEX; + } + else if ((remote & ANLPAR_TXFD) != 0) { + xprintf("100BaseT FDX\n"); + linkspeed = ETHER_SPEED_100FDX; + mode |= V_MACM_PORTMODE(K_MACM_PORTMODE_MII); + } + else if ((remote & ANLPAR_TXHD) != 0) { + xprintf("100BaseT HDX\n"); + linkspeed = ETHER_SPEED_100HDX; + mode |= V_MACM_PORTMODE(K_MACM_PORTMODE_MII) | M_MACM_HALFDUPLEX; + } + else if ((remote & ANLPAR_10FD) != 0) { + xprintf("10BaseT FDX\n"); + linkspeed = ETHER_SPEED_10FDX; + mode |= V_MACM_PORTMODE(K_MACM_PORTMODE_MII); + } + else if ((remote & ANLPAR_10HD) != 0) { + xprintf("10BaseT HDX\n"); + linkspeed = ETHER_SPEED_10HDX; + mode |= V_MACM_PORTMODE(K_MACM_PORTMODE_MII) | M_MACM_HALFDUPLEX; + } + + WRITECSR(sc, R_MAC_MODE, mode); + } + else { + /* no link partner convergence */ + xprintf("Unknown\n"); + linkspeed = ETHER_SPEED_UNKNOWN; + remote = xremote = 0; + if (G_MACM_PORTMODE(mode) == K_MACM_PORTMODE_NONE) { + /* Keep any previous port mode as the one most likely to reappear. + Otherwise, choose one, and 10/100FDX is more likely. */ + mode |= V_MACM_PORTMODE(K_MACM_PORTMODE_MII); + WRITECSR(sc, R_MAC_MODE, mode); + } + } + + /* clear latching bits, XXX fix flakey reads */ + status = mii_read_register_v(sc, sc->phy_addr, MII_BMSR, 1); + (void)mii_read_register(sc, sc->phy_addr, MII_ISR); + + sc->phy_status = status; + sc->phy_ability = remote; + sc->phy_xability = xremote; + + mii_dump(sc, "final PHY"); +} + + +static void +t3_clear(t3_ether_t *sc, unsigned reg, uint32_t mask) +{ + uint32_t val; + int timeout; + + val = READCSR(sc, reg); + val &= ~mask; + WRITECSR(sc, reg, val); + val = READCSR(sc, reg); + + for (timeout = 4000; (val & mask) != 0 && timeout > 0; timeout -= 100) { + cfe_usleep(100); + val = READCSR(sc, reg); + } + if (timeout <= 0) + xprintf("%s: cannot clear %04X/%08X\n", t3_devname(sc), reg, mask); +} + + +/* The following functions collectively implement the recommended + BCM5700 Initialization Procedure (Section 8: Device Control) */ + +static int +t3_coldreset(t3_ether_t *sc) +{ + pcireg_t cmd; + pcireg_t bhlc, subsysid; + pcireg_t bar0, bar1; + pcireg_t cmdx; + uint32_t mhc, mcr, mcfg; + uint32_t mode; + int timeout; + + /* Steps 1-18 */ + /* Enable memory, also clear R/WC status bits (1) */ + cmd = pci_conf_read(sc->tag, PCI_COMMAND_STATUS_REG); + cmd |= PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_MASTER_ENABLE; + pci_conf_write(sc->tag, PCI_COMMAND_STATUS_REG, cmd); + + /* Clear and disable INTA output. (2) */ + mhc = READCSR(sc, R_MISC_HOST_CTRL); + mhc |= M_MHC_MASKPCIINT | M_MHC_CLEARINTA; + WRITECSR(sc, R_MISC_HOST_CTRL, mhc); + + /* Save some config registers modified by core clock reset (3). */ + bhlc = pci_conf_read(sc->tag, PCI_BHLC_REG); + subsysid = pci_conf_read(sc->tag, PCI_SUBSYS_ID_REG); + /* Empirically, these are clobbered too. */ + bar0 = pci_conf_read(sc->tag, PCI_MAPREG(0)); + bar1 = pci_conf_read(sc->tag, PCI_MAPREG(1)); + + /* Reset the core clocks (4, 5). */ + mcfg = READCSR(sc, R_MISC_CFG); + mcfg |= M_MCFG_CORERESET; + WRITECSR(sc, R_MISC_CFG, mcfg); + cfe_usleep(100); /* 100 usec delay */ + + /* NB: Until the BARs are restored and reenabled, only PCI + configuration reads and writes will succeed. */ + + /* Reenable MAC memory (7) */ + pci_conf_write(sc->tag, PCI_MAPREG(0), bar0); + pci_conf_write(sc->tag, PCI_MAPREG(1), bar1); + (void)pci_conf_read(sc->tag, PCI_MAPREG(1)); /* push */ + pci_conf_write(sc->tag, PCI_COMMAND_STATUS_REG, cmd); + (void)pci_conf_read(sc->tag, PCI_COMMAND_STATUS_REG); /* push */ + + /* Undo some of the resets (6) */ + mhc = READCSR(sc, R_MISC_HOST_CTRL); + mhc |= M_MHC_MASKPCIINT; + WRITECSR(sc, R_MISC_HOST_CTRL, mhc); + + /* Verify that core clock resets completed and autocleared. */ + mcfg = READCSR(sc, R_MISC_CFG); + if ((mcfg & M_MCFG_CORERESET) != 0) { + xprintf("bcm5700: core clocks stuck in reset\n"); + } + + /* Configure PCI-X (8) */ + if (sc->device != K_PCI_ID_BCM5705) { + cmdx = pci_conf_read(sc->tag, PCI_PCIX_CMD_REG); + cmdx &= ~PCIX_CMD_RLXORDER_ENABLE; + pci_conf_write(sc->tag, PCI_PCIX_CMD_REG, cmdx); + } + + /* Enable memory arbiter (9) */ + mode = READCSR(sc, R_MEM_MODE); + mode |= M_MAM_ENABLE; /* enable memory arbiter */ + WRITECSR(sc, R_MEM_MODE, mode); + + /* Assume no external SRAM for now (10) */ + + /* Set up MHC for endianness and write enables (11-15) */ + mhc = READCSR(sc, R_MISC_HOST_CTRL); +#ifdef __MIPSEL + mhc |= M_MHC_ENWORDSWAP; /* XXX check this */ +#endif +#ifdef __MIPSEB + /* Since we use match-bits for Direct PCI access, don't swap bytes. */ +#if PIOSWAP + mhc |= M_MHC_ENWORDSWAP; +#endif +#endif + mhc |= M_MHC_ENINDIRECT | M_MHC_ENPCISTATERW | M_MHC_ENCLKCTRLRW; + WRITECSR(sc, R_MISC_HOST_CTRL, mhc); + + /* Set byte swapping (16, 17) */ + mcr = READCSR(sc, R_MODE_CTRL); +#ifdef __MIPSEL + mcr &= ~M_MCTL_BSWAPDATA; + mcr |= M_MCTL_WSWAPCTRL | M_MCTL_WSWAPDATA; /* XXX check this */ +#endif +#ifdef __MIPSEB + mcr &= ~(M_MCTL_BSWAPCTRL | M_MCTL_BSWAPDATA); + mcr |= M_MCTL_WSWAPCTRL | M_MCTL_WSWAPDATA; +#endif + WRITECSR(sc, R_MODE_CTRL, mcr); + + /* Disable PXE restart, wait for firmware (18, 19) */ + if (READMEM(sc, A_PXE_MAILBOX) != T3_MAGIC_NUMBER) { + /* Apparently, if the magic number is already set, firmware + ignores this attempted handshake. */ + WRITEMEM(sc, A_PXE_MAILBOX, T3_MAGIC_NUMBER); + for (timeout = CFE_HZ; timeout > 0; timeout -= CFE_HZ/10) { + if (READMEM(sc, A_PXE_MAILBOX) == ~T3_MAGIC_NUMBER) + break; + cfe_sleep(CFE_HZ/10); + } + if (READMEM(sc, A_PXE_MAILBOX) != ~T3_MAGIC_NUMBER) + xprintf("bcm5700: no firmware PXE rendevous\n"); + } + else + xprintf("bcm5700: PXE magic number already set\n"); + + /* Clear Ethernet MAC Mode (20) */ + WRITECSR(sc, R_MAC_MODE, 0x00000000); + + /* Restore remaining config registers (21) */ + pci_conf_write(sc->tag, PCI_BHLC_REG, bhlc); + pci_conf_write(sc->tag, PCI_SUBSYS_ID_REG, subsysid); + + return 0; +} + +/* XXX Not clear that the following is useful. */ +static int +t3_warmreset(t3_ether_t *sc) +{ + uint32_t mode; + + /* Enable memory arbiter (9) */ + mode = READCSR(sc, R_MEM_MODE); + mode |= M_MAM_ENABLE; /* enable memory arbiter */ + WRITECSR(sc, R_MEM_MODE, mode); + + /* Clear Ethernet MAC Mode (20) */ + WRITECSR(sc, R_MAC_MODE, 0x00000000); + + return 0; +} + + +static int +t3_init_registers(t3_ether_t *sc) +{ + unsigned offset; + uint32_t dmac, mcr, mcfg; + + /* Steps 22-29 */ + + /* Clear MAC statistics block (22) */ + for (offset = A_MAC_STATS; offset < A_MAC_STATS+L_MAC_STATS; offset += 4) { + WRITEMEM(sc, offset, 0); + } + + /* Clear driver status memory region (23) */ + /* ASSERT (sizeof(t3_status_t) == L_MAC_STATUS) */ + memset((uint8_t *)sc->status, 0, sizeof(t3_status_t)); + + /* Set up PCI DMA control (24) */ + dmac = READCSR(sc, R_DMA_RW_CTRL); + dmac &= ~(M_DMAC_RDCMD | M_DMAC_WRCMD | M_DMAC_MINDMA); + dmac |= V_DMAC_RDCMD(K_PCI_MEMRD) | V_DMAC_WRCMD(K_PCI_MEMWR); + switch (sc->device) { + case K_PCI_ID_BCM5700: + case K_PCI_ID_BCM5701: + case K_PCI_ID_BCM5702: + dmac |= V_DMAC_MINDMA(0xF); /* "Recommended" */ + break; + default: + dmac |= V_DMAC_MINDMA(0x0); + break; + } + WRITECSR(sc, R_DMA_RW_CTRL, dmac); + + /* Set DMA byte swapping (25) - XXX repeat of (17) */ + mcr = READCSR(sc, R_MODE_CTRL); +#ifdef __MIPSEL + mcr &= ~M_MCTL_BSWAPDATA; + mcr |= M_MCTL_WSWAPCTRL | M_MCTL_WSWAPDATA; /* XXX check this */ +#endif +#ifdef __MIPSEB + mcr &= ~(M_MCTL_BSWAPCTRL | M_MCTL_BSWAPDATA); + mcr |= M_MCTL_WSWAPCTRL | M_MCTL_WSWAPDATA; +#endif + WRITECSR(sc, R_MODE_CTRL, mcr); + + /* Configure host rings (26) */ + mcr |= M_MCTL_HOSTBDS; + WRITECSR(sc, R_MODE_CTRL, mcr); + + /* Indicate driver ready, disable checksums (27, 28) */ + mcr |= M_MCTL_HOSTUP; + mcr |= (M_MCTL_NOTXPHSUM | M_MCTL_NORXPHSUM); + WRITECSR(sc, R_MODE_CTRL, mcr); + + /* Configure timer (29) */ + mcfg = READCSR(sc, R_MISC_CFG); + mcfg &= ~M_MCFG_PRESCALER; + mcfg |= V_MCFG_PRESCALER(66-1); /* 66 MHz */ + WRITECSR(sc, R_MISC_CFG, mcfg); + + return 0; +} + +static int +t3_init_pools(t3_ether_t *sc) +{ + uint32_t mode; + int timeout; + + /* Steps 30-36. These use "recommended" settings (p 150) */ + + /* Configure the MAC memory pool (30) */ + if (sc->device != K_PCI_ID_BCM5705) { + WRITECSR(sc, R_BMGR_MBUF_BASE, A_BUFFER_POOL); + WRITECSR(sc, R_BMGR_MBUF_LEN, L_BUFFER_POOL); + } + else { + /* Note: manual appears to recommend not even writing these (?) */ + /* WRITECSR(sc, R_BMGR_MBUF_BASE, A_RXMBUF); */ + /* WRITECSR(sc, R_BMGR_MBUF_LEN, 0x8000); */ + } + + /* Configure the MAC DMA resource pool (31) */ + WRITECSR(sc, R_BMGR_DMA_BASE, A_DMA_DESCS); + WRITECSR(sc, R_BMGR_DMA_LEN, L_DMA_DESCS); + + /* Configure the MAC memory watermarks (32) */ + WRITECSR(sc, R_BMGR_MBUF_DMA_LOW, 0x50); + WRITECSR(sc, R_BMGR_MBUF_RX_LOW, 0x20); + WRITECSR(sc, R_BMGR_MBUF_HIGH, 0x60); + + /* Configure the DMA resource watermarks (33) */ + WRITECSR(sc, R_BMGR_DMA_LOW, 5); + WRITECSR(sc, R_BMGR_DMA_HIGH, 10); + + /* Enable the buffer manager (34, 35) */ + mode = READCSR(sc, R_BMGR_MODE); + mode |= (M_BMODE_ENABLE | M_BMODE_MBUFLOWATTN); + WRITECSR(sc, R_BMGR_MODE, mode); + for (timeout = CFE_HZ/2; timeout > 0; timeout -= CFE_HZ/10) { + mode = READCSR(sc, R_BMGR_MODE); + if ((mode & M_BMODE_ENABLE) != 0) + break; + cfe_sleep(CFE_HZ/10); + } + if ((mode & M_BMODE_ENABLE) == 0) + xprintf("bcm5700: buffer manager not enabled\n"); + + /* Enable internal queues (36) */ + WRITECSR(sc, R_FTQ_RESET, 0xFFFFFFFF); + (void)READCSR(sc, R_FTQ_RESET); /* push */ + cfe_sleep(1); + WRITECSR(sc, R_FTQ_RESET, 0x00000000); + + return 0; +} + +static int +t3_init_rings(t3_ether_t *sc) +{ + unsigned rcbp; + int i; + + /* Steps 37-46 */ + + /* Initialize RCBs for Standard Receive Buffer Ring (37) */ + WRITECSR(sc, R_STD_RCV_BD_RCB+RCB_HOST_ADDR_HIGH, 0); + WRITECSR(sc, R_STD_RCV_BD_RCB+RCB_HOST_ADDR_LOW, PTR_TO_PCI(sc->rxp_std)); + WRITECSR(sc, R_STD_RCV_BD_RCB+RCB_CTRL, V_RCB_MAXLEN(ETH_PKTBUF_LEN)); + WRITECSR(sc, R_STD_RCV_BD_RCB+RCB_NIC_ADDR, A_STD_RCV_RINGS); + + /* Disable RCBs for Jumbo and Mini Receive Buffer Rings (38,39) */ + WRITECSR(sc, R_JUMBO_RCV_BD_RCB+RCB_CTRL, + RCB_FLAG_USE_EXT_RCV_BD | RCB_FLAG_RING_DISABLED); + WRITECSR(sc, R_JUMBO_RCV_BD_RCB+RCB_NIC_ADDR, A_JUMBO_RCV_RINGS); + WRITECSR(sc, R_MINI_RCV_BD_RCB+RCB_CTRL, RCB_FLAG_RING_DISABLED); + WRITECSR(sc, R_MINI_RCV_BD_RCB+RCB_NIC_ADDR, 0xe000); + + /* Set BD ring replenish thresholds (40) */ + WRITECSR(sc, R_MINI_RCV_BD_THRESH, 128); +#if T3_BRINGUP + WRITECSR(sc, R_STD_RCV_BD_THRESH, 1); +#else + WRITECSR(sc, R_STD_RCV_BD_THRESH, 25); +#endif + WRITECSR(sc, R_JUMBO_RCV_BD_THRESH, 16); + + /* Disable unused send producer rings 2-16 (41) */ + for (rcbp = A_SND_RCB(1); rcbp <= A_SND_RCB(16); rcbp += RCB_SIZE) + WRITEMEM(sc, rcbp+RCB_CTRL, RCB_FLAG_RING_DISABLED); + + /* Initialize send producer index registers (42) */ + for (i = 1; i <= TXP_MAX_RINGS; i++) { + WRITEMBOX(sc, R_SND_BD_PI(i), 0); + WRITEMBOX(sc, R_SND_BD_NIC_PI(i), 0); + } + + /* Initialize send producer ring 1 (43) */ + WRITEMEM(sc, A_SND_RCB(1)+RCB_HOST_ADDR_HIGH, 0); + WRITEMEM(sc, A_SND_RCB(1)+RCB_HOST_ADDR_LOW, PTR_TO_PCI(sc->txp_1)); + WRITEMEM(sc, A_SND_RCB(1)+RCB_CTRL, V_RCB_MAXLEN(TXP_RING_ENTRIES)); + WRITEMEM(sc, A_SND_RCB(1)+RCB_NIC_ADDR, A_SND_RINGS); + + /* Disable unused receive return rings (44) */ + for (rcbp = A_RTN_RCB(1); rcbp <= A_RTN_RCB(16); rcbp += RCB_SIZE) + WRITEMEM(sc, rcbp+RCB_CTRL, RCB_FLAG_RING_DISABLED); + + /* Initialize receive return ring 1 (45) */ + WRITEMEM(sc, A_RTN_RCB(1)+RCB_HOST_ADDR_HIGH, 0); + WRITEMEM(sc, A_RTN_RCB(1)+RCB_HOST_ADDR_LOW, PTR_TO_PCI(sc->rxr_1)); + WRITEMEM(sc, A_RTN_RCB(1)+RCB_CTRL, V_RCB_MAXLEN(sc->rxr_entries)); + WRITEMEM(sc, A_RTN_RCB(1)+RCB_NIC_ADDR, 0x0000); + + /* Initialize receive producer ring mailboxes (46) */ + WRITEMBOX(sc, R_RCV_BD_STD_PI, 0); + WRITEMBOX(sc, R_RCV_BD_JUMBO_PI, 0); + WRITEMBOX(sc, R_RCV_BD_MINI_PI, 0); + + return 0; +} + +static int +t3_configure_mac(t3_ether_t *sc) +{ + uint32_t low, high; + uint32_t seed; + int i; + + /* Steps 47-52 */ + + /* Configure the MAC unicast address (47) */ + high = (sc->hwaddr[0] << 8) | (sc->hwaddr[1]); + low = ((sc->hwaddr[2] << 24) | (sc->hwaddr[3] << 16) + | (sc->hwaddr[4] << 8) | sc->hwaddr[5]); + /* For now, use a single MAC address */ + WRITECSR(sc, R_MAC_ADDR1_HIGH, high); WRITECSR(sc, R_MAC_ADDR1_LOW, low); + WRITECSR(sc, R_MAC_ADDR2_HIGH, high); WRITECSR(sc, R_MAC_ADDR2_LOW, low); + WRITECSR(sc, R_MAC_ADDR3_HIGH, high); WRITECSR(sc, R_MAC_ADDR3_LOW, low); + WRITECSR(sc, R_MAC_ADDR4_HIGH, high); WRITECSR(sc, R_MAC_ADDR4_LOW, low); + + /* Configure the random backoff seed (48) */ + seed = 0; + for (i = 0; i < 6; i++) + seed += sc->hwaddr[i]; + seed &= 0x3FF; + WRITECSR(sc, R_TX_BACKOFF, seed); + + /* Configure the MTU (49) */ + WRITECSR(sc, R_RX_MTU, MAX_ETHER_PACK+VLAN_TAG_LEN); + + /* Configure the tx IPG (50) */ + WRITECSR(sc, R_TX_LENS, + V_TXLEN_SLOT(0x20) | V_TXLEN_IPG(0x6) | V_TXLEN_IPGCRS(0x2)); + + /* Configure the default rx return ring 1 (51) */ + WRITECSR(sc, R_RX_RULES_CFG, V_RULESCFG_DEFAULT(1)); + + /* Configure the receive lists and enable statistics (52) */ + WRITECSR(sc, R_RCV_LIST_CFG, + V_LISTCFG_GROUP(1) | V_LISTCFG_ACTIVE(1) | V_LISTCFG_BAD(1)); + /* was V_LISTCFG_DEFAULT(1) | V_LISTCFG_ACTIVE(16) | V_LISTCFG_BAD(1) */ + + return 0; +} + +static int +t3_enable_stats(t3_ether_t *sc) +{ + uint32_t ctrl; + + /* Steps 53-56 */ + + /* Enable rx stats (53,54) */ + WRITECSR(sc, R_RCV_LIST_STATS_ENB, 0xFFFFFF); + ctrl = READCSR(sc, R_RCV_LIST_STATS_CTRL); + ctrl |= M_STATS_ENABLE; + WRITECSR(sc, R_RCV_LIST_STATS_CTRL, ctrl); + + /* Enable tx stats (55,56) */ + WRITECSR(sc, R_SND_DATA_STATS_ENB, 0xFFFFFF); + ctrl = READCSR(sc, R_SND_DATA_STATS_CTRL); + ctrl |= (M_STATS_ENABLE | M_STATS_FASTUPDATE); + WRITECSR(sc, R_SND_DATA_STATS_CTRL, ctrl); + + return 0; +} + +static int +t3_init_coalescing(t3_ether_t *sc) +{ + uint32_t mode; + int timeout; + + /* Steps 57-68 */ + + /* Disable the host coalescing engine (57, 58) */ + WRITECSR(sc, R_HOST_COAL_MODE, 0); + for (timeout = CFE_HZ/2; timeout > 0; timeout -= CFE_HZ/10) { + mode = READCSR(sc, R_HOST_COAL_MODE); + if (mode == 0) + break; + cfe_sleep(CFE_HZ/10); + } + if (mode != 0) + xprintf("bcm5700: coalescing engine not disabled\n"); + + /* Set coalescing parameters (59-62) */ +#if T3_BRINGUP + WRITECSR(sc, R_RCV_COAL_TICKS, 0); + WRITECSR(sc, R_RCV_COAL_MAX_CNT, 1); +#else + WRITECSR(sc, R_RCV_COAL_TICKS, 150); + WRITECSR(sc, R_RCV_COAL_MAX_CNT, 10); +#endif + WRITECSR(sc, R_RCV_COAL_INT_TICKS, 0); + WRITECSR(sc, R_RCV_COAL_INT_CNT, 0); +#if T3_BRINGUP + WRITECSR(sc, R_SND_COAL_TICKS, 0); + WRITECSR(sc, R_SND_COAL_MAX_CNT, 1); +#else + WRITECSR(sc, R_SND_COAL_TICKS, 150); + WRITECSR(sc, R_SND_COAL_MAX_CNT, 10); +#endif + WRITECSR(sc, R_SND_COAL_INT_TICKS, 0); + WRITECSR(sc, R_SND_COAL_INT_CNT, 0); + + /* Initialize host status block address (63) */ + WRITECSR(sc, R_STATUS_HOST_ADDR, 0); + WRITECSR(sc, R_STATUS_HOST_ADDR+4, PTR_TO_PCI(sc->status)); + + /* Initialize host statistics block address (64) */ + WRITECSR(sc, R_STATS_HOST_ADDR, 0); + WRITECSR(sc, R_STATS_HOST_ADDR+4, PTR_TO_PCI(sc->stats)); + + /* Set statistics block NIC address and tick count (65, 66) */ + WRITECSR(sc, R_STATS_TICKS, 1000000); + WRITECSR(sc, R_STATS_BASE_ADDR, A_MAC_STATS); + + /* Set status block NIC address (67) */ + WRITECSR(sc, R_STATUS_BASE_ADDR, A_MAC_STATUS); + + /* Enable the host coalescing engine (68) */ + WRITECSR(sc, R_HOST_COAL_MODE, M_HCM_ENABLE); + + return 0; +} + +static int +t3_init_dma(t3_ether_t *sc) +{ + uint32_t mode; + + /* Steps 69-87 */ + + /* Enable receive BD completion, placement, and selector blocks (69-71) */ + WRITECSR(sc, R_RCV_BD_COMP_MODE, M_MODE_ENABLE | M_MODE_ATTNENABLE); + WRITECSR(sc, R_RCV_LIST_MODE, M_MODE_ENABLE); + if (sc->device != K_PCI_ID_BCM5705) { + WRITECSR(sc, R_RCV_LIST_SEL_MODE, M_MODE_ENABLE | M_MODE_ATTNENABLE); + } + + /* Enable DMA engines, enable and clear statistics (72, 73) */ + mode = READCSR(sc, R_MAC_MODE); + mode |= (M_MACM_FHDEENB | M_MACM_RDEENB | M_MACM_TDEENB | + M_MACM_RXSTATSENB | M_MACM_RXSTATSCLR | + M_MACM_TXSTATSENB | M_MACM_TXSTATSCLR); +#if T3_AUTOPOLL + mode |= V_MACM_PORTMODE(K_MACM_PORTMODE_MII); +#endif + + WRITECSR(sc, R_MAC_MODE, mode); + +#if T3_AUTOPOLL + WRITECSR(sc, R_MISC_LOCAL_CTRL, M_MLCTL_INTATTN); +#endif + + /* Configure GPIOs (74) - skipped */ + + /* Clear interrupt mailbox (75) */ + WRITEMBOX(sc, R_INT_MBOX(0), 0); + + /* Enable DMA completion block (76) */ + if (sc->device != K_PCI_ID_BCM5705) { + WRITECSR(sc, R_DMA_COMP_MODE, M_MODE_ENABLE); + } + + /* Configure write and read DMA modes (77, 78) */ + WRITECSR(sc, R_WR_DMA_MODE, M_MODE_ENABLE | M_ATTN_ALL); + WRITECSR(sc, R_RD_DMA_MODE, M_MODE_ENABLE | M_ATTN_ALL); + + return 0; +} + +static int +t3_init_enable(t3_ether_t *sc) +{ + uint32_t mhc; + uint32_t pmcs; +#if T3_AUTOPOLL + uint32_t mode, mask; +#else + int i; +#endif + + /* Steps 79-97 */ + + /* Enable completion functional blocks (79-82) */ + WRITECSR(sc, R_RCV_COMP_MODE, M_MODE_ENABLE | M_MODE_ATTNENABLE); + if (sc->device != K_PCI_ID_BCM5705) { + WRITECSR(sc, R_MBUF_FREE_MODE, M_MODE_ENABLE); + } + WRITECSR(sc, R_SND_DATA_COMP_MODE, M_MODE_ENABLE); + WRITECSR(sc, R_SND_BD_COMP_MODE, M_MODE_ENABLE | M_MODE_ATTNENABLE); + + /* Enable initiator functional blocks (83-86) */ + WRITECSR(sc, R_RCV_BD_INIT_MODE, M_MODE_ENABLE | M_MODE_ATTNENABLE); + WRITECSR(sc, R_RCV_DATA_INIT_MODE, M_MODE_ENABLE | M_RCVINITMODE_RTNSIZE); + WRITECSR(sc, R_SND_DATA_MODE, M_MODE_ENABLE); + WRITECSR(sc, R_SND_BD_INIT_MODE, M_MODE_ENABLE | M_MODE_ATTNENABLE); + + /* Enable the send BD selector (87) */ + WRITECSR(sc, R_SND_BD_SEL_MODE, M_MODE_ENABLE | M_MODE_ATTNENABLE); + + /* Download firmware (88) - skipped */ + + /* Enable the MAC (89,90) */ + WRITECSR(sc, R_TX_MODE, M_MODE_ENABLE); /* optional flow control */ + WRITECSR(sc, R_RX_MODE, M_MODE_ENABLE); /* other options */ + + /* Disable auto-polling (91) */ + mii_access_init(sc); + + /* Configure power state (92) */ + pmcs = READCSR(sc, PCI_PMCSR_REG); + pmcs &= ~PCI_PMCSR_STATE_MASK; + pmcs |= PCI_PMCSR_STATE_D0; + WRITECSR(sc, PCI_PMCSR_REG, pmcs); + +#if T3_AUTOPOLL + /* Program hardware LED control (93) */ + WRITECSR(sc, R_MAC_LED_CTRL, 0x00); /* LEDs at PHY layer */ +#endif + +#if T3_AUTOPOLL + /* Ack/clear link change events */ + WRITECSR(sc, R_MAC_STATUS, M_LINKCHNG_CLR); + WRITECSR(sc, R_MI_STATUS, 0); + + /* Enable autopolling */ + mode = READCSR(sc, R_MI_MODE); + mode |= M_MIMODE_POLLING | 0x000c000; + WRITECSR(sc, R_MI_MODE, mode); + + /* Enable link state attentions */ + mask = READCSR(sc, R_MAC_EVENT_ENB); + mask |= M_EVT_LINKCHNG; + WRITECSR(sc, R_MAC_EVENT_ENB, mask); +#else + /* Initialize link (94) */ + WRITECSR(sc, R_MI_STATUS, M_MISTAT_LINKED); + + /* Start autonegotiation (95) - see t3_initlink below */ + + /* Setup multicast filters (96) */ + for (i = 0; i < 4; i++) + WRITECSR(sc, R_MAC_HASH(i), 0); +#endif /* T3_AUTOPOLL */ + + /* Enable interrupts (97) */ + mhc = READCSR(sc, R_MISC_HOST_CTRL); + mhc &= ~M_MHC_MASKPCIINT; + WRITECSR(sc, R_MISC_HOST_CTRL, mhc); + + return 0; +} + + +static void +t3_initlink(t3_ether_t *sc) +{ + uint32_t mcr; + + sc->phy_addr = mii_probe(sc); + if (sc->phy_addr < 0) { + xprintf("%s: no PHY found\n", t3_devname(sc)); + return; + } +#if T3_DEBUG + xprintf("%s: PHY addr %d\n", t3_devname(sc), sc->phy_addr); +#endif + if (1) /* XXX Support only autonegotiation for now */ + mii_autonegotiate(sc); + else + mii_set_speed(sc, ETHER_SPEED_10HDX); + + mii_enable_interrupts(sc); + + mcr = READCSR(sc, R_MODE_CTRL); + mcr |= M_MCTL_MACINT; + WRITECSR(sc, R_MODE_CTRL, mcr); + + sc->mii_polling = 0; + sc->phy_change = 0; +} + +static void +t3_shutdownlink(t3_ether_t *sc) +{ + uint32_t mcr; + + mcr = READCSR(sc, R_MODE_CTRL); + mcr &= ~M_MCTL_MACINT; + WRITECSR(sc, R_MODE_CTRL, mcr); + + WRITECSR(sc, R_MAC_EVENT_ENB, 0); + + /* The manual is fuzzy about what to do with the PHY at this + point. Empirically, resetting the 5705 PHY (but not others) + will cause it to get stuck in 10/100 MII mode. */ + if (sc->device != K_PCI_ID_BCM5705) + mii_write_register(sc, sc->phy_addr, MII_BMCR, BMCR_RESET); + + sc->mii_polling = 0; + sc->phy_change = 0; +} + + +static void +t3_hwinit(t3_ether_t *sc) +{ + if (sc->state != eth_state_on) { + + if (sc->state == eth_state_uninit) { + WRITECSR(sc, R_MEMWIN_BASE_ADDR, 0); /* Default memory window */ + t3_coldreset(sc); + } + else + t3_warmreset(sc); + + t3_init_registers(sc); + t3_init_pools(sc); + t3_init_rings(sc); + t3_configure_mac(sc); + t3_enable_stats(sc); + t3_init_coalescing(sc); + t3_init_dma(sc); + t3_init_enable(sc); +#if T3_DEBUG + dumpcsrs(sc, "end init"); +#else + (void)dumpcsrs; +#endif + + eeprom_access_init(sc); +#if T3_DEBUG + { + uint32_t eeprom[0x100/4]; + int i; + + cfe_sleep(1); + /* XXX Apparently a few reads can be required to get the + AutoAccess logic into a good state. ??? */ + for (i = 0; i < 4; i++) { + eeprom_read_range(sc, 0, 4, eeprom); + } + + eeprom_read_range(sc, 0, sizeof(eeprom), eeprom); + eeprom_dump_range("Boot Strap", eeprom, 0x00, 20); + eeprom_dump_range("Manufacturing Info", eeprom, 0x74, 140); + } +#else + (void)eeprom_read_range; + (void)eeprom_dump_range; +#endif + + t3_initlink(sc); + + sc->state = eth_state_off; + } +} + +static void +t3_hwshutdown(t3_ether_t *sc) +{ + /* Receive path shutdown */ + t3_clear(sc, R_RX_MODE, M_MODE_ENABLE); + t3_clear(sc, R_RCV_BD_INIT_MODE, M_MODE_ENABLE); + t3_clear(sc, R_RCV_LIST_MODE, M_MODE_ENABLE); + if (sc->device != K_PCI_ID_BCM5705) { + t3_clear(sc, R_RCV_LIST_SEL_MODE, M_MODE_ENABLE); + } + t3_clear(sc, R_RCV_DATA_INIT_MODE, M_MODE_ENABLE); + t3_clear(sc, R_RCV_COMP_MODE, M_MODE_ENABLE); + t3_clear(sc, R_RCV_BD_COMP_MODE, M_MODE_ENABLE); + + /* Transmit path shutdown */ + t3_clear(sc, R_SND_BD_SEL_MODE, M_MODE_ENABLE); + t3_clear(sc, R_SND_BD_INIT_MODE, M_MODE_ENABLE); + t3_clear(sc, R_SND_DATA_MODE, M_MODE_ENABLE); + t3_clear(sc, R_RD_DMA_MODE, M_MODE_ENABLE); + t3_clear(sc, R_SND_DATA_COMP_MODE, M_MODE_ENABLE); + if (sc->device != K_PCI_ID_BCM5705) { + t3_clear(sc, R_DMA_COMP_MODE, M_MODE_ENABLE); + } + t3_clear(sc, R_SND_BD_COMP_MODE, M_MODE_ENABLE); + t3_clear(sc, R_TX_MODE, M_MODE_ENABLE); + + /* Memory shutdown */ + t3_clear(sc, R_HOST_COAL_MODE, M_HCM_ENABLE); + t3_clear(sc, R_WR_DMA_MODE, M_MODE_ENABLE); + if (sc->device != K_PCI_ID_BCM5705) { + t3_clear(sc, R_MBUF_FREE_MODE, M_MODE_ENABLE); + } + WRITECSR(sc, R_FTQ_RESET, 0xFFFFFFFF); + cfe_sleep(1); + WRITECSR(sc, R_FTQ_RESET, 0x00000000); + t3_clear(sc, R_BMGR_MODE, M_BMODE_ENABLE); + t3_clear(sc, R_MEM_MODE, M_MAM_ENABLE); + + t3_shutdownlink(sc); + + WRITECSR(sc, R_MEMWIN_BASE_ADDR, 0); /* Default memory window */ + t3_coldreset(sc); + + sc->state = eth_state_uninit; +} + + +static void +t3_isr(void *arg) +{ + t3_ether_t *sc = (t3_ether_t *)arg; + volatile t3_status_t *status = sc->status; + uint32_t mac_status; + int handled; + + do { + WRITEMBOX(sc, R_INT_MBOX(0), 1); + + handled = 0; + mac_status = READCSR(sc, R_MAC_STATUS); /* force ordering */ + status->status &= ~M_STATUS_UPDATED; + + if (status->index[RI(1)].return_p != sc->rxr_1_index) { + handled = 1; + if (IPOLL) sc->rx_interrupts++; + t3_procrxring(sc); + } + + if (status->index[RI(1)].send_c != sc->txc_1_index) { + handled = 1; + if (IPOLL) sc->tx_interrupts++; + t3_proctxring(sc); + } + + if ((status->status & M_STATUS_LINKCHNG) != 0) { + handled = 1; +#if T3_AUTOPOLL + WRITECSR(sc, R_MAC_STATUS, M_LINKCHNG_CLR); +#endif + WRITECSR(sc, R_MAC_STATUS, M_EVT_MICOMPLETE); + + status->status &= ~M_STATUS_LINKCHNG; + sc->phy_change = 1; + } + + WRITEMBOX(sc, R_INT_MBOX(0), 0); + (void)READMBOX(sc, R_INT_MBOX(0)); /* push */ + +#if (!XPOLL) + if (!handled) + sc->bogus_interrupts++; +#endif + + } while ((status->status & M_STATUS_UPDATED) != 0); + + if (sc->rxp_std_index != sc->prev_rxp_std_index) { + sc->prev_rxp_std_index = sc->rxp_std_index; + WRITEMBOX(sc, R_RCV_BD_STD_PI, sc->rxp_std_index); + } +} + + +static void +t3_start(t3_ether_t *sc) +{ + t3_hwinit(sc); + + sc->intmask = 0; + +#if IPOLL + cfe_request_irq(sc->irq, t3_isr, sc, CFE_IRQ_FLAGS_SHARED, 0); + +#if T3_AUTOPOLL + sc->intmask |= M_EVT_LINKCHNG; +#else + sc->intmask |= M_EVT_LINKCHNG | M_EVT_MIINT; +#endif + WRITECSR(sc, R_MAC_EVENT_ENB, sc->intmask); +#endif + + /* Post some Rcv Producer buffers */ + sc->prev_rxp_std_index = sc->rxp_std_index; + WRITEMBOX(sc, R_RCV_BD_STD_PI, sc->rxp_std_index); + + sc->state = eth_state_on; +} + +static void +t3_stop(t3_ether_t *sc) +{ + WRITECSR(sc, R_MAC_EVENT_ENB, 0); + sc->intmask = 0; +#if IPOLL + cfe_free_irq(sc->irq, 0); +#endif + + if (sc->state == eth_state_on) { + sc->state = eth_state_off; + t3_hwshutdown(sc); + t3_reinit(sc); + } +} + + +static int t3_ether_open(cfe_devctx_t *ctx); +static int t3_ether_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int t3_ether_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); +static int t3_ether_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int t3_ether_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int t3_ether_close(cfe_devctx_t *ctx); +static void t3_ether_poll(cfe_devctx_t *ctx, int64_t ticks); +static void t3_ether_reset(void *softc); + +const static cfe_devdisp_t t3_ether_dispatch = { + t3_ether_open, + t3_ether_read, + t3_ether_inpstat, + t3_ether_write, + t3_ether_ioctl, + t3_ether_close, + t3_ether_poll, + t3_ether_reset +}; + +cfe_driver_t bcm5700drv = { + "BCM570x Ethernet", + "eth", + CFE_DEV_NETWORK, + &t3_ether_dispatch, + t3_ether_probe +}; + + +static void +t3_delete_sc(t3_ether_t *sc) +{ + xprintf("BCM570x attach: No memory to complete probe\n"); + if (sc != NULL) { + if (sc->txp_1 != NULL) + KFREE(sc->txp_1); + if (sc->rxr_1 != NULL) + KFREE(sc->rxr_1); + if (sc->rxp_std != NULL) + KFREE(sc->rxp_std); + if (sc->stats != NULL) + KFREE(sc->stats); + if (sc->status != NULL) + KFREE((t3_ether_t *)sc->status); + KFREE(sc); + } +} + +static int +t3_ether_attach(cfe_driver_t *drv, pcitag_t tag, int index) +{ + t3_ether_t *sc; + char descr[80]; + phys_addr_t pa; + uint32_t base; + uint32_t pcictrl; + uint32_t addr; + pcireg_t device, class; + const char *devname; + int i; + + pci_map_mem(tag, PCI_MAPREG(0), PCI_MATCH_BITS, &pa); + base = (uint32_t)pa; + + sc = (t3_ether_t *) KMALLOC(sizeof(t3_ether_t), 0); + if (sc == NULL) { + t3_delete_sc(sc); + return 0; + } + + memset(sc, 0, sizeof(*sc)); + + sc->status = NULL; + sc->stats = NULL; + + device = pci_conf_read(tag, PCI_ID_REG); + class = pci_conf_read(tag, PCI_CLASS_REG); + sc->tag = tag; + sc->device = PCI_PRODUCT(device); + sc->revision = PCI_REVISION(class); + + sc->status = (t3_status_t *) KMALLOC(sizeof(t3_status_t), CACHE_ALIGN); + if (sc->status == NULL) { + t3_delete_sc(sc); + return 0; + } + + sc->stats = (t3_stats_t *) KMALLOC(sizeof(t3_stats_t), CACHE_ALIGN); + if (sc->stats == NULL) { + t3_delete_sc(sc); + return 0; + } + + if (sc->device == K_PCI_ID_BCM5705) + sc->rxr_entries = RXR_RING_ENTRIES_05; + else + sc->rxr_entries = RXR_RING_ENTRIES; + + sc->rxp_std = + (t3_rcv_bd_t *) KMALLOC(RXP_STD_ENTRIES*RCV_BD_SIZE, CACHE_ALIGN); + sc->rxr_1 = + (t3_rcv_bd_t *) KMALLOC(sc->rxr_entries*RCV_BD_SIZE, CACHE_ALIGN); + sc->txp_1 = + (t3_snd_bd_t *) KMALLOC(TXP_RING_ENTRIES*SND_BD_SIZE, CACHE_ALIGN); + if (sc->rxp_std == NULL || sc->rxr_1 == NULL || sc->txp_1 == NULL) { + t3_delete_sc(sc); + return 0; + } + + sc->regbase = base; + + /* NB: the relative base of memory depends on the access model */ + pcictrl = pci_conf_read(tag, R_PCI_STATE); +#if 0 /* XXX This gets spontaneously reset somehow! */ + if ((pcictrl & M_PCIS_FLATVIEW) != 0) + sc->membase = base + 0x01000000; /* Flat mode */ + else +#endif + sc->membase = base + 0x8000; /* Normal mode: 32K window */ + + sc->irq = pci_conf_read(tag, PCI_BPARAM_INTERRUPT_REG) & 0xFF; + + sc->devctx = NULL; + + /* Assume on-chip firmware has initialized the MAC address. */ + addr = READCSR(sc, R_MAC_ADDR1_HIGH); + for (i = 0; i < 2; i++) + sc->hwaddr[i] = (addr >> (8*(1-i))) & 0xff; + addr = READCSR(sc, R_MAC_ADDR1_LOW); + for (i = 0; i < 4; i++) + sc->hwaddr[2+i] = (addr >> (8*(3-i))) & 0xff; + + t3_init(sc); + + sc->state = eth_state_uninit; + + switch (sc->device) { + case K_PCI_ID_BCM5700: + devname = "BCM5700"; break; + case K_PCI_ID_BCM5701: + devname = "BCM5701"; break; + case K_PCI_ID_BCM5702: + devname = "BCM5702"; break; + case K_PCI_ID_BCM5703: + devname = "BCM5703"; break; + case K_PCI_ID_BCM5705: + devname = "BCM5705"; break; + default: + devname = "BCM570x"; break; + } + xsprintf(descr, "%s Ethernet at 0x%X (%02X-%02X-%02X-%02X-%02X-%02X)", + devname, sc->regbase, + sc->hwaddr[0], sc->hwaddr[1], sc->hwaddr[2], + sc->hwaddr[3], sc->hwaddr[4], sc->hwaddr[5]); + + cfe_attach(drv, sc, NULL, descr); + return 1; +} + +static void +t3_ether_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + int index; + int n; + + n = 0; + index = 0; + for (;;) { + pcitag_t tag; + pcireg_t device; + + if (pci_find_class(PCI_CLASS_NETWORK, index, &tag) != 0) + break; + + index++; + + device = pci_conf_read(tag, PCI_ID_REG); + if (PCI_VENDOR(device) == K_PCI_VENDOR_BROADCOM) { + switch (PCI_PRODUCT(device)) { + case K_PCI_ID_BCM5700: + case K_PCI_ID_BCM5701: + case K_PCI_ID_BCM5702: + case K_PCI_ID_BCM5703: + case K_PCI_ID_BCM5705: + t3_ether_attach(drv, tag, n); + n++; + break; + default: + break; + } + } + } +} + + +/* The functions below are called via the dispatch vector for the Tigon 3 */ + +static int +t3_ether_open(cfe_devctx_t *ctx) +{ + t3_ether_t *sc = ctx->dev_softc; + t3_stats_t *stats = sc->stats; + int i; + + if (sc->state == eth_state_on) + t3_stop(sc); + + sc->devctx = ctx; + + sc->rx_interrupts = sc->tx_interrupts = sc->bogus_interrupts = 0; + for (i = 0; i < L_MAC_STATS/sizeof(uint64_t); i++) + stats->stats[i] = 0; + + t3_start(sc); + + if (XPOLL) t3_isr(sc); + return 0; +} + +static int +t3_ether_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + t3_ether_t *sc = ctx->dev_softc; + eth_pkt_t *pkt; + int blen; + + if (XPOLL) t3_isr(sc); + + if (sc->state != eth_state_on) return -1; + + CS_ENTER(sc); + pkt = (eth_pkt_t *) q_deqnext(&(sc->rxqueue)); + CS_EXIT(sc); + + if (pkt == NULL) { + buffer->buf_retlen = 0; + return 0; + } + + blen = buffer->buf_length; + if (blen > pkt->length) blen = pkt->length; + + blockcopy(buffer->buf_ptr, pkt->buffer, blen); + buffer->buf_retlen = blen; + + eth_free_pkt(sc, pkt); + + if (XPOLL) t3_isr(sc); + return 0; +} + +static int +t3_ether_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat) +{ + t3_ether_t *sc = ctx->dev_softc; + + if (XPOLL) t3_isr(sc); + + if (sc->state != eth_state_on) return -1; + + /* We avoid an interlock here because the result is a hint and an + interrupt cannot turn a non-empty queue into an empty one. */ + inpstat->inp_status = (q_isempty(&(sc->rxqueue))) ? 0 : 1; + + return 0; +} + +static int +t3_ether_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + t3_ether_t *sc = ctx->dev_softc; + eth_pkt_t *pkt; + int blen; + + if (XPOLL) t3_isr(sc); + + if (sc->state != eth_state_on) return -1; + + pkt = eth_alloc_pkt(sc); + if (!pkt) return CFE_ERR_NOMEM; + + blen = buffer->buf_length; + if (blen > pkt->length) blen = pkt->length; + + blockcopy(pkt->buffer, buffer->buf_ptr, blen); + pkt->length = blen; + + if (t3_transmit(sc, pkt) != 0) { + eth_free_pkt(sc,pkt); + return CFE_ERR_IOERR; + } + + if (XPOLL) t3_isr(sc); + return 0; +} + +static int +t3_ether_ioctl(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + t3_ether_t *sc = ctx->dev_softc; + + switch ((int)buffer->buf_ioctlcmd) { + case IOCTL_ETHER_GETHWADDR: + memcpy(buffer->buf_ptr, sc->hwaddr, sizeof(sc->hwaddr)); + return 0; + + default: + return -1; + } +} + +static int +t3_ether_close(cfe_devctx_t *ctx) +{ + t3_ether_t *sc = ctx->dev_softc; + t3_stats_t *stats = sc->stats; + uint32_t inpkts, outpkts, interrupts; + int i; + + t3_stop(sc); + +#if T3_BRINGUP + for (i = 0; i < L_MAC_STATS/sizeof(uint64_t); i++) { + if (stats->stats[i] != 0) + xprintf(" stats[%d] = %8lld\n", i, stats->stats[i]); + } +#else + (void) i; +#endif + + inpkts = stats->stats[ifHCInUcastPkts] + + stats->stats[ifHCInMulticastPkts] + + stats->stats[ifHCInBroadcastPkts]; + outpkts = stats->stats[ifHCOutUcastPkts] + + stats->stats[ifHCOutMulticastPkts] + + stats->stats[ifHCOutBroadcastPkts]; + interrupts = stats->stats[nicInterrupts]; + + /* Empirically, counters on the 5705 are always zero. */ + if (sc->device != K_PCI_ID_BCM5705) { + xprintf("%s: %d sent, %d received, %d interrupts\n", + t3_devname(sc), outpkts, inpkts, interrupts); + if (IPOLL) { + xprintf(" %d rx interrupts, %d tx interrupts", + sc->rx_interrupts, sc->tx_interrupts); + if (sc->bogus_interrupts != 0) + xprintf(", %d bogus interrupts", sc->bogus_interrupts); + xprintf("\n"); + } + } + + sc->devctx = NULL; + return 0; +} + +static void +t3_ether_poll(cfe_devctx_t *ctx, int64_t ticks) +{ + t3_ether_t *sc = ctx->dev_softc; + int changed; + + if (sc->phy_change && sc->state != eth_state_uninit && !sc->mii_polling) { + uint32_t mask; + + sc->mii_polling++; + mask = READCSR(sc, R_MAC_EVENT_ENB); + WRITECSR(sc, R_MAC_EVENT_ENB, 0); + + changed = mii_poll(sc); + if (changed) { + mii_autonegotiate(sc); + } + sc->phy_change = 0; + sc->mii_polling--; + + WRITECSR(sc, R_MAC_EVENT_ENB, mask); + } +} + +static void +t3_ether_reset(void *softc) +{ + t3_ether_t *sc = (t3_ether_t *)softc; + + /* Turn off the Ethernet interface. */ + + if (sc->state == eth_state_on) + t3_stop(sc); + + sc->state = eth_state_uninit; +} diff --git a/cfe/cfe/dev/dev_bcm5821.c b/cfe/cfe/dev/dev_bcm5821.c new file mode 100644 index 0000000..3dede0f --- /dev/null +++ b/cfe/cfe/dev/dev_bcm5821.c @@ -0,0 +1,1592 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * BC5821 crypto accelerator driver File: dev_bcm5821.c + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + +/* + CFE Driver plus test programs for the BCM5820 and BCM5821 crypto + coprocessor chips. + Reference: + BCM5821 Super-eCommerce Processor + Data Sheet 5821-DS105-D1 (draft, 7/26/02) + Broadcom Corp., 16215 Alton Parkway, Irvine, CA. +*/ + +/* The performance counter usage assumes a BCM11xx or BCM1250 part */ +#ifndef _SB14XX_ + +#include "sbmips.h" +#include "sb1250_defs.h" +#include "sb1250_regs.h" + +#ifndef _SB_MAKE64 +#define _SB_MAKE64(x) ((uint64_t)(x)) +#endif +#ifndef _SB_MAKEMASK1 +#define _SB_MAKEMASK1(n) (_SB_MAKE64(1) << _SB_MAKE64(n)) +#endif + +#include "lib_types.h" +#include "lib_hssubr.h" +#include "lib_malloc.h" +#include "lib_string.h" +#include "lib_printf.h" +#include "lib_queue.h" + +#include "addrspace.h" + +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_timer.h" +#include "cfe_devfuncs.h" +#include "cfe_irq.h" + +#include "pcivar.h" +#include "pcireg.h" + +#include "bcm5821.h" + +/* The version that works by polling the CPU's Cause register doesn't + do handshakes or checks to detect merged interrupts. It currently + works when the 5821 is on the direct PCI bus but can behave + erratically when the 5821 is behind an LDT-to-PCI bridge that does + interrupt mapping and relies on EOI. */ + +extern int32_t _getcause(void); /* return value of CP0 CAUSE */ + +#define IMR_POINTER(cpu,reg) \ + ((volatile uint64_t *)(PHYS_TO_K1(A_IMR_REGISTER(cpu,reg)))) + +#define CACHE_LINE_SIZE 32 + +static void bcm5821_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + +typedef struct bcm5821_state_s { + uint32_t regbase; + uint8_t irq; + pcitag_t tag; /* tag for configuration registers */ + + uint16_t device; /* chip device code */ + uint8_t revision; /* chip revision */ + +} bcm5821_state_t; + + +/* Address mapping macros */ + +/* Note that PTR_TO_PHYS only works with 32-bit addresses, but then + so does the bcm528x. */ +#define PTR_TO_PHYS(x) (K0_TO_PHYS((uintptr_t)(x))) +#define PHYS_TO_PTR(a) ((void *)PHYS_TO_K0(a)) + +/* For the 5821, all mappings through the PCI host bridge use match + bits mode. This works because the NORM_PCI bit in DMA Control is + clear. The 5820 does not have such a bit, so pointers to data byte + sequences use match bytes, but control blocks use match bits. */ +#define PHYS_TO_PCI(a) ((uint32_t) (a) | 0x20000000) +#define PHYS_TO_PCI_D(a) (a) +#define PCI_TO_PHYS(a) ((uint32_t) (a) & 0x1FFFFFFF) + +#if __long64 +#define READCSR(sc,csr) \ + (*((volatile uint32_t *) \ + (PHYS_TO_XKSEG_UNCACHED((sc)->regbase+(csr))))) + +#define WRITECSR(sc,csr,val) \ + (*((volatile uint32_t *) \ + (PHYS_TO_XKSEG_UNCACHED((sc)->regbase+(csr)))) = (val)) +#else +#define READCSR(sc,csr) \ + (hs_read32(PHYS_TO_XKSEG_UNCACHED((sc)->regbase+(csr)))) + +#define WRITECSR(sc,csr,val) \ + (hs_write32(PHYS_TO_XKSEG_UNCACHED((sc)->regbase+(csr)), (val))) +#endif + +static void +dumpcsrs(bcm5821_state_t *sc, const char *legend) +{ + xprintf("%s:\n", legend); + xprintf("---DMA---\n"); + /* DMA control and status registers */ + xprintf("MCR1: %08X CTRL: %08X STAT: %08X ERR: %08X\n", + READCSR(sc, R_MCR1), READCSR(sc, R_DMA_CTRL), + READCSR(sc, R_DMA_STAT), READCSR(sc, R_DMA_ERR)); + xprintf("MCR2: %08X\n", READCSR(sc, R_MCR2)); + xprintf("-------------\n"); +} + + +static void +bcm5821_init(bcm5821_state_t *sc) +{ +} + +static void +bcm5821_hwinit(bcm5821_state_t *sc) +{ + uint32_t ctrl; + uint32_t status; + + ctrl = (M_DMA_CTRL_MCR1_INT_EN | M_DMA_CTRL_MCR2_INT_EN | + M_DMA_CTRL_DMAERR_EN); + if (sc->device == K_PCI_ID_BCM5820) + ctrl |= (M_DMA_CTRL_NORM_PCI | M_DMA_CTRL_LE_CRYPTO); + /* Note for 5821: M_DMA_CTRL_NORM_PCI, M_DMA_CTRL_LE_CRYPTO not set. */ +#if 0 /* Empirically, this reduces performance. */ + if (sc->device != K_PCI_ID_BCM5820) + ctrl |= M_DMA_CTRL_WR_BURST; +#endif + WRITECSR(sc, R_DMA_CTRL, ctrl); + + status = READCSR(sc, R_DMA_STAT); + WRITECSR(sc, R_DMA_STAT, status); /* reset write-to-clear bits */ + status = READCSR(sc, R_DMA_STAT); + + dumpcsrs(sc, "init"); +} + + +static void +bcm5821_start(bcm5821_state_t *sc) +{ + bcm5821_hwinit(sc); +} + +static void +bcm5821_stop(bcm5821_state_t *sc) +{ + WRITECSR(sc, R_DMA_CTRL, 0); +} + + +static int bcm5821_open(cfe_devctx_t *ctx); +static int bcm5821_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int bcm5821_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); +static int bcm5821_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int bcm5821_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int bcm5821_close(cfe_devctx_t *ctx); + +const static cfe_devdisp_t bcm5821_dispatch = { + bcm5821_open, + bcm5821_read, + bcm5821_inpstat, + bcm5821_write, + bcm5821_ioctl, + bcm5821_close, + NULL, + NULL +}; + +cfe_driver_t bcm5821drv = { + "BCM582x crypto", + "crypt", + CFE_DEV_OTHER, + &bcm5821_dispatch, + bcm5821_probe +}; + + +static int +bcm5821_attach(cfe_driver_t *drv, pcitag_t tag, int index) +{ + bcm5821_state_t *sc; + char descr[80]; + phys_addr_t pa; + uint32_t base; + pcireg_t device, class; + + pci_map_mem(tag, PCI_MAPREG(0), PCI_MATCH_BITS, &pa); + base = (uint32_t)pa; + + sc = (bcm5821_state_t *) KMALLOC(sizeof(bcm5821_state_t),0); + if (sc == NULL) { + xprintf("BCM5821: No memory to complete probe\n"); + return 0; + } + + memset(sc, 0, sizeof(*sc)); + + sc->regbase = base; + + sc->irq = pci_conf_read(tag, PCI_BPARAM_INTERRUPT_REG) & 0xFF; + + device = pci_conf_read(tag, PCI_ID_REG); + class = pci_conf_read(tag, PCI_CLASS_REG); + + sc->tag = tag; + sc->device = PCI_PRODUCT(device); + sc->revision = PCI_REVISION(class); + + bcm5821_init(sc); + + xsprintf(descr, "BCM%04X Crypto at 0x%08X", sc->device, base); + cfe_attach(drv, sc, NULL, descr); + + return 1; +} + +static void +bcm5821_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + int index; + int n; + + n = 0; + index = 0; + for (;;) { + pcitag_t tag; + pcireg_t device; + + if (pci_find_class(PCI_CLASS_PROCESSOR, index, &tag) != 0) + break; + + index++; + + device = pci_conf_read(tag, PCI_ID_REG); + if (PCI_VENDOR(device) == K_PCI_VENDOR_BROADCOM) { + if (PCI_PRODUCT(device) == K_PCI_ID_BCM5820 || + PCI_PRODUCT(device) == K_PCI_ID_BCM5821) { + bcm5821_attach(drv, tag, n); + n++; + } + } + } +} + + +/* The functions below are called via the dispatch vector for the 5821 */ + +static int +bcm5821_open(cfe_devctx_t *ctx) +{ + bcm5821_state_t *sc = ctx->dev_softc; + + bcm5821_start(sc); + return 0; +} + +static int +bcm5821_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + return -1; +} + +static int +bcm5821_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat) +{ + return 0; +} + +static int +bcm5821_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + return -1; +} + +static int +bcm5821_ioctl(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + return -1; +} + +static int +bcm5821_close(cfe_devctx_t *ctx) +{ + bcm5821_state_t *sc = ctx->dev_softc; + + bcm5821_stop(sc); + return 0; +} + + +/* Additional hooks for testing. */ + +static int +bcm5821_dump_cc1 (uint32_t *cc) +{ + int i; + unsigned op = G_CC_OPCODE(cc[0]); + unsigned cc_words = G_CC_LEN(cc[0])/4; + int chain_out; /* Whether the output is chained or fixed */ + + chain_out = 1; /* default */ + + switch (op) { + + case K_SSL_MAC: + xprintf("(SSL_MAC)\n"); + for (i = 0; i < SSL_MAC_CMD_WORDS; i++) + xprintf(" %2d: %08x\n", i, cc[i]); + chain_out = 0; + break; + + case K_ARC4: + xprintf("(ARCFOUR)\n"); + for (i = 0; i < 3; i++) + xprintf(" %2d: %08x\n", i, cc[i]); + for (i = 0; i < 256/4; i += 4) + xprintf(" %2d: %08x %08x %08x %08x\n", + i+3, cc[i+3], cc[i+4], cc[i+5], cc[i+6]); + break; + + case K_HASH: + xprintf("(HASH)\n"); + for (i = 0; i < 2; i++) + xprintf(" %2d: %08x\n", i, cc[i]); + chain_out = 0; + break; + + case K_TLS_HMAC: + chain_out = 0; + /* fall through */ + + default: /* NYI: K_IPSEC_3DES (5821 only), K_SSL_3DES */ + xprintf("\n"); + for (i = 0; i < cc_words; i++) + xprintf(" %2d: %08x\n", i, cc[i]); + break; + } + + return chain_out; +} + +static int +bcm5821_dump_cc2 (uint32_t *cc) +{ + int i; + unsigned op = G_CC_OPCODE(cc[0]); + unsigned cc_words = G_CC_LEN(cc[0])/4; + int chain_out; /* Whether the output is chained or fixed */ + + chain_out = 1; /* default */ + + switch (op) { + + case K_RNG_DIRECT: + xprintf(" RNG_DIRECT\n"); + chain_out = 0; + for (i = 0; i < 1; i++) + xprintf(" %2d: %08x\n", i, cc[i]); + break; + + case K_RNG_SHA1: + xprintf(" RNG_SHA1\n"); + chain_out = 0; + for (i = 0; i < 1; i++) + xprintf(" %2d: %08x\n", i, cc[i]); + break; + + default: /* NYI: K_DH_*_GEN, K_RSA_*_OP, K_DSA_*, K_MOD_* */ + xprintf(" %04x\n", op); + for (i = 0; i < cc_words; i++) + xprintf(" %2d: %08x\n", i, cc[i]); + break; + } + return chain_out; +} + +static void +bcm5821_dump_pkt (uint32_t *pkt, int port) +{ + uint32_t *cc = PHYS_TO_PTR(PCI_TO_PHYS(pkt[0])); + uint32_t *chain; + int chain_out; + int i, j; + + xprintf(" %2d: %08x ", 0, pkt[0]); + chain_out = (port == 1 ? bcm5821_dump_cc1 : bcm5821_dump_cc2)(cc); + + for (i = 1; i < PD_SIZE/4; i++) { + xprintf(" %2d: %08x\n", i, pkt[i]); + + if (pkt[i] != 0) { + switch (i) { + case 2: + chain = PHYS_TO_PTR(PCI_TO_PHYS(pkt[i])); + for (j = 0; j < CHAIN_WORDS; j++) + xprintf(" %2d: %08x\n", j, chain[j]); + break; + case 6: + if (chain_out) { + chain = PHYS_TO_PTR(PCI_TO_PHYS(pkt[i])); + for (j = 0; j < CHAIN_WORDS; j++) + xprintf(" %2d: %08x\n", j, chain[j]); + } + break; + default: + break; + } + } + } +} + +static void +bcm5821_dump_mcr (uint32_t mcr[], int port) +{ + unsigned i; + unsigned npkts = G_MCR_NUM_PACKETS(mcr[0]); + + xprintf("MCR header %08x at %p:\n", mcr[0], mcr); + for (i = 0; i < npkts; i++) { + xprintf(" packet %d:\n", i+1); + bcm5821_dump_pkt(&mcr[1 + i*(PD_SIZE/4)], port); + } +} + + +static void +bcm5821_show_pkt1 (uint32_t *pkt) +{ + uint32_t *cc = PHYS_TO_PTR(PCI_TO_PHYS(pkt[0])); + unsigned op = G_CC_OPCODE(cc[0]); + int i; + + switch (op) { + case K_SSL_MAC: + { + uint32_t *hash = PHYS_TO_PTR(PCI_TO_PHYS(pkt[6])); + xprintf("SSL_MAC hash:\n"); + xprintf(" %08x %08x %08x %08x\n", + hash[0], hash[1], hash[2], hash[3]); + xprintf(" %08x\n", hash[4]); + } + break; + case K_TLS_HMAC: + { + uint32_t *hash = PHYS_TO_PTR(PCI_TO_PHYS(pkt[7])); + xprintf("TLS_HMAC hash:\n"); + xprintf(" %08x %08x %08x %08x\n", + hash[0], hash[1], hash[2], hash[3]); + xprintf(" %08x\n", hash[4]); + } + break; + case K_ARC4: + { + uint32_t *output = PHYS_TO_PTR(PCI_TO_PHYS(pkt[5])); + uint32_t *chain = PHYS_TO_PTR(PCI_TO_PHYS(pkt[6])); + uint32_t *update = PHYS_TO_PTR(PCI_TO_PHYS(chain[0])); + + xprintf("ARCFOUR output\n"); + for (i = 0; i < 64; i += 4) + xprintf (" %08x %08x %08x %08x\n", + output[i+0], output[i+1], output[i+2], output[i+3]); + xprintf("ARCFOUR update\n"); + xprintf(" %08x\n", update[0]); + for (i = 0; i < 256/4; i += 4) + xprintf (" %08x %08x %08x %08x\n", + update[i+1], update[i+2], update[i+3], update[i+4]); + } + break; + case K_HASH: + { + uint8_t *digest = PHYS_TO_PTR(PCI_TO_PHYS(pkt[6])); + + xprintf("HASH digest "); + for (i = 0; i < 16; i++) + xprintf("%02x", digest[i]); + xprintf("\n"); + } + break; + default: + break; + } +} + +static void +bcm5821_show_pkt2 (uint32_t *pkt) +{ + uint32_t *cc = PHYS_TO_PTR(PCI_TO_PHYS(pkt[0])); + unsigned op = G_CC_OPCODE(cc[0]); + int i; + + switch (op) { + case K_RNG_DIRECT: + case K_RNG_SHA1: + { + uint32_t *output = PHYS_TO_PTR(PCI_TO_PHYS(pkt[5])); + size_t len = V_DBC_DATA_LEN(pkt[7])/sizeof(uint32_t); + + xprintf("RNG output\n"); + for (i = 0; i < len; i += 4) + xprintf (" %08x %08x %08x %08x\n", + output[i+0], output[i+1], output[i+2], output[i+3]); + } + break; + default: + break; + } +} + +static void +bcm5821_show_mcr (uint32_t mcr[], int port) +{ + unsigned i; + unsigned npkts = G_MCR_NUM_PACKETS(mcr[0]); + + xprintf("MCR at %p:\n", mcr); + for (i = 0; i < npkts; i++) { + xprintf("packet %d:\n", i+1); + if (port == 1) + bcm5821_show_pkt1(&mcr[1 + i*(PD_SIZE/4)]); + else + bcm5821_show_pkt2(&mcr[1 + i*(PD_SIZE/4)]); + } +} + + +static uint32_t * +bcm5821_alloc_hash (const uint8_t *msg, size_t msg_len, int swap) +{ + uint32_t *mcr; + uint32_t *cmd; /* always reads at least 64 bytes */ + uint8_t *message; + uint8_t *digest; + int i; + + message = KMALLOC(msg_len, CACHE_LINE_SIZE); + for (i = 0; i < msg_len; i++) + message[i] = msg[i]; + + digest = KMALLOC(16, CACHE_LINE_SIZE); + for (i = 0; i < 16; i++) + digest[i] = 0; + + mcr = KMALLOC(MCR_WORDS(1)*4, CACHE_LINE_SIZE); + mcr[0] = V_MCR_NUM_PACKETS(1); + + cmd = KMALLOC(64, CACHE_LINE_SIZE); /* Always allocate >= 64 bytes */ + cmd[0] = V_CC_OPCODE(K_HASH) | V_CC_LEN(8); + cmd[1] = V_CC_FLAGS(K_HASH_FLAGS_MD5); + + mcr[1] = PHYS_TO_PCI(PTR_TO_PHYS(cmd)); + + /* input fragment */ + mcr[2] = swap ? PHYS_TO_PCI_D(PTR_TO_PHYS(message)) + : PHYS_TO_PCI(PTR_TO_PHYS(message)); + mcr[3] = 0; + mcr[4] = V_DBC_DATA_LEN(msg_len); + + mcr[5] = V_PD_PKT_LEN(msg_len); + + mcr[6] = 0; + mcr[7] = swap ? PHYS_TO_PCI_D(PTR_TO_PHYS(digest)) + : PHYS_TO_PCI(PTR_TO_PHYS(digest)); + mcr[8] = 0; + + return mcr; +} + +static void +bcm5821_free_hash (uint32_t mcr[]) +{ + KFREE(PHYS_TO_PTR(PCI_TO_PHYS(mcr[1]))); + KFREE(PHYS_TO_PTR(PCI_TO_PHYS(mcr[2]))); + KFREE(PHYS_TO_PTR(PCI_TO_PHYS(mcr[7]))); + + KFREE(mcr); +} + + +static uint32_t * +bcm5821_alloc_hmac (const char *key, int key_len, + const char *msg, int msg_len, + int swap) +{ + uint32_t *message; + uint32_t *cmd; + uint32_t *mcr; + uint32_t *hash; + int i; + + message = KMALLOC(msg_len, CACHE_LINE_SIZE); + memcpy((uint8_t *)message, msg, msg_len); + + mcr = KMALLOC(MCR_WORDS(1)*4, CACHE_LINE_SIZE); + mcr[0] = V_MCR_NUM_PACKETS(1); + + /* packet 1 */ + + cmd = KMALLOC(TLS_HMAC_CMD_WORDS*4, CACHE_LINE_SIZE); + cmd[0] = V_CC_OPCODE(K_TLS_HMAC) | V_CC_LEN(TLS_HMAC_CMD_WORDS*4); + cmd[1] = V_CC_FLAGS(K_HASH_FLAGS_MD5); + + /* XXX This is not correct. The key is used to compute the inner + and outer states. */ + for (i = 2; i < 7; i++) + cmd[i] = 0x36363636; /* XXX MAC write secret */ + cmd[6] = 0x00000000; /* must be zero for SSL */ + for (i = 8; i < 13; i++) + cmd[i] = 0x5c5c5c5c; + cmd[13] = 0; /* seq num */ + cmd[14] = 1; + cmd[15] = 0x03000000 | (msg_len << 8); /* XXX type/len/rsvd */ + + mcr[1] = PHYS_TO_PCI(PTR_TO_PHYS(cmd)); + + /* input fragment */ + mcr[2] = swap ? PHYS_TO_PCI_D(PTR_TO_PHYS(message)) + : PHYS_TO_PCI(PTR_TO_PHYS(message)); + mcr[3] = 0; + mcr[4] = V_DBC_DATA_LEN(msg_len); + + mcr[5] = V_PD_PKT_LEN(msg_len); + + hash = KMALLOC(5*4, CACHE_LINE_SIZE); + for (i = 0; i < 5; i++) + hash[i] = 0; + + mcr[6] = 0; + mcr[7] = swap ? PHYS_TO_PCI_D(PTR_TO_PHYS(hash)) + : PHYS_TO_PCI(PTR_TO_PHYS(hash)); + mcr[8] = 0; + + return mcr; +} + +static void +bcm5821_free_hmac (uint32_t mcr[]) +{ + KFREE(PHYS_TO_PTR(PCI_TO_PHYS(mcr[1]))); + KFREE(PHYS_TO_PTR(PCI_TO_PHYS(mcr[2]))); + KFREE(PHYS_TO_PTR(PCI_TO_PHYS(mcr[7]))); + + KFREE(mcr); +} + + +static int test_init = 0; + +/* Timing */ + +/* For Pass 1, dedicate an SCD peformance counter to use as a counter + of ZBbus cycles. */ +#include "sb1250_scd.h" +#define ZCTR_MODULUS 0x10000000000LL + +/* The counter is a shared resource that must be reset periodically + since it doesn't roll over. Furthermore, there is a pass one bug + that makes the interrupt unreliable and the final value either all + ones or all zeros. We therefore reset the count when it exceeds + half the modulus. We also assume that intervals of interest + are much less than half the modulus and attempt to adjust for + the reset in zclk_elapsed. */ + +static void +zclk_init(uint64_t val) +{ + *((volatile uint64_t *) UNCADDR(A_SCD_PERF_CNT_0)) = val; + *((volatile uint64_t *) UNCADDR(A_SCD_PERF_CNT_CFG)) = + V_SPC_CFG_SRC0(1) | M_SPC_CFG_ENABLE; +} + +static uint64_t +zclk_get(void) +{ + uint64_t ticks; + + ticks = *((volatile uint64_t *) UNCADDR(A_SCD_PERF_CNT_0)); + if (ticks == 0 || ticks == ZCTR_MODULUS-1) { + ticks = 0; + zclk_init(ticks); + } + else if (ticks >= ZCTR_MODULUS/2) { + ticks -= ZCTR_MODULUS/2; + zclk_init(ticks); /* Ignore the fudge and lose a few ticks */ + } + return ticks; +} + +static uint64_t +zclk_elapsed(uint64_t stop, uint64_t start) +{ + return ((stop >= start) ? stop : stop + ZCTR_MODULUS/2) - start; +} + + +/* Auxiliary functions */ + +static uint32_t * +bcm5821_alloc_composite(int input_size) +{ + uint32_t *input, *output; + uint32_t *cmd; + uint32_t *chain; + uint32_t *mcr; + uint32_t *hash; + uint32_t *update; + uint8_t *arc4_state; + int i; + + input = KMALLOC(input_size, CACHE_LINE_SIZE); + for (i = 0; i < input_size; i++) + ((uint8_t *)input)[i] = i & 0xFF; + output = KMALLOC(input_size + 16, CACHE_LINE_SIZE); + for (i = 0; i < input_size + 16; i++) + ((uint8_t *)output)[i] = 0xFF; + + mcr = KMALLOC(MCR_WORDS(2)*4, CACHE_LINE_SIZE); + mcr[0] = V_MCR_NUM_PACKETS(2); + + /* packet 1 */ + + cmd = KMALLOC(SSL_MAC_CMD_WORDS*4, CACHE_LINE_SIZE); + cmd[0] = V_CC_OPCODE(K_SSL_MAC) | V_CC_LEN(SSL_MAC_CMD_WORDS*4); + cmd[1] = V_CC_FLAGS(K_HASH_FLAGS_MD5); + for (i = 2; i < 6; i++) + cmd[i] = 0x01020304; /* XXX MAC write secret */ + cmd[6] = 0x00000000; /* must be zero for SSL */ + for (i = 7; i < 19; i++) + cmd[i] = 0x36363636; + cmd[19] = 0; /* seq num */ + cmd[20] = 1; + cmd[21] = 0x03000000 | (input_size << 8); /* type/len/rsvd */ + + mcr[1] = PHYS_TO_PCI(PTR_TO_PHYS(cmd)); + + /* input fragment */ + mcr[2] = PHYS_TO_PCI(PTR_TO_PHYS(input)); + mcr[3] = 0; + mcr[4] = V_DBC_DATA_LEN(input_size); + + mcr[5] = V_PD_PKT_LEN(input_size); + + hash = KMALLOC(5*4, CACHE_LINE_SIZE); + for (i = 0; i < 5; i++) + hash[i] = 0; + + mcr[6] = 0; + mcr[7] = PHYS_TO_PCI(PTR_TO_PHYS(hash)); + mcr[8] = 0; + + /* packet 2 */ + + cmd = KMALLOC(ARC4_CMD_WORDS*4, CACHE_LINE_SIZE); + cmd[0] = V_CC_OPCODE(K_ARC4) | V_CC_LEN(ARC4_CMD_WORDS*4); + cmd[1] = M_ARC4_FLAGS_WRITEBACK; + cmd[2] = 0x000100F3; + arc4_state = (uint8_t *)&cmd[3]; + for (i = 0; i < 256; i++) + arc4_state[i] = i; + + mcr[8+1] = PHYS_TO_PCI(PTR_TO_PHYS(cmd)); + + /* input fragment */ + chain = KMALLOC(CHAIN_WORDS*4, CACHE_LINE_SIZE); + + mcr[8+2] = PHYS_TO_PCI(PTR_TO_PHYS(input)); + mcr[8+3] = PHYS_TO_PCI(PTR_TO_PHYS(chain)); + mcr[8+4] = V_DBC_DATA_LEN(input_size); + + /* MAC fragment */ + chain[0] = PHYS_TO_PCI(PTR_TO_PHYS(hash)); + chain[1] = 0; + chain[2] = V_DBC_DATA_LEN(16); + + mcr[8+5] = V_PD_PKT_LEN(input_size + 16); + + /* output fragment */ + chain = KMALLOC(CHAIN_WORDS*4, CACHE_LINE_SIZE); + + mcr[8+6] = PHYS_TO_PCI(PTR_TO_PHYS(output)); + mcr[8+7] = PHYS_TO_PCI(PTR_TO_PHYS(chain)); + mcr[8+8] = V_DBC_DATA_LEN(input_size + 16); + + update = KMALLOC(ARC4_STATE_WORDS*4, CACHE_LINE_SIZE); + for (i = 0; i < ARC4_STATE_WORDS; i++) + update[i] = 0xFFFFFFFF; + + /* output update */ + chain[0] = PHYS_TO_PCI(PTR_TO_PHYS(update)); + chain[1] = 0; + chain[2] = V_DBC_DATA_LEN(ARC4_STATE_WORDS*4); /* not actually used */ + + return mcr; +} + +static void +bcm5821_free_composite (uint32_t mcr[]) +{ + uint32_t *chain; + + /* packet 1 */ + + KFREE(PHYS_TO_PTR(PCI_TO_PHYS(mcr[1]))); + KFREE(PHYS_TO_PTR(PCI_TO_PHYS(mcr[2]))); + KFREE(PHYS_TO_PTR(PCI_TO_PHYS(mcr[7]))); + + /* packet 2 */ + KFREE(PHYS_TO_PTR(PCI_TO_PHYS(mcr[8+1]))); + /* mcr[8+2] already freed */ + chain = PHYS_TO_PTR(PCI_TO_PHYS(mcr[8+3])); + KFREE(PHYS_TO_PTR(PCI_TO_PHYS(chain[0]))); KFREE(chain); + KFREE(PHYS_TO_PTR(PCI_TO_PHYS(mcr[8+6]))); + chain = PHYS_TO_PTR(PCI_TO_PHYS(mcr[8+7])); + KFREE(PHYS_TO_PTR(PCI_TO_PHYS(chain[0]))); KFREE(chain); + + KFREE(mcr); +} + + +static void +flush_l2(void) +{ + /* Temporary hack: churn through all of L2 */ + volatile uint64_t *lomem; + uint64_t t; + int i; + + lomem = (uint64_t *)(0xFFFFFFFF80000000LL); /* kseg0 @ 0 */ + t = 0; + for (i = 0; i < (512/8)*1024; i++) + t ^= lomem[i]; +} + +#ifdef IRQ +static void +bcm5821_interrupt(void *ctx) +{ +} +#endif + + +#define POOL_SIZE 4 +#define MCR_QUEUE_DEPTH 2 + +static int +bcm5821_composite (bcm5821_state_t *sc, size_t len, int trials) +{ + uint32_t *mcr[POOL_SIZE]; + uint32_t status; + uint64_t start, stop, ticks; + uint64_t tpb, Mbs; + int i; + int next, last, run; + + for (i = 0; i < POOL_SIZE; i++) + mcr[i] = bcm5821_alloc_composite(len); + + (void)bcm5821_dump_mcr; /*bcm5821_dump_mcr(mcr[0], 1);*/ + + next = last = 0; + run = 0; + + /* Force all descriptors and buffers out of L1 */ + cfe_flushcache(CFE_CACHE_FLUSH_D); + (void)flush_l2; /* XXX for now */ + + status = READCSR(sc, R_DMA_STAT); + WRITECSR(sc, R_DMA_STAT, status); /* clear pending bits */ + status = READCSR(sc, R_DMA_STAT); + + for (i = 0; i < 1000; i++) { + status = READCSR(sc, R_DMA_STAT); + if ((status & M_DMA_STAT_MCR1_FULL) == 0) + break; + cfe_sleep(1); + } + if (i == 1000) { + dumpcsrs(sc, "bcm5821: full bit never clears"); + return -1; + } + +#ifdef IRQ + /* Enable interrupt polling, but the handler is never called. */ + cfe_request_irq(sc->irq, bcm5821_interrupt, NULL, 0, 0); +#endif + + zclk_init(0); /* Time origin is arbitrary. */ + start = zclk_get(); + + /* MCR ports are double buffered. */ + for (i = 0; i < MCR_QUEUE_DEPTH; i++) { + while ((READCSR(sc, R_DMA_STAT) & M_DMA_STAT_MCR1_FULL) != 0) + continue; + WRITECSR(sc, R_MCR1, PHYS_TO_PCI(PTR_TO_PHYS(mcr[next]))); + next = (next + 1) % POOL_SIZE; + } + + while (1) { +#ifdef IRQ + while ((_getcause() & M_CAUSE_IP2) == 0) + continue; + + status = READCSR(sc, R_DMA_STAT); + if ((status & M_DMA_STAT_MCR1_INTR) == 0) { + /* This apparently is MCR1_ALL_EMPTY, timing of which is unclear. */ + WRITECSR(sc, R_DMA_STAT, + M_DMA_STAT_DMAERR_INTR | M_DMA_STAT_MCR1_INTR); + continue; + } + + stop = zclk_get(); + WRITECSR(sc, R_DMA_STAT, + M_DMA_STAT_DMAERR_INTR | M_DMA_STAT_MCR1_INTR); +#else + volatile uint32_t *last_mcr = mcr[last]; + + while ((*last_mcr & M_MCR_DONE) == 0) + continue; + + stop = zclk_get(); +#endif + + run++; + if (run == trials) + break; + + while ((READCSR(sc, R_DMA_STAT) & M_DMA_STAT_MCR1_FULL) != 0) + continue; + WRITECSR(sc, R_MCR1, PHYS_TO_PCI(PTR_TO_PHYS(mcr[next]))); + next = (next + 1) % POOL_SIZE; + + /* Clear the DONE and ERROR bits. This will bring one line of + the MCR back into L1. Flush? */ + mcr[last][0] = V_MCR_NUM_PACKETS(2); + last = (last + 1) % POOL_SIZE; + } + +#ifdef IRQ + status = READCSR(sc, R_DMA_STAT); + WRITECSR(sc, R_DMA_STAT, status); /* clear pending bits */ + cfe_free_irq(sc->irq, 0); +#endif + + ticks = zclk_elapsed(stop, start) / trials; + xprintf("bcm5821: Composite %lld ticks for %d bytes, %d packets\n", + ticks, len, trials); + /* Scaling for two decimal places. */ + tpb = (ticks*100) / len; + Mbs = (2000*100)*100 / tpb; + xprintf(" rate %lld.%02lld Mbps\n", Mbs/100, Mbs % 100); + + if (trials == 1) + { + bcm5821_show_mcr(mcr[0], 1); + } + + for (i = 0; i < POOL_SIZE; i++) + bcm5821_free_composite(mcr[i]); + + return 0; +} + + +/* The following code depends on having a separate interrupt per + device, and there are only 4 PCI interrupts. */ +#define MAX_DEVICES 4 + +struct dev_info { + bcm5821_state_t *sc; + uint64_t irq_mask; + int index[MCR_QUEUE_DEPTH]; +}; + + +#define N_DEVICES 2 + +static int +bcm5821_composite2 (bcm5821_state_t *sc0, bcm5821_state_t *sc1, + size_t len, int trials) +{ + uint32_t *mcr[POOL_SIZE]; + uint32_t ring[POOL_SIZE]; + uint32_t status; + uint64_t start, stop, ticks; + uint64_t tpb, Mbs; + int i; + int next, last; + int started, run; + int d; + struct dev_info dev[N_DEVICES]; + uint64_t masks; + bcm5821_state_t *sc; +#ifdef IRQ + volatile uint64_t *irqstat = IMR_POINTER(0, R_IMR_INTERRUPT_SOURCE_STATUS); +#endif + uint64_t pending; + + dev[0].sc = sc0; dev[1].sc = sc1; + + for (i = 0; i < POOL_SIZE; i++) + mcr[i] = bcm5821_alloc_composite(len); + for (i = 0; i < POOL_SIZE; i++) + ring[i] = i; + next = last = 0; + + (void)bcm5821_dump_mcr; /*bcm5821_dump_mcr(mcr[0], 1);*/ + + started = run = 0; + + /* Force all descriptors and buffers out of L1 */ + cfe_flushcache(CFE_CACHE_FLUSH_D); + (void)flush_l2; /* XXX for now */ + + masks = 0; + for (d = 0; d < N_DEVICES; d++) { + sc = dev[d].sc; + dev[d].irq_mask = 1LL << (sc->irq); + masks |= dev[d].irq_mask; + + status = READCSR(sc, R_DMA_STAT); + WRITECSR(sc, R_DMA_STAT, status); /* clear pending bits */ + status = READCSR(sc, R_DMA_STAT); + + for (i = 0; i < 1000; i++) { + status = READCSR(sc, R_DMA_STAT); + if ((status & M_DMA_STAT_MCR1_FULL) == 0) + break; + cfe_sleep(1); + } + + if (i == 1000) { + dumpcsrs(sc, "bcm5821: full bit never clears"); + return -1; + } + +#ifdef IRQ + /* Enable interrupt polling, but the handler is never called. */ + cfe_request_irq(sc->irq, bcm5821_interrupt, NULL, 0, 0); +#endif + } + + stop = 0; /* Keep compiler happy */ + zclk_init(0); /* Time origin is arbitrary. */ + start = zclk_get(); + + for (d = 0; d < N_DEVICES; d++) { + sc = dev[d].sc; + + /* MCR ports are double buffered. */ + for (i = 0; i < 2; i++) { + int index = ring[next]; + while ((READCSR(sc, R_DMA_STAT) & M_DMA_STAT_MCR1_FULL) != 0) + continue; + WRITECSR(sc, R_MCR1, PHYS_TO_PCI(PTR_TO_PHYS(mcr[index]))); + dev[d].index[i] = index; + next = (next + 1) % POOL_SIZE; + started++; + } + } + + while (trials == 0 || run != trials) { +#ifdef IRQ + while ((_getcause() & M_CAUSE_IP2) == 0) + continue; + + pending = *irqstat; +#else + pending = 0; + while (pending == 0) { + for (d = 0; d < N_DEVICES; d++) { + volatile uint32_t *last_mcr = mcr[dev[d].index[0]]; + + if ((*last_mcr & M_MCR_DONE) != 0) + pending |= dev[d].irq_mask; + } + } +#endif + + stop = zclk_get(); + + for (d = 0; d < N_DEVICES; d++) { + if ((dev[d].irq_mask & pending) != 0) { + sc = dev[d].sc; + +#ifdef IRQ + status = READCSR(sc, R_DMA_STAT); + if ((status & M_DMA_STAT_MCR1_INTR) == 0) { + /* Apparently MCR1_ALL_EMPTY, timing of which is unclear. */ + WRITECSR(sc, R_DMA_STAT, + M_DMA_STAT_DMAERR_INTR | M_DMA_STAT_MCR1_INTR); + continue; + } + WRITECSR(sc, R_DMA_STAT, + M_DMA_STAT_DMAERR_INTR | M_DMA_STAT_MCR1_INTR); +#endif + ring[last] = dev[d].index[0]; + /* Clear the DONE and ERROR bits. This will bring one line of + the MCR back into L1. Flush? */ + mcr[ring[last]][0] = V_MCR_NUM_PACKETS(2); + last = (last + 1) % POOL_SIZE; + + run++; + if (run == trials) + break; + + dev[d].index[0] = dev[d].index[1]; + if (trials == 0 || started < trials) { + int index = ring[next]; + while ((READCSR(sc, R_DMA_STAT) & M_DMA_STAT_MCR1_FULL) != 0) + continue; + WRITECSR(sc, R_MCR1, PHYS_TO_PCI(PTR_TO_PHYS(mcr[index]))); + dev[d].index[1] = index; + next = (next + 1) % POOL_SIZE; + started++; + } + } + } + } + + for (d = 0; d < N_DEVICES; d++) { + sc = dev[d].sc; + status = READCSR(sc, R_DMA_STAT); + WRITECSR(sc, R_DMA_STAT, status); /* clear pending bits */ +#ifdef IRQ + cfe_free_irq(sc->irq, 0); +#endif + } + + ticks = zclk_elapsed(stop, start) / trials; + xprintf("bcm5821: Composite %lld ticks for %d bytes, %d packets\n", + ticks, len, trials); + /* Scaling for two decimal places. */ + tpb = (ticks*100) / len; + Mbs = (2000*100)*100 / tpb; + xprintf(" rate %lld.%02lld Mbps\n", Mbs/100, Mbs % 100); + + for (i = 0; i < POOL_SIZE; i++) + bcm5821_free_composite(mcr[i]); + + return 0; +} + + +extern cfe_devctx_t *cfe_handle_table[]; + +int bcm5821_test (int device, int trials); +int +bcm5821_test (int device, int trials) +{ + cfe_devctx_t *ctx = cfe_handle_table[device]; + bcm5821_state_t *sc = ctx->dev_softc; + + if (!test_init) { + zclk_init(0); /* Time origin is arbitrary */ + test_init = 1; + } + + bcm5821_composite(sc, 1472, trials); + + return 0; +} + +int bcm5821_test2 (int device0, int device2, int trials); +int +bcm5821_test2 (int device0, int device1, int trials) +{ + cfe_devctx_t *ctx0 = cfe_handle_table[device0]; + cfe_devctx_t *ctx1 = cfe_handle_table[device1]; + bcm5821_state_t *sc0 = ctx0->dev_softc; + bcm5821_state_t *sc1 = ctx1->dev_softc; + + if (!test_init) { + zclk_init(0); /* Time origin is arbitrary */ + test_init = 1; + } + + bcm5821_composite2(sc0, sc1, 1472, trials); + + return 0; +} + + +static int +bcm5821_hash_md5 (bcm5821_state_t *sc, const char *msg) +{ + size_t len = strlen(msg); + uint32_t *mcr; + uint32_t status; + int i; + int swap = (sc->device == K_PCI_ID_BCM5820); + + mcr = bcm5821_alloc_hash(msg, len, swap); + + /* bcm5821_dump_mcr(mcr, 1); */ + + status = READCSR(sc, R_DMA_STAT); + WRITECSR(sc, R_DMA_STAT, status); /* clear pending bits */ + status = READCSR(sc, R_DMA_STAT); + + for (i = 0; i < 1000; i++) { + status = READCSR(sc, R_DMA_STAT); + if ((status & M_DMA_STAT_MCR1_FULL) == 0) + break; + cfe_sleep(1); + } + if (i == 1000) { + dumpcsrs(sc, "bcm5821: full bit never clears"); + return -1; + } + + WRITECSR(sc, R_MCR1, PHYS_TO_PCI(PTR_TO_PHYS(mcr))); + + for (i = 0; i < 1000; i++) { +#ifdef IRQ + status = READCSR(sc, R_DMA_STAT); + if ((status & M_DMA_STAT_MCR1_INTR) != 0) + break; +#else + if ((mcr[0] & M_MCR_DONE) != 0) + break; +#endif + cfe_sleep(1); + } + if (i == 1000) { + dumpcsrs(sc, "bcm5821: done bit never sets"); + /*return -1;*/ + } + + status = READCSR(sc, R_DMA_STAT); + WRITECSR(sc, R_DMA_STAT, status); /* clear pending bits */ + + /* bcm5821_dump_mcr(mcr, 1); */ + + bcm5821_show_mcr(mcr, 1); + + bcm5821_free_hash(mcr); + + return 0; +} + + +static int +bcm5821_hmac_md5 (bcm5821_state_t *sc, + const uint8_t key[], size_t key_len, + const uint8_t data[], size_t data_len) +{ + uint32_t *mcr; + uint32_t status; + int i; + int swap = (sc->device == K_PCI_ID_BCM5820); + + mcr = bcm5821_alloc_hmac(key, key_len, data, data_len, swap); + + status = READCSR(sc, R_DMA_STAT); + WRITECSR(sc, R_DMA_STAT, status); /* clear pending bits */ + status = READCSR(sc, R_DMA_STAT); + + for (i = 0; i < 1000; i++) { + status = READCSR(sc, R_DMA_STAT); + if ((status & M_DMA_STAT_MCR1_FULL) == 0) + break; + cfe_sleep(1); + } + if (i == 1000) { + dumpcsrs(sc, "bcm5821: full bit never clears"); + return -1; + } +#if 0 /* disable: work in progress */ + + while ((READCSR(sc, R_DMA_STAT) & M_DMA_STAT_MCR1_FULL) != 0) + continue; + WRITECSR(sc, R_MCR1, PHYS_TO_PCI(PTR_TO_PHYS(mcr))); + + for (i = 0; i < 1000; i++) { + if ((mcr[0] & M_MCR_DONE) != 0) + break; + cfe_sleep(1); + } + if (i == 1000) { + dumpcsrs(sc, "bcm5821: done bit never sets"); + return -1; + } + + status = READCSR(sc, R_DMA_STAT); + WRITECSR(sc, R_DMA_STAT, status); /* clear pending bits */ + + bcm5821_show_mcr(mcr, 1); +#endif + + bcm5821_free_hmac(mcr); + return 0; +} + +/* Sanity check on the implementation using RFC test suites. */ + +int bcm5821_check (int device); +int +bcm5821_check (int device) +{ + static unsigned char k1[16] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b + }; + static unsigned char m1[] = "Hi There"; + + static unsigned char k2[] = "Jefe"; + static unsigned char m2[] = "what do ya want for nothing?"; + + static unsigned char k3[16] = { + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA + }; + static unsigned char m3[50] = { + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD + }; + + cfe_devctx_t *ctx = cfe_handle_table[device]; + bcm5821_state_t *sc = ctx->dev_softc; + + if (!test_init) { + zclk_init(0); /* Time origin is arbitrary */ + test_init = 1; + } + +#if 0 /* 5821 cannot handle 0-length fragments (see Appendix B) */ + bcm5821_hash_md5(sc, ""); +#endif + bcm5821_hash_md5(sc, "a"); + bcm5821_hash_md5(sc, "abc"); + bcm5821_hash_md5(sc, "message digest"); + bcm5821_hash_md5(sc, "abcdefghijklmnopqrstuvwxyz"); + bcm5821_hash_md5(sc, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + bcm5821_hash_md5(sc, "12345678901234567890123456789012345678901234567890123456789012345678901234567890"); + + bcm5821_hmac_md5(sc, k1, sizeof(k1), m1, strlen(m1)); + bcm5821_hmac_md5(sc, k2, strlen(k2), m2, strlen(m2)); + bcm5821_hmac_md5(sc, k3, sizeof(k3), m3, sizeof(m3)); + + return 0; +} + +/* Output of md5 test suite (md5 -x) + +MD5 test suite: +MD5 ("") = d41d8cd98f00b204e9800998ecf8427e +MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661 +MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72 +MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0 +MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b +MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = d174ab98d277d9f5a5611c2c9f419d9f +MD5 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = 57edf4a22be3c955ac49da2e2107b67a + +*/ + +/* HMAC-MD5 test suite + +Test Vectors (Trailing '\0' of a character string not included in test): + + key = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b + key_len = 16 bytes + data = "Hi There" + data_len = 8 bytes + digest = 0x9294727a3638bb1c13f48ef8158bfc9d + + key = "Jefe" + data = "what do ya want for nothing?" + data_len = 28 bytes + digest = 0x750c783e6ab0b503eaa86e310a5db738 + + key = 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + key_len 16 bytes + data = 0xDDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD + data_len = 50 bytes + digest = 0x56be34521d144c88dbb8c733f0e8b3f6 + +*/ +#endif /* !_BCM14XX_ */ + + +#if 0 /* ---------- XXX still under construction XXX ---------- */ + +static uint32_t * +bcm5821_alloc_rng (int output_size) +{ + uint32_t *output; + uint32_t *mcr; + uint32_t *cmd; + int i; + + output = (uint32_t *)KMALLOC(output_size, CACHE_LINE_SIZE); + for (i = 0; i < output_size/sizeof(uint32_t); i++) + output[i] = 0xDEADBEEF; + + mcr = KMALLOC(MCR_WORDS(1)*4, CACHE_LINE_SIZE); + mcr[0] = V_MCR_NUM_PACKETS(1); + + cmd = KMALLOC(16, CACHE_LINE_SIZE); /* Always allocate >= 64 bytes */ + cmd[0] = V_CC_OPCODE(K_RNG_DIRECT) | V_CC_LEN(4); + + mcr[1] = PHYS_TO_PCI(PTR_TO_PHYS(cmd)); + + /* input fragment */ + mcr[2] = 0; + mcr[3] = 0; + mcr[4] = V_DBC_DATA_LEN(0); + + mcr[5] = V_PD_PKT_LEN(output_size); + + /* output fragment */ + mcr[6] = PHYS_TO_PCI(PTR_TO_PHYS(output)); + mcr[7] = 0; + mcr[8] = V_DBC_DATA_LEN(sizeof(output)); + + return mcr; +} + + +static uint32_t * +bcm5821_alloc_ssl_mac (int msg_size) +{ + uint32_t *message; + uint32_t *cmd; + uint32_t *mcr; + uint32_t *hash; + int i; + + message = KMALLOC(msg_size, CACHE_LINE_SIZE); + for (i = 0; i < msg_size; i++) + ((uint8_t *)message)[i] = i & 0xFF; /* was 0xDEADBEEF */ + + mcr = KMALLOC(MCR_WORDS(1)*4, CACHE_LINE_SIZE); + mcr[0] = V_MCR_NUM_PACKETS(1); + + /* packet 1 */ + + cmd = KMALLOC(SSL_MAC_CMD_WORDS*4, CACHE_LINE_SIZE); + cmd[0] = V_CC_OPCODE(K_SSL_MAC) | V_CC_LEN(SSL_MAC_CMD_WORDS*4); + cmd[1] = V_CC_FLAGS(K_HASH_FLAGS_MD5); + for (i = 2; i < 6; i++) + cmd[i] = 0x01020304; /* XXX MAC write secret */ + cmd[6] = 0x00000000; /* must be zero for SSL */ + for (i = 7; i < 19; i++) + cmd[i] = 0x36363636; + cmd[19] = 0; /* seq num */ + cmd[20] = 1; + cmd[21] = 0x03000000 | (msg_size << 8); /* type/len/rsvd */ + + mcr[1] = PHYS_TO_PCI(PTR_TO_PHYS(cmd)); + + /* input fragment */ + mcr[2] = PHYS_TO_PCI(PTR_TO_PHYS(message)); + mcr[3] = 0; + mcr[4] = V_DBC_DATA_LEN(msg_size); + + mcr[5] = V_PD_PKT_LEN(msg_size); + + hash = KMALLOC(5*4, CACHE_LINE_SIZE); + for (i = 0; i < 5; i++) + hash[i] = 0; + + mcr[6] = 0; + mcr[7] = PHYS_TO_PCI(PTR_TO_PHYS(hash)); + mcr[8] = 0; + + return mcr; +} + +static uint32_t * +bcm5821_alloc_arc4 (int input_size) +{ + uint32_t *mcr; + uint32_t cmd[3 + 256/4]; + uint32_t input[64], output[64]; + uint32_t chain[3]; + uint32_t update[1 + 256/4]; + uint32_t status; + uint8_t *arc4_state; + int i; + + xprintf("\nARC4\n"); + + status = READCSR(sc, R_DMA_STAT); + WRITECSR(sc, R_DMA_STAT, status); /* clear pending bits */ + status = READCSR(sc, R_DMA_STAT); + + for (i = 0; i < 64; i++) +#if 0 + input[i] = (i << 24) | ((i+1) << 16) | ((i+2) << 8) | (i+3); +#else + input[i] = 0x5555AAAA; +#endif + for (i = 0; i < 64; i++) + output[i] = 0xDEADBEEF; + for (i = 0; i < 1 + 256/4; i++) + update[i] = 0xFEEDFACE; + + cmd[0] = V_CC_OPCODE(K_ARC4) | V_CC_LEN(sizeof(cmd)); + cmd[1] = M_ARC4_FLAGS_WRITEBACK; +#if 0 + cmd[2] = 0x000100F3; +#else + cmd[2] = 0x00000000; +#endif + arc4_state = (uint8_t *)&cmd[3]; + for (i = 0; i < 256; i++) + arc4_state[i] = i; + + mcr[0] = V_MCR_NUM_PACKETS(1); + + /* packet 1 */ + mcr[1] = PHYS_TO_PCI(PTR_TO_PHYS(cmd)); + + /* input fragment */ + mcr[2] = PHYS_TO_PCI(PTR_TO_PHYS(input)); + mcr[3] = 0; + mcr[4] = V_DBC_DATA_LEN(sizeof(input)); + + mcr[5] = V_PD_PKT_LEN(sizeof(input)); + + /* output fragment */ + mcr[6] = PHYS_TO_PCI(PTR_TO_PHYS(output)); + mcr[7] = PHYS_TO_PCI(PTR_TO_PHYS(chain)); + mcr[8] = V_DBC_DATA_LEN(sizeof(output)); + + /* output update */ + chain[0] = PHYS_TO_PCI(PTR_TO_PHYS(update)); + chain[1] = 0; + chain[2] = V_DBC_DATA_LEN(sizeof(update)); /* not actually used */ + + return mcr; +} +#endif /* 0 */ diff --git a/cfe/cfe/dev/dev_dc21143.c b/cfe/cfe/dev/dev_dc21143.c new file mode 100644 index 0000000..f133cbb --- /dev/null +++ b/cfe/cfe/dev/dev_dc21143.c @@ -0,0 +1,2388 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * DC2114x Ethernet Driver File: dev_dc21143.c + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + +#include "sbmips.h" + +#ifndef _SB_MAKE64 +#define _SB_MAKE64(x) ((uint64_t)(x)) +#endif +#ifndef _SB_MAKEMASK1 +#define _SB_MAKEMASK1(n) (_SB_MAKE64(1) << _SB_MAKE64(n)) +#endif + +#include "lib_types.h" +#include "lib_hssubr.h" +#include "lib_malloc.h" +#include "lib_string.h" +#include "lib_printf.h" +#include "lib_queue.h" + +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_ioctl.h" +#include "cfe_timer.h" +#include "cfe_irq.h" + +#include "pcivar.h" +#include "pcireg.h" + +#include "dc21143.h" +#include "mii.h" + +/* This is a driver for specific configurations of the DC21041, + DC21140A and DC21143, not a generic Tulip driver. The prefix + "tulip_" is used to indicate generic Tulip functions, while + "dc21041_", "dc21140_" or "dc21143_" indicates functions specific + to a chip variant. + + The 21041 driver assumes a 10BT HD interface, since autonegotiation + is known to be broken in the early revisons of that chip. Example + cards come from DEC. + + The 21140 driver assumes that the PHY uses a standard MII interface + for both 100BT and 10BT. Example cards come from DEC (National DP83840 + Twister PHY) and Netgear (Level One PHY). + + The 21143 driver assumes that the PHY uses the SYM ("5 wire") interface + for 100BT with pass-through for 10BT. Example cards come from DEC + (MicroLinear ML6694 PHY) and Znyx (Kendin KS8761 PHY). There is no + support for MII or AUI interfaces. + + This SB1250 version takes advantage of DMA coherence and uses + "preserve bit lanes" addresses for all accesses that cross the + ZBbus-PCI bridge. */ + +#ifndef TULIP_DEBUG +#define TULIP_DEBUG 0 +#endif + +#ifndef TULIP_TUNE +#define TULIP_TUNE 1 +#endif + +#define ENET_ADDR_LEN 6 /* size of an ethernet address */ +#define MAX_ETHER_PACK 1518 /* max size of a packet */ +#define CRC_SIZE 4 /* size of CRC field */ + +/* The following must be a multiple of 4. In late versions of the + tulip, the rx DMA engine also apparently writes beyond 1520 bytes + for received packets > 1516 bytes (WriteInvalidate only?). */ +#define ETH_BUFFER_SIZE 1536 + +typedef struct eth_pkt_s { + queue_t next; + void *devctx; + unsigned char *buffer; + unsigned int flags; + int length; + /* packet data goes here, should always be longword aligned */ +} eth_pkt_t; + +/* packet flags */ +#define ETH_TX_SETUP 1 + +static void +show_packet(eth_pkt_t *pkt) +{ + int i; + int n = (pkt->length < 32 ? pkt->length : 32); + + xprintf("[%4d]:", pkt->length); + for (i = 0; i < n; i++) { + if (i % 4 == 0) + xprintf(" "); + xprintf("%02x", pkt->buffer[i]); + } + xprintf("\n"); +} + + +/* Descriptor structures */ + +typedef struct rx_dscr { + uint32_t rxd_flags; + uint32_t rxd_bufsize; + uint32_t rxd_bufaddr1; + uint32_t rxd_bufaddr2; +} rx_dscr; + +typedef struct tx_dscr { + uint32_t txd_flags; + uint32_t txd_bufsize; + uint32_t txd_bufaddr1; + uint32_t txd_bufaddr2; +} tx_dscr; + +/* CAM structure */ + +typedef union { + struct { + uint32_t physical[CAM_PERFECT_ENTRIES][3]; + } p; + struct { + uint32_t hash[32]; + uint32_t mbz[7]; + uint32_t physical[3]; + } h; +} tulip_cam; + + +typedef enum { + eth_state_uninit, + eth_state_setup, + eth_state_off, + eth_state_on, + eth_state_broken +} eth_state_t; + +#define ETH_PKTPOOL_SIZE 20 +#define ETH_PKTBUF_SIZE (sizeof(eth_pkt_t) + ((ETH_BUFFER_SIZE+7)&~7)) + +typedef struct tulip_softc { + uint32_t membase; + uint8_t irq; /* interrupt mapping (not currently used) */ + pcitag_t tag; /* tag for configuration registers */ + + uint8_t hwaddr[ENET_ADDR_LEN]; + uint16_t device; /* chip device code */ + uint8_t revision; /* chip revision and step (Table 3-7) */ + + /* current state */ + eth_state_t state; + int bus_errors; + + /* These fields are the chip startup values. */ +// uint16_t media; /* media type */ + uint32_t opmode; /* operating mode */ + uint32_t intmask; /* interrupt mask */ + + /* This field is set before calling dc2114x_hwinit */ + int linkspeed; /* encoding from cfe_ioctl */ + + /* Packet free list */ + queue_t freelist; + uint8_t *pktpool; + queue_t rxqueue; + + /* The descriptor tables */ + uint8_t *rxdscrmem; /* receive descriptors */ + uint8_t *txdscrmem; /* transmit descriptors */ + + eth_pkt_t **rxdscrinfo; + eth_pkt_t **txdscrinfo; + + /* These fields keep track of where we are in tx/rx processing */ + volatile rx_dscr *rxdscr_start; /* beginning of ring */ + volatile rx_dscr *rxdscr_end; /* end of ring */ + volatile rx_dscr *rxdscr_remove; /* next one we expect tulip to use */ + volatile rx_dscr *rxdscr_add; /* next place to put a buffer */ + int rxdscr_onring; + + volatile tx_dscr *txdscr_start; /* beginning of ring */ + volatile tx_dscr *txdscr_end; /* end of ring */ + volatile tx_dscr *txdscr_remove; /* next one we will use for tx */ + volatile tx_dscr *txdscr_add; /* next place to put a buffer */ + + /* These fields describe the PHY */ + int mii_addr; + +} tulip_softc; + + +/* Driver parameterization */ + +#define MAXRXDSCR 16 +#define MAXTXDSCR 16 +#define MINRXRING 8 + +#define MEDIA_UNKNOWN 0 +#define MEDIA_AUI 1 +#define MEDIA_BNC 2 +#define MEDIA_UTP_FULL_DUPLEX 3 +#define MEDIA_UTP_NO_LINK_TEST 4 +#define MEDIA_UTP 5 + +/* Prototypes */ + +static void tulip_ether_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + + +/* Address mapping macros */ + +/* Note that PHYSADDR only works with 32-bit addresses, but the + so does the Tulip. */ +#define PHYSADDR(sc,x) (K0_TO_PHYS((uintptr_t)(x))) + +#define PCIADDR(a,b) ((uint32_t) (b)) +#define PCIADDRX(a,b) ((uint32_t) (b) | 0x20000000) +#define PCI_TO_CPU(a) (a) + +#if __long64 +#define READCSR(sc,csr) \ + (*((volatile uint32_t *) \ + (PHYS_TO_XKSEG_UNCACHED(PCI_TO_CPU((sc)->membase)+(csr))))) + +#define WRITECSR(sc,csr,val) \ + (*((volatile uint32_t *) \ + (PHYS_TO_XKSEG_UNCACHED(PCI_TO_CPU((sc)->membase)+(csr)))) = (val)) + +#else +#define READCSR(sc,csr) \ + (hs_read32(PHYS_TO_XKSEG_UNCACHED(PCI_TO_CPU((sc)->membase)+(csr)))) + +#define WRITECSR(sc,csr,val) \ + (hs_write32(PHYS_TO_XKSEG_UNCACHED(PCI_TO_CPU((sc)->membase)+(csr)), \ + (val))) + +#endif + + +#define RESET_ADAPTER(sc) \ + { \ + WRITECSR((sc), R_CSR_BUSMODE, M_CSR0_SWRESET); \ + cfe_sleep(CFE_HZ/10); \ + } + + +/* Debugging */ + +static void +dumpstat(tulip_softc *sc) +{ + xprintf("-- CSR 5 = %08X CSR 6 = %08x\n", + READCSR(sc, R_CSR_STATUS), READCSR(sc, R_CSR_OPMODE)); +} + +static void +dumpcsrs(tulip_softc *sc) +{ + int idx; + + xprintf("-------------\n"); + for (idx = 0; idx < 16; idx++) { + xprintf("CSR %2d = %08X\n", idx, READCSR(sc, idx*8)); + } + xprintf("-------------\n"); +} + + +/* Packet management */ + +/* ********************************************************************* + * ETH_ALLOC_PKT(s) + * + * Allocate a packet from the free list. + * + * Input parameters: + * s - eth structure + * + * Return value: + * pointer to packet structure, or NULL if none available + ********************************************************************* */ +#define ETHPKT_ALIGN ((uintptr_t) 4) + +static eth_pkt_t * +eth_alloc_pkt(tulip_softc *s) +{ + uintptr_t addr; + eth_pkt_t *pkt; + + pkt = (eth_pkt_t *) q_deqnext(&s->freelist); + if (!pkt) return NULL; + + addr = (uintptr_t) (pkt+1); + if (addr & (ETHPKT_ALIGN-1)) { + addr = (addr + ETHPKT_ALIGN) & ~(ETHPKT_ALIGN-1); + } + + pkt->buffer = (uint8_t *) addr; + pkt->length = ETH_BUFFER_SIZE; + pkt->flags = 0; + + return pkt; +} + + +/* ********************************************************************* + * ETH_FREE_PKT(s,pkt) + * + * Return a packet to the free list + * + * Input parameters: + * s - sbmac structure + * pkt - packet to return + * + * Return value: + * nothing + ********************************************************************* */ +static void +eth_free_pkt(tulip_softc *s, eth_pkt_t *pkt) +{ + q_enqueue(&s->freelist, &pkt->next); +} + + +/* ********************************************************************* + * ETH_INITFREELIST(s) + * + * Initialize the buffer free list for this mac. The memory + * allocated to the free list is carved up and placed on a linked + * list of buffers for use by the mac. + * + * Input parameters: + * s - eth structure + * + * Return value: + * nothing + ********************************************************************* */ +static void +eth_initfreelist(tulip_softc *s) +{ + int idx; + unsigned char *ptr; + eth_pkt_t *pkt; + + q_init(&s->freelist); + + ptr = s->pktpool; + for (idx = 0; idx < ETH_PKTPOOL_SIZE; idx++) { + pkt = (eth_pkt_t *) ptr; + eth_free_pkt(s, pkt); + ptr += ETH_PKTBUF_SIZE; + } +} + + +/* Descriptor ring management */ + +static int +tulip_add_rcvbuf(tulip_softc *sc, eth_pkt_t *pkt) +{ + volatile rx_dscr *rxd; + volatile rx_dscr *nextrxd; + int idx; + uint32_t flags = 0; + + rxd = sc->rxdscr_add; + + /* Figure out where the next descriptor will go */ + nextrxd = rxd+1; + if (nextrxd == sc->rxdscr_end) { + nextrxd = sc->rxdscr_start; + flags = M_RDES1_ENDOFRING; + } + + /* + * If the next one is the same as our remove pointer, + * the ring is considered full. (it actually has room for + * one more, but we reserve the remove == add case for "empty") + */ + if (nextrxd == sc->rxdscr_remove) return -1; + + /* Save this packet pointer. */ + idx = rxd - sc->rxdscr_start; + sc->rxdscrinfo[idx] = pkt; + + rxd->rxd_bufsize = V_RDES1_BUF1SIZE(ETH_BUFFER_SIZE) | flags; + rxd->rxd_bufaddr1 = PCIADDRX(sc, PHYSADDR(sc, pkt->buffer)); + rxd->rxd_bufaddr2 = 0; + rxd->rxd_flags = M_RDES0_OWNADAP; + + /* success, advance the pointer */ + sc->rxdscr_add = nextrxd; + sc->rxdscr_onring++; + + return 0; +} + +static void +tulip_fillrxring(tulip_softc *sc) +{ + eth_pkt_t *pkt; + + while (sc->rxdscr_onring < MINRXRING) { + pkt = eth_alloc_pkt(sc); + if (pkt == NULL) { + /* could not allocate a buffer */ + break; + } + if (tulip_add_rcvbuf(sc, pkt) != 0) { + /* could not add buffer to ring */ + eth_free_pkt(sc, pkt); + break; + } + } +} + + +/* ********************************************************************* + * TULIP_RX_CALLBACK(sc, pkt) + * + * Receive callback routine. This routine is invoked when a + * buffer queued for receives is filled. In this simple driver, + * all we do is add the packet to a per-MAC queue for later + * processing, and try to put a new packet in the place of the one + * that was removed from the queue. + * + * Input parameters: + * sc - interface + * ptk - packet context (sbeth_pkt structure) + * + * Return value: + * nothing + ********************************************************************* */ +static void +tulip_rx_callback(tulip_softc *sc, eth_pkt_t *pkt) +{ + if (TULIP_DEBUG) show_packet(pkt); /* debug */ + + q_enqueue(&sc->rxqueue, &pkt->next); + + tulip_fillrxring(sc); +} + + +static void +tulip_procrxring(tulip_softc *sc) +{ + volatile rx_dscr *rxd; + eth_pkt_t *pkt; + eth_pkt_t *newpkt; + int idx; + uint32_t flags; + + for (;;) { + rxd = (volatile rx_dscr *) sc->rxdscr_remove; + idx = rxd - sc->rxdscr_start; + + flags = rxd->rxd_flags; + + if (flags & M_RDES0_OWNADAP) { + /* end of ring, no more packets */ + break; + } + + pkt = sc->rxdscrinfo[idx]; + + /* Drop error packets */ + if (flags & M_RDES0_ERRORSUM) { + xprintf("DC2114x: rx error %04X\n", flags & 0xFFFF); + tulip_add_rcvbuf(sc, pkt); + goto next; + } + + /* Pass up the packet */ + pkt->length = G_RDES0_FRAMELEN(flags) - CRC_SIZE; + tulip_rx_callback(sc, pkt); + + /* put a buffer back on the ring to replace this one */ + newpkt = eth_alloc_pkt(sc); + if (newpkt) tulip_add_rcvbuf(sc, newpkt); + +next: + /* update the pointer, accounting for buffer wrap. */ + rxd++; + if (rxd == sc->rxdscr_end) + rxd = sc->rxdscr_start; + + sc->rxdscr_remove = (rx_dscr *) rxd; + sc->rxdscr_onring--; + } +} + + +static int +tulip_add_txbuf(tulip_softc *sc, eth_pkt_t *pkt) +{ + volatile tx_dscr *txd; + volatile tx_dscr *nexttxd; + int idx; + uint32_t bufsize = 0; + + txd = sc->txdscr_add; + + /* Figure out where the next descriptor will go */ + nexttxd = (txd+1); + if (nexttxd == sc->txdscr_end) { + nexttxd = sc->txdscr_start; + bufsize = M_TDES1_ENDOFRING; + } + + /* If the next one is the same as our remove pointer, + the ring is considered full. (it actually has room for + one more, but we reserve the remove == add case for "empty") */ + + if (nexttxd == sc->txdscr_remove) return -1; + + /* Save this packet pointer. */ + idx = txd - sc->txdscr_start; + sc->txdscrinfo[idx] = pkt; + + bufsize |= V_TDES1_BUF1SIZE(pkt->length) | + M_TDES1_FIRSTSEG | M_TDES1_LASTSEG | M_TDES1_INTERRUPT; + if (pkt->flags & ETH_TX_SETUP) { + /* For a setup packet, FIRSTSEG and LASTSEG should be clear (!) */ + bufsize ^= M_TDES1_SETUP | M_TDES1_FIRSTSEG | M_TDES1_LASTSEG; + } + txd->txd_bufsize = bufsize; + txd->txd_bufaddr1 = PCIADDRX(sc, PHYSADDR(sc, pkt->buffer)); + txd->txd_bufaddr2 = 0; + txd->txd_flags = M_TDES0_OWNADAP; + + /* success, advance the pointer */ + sc->txdscr_add = nexttxd; + + return 0; +} + + +static int +tulip_transmit(tulip_softc *sc,eth_pkt_t *pkt) +{ + tulip_add_txbuf(sc, pkt); + + WRITECSR(sc, R_CSR_TXPOLL, 1); + return 0; +} + + +static void +tulip_proctxring(tulip_softc *sc) +{ + volatile tx_dscr *txd; + eth_pkt_t *pkt; + int idx; + uint32_t flags; + + for (;;) { + txd = (volatile tx_dscr *) sc->txdscr_remove; + idx = txd - sc->txdscr_start; + + flags = txd->txd_flags; + + if (txd == sc->txdscr_add) { + /* ring is empty, no buffers to process */ + break; + } + + if (flags & M_RDES0_OWNADAP) { + /* Reached a packet still being transmitted */ + break; + } + + /* Check for a completed setup packet */ + pkt = sc->txdscrinfo[idx]; + if (pkt->flags & ETH_TX_SETUP) { + if (sc->state == eth_state_setup) { + /* check flag bits */ + sc->state = eth_state_on; + } + pkt->flags &=~ ETH_TX_SETUP; + } + + /* Just free the packet */ + eth_free_pkt(sc, pkt); + + /* update the pointer, accounting for buffer wrap. */ + txd++; + if (txd == sc->txdscr_end) + txd = sc->txdscr_start; + + sc->txdscr_remove = (tx_dscr *) txd; + } +} + + +static void +tulip_initrings(tulip_softc *sc) +{ + volatile tx_dscr *txd; + volatile rx_dscr *rxd; + + /* Claim ownership of all transmit descriptors */ + + for (txd = sc->txdscr_start; txd != sc->txdscr_end; txd++) + txd->txd_flags = 0; + for (rxd = sc->rxdscr_start; rxd != sc->rxdscr_end; rxd++) + rxd->rxd_flags = 0; + + /* Init the ring pointers */ + + sc->txdscr_add = sc->txdscr_remove = sc->txdscr_start; + sc->rxdscr_add = sc->rxdscr_remove = sc->rxdscr_start; + sc->rxdscr_onring = 0; + + /* Add stuff to the receive ring */ + + tulip_fillrxring(sc); +} + + +static int +tulip_init(tulip_softc *sc) +{ + /* Allocate descriptor rings and lookaside lists */ + sc->rxdscrmem = KMALLOC(MAXRXDSCR*sizeof(rx_dscr),sizeof(rx_dscr)); + sc->txdscrmem = KMALLOC(MAXTXDSCR*sizeof(tx_dscr),sizeof(tx_dscr)); + sc->rxdscrinfo = KMALLOC(MAXRXDSCR*sizeof(eth_pkt_t *),sizeof(eth_pkt_t)); + sc->txdscrinfo = KMALLOC(MAXTXDSCR*sizeof(eth_pkt_t *),sizeof(eth_pkt_t)); + + /* Allocate buffer pool */ + sc->pktpool = KMALLOC(ETH_PKTPOOL_SIZE*ETH_PKTBUF_SIZE, 0); + eth_initfreelist(sc); + q_init(&sc->rxqueue); + + /* Fill in pointers to the rings */ + sc->rxdscr_start = (rx_dscr *) (sc->rxdscrmem); + sc->rxdscr_end = sc->rxdscr_start + MAXRXDSCR; + sc->rxdscr_add = sc->rxdscr_start; + sc->rxdscr_remove = sc->rxdscr_start; + sc->rxdscr_onring = 0; + + sc->txdscr_start = (tx_dscr *) (sc->txdscrmem); + sc->txdscr_end = sc->txdscr_start + MAXTXDSCR; + sc->txdscr_add = sc->txdscr_start; + sc->txdscr_remove = sc->txdscr_start; + + tulip_initrings(sc); + + return 0; +} + + +static void +tulip_resetrings(tulip_softc *sc) +{ + volatile tx_dscr *txd; + volatile rx_dscr *rxd; + int idx; + + /* Free already-sent descriptors and buffers */ + tulip_proctxring(sc); + + /* Free any pending but unsent */ + txd = (volatile tx_dscr *) sc->txdscr_remove; + while (txd != sc->txdscr_add) { + idx = txd - sc->txdscr_start; + eth_free_pkt(sc, sc->txdscrinfo[idx]); + txd->txd_flags &=~ M_TDES0_OWNADAP; + + txd++; + if (txd == sc->txdscr_end) + txd = sc->txdscr_start; + } + sc->txdscr_add = sc->txdscr_remove; + + /* Discard any received packets as well as all free buffers */ + rxd = (volatile rx_dscr *) sc->rxdscr_remove; + while (rxd != sc->rxdscr_add) { + idx = rxd - sc->rxdscr_start; + eth_free_pkt(sc, sc->rxdscrinfo[idx]); + rxd->rxd_flags &=~ M_RDES0_OWNADAP; + + rxd++; + if (rxd == sc->rxdscr_end) + rxd = sc->rxdscr_start; + sc->rxdscr_onring--; + } + + /* Reestablish the initial state. */ + tulip_initrings(sc); +} + + +/* CRCs */ + +#define IEEE_CRC32_POLY 0xEDB88320UL /* CRC-32 Poly -- either endian */ + +static uint32_t +tulip_crc32(const uint8_t *databuf, unsigned int datalen) +{ + unsigned int idx, bit, data; + uint32_t crc; + + crc = 0xFFFFFFFFUL; + for (idx = 0; idx < datalen; idx++) + for (data = *databuf++, bit = 0; bit < 8; bit++, data >>= 1) + crc = (crc >> 1) ^ (((crc ^ data) & 1) ? IEEE_CRC32_POLY : 0); + return crc; +} + +#define tulip_mchash(mca) (tulip_crc32((mca), 6) & 0x1FF) + + +/* Serial ROM access */ + +/**************************************************************************** + * tulip_spin(sc, ns) + * + * Spin for a short interval (nominally in nanoseconds) + * + * Input Parameters: ns - minimum required nsec. + * + * The delay loop uses uncached PCI reads, each of which requires + * at least 3 PCI bus clocks (45 ns at 66 MHz) to complete. The + * actual delay will be longer (much longer if preempted). + ***************************************************************************/ + +#define PCI_MIN_DELAY 45 + +static void +tulip_spin(tulip_softc *sc, long nanoseconds) +{ + long delay; + volatile uint32_t t; + + for (delay = nanoseconds; delay > 0; delay -= PCI_MIN_DELAY) + t = READCSR(sc, R_CSR_BUSMODE); +} + + +/* + * Delays below are chosen to meet specs for NS93C64 (slow M variant). + * Current parts are faster. + * Reference: NS Memory Data Book, 1994 + */ + +#define SROM_SIZE 128 +#define SROM_MAX_CYCLES 32 + +#define SROM_CMD_BITS 3 +#define SROM_ADDR_BITS 6 + +#define K_SROM_READ_CMD 06 +#define K_SROM_WRITE_CMD 05 + +#define SROM_ADDR_OFFSET 0x14 +#define SROM_CRC_OFFSET (SROM_SIZE-2) + + +static void +srom_idle_state(tulip_softc *sc) +{ + uint32_t csr9; + unsigned int i; + + csr9 = READCSR(sc, R_CSR_ROM_MII); + + csr9 |= M_CSR9_SROMCHIPSEL; + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 100); /* CS setup (Tcss=100) */ + + /* Run the clock through the maximum number of pending read cycles */ + for (i = 0; i < SROM_MAX_CYCLES*2; i++) { + csr9 ^= M_CSR9_SROMCLOCK; + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 1000); /* SK period (Fsk=0.5MHz) */ + } + + /* Deassert SROM Chip Select */ + csr9 &=~ M_CSR9_SROMCHIPSEL; + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 50); /* CS recovery (Tsks=50) */ +} + +static void +srom_send_command_bit(tulip_softc *sc, unsigned int data) +{ + uint32_t csr9; + + csr9 = READCSR(sc, R_CSR_ROM_MII); + + /* Place the data bit on the bus */ + if (data == 1) + csr9 |= M_CSR9_SROMDATAIN; + else + csr9 &=~ M_CSR9_SROMDATAIN; + + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 360); /* setup: Tdis=200 */ + + /* Now clock the data into the SROM */ + WRITECSR(sc, R_CSR_ROM_MII, csr9 | M_CSR9_SROMCLOCK); + tulip_spin(sc, 900); /* clock high, Tskh=500 */ + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 450); /* clock low, Tskl=250 */ + + /* Now clear the data bit */ + csr9 &=~ M_CSR9_SROMDATAIN; /* data invalid, Tidh=20 for SK^ */ + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 270); /* min cycle, 1/Fsk=2000 */ +} + +static uint16_t +srom_read_bit(tulip_softc *sc) +{ + uint32_t csr9; + + csr9 = READCSR(sc, R_CSR_ROM_MII); + + /* Generate a clock cycle before doing a read */ + WRITECSR(sc, R_CSR_ROM_MII, csr9 | M_CSR9_SROMCLOCK); /* rising edge */ + tulip_spin(sc, 1000); /* clock high, Tskh=500, Tpd=1000 */ + WRITECSR(sc, R_CSR_ROM_MII, csr9); /* falling edge */ + tulip_spin(sc, 1000); /* clock low, 1/Fsk=2000 */ + + csr9 = READCSR(sc, R_CSR_ROM_MII); + return ((csr9 & M_CSR9_SROMDATAOUT) != 0 ? 1 : 0); +} + +#define CMD_BIT_MASK (1 << (SROM_CMD_BITS+SROM_ADDR_BITS-1)) + +static uint16_t +srom_read_word(tulip_softc *sc, unsigned int index) +{ + uint16_t command, word; + uint32_t csr9; + unsigned int i; + + csr9 = READCSR(sc, R_CSR_ROM_MII) | M_CSR9_SROMCHIPSEL; + + /* Assert the SROM CS line */ + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 100); /* CS setup, Tcss = 100 */ + + /* Send the read command to the SROM */ + command = (K_SROM_READ_CMD << SROM_ADDR_BITS) | index; + for (i = 0; i < SROM_CMD_BITS+SROM_ADDR_BITS; i++) { + srom_send_command_bit(sc, (command & CMD_BIT_MASK) != 0 ? 1 : 0); + command <<= 1; + } + + /* Now read the bits from the SROM (MSB first) */ + word = 0; + for (i = 0; i < 16; ++i) { + word <<= 1; + word |= srom_read_bit(sc); + } + + /* Clear the SROM CS Line, CS hold, Tcsh = 0 */ + WRITECSR(sc, R_CSR_ROM_MII, csr9 &~ M_CSR9_SROMCHIPSEL); + + return word; +} + + +/**************************************************************************** + * srom_calc_crc() + * + * Calculate the CRC of the SROM and return it. We compute the + * CRC per Appendix A of the 21140A ROM/external register data + * sheet (EC-QPQWA-TE). + ***************************************************************************/ + +static uint16_t +srom_calc_crc(tulip_softc *sc, uint8_t srom[], int length) +{ + uint32_t crc = tulip_crc32(srom, length) ^ 0xffffffff; + + return (uint16_t)(crc & 0xffff); +} + +/**************************************************************************** + * srom_read_all(sc, uint8_t dest) + * + * Read the entire SROM into the srom array + * + * Input parameters: + * sc - tulip state + ***************************************************************************/ + +#define STORED_CRC(rom) \ + ((rom)[SROM_CRC_OFFSET] | ((rom)[SROM_CRC_OFFSET+1] << 8)) + +static int +srom_read_all(tulip_softc *sc, uint8_t dest[]) +{ + int i; + uint16_t crc, temp; + + WRITECSR(sc, R_CSR_ROM_MII, M_CSR9_SERROMSEL|M_CSR9_ROMREAD); + + srom_idle_state(sc); + + for (i = 0; i < SROM_SIZE/2; i++) { + temp = srom_read_word(sc, i); + dest[2*i] = temp & 0xff; + dest[2*i+1] =temp >> 8; + } + + WRITECSR(sc, R_CSR_ROM_MII, 0); /* CS hold, Tcsh=0 */ + + crc = srom_calc_crc(sc, dest, SROM_CRC_OFFSET); + if (crc != STORED_CRC(dest)) { + crc = srom_calc_crc(sc, dest, 94); /* "alternative" */ + if (crc != STORED_CRC(dest)) { + xprintf("DC2114x: Invalid SROM CRC, calc %04x, stored %04x\n", + crc, STORED_CRC(dest)); + return 0/*-1*/; + } + } + return 0; +} + +static int +srom_read_addr(tulip_softc *sc, uint8_t buf[]) +{ + uint8_t srom[SROM_SIZE]; + + if (srom_read_all(sc, srom) == 0) { + memcpy(buf, &srom[SROM_ADDR_OFFSET], ENET_ADDR_LEN); + return 0; + } + + return -1; +} + + +/**************************************************************************** + * MII access utility routines + ***************************************************************************/ + +/* MII clock limited to 2.5 MHz, transactions end with MDIO tristated */ + +static void +mii_write_bits(tulip_softc *sc, uint32_t data, unsigned int count) +{ + uint32_t csr9; + uint32_t bitmask; + + csr9 = READCSR(sc, R_CSR_ROM_MII) &~ (M_CSR9_MDC | M_CSR9_MIIMODE); + + for (bitmask = 1 << (count-1); bitmask != 0; bitmask >>= 1) { + csr9 &=~ M_CSR9_MDO; + if ((data & bitmask) != 0) csr9 |= M_CSR9_MDO; + WRITECSR(sc, R_CSR_ROM_MII, csr9); + + tulip_spin(sc, 2000); /* setup */ + WRITECSR(sc, R_CSR_ROM_MII, csr9 | M_CSR9_MDC); + tulip_spin(sc, 2000); /* hold */ + WRITECSR(sc, R_CSR_ROM_MII, csr9); + } +} + +static void +mii_turnaround(tulip_softc *sc) +{ + uint32_t csr9; + + csr9 = READCSR(sc, R_CSR_ROM_MII) | M_CSR9_MIIMODE; + + /* stop driving data */ + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 2000); /* setup */ + WRITECSR(sc, R_CSR_ROM_MII, csr9 | M_CSR9_MDC); + tulip_spin(sc, 2000); /* clock high */ + WRITECSR(sc, R_CSR_ROM_MII, csr9); + + /* read back and check for 0 here? */ +} + +/**************************************************************************** + * mii_read_register + * + * This routine reads a register from the PHY chip using the MII + * serial management interface. + * + * Input parameters: + * index - index of register to read (0-31) + * + * Return value: + * word read from register + ***************************************************************************/ + +static uint16_t +mii_read_register(tulip_softc *sc, unsigned int index) +{ + /* Send the command and address to the PHY. The sequence is + a synchronization sequence (32 1 bits) + a "start" command (2 bits) + a "read" command (2 bits) + the PHY addr (5 bits) + the register index (5 bits) + */ + uint32_t csr9; + uint16_t word; + int i; + + mii_write_bits(sc, 0xff, 8); + mii_write_bits(sc, 0xffffffff, 32); + mii_write_bits(sc, MII_COMMAND_START, 2); + mii_write_bits(sc, MII_COMMAND_READ, 2); + mii_write_bits(sc, sc->mii_addr /* XXX sc->phy.index */, 5); + mii_write_bits(sc, index, 5); + + mii_turnaround(sc); + + csr9 = (READCSR(sc, R_CSR_ROM_MII) &~ M_CSR9_MDC) | M_CSR9_MIIMODE; + word = 0; + + for (i = 0; i < 16; i++) { + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 2000); /* clock width low */ + WRITECSR(sc, R_CSR_ROM_MII, csr9 | M_CSR9_MDC); + tulip_spin(sc, 2000); /* clock width high */ + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 1000); /* output delay */ + word <<= 1; + if ((READCSR(sc, R_CSR_ROM_MII) & M_CSR9_MDI) != 0) + word |= 0x0001; + } + + return word; + + /* reset to output mode? */ +} + +/**************************************************************************** + * mii_write_register + * + * This routine writes a register in the PHY chip using the MII + * serial management interface. + * + * Input parameters: + * index - index of register to write (0-31) + * value - word to write + ***************************************************************************/ + +static void +mii_write_register(tulip_softc *sc, unsigned int index, uint16_t value) +{ + mii_write_bits(sc, 0xff, 8); + mii_write_bits(sc, 0xffffffff, 32); + mii_write_bits(sc, MII_COMMAND_START, 2); + mii_write_bits(sc, MII_COMMAND_WRITE, 2); + mii_write_bits(sc, sc->mii_addr /* XXX sc->phy.index */, 5); + mii_write_bits(sc, index, 5); + mii_write_bits(sc, MII_COMMAND_ACK, 2); + mii_write_bits(sc, value, 16); + + /* reset to input mode? */ +} + + +static int +mii_probe(tulip_softc *sc) +{ + int i; + uint16_t id1, id2; + + for (i = 0; i < 32; i++) { + sc->mii_addr = i; + id1 = mii_read_register(sc, MII_PHYIDR1); + id2 = mii_read_register(sc, MII_PHYIDR2); + if ((id1 != 0xffff || id2 != 0xffff) && + (id1 != 0x0000 || id2 != 0x0000)) { + return 0; + } + } + return -1; +} + +#if 0 +static void +mii_dump(tulip_softc *sc) +{ + int i; + uint16_t r; + + for (i = 0; i <=6; ++i) { + r = mii_read_register(sc, i); + if (r != 0) xprintf("MII_REG%02x: %04x\n", i, r); + } + for (i = 21; i <= 25; ++i) { + r = mii_read_register(sc, i); + if (r != 0) printf("MII_REG%02x: %04x\n", i, r); + } +} +#else +#define mii_dump(sc) +#endif + + +#if 0 +static void +tulip_getaddr(tulip_softc *sc, uint8_t *buf) +{ + memcpy(buf, sc->hwaddr, ENET_ADDR_LEN); +} +#endif + + +/* Chip specific code */ + +static void +dc21143_set_speed(tulip_softc *sc, int speed) +{ + uint32_t opmode = 0; + + WRITECSR(sc, R_CSR_SIAMODE0, 0); + + switch (speed) { + case ETHER_SPEED_AUTO: + break; + case ETHER_SPEED_10HDX: + default: + WRITECSR(sc, R_CSR_SIAMODE1, M_CSR14_10BT_HD); + WRITECSR(sc, R_CSR_SIAMODE2, M_CSR15_DEFAULT_VALUE); + opmode = M_CSR6_SPEED_10; + break; + case ETHER_SPEED_10FDX: + WRITECSR(sc, R_CSR_SIAMODE1, M_CSR14_10BT_FD); + WRITECSR(sc, R_CSR_SIAMODE2, M_CSR15_DEFAULT_VALUE); + opmode = M_CSR6_SPEED_10 | M_CSR6_FULLDUPLEX; + break; + case ETHER_SPEED_100HDX: + WRITECSR(sc, R_CSR_SIAMODE1, 0); + WRITECSR(sc, R_CSR_SIAMODE2, M_CSR15_DEFAULT_VALUE); + opmode = M_CSR6_SPEED_100; + break; + case ETHER_SPEED_100FDX: + WRITECSR(sc, R_CSR_SIAMODE1, 0); + WRITECSR(sc, R_CSR_SIAMODE2, M_CSR15_DEFAULT_VALUE); + opmode = M_CSR6_SPEED_100 | M_CSR6_FULLDUPLEX; + break; + } + + WRITECSR(sc, R_CSR_SIAMODE0, M_CSR13_CONN_NOT_RESET); + + opmode |= M_CSR6_MBO; +#if TULIP_TUNE + opmode |= V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72); +#else + opmode |= M_CSR6_STOREFWD; +#endif + WRITECSR(sc, R_CSR_OPMODE, opmode); +} + +static void +dc21143_autonegotiate(tulip_softc *sc) +{ + uint32_t opmode; + uint32_t tempword; + int count; + int linkspeed; + + linkspeed = ETHER_SPEED_UNKNOWN; + + /* Program the media setup into the CSRs. */ + /* reset SIA */ + WRITECSR(sc, R_CSR_SIAMODE0, 0); + + /* set to speed_10, fullduplex to start_nway */ + opmode = + M_CSR6_SPEED_10 | + M_CSR6_FULLDUPLEX | + M_CSR6_MBO; + WRITECSR(sc, R_CSR_OPMODE, opmode); + + /* Choose advertised capabilities */ + tempword = + M_CSR14_100BASETHALFDUP | + M_CSR14_100BASETFULLDUP | + M_CSR14_HALFDUPLEX10BASET; + WRITECSR(sc, R_CSR_SIAMODE1, tempword); + + /* Enable autonegotiation */ + tempword |= M_CSR14_AUTONEGOTIATE | 0xffff; + WRITECSR(sc, R_CSR_SIAMODE1, tempword); + WRITECSR(sc, R_CSR_SIAMODE2, M_CSR15_DEFAULT_VALUE); + WRITECSR(sc, R_CSR_OPMODE, opmode); + WRITECSR(sc, R_CSR_SIAMODE0, M_CSR13_CONN_NOT_RESET); + + /* STATE check nway, poll until a valid 10/100mbs signal seen */ + WRITECSR(sc, R_CSR_STATUS, M_CSR5_LINKPASS); /* try to clear this... */ + + /* (Re)start negotiation */ + tempword = READCSR(sc, R_CSR_SIASTATUS); + tempword &=~ M_CSR12_AUTONEGARBIT; + tempword |= V_CSR12_AUTONEGARBIT(0x1); + + for (count = 0; count <= 130; count++) { + tempword = READCSR(sc, R_CSR_STATUS); + if (tempword & M_CSR5_LINKPASS) + break; + cfe_sleep(CFE_HZ/100); + } + + if (count > 130) + xprintf("DC21143: Link autonegotiation failed\n"); + + /* STATE configure nway, check to see if any abilities common to us. + If they do, set to highest mode, if not, we will see if the partner + will do 100mb or 10mb - then set it */ + + tempword = READCSR(sc, R_CSR_SIASTATUS); + /* clear the autonegogiate complete bit */ + WRITECSR(sc, R_CSR_STATUS, M_CSR5_LINKPASS); + + if (tempword & M_CSR12_LINKPARTNEG) { + /* A link partner was negogiated... */ + + xprintf("DC21143: Negotiated "); + if (tempword & 0x01000000) { /* 100FD */ + xprintf("100Mb/s FDX"); + linkspeed = ETHER_SPEED_100FDX; + } + else if (tempword & 0x00800000) { /* 100HD */ + xprintf("100Mb/s HDX"); + linkspeed = ETHER_SPEED_100HDX; + } + else if (tempword & 0x00400000) { /* 10FD */ + xprintf("10Mb/s FDX"); + linkspeed = ETHER_SPEED_10FDX; + } + else if (tempword & 0x00200000) { /* 10HD */ + xprintf("10Mb/s HDX"); + linkspeed = ETHER_SPEED_10HDX; + } + xprintf("\n"); + } + else { + /* no link partner negotiation */ + /* disable link for 1.3 seconds to break any existing connections */ + + /* xprintf("Disabling link, setting to 10mb half duplex\n"); */ + dc21143_set_speed(sc, ETHER_SPEED_10HDX); + cfe_sleep(CFE_HZ/8); + + tempword = READCSR(sc, R_CSR_SIASTATUS); + + if ((tempword & 0x02) == 0) { + /* 100 mb signal present set to 100mb */ + xprintf("No link partner... setting to 100mb/s HDX\n"); + linkspeed = ETHER_SPEED_100HDX; + } + else if ((tempword & 0x04) == 0) { + /* 10 mb signal present */ + xprintf("No link partner... setting to 10mb/s HDX\n"); + linkspeed = ETHER_SPEED_10HDX; + } + else { + /* couldn't determine line speed, so set to 10mbs */ + xprintf("Unknown; defaulting to 10Mb/s HDX\n"); + linkspeed = ETHER_SPEED_10HDX; + } + } + + dc21143_set_speed(sc, linkspeed); +} + +static void +dc21143_set_loopback(tulip_softc *sc, int mode) +{ + uint32_t v; + + WRITECSR(sc, R_CSR_SIAMODE0, 0); + if (mode == ETHER_LOOPBACK_EXT) { + /* deal with CSRs 13-15 */ + } + cfe_sleep(CFE_HZ/10); /* check this */ + + /* Update the SIA registers */ + v = READCSR(sc, R_CSR_SIAMODE0); + WRITECSR(sc, R_CSR_SIAMODE0, v &~ 0xFFFF); + v = READCSR(sc, R_CSR_SIAMODE1); + WRITECSR(sc, R_CSR_SIAMODE1, v &~ 0xFFFF); + v = READCSR(sc, R_CSR_SIAMODE2); + WRITECSR(sc, R_CSR_SIAMODE2, v | 0xC000); /* WC of HCKR, RMP */ + WRITECSR(sc, R_CSR_SIAMODE2, (v &~ 0xFFFF) | M_CSR15_GP_AUIBNC); + + WRITECSR(sc, R_CSR_SIAMODE0, M_CSR13_CONN_NOT_RESET); +} + +static void +dc21143_hwinit(tulip_softc *sc) +{ + uint32_t v; + uint32_t csr6word, csr14word; + + /* CSR0 - bus mode */ + WRITECSR(sc, R_CSR_SIAMODE2, M_CSR15_CONFIG_GEPS_LEDS); +#if TULIP_TUNE + v = V_CSR0_SKIPLEN(0) | + V_CSR0_CACHEALIGN(K_CSR0_ALIGN32) | + M_CSR0_READMULTENAB | M_CSR0_READLINEENAB | + M_CSR0_WRITEINVALENAB | + V_CSR0_BURSTLEN(K_CSR0_BURST32); +#else + v = V_CSR0_SKIPLEN(0) | + V_CSR0_CACHEALIGN(K_CSR0_ALIGN128) | + V_CSR0_BURSTLEN(K_CSR0_BURST32); +#endif +#ifdef __MIPSEB + v |= M_CSR0_BIGENDIAN; /* big-endian data serialization */ +#endif + WRITECSR(sc, R_CSR_BUSMODE, v); + + /* CSR6 - operation mode */ + v = M_CSR6_PORTSEL | +#if TULIP_TUNE + V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72) | +#else + M_CSR6_STOREFWD | +#endif + M_CSR6_MBO | + M_CSR6_PCSFUNC | + M_CSR6_SCRAMMODE; + WRITECSR(sc, R_CSR_OPMODE, v); + + /* About to muck the SIA, reset it.(?) */ + /* WRITECSR(sc, R_CSR_SIASTATUS, 1); */ + + /* Must shut off all transmit/receive in order to attempt to + achieve Full Duplex */ + csr6word = READCSR(sc, R_CSR_OPMODE); + WRITECSR(sc, R_CSR_OPMODE, csr6word &~ (M_CSR6_TXSTART | M_CSR6_RXSTART)); + csr6word = READCSR(sc, R_CSR_OPMODE); + + WRITECSR(sc, R_CSR_RXRING, PCIADDRX(sc, PHYSADDR(sc, sc->rxdscr_start))); + WRITECSR(sc, R_CSR_TXRING, PCIADDRX(sc, PHYSADDR(sc, sc->txdscr_start))); + + if (sc->linkspeed == ETHER_SPEED_AUTO) { + dc21143_autonegotiate(sc); + } + else { + /* disable autonegotiate so we can set full duplex to on */ + WRITECSR(sc, R_CSR_SIAMODE0, 0); + csr14word = READCSR(sc, R_CSR_SIAMODE1); + csr14word &=~ M_CSR14_AUTONEGOTIATE; + WRITECSR(sc, R_CSR_SIAMODE1, csr14word); + WRITECSR(sc, R_CSR_SIAMODE0, M_CSR13_CONN_NOT_RESET); + + dc21143_set_speed(sc, sc->linkspeed); + } +} + + +static void +dc21140_set_speed(tulip_softc *sc, int speed, int autoneg) +{ + uint16_t control; + uint16_t pcr; + uint32_t opmode = 0; + + pcr = mii_read_register(sc, 0x17); + pcr |= (0x400|0x100|0x40|0x20); + mii_write_register(sc, 0x17, pcr); + + control = mii_read_register(sc, MII_BMCR); + + if (!autoneg) { + control &=~ (BMCR_ANENABLE | BMCR_RESTARTAN); + mii_write_register(sc, MII_BMCR, control); + control &=~ (BMCR_SPEED0 | BMCR_SPEED1 | BMCR_DUPLEX); + } + + switch (speed) { + case ETHER_SPEED_10HDX: + default: + opmode = M_CSR6_SPEED_10_MII; + break; + case ETHER_SPEED_10FDX: + control |= BMCR_DUPLEX; + opmode = M_CSR6_SPEED_10_MII | M_CSR6_FULLDUPLEX; + break; + case ETHER_SPEED_100HDX: + control |= BMCR_SPEED100; + opmode = M_CSR6_SPEED_100_MII; + break; + case ETHER_SPEED_100FDX: + control |= BMCR_SPEED100 | BMCR_DUPLEX ; + opmode = M_CSR6_SPEED_100_MII | M_CSR6_FULLDUPLEX; + break; + } + + if (!autoneg) + mii_write_register(sc, MII_BMCR, control); + + opmode |= M_CSR6_MBO; +#if TULIP_TUNE + opmode |= V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72); +#else + opmode |= M_CSR6_STOREFWD; +#endif + WRITECSR(sc, R_CSR_OPMODE, opmode); +#if 0 + xprintf("setspeed PHY\n"); mii_dump(sc); +#endif +} + +static void +dc21140_autonegotiate(tulip_softc *sc) +{ + uint16_t control, status, cap; + unsigned int timeout; + int linkspeed; + int autoneg; + + linkspeed = ETHER_SPEED_UNKNOWN; + + /* Read twice to clear latching bits */ + status = mii_read_register(sc, MII_BMSR); + status = mii_read_register(sc, MII_BMSR); +#if 0 + xprintf("query PHY\n"); mii_dump(sc); +#endif + + if ((status & (BMSR_AUTONEG | BMSR_LINKSTAT)) == + (BMSR_AUTONEG | BMSR_LINKSTAT)) + control = mii_read_register(sc, MII_BMCR); + else { + /* reset the PHY */ + mii_write_register(sc, MII_BMCR, BMCR_RESET); + timeout = 3000; + for (;;) { + control = mii_read_register(sc, MII_BMCR); + if ((control && BMCR_RESET) == 0) break; + cfe_sleep(CFE_HZ/2); + timeout -= 500; + if (timeout <= 0) break; + } + if ((control & BMCR_RESET) != 0) { + xprintf("DC21140: PHY reset failed\n"); + return; + } + + status = mii_read_register(sc, MII_BMSR); + cap = ((status >> 6) & (ANAR_TXFD | ANAR_TXHD | ANAR_10FD | ANAR_10HD)) + | PSB_802_3; + mii_write_register(sc, MII_ANAR, cap); + control |= (BMCR_ANENABLE | BMCR_RESTARTAN); + mii_write_register(sc, MII_BMCR, control); + + timeout = 3000; + for (;;) { + status = mii_read_register(sc, MII_BMSR); + if ((status & BMSR_ANCOMPLETE) != 0) break; + cfe_sleep(CFE_HZ/2); + timeout -= 500; + if (timeout <= 0) break; + } +#if 0 + xprintf("done PHY\n"); mii_dump(sc); +#endif + } + + if ((status & BMSR_ANCOMPLETE) != 0) { + /* A link partner was negogiated... */ + + uint16_t remote = mii_read_register(sc, MII_ANLPAR); + + autoneg = 1; + xprintf("DC21140: Negotiated "); + if ((remote & ANLPAR_TXFD) != 0) { + xprintf("100Mb/s FDX"); + linkspeed = ETHER_SPEED_100FDX; + } + else if ((remote & ANLPAR_TXHD) != 0) { + xprintf("100Mb/s HDX"); + linkspeed = ETHER_SPEED_100HDX; + } + else if ((remote & ANLPAR_10FD) != 0) { + xprintf("10Mb/s FDX"); + linkspeed = ETHER_SPEED_10FDX; + } + else if ((remote & ANLPAR_10HD) != 0) { + xprintf("10Mb/s HDX"); + linkspeed = ETHER_SPEED_10HDX; + } + xprintf("\n"); + } + else { + /* no link partner negotiation */ + + autoneg = 0; + xprintf("DC21140: MII negotiation failed, assuming 10BT\n"); + control &=~ (BMCR_ANENABLE | BMCR_RESTARTAN); + mii_write_register(sc, MII_BMCR, control); + linkspeed = ETHER_SPEED_10HDX; + } + + if ((status & BMSR_LINKSTAT) == 0) + mii_write_register(sc, MII_BMCR, control); + dc21140_set_speed(sc, linkspeed, autoneg); + + status = mii_read_register(sc, MII_BMSR); /* clear latching bits */ +#if 0 + xprintf("final PHY\n"); mii_dump(sc); +#endif +} + +static void +dc21140_set_loopback(tulip_softc *sc, int mode) +{ + if (mode == ETHER_LOOPBACK_EXT) + xprintf("DC21140: external loopback mode NYI\n"); +} + +static void +dc21140_hwinit(tulip_softc *sc) +{ + uint32_t v; + uint32_t csr6word; + + /* The following assume GP port bits wired per the DEC reference design. + XXX Interpret the srom initialization strings instead */ + WRITECSR(sc, R_CSR_GENPORT, M_CSR12_CONTROL | 0x1F); + WRITECSR(sc, R_CSR_GENPORT, 0); /* release PHY reset */ + + /* Select MII interface */ + WRITECSR(sc, R_CSR_OPMODE, M_CSR6_PORTSEL); + RESET_ADAPTER(sc); + + mii_probe(sc); + + /* CSR0 - bus mode */ +#if TULIP_TUNE + v = V_CSR0_SKIPLEN(0) | + V_CSR0_CACHEALIGN(K_CSR0_ALIGN32) | + M_CSR0_READMULTENAB | M_CSR0_READLINEENAB | + M_CSR0_WRITEINVALENAB | + V_CSR0_BURSTLEN(K_CSR0_BURST32); +#else + v = V_CSR0_SKIPLEN(0) | + V_CSR0_CACHEALIGN(K_CSR0_ALIGN128) | + V_CSR0_BURSTLEN(K_CSR0_BURST32); +#endif +#ifdef __MIPSEB + v |= M_CSR0_BIGENDIAN; /* big-endian data serialization */ +#endif + WRITECSR(sc, R_CSR_BUSMODE, v); + + /* CSR6 - operation mode */ + v = M_CSR6_PORTSEL | +#if TULIP_TUNE + V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72) | +#else + M_CSR6_STOREFWD | +#endif + M_CSR6_MBO; + WRITECSR(sc, R_CSR_OPMODE, v); + + /* Must shut off all transmit/receive in order to attempt to + achieve Full Duplex */ + csr6word = READCSR(sc, R_CSR_OPMODE); + WRITECSR(sc, R_CSR_OPMODE, csr6word &~ (M_CSR6_TXSTART | M_CSR6_RXSTART)); + csr6word = READCSR(sc, R_CSR_OPMODE); + + WRITECSR(sc, R_CSR_RXRING, PCIADDRX(sc, PHYSADDR(sc, sc->rxdscr_start))); + WRITECSR(sc, R_CSR_TXRING, PCIADDRX(sc, PHYSADDR(sc, sc->txdscr_start))); + + if (sc->linkspeed == ETHER_SPEED_AUTO) { + dc21140_autonegotiate(sc); + } + else { + dc21140_set_speed(sc, sc->linkspeed, 0); + } +} + + +static void +dc21041_set_loopback(tulip_softc *sc, int mode) +{ + uint32_t v; + + WRITECSR(sc, R_CSR_SIAMODE0, 0); + if (mode == ETHER_LOOPBACK_EXT) { + /* deal with CSRs 13-15 */ + } + cfe_sleep(CFE_HZ/10); /* check this */ + + /* Update the SIA registers */ + v = READCSR(sc, R_CSR_SIAMODE0); + WRITECSR(sc, R_CSR_SIAMODE0, v &~ 0xFFFF); + WRITECSR(sc, R_CSR_SIAMODE1, 0x7A3F); + WRITECSR(sc, R_CSR_SIAMODE2, 0x0008); + + WRITECSR(sc, R_CSR_SIAMODE0, 0xEF01); +} + +static void +dc21041_hwinit(tulip_softc *sc) +{ + uint32_t v; + uint32_t opmode; + + /* CSR0 - bus mode */ + v = V_CSR0_SKIPLEN(0) | + V_CSR0_CACHEALIGN(K_CSR0_ALIGN128) | + V_CSR0_BURSTLEN(K_CSR0_BURST32); +#ifdef __MIPSEB + v |= M_CSR0_BIGENDIAN; /* big-endian data serialization */ +#endif + WRITECSR(sc, R_CSR_BUSMODE, v); + + /* set initial interrupt mask here? */ + + WRITECSR(sc, R_CSR_RXRING, PCIADDRX(sc, PHYSADDR(sc, sc->rxdscr_start))); + WRITECSR(sc, R_CSR_TXRING, PCIADDRX(sc, PHYSADDR(sc, sc->txdscr_start))); + + WRITECSR(sc, R_CSR_SIAMODE0, 0); + + /* For now, always force 10BT, HDX */ + WRITECSR(sc, R_CSR_SIAMODE1, 0x7F3F); + WRITECSR(sc, R_CSR_SIAMODE2, 0x0008); + opmode = M_CSR6_SPEED_10; + + WRITECSR(sc, R_CSR_SIAMODE0, 0xEF01); + cfe_sleep(CFE_HZ/10); + + opmode |= V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72); + WRITECSR(sc, R_CSR_OPMODE, opmode); +} + + +static void +tulip_hwinit(tulip_softc *sc) +{ + if (sc->state == eth_state_uninit) { + RESET_ADAPTER(sc); + sc->state = eth_state_off; + sc->bus_errors = 0; + + switch (sc->device) { + case K_PCI_ID_DC21041: + dc21041_hwinit(sc); + break; + case K_PCI_ID_DC21140: + dc21140_hwinit(sc); + break; + case K_PCI_ID_DC21143: + dc21143_hwinit(sc); + break; + default: + break; + } + } +} + + +static void +tulip_isr(tulip_softc *sc) +{ + uint32_t status; + uint32_t csr5; + + for (;;) { + + /* Read the interrupt status. */ + csr5 = READCSR(sc, R_CSR_STATUS); +// status &= sc->intmask; /* keep only the bits we like. */ + status = csr5 & ( + M_CSR5_RXINT | M_CSR5_RXBUFUNAVAIL | + M_CSR5_TXINT | M_CSR5_TXUNDERFLOW | + M_CSR5_FATALBUSERROR); + + /* if there are no more interrupts, leave now. */ + if (status == 0) break; + + /* Clear the pending interrupt. */ + WRITECSR(sc, R_CSR_STATUS, status); + + /* Now, test each unmasked bit in the interrupt register and + handle each interrupt type appropriately. */ + + if (status & M_CSR5_FATALBUSERROR) { + xprintf("DC21143: bus error %02x\n", G_CSR5_ERRORBITS(csr5)); + dumpstat(sc); + sc->bus_errors++; + if (sc->bus_errors >= 2) { + dumpcsrs(sc); + RESET_ADAPTER(sc); + sc->state = eth_state_off; + sc->bus_errors = 0; + } + } + + if (status & M_CSR5_RXINT) { + tulip_procrxring(sc); + } + + if (status & M_CSR5_TXINT) { + tulip_proctxring(sc); + } + + if (status & (M_CSR5_TXUNDERFLOW | M_CSR5_RXBUFUNAVAIL)) { + if (status & M_CSR5_TXUNDERFLOW) { + xprintf("DC21143: tx underrun, %08x\n", csr5); + /* Try to restart */ + WRITECSR(sc, R_CSR_TXPOLL, 1); + } + if (status & M_CSR5_RXBUFUNAVAIL) { + /* Try to restart */ + WRITECSR(sc, R_CSR_RXPOLL, 1); + } + } + } +} + + +static void +tulip_start(tulip_softc *sc) +{ + uint32_t opmode; + int idx; + tulip_cam *cam; + eth_pkt_t *pkt; + + tulip_hwinit(sc); + + WRITECSR(sc, R_CSR_RXRING, PCIADDRX(sc, PHYSADDR(sc, sc->rxdscr_start))); + WRITECSR(sc, R_CSR_TXRING, PCIADDRX(sc, PHYSADDR(sc, sc->txdscr_start))); + + opmode = READCSR(sc, R_CSR_OPMODE); + opmode &=~ M_CSR6_OPMODE; /* no loopback */ + opmode |= (M_CSR6_TXSTART | M_CSR6_RXSTART); + + sc->intmask = 0; + WRITECSR(sc, R_CSR_INTMASK, 0); /* no interrupts */ + WRITECSR(sc, R_CSR_OPMODE, opmode); + + pkt = eth_alloc_pkt(sc); + if (pkt) { + pkt->length = CAM_SETUP_BUFFER_SIZE; + cam = (tulip_cam *) pkt->buffer; + +#ifdef __MIPSEB + cam->p.physical[0][0] = (((uint32_t) sc->hwaddr[0] << 8) | + (uint32_t) sc->hwaddr[1]) << 16; + cam->p.physical[0][1] = (((uint32_t) sc->hwaddr[2] << 8) | + (uint32_t) sc->hwaddr[3]) << 16; + cam->p.physical[0][2] = (((uint32_t) sc->hwaddr[4] << 8) | + (uint32_t) sc->hwaddr[5]) << 16; + for (idx = 1; idx < CAM_PERFECT_ENTRIES; idx++) { + cam->p.physical[idx][0] = 0xFFFF0000; + cam->p.physical[idx][1] = 0xFFFF0000; + cam->p.physical[idx][2] = 0xFFFF0000; + } +#else + cam->p.physical[0][0] = ((uint32_t) sc->hwaddr[0]) | + (((uint32_t) sc->hwaddr[1]) << 8); + cam->p.physical[0][1] = ((uint32_t) sc->hwaddr[2]) | + (((uint32_t) sc->hwaddr[3]) << 8); + cam->p.physical[0][2] = ((uint32_t) sc->hwaddr[4]) | + (((uint32_t) sc->hwaddr[5]) << 8); + for (idx = 1; idx < CAM_PERFECT_ENTRIES; idx++) { + cam->p.physical[idx][0] = 0x0000FFFF; + cam->p.physical[idx][1] = 0x0000FFFF; + cam->p.physical[idx][2] = 0x0000FFFF; + } +#endif + + pkt->flags |= ETH_TX_SETUP; + if (tulip_transmit(sc, pkt) != 0) { + xprintf("DC21143: failed setup\n"); + dumpstat(sc); + eth_free_pkt(sc, pkt); + return; + } + sc->state = eth_state_setup; + } +} + +static void +tulip_stop(tulip_softc *sc) +{ + uint32_t opmode; + uint32_t status; + int count; + + WRITECSR(sc, R_CSR_INTMASK, 0); + sc->intmask = 0; + + opmode = READCSR(sc, R_CSR_OPMODE); + opmode &=~ (M_CSR6_TXSTART | M_CSR6_RXSTART); + WRITECSR(sc, R_CSR_OPMODE, opmode); + + /* wait for any DMA activity to terminate */ + for (count = 0; count <= 130; count++) { + status = READCSR(sc, R_CSR_STATUS); + if ((status & (M_CSR5_RXPROCSTATE | M_CSR5_TXPROCSTATE)) == 0) + break; + cfe_sleep(CFE_HZ/10); + } + if (count > 130) { + xprintf("DC21143: idle state not achieved\n"); + dumpstat(sc); + } +} + + +static void +tulip_start_loopback(tulip_softc *sc, int mode) +{ + uint32_t opmode; + + opmode = READCSR(sc, R_CSR_OPMODE); + + switch (sc->device) { + case K_PCI_ID_DC21041: + dc21041_set_loopback(sc, mode); + break; + case K_PCI_ID_DC21140: + dc21140_set_loopback(sc, mode); + break; + case K_PCI_ID_DC21143: + dc21143_set_loopback(sc, mode); + break; + default: + break; + } + cfe_sleep(CFE_HZ/10); + + WRITECSR(sc, R_CSR_RXRING, PCIADDRX(sc, PHYSADDR(sc, sc->rxdscr_start))); + WRITECSR(sc, R_CSR_TXRING, PCIADDRX(sc, PHYSADDR(sc, sc->txdscr_start))); + + sc->intmask = 0; /* no interrupts */ + WRITECSR(sc, R_CSR_INTMASK, 0); + + opmode &=~ (M_CSR6_OPMODE | M_CSR6_FULLDUPLEX); + opmode |= M_CSR6_PORTSEL; + if (mode == ETHER_LOOPBACK_EXT) + opmode |= M_CSR6_EXTLOOPBACK; + else + opmode |= M_CSR6_INTLOOPBACK; + opmode |= M_CSR6_TXSTART | M_CSR6_RXSTART; + WRITECSR(sc, R_CSR_OPMODE, opmode); +} + + +/* ********************************************************************* + * ETH_PARSE_XDIGIT(c) + * + * Parse a hex digit, returning its value + * + * Input parameters: + * c - character + * + * Return value: + * hex value, or -1 if invalid + ********************************************************************* */ +static int +eth_parse_xdigit(char c) +{ + int digit; + + if ((c >= '0') && (c <= '9')) digit = c - '0'; + else if ((c >= 'a') && (c <= 'f')) digit = c - 'a' + 10; + else if ((c >= 'A') && (c <= 'F')) digit = c - 'A' + 10; + else digit = -1; + + return digit; +} + +/* ********************************************************************* + * ETH_PARSE_HWADDR(str,hwaddr) + * + * Convert a string in the form xx:xx:xx:xx:xx:xx into a 6-byte + * Ethernet address. + * + * Input parameters: + * str - string + * hwaddr - pointer to hardware address + * + * Return value: + * 0 if ok, else -1 + ********************************************************************* */ +static int +eth_parse_hwaddr(char *str, uint8_t *hwaddr) +{ + int digit1, digit2; + int idx = ENET_ADDR_LEN; + + while (*str && (idx > 0)) { + digit1 = eth_parse_xdigit(*str); + if (digit1 < 0) return -1; + str++; + if (!*str) return -1; + + if ((*str == ':') || (*str == '-')) { + digit2 = digit1; + digit1 = 0; + } + else { + digit2 = eth_parse_xdigit(*str); + if (digit2 < 0) return -1; + str++; + } + + *hwaddr++ = (digit1 << 4) | digit2; + idx--; + + if ((*str == ':') || (*str == '-')) + str++; + } + return 0; +} + +/* ********************************************************************* + * ETH_INCR_HWADDR(hwaddr,incr) + * + * Increment a 6-byte Ethernet hardware address, with carries + * + * Input parameters: + * hwaddr - pointer to hardware address + * incr - desired increment + * + * Return value: + * none + ********************************************************************* */ +static void +eth_incr_hwaddr(uint8_t *hwaddr, unsigned incr) +{ + int idx; + int carry; + + idx = 5; + carry = incr; + while (idx >= 0 && carry != 0) { + unsigned sum = hwaddr[idx] + carry; + + hwaddr[idx] = sum & 0xff; + carry = sum >> 8; + idx--; + } +} + + +/* ********************************************************************* + * Declarations for CFE Device Driver Interface routines + ********************************************************************* */ + +static int tulip_ether_open(cfe_devctx_t *ctx); +static int tulip_ether_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int tulip_ether_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); +static int tulip_ether_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int tulip_ether_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int tulip_ether_close(cfe_devctx_t *ctx); + +/* ********************************************************************* + * CFE Device Driver dispatch structure + ********************************************************************* */ + +const static cfe_devdisp_t tulip_ether_dispatch = { + tulip_ether_open, + tulip_ether_read, + tulip_ether_inpstat, + tulip_ether_write, + tulip_ether_ioctl, + tulip_ether_close, + NULL, + NULL +}; + +/* ********************************************************************* + * CFE Device Driver descriptor + ********************************************************************* */ + +const cfe_driver_t dc21143drv = { + "DC2114x Ethernet", + "eth", + CFE_DEV_NETWORK, + &tulip_ether_dispatch, + tulip_ether_probe +}; + + +static int +tulip_ether_attach(cfe_driver_t *drv, + pcitag_t tag, int index, uint8_t hwaddr[], int host) +{ + tulip_softc *softc; + uint32_t device; + uint32_t class; + uint32_t reg; + phys_addr_t pa; + char descr[100]; + uint8_t romaddr[ENET_ADDR_LEN]; +#if 0 /* temporary */ + int i; + uint8_t srom[SROM_SIZE]; +#endif + + device = pci_conf_read(tag, R_CFG_CFID); + + class = pci_conf_read(tag, R_CFG_CFRV); + + reg = pci_conf_read(tag, R_CFG_CPMS); + + reg = pci_conf_read(tag, R_CFG_CFDD); + pci_conf_write(tag, R_CFG_CFDD, 0); + reg = pci_conf_read(tag, R_CFG_CFDD); + +#if 1 + /* Use memory space for the CSRs */ + pci_map_mem(tag, R_CFG_CBMA, PCI_MATCH_BITS, &pa); +#else + /* Use i/o space for the CSRs */ + pci_map_io(tag, R_CFG_CBIO, PCI_MATCH_BITS, &pa); +#endif + + softc = (tulip_softc *) KMALLOC(sizeof(tulip_softc), 0); + if (softc == NULL) { + xprintf("DC2114x: No memory to complete probe\n"); + return 0; + } + + memset(softc, 0, sizeof(*softc)); + + softc->membase = (uint32_t)pa; + + /* If we are in Host mode, we can receive interrupts. Otherwise, + we can see the CSRs but our CPU will not get interrupts. */ + if (host) + softc->irq = pci_conf_read(tag, R_CFG_CFIT) & 0xFF; + else + softc->irq = 0xFF; + + softc->tag = tag; + softc->device = PCI_PRODUCT(device); + softc->revision = PCI_REVISION(class); +#if 1 + softc->linkspeed = ETHER_SPEED_AUTO; /* select autonegotiation */ +#else + softc->linkspeed = ETHER_SPEED_100HDX; /* 100 Mbps, full duplex */ +#endif + memcpy(softc->hwaddr, hwaddr, ENET_ADDR_LEN); + + tulip_init(softc); + + /* Prefer address in srom */ + if (srom_read_addr(softc, romaddr) == 0) + memcpy(softc->hwaddr, romaddr, ENET_ADDR_LEN); + + softc->state = eth_state_uninit; + + xsprintf(descr, "%s at 0x%X (%02x-%02x-%02x-%02x-%02x-%02x)", + drv->drv_description, softc->membase, + softc->hwaddr[0], softc->hwaddr[1], softc->hwaddr[2], + softc->hwaddr[3], softc->hwaddr[4], softc->hwaddr[5]); + cfe_attach(drv, softc, NULL, descr); + return 1; +} + + +/* ********************************************************************* + * TULIP_ETHER_PROBE(drv,probe_a,probe_b,probe_ptr) + * + * Probe and install drivers for all DC2114x Ethernet controllers. + * For each, creates a context structure and attaches to the + * specified MAC devices. + * + * Input parameters: + * drv - driver descriptor + * probe_a - not used + * probe_b - not used + * probe_ptr - string pointer to hardware address for the first + * MAC, in the form xx:xx:xx:xx:xx:xx + * + * Return value: + * nothing + ********************************************************************* */ +static void +tulip_ether_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + int index; + int n; + uint8_t hwaddr[ENET_ADDR_LEN]; + + if (probe_ptr) + eth_parse_hwaddr((char *) probe_ptr, hwaddr); + else { + /* use default address 40-00-00-10-11-11 */ + hwaddr[0] = 0x40; hwaddr[1] = 0x00; + hwaddr[2] = 0x00; hwaddr[3] = 0x10; + hwaddr[4] = 0x11; hwaddr[5] = 0x11; + } + + n = 0; + index = 0; + for (;;) { + pcitag_t tag; + pcireg_t device; + + if (pci_find_class(PCI_CLASS_NETWORK, index, &tag) != 0) + break; + + index++; + + device = pci_conf_read(tag, R_CFG_CFID); + if (PCI_VENDOR(device) != K_PCI_VENDOR_DEC) + continue; + switch (PCI_PRODUCT(device)) { + case K_PCI_ID_DC21041: + break; + case K_PCI_ID_DC21140: + break; + case K_PCI_ID_DC21143: + break; + default: + continue; + } + + tulip_ether_attach(drv, tag, index, hwaddr, 1); + + n++; + eth_incr_hwaddr(hwaddr, 1); + } +} + + +/* The functions below are called via the dispatch vector for the 2114x. */ + +/* ********************************************************************* + * TULIP_ETHER_OPEN(ctx) + * + * Open the Ethernet device. The MAC is reset, initialized, and + * prepared to receive and send packets. + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +tulip_ether_open(cfe_devctx_t *ctx) +{ + tulip_softc *softc = ctx->dev_softc; + + if (softc->state == eth_state_on) + tulip_stop(softc); + + tulip_start(softc); + softc->state = eth_state_on; + + return 0; +} + +/* ********************************************************************* + * TULIP_ETHER_READ(ctx,buffer) + * + * Read a packet from the Ethernet device. If no packets are + * available, the read will succeed but return 0 bytes. + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * buffer - pointer to buffer descriptor. + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +tulip_ether_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + tulip_softc *softc = ctx->dev_softc; + eth_pkt_t *pkt; + int blen; + + if (softc->state != eth_state_on) return -1; + + tulip_isr(softc); + + pkt = (eth_pkt_t *) q_deqnext(&(softc->rxqueue)); + + if (pkt == NULL) { + buffer->buf_retlen = 0; + return 0; + } + + blen = buffer->buf_length; + if (blen > pkt->length) blen = pkt->length; + + memcpy(buffer->buf_ptr, pkt->buffer, blen); + buffer->buf_retlen = blen; + + eth_free_pkt(softc,pkt); + tulip_fillrxring(softc); + tulip_isr(softc); + + return 0; +} + +/* ********************************************************************* + * TULIP_ETHER_INPSTAT(ctx,inpstat) + * + * Check for received packets on the Ethernet device + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * inpstat - pointer to input status structure + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +tulip_ether_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat) +{ + tulip_softc *softc = ctx->dev_softc; + + if (softc->state != eth_state_on) return -1; + + tulip_isr(softc); + + inpstat->inp_status = (q_isempty(&(softc->rxqueue))) ? 0 : 1; + + return 0; +} + +/* ********************************************************************* + * TULIP_ETHER_WRITE(ctx,buffer) + * + * Write a packet to the Ethernet device. + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * buffer - pointer to buffer descriptor. + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +tulip_ether_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + tulip_softc *softc = ctx->dev_softc; + eth_pkt_t *pkt; + int blen; + + if (softc->state != eth_state_on) return -1; + + pkt = eth_alloc_pkt(softc); + if (!pkt) return -1; + + blen = buffer->buf_length; + if (blen > pkt->length) blen = pkt->length; + + memcpy(pkt->buffer, buffer->buf_ptr, blen); + pkt->length = blen; + + tulip_isr(softc); + + if (tulip_transmit(softc, pkt) != 0) { + eth_free_pkt(softc,pkt); + return -1; + } + + tulip_isr(softc); + + return 0; +} + +/* ********************************************************************* + * TULIP_ETHER_IOCTL(ctx,buffer) + * + * Do device-specific I/O control operations for the device + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * buffer - pointer to buffer descriptor. + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +tulip_ether_ioctl(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + tulip_softc *softc = ctx->dev_softc; + int mode; + + switch ((int)buffer->buf_ioctlcmd) { + case IOCTL_ETHER_GETHWADDR: + memcpy(buffer->buf_ptr, softc->hwaddr, sizeof(softc->hwaddr)); + return 0; + + case IOCTL_ETHER_SETHWADDR: + return -1; /* need to do this carefully */ + + /* XXX IOCTLs to set speed, etc.? */ + + case IOCTL_ETHER_GETLOOPBACK: + *((int *) buffer) = 0; /* XXX place holder */ + return 0; + + case IOCTL_ETHER_SETLOOPBACK: + tulip_stop(softc); + tulip_resetrings(softc); + mode = *((int *) buffer->buf_ptr); + if (mode == ETHER_LOOPBACK_INT || mode == ETHER_LOOPBACK_EXT) { + tulip_start_loopback(softc, mode); + } + else if (mode == ETHER_LOOPBACK_OFF) { + tulip_start(softc); + } + softc->state = eth_state_on; + return 0; + + default: + return -1; + } +} + +/* ********************************************************************* + * TULIP_ETHER_CLOSE(ctx) + * + * Close the Ethernet device. + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +tulip_ether_close(cfe_devctx_t *ctx) +{ + tulip_softc *softc = ctx->dev_softc; + + tulip_stop(softc); + softc->state = eth_state_off; + + /* resynchronize descriptor rings */ + tulip_resetrings(softc); + + return 0; +} diff --git a/cfe/cfe/dev/dev_dp83815.c b/cfe/cfe/dev/dev_dp83815.c new file mode 100644 index 0000000..b9fb644 --- /dev/null +++ b/cfe/cfe/dev/dev_dp83815.c @@ -0,0 +1,2290 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * NS DP83815 Ethernet Driver File: dev_dp83815.c + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + +#include "sbmips.h" + +#ifndef _SB_MAKE64 +#define _SB_MAKE64(x) ((uint64_t)(x)) +#endif +#ifndef _SB_MAKEMASK1 +#define _SB_MAKEMASK1(n) (_SB_MAKE64(1) << _SB_MAKE64(n)) +#endif + +#include "lib_types.h" +#include "lib_hssubr.h" +#include "lib_malloc.h" +#include "lib_string.h" +#define blockcopy memcpy +#include "lib_printf.h" +#include "lib_queue.h" + +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_ioctl.h" +#include "cfe_timer.h" +#include "cfe_error.h" +#include "cfe_irq.h" + +#include "pcivar.h" +#include "pcireg.h" + +#include "dp83815.h" +#include "mii.h" + +/* This is a driver for the National Semiconductor DP83815 (MacPhyter) + 10/100 MAC with integrated PHY. + + The current version has been developed for the Netgear FA311 and + FA312 NICs. These include an EEPROM with automatically loaded + setup information that includes station address filtering. + Operation without such an EEPROM has not been tested. + + This SB1250 version takes advantage of DMA coherence and uses + "preserve bit lanes" addresses for all accesses that cross the + ZBbus-PCI bridge. */ + +#ifndef MACPHYTER_DEBUG +#define MACPHYTER_DEBUG 0 +#endif +#ifndef MACPHYTER_TEST +#define MACPHYTER_TEST 0 +#endif + +/* Set IPOLL to drive processing through the pseudo-interrupt + dispatcher. Set XPOLL to drive processing by an external polling + agent. Setting both is ok. */ + +#ifndef IPOLL +#define IPOLL 0 +#endif +#ifndef XPOLL +#define XPOLL 1 +#endif + +#define ENET_ADDR_LEN 6 /* size of an ethernet address */ +#define MIN_ETHER_PACK 64 /* min size of a packet */ +#define MAX_ETHER_PACK 1518 /* max size of a packet */ +#define CRC_SIZE 4 /* size of CRC field */ + +/* Packet buffers. For the DP83815, an rx packet must be aligned to a + 32-bit word boundary, and we would like it aligned to a cache line + boundary for performance. Also, the buffers "should" be allocated + in 32 byte multiples (5.3.2). */ + +#define ETH_PKTBUF_LEN (((MAX_ETHER_PACK+31)/32)*32) + +#if __long64 +typedef struct eth_pkt_s { + queue_t next; /* 16 */ + uint8_t *buffer; /* 8 */ + uint32_t flags; /* 4 */ + int32_t length; /* 4 */ + uint8_t data[ETH_PKTBUF_LEN]; +} eth_pkt_t; +#else +typedef struct eth_pkt_s { + queue_t next; /* 8 */ + uint8_t *buffer; /* 4 */ + uint32_t flags; /* 4 */ + int32_t length; /* 4 */ + uint32_t unused[3]; /* 12 */ + uint8_t data[ETH_PKTBUF_LEN]; +} eth_pkt_t; +#endif + +#define CACHE_ALIGN 32 +#define ETH_PKTBUF_LINES ((sizeof(eth_pkt_t) + (CACHE_ALIGN-1))/CACHE_ALIGN) +#define ETH_PKTBUF_SIZE (ETH_PKTBUF_LINES*CACHE_ALIGN) +#define ETH_PKTBUF_OFFSET (offsetof(eth_pkt_t, data)) + +#define ETH_PKT_BASE(data) ((eth_pkt_t *)((data) - ETH_PKTBUF_OFFSET)) + +/* packet flags */ +#define ETH_TX_SETUP 1 /* assumes Perfect Filtering format */ + +static void +show_packet(char c, eth_pkt_t *pkt) +{ + int i; + int n = (pkt->length < 32 ? pkt->length : 32); + + xprintf("%c[%4d]:", c, pkt->length); + for (i = 0; i < n; i++) { + if (i % 4 == 0) + xprintf(" "); + xprintf("%02x", pkt->buffer[i]); + } + xprintf("\n"); +} + + +/* Descriptor structures. NOTE: To avoid having descriptors straddle + cache lines, we append a pad word, ignored by DMA, to each. */ + +typedef struct rx_dscr { + pci_addr_t rxd_link; + uint32_t rxd_cmdsts; + pci_addr_t rxd_bufptr; + uint32_t rxd_pad; +} rx_dscr; + +typedef struct tx_dscr { + pci_addr_t txd_link; + uint32_t txd_cmdsts; + pci_addr_t txd_bufptr; + uint32_t txd_pad; +} tx_dscr; + + +/* Driver data structures */ + +typedef enum { + eth_state_uninit, + eth_state_off, + eth_state_on, + eth_state_broken +} eth_state_t; + +#define ETH_PKTPOOL_SIZE 32 + +typedef struct dp83815_softc { + uint32_t membase; + uint8_t irq; /* interrupt mapping (used if IPOLL) */ + pcitag_t tag; /* tag for configuration registers */ + + uint8_t hwaddr[ENET_ADDR_LEN]; + uint16_t device; /* chip device code */ + uint8_t revision; /* chip revision and step */ + + eth_state_t state; /* current state */ + uint32_t intmask; /* interrupt mask */ + + /* These fields are set before calling dp83815_hwinit */ + int linkspeed; /* encodings from cfe_ioctl */ + int loopback; + + /* Packet free list */ + queue_t freelist; + uint8_t *pktpool; + queue_t rxqueue; + + /* The descriptor tables */ + uint8_t *rxdscrmem; /* receive descriptors */ + uint8_t *txdscrmem; /* transmit descriptors */ + + /* These fields keep track of where we are in tx/rx processing */ + volatile rx_dscr *rxdscr_start; /* beginning of ring */ + volatile rx_dscr *rxdscr_end; /* end of ring */ + volatile rx_dscr *rxdscr_remove; /* next one we expect DMA to use */ + volatile rx_dscr *rxdscr_add; /* next place to put a buffer */ + int rxdscr_onring; + + volatile tx_dscr *txdscr_start; /* beginning of ring */ + volatile tx_dscr *txdscr_end; /* end of ring */ + volatile tx_dscr *txdscr_remove; /* next one we will use for tx */ + volatile tx_dscr *txdscr_add; /* next place to put a buffer */ + + cfe_devctx_t *devctx; + + /* These fields describe the PHY */ + int phy_addr; + int phy_check; + uint32_t phy_status; + + /* Statistics */ + uint32_t inpkts; + uint32_t outpkts; + uint32_t interrupts; + uint32_t rx_interrupts; + uint32_t tx_interrupts; + uint32_t bus_errors; +} dp83815_softc; + + +/* Entry to and exit from critical sections (currently relative to + interrupts only, not SMP) */ + +#if CFG_INTERRUPTS +#define CS_ENTER(sc) cfe_disable_irq(sc->irq) +#define CS_EXIT(sc) cfe_enable_irq(sc->irq) +#else +#define CS_ENTER(sc) ((void)0) +#define CS_EXIT(sc) ((void)0) +#endif + + +/* Driver parameterization */ + +#define MAXRXDSCR 32 +#define MAXTXDSCR 32 +#define MINRXRING 8 + + +/* Prototypes */ + +static void dp83815_ether_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + + +/* Address mapping macros */ + +/* Note that PTR_TO_PHYS only works with 32-bit addresses, but then + so does the dp83815. */ +#define PTR_TO_PHYS(x) (K0_TO_PHYS((uintptr_t)(x))) +#define PHYS_TO_PTR(a) ((uint8_t *)PHYS_TO_K0(a)) + +/* All mappings through the PCI host bridge use match bits mode. */ +#define PHYS_TO_PCI(a) ((uint32_t) (a) | 0x20000000) +#define PCI_TO_PHYS(a) ((uint32_t) (a) & 0x1FFFFFFF) + +#define PCI_TO_PTR(a) (PHYS_TO_PTR(PCI_TO_PHYS(a))) +#define PTR_TO_PCI(x) (PHYS_TO_PCI(PTR_TO_PHYS(x))) + +#if __long64 +#define READCSR(sc,csr) \ + (*((volatile uint32_t *) (PHYS_TO_XKSEG_UNCACHED((sc)->membase + (csr))))) + +#define WRITECSR(sc,csr,val) \ + (*((volatile uint32_t *) \ + (PHYS_TO_XKSEG_UNCACHED((sc)->membase + (csr)))) = (val)) + +#else +#define READCSR(sc,csr) \ + (hs_read32(PHYS_TO_XKSEG_UNCACHED((sc)->membase + (csr)))) + +#define WRITECSR(sc,csr,val) \ + (hs_write32(PHYS_TO_XKSEG_UNCACHED((sc)->membase + (csr)), (val))) + +#endif + +#define RESET_ADAPTER(sc) \ + { \ + /* XXX */ \ + } + + +/* Debugging */ + +static void +dumpstat(dp83815_softc *sc) +{ + xprintf("-- CR = %08X CFG = %08x\n", + READCSR(sc, R_CR), READCSR(sc, R_CFG)); +} + +static void +dumpcsrs(dp83815_softc *sc) +{ + int reg; + + xprintf("-------------\n"); + for (reg = 0; reg < R_MIBC; reg += 4) { + xprintf("CSR %02X = %08X\n", reg, READCSR(sc, reg)); + } + xprintf("-------------\n"); +} + + +/* Packet management */ + +/* ********************************************************************* + * ETH_ALLOC_PKT(sc) + * + * Allocate a packet from the free list. + * + * Input parameters: + * sc - eth structure + * + * Return value: + * pointer to packet structure, or NULL if none available + ********************************************************************* */ +static eth_pkt_t * +eth_alloc_pkt(dp83815_softc *sc) +{ + eth_pkt_t *pkt; + + CS_ENTER(sc); + pkt = (eth_pkt_t *) q_deqnext(&sc->freelist); + CS_EXIT(sc); + if (!pkt) return NULL; + + pkt->buffer = pkt->data; + pkt->length = ETH_PKTBUF_LEN; + pkt->flags = 0; + + return pkt; +} + + +/* ********************************************************************* + * ETH_FREE_PKT(sc,pkt) + * + * Return a packet to the free list + * + * Input parameters: + * sc - sbmac structure + * pkt - packet to return + * + * Return value: + * nothing + ********************************************************************* */ +static void +eth_free_pkt(dp83815_softc *sc, eth_pkt_t *pkt) +{ + CS_ENTER(sc); + q_enqueue(&sc->freelist, &pkt->next); + CS_EXIT(sc); +} + + +/* ********************************************************************* + * ETH_INITFREELIST(sc) + * + * Initialize the buffer free list for this mac. The memory + * allocated to the free list is carved up and placed on a linked + * list of buffers for use by the mac. + * + * Input parameters: + * sc - eth structure + * + * Return value: + * nothing + ********************************************************************* */ +static void +eth_initfreelist(dp83815_softc *sc) +{ + int idx; + uint8_t *ptr; + eth_pkt_t *pkt; + + q_init(&sc->freelist); + + ptr = sc->pktpool; + for (idx = 0; idx < ETH_PKTPOOL_SIZE; idx++) { + pkt = (eth_pkt_t *) ptr; + eth_free_pkt(sc, pkt); + ptr += ETH_PKTBUF_SIZE; + } +} + + +/* Utilities */ + +static const char * +dp83815_devname(dp83815_softc *sc) +{ + return (sc->devctx != NULL ? cfe_device_name(sc->devctx) : "eth?"); +} + + +/* Descriptor ring management */ + +static int +dp83815_add_rcvbuf(dp83815_softc *sc, eth_pkt_t *pkt) +{ + volatile rx_dscr *rxd; + volatile rx_dscr *nextrxd; + + rxd = sc->rxdscr_add; + + /* Figure out where the next descriptor will go */ + nextrxd = rxd+1; + if (nextrxd == sc->rxdscr_end) { + nextrxd = sc->rxdscr_start; + } + + /* + * If the next one is the same as our remove pointer, + * the ring is considered full. (it actually has room for + * one more, but we reserve the remove == add case for "empty") + */ + if (nextrxd == sc->rxdscr_remove) return -1; + + rxd->rxd_bufptr = PTR_TO_PCI(pkt->buffer); + rxd->rxd_cmdsts = M_DES1_INTR | V_DES1_SIZE(ETH_PKTBUF_LEN); + + /* success, advance the pointer */ + sc->rxdscr_add = nextrxd; + CS_ENTER(sc); + sc->rxdscr_onring++; + CS_EXIT(sc); + + return 0; +} + +static void +dp83815_fillrxring(dp83815_softc *sc) +{ + eth_pkt_t *pkt; + + while (1) { + CS_ENTER(sc); + if (sc->rxdscr_onring >= MINRXRING) { + CS_EXIT(sc); + break; + } + CS_EXIT(sc); + pkt = eth_alloc_pkt(sc); + if (pkt == NULL) { + /* could not allocate a buffer */ + break; + } + if (dp83815_add_rcvbuf(sc, pkt) != 0) { + /* could not add buffer to ring */ + eth_free_pkt(sc, pkt); + break; + } + } +} + + +/* ********************************************************************* + * DP83815_RX_CALLBACK(sc, pkt) + * + * Receive callback routine. This routine is invoked when a + * buffer queued for receives is filled. In this simple driver, + * all we do is add the packet to a per-MAC queue for later + * processing, and try to put a new packet in the place of the one + * that was removed from the queue. + * + * Input parameters: + * sc - interface + * ptk - packet context (eth_pkt structure) + * + * Return value: + * nothing + ********************************************************************* */ +static void +dp83815_rx_callback(dp83815_softc *sc, eth_pkt_t *pkt) +{ + if (MACPHYTER_DEBUG) show_packet('>', pkt); /* debug */ + + CS_ENTER(sc); + q_enqueue(&sc->rxqueue, &pkt->next); + CS_EXIT(sc); + sc->inpkts++; + + dp83815_fillrxring(sc); +} + + +static void +dp83815_procrxring(dp83815_softc *sc) +{ + volatile rx_dscr *rxd; + eth_pkt_t *pkt; + eth_pkt_t *newpkt; + uint32_t cmdsts; + + for (;;) { + rxd = (volatile rx_dscr *) sc->rxdscr_remove; + + cmdsts = rxd->rxd_cmdsts; + if ((cmdsts & M_DES1_OWN) == 0) { + /* end of ring, no more packets */ + break; + } + + pkt = ETH_PKT_BASE(PCI_TO_PTR(rxd->rxd_bufptr)); + pkt->length = G_DES1_SIZE(cmdsts) - CRC_SIZE; + + /* Drop error packets */ + if (cmdsts & M_DES1_RX_ERRORS) { +#if MACPHYTER_DEBUG + if (pkt->length >= MIN_ETHER_PACK - CRC_SIZE) + xprintf("%s: rx error %08X\n", dp83815_devname(sc), cmdsts); +#endif + dp83815_add_rcvbuf(sc, pkt); + goto next; + } + + /* Pass up the packet */ + dp83815_rx_callback(sc, pkt); + + /* put a buffer back on the ring to replace this one */ + newpkt = eth_alloc_pkt(sc); + if (newpkt) dp83815_add_rcvbuf(sc, newpkt); + +next: + /* update the pointer, accounting for buffer wrap. */ + rxd++; + if (rxd == sc->rxdscr_end) + rxd = sc->rxdscr_start; + + sc->rxdscr_remove = (rx_dscr *) rxd; + CS_ENTER(sc); + sc->rxdscr_onring--; + CS_EXIT(sc); + } +} + + +static int +dp83815_add_txbuf(dp83815_softc *sc, eth_pkt_t *pkt) +{ + volatile tx_dscr *txd; + volatile tx_dscr *nexttxd; + + txd = sc->txdscr_add; + + /* Figure out where the next descriptor will go */ + nexttxd = (txd+1); + if (nexttxd == sc->txdscr_end) { + nexttxd = sc->txdscr_start; + } + + /* If the next one is the same as our remove pointer, + the ring is considered full. (it actually has room for + one more, but we reserve the remove == add case for "empty") */ + + if (nexttxd == sc->txdscr_remove) return -1; + + txd->txd_bufptr = PTR_TO_PCI(pkt->buffer); + txd->txd_cmdsts = M_DES1_INTR | M_DES1_OWN | V_DES1_SIZE(pkt->length); + + /* success, advance the pointer */ + sc->txdscr_add = nexttxd; + + return 0; +} + + +static int +dp83815_transmit(dp83815_softc *sc,eth_pkt_t *pkt) +{ + int rv; + + if (MACPHYTER_DEBUG) show_packet('<', pkt); /* debug */ + + rv = dp83815_add_txbuf(sc, pkt); + sc->outpkts++; + + WRITECSR(sc, R_CR, M_CR_TXE | M_CR_RXE); + return rv; +} + + +static void +dp83815_proctxring(dp83815_softc *sc) +{ + volatile tx_dscr *txd; + eth_pkt_t *pkt; + uint32_t cmdsts; + + for (;;) { + txd = (volatile tx_dscr *) sc->txdscr_remove; + + if (txd == sc->txdscr_add) { + /* ring is empty, no buffers to process */ + break; + } + + cmdsts = txd->txd_cmdsts; + if (cmdsts & M_DES1_OWN) { + /* Reached a packet still being transmitted */ + break; + } + + /* Just free the packet */ + pkt = ETH_PKT_BASE(PCI_TO_PTR(txd->txd_bufptr)); + eth_free_pkt(sc, pkt); + + /* update the pointer, accounting for buffer wrap. */ + txd++; + if (txd == sc->txdscr_end) + txd = sc->txdscr_start; + + sc->txdscr_remove = (tx_dscr *) txd; + } +} + + +static void +dp83815_initrings(dp83815_softc *sc) +{ + volatile tx_dscr *txd, *txn; + volatile rx_dscr *rxd, *rxn; + + /* Claim ownership of all descriptors for the driver */ + + for (txd = sc->txdscr_start; txd != sc->txdscr_end; txd++) { + txn = txd + 1; + if (txn == sc->txdscr_end) txn = sc->txdscr_start; + txd->txd_link = PTR_TO_PCI(txn); + txd->txd_cmdsts = 0; + txd->txd_pad = 0; + } + for (rxd = sc->rxdscr_start; rxd != sc->rxdscr_end; rxd++) { + rxn = rxd + 1; + if (rxn == sc->rxdscr_end) rxn = sc->rxdscr_start; + rxd->rxd_link = PTR_TO_PCI(rxn); + rxd->rxd_cmdsts = M_DES1_OWN; + rxd->rxd_pad = 0; + } + + /* Init the ring pointers */ + + sc->txdscr_add = sc->txdscr_remove = sc->txdscr_start; + sc->rxdscr_add = sc->rxdscr_remove = sc->rxdscr_start; + sc->rxdscr_onring = 0; + + /* Add stuff to the receive ring */ + + dp83815_fillrxring(sc); +} + + +static int +dp83815_init(dp83815_softc *sc) +{ + /* Allocate descriptor rings */ + sc->rxdscrmem = KMALLOC(MAXRXDSCR*sizeof(rx_dscr), sizeof(rx_dscr)); + sc->txdscrmem = KMALLOC(MAXTXDSCR*sizeof(tx_dscr), sizeof(tx_dscr)); + + /* Allocate buffer pool */ + sc->pktpool = KMALLOC(ETH_PKTPOOL_SIZE*ETH_PKTBUF_SIZE, CACHE_ALIGN); + eth_initfreelist(sc); + q_init(&sc->rxqueue); + + /* Fill in pointers to the rings */ + sc->rxdscr_start = (rx_dscr *) (sc->rxdscrmem); + sc->rxdscr_end = sc->rxdscr_start + MAXRXDSCR; + sc->rxdscr_add = sc->rxdscr_start; + sc->rxdscr_remove = sc->rxdscr_start; + sc->rxdscr_onring = 0; + + sc->txdscr_start = (tx_dscr *) (sc->txdscrmem); + sc->txdscr_end = sc->txdscr_start + MAXTXDSCR; + sc->txdscr_add = sc->txdscr_start; + sc->txdscr_remove = sc->txdscr_start; + + dp83815_initrings(sc); + + return 0; +} + + +static void +dp83815_resetrings(dp83815_softc *sc) +{ + volatile tx_dscr *txd; + volatile rx_dscr *rxd; + eth_pkt_t *pkt; + + /* Free already-sent descriptors and buffers */ + dp83815_proctxring(sc); + + /* Free any pending but unsent */ + txd = (volatile tx_dscr *) sc->txdscr_remove; + while (txd != sc->txdscr_add) { + txd->txd_cmdsts &=~ M_DES1_OWN; + pkt = ETH_PKT_BASE(PCI_TO_PTR(txd->txd_bufptr)); + eth_free_pkt(sc, pkt); + + txd++; + if (txd == sc->txdscr_end) + txd = sc->txdscr_start; + } + sc->txdscr_add = sc->txdscr_remove; + + /* Discard any received packets as well as all free buffers */ + rxd = (volatile rx_dscr *) sc->rxdscr_remove; + while (rxd != sc->rxdscr_add) { + rxd->rxd_cmdsts |= M_DES1_OWN; + pkt = ETH_PKT_BASE(PCI_TO_PTR(rxd->rxd_bufptr)); + eth_free_pkt(sc, pkt); + + rxd++; + if (rxd == sc->rxdscr_end) + rxd = sc->rxdscr_start; + CS_ENTER(sc); + sc->rxdscr_onring--; + CS_EXIT(sc); + } + + /* Reestablish the initial state. */ + dp83815_initrings(sc); +} + + +#if 0 /* XXX Multicast filtering not yet implemented. */ +/* CRCs */ + +#define IEEE_CRC32_POLY 0xEDB88320UL /* CRC-32 Poly -- either endian */ + +static uint32_t +dp83815_crc32(const uint8_t *databuf, unsigned int datalen) +{ + unsigned int idx, bit, data; + uint32_t crc; + + crc = 0xFFFFFFFFUL; + for (idx = 0; idx < datalen; idx++) + for (data = *databuf++, bit = 0; bit < 8; bit++, data >>= 1) + crc = (crc >> 1) ^ (((crc ^ data) & 1) ? IEEE_CRC32_POLY : 0); + return crc; +} + +#define dp83815_mchash(mca) (dp83815_crc32((mca), 6) & 0x1FF) +#endif + + +#if MACPHYTER_TEST +/* EEPROM access */ + +/* Current NICs use the EEPROM auto-load feature and there is no need + for explicit EEPROM access. The following routines are included + for future applications and have been tested (Netgear FA311). */ + +/**************************************************************************** + * dp83815_spin(sc, ns) + * + * Spin for a short interval (nominally in nanoseconds) + * + * Input Parameters: ns - minimum required nsec. + * + * The delay loop uses uncached PCI reads, each of which requires + * at least 3 PCI bus clocks (45 ns at 66 MHz) to complete. The + * actual delay will be longer (much longer if preempted). + ***************************************************************************/ + +#define PCI_MIN_DELAY 45 + +static void +dp83815_spin(dp83815_softc *sc, long nanoseconds) +{ + long delay; + volatile uint32_t t; + + for (delay = nanoseconds; delay > 0; delay -= PCI_MIN_DELAY) + t = READCSR(sc, R_SRR); +} + + +/* + * The recommended EEPROM is the NM9306. + * Delays below are chosen to meet specs for NS93C64 (slow M variant). + * Current parts are faster. + * Reference: NS Memory Data Book, 1994 + */ + +#define EEPROM_SIZE (2*0x0C) +#define EEPROM_MAX_CYCLES 32 + +#define EEPROM_CMD_BITS 3 +#define EEPROM_ADDR_BITS 6 + +#define K_EEPROM_READ_CMD 06 +#define K_EEPROM_WRITE_CMD 05 + +#define EEPROM_CRC_INDEX (EEPROM_SIZE-2) + +#define EEPROM_WORD(rom,offset) ((rom)[offset] | ((rom)[offset+1] << 8)) + +static void +eeprom_idle_state(dp83815_softc *sc) +{ + uint32_t ctrl; + unsigned int i; + + ctrl = READCSR(sc, R_MEAR); + + ctrl |= M_MEAR_EESEL; + WRITECSR(sc, R_MEAR, ctrl); + dp83815_spin(sc, 100); /* CS setup (Tcss=100) */ + + /* Run the clock through the maximum number of pending read cycles */ + for (i = 0; i < EEPROM_MAX_CYCLES*2; i++) { + ctrl ^= M_MEAR_EECLK; + WRITECSR(sc, R_MEAR, ctrl); + dp83815_spin(sc, 1000); /* SK period (Fsk=0.5MHz) */ + } + + /* Deassert EEPROM Chip Select */ + ctrl &=~ M_MEAR_EESEL; + WRITECSR(sc, R_MEAR, ctrl); + dp83815_spin(sc, 50); /* CS recovery (Tsks=50) */ +} + +static void +eeprom_send_command_bit(dp83815_softc *sc, unsigned int data) +{ + uint32_t ctrl; + + ctrl = READCSR(sc, R_MEAR); + + /* Place the data bit on the bus */ + if (data == 1) + ctrl |= M_MEAR_EEDI; + else + ctrl &=~ M_MEAR_EEDI; + + WRITECSR(sc, R_MEAR, ctrl); + dp83815_spin(sc, 360); /* setup: Tdis=200 */ + + /* Now clock the data into the EEPROM */ + WRITECSR(sc, R_MEAR, ctrl | M_MEAR_EECLK); + dp83815_spin(sc, 900); /* clock high, Tskh=500 */ + WRITECSR(sc, R_MEAR, ctrl); + dp83815_spin(sc, 450); /* clock low, Tskl=250 */ + + /* Now clear the data bit */ + ctrl &=~ M_MEAR_EEDI; /* data invalid, Tidh=20 for SK^ */ + WRITECSR(sc, R_MEAR, ctrl); + dp83815_spin(sc, 270); /* min cycle, 1/Fsk=2000 */ +} + +static uint16_t +eeprom_read_bit(dp83815_softc *sc) +{ + uint32_t ctrl; + + ctrl = READCSR(sc, R_MEAR); + + /* Generate a clock cycle before doing a read */ + WRITECSR(sc, R_MEAR, ctrl | M_MEAR_EECLK); /* rising edge */ + dp83815_spin(sc, 1000); /* clock high, Tskh=500, Tpd=1000 */ + WRITECSR(sc, R_MEAR, ctrl); /* falling edge */ + dp83815_spin(sc, 1000); /* clock low, 1/Fsk=2000 */ + + ctrl = READCSR(sc, R_MEAR); + return ((ctrl & M_MEAR_EEDO) != 0 ? 1 : 0); +} + +#define CMD_BIT_MASK (1 << (EEPROM_CMD_BITS+EEPROM_ADDR_BITS-1)) + +static uint16_t +eeprom_read_word(dp83815_softc *sc, unsigned int index) +{ + uint16_t command, word; + uint32_t ctrl; + unsigned int i; + + ctrl = READCSR(sc, R_MEAR) | M_MEAR_EESEL; + + /* Assert the EEPROM CS line */ + WRITECSR(sc, R_MEAR, ctrl); + dp83815_spin(sc, 100); /* CS setup, Tcss = 100 */ + + /* Send the read command to the EEPROM */ + command = (K_EEPROM_READ_CMD << EEPROM_ADDR_BITS) | index; + for (i = 0; i < EEPROM_CMD_BITS+EEPROM_ADDR_BITS; i++) { + eeprom_send_command_bit(sc, (command & CMD_BIT_MASK) != 0 ? 1 : 0); + command <<= 1; + } + + /* Now read the bits from the EEPROM (MSB first) */ + word = 0; + for (i = 0; i < 16; ++i) { + word <<= 1; + word |= eeprom_read_bit(sc); + } + + /* Clear the EEPROM CS Line, CS hold, Tcsh = 0 */ + WRITECSR(sc, R_MEAR, ctrl &~ M_MEAR_EESEL); + + return word; +} + + +/**************************************************************************** + * eeprom_checksum() + * + * Calculate the checksum of the EEPROM and return it. See Section + * 4.2.4 for the algorithm. + ***************************************************************************/ + +static uint16_t +eeprom_checksum(const uint8_t rom[]) +{ + uint16_t sum; + int i; + + sum = 0; + for (i = 0; i < EEPROM_SIZE-1; i++) + sum += rom[i]; + sum ^= 0xFF; + return (((sum + 1) & 0xFF) << 8) | 0x55; +} + + +/**************************************************************************** + * eeprom_read_all(sc, uint8_t dest) + * + * Read the entire EEPROM into the srom array + * + * Input parameters: + * sc - dp83815 state + ***************************************************************************/ + +static int +eeprom_read_all(dp83815_softc *sc, uint8_t dest[]) +{ + int i; + uint16_t cksum, temp; + + WRITECSR(sc, R_MEAR, M_MEAR_EESEL); + + eeprom_idle_state(sc); + + for (i = 0; i < EEPROM_SIZE/2; i++) { + temp = eeprom_read_word(sc, i); + dest[2*i] = temp & 0xFF; + dest[2*i+1] = temp >> 8; + } + + WRITECSR(sc, R_MEAR, 0); /* CS hold, Tcsh=0 */ + + cksum = eeprom_checksum(dest);; + if (cksum != EEPROM_WORD(dest, EEPROM_CRC_INDEX)) { + xprintf("%s: Invalid EEPROM CHECKSUM, calc %04x, stored %04x\n", + dp83815_devname(sc), + cksum, EEPROM_WORD(dest, EEPROM_CRC_INDEX)); + return 0/*-1*/; + } + return 0; +} + +static int +eeprom_read_addr(const uint8_t rom[], uint8_t buf[]) +{ + uint16_t s; + unsigned offset, mask; + int i, j; + + if (eeprom_checksum(rom) != EEPROM_WORD(rom, EEPROM_SIZE-2)) + return -1; + + s = 0; + offset = 2*6; mask = 0x1; + i = j = 0; + do { + s >>= 1; + if ((EEPROM_WORD(rom, offset) & mask) != 0) s |= 0x8000; + mask >>= 1; + if (mask == 0) { + offset +=2; mask = 0x8000; + } + i++; + if (i % 16 == 0) { + buf[j++] = s & 0xFF; + buf[j++] = s >> 8; + s = 0; + } + } while (i < ENET_ADDR_LEN*8); + + return 0; +} +#endif /* MACPHYTER_TEST */ + +#if 0 +static void +eeprom_dump(uint8_t srom[]) +{ + int i; + + xprintf("DP83815: EEPROM data:"); + for (i = 0; i < EEPROM_SIZE; i++) { + if (i % 16 == 0) + xprintf("\n %02x: ", i); + xprintf(" %02x", srom[i]); + } + xprintf("\n"); +} +#else +#define eeprom_dump(srom) +#endif + + +static int +dp83815_get_pm_addr(dp83815_softc *sc, uint8_t buf[]) +{ + uint32_t rfcr; + unsigned rfaddr; + unsigned i; + uint32_t rfdata; + + rfcr = READCSR(sc, R_RFCR); + rfaddr = K_RFCR_PMATCH_ADDR; + + for (i = 0; i < ENET_ADDR_LEN/2; i++) { + rfcr &=~ M_RFCR_RFADDR; + rfcr |= V_RFCR_RFADDR(rfaddr); + WRITECSR(sc, R_RFCR, rfcr); + rfdata = READCSR(sc, R_RFDR); + buf[2*i] = rfdata & 0xFF; + buf[2*i+1] = (rfdata >> 8) & 0xFF; + rfaddr += 2; + } + + return 0; +} + + +#if MACPHYTER_TEST +/* MII access */ + +/* Current NICs use the internal PHY, which can be accessed more + simply via internal registers. The following routines are + primarily for management access to an external PHY and are retained + for future applications. They have been tested on a Netgear FA311. */ + +/**************************************************************************** + * MII access utility routines + ***************************************************************************/ + +/* MII clock limited to 2.5 MHz (DP83815 allows 25 MHz), transactions + end with MDIO tristated */ + +static void +mii_write_bits(dp83815_softc *sc, uint32_t data, unsigned int count) +{ + uint32_t ctrl; + uint32_t bitmask; + + ctrl = READCSR(sc, R_MEAR) & ~M_MEAR_MDC; + ctrl |= M_MEAR_MDDIR; + + for (bitmask = 1 << (count-1); bitmask != 0; bitmask >>= 1) { + ctrl &=~ M_MEAR_MDIO; + if ((data & bitmask) != 0) ctrl |= M_MEAR_MDIO; + WRITECSR(sc, R_MEAR, ctrl); + + dp83815_spin(sc, 2000); /* setup */ + WRITECSR(sc, R_MEAR, ctrl | M_MEAR_MDC); + dp83815_spin(sc, 2000); /* hold */ + WRITECSR(sc, R_MEAR, ctrl); + } +} + +static void +mii_turnaround(dp83815_softc *sc) +{ + uint32_t ctrl; + + ctrl = READCSR(sc, R_MEAR) &~ M_MEAR_MDDIR; + + /* stop driving data */ + WRITECSR(sc, R_MEAR, ctrl); + dp83815_spin(sc, 2000); /* setup */ + WRITECSR(sc, R_MEAR, ctrl | M_MEAR_MDC); + dp83815_spin(sc, 2000); /* clock high */ + WRITECSR(sc, R_MEAR, ctrl); + + /* read back and check for 0 here? */ +} + +/**************************************************************************** + * mii_read_register + * + * This routine reads a register from the PHY chip using the MII + * serial management interface. + * + * Input parameters: + * index - index of register to read (0-31) + * + * Return value: + * word read from register + ***************************************************************************/ + +static uint16_t +mii_read_register(dp83815_softc *sc, unsigned int index) +{ + /* Send the command and address to the PHY. The sequence is + a synchronization sequence (32 1 bits) + a "start" command (2 bits) + a "read" command (2 bits) + the PHY addr (5 bits) + the register index (5 bits) + */ + uint32_t ctrl; + uint16_t word; + int i; + + mii_write_bits(sc, 0xFF, 8); + mii_write_bits(sc, 0xFFFFFFFF, 32); + mii_write_bits(sc, MII_COMMAND_START, 2); + mii_write_bits(sc, MII_COMMAND_READ, 2); + mii_write_bits(sc, sc->phy_addr, 5); + mii_write_bits(sc, index, 5); + + mii_turnaround(sc); + + ctrl = READCSR(sc, R_MEAR) &~ (M_MEAR_MDC | M_MEAR_MDDIR); + word = 0; + + for (i = 0; i < 16; i++) { + WRITECSR(sc, R_MEAR, ctrl); + dp83815_spin(sc, 2000); /* clock width low */ + WRITECSR(sc, R_MEAR, ctrl | M_MEAR_MDC); + dp83815_spin(sc, 2000); /* clock width high */ + WRITECSR(sc, R_MEAR, ctrl); + dp83815_spin(sc, 1000); /* output delay */ + word <<= 1; + if ((READCSR(sc, R_MEAR) & M_MEAR_MDIO) != 0) + word |= 0x0001; + } + + return word; + + /* reset to output mode? */ +} + +/**************************************************************************** + * mii_write_register + * + * This routine writes a register in the PHY chip using the MII + * serial management interface. + * + * Input parameters: + * index - index of register to write (0-31) + * value - word to write + ***************************************************************************/ + +static void +mii_write_register(dp83815_softc *sc, unsigned int index, uint16_t value) +{ + mii_write_bits(sc, 0xFF, 8); + mii_write_bits(sc, 0xFFFFFFFF, 32); + mii_write_bits(sc, MII_COMMAND_START, 2); + mii_write_bits(sc, MII_COMMAND_WRITE, 2); + mii_write_bits(sc, sc->phy_addr, 5); + mii_write_bits(sc, index, 5); + mii_write_bits(sc, MII_COMMAND_ACK, 2); + mii_write_bits(sc, value, 16); + + /* reset to input mode? */ +} + + +static int +mii_probe(dp83815_softc *sc) +{ + int i; + uint16_t id1, id2; + + /* Empirically, bit-banged access will return register 0 of the + integrated PHY for all registers of all unpopulated PHY + addresses. */ + for (i = 0; i < 32; i++) { + sc->phy_addr = i; + id1 = mii_read_register(sc, MII_PHYIDR1); + id2 = mii_read_register(sc, MII_PHYIDR2); + if ((id1 != 0x0000 && id1 != 0xFFFF) || + (id2 != 0x0000 && id2 != 0xFFFF)) { + if (id1 != id2) return 0; + } + } + return -1; +} + +#if 0 +#define OUI_NAT_SEMI 0x080017 +#define IDR_NAT_SEMI 0x080017 /* NSC does not bit-reverse each byte */ +#define PART_83815 0x02 + +static void +mii_dump(dp83815_softc *sc, const char *label) +{ + int i; + uint16_t r; + uint32_t idr, part; + + xprintf("%s\n", label); + idr = 0; + for (i = 0; i <= 6; ++i) { + r = mii_read_register(sc, i); + xprintf("MII_REG%02x: %04x\n", i, r); + if (i == MII_PHYIDR1) { + idr |= r << 6; + } + else if (i == MII_PHYIDR2) { + idr |= (r >> 10) & 0x3F; + part = (r >> 4) & 0x3F; + } + } + if (idr == IDR_NAT_SEMI && part == PART_83815) { + r = mii_read_register(sc, 7); + xprintf("MII_REG%02x: %04x\n", i, r); + for (i = 0x10; i <= 0x16; ++i) { + r = mii_read_register(sc, i); + xprintf("MII_REG%02x: %04x\n", i, r); + } + for (i = 0x19; i <= 0x1A; ++i) { + r = mii_read_register(sc, i); + xprintf("MII_REG%02x: %04x\n", i, r); + } + } +} +#else +#define mii_dump(sc,label) +#endif + + +/* The following functions are suitable for explicit MII access. */ + +static void +mii_set_speed(dp83815_softc *sc, int speed) +{ + uint16_t control; + + control = mii_read_register(sc, MII_BMCR); + + control &=~ (BMCR_ANENABLE | BMCR_RESTARTAN); + mii_write_register(sc, MII_BMCR, control); + control &=~ (BMCR_SPEED0 | BMCR_SPEED1 | BMCR_DUPLEX); + + switch (speed) { + case ETHER_SPEED_10HDX: + default: + break; + case ETHER_SPEED_10FDX: + control |= BMCR_DUPLEX; + break; + case ETHER_SPEED_100HDX: + control |= BMCR_SPEED100; + break; + case ETHER_SPEED_100FDX: + control |= BMCR_SPEED100 | BMCR_DUPLEX ; + break; + } + + mii_write_register(sc, MII_BMCR, control); +} + +static void +mii_autonegotiate(dp83815_softc *sc) +{ + uint16_t control, status, cap; + int timeout; + int linkspeed; + + linkspeed = ETHER_SPEED_UNKNOWN; + + /* Read twice to clear latching bits */ + status = mii_read_register(sc, MII_BMSR); + status = mii_read_register(sc, MII_BMSR); + mii_dump(sc, "query PHY"); + + if ((status & (BMSR_AUTONEG | BMSR_LINKSTAT)) == + (BMSR_AUTONEG | BMSR_LINKSTAT)) + control = mii_read_register(sc, MII_BMCR); + else { + /* reset the PHY */ + mii_write_register(sc, MII_BMCR, BMCR_RESET); + timeout = 3*CFE_HZ; + for (;;) { + control = mii_read_register(sc, MII_BMCR); + if ((control && BMCR_RESET) == 0 || timeout <= 0) + break; + cfe_sleep(CFE_HZ/2); + timeout -= CFE_HZ/2; + } + if ((control & BMCR_RESET) != 0) { + xprintf("%s: PHY reset failed\n", dp83815_devname(sc)); + return; + } + + status = mii_read_register(sc, MII_BMSR); + cap = ((status >> 6) & (ANAR_TXFD | ANAR_TXHD | ANAR_10FD | ANAR_10HD)) + | PSB_802_3; + mii_write_register(sc, MII_ANAR, cap); + control |= (BMCR_ANENABLE | BMCR_RESTARTAN); + mii_write_register(sc, MII_BMCR, control); + + timeout = 3*CFE_HZ; + for (;;) { + status = mii_read_register(sc, MII_BMSR); + if ((status & BMSR_ANCOMPLETE) != 0 || timeout <= 0) + break; + cfe_sleep(CFE_HZ/2); + timeout -= CFE_HZ/2; + } + mii_dump(sc, "done PHY"); + } + + xprintf("%s: Link speed: ", dp83815_devname(sc)); + if ((status & BMSR_ANCOMPLETE) != 0) { + /* A link partner was negogiated... */ + + uint16_t remote = mii_read_register(sc, MII_ANLPAR); + + if ((remote & ANLPAR_TXFD) != 0) { + xprintf("100BaseT FDX\n"); + linkspeed = ETHER_SPEED_100FDX; + } + else if ((remote & ANLPAR_TXHD) != 0) { + xprintf("100BaseT HDX\n"); + linkspeed = ETHER_SPEED_100HDX; + } + else if ((remote & ANLPAR_10FD) != 0) { + xprintf("10BaseT FDX\n"); + linkspeed = ETHER_SPEED_10FDX; + } + else if ((remote & ANLPAR_10HD) != 0) { + xprintf("10BaseT HDX\n"); + linkspeed = ETHER_SPEED_10HDX; + } + } + else { + /* no link partner negotiation */ + control &=~ (BMCR_ANENABLE | BMCR_RESTARTAN); + mii_write_register(sc, MII_BMCR, control); + xprintf("10BaseT HDX (assumed)\n"); + linkspeed = ETHER_SPEED_10HDX; + if ((status & BMSR_LINKSTAT) == 0) + mii_write_register(sc, MII_BMCR, control); + mii_set_speed(sc, linkspeed); + } + + status = mii_read_register(sc, MII_BMSR); /* clear latching bits */ + mii_dump(sc, "final PHY"); +} +#endif /* MACPHYTER_TEST */ + + +static void +dp83815_phyupdate(dp83815_softc *sc, uint32_t status) +{ + xprintf("%s: Link speed: ", dp83815_devname(sc)); + if ((status & M_CFG_LNKSTS) != 0) { + switch (status & (M_CFG_SPEED100 | M_CFG_FDUP)) { + case (M_CFG_SPEED100 | M_CFG_FDUP): + sc->linkspeed = ETHER_SPEED_100FDX; + xprintf("100BaseT FDX\n"); + break; + case (M_CFG_SPEED100): + sc->linkspeed = ETHER_SPEED_100HDX; + xprintf("100BaseT HDX\n"); + break; + case (M_CFG_FDUP): + sc->linkspeed = ETHER_SPEED_10FDX; + xprintf("10BaseT FDX\n"); + break; + default: + sc->linkspeed = ETHER_SPEED_10HDX; + xprintf("10BaseT HDX\n"); + break; + } + if ((status & M_CFG_SPEED100) != 0) { + uint32_t t; + + /* This is a reputed fix that improves 100BT rx + performance on short cables with "a small number" + of DP83815 chips. It comes from Bruce at NatSemi + via the Soekris support web page (see appended + note). */ + + WRITECSR(sc, R_PGSEL, 0x0001); + (void)READCSR(sc, R_PGSEL); /* push */ + t = READCSR(sc, R_DSPCFG); + WRITECSR(sc, R_DSPCFG, (t & 0xFFF) | 0x1000); + cfe_sleep(1); + t = READCSR(sc, R_TSTDAT) & 0xFF; + if ((t & 0x0080) == 0 || ((t > 0x00D8) && (t <= 0x00FF))) { + WRITECSR(sc, R_TSTDAT, 0x00E8); + t = READCSR(sc, R_DSPCFG); + WRITECSR(sc, R_DSPCFG, t | 0x0020); + } + WRITECSR(sc, R_PGSEL, 0); + } + if ((status & M_CFG_FDUP) != (sc->phy_status & M_CFG_FDUP)) { + uint32_t txcfg; + + txcfg = READCSR(sc, R_TXCFG); + if (status & M_CFG_FDUP) + txcfg |= M_TXCFG_CSI; + else + txcfg &= ~M_TXCFG_CSI; + WRITECSR(sc, R_RXCFG, txcfg); + } + } + else { + xprintf("Unknown\n"); + } + + sc->phy_status = status; +} + +static void +dp83815_hwinit(dp83815_softc *sc) +{ + if (sc->state == eth_state_uninit) { + uint32_t cfg; + uint32_t txcfg, rxcfg; + uint32_t ready; + int timeout; + + /* RESET_ADAPTER(sc); */ + sc->state = eth_state_off; + sc->bus_errors = 0; + + cfg = READCSR(sc, R_CFG); + cfg |= M_CFG_BEM; /* We will use match bits */ + WRITECSR(sc, R_CFG, cfg); + + sc->phy_status = 0; + dp83815_phyupdate(sc, cfg & M_CFG_LNKSUMMARY); + + txcfg = READCSR(sc, R_TXCFG); + txcfg |= M_TXCFG_ATP; + /* XXX fix up FLTH, DRTH? */ + WRITECSR(sc, R_TXCFG, txcfg); + + rxcfg = READCSR(sc, R_RXCFG); + /* Set an aggressive rx drain threshhold of 16 (2*8) bytes */ + rxcfg &= ~M_RXCFG_DRTH; + rxcfg |= V_RXCFG_DRTH(2); + WRITECSR(sc, R_RXCFG, rxcfg); + +#if MACPHYTER_TEST + { + uint8_t srom[EEPROM_SIZE]; + uint8_t addr[ENET_ADDR_LEN]; + + eeprom_read_all(sc, srom); + eeprom_dump(srom); + xprintf(" checksum %04x\n", eeprom_checksum(srom)); + if (eeprom_read_addr(srom, addr) == 0) + xprintf(" addr: %02x-%02x-%02x-%02x-%02x-%02x\n", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + + mii_probe(sc); + xprintf("MII address %02x\n", sc->phy_addr); + mii_dump(sc, "DP83815 PHY:"); + (void)mii_autonegotiate; + } +#endif /* MACPHYTER_TEST */ + + /* XXX fix up rx filtering here. We are relying on the EEPROM. */ + + /* XXX This is inappropriate on a restart. */ + timeout = 2*CFE_HZ; + ready = 0; + for (;;) { + ready |= READCSR(sc, R_ISR); + if ((ready & (M_INT_TXRCMP | M_INT_RXRCMP)) + == (M_INT_TXRCMP | M_INT_RXRCMP) || timeout <= 0) + break; + cfe_sleep(CFE_HZ/10); + timeout -= CFE_HZ/10; + } + if ((ready & M_INT_TXRCMP) == 0) + xprintf("%s: tx reset failed\n", dp83815_devname(sc)); + if ((ready & M_INT_RXRCMP) == 0) + xprintf("%s: rx reset failed\n", dp83815_devname(sc)); + } +} + +static void +dp83815_setspeed(dp83815_softc *sc, int speed) +{ + /* XXX Not yet implemented - autonegotiation only. */ +} + +static void +dp83815_setloopback(dp83815_softc *sc, int mode) +{ + /* XXX Not yet implemented. */ +} + + +static void +dp83815_isr(void *arg) +{ + dp83815_softc *sc = (dp83815_softc *)arg; + uint32_t status; + uint32_t isr; + +#if IPOLL + sc->interrupts++; +#endif + + for (;;) { + + /* Read (and clear) the interrupt status. */ + isr = READCSR(sc, R_ISR); + status = isr & sc->intmask; + + /* if there are no more interrupts, leave now. */ + if (status == 0) break; + + /* Now, test each unmasked bit in the interrupt register and + handle each interrupt type appropriately. */ + + if (status & (M_INT_RTABT | M_INT_RMABT)) { + WRITECSR(sc, R_IER, 0); + + xprintf("%s: bus error\n", dp83815_devname(sc)); + dumpstat(sc); + sc->bus_errors++; + if (sc->bus_errors >= 2) { + dumpcsrs(sc); + RESET_ADAPTER(sc); + sc->state = eth_state_off; + sc->bus_errors = 0; + } +#if IPOLL + else + WRITECSR(sc, R_IMR, sc->intmask); +#endif + } + + if (status & M_INT_RXDESC) { +#if IPOLL + sc->rx_interrupts++; +#endif + dp83815_procrxring(sc); + } + + if (status & M_INT_TXDESC) { +#if IPOLL + sc->tx_interrupts++; +#endif + dp83815_proctxring(sc); + } + + if (status & (M_INT_TXURN | M_INT_RXORN)) { + if (status & M_INT_TXURN) { + xprintf("%s: tx underrun, %08x\n", dp83815_devname(sc), isr); + /* XXX Try to restart */ + } + if (status & M_INT_RXORN) { + xprintf("%s: tx overrun, %08x\n", dp83815_devname(sc), isr); + /* XXX Try to restart */ + } + } + + if (status & M_INT_PHY) { + sc->intmask &= ~ M_INT_PHY; + WRITECSR(sc, R_IMR, sc->intmask); + (void)READCSR(sc, R_MISR); /* Clear at PHY */ + sc->phy_check = 1; + } + + } +} + +static void +dp83815_checkphy(dp83815_softc *sc) +{ + uint32_t cfg; + uint32_t status; + + (void)READCSR(sc, R_MISR); /* Clear at PHY */ + cfg = READCSR(sc, R_CFG); + status = cfg & M_CFG_LNKSUMMARY; + if (status != sc->phy_status) { + /* XXX Can we really do the phy update with active rx and tx? */ + dp83815_phyupdate(sc, status); + } + + sc->intmask |= M_INT_PHY; + WRITECSR(sc, R_IMR, sc->intmask); +} + + +static void +dp83815_start(dp83815_softc *sc) +{ + dp83815_hwinit(sc); + + /* Set up loopback here */ + + sc->intmask = 0; + WRITECSR(sc, R_IER, 0); /* no interrupts */ + WRITECSR(sc, R_IMR, 0); + (void)READCSR(sc, R_ISR); /* clear any pending */ + + sc->phy_status = READCSR(sc, R_CFG) & M_CFG_LNKSUMMARY; + sc->phy_check = 0; + + sc->intmask = M_INT_RXDESC | M_INT_TXDESC; + sc->intmask |= M_INT_RTABT | M_INT_RMABT | M_INT_RXORN | M_INT_TXURN; + sc->intmask |= M_INT_PHY; + +#if IPOLL + cfe_request_irq(sc->irq, dp83815_isr, sc, CFE_IRQ_FLAGS_SHARED, 0); + WRITECSR(sc, R_IMR, sc->intmask); + WRITECSR(sc, R_IER, M_IER_IE); +#endif + + (void)READCSR(sc, R_MISR); /* clear any pending */ + WRITECSR(sc, R_MISR, MISR_MSKJAB | MISR_MSKRF | MISR_MSKFHF | MISR_MSKRHF); + WRITECSR(sc, R_MICR, MICR_INTEN); + + WRITECSR(sc, R_TXDP, PTR_TO_PCI(sc->txdscr_start)); + WRITECSR(sc, R_RXDP, PTR_TO_PCI(sc->rxdscr_start)); + + WRITECSR(sc, R_MIBC, M_MIBC_ACLR); /* zero hw MIB counters */ + + WRITECSR(sc, R_CR, M_CR_TXE | M_CR_RXE); + sc->state = eth_state_on; +} + +static void +dp83815_stop(dp83815_softc *sc) +{ + uint32_t status; + int count; + + /* Make sure that no further interrutps will be processed. */ + sc->intmask = 0; + WRITECSR(sc, R_IER, 0); + WRITECSR(sc, R_IMR, 0); + +#if IPOLL + (void)READCSR(sc, R_IER); /* Push */ + cfe_free_irq(sc->irq, 0); +#endif + + WRITECSR(sc, R_CR, M_CR_TXD | M_CR_RXD); + + /* wait for any DMA activity to terminate */ + for (count = 0; count <= 13; count++) { + status = READCSR(sc, R_CR); + if ((status & (M_CR_TXE | M_CR_RXE)) == 0) + break; + cfe_sleep(CFE_HZ/10); + } + if (count > 13) { + xprintf("%s: idle state not achieved\n", dp83815_devname(sc)); + dumpstat(sc); + RESET_ADAPTER(sc); + sc->state = eth_state_uninit; +#if 1 + sc->linkspeed = ETHER_SPEED_AUTO; +#endif + } +#if 0 /* XXX Not yet implemented. */ + else if (sc->loopback != ETHER_LOOPBACK_OFF) { + dp83815_setloopback(sc, ETHER_LOOPBACK_OFF); + } +#endif + + (void)READCSR(sc, R_ISR); /* Clear any stragglers. */ +} + + +/* ********************************************************************* + * ETH_PARSE_XDIGIT(c) + * + * Parse a hex digit, returning its value + * + * Input parameters: + * c - character + * + * Return value: + * hex value, or -1 if invalid + ********************************************************************* */ +static int +eth_parse_xdigit(char c) +{ + int digit; + + if ((c >= '0') && (c <= '9')) digit = c - '0'; + else if ((c >= 'a') && (c <= 'f')) digit = c - 'a' + 10; + else if ((c >= 'A') && (c <= 'F')) digit = c - 'A' + 10; + else digit = -1; + + return digit; +} + +/* ********************************************************************* + * ETH_PARSE_HWADDR(str,hwaddr) + * + * Convert a string in the form xx:xx:xx:xx:xx:xx into a 6-byte + * Ethernet address. + * + * Input parameters: + * str - string + * hwaddr - pointer to hardware address + * + * Return value: + * 0 if ok, else -1 + ********************************************************************* */ +static int +eth_parse_hwaddr(char *str, uint8_t *hwaddr) +{ + int digit1, digit2; + int idx = ENET_ADDR_LEN; + + while (*str && (idx > 0)) { + digit1 = eth_parse_xdigit(*str); + if (digit1 < 0) return -1; + str++; + if (!*str) return -1; + + if ((*str == ':') || (*str == '-')) { + digit2 = digit1; + digit1 = 0; + } + else { + digit2 = eth_parse_xdigit(*str); + if (digit2 < 0) return -1; + str++; + } + + *hwaddr++ = (digit1 << 4) | digit2; + idx--; + + if ((*str == ':') || (*str == '-')) + str++; + } + return 0; +} + +/* ********************************************************************* + * ETH_INCR_HWADDR(hwaddr,incr) + * + * Increment a 6-byte Ethernet hardware address, with carries + * + * Input parameters: + * hwaddr - pointer to hardware address + * incr - desired increment + * + * Return value: + * none + ********************************************************************* */ +static void +eth_incr_hwaddr(uint8_t *hwaddr, unsigned incr) +{ + int idx; + int carry; + + idx = 5; + carry = incr; + while (idx >= 0 && carry != 0) { + unsigned sum = hwaddr[idx] + carry; + + hwaddr[idx] = sum & 0xFF; + carry = sum >> 8; + idx--; + } +} + + +/* ********************************************************************* + * Declarations for CFE Device Driver Interface routines + ********************************************************************* */ + +static int dp83815_ether_open(cfe_devctx_t *ctx); +static int dp83815_ether_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int dp83815_ether_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); +static int dp83815_ether_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int dp83815_ether_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int dp83815_ether_close(cfe_devctx_t *ctx); +static void dp83815_ether_poll(cfe_devctx_t *ctx, int64_t ticks); +static void dp83815_ether_reset(void *softc); + +/* ********************************************************************* + * CFE Device Driver dispatch structure + ********************************************************************* */ + +const static cfe_devdisp_t dp83815_ether_dispatch = { + dp83815_ether_open, + dp83815_ether_read, + dp83815_ether_inpstat, + dp83815_ether_write, + dp83815_ether_ioctl, + dp83815_ether_close, + dp83815_ether_poll, + dp83815_ether_reset +}; + +/* ********************************************************************* + * CFE Device Driver descriptor + ********************************************************************* */ + +const cfe_driver_t dp83815drv = { + "DP83815 Ethernet", + "eth", + CFE_DEV_NETWORK, + &dp83815_ether_dispatch, + dp83815_ether_probe +}; + + +static int +dp83815_ether_attach(cfe_driver_t *drv, + pcitag_t tag, int index, uint8_t hwaddr[]) +{ + dp83815_softc *sc; + uint32_t device; + uint32_t class; + phys_addr_t pa; + uint8_t promaddr[ENET_ADDR_LEN]; + char descr[100]; + uint32_t srr; + + device = pci_conf_read(tag, R_CFGID); + class = pci_conf_read(tag, R_CFGRID); + +#if 1 + /* Use memory space for the CSRs */ + pci_map_mem(tag, R_CFGMA, PCI_MATCH_BITS, &pa); +#else + /* Use i/o space for the CSRs */ + pci_map_io(tag, R_CFGIOA, PCI_MATCH_BITS, &pa); +#endif + + sc = (dp83815_softc *) KMALLOC(sizeof(dp83815_softc), 0); + + if (sc == NULL) { + xprintf("DP83815: No memory to complete probe\n"); + return 0; + } + memset(sc, 0, sizeof(*sc)); + + sc->membase = (uint32_t)pa; + sc->irq = pci_conf_read(tag, R_CFGINT) & 0xFF; + sc->tag = tag; + sc->device = PCI_PRODUCT(device); + sc->revision = PCI_REVISION(class); + sc->devctx = NULL; + +#if 1 + sc->linkspeed = ETHER_SPEED_AUTO; /* select autonegotiation */ +#else + sc->linkspeed = ETHER_SPEED_100FDX; /* 100 Mbps, full duplex */ +#endif + sc->loopback = ETHER_LOOPBACK_OFF; + memcpy(sc->hwaddr, hwaddr, ENET_ADDR_LEN); + + srr = READCSR(sc, R_SRR); +#if 0 + /* The NS data sheet recommends the following for "optimal + performance" of CVNG parts. Tested on a sample of one CVNG + part on an NS "Macphyter Demo II" eval board, it seemed to + produce slightly less reliable initial behavior. */ + if (G_SRR_REV(srr) == K_REV_CVNG) { + /* Update PHY DSP registers per data sheet. */ + WRITECSR(sc, R_PGSEL, 0x0001); + (void)READCSR(sc, R_PGSEL); /* push */ + WRITECSR(sc, R_PMDCSR, 0x189C); + WRITECSR(sc, R_TSTDAT, 0x0000); + WRITECSR(sc, R_DSPCFG, 0x5040); + WRITECSR(sc, R_SDCFG, 0x008C); + } +#endif + + dp83815_init(sc); + + /* Prefer the address in EEPROM. This will be read into the + PMATCH register upon power up. Unfortunately, how to test for + completion of the auto-load (but see PTSCR_EELOAD_EN). */ + if (dp83815_get_pm_addr(sc, promaddr) == 0) { + memcpy(sc->hwaddr, promaddr, ENET_ADDR_LEN); + } + + sc->state = eth_state_uninit; + + xsprintf(descr, "%s at 0x%X (%02x-%02x-%02x-%02x-%02x-%02x)", + drv->drv_description, sc->membase, + sc->hwaddr[0], sc->hwaddr[1], sc->hwaddr[2], + sc->hwaddr[3], sc->hwaddr[4], sc->hwaddr[5]); + + cfe_attach(drv, sc, NULL, descr); + return 1; +} + + +/* ********************************************************************* + * DP83815_ETHER_PROBE(drv,probe_a,probe_b,probe_ptr) + * + * Probe and install drivers for all dp83815 Ethernet controllers. + * For each, create a context structure and attach to the + * specified network device. + * + * Input parameters: + * drv - driver descriptor + * probe_a - not used + * probe_b - not used + * probe_ptr - string pointer to hardware address for the first + * MAC, in the form xx:xx:xx:xx:xx:xx + * + * Return value: + * nothing + ********************************************************************* */ +static void +dp83815_ether_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + int n; + uint8_t hwaddr[ENET_ADDR_LEN]; + + if (probe_ptr) + eth_parse_hwaddr((char *) probe_ptr, hwaddr); + else { + /* use default address 02-00-00-10-0B-00 */ + hwaddr[0] = 0x02; hwaddr[1] = 0x00; hwaddr[2] = 0x00; + hwaddr[3] = 0x10; hwaddr[4] = 0x0B; hwaddr[5] = 0x00; + } + + n = 0; + for (;;) { + pcitag_t tag; + + if (pci_find_device(K_PCI_VENDOR_NSC, K_PCI_ID_DP83815, n, &tag) != 0) + break; + dp83815_ether_attach(drv, tag, n, hwaddr); + n++; + eth_incr_hwaddr(hwaddr, 1); + } +} + + +/* The functions below are called via the dispatch vector for the 83815. */ + +/* ********************************************************************* + * DP83815_ETHER_OPEN(ctx) + * + * Open the Ethernet device. The MAC is reset, initialized, and + * prepared to receive and send packets. + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +dp83815_ether_open(cfe_devctx_t *ctx) +{ + dp83815_softc *sc = ctx->dev_softc; + + if (sc->state == eth_state_on) + dp83815_stop(sc); + + sc->devctx = ctx; + + sc->inpkts = sc->outpkts = 0; + sc->interrupts = 0; + sc->rx_interrupts = sc->tx_interrupts = 0; + + dp83815_start(sc); + +#if XPOLL + dp83815_isr(sc); +#endif + + return 0; +} + +/* ********************************************************************* + * DP83815_ETHER_READ(ctx,buffer) + * + * Read a packet from the Ethernet device. If no packets are + * available, the read will succeed but return 0 bytes. + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * buffer - pointer to buffer descriptor. + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +dp83815_ether_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + dp83815_softc *sc = ctx->dev_softc; + eth_pkt_t *pkt; + int blen; + +#if XPOLL + dp83815_isr(sc); +#endif + + if (sc->state != eth_state_on) return -1; + + CS_ENTER(sc); + pkt = (eth_pkt_t *) q_deqnext(&(sc->rxqueue)); + CS_EXIT(sc); + + if (pkt == NULL) { + buffer->buf_retlen = 0; + return 0; + } + + blen = buffer->buf_length; + if (blen > pkt->length) blen = pkt->length; + + blockcopy(buffer->buf_ptr, pkt->buffer, blen); + buffer->buf_retlen = blen; + + eth_free_pkt(sc, pkt); + dp83815_fillrxring(sc); + +#if XPOLL + dp83815_isr(sc); +#endif + + return 0; +} + +/* ********************************************************************* + * DP83815_ETHER_INPSTAT(ctx,inpstat) + * + * Check for received packets on the Ethernet device + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * inpstat - pointer to input status structure + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +dp83815_ether_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat) +{ + dp83815_softc *sc = ctx->dev_softc; + +#if XPOLL + dp83815_isr(sc); +#endif + + if (sc->state != eth_state_on) return -1; + + /* We avoid an interlock here because the result is a hint and an + interrupt cannot turn a non-empty queue into an empty one. */ + inpstat->inp_status = (q_isempty(&(sc->rxqueue))) ? 0 : 1; + + return 0; +} + +/* ********************************************************************* + * DP83815_ETHER_WRITE(ctx,buffer) + * + * Write a packet to the Ethernet device. + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * buffer - pointer to buffer descriptor. + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +dp83815_ether_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + dp83815_softc *sc = ctx->dev_softc; + eth_pkt_t *pkt; + int blen; + +#if XPOLL + dp83815_isr(sc); +#endif + + if (sc->state != eth_state_on) return -1; + + pkt = eth_alloc_pkt(sc); + if (!pkt) return CFE_ERR_NOMEM; + + blen = buffer->buf_length; + if (blen > pkt->length) blen = pkt->length; + + blockcopy(pkt->buffer, buffer->buf_ptr, blen); + pkt->length = blen; + + if (dp83815_transmit(sc, pkt) != 0) { + eth_free_pkt(sc,pkt); + return CFE_ERR_IOERR; + } + +#if XPOLL + dp83815_isr(sc); +#endif + + return 0; +} + +/* ********************************************************************* + * DP83815_ETHER_IOCTL(ctx,buffer) + * + * Do device-specific I/O control operations for the device + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * buffer - pointer to buffer descriptor. + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +dp83815_ether_ioctl(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + dp83815_softc *sc = ctx->dev_softc; + int *argp; + int mode; + int speed; + + switch ((int)buffer->buf_ioctlcmd) { + case IOCTL_ETHER_GETHWADDR: + memcpy(buffer->buf_ptr, sc->hwaddr, sizeof(sc->hwaddr)); + return 0; + + case IOCTL_ETHER_SETHWADDR: + return -1; /* not supported */ + + case IOCTL_ETHER_GETSPEED: + argp = (int *) buffer->buf_ptr; + *argp = sc->linkspeed; + return 0; + + case IOCTL_ETHER_SETSPEED: + dp83815_stop(sc); + dp83815_resetrings(sc); + speed = *((int *) buffer->buf_ptr); + dp83815_setspeed(sc, speed); + dp83815_start(sc); + sc->state = eth_state_on; + return 0; + + case IOCTL_ETHER_GETLINK: + argp = (int *) buffer->buf_ptr; + *argp = sc->linkspeed; + return 0; + + case IOCTL_ETHER_GETLOOPBACK: + *((int *) buffer) = sc->loopback; + return 0; + + case IOCTL_ETHER_SETLOOPBACK: + dp83815_stop(sc); + dp83815_resetrings(sc); + mode = *((int *) buffer->buf_ptr); + sc->loopback = ETHER_LOOPBACK_OFF; /* default */ + if (mode == ETHER_LOOPBACK_INT || mode == ETHER_LOOPBACK_EXT) { + dp83815_setloopback(sc, mode); + } + dp83815_start(sc); + sc->state = eth_state_on; + return 0; + + default: + return -1; + } +} + +/* ********************************************************************* + * DP83815_ETHER_CLOSE(ctx) + * + * Close the Ethernet device. + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +dp83815_ether_close(cfe_devctx_t *ctx) +{ + dp83815_softc *sc = ctx->dev_softc; + + sc->state = eth_state_off; + dp83815_stop(sc); + + /* resynchronize descriptor rings */ + dp83815_resetrings(sc); + + xprintf("%s: %d sent, %d received, %d interrupts\n", + dp83815_devname(sc), sc->outpkts, sc->inpkts, sc->interrupts); + xprintf(" %d rx interrupts, %d tx interrupts\n", + sc->rx_interrupts, sc->tx_interrupts); + + sc->devctx = NULL; + return 0; +} + + +/* ********************************************************************* + * DP83815_ETHER_POLL(ctx,ticks) + * + * TBD + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * ticks- current time in ticks + * + * Return value: + * nothing + ********************************************************************* */ + +static void +dp83815_ether_poll(cfe_devctx_t *ctx, int64_t ticks) +{ + dp83815_softc *sc = ctx->dev_softc; + + if (sc->phy_check) { + sc->phy_check = 0; + dp83815_checkphy(sc); + } +} + + +/* ********************************************************************* + * DP83815_ETHER_RESET(softc) + * + * This routine is called when CFE is restarted after a + * program exits. We can clean up pending I/Os here. + * + * Input parameters: + * softc - pointer to dp83815_softc + * + * Return value: + * nothing + ********************************************************************* */ + +static void +dp83815_ether_reset(void *softc) +{ + dp83815_softc *sc = (dp83815_softc *)softc; + + /* Turn off the Ethernet interface. */ + + /* RESET_ADAPTER(sc); */ + + sc->state = eth_state_uninit; +} diff --git a/cfe/cfe/dev/dev_ds17887clock.c b/cfe/cfe/dev/dev_ds17887clock.c new file mode 100644 index 0000000..2ee82a3 --- /dev/null +++ b/cfe/cfe/dev/dev_ds17887clock.c @@ -0,0 +1,422 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * DS17887-3 RTC driver File: dev_sb1250_ds17887clock.c + * + * This module contains a CFE driver for a DS17887-3 generic bus + * real-time-clock. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + +#include "lib_types.h" +#include "lib_malloc.h" +#include "lib_printf.h" +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_ioctl.h" + +#include "lib_physio.h" + + +/* ********************************************************************* + * Constants + ********************************************************************* */ + +/* + * Register bits + */ + +#define DS17887REGA_UIP 0x80 /* Update-in-progress */ +#define DS17887REGA_DV2 0x40 /* Countdown chain */ +#define DS17887REGA_DV1 0x20 /* Oscillator enable */ +#define DS17887REGA_DV0 0x10 /* Bank Select */ +#define DS17887REGA_RS3 0x08 /* Rate-selection bits */ +#define DS17887REGA_RS2 0x04 +#define DS17887REGA_RS1 0x02 +#define DS17887REGA_RS0 0x01 + +#define DS17887REGB_SET 0x80 /* Set bit */ +#define DS17887REGB_PIE 0x40 /* Periodic Interrupt Enable */ +#define DS17887REGB_AIE 0x20 /* Alarm Interrupt Enable */ +#define DS17887REGB_UIE 0x10 /* Update-ended Interrupt Enable */ +#define DS17887REGB_SQWE 0x08 /* Square-wave Enable */ +#define DS17887REGB_DM 0x04 /* Data Mode (binary) */ +#define DS17887REGB_24 0x02 /* 24-hour mode control bit */ +#define DS17887REGB_DSE 0x01 /* Daylight Savings Enable */ + +#define DS17887REGC_IRQF 0x80 /* Interrupt request flag */ +#define DS17887REGC_PF 0x40 /* Periodic interrupt flag */ +#define DS17887REGC_AF 0x20 /* Alarm interrupt flag */ +#define DS17887REGC_UF 0x10 /* Update ended interrupt flag */ + +#define DS17887REGD_VRT 0x80 /* Valid RAM and time */ + +/* + * Register numbers + */ + +#define DS17887REG_SC 0x00 /* seconds */ +#define DS17887REG_SCA 0x01 /* seconds alarm */ +#define DS17887REG_MN 0x02 /* minutes */ +#define DS17887REG_MNA 0x03 /* minutes alarm */ +#define DS17887REG_HR 0x04 /* hours */ +#define DS17887REG_HRA 0x05 /* hours alarm */ +#define DS17887REG_DW 0x06 /* day of week */ +#define DS17887REG_DM 0x07 /* day of month */ +#define DS17887REG_MO 0x08 /* month */ +#define DS17887REG_YR 0x09 /* year */ +#define DS17887REG_A 0x0A /* register A */ +#define DS17887REG_B 0x0B /* register B */ +#define DS17887REG_C 0x0C /* register C */ +#define DS17887REG_D 0x0D /* register D */ + +#define DS17887REG_CE 0x48 /* century (bank 1 only) */ + +#define BCD(x) (((x) % 10) + (((x) / 10) << 4)) +#define SET_TIME 0x00 +#define SET_DATE 0x01 + +#define WRITECSR(p,v) phys_write8((p),(v)) +#define READCSR(p) phys_read8((p)) + +/* ********************************************************************* + * Forward declarations + ********************************************************************* */ + +static void ds17887_clock_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + +static int ds17887_clock_open(cfe_devctx_t *ctx); +static int ds17887_clock_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int ds17887_clock_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); +static int ds17887_clock_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int ds17887_clock_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int ds17887_clock_close(cfe_devctx_t *ctx); + + +/* ********************************************************************* + * Device dispatch + ********************************************************************* */ + +const static cfe_devdisp_t ds17887_clock_dispatch = { + ds17887_clock_open, + ds17887_clock_read, + ds17887_clock_inpstat, + ds17887_clock_write, + ds17887_clock_ioctl, + ds17887_clock_close, + NULL, + NULL +}; + +const cfe_driver_t ds17887_clock = { + "DS17887 RTC", + "clock", + CFE_DEV_CLOCK, + &ds17887_clock_dispatch, + ds17887_clock_probe +}; + + +/* ********************************************************************* + * Structures + ********************************************************************* */ +typedef struct ds17887_clock_s { + physaddr_t clock_base; +} ds17887_clock_t; + +/* ********************************************************************* + * ds17887_clock_probe(drv,a,b,ptr) + * + * Probe routine for this driver. This routine creates the + * local device context and attaches it to the driver list + * within CFE. + * + * Input parameters: + * drv - driver handle + * a,b - probe hints (longs) + * ptr - probe hint (pointer) + * + * Return value: + * nothing + ********************************************************************* */ + +static void ds17887_clock_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + ds17887_clock_t *softc; + char descr[80]; + + softc = (ds17887_clock_t *) KMALLOC(sizeof(ds17887_clock_t),0); + + /* + * Probe_a is the clock base address + * Probe_b is unused. + * Probe_ptr is unused. + */ + + softc->clock_base = probe_a; + + xsprintf(descr,"%s at 0x%X", + drv->drv_description,(uint32_t)probe_a); + cfe_attach(drv,softc,NULL,descr); + +} + +/* ********************************************************************* + * ds17887_clock_open(ctx) + * + * Open this device. For the DS17887, we do a quick test + * read to be sure the device is out there. + * + * Input parameters: + * ctx - device context (can obtain our softc here) + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int ds17887_clock_open(cfe_devctx_t *ctx) +{ + ds17887_clock_t *softc = ctx->dev_softc; + uint8_t byte; + physaddr_t clockbase; + + clockbase = softc->clock_base; + + /* Make sure battery is still good and RTC valid */ + if ( !(READCSR(clockbase+DS17887REG_D) & DS17887REGD_VRT) ) { + printf("Warning: Battery has failed. Clock setting is not accurate.\n"); + } + + /* Switch to bank 1. Mainly for century byte */ + byte = (uint8_t) (READCSR(clockbase+DS17887REG_A) & 0xFF); + WRITECSR(clockbase+DS17887REG_A,DS17887REGA_DV0 | DS17887REGA_DV1 | byte); + + /* Set data mode to BCD, 24-hour mode, and enable daylight savings */ + byte = (uint8_t) (READCSR(clockbase+DS17887REG_B) & 0xFF); + byte &= (~DS17887REGB_DM & ~DS17887REGB_AIE); + WRITECSR(clockbase+DS17887REG_B, DS17887REGB_24 | DS17887REGB_DSE | byte ); + + return 0; +} + +/* ********************************************************************* + * ds17887_clock_read(ctx,buffer) + * + * Read time/date from the RTC. Read a total of 8 bytes in this format: + * hour-minute-second-month-day-year1-year2 + * + * Input parameters: + * ctx - device context (can obtain our softc here) + * buffer - buffer descriptor (target buffer, length, offset) + * + * Return value: + * number of bytes read + * -1 if an error occured + ********************************************************************* */ + +static int ds17887_clock_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + + ds17887_clock_t *softc = ctx->dev_softc; + unsigned char *bptr; + physaddr_t clockbase; + + clockbase = softc->clock_base; + + bptr = buffer->buf_ptr; + + *bptr++ = READCSR(clockbase+DS17887REG_HR); + *bptr++ = READCSR(clockbase+DS17887REG_MN); + *bptr++ = READCSR(clockbase+DS17887REG_SC); + *bptr++ = READCSR(clockbase+DS17887REG_MO); + *bptr++ = READCSR(clockbase+DS17887REG_DM); + *bptr++ = READCSR(clockbase+DS17887REG_YR); + *bptr++ = READCSR(clockbase+DS17887REG_CE); + + buffer->buf_retlen = 8; + return 0; +} + +/* ********************************************************************* + * ds17887_clock_write(ctx,buffer) + * + * Write time/date to the RTC. Write in this format: + * hour-minute-second-month-day-year1-year2-(time/date flag) + * + * Input parameters: + * ctx - device context (can obtain our softc here) + * buffer - buffer descriptor (target buffer, length, offset) + * + * Return value: + * number of bytes written + * -1 if an error occured + ********************************************************************* */ + +static int ds17887_clock_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + ds17887_clock_t *softc = ctx->dev_softc; + uint8_t byte; + unsigned char *bptr; + uint8_t hr,min,sec; + uint8_t mo,day,yr,y2k; + uint8_t timeDateFlag; + physaddr_t clockbase; + + clockbase = softc->clock_base; + + bptr = buffer->buf_ptr; + + /* Set SET bit */ + byte = (uint8_t) (READCSR(clockbase+DS17887REG_B) & 0xFF); + WRITECSR(clockbase+DS17887REG_B,DS17887REGB_SET | byte); + + timeDateFlag = *(bptr + 7); + + /* write time or date */ + if(timeDateFlag == SET_TIME) { + + hr = (uint8_t) *bptr; + WRITECSR(clockbase+DS17887REG_HR,BCD(hr)); + + min = (uint8_t) *(bptr+1); + WRITECSR(clockbase+DS17887REG_MN,BCD(min)); + + sec = (uint8_t) *(bptr+2); + WRITECSR(clockbase+DS17887REG_SC,BCD(sec)); + + buffer->buf_retlen = 3; + } + else if(timeDateFlag == SET_DATE) { + + mo = (uint8_t) *(bptr+3); + WRITECSR(clockbase+DS17887REG_MO,BCD(mo)); + + day = (uint8_t) *(bptr+4); + WRITECSR(clockbase+DS17887REG_DM,BCD(day)); + + yr = (uint8_t) *(bptr+5); + WRITECSR(clockbase+DS17887REG_YR,BCD(yr)); + + y2k = (uint8_t) *(bptr+6); + WRITECSR(clockbase+DS17887REG_CE,y2k); + + buffer->buf_retlen = 4; + } + else { + return -1; + } + + /* clear SET bit */ + byte = (uint8_t) (READCSR(clockbase+DS17887REG_B) & 0xFF); + WRITECSR(clockbase+DS17887REG_B,~DS17887REGB_SET & byte); + + return 0; +} + +/* ********************************************************************* + * ds17887_clock_inpstat(ctx,inpstat) + * + * Test input (read) status for the device + * + * Input parameters: + * ctx - device context (can obtain our softc here) + * inpstat - input status descriptor to receive value + * + * Return value: + * 0 if ok + * -1 if an error occured + ********************************************************************* */ + +static int ds17887_clock_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat) +{ + inpstat->inp_status = 1; + + return 0; +} + +/* ********************************************************************* + * ds17887_clock_ioctl(ctx,buffer) + * + * Perform miscellaneous I/O control operations on the device. + * + * Input parameters: + * ctx - device context (can obtain our softc here) + * buffer - buffer descriptor (target buffer, length, offset) + * + * Return value: + * number of bytes read + * -1 if an error occured + ********************************************************************* */ + +static int ds17887_clock_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + return 0; +} + +/* ********************************************************************* + * ds17887_clock_close(ctx,buffer) + * + * Close the device. + * + * Input parameters: + * ctx - device context (can obtain our softc here) + * + * Return value: + * 0 if ok + * -1 if an error occured + ********************************************************************* */ + +static int ds17887_clock_close(cfe_devctx_t *ctx) +{ + return 0; +} + + + + + + + + diff --git a/cfe/cfe/dev/dev_flash.c b/cfe/cfe/dev/dev_flash.c new file mode 100644 index 0000000..98ad3e7 --- /dev/null +++ b/cfe/cfe/dev/dev_flash.c @@ -0,0 +1,1367 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * Flash device driver File: dev_flash.c + * + * This driver supports various types of flash + * parts. You can also put the environment storage in + * the flash - the top sector is reserved for that purpose. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "lib_types.h" +#include "lib_malloc.h" +#include "lib_printf.h" +#include "lib_string.h" +#include "addrspace.h" +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_ioctl.h" +#include "cfe_error.h" + +#include "dev_flash.h" + +/* ********************************************************************* + * Macros + ********************************************************************* */ + +#define FLASHCMD(sc,x,y) *((volatile unsigned char *)(sc->flashdrv_cmdaddr+ \ + ((x)<<(sc)->flashdrv_widemode))) = (y) +#define FLASHSTATUS(sc,x) *((volatile unsigned char *)(sc->flashdrv_cmdaddr+ \ + ((x)<<(sc)->flashdrv_widemode))) + +#define WRITEFLASH_K1(sc,x,y) *((volatile unsigned char *)(sc->flashdrv_cmdaddr+(x))) = (y) +#define READFLASH_K1(sc,x) *((volatile unsigned char *)(sc->flashdrv_cmdaddr+(x))) + +#define WRITEFLASH_K1W(sc,x,y) *((volatile unsigned short *)(sc->flashdrv_cmdaddr+(x))) = (y) +#define READFLASH_K1W(sc,x) *((volatile unsigned short *)(sc->flashdrv_cmdaddr+(x))) + + +#define GETCFIBYTE(softc,offset) READFLASH_K1(softc,((offset) << (softc->flashdrv_widemode))) + +/* ********************************************************************* + * Forward declarations + ********************************************************************* */ + + +static void flashdrv_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + + +static int flashdrv_open(cfe_devctx_t *ctx); +static int flashdrv_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int flashdrv_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); +static int flashdrv_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int flashdrv_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int flashdrv_close(cfe_devctx_t *ctx); + +/* ********************************************************************* + * Device dispatch + ********************************************************************* */ + +const static cfe_devdisp_t flashdrv_dispatch = { + flashdrv_open, + flashdrv_read, + flashdrv_inpstat, + flashdrv_write, + flashdrv_ioctl, + flashdrv_close, + NULL, + NULL +}; + +const cfe_driver_t flashdrv = { + "CFI flash", + "flash", + CFE_DEV_FLASH, + &flashdrv_dispatch, + flashdrv_probe +}; + + +/* ********************************************************************* + * Structures + ********************************************************************* */ + +typedef struct flash_cfidata_s { + unsigned int cfidata_cmdset; /* ID of primary command set */ + unsigned int cfidata_devif; /* device interface byte */ + unsigned int cfidata_size; /* probed device size */ +} flash_cfidata_t; + +typedef struct flashops_s flashops_t; + +typedef struct flashdrv_s { + flash_probe_t flashdrv_probe; /* data from probe */ + int flashdrv_devsize; /* size reported by driver */ + unsigned char *flashdrv_cmdaddr; /* virtual address (K1) */ + int flashdrv_widemode; /* 1=wide flash in byte mode, 0=narrow flash */ + int flashdrv_initialized; /* true if we've probed already */ + flash_info_t flashdrv_info; + int flashdrv_nvram_ok; /* true if we can use as NVRAM */ + int flashdrv_unlocked; /* true if we can r/w past devsize */ + nvram_info_t flashdrv_nvraminfo; + flashops_t *flashdrv_ops; + flash_cfidata_t flashdrv_cfidata; +} flashdrv_t; + +struct flashops_s { + int (*erasesector)(flashdrv_t *f,int offset); + int (*writeblk)(flashdrv_t *f,int offset,void *buf,int len); +}; + +/* ********************************************************************* + * Macros + ********************************************************************* */ + +#define FLASHOP_ERASE_SECTOR(softc,sect) (*((softc)->flashdrv_ops->erasesector))((softc),(sect)) +#define FLASHOP_WRITE_BLOCK(softc,off,buf,len) (*((softc)->flashdrv_ops->writeblk))((softc),(off),(buf),(len)) + +/* ********************************************************************* + * forward declarations + ********************************************************************* */ + + +static int flash_sector_query(flashdrv_t *softc,flash_sector_t *sector); + +static int amd_flash_write_block(flashdrv_t *softc,int offset,void *buf,int len); +static int amd_flash_erase_sector(flashdrv_t *softc,int offset); + +static int intel_flash_write_block(flashdrv_t *softc,int offset,void *buf,int len); +static int intel_flash_erase_sector(flashdrv_t *softc,int offset); + +static flashops_t amd_flashops = { + amd_flash_erase_sector, + amd_flash_write_block, +}; + +static flashops_t intel_flashops = { + intel_flash_erase_sector, + intel_flash_write_block, +}; + +#define FLASHOPS_DEFAULT amd_flashops + + + +/* ********************************************************************* + * Externs + ********************************************************************* */ + +extern void *flash_write_all_ptr; +extern int flash_write_all_len; + +extern void _cfe_flushcache(int); + + +#if 0 +/* ********************************************************************* + * jedec_flash_maufacturer(softc) + * + * Return the manufacturer ID for this flash part. + * + * Input parameters: + * softc - flash context + * + * Return value: + * nothing + ********************************************************************* */ + +static unsigned int jedec_flash_manufacturer(flashdrv_t *softc) +{ + unsigned int res; + + /* Do an "unlock write" sequence */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_MAGIC_1); + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_2,AMD_FLASH_MAGIC_2); + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_AUTOSEL); + + res = FLASHSTATUS(softc,FLASH_JEDEC_OFFSET_MFR) & 0xFF; + + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_RESET); + + return res; +} + +/* ********************************************************************* + * jedec_flash_type(softc) + * + * Return the manufacturer's type for the flash + * + * Input parameters: + * softc - flash context + * + * Return value: + * nothing + ********************************************************************* */ +static unsigned int jedec_flash_type(flashdrv_t *softc) +{ + unsigned int res; + + /* Do an "unlock write" sequence */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_MAGIC_1); + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_2,AMD_FLASH_MAGIC_2); + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_AUTOSEL); + + res = FLASHSTATUS(softc,FLASH_JEDEC_OFFSET_DEV) & 0xFF; + + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_RESET); + + return res; +} + +#endif + +/* ********************************************************************* + * amd_flash_write_byte(softc,offset,val) + * + * Write a single byte to the flash. The sector that the flash + * byte is in should have been previously erased, or else this + * routine may hang. + * + * Input parameters: + * softc - flash context + * offset - distance in bytes into the flash + * val - byte to write + * + * Return value: + * 0 if ok + * else if flash could not be written + ********************************************************************* */ +static inline int amd_flash_write_byte(flashdrv_t *softc,int offset, unsigned char val) +{ + unsigned int value; + + /* Do an "unlock write" sequence */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_MAGIC_1); + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_2,AMD_FLASH_MAGIC_2); + + /* Send a program command */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_PROGRAM); + + /* Write a byte */ + WRITEFLASH_K1(softc,offset,val); + + for (;;) { + value = READFLASH_K1(softc,offset) & 0xFF; + + if ((value & 0x80) == (val & 0x80)) { + return 0; + } + if ((value & 0x20) != 0x20) { + continue; + } + + if ((READFLASH_K1(softc,offset) & 0x80) == (val & 0x80)) { + return 0; + } + else { + return -1; + } + } +} + + +/* ********************************************************************* + * amd_flash_write_block(softc,offset,val) + * + * Write a single byte to the flash. The sector that the flash + * byte is in should have been previously erased, or else this + * routine may hang. + * + * Input parameters: + * softc - flash context + * offset - distance in bytes into the flash + * buf - buffer of bytes to write + * len - number of bytes to write + * + * Return value: + * number of bytes written + ********************************************************************* */ +static int amd_flash_write_block(flashdrv_t *softc,int offset,void *buf,int len) +{ + unsigned char *ptr; + + ptr = buf; + + while (len) { + if (amd_flash_write_byte(softc,offset,*ptr) < 0) break; + len--; + ptr++; + offset++; + + } + + return (ptr - (unsigned char *)buf); +} + + +/* ********************************************************************* + * amd_flash_erase_sector(softc,offset) + * + * Erase a single sector in the flash device + * + * Input parameters: + * softc - device context + * offset - offset in flash of sector to erase + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +static int amd_flash_erase_sector(flashdrv_t *softc,int offset) +{ + /* Do an "unlock write" sequence */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_MAGIC_1); /* cycles 1-2 */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_2,AMD_FLASH_MAGIC_2); + + /* send the erase command */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_ERASE_3); /* cycle 3 */ + + /* Do an "unlock write" sequence */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_MAGIC_1); /* cycles 4-5 */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_2,AMD_FLASH_MAGIC_2); + + /* + * Send the "erase sector" qualifier - don't use FLASHCMD + * because it changes the offset. + */ + WRITEFLASH_K1(softc,offset,AMD_FLASH_ERASE_SEC_6); + + while ((READFLASH_K1(softc,offset) & 0x80) != 0x80) { + /* NULL LOOP */ + } + + return 0; +} + + + +/* ********************************************************************* + * intel_flash_write_byte(softc,offset,val) + * + * Write a single byte to the flash. The sector that the flash + * byte is in should have been previously erased, or else this + * routine may hang. + * + * Input parameters: + * softc - flash context + * offset - distance in bytes into the flash + * val - byte to write + * + * Return value: + * 0 if ok + * else if flash could not be written + ********************************************************************* */ +static inline int intel_flash_write_byte(flashdrv_t *softc, + int offset, unsigned char val) +{ + unsigned int value; + + /* Send a program command */ + WRITEFLASH_K1(softc,offset,INTEL_FLASH_PROGRAM); + + /* Write a byte */ + WRITEFLASH_K1(softc,offset,val); + + + while ((READFLASH_K1(softc,offset) & 0x80) != 0x80) { + /* NULL LOOP */ + } + + value = READFLASH_K1(softc,offset) & 0xFF; + + if (value & (0x01|0x08|0x10)) return -1; + return 0; +} + +/* ********************************************************************* + * intel_flash_write_word(softc,offset,val) + * + * Write a single word to the flash. The sector that the flash + * byte is in should have been previously erased, or else this + * routine may hang. + * + * Input parameters: + * softc - flash context + * offset - distance in bytes into the flash + * val - word to write + * + * Return value: + * 0 if ok + * else if flash could not be written + ********************************************************************* */ +static inline int intel_flash_write_word(flashdrv_t *softc, + int offset, unsigned short val) +{ + unsigned int value; + + + /* Send a program command */ + WRITEFLASH_K1W(softc,offset,INTEL_FLASH_PROGRAM); + + /* Write a byte */ + WRITEFLASH_K1W(softc,offset,val); + + + while ((READFLASH_K1W(softc,offset) & 0x80) != 0x80) { + /* NULL LOOP */ + } + + value = READFLASH_K1W(softc,offset) & 0xFF; + + if (value & (0x01|0x08|0x10)) return -1; + return 0; +} + +/* ********************************************************************* + * intel_flash_write_block(softc,offset,val) + * + * Write a single byte to the flash. The sector that the flash + * byte is in should have been previously erased, or else this + * routine may hang. + * + * Input parameters: + * softc - flash context + * offset - distance in bytes into the flash + * buf - buffer of bytes to write + * len - number of bytes to write + * + * Return value: + * number of bytes written + ********************************************************************* */ +static int intel_flash_write_block(flashdrv_t *softc,int offset,void *buf,int len) +{ + unsigned char *ptr; + unsigned short *ptrw; + + if (softc->flashdrv_probe.flash_flags & FLASH_FLG_16BIT) { + ptrw = buf; + offset &= ~1; /* offset must be even */ + while (len > 0) { + if (intel_flash_write_word(softc,offset,*ptrw) < 0) break; + len-=2; + ptrw++; + offset+=2; + } + WRITEFLASH_K1(softc,offset,INTEL_FLASH_READ_MODE); + return ((unsigned char *) ptrw - (unsigned char *)buf); + } + else { + ptr = buf; + while (len) { + if (intel_flash_write_byte(softc,offset,*ptr) < 0) break; + len--; + ptr++; + offset++; + } + WRITEFLASH_K1(softc,offset,INTEL_FLASH_READ_MODE); + return (ptr - (unsigned char *)buf); + } + +} + + +/* ********************************************************************* + * intel_flash_erase_sector(softc,offset) + * + * Erase a single sector on the flash device + * + * Input parameters: + * softc - device context + * offset - offset in flash of sector to erase + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ +static int intel_flash_erase_sector(flashdrv_t *softc,int offset) +{ + WRITEFLASH_K1(softc,offset,INTEL_FLASH_ERASE_BLOCK); + WRITEFLASH_K1(softc,offset,INTEL_FLASH_ERASE_CONFIRM); + + while ((READFLASH_K1(softc,offset) & 0x80) != 0x80) { + /* NULL LOOP */ + } + WRITEFLASH_K1(softc,offset,INTEL_FLASH_READ_MODE); + + return 0; +} + + + + + +/* ********************************************************************* + * FLASH_ERASE_RANGE(softc,range) + * + * Erase a range of sectors + * + * Input parameters: + * softc - our flash + * range - range structure + * + * Return value: + * 0 if ok + * else error + ********************************************************************* */ + +static int flash_erase_range(flashdrv_t *softc,flash_range_t *range) +{ + flash_sector_t sector; + int res; + + if (softc->flashdrv_info.flash_type != FLASH_TYPE_FLASH) { + return CFE_ERR_UNSUPPORTED; + } + + + if (range->range_base+range->range_length > softc->flashdrv_devsize) { + return CFE_ERR_INV_PARAM; + } + + res = 0; + + sector.flash_sector_idx = 0; + + for (;;) { + res = flash_sector_query(softc,§or); + if (res != 0) break; + if (sector.flash_sector_status == FLASH_SECTOR_INVALID) { + break; + } + + if ((sector.flash_sector_offset >= range->range_base) && + (sector.flash_sector_offset < + (range->range_base+range->range_length-1))) { + + if (softc->flashdrv_nvram_ok && + (sector.flash_sector_offset >= softc->flashdrv_nvraminfo.nvram_offset)) { + break; + } + res = FLASHOP_ERASE_SECTOR(softc,sector.flash_sector_offset); + if (res != 0) break; + } + sector.flash_sector_idx++; + } + + return res; + +} + +/* ********************************************************************* + * FLASH_ERASE_ALL(softc) + * + * Erase the entire flash device, except the NVRAM area, + * sector-by-sector. + * + * Input parameters: + * softc - our flash + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int flash_erase_all(flashdrv_t *softc) +{ + flash_range_t range; + + range.range_base = 0; + range.range_length = softc->flashdrv_devsize; + + return flash_erase_range(softc,&range); +} + +/* ********************************************************************* + * FLASH_CFI_GETSECTORS(softc) + * + * Query the CFI information and store the sector info in our + * private probe structure. + * + * Input parameters: + * softc - our flash info + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int flash_cfi_getsectors(flashdrv_t *softc) +{ + int idx; + int regcnt; + int nblks; + int blksiz; + + regcnt = GETCFIBYTE(softc,FLASH_CFI_REGION_COUNT); + + softc->flashdrv_probe.flash_nsectors = regcnt; + + for (idx = 0; idx < regcnt; idx++) { + nblks = ((int)GETCFIBYTE(softc,FLASH_CFI_REGION_TABLE+0+idx*4) + + (int)(GETCFIBYTE(softc,FLASH_CFI_REGION_TABLE+1+idx*4)<<8)) + 1; + blksiz = ((int)GETCFIBYTE(softc,FLASH_CFI_REGION_TABLE+2+idx*4) + + (int)(GETCFIBYTE(softc,FLASH_CFI_REGION_TABLE+3+idx*4)<<8)) * 256; + softc->flashdrv_probe.flash_sectors[idx] = + FLASH_SECTOR_RANGE(nblks,blksiz); + } + + + return 0; +} + +/* ********************************************************************* + * FLASH_SECTOR_QUERY(softc,sector) + * + * Query the sector information about a particular sector. You can + * call this iteratively to find out about all of the sectors. + * + * Input parameters: + * softc - our flash info + * sector - structure to receive sector information + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int flash_sector_query(flashdrv_t *softc,flash_sector_t *sector) +{ + int idx; + int nblks; + int blksiz; + unsigned int offset; + int curblk; + + if (softc->flashdrv_info.flash_type != FLASH_TYPE_FLASH) { + return CFE_ERR_UNSUPPORTED; + } + + if (softc->flashdrv_probe.flash_nsectors == 0) { + return CFE_ERR_UNSUPPORTED; + } + + offset = 0; + curblk = 0; + for (idx = 0; idx < softc->flashdrv_probe.flash_nsectors; idx++) { + nblks = FLASH_SECTOR_NBLKS(softc->flashdrv_probe.flash_sectors[idx]); + blksiz = FLASH_SECTOR_SIZE(softc->flashdrv_probe.flash_sectors[idx]); + if (sector->flash_sector_idx < curblk+nblks) { + sector->flash_sector_status = FLASH_SECTOR_OK; + sector->flash_sector_offset = + offset + (sector->flash_sector_idx-curblk)*blksiz; + sector->flash_sector_size = blksiz; + break; + } + + offset += (nblks)*blksiz; + curblk += nblks; + } + + + if (idx == softc->flashdrv_probe.flash_nsectors) { + sector->flash_sector_status = FLASH_SECTOR_INVALID; + } + + return 0; +} + + +/* ********************************************************************* + * FLASH_SET_CMDSET(softc,cmdset) + * + * Set the command-set that we'll honor for this flash. + * + * Input parameters: + * softc - our flash + * cmdset - FLASH_CFI_CMDSET_xxx + * + * Return value: + * nothing + ********************************************************************* */ + +static void flash_set_cmdset(flashdrv_t *softc,int cmdset) +{ + switch (cmdset) { + case FLASH_CFI_CMDSET_INTEL_ECS: + case FLASH_CFI_CMDSET_INTEL_STD: + softc->flashdrv_ops = &intel_flashops; + /* XXX: Intel flashes don't have the "a-1" line. Yay. */ + softc->flashdrv_widemode = 0; + break; + case FLASH_CFI_CMDSET_AMD_STD: + case FLASH_CFI_CMDSET_AMD_ECS: + softc->flashdrv_ops = &amd_flashops; + break; + default: + /* we don't understand the command set - treat it like ROM */ + softc->flashdrv_info.flash_type = FLASH_TYPE_ROM; + } +} + + +/* ********************************************************************* + * FLASH_CFI_PROBE(softc) + * + * Try to do a CFI query on this device. If we find the m + * magic signature, extract some useful information from the + * query structure. + * + * Input parameters: + * softc - out flash + * + * Return value: + * 0 if successful, <0 if error + ********************************************************************* */ +static int flash_cfi_probe(flashdrv_t *softc) +{ + FLASHCMD(softc,FLASH_CFI_QUERY_ADDR,FLASH_CFI_QUERY_MODE); + + if (!((GETCFIBYTE(softc,FLASH_CFI_SIGNATURE+0) == 'Q') && + (GETCFIBYTE(softc,FLASH_CFI_SIGNATURE+1) == 'R') && + (GETCFIBYTE(softc,FLASH_CFI_SIGNATURE+2) == 'Y'))) { + + FLASHCMD(softc,FLASH_CFI_QUERY_ADDR,FLASH_CFI_QUERY_EXIT); + return CFE_ERR_UNSUPPORTED; + } + + /* + * Gather info from flash + */ + + softc->flashdrv_cfidata.cfidata_cmdset = + ((unsigned int) (GETCFIBYTE(softc,FLASH_CFI_COMMAND_SET))) + + (((unsigned int) (GETCFIBYTE(softc,FLASH_CFI_COMMAND_SET+1))) << 8); + + softc->flashdrv_cfidata.cfidata_devif = + ((unsigned int) (GETCFIBYTE(softc,FLASH_CFI_DEVICE_INTERFACE))) + + (((unsigned int) (GETCFIBYTE(softc,FLASH_CFI_DEVICE_INTERFACE+1))) << 8); + + softc->flashdrv_cfidata.cfidata_size = + 1 << ((unsigned int) (GETCFIBYTE(softc,FLASH_CFI_DEVICE_SIZE))); + + flash_cfi_getsectors(softc); + + /* + * Don't need to be in query mode anymore. + */ + + FLASHCMD(softc,FLASH_CFI_QUERY_ADDR,FLASH_CFI_QUERY_EXIT); + + softc->flashdrv_info.flash_type = FLASH_TYPE_FLASH; + + flash_set_cmdset(softc,softc->flashdrv_cfidata.cfidata_cmdset); + + return 0; + +} + +/* ********************************************************************* + * FLASH_GETWIDTH(softc,info) + * + * Try to determine the width of the flash. This is needed for + * management purposes, since some 16-bit flash parts in 8-bit mode + * have an "A-1" (address line -1) wire to select bytes within + * a 16-bit word. When this is present, the flash commands + * will have different offsets. + * + * Input parameters: + * softc - our flash + * info - flash info structure + * + * Return value: + * nothing + ********************************************************************* */ + +static void flash_getwidth(flashdrv_t *softc,flash_info_t *info) +{ + softc->flashdrv_widemode = 0; /* first try narrow */ + + if (flash_cfi_probe(softc) == 0) { + return; + } + + softc->flashdrv_widemode = 1; /* then wide */ + + if (flash_cfi_probe(softc) == 0) { + return; + } + + /* Just return, assume not wide if no CFI interface */ + softc->flashdrv_widemode = 0; + + softc->flashdrv_info.flash_type = FLASH_TYPE_ROM; /* no CFI: treat as ROM */ +} + +/* ********************************************************************* + * flash_getinfo(softc) + * + * Try to determine if the specified region is flash, ROM, SRAM, + * or something else. + * + * Input parameters: + * softc - our context + * + * Return value: + * nothing + ********************************************************************* */ + +static void flash_getinfo(flashdrv_t *softc) +{ + uint8_t save0,save1; + volatile uint8_t *ptr; + flash_info_t *info = &(softc->flashdrv_info); + + /* + * Set up some defaults based on the probe data + */ + + softc->flashdrv_widemode = 0; + info->flash_base = softc->flashdrv_probe.flash_phys; + info->flash_size = softc->flashdrv_probe.flash_size; + softc->flashdrv_devsize = softc->flashdrv_probe.flash_size; + info->flash_type = FLASH_TYPE_UNKNOWN; + info->flash_flags = 0; + + /* + * If we've been told not to try probing, just assume + * we're a flash part. + */ + + if (softc->flashdrv_probe.flash_flags & FLASH_FLG_MANUAL) { + info->flash_type = FLASH_TYPE_FLASH; + if (softc->flashdrv_probe.flash_flags & FLASH_FLG_WIDE) { + softc->flashdrv_widemode = TRUE; + } + if (softc->flashdrv_probe.flash_cmdset) { + flash_set_cmdset(softc,softc->flashdrv_probe.flash_cmdset); + } + return; + } + + /* + * Attempt to read/write byte zero. If it is changable, + * this is SRAM (or maybe a ROM emulator with the write line hooked up) + */ + + ptr = (volatile uint8_t *) UNCADDR(softc->flashdrv_probe.flash_phys); + save0 = *ptr; /* save old value */ + save1 = *(ptr+1); /* save old value */ + *(ptr) = 0x55; + if ((*ptr) == 0x55) { + *(ptr) = 0xAA; + if ((*ptr) == 0xAA) { + info->flash_type = FLASH_TYPE_SRAM; + } + } + + if (*ptr == save0) info->flash_type = FLASH_TYPE_ROM; + else (*ptr) = save0; /* restore old value */ + + /* + * If we thought it was ROM, try doing a CFI query + * to see if it was flash. This check is kind of kludgey + * but should work. + */ + + if (info->flash_type == FLASH_TYPE_ROM) { + flash_getwidth(softc,info); + if (info->flash_type == FLASH_TYPE_FLASH) { + } + } +} + +/* ********************************************************************* + * flashdrv_setup_nvram(softc) + * + * If we're going to be using a sector of the flash for NVRAM, + * go find that sector and set it up. + * + * Input parameters: + * softc - our flash + * + * Return value: + * nothing. flashdrv_nvram_ok might change though. + ********************************************************************* */ + +static void flashdrv_setup_nvram(flashdrv_t *softc) +{ + flash_sector_t sector; + int res; + + softc->flashdrv_nvram_ok = FALSE; + + if (softc->flashdrv_info.flash_type != FLASH_TYPE_FLASH) { + return; + } + + sector.flash_sector_idx = 0; + for (;;) { + res = flash_sector_query(softc,§or); + if (res == CFE_ERR_UNSUPPORTED) break; + if (res == 0) { + if (sector.flash_sector_status != FLASH_SECTOR_INVALID) { + sector.flash_sector_idx++; + continue; + } + } + break; + } + + /* The sector offset will still contain the value at the end + of the last successful call. That's the last sector, so + we can now use this to fill in the NVRAM info structure */ + + if (res != CFE_ERR_UNSUPPORTED) { + softc->flashdrv_nvraminfo.nvram_offset = sector.flash_sector_offset; + softc->flashdrv_nvraminfo.nvram_size = sector.flash_sector_size; + softc->flashdrv_nvraminfo.nvram_eraseflg = TRUE; /* needs erase */ + softc->flashdrv_nvram_ok = TRUE; + /* + * Set the flash's size as reported in the flash_info structure + * to be the size without the NVRAM sector at the end. + */ + softc->flashdrv_info.flash_size = sector.flash_sector_offset; + softc->flashdrv_devsize = sector.flash_sector_offset; + } + +} + + +/* ********************************************************************* + * flashdrv_probe(drv,probe_a,probe_b,probe_ptr) + * + * Device probe routine. Attach the flash device to + * CFE's device table. + * + * Input parameters: + * drv - driver descriptor + * probe_a - physical address of flash + * probe_b - size of flash (bytes) + * probe_ptr - unused + * + * Return value: + * nothing + ********************************************************************* */ + +static void flashdrv_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + flashdrv_t *softc; + flash_probe_t *probe; + char descr[80]; + + probe = (flash_probe_t *) probe_ptr; + + /* + * probe_a is the flash base address + * probe_b is the size of the flash + * probe_ptr is unused. + */ + + softc = (flashdrv_t *) KMALLOC(sizeof(flashdrv_t),0); + if (softc) { + memset(softc,0,sizeof(flashdrv_t)); + + if (probe) { + /* Passed probe structure, do fancy stuff */ + memcpy(&(softc->flashdrv_probe),probe,sizeof(flash_probe_t)); + if (softc->flashdrv_probe.flash_prog_phys == 0) { + softc->flashdrv_probe.flash_prog_phys = + softc->flashdrv_probe.flash_phys; + } + } + else { + /* Didn't pass probe structure, do the compatible thing */ + softc->flashdrv_probe.flash_phys = probe_a; + softc->flashdrv_probe.flash_prog_phys = probe_a; + softc->flashdrv_probe.flash_size = probe_b; + softc->flashdrv_probe.flash_flags = FLASH_FLG_NVRAM; + } + + softc->flashdrv_cmdaddr = (char *) UNCADDR(softc->flashdrv_probe.flash_prog_phys); + softc->flashdrv_initialized = 0; + softc->flashdrv_ops = &FLASHOPS_DEFAULT; + xsprintf(descr,"%s at %08X size %uKB",drv->drv_description, + softc->flashdrv_probe.flash_phys, + softc->flashdrv_probe.flash_size/1024); + cfe_attach(drv,softc,NULL,descr); + } + +} + + +/* ********************************************************************* + * flashdrv_open(ctx) + * + * Called when the flash device is opened. + * + * Input parameters: + * ctx - device context + * + * Return value: + * 0 if ok else error code + ********************************************************************* */ + +static int flashdrv_open(cfe_devctx_t *ctx) +{ + flashdrv_t *softc = ctx->dev_softc; + + /* + * do initialization + */ + + if (!softc->flashdrv_initialized) { + + /* + * Assume it's not an NVRAM-capable flash + */ + + softc->flashdrv_nvram_ok = FALSE; + + /* + * Probe flash for geometry + */ + flash_getinfo(softc); + + /* + * Find the last sector if in NVRAM mode + */ + + if (softc->flashdrv_probe.flash_flags & FLASH_FLG_NVRAM) { + flashdrv_setup_nvram(softc); + } + + softc->flashdrv_initialized = TRUE; + } + + return 0; +} + + +/* ********************************************************************* + * flashdrv_read(ctx,buffer) + * + * Read data from the flash device. The flash device is + * considered to be like a disk (you need to specify the offset). + * + * Input parameters: + * ctx - device context + * buffer - buffer descriptor + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +static int flashdrv_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + flashdrv_t *softc = ctx->dev_softc; + unsigned char *bptr; + unsigned char *flashbase; + int offset; + int blen; + + /* + * For now, read the flash from K1 (always). Eventually + * we need to flush the cache after a write. + */ + + flashbase = (unsigned char *) UNCADDR(softc->flashdrv_probe.flash_phys); + + bptr = buffer->buf_ptr; + blen = buffer->buf_length; + offset = (int) buffer->buf_offset; + + if (!(softc->flashdrv_unlocked)) { + if ((offset + blen) > softc->flashdrv_devsize) { + blen = softc->flashdrv_devsize - offset; + } + } + +#ifdef _FLASH_BROKEN_BYTEREAD_ + /* + * BCM1250 users: don't worry about this. This hack is for + * something else and should not be used with the BCM1250. + */ + if (softc->flashdrv_probe.flash_flags & FLASH_FLG_16BIT) { + uint16_t *src; + int len = blen; + int idx = 0; + union { + uint16_t x; + char b[2]; + } u; + + src = (uint16_t *) flashbase; + while (len > 0) { + u.x = src[(idx+offset)>>1]; + *bptr++ = u.b[(idx+offset)&1]; + len--; + idx++; + } + } + else { + memcpy(bptr,flashbase + offset, blen); + } +#else + memcpy(bptr,flashbase + offset, blen); +#endif + + buffer->buf_retlen = blen; + + return 0; +} + +/* ********************************************************************* + * flashdrv_inpstat(ctx,inpstat) + * + * Return "input status". For flash devices, we always return true. + * + * Input parameters: + * ctx - device context + * inpstat - input status structure + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +static int flashdrv_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat) +{ + /* flashdrv_t *softc = ctx->dev_softc; */ + + inpstat->inp_status = 1; + return 0; +} + + +/* ********************************************************************* + * flash_writeall(softc,buffer) + * + * Write the entire flash and reboot. This is a special case + * used for when the flash currently being used for CFE's + * execution is updated. A small assembly routine is relocated + * to DRAM to do the update (so that the programming routine is + * not erased while we're running it), and then the update + * is done. When completed, CFE is restarted. + * + * (we could get really sleazy here and touch the routine first + * so it will stay in the cache, thereby eliminating the need + * to relocate it, but that's dangerous) + * + * Input parameters: + * softc - our context + * buffer - buffer descriptor + * + * Return value: + * does not return + ********************************************************************* */ + +static int flash_writeall(flashdrv_t *softc,iocb_buffer_t *buffer) +{ + void *rptr; + void (*routine)(unsigned char *data,unsigned int flashbase, + unsigned int size,unsigned int secsize); + + rptr = KMALLOC(flash_write_all_len,0); + + if (!rptr) return CFE_ERR_NOMEM; + + memcpy(rptr,flash_write_all_ptr,flash_write_all_len); + + _cfe_flushcache(0); + + routine = rptr; + + (*routine)(buffer->buf_ptr, + softc->flashdrv_probe.flash_phys, + buffer->buf_length, + 65536); + + return -1; +} + + +/* ********************************************************************* + * flashdrv_write(ctx,buffer) + * + * Write data to the flash device. The flash device is + * considered to be like a disk (you need to specify the offset). + * + * Input parameters: + * ctx - device context + * buffer - buffer descriptor + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +static int flashdrv_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + flashdrv_t *softc = ctx->dev_softc; + unsigned char *bptr; + int offset; + int blen; + int res; + + bptr = buffer->buf_ptr; + blen = buffer->buf_length; + offset = (int) buffer->buf_offset; + + if (!(softc->flashdrv_unlocked)) { + if ((offset + blen) > softc->flashdrv_devsize) { + blen = softc->flashdrv_devsize - offset; + } + } + + res = FLASHOP_WRITE_BLOCK(softc,offset,bptr,blen); + + buffer->buf_retlen = res; + + /* XXX flush the cache here? */ + + return (res == blen) ? 0 : CFE_ERR_IOERR; +} + +/* ********************************************************************* + * flashdrv_ioctl(ctx,buffer) + * + * Handle special IOCTL functions for the flash. Flash devices + * support NVRAM information, sector and chip erase, and a + * special IOCTL for updating the running copy of CFE. + * + * Input parameters: + * ctx - device context + * buffer - descriptor for IOCTL parameters + * + * Return value: + * 0 if ok else error + ********************************************************************* */ +static int flashdrv_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + flashdrv_t *softc = ctx->dev_softc; + nvram_info_t *info; + int offset; + + /* + * If using flash to store environment, only the last sector + * is used for environment stuff. + */ + + switch ((int)buffer->buf_ioctlcmd) { + case IOCTL_NVRAM_ERASE: + if (softc->flashdrv_nvram_ok == FALSE) return CFE_ERR_UNSUPPORTED; + FLASHOP_ERASE_SECTOR(softc,softc->flashdrv_nvraminfo.nvram_offset); + return 0; + + case IOCTL_NVRAM_GETINFO: + info = (nvram_info_t *) buffer->buf_ptr; + if (buffer->buf_length != sizeof(nvram_info_t)) return CFE_ERR_INV_PARAM; + if (softc->flashdrv_nvram_ok == FALSE) return CFE_ERR_UNSUPPORTED; + info->nvram_offset = softc->flashdrv_nvraminfo.nvram_offset; + info->nvram_size = softc->flashdrv_nvraminfo.nvram_size; + info->nvram_eraseflg = softc->flashdrv_nvraminfo.nvram_eraseflg; + buffer->buf_retlen = sizeof(nvram_info_t); + return 0; + + case IOCTL_FLASH_ERASE_SECTOR: + offset = (int) buffer->buf_offset; + if (!(softc->flashdrv_unlocked)) { + if (offset >= softc->flashdrv_devsize) return -1; + } + FLASHOP_ERASE_SECTOR(softc,offset); + return 0; + + case IOCTL_FLASH_ERASE_ALL: + offset = (int) buffer->buf_offset; + if (offset != 0) return -1; + flash_erase_all(softc); + return 0; + + case IOCTL_FLASH_WRITE_ALL: + flash_writeall(softc,buffer); + return -1; /* should not return */ + + case IOCTL_FLASH_GETINFO: + memcpy(buffer->buf_ptr,&(softc->flashdrv_info),sizeof(flash_info_t)); + return 0; + + case IOCTL_FLASH_GETSECTORS: + return flash_sector_query(softc,(flash_sector_t *) buffer->buf_ptr); + + + case IOCTL_FLASH_ERASE_RANGE: + return flash_erase_range(softc,(flash_range_t *) buffer->buf_ptr); + + case IOCTL_NVRAM_UNLOCK: + softc->flashdrv_unlocked = TRUE; + break; + + default: + return -1; + } + + return -1; +} + + +/* ********************************************************************* + * flashdrv_close(ctx) + * + * Close the flash device. + * + * Input parameters: + * ctx - device context + * + * Return value: + * 0 + ********************************************************************* */ +static int flashdrv_close(cfe_devctx_t *ctx) +{ + /* flashdrv_t *softc = ctx->dev_softc; */ + + /* XXX Invalidate the cache */ + + return 0; +} + + diff --git a/cfe/cfe/dev/dev_ht7520.c b/cfe/cfe/dev/dev_ht7520.c new file mode 100644 index 0000000..8e64971 --- /dev/null +++ b/cfe/cfe/dev/dev_ht7520.c @@ -0,0 +1,167 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * HT7520 (Golem) Bridge Support File: dev_ht7520.c + * + ********************************************************************* + * + * Copyright 2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + +#include "lib_types.h" +#include "lib_physio.h" + +#include "pcireg.h" +#include "pcivar.h" + +extern int eoi_implemented; + +void ht7520apic_preset (pcitag_t tag); +void ht7520apic_setup (pcitag_t tag); + + +/* PLX HT7520 (LDT to PCI-X bridge + APIC) specific definitions */ + +#define PCI_VENDOR_AMD 0x1022 +#define PCI_PRODUCT_PLX_HT7520 0x7450 +#define PCI_PRODUCT_PLX_HT7520_APIC 0x7451 + +/* HT7520 specific registers */ + +/* APIC configuration registers */ + +#define APIC_CONTROL_REG 0x0044 + +#define APIC_CONTROL_OSVISBAR (1 << 0) +#define APIC_CONTROL_IOAEN (1 << 1) + +#define APIC_BASE_ADDR_REG 0x0048 + +/* APIC registers in BAR0 memory space */ + +#define HT7520_APIC_INDEX_REG 0x0000 +#define HT7520_APIC_DATA_REG 0x0010 + +#define APIC_ID_INDEX 0x00 +#define APIC_VERSION_INDEX 0x01 +#define APIC_ARBID_INDEX 0x02 +#define APIC_RDR_BASE_INDEX 0x10 +#define APIC_RDR_LO_INDEX(n) (APIC_RDR_BASE_INDEX + 2*(n)) +#define APIC_RDR_HI_INDEX(n) (APIC_RDR_BASE_INDEX + 2*(n) + 1) + +#define RDR_HI_DEST_SHIFT (56-32) +#define RDR_HI_DEST_MASK (0xff << RDR_HI_DEST_SHIFT) +#define RDR_LO_IM (1 << 16) +#define RDR_LO_TM (1 << 15) +#define RDR_LO_IRR (1 << 14) +#define RDR_LO_POL (1 << 13) +#define RDR_LO_DS (1 << 12) +#define RDR_LO_DM (1 << 11) +#define RDR_LO_MT_SHIFT 8 +#define RDR_LO_MT_MASK (3 << RDR_LO_MT_SHIFT) +#define RDR_LO_IV_SHIFT 0 +#define RDR_LO_IV_MASK (0xff << RDR_LO_IV_SHIFT) + +void +ht7520apic_preset (pcitag_t tag) +{ + pcireg_t ctrl; + + /* For some reason, BAR0 (necessary for setting the interrupt + mapping) is hidden by default; the following makes it + visible. */ + ctrl = pci_conf_read(tag, APIC_CONTROL_REG); + ctrl |= APIC_CONTROL_IOAEN | APIC_CONTROL_OSVISBAR; + pci_conf_write(tag, APIC_CONTROL_REG, ctrl); + ctrl = pci_conf_read(tag, APIC_CONTROL_REG); /* push */ +} + +void +ht7520apic_setup (pcitag_t tag) +{ + int bus, device, function; + pcitag_t br_tag; + int secondary; + struct pci_bus *pb; + unsigned offset; + phys_addr_t apic_addr; + uint32_t rdrh, rdrl; + int i; + + /* The HT7520 splits the bridge and APIC functionality between two + functions. The following code depends upon a known + relationship between the bridge and APIC tags, with a temporary + fudge for the simulator. NB: We assume that the bridge + function has already been initialized. */ + + pci_break_tag(tag, &bus, &device, &function); + +#ifdef _FUNCSIM_ + br_tag = pci_make_tag(bus, device-2, 0); +#else + br_tag = pci_make_tag(bus, device, function-1); +#endif + secondary = (pci_conf_read(br_tag, PPB_BUSINFO_REG) >> 8) & 0xff; + pb = &_pci_bus[secondary]; + + /* Set up interrupt mappings. */ + pci_map_mem(tag, PCI_MAPREG(0), PCI_MATCH_BITS, &apic_addr); + + offset = pb->inta_shift % 4; + for (i = 0; i < 4; i++) { + phys_write32(apic_addr + HT7520_APIC_INDEX_REG, APIC_RDR_HI_INDEX(i)); + rdrh = 0x03 << RDR_HI_DEST_SHIFT; /* CPU 0 + CPU 1 */ + phys_write32(apic_addr + HT7520_APIC_DATA_REG, rdrh); + rdrh = phys_read32(apic_addr + HT7520_APIC_DATA_REG); /* push */ + + phys_write32(apic_addr + HT7520_APIC_INDEX_REG, APIC_RDR_LO_INDEX(i)); + if (eoi_implemented) { + /* Passes >=2 have working EOI. Trigger=Level */ + rdrl = (RDR_LO_TM | /* Level */ + RDR_LO_POL | /* Active Low */ + RDR_LO_DM | /* Logical */ + 0x0 << RDR_LO_MT_SHIFT | /* Fixed */ + (56+offset) << RDR_LO_IV_SHIFT); /* Vector */ + } else { + /* Pass 1 lacks working EOI. Trigger=Edge. Note that + LO_POL appears mis-documented for edges. */ + rdrl = (RDR_LO_DM | /* Logical */ + 0x0 << RDR_LO_MT_SHIFT | /* Fixed */ + (56+offset) << RDR_LO_IV_SHIFT); /* Vector */ + } + phys_write32(apic_addr + HT7520_APIC_DATA_REG, rdrl); + offset = (offset + 1) % 4; + } +} diff --git a/cfe/cfe/dev/dev_ide_common.c b/cfe/cfe/dev/dev_ide_common.c new file mode 100644 index 0000000..e15ac8a --- /dev/null +++ b/cfe/cfe/dev/dev_ide_common.c @@ -0,0 +1,1249 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * Generic IDE disk driver File: dev_ide_common.c + * + * This is a simple driver for IDE hard disks. The mechanics + * of talking to the I/O ports are abstracted sufficiently to make + * this driver usable for various bus interfaces. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "lib_types.h" +#include "lib_malloc.h" +#include "lib_printf.h" +#include "lib_string.h" +#include "cfe_timer.h" +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_ioctl.h" +#include "cfe_error.h" + +#include "dev_ide_common.h" + +#include "dev_ide.h" + +/* ********************************************************************* + * Macros + ********************************************************************* */ + +#define DISK_MASTER 0 +#define DISK_SLAVE 1 + +#define IDE_WRITEREG8(ide,reg,val) IDEDISP_WRITEREG8(ide->idecommon_dispatch,reg,val) +#define IDE_WRITEREG16(ide,reg,val) IDEDISP_WRITEREG8(ide->idecommon_dispatch,reg,val) +#define IDE_WRITEBUF(ide,reg,buf,len) IDEDISP_WRITEBUF(ide->idecommon_dispatch,reg,buf,len) +#define IDE_READREG8(ide,reg) IDEDISP_READREG8(ide->idecommon_dispatch,reg) +#define IDE_READREG16(ide,reg) IDEDISP_READREG16(ide->idecommon_dispatch,reg) +#define IDE_READBUF(ide,reg,buf,len) IDEDISP_READBUF(ide->idecommon_dispatch,reg,buf,len) + +#define GETWORD_LE(buf,wordidx) (((unsigned int) (buf)[(wordidx)*2]) + \ + (((unsigned int) (buf)[(wordidx)*2+1]) << 8)) + +#define _IDE_DEBUG_ + + +static void idecommon_testdrq(idecommon_t *ide); + +/* ********************************************************************* + * idecommon_sectorshift(size) + * + * Given a sector size, return log2(size). We cheat; this is + * only needed for 2048 and 512-byte sectors. + * Explicitly using shifts and masks in sector number calculations + * helps on 32-bit-only platforms, since we probably won't need + * a helper library. + * + * Input parameters: + * size - sector size + * + * Return value: + * # of bits to shift + ********************************************************************* */ + +#define idecommon_sectorshift(size) (((size)==2048)?11:9) + +/* ********************************************************************* + * idecommon_waitnotbusy(ide) + * + * Wait for an IDE device to report "not busy" + * + * Input parameters: + * ide - IDE interface + * + * Return value: + * 0 if ok, else -1 if timeout + ********************************************************************* */ + +static int idecommon_waitnotbusy(idecommon_t *ide) +{ + int32_t timer; + uint8_t status; + + TIMER_SET(timer,10*CFE_HZ); + + while (!TIMER_EXPIRED(timer)) { + status = IDE_READREG8(ide,IDE_REG_STATUS); + if (!(status & IDE_STS_BSY) && (status & IDE_STS_DRQ)) { + idecommon_testdrq(ide); + continue; + } + if ((status & (IDE_STS_BSY | IDE_STS_DRQ )) == 0) return 0; + POLL(); + } + +#ifdef _IDE_DEBUG_ + xprintf("Device did not become unbusy\n"); +#endif + return -1; +} + +#if 0 +/* ********************************************************************* + * idecommon_waitready(ide) + * + * Wait for the specified device to become ready. + * + * Input parameters: + * ide - IDE interface + * + * Return value: + * 0 if device became ready + * -1 if device did not become ready + ********************************************************************* */ + +static int idecommon_waitready(idecommon_t *ide) +{ + int32_t timer; + uint8_t status; + + TIMER_SET(timer,10*CFE_HZ); + + while (!TIMER_EXPIRED(timer)) { + status = IDE_READREG8(ide,IDE_REG_STATUS); + if (status & IDE_STS_RDY) return 0; + POLL(); + } + +#ifdef _IDE_DEBUG_ + xprintf("Disk did not become ready\n"); +#endif + + return -1; +} +#endif + +/* ********************************************************************* + * idecommon_waitbusy(idx) + * + * Wait for an IDE disk to start processing a command, or at + * least long enough to indicate that it is doing so. + * The code below looks suspiciously like a timing loop. + * unfortunately, that's what it is, determined empirically + * for certain ATA flash cards. Without this many reads to the + * ALTSTAT register, the PCMCIA controller deasserts the + * card detect pins briefly. Anyone have any clues? + * + * Input parameters: + * ide - IDE interface + * + * Return value: + * void + ********************************************************************* */ + +static void idecommon_waitbusy(idecommon_t *ide) +{ + int idx; + + for (idx = 0; idx < 10; idx++) { + IDE_READREG8(ide,IDE_REG_ALTSTAT); + IDE_READREG8(ide,IDE_REG_ALTSTAT); + IDE_READREG8(ide,IDE_REG_ALTSTAT); + IDE_READREG8(ide,IDE_REG_ALTSTAT); + } +} + + +/* ********************************************************************* + * idecommon_wait_drq(ide) + * + * Wait for the BUSY bit to be clear and the DRQ bit to be set. + * This is usually the indication that it's time to transfer data. + * + * Input parameters: + * ide - IDE interface + * 0 if DRQ is set + * -1 if timeout occured + ********************************************************************* */ + +static int idecommon_wait_drq(idecommon_t *ide) +{ + int32_t timer; + uint8_t status; + + TIMER_SET(timer,10*CFE_HZ); + + while (!TIMER_EXPIRED(timer)) { + POLL(); + status = IDE_READREG8(ide,IDE_REG_STATUS); + if (!(status & IDE_STS_BSY) && (status & IDE_STS_ERR)) { + xprintf("Drive status: %02X error %02X\n",status, + IDE_READREG8(ide,IDE_REG_ERROR)); + return -1; + } + if (!(status & IDE_STS_BSY) && (status & IDE_STS_DRQ)) return 0; + } + +#ifdef _IDE_DEBUG_ + xprintf("Timeout waiting for disk\n"); +#endif + + return -1; +} + +/* ********************************************************************* + * idecommon_testdrq(ide) + * + * Debug routine. Check the DRQ bit. If it's set, it is not + * supposed to be, so transfer data until it clears and report + * how much we had to transfer. + * + * Input parameters: + * ide - IDE interface + * + * Return value: + * nothing + ********************************************************************* */ + +#ifdef _IDE_DEBUG_ +static void idecommon_testdrq(idecommon_t *ide) +{ + uint8_t status; + uint16_t data; + int idx; + + status = IDE_READREG8(ide,IDE_REG_STATUS); + if (status & IDE_STS_DRQ) { + xprintf("Error: DRQ should be zero\n"); + idx = 0; + while (status & IDE_STS_DRQ) { + data = IDE_READREG16(ide,IDE_REG_DATA); + idx++; + status = IDE_READREG8(ide,IDE_REG_STATUS); + } + xprintf("Had to read data %d times to clear DRQ\n",idx); + } +} +#else +#define idecommon_testdrq(ide) +#endif + + +/* ********************************************************************* + * idecommon_dumpregs(ide) + * + * Dump out the IDE registers. (debug routine) + * + * Input parameters: + * ide - ide disk + * + * Return value: + * nothing + ********************************************************************* */ + +static void idecommon_dumpregs(idecommon_t *ide) +{ +#if 0 + uint8_t reg = 0; + + reg = IDE_READREG8(ide,IDE_REG_STATUS); + xprintf("Status:%02X ",reg); + + reg = IDE_READREG8(ide,IDE_REG_DRVHD); + xprintf("DrvHd:%02X ",reg); + + reg = IDE_READREG8(ide,IDE_REG_CYLLSB); + xprintf("CylLSB:%02X ",reg); + + reg = IDE_READREG8(ide,IDE_REG_CYLMSB); + xprintf("CylMSB:%02X ",reg); + + reg = IDE_READREG8(ide,IDE_REG_SECNUM); + xprintf("SecNum:%02X ",reg); + + reg = IDE_READREG8(ide,IDE_REG_SECCNT); + xprintf("SecCnt:%02X ",reg); + + xprintf("\n"); +#endif +} + + +/* ********************************************************************* + * idecommon_reset(ide) + * + * Reset the IDE interface. + * + * Input parameters: + * ide - IDE interface + * + * Return value: + * 0 if ok, else -1 if a timeout occured + ********************************************************************* */ + +static int idecommon_reset(idecommon_t *ide) +{ + return 0; +} + +/* ********************************************************************* + * idecommon_identify(ide,buffer) + * + * Execute an IDENTIFY command to get information about the disk. + * + * Input parameters: + * ide - IDE interface + * buffer - pointer to 512 byte buffer + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int idecommon_identify(idecommon_t *ide,unsigned char *buffer) +{ + + /* Device Select Protocol; see ATAPI-4 sect 9.6 */ + + if (idecommon_waitnotbusy(ide) < 0) return -1; + IDE_WRITEREG8(ide,IDE_REG_DRVHD,(ide->idecommon_unit<<4)|0); + if (idecommon_waitnotbusy(ide) < 0) return -1; + + /* Set device registers */ + + IDE_WRITEREG8(ide,IDE_REG_CYLLSB,0); + IDE_WRITEREG8(ide,IDE_REG_CYLMSB,0); + IDE_WRITEREG8(ide,IDE_REG_SECNUM,1); + IDE_WRITEREG8(ide,IDE_REG_SECCNT,1); + + idecommon_testdrq(ide); + + /* Issue command, then read ALT STATUS (9.7) */ + + if (ide->idecommon_atapi) { + IDE_WRITEREG8(ide,IDE_REG_COMMAND,IDE_CMD_ATAPI_IDENTIFY); + } + else { + IDE_WRITEREG8(ide,IDE_REG_COMMAND,IDE_CMD_DRIVE_INFO); + } + + IDE_READREG8(ide,IDE_REG_ALTSTAT); + idecommon_waitbusy(ide); /* should not be necessary */ + + /* Wait BSY=0 && DRQ=1, then read buffer, see sect 9.7 */ + + if (idecommon_wait_drq(ide) < 0) return -1; + IDE_READBUF(ide,IDE_REG_DATA,buffer,DISK_SECTORSIZE); + + idecommon_testdrq(ide); + + return 0; +} + +/* ********************************************************************* + * idecommon_packet(ide,packet,pktlen,databuf,datalen) + * + * Process an IDE "packet" command, for ATAPI devices + * + * Input parameters: + * ide - IDE interface + * packet,pktlen - command packet + * databuf,datalen - data buffer + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int idecommon_packet(idecommon_t *ide, + uint8_t *packet,int pktlen, + uint8_t *databuf,int datalen) +{ + uint8_t status; + + /* + * Not valid on non-ATAPI disks + */ + + if (!ide->idecommon_atapi) return -1; + + /* + * Set up the standard IDE registers for an ATAPI PACKET command + */ + + /* Device Select Protocol */ + if (idecommon_waitnotbusy(ide) < 0) return -1; + IDE_WRITEREG8(ide,IDE_REG_DRVHD,(ide->idecommon_unit<<4)); + if (idecommon_waitnotbusy(ide) < 0) return -1; + + /* Device Registers */ + IDE_WRITEREG8(ide,IDE_REG_BCLSB,(datalen & 0xFF)); + IDE_WRITEREG8(ide,IDE_REG_BCMSB,((datalen >> 8) & 0xFF)); + IDE_WRITEREG8(ide,IDE_REG_SECNUM,0); + IDE_WRITEREG8(ide,IDE_REG_SECCNT,0); + IDE_WRITEREG8(ide,IDE_REG_COMMAND,IDE_CMD_ATAPI_PACKET); + + /* + * Wait for the DRQ bit to indicate that we should send + * the packet. + */ + + if (idecommon_wait_drq(ide) < 0) return -1; + + status = IDE_READREG8(ide,IDE_REG_IR); + + /* + * Send the packet to the device + */ + + IDE_WRITEBUF(ide,IDE_REG_DATA,packet,pktlen); + + /* + * Wait for BSY to be cleared and DRQ to be set. + */ + + if (idecommon_wait_drq(ide) < 0) return -1; + status = IDE_READREG8(ide,IDE_REG_ALTSTAT); + if (idecommon_wait_drq(ide) < 0) return -1; + status = IDE_READREG8(ide,IDE_REG_IR); + + + /* + * Transfer data, if necessary. The direction will depend + * on what the drive says. If this is a non-data command, + * passing databuf == NULL can avoid all this. + */ + + if (databuf) { + status = IDE_READREG8(ide,IDE_REG_IR); + if (status & IDE_IR_CD) { + xprintf("Error: Command/data should be zero\n"); + } + + if (status & IDE_IR_IO) { /* from device (READ) */ + IDE_READBUF(ide,IDE_REG_DATA,databuf,datalen); + } + else { /* to device (WRITE) */ + IDE_WRITEBUF(ide,IDE_REG_DATA,databuf,datalen); + } + + } + + + idecommon_testdrq(ide); + + return 0; + +} + + +/* ********************************************************************* + * idecommon_request_sense(ide) + * + * Request sense data. This also clears a UNIT_ATTENTION condition + * + * Input parameters: + * ide - IDE interface + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ +static int idecommon_request_sense(idecommon_t *ide) +{ + uint8_t cdb[12]; + uint8_t sensedata[32]; + int res; + int numbytes; + + numbytes = sizeof(sensedata); + + cdb[0] = CDB_CMD_REQSENSE; + cdb[1] = 0; + cdb[2] = 0; + cdb[3] = 0; + cdb[4] = sizeof(sensedata); + cdb[5] = 0; + cdb[6] = 0; + cdb[7] = 0; + cdb[8] = 0; + cdb[9] = 0; + cdb[10] = 0; + cdb[11] = 0; + + memset(sensedata,0,sizeof(sensedata)); + + res = idecommon_packet(ide,cdb,sizeof(cdb),sensedata,numbytes); + +#if 0 + xprintf("Sense data: "); + xprintf("Err:%02X ",sensedata[0]); + xprintf("Key:%02X ",sensedata[1]); + xprintf("Information:%02X%02X ",sensedata[2],sensedata[3]); + xprintf("ASC:%02X ASCQ:%02X ",sensedata[12],sensedata[13]); + xprintf("\n"); +#endif + + idecommon_testdrq(ide); + + return res; +} + + +/* ********************************************************************* + * idecommon_read_atapi(ide,lba,numsec,buffer) + * + * Read sector(s) from the device. This version is for ATAPI devs. + * + * Input parameters: + * ide - IDE interface + * lba - logical block address + * numsec - number of sectors + * buffer - buffer address + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int idecommon_read_atapi(idecommon_t *ide,uint64_t lba, + int numsec,unsigned char *buffer) +{ + uint8_t cdb[12]; + int res = 0; + int numbytes; + int idx; + + numbytes = numsec << idecommon_sectorshift(ide->idecommon_sectorsize); + + cdb[0] = CDB_CMD_READ; + cdb[1] = 0; + cdb[2] = ((lba >> 24) & 0xFF); + cdb[3] = ((lba >> 16) & 0xFF); + cdb[4] = ((lba >> 8) & 0xFF); + cdb[5] = ((lba >> 0) & 0xFF); + cdb[6] = 0; + cdb[7] = ((numsec >> 8) & 0xFF); + cdb[8] = ((numsec >> 0) & 0xFF); + cdb[9] = 0; + cdb[10] = 0; + cdb[11] = 0; + + for (idx = 0; idx < 4; idx++) { + res = idecommon_packet(ide,cdb,sizeof(cdb),buffer,numbytes); + if (res < 0) { + idecommon_request_sense(ide); + continue; + } + break; + } + + return res; +} + + +/* ********************************************************************* + * idecommon_write_atapi(ide,lba,numsec,buffer) + * + * Write sector(s) to the device. This version is for ATAPI disks + * + * Input parameters: + * ide - IDE interface + * lba - logical block address + * numsec - number of sectors + * buffer - buffer address + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int idecommon_write_atapi(idecommon_t *ide,uint64_t lba, + int numsec,unsigned char *buffer) +{ + uint8_t cdb[12]; + int res; + int numbytes; + + numbytes = numsec << idecommon_sectorshift(ide->idecommon_sectorsize); + + cdb[0] = CDB_CMD_WRITE; + cdb[1] = 0; + cdb[2] = ((lba >> 24) & 0xFF); + cdb[3] = ((lba >> 16) & 0xFF); + cdb[4] = ((lba >> 8) & 0xFF); + cdb[5] = ((lba >> 0) & 0xFF); + cdb[6] = 0; + cdb[7] = ((numsec >> 8) & 0xFF); + cdb[8] = ((numsec >> 0) & 0xFF); + cdb[9] = 0; + cdb[10] = 0; + cdb[11] = 0; + + res = idecommon_packet(ide,cdb,sizeof(cdb),buffer,numbytes); + + return res; +} + + +/* ********************************************************************* + * idecommon_read_lba(ide,lba,numsec,buffer) + * + * Read sector(s) from the device. + * + * Input parameters: + * ide - IDE interface + * lba - logical block address + * numsec - number of sectors + * buffer - buffer address + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int idecommon_read_lba(idecommon_t *ide,uint64_t lba,int numsec,unsigned char *buffer) +{ + int secidx; + unsigned char *ptr; + + if (idecommon_waitnotbusy(ide) < 0) return -1; + IDE_WRITEREG8(ide,IDE_REG_DRVHD,(ide->idecommon_unit<<4) | ((lba >> 24) & 0x0F) | 0x40); + if (idecommon_waitnotbusy(ide) < 0) return -1; + + IDE_WRITEREG8(ide,IDE_REG_CYLMSB,((lba >> 16) & 0xFF)); + IDE_WRITEREG8(ide,IDE_REG_CYLLSB,((lba >> 8) & 0xFF)); + IDE_WRITEREG8(ide,IDE_REG_SECNUM,(lba & 0xFF)); + IDE_WRITEREG8(ide,IDE_REG_SECCNT,numsec); + + idecommon_testdrq(ide); + + IDE_WRITEREG8(ide,IDE_REG_COMMAND,IDE_CMD_READ); + + idecommon_waitbusy(ide); + if (idecommon_wait_drq(ide) < 0) return -1; + + ptr = buffer; + + for (secidx = 0; secidx < numsec; secidx++) { + IDE_READBUF(ide,IDE_REG_DATA,ptr,ide->idecommon_sectorsize); + ptr += ide->idecommon_sectorsize; + } + + idecommon_testdrq(ide); + + return 0; +} + + +/* ********************************************************************* + * idecommon_write_lba(ide,lba,numsec,buffer) + * + * Write sector(s) from the device. + * + * Input parameters: + * ide - IDE interface + * lba - logical block address + * numsec - number of sectors + * buffer - buffer address + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int idecommon_write_lba(idecommon_t *ide,uint64_t lba,int numsec,unsigned char *buffer) +{ + int secidx; + uint8_t *ptr; + + if (idecommon_waitnotbusy(ide) < 0) return -1; + IDE_WRITEREG8(ide,IDE_REG_DRVHD,(ide->idecommon_unit<<4) | ((lba >> 24) & 0x0F) | 0x40); + if (idecommon_waitnotbusy(ide) < 0) return -1; + + IDE_WRITEREG8(ide,IDE_REG_CYLMSB,((lba >> 16) & 0xFF)); + IDE_WRITEREG8(ide,IDE_REG_CYLLSB,((lba >> 8) & 0xFF)); + IDE_WRITEREG8(ide,IDE_REG_SECNUM,(lba & 0xFF)); + IDE_WRITEREG8(ide,IDE_REG_SECCNT,numsec); + + IDE_WRITEREG8(ide,IDE_REG_COMMAND,IDE_CMD_WRITE); + + if (idecommon_wait_drq(ide) < 0) return -1; + + ptr = buffer; + + for (secidx = 0; secidx < numsec; secidx++) { + IDE_WRITEBUF(ide,IDE_REG_DATA,ptr,ide->idecommon_sectorsize); + ptr += ide->idecommon_sectorsize; + } + + idecommon_testdrq(ide); + + return 0; +} + + +/* ********************************************************************* + * idecommon_diagnostic(ide) + * + * run the device diagnostics on the IDE device. This also + * helps us determine if it's an IDE or ATAPI disk, since the + * diagnostic will leave a signature in the registers. + * + * Input parameters: + * softc - IDE interface + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int idecommon_diagnostic(idecommon_t *softc) +{ + if (idecommon_waitnotbusy(softc) < 0) return -1; + IDE_WRITEREG8(softc,IDE_REG_DRVHD,(softc->idecommon_unit<<4)); + if (idecommon_waitnotbusy(softc) < 0) return -1; + + IDE_WRITEREG8(softc,IDE_REG_COMMAND,IDE_CMD_DIAGNOSTIC); + if (idecommon_waitnotbusy(softc) < 0) return -1; + + cfe_sleep(CFE_HZ/2); + idecommon_dumpregs(softc); + + return 0; +} + + +/* ********************************************************************* + * idecommon_getmodel(buffer,model) + * + * Get the ASCII model name out of an IDE identify buffer. some + * byte swapping is involved here. The trailing blanks are trimmed. + * + * Input parameters: + * buffer - 512-byte buffer from IDENTIFY command + * model - 41-byte string (max) for model name + * + * Return value: + * nothing + ********************************************************************* */ + +static void idecommon_getmodel(uint8_t *buffer,char *model) +{ + uint16_t w; + int idx; + + for (idx = 0; idx < 20; idx++) { + w = GETWORD_LE(buffer,27+idx); + model[idx*2] = w >> 8; + model[idx*2+1] = w & 0xFF; + } + for (idx = 39; idx > 0; idx--) { + if (model[idx] != ' ') { + model[idx+1] = '\0'; + break; + } + } + +} + +/* ********************************************************************* + * idecommon_devprobe(softc) + * + * Probe the IDE device, to determine if it's actually present + * or not. If present, determine if it's IDE or ATAPI and + * get the device size. Init our internal structures so we know + * how to talk to the device. + * + * Input parameters: + * softc - IDE structure + * noisy - display stuff as we probe + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +int idecommon_devprobe(idecommon_t *softc,int noisy) +{ + int res; + int atapi; + unsigned char buffer[DISK_SECTORSIZE]; + unsigned char model[41]; + uint64_t ttlsect; + int devtype; + uint16_t w; + char *typename; + + /* + * Reset the drive + */ + + res = idecommon_reset(softc); + if (res < 0) return -1; + + /* + * Run diagnostic to get the signature. + */ + + res = idecommon_diagnostic(softc); + if (res < 0) return res; + + /* + * Test signature + */ + + atapi = 0; + if ((IDE_READREG8(softc,IDE_REG_CYLLSB) == ATAPI_SIG_LSB) && + (IDE_READREG8(softc,IDE_REG_CYLMSB) == ATAPI_SIG_MSB)) { + atapi = 1; + } + + if (noisy) { + if (atapi) xprintf("ATAPI: "); + else xprintf("IDE: "); + } + + /* + * Do tha appropriate IDENTIFY command to get device information + */ + + softc->idecommon_atapi = atapi; + res = idecommon_identify(softc,buffer); + if (res < 0) return -1; + + /* + * Using that information, determine our device type + */ + + if (!atapi) { + devtype = IDE_DEVTYPE_DISK; + typename = "Disk"; + } + else { + w = GETWORD_LE(buffer,0); + switch ((w >> 8) & 31) { + case 5: /* CD-ROM */ + devtype = IDE_DEVTYPE_CDROM; + typename = "CD-ROM"; + break; + default: + devtype = IDE_DEVTYPE_ATAPIDISK; + typename = "Disk"; + break; + } + } + + /* + * Say nice things about the device. + */ + + idecommon_getmodel(buffer,model); + if (noisy) xprintf("%s, \"%s\"",typename,model); + + +#ifdef _IDE_DEBUG_ + if (!softc->idecommon_atapi) { + ttlsect = (GETWORD_LE(buffer,57) + (GETWORD_LE(buffer,58) << 16)); + if (noisy) xprintf(", Sectors: %llu (%lld MB)",ttlsect, + (uint64_t) (ttlsect/2048)); + } + else { + ttlsect = 0; + } +#endif + if (noisy) xprintf("\n"); + + /* + * Initialize internal structure info, especially pointers to the + * read/write routines and the sector size. + */ + + softc->idecommon_ttlsect = ttlsect; + idecommon_init(softc,devtype); + + return res; +} + +/* ********************************************************************* + * idecommon_open(ctx) + * + * Process the CFE OPEN call for this device. For IDE disks, + * the device is reset and identified, and the geometry is + * determined. + * + * Input parameters: + * ctx - device context + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + + +int idecommon_open(cfe_devctx_t *ctx) +{ + idecommon_t *softc = ctx->dev_softc; + int res; + + if (softc->idecommon_deferprobe) { + res = idecommon_devprobe(softc,0); + if (res < 0) return res; + } + + return 0; +} + +/* ********************************************************************* + * idecommon_read(ctx,buffer) + * + * Process a CFE READ command for the IDE device. This is + * more complex than it looks, since CFE offsets are byte offsets + * and we may need to read partial sectors. + * + * Input parameters: + * ctx - device context + * buffer - buffer descriptor + * + * Return value: + * number of bytes read, or <0 if an error occured + ********************************************************************* */ + +int idecommon_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + idecommon_t *softc = ctx->dev_softc; + unsigned char *bptr; + int blen; + int numsec; + int res = 0; + int amtcopy; + uint64_t lba; + uint64_t offset; + unsigned char sector[MAX_SECTORSIZE]; + int sectorshift; + + sectorshift = idecommon_sectorshift(softc->idecommon_sectorsize); + + bptr = buffer->buf_ptr; + blen = buffer->buf_length; + offset = buffer->buf_offset; + numsec = (blen + softc->idecommon_sectorsize - 1) >> sectorshift; + + if (offset & (softc->idecommon_sectorsize-1)) { + lba = (offset >> sectorshift); + res = (*softc->idecommon_readfunc)(softc,lba,1,sector); + if (res < 0) goto out; + amtcopy = softc->idecommon_sectorsize - (offset & (softc->idecommon_sectorsize-1)); + if (amtcopy > blen) amtcopy = blen; + memcpy(bptr,§or[offset & (softc->idecommon_sectorsize-1)],amtcopy); + bptr += amtcopy; + offset += amtcopy; + blen -= amtcopy; + } + + while (blen >= softc->idecommon_sectorsize) { + lba = (offset >> sectorshift); + amtcopy = softc->idecommon_sectorsize; + res = (*softc->idecommon_readfunc)(softc,lba,1,bptr); + if (res < 0) goto out; + bptr += amtcopy; + offset += amtcopy; + blen -= amtcopy; + } + + if (blen) { + lba = (offset >> sectorshift); + res = (*softc->idecommon_readfunc)(softc,lba,1,sector); + if (res < 0) goto out; + amtcopy = blen; + memcpy(bptr,sector,amtcopy); + bptr += amtcopy; + offset += amtcopy; + blen -= amtcopy; + } + +out: + buffer->buf_retlen = bptr - buffer->buf_ptr; + + return res; +} + +/* ********************************************************************* + * idecommon_inpstat(ctx,inpstat) + * + * Test input status for the IDE disk. Disks are always ready + * to read. + * + * Input parameters: + * ctx - device context + * inpstat - input status structure + * + * Return value: + * 0 + ********************************************************************* */ + +int idecommon_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat) +{ + /* idecommon_t *softc = ctx->dev_softc; */ + + inpstat->inp_status = 1; + return 0; +} + +/* ********************************************************************* + * idecommon_write(ctx,buffer) + * + * Process a CFE WRITE command for the IDE device. If the write + * involves partial sectors, the affected sectors are read first + * and the changes are merged in. + * + * Input parameters: + * ctx - device context + * buffer - buffer descriptor + * + * Return value: + * number of bytes write, or <0 if an error occured + ********************************************************************* */ + +int idecommon_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + idecommon_t *softc = ctx->dev_softc; + unsigned char *bptr; + int blen; + int numsec; + int res = 0; + int amtcopy; + uint64_t offset; + uint64_t lba; + unsigned char sector[MAX_SECTORSIZE]; + int sectorshift; + + sectorshift = (softc->idecommon_sectorsize == 2048) ? 11 : 9; + + bptr = buffer->buf_ptr; + blen = buffer->buf_length; + offset = buffer->buf_offset; + numsec = (blen + softc->idecommon_sectorsize - 1) >> sectorshift; + + if (offset & (softc->idecommon_sectorsize-1)) { + lba = (offset >> sectorshift); + res = (*softc->idecommon_readfunc)(softc,lba,1,sector); + if (res < 0) goto out; + amtcopy = softc->idecommon_sectorsize - (offset & (softc->idecommon_sectorsize-1)); + if (amtcopy > blen) amtcopy = blen; + memcpy(§or[offset & (softc->idecommon_sectorsize-1)],bptr,amtcopy); + res = (*softc->idecommon_writefunc)(softc,lba,1,sector); + if (res < 0) goto out; + bptr += amtcopy; + offset += amtcopy; + blen -= amtcopy; + } + + while (blen >= softc->idecommon_sectorsize) { + amtcopy = softc->idecommon_sectorsize; + lba = (offset >> sectorshift); + res = (*softc->idecommon_writefunc)(softc,lba,1,bptr); + if (res < 0) goto out; + bptr += amtcopy; + offset += amtcopy; + blen -= amtcopy; + } + + if (blen) { + lba = (offset >> sectorshift); + res = (*softc->idecommon_readfunc)(softc,lba,1,sector); + if (res < 0) goto out; + amtcopy = blen; + memcpy(sector,bptr,amtcopy); + res = (*softc->idecommon_writefunc)(softc,lba,1,sector); + if (res < 0) goto out; + bptr += amtcopy; + offset += amtcopy; + blen -= amtcopy; + } + +out: + buffer->buf_retlen = bptr - buffer->buf_ptr; + + return res; +} + + +/* ********************************************************************* + * idecommon_ioctl(ctx,buffer) + * + * Process device I/O control requests for the IDE device. + * + * Input parameters: + * ctx - device context + * buffer - buffer descriptor + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int idecommon_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + idecommon_t *softc = ctx->dev_softc; + unsigned int *info = (unsigned int *) buffer->buf_ptr; + unsigned long long *linfo = (unsigned long long *) buffer->buf_ptr; + blockdev_info_t *devinfo; + + switch ((int)buffer->buf_ioctlcmd) { + case IOCTL_BLOCK_GETBLOCKSIZE: + *info = softc->idecommon_sectorsize; + break; + case IOCTL_BLOCK_GETTOTALBLOCKS: + *linfo = softc->idecommon_ttlsect; + break; + case IOCTL_BLOCK_GETDEVTYPE: + devinfo = (blockdev_info_t *) buffer->buf_ptr; + devinfo->blkdev_totalblocks = softc->idecommon_ttlsect; + devinfo->blkdev_blocksize = softc->idecommon_sectorsize; + devinfo->blkdev_devtype = (softc->idecommon_devtype == IDE_DEVTYPE_CDROM) ? + BLOCK_DEVTYPE_CDROM : BLOCK_DEVTYPE_DISK; + break; + default: + return -1; + } + + return 0; +} + +/* ********************************************************************* + * idecommon_close(ctx) + * + * Close the I/O device. + * + * Input parameters: + * ctx - device context + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +int idecommon_close(cfe_devctx_t *ctx) +{ + /* idecommon_t *softc = ctx->dev_softc; */ + + return 0; +} + + +/* ********************************************************************* + * idecommon_init(ide,devtype) + * + * Set up internal values based on the device type + * + * Input parameters: + * ide - IDE interface + * devtype - device type + * + * Return value: + * nothing + ********************************************************************* */ + +void idecommon_init(idecommon_t *ide,int devtype) +{ + + ide->idecommon_devtype = devtype; + + switch (ide->idecommon_devtype) { + case IDE_DEVTYPE_DISK: + ide->idecommon_atapi = FALSE; + ide->idecommon_sectorsize = DISK_SECTORSIZE; + break; + case IDE_DEVTYPE_CDROM: + ide->idecommon_atapi = TRUE; + ide->idecommon_sectorsize = CDROM_SECTORSIZE; + break; + case IDE_DEVTYPE_ATAPIDISK: + ide->idecommon_atapi = TRUE; + ide->idecommon_sectorsize = DISK_SECTORSIZE; + break; + default: + ide->idecommon_atapi = FALSE; + ide->idecommon_sectorsize = DISK_SECTORSIZE; + break; + } + + if (ide->idecommon_atapi) { + ide->idecommon_readfunc = idecommon_read_atapi; + ide->idecommon_writefunc = idecommon_write_atapi; + } + else { + ide->idecommon_readfunc = idecommon_read_lba; + ide->idecommon_writefunc = idecommon_write_lba; + } +} + +/* ********************************************************************* + * idecommon_attach(devdisp) + * + * Set up a cfe_devdisp structure that points at the idecommon + * structures. + * + * Input parameters: + * devdisp - cfe_devdisp_t structure + * + * Return value: + * nothing + ********************************************************************* */ +void idecommon_attach(cfe_devdisp_t *disp) +{ + disp->dev_open = idecommon_open; + disp->dev_read = idecommon_read; + disp->dev_inpstat = idecommon_inpstat; + disp->dev_write = idecommon_write; + disp->dev_ioctl = idecommon_ioctl; + disp->dev_close = idecommon_close; + disp->dev_poll = NULL; + disp->dev_reset = NULL; +} diff --git a/cfe/cfe/dev/dev_ide_pci.c b/cfe/cfe/dev/dev_ide_pci.c new file mode 100644 index 0000000..ecf4c2a --- /dev/null +++ b/cfe/cfe/dev/dev_ide_pci.c @@ -0,0 +1,358 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * PCI IDE disk driver File: dev_ide_pci.c + * + * This is a simple driver for IDE hard disks that are connected + * to PCI IDE controllers, such as a Promise UltraXX. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "lib_types.h" +#include "lib_malloc.h" +#include "lib_printf.h" +#include "lib_string.h" +#include "cfe_timer.h" +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_ioctl.h" + +#include "dev_ide_common.h" + +#include "dev_ide.h" + +#include "pcivar.h" +#include "pcireg.h" + +/* ********************************************************************* + * Macros + ********************************************************************* */ + +#define _BYTESWAP_ /* don't byteswap these disks */ + +#define OUTB(x,y) outb(x,y) +#define OUTW(x,y) outw(x,y) +#define INB(x) inb(x) +#define INW(x) inw(x) + +/* ********************************************************************* + * Forward declarations + ********************************************************************* */ + +extern void _wbflush(void); + +static void idedrv_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + +/* ********************************************************************* + * Device Dispatch + ********************************************************************* */ + +static cfe_devdisp_t idedrv_dispatch = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +const cfe_driver_t pciidedrv = { + "PCI IDE disk", + "ide", + CFE_DEV_DISK, + &idedrv_dispatch, + idedrv_probe +}; + +const cfe_driver_t pciatapidrv = { + "PCI ATAPI device", + "atapi", + CFE_DEV_DISK, + &idedrv_dispatch, + idedrv_probe +}; + +/* ********************************************************************* + * Supported PCI devices + ********************************************************************* */ + +#define DEVID(vid,pid) (((pid)<<16)|(vid)) + +static uint32_t pciidedrv_devlist[] = { + DEVID(0x105a,0x4d33), /* Promise Ultra33 */ + DEVID(0x1095,0x0649), /* CMD PCI0649 */ + DEVID(0x1095,0x0648), /* CMD PCI0648 */ + 0xFFFFFFFF +}; + + +/* ********************************************************************* + * Port I/O routines + * + * These routines are called back from the common code to do + * I/O cycles to the IDE disk. We provide routines for + * reading and writing bytes, words, and strings of words. + ********************************************************************* */ + +static uint8_t idedrv_inb(idecommon_dispatch_t *disp,uint32_t reg) +{ + return INB(reg+disp->baseaddr); +} + +static uint16_t idedrv_inw(idecommon_dispatch_t *disp,uint32_t reg) +{ + return INW(reg+disp->baseaddr); +} + +static void idedrv_ins(idecommon_dispatch_t *disp,uint32_t reg,uint8_t *buf,int len) +{ + uint16_t data; + + while (len > 0) { + data = INW(reg+disp->baseaddr); + +#ifdef _BYTESWAP_ + *buf++ = (data >> 8) & 0xFF; + *buf++ = (data & 0xFF); +#else + *buf++ = (data & 0xFF); + *buf++ = (data >> 8) & 0xFF; +#endif + len--; + len--; + } + +} + +static void idedrv_outb(idecommon_dispatch_t *disp,uint32_t reg,uint8_t val) +{ + OUTB(reg+disp->baseaddr,val); +} + +static void idedrv_outw(idecommon_dispatch_t *disp,uint32_t reg,uint16_t val) +{ + OUTW(reg+disp->baseaddr,val); +} + +static void idedrv_outs(idecommon_dispatch_t *disp,uint32_t reg,uint8_t *buf,int len) +{ + uint16_t data; + + while (len > 0) { +#ifdef _BYTESWAP_ + data = (uint16_t) buf[1] + ((uint16_t) buf[0] << 8); +#else + data = (uint16_t) buf[0] + ((uint16_t) buf[1] << 8); +#endif + + OUTW(reg+disp->baseaddr,data); + + buf++; + buf++; + len--; + len--; + } +} + + +/* ********************************************************************* + * pciidedrv_find(devid,list) + * + * Find a particular product ID on the list. Return >= 0 if + * the ID is valid. + * + * Input parameters: + * devid - product and device ID we have + * list - list of product and device IDs we're looking for + * + * Return value: + * index into table, or -1 if not found + ********************************************************************* */ +static int pciidedrv_find(uint32_t devid,uint32_t *list) +{ + int idx = 0; + + while (list[idx] != 0xFFFFFFFF) { + if (list[idx] == devid) return idx; + idx++; + } + + return -1; +} + + +/* ********************************************************************* + * idedrv_probe(drv,probe_a,probe_b,probe_ptr) + * + * Our probe routine. Attach an IDE device to the firmware. + * + * Input parameters: + * drv - driver structure + * probe_a - physical address of IDE registers + * probe_b - unit number + * probe_ptr - not used + * + * Return value: + * nothing + ********************************************************************* */ + +static void idedrv_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + idecommon_t *softc; + idecommon_dispatch_t *disp; + char descr[80]; + char unitstr[50]; + pcitag_t tag; + int index; + uint32_t devid; + uint32_t reg; + int res; + int unit; + cfe_driver_t *realdrv; + int attached = 0; + + /* + * probe_a is unused + * probe_b is unused + * probe_ptr is unused. + */ + + index = 0; + + for (;;) { + if (pci_find_class(PCI_CLASS_MASS_STORAGE,index,&tag) != 0) break; + index++; + devid = pci_conf_read(tag,PCI_ID_REG); + + if (pciidedrv_find(devid,pciidedrv_devlist) < 0) { + continue; + } + + + reg = pci_conf_read(tag,PCI_MAPREG(0)); + + if (PCI_MAPREG_TYPE(reg) != PCI_MAPREG_TYPE_IO) { + xprintf("Skipping this IDE device, we don't do memory mapped IDE yet\n"); + continue; + } + + reg &= ~PCI_MAPREG_TYPE_MASK; + + for (unit = 0; unit < 2; unit++) { + + /* + * If we've deliberately disabled probing of this + * device, then skip it. + */ + + if (IDE_PROBE_GET_TYPE(probe_b,unit) == IDE_DEVTYPE_NOPROBE) { + continue; + } + + softc = (idecommon_t *) KMALLOC(sizeof(idecommon_t),0); + disp = (idecommon_dispatch_t *) KMALLOC(sizeof(idecommon_dispatch_t),0); + + if (!softc || !disp) { + if (softc) KFREE(softc); + if (disp) KFREE(disp); + return; /* out of memory, stop here */ + } + + + softc->idecommon_addr = reg; + disp->ref = softc; + disp->baseaddr = softc->idecommon_addr; + softc->idecommon_deferprobe = 0; + softc->idecommon_dispatch = disp; + softc->idecommon_unit = unit; + + disp->outb = idedrv_outb; + disp->outw = idedrv_outw; + disp->outs = idedrv_outs; + + disp->inb = idedrv_inb; + disp->inw = idedrv_inw; + disp->ins = idedrv_ins; + + /* + * If we're autoprobing, do it now. Loop back if we have + * trouble finding the device. + * + * If not autoprobing, assume the device is there and set the + * common routines to double check later. + */ + + if (IDE_PROBE_GET_TYPE(probe_b,unit) == IDE_DEVTYPE_AUTO) { + res = idecommon_devprobe(softc,1); + if (res < 0) { + KFREE(softc); + KFREE(disp); + continue; + } + } + else { + idecommon_init(softc,IDE_PROBE_GET_TYPE(probe_b,unit)); + softc->idecommon_deferprobe = 1; + } + + xsprintf(descr,"%s unit %d at I/O %04X",drv->drv_description, + softc->idecommon_unit,softc->idecommon_addr); + xsprintf(unitstr,"%d",unit); + + realdrv = (cfe_driver_t *) (softc->idecommon_atapi ? &pciatapidrv : &pciidedrv); + + idecommon_attach(&idedrv_dispatch); + cfe_attach(realdrv,softc,unitstr,descr); + attached++; + } + + } + + xprintf("PCIIDE: %d controllers found\n",attached); +} + + diff --git a/cfe/cfe/dev/dev_newflash.c b/cfe/cfe/dev/dev_newflash.c new file mode 100644 index 0000000..d196e92 --- /dev/null +++ b/cfe/cfe/dev/dev_newflash.c @@ -0,0 +1,1428 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * "New" Flash device driver File: dev_newflash.c + * + * This driver supports various types of flash + * parts. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "lib_types.h" +#include "lib_malloc.h" +#include "lib_printf.h" +#include "lib_string.h" +#include "addrspace.h" +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_ioctl.h" +#include "cfe_error.h" + +#include "bsp_config.h" +#include "dev_newflash.h" + +/* ********************************************************************* + * Macros + ********************************************************************* */ + +#define GETCFIBYTE(arr,x) (arr[(x)*2]) + +#define min(a,b) ((a) < (b) ? (a) : (b)) +#define max(a,b) ((a) > (b) ? (a) : (b)) + +/* + * Get the address of the flash sector buffer from the + * config file. Addresses are PHYSICAL. + */ + +#ifndef CFG_FLASH_SECTOR_BUFFER_ADDR +#define CFG_FLASH_SECTOR_BUFFER_ADDR (100*1024*1024-128*1024) +#endif + +#ifndef CFG_FLASH_SECTOR_BUFFER_SIZE +#define CFG_FLASH_SECTOR_BUFFER_SIZE (128*1024) +#endif + + +/*#define _NEWFLASH_DEBUG_ */ + +/* ********************************************************************* + * Forward declarations + ********************************************************************* */ + + +static void flashdrv_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + + +static int flashdrv_open(cfe_devctx_t *ctx); +static int flashdrv_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int flashdrv_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); +static int flashdrv_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int flashdrv_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int flashdrv_close(cfe_devctx_t *ctx); + +/* ********************************************************************* + * Device dispatch + ********************************************************************* */ + +const static cfe_devdisp_t flashdrv_dispatch = { + flashdrv_open, + flashdrv_read, + flashdrv_inpstat, + flashdrv_write, + flashdrv_ioctl, + flashdrv_close, + NULL, + NULL +}; + +const cfe_driver_t newflashdrv = { + "New CFI flash", + "flash", + CFE_DEV_FLASH, + &flashdrv_dispatch, + flashdrv_probe +}; + + + +/* ********************************************************************* + * Externs + ********************************************************************* */ + +extern void *flashop_engine_ptr; +extern int flashop_engine_len; + +extern void _cfe_flushcache(int); + +static int flash_sector_query(flashdev_t *softc,flash_sector_t *sector); + +/* ********************************************************************* + * Globals + ********************************************************************* */ + +/* + * This is a pointer to a DRAM version of our flash subroutines. + * We make a global here so that it doesn't get copied multiple + * times for each flash we instantiate. + */ + +static int (*flashop_engine_ram)(flashinstr_t *prog) = NULL; +static uint8_t *flash_sector_buffer = NULL; + +/* ********************************************************************* + * FLASH_OP_BEGIN(softc) + * + * Reset the pointer to the flash operations so that we can + * begin filling in new instructions to execute + * + * Input parameters: + * softc - our softc. + * + * Return value: + * nothing + ********************************************************************* */ + +#define flash_op_begin(softc) softc->fd_iptr = 0; + +/* ********************************************************************* + * FLASH_OP_ADD(softc,op,dest,src,cnt) + * + * Add an instruction to the flashop table + * + * Input parameters: + * softc - our flash + * op,dest,src,cnt - data for the opcode + * + * Return value: + * nothing + ********************************************************************* */ + +static void flash_op_add(flashdev_t *softc,long base,long op,long dest,long src,long cnt) +{ + flashinstr_t *fi = &(softc->fd_inst[softc->fd_iptr]); + + fi->fi_op = op; + fi->fi_base = base; + fi->fi_dest = dest; + fi->fi_src = src; + fi->fi_cnt = cnt; + + softc->fd_iptr++; +} + + +/* ********************************************************************* + * FLASH_OP_EXECUTE(softc) + * + * Execute the stored "flash operations" + * + * Input parameters: + * softc - our flash + * + * Return value: + * 0 if ok, else # of failures (less than zero) + ********************************************************************* */ + +static int flash_op_execute(flashdev_t *softc) +{ + flash_op_add(softc,softc->fd_probe.flash_phys,FEOP_RETURN,0,0,0); + +#ifdef _NEWFLASH_DEBUG_ + if (1) { + int idx; + printf("---------------\nCalling engine @ %08X\n",flashop_engine_ram); + for (idx = 0; idx < softc->fd_iptr; idx++) { + printf("%2d %08X %08X %08X %08X\n", + softc->fd_inst[idx].fi_op, + softc->fd_inst[idx].fi_base, + softc->fd_inst[idx].fi_dest, + softc->fd_inst[idx].fi_src, + softc->fd_inst[idx].fi_cnt); + } + } +#endif + + /* + * If someone hooked the flashop engine, call the hook. + */ + if (softc->fd_probe.flash_engine_hook) { + return (*(softc->fd_probe.flash_engine_hook))(&(softc->fd_inst[0])); + } + + /* + * Otherwise, call the standard one. + */ + if (!flashop_engine_ram) return CFE_ERR_UNSUPPORTED; + return (*flashop_engine_ram)(&(softc->fd_inst[0])); +} + + +/* ********************************************************************* + * FLASH_SETUP_ENGINE() + * + * Set up the "flash engine", copying the routine to DRAM + * and flushing the cache so we can call it. + * + * Input parameters: + * nothing + * + * Return value: + * nothing + ********************************************************************* */ + +static void flash_setup_engine(void) +{ +#if ((CFG_RAMAPP) || (CFG_EMBEDDED_PIC)) + /* CFE is relocated, no need to copy flash engine to heap */ + flashop_engine_ram = (void *) flashop_engine_ptr; +#else + /* Copy flash engine to heap */ + uint32_t *dst,*src; + int idx; + + if (flashop_engine_ram) return; /* already done */ + + /* + * Allocate space for engine + */ + + flashop_engine_ram = (void *) KMALLOC(flashop_engine_len,0); + if (!flashop_engine_ram) return; + + /* + * Copy engine to RAM - do it 32-bits at a time to avoid + * a certain platform with broken byte reads (no, not the 1250) + */ + + dst = (uint32_t *) flashop_engine_ram; + src = (uint32_t *) flashop_engine_ptr; + for (idx = 0; idx < flashop_engine_len/sizeof(uint32_t); idx++) { + *dst++ = *src++; + } + + /* + * Flush the d-cache, invalidate the I-cache. + */ + + _cfe_flushcache(1); + _cfe_flushcache(2); +#endif +} + +/* ********************************************************************* + * FLASH_ERASE_RANGE(softc,range) + * + * Erase a range of sectors + * + * Input parameters: + * softc - our flash + * range - range structure + * + * Return value: + * 0 if ok + * else error + ********************************************************************* */ + +static int flash_erase_range(flashdev_t *softc,flash_range_t *range) +{ + flash_sector_t sector; + int res; + + if (softc->fd_probe.flash_type != FLASH_TYPE_FLASH) { + return CFE_ERR_UNSUPPORTED; + } + + if (range->range_base+range->range_length > softc->fd_probe.flash_size) { + return CFE_ERR_INV_PARAM; + } + + res = 0; + + sector.flash_sector_idx = 0; + + for (;;) { + res = flash_sector_query(softc,§or); + if (res != 0) break; + if (sector.flash_sector_status == FLASH_SECTOR_INVALID) { + break; + } + + if ((sector.flash_sector_offset >= range->range_base) && + (sector.flash_sector_offset < + (range->range_base+range->range_length-1))) { + + flash_op_begin(softc); + flash_op_add(softc,softc->fd_probe.flash_phys, + softc->fd_erasefunc, + sector.flash_sector_offset, + 0,0); + res = flash_op_execute(softc); + + if (res != 0) break; + } + sector.flash_sector_idx++; + } + + return res; + +} + +/* ********************************************************************* + * FLASH_ERASE_ALL(softc) + * + * Erase the entire flash device, except the NVRAM area, + * sector-by-sector. + * + * Input parameters: + * softc - our flash + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int flash_erase_all(flashdev_t *softc) +{ + flash_range_t range; + + range.range_base = 0; + range.range_length = softc->fd_probe.flash_size * + softc->fd_probe.flash_nchips; + + return flash_erase_range(softc,&range); +} + + +/* ********************************************************************* + * flash_range_intersection(sector,inrange,outrange) + * + * Compute the intersection between a flash range and a + * sector. + * + * Input parameters: + * sector - sector to examine + * range - range we are checking + * outrange - where to put resulting intersection range + * + * Return value: + * 1 - range is an entire sector + * 0 - range is a partial sector + * -1 - range has no intersection + ********************************************************************* */ + +static int flash_range_intersection(flash_sector_t *sector, + flash_range_t *inrange, + flash_range_t *outrange) +{ + int start,end; + + /* + * compute the start and end pointers + */ + + start = (int) (max(sector->flash_sector_offset, + inrange->range_base)); + + end = (int) (min((sector->flash_sector_offset+sector->flash_sector_size), + (inrange->range_base+inrange->range_length))); + + /* + * if the end is in the right place wrt the start, + * there is an intersection. + */ + + if (end > start) { + outrange->range_base = (unsigned int) start; + outrange->range_length = (unsigned int) (end-start); + + if ((sector->flash_sector_offset == outrange->range_base) && + (sector->flash_sector_size == outrange->range_length)) { + return 1; /* instersection: entire sector */ + } + else { + return 0; /* intersection: partial sector */ + } + } + else { + outrange->range_base = (unsigned int) start; + outrange->range_length = 0; + return -1; /* intersection: none */ + } +} + + +/* ********************************************************************* + * FLASH_SECTOR_QUERY(softc,sector) + * + * Query the sector information about a particular sector. You can + * call this iteratively to find out about all of the sectors. + * + * Input parameters: + * softc - our flash info + * sector - structure to receive sector information + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int flash_sector_query(flashdev_t *softc,flash_sector_t *sector) +{ + int idx; + int nblks; + int blksiz; + unsigned int offset; + int whichchip; + int secidx; + int curblk; + + if (softc->fd_probe.flash_type != FLASH_TYPE_FLASH) { + return CFE_ERR_UNSUPPORTED; + } + + if (softc->fd_probe.flash_nsectors == 0) { + return CFE_ERR_UNSUPPORTED; + } + + /* Figure out which chip */ + whichchip = sector->flash_sector_idx / softc->fd_ttlsect; + if (whichchip >= softc->fd_probe.flash_nchips) { + sector->flash_sector_status = FLASH_SECTOR_INVALID; + return 0; + } + + /* Within that chip, get sector info */ + offset = softc->fd_probe.flash_size * whichchip; + secidx = sector->flash_sector_idx % softc->fd_ttlsect; + curblk = 0; + + for (idx = 0; idx < softc->fd_probe.flash_nsectors; idx++) { + nblks = FLASH_SECTOR_NBLKS(softc->fd_probe.flash_sectors[idx]); + blksiz = FLASH_SECTOR_SIZE(softc->fd_probe.flash_sectors[idx]); + if (secidx < curblk+nblks) { + sector->flash_sector_status = FLASH_SECTOR_OK; + sector->flash_sector_offset = + offset + (secidx-curblk)*blksiz; + sector->flash_sector_size = blksiz; + break; + } + + offset += (nblks)*blksiz; + curblk += nblks; + } + + + if (idx == softc->fd_probe.flash_nsectors) { + sector->flash_sector_status = FLASH_SECTOR_INVALID; + } + + return 0; +} + + +/* ********************************************************************* + * FLASH_SET_CMDSET(softc,cmdset,bus16,dev16) + * + * Set the command-set that we'll honor for this flash. + * + * Input parameters: + * softc - our flash + * cmdset - FLASH_CFI_CMDSET_xxx + * bus16 - true if bus is 16 bits wide + * dev16 - true if device supports 16-bit operation + * + * So: bus16 && dev16 -> 16-bit commands + * !bus16 && dev16 -> 8-bit commands to 16-bit flash with BYTE# + * !bus16 && !dev16 -> 8-bit commands + * + * Return value: + * nothing + ********************************************************************* */ + +static void flash_set_cmdset(flashdev_t *softc,int cmdset,int bus16,int dev16) +{ + switch (cmdset) { +#if (FLASH_DRIVERS & FLASH_DRIVER_INTEL) + case FLASH_CFI_CMDSET_INTEL_ECS: + case FLASH_CFI_CMDSET_INTEL_STD: + if (bus16) { + softc->fd_erasefunc = FEOP_INTEL_ERASE16; + softc->fd_pgmfunc = FEOP_INTEL_PGM16; + softc->fd_readfunc = FEOP_READ16; + } + else { + softc->fd_erasefunc = FEOP_INTEL_ERASE8; + softc->fd_pgmfunc = FEOP_INTEL_PGM8; + softc->fd_readfunc = FEOP_READ8; + } + break; +#endif +#if (FLASH_DRIVERS & FLASH_DRIVER_AMD) + case FLASH_CFI_CMDSET_AMD_STD: + case FLASH_CFI_CMDSET_AMD_ECS: + if (!bus16 && !dev16) { /* 8-bit bus, 8-bit flash */ + softc->fd_erasefunc = FEOP_AMD_ERASE8; + softc->fd_pgmfunc = FEOP_AMD_PGM8; + softc->fd_readfunc = FEOP_READ8; + } + else if (bus16 && dev16) { /* 16-bit bus, 16-bit flash */ + softc->fd_erasefunc = FEOP_AMD_ERASE16; + softc->fd_pgmfunc = FEOP_AMD_PGM16; + softc->fd_readfunc = FEOP_READ16; + } + else { /* 8-bit bus, 16-bit flash w/BYTE# */ + softc->fd_erasefunc = FEOP_AMD_ERASE16B; + softc->fd_pgmfunc = FEOP_AMD_PGM16B; + softc->fd_readfunc = FEOP_READ8; + } + break; +#endif + default: + /* we don't understand the command set - treat it like ROM */ + softc->fd_erasefunc = FEOP_RETURN; + softc->fd_pgmfunc = FEOP_RETURN; + softc->fd_readfunc = bus16 ? FEOP_READ16 : FEOP_READ8; + break; + } +} + +#if (FLASH_DRIVERS & FLASH_DRIVER_CFI) +/* ********************************************************************* + * FLASH_CFI_PROBE(softc) + * + * Try to do a CFI query on this device. If we find the m + * magic signature, extract some useful information from the + * query structure. + * + * Input parameters: + * softc - out flash + * + * Return value: + * 0 if successful, <0 if error + ********************************************************************* */ +static int flash_cfi_probe(flashdev_t *softc) +{ + uint8_t cfidata[FLASH_MAX_CFIDATA]; + unsigned int cmdset; + unsigned int devif; + int bus16 = 0; + int dev16 = 0; + int idx; + int found = 0; + int regcnt; + int nblks; + int blksiz; + int insane = 0; + uint8_t insaner = 0; + + if (softc->fd_probe.flash_flags & FLASH_FLG_BUS16) { + bus16 = 1; + } + + /* + * Do a CFI query (16-bit) + */ + + idx = FEOP_CFIQUERY8; + if (softc->fd_probe.flash_flags & FLASH_FLG_DEV16) { + idx = (softc->fd_probe.flash_flags & FLASH_FLG_BUS16) ? + FEOP_CFIQUERY16 : FEOP_CFIQUERY16B; + } + + flash_op_begin(softc); + flash_op_add(softc,softc->fd_probe.flash_phys, + idx,(long)cfidata,0,FLASH_MAX_CFIDATA); + flash_op_execute(softc); + + /* + * Look for signature. + */ + + + if ((GETCFIBYTE(cfidata,FLASH_CFI_SIGNATURE+0) == 'Q') && + (GETCFIBYTE(cfidata,FLASH_CFI_SIGNATURE+1) == 'R') && + (GETCFIBYTE(cfidata,FLASH_CFI_SIGNATURE+2) == 'Y')) { + found = 1; + } + + + /* + * No CFI, bail. First, set the command set to an invalid + * value so that we'll use default routines to read but not do programming + */ + + if (!found) { + flash_set_cmdset(softc,-1,bus16,dev16); + return CFE_ERR_UNSUPPORTED; + } + + softc->fd_probe.flash_type = FLASH_TYPE_FLASH; + + /* + * Gather info from flash + */ + + cmdset = ((unsigned int) (GETCFIBYTE(cfidata,FLASH_CFI_COMMAND_SET))) + + (((unsigned int) (GETCFIBYTE(cfidata,FLASH_CFI_COMMAND_SET+1))) << 8); + + + devif = ((unsigned int) (GETCFIBYTE(cfidata,FLASH_CFI_DEVICE_INTERFACE))) + + (((unsigned int) (GETCFIBYTE(cfidata,FLASH_CFI_DEVICE_INTERFACE+1))) << 8); + + + softc->fd_probe.flash_size = (1 << (unsigned int)(GETCFIBYTE(cfidata,FLASH_CFI_DEVICE_SIZE))); + + /* + * It's a 16-bit device if it is either always 16 bits or can be. + * we'll use "bus16" to decide if the BYTE# pin was strapped + */ + + dev16 = 0; + if ((devif == FLASH_CFI_DEVIF_X16) || (devif == FLASH_CFI_DEVIF_X8X16)) dev16 = 1; + + regcnt = GETCFIBYTE(cfidata,FLASH_CFI_REGION_COUNT); + + softc->fd_probe.flash_nsectors = regcnt; + +#if 1 + /* + * Hiss! Some AMD top-boot flash parts have broken CFI tables - they are backwards! + * Do some extra probing to find it. + */ + + if (cmdset == FLASH_CFI_CMDSET_AMD_STD) { + idx = FEOP_AMD_DEVCODE8; + if (softc->fd_probe.flash_flags & FLASH_FLG_DEV16) { + idx = (softc->fd_probe.flash_flags & FLASH_FLG_BUS16) ? + FEOP_AMD_DEVCODE16 : FEOP_AMD_DEVCODE16B; + } + + flash_op_begin(softc); + flash_op_add(softc,softc->fd_probe.flash_phys, + idx,(long)&insaner,0,0); + flash_op_execute(softc); +#ifdef _NEWFLASH_DEBUG_ + xprintf("Insaner = 0x%x\n", insaner); +#endif /* _NEWFLASH_DEBUG_ */ + if(((insaner & 0xFF) == 0xC4)||((insaner & 0xFF) == 0xF6)){ + insane = 1; +#ifdef _NEWFLASH_DEBUG_ + xprintf("Warning: insane AMD part, backwards CFI table!\n"); +#endif /* _NEWFLASH_DEBUG_ */ + } + } +#else + insane = insaner = 1; +#endif /* 0 */ + + + for (idx = 0; idx < regcnt; idx++) { + nblks = ((int)GETCFIBYTE(cfidata,FLASH_CFI_REGION_TABLE+0+idx*4) + + (int)(GETCFIBYTE(cfidata,FLASH_CFI_REGION_TABLE+1+idx*4)<<8)) + 1; + blksiz = ((int)GETCFIBYTE(cfidata,FLASH_CFI_REGION_TABLE+2+idx*4) + + (int)(GETCFIBYTE(cfidata,FLASH_CFI_REGION_TABLE+3+idx*4)<<8)) * 256; + + if (insane) { + /* Insane */ + softc->fd_probe.flash_sectors[((regcnt-1)-idx)] = + FLASH_SECTOR_RANGE(nblks,blksiz); + } + else { + /* Sane */ + softc->fd_probe.flash_sectors[idx] = + FLASH_SECTOR_RANGE(nblks,blksiz); + } + } + + /* + * Set the command set we're going to use. + */ + + flash_set_cmdset(softc,cmdset,bus16,dev16); + + return 0; + +} + + + +/* ********************************************************************* + * FLASH_DO_PROBE(softc) + * + * Probe to see if we're ROM or RAM. If ROM, see if we're flash. + * If flash, do CFI query. + * + * Input parameters: + * softc - our structure + * + * Return value: + * FLASH_TYPE_xxx + ********************************************************************* */ +static int flash_do_probe(flashdev_t *softc) +{ + uint8_t test_byte0,test_byte1; + uint8_t save0,save1; + volatile uint8_t *ptr; + + /* + * flash_do_probe is called before we open the device, so we + * need to allocate space for instructions so the flashop + * engine will work. + */ + + softc->fd_inst = KMALLOC(FLASH_MAX_INST*sizeof(flashinstr_t),0); + if (!softc->fd_inst) return FLASH_TYPE_ROM; + + /* + * Attempt to read/write byte zero. If it is changable, + * this is SRAM (or maybe a ROM emulator with the write line hooked up) + */ + + ptr = (volatile uint8_t *) UNCADDR(softc->fd_probe.flash_phys); + save0 = *ptr; /* save old value */ + save1 = *(ptr+1); /* save old value */ + + test_byte0 = (save0 != 0x88) ? 0x88 : 0x89; + test_byte1 = (save1 != 0x99) ? 0x99 : 0x91; + + *(ptr) = test_byte0; + *(ptr+1) = test_byte1; + + if ((*ptr == test_byte0) && (*(ptr+1) == test_byte1)) { + softc->fd_probe.flash_type = FLASH_TYPE_SRAM; + + /*Only write back saved values if it's RAM*/ + *(ptr) = save0; + *(ptr+1) = save1; + +#ifdef _NEWFLASH_DEBUG_ + xprintf("Flash type SRAM\n"); +#endif + + } + else { + softc->fd_probe.flash_type = FLASH_TYPE_ROM; + +#ifdef _NEWFLASH_DEBUG_ + xprintf("Flash type ROM\n"); +#endif + + } + + /* + * If we thought it was ROM, try doing a CFI query + * to see if it was flash. This check is kind of kludgey + * but should work. + */ + + if (softc->fd_probe.flash_type == FLASH_TYPE_ROM) { + flash_cfi_probe(softc); + } + + + KFREE(softc->fd_inst); + softc->fd_inst = NULL; + + return softc->fd_probe.flash_type; +} + +#endif /* (FLASH_DRIVERS & FLASH_DRIVER_CFI) */ + + +/* ********************************************************************* + * flash_do_parts(probe,parts) + * + * Partition the flash into the sizes specified. We use + * the sizes in the table to generate a table of {offset,size} + * pairs that eventually become partitions. + * + * The only thing magical about this is that size "0" means + * "fill to max" and that partitions beyond the "0" are aligned + * to the top of the flash. Therefore, if you had a 4MB + * flash and listed four partitions, 512K, 0, 512K, 512K, + * then there would be a 2.5M partition in the middle and two + * 512K partitions at the top. + * + * Input parameters: + * probe - flash probe data (user-supplied table) + * parts - our partition table (output) + * + * Return value: + * nothing + ********************************************************************* */ + +static void flash_do_parts(flashdev_t *softc) +{ + int idx; + int middlepart = -1; + int lobound = 0; + newflash_probe_t *probe = &(softc->fd_probe); + flashpart_t *parts = &(softc->fd_parts[0]); + int hibound = probe->flash_size*probe->flash_nchips; + + for (idx = 0; idx < probe->flash_nparts; idx++) { + if (probe->flash_parts[idx].fp_size == 0) { + middlepart = idx; + break; + } + parts[idx].fp_offset = lobound; + parts[idx].fp_size = probe->flash_parts[idx].fp_size; + lobound += probe->flash_parts[idx].fp_size; + } + + if (idx != probe->flash_nparts) { + for (idx = probe->flash_nparts - 1; idx > middlepart; + idx--) { + parts[idx].fp_size = probe->flash_parts[idx].fp_size; + hibound -= probe->flash_parts[idx].fp_size; + parts[idx].fp_offset = hibound; + } + } + + if (middlepart != -1) { + parts[middlepart].fp_offset = lobound; + parts[middlepart].fp_size = hibound - lobound; + } + +#ifdef _NEWFLASH_DEBUG_ + printf("Partition information:\n"); + for (idx = 0; idx < probe->flash_nparts;idx++) { + printf("#%02d %08X -> %08X (%d)\n",idx, + parts[idx].fp_offset,parts[idx].fp_offset+parts[idx].fp_size-1, + parts[idx].fp_size); + } +#endif +} + + +/* ********************************************************************* + * flashdrv_allocbuf(dev) + * + * Allocate sector buffer for flash programming. Use a global + * buffer for all devices. + * + * Input parameters: + * dev - our device + * + * Return value: + * nothing + ********************************************************************* */ +static void flashdrv_allocbuf(flashdev_t *softc) +{ + if (!flash_sector_buffer) { +#if CFG_FLASH_ALLOC_SECTOR_BUFFER + flash_sector_buffer = KMALLOC(CFG_FLASH_SECTOR_BUFFER_SIZE,0); + if (!flash_sector_buffer) { + printf("FLASH: Could not allocate sector buffer, using default\n"); + flash_sector_buffer = (uint8_t *) KERNADDR(CFG_FLASH_SECTOR_BUFFER_SIZE); + } +#else + flash_sector_buffer = (uint8_t *) KERNADDR(CFG_FLASH_SECTOR_BUFFER_ADDR); +#endif + } + + softc->fd_sectorbuffer = flash_sector_buffer; +} + +/* ********************************************************************* + * flashdrv_probe(drv,probe_a,probe_b,probe_ptr) + * + * Device probe routine. Attach the flash device to + * CFE's device table. + * + * Input parameters: + * drv - driver descriptor + * probe_a - physical address of flash + * probe_b - size of flash (bytes) + * probe_ptr - unused + * + * Return value: + * nothing + ********************************************************************* */ + +static void flashdrv_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + flashdev_t *softc; + newflash_probe_t *probe; + int idx; + char descr[80]; + static int flashidx = 0; + char *x; + + /* + * First thing to do is configure the flashop engine + * if not already done. Basically we copy a bit of + * position-independent code into the DRAM. + */ + + flash_setup_engine(); + + /* + * Now, on with the probing. + */ + + probe = (newflash_probe_t *) probe_ptr; + + /* + * probe_a is the flash base address + * probe_b is the size of the flash + * probe_ptr is unused. + */ + + softc = (flashdev_t *) KMALLOC(sizeof(flashdev_t),0); + if (softc) { + memset(softc,0,sizeof(flashdev_t)); + + flashdrv_allocbuf(softc); + + if (probe) { + /* + * Passed probe structure, do fancy stuff + */ + memcpy(&(softc->fd_probe),probe,sizeof(newflash_probe_t)); + if (softc->fd_probe.flash_nchips == 0) { + softc->fd_probe.flash_nchips = 1; + } + } + else { + /* Didn't pass probe structure, do the compatible thing */ + softc->fd_probe.flash_phys = probe_a; + softc->fd_probe.flash_size = (probe_b & FLASH_SIZE_MASK); + softc->fd_probe.flash_flags = (probe_b & FLASH_FLG_MASK); + softc->fd_probe.flash_nchips = 1; + } + + if (softc->fd_probe.flash_flags & FLASH_FLG_MANUAL) { + /* Manual probing, just set the command set. */ + flash_set_cmdset(softc,softc->fd_probe.flash_cmdset, + ((softc->fd_probe.flash_flags & FLASH_FLG_BUS16) ? 1 : 0), + ((softc->fd_probe.flash_flags & FLASH_FLG_DEV16) ? 1 : 0)); + } + else { + /* Do automatic probing */ +#if (FLASH_DRIVERS & FLASH_DRIVER_CFI) + flash_do_probe(softc); +#else + return; /* No automatic probing, bail! */ +#endif + } + + /* Remember total size of all devices */ + softc->fd_ttlsize = softc->fd_probe.flash_nchips * softc->fd_probe.flash_size; + + /* Set description */ + x = descr; + x += xsprintf(x,"%s at %08X size %uKB",drv->drv_description, + softc->fd_probe.flash_phys, + softc->fd_ttlsize/1024); + if (softc->fd_probe.flash_nchips > 1) { + xsprintf(x," (%d chips)",softc->fd_probe.flash_nchips); + } + + /* + * If flash is not partitioned, just instantiate one + * device. Otherwise, instantiate multiple flashes + * to cover the entire device. + */ + + if (softc->fd_probe.flash_nparts == 0) { + softc->fd_parts[0].fp_dev = softc; + softc->fd_parts[0].fp_offset = 0; + softc->fd_parts[0].fp_size = softc->fd_probe.flash_size; + cfe_attach(drv,&(softc->fd_parts[0]),NULL,descr); + } + else { + /* + * Partition flash into chunks + */ + flash_do_parts(softc); + + /* + * Instantiate devices for each piece + */ + + for (idx = 0; idx < softc->fd_probe.flash_nparts; idx++) { + char name[32]; + char *nptr; + + xsprintf(descr,"%s at %08X offset %08X size %uKB", + drv->drv_description, + softc->fd_probe.flash_phys, + softc->fd_parts[idx].fp_offset, + (softc->fd_parts[idx].fp_size+1023)/1024); + + softc->fd_parts[idx].fp_dev = softc; + if (softc->fd_probe.flash_parts[idx].fp_name == NULL) { + sprintf(name,"%d",idx); + nptr = name; + } + else { + nptr = softc->fd_probe.flash_parts[idx].fp_name; + } + cfe_attach_idx(drv, + flashidx, + &(softc->fd_parts[idx]), + nptr, + descr); + } + } + + flashidx++; + + /* Count total sectors on the device */ + + softc->fd_ttlsect = 0; + for (idx = 0; idx < softc->fd_probe.flash_nsectors; idx++) { + softc->fd_ttlsect += FLASH_SECTOR_NBLKS(softc->fd_probe.flash_sectors[idx]); + } + + } + +} + + +/* ********************************************************************* + * flashdrv_open(ctx) + * + * Called when the flash device is opened. + * + * Input parameters: + * ctx - device context + * + * Return value: + * 0 if ok else error code + ********************************************************************* */ + +static int flashdrv_open(cfe_devctx_t *ctx) +{ + flashpart_t *part = ctx->dev_softc; + flashdev_t *softc = part->fp_dev; + int ttlsect = softc->fd_ttlsect; + + /* + * Calculate number of flashop instructions we'll need at most. + * This will be two for each sector plus two more for the first + * and last sectors, plus two extra + */ + + ttlsect = (ttlsect * 2 * softc->fd_probe.flash_nchips) + 6; + + /* + * Allocate memory for instructions. + */ + +#ifdef _NEWFLASH_DEBUG_ + printf("%s: allocating %d instructions\n",cfe_device_name(ctx),ttlsect); +#endif + + softc->fd_inst = KMALLOC(ttlsect*sizeof(flashinstr_t),0); + if (!softc->fd_inst) return CFE_ERR_NOMEM; + + return 0; +} + + +/* ********************************************************************* + * flashdrv_read(ctx,buffer) + * + * Read data from the flash device. The flash device is + * considered to be like a disk (you need to specify the offset). + * + * Input parameters: + * ctx - device context + * buffer - buffer descriptor + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +static int flashdrv_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + flashpart_t *part = ctx->dev_softc; + flashdev_t *softc = part->fp_dev; + int blen; + int offset; + + blen = buffer->buf_length; + offset = (long)buffer->buf_offset; + + if ((offset + blen) > part->fp_size) { + blen = part->fp_size - offset; + } + + offset += part->fp_offset; + + if (blen > 0) { + flash_op_begin(softc); + flash_op_add(softc,softc->fd_probe.flash_phys, + softc->fd_readfunc,(long)buffer->buf_ptr,offset,blen); + flash_op_execute(softc); + } + + buffer->buf_retlen = blen; + + return 0; +} + +/* ********************************************************************* + * flashdrv_inpstat(ctx,inpstat) + * + * Return "input status". For flash devices, we always return true. + * + * Input parameters: + * ctx - device context + * inpstat - input status structure + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +static int flashdrv_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat) +{ + inpstat->inp_status = 1; + return 0; +} + + +/* ********************************************************************* + * flashdrv_write(ctx,buffer) + * + * Write data to the flash device. The flash device is + * considered to be like a disk (you need to specify the offset). + * + * Input parameters: + * ctx - device context + * buffer - buffer descriptor + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +static int flashdrv_write2(cfe_devctx_t *ctx,iocb_buffer_t *buffer,int reboot) +{ + flashpart_t *part = ctx->dev_softc; + flashdev_t *softc = part->fp_dev; + int blen; + int res; + int offset; + int whichchip; + long chipbase; + flash_range_t outrange; + flash_range_t inrange; + flash_sector_t sector; + + blen = buffer->buf_length; + offset = (long)buffer->buf_offset; + + /* Compute range within physical flash */ + + if ((offset + blen) > part->fp_size) { + blen = part->fp_size - offset; + } + + offset += part->fp_offset; + + /* Handle case of writing nothing */ + + if (blen == 0) { + buffer->buf_retlen = blen; + return (buffer->buf_length == blen) ? 0 : CFE_ERR_IOERR; + } + + /* now, offset/blen forms the range we want to write to. */ + + inrange.range_base = offset; + inrange.range_length = blen; + + sector.flash_sector_idx = 0; + + flash_op_begin(softc); + + for (;;) { + res = flash_sector_query(softc,§or); + if (res != 0) break; + if (sector.flash_sector_status == FLASH_SECTOR_INVALID) { + break; + } + + whichchip = sector.flash_sector_idx / softc->fd_ttlsect; + chipbase = softc->fd_probe.flash_phys + + (long) (whichchip * softc->fd_probe.flash_size); + + res = flash_range_intersection(§or,&inrange,&outrange); + + switch (res) { + case 1: /* Erease/program entire sector */ + flash_op_add(softc,chipbase, + softc->fd_erasefunc, + sector.flash_sector_offset, + 0,0); + flash_op_add(softc,chipbase, + softc->fd_pgmfunc, + outrange.range_base, + ((long)buffer->buf_ptr)+(outrange.range_base-inrange.range_base), + outrange.range_length); + break; + + case 0: /* Erase/reprogram partial sector */ + /* Save old sector */ + flash_op_add(softc,chipbase, + softc->fd_readfunc, + (long)(softc->fd_sectorbuffer), + sector.flash_sector_offset, + sector.flash_sector_size); + /* Copy in new stuff */ + flash_op_add(softc,chipbase, + FEOP_MEMCPY, + ((long)(softc->fd_sectorbuffer))+(outrange.range_base-sector.flash_sector_offset), + ((long)buffer->buf_ptr)+(outrange.range_base-inrange.range_base), + outrange.range_length); + /* Erase sector */ + flash_op_add(softc,chipbase, + softc->fd_erasefunc, + sector.flash_sector_offset, + 0,0); + /* Program sector */ + flash_op_add(softc,chipbase, + softc->fd_pgmfunc, + sector.flash_sector_offset, + (long)(softc->fd_sectorbuffer), + sector.flash_sector_size); + break; + + case -1: /* No intersection */ + break; + } + + sector.flash_sector_idx++; + + } + + if (reboot) { + flash_op_add(softc,softc->fd_probe.flash_phys,FEOP_REBOOT,0,0,0); + } + + res = flash_op_execute(softc); + + buffer->buf_retlen = blen; + + return (res == 0) ? 0 : CFE_ERR_IOERR; +} + +static int flashdrv_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + return flashdrv_write2(ctx,buffer,0); +} + + +/* ********************************************************************* + * flashdrv_ioctl(ctx,buffer) + * + * Handle special IOCTL functions for the flash. Flash devices + * support NVRAM information, sector and chip erase, and a + * special IOCTL for updating the running copy of CFE. + * + * Input parameters: + * ctx - device context + * buffer - descriptor for IOCTL parameters + * + * Return value: + * 0 if ok else error + ********************************************************************* */ +static int flashdrv_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + flashpart_t *part = ctx->dev_softc; + flashdev_t *softc = part->fp_dev; + nvram_info_t *nvinfo; + flash_info_t *info; + flash_range_t range; + int offset; + + switch ((int)buffer->buf_ioctlcmd) { + case IOCTL_NVRAM_GETINFO: + /* + * We only support NVRAM on flashes that have been partitioned + * into at least two partitions. Every partition supports + * being an NVRAM in that case, but we'll only attach one + * of them to the environment subsystem. + */ + if (softc->fd_probe.flash_nparts <= 1) { + return CFE_ERR_UNSUPPORTED; + } + nvinfo = (nvram_info_t *) buffer->buf_ptr; + if (buffer->buf_length != sizeof(nvram_info_t)) return CFE_ERR_INV_PARAM; + + nvinfo->nvram_offset = 0; + nvinfo->nvram_size = part->fp_size; + nvinfo->nvram_eraseflg = 1; + buffer->buf_retlen = sizeof(nvram_info_t); + return 0; + break; + + case IOCTL_FLASH_ERASE_SECTOR: + offset = (int) buffer->buf_offset; + offset += part->fp_offset; + if (offset >= softc->fd_probe.flash_size) return -1; + + flash_op_begin(softc); + flash_op_add(softc, + softc->fd_probe.flash_phys, + softc->fd_erasefunc, + offset, + 0,0); + flash_op_execute(softc); + return 0; + + case IOCTL_FLASH_ERASE_ALL: + offset = (int) buffer->buf_offset; + if (offset != 0) return -1; + flash_erase_all(softc); + return 0; + + case IOCTL_FLASH_WRITE_ALL: + /* Write file and reboot */ + flashdrv_write2(ctx,buffer,1); + return -1; /* should not return */ + + case IOCTL_FLASH_GETINFO: + info = (flash_info_t *) buffer->buf_ptr; + info->flash_base = softc->fd_probe.flash_phys; + info->flash_size = softc->fd_probe.flash_size; + info->flash_type = softc->fd_probe.flash_type; + info->flash_flags = FLASH_FLAG_NOERASE; + return 0; + + case IOCTL_FLASH_GETSECTORS: + return flash_sector_query(softc,(flash_sector_t *) buffer->buf_ptr); + + case IOCTL_FLASH_ERASE_RANGE: + memcpy(&range,buffer->buf_ptr,sizeof(flash_range_t)); + range.range_base += part->fp_offset; + if (range.range_length > part->fp_size) { + range.range_length = part->fp_size; + } + return flash_erase_range(softc,&range); + + default: + /* Call hook if present. */ + if (softc->fd_probe.flash_ioctl_hook) { + return (*(softc->fd_probe.flash_ioctl_hook))(ctx,buffer); + } + return -1; + } + + return -1; +} + + +/* ********************************************************************* + * flashdrv_close(ctx) + * + * Close the flash device. + * + * Input parameters: + * ctx - device context + * + * Return value: + * 0 + ********************************************************************* */ +static int flashdrv_close(cfe_devctx_t *ctx) +{ + flashpart_t *part = ctx->dev_softc; + flashdev_t *softc = part->fp_dev; + + if (softc->fd_inst) { + KFREE(softc->fd_inst); + } + + softc->fd_inst = NULL; + + /* XXX Invalidate the cache ?!?! */ + + return 0; +} + + diff --git a/cfe/cfe/dev/dev_ns16550.c b/cfe/cfe/dev/dev_ns16550.c new file mode 100644 index 0000000..a70eecf --- /dev/null +++ b/cfe/cfe/dev/dev_ns16550.c @@ -0,0 +1,255 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * NS16550 UART driver File: dev_ns16550.c + * + * This is a console device driver for an NS16550 UART, either + * on-board or as a PCI-device. In the case of a PCI device, + * our probe routine is called from the PCI probe code + * over in dev_ns16550_pci.c + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "lib_types.h" +#include "lib_malloc.h" +#include "lib_printf.h" +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_ioctl.h" + +#include "lib_physio.h" + +#include "bsp_config.h" + +#include "ns16550.h" + +#define WRITECSR(p,v) phys_write8((p),(v)) +#define READCSR(p) phys_read8((p)) + +static int ns16550_uart_open(cfe_devctx_t *ctx); +static int ns16550_uart_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int ns16550_uart_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); +static int ns16550_uart_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int ns16550_uart_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int ns16550_uart_close(cfe_devctx_t *ctx); + +void ns16550_uart_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + + +const cfe_devdisp_t ns16550_uart_dispatch = { + ns16550_uart_open, + ns16550_uart_read, + ns16550_uart_inpstat, + ns16550_uart_write, + ns16550_uart_ioctl, + ns16550_uart_close, + NULL, + NULL +}; + +const cfe_driver_t ns16550_uart = { + "NS16550 UART", + "uart", + CFE_DEV_SERIAL, + &ns16550_uart_dispatch, + ns16550_uart_probe +}; + +typedef struct ns16550_uart_s { + physaddr_t uart_base; + int uart_flowcontrol; + int uart_speed; +} ns16550_uart_t; + + +/* + * NS16550-compatible UART. + * probe_a: physical address of UART + */ + +void ns16550_uart_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + ns16550_uart_t *softc; + char descr[80]; + + softc = (ns16550_uart_t *) KMALLOC(sizeof(ns16550_uart_t),0); + if (softc) { + softc->uart_base = probe_a; + softc->uart_speed = CFG_SERIAL_BAUD_RATE; + softc->uart_flowcontrol = SERIAL_FLOW_NONE; + xsprintf(descr, "%s at 0x%X", drv->drv_description, (uint32_t)probe_a); + + cfe_attach(drv, softc, NULL, descr); + } +} + +#define DELAY(n) delay(n) +extern int32_t _getticks(void); +static void delay(int ticks) +{ + int32_t t; + + t = _getticks() + ticks; + while (_getticks() < t) + ; /* NULL LOOP */ +} + +static void ns16550_uart_setflow(ns16550_uart_t *softc) +{ + /* noop for now */ +} + + +static int ns16550_uart_open(cfe_devctx_t *ctx) +{ + ns16550_uart_t *softc = ctx->dev_softc; + int baudrate = CFG_SERIAL_BAUD_RATE; + unsigned int brtc; + + brtc = BRTC(baudrate); + + WRITECSR(softc->uart_base+R_UART_CFCR,CFCR_DLAB); + WRITECSR(softc->uart_base+R_UART_DATA,brtc & 0xFF); + WRITECSR(softc->uart_base+R_UART_IER,brtc>>8); + WRITECSR(softc->uart_base+R_UART_CFCR,CFCR_8BITS); + WRITECSR(softc->uart_base+R_UART_MCR,MCR_DTR | MCR_RTS | MCR_IENABLE); + WRITECSR(softc->uart_base+R_UART_IER,0); + + WRITECSR(softc->uart_base+R_UART_FIFO,FIFO_ENABLE); + DELAY(100); + WRITECSR(softc->uart_base+R_UART_FIFO, + FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_1); + DELAY(100); + + if ((READCSR(softc->uart_base+R_UART_IIR) & IIR_FIFO_MASK) != IIR_FIFO_MASK) { + WRITECSR(softc->uart_base+R_UART_FIFO,0); + } + ns16550_uart_setflow(softc); + + return 0; +} + +static int ns16550_uart_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + ns16550_uart_t *softc = ctx->dev_softc; + unsigned char *bptr; + int blen; + + bptr = buffer->buf_ptr; + blen = buffer->buf_length; + + while ((blen > 0) && (READCSR(softc->uart_base+R_UART_LSR) & LSR_RXRDY)) { + *bptr++ = (READCSR(softc->uart_base+R_UART_DATA) & 0xFF); + blen--; + } + + buffer->buf_retlen = buffer->buf_length - blen; + return 0; +} + +static int ns16550_uart_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat) +{ + ns16550_uart_t *softc = ctx->dev_softc; + + inpstat->inp_status = (READCSR(softc->uart_base+R_UART_LSR) & LSR_RXRDY) ? 1 : 0; + + return 0; +} + +static int ns16550_uart_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + ns16550_uart_t *softc = ctx->dev_softc; + unsigned char *bptr; + int blen; + + bptr = buffer->buf_ptr; + blen = buffer->buf_length; + while ((blen > 0) && (READCSR(softc->uart_base+R_UART_LSR) & LSR_TXRDY)) { + WRITECSR(softc->uart_base+R_UART_DATA, *bptr++); + blen--; + } + + buffer->buf_retlen = buffer->buf_length - blen; + return 0; +} + +static int ns16550_uart_ioctl(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + ns16550_uart_t *softc = ctx->dev_softc; + + unsigned int *info = (unsigned int *) buffer->buf_ptr; + + switch ((int)buffer->buf_ioctlcmd) { + case IOCTL_SERIAL_GETSPEED: + *info = softc->uart_speed; + break; + case IOCTL_SERIAL_SETSPEED: + softc->uart_speed = *info; + /* NYI */ + break; + case IOCTL_SERIAL_GETFLOW: + *info = softc->uart_flowcontrol; + break; + case IOCTL_SERIAL_SETFLOW: + softc->uart_flowcontrol = *info; + ns16550_uart_setflow(softc); + break; + default: + return -1; + } + + return 0; +} + +static int ns16550_uart_close(cfe_devctx_t *ctx) +{ + ns16550_uart_t *softc = ctx->dev_softc; + + WRITECSR(softc->uart_base+R_UART_MCR,0); + + return 0; +} + + diff --git a/cfe/cfe/dev/dev_ns16550_pci.c b/cfe/cfe/dev/dev_ns16550_pci.c new file mode 100644 index 0000000..021f34c --- /dev/null +++ b/cfe/cfe/dev/dev_ns16550_pci.c @@ -0,0 +1,114 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * NS16550 UART driver (PCI) File: dev_ns16550_pci.c + * + * This is a console device driver for a PCI NS16550 UART + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "lib_types.h" +#include "lib_malloc.h" +#include "lib_printf.h" +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_ioctl.h" + +#include "pcivar.h" +#include "pcireg.h" + + +/* Probe routine for real UART driver */ +extern void ns16550_uart_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + +/* Probe routine for this UART driver. */ +static void ns16550pci_uart_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + +/* We just glom onto the dispatch table in the real driver */ +extern const cfe_devdisp_t ns16550_uart_dispatch; + +const cfe_driver_t ns16550pci_uart = { + "PCI NS16550 UART", + "uart", + CFE_DEV_SERIAL, + &ns16550_uart_dispatch, + ns16550pci_uart_probe +}; + + +static void ns16550pci_uart_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + phys_addr_t pa; + pcitag_t tag; + int index = 0; + + /* + * NS16550-compatible UART on the PCI bus + * probe_a, probe_b and probe_ptr are unused. + */ + + /* + * This is for a SIIG card. Should probably do a little + * vendor ID table like we did for the IDE driver so + * we can spport other cards. + */ + + for (;;) { + + if (pci_find_device(0x131f,0x2000,index,&tag) != 0) { + break; + } + + pci_map_io(tag, PCI_MAPREG(0), PCI_MATCH_BYTES, &pa); + xprintf("NS16550PCI: I/O mapped registers start at %08X", (uint32_t)pa); + + ns16550_uart_probe(drv,pa,0,NULL); + + index++; + } +} + diff --git a/cfe/cfe/dev/dev_null.c b/cfe/cfe/dev/dev_null.c new file mode 100644 index 0000000..d995919 --- /dev/null +++ b/cfe/cfe/dev/dev_null.c @@ -0,0 +1,142 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * Null console device File: dev_null.c + * + * This is a null console device, useful for console-less + * operation, or when using chip simulators. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + + +#include "lib_types.h" +#include "lib_malloc.h" +#include "lib_printf.h" +#include "cfe_iocb.h" +#include "cfe_device.h" + +static void nulldrv_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + + +static int nulldrv_open(cfe_devctx_t *ctx); +static int nulldrv_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int nulldrv_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); +static int nulldrv_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int nulldrv_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int nulldrv_close(cfe_devctx_t *ctx); + +const static cfe_devdisp_t nulldrv_dispatch = { + nulldrv_open, + nulldrv_read, + nulldrv_inpstat, + nulldrv_write, + nulldrv_ioctl, + nulldrv_close, + NULL, + NULL +}; + +const cfe_driver_t nulldrv = { + "Null console device", + "null", + CFE_DEV_SERIAL, + &nulldrv_dispatch, + nulldrv_probe +}; + + +static void nulldrv_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + cfe_attach(drv,NULL,NULL,drv->drv_description); +} + + +static int nulldrv_open(cfe_devctx_t *ctx) +{ +/* nulldrv_t *softc = ctx->dev_softc; */ + + return 0; +} + +static int nulldrv_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ +/* nulldrv_t *softc = ctx->dev_softc; */ + + buffer->buf_retlen = buffer->buf_length; + return 0; +} + +static int nulldrv_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat) +{ +/* nulldrv_t *softc = ctx->dev_softc; */ + + inpstat->inp_status = 0; + + return 0; +} + +static int nulldrv_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ +/* nulldrv_t *softc = ctx->dev_softc; */ + + buffer->buf_retlen = buffer->buf_length; + return 0; +} + +static int nulldrv_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ +/* nulldrv_t *softc = ctx->dev_softc;*/ + + return -1; +} + +static int nulldrv_close(cfe_devctx_t *ctx) +{ +/* nulldrv_t *softc = ctx->dev_softc; */ + + return 0; +} + + diff --git a/cfe/cfe/dev/dev_promice.c b/cfe/cfe/dev/dev_promice.c new file mode 100644 index 0000000..fe79086 --- /dev/null +++ b/cfe/cfe/dev/dev_promice.c @@ -0,0 +1,402 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * PromICE console device File: dev_promice.c + * + * This device driver supports Grammar Engine's PromICE AI2 + * serial communications options. With this console, you can + * communicate with the firmware using only uncached reads in the + * boot ROM space. See Grammar Engine's PromICE manuals + * for more information at http://www.gei.com + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +/* + * Example PromICE initialization file: + * + * ----------------------- + * output=com1 + * pponly=lpt1 + * rom=27040 + * word=8 + * file=cfe.srec + * ailoc 7FC00,9600 + * ----------------------- + * + * The offset specified in the 'ailoc' line must be the location where you + * will configure the AI2 serial port. In this example, the ROM is assumed + * to be 512KB, and the AI2 serial port is at 511KB, or offset 0x7FC00. + * This area is filled with 0xCC to detect AI2's initialization sequence + * properly (see the PromICE manual). You should connect your + * PromICE's serial port up to the PC and run a terminal emulator on it. + * The parallel port will be used for downloading data to the PromICE. + * + * If you have connected the write line to the PromICE, then you can + * define the _AIDIRT_ symbol to increase performance. + */ + + + +#include "lib_types.h" +#include "lib_malloc.h" +#include "lib_printf.h" +#include "lib_string.h" +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "addrspace.h" + +#define _AIDIRT_ + +/* ********************************************************************* + * Prototypes + ********************************************************************* */ + +static void promice_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + + +static int promice_open(cfe_devctx_t *ctx); +static int promice_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int promice_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); +static int promice_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int promice_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int promice_close(cfe_devctx_t *ctx); + +/* ********************************************************************* + * Device dispatch table + ********************************************************************* */ + +const static cfe_devdisp_t promice_dispatch = { + promice_open, + promice_read, + promice_inpstat, + promice_write, + promice_ioctl, + promice_close, + NULL, + NULL +}; + +/* ********************************************************************* + * Device descriptor + ********************************************************************* */ + +const cfe_driver_t promice_uart = { + "PromICE AI2 Serial Port", + "promice", + CFE_DEV_SERIAL, + &promice_dispatch, + promice_probe +}; + +/* ********************************************************************* + * Local constants and structures + ********************************************************************* */ + +/* + * If your PromICE is connected to a 32-bit host (emulating four + * flash ROMs) and the SB1250 is set to boot from that host, define + * the PROMICE_32BITS symbol to make sure the AI2 interface is + * configured correctly. + */ + +/*#define PROMICE_32BITS*/ + +#ifdef PROMICE_32BITS +#define WORDSIZE 4 +#define WORDTYPE uint32_t +#else +#define WORDSIZE 1 +#define WORDTYPE uint8_t +#endif + + +#define ZERO_OFFSET (0) +#define ONE_OFFSET (1) +#define DATA_OFFSET (2) +#define STATUS_OFFSET (3) + +#define TDA 0x01 /* Target data available */ +#define HDA 0x02 /* Host data available */ +#define OVR 0x04 /* Host data overflow */ + +typedef struct promice_s { + unsigned long ai2_addr; + unsigned int ai2_wordsize; + volatile WORDTYPE *zero; + volatile WORDTYPE *one; + volatile WORDTYPE *data; + volatile WORDTYPE *status; +} promice_t; + + +/* ********************************************************************* + * promice_probe(drv,probe_a,probe_b,probe_ptr) + * + * Device "Probe" routine. This routine creates the soft + * context for the device and calls the attach routine. + * + * Input parameters: + * drv - driver structure + * probe_a,probe_b,probe_ptr - probe args + * + * Return value: + * nothing + ********************************************************************* */ + + +static void promice_probe(cfe_driver_t *drv, + unsigned long probe_a, + unsigned long probe_b, + void *probe_ptr) +{ + promice_t *softc; + char descr[80]; + + /* + * probe_a is the address in the ROM of the AI2 interface + * on the PromICE. + * probe_b is the word size (1,2,4) + */ + + softc = (promice_t *) KMALLOC(sizeof(promice_t),0); + if (softc) { + softc->ai2_addr = probe_a; + if (probe_b) softc->ai2_wordsize = probe_b; + else softc->ai2_wordsize = WORDSIZE; + xsprintf(descr,"%s at 0x%X",drv->drv_description,probe_a); + cfe_attach(drv,softc,NULL,descr); + } +} + + + +/* ********************************************************************* + * promice_open(ctx) + * + * Open the device + * + * Input parameters: + * ctx - device context + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ +static int promice_open(cfe_devctx_t *ctx) +{ + promice_t *softc = ctx->dev_softc; + uint8_t dummy; + + softc->zero = (volatile WORDTYPE *) + UNCADDR(softc->ai2_addr + (ZERO_OFFSET*softc->ai2_wordsize)); + softc->one = (volatile WORDTYPE *) + UNCADDR(softc->ai2_addr + (ONE_OFFSET*softc->ai2_wordsize)); + softc->data = (volatile WORDTYPE *) + UNCADDR(softc->ai2_addr + (DATA_OFFSET*softc->ai2_wordsize)); + softc->status = (volatile WORDTYPE *) + UNCADDR(softc->ai2_addr + (STATUS_OFFSET*softc->ai2_wordsize)); + + /* + * Wait for bit 3 to clear so we know the interface is ready. + */ + + while (*(softc->status) == 0xCC) ; /* NULL LOOP */ + + /* + * a dummy read is required to clear out the interface. + */ + + dummy = *(softc->data); + + return 0; +} + +/* ********************************************************************* + * promice_read(ctx,buffer) + * + * Read data from the device + * + * Input parameters: + * ctx - device context + * buffer - I/O buffer descriptor + * + * Return value: + * number of bytes transferred, or <0 if error + ********************************************************************* */ +static int promice_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + promice_t *softc = ctx->dev_softc; + unsigned char *bptr; + int blen; + + bptr = buffer->buf_ptr; + blen = buffer->buf_length; + + while ((blen > 0) && (*(softc->status) & HDA)) { + *bptr++ = *(softc->data); + blen--; + } + + buffer->buf_retlen = buffer->buf_length - blen; + return 0; +} + +/* ********************************************************************* + * promice_inpstat(ctx,inpstat) + * + * Determine if read data is available + * + * Input parameters: + * ctx - device context + * inpstat - input status structure + * + * Return value: + * 0 + ********************************************************************* */ + +static int promice_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat) +{ + promice_t *softc = ctx->dev_softc; + + inpstat->inp_status = (*(softc->status) & HDA) ? 1 : 0; + + return 0; +} + +/* ********************************************************************* + * promice_write(ctx,buffer) + * + * Write data to the device + * + * Input parameters: + * ctx - device context + * buffer - I/O buffer descriptor + * + * Return value: + * number of bytes transferred, or <0 if error + ********************************************************************* */ +static int promice_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + promice_t *softc = ctx->dev_softc; + unsigned char *bptr; + int blen; + uint8_t data; +#ifndef _AIDIRT_ + uint8_t dummy; + int count; +#endif + + bptr = buffer->buf_ptr; + blen = buffer->buf_length; + + + /* + * The AI2 interface requires you to transmit characters + * one bit at a time. First a '1' start bit, + * then 8 data bits (lsb first) then another '1' stop bit. + * + * Just reference the right memory location to transmit a bit. + */ + + while ((blen > 0) && !(*(softc->status) & TDA)) { + +#ifdef _AIDIRT_ + data = *bptr++; + *(softc->zero) = data; +#else + dummy = *(softc->one); /* send start bit */ + + data = *bptr++; + + for (count = 0; count < 8; count++) { + if (data & 1) dummy = *(softc->one); + else dummy = *(softc->zero); + data >>= 1; /* shift in next bit */ + } + + dummy = *(softc->one); /* send stop bit */ +#endif + + blen--; + } + + buffer->buf_retlen = buffer->buf_length - blen; + return 0; +} + +/* ********************************************************************* + * promice_ioctl(ctx,buffer) + * + * Do I/O control operations + * + * Input parameters: + * ctx - device context + * buffer - I/O control args + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int promice_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + return -1; +} + +/* ********************************************************************* + * promice_close(ctx) + * + * Close the device. + * + * Input parameters: + * ctx - device context + * + * Return value: + * 0 + ********************************************************************* */ + +static int promice_close(cfe_devctx_t *ctx) +{ + return 0; +} + + diff --git a/cfe/cfe/dev/dev_sp1011.c b/cfe/cfe/dev/dev_sp1011.c new file mode 100644 index 0000000..8e0d16b --- /dev/null +++ b/cfe/cfe/dev/dev_sp1011.c @@ -0,0 +1,151 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * SP1011 (Sturgeon) Support File: dev_sp1011.c + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + +#include "lib_types.h" + +#include "pcireg.h" +#include "pcivar.h" + +extern int eoi_implemented; + +void sturgeon_setup (pcitag_t tag, pci_flags_t flags); + + +/* Sturgeon LDT-to-PCI bridge (LPB) specific definitions. */ + +#define PCI_VENDOR_API 0x14d9 +#define PCI_PRODUCT_API_STURGEON 0x0010 + +/* Sturgeon specific registers */ +#define LPB_READ_CTRL_REG 0x60 +#define LPB_READ_CTRL_MASK 0xffffff + +#define LPB_READ_CTRL_PREF_EN (1 << 0) +#define LPB_READ_CTRL_RD_PREF_EN (1 << 1) +#define LPB_READ_CTRL_MULT_PREF_SHIFT 2 +#define LPB_READ_CTRL_MULT_PREF_MASK (7 << LPB_READ_CTRL_MULT_PREF_SHIFT) +#define LPB_READ_CTRL_LINE_PREF_SHIFT 5 +#define LPB_READ_CTRL_LINE_PREF_MASK (7 << LPB_READ_CTRL_LINE_PREF_SHIFT) +#define LPB_READ_CTRL_DEL_REQ_SHIFT 8 +#define LPB_READ_CTRL_DEL_REQ_MASK (3 << LPB_READ_CTRL_DEL_REQ_SHIFT) + +#define LPB_INT_CTRL_BASE 0xa0 + +#define LPB_INT_CTRL_ENABLE (1 << 15) +#define LPB_INT_CTRL_DESTMODE (1 << 14) +#define LPB_INT_CTRL_DEST_SHIFT 6 +#define LPB_INT_CTRL_DEST_MASK (0xff << LPB_INT_CTRL_DEST_SHIFT) +#define LPB_INT_CTRL_MSGTYPE_SHIFT 4 +#define LPB_INT_CTRL_MSGTYPE_MASK (0x3 << LPB_INT_CTRL_MSGTYPE_SHIFT) +#define LPB_INT_CTRL_POLARITY (1 << 3) +#define LPB_INT_CTRL_TRIGGERMODE (1 << 2) +#define LPB_INT_CTRL_VECTOR_SHIFT 0 +#define LPB_INT_CTRL_VECTOR_MASK (0x3 << 0) + +#define LPB_INT_BLOCK1_REG 0xc4 + +void +sturgeon_setup (pcitag_t tag, pci_flags_t flags) +{ + int bus, device, function; + int secondary; + struct pci_bus *pb; + unsigned offset; + pcireg_t t, ctrl, intmap; + + pci_break_tag(tag, &bus, &device, &function); + + secondary = (pci_conf_read(tag, PPB_BUSINFO_REG) >> 8) & 0xff; + pb = &_pci_bus[secondary]; + + /* set up READ CONTROL register for selected prefetch option */ + ctrl = pci_conf_read(tag, LPB_READ_CTRL_REG) & ~LPB_READ_CTRL_MASK; + if ((flags & PCI_FLG_LDT_PREFETCH) != 0) { + /* Prefetch enable: all cycle types, 2 delayed requests, + 4 lines of fetch ahead for MemRdMult */ + ctrl |= (LPB_READ_CTRL_PREF_EN + | LPB_READ_CTRL_RD_PREF_EN + | (3 << LPB_READ_CTRL_MULT_PREF_SHIFT) + | (1 << LPB_READ_CTRL_DEL_REQ_SHIFT)); + } else { + /* No prefetch */ + ctrl |= 0; + } + pci_conf_write(tag, LPB_READ_CTRL_REG, ctrl); + + /* It's apparently not possible for software to distinguish + the CSWARM's debug Sturgeon (which has floating interrupt + inputs), so we route interrupts only if there are secondary + devices. */ + if (pb->ndev > 0) { + /* Setup interrupt mapping for Block 1: + Enabled, Dest=Logical (CPU 0 + CPU 1), Type=Fixed */ + intmap = (LPB_INT_CTRL_ENABLE | + LPB_INT_CTRL_DESTMODE | /* Logical */ + (0x3 << LPB_INT_CTRL_DEST_SHIFT) | /* CPU 0+1 */ + (0x0 << LPB_INT_CTRL_MSGTYPE_SHIFT)); /* Fixed */ + if (eoi_implemented) { + /* Passes >=2 have working EOI. Trigger=Level */ + intmap |= LPB_INT_CTRL_TRIGGERMODE; /* Level */ + } else { + /* Pass 1 lacks working EOI. Trigger=Edge */ + intmap |= 0; /* Edge */ + } + + offset = pb->inta_shift % 4; + t = (intmap + offset); + offset = (offset+1) % 4; + t |= (intmap + offset) << 16; + pci_conf_write(tag, LPB_INT_CTRL_BASE + 8, t); + + offset = (offset+1) % 4; + t = (intmap + offset); + offset = (offset+1) % 4; + t |= (intmap + offset) << 16; + pci_conf_write(tag, LPB_INT_CTRL_BASE + 12, t); + + t = pci_conf_read(tag, LPB_INT_BLOCK1_REG); + t &= 0xffffff00; + t |= (0x40 | (56 >> 2)); + pci_conf_write(tag, LPB_INT_BLOCK1_REG, t); + } +} diff --git a/cfe/cfe/dev/dev_tulip.c b/cfe/cfe/dev/dev_tulip.c new file mode 100644 index 0000000..dec6b56 --- /dev/null +++ b/cfe/cfe/dev/dev_tulip.c @@ -0,0 +1,2985 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * DC21x4x Ethernet Driver File: dev_tulip.c + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + +#include "sbmips.h" + +#ifndef _SB_MAKE64 +#define _SB_MAKE64(x) ((uint64_t)(x)) +#endif +#ifndef _SB_MAKEMASK1 +#define _SB_MAKEMASK1(n) (_SB_MAKE64(1) << _SB_MAKE64(n)) +#endif + +#include "lib_types.h" +#include "lib_hssubr.h" +#include "lib_malloc.h" +#include "lib_string.h" +#define blockcopy memcpy +#include "lib_printf.h" +#include "lib_queue.h" + +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_ioctl.h" +#include "cfe_timer.h" +#include "cfe_error.h" +#include "cfe_irq.h" + +#include "pcivar.h" +#include "pcireg.h" + +#include "dc21143.h" +#include "mii.h" + +/* This is a driver for specific configurations of the DC21040, DC21041, + DC21140A and DC21143, not a generic Tulip driver. The prefix + "tulip_" is used to indicate generic Tulip functions, while + "dc21040_", "dc21041_", "dc21140_" or "dc21143_" indicates functions + specific to a chip variant. + + The 21041 driver assumes a 10BT HD interface, since autonegotiation + is known to be broken in the early revisons of that chip. Example + cards come from DEC and SMC. Essentially the same driver is used + for 21040 cards. + + The 21140 driver assumes that the PHY uses a standard MII interface + for both 100BT and 10BT. Example cards come from DEC (National DP83840 + plus Twister PHY) and Netgear (Level One PHY). + + Some early 21140 boards are exceptions and use SYM plus SRL + with different PHY chips for 10 and 100 (limited support). + + The 21143 driver assumes by default that the PHY uses the SYM ("5 + wire") interface for 100BT with pass-through for 10BT. Example + cards come from DEC (MicroLinear ML6694 PHY) and Znyx (QS6611 or + Kendin KS8761 PHY). It also supports an MII interface for + recognized adapters. An example card comes from Adaptec (National + DP83840A and Twister PHY). There is no support for AUI interfaces. + + This SB1250 version takes advantage of DMA coherence and uses + "preserve bit lanes" addresses for all accesses that cross the + ZBbus-PCI bridge. */ + +#ifndef TULIP_DEBUG +#define TULIP_DEBUG 0 +#endif + +#ifndef TULIP_TUNE +#define TULIP_TUNE 1 +#endif + +/* Set IPOLL to drive processing through the pseudo-interrupt + dispatcher. Set XPOLL to drive processing by an external polling + agent. Setting both is ok. */ + +#ifndef IPOLL +#define IPOLL 0 +#endif +#ifndef XPOLL +#define XPOLL 1 +#endif + +#define ENET_ADDR_LEN 6 /* size of an ethernet address */ +#define MAX_ETHER_PACK 1518 /* max size of a packet */ +#define CRC_SIZE 4 /* size of CRC field */ + +/* Packet buffers. For tulip, the packet must be aligned to a 32-bit + word boundary, and we would like it aligned to a cache line + boundary for performance. */ + +#define CACHE_ALIGN 32 + +#if __long64 +typedef struct eth_pkt_s { + queue_t next; /* 16 */ + uint8_t *buffer; /* 8 */ + uint32_t flags; /* 4 */ + int32_t length; /* 4 */ + uint8_t data[MAX_ETHER_PACK]; +} eth_pkt_t; +#else +typedef struct eth_pkt_s { + queue_t next; /* 8 */ + uint8_t *buffer; /* 4 */ + uint32_t flags; /* 4 */ + int32_t length; /* 4 */ + uint32_t unused[3]; /* 12 */ + uint8_t data[MAX_ETHER_PACK]; +} eth_pkt_t; +#endif + +#define ETH_PKTBUF_LINES ((sizeof(eth_pkt_t) + (CACHE_ALIGN-1))/CACHE_ALIGN) +#define ETH_PKTBUF_SIZE (ETH_PKTBUF_LINES*CACHE_ALIGN) +#define ETH_PKTBUF_OFFSET (offsetof(eth_pkt_t, data)) + +#define ETH_PKT_BASE(data) ((eth_pkt_t *)((data) - ETH_PKTBUF_OFFSET)) + +/* packet flags */ +#define ETH_TX_SETUP 1 /* assumes Perfect Filtering format */ + +static void +show_packet(char c, eth_pkt_t *pkt) +{ + int i; + int n = (pkt->length < 32 ? pkt->length : 32); + + xprintf("%c[%4d]:", c, pkt->length); + for (i = 0; i < n; i++) { + if (i % 4 == 0) + xprintf(" "); + xprintf("%02x", pkt->buffer[i]); + } + xprintf("\n"); +} + + +/* Descriptor structures */ + +typedef struct rx_dscr { + uint32_t rxd_flags; + uint32_t rxd_bufsize; + pci_addr_t rxd_bufaddr1; + pci_addr_t rxd_bufaddr2; +} rx_dscr; + +typedef struct tx_dscr { + uint32_t txd_flags; + uint32_t txd_bufsize; + pci_addr_t txd_bufaddr1; + pci_addr_t txd_bufaddr2; +} tx_dscr; + +/* CAM structure */ + +typedef union { + struct { + uint32_t physical[CAM_PERFECT_ENTRIES][3]; + } p; + struct { + uint32_t hash[32]; + uint32_t mbz[7]; + uint32_t physical[3]; + } h; +} tulip_cam; + + +/* Driver data structures */ + +typedef enum { + eth_state_uninit, + eth_state_setup, + eth_state_off, + eth_state_on, + eth_state_broken +} eth_state_t; + +#define ETH_PKTPOOL_SIZE 32 +#define ETH_PKT_SIZE MAX_ETHER_PACK + +typedef struct tulip_softc { + uint32_t membase; + uint8_t irq; /* interrupt mapping (used if IPOLL) */ + pcitag_t tag; /* tag for configuration registers */ + + uint8_t hwaddr[ENET_ADDR_LEN]; + uint16_t device; /* chip device code */ + uint8_t revision; /* chip revision and step (Table 3-7) */ + + /* current state */ + eth_state_t state; + + /* These fields are the chip startup values. */ +// uint16_t media; /* media type */ + uint32_t opmode; /* operating mode */ + uint32_t intmask; /* interrupt mask */ + uint32_t gpdata; /* output bits for csr15 (21143) */ + + /* These fields are set before calling dc21x4x_hwinit */ + int linkspeed; /* encodings from cfe_ioctl */ + int loopback; + + /* Packet free list */ + queue_t freelist; + uint8_t *pktpool; + queue_t rxqueue; + + /* The descriptor tables */ + uint8_t *rxdscrmem; /* receive descriptors */ + uint8_t *txdscrmem; /* transmit descriptors */ + + /* These fields keep track of where we are in tx/rx processing */ + volatile rx_dscr *rxdscr_start; /* beginning of ring */ + volatile rx_dscr *rxdscr_end; /* end of ring */ + volatile rx_dscr *rxdscr_remove; /* next one we expect tulip to use */ + volatile rx_dscr *rxdscr_add; /* next place to put a buffer */ + int rxdscr_onring; + + volatile tx_dscr *txdscr_start; /* beginning of ring */ + volatile tx_dscr *txdscr_end; /* end of ring */ + volatile tx_dscr *txdscr_remove; /* next one we will use for tx */ + volatile tx_dscr *txdscr_add; /* next place to put a buffer */ + + cfe_devctx_t *devctx; + + /* These fields describe the PHY */ + enum {SRL, MII, SYM} phy_type; + int mii_addr; + + /* Statistics */ + uint32_t inpkts; + uint32_t outpkts; + uint32_t interrupts; + uint32_t rx_interrupts; + uint32_t tx_interrupts; + uint32_t bus_errors; +} tulip_softc; + + +/* Entry to and exit from critical sections (currently relative to + interrupts only, not SMP) */ + +#if CFG_INTERRUPTS +#define CS_ENTER(sc) cfe_disable_irq(sc->irq) +#define CS_EXIT(sc) cfe_enable_irq(sc->irq) +#else +#define CS_ENTER(sc) ((void)0) +#define CS_EXIT(sc) ((void)0) +#endif + + +/* Driver parameterization */ + +#define MAXRXDSCR 32 +#define MAXTXDSCR 32 +#define MINRXRING 8 + +#define MEDIA_UNKNOWN 0 +#define MEDIA_AUI 1 +#define MEDIA_BNC 2 +#define MEDIA_UTP_FULL_DUPLEX 3 +#define MEDIA_UTP_NO_LINK_TEST 4 +#define MEDIA_UTP 5 + +/* Prototypes */ + +static void tulip_ether_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + + +/* Address mapping macros */ + +/* Note that PTR_TO_PHYS only works with 32-bit addresses, but then + so does the Tulip. */ +#define PTR_TO_PHYS(x) (K0_TO_PHYS((uintptr_t)(x))) +#define PHYS_TO_PTR(a) ((uint8_t *)PHYS_TO_K0(a)) + +/* All mappings through the PCI host bridge use match bits mode. */ +#define PHYS_TO_PCI(a) ((uint32_t) (a) | 0x20000000) +#define PCI_TO_PHYS(a) ((uint32_t) (a) & 0x1FFFFFFF) + +#define PCI_TO_PTR(a) (PHYS_TO_PTR(PCI_TO_PHYS(a))) +#define PTR_TO_PCI(x) (PHYS_TO_PCI(PTR_TO_PHYS(x))) + +#if __long64 +#define READCSR(sc,csr) \ + (*((volatile uint32_t *) (PHYS_TO_XKSEG_UNCACHED((sc)->membase + (csr))))) + +#define WRITECSR(sc,csr,val) \ + (*((volatile uint32_t *) \ + (PHYS_TO_XKSEG_UNCACHED((sc)->membase + (csr)))) = (val)) + +#else +#define READCSR(sc,csr) \ + (hs_read32(PHYS_TO_XKSEG_UNCACHED((sc)->membase + (csr)))) + +#define WRITECSR(sc,csr,val) \ + (hs_write32(PHYS_TO_XKSEG_UNCACHED((sc)->membase + (csr)), (val))) + +#endif + +#define RESET_ADAPTER(sc) \ + { \ + WRITECSR((sc), R_CSR_BUSMODE, M_CSR0_SWRESET); \ + cfe_sleep(CFE_HZ/10); \ + } + + +/* Debugging */ + +static void +dumpstat(tulip_softc *sc) +{ + xprintf("-- CSR 5 = %08X CSR 6 = %08x\n", + READCSR(sc, R_CSR_STATUS), READCSR(sc, R_CSR_OPMODE)); +} + +static void +dumpcsrs(tulip_softc *sc) +{ + int idx; + + xprintf("-------------\n"); + for (idx = 0; idx < 16; idx++) { + xprintf("CSR %2d = %08X\n", idx, READCSR(sc, idx*8)); + } + xprintf("-------------\n"); +} + + +/* Packet management */ + +/* ********************************************************************* + * ETH_ALLOC_PKT(sc) + * + * Allocate a packet from the free list. + * + * Input parameters: + * sc - eth structure + * + * Return value: + * pointer to packet structure, or NULL if none available + ********************************************************************* */ +static eth_pkt_t * +eth_alloc_pkt(tulip_softc *sc) +{ + eth_pkt_t *pkt; + + CS_ENTER(sc); + pkt = (eth_pkt_t *) q_deqnext(&sc->freelist); + CS_EXIT(sc); + if (!pkt) return NULL; + + pkt->buffer = pkt->data; + pkt->length = ETH_PKT_SIZE; + pkt->flags = 0; + + return pkt; +} + + +/* ********************************************************************* + * ETH_FREE_PKT(sc,pkt) + * + * Return a packet to the free list + * + * Input parameters: + * sc - sbmac structure + * pkt - packet to return + * + * Return value: + * nothing + ********************************************************************* */ +static void +eth_free_pkt(tulip_softc *sc, eth_pkt_t *pkt) +{ + CS_ENTER(sc); + q_enqueue(&sc->freelist, &pkt->next); + CS_EXIT(sc); +} + + +/* ********************************************************************* + * ETH_INITFREELIST(sc) + * + * Initialize the buffer free list for this mac. The memory + * allocated to the free list is carved up and placed on a linked + * list of buffers for use by the mac. + * + * Input parameters: + * sc - eth structure + * + * Return value: + * nothing + ********************************************************************* */ +static void +eth_initfreelist(tulip_softc *sc) +{ + int idx; + uint8_t *ptr; + eth_pkt_t *pkt; + + q_init(&sc->freelist); + + ptr = sc->pktpool; + for (idx = 0; idx < ETH_PKTPOOL_SIZE; idx++) { + pkt = (eth_pkt_t *) ptr; + eth_free_pkt(sc, pkt); + ptr += ETH_PKTBUF_SIZE; + } +} + + +/* Utilities */ + +static const char * +tulip_devname(tulip_softc *sc) +{ + return (sc->devctx != NULL ? cfe_device_name(sc->devctx) : "eth?"); +} + + +/* Descriptor ring management */ + +static int +tulip_add_rcvbuf(tulip_softc *sc, eth_pkt_t *pkt) +{ + volatile rx_dscr *rxd; + volatile rx_dscr *nextrxd; + uint32_t flags = 0; + + rxd = sc->rxdscr_add; + + /* Figure out where the next descriptor will go */ + nextrxd = rxd+1; + if (nextrxd == sc->rxdscr_end) { + nextrxd = sc->rxdscr_start; + flags = M_RDES1_ENDOFRING; + } + + /* + * If the next one is the same as our remove pointer, + * the ring is considered full. (it actually has room for + * one more, but we reserve the remove == add case for "empty") + */ + if (nextrxd == sc->rxdscr_remove) return -1; + + rxd->rxd_bufsize = V_RDES1_BUF1SIZE(1520) | flags; + rxd->rxd_bufaddr1 = PTR_TO_PCI(pkt->buffer); + rxd->rxd_bufaddr2 = 0; + rxd->rxd_flags = M_RDES0_OWNADAP; + + /* success, advance the pointer */ + sc->rxdscr_add = nextrxd; + CS_ENTER(sc); + sc->rxdscr_onring++; + CS_EXIT(sc); + + return 0; +} + +static void +tulip_fillrxring(tulip_softc *sc) +{ + eth_pkt_t *pkt; + + while (1) { + CS_ENTER(sc); + if (sc->rxdscr_onring >= MINRXRING) { + CS_EXIT(sc); + break; + } + CS_EXIT(sc); + pkt = eth_alloc_pkt(sc); + if (pkt == NULL) { + /* could not allocate a buffer */ + break; + } + if (tulip_add_rcvbuf(sc, pkt) != 0) { + /* could not add buffer to ring */ + eth_free_pkt(sc, pkt); + break; + } + } +} + + +/* ********************************************************************* + * TULIP_RX_CALLBACK(sc, pkt) + * + * Receive callback routine. This routine is invoked when a + * buffer queued for receives is filled. In this simple driver, + * all we do is add the packet to a per-MAC queue for later + * processing, and try to put a new packet in the place of the one + * that was removed from the queue. + * + * Input parameters: + * sc - interface + * ptk - packet context (eth_pkt structure) + * + * Return value: + * nothing + ********************************************************************* */ +static void +tulip_rx_callback(tulip_softc *sc, eth_pkt_t *pkt) +{ + if (TULIP_DEBUG) show_packet('>', pkt); /* debug */ + + CS_ENTER(sc); + q_enqueue(&sc->rxqueue, &pkt->next); + CS_EXIT(sc); + sc->inpkts++; + + tulip_fillrxring(sc); +} + + +static void +tulip_procrxring(tulip_softc *sc) +{ + volatile rx_dscr *rxd; + eth_pkt_t *pkt; + eth_pkt_t *newpkt; + uint32_t flags; + + for (;;) { + rxd = (volatile rx_dscr *) sc->rxdscr_remove; + + flags = rxd->rxd_flags; + if (flags & M_RDES0_OWNADAP) { + /* end of ring, no more packets */ + break; + } + + pkt = ETH_PKT_BASE(PCI_TO_PTR(rxd->rxd_bufaddr1)); + + /* Drop error packets */ + if (flags & M_RDES0_ERRORSUM) { + xprintf("%s: rx error %04X\n", tulip_devname(sc), flags & 0xFFFF); + tulip_add_rcvbuf(sc, pkt); + goto next; + } + + /* Pass up the packet */ + pkt->length = G_RDES0_FRAMELEN(flags) - CRC_SIZE; + tulip_rx_callback(sc, pkt); + + /* put a buffer back on the ring to replace this one */ + newpkt = eth_alloc_pkt(sc); + if (newpkt) tulip_add_rcvbuf(sc, newpkt); + +next: + /* update the pointer, accounting for buffer wrap. */ + rxd++; + if (rxd == sc->rxdscr_end) + rxd = sc->rxdscr_start; + + sc->rxdscr_remove = (rx_dscr *) rxd; + CS_ENTER(sc); + sc->rxdscr_onring--; + CS_EXIT(sc); + } +} + + +static int +tulip_add_txbuf(tulip_softc *sc, eth_pkt_t *pkt) +{ + volatile tx_dscr *txd; + volatile tx_dscr *nexttxd; + uint32_t bufsize = 0; + + txd = sc->txdscr_add; + + /* Figure out where the next descriptor will go */ + nexttxd = (txd+1); + if (nexttxd == sc->txdscr_end) { + nexttxd = sc->txdscr_start; + bufsize = M_TDES1_ENDOFRING; + } + + /* If the next one is the same as our remove pointer, + the ring is considered full. (it actually has room for + one more, but we reserve the remove == add case for "empty") */ + + if (nexttxd == sc->txdscr_remove) return -1; + + bufsize |= V_TDES1_BUF1SIZE(pkt->length) | + M_TDES1_FIRSTSEG | M_TDES1_LASTSEG | M_TDES1_INTERRUPT; + if (pkt->flags & ETH_TX_SETUP) { + /* For a setup packet, FIRSTSEG and LASTSEG should be clear (!) */ + bufsize ^= M_TDES1_SETUP | M_TDES1_FIRSTSEG | M_TDES1_LASTSEG; + } + txd->txd_bufsize = bufsize; + txd->txd_bufaddr1 = PTR_TO_PCI(pkt->buffer); + txd->txd_bufaddr2 = 0; + txd->txd_flags = M_TDES0_OWNADAP; + + /* success, advance the pointer */ + sc->txdscr_add = nexttxd; + + return 0; +} + + +static int +tulip_transmit(tulip_softc *sc,eth_pkt_t *pkt) +{ + int rv; + + if (TULIP_DEBUG) show_packet('<', pkt); /* debug */ + + rv = tulip_add_txbuf(sc, pkt); + sc->outpkts++; + + WRITECSR(sc, R_CSR_TXPOLL, 1); + return rv; +} + + +static void +tulip_proctxring(tulip_softc *sc) +{ + volatile tx_dscr *txd; + eth_pkt_t *pkt; + uint32_t flags; + + for (;;) { + txd = (volatile tx_dscr *) sc->txdscr_remove; + + if (txd == sc->txdscr_add) { + /* ring is empty, no buffers to process */ + break; + } + + flags = txd->txd_flags; + if (flags & M_TDES0_OWNADAP) { + /* Reached a packet still being transmitted */ + break; + } + + /* Check for a completed setup packet */ + pkt = ETH_PKT_BASE(PCI_TO_PTR(txd->txd_bufaddr1)); + if (pkt->flags & ETH_TX_SETUP) { + if (sc->state == eth_state_setup) { + uint32_t opmode; + + /* check flag bits */ + opmode = READCSR(sc, R_CSR_OPMODE); + opmode |= M_CSR6_RXSTART; + WRITECSR(sc, R_CSR_OPMODE, opmode); + sc->inpkts = sc->outpkts = 0; + sc->state =eth_state_on; + } + pkt->flags &=~ ETH_TX_SETUP; + } + + /* Just free the packet */ + eth_free_pkt(sc, pkt); + + /* update the pointer, accounting for buffer wrap. */ + txd++; + if (txd == sc->txdscr_end) + txd = sc->txdscr_start; + + sc->txdscr_remove = (tx_dscr *) txd; + } +} + + +static void +tulip_initrings(tulip_softc *sc) +{ + volatile tx_dscr *txd; + volatile rx_dscr *rxd; + + /* Claim ownership of all descriptors for the driver */ + + for (txd = sc->txdscr_start; txd != sc->txdscr_end; txd++) + txd->txd_flags = 0; + for (rxd = sc->rxdscr_start; rxd != sc->rxdscr_end; rxd++) + rxd->rxd_flags = 0; + + /* Init the ring pointers */ + + sc->txdscr_add = sc->txdscr_remove = sc->txdscr_start; + sc->rxdscr_add = sc->rxdscr_remove = sc->rxdscr_start; + sc->rxdscr_onring = 0; + + /* Add stuff to the receive ring */ + + tulip_fillrxring(sc); +} + + +static int +tulip_init(tulip_softc *sc) +{ + /* Allocate descriptor rings */ + sc->rxdscrmem = KMALLOC(MAXRXDSCR*sizeof(rx_dscr), sizeof(rx_dscr)); + sc->txdscrmem = KMALLOC(MAXTXDSCR*sizeof(tx_dscr), sizeof(tx_dscr)); + + /* Allocate buffer pool */ + sc->pktpool = KMALLOC(ETH_PKTPOOL_SIZE*ETH_PKTBUF_SIZE, CACHE_ALIGN); + eth_initfreelist(sc); + q_init(&sc->rxqueue); + + /* Fill in pointers to the rings */ + sc->rxdscr_start = (rx_dscr *) (sc->rxdscrmem); + sc->rxdscr_end = sc->rxdscr_start + MAXRXDSCR; + sc->rxdscr_add = sc->rxdscr_start; + sc->rxdscr_remove = sc->rxdscr_start; + sc->rxdscr_onring = 0; + + sc->txdscr_start = (tx_dscr *) (sc->txdscrmem); + sc->txdscr_end = sc->txdscr_start + MAXTXDSCR; + sc->txdscr_add = sc->txdscr_start; + sc->txdscr_remove = sc->txdscr_start; + + tulip_initrings(sc); + + return 0; +} + + +static void +tulip_resetrings(tulip_softc *sc) +{ + volatile tx_dscr *txd; + volatile rx_dscr *rxd; + eth_pkt_t *pkt; + + /* Free already-sent descriptors and buffers */ + tulip_proctxring(sc); + + /* Free any pending but unsent */ + txd = (volatile tx_dscr *) sc->txdscr_remove; + while (txd != sc->txdscr_add) { + txd->txd_flags &=~ M_TDES0_OWNADAP; + pkt = ETH_PKT_BASE(PCI_TO_PTR(txd->txd_bufaddr1)); + eth_free_pkt(sc, pkt); + + txd++; + if (txd == sc->txdscr_end) + txd = sc->txdscr_start; + } + sc->txdscr_add = sc->txdscr_remove; + + /* Discard any received packets as well as all free buffers */ + rxd = (volatile rx_dscr *) sc->rxdscr_remove; + while (rxd != sc->rxdscr_add) { + rxd->rxd_flags &=~ M_RDES0_OWNADAP; + pkt = ETH_PKT_BASE(PCI_TO_PTR(rxd->rxd_bufaddr1)); + eth_free_pkt(sc, pkt); + + rxd++; + if (rxd == sc->rxdscr_end) + rxd = sc->rxdscr_start; + CS_ENTER(sc); + sc->rxdscr_onring--; + CS_EXIT(sc); + } + + /* Reestablish the initial state. */ + tulip_initrings(sc); +} + + +/* CRCs */ + +#define IEEE_CRC32_POLY 0xEDB88320UL /* CRC-32 Poly -- either endian */ + +static uint32_t +tulip_crc32(const uint8_t *databuf, unsigned int datalen) +{ + unsigned int idx, bit, data; + uint32_t crc; + + crc = 0xFFFFFFFFUL; + for (idx = 0; idx < datalen; idx++) + for (data = *databuf++, bit = 0; bit < 8; bit++, data >>= 1) + crc = (crc >> 1) ^ (((crc ^ data) & 1) ? IEEE_CRC32_POLY : 0); + return crc; +} + +#define tulip_mchash(mca) (tulip_crc32((mca), 6) & 0x1FF) + + +/* Serial ROM access */ + +/**************************************************************************** + * tulip_spin(sc, ns) + * + * Spin for a short interval (nominally in nanoseconds) + * + * Input Parameters: ns - minimum required nsec. + * + * The delay loop uses uncached PCI reads, each of which requires + * at least 3 PCI bus clocks (45 ns at 66 MHz) to complete. The + * actual delay will be longer (much longer if preempted). + ***************************************************************************/ + +#define PCI_MIN_DELAY 45 + +static void +tulip_spin(tulip_softc *sc, long nanoseconds) +{ + long delay; + volatile uint32_t t; + + for (delay = nanoseconds; delay > 0; delay -= PCI_MIN_DELAY) + t = READCSR(sc, R_CSR_BUSMODE); +} + + +/* + * Delays below are chosen to meet specs for NS93C64 (slow M variant). + * Current parts are faster. + * Reference: NS Memory Data Book, 1994 + */ + +#define SROM_SIZE 128 +#define SROM_MAX_CYCLES 32 + +#define SROM_CMD_BITS 3 +#define SROM_ADDR_BITS 6 + +#define K_SROM_READ_CMD 06 +#define K_SROM_WRITE_CMD 05 + +#define SROM_VENDOR_INDEX 0x00 +#define SROM_FORMAT_INDEX 0x12 +#define SROM_ADDR_INDEX 0x14 + +#define SROM_DEVICE0_INDEX 0x1A +#define SROM_LEAF0_OFFSET_INDEX 0x1B + +#define SROM_CRC_INDEX (SROM_SIZE-2) +/* Note recent chips supporting wake-on-lan have CRC in bytes 94, 95 */ + +#define SROM_WORD(rom,offset) ((rom)[offset] | ((rom)[offset+1] << 8)) + +static void +srom_idle_state(tulip_softc *sc) +{ + uint32_t csr9; + unsigned int i; + + csr9 = READCSR(sc, R_CSR_ROM_MII); + + csr9 |= M_CSR9_SROMCHIPSEL; + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 100); /* CS setup (Tcss=100) */ + + /* Run the clock through the maximum number of pending read cycles */ + for (i = 0; i < SROM_MAX_CYCLES*2; i++) { + csr9 ^= M_CSR9_SROMCLOCK; + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 1000); /* SK period (Fsk=0.5MHz) */ + } + + /* Deassert SROM Chip Select */ + csr9 &=~ M_CSR9_SROMCHIPSEL; + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 50); /* CS recovery (Tsks=50) */ +} + +static void +srom_send_command_bit(tulip_softc *sc, unsigned int data) +{ + uint32_t csr9; + + csr9 = READCSR(sc, R_CSR_ROM_MII); + + /* Place the data bit on the bus */ + if (data == 1) + csr9 |= M_CSR9_SROMDATAIN; + else + csr9 &=~ M_CSR9_SROMDATAIN; + + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 360); /* setup: Tdis=200 */ + + /* Now clock the data into the SROM */ + WRITECSR(sc, R_CSR_ROM_MII, csr9 | M_CSR9_SROMCLOCK); + tulip_spin(sc, 900); /* clock high, Tskh=500 */ + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 450); /* clock low, Tskl=250 */ + + /* Now clear the data bit */ + csr9 &=~ M_CSR9_SROMDATAIN; /* data invalid, Tidh=20 for SK^ */ + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 270); /* min cycle, 1/Fsk=2000 */ +} + +static uint16_t +srom_read_bit(tulip_softc *sc) +{ + uint32_t csr9; + + csr9 = READCSR(sc, R_CSR_ROM_MII); + + /* Generate a clock cycle before doing a read */ + WRITECSR(sc, R_CSR_ROM_MII, csr9 | M_CSR9_SROMCLOCK); /* rising edge */ + tulip_spin(sc, 1000); /* clock high, Tskh=500, Tpd=1000 */ + WRITECSR(sc, R_CSR_ROM_MII, csr9); /* falling edge */ + tulip_spin(sc, 1000); /* clock low, 1/Fsk=2000 */ + + csr9 = READCSR(sc, R_CSR_ROM_MII); + return ((csr9 & M_CSR9_SROMDATAOUT) != 0 ? 1 : 0); +} + +#define CMD_BIT_MASK (1 << (SROM_CMD_BITS+SROM_ADDR_BITS-1)) + +static uint16_t +srom_read_word(tulip_softc *sc, unsigned int index) +{ + uint16_t command, word; + uint32_t csr9; + unsigned int i; + + csr9 = READCSR(sc, R_CSR_ROM_MII) | M_CSR9_SROMCHIPSEL; + + /* Assert the SROM CS line */ + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 100); /* CS setup, Tcss = 100 */ + + /* Send the read command to the SROM */ + command = (K_SROM_READ_CMD << SROM_ADDR_BITS) | index; + for (i = 0; i < SROM_CMD_BITS+SROM_ADDR_BITS; i++) { + srom_send_command_bit(sc, (command & CMD_BIT_MASK) != 0 ? 1 : 0); + command <<= 1; + } + + /* Now read the bits from the SROM (MSB first) */ + word = 0; + for (i = 0; i < 16; ++i) { + word <<= 1; + word |= srom_read_bit(sc); + } + + /* Clear the SROM CS Line, CS hold, Tcsh = 0 */ + WRITECSR(sc, R_CSR_ROM_MII, csr9 &~ M_CSR9_SROMCHIPSEL); + + return word; +} + + +/**************************************************************************** + * srom_calc_crc() + * + * Calculate the CRC of the SROM and return it. We compute the + * CRC per Appendix A of the 21140A ROM/external register data + * sheet (EC-QPQWA-TE). + ***************************************************************************/ + +static uint16_t +srom_calc_crc(tulip_softc *sc, uint8_t srom[], int length) +{ + uint32_t crc = tulip_crc32(srom, length) ^ 0xFFFFFFFF; + + return (uint16_t)(crc & 0xFFFF); +} + +/**************************************************************************** + * srom_read_all(sc, uint8_t dest) + * + * Read the entire SROM into the srom array + * + * Input parameters: + * sc - tulip state + ***************************************************************************/ + +static int +srom_read_all(tulip_softc *sc, uint8_t dest[]) +{ + int i; + uint16_t crc, temp; + + WRITECSR(sc, R_CSR_ROM_MII, M_CSR9_SERROMSEL|M_CSR9_ROMREAD); + + srom_idle_state(sc); + + for (i = 0; i < SROM_SIZE/2; i++) { + temp = srom_read_word(sc, i); + dest[2*i] = temp & 0xFF; + dest[2*i+1] =temp >> 8; + } + + WRITECSR(sc, R_CSR_ROM_MII, 0); /* CS hold, Tcsh=0 */ + + crc = srom_calc_crc(sc, dest, SROM_CRC_INDEX); + if (crc != SROM_WORD(dest, SROM_CRC_INDEX)) { + crc = srom_calc_crc(sc, dest, 94); /* "alternative" */ + if (crc != SROM_WORD(dest, 94)) { + xprintf("%s: Invalid SROM CRC, calc %04x, stored %04x\n", + tulip_devname(sc), crc, SROM_WORD(dest, 94)); + return 0/*-1*/; + } + } + return 0; +} + +static int +srom_read_addr(tulip_softc *sc, uint8_t buf[]) +{ + uint8_t srom[SROM_SIZE]; + + if (srom_read_all(sc, srom) == 0) { + memcpy(buf, &srom[SROM_ADDR_INDEX], ENET_ADDR_LEN); + return 0; + } + + return -1; +} + + +/**************************************************************************** + * earom_read_all(sc, uint8_t dest) + * + * Read the entire Ethernet address ROM into the srom array (21040 only) + * + * Input parameters: + * sc - tulip state + ***************************************************************************/ + +static int +earom_read_all(tulip_softc *sc, uint8_t dest[]) +{ + int i; + uint32_t csr9; + + WRITECSR(sc, R_CSR_ROM_MII, 0); /* reset pointer */ + + for (i = 0; i < SROM_SIZE; i++) { + for (;;) { + csr9 = READCSR(sc, R_CSR_ROM_MII); + if ((csr9 & M_CSR9_DATANOTVALID) == 0) + break; + POLL(); /* XXX need a timeout */ + } + dest[i] = G_CSR9_ROMDATA(csr9); + } + + return 0; +} + +static int +earom_read_addr(tulip_softc *sc, uint8_t buf[]) +{ + uint8_t srom[SROM_SIZE]; + + if (earom_read_all(sc, srom) == 0) { + memcpy(buf, &srom[0], ENET_ADDR_LEN); + return 0; + } + + return -1; +} + + +static int +rom_read_all(tulip_softc *sc, uint8_t buf[]) +{ + if (sc->device == K_PCI_ID_DC21040) + return earom_read_all(sc, buf); + else + return srom_read_all(sc, buf); +} + +static int +rom_read_addr(tulip_softc *sc, uint8_t buf[]) +{ + if (sc->device == K_PCI_ID_DC21040) + return earom_read_addr(sc, buf); + else + return srom_read_addr(sc, buf); +} + +#if 0 +static void +rom_dump(uint8_t srom[]) +{ + int i; + + xprintf("DC21x4x: SROM data:"); + for (i = 0; i < SROM_SIZE; i++) { + if (i % 16 == 0) + xprintf("\n %02x: ", i); + xprintf(" %02x", srom[i]); + } + xprintf("\n"); +} +#else +#define rom_dump(srom) +#endif + + +/**************************************************************************** + * MII access utility routines + ***************************************************************************/ + +/* MII clock limited to 2.5 MHz, transactions end with MDIO tristated */ + +static void +mii_write_bits(tulip_softc *sc, uint32_t data, unsigned int count) +{ + uint32_t csr9; + uint32_t bitmask; + + csr9 = READCSR(sc, R_CSR_ROM_MII) &~ (M_CSR9_MDC | M_CSR9_MIIMODE); + + for (bitmask = 1 << (count-1); bitmask != 0; bitmask >>= 1) { + csr9 &=~ M_CSR9_MDO; + if ((data & bitmask) != 0) csr9 |= M_CSR9_MDO; + WRITECSR(sc, R_CSR_ROM_MII, csr9); + + tulip_spin(sc, 2000); /* setup */ + WRITECSR(sc, R_CSR_ROM_MII, csr9 | M_CSR9_MDC); + tulip_spin(sc, 2000); /* hold */ + WRITECSR(sc, R_CSR_ROM_MII, csr9); + } +} + +static void +mii_turnaround(tulip_softc *sc) +{ + uint32_t csr9; + + csr9 = READCSR(sc, R_CSR_ROM_MII) | M_CSR9_MIIMODE; + + /* stop driving data */ + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 2000); /* setup */ + WRITECSR(sc, R_CSR_ROM_MII, csr9 | M_CSR9_MDC); + tulip_spin(sc, 2000); /* clock high */ + WRITECSR(sc, R_CSR_ROM_MII, csr9); + + /* read back and check for 0 here? */ +} + +/**************************************************************************** + * mii_read_register + * + * This routine reads a register from the PHY chip using the MII + * serial management interface. + * + * Input parameters: + * index - index of register to read (0-31) + * + * Return value: + * word read from register + ***************************************************************************/ + +static uint16_t +mii_read_register(tulip_softc *sc, unsigned int index) +{ + /* Send the command and address to the PHY. The sequence is + a synchronization sequence (32 1 bits) + a "start" command (2 bits) + a "read" command (2 bits) + the PHY addr (5 bits) + the register index (5 bits) + */ + uint32_t csr9; + uint16_t word; + int i; + + mii_write_bits(sc, 0xFF, 8); + mii_write_bits(sc, 0xFFFFFFFF, 32); + mii_write_bits(sc, MII_COMMAND_START, 2); + mii_write_bits(sc, MII_COMMAND_READ, 2); + mii_write_bits(sc, sc->mii_addr, 5); + mii_write_bits(sc, index, 5); + + mii_turnaround(sc); + + csr9 = (READCSR(sc, R_CSR_ROM_MII) &~ M_CSR9_MDC) | M_CSR9_MIIMODE; + word = 0; + + for (i = 0; i < 16; i++) { + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 2000); /* clock width low */ + WRITECSR(sc, R_CSR_ROM_MII, csr9 | M_CSR9_MDC); + tulip_spin(sc, 2000); /* clock width high */ + WRITECSR(sc, R_CSR_ROM_MII, csr9); + tulip_spin(sc, 1000); /* output delay */ + word <<= 1; + if ((READCSR(sc, R_CSR_ROM_MII) & M_CSR9_MDI) != 0) + word |= 0x0001; + } + + return word; + + /* reset to output mode? */ +} + +/**************************************************************************** + * mii_write_register + * + * This routine writes a register in the PHY chip using the MII + * serial management interface. + * + * Input parameters: + * index - index of register to write (0-31) + * value - word to write + ***************************************************************************/ + +static void +mii_write_register(tulip_softc *sc, unsigned int index, uint16_t value) +{ + mii_write_bits(sc, 0xFF, 8); + mii_write_bits(sc, 0xFFFFFFFF, 32); + mii_write_bits(sc, MII_COMMAND_START, 2); + mii_write_bits(sc, MII_COMMAND_WRITE, 2); + mii_write_bits(sc, sc->mii_addr, 5); + mii_write_bits(sc, index, 5); + mii_write_bits(sc, MII_COMMAND_ACK, 2); + mii_write_bits(sc, value, 16); + + /* reset to input mode? */ +} + + +static int +mii_probe(tulip_softc *sc) +{ + int i; + uint16_t id1, id2; + + for (i = 0; i < 32; i++) { + sc->mii_addr = i; + id1 = mii_read_register(sc, MII_PHYIDR1); + id2 = mii_read_register(sc, MII_PHYIDR2); + if ((id1 != 0x0000 && id1 != 0xFFFF) || + (id2 != 0x0000 && id2 != 0xFFFF)) { + return 0; + } + } + return -1; +} + +#if 0 +#define OUI_NAT_SEMI 0x080017 +#define OUI_LEVEL_ONE 0x1E0400 /* 0x00207B, bit reversed and truncated */ + +static void +mii_dump(tulip_softc *sc, const char *label) +{ + int i; + uint16_t r; + uint32_t oui, part; + + xprintf("%s\n", label); + oui = 0; + for (i = 0; i <= 6; ++i) { + r = mii_read_register(sc, i); + if (r != 0) xprintf("MII_REG%02x: %04x\n", i, r); + if (i == MII_PHYIDR1) { + oui = r << 6; + } + else if (i == MII_PHYIDR2) { + oui |= (r >> 10) & 0x3F; + part = (r >> 4) & 0x3F; + } + } + if (oui == OUI_NAT_SEMI) { /* DP83840, DP83840A */ + for (i = 0x15; i <= 0x19; ++i) { + r = mii_read_register(sc, i); + if (r != 0) xprintf("MII_REG%02x: %04x\n", i, r); + } + } + else if (oui == OUI_LEVEL_ONE) { /* LXT970, etc. */ + for (i = 0x10; i <= 0x14; ++i) { + r = mii_read_register(sc, i); + if (r != 0) xprintf("MII_REG%02x: %04x\n", i, r); + } + } +} +#else +#define mii_dump(sc,label) +#endif + + +/* The following functions are suitable for all tulips with MII + interfaces. */ + +static void +mii_set_speed(tulip_softc *sc, int speed, int autoneg) +{ + uint16_t control; + uint16_t pcr; + uint32_t opmode = 0; + + /* This is really just for NS DP83840/A. Needed? */ + pcr = mii_read_register(sc, 0x17); + pcr |= (0x400|0x100|0x40|0x20); + mii_write_register(sc, 0x17, pcr); + + control = mii_read_register(sc, MII_BMCR); + + if (!autoneg) { + control &=~ (BMCR_ANENABLE | BMCR_RESTARTAN); + mii_write_register(sc, MII_BMCR, control); + control &=~ (BMCR_SPEED0 | BMCR_SPEED1 | BMCR_DUPLEX); + } + + switch (speed) { + case ETHER_SPEED_10HDX: + default: + opmode = M_CSR6_SPEED_10_MII; + break; + case ETHER_SPEED_10FDX: + control |= BMCR_DUPLEX; + opmode = M_CSR6_SPEED_10_MII | M_CSR6_FULLDUPLEX; + break; + case ETHER_SPEED_100HDX: + control |= BMCR_SPEED100; + opmode = M_CSR6_SPEED_100_MII; + break; + case ETHER_SPEED_100FDX: + control |= BMCR_SPEED100 | BMCR_DUPLEX ; + opmode = M_CSR6_SPEED_100_MII | M_CSR6_FULLDUPLEX; + break; + } + + if (!autoneg) + mii_write_register(sc, MII_BMCR, control); + + opmode |= M_CSR6_MBO; +#if TULIP_TUNE + opmode |= V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72); +#else + opmode |= M_CSR6_STOREFWD; +#endif + WRITECSR(sc, R_CSR_OPMODE, opmode); + mii_dump(sc, "setspeed PHY"); +} + +static void +mii_autonegotiate(tulip_softc *sc) +{ + uint16_t control, status, cap; + unsigned int timeout; + int linkspeed; + int autoneg; + + linkspeed = ETHER_SPEED_UNKNOWN; + + /* Read twice to clear latching bits */ + status = mii_read_register(sc, MII_BMSR); + status = mii_read_register(sc, MII_BMSR); + mii_dump(sc, "query PHY"); + + if ((status & (BMSR_AUTONEG | BMSR_LINKSTAT)) == + (BMSR_AUTONEG | BMSR_LINKSTAT)) + control = mii_read_register(sc, MII_BMCR); + else { + /* reset the PHY */ + mii_write_register(sc, MII_BMCR, BMCR_RESET); + timeout = 3000; + for (;;) { + control = mii_read_register(sc, MII_BMCR); + if ((control && BMCR_RESET) == 0) break; + cfe_sleep(CFE_HZ/2); + timeout -= 500; + if (timeout <= 0) break; + } + if ((control & BMCR_RESET) != 0) { + xprintf("%s: PHY reset failed\n", tulip_devname(sc)); + return; + } + + status = mii_read_register(sc, MII_BMSR); + cap = ((status >> 6) & (ANAR_TXFD | ANAR_TXHD | ANAR_10FD | ANAR_10HD)) + | PSB_802_3; + mii_write_register(sc, MII_ANAR, cap); + control |= (BMCR_ANENABLE | BMCR_RESTARTAN); + mii_write_register(sc, MII_BMCR, control); + + timeout = 3000; + for (;;) { + status = mii_read_register(sc, MII_BMSR); + if ((status & BMSR_ANCOMPLETE) != 0) break; + cfe_sleep(CFE_HZ/2); + timeout -= 500; + if (timeout <= 0) break; + } + mii_dump(sc, "done PHY"); + } + + xprintf("%s: Link speed: ", tulip_devname(sc)); + if ((status & BMSR_ANCOMPLETE) != 0) { + /* A link partner was negogiated... */ + + uint16_t remote = mii_read_register(sc, MII_ANLPAR); + + autoneg = 1; + if ((remote & ANLPAR_TXFD) != 0) { + xprintf("100BaseT FDX"); + linkspeed = ETHER_SPEED_100FDX; + } + else if ((remote & ANLPAR_TXHD) != 0) { + xprintf("100BaseT HDX"); + linkspeed = ETHER_SPEED_100HDX; + } + else if ((remote & ANLPAR_10FD) != 0) { + xprintf("10BaseT FDX"); + linkspeed = ETHER_SPEED_10FDX; + } + else if ((remote & ANLPAR_10HD) != 0) { + xprintf("10BaseT HDX"); + linkspeed = ETHER_SPEED_10HDX; + } + xprintf("\n"); + } + else { + /* no link partner negotiation */ + + autoneg = 0; + xprintf("Unknown, assuming 10BaseT\n"); + control &=~ (BMCR_ANENABLE | BMCR_RESTARTAN); + mii_write_register(sc, MII_BMCR, control); + linkspeed = ETHER_SPEED_10HDX; + } + + if ((status & BMSR_LINKSTAT) == 0) + mii_write_register(sc, MII_BMCR, control); + mii_set_speed(sc, linkspeed, autoneg); + + status = mii_read_register(sc, MII_BMSR); /* clear latching bits */ + mii_dump(sc, "final PHY"); +} + + +/* Chip specific code */ + +static void +dc21143_set_speed(tulip_softc *sc, int speed) +{ + uint32_t opmode = 0; + + WRITECSR(sc, R_CSR_SIAMODE0, 0); + + switch (speed) { + case ETHER_SPEED_AUTO: + break; + case ETHER_SPEED_10HDX: + default: + WRITECSR(sc, R_CSR_SIAMODE1, M_CSR14_10BT_HD); + WRITECSR(sc, R_CSR_SIAMODE2, sc->gpdata); + opmode = M_CSR6_SPEED_10; + break; + case ETHER_SPEED_10FDX: + WRITECSR(sc, R_CSR_SIAMODE1, M_CSR14_10BT_FD); + WRITECSR(sc, R_CSR_SIAMODE2, sc->gpdata); + opmode = M_CSR6_SPEED_10 | M_CSR6_FULLDUPLEX; + break; + case ETHER_SPEED_100HDX: + WRITECSR(sc, R_CSR_SIAMODE1, 0); + WRITECSR(sc, R_CSR_SIAMODE2, sc->gpdata); + opmode = M_CSR6_SPEED_100; + break; + case ETHER_SPEED_100FDX: + WRITECSR(sc, R_CSR_SIAMODE1, 0); + WRITECSR(sc, R_CSR_SIAMODE2, sc->gpdata); + opmode = M_CSR6_SPEED_100 | M_CSR6_FULLDUPLEX; + break; + } + + WRITECSR(sc, R_CSR_SIAMODE0, M_CSR13_CONN_NOT_RESET); + + opmode |= M_CSR6_MBO; +#if TULIP_TUNE + opmode |= V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72); +#else + opmode |= M_CSR6_STOREFWD; +#endif + WRITECSR(sc, R_CSR_OPMODE, opmode); +} + +static void +dc21143_autonegotiate(tulip_softc *sc) +{ + uint32_t opmode; + uint32_t tempword; + int count; + int linkspeed; + + linkspeed = ETHER_SPEED_UNKNOWN; + + /* Program the media setup into the CSRs. */ + /* reset SIA */ + WRITECSR(sc, R_CSR_SIAMODE0, 0); + + /* set to speed_10, fullduplex to start_nway */ + opmode = + M_CSR6_SPEED_10 | + M_CSR6_FULLDUPLEX | + M_CSR6_MBO; + WRITECSR(sc, R_CSR_OPMODE, opmode); + + /* Choose advertised capabilities */ + tempword = + M_CSR14_100BASETHALFDUP | + M_CSR14_100BASETFULLDUP | + M_CSR14_HALFDUPLEX10BASET; + WRITECSR(sc, R_CSR_SIAMODE1, tempword); + + /* Enable autonegotiation */ + tempword |= M_CSR14_AUTONEGOTIATE | 0xFFFF; + WRITECSR(sc, R_CSR_SIAMODE1, tempword); + WRITECSR(sc, R_CSR_SIAMODE2, sc->gpdata); + WRITECSR(sc, R_CSR_OPMODE, opmode); + WRITECSR(sc, R_CSR_SIAMODE0, M_CSR13_CONN_NOT_RESET); + + /* STATE check nway, poll until a valid 10/100mbs signal seen */ + WRITECSR(sc, R_CSR_STATUS, M_CSR5_LINKPASS); /* try to clear this... */ + + /* (Re)start negotiation */ + tempword = READCSR(sc, R_CSR_SIASTATUS); + tempword &=~ M_CSR12_AUTONEGARBIT; + tempword |= V_CSR12_AUTONEGARBIT(0x1); + + for (count = 0; count <= 13; count++) { + tempword = READCSR(sc, R_CSR_STATUS); + if (tempword & M_CSR5_LINKPASS) + break; + cfe_sleep(CFE_HZ/10); + } + + if (count > 13) + xprintf("%s: Link autonegotiation failed\n", tulip_devname(sc)); + + /* STATE configure nway, check to see if any abilities common to us. + If they do, set to highest mode, if not, we will see if the partner + will do 100mb or 10mb - then set it */ + + tempword = READCSR(sc, R_CSR_SIASTATUS); + /* clear the autonegogiate complete bit */ + WRITECSR(sc, R_CSR_STATUS, M_CSR5_LINKPASS); + + if (tempword & M_CSR12_LINKPARTNEG) { + /* A link partner was negogiated... */ + + xprintf("%s: Link speed: ", tulip_devname(sc)); + if (tempword & 0x01000000) { /* 100FD */ + xprintf("100BaseT FDX"); + linkspeed = ETHER_SPEED_100FDX; + } + else if (tempword & 0x00800000) { /* 100HD */ + xprintf("100BaseT HDX"); + linkspeed = ETHER_SPEED_100HDX; + } + else if (tempword & 0x00400000) { /* 10FD */ + xprintf("10BaseT FDX"); + linkspeed = ETHER_SPEED_10FDX; + } + else if (tempword & 0x00200000) { /* 10HD */ + xprintf("10BaseT HDX"); + linkspeed = ETHER_SPEED_10HDX; + } + xprintf("\n"); + } + else { + /* no link partner negotiation */ + /* disable link for 1.3 seconds to break any existing connections */ + + xprintf("%s: ", tulip_devname(sc)); + dc21143_set_speed(sc, ETHER_SPEED_10HDX); + cfe_sleep(CFE_HZ/8); + + tempword = READCSR(sc, R_CSR_SIASTATUS); + + if ((tempword & 0x02) == 0) { + /* 100 mb signal present set to 100mb */ + xprintf("No link partner... setting to 100BaseT HDX\n"); + linkspeed = ETHER_SPEED_100HDX; + } + else if ((tempword & 0x04) == 0) { + /* 10 mb signal present */ + xprintf("No link partner... setting to 10BaseT HDX\n"); + linkspeed = ETHER_SPEED_10HDX; + } + else { + /* couldn't determine line speed, so set to 10mbs */ + xprintf("Unknown; defaulting to 10BaseT HDX\n"); + linkspeed = ETHER_SPEED_10HDX; + } + } + + dc21143_set_speed(sc, linkspeed); +} + +static void +dc21143_set_loopback(tulip_softc *sc, int mode) +{ + uint32_t v; + + WRITECSR(sc, R_CSR_SIAMODE0, 0); + if (mode == ETHER_LOOPBACK_EXT) { + /* deal with CSRs 13-15 */ + } + cfe_sleep(CFE_HZ/10); /* check this */ + + /* Update the SIA registers */ + v = READCSR(sc, R_CSR_SIAMODE0); + WRITECSR(sc, R_CSR_SIAMODE0, v &~ 0xFFFF); + v = READCSR(sc, R_CSR_SIAMODE1); + WRITECSR(sc, R_CSR_SIAMODE1, v &~ 0xFFFF); + v = READCSR(sc, R_CSR_SIAMODE2); + WRITECSR(sc, R_CSR_SIAMODE2, v | 0xC000); /* WC of HCKR, RMP */ + if (mode == ETHER_LOOPBACK_OFF) + WRITECSR(sc, R_CSR_SIAMODE2, sc->gpdata); + else + WRITECSR(sc, R_CSR_SIAMODE2, (v &~ 0xFFFF) | M_CSR15_GP_AUIBNC); + + WRITECSR(sc, R_CSR_SIAMODE0, M_CSR13_CONN_NOT_RESET); + + sc->loopback = mode; +} + +/* Known vendors with cards requiring special initialization. */ +#define K_PCI_VENDOR_COGENT 0x1109 /* inherited by Adaptec */ +#define K_PCI_VENDOR_PHOBOS 0x13D8 +#define K_PCI_VENDOR_ZNYZ 0x110D +#define K_PCI_VENDOR_KINGSTON 0x2646 + +static void +dc21143_hwinit(tulip_softc *sc, uint8_t srom[]) +{ + uint32_t v; + uint32_t csr6word, csr14word; + + if (SROM_WORD(srom, SROM_VENDOR_INDEX) == K_PCI_VENDOR_COGENT) { + /* Cogent/Adaptec MII (ANA-6911A). */ + sc->phy_type = MII; + WRITECSR(sc, R_CSR_SIAMODE2, 0x0821 << 16); + WRITECSR(sc, R_CSR_SIAMODE2, 0x0001 << 16); + cfe_sleep(CFE_HZ/10); + WRITECSR(sc, R_CSR_SIAMODE2, 0x0000 << 16); + cfe_sleep(CFE_HZ/2); + sc->gpdata = 0; + } + else if (SROM_WORD(srom, SROM_VENDOR_INDEX) == K_PCI_VENDOR_ZNYZ) { + /* Znyz 34xQ adapters */ + sc->phy_type = SYM; + + /* The ZX345Q with wake-on-LAN enabled apparently clears ANE and + TAS on power up (but not cold reset) */ + WRITECSR(sc, R_CSR_SIAMODE1, 0xFFFFFFFF); + + /* The following is a reset workaround for QS/Kendin PHYs + as suggested by an Intel app note. Bit 0x40000 is the PHY + reset (low true) on Znyx cards. */ + WRITECSR(sc, R_CSR_SIAMODE2, + M_CSR15_GP_CONTROLWRITE | + 0xF0000 | /* all outputs */ + M_CSR15_GP_LED1 | + M_CSR15_GP_AUIBNC); + cfe_sleep(CFE_HZ/5); + WRITECSR(sc, R_CSR_SIAMODE2, 0x40000); /* release reset */ + cfe_sleep(CFE_HZ/5); + sc->gpdata = 0x40000 | M_CSR15_GP_AUIBNC; + } + else if (SROM_WORD(srom, SROM_VENDOR_INDEX) == K_PCI_VENDOR_KINGSTON) { + /* Kingston KNE100TX */ + sc->phy_type = MII; + sc->gpdata = 0; + } + else if (SROM_WORD(srom, SROM_VENDOR_INDEX) == K_PCI_VENDOR_PHOBOS) { + /* Phobos 430TX quad card */ + sc->phy_type = MII; +#if 0 /* per EEPROM */ + WRITECSR(sc, R_CSR_SIAMODE2, 0x080E << 16); + WRITECSR(sc, R_CSR_SIAMODE2, 0x000E << 16); + cfe_sleep(CFE_HZ/10); + sc->gpdata = 0x0E; +#else /* following Adaptec 21143 with MII interface */ + WRITECSR(sc, R_CSR_SIAMODE2, 0x0821 << 16); + WRITECSR(sc, R_CSR_SIAMODE2, 0x0001 << 16); + cfe_sleep(CFE_HZ/10); + WRITECSR(sc, R_CSR_SIAMODE2, 0x0000 << 16); + cfe_sleep(CFE_HZ/2); + sc->gpdata = 0; +#endif + } + else { + /* Most 21143 cards use the SYM interface. */ + sc->phy_type = SYM; + WRITECSR(sc, R_CSR_SIAMODE2, M_CSR15_CONFIG_GEPS_LEDS); + sc->gpdata = M_CSR15_DEFAULT_VALUE; + } + + if (sc->phy_type == MII) { + mii_probe(sc); + } + + /* CSR0 - bus mode */ +#if TULIP_TUNE + v = V_CSR0_SKIPLEN(0) | + V_CSR0_CACHEALIGN(K_CSR0_ALIGN32) | + M_CSR0_READMULTENAB | M_CSR0_READLINEENAB | + M_CSR0_WRITEINVALENAB | + V_CSR0_BURSTLEN(K_CSR0_BURST32); +#else + v = V_CSR0_SKIPLEN(0) | + V_CSR0_CACHEALIGN(K_CSR0_ALIGN32) | + V_CSR0_BURSTLEN(K_CSR0_BURST32); +#endif +#ifdef __MIPSEB + v |= M_CSR0_BIGENDIAN; /* big-endian data serialization */ +#endif + WRITECSR(sc, R_CSR_BUSMODE, v); + + /* CSR6 - operation mode */ + v = M_CSR6_PORTSEL | +#if TULIP_TUNE + V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72) | +#else + M_CSR6_STOREFWD | +#endif + M_CSR6_MBO; + if (sc->phy_type == SYM) + v |= M_CSR6_PCSFUNC |M_CSR6_SCRAMMODE; + WRITECSR(sc, R_CSR_OPMODE, v); + + /* About to muck with the SIA, reset it.(?) */ + /* WRITECSR(sc, R_CSR_SIASTATUS, 0); */ + + /* Must shut off all transmit/receive in order to attempt to + achieve Full Duplex */ + csr6word = READCSR(sc, R_CSR_OPMODE); + WRITECSR(sc, R_CSR_OPMODE, csr6word &~ (M_CSR6_TXSTART | M_CSR6_RXSTART)); + csr6word = READCSR(sc, R_CSR_OPMODE); + + WRITECSR(sc, R_CSR_RXRING, PTR_TO_PCI(sc->rxdscr_start)); + WRITECSR(sc, R_CSR_TXRING, PTR_TO_PCI(sc->txdscr_start)); + + if (sc->phy_type == MII) { + if (sc->linkspeed == ETHER_SPEED_AUTO) + mii_autonegotiate(sc); + else + mii_set_speed(sc, sc->linkspeed, 0); + } + else { + if (sc->linkspeed == ETHER_SPEED_AUTO) { + dc21143_autonegotiate(sc); + } + else { + /* disable autonegotiate so we can set full duplex to on */ + WRITECSR(sc, R_CSR_SIAMODE0, 0); + csr14word = READCSR(sc, R_CSR_SIAMODE1); + csr14word &=~ M_CSR14_AUTONEGOTIATE; + WRITECSR(sc, R_CSR_SIAMODE1, csr14word); + WRITECSR(sc, R_CSR_SIAMODE0, M_CSR13_CONN_NOT_RESET); + + dc21143_set_speed(sc, sc->linkspeed); + } + } +} + + +static void +dc21140_set_speed(tulip_softc *sc, int speed, int autoneg) +{ + mii_set_speed(sc, speed, autoneg); +} + +static void +dc21140_set_loopback(tulip_softc *sc, int mode) +{ + if (mode == ETHER_LOOPBACK_EXT) { + xprintf("%s: external loopback mode NYI\n", tulip_devname(sc)); + mode = ETHER_LOOPBACK_OFF; + } + else if (mode != ETHER_LOOPBACK_INT) + mode = ETHER_LOOPBACK_OFF; + + sc->loopback = mode; +} + +static void +dc21140_hwinit(tulip_softc *sc, uint8_t srom[]) +{ + uint16_t leaf; + uint8_t gpr_control, gpr_data; + uint32_t v; + uint32_t opmode; + + if (srom[SROM_FORMAT_INDEX] == 0 || srom[SROM_FORMAT_INDEX] > 4) { + gpr_control = 0x1F; + gpr_data = 0x00; + sc->phy_type = MII; /* Most 21140 cards use MII */ + } + else { + leaf = SROM_WORD(srom, SROM_LEAF0_OFFSET_INDEX); + gpr_control = srom[leaf+2]; + /* XXX We should parse and check all the leaf info */ + if ((srom[leaf+4] & 0x80) == 0) { + gpr_data = 0x85; /* SYM, 100 Mb/s */ + sc->phy_type = SYM; + } + else { + gpr_data = 0x00; /* MII */ + sc->phy_type = MII; + } + } + + /* Assume that we will use MII or SYM interface */ + WRITECSR(sc, R_CSR_OPMODE, M_CSR6_PORTSEL); + RESET_ADAPTER(sc); + + WRITECSR(sc, R_CSR_GENPORT, M_CSR12_CONTROL | gpr_control); + WRITECSR(sc, R_CSR_GENPORT, gpr_data); /* setup PHY */ + + if (sc->phy_type == MII) { + mii_probe(sc); + } + + /* CSR0 - bus mode */ +#if TULIP_TUNE + v = V_CSR0_SKIPLEN(0) | + V_CSR0_CACHEALIGN(K_CSR0_ALIGN32) | + M_CSR0_READMULTENAB | M_CSR0_READLINEENAB | + M_CSR0_WRITEINVALENAB | + V_CSR0_BURSTLEN(K_CSR0_BURST32); +#else + v = V_CSR0_SKIPLEN(0) | + V_CSR0_CACHEALIGN(K_CSR0_ALIGN32) | + V_CSR0_BURSTLEN(K_CSR0_BURST32); +#endif +#ifdef __MIPSEB + v |= M_CSR0_BIGENDIAN; /* big-endian data serialization */ +#endif + WRITECSR(sc, R_CSR_BUSMODE, v); + + /* CSR6 - operation mode */ + v = M_CSR6_PORTSEL | +#if TULIP_TUNE + V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72) | +#else + M_CSR6_STOREFWD | +#endif + M_CSR6_MBO; + WRITECSR(sc, R_CSR_OPMODE, v); + + /* Must shut off all transmit/receive in order to attempt to + achieve Full Duplex */ + opmode = READCSR(sc, R_CSR_OPMODE); + WRITECSR(sc, R_CSR_OPMODE, opmode &~ (M_CSR6_TXSTART | M_CSR6_RXSTART)); + opmode = READCSR(sc, R_CSR_OPMODE); + + WRITECSR(sc, R_CSR_RXRING, PTR_TO_PCI(sc->rxdscr_start)); + WRITECSR(sc, R_CSR_TXRING, PTR_TO_PCI(sc->txdscr_start)); + + if (sc->phy_type == MII) { + if (sc->linkspeed == ETHER_SPEED_AUTO) + mii_autonegotiate(sc); + else + mii_set_speed(sc, sc->linkspeed, 0); + } + else { + /* XXX The 21140 requires a soft reset after changing PORTSEL. + For now, remain committed to the SYM port (100 Mb/s) */ + switch (sc->linkspeed) { + default: + sc->linkspeed = ETHER_SPEED_100HDX; /* for now */ + /* fall through */ + case ETHER_SPEED_100HDX: + opmode |= M_CSR6_SPEED_100; + break; + case ETHER_SPEED_100FDX: + opmode |= M_CSR6_SPEED_100 | M_CSR6_FULLDUPLEX; + break; + } + + /* XXX Need to reset and reinitialize if we choose SPEED_10 above */ + WRITECSR(sc, R_CSR_OPMODE, opmode); + } +} + + +static void +dc21041_set_speed(tulip_softc *sc, int speed) +{ + uint32_t opmode = 0; + + WRITECSR(sc, R_CSR_SIAMODE0, 0); + + /* For now, always force 10BT, HDX (21041, Table 3-62) */ + switch (speed) { + case ETHER_SPEED_10HDX: + default: + WRITECSR(sc, R_CSR_SIAMODE1, 0x7F3F); + WRITECSR(sc, R_CSR_SIAMODE2, 0x0008); + opmode = M_CSR6_SPEED_10; + break; + } + + WRITECSR(sc, R_CSR_SIAMODE0, 0xEF00 | M_CSR13_CONN_NOT_RESET); + cfe_sleep(CFE_HZ/10); + + opmode |= V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72); + WRITECSR(sc, R_CSR_OPMODE, opmode); +} + +static void +dc21041_set_loopback(tulip_softc *sc, int mode) +{ + /* For now, always assume 10BT */ + uint32_t mode0; + + WRITECSR(sc, R_CSR_SIAMODE0, 0); + cfe_sleep(CFE_HZ/10); /* check this */ + + /* Update the SIA registers */ + if (mode == ETHER_LOOPBACK_EXT) { + /* NB: this is really just internal but through the 10BT endec */ + WRITECSR(sc, R_CSR_SIAMODE1, 0x7A3F); + WRITECSR(sc, R_CSR_SIAMODE2, 0x0008); + mode0 = 0; + } + else if (mode == ETHER_LOOPBACK_INT) { + /* MAC internal loopback, no SIA */ + WRITECSR(sc, R_CSR_SIAMODE1, 0x0000); + WRITECSR(sc, R_CSR_SIAMODE2, 0x000E); + mode0 = M_CSR13_CONN_AUI_10BT; + } + else { + mode = ETHER_LOOPBACK_OFF; + WRITECSR(sc, R_CSR_SIAMODE1, 0x7F3F); + WRITECSR(sc, R_CSR_SIAMODE2, 0x0008); + mode0 = 0; + } + + WRITECSR(sc, R_CSR_SIAMODE0, 0xEF00 | mode0 | M_CSR13_CONN_NOT_RESET ); + + sc->loopback = mode; +} + +static void +dc21041_hwinit(tulip_softc *sc, uint8_t srom[]) +{ + uint32_t v; + + sc->phy_type = SRL; + + /* CSR0 - bus mode */ + v = V_CSR0_SKIPLEN(0) | + V_CSR0_CACHEALIGN(K_CSR0_ALIGN32) | + V_CSR0_BURSTLEN(K_CSR0_BURST32); +#ifdef __MIPSEB + v |= M_CSR0_BIGENDIAN; /* big-endian data serialization */ +#endif + WRITECSR(sc, R_CSR_BUSMODE, v); + + WRITECSR(sc, R_CSR_INTMASK, 0); + + WRITECSR(sc, R_CSR_RXRING, PTR_TO_PCI(sc->rxdscr_start)); + WRITECSR(sc, R_CSR_TXRING, PTR_TO_PCI(sc->txdscr_start)); + + /* For now, always force 10BT, HDX (21041, Table 3-62) */ + dc21041_set_speed(sc, ETHER_SPEED_10HDX); +} + + +static void +dc21040_set_speed(tulip_softc *sc, int speed) +{ + uint32_t opmode = 0; + + WRITECSR(sc, R_CSR_SIAMODE0, 0); + + /* For now, force 10BT, HDX unless FDX requested (21040, Table 3-53) */ + switch (speed) { + case ETHER_SPEED_10HDX: + default: + WRITECSR(sc, R_CSR_SIAMODE1, 0xFFFF); + WRITECSR(sc, R_CSR_SIAMODE2, 0x0000); + opmode = 0; + break; + case ETHER_SPEED_10FDX: + WRITECSR(sc, R_CSR_SIAMODE1, 0xFFFD); + WRITECSR(sc, R_CSR_SIAMODE2, 0x0000); + opmode = M_CSR6_FULLDUPLEX; + break; + } + + WRITECSR(sc, R_CSR_SIAMODE0, 0xEF00 | M_CSR13_CONN_NOT_RESET); + cfe_sleep(CFE_HZ/10); + + opmode |= V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72); + WRITECSR(sc, R_CSR_OPMODE, opmode); +} + +static void +dc21040_set_loopback(tulip_softc *sc, int mode) +{ + WRITECSR(sc, R_CSR_SIAMODE0, 0); + cfe_sleep(CFE_HZ/10); /* check this */ + + /* Update the SIA registers */ + if (mode == ETHER_LOOPBACK_EXT) { + /* NB: this is on-chip loopback through the 10BT endec */ + WRITECSR(sc, R_CSR_SIAMODE1, 0xFEFB); + WRITECSR(sc, R_CSR_SIAMODE2, 0x0008); + } + else if (mode == ETHER_LOOPBACK_INT) { + /* MAC internal loopback, no SIA */ + WRITECSR(sc, R_CSR_SIAMODE1, 0x0000); + WRITECSR(sc, R_CSR_SIAMODE2, 0x0000); + } + else { + mode = ETHER_LOOPBACK_OFF; + WRITECSR(sc, R_CSR_SIAMODE1, 0xFFFF); + WRITECSR(sc, R_CSR_SIAMODE2, 0x0000); + } + + WRITECSR(sc, R_CSR_SIAMODE0, 0x8F00 | M_CSR13_CONN_NOT_RESET ); + + sc->loopback = mode; +} + +static void +dc21040_hwinit(tulip_softc *sc, uint8_t srom[]) +{ + uint32_t v; + + sc->phy_type = SRL; + + /* CSR0 - bus mode */ + v = V_CSR0_SKIPLEN(0) | + V_CSR0_CACHEALIGN(K_CSR0_ALIGN32) | + V_CSR0_BURSTLEN(K_CSR0_BURST32); +#ifdef __MIPSEB + v |= M_CSR0_BIGENDIAN; /* big-endian data serialization */ +#endif + WRITECSR(sc, R_CSR_BUSMODE, v); + + WRITECSR(sc, R_CSR_INTMASK, 0); + + dc21040_set_speed(sc, sc->linkspeed); +} + + +static void +tulip_hwinit(tulip_softc *sc) +{ + if (sc->state == eth_state_uninit) { + uint8_t srom[SROM_SIZE]; + + /* Wake-on-LAN apparently powers up with PORTSEL = 1 */ + WRITECSR(sc, R_CSR_OPMODE, + READCSR(sc, R_CSR_OPMODE) &~ M_CSR6_PORTSEL); + + RESET_ADAPTER(sc); + sc->state = eth_state_off; + sc->bus_errors = 0; + + rom_read_all(sc, srom); /* XXX read just once? */ + rom_dump(srom); + + switch (sc->device) { + case K_PCI_ID_DC21040: + dc21040_hwinit(sc, srom); + break; + case K_PCI_ID_DC21041: + dc21041_hwinit(sc, srom); + break; + case K_PCI_ID_DC21140: + dc21140_hwinit(sc, srom); + break; + case K_PCI_ID_DC21143: + dc21143_hwinit(sc, srom); + break; + default: + break; + } + } +} + +static void +tulip_setaddr(tulip_softc *sc) +{ + int idx; + tulip_cam *cam; + eth_pkt_t *pkt; + + pkt = eth_alloc_pkt(sc); + if (pkt) { + pkt->length = CAM_SETUP_BUFFER_SIZE; + cam = (tulip_cam *) pkt->buffer; + +#ifdef __MIPSEB + cam->p.physical[0][0] = (((uint32_t) sc->hwaddr[0] << 8) | + (uint32_t) sc->hwaddr[1]) << 16; + cam->p.physical[0][1] = (((uint32_t) sc->hwaddr[2] << 8) | + (uint32_t) sc->hwaddr[3]) << 16; + cam->p.physical[0][2] = (((uint32_t) sc->hwaddr[4] << 8) | + (uint32_t) sc->hwaddr[5]) << 16; + for (idx = 1; idx < CAM_PERFECT_ENTRIES; idx++) { + cam->p.physical[idx][0] = 0xFFFF0000; + cam->p.physical[idx][1] = 0xFFFF0000; + cam->p.physical[idx][2] = 0xFFFF0000; + } +#else + cam->p.physical[0][0] = ((uint32_t) sc->hwaddr[0]) | + (((uint32_t) sc->hwaddr[1]) << 8); + cam->p.physical[0][1] = ((uint32_t) sc->hwaddr[2]) | + (((uint32_t) sc->hwaddr[3]) << 8); + cam->p.physical[0][2] = ((uint32_t) sc->hwaddr[4]) | + (((uint32_t) sc->hwaddr[5]) << 8); + for (idx = 1; idx < CAM_PERFECT_ENTRIES; idx++) { + cam->p.physical[idx][0] = 0x0000FFFF; + cam->p.physical[idx][1] = 0x0000FFFF; + cam->p.physical[idx][2] = 0x0000FFFF; + } +#endif + + pkt->flags |= ETH_TX_SETUP; + sc->state = eth_state_setup; + if (tulip_transmit(sc, pkt) != 0) { + xprintf("%s: failed setup\n", tulip_devname(sc)); + dumpstat(sc); + eth_free_pkt(sc, pkt); + } + } +} + +static void +tulip_setspeed(tulip_softc *sc, int speed) +{ + switch (sc->device) { + case K_PCI_ID_DC21040: + dc21040_set_speed(sc, speed); + break; + case K_PCI_ID_DC21041: + dc21041_set_speed(sc, speed); + break; + case K_PCI_ID_DC21140: + dc21140_set_speed(sc, speed, 0); + break; + case K_PCI_ID_DC21143: + dc21143_set_speed(sc, speed); + break; + default: + break; + } +} + +static void +tulip_setloopback(tulip_softc *sc, int mode) +{ + switch (sc->device) { + case K_PCI_ID_DC21040: + dc21040_set_loopback(sc, mode); + break; + case K_PCI_ID_DC21041: + dc21041_set_loopback(sc, mode); + break; + case K_PCI_ID_DC21140: + dc21140_set_loopback(sc, mode); + break; + case K_PCI_ID_DC21143: + dc21143_set_loopback(sc, mode); + break; + default: + break; + } + cfe_sleep(CFE_HZ/10); +} + + +static void +tulip_isr(void *arg) +{ + uint32_t status; + uint32_t csr5; + tulip_softc *sc = (tulip_softc *)arg; + +#if IPOLL + sc->interrupts++; +#endif + + for (;;) { + + /* Read the interrupt status. */ + csr5 = READCSR(sc, R_CSR_STATUS); + status = csr5 & ( + M_CSR5_RXINT | M_CSR5_RXBUFUNAVAIL | + M_CSR5_TXINT | M_CSR5_TXUNDERFLOW | + M_CSR5_FATALBUSERROR); + + /* if there are no more interrupts, leave now. */ + if (status == 0) break; + + /* Clear the pending interrupt. */ + WRITECSR(sc, R_CSR_STATUS, status); + + /* Now, test each unmasked bit in the interrupt register and + handle each interrupt type appropriately. */ + + if (status & M_CSR5_FATALBUSERROR) { + WRITECSR(sc, R_CSR_INTMASK, 0); + + xprintf("%s: bus error %02x\n", + tulip_devname(sc), G_CSR5_ERRORBITS(csr5)); + dumpstat(sc); + sc->bus_errors++; + if (sc->bus_errors >= 2) { + dumpcsrs(sc); + RESET_ADAPTER(sc); + sc->state = eth_state_off; + sc->bus_errors = 0; + } +#if IPOLL + else + WRITECSR(sc, R_CSR_INTMASK, sc->intmask); +#endif + } + + if (status & M_CSR5_RXINT) { +#if IPOLL + sc->rx_interrupts++; +#endif + tulip_procrxring(sc); + } + + if (status & M_CSR5_TXINT) { +#if IPOLL + sc->tx_interrupts++; +#endif + tulip_proctxring(sc); + } + + if (status & (M_CSR5_TXUNDERFLOW | M_CSR5_RXBUFUNAVAIL)) { + if (status & M_CSR5_TXUNDERFLOW) { + xprintf("%s: tx underrun, %08x\n", tulip_devname(sc), csr5); + /* Try to restart */ + WRITECSR(sc, R_CSR_TXPOLL, 1); + } + if (status & M_CSR5_RXBUFUNAVAIL) { + /* Try to restart */ + WRITECSR(sc, R_CSR_RXPOLL, 1); + } + } + } +} + + +static void +tulip_start(tulip_softc *sc) +{ + uint32_t opmode; + + tulip_hwinit(sc); + + WRITECSR(sc, R_CSR_RXRING, PTR_TO_PCI(sc->rxdscr_start)); + WRITECSR(sc, R_CSR_TXRING, PTR_TO_PCI(sc->txdscr_start)); + + opmode = READCSR(sc, R_CSR_OPMODE); + opmode &=~ M_CSR6_OPMODE; /* no loopback */ + if (sc->loopback != ETHER_LOOPBACK_OFF) { + opmode &=~ M_CSR6_FULLDUPLEX; + opmode |= M_CSR6_PORTSEL; + if (sc->loopback == ETHER_LOOPBACK_EXT) + opmode |= M_CSR6_EXTLOOPBACK; + else + opmode |= M_CSR6_INTLOOPBACK; + } + + sc->intmask = 0; + WRITECSR(sc, R_CSR_INTMASK, 0); /* no interrupts */ + WRITECSR(sc, R_CSR_STATUS, 0x1FFFF); /* clear any pending */ + READCSR(sc, R_CSR_STATUS); /* push the write */ + + sc->interrupts = 0; + sc->rx_interrupts = sc->tx_interrupts = 0; + +#if IPOLL + cfe_request_irq(sc->irq, tulip_isr, sc, CFE_IRQ_FLAGS_SHARED, 0); + + sc->intmask = M_CSR7_RXINT | M_CSR7_TXINT | + M_CSR7_NORMALINT; + sc->intmask |= M_CSR7_FATALBUSERROR | M_CSR7_TXUNDERFLOW | + M_CSR7_ABNORMALINT; + WRITECSR(sc, R_CSR_INTMASK, sc->intmask); +#endif + + if (sc->loopback == ETHER_LOOPBACK_OFF) { + opmode |= M_CSR6_TXSTART; + WRITECSR(sc, R_CSR_OPMODE, opmode); + tulip_setaddr(sc); + } + else { + opmode |= M_CSR6_TXSTART | M_CSR6_RXSTART; + WRITECSR(sc, R_CSR_OPMODE, opmode); + } +} + +static void +tulip_stop(tulip_softc *sc) +{ + uint32_t opmode; + uint32_t status; + int count; + + WRITECSR(sc, R_CSR_INTMASK, 0); + sc->intmask = 0; +#if IPOLL + cfe_free_irq(sc->irq, 0); +#endif + WRITECSR(sc, R_CSR_STATUS, 0x1FFFF); + opmode = READCSR(sc, R_CSR_OPMODE); + opmode &=~ (M_CSR6_TXSTART | M_CSR6_RXSTART); + WRITECSR(sc, R_CSR_OPMODE, opmode); + + /* wait for any DMA activity to terminate */ + for (count = 0; count <= 13; count++) { + status = READCSR(sc, R_CSR_STATUS); + if ((status & (M_CSR5_RXPROCSTATE | M_CSR5_TXPROCSTATE)) == 0) + break; + cfe_sleep(CFE_HZ/10); + } + if (count > 13) { + xprintf("%s: idle state not achieved\n", tulip_devname(sc)); + dumpstat(sc); + RESET_ADAPTER(sc); + sc->state = eth_state_uninit; +#if 1 + sc->linkspeed = ETHER_SPEED_AUTO; +#endif + } + else if (sc->loopback != ETHER_LOOPBACK_OFF) { + tulip_setloopback(sc, ETHER_LOOPBACK_OFF); + opmode &=~ M_CSR6_OPMODE; + WRITECSR(sc, R_CSR_OPMODE, opmode); + } + + if (sc->outpkts > 1) { + /* heuristic: suppress stats for initial mode changes */ + xprintf("%s: %d sent, %d received, %d interrupts\n", + tulip_devname(sc), sc->outpkts, sc->inpkts, sc->interrupts); + xprintf(" %d rx interrupts, %d tx interrupts\n", + sc->rx_interrupts, sc->tx_interrupts); + } +} + + +/* ********************************************************************* + * ETH_PARSE_XDIGIT(c) + * + * Parse a hex digit, returning its value + * + * Input parameters: + * c - character + * + * Return value: + * hex value, or -1 if invalid + ********************************************************************* */ +static int +eth_parse_xdigit(char c) +{ + int digit; + + if ((c >= '0') && (c <= '9')) digit = c - '0'; + else if ((c >= 'a') && (c <= 'f')) digit = c - 'a' + 10; + else if ((c >= 'A') && (c <= 'F')) digit = c - 'A' + 10; + else digit = -1; + + return digit; +} + +/* ********************************************************************* + * ETH_PARSE_HWADDR(str,hwaddr) + * + * Convert a string in the form xx:xx:xx:xx:xx:xx into a 6-byte + * Ethernet address. + * + * Input parameters: + * str - string + * hwaddr - pointer to hardware address + * + * Return value: + * 0 if ok, else -1 + ********************************************************************* */ +static int +eth_parse_hwaddr(char *str, uint8_t *hwaddr) +{ + int digit1, digit2; + int idx = ENET_ADDR_LEN; + + while (*str && (idx > 0)) { + digit1 = eth_parse_xdigit(*str); + if (digit1 < 0) return -1; + str++; + if (!*str) return -1; + + if ((*str == ':') || (*str == '-')) { + digit2 = digit1; + digit1 = 0; + } + else { + digit2 = eth_parse_xdigit(*str); + if (digit2 < 0) return -1; + str++; + } + + *hwaddr++ = (digit1 << 4) | digit2; + idx--; + + if ((*str == ':') || (*str == '-')) + str++; + } + return 0; +} + +/* ********************************************************************* + * ETH_INCR_HWADDR(hwaddr,incr) + * + * Increment a 6-byte Ethernet hardware address, with carries + * + * Input parameters: + * hwaddr - pointer to hardware address + * incr - desired increment + * + * Return value: + * none + ********************************************************************* */ +static void +eth_incr_hwaddr(uint8_t *hwaddr, unsigned incr) +{ + int idx; + int carry; + + idx = 5; + carry = incr; + while (idx >= 0 && carry != 0) { + unsigned sum = hwaddr[idx] + carry; + + hwaddr[idx] = sum & 0xFF; + carry = sum >> 8; + idx--; + } +} + + +/* ********************************************************************* + * Declarations for CFE Device Driver Interface routines + ********************************************************************* */ + +static int tulip_ether_open(cfe_devctx_t *ctx); +static int tulip_ether_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int tulip_ether_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); +static int tulip_ether_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int tulip_ether_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int tulip_ether_close(cfe_devctx_t *ctx); +#if 0 +static void tulip_ether_reset(void *softc); +#endif + +/* ********************************************************************* + * CFE Device Driver dispatch structure + ********************************************************************* */ + +const static cfe_devdisp_t tulip_ether_dispatch = { + tulip_ether_open, + tulip_ether_read, + tulip_ether_inpstat, + tulip_ether_write, + tulip_ether_ioctl, + tulip_ether_close, + NULL, /* tulip_ether_poll */ + NULL /* tulip_ether_reset */ +}; + +/* ********************************************************************* + * CFE Device Driver descriptor + ********************************************************************* */ + +const cfe_driver_t dc21143drv = { + "DC21x4x Ethernet", + "eth", + CFE_DEV_NETWORK, + &tulip_ether_dispatch, + tulip_ether_probe +}; + + +static int +tulip_ether_attach(cfe_driver_t *drv, + pcitag_t tag, int index, uint8_t hwaddr[]) +{ + tulip_softc *softc; + uint32_t device; + uint32_t class; + uint32_t reg; + phys_addr_t pa; + const char *devname; + char descr[100]; + uint8_t romaddr[ENET_ADDR_LEN]; + + device = pci_conf_read(tag, R_CFG_CFID); + class = pci_conf_read(tag, R_CFG_CFRV); + + reg = pci_conf_read(tag, R_CFG_CPMS); + + reg = pci_conf_read(tag, R_CFG_CFDD); + pci_conf_write(tag, R_CFG_CFDD, 0); + reg = pci_conf_read(tag, R_CFG_CFDD); + +#if 1 + /* Use memory space for the CSRs */ + pci_map_mem(tag, R_CFG_CBMA, PCI_MATCH_BITS, &pa); +#else + /* Use i/o space for the CSRs */ + pci_map_io(tag, R_CFG_CBIO, PCI_MATCH_BITS, &pa); +#endif + + softc = (tulip_softc *) KMALLOC(sizeof(tulip_softc), 0); + if (softc == NULL) { + xprintf("DC21x4x: No memory to complete probe\n"); + return 0; + } + memset(softc, 0, sizeof(*softc)); + + softc->membase = (uint32_t)pa; + softc->irq = pci_conf_read(tag, R_CFG_CFIT) & 0xFF; + + softc->tag = tag; + softc->device = PCI_PRODUCT(device); + softc->revision = PCI_REVISION(class); + softc->devctx = NULL; + +#if 1 + softc->linkspeed = ETHER_SPEED_AUTO; /* select autonegotiation */ +#else + softc->linkspeed = ETHER_SPEED_100FDX; /* 100 Mbps, full duplex */ +#endif + softc->loopback = ETHER_LOOPBACK_OFF; + memcpy(softc->hwaddr, hwaddr, ENET_ADDR_LEN); + + tulip_init(softc); + + /* Prefer address in srom */ + if (rom_read_addr(softc, romaddr) == 0) { + memcpy(softc->hwaddr, romaddr, ENET_ADDR_LEN); + } + + softc->state = eth_state_uninit; + + switch (PCI_PRODUCT(device)) { + case K_PCI_ID_DC21040: + devname = "DC21040"; break; + case K_PCI_ID_DC21041: + devname = "DC21041"; break; + case K_PCI_ID_DC21140: + devname = "DC21140"; break; + case K_PCI_ID_DC21143: + devname = "DC21143"; break; + default: + devname = "DC21x4x"; break; + } + + xsprintf(descr, "%s Ethernet at 0x%X (%02X-%02X-%02X-%02X-%02X-%02X)", + devname, softc->membase, + softc->hwaddr[0], softc->hwaddr[1], softc->hwaddr[2], + softc->hwaddr[3], softc->hwaddr[4], softc->hwaddr[5]); + + cfe_attach(drv, softc, NULL, descr); + return 1; +} + + +/* ********************************************************************* + * TULIP_ETHER_PROBE(drv,probe_a,probe_b,probe_ptr) + * + * Probe and install drivers for all DC21x4x Ethernet controllers. + * For each, create a context structure and attach to the + * specified network device. + * + * Input parameters: + * drv - driver descriptor + * probe_a - not used + * probe_b - not used + * probe_ptr - string pointer to hardware address for the first + * MAC, in the form xx:xx:xx:xx:xx:xx + * + * Return value: + * nothing + ********************************************************************* */ +static void +tulip_ether_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + int index; + int n; + uint8_t hwaddr[ENET_ADDR_LEN]; + + if (probe_ptr) + eth_parse_hwaddr((char *) probe_ptr, hwaddr); + else { + /* use default address 40-00-00-10-11-11 */ + hwaddr[0] = 0x40; hwaddr[1] = 0x00; hwaddr[2] = 0x00; + hwaddr[3] = 0x10; hwaddr[4] = 0x11; hwaddr[5] = 0x11; + } + + n = 0; + index = 0; + for (;;) { + pcitag_t tag; + pcireg_t device; + + if (pci_find_class(PCI_CLASS_NETWORK, index, &tag) != 0) + break; + + index++; + + device = pci_conf_read(tag, R_CFG_CFID); + if (PCI_VENDOR(device) == K_PCI_VENDOR_DEC) { +#if 0 /* this currently (2.1.1) generates a bad code in PIC mode */ + switch (PCI_PRODUCT(device)) { + case K_PCI_ID_DC21040: + case K_PCI_ID_DC21041: + case K_PCI_ID_DC21140: + case K_PCI_ID_DC21143: + tulip_ether_attach(drv, tag, n, hwaddr); + n++; + eth_incr_hwaddr(hwaddr, 1); + break; + default: + break; + } +#else + if (PCI_PRODUCT(device) == K_PCI_ID_DC21040 || + PCI_PRODUCT(device) == K_PCI_ID_DC21041 || + PCI_PRODUCT(device) == K_PCI_ID_DC21140 || + PCI_PRODUCT(device) == K_PCI_ID_DC21143) { + + tulip_ether_attach(drv, tag, n, hwaddr); + n++; + eth_incr_hwaddr(hwaddr, 1); + } +#endif + } + } +} + + +/* The functions below are called via the dispatch vector for the 21x4x. */ + +/* ********************************************************************* + * TULIP_ETHER_OPEN(ctx) + * + * Open the Ethernet device. The MAC is reset, initialized, and + * prepared to receive and send packets. + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +tulip_ether_open(cfe_devctx_t *ctx) +{ + tulip_softc *softc = ctx->dev_softc; + + if (softc->state == eth_state_on) + tulip_stop(softc); + + softc->devctx = ctx; + tulip_start(softc); + +#if XPOLL + tulip_isr(softc); +#endif + + return 0; +} + +/* ********************************************************************* + * TULIP_ETHER_READ(ctx,buffer) + * + * Read a packet from the Ethernet device. If no packets are + * available, the read will succeed but return 0 bytes. + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * buffer - pointer to buffer descriptor. + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +tulip_ether_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + tulip_softc *softc = ctx->dev_softc; + eth_pkt_t *pkt; + int blen; + +#if XPOLL + tulip_isr(softc); +#endif + + if (softc->state != eth_state_on) return -1; + + CS_ENTER(softc); + pkt = (eth_pkt_t *) q_deqnext(&(softc->rxqueue)); + CS_EXIT(softc); + + if (pkt == NULL) { + buffer->buf_retlen = 0; + return 0; + } + + blen = buffer->buf_length; + if (blen > pkt->length) blen = pkt->length; + + blockcopy(buffer->buf_ptr, pkt->buffer, blen); + buffer->buf_retlen = blen; + + eth_free_pkt(softc, pkt); + tulip_fillrxring(softc); + +#if XPOLL + tulip_isr(softc); +#endif + + return 0; +} + +/* ********************************************************************* + * TULIP_ETHER_INPSTAT(ctx,inpstat) + * + * Check for received packets on the Ethernet device + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * inpstat - pointer to input status structure + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +tulip_ether_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat) +{ + tulip_softc *softc = ctx->dev_softc; + +#if XPOLL + tulip_isr(softc); +#endif + + if (softc->state != eth_state_on) return -1; + + /* We avoid an interlock here because the result is a hint and an + interrupt cannot turn a non-empty queue into an empty one. */ + inpstat->inp_status = (q_isempty(&(softc->rxqueue))) ? 0 : 1; + + return 0; +} + +/* ********************************************************************* + * TULIP_ETHER_WRITE(ctx,buffer) + * + * Write a packet to the Ethernet device. + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * buffer - pointer to buffer descriptor. + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +tulip_ether_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + tulip_softc *softc = ctx->dev_softc; + eth_pkt_t *pkt; + int blen; + +#if XPOLL + tulip_isr(softc); +#endif + + if (softc->state != eth_state_on) return -1; + + pkt = eth_alloc_pkt(softc); + if (!pkt) return CFE_ERR_NOMEM; + + blen = buffer->buf_length; + if (blen > pkt->length) blen = pkt->length; + + blockcopy(pkt->buffer, buffer->buf_ptr, blen); + pkt->length = blen; + + if (tulip_transmit(softc, pkt) != 0) { + eth_free_pkt(softc,pkt); + return CFE_ERR_IOERR; + } + +#if XPOLL + tulip_isr(softc); +#endif + + return 0; +} + +/* ********************************************************************* + * TULIP_ETHER_IOCTL(ctx,buffer) + * + * Do device-specific I/O control operations for the device + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * buffer - pointer to buffer descriptor. + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +tulip_ether_ioctl(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + tulip_softc *softc = ctx->dev_softc; + int *argp; + int mode; + int speed; + + switch ((int)buffer->buf_ioctlcmd) { + case IOCTL_ETHER_GETHWADDR: + memcpy(buffer->buf_ptr, softc->hwaddr, sizeof(softc->hwaddr)); + return 0; + + case IOCTL_ETHER_SETHWADDR: + return -1; /* not supported */ + + case IOCTL_ETHER_GETSPEED: + argp = (int *) buffer->buf_ptr; + *argp = softc->linkspeed; + return 0; + + case IOCTL_ETHER_SETSPEED: + tulip_stop(softc); + tulip_resetrings(softc); + speed = *((int *) buffer->buf_ptr); + tulip_setspeed(softc, speed); + tulip_start(softc); + softc->state = eth_state_on; + return 0; + + case IOCTL_ETHER_GETLINK: + argp = (int *) buffer->buf_ptr; + *argp = softc->linkspeed; + return 0; + + case IOCTL_ETHER_GETLOOPBACK: + *((int *) buffer) = softc->loopback; + return 0; + + case IOCTL_ETHER_SETLOOPBACK: + tulip_stop(softc); + tulip_resetrings(softc); + mode = *((int *) buffer->buf_ptr); + softc->loopback = ETHER_LOOPBACK_OFF; /* default */ + if (mode == ETHER_LOOPBACK_INT || mode == ETHER_LOOPBACK_EXT) { + tulip_setloopback(softc, mode); + } + tulip_start(softc); + softc->state = eth_state_on; + return 0; + + default: + return -1; + } +} + +/* ********************************************************************* + * TULIP_ETHER_CLOSE(ctx) + * + * Close the Ethernet device. + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * + * Return value: + * status, 0 = ok + ********************************************************************* */ +static int +tulip_ether_close(cfe_devctx_t *ctx) +{ + tulip_softc *softc = ctx->dev_softc; + + softc->state = eth_state_off; + tulip_stop(softc); + + /* resynchronize descriptor rings */ + tulip_resetrings(softc); + + softc->devctx = NULL; + return 0; +} + + +#if 0 +/* ********************************************************************* + * TULIP_ETHER_POLL(ctx,ticks) + * + * TBD + * + * Input parameters: + * ctx - device context (includes ptr to our softc) + * ticks- current time in ticks + * + * Return value: + * nothing + ********************************************************************* */ + +static void +tulip_ether_poll(cfe_devctx_t *ctx, int64_t ticks) +{ +} + +/* ********************************************************************* + * TULIP_ETHER_RESET(softc) + * + * This routine is called when CFE is restarted after a + * program exits. We can clean up pending I/Os here. + * + * Input parameters: + * softc - pointer to tulip_softc + * + * Return value: + * nothing + ********************************************************************* */ + +static void +tulip_ether_reset(void *softc) +{ + tulip_softc *sc = (tulip_softc *)softc; + + /* Turn off the Ethernet interface. */ + + RESET_ADAPTER(sc); +} +#endif diff --git a/cfe/cfe/dev/dp83815.h b/cfe/cfe/dev/dp83815.h new file mode 100644 index 0000000..1d50422 --- /dev/null +++ b/cfe/cfe/dev/dp83815.h @@ -0,0 +1,559 @@ +/* + * Register and bit definitions for the National Semiconductor + * DP83815 10/100 Integrated Ethernet MAC and PHY. + * Reference: + * DP83815 10/100 Mb/s Integrated PCI Ethernet Media Access + * Controller and Physical Layer (MacPhyter) + * Hardware Reference Manual, Revision 1.0. + * National Semiconductor Corp., December 2000 + */ +#ifndef _DP83815_H_ +#define _DP83815_H_ + +#define _DD_MAKEMASK1(n) (1 << (n)) +#define _DD_MAKEMASK(v,n) ((((1)<<(v))-1) << (n)) +#define _DD_MAKEVALUE(v,n) ((v) << (n)) +#define _DD_GETVALUE(v,n,m) (((v) & (m)) >> (n)) + + +/* PCI Configuration Register offsets (MacPhyter nomenclature) */ + +#define R_CFGID PCI_ID_REG +#define K_PCI_VENDOR_NSC 0x100B +#define K_PCI_ID_DP83815 0x0020 + +#define R_CFGCS PCI_COMMAND_STATUS_REG +#define R_CFGRID PCI_CLASS_REG +#define R_CFGLAT PCI_BHLC_REG + +#define R_CFGIOA PCI_MAPREG(0) +#define R_CFGMA PCI_MAPREG(1) + +#define R_CFGSID PCI_SUBSYS_ID_REG +#define R_CFGROM PCI_MAPREG_ROM +#define R_CAPPTR PCI_CAPLISTPTR_REG +#define R_CFGINT PCI_BPARAM_INTERRUPT_REG + +/* Power management capability */ +#define R_PMCAP 0x40 +#define R_PMCSR 0x44 + + +/* MacPhyter Operational Register offsets */ + +#define R_CR 0x00 +#define R_CFG 0x04 +#define R_MEAR 0x08 +#define R_PTSCR 0x0C +#define R_ISR 0x10 +#define R_IMR 0x14 +#define R_IER 0x18 +#define R_TXDP 0x20 +#define R_TXCFG 0x24 +#define R_RXDP 0x30 +#define R_RXCFG 0x34 +#define R_CCSR 0x3C +#define R_WCSR 0x40 +#define R_PCR 0x44 +#define R_RFCR 0x48 +#define R_RFDR 0x4C +#define R_BRAR 0x50 +#define R_BRDR 0x54 +#define R_SRR 0x58 +#define R_MIBC 0x5C +#define R_MIB(n) (0x60+4*(n)) + +/* MacPhyter MIB Registers */ + +#define R_MIB_RXErroredPkts R_MIB(0) +#define R_MIB_RXFCSErrors R_MIB(1) +#define R_MIB_RXMsdPktErrors R_MIB(2) +#define R_MIB_RXFAErrors R_MIB(3) +#define R_MIB_RXSymbolErrors R_MIB(4) +#define R_MIB_RXFrameTooLong R_MIB(5) +#define R_MIB_TXSQEErrors R_MIB(6) + +#define N_MIB_REGISTERS 7 + + +/* MacPhyter Internal PHY Register offsets */ + +#define R_BMCR 0x80 +#define R_BMSR 0x84 +#define R_PHYIDR1 0x88 +#define R_PHYIDR2 0x8C +#define R_ANAR 0x90 +#define R_ANLPAR 0x94 +#define R_ANER 0x98 +#define R_ANNPTR 0x9C +#define R_PHYSTS 0xC0 +#define R_MICR 0xC4 +#define R_MISR 0xC8 +#define R_FCSCR 0xD0 +#define R_RECR 0xD4 +#define R_PHYCR 0xE4 +#define R_TBTSCR 0xE8 + +/* Undocumented, updated for CVNG (SRR=0x0302) parts */ + +#define R_PGSEL 0xCC +#define R_PMDCSR 0xE4 /* R_PHYCR */ +#define R_DSPCFG 0xF4 +#define R_SDCFG 0xF8 +#define R_TSTDAT 0xFC + + +/* 0x00 CR: Command Register */ + +#define M_CR_TXE _DD_MAKEMASK1(0) +#define M_CR_TXD _DD_MAKEMASK1(1) +#define M_CR_RXE _DD_MAKEMASK1(2) +#define M_CR_RXD _DD_MAKEMASK1(3) +#define M_CR_TXR _DD_MAKEMASK1(4) +#define M_CR_RXR _DD_MAKEMASK1(5) +#define M_CR_SWI _DD_MAKEMASK1(7) +#define M_CR_RST _DD_MAKEMASK1(8) + + +/* 0x04 CFG: Configuration and Media Status Register */ + +#define M_CFG_BEM _DD_MAKEMASK1(0) +#define M_CFG_BROM_DIS _DD_MAKEMASK1(2) +#define M_CFG_PESEL _DD_MAKEMASK1(3) +#define M_CFG_EXD _DD_MAKEMASK1(4) +#define M_CFG_POW _DD_MAKEMASK1(5) +#define M_CFG_SB _DD_MAKEMASK1(6) +#define M_CFG_REQALG _DD_MAKEMASK1(7) +#define M_CFG_EUPHCOMP _DD_MAKEMASK1(8) +#define M_CFG_PHY_DIS _DD_MAKEMASK1(9) +#define M_CFG_PHY_RST _DD_MAKEMASK1(10) +#define M_CFG_EXT_PHY _DD_MAKEMASK1(12) + +#define S_CFG_ANEG_SEL 13 +#define M_CFG_ANEG_SEL _DD_MAKEMASK(3,S_CFG_ANEG_SEL) +#define V_CFG_ANEG_SEL(x) _DD_MAKEVALUE(x,S_CFG_ANEG_SEL) +#define G_CFG_ANEG_SEL(x) _DD_GETVALUE(x,S_CFG_ANEG_SEL,M_CFG_ANEG_SEL) + +#define K_ANEG_10H 0x0 +#define K_ANEG_100H 0x2 +#define K_ANEG_10F 0x4 +#define K_ANEG_100F 0x6 +#define K_ANEG_10HF 0x1 +#define K_ANEG_10H_100H 0x3 +#define K_ANEG_100HF 0x5 +#define K_ANEG_10HF_100HF 0x7 +#define K_ANEG_ALL 0x7 + +#define M_CFG_PAUSE_ADV _DD_MAKEMASK1(16) +#define M_CFG_PINT_ACEN _DD_MAKEMASK1(17) + +#define S_CFG_PHY_CFG 18 +#define M_CFG_PHY_CFG _DD_MAKEMASK(6,S_CFG_PHY_CFG) + +#define M_CFG_ANEG_DN _DD_MAKEMASK1(27) +#define M_CFG_POL _DD_MAKEMASK1(28) +#define M_CFG_FDUP _DD_MAKEMASK1(29) +#define M_CFG_SPEED100 _DD_MAKEMASK1(30) +#define M_CFG_LNKSTS _DD_MAKEMASK1(31) +#define M_CFG_LNKSUMMARY (M_CFG_LNKSTS | M_CFG_SPEED100 | M_CFG_FDUP) + + +/* 0x08 MEAR: EEPROM Access Register (see 4.2.4 for EEPROM Map) */ + +#define M_MEAR_EEDI _DD_MAKEMASK1(0) +#define M_MEAR_EEDO _DD_MAKEMASK1(1) +#define M_MEAR_EECLK _DD_MAKEMASK1(2) +#define M_MEAR_EESEL _DD_MAKEMASK1(3) +#define M_MEAR_MDIO _DD_MAKEMASK1(4) +#define M_MEAR_MDDIR _DD_MAKEMASK1(5) +#define M_MEAR_MDC _DD_MAKEMASK1(6) + + +/* 0x0C PTSCR: PCI Test Control Register */ + +#define M_PTSCR_EEBIST_FAIL _DD_MAKEMASK1(0) +#define M_PTSCR_EEBIST_EN _DD_MAKEMASK1(1) +#define M_PTSCR_EELOAD_EN _DD_MAKEMASK1(2) +#define M_PTSCR_RBIST_RXFFAIL _DD_MAKEMASK1(3) +#define M_PTSCR_RBIST_TXFAIL _DD_MAKEMASK1(4) +#define M_PTSCR_RBIST_RXFAIL _DD_MAKEMASK1(5) +#define M_PTSCR_RBIST_DONE _DD_MAKEMASK1(6) +#define M_PTSCR_RBIST_EN _DD_MAKEMASK1(7) +#define M_PTSCR_MBZ8 _DD_MAKEMASK1(8) +#define M_PTSCR_MBZ9 _DD_MAKEMASK1(9) +#define M_PTSCR_RBIST_RST _DD_MAKEMASK1(10) +#define M_PTSCR_MBZ12 _DD_MAKEMASK1(12) + + +/* 0x10 ISR: Interrupt Status Register */ +/* 0x14 IMR: Interrupt Mask Register */ + +#define M_INT_RXOK _DD_MAKEMASK1(0) +#define M_INT_RXDESC _DD_MAKEMASK1(1) +#define M_INT_RXERR _DD_MAKEMASK1(2) +#define M_INT_RXEARLY _DD_MAKEMASK1(3) +#define M_INT_RXIDLE _DD_MAKEMASK1(4) +#define M_INT_RXORN _DD_MAKEMASK1(5) +#define M_INT_TXOK _DD_MAKEMASK1(6) +#define M_INT_TXDESC _DD_MAKEMASK1(7) +#define M_INT_TXERR _DD_MAKEMASK1(8) +#define M_INT_TXIDLE _DD_MAKEMASK1(9) +#define M_INT_TXURN _DD_MAKEMASK1(10) +#define M_INT_MIB _DD_MAKEMASK1(11) +#define M_INT_SWI _DD_MAKEMASK1(12) +#define M_INT_PME _DD_MAKEMASK1(13) +#define M_INT_PHY _DD_MAKEMASK1(14) +#define M_INT_HIBERR _DD_MAKEMASK1(15) +#define M_INT_RXSOVR _DD_MAKEMASK1(16) +#define M_INT_RTABT _DD_MAKEMASK1(20) +#define M_INT_RMABT _DD_MAKEMASK1(21) +#define M_INT_SSERR _DD_MAKEMASK1(22) +#define M_INT_DPERR _DD_MAKEMASK1(23) +#define M_INT_RXRCMP _DD_MAKEMASK1(24) +#define M_INT_TXRCMP _DD_MAKEMASK1(25) + + +/* 0x18 IER: Interrupt Enable Register */ + +#define M_IER_IE _DD_MAKEMASK1(0) + + +/* 0x20 TXDP: Transmit Descriptor Pointer Register */ + + +/* 0x24 TXCFG: Transmit Configuration Register */ + +#define S_TXCFG_DRTH 0 +#define M_TXCFG_DRTH _DD_MAKEMASK(6,S_TXCFG_DRTH) +#define V_TXCFG_DRTH(x) _DD_MAKEVALUE(x,S_TXCFG_DRTH) +#define G_TXCFG_DRTH(x) _DD_GETVALUE(x,S_TXCFG_DRTH,M_TXCFG_DRTH) + +#define S_TXCFG_FLTH 8 +#define M_TXCFG_FLTH _DD_MAKEMASK(6,S_TXCFG_FLTH) +#define V_TXCFG_FLTH(x) _DD_MAKEVALUE(x,S_TXCFG_FLTH) +#define G_TXCFG_FLTH(x) _DD_GETVALUE(x,S_TXCFG_FLTH,M_TXCFG_FLTH) + +#define S_TXCFG_MXDMA 20 +#define M_TXCFG_MXDMA _DD_MAKEMASK(3,S_TXCFG_MXDMA) +#define V_TXCFG_MXDMA(x) _DD_MAKEVALUE(x,S_TXCFG_MXDMA) +#define G_TXCFG_MXDMA(x) _DD_GETVALUE(x,S_TXCFG_MXDMA,M_TXCFG_MXDMA) + +/* Max DMA burst size (bytes) - RX also */ +#define K_MXDMA_512 0x0 +#define K_MXDMA_4 0x1 +#define K_MXDMA_8 0x2 +#define K_MXDMA_16 0x3 +#define K_MXDMA_32 0x4 +#define K_MXDMA_64 0x5 +#define K_MXDMA_128 0x6 +#define K_MXDMA_256 0x7 + +#define M_TXCFG_ECRETRY _DD_MAKEMASK1(23) + +#define S_TXCFG_IFG 26 +#define M_TXCFG_IFG _DD_MAKEMASK(2,S_TXCFG_IFG) +#define V_TXCFG_IFG(x) _DD_MAKEVALUE(x,S_TXCFG_IFG) +#define G_TXCFG_IFG(x) _DD_GETVALUE(x,S_TXCFG_IFG,M_TXCFG_IFG) + +#define M_TXCFG_ATP _DD_MAKEMASK1(28) +#define M_TXCFG_MLB _DD_MAKEMASK1(29) +#define M_TXCFG_HBI _DD_MAKEMASK1(30) +#define M_TXCFG_CSI _DD_MAKEMASK1(31) + + +/* 0x30 RXDP: Receive Descriptor Pointer Register */ + + +/* 0x34 RXCFG: Receive Configuration Register */ + +#define S_RXCFG_DRTH 1 +#define M_RXCFG_DRTH _DD_MAKEMASK(5,S_RXCFG_DRTH) +#define V_RXCFG_DRTH(x) _DD_MAKEVALUE(x,S_RXCFG_DRTH) +#define G_RXCFG_DRTH(x) _DD_GETVALUE(x,S_RXCFG_DRTH,M_RXCFG_DRTH) + +#define S_RXCFG_MXDMA 20 +#define M_RXCFG_MXDMA _DD_MAKEMASK(3,S_RXCFG_MXDMA) +#define V_RXCFG_MXDMA(x) _DD_MAKEVALUE(x,S_RXCFG_MXDMA) +#define G_RXCFG_MXDMA(x) _DD_GETVALUE(x,S_RXCFG_MXDMA,M_RXCFG_MXDMA) + +#define M_RXCFG_ALP _DD_MAKEMASK1(27) +#define M_RXCFG_ATX _DD_MAKEMASK1(28) +#define M_RXCFG_ARP _DD_MAKEMASK1(30) +#define M_RXCFG_AEP _DD_MAKEMASK1(31) + + +/* 0x3C CCSR: CLKRUN Control/Status Register */ + +#define M_CCSR_CLKRUN_EN _DD_MAKEMASK1(0) +#define M_CCSR_PMEEN _DD_MAKEMASK1(8) +#define M_CCSR_PMESTS _DD_MAKEMASK1(15) + + +/* 0x40 WCSR: Wake Command/Status Register */ + +#define M_WCSR_WKPHY _DD_MAKEMASK1(0) +#define M_WCSR_WKUCP _DD_MAKEMASK1(1) +#define M_WCSR_WKMCP _DD_MAKEMASK1(2) +#define M_WCSR_WKBCP _DD_MAKEMASK1(3) +#define M_WCSR_WKARP _DD_MAKEMASK1(4) +#define M_WCSR_WKPAT0 _DD_MAKEMASK1(5) +#define M_WCSR_WKPAT1 _DD_MAKEMASK1(6) +#define M_WCSR_WKPAT2 _DD_MAKEMASK1(7) +#define M_WCSR_WKPAT3 _DD_MAKEMASK1(8) +#define M_WCSR_WKMAG _DD_MAKEMASK1(9) +#define M_WCSR_MPSOE _DD_MAKEMASK1(10) +#define M_WCSR_SOHACK _DD_MAKEMASK1(20) +#define M_WCSR_PHYINT _DD_MAKEMASK1(22) +#define M_WCSR_UCASTR _DD_MAKEMASK1(23) +#define M_WCSR_MCASTR _DD_MAKEMASK1(24) +#define M_WCSR_BCASTR _DD_MAKEMASK1(25) +#define M_WCSR_ARPR _DD_MAKEMASK1(26) +#define M_WCSR_PATM0 _DD_MAKEMASK1(27) +#define M_WCSR_PATM1 _DD_MAKEMASK1(28) +#define M_WCSR_PATM2 _DD_MAKEMASK1(29) +#define M_WCSR_PATM3 _DD_MAKEMASK1(30) +#define M_WCSR_MPR _DD_MAKEMASK1(31) + + +/* 0x44 PCR: Pause Control/Status Register */ + +#define S_PCR_PAUSE_CNT 0 +#define M_PCR_PAUSE_CNT _DD_MAKEMASK(16,S_PCR_PAUSE_CNT) +#define V_PCR_PAUSE_CNT(x) _DD_MAKEVALUE(x,S_PCR_PAUSE_CNT) +#define G_PCR_PAUSE_CNT(x) _DD_GETVALUE(x,S_PCR_PAUSE_CNT,M_PCR_PAUSE_CNT) + +#define M_PCR_MLD_EN _DD_MAKEMASK1(16) +#define M_PCR_PSNEG _DD_MAKEMASK1(21) +#define M_PCR_PS_RCVD _DD_MAKEMASK1(22) +#define M_PCR_PS_ACT _DD_MAKEMASK1(23) +#define M_PCR_PS_DA _DD_MAKEMASK1(29) +#define M_PCR_PS_MCAST _DD_MAKEMASK1(30) +#define M_PCR_PSEN _DD_MAKEMASK1(31) + + +/* 0x48 RFCR: Receive Filter/Match Control Register */ + + +#define S_RFCR_RFADDR 0 +#define M_RFCR_RFADDR _DD_MAKEMASK(10,S_RFCR_RFADDR) +#define V_RFCR_RFADDR(x) _DD_MAKEVALUE(x,S_RFCR_RFADDR) +#define G_RFCR_RFADDR(x) _DD_GETVALUE(x,S_RFCR_RFADDR,M_RFCR_RFADDR) + +#define K_RFCR_PMATCH_ADDR 0x000 +#define K_RFCR_PCOUNT_ADDR 0x006 +#define K_RFCR_FILTER_ADDR 0x200 + +#define M_RFCR_ULM _DD_MAKEMASK1(19) +#define M_RFCR_UHEN _DD_MAKEMASK1(20) +#define M_RFCR_MHEN _DD_MAKEMASK1(21) +#define M_RFCR_AARP _DD_MAKEMASK1(22) +#define M_RFCR_APAT0 _DD_MAKEMASK1(23) +#define M_RFCR_APAT1 _DD_MAKEMASK1(24) +#define M_RFCR_APAT2 _DD_MAKEMASK1(25) +#define M_RFCR_APAT3 _DD_MAKEMASK1(26) +#define M_RFCR_APAT (M_RFCR_APAT0 | M_RFCR_APAT1 | \ + M_RFCR_APAT2 | M_RFCR_APAT3 ) +#define M_RFCR_APM _DD_MAKEMASK1(27) +#define M_RFCR_AAU _DD_MAKEMASK1(28) +#define M_RFCR_AAM _DD_MAKEMASK1(29) +#define M_RFCR_AAB _DD_MAKEMASK1(30) +#define M_RFCR_RFEN _DD_MAKEMASK1(31) + + +/* 0x4C RFDR: Receive Filter/Match Data Register */ + +#define S_RFDR_RFDATA 0 +#define M_RFDR_RFDATA _DD_MAKEMASK(16,S_RFDR_RFDATA) +#define V_RFDR_RFDATA(x) _DD_MAKEVALUE(x,S_RFDR_RFDATA) +#define G_RFDR_RFDATA(x) _DD_GETVALUE(x,S_RFDR_RFDATA,M_RFDR_RFDATA) + +#define S_RFDR_BMASK 16 +#define M_RFDR_BMASK _DD_MAKEMASK(2,S_RFDR_BMASK) +#define V_RFDR_BMASK(x) _DD_MAKEVALUE(x,S_RFDR_BMASK) +#define G_RFDR_BMASK(x) _DD_GETVALUE(x,S_RFDR_BMASK,M_RFDR_BMASK) + + +/* 0x50 BRAR: Boot ROM Address Register */ + +#define S_BRAR_ADDR 0 +#define M_BRAR_ADDR _DD_MAKEMASK(16,S_BRAR_ADDR) +#define V_BRAR_ADDR(x) _DD_MAKEVALUE(x,S_BRAR_ADDR) +#define G_BRAR_ADDR(x) _DD_GETVALUE(x,S_BRAR_ADDR,M_BRAR_ADDR) + +#define M_BRAR_AUTOINC _DD_MAKEMASK1(31) + + +/* 0x54 BRDR: Boot ROM Data Register */ + + +/* 0x58 SRR: Silicon Revision Register */ + +#define S_SRR_REV 0 +#define M_SRR_REV _DD_MAKEMASK(16,S_SRR_REV) +#define V_SRR_REV(x) _DD_MAKEVALUE(x,S_SRR_REV) +#define G_SRR_REV(x) _DD_GETVALUE(x,S_SRR_REV,M_SRR_REV) + +#define K_REV_CVNG 0x00000302 +#define K_REV_DVNG_UJB 0x00000403 + + +/* 0x5C MIBC: Management Information Base Control Register */ + +#define M_MIBC_WRN _DD_MAKEMASK1(0) +#define M_MIBC_FRZ _DD_MAKEMASK1(1) +#define M_MIBC_ACLR _DD_MAKEMASK1(2) +#define M_MIBC_MIBS _DD_MAKEMASK1(3) + + +/* MIB Counters */ + +/* 0x60 RXErroredPkts */ +/* 0x64 RXFCSErrors */ +/* 0x68 RXMsdPktErrors */ +/* 0x6C RXFAErrors */ +/* 0x70 RXSymbolErrors */ +/* 0x74 RXFrameTooLong */ +/* 0x78 TXSQEErrors */ + + +/* See ../net/mii.h for fields of standard (MII) PHY registers */ + +#define K_83815_PHYID1 0x2000 +#define K_83815_PHYID2 0x5C21 + +#define K_ANNPTR_NULL 0x0001 + + +/* 0xC0 PHYSTS: PHY Status Register */ + +#define PHYSTS_RXERRLATCH 0x2000 +#define PHYSTS_POLARITYSTAT 0x1000 +#define PHYSTS_FALSECARRLATCH 0x0800 +#define PHYSTS_SIGNALDETECT 0x0400 +#define PHYSTS_DESCRAMBLOCK 0x0200 +#define PHYSTS_PAGERECVD 0x0100 +#define PHYSTS_MIIINT 0x0080 +#define PHYSTS_REMOTEFAULT 0x0040 +#define PHYSTS_JABBERDET 0x0020 +#define PHYSTS_ANCOMPLETE 0x0010 +#define PHYSTS_LOOPBACK 0x0008 +#define PHYSTS_DUPLEX 0x0004 +#define PHYSTS_SPEED10 0x0002 +#define PHYSTS_LINKSTAT 0x0001 + + +/* 0xC4 MICR: MII Interrupt Control Register */ + +#define MICR_INTEN 0x0002 +#define MICR_TINT 0x0001 + + +/* 0xC8 MISR: MII Interrupt Status and Misc. Control Register */ + +#define MISR_MINT 0x8000 +#define MISR_MSKLINK 0x4000 +#define MISR_MSKJAB 0x2000 +#define MISR_MSKRF 0x1000 +#define MISR_MSKANC 0x0800 +#define MISR_MSKFHF 0x0400 +#define MISR_MSKRHF 0x0200 + + +/* 0xD0 FCSCR: False Carrier Sense Counter Register */ + +#define FCSCR_FCSCNT 0x00FF + + +/* 0xD4 RECR: Receiver Error Counter Register */ + +#define RECR_RXERCNT 0x00FF + + +/* 0xD8 PCSR: 100 Mb/s PCS Configuration and Status Register */ + +#define PCSR_BYP4B5B 0x1000 +#define PCSR_FREECLK 0x0800 +#define PCSR_TQEN 0x0400 +#define PCSR_SDFORCEB 0x0200 +#define PCSR_SDOPTION 0x0100 +#define PCSR_FORCE100OK 0x0020 +#define PCSR_NRZIBYPASS 0x0004 + + +/* 0xE4 PHYCR: PHY Control Register */ + +#define PHYCR_PSR15 0x0800 +#define PHYCR_BISTSTATUS 0x0400 +#define PHYCR_BISTSTART 0x0200 +#define PHYCR_BPSTRETCH 0x0100 +#define PHYCR_PAUSESTS 0x0080 +#define PHYCR_PHYADDR 0x001F + + +/* 0xE8 TBTSCR: 10Base-T Status/Control Register */ + +#define TBTSCR_LPBK10DIS 0x0100 +#define TBTSCR_LPDIS 0x0080 +#define TBTSCR_FORCELINK10 0x0040 +#define TBTSCR_FORCEPOLCOR 0x0020 +#define TBTSCR_POLARITY 0x0010 +#define TBTSCR_AUTOPOLDIS 0x0008 +#define TBTSCR_HBDIS 0x0002 +#define TBTSCR_JABBERDIS 0x0001 + + +/* MacPhyter Transmit and Receive Descriptors */ + +/* Common Command/Status Fields */ +#define S_DES1_SIZE 0 +#define M_DES1_SIZE _DD_MAKEMASK(12,S_DES1_SIZE) +#define V_DES1_SIZE(x) _DD_MAKEVALUE(x,S_DES1_SIZE) +#define G_DES1_SIZE(x) _DD_GETVALUE(x,S_DES1_SIZE,M_DES1_SIZE) + +#define M_DES1_OK _DD_MAKEMASK1(27) +#define M_DES1_INTR _DD_MAKEMASK1(29) +#define M_DES1_MORE _DD_MAKEMASK1(30) +#define M_DES1_OWN _DD_MAKEMASK1(31) + +/* Transmit Command/Status Bits */ +#define S_DES1_CCNT 16 +#define M_DES1_CCNT _DD_MAKEMASK(4,S_DES1_CCNT) +#define V_DES1_CCNT(x) _DD_MAKEVALUE(x,S_DES1_CCNT) +#define G_DES1_CCNT(x) _DD_GETVALUE(x,S_DES1_CCNT,M_DES1_CCNT) + +#define M_DES1_EC _DD_MAKEMASK1(20) +#define M_DES1_OWC _DD_MAKEMASK1(21) +#define M_DES1_ED _DD_MAKEMASK1(22) +#define M_DES1_TD _DD_MAKEMASK1(23) +#define M_DES1_CRS _DD_MAKEMASK1(24) +#define M_DES1_TFU _DD_MAKEMASK1(25) +#define M_DES1_TXA _DD_MAKEMASK1(26) +#define M_DES1_SUPCRC _DD_MAKEMASK1(28) + +/* Receive Command/Status Bits */ +#define M_DES1_COL _DD_MAKEMASK1(16) +#define M_DES1_LBP _DD_MAKEMASK1(17) +#define M_DES1_FAE _DD_MAKEMASK1(18) +#define M_DES1_CRCE _DD_MAKEMASK1(19) +#define M_DES1_ISE _DD_MAKEMASK1(20) +#define M_DES1_RUNT _DD_MAKEMASK1(21) +#define M_DES1_LONG _DD_MAKEMASK1(22) +#define M_DES1_RX_ERRORS (M_DES1_CRCE | \ + M_DES1_COL | M_DES1_FAE | M_DES1_ISE | \ + M_DES1_RUNT | M_DES1_LONG | M_DES1_RXO) + +#define S_DES1_DEST 23 +#define M_DES1_DEST _DD_MAKEMASK(2,S_DES1_DEST) +#define V_DES1_DEST(x) _DD_MAKEVALUE(x,S_DES1_DEST) +#define G_DES1_DEST(x) _DD_GETVALUE(x,S_DES1_DEST,M_DES1_DEST) + +#define K_DEST_REJECT 0 +#define K_DEST_UNICAST 1 +#define K_DEST_MULTICAST 2 +#define K_DEST_BROADCAST 3 + +#define M_DES1_RXO _DD_MAKEMASK1(25) +#define M_DES1_RXA _DD_MAKEMASK1(26) +#define M_DES1_INCCRC _DD_MAKEMASK1(28) + +#endif /* _DP83815_H_ */ diff --git a/cfe/cfe/dev/ns16550.h b/cfe/cfe/dev/ns16550.h new file mode 100644 index 0000000..b603ccc --- /dev/null +++ b/cfe/cfe/dev/ns16550.h @@ -0,0 +1,143 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * NS16550 Serial Port definitions File: ns16550.h + * + * This defines the hardware registers of 16550 compatible UARTs + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + +/* + * NS16550 UART registers + */ + +#ifndef _NS16550_H_ +#define _NS16550_H_ + +#ifndef NS16550_HZ +#define NS16550_HZ 1843200 +#endif + +/* + * NS16550 UART registers + */ + +/* Register definitions */ + +#define R_UART_DATA 0 +#define R_UART_IER 1 +#define R_UART_IIR 2 +#define R_UART_FIFO R_UART_IIR +#define R_UART_CFCR 3 +#define R_UART_MCR 4 +#define R_UART_LSR 5 +#define R_UART_MSR 6 +#define R_UART_SCR 7 + +/* 16 bit baud rate divisor (lower byte in UART_DATA, upper in UART_IER) */ +#define BRTC(x) (NS16550_HZ / (16*(x))) + +/* interrupt enable register */ +#define IER_ERXRDY 0x1 /* int on rx ready */ +#define IER_ETXRDY 0x2 /* int on tx ready */ +#define IER_ERLS 0x4 /* int on line status change */ +#define IER_EMSC 0x8 /* int on modem status change */ + +/* interrupt identification register */ +#define IIR_IMASK 0xf /* mask */ +#define IIR_RXTOUT 0xc /* receive timeout */ +#define IIR_RLS 0x6 /* receive line status */ +#define IIR_RXRDY 0x4 /* receive ready */ +#define IIR_TXRDY 0x2 /* transmit ready */ +#define IIR_NOPEND 0x1 /* nothing */ +#define IIR_MLSC 0x0 /* modem status */ +#define IIR_FIFO_MASK 0xc0 /* set if FIFOs are enabled */ + +/* fifo control register */ +#define FIFO_ENABLE 0x01 /* enable fifo */ +#define FIFO_RCV_RST 0x02 /* reset receive fifo */ +#define FIFO_XMT_RST 0x04 /* reset transmit fifo */ +#define FIFO_DMA_MODE 0x08 /* enable dma mode */ +#define FIFO_TRIGGER_1 0x00 /* trigger at 1 char */ +#define FIFO_TRIGGER_4 0x40 /* trigger at 4 chars */ +#define FIFO_TRIGGER_8 0x80 /* trigger at 8 chars */ +#define FIFO_TRIGGER_14 0xc0 /* trigger at 14 chars */ + +/* character format control register */ +#define CFCR_DLAB 0x80 /* divisor latch */ +#define CFCR_SBREAK 0x40 /* send break */ +#define CFCR_PZERO 0x30 /* zero parity */ +#define CFCR_PONE 0x20 /* one parity */ +#define CFCR_PEVEN 0x10 /* even parity */ +#define CFCR_PODD 0x00 /* odd parity */ +#define CFCR_PENAB 0x08 /* parity enable */ +#define CFCR_STOPB 0x04 /* 2 stop bits */ +#define CFCR_8BITS 0x03 /* 8 data bits */ +#define CFCR_7BITS 0x02 /* 7 data bits */ +#define CFCR_6BITS 0x01 /* 6 data bits */ +#define CFCR_5BITS 0x00 /* 5 data bits */ + +/* modem control register */ +#define MCR_LOOPBACK 0x10 /* loopback */ +#define MCR_IENABLE 0x08 /* output 2 = int enable */ +#define MCR_DRS 0x04 /* output 1 = xxx */ +#define MCR_RTS 0x02 /* enable RTS */ +#define MCR_DTR 0x01 /* enable DTR */ + +/* line status register */ +#define LSR_RCV_FIFO 0x80 /* error in receive fifo */ +#define LSR_TSRE 0x40 /* transmitter empty */ +#define LSR_TXRDY 0x20 /* transmitter ready */ +#define LSR_BI 0x10 /* break detected */ +#define LSR_FE 0x08 /* framing error */ +#define LSR_PE 0x04 /* parity error */ +#define LSR_OE 0x02 /* overrun error */ +#define LSR_RXRDY 0x01 /* receiver ready */ +#define LSR_RCV_MASK 0x1f + +/* modem status register */ +#define MSR_DCD 0x80 /* DCD active */ +#define MSR_RI 0x40 /* RI active */ +#define MSR_DSR 0x20 /* DSR active */ +#define MSR_CTS 0x10 /* CTS active */ +#define MSR_DDCD 0x08 /* DCD changed */ +#define MSR_TERI 0x04 /* RI changed */ +#define MSR_DDSR 0x02 /* DSR changed */ +#define MSR_DCTS 0x01 /* CTS changed */ + +#endif /* _NS16550_H_ */ diff --git a/cfe/cfe/dev/pci_devs.c b/cfe/cfe/dev/pci_devs.c new file mode 100644 index 0000000..a1ba789 --- /dev/null +++ b/cfe/cfe/dev/pci_devs.c @@ -0,0 +1,79 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * PCI device selection and initialization File: pci_devs.c + * + * These are the routines to include the PCI drivers and to hook any + * devices with special configuration requirements.. + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#if CFG_PCI +#include "sbmips.h" +#include "lib_types.h" +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "dev_ide.h" +#include "env_subr.h" + +extern cfe_driver_t pciidedrv; /* PCI IDE controller */ +extern cfe_driver_t dc21143drv; /* Tulip Ethernet */ +extern cfe_driver_t dp83815drv; /* MacPhyter Ethernet */ +#if CFG_DOWNLOAD +extern cfe_driver_t bcm1250drv; /* BCM1250 as a device */ +#endif +extern cfe_driver_t ns16550pci_uart; /* PCI serial port */ + +void pci_add_devices(int init_pci); +void pci_add_devices(int init_pci) +{ + if (init_pci) { + cfe_add_device(&pciidedrv,0,IDE_PROBE_MASTER_TYPE(IDE_DEVTYPE_DISK),NULL); + cfe_add_device(&dc21143drv,0,0,env_getenv("TULIP0_HWADDR")); + cfe_add_device(&dp83815drv,0,0,NULL); + +#if CFG_DOWNLOAD + /* Access to bcm1250 in PCI device mode */ + cfe_add_device(&bcm1250drv,0,0,NULL); +#endif + cfe_add_device(&ns16550pci_uart,0,0,0); + } +} +#endif /* CFG_PCI */ + |